Merge "msm: audio: qdsp6v2: Fix seek issue in tunnel player"
diff --git a/Documentation/arm/msm/msm_smp2p.txt b/Documentation/arm/msm/msm_smp2p.txt
new file mode 100644
index 0000000..4f77614
--- /dev/null
+++ b/Documentation/arm/msm/msm_smp2p.txt
@@ -0,0 +1,416 @@
+Introduction
+============
+The Shared Memory Point to Point (SMP2P) protocol facilitates communication of
+a single 32-bit value between two processors. Each value has a single writer
+(the local side) and a single reader (the remote side). Values are uniquely
+identified in the system by the directed edge (local processor ID to remote
+processor ID) and a string identifier.
+
+Version and feature negotiation has been included in the design to allow for
+phased upgrades of all processors.
+
+Software Architecture Description
+=================================
+The data and interrupt coupling between processors is shown in Fig. 1. Each
+processor is responsible for creating the outgoing SMEM items and each item is
+writable by the local processor and readable by the remote processor. By using
+two separate SMEM items that are single-reader and single-writer, SMP2P does
+not require any remote locking mechanisms.
+
+The client API uses the Linux GPIO and interrupt framework to expose a virtual
+GPIO and a virtual interrupt controller for each entry.
+
+ =================
+ | |
+ -----write------>|SMEM item A->B |-----read------
+ | | | |
+ | ================= |
+ | |
+ | v
+ GPIO API => ------------ ======= Interrupt line ======> ------------
+ Processor A Processor B
+ Interrupt <= ------------ <====== Interrupt line ======= ------------
+ API ^ |
+ | |
+ | |
+ | ================= |
+ | | | |
+ ------read-------|SMEM item A<-B |<-----write----
+ | |
+ =================
+
+ Fig 1
+
+
+Design
+======
+Each SMEM item contains a header that is used to identify and manage the edge
+along with an array of actual entries. The overall structure is captured in
+Fig 2 and the details of the header and entries are covered later in this
+section. The memory format of all shared structures is little-endian.
+
+ -----------------------------------------------
+ | SMEM item A->B |
+ | |
+ | ----------------------------------------- |
+ | |31 24| 16| 8| 0| |
+ | |----------|---------|----------|---------| |
+ | | Identifier Constant(Magic Number) | |
+ | |----------|---------|----------|---------| |
+ | | Feature Flags |Version | |
+ | | |Number | |
+ | |----------|---------|----------|---------| |
+ | | Remote Proc ID |Local Proc ID | |
+ | |----------|---------|----------|---------| |
+ | | Entries Valid | Entries Total | |
+ | |-----------------------------------------| |
+ | |
+ | |
+ | ----------------------------------------- |
+ | | Entry 0 | |
+ | | ---------------------------------- | |
+ | | | Identifier String | | |
+ | | |---------------------------------| | |
+ | | | Data | | |
+ | | |---------------------------------| | |
+ | |---------------------------------------| |
+ | ----------------------------------------- |
+ | | Entry 1 | |
+ | | ---------------------------------- | |
+ | | | Identifier String | | |
+ | | |---------------------------------| | |
+ | | | Data | | |
+ | | |---------------------------------| | |
+ | |---------------------------------------| |
+ | - |
+ | - |
+ | - |
+ | ----------------------------------------- |
+ | | Entry N | |
+ | | ---------------------------------- | |
+ | | | Identifier String | | |
+ | | |---------------------------------| | |
+ | | | Data | | |
+ | | |---------------------------------| | |
+ | |---------------------------------------| |
+ -----------------------------------------------
+
+ Fig 2
+
+
+The header of each SMEM item contains metadata that describes the processors
+using the edge, the version information, and the entry count. The constant
+identifier is used as a magic number to enable extraction of the items from a
+memory dump. The size of each entry depends upon the version, but the number
+of total entries (and hence the size of each SMEM item) is configurable with a
+suggested value of 16.
+
+The number of valid entries is used to indicate how many of the Entries Total
+are currently used and are current valid.
+
+ ---------------------------------------------------------------------------
+ |Field Size Description Valid Values |
+ ---------------------------------------------------------------------------
+ | Identifier 4 Bytes Value used to identify |
+ | Constant structure in memory. Must be set to $SMP |
+ | Useful for debugging. (0x504D5324) |
+ ---------------------------------------------------------------------------
+ | Local 2 Bytes Writing processor ID. Refer Processor ID Table 3|
+ | Processor |
+ | ID |
+ ---------------------------------------------------------------------------
+ | Remote 2 Bytes Reading processor ID. Refer Processor ID Table 3|
+ | Processor |
+ | ID |
+ ---------------------------------------------------------------------------
+ | Version 1 Bytes Refer to Version |
+ | Number Feature Negotiation Must be set to 1. |
+ | section. |
+ ---------------------------------------------------------------------------
+ | Feature 3 Bytes Refer to Version |
+ | flags and Feature Negotiation Must be set to zero. |
+ | section. |
+ ---------------------------------------------------------------------------
+ | Entries 2 Bytes Total number of Must be 0 or greater. |
+ | Total entries. |
+ ---------------------------------------------------------------------------
+ | Entries 2 Bytes Number of valid Must be between 0 |
+ | Valid entries. and Entries Total. |
+ ---------------------------------------------------------------------------
+ | Reserved 4 Bytes Reserved Must be set to 0. |
+ ---------------------------------------------------------------------------
+ Table 1 - SMEM Item Header
+
+The content of each SMEM entries is described in Table 2 and consists of a
+string identifier and a 32-bit data value. The string identifier must be
+unique for each SMEM item. The data value is opaque to SMP2P giving the client
+complete flexibility as to its usage.
+
+ ----------------------- --------------------- -----------------------------
+ | Field | Size | Description | Valid Values |
+ ------------|----------|---------------------|-----------------------------
+ | | | | |
+ | Identifier | 16 Bytes | Null Terminated | NON-NULL for |
+ | String | | ASCII string. | valid entries. |
+ | | | | |
+ ------------|----------|---------------------|-----------------------------
+ | Data | 4 Bytes | Data | Any (client defined) |
+ ------------ ---------- --------------------- -----------------------------
+ Table 2 - Entry Format
+
+
+The processor IDs in the system are fixed and new processors IDs will be
+added to the end of the list (Table 3).
+
+ -------------------------------------------------
+ | Processor Name | ID value |
+ -------------------------------------------------
+ | Application processor | 0 |
+ -------------------------------------------------
+ | Modem processor | 1 |
+ -------------------------------------------------
+ | Audio processor | 2 |
+ -------------------------------------------------
+ | Sensor processor | 3 |
+ -------------------------------------------------
+ | Wireless processor | 4 |
+ -------------------------------------------------
+ | Modem Fw | 5 |
+ -------------------------------------------------
+ | Power processor | 6 |
+ -------------------------------------------------
+ | NUM PROCESSORS | 7 |
+ -------------------------------------------------
+ Table 3 - Processor IDs
+
+SMEM Item
+---------
+The responsibility of creating an SMEM item is with the local processor that is
+initiating outbound traffic. After creating the item, the local and remote
+processors negotiate the version and feature flags for the item to ensure
+compatibility.
+
+Table 4 lists the SMEM item base identifiers. To get the SMEM item ID for a
+particular edge, the remote processor ID (Table 3) is added to the base item ID
+for the local processor (Table 4). For example, the Apps ==> Modem (id 1) SMEM
+Item ID will be 427 + 1 = 428.
+
+ --------------------------------------------------
+ | Description | SMEM ID value |
+ --------------------------------------------------
+ | Apps SMP2P SMEM Item base | 427 |
+ --------------------------------------------------
+ | Modem SMP2P SMEM Item base | 435 |
+ --------------------------------------------------
+ | Audio SMP2P SMEM Item base | 443 |
+ --------------------------------------------------
+ | Wireless SMP2P SMEM Item base | 451 |
+ --------------------------------------------------
+ | Power SMP2P SMEM Item base | 459 |
+ --------------------------------------------------
+ Table 4 - SMEM Items Base IDs
+
+
+Version and Feature Negotiation
+-------------------------------
+To enable upgrading without breaking the system and to enable graceful feature
+fall-back support, SMP2P supports a version number and feature flags. The
+combination of the version number and feature flags enable:
+ 1) SMP2P software updates to be rolled out to each processor separately.
+ 2) Individual features to be enabled or disabled per connection or edge.
+
+The version number represents any change in SMP2P that breaks compatibility
+between processors. Examples would be a change in the shared data structures
+or changes to fundamental behavior. Each implementation of SMP2P must be able
+to support a minimum of the current version and the previous version.
+
+The feature flags represent any changes in SMP2P that are optional and
+backwards compatible. Endpoints will negotiate the supported flag when the
+SMEM items are created and they cannot be changed after negotiation has been
+completed.
+
+
+Negotiation Algorithm
+----------------------
+While creating the SMEM item the following algorithm shall be used.
+
+ if remote endpoint's SMEM Item exists
+ Read remote version number and flags
+ Local version number must be lower of
+ - remote version number
+ - highest supported local version number
+ Flags value is bitwise AND of
+ - remote feature flags
+ - locally supported flags
+ Create SMEM item and populate negotiated number and flags
+ Interrupt remote processor
+ if version and flags match, negotiation is complete, else wait
+ for remote interrupt below.
+ Else
+ Create SMEM item and populate it with highest supported version and any
+ requested feature flag.
+ Interrupt remote processor.
+ Wait for Interrupt below.
+
+Upon receiving the interrupt from remote processor and negotiation is not
+complete, check the version number and feature flags:
+ if equal, negotiation is complete.
+ if remote number is less than local number, and remote number is
+ supported:
+ Set local version number to remote version number
+ Bitwise AND local flags with remote flags
+ Interrupt remote processor
+ Negotiation is complete
+ if remote number is not supported, then negotiation has failed
+ Set version number to 0xFF and report failure in kernel log.
+ if remote number is more than local number:
+ Wait for remote endpoint to process our interrupt and negotiate down.
+
+
+Creating an SMEM Entry
+----------------------
+Each new SMEM entry used in data transfer must be created at the end of the
+entry array in the SMEM item and cannot be deleted until the system is
+rebooted. The following sequence is be followed:
+ 1) Compare Entries Valid and Entries Total to verify if there is room in the
+ entry array for this request (if not, return error code to client).
+ 2) Populate the Identifier of new entry and do a write memory barrier.
+ 3) Update Entries Valid and Entries Total and do a write memory barrier.
+ 4) Interrupt remote endpoint.
+
+
+Entry Write
+-----------
+An entry write is achieved by the following sequence of operations:
+ 1) Update data field in the entry and do a write memory barrier.
+ 2) Interrupt remote endpoint.
+
+
+Entry Read / Receiving Interrupts
+---------------------------------
+An interrupt will be received from the remote system for one or more of the following events:
+ 1) Initialization
+ 2) Entry change
+ 3) New Entry
+
+As long as the SMEM item initialization is complete, then each interrupt should
+trigger SMP2P to:
+ 1) Compare valid entry data value to cached value and notify client if it
+ has changed.
+ 2) Compare Entries Valid to cached value. If changed, initialize new entries.
+
+Performance
+===========
+No performance issues are anticipated as the signaling rate is expected to be
+low and is performed in interrupt context which minimizes latency.
+
+Interfaces
+================
+SMP2P is only supported in the kernel and interfaces with clients through the
+GPIO and interrupt subsystems.
+
+To map an entry to the client, the client must add two nodes to the Device
+Tree:
+ 1) A node that matches "qcom,smp2pgpio" to create the entry
+ 2) A node that matches the client driver to provide the GPIO pin mapping
+
+The details of the device tree entries are contained in the file
+Documentionat/devicetree/bindings/arm/msm/smp2p.txt.
+
+
+ /* SMP2P Test Driver for inbound entry. */
+ smp2pgpio_smp2p_7_in: qcom,smp2pgpio-smp2p-7-in {
+ compatible = "qcom,smp2pgpio";
+ qcom,entry-name = "smp2p";
+ qcom,remote-pid = <7>;
+ qcom,is_inbound;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ /* SMP2P Test Client for inbound entry. */
+ qcom,smp2pgpio_test_smp2p_7_in {
+ compatible = "qcom,smp2pgpio_test_smp2p_7_in";
+ gpios = <&smp2pgpio_smp2p_7_in 0 0>,
+ <&smp2pgpio_smp2p_7_in 1 0>,
+ . . .
+ <&smp2pgpio_smp2p_7_in 31 0>;
+ };
+
+ /* SMP2P Test Driver for outbound entries */
+ smp2pgpio_smp2p_345_out: qcom,smp2pgpio-smp2p-7-out {
+ compatible = "qcom,smp2pgpio";
+ qcom,entry-name = "smp2p";
+ qcom,remote-pid = <7>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ /* SMP2P Test Client for outbound entry. */
+ qcom,smp2pgpio_test_smp2p_7_out {
+ compatible = "qcom,smp2pgpio_test_smp2p_7_out";
+ gpios = <&smp2pgpio_smp2p_7_out 0 0>,
+ <&smp2pgpio_smp2p_7_out 1 0>,
+ . . .
+ <&smp2pgpio_smp2p_7_out 31 0>;
+
+The client can use a match entry for "qcom,smp2pgpio_test_smp2p_7_in" to
+retrieve the Device Tree configuration node. Once that node has been
+retrieved, the client can call of_get_gpio() to get the virtual GPIO pin and
+also use gpio_to_irq() to map the GPIO pin to a virtual interrupt. After that
+point, the standard GPIO and Interrupt APIs can be used to manipulate the SMP2P
+entries and receive notifications of changes. Examples of typical function
+calls are shown below:
+ of_get_gpio()
+ gpio_get_value()
+ gpio_set_value()
+ gpio_to_irq()
+ request_irq()
+ free_irq()
+
+Please reference the unit test code for example usage.
+
+Debug
+=====
+The state values and names for all entries accessible by the Apps are
+accessible through debugfs nodes for general debug purposes.
+
+Debugfs entries for triggering unit-tests are also exported.
+
+Internal logging will be performed using the IPC Logging module to enable
+post-mortem analysis.
+
+Testing
+=======
+On-target unit testing will be done to verify internal functionality and the
+GPIO/IRQ API's.
+
+Driver parameters
+=================
+One module parameter will be provided to change the verbosity of internal logging.
+
+Config options
+==============
+Configuration of interrupts will be done using Device Tree. By default, the
+testing components will be enabled since it does not affect performance and has
+a minimal impact on kernel size. However, customers can disable the testing
+components for size optimization.
+
+ CONFIG_MSM_SMP2P - enables SMP2P core functionality
+ CONFIG_MSM_SMP2P_TEST - enables unit test support
+
+Dependencies
+===========
+Requires SMEM for creating the SMEM items.
+
+User Space utilities
+====================
+No userspace utilities are planned.
+
+Known issues
+============
+None.
diff --git a/Documentation/devicetree/bindings/arm/msm/jtag-mm.txt b/Documentation/devicetree/bindings/arm/msm/jtag-mm.txt
new file mode 100644
index 0000000..21dead3
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/msm/jtag-mm.txt
@@ -0,0 +1,21 @@
+* JTAG-MM
+
+The jtag-mm entry specifies the memory mapped addresses for the debug and ETM
+registers. The jtag-mm driver uses these to save and restore the registers
+using memory mapped access during power collapse so as to retain their state
+accross power collapse. This is necessary in case cp14 access to the registers
+is not permitted.
+
+Required Properties:
+compatible: component name used for driver matching, should be "qcom,jtag-mm"
+reg: physical base address and length of the register set
+reg-names: should be "etm-base" for etm register set and "debug-base" for debug
+ register set.
+
+Example:
+jtag_mm: jtagmm@fc332000 {
+ compatible = "qcom,jtag-mm";
+ reg = <0xfc332000 0x1000>,
+ <0xfc333000 0x1000>;
+ reg-names = "etm-base","debug-base";
+ };
diff --git a/Documentation/devicetree/bindings/arm/msm/lpm-levels.txt b/Documentation/devicetree/bindings/arm/msm/lpm-levels.txt
index 4129f7f..b359c49 100644
--- a/Documentation/devicetree/bindings/arm/msm/lpm-levels.txt
+++ b/Documentation/devicetree/bindings/arm/msm/lpm-levels.txt
@@ -8,6 +8,10 @@
lpm-level. The units for voltage are dependent on the PMIC used on the target
and are in uV.
+The optional properties are:
+
+- qcom,use-qtimer: Indicates whether the target uses the synchronized QTimer.
+
The required nodes for lpm-levels are:
- compatible: "qcom,lpm-levels"
@@ -34,6 +38,7 @@
Example:
qcom,lpm-levels {
+ qcom,use-qtimer;
qcom,lpm-level@0 {
reg = <0>;
qcom,mode = <0>; /* MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT */
diff --git a/Documentation/devicetree/bindings/arm/msm/msm_tspp.txt b/Documentation/devicetree/bindings/arm/msm/msm_tspp.txt
new file mode 100644
index 0000000..2b5e143
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/msm/msm_tspp.txt
@@ -0,0 +1,73 @@
+TSPP Driver
+
+For information on the TSPP driver, please refer to the TSPP driver
+documentation: Documentation/arm/msm/tspp.txt.
+
+The devicetree representation of the TSPP block should be:
+
+Required properties:
+
+- compatible: "qcom,msm_tspp"
+- cell-index: <0> - represents device ID.
+- reg: physical memory base addresses and sizes for the following:
+ TSIF0, TSIF1, TSPP and TSPP_BAM.
+- reg-names: names of the memory regions.
+- interrupts: represents IRQ numbers for the following:
+ TSIF_TSPP_IRQ, TSIF0_IRQ, TSIF1_IRQ, TSIF_BAM_IRQ.
+- interrupt-names: TSPP, TSIF and BAM interrupt names.
+- qcom,tsif-pclk: interface clock name.
+- qcom,tsif-ref-clk: reference clock name.
+ The driver uses clk_get to get the clocks by name. The clocks
+ should be defined in the relevant clock file (e.g. clock-8974.c).
+- gpios: GPIO numbers for TSIF0 (CLK, EN, DATA and SYNC) and TSIF1 (same).
+- qcom,gpio-names: GPIO names - strings describing the GPIO functionality.
+- qcom,gpios-func: GPIO functionality according to the GPIO functionality table.
+ GPIO pins can have more than a single functionality, and the TSPP driver
+ is responsible for configuring the GPIOs to work in TSIF functionality
+ based on this parameter.
+ Note: it is assumed that the functionality value (e.g. 1 in 8974 case)
+ is applicable to all TSIF GPIOs.
+
+Example (for 8974 platform, avaialble at msm8974.dtsi):
+
+ tspp: msm_tspp@f99d8000 {
+ compatible = "qcom,msm_tspp";
+ cell-index = <0>;
+ reg = <0xf99d8000 0x1000>, /* MSM_TSIF0_PHYS */
+ <0xf99d9000 0x1000>, /* MSM_TSIF1_PHYS */
+ <0xf99da000 0x1000>, /* MSM_TSPP_PHYS */
+ <0xf99c4000 0x14000>; /* MSM_TSPP_BAM_PHYS */
+ reg-names = "MSM_TSIF0_PHYS",
+ "MSM_TSIF1_PHYS",
+ "MSM_TSPP_PHYS",
+ "MSM_TSPP_BAM_PHYS";
+ interrupts = <0 153 0>, /* TSIF_TSPP_IRQ */
+ <0 151 0>, /* TSIF0_IRQ */
+ <0 152 0>, /* TSIF1_IRQ */
+ <0 154 0>; /* TSIF_BAM_IRQ */
+ interrupt-names = "TSIF_TSPP_IRQ",
+ "TSIF0_IRQ",
+ "TSIF1_IRQ",
+ "TSIF_BAM_IRQ";
+ qcom,tsif-pclk = "iface_clk";
+ qcom,tsif-ref-clk = "ref_clk";
+ gpios = <&msmgpio 89 0>, /* TSIF0 CLK */
+ <&msmgpio 90 0>, /* TSIF0 EN */
+ <&msmgpio 91 0>, /* TSIF0 DATA */
+ <&msmgpio 92 0>, /* TSIF0 SYNC */
+ <&msmgpio 93 0>, /* TSIF1 CLK */
+ <&msmgpio 94 0>, /* TSIF1 EN */
+ <&msmgpio 95 0>, /* TSIF1 DATA */
+ <&msmgpio 96 0>; /* TSIF1 SYNC */
+ qcom,gpio-names = "tsif_clk",
+ "tsif_en",
+ "tsif_data",
+ "tsif_sync",
+ "tsif_clk",
+ "tsif_en",
+ "tsif_data",
+ "tsif_sync";
+ qcom,gpios-func = <1>;
+ };
+
+
diff --git a/Documentation/devicetree/bindings/arm/msm/rpm-regulator-smd.txt b/Documentation/devicetree/bindings/arm/msm/rpm-regulator-smd.txt
index 93b5144..d930799 100644
--- a/Documentation/devicetree/bindings/arm/msm/rpm-regulator-smd.txt
+++ b/Documentation/devicetree/bindings/arm/msm/rpm-regulator-smd.txt
@@ -113,8 +113,14 @@
14 = 1.37
15 = 1.28
16 = 1.20
-- qcom,init-head-room: Voltage head room in uV required for the
- regulator
+- qcom,init-head-room: Voltage head room in mV required for the
+ regulator. This head room value should be used
+ in situations where the device connected to the
+ output of the regulator has low noise tolerance.
+ Note that the RPM independently enforces a
+ safety head room value for subregulated LDOs
+ which is sufficient to account for LDO drop-out
+ voltage.
- qcom,init-quiet-mode: Specify that quiet mode is needed for an SMPS
regulator in order to have lower output noise.
Supported values are:
diff --git a/Documentation/devicetree/bindings/arm/msm/rpm-smd.txt b/Documentation/devicetree/bindings/arm/msm/rpm-smd.txt
index 8ebd3ba..7235a1a 100644
--- a/Documentation/devicetree/bindings/arm/msm/rpm-smd.txt
+++ b/Documentation/devicetree/bindings/arm/msm/rpm-smd.txt
@@ -20,6 +20,17 @@
- rpm-channel-type: The interal SMD edge for this subsystem found in
<mach/msm_smd.h>
+Optional properties
+- rpm-standlone: Allow the driver to run in standalone mode. This is a
+ suggestion to the RPM driver and if the SMD
+ channel is made available for RPM, the driver
+ would continue to send requests to RPM processor,
+ but if the SMD channel is unavailable, driver will
+ return success even though the data is not sent
+ to the RPM processor. In the absence of this
+ option, the driver will fail if the SMD channel
+ is unavailable.
+
Example:
qcom,rpm-smd {
diff --git a/Documentation/devicetree/bindings/arm/msm/smp2p.txt b/Documentation/devicetree/bindings/arm/msm/smp2p.txt
new file mode 100644
index 0000000..7a5f506
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/msm/smp2p.txt
@@ -0,0 +1,21 @@
+Qualcomm SMSM Point-to-Point (SMP2P)
+
+Required properties:
+-compatible : should be "qcom,smp2p"
+-reg : the location and offset of the irq register base memory
+-reg-names : "irq-reg-base", "irq-reg-offset" - string to identify the irq
+ register region and offset values
+-qcom,remote-pid : the SMP2P remote processor ID (see smp2p_private_api.h)
+-qcom,irq-bitmask : the sending irq bitmask
+-interrupts : the receiving interrupt line
+
+Example:
+
+ qcom,smp2p-modem {
+ compatible = "qcom,smp2p";
+ reg = <0xfa006000 0x1000>, <0x8 0x0>;
+ reg-names = "irq-reg-base", "irq-reg-offset";
+ qcom,remote-pid = <1>;
+ qcom,irq-bitmask = <0x4000>;
+ interrupts = <0 27 1>;
+ };
diff --git a/Documentation/devicetree/bindings/coresight/coresight.txt b/Documentation/devicetree/bindings/coresight/coresight.txt
index f860618..b3037d8 100644
--- a/Documentation/devicetree/bindings/coresight/coresight.txt
+++ b/Documentation/devicetree/bindings/coresight/coresight.txt
@@ -32,6 +32,7 @@
- coresight-child-ports : list of input port numbers of the children
- coresight-default-sink : represents the default compile time CoreSight sink
- qcom,pc-save : program counter save implemented
+- qcom,blk-size : block size for tmc-etr to usb transfers
Examples:
diff --git a/Documentation/devicetree/bindings/gpio/gpio-smp2p.txt b/Documentation/devicetree/bindings/gpio/gpio-smp2p.txt
new file mode 100644
index 0000000..131c9d1
--- /dev/null
+++ b/Documentation/devicetree/bindings/gpio/gpio-smp2p.txt
@@ -0,0 +1,77 @@
+Qualcomm SMSM Point-to-Point (SMP2P) GPIO Driver
+
+Used to map an SMP2P entry and remote processor ID to a virtual GPIO controller
+and virtual interrupt controller.
+
+Required properties:
+-compatible : should be "qcom,smp2pgpio";
+-qcom,entry-name : name of the SMP2P entry
+-qcom,remote-pid : the SMP2P remote processor ID (see smp2p_private_api.h)
+-gpio-controller : specifies that this is a GPIO controller
+-#gpio-cells : number of GPIO cells (should always be <2>)
+-interrupt-controller : specifies that this is an interrupt controller
+-#interrupt-cells : number of interrupt cells (should always be <2>)
+
+Optional properties:
+-qcom,is-inbound : specifies that this is an inbound entry (default is outbound)
+
+Comments:
+All device tree entries must be unique. Therefore to prevent naming collisions
+between clients, it is recommended that the DT nodes should be named using the
+format:
+ smp2pgpio_<ENTRY_NAME>_<REMOTE PID>_<in|out>
+
+Example:
+ /* Maps inbound "smp2p" entry on remote PID 7 to GPIO controller. */
+ smp2pgpio_smp2p_7_in: qcom,smp2pgpio-smp2p-7-in {
+ compatible = "qcom,smp2pgpio";
+ qcom,entry-name = "smp2p";
+ qcom,remote-pid = <7>;
+ qcom,is-inbound;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ /*
+ * Maps inbound "smp2p" entry on remote PID 7 to client driver
+ * "qcom,smp2pgpio_test_smp2p_7_in".
+ *
+ * Note: If all 32-pins are used by this client, then you
+ * can just list pin 0 here as a shortcut.
+ */
+ qcom,smp2pgpio_test_smp2p_7_in {
+ compatible = "qcom,smp2pgpio_test_smp2p_7_in";
+ gpios = <&smp2pgpio_smp2p_7_in 0 0>, /* pin 0 */
+ <&smp2pgpio_smp2p_7_in 1 0>,
+ . . .
+ <&smp2pgpio_smp2p_7_in 31 0>; /* pin 31 */
+ };
+
+
+ /* Maps outbound "smp2p" entry on remote PID 7 to GPIO controller. */
+ smp2pgpio_smp2p_7_out: qcom,smp2pgpio-smp2p-7-out {
+ compatible = "qcom,smp2pgpio";
+ qcom,entry-name = "smp2p";
+ qcom,remote-pid = <7>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ /*
+ * Maps outbound "smp2p" entry on remote PID 7 to client driver
+ * "qcom,smp2pgpio_test_smp2p_7_out".
+ *
+ * Note: If all 32-pins are used by this client, then you
+ * can just list pin 0 here as a shortcut.
+ */
+ qcom,smp2pgpio_test_smp2p_7_out {
+ compatible = "qcom,smp2pgpio_test_smp2p_7_out";
+ gpios = <&smp2pgpio_smp2p_7_out 0 0>, /* pin 0 */
+ <&smp2pgpio_smp2p_7_out 1 0>,
+ . . .
+ <&smp2pgpio_smp2p_7_out 31 0>; /* pin 31 */
+ };
diff --git a/Documentation/devicetree/bindings/iommu/msm_iommu.txt b/Documentation/devicetree/bindings/iommu/msm_iommu.txt
index f093f51..2a631ed 100644
--- a/Documentation/devicetree/bindings/iommu/msm_iommu.txt
+++ b/Documentation/devicetree/bindings/iommu/msm_iommu.txt
@@ -3,12 +3,19 @@
Required properties:
- compatible : one of:
- "qcom,msm-smmu-v2"
-- reg : offset and length of the register set for the device.
+- reg : offset and length of the register set for the device. Optional
+ offset and length for clock register for additional clock that
+ needs to be turned on for access to this IOMMU.
+- reg-names: "iommu_base", "clk_base" (optional)
+- label: name of this IOMMU instance.
Optional properties:
- qcom,iommu-secure-id : Secure identifier for the IOMMU block
- qcom,secure-context : boolean indicating that a context is secure and
programmed by the secure environment.
+- qcom,alt-vdd-supply : Alternative regulator needed to access IOMMU
+ configuration registers.
+- interrupts : should contain the performance monitor overflow interrupt number.
- List of sub nodes, one for each of the translation context banks supported.
Each sub node has the following required properties:
@@ -39,23 +46,25 @@
Example:
- qcom,iommu@fda64000 {
- compatible = "qcom,msm-smmu-v2";
- reg = <0xfda64000 0x10000>;
+ qcom,iommu@fda64000 {
+ compatible = "qcom,msm-smmu-v2";
+ reg = <0xfda64000 0x10000>;
+ reg-names = "iommu_base";
vdd-supply = <&gdsc_iommu>;
qcom,iommu-bfb-regs = <0x204c 0x2050>;
qcom,iommu-bfb-data = <0xffff 0xffce>;
+ label = "iommu_0";
- qcom,iommu-ctx@fda6c000 {
- reg = <0xfda6c000 0x1000>;
- interrupts = <0 70 0>;
- qcom,iommu-ctx-sids = <0 2>;
+ qcom,iommu-ctx@fda6c000 {
+ reg = <0xfda6c000 0x1000>;
+ interrupts = <0 70 0>;
+ qcom,iommu-ctx-sids = <0 2>;
label = "ctx_0";
- };
- qcom,iommu-ctx@fda6d000 {
- reg = <0xfda6d000 0x1000>;
- interrupts = <0 71 0>;
- qcom,iommu-ctx-sids = <1>;
+ };
+ qcom,iommu-ctx@fda6d000 {
+ reg = <0xfda6d000 0x1000>;
+ interrupts = <0 71 0>;
+ qcom,iommu-ctx-sids = <1>;
label = "ctx_1";
- };
- };
+ };
+ };
diff --git a/Documentation/devicetree/bindings/media/video/msm-cam-server.txt b/Documentation/devicetree/bindings/media/video/msm-cam-server.txt
deleted file mode 100644
index 2b6f513..0000000
--- a/Documentation/devicetree/bindings/media/video/msm-cam-server.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-* Qualcomm MSM Camera Server
-
-Required properties:
-- compatible :
- - "qcom,cam_server"
-
-Example:
-
- qcom,cam_server {
- compatible = "qcom,cam_server";
- };
diff --git a/Documentation/devicetree/bindings/media/video/msm-cam.txt b/Documentation/devicetree/bindings/media/video/msm-cam.txt
new file mode 100644
index 0000000..b5b6cf6
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/video/msm-cam.txt
@@ -0,0 +1,15 @@
+* Qualcomm MSM Camera
+
+Required properties:
+- compatible :
+ - "qcom,msm-cam"
+- reg : offset and length of msm camera device registers.
+- reg-names : should specify relevant names for each reg property defined.
+
+Example:
+
+ qcom,msm-cam@fd8c0000 {
+ compatible = "qcom,msm-cam";
+ reg = <0xfd8C0000 0x10000>;
+ reg-names = "msm-cam";
+ };
diff --git a/Documentation/devicetree/bindings/media/video/msm-cci.txt b/Documentation/devicetree/bindings/media/video/msm-cci.txt
index e256265..cf33f50 100644
--- a/Documentation/devicetree/bindings/media/video/msm-cci.txt
+++ b/Documentation/devicetree/bindings/media/video/msm-cci.txt
@@ -11,6 +11,25 @@
- interrupts : should contain the cci interrupt.
- interrupt-names : should specify relevant names to each interrupts
property defined.
+- gpios : should contain phandle to gpio controller node and array of
+ #gpio-cells specifying specific gpio (controller specific)
+- qcom,gpio-req-tbl-num : should contain index to gpios specific to this sensor
+- qcom,gpio-req-tbl-flags : should contain direction of gpios present in
+ qcom,gpio-req-tbl-num property (in the same order)
+- qcom,gpio-req-tbl-label : should contain name of gpios present in
+ qcom,gpio-req-tbl-num property (in the same order)
+- qcom,hw-thigh : should contain high period of the SCL clock in terms of CCI
+ clock cycle
+- qcom,hw-tlow : should contain high period of the SCL clock in terms of CCI
+ clock cycle
+- qcom,hw-tsu-sto : should contain setup time for STOP condition
+- qcom,hw-tsu-sta : should contain setup time for Repeated START condition
+- qcom,hw-thd-dat : should contain hold time for the data
+- qcom,hw-thd-sta : should contain hold time for START condition
+- qcom,hw-tbuf : should contain free time between a STOP and a START condition
+- qcom,hw-scl-stretch-en : should contain enable or disable clock stretching
+- qcom,hw-trdhld : should contain internal hold time for SDA
+- qcom,hw-tsp : should contain filtering of glitches
[Second level nodes]
* Qualcomm MSM Sensor
@@ -22,16 +41,14 @@
- "qcom,s5k3l1yx"
- reg : should contain i2c slave address of the camera sensor and
length of data field which is 0x0
-- qcom,csi-if : should contain number of csid cores required at the receiver
- side
- - 1 for 2D sensor
- - 2 for 3D sensor
-- qcom,csid-core : should contain csid core instance that will used to receive
- sensor data
+- qcom,slave-id : should contain i2c slave address, device id address
+ and expected id read value
+- qcom,csiphy-sd-index : should contain csiphy instance that will used to
+ receive sensor data
+ - 0, 1, 2
+- qcom,csid-sd-index : should contain csid core instance that will used to
+ receive sensor data
- 0, 1, 2, 3
-- qcom,is-vpe : should be enabled if VPE module is required for post processing
- of this sensor
- - 1 if required, 0 otherwise
- qcom,sensor-name : should contain unique sensor name to differentiate from
other sensor
- "s5k3l1yx"
@@ -52,7 +69,7 @@
regulators mentioned in qcom,cam-vreg-name property (in the same order)
- qcom,cam-vreg-op-mode : should contain optimum voltage level for regulators
mentioned in qcom,cam-vreg-name property (in the same order)
-- qcom,camera-type : should contain sensor type
+- qcom,sensor-mode : should contain sensor mode supported
- 0 -> back camera 2D
- 1 -> front camera 2D
- 2 -> back camera 3D
@@ -62,7 +79,10 @@
- 1 -> yuv format
Optional properties:
-- qcom,flash-src-index : should contain phandle to flash source node if flash
+- qcom,is-vpe : should be enabled if VPE module is required for post processing
+ of this sensor
+ - 1 if required, 0 otherwise
+- qcom,led-flash-sd-index : should contain phandle to flash source node if flash
is supported for this sensor
- led_flash0, led_flash1
- qcom,mount-angle : should contain the physical mount angle of the sensor on
@@ -74,12 +94,7 @@
- cam_vaf-supply : should contain regulator from which AF voltage is supplied
- gpios : should contain phandle to gpio controller node and array of
#gpio-cells specifying specific gpio (controller specific)
-- qcom,gpio-common-tbl-num : should contain index to gpios shared between
- different sensors
-- qcom,gpio-common-tbl-flags : should contain direction of gpios present in
- qcom,gpio-common-tbl-num property (in the same order)
-- qcom,gpio-common-tbl-label : should contain name of gpios present in
- qcom,gpio-common-tbl-num property (in the same order)
+- qcom,gpio-reset : should contain index to gpio used by sensors reset_n
- qcom,gpio-req-tbl-num : should contain index to gpios specific to this sensor
- qcom,gpio-req-tbl-flags : should contain direction of gpios present in
qcom,gpio-req-tbl-num property (in the same order)
@@ -107,7 +122,9 @@
for actuator
- qcom,actuator-vcm-enable : should contain value to be set for actuator vcm
gpio
-
+- qcom,sensor-position : should contain the mount angle of the camera sensor
+ - 0 -> back camera
+ - 1 -> front camera
Example:
qcom,cci@0xfda0c000 {
@@ -117,13 +134,33 @@
reg-names = "cci";
interrupts = <0 50 0>;
interrupt-names = "cci";
+ gpios = <&msmgpio 19 0>,
+ <&msmgpio 20 0>,
+ <&msmgpio 21 0>,
+ <&msmgpio 22 0>;
+ qcom,gpio-tbl-num = <0 1 2 3>;
+ qcom,gpio-tbl-flags = <1 1 1 1>;
+ qcom,gpio-tbl-label = "CCI_I2C_DATA0",
+ "CCI_I2C_CLK0",
+ "CCI_I2C_DATA1",
+ "CCI_I2C_CLK1";
+ qcom,hw-thigh = <78>;
+ qcom,hw-tlow = <114>;
+ qcom,hw-tsu-sto = <28>;
+ qcom,hw-tsu-sta = <28>;
+ qcom,hw-thd-dat = <10>;
+ qcom,hw-thd-sta = <77>;
+ qcom,hw-tbuf = <118>;
+ qcom,hw-scl-stretch-en = <0>;
+ qcom,hw-trdhld = <6>;
+ qcom,hw-tsp = <1>;
qcom,s5k3l1yx@6e {
compatible = "qcom,s5k3l1yx";
reg = <0x6e 0x0>;
- qcom,csi-if = <1>;
- qcom,csid-core = <0>;
- qcom,is-vpe = <1>;
- qcom,flash-type = <0>;
+ qcom,slave-id = <0x6e 0x0 0x3121>;
+ qcom,led-flash-sd-index = <0>;
+ qcom,csiphy-sd-index = <2>;
+ qcom,csid-sd-index = <0>;
qcom,mount-angle = <90>;
qcom,sensor-name = "s5k3l1yx";
cam_vdig-supply = <&pm8941_l3>;
@@ -137,23 +174,18 @@
qcom,cam-vreg-op-mode = <105000 80000 0 100000>;
qcom,gpio-no-mux = <0>;
gpios = <&msmgpio 15 0>,
- <&msmgpio 19 0>,
- <&msmgpio 20 0>,
- <&msmgpio 90 0>;
- qcom,gpio-common-tbl-num = <0 1 2>;
- qcom,gpio-common-tbl-flags = <1 1 1>;
- qcom,gpio-common-tbl-label = "CAMIF_MCLK", "CAMIF_I2C_DATA",
- "CAMIF_I2C_CLK";
- qcom,gpio-req-tbl-num = <3>;
- qcom,gpio-req-tbl-flags = <0>;
- qcom,gpio-req-tbl-label = "CAM_RESET1";
- qcom,gpio-set-tbl-num = <3 3>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-req-tbl-num = <0 1>;
+ qcom,gpio-req-tbl-flags = <1 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK",
+ "CAM_RESET1";
+ qcom,gpio-set-tbl-num = <1 1>;
qcom,gpio-set-tbl-flags = <0 2>;
qcom,gpio-set-tbl-delay = <1000 4000>;
qcom,csi-lane-assign = <0x4320>;
qcom,csi-lane-mask = <0x1F>;
qcom,csi-phy-sel = <0>;
- qcom,camera-type = <0>;
- qcom,sensor-type = <0>;
+ qcom,sensor-position = <0>;
+ qcom,sensor-mode = <1>;
};
};
diff --git a/Documentation/devicetree/bindings/media/video/msm-vfe.txt b/Documentation/devicetree/bindings/media/video/msm-vfe.txt
index 7a70cac..f02f35e 100644
--- a/Documentation/devicetree/bindings/media/video/msm-vfe.txt
+++ b/Documentation/devicetree/bindings/media/video/msm-vfe.txt
@@ -4,6 +4,7 @@
- cell-index: vfe hardware core index
- compatible :
- "qcom,vfe"
+ - "qcom,vfe40"
- reg : offset and length of the register set for the device
for the vfe operating in compatible mode.
- reg-names : should specify relevant names to each reg property defined.
diff --git a/Documentation/devicetree/bindings/media/video/msm-vidc.txt b/Documentation/devicetree/bindings/media/video/msm-vidc.txt
index 79a3ab5..6af445b 100644
--- a/Documentation/devicetree/bindings/media/video/msm-vidc.txt
+++ b/Documentation/devicetree/bindings/media/video/msm-vidc.txt
@@ -3,6 +3,11 @@
Required properties:
- compatible : one of:
- "qcom,msm-vidc"
+- hfi : supported Host-Firmware Interface, one of:
+ - "venus"
+ - "q6"
+
+Optional properties:
- reg : offset and length of the register set for the device.
- interrupts : should contain the vidc interrupt.
- vidc-cp-map : start and size of device virtual address range for secure buffers.
@@ -29,4 +34,5 @@
<243000 133330000>,
<108000 100000000>,
<36000 50000000>;
+ hfi = "venus";
};
diff --git a/Documentation/devicetree/bindings/pil/pil-q6v5-mss.txt b/Documentation/devicetree/bindings/pil/pil-q6v5-mss.txt
index 5bef9b8..22e43ab 100644
--- a/Documentation/devicetree/bindings/pil/pil-q6v5-mss.txt
+++ b/Documentation/devicetree/bindings/pil/pil-q6v5-mss.txt
@@ -20,6 +20,8 @@
images and self-authentication is not desired;
<1> if the hardware requires self-authenticating images.
- qcom,is-loadable: if PIL is required to load the modem image
+- qcom,gpio-err-fatal: GPIO used by the modem to indicate error fatal to the apps.
+- qcom,gpio-force-stop: GPIO used by the apps to force the modem to shutdown.
Example:
qcom,mss@fc880000 {
@@ -38,4 +40,10 @@
qcom,is-loadable;
qcom,firmware-name = "mba";
qcom,pil-self-auth = <1>;
+
+ /* GPIO inputs from mss */
+ gpio_err_fatal = <&smp2pgpio_ssr_smp2p_1_in 0 0>;
+
+ /* GPIO output to mss */
+ gpio_force_stop = <&smp2pgpio_ssr_smp2p_1_out 0 0>;
};
diff --git a/Documentation/devicetree/bindings/power/bq28400-battery.txt b/Documentation/devicetree/bindings/power/bq28400-battery.txt
index 1460d70..a95e61f 100644
--- a/Documentation/devicetree/bindings/power/bq28400-battery.txt
+++ b/Documentation/devicetree/bindings/power/bq28400-battery.txt
@@ -4,15 +4,20 @@
The device interface is I2C, its I2C slave 7-bit address is 0xb.
The device is usually embedded inside the "smart battery" pack.
-node required properties:
-- compatible: Must be "ti,bq28400-battery" or "ti,bq30z55-battery"
-- reg: I2C Address must be 0xb.
+Required properties:
+
+ - compatible : Must be "ti,bq28400-battery" or "ti,bq30z55-battery"
+ - reg : I2C Address must be 0xb.
+ - ti,temp-cold : Cold temperature limit in celsius degree
+ - ti,temp-hot : Hot temperature limit in celsius degree
Example:
i2c@f9967000 {
battery@b {
compatible = "ti,bq28400-battery", "ti,bq30z55-battery";
reg = <0xb>;
+ ti,temp-cold = <2>;
+ ti,temp-hot = <43>;
};
};
diff --git a/Documentation/devicetree/bindings/regulator/fixed-regulator.txt b/Documentation/devicetree/bindings/regulator/fixed-regulator.txt
index 9cf57fd..7637adc 100644
--- a/Documentation/devicetree/bindings/regulator/fixed-regulator.txt
+++ b/Documentation/devicetree/bindings/regulator/fixed-regulator.txt
@@ -8,6 +8,7 @@
- startup-delay-us: startup time in microseconds
- enable-active-high: Polarity of GPIO is Active high
If this property is missing, the default assumed is Active low.
+- parent-supply: phandle to the parent supply/regulator node if one exists.
Any property defined as part of the core regulator
binding, defined in regulator.txt, can also be used.
diff --git a/Documentation/devicetree/bindings/regulator/krait-regulator.txt b/Documentation/devicetree/bindings/regulator/krait-regulator.txt
index fddae80..149445d 100644
--- a/Documentation/devicetree/bindings/regulator/krait-regulator.txt
+++ b/Documentation/devicetree/bindings/regulator/krait-regulator.txt
@@ -2,13 +2,25 @@
Required properties:
- compatible: Must be "qcom,krait-regulator"
-- reg: Specifies the address and size for this regulator device
+- reg: Specifies the address and size for this regulator device,
+ also specifies the address and the size for the MDD area
+ to be used along with the regulator
+- reg-names: "acs" -string to identify the area where main power control
+ registers reside.
+ "mdd" - string to identify the area where mdd registers reside.
- qcom,headroom-voltage: The minimum required voltage drop between the input
voltage and the output voltage for the LDO to be
- operational, in microvolts
-- qcom,retention-voltage: The value for retention voltage in microvolts
-- qcom,ldo-default-voltage: The default value for LDO voltage in microvolts
-- qcom,ldo-threshold-voltage: The voltage value above which LDO is nonfunctional
+ operational, in microvolts. Acceptable values are from
+ 50000uV to 250000uV
+- qcom,retention-voltage: The value for retention voltage in microvolts. Acceptable
+ values are from 465000uV to 750000uV
+- qcom,ldo-default-voltage: The default value for LDO voltage in microvolts. Acceptable
+ values are from 465000uV to 750000uV
+- qcom,ldo-threshold-voltage: The voltage value above which LDO is nonfunctional.
+ Acceptable values are from 600000uV to 900000uV
+- qcom,ldo-delta-voltage: The delta used to reduce the requested voltage in order
+ to derive the LDO output voltage while switching
+ to LDO mode. Acceptable values are from 1000uV to 100000uV
Any property defined as part of the core regulator
binding, defined in regulator.txt, can also be used.
@@ -17,11 +29,15 @@
krait0_vreg: regulator@f9088000 {
compatible = "qcom,krait-regulator";
regulator-name = "krait0";
- reg = <0xf9088000 0x1000>;
+ reg = <0xf9088000 0x1000>, /* APCS_ALIAS0_KPSS_ACS */
+ <0xf908a800 0x1000>; /* APCS_ALIAS0_KPSS_MDD */
+ reg-names = "acs", "mdd";
regulator-min-microvolt = <500000>;
regulator-max-microvolt = <1100000>;
qcom,headroom-voltage = <150000>;
qcom,retention-voltage = <745000>;
qcom,ldo-default-voltage = <745000>;
qcom,ldo-threshold-voltage = <750000>;
+ qcom,ldo-delta-voltage = <50000>;
};
+
diff --git a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
index 032c814..925c8eb 100644
--- a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
+++ b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
@@ -85,9 +85,11 @@
- qcom,msm-dai-q6-dev-id : The slimbus multi channel port ID
Value is from 16384 to 16393
BT SCO port ID value from 12288 to 12289
- RT Proxy port ID values from 224 to 225 and 240 to 241
+ RT Proxy port ID values from 224 to 225 and 240 to
+ 241
FM Rx and TX port ID values from 12292 to 12293
- incall record Rx and TX port ID values from 32771 to 32772
+ incall record Rx and TX port ID values from 32771 to
+ 32772
* msm-auxpcm
@@ -97,25 +99,42 @@
- compatible : "qcom,msm-auxpcm-resource"
- - qcom,msm-cpudai-auxpcm-clk: clock for auxpcm
+ - qcom,msm-cpudai-auxpcm-clk: clock for auxpcm. The first value is
+ for 8khz mode, the second is for
+ 16khz
- - qcom,msm-cpudai-auxpcm-mode: mode information
+ - qcom,msm-cpudai-auxpcm-mode: mode information. The first value is
+ for 8khz mode, the second is for
+ 16khz
0 - for PCM
- - qcom,msm-cpudai-auxpcm-sync: sync information
+ - qcom,msm-cpudai-auxpcm-sync: sync information. The first value is
+ for 8khz mode, the second is for
+ 16khz
- - qcom,msm-cpudai-auxpcm-frame: No.of bytes per frame
+ - qcom,msm-cpudai-auxpcm-frame: No.of bytes per frame. The first
+ value is for 8khz mode, the second
+ is for 16khz
5 - 256BPF
+ 4 - 128BPF
- - qcom,msm-cpudai-auxpcm-quant: Type of quantization
+ - qcom,msm-cpudai-auxpcm-quant: Type of quantization. The first
+ value is for 8khz mode, the second
+ is for 16khz
2 - Linear quantization
- qcom,msm-cpudai-auxpcm-slot: Slot number for multichannel scenario
+ The first value is for 8khz mode the
+ second is for 16khz
Value is 1
- - qcom,msm-cpudai-auxpcm-data: Data field - 0
+ - qcom,msm-cpudai-auxpcm-data: Data field - 0. The first value is
+ for 8khz mode, the second is for
+ 16khz
- - qcom,msm-cpudai-auxpcm-pcm-clk-rate: Clock rate for pcm - 2048000
+ - qcom,msm-cpudai-auxpcm-pcm-clk-rate: Clock rate for pcm - 2048000. The
+ first value is for 8khz mode, the
+ second is for auxpcm
[Second Level Nodes]
@@ -144,22 +163,24 @@
- qcom,msm_bus,num_cases: Total number of use cases
- - qcom,msm_bus,active_only: Context flag for requests in active or
- dual (active & sleep) contex
+ - qcom,msm_bus,active_only: Context flag for requests in active
+ or dual (active & sleep) contex
- qcom,msm_bus,num_paths: Total number of master-slave pairs
- - qcom,msm_bus,vectors: Arrays of unsigned integers representing:
- master-id, slave-id, arbitrated bandwidth,
- instantaneous bandwidth
+ - qcom,msm_bus,vectors: Arrays of unsigned integers
+ representing:
+ master-id, slave-id, arbitrated
+ bandwidth,
+ instantaneous bandwidth
* wcd9xxx_intc
Required properties:
- compatible : "qcom,wcd9xxx-irq"
- - interrupt-controller : Mark this device node as an interrupt
- controller
+ - interrupt-controller : Mark this device node as an
+ interrupt controller
- #interrupt-cells : Should be 1
@@ -302,13 +323,13 @@
qcom,msm-auxpcm {
compatible = "qcom,msm-auxpcm-resource";
qcom,msm-cpudai-auxpcm-clk = "pcm_clk";
- qcom,msm-cpudai-auxpcm-mode = <0>;
- qcom,msm-cpudai-auxpcm-sync = <1>;
- qcom,msm-cpudai-auxpcm-frame = <5>;
- qcom,msm-cpudai-auxpcm-quant = <2>;
- qcom,msm-cpudai-auxpcm-slot = <1>;
- qcom,msm-cpudai-auxpcm-data = <0>;
- qcom,msm-cpudai-auxpcm-pcm-clk-rate = <2048000>;
+ qcom,msm-cpudai-auxpcm-mode = <0>, <0>;
+ qcom,msm-cpudai-auxpcm-sync = <1>, <1>;
+ qcom,msm-cpudai-auxpcm-frame = <5>, <4>;
+ qcom,msm-cpudai-auxpcm-quant = <2>, <2>;
+ qcom,msm-cpudai-auxpcm-slot = <1>, <1>;
+ qcom,msm-cpudai-auxpcm-data = <0>, <0>;
+ qcom,msm-cpudai-auxpcm-pcm-clk-rate = <2048000>, <2048000>;
qcom,msm-auxpcm-rx {
qcom,msm-auxpcm-dev-id = <4106>;
@@ -357,6 +378,10 @@
- taiko-mclk-clk : phandle to PMIC8941 clkdiv1 node.
- qcom,taiko-mclk-clk-freq : Taiko mclk Freq in Hz. currently only 9600000Hz
is supported.
+- prim-auxpcm-gpio-clk : GPIO on which AUXPCM clk signal is coming.
+- prim-auxpcm-gpio-sync : GPIO on which AUXPCM SYNC signal is coming.
+- prim-auxpcm-gpio-din : GPIO on which AUXPCM DIN signal is coming.
+- prim-auxpcm-gpio-dout : GPIO on which AUXPCM DOUT signal is coming.
Optional properties:
- qcom,hdmi-audio-rx: specifies if HDMI audio support is enabled or not.
@@ -401,6 +426,11 @@
qcom,taiko-mclk-clk-freq = <9600000>;
qcom,hdmi-audio-rx;
+
+ prim-auxpcm-gpio-clk = <&msmgpio 65 0>;
+ prim-auxpcm-gpio-sync = <&msmgpio 66 0>;
+ prim-auxpcm-gpio-din = <&msmgpio 67 0>;
+ prim-auxpcm-gpio-dout = <&msmgpio 68 0>;
};
* msm-dai-mi2s
@@ -416,25 +446,34 @@
Required properties:
- compatible : "qcom,msm-dai-q6-mi2s"
- - qcom,msm-dai-q6-mi2s-dev-id: MSM or MDM can use Slimbus or I2S interface to transfer data
- to (WCD9XXX) codec. If slimbus interface is used then
- "msm-dai-q6" needs to be filled with correct data for slimbus
- interface. The sections "msm-dai-mi2s" is used by MDM or MSM
- to use I2S interface with codec. This section is used by CPU
- driver in ASOC MSM to configure MI2S interface. MSM internally
- has multiple MI2S namely Primary, Secondary, Tertiary and
- Quaternary MI2S. They are represented with id 0, 1, 2, 3
- respectively. The field "qcom,msm-dai-q6-mi2s-dev-id" represents
- which of the MI2S block is used. These MI2S are connected to I2S
- interface.
+ - qcom,msm-dai-q6-mi2s-dev-id: MSM or MDM can use Slimbus or I2S interface to
+ transfer data to (WCD9XXX) codec.
+ If slimbus interface is used then "msm-dai-q6"
+ needs to be filled with correct data for
+ slimbus interface.
+ The sections "msm-dai-mi2s" is used by MDM or
+ MSM to use I2S interface with codec.
+ This section is used by CPU driver in ASOC MSM
+ to configure MI2S interface. MSM internally
+ has multiple MI2S namely Primary, Secondary,
+ Tertiary and Quaternary MI2S.
+ They are represented with id 0, 1, 2, 3
+ respectively.
+ The field "qcom,msm-dai-q6-mi2s-dev-id"
+ represents which of the MI2S block is used.
+ These MI2S are connected to I2S interface.
- - qcom,msm-mi2s-rx-lines: Each MI2S interface in MSM has one or more SD lines. These lines
- are used for data transfer between codec and MSM. This element in
- indicates which output RX lines are used in the MI2S interface.
+ - qcom,msm-mi2s-rx-lines: Each MI2S interface in MSM has one or more SD
+ lines. These lines are used for data transfer
+ between codec and MSM.
+ This element in indicates which output RX lines
+ are used in the MI2S interface.
- - qcom,msm-mi2s-tx-lines: Each MI2S interface in MSM has one or more SD lines. These lines
- are used for data transfer between codec and MSM. This element in
- indicates which input TX lines are used in the MI2S interface.
+ - qcom,msm-mi2s-tx-lines: Each MI2S interface in MSM has one or more SD
+ lines. These lines are used for data transfer
+ between codec and MSM.
+ This element in indicates which input TX lines
+ are used in the MI2S interface.
Example:
@@ -514,9 +553,9 @@
It is possible that some MSM use PIL to load the ADSP image. While
other MSM may use SBL to load the ADSP image at boot. Audio APR needs
state of ADSP to register and enable APR to be used for sending commands
- to ADSP. so adsp-state represents the state of ADSP to ADSP loader. Value
- of 0 indicates ADSP loader needs to use PIL and value of 2 means ADSP
- image is already loaded by SBL.
+ to ADSP. so adsp-state represents the state of ADSP to ADSP loader.
+ Value of 0 indicates ADSP loader needs to use PIL and value of 2 means
+ ADSP image is already loaded by SBL.
Example:
diff --git a/Documentation/devicetree/bindings/sound/taiko_codec.txt b/Documentation/devicetree/bindings/sound/taiko_codec.txt
index 74c25a0..989bea8 100644
--- a/Documentation/devicetree/bindings/sound/taiko_codec.txt
+++ b/Documentation/devicetree/bindings/sound/taiko_codec.txt
@@ -8,33 +8,39 @@
- qcom,cdc-reset-gpio: gpio used for codec SOC reset.
- <supply-name>-supply: phandle to the regulator device tree node
- - qcom,<supply-name>-voltage - specifies voltage levels for supply. Should be
- specified in pairs (min, max), units mV.
+ - qcom,<supply-name>-voltage - specifies voltage levels for supply.
+ Should be specified in pairs (min, max), units mV.
- qcom,<supply-name>-current - specifies max current in mA that can drawn
- from the <supply-name>.
+ from the <supply-name>.
- above three properties with "supply-name" set to "qcom,cdc-vdd-buck", "qcom,cdc-vdd-tx-h",
- "qcom,cdc-vdd-rx-h", "qcom,cdc-vddpx-1", "qcom,cdc-vdd-a-1p2v", "qcom,cdc-vddcx-1",
- "qcom,cdc-vddcx-2" should be present.
+ above three properties with "supply-name" set to "qcom,cdc-vdd-buck",
+ "qcom,cdc-vdd-tx-h", "qcom,cdc-vdd-rx-h", "qcom,cdc-vddpx-1",
+ "qcom,cdc-vdd-a-1p2v", "qcom,cdc-vddcx-1", "qcom,cdc-vddcx-2"
+ should be present.
- - qcom,cdc-micbias-ldoh-v - LDOH output in volts ( should be 1.95 V and 3.00 V).
+ - qcom,cdc-micbias-ldoh-v - LDOH output in volts (should be 1.95 V and 3.00 V).
- qcom,cdc-micbias-cfilt1-mv - cfilt1 output voltage in milli volts.
- qcom,cdc-micbias-cfilt2-mv - cfilt2 output voltage in milli volts.
- qcom,cdc-micbias-cfilt3-mv - cfilt3 output voltage in milli volts.
cfilt voltage can be set to max of qcom,cdc-micbias-ldoh-v - 0.15V.
- - qcom,cdc-micbias1-cfilt-sel = cfilt to use for micbias1 (should be from 1 to 3).
- - qcom,cdc-micbias2-cfilt-sel = cfilt to use for micbias2 (should be from 1 to 3).
- - qcom,cdc-micbias3-cfilt-sel = cfilt to use for micbias3 (should be from 1 to 3).
- - qcom,cdc-micbias4-cfilt-sel = cfilt to use for micbias4 (should be from 1 to 3).
+ - qcom,cdc-micbias1-cfilt-sel = cfilt to use for micbias1
+ (should be from 1 to 3).
+ - qcom,cdc-micbias2-cfilt-sel = cfilt to use for micbias2
+ (should be from 1 to 3).
+ - qcom,cdc-micbias3-cfilt-sel = cfilt to use for micbias3
+ (should be from 1 to 3).
+ - qcom,cdc-micbias4-cfilt-sel = cfilt to use for micbias4
+ (should be from 1 to 3).
This value represents the connected CFLIT to MIC Bias.
- qcom,cdc-micbias1-ext-cap: Boolean. Enable micbias 1 external capacitor mode.
- qcom,cdc-micbias2-ext-cap: Boolean. Enable micbias 2 external capacitor mode.
- qcom,cdc-micbias3-ext-cap: Boolean. Enable micbias 3 external capacitor mode.
- qcom,cdc-micbias4-ext-cap: Boolean. Enable micbias 4 external capacitor mode.
- - qcom,cdc-mclk-clk-rate - Specifies the master clock rate in Hz required for codec.
+ - qcom,cdc-mclk-clk-rate - Specifies the master clock rate in Hz required for
+ codec.
- qcom,cdc-slim-ifd-dev - namme of the codec slim interface device.
- qcom,cdc-slim-ifd-elemental-addr - codec slimbus slave interface device
enumeration address.
@@ -97,33 +103,39 @@
- reg: represents the slave address provided to the I2C driver.
- qcom,cdc-reset-gpio: gpio used for codec SOC reset.
- <supply-name>-supply: phandle to the regulator device tree node.
- - qcom,<supply-name>-voltage - specifies voltage levels for supply. Should be
- specified in pairs (min, max), units mV.
+ - qcom,<supply-name>-voltage - specifies voltage levels for supply.
+ Should be specified in pairs (min, max), units mV.
- qcom,<supply-name>-current - specifies max current in mA that can drawn
- from the <supply-name>.
+ from the <supply-name>.
- above three properties with "supply-name" set to "qcom,cdc-vdd-buck", "qcom,cdc-vdd-tx-h",
- "qcom,cdc-vdd-rx-h", "qcom,cdc-vddpx-1", "qcom,cdc-vdd-a-1p2v", "qcom,cdc-vddcx-1",
- "qcom,cdc-vddcx-2" should be present.
+ above three properties with "supply-name" set to "qcom,cdc-vdd-buck",
+ "qcom,cdc-vdd-tx-h", "qcom,cdc-vdd-rx-h", "qcom,cdc-vddpx-1",
+ "qcom,cdc-vdd-a-1p2v", "qcom,cdc-vddcx-1", "qcom,cdc-vddcx-2"
+ should be present.
- - qcom,cdc-micbias-ldoh-v - LDOH output in volts ( should be 1.95 V and 3.00 V).
+ - qcom,cdc-micbias-ldoh-v - LDOH output in volts (should be 1.95 V and 3.00 V).
- qcom,cdc-micbias-cfilt1-mv - cfilt1 output voltage in milli volts.
- qcom,cdc-micbias-cfilt2-mv - cfilt2 output voltage in milli volts.
- qcom,cdc-micbias-cfilt3-mv - cfilt3 output voltage in milli volts.
cfilt voltage can be set to max of qcom,cdc-micbias-ldoh-v - 0.15V.
- - qcom,cdc-micbias1-cfilt-sel = cfilt to use for micbias1 (should be from 1 to 3).
- - qcom,cdc-micbias2-cfilt-sel = cfilt to use for micbias2 (should be from 1 to 3).
- - qcom,cdc-micbias3-cfilt-sel = cfilt to use for micbias3 (should be from 1 to 3).
- - qcom,cdc-micbias4-cfilt-sel = cfilt to use for micbias4 (should be from 1 to 3).
+ - qcom,cdc-micbias1-cfilt-sel = cfilt to use for micbias1
+ (should be from 1 to 3).
+ - qcom,cdc-micbias2-cfilt-sel = cfilt to use for micbias2
+ (should be from 1 to 3).
+ - qcom,cdc-micbias3-cfilt-sel = cfilt to use for micbias3
+ (should be from 1 to 3).
+ - qcom,cdc-micbias4-cfilt-sel = cfilt to use for micbias4
+ (should be from 1 to 3).
This value represents the connected CFLIT to MIC Bias.
- qcom,cdc-micbias1-ext-cap: Boolean. Enable micbias 1 external capacitor mode.
- qcom,cdc-micbias2-ext-cap: Boolean. Enable micbias 2 external capacitor mode.
- qcom,cdc-micbias3-ext-cap: Boolean. Enable micbias 3 external capacitor mode.
- qcom,cdc-micbias4-ext-cap: Boolean. Enable micbias 4 external capacitor mode.
- - qcom,cdc-mclk-clk-rate - Specifies the master clock rate in Hz required for codec.
+ - qcom,cdc-mclk-clk-rate - Specifies the master clock rate in Hz required for
+ codec.
Example:
i2c@f9925000 {
@@ -143,7 +155,8 @@
reg = <0x0d>;
qcom,cdc-reset-gpio = <&msmgpio 22 0>;
interrupt-parent = <&wcd9xxx_intc>;
- interrupts = <0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28>;
+ interrupts = <0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
+ 20 21 22 23 24 25 26 27 28>;
cdc-vdd-buck-supply = <&pm8019_l11>;
qcom,cdc-vdd-buck-voltage = <1800000 1800000>;
diff --git a/Documentation/devicetree/bindings/tty/serial/msm_serial_hs.txt b/Documentation/devicetree/bindings/tty/serial/msm_serial_hs.txt
new file mode 100644
index 0000000..dc115e0
--- /dev/null
+++ b/Documentation/devicetree/bindings/tty/serial/msm_serial_hs.txt
@@ -0,0 +1,74 @@
+* Qualcomm MSM HSUART
+
+Required properties:
+- compatible :
+ - "qcom,msm-hsuart-v14" to be used for UARTDM Core v1.4
+- reg : offset and length of the register set for both the device,
+ uart core and bam core
+- reg-names :
+ - "core_mem" to be used as name of the uart core
+ - "bam_mem" to be used as name of the bam core
+- interrupts : interrupts for both the device,uart core and bam core
+- interrupt-names :
+ - "core_irq" to be used as uart irq
+ - "bam irq" to be used as bam irq
+- bam-tx-ep-pipe-index : BAM TX Endpoint Pipe Index for HSUART
+- bam-rx-ep-pipe-index : BAM RX Endpoint Pipe Index for HSUART
+
+BLSP has a static pipe allocation and assumes a pair-pipe for each uart core.
+Pipes [2*i : 2*i+1] are allocated for UART cores where i = [0 : 5].
+Hence, Minimum and Maximum permitted value of endpoint pipe index to be used
+with uart core is 0 and 11 respectively.
+
+There is one HSUART block used in MSM devices,
+"qcom,msm-hsuart-v14". The msm-serial-hs driver is
+able to handle this, and matches against the "qcom,msm-hsuart-v14"
+as the compatibility.
+
+The registers for the "qcom,msm-hsuart-v14" device need to specify both
+register blocks - uart core and bam core.
+
+Example:
+
+ uart7@f995d000 {
+ compatible = "qcom,msm-hsuart-v14";
+ reg = <0xf995d000 0x1000>,
+ <0xf9944000 0x5000>;
+ reg-names = "core_mem", "bam_mem";
+ interrupts = <0 113 0>, <0 239 0>;
+ interrupt-names = "core_irq", "bam_irq";
+ };
+
+Optional properties:
+- qcom,<gpio-name>-gpio : handle to the GPIO node, see "gpios property" in
+Documentation/devicetree/bindings/gpio/gpio.txt.
+"gpio-name" can be "tx", "rx", "cts" and "rfr" based on number of UART GPIOs
+need to configured.
+Gpio's are optional if it is required to be not configured by UART driver or
+case where there is nothing connected and we want to use internal loopback mode
+for uart.
+- qcom, wakeup_irq : UART RX GPIO IRQ line to be configured as wakeup source.
+- qcom,inject_rx_on_wakeup : inject_rx_on_wakeup enables feature where on
+receiving interrupt with UART RX GPIO IRQ line (i.e. above wakeup_irq property),
+HSUART driver injects provided character with property rx_to_inject.
+- qcom, rx_to_inject : The character to be inserted on wakeup.
+
+
+Example:
+
+ uart7: uart@f995d000 {
+ compatible = "qcom,msm-hsuart-v14"
+ reg = <0x19c40000 0x1000">,
+ <0xf9944000 0x5000>;
+ reg-names = "core_mem", "bam_mem";
+ interrupts = <0 113 0>, <0 239 0>;
+ interrupt-names = "core_irq", "bam_irq";
+
+ qcom,tx-gpio = <&msmgpio 41 0x00>;
+ qcom,rx-gpio = <&msmgpio 42 0x00>;
+ qcom,cts-gpio = <&msmgpio 43 0x00>;
+ qcom,rfr-gpio = <&msmgpio 44 0x00>;
+
+ qcom,bam-tx-ep-pipe-index = <0>;
+ qcom,bam-rx-ep-pipe-index = <1>;
+ };
diff --git a/Documentation/devicetree/bindings/usb/msm-hsusb.txt b/Documentation/devicetree/bindings/usb/msm-hsusb.txt
index 8b7b31b..2cb2182 100644
--- a/Documentation/devicetree/bindings/usb/msm-hsusb.txt
+++ b/Documentation/devicetree/bindings/usb/msm-hsusb.txt
@@ -75,6 +75,34 @@
<87 512 60000000 960000000>;
};
+MSM HSUSB EHCI controller
+
+Required properties :
+- compatible : should be "qcom,ehci-host"
+- reg : offset and length of the register set in the memory map
+- interrupts: IRQ lines used by this controller
+- interrupt-names : Required interrupt resource entries are:
+ HSUSB EHCI expects "core_irq" and optionally "async_irq".
+- <supply-name>-supply: handle to the regulator device tree node
+ Required "supply-name" is "HSUSB_VDDCX" "HSUSB_1p8-supply" "HSUSB_3p3-supply".
+- qcom,usb2-power-budget: maximum vbus power (in mA) that can be provided.
+
+Optional properties :
+- qcom,usb2-enable-hsphy2: If present, select second PHY for USB operation.
+
+Example MSM HSUSB EHCI controller device node :
+ ehci: qcom,ehci-host@f9a55000 {
+ compatible = "qcom,ehci-host";
+ reg = <0xf9a55000 0x400>;
+ interrupts = <0 134 0>, <0 140 0>;
+ interrupt-names = "core_irq", "async_irq";
+ HSUSB_VDDCX-supply = <&pm8841_s2>;
+ HSUSB_1p8-supply = <&pm8941_l6>;
+ HSUSB_3p3-supply = <&pm8941_l24>;
+ qcom,usb2-enable-hsphy2;
+ qcom,usb2-power-budget = <500>;
+ };
+
ANDROID USB:
Required properties:
@@ -154,6 +182,8 @@
Optional properties :
- qcom,ignore-core-reset-ack: If present then BAM ignores ACK from USB core
while performing PIPE RESET
+- qcom,disable-clk-gating: If present then disable BAM clock gating.
+
Example USB BAM controller device node:
diff --git a/Documentation/devicetree/bindings/usb/msm-ssusb.txt b/Documentation/devicetree/bindings/usb/msm-ssusb.txt
index 5d10940..7add91f 100644
--- a/Documentation/devicetree/bindings/usb/msm-ssusb.txt
+++ b/Documentation/devicetree/bindings/usb/msm-ssusb.txt
@@ -41,7 +41,12 @@
- qcom,charging-disabled: If present then battery charging using USB
is disabled.
- qcom,dwc-hsphy-init: This property if present represents phy init
- value to be used for overriding HSPHY parameters.
+ value to be used for overriding HSPHY parameters into QSCRATCH register.
+ This 32 bit value represents parameters as follows:
+ bits 0-5 PARAMETER_OVERRIDE_A
+ bits 6-12 PARAMETER_OVERRIDE_B
+ bits 13-19 PARAMETER_OVERRIDE_C
+ bits 20-25 PARAMETER_OVERRIDE_D
Example MSM USB3.0 controller device node :
usb@f9200000 {
diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt
index 5aecb48..2d4a6db 100644
--- a/Documentation/networking/ip-sysctl.txt
+++ b/Documentation/networking/ip-sysctl.txt
@@ -1098,6 +1098,11 @@
Functional default: enabled if accept_ra is enabled.
disabled if accept_ra is disabled.
+accept_ra_prefix_route - BOOLEAN
+ Set the prefix route for the autoconfigured interface address
+
+ Functional default: enabled
+
accept_redirects - BOOLEAN
Accept Redirects.
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 23181d9..5a9cecf 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -782,6 +782,7 @@
select HAVE_CLK_PREPARE
select NEED_MACH_MEMORY_H
select NEED_MACH_IO_H
+ select SOC_BUS
help
Support for Qualcomm MSM/QSD based systems. This runs on the
apps processor of the MSM/QSD and depends on a shared memory
diff --git a/arch/arm/boot/dts/msm-iommu.dtsi b/arch/arm/boot/dts/msm-iommu.dtsi
index 8343c7a..9bb642d 100755
--- a/arch/arm/boot/dts/msm-iommu.dtsi
+++ b/arch/arm/boot/dts/msm-iommu.dtsi
@@ -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
@@ -17,7 +17,11 @@
#size-cells = <1>;
ranges;
reg = <0xfda64000 0x10000>;
+ reg-names = "iommu_base";
+ interrupts = <0 67 0>;
vdd-supply = <&gdsc_jpeg>;
+ qcom,needs-alt-core-clk;
+ label = "jpeg_iommu";
status = "disabled";
qcom,iommu-bfb-regs = <0x204c
@@ -36,8 +40,8 @@
0x2010
0x2014>;
- qcom,iommu-bfb-data = <0xffffffff
- 0xffffffff
+ qcom,iommu-bfb-data = <0x0000ffff
+ 0x0
0x4
0x4
0x0
@@ -80,8 +84,11 @@
#size-cells = <1>;
ranges;
reg = <0xfd928000 0x10000>;
+ reg-names = "iommu_base";
+ interrupts = <0 73 0>;
vdd-supply = <&gdsc_mdss>;
qcom,iommu-secure-id = <1>;
+ label = "mdp_iommu";
status = "disabled";
qcom,iommu-bfb-regs = <0x204c
@@ -104,7 +111,7 @@
0x2020>;
qcom,iommu-bfb-data = <0xffffffff
- 0xffffffff
+ 0x0
0x00000004
0x00000010
0x00000000
@@ -143,10 +150,14 @@
#address-cells = <1>;
#size-cells = <1>;
ranges;
- reg = <0xfdc84000 0x10000>;
+ reg = <0xfdc84000 0x10000
+ 0xfdce0004 0x4>;
+ reg-names = "iommu_base", "clk_base";
+ interrupts = <0 45 0>;
vdd-supply = <&gdsc_venus>;
qcom,iommu-secure-id = <0>;
qcom,needs-alt-core-clk;
+ label = "venus_iommu";
status = "disabled";
qcom,iommu-bfb-regs = <0x204c
@@ -229,8 +240,12 @@
#size-cells = <1>;
ranges;
reg = <0xfdb10000 0x10000>;
+ reg-names = "iommu_base";
+ interrupts = <0 38 0>;
vdd-supply = <&gdsc_oxili_cx>;
+ qcom,alt-vdd-supply = <&gdsc_oxili_gx>;
qcom,needs-alt-core-clk;
+ label = "kgsl_iommu";
status = "disabled";
qcom,iommu-bfb-regs = <0x204c
@@ -246,8 +261,8 @@
0x2414
0x2008>;
- qcom,iommu-bfb-data = <0xffffffff
- 0xffffffff
+ qcom,iommu-bfb-data = <0x00000003
+ 0x0
0x00000004
0x00000010
0x00000000
@@ -280,7 +295,11 @@
#size-cells = <1>;
ranges;
reg = <0xfda44000 0x10000>;
+ reg-names = "iommu_base";
+ interrupts = <0 62 0>;
vdd-supply = <&gdsc_vfe>;
+ qcom,needs-alt-core-clk;
+ label = "vfe_iommu";
status = "disabled";
qcom,iommu-bfb-regs = <0x204c
@@ -303,7 +322,7 @@
0x2020>;
qcom,iommu-bfb-data = <0xffffffff
- 0xffffffff
+ 0x00000000
0x4
0x8
0x0
diff --git a/arch/arm/boot/dts/msm-pm8941.dtsi b/arch/arm/boot/dts/msm-pm8941.dtsi
index 098f543..74a95e6 100644
--- a/arch/arm/boot/dts/msm-pm8941.dtsi
+++ b/arch/arm/boot/dts/msm-pm8941.dtsi
@@ -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
@@ -77,7 +77,6 @@
qcom,bms-calculate-soc-ms = <20000>;
qcom,bms-chg-term-ua = <100000>;
qcom,bms-batt-type = <0>;
- qcom,bms-use-external-rsense;
qcom,bms-iadc@3800 {
reg = <0x3800 0x100>;
diff --git a/arch/arm/boot/dts/msm8226-iommu.dtsi b/arch/arm/boot/dts/msm8226-iommu.dtsi
new file mode 100644
index 0000000..66a7848
--- /dev/null
+++ b/arch/arm/boot/dts/msm8226-iommu.dtsi
@@ -0,0 +1,37 @@
+/* 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/ "msm-iommu.dtsi"
+
+&jpeg_iommu {
+ status = "ok";
+};
+
+&mdp_iommu {
+ status = "ok";
+ /* HACK: set to -1 during pre-si due to lack of TZ */
+ qcom,iommu-secure-id = <0xFFFFFFFF>;
+};
+
+&venus_iommu {
+ status = "ok";
+ /* HACK: set to -1 during pre-si due to lack of TZ */
+ qcom,iommu-secure-id = <0xFFFFFFFF>;
+};
+
+&kgsl_iommu {
+ status = "ok";
+};
+
+&vfe_iommu {
+ status = "ok";
+};
diff --git a/arch/arm/boot/dts/msm8226-pm.dtsi b/arch/arm/boot/dts/msm8226-pm.dtsi
new file mode 100644
index 0000000..e107b36
--- /dev/null
+++ b/arch/arm/boot/dts/msm8226-pm.dtsi
@@ -0,0 +1,402 @@
+/* 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/ "skeleton.dtsi"
+
+/ {
+ qcom,spm@f9089000 {
+ compatible = "qcom,spm-v2";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ reg = <0xf9089000 0x1000>;
+ qcom,core-id = <0>;
+ qcom,saw2-ver-reg = <0xfd0>;
+ qcom,saw2-cfg = <0x01>;
+ qcom,saw2-spm-dly= <0x20000400>;
+ qcom,saw2-spm-ctl = <0x1>;
+ qcom,saw2-spm-cmd-wfi = [60 03 60 76 76 0b 0f];
+ qcom,saw2-spm-cmd-spc = [00 20 10 80 90 5b 60 03 60 3b 76 76 94
+ 5b 80 10 2b 30 06 26 30 0f];
+ qcom,saw2-spm-cmd-pc = [00 20 10 80 90 5b 60 07 3b 76 76 0b 94
+ 5b 80 10 2b 30 06 26 30 0f];
+ };
+
+ qcom,spm@f9099000 {
+ compatible = "qcom,spm-v2";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ reg = <0xf9099000 0x1000>;
+ qcom,core-id = <1>;
+ qcom,saw2-ver-reg = <0xfd0>;
+ qcom,saw2-cfg = <0x01>;
+ qcom,saw2-spm-dly= <0x20000400>;
+ qcom,saw2-spm-ctl = <0x1>;
+ qcom,saw2-spm-cmd-wfi = [60 03 60 76 76 0b 0f];
+ qcom,saw2-spm-cmd-spc = [00 20 10 80 90 5b 60 03 60 3b 76 76 94
+ 5b 80 10 2b 30 06 26 30 0f];
+ qcom,saw2-spm-cmd-pc = [00 20 10 80 90 5b 60 07 3b 76 76 0b 94
+ 5b 80 10 2b 30 06 26 30 0f];
+ };
+
+ qcom,spm@f90a9000 {
+ compatible = "qcom,spm-v2";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ reg = <0xf90a9000 0x1000>;
+ qcom,core-id = <2>;
+ qcom,saw2-ver-reg = <0xfd0>;
+ qcom,saw2-cfg = <0x01>;
+ qcom,saw2-spm-dly= <0x20000400>;
+ qcom,saw2-spm-ctl = <0x1>;
+ qcom,saw2-spm-cmd-wfi = [60 03 60 76 76 0b 0f];
+ qcom,saw2-spm-cmd-spc = [00 20 10 80 90 5b 60 03 60 3b 76 76 94
+ 5b 80 10 2b 30 06 26 30 0f];
+ qcom,saw2-spm-cmd-pc = [00 20 10 80 90 5b 60 07 3b 76 76 0b 94
+ 5b 80 10 2b 30 06 26 30 0f];
+ };
+
+ qcom,spm@f90b9000 {
+ compatible = "qcom,spm-v2";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ reg = <0xf90b9000 0x1000>;
+ qcom,core-id = <3>;
+ qcom,saw2-ver-reg = <0xfd0>;
+ qcom,saw2-cfg = <0x01>;
+ qcom,saw2-spm-dly= <0x20000400>;
+ qcom,saw2-spm-ctl = <0x1>;
+ qcom,saw2-spm-cmd-wfi = [60 03 60 76 76 0b 0f];
+ qcom,saw2-spm-cmd-spc = [00 20 10 80 90 5b 60 03 60 3b 76 76 94
+ 5b 80 10 2b 30 06 26 30 0f];
+ qcom,saw2-spm-cmd-pc = [00 20 10 80 90 5b 60 07 3b 76 76 0b 94
+ 5b 80 10 2b 30 06 26 30 0f];
+ };
+
+ qcom,spm@f9012000 {
+ compatible = "qcom,spm-v2";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ reg = <0xf9012000 0x1000>;
+ qcom,core-id = <0xffff>; /* L2/APCS SAW */
+ qcom,saw2-ver-reg = <0xfd0>;
+ qcom,saw2-cfg = <0x14>;
+ qcom,saw2-spm-dly= <0x20000400>;
+ qcom,saw2-spm-ctl = <0x1>;
+ qcom,saw2-pmic-data0 = <0x02030080>;
+ qcom,saw2-pmic-data1 = <0x00030000>;
+ qcom,vctl-timeout-us = <50>;
+ qcom,vctl-port = <0x0>;
+ qcom,phase-port = <0x1>;
+ qcom,pfm-port = <0x2>;
+ qcom,saw2-spm-cmd-ret = [0b 00 03 00 7b 0f];
+ qcom,saw2-spm-cmd-gdhs = [00 20 32 60 70 80 0b 6b c0 e0 d0 42 07
+ 78 1f 80 4e d0 e0 c0 22 6b 50 4b 60 02 32 50 7b
+ 0f];
+ qcom,saw2-spm-cmd-pc = [00 32 60 70 80 b0 0b 10 e0 d0 6b c0
+ 42 f0 11 07 01 b0 78 1f 80 4e c0 d0 12 e0 6b 50 4b
+ 60 02 32 50 f0 7b 0f]; /*APCS_PMIC_OFF_L2RAM_OFF*/
+ };
+
+ qcom,lpm-resources {
+ compatible = "qcom,lpm-resources";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ qcom,lpm-resources@0 {
+ reg = <0x0>;
+ qcom,name = "vdd-dig";
+ qcom,resource-type = <0>;
+ qcom,type = <0x62706d73>; /* "smpb" */
+ qcom,id = <0x02>;
+ qcom,key = <0x6e726f63>; /* "corn" */
+ qcom,init-value = <5>; /* Super Turbo */
+ };
+
+ qcom,lpm-resources@1 {
+ reg = <0x1>;
+ qcom,name = "vdd-mem";
+ qcom,resource-type = <0>;
+ qcom,type = <0x62706d73>; /* "smpb" */
+ qcom,id = <0x01>;
+ qcom,key = <0x7675>; /* "uv" */
+ qcom,init-value = <1050000>; /* Super Turbo */
+ };
+
+ qcom,lpm-resources@2 {
+ reg = <0x2>;
+ qcom,name = "pxo";
+ qcom,resource-type = <0>;
+ qcom,type = <0x306b6c63>; /* "clk0" */
+ qcom,id = <0x00>;
+ qcom,key = <0x62616e45>; /* "Enab" */
+ qcom,init-value = <1>; /* On */
+ };
+
+ qcom,lpm-resources@3 {
+ reg = <0x3>;
+ qcom,name = "l2";
+ qcom,resource-type = <1>;
+ qcom,init-value = <2>; /* Retention */
+ };
+ };
+
+ qcom,lpm-levels {
+ compatible = "qcom,lpm-levels";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ qcom,lpm-level@0 {
+ reg = <0x0>;
+ qcom,mode = <0>; /* MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT */
+ qcom,xo = <1>; /* ON */
+ qcom,l2 = <3>; /* ACTIVE */
+ qcom,vdd-mem-upper-bound = <1150000>; /* MAX */
+ qcom,vdd-mem-lower-bound = <1050000>; /* ACTIVE */
+ qcom,vdd-dig-upper-bound = <5>; /* MAX */
+ qcom,vdd-dig-lower-bound = <3>; /* ACTIVE */
+ qcom,latency-us = <1>;
+ qcom,ss-power = <784>;
+ qcom,energy-overhead = <190000>;
+ qcom,time-overhead = <100>;
+ };
+
+ qcom,lpm-level@1 {
+ reg = <0x1>;
+ qcom,mode = <4>; /* MSM_PM_SLEEP_MODE_RETENTION*/
+ qcom,xo = <1>; /* ON */
+ qcom,l2 = <3>; /* ACTIVE */
+ qcom,vdd-mem-upper-bound = <1150000>; /* MAX */
+ qcom,vdd-mem-lower-bound = <1050000>; /* ACTIVE */
+ qcom,vdd-dig-upper-bound = <5>; /* MAX */
+ qcom,vdd-dig-lower-bound = <3>; /* ACTIVE */
+ qcom,latency-us = <75>;
+ qcom,ss-power = <735>;
+ qcom,energy-overhead = <77341>;
+ qcom,time-overhead = <105>;
+ };
+
+
+ qcom,lpm-level@2 {
+ reg = <0x2>;
+ qcom,mode = <2>; /* MSM_PM_SLEEP_MODE_STANDALONE_POWER_COLLAPSE */
+ qcom,xo = <1>; /* ON */
+ qcom,l2 = <3>; /* ACTIVE */
+ qcom,vdd-mem-upper-bound = <1150000>; /* MAX */
+ qcom,vdd-mem-lower-bound = <1050000>; /* ACTIVE */
+ qcom,vdd-dig-upper-bound = <5>; /* MAX */
+ qcom,vdd-dig-lower-bound = <3>; /* ACTIVE */
+ qcom,latency-us = <95>;
+ qcom,ss-power = <725>;
+ qcom,energy-overhead = <99500>;
+ qcom,time-overhead = <130>;
+ };
+
+ qcom,lpm-level@3 {
+ reg = <0x3>;
+ qcom,mode = <3>; /* MSM_PM_SLEEP_MODE_POWER_COLLAPSE */
+ qcom,xo = <1>; /* ON */
+ qcom,l2 = <1>; /* GDHS */
+ qcom,vdd-mem-upper-bound = <1150000>; /* MAX */
+ qcom,vdd-mem-lower-bound = <1050000>; /* ACTIVE */
+ qcom,vdd-dig-upper-bound = <5>; /* MAX */
+ qcom,vdd-dig-lower-bound = <3>; /* ACTIVE */
+ qcom,latency-us = <2000>;
+ qcom,ss-power = <138>;
+ qcom,energy-overhead = <1208400>;
+ qcom,time-overhead = <3200>;
+ };
+
+ qcom,lpm-level@4 {
+ reg = <0x4>;
+ qcom,mode = <3>; /* MSM_PM_SLEEP_MODE_POWER_COLLAPSE */
+ qcom,xo = <1>; /* ON */
+ qcom,l2 = <0>; /* OFF */
+ qcom,vdd-mem-upper-bound = <1050000>; /* ACTIVE */
+ qcom,vdd-mem-lower-bound = <750000>; /* RETENTION HIGH */
+ qcom,vdd-dig-upper-bound = <3>; /* ACTIVE */
+ qcom,vdd-dig-lower-bound = <2>; /* RETENTION HIGH */
+ qcom,latency-us = <3000>;
+ qcom,ss-power = <110>;
+ qcom,energy-overhead = <1250300>;
+ qcom,time-overhead = <3500>;
+ };
+
+ qcom,lpm-level@5 {
+ reg = <0x5>;
+ qcom,mode = <3>; /* MSM_PM_SLEEP_MODE_POWER_COLLAPSE */
+ qcom,xo = <0>; /* OFF */
+ qcom,l2 = <1>; /* GDHS */
+ qcom,vdd-mem-upper-bound = <1150000>; /* MAX */
+ qcom,vdd-mem-lower-bound = <1050000>; /* ACTIVE */
+ qcom,vdd-dig-upper-bound = <5>; /* MAX */
+ qcom,vdd-dig-lower-bound = <3>; /* ACTIVE */
+ qcom,latency-us = <3000>;
+ qcom,ss-power = <68>;
+ qcom,energy-overhead = <1350200>;
+ qcom,time-overhead = <4000>;
+ };
+
+ qcom,lpm-level@6 {
+ reg = <0x6>;
+ qcom,mode = <3>; /* MSM_PM_SLEEP_MODE_POWER_COLLAPSE */
+ qcom,xo = <0>; /* OFF */
+ qcom,l2 = <0>; /* OFF */
+ qcom,vdd-mem-upper-bound = <1150000>; /* MAX */
+ qcom,vdd-mem-lower-bound = <1050000>; /* ACTIVE */
+ qcom,vdd-dig-upper-bound = <5>; /* MAX */
+ qcom,vdd-dig-lower-bound = <3>; /* ACTIVE */
+ qcom,latency-us = <10300>;
+ qcom,ss-power = <63>;
+ qcom,energy-overhead = <2128000>;
+ qcom,time-overhead = <18200>;
+ };
+
+ qcom,lpm-level@7 {
+ reg = <0x7>;
+ qcom,mode= <3>; /* MSM_PM_SLEEP_MODE_POWER_COLLAPSE */
+ qcom,xo = <0>; /* OFF */
+ qcom,l2 = <0>; /* OFF */
+ qcom,vdd-mem-upper-bound = <1050000>; /* ACTIVE */
+ qcom,vdd-mem-lower-bound = <750000>; /* RETENTION HIGH */
+ qcom,vdd-dig-upper-bound = <3>; /* ACTIVE */
+ qcom,vdd-dig-lower-bound = <2>; /* RETIONTION HIGH */
+ qcom,latency-us = <18000>;
+ qcom,ss-power = <10>;
+ qcom,energy-overhead = <3202600>;
+ qcom,time-overhead = <27000>;
+ };
+
+ qcom,lpm-level@8 {
+ reg = <0x8>;
+ qcom,mode= <3>; /* MSM_PM_SLEEP_MODE_POWER_COLLAPSE */
+ qcom,xo = <0>; /* OFF */
+ qcom,l2 = <0>; /* OFF */
+ qcom,vdd-mem-upper-bound = <750000>; /* RETENTION HIGH */
+ qcom,vdd-mem-lower-bound = <750000>; /* RETENTION LOW */
+ qcom,vdd-dig-upper-bound = <2>; /* RETENTION HIGH */
+ qcom,vdd-dig-lower-bound = <0>; /* RETENTION LOW */
+ qcom,latency-us = <20000>;
+ qcom,ss-power = <2>;
+ qcom,energy-overhead = <4252000>;
+ qcom,time-overhead = <32000>;
+ };
+ };
+
+ qcom,pm-boot {
+ compatible = "qcom,pm-boot";
+ qcom,mode = <0>; /* MSM_PM_BOOT_CONFIG_TZ */
+ };
+
+ qcom,mpm@fc4281d0 {
+ compatible = "qcom,mpm-v2";
+ reg = <0xfc4281d0 0x1000>, /* MSM_RPM_MPM_BASE 4K */
+ <0xf9011008 0x4>; /* MSM_APCS_GCC_BASE 4K */
+ reg-names = "vmpm", "ipc";
+ interrupts = <0 171 1>;
+
+ qcom,ipc-bit-offset = <1>;
+
+ qcom,gic-parent = <&intc>;
+ qcom,gic-map = <47 172>, /* usb2_hsic_async_wakeup_irq */
+ <53 104>, /* mdss_irq */
+ <62 222>, /* ee0_krait_hlos_spmi_periph_irq */
+ <0xff 57>, /* mss_to_apps_irq(0) */
+ <0xff 58>, /* mss_to_apps_irq(1) */
+ <0xff 59>, /* mss_to_apps_irq(2) */
+ <0xff 60>, /* mss_to_apps_irq(3) */
+ <0xff 173>, /* o_wcss_apss_smd_hi */
+ <0xff 174>, /* o_wcss_apss_smd_med */
+ <0xff 175>, /* o_wcss_apss_smd_low */
+ <0xff 176>, /* o_wcss_apss_smsm_irq */
+ <0xff 177>, /* o_wcss_apss_wlan_data_xfer_done */
+ <0xff 178>, /* o_wcss_apss_wlan_rx_data_avail */
+ <0xff 179>, /* o_wcss_apss_asic_intr
+
+ <0xff 188>, /* lpass_irq_out_apcs(0) */
+ <0xff 189>, /* lpass_irq_out_apcs(1) */
+ <0xff 190>, /* lpass_irq_out_apcs(2) */
+ <0xff 191>, /* lpass_irq_out_apcs(3) */
+ <0xff 192>, /* lpass_irq_out_apcs(4) */
+ <0xff 193>, /* lpass_irq_out_apcs(5) */
+ <0xff 194>, /* lpass_irq_out_apcs(6) */
+ <0xff 195>, /* lpass_irq_out_apcs(7) */
+ <0xff 196>, /* lpass_irq_out_apcs(8) */
+ <0xff 197>, /* lpass_irq_out_apcs(9) */
+ <0xff 200>, /* rpm_ipc(4) */
+ <0xff 201>, /* rpm_ipc(5) */
+ <0xff 202>, /* rpm_ipc(6) */
+ <0xff 203>, /* rpm_ipc(7) */
+ <0xff 204>, /* rpm_ipc(24) */
+ <0xff 205>, /* rpm_ipc(25) */
+ <0xff 206>, /* rpm_ipc(26) */
+ <0xff 207>, /* rpm_ipc(27) */
+ <0xff 240>; /* summary_irq_kpss */
+
+ qcom,gpio-parent = <&msmgpio>;
+ qcom,gpio-map = <3 102>,
+ <4 1 >,
+ <5 5 >,
+ <6 9 >,
+ <7 18>,
+ <8 20>,
+ <9 24>,
+ <10 27>,
+ <11 28>,
+ <12 34>,
+ <13 35>,
+ <14 37>,
+ <15 42>,
+ <16 44>,
+ <17 46>,
+ <18 50>,
+ <19 54>,
+ <20 59>,
+ <21 61>,
+ <22 62>,
+ <23 64>,
+ <24 65>,
+ <25 66>,
+ <26 67>,
+ <27 68>,
+ <28 71>,
+ <29 72>,
+ <30 73>,
+ <31 74>,
+ <32 75>,
+ <33 77>,
+ <34 79>,
+ <35 80>,
+ <36 82>,
+ <37 86>,
+ <38 92>,
+ <39 93>,
+ <40 95>;
+ };
+
+ qcom,pc-cntr@fe805664 {
+ compatible = "qcom,pc-cntr";
+ reg = <0xfe805664 0x40>;
+ };
+
+ qcom,pm-8x60 {
+ compatible = "qcom,pm-8x60";
+ qcom,pc-mode = <0>; /*MSM_PC_TZ_L2_INT */
+ qcom,use-sync-timer;
+ };
+
+ qcom,rpm-stats@0xfc19dbd0{
+ compatible = "qcom,rpm-stats";
+ reg = <0xfc19dbd0 0x1000>;
+ reg-names = "phys_addr_base";
+ qcom,sleep-stats-version = <2>;
+ };
+};
diff --git a/arch/arm/boot/dts/msm8226.dtsi b/arch/arm/boot/dts/msm8226.dtsi
index 36bcdd7..0277a1c 100644
--- a/arch/arm/boot/dts/msm8226.dtsi
+++ b/arch/arm/boot/dts/msm8226.dtsi
@@ -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
@@ -13,6 +13,8 @@
/include/ "skeleton.dtsi"
/include/ "msm8226-ion.dtsi"
/include/ "msm-gdsc.dtsi"
+/include/ "msm8226-iommu.dtsi"
+/include/ "msm8226-pm.dtsi"
/ {
model = "Qualcomm MSM 8226";
@@ -69,11 +71,11 @@
usb@f9a55000 {
compatible = "qcom,hsusb-otg";
reg = <0xf9a55000 0x400>;
- interrupts = <0 134 0>;
- interrupt-names = "core_irq";
- HSUSB_VDDCX-supply = <&pm8026_s1>;
- HSUSB_1p8-supply = <&pm8026_l10>;
- HSUSB_3p3-supply = <&pm8026_l20>;
+ interrupts = <0 134 0>, <0 140 0>;
+ interrupt-names = "core_irq", "async_irq";
+ HSUSB_VDDCX-supply = <&pm8026_s1>;
+ HSUSB_1p8-supply = <&pm8026_l10>;
+ HSUSB_3p3-supply = <&pm8026_l20>;
qcom,hsusb-otg-phy-type = <2>;
qcom,hsusb-otg-mode = <1>;
@@ -387,6 +389,13 @@
};
};
+ rpm_bus: qcom,rpm-smd {
+ compatible = "qcom,rpm-smd";
+ rpm-channel-name = "rpm_requests";
+ rpm-channel-type = <15>; /* SMD_APPS_RPM */
+ rpm-standalone;
+ };
+
sdcc1: qcom,sdcc@f9824000 {
cell-index = <1>; /* SDC1 eMMC slot */
compatible = "qcom,msm-sdcc";
@@ -546,6 +555,17 @@
<0x1d80002b>; /* WLED */
};
+ i2c@f9926000 { /* BLSP-1 QUP-4 */
+ cell-index = <0>;
+ compatible = "qcom,i2c-qup";
+ reg = <0xf9926000 0x1000>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg-names = "qup_phys_addr";
+ interrupts = <0 98 0>;
+ interrupt-names = "qup_err_intr";
+ qcom,i2c-bus-freq = <100000>;
+ };
};
&gdsc_venus {
diff --git a/arch/arm/boot/dts/msm8910-iommu-domains.dtsi b/arch/arm/boot/dts/msm8610-iommu-domains.dtsi
similarity index 93%
rename from arch/arm/boot/dts/msm8910-iommu-domains.dtsi
rename to arch/arm/boot/dts/msm8610-iommu-domains.dtsi
index dbdbd5f..52a8c47 100644
--- a/arch/arm/boot/dts/msm8910-iommu-domains.dtsi
+++ b/arch/arm/boot/dts/msm8610-iommu-domains.dtsi
@@ -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
diff --git a/arch/arm/boot/dts/msm8910-ion.dtsi b/arch/arm/boot/dts/msm8610-ion.dtsi
similarity index 95%
rename from arch/arm/boot/dts/msm8910-ion.dtsi
rename to arch/arm/boot/dts/msm8610-ion.dtsi
index 88bb1ab..0abaca5 100644
--- a/arch/arm/boot/dts/msm8910-ion.dtsi
+++ b/arch/arm/boot/dts/msm8610-ion.dtsi
@@ -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
diff --git a/arch/arm/boot/dts/msm8610-pm.dtsi b/arch/arm/boot/dts/msm8610-pm.dtsi
new file mode 100644
index 0000000..e107b36
--- /dev/null
+++ b/arch/arm/boot/dts/msm8610-pm.dtsi
@@ -0,0 +1,402 @@
+/* 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/ "skeleton.dtsi"
+
+/ {
+ qcom,spm@f9089000 {
+ compatible = "qcom,spm-v2";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ reg = <0xf9089000 0x1000>;
+ qcom,core-id = <0>;
+ qcom,saw2-ver-reg = <0xfd0>;
+ qcom,saw2-cfg = <0x01>;
+ qcom,saw2-spm-dly= <0x20000400>;
+ qcom,saw2-spm-ctl = <0x1>;
+ qcom,saw2-spm-cmd-wfi = [60 03 60 76 76 0b 0f];
+ qcom,saw2-spm-cmd-spc = [00 20 10 80 90 5b 60 03 60 3b 76 76 94
+ 5b 80 10 2b 30 06 26 30 0f];
+ qcom,saw2-spm-cmd-pc = [00 20 10 80 90 5b 60 07 3b 76 76 0b 94
+ 5b 80 10 2b 30 06 26 30 0f];
+ };
+
+ qcom,spm@f9099000 {
+ compatible = "qcom,spm-v2";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ reg = <0xf9099000 0x1000>;
+ qcom,core-id = <1>;
+ qcom,saw2-ver-reg = <0xfd0>;
+ qcom,saw2-cfg = <0x01>;
+ qcom,saw2-spm-dly= <0x20000400>;
+ qcom,saw2-spm-ctl = <0x1>;
+ qcom,saw2-spm-cmd-wfi = [60 03 60 76 76 0b 0f];
+ qcom,saw2-spm-cmd-spc = [00 20 10 80 90 5b 60 03 60 3b 76 76 94
+ 5b 80 10 2b 30 06 26 30 0f];
+ qcom,saw2-spm-cmd-pc = [00 20 10 80 90 5b 60 07 3b 76 76 0b 94
+ 5b 80 10 2b 30 06 26 30 0f];
+ };
+
+ qcom,spm@f90a9000 {
+ compatible = "qcom,spm-v2";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ reg = <0xf90a9000 0x1000>;
+ qcom,core-id = <2>;
+ qcom,saw2-ver-reg = <0xfd0>;
+ qcom,saw2-cfg = <0x01>;
+ qcom,saw2-spm-dly= <0x20000400>;
+ qcom,saw2-spm-ctl = <0x1>;
+ qcom,saw2-spm-cmd-wfi = [60 03 60 76 76 0b 0f];
+ qcom,saw2-spm-cmd-spc = [00 20 10 80 90 5b 60 03 60 3b 76 76 94
+ 5b 80 10 2b 30 06 26 30 0f];
+ qcom,saw2-spm-cmd-pc = [00 20 10 80 90 5b 60 07 3b 76 76 0b 94
+ 5b 80 10 2b 30 06 26 30 0f];
+ };
+
+ qcom,spm@f90b9000 {
+ compatible = "qcom,spm-v2";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ reg = <0xf90b9000 0x1000>;
+ qcom,core-id = <3>;
+ qcom,saw2-ver-reg = <0xfd0>;
+ qcom,saw2-cfg = <0x01>;
+ qcom,saw2-spm-dly= <0x20000400>;
+ qcom,saw2-spm-ctl = <0x1>;
+ qcom,saw2-spm-cmd-wfi = [60 03 60 76 76 0b 0f];
+ qcom,saw2-spm-cmd-spc = [00 20 10 80 90 5b 60 03 60 3b 76 76 94
+ 5b 80 10 2b 30 06 26 30 0f];
+ qcom,saw2-spm-cmd-pc = [00 20 10 80 90 5b 60 07 3b 76 76 0b 94
+ 5b 80 10 2b 30 06 26 30 0f];
+ };
+
+ qcom,spm@f9012000 {
+ compatible = "qcom,spm-v2";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ reg = <0xf9012000 0x1000>;
+ qcom,core-id = <0xffff>; /* L2/APCS SAW */
+ qcom,saw2-ver-reg = <0xfd0>;
+ qcom,saw2-cfg = <0x14>;
+ qcom,saw2-spm-dly= <0x20000400>;
+ qcom,saw2-spm-ctl = <0x1>;
+ qcom,saw2-pmic-data0 = <0x02030080>;
+ qcom,saw2-pmic-data1 = <0x00030000>;
+ qcom,vctl-timeout-us = <50>;
+ qcom,vctl-port = <0x0>;
+ qcom,phase-port = <0x1>;
+ qcom,pfm-port = <0x2>;
+ qcom,saw2-spm-cmd-ret = [0b 00 03 00 7b 0f];
+ qcom,saw2-spm-cmd-gdhs = [00 20 32 60 70 80 0b 6b c0 e0 d0 42 07
+ 78 1f 80 4e d0 e0 c0 22 6b 50 4b 60 02 32 50 7b
+ 0f];
+ qcom,saw2-spm-cmd-pc = [00 32 60 70 80 b0 0b 10 e0 d0 6b c0
+ 42 f0 11 07 01 b0 78 1f 80 4e c0 d0 12 e0 6b 50 4b
+ 60 02 32 50 f0 7b 0f]; /*APCS_PMIC_OFF_L2RAM_OFF*/
+ };
+
+ qcom,lpm-resources {
+ compatible = "qcom,lpm-resources";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ qcom,lpm-resources@0 {
+ reg = <0x0>;
+ qcom,name = "vdd-dig";
+ qcom,resource-type = <0>;
+ qcom,type = <0x62706d73>; /* "smpb" */
+ qcom,id = <0x02>;
+ qcom,key = <0x6e726f63>; /* "corn" */
+ qcom,init-value = <5>; /* Super Turbo */
+ };
+
+ qcom,lpm-resources@1 {
+ reg = <0x1>;
+ qcom,name = "vdd-mem";
+ qcom,resource-type = <0>;
+ qcom,type = <0x62706d73>; /* "smpb" */
+ qcom,id = <0x01>;
+ qcom,key = <0x7675>; /* "uv" */
+ qcom,init-value = <1050000>; /* Super Turbo */
+ };
+
+ qcom,lpm-resources@2 {
+ reg = <0x2>;
+ qcom,name = "pxo";
+ qcom,resource-type = <0>;
+ qcom,type = <0x306b6c63>; /* "clk0" */
+ qcom,id = <0x00>;
+ qcom,key = <0x62616e45>; /* "Enab" */
+ qcom,init-value = <1>; /* On */
+ };
+
+ qcom,lpm-resources@3 {
+ reg = <0x3>;
+ qcom,name = "l2";
+ qcom,resource-type = <1>;
+ qcom,init-value = <2>; /* Retention */
+ };
+ };
+
+ qcom,lpm-levels {
+ compatible = "qcom,lpm-levels";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ qcom,lpm-level@0 {
+ reg = <0x0>;
+ qcom,mode = <0>; /* MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT */
+ qcom,xo = <1>; /* ON */
+ qcom,l2 = <3>; /* ACTIVE */
+ qcom,vdd-mem-upper-bound = <1150000>; /* MAX */
+ qcom,vdd-mem-lower-bound = <1050000>; /* ACTIVE */
+ qcom,vdd-dig-upper-bound = <5>; /* MAX */
+ qcom,vdd-dig-lower-bound = <3>; /* ACTIVE */
+ qcom,latency-us = <1>;
+ qcom,ss-power = <784>;
+ qcom,energy-overhead = <190000>;
+ qcom,time-overhead = <100>;
+ };
+
+ qcom,lpm-level@1 {
+ reg = <0x1>;
+ qcom,mode = <4>; /* MSM_PM_SLEEP_MODE_RETENTION*/
+ qcom,xo = <1>; /* ON */
+ qcom,l2 = <3>; /* ACTIVE */
+ qcom,vdd-mem-upper-bound = <1150000>; /* MAX */
+ qcom,vdd-mem-lower-bound = <1050000>; /* ACTIVE */
+ qcom,vdd-dig-upper-bound = <5>; /* MAX */
+ qcom,vdd-dig-lower-bound = <3>; /* ACTIVE */
+ qcom,latency-us = <75>;
+ qcom,ss-power = <735>;
+ qcom,energy-overhead = <77341>;
+ qcom,time-overhead = <105>;
+ };
+
+
+ qcom,lpm-level@2 {
+ reg = <0x2>;
+ qcom,mode = <2>; /* MSM_PM_SLEEP_MODE_STANDALONE_POWER_COLLAPSE */
+ qcom,xo = <1>; /* ON */
+ qcom,l2 = <3>; /* ACTIVE */
+ qcom,vdd-mem-upper-bound = <1150000>; /* MAX */
+ qcom,vdd-mem-lower-bound = <1050000>; /* ACTIVE */
+ qcom,vdd-dig-upper-bound = <5>; /* MAX */
+ qcom,vdd-dig-lower-bound = <3>; /* ACTIVE */
+ qcom,latency-us = <95>;
+ qcom,ss-power = <725>;
+ qcom,energy-overhead = <99500>;
+ qcom,time-overhead = <130>;
+ };
+
+ qcom,lpm-level@3 {
+ reg = <0x3>;
+ qcom,mode = <3>; /* MSM_PM_SLEEP_MODE_POWER_COLLAPSE */
+ qcom,xo = <1>; /* ON */
+ qcom,l2 = <1>; /* GDHS */
+ qcom,vdd-mem-upper-bound = <1150000>; /* MAX */
+ qcom,vdd-mem-lower-bound = <1050000>; /* ACTIVE */
+ qcom,vdd-dig-upper-bound = <5>; /* MAX */
+ qcom,vdd-dig-lower-bound = <3>; /* ACTIVE */
+ qcom,latency-us = <2000>;
+ qcom,ss-power = <138>;
+ qcom,energy-overhead = <1208400>;
+ qcom,time-overhead = <3200>;
+ };
+
+ qcom,lpm-level@4 {
+ reg = <0x4>;
+ qcom,mode = <3>; /* MSM_PM_SLEEP_MODE_POWER_COLLAPSE */
+ qcom,xo = <1>; /* ON */
+ qcom,l2 = <0>; /* OFF */
+ qcom,vdd-mem-upper-bound = <1050000>; /* ACTIVE */
+ qcom,vdd-mem-lower-bound = <750000>; /* RETENTION HIGH */
+ qcom,vdd-dig-upper-bound = <3>; /* ACTIVE */
+ qcom,vdd-dig-lower-bound = <2>; /* RETENTION HIGH */
+ qcom,latency-us = <3000>;
+ qcom,ss-power = <110>;
+ qcom,energy-overhead = <1250300>;
+ qcom,time-overhead = <3500>;
+ };
+
+ qcom,lpm-level@5 {
+ reg = <0x5>;
+ qcom,mode = <3>; /* MSM_PM_SLEEP_MODE_POWER_COLLAPSE */
+ qcom,xo = <0>; /* OFF */
+ qcom,l2 = <1>; /* GDHS */
+ qcom,vdd-mem-upper-bound = <1150000>; /* MAX */
+ qcom,vdd-mem-lower-bound = <1050000>; /* ACTIVE */
+ qcom,vdd-dig-upper-bound = <5>; /* MAX */
+ qcom,vdd-dig-lower-bound = <3>; /* ACTIVE */
+ qcom,latency-us = <3000>;
+ qcom,ss-power = <68>;
+ qcom,energy-overhead = <1350200>;
+ qcom,time-overhead = <4000>;
+ };
+
+ qcom,lpm-level@6 {
+ reg = <0x6>;
+ qcom,mode = <3>; /* MSM_PM_SLEEP_MODE_POWER_COLLAPSE */
+ qcom,xo = <0>; /* OFF */
+ qcom,l2 = <0>; /* OFF */
+ qcom,vdd-mem-upper-bound = <1150000>; /* MAX */
+ qcom,vdd-mem-lower-bound = <1050000>; /* ACTIVE */
+ qcom,vdd-dig-upper-bound = <5>; /* MAX */
+ qcom,vdd-dig-lower-bound = <3>; /* ACTIVE */
+ qcom,latency-us = <10300>;
+ qcom,ss-power = <63>;
+ qcom,energy-overhead = <2128000>;
+ qcom,time-overhead = <18200>;
+ };
+
+ qcom,lpm-level@7 {
+ reg = <0x7>;
+ qcom,mode= <3>; /* MSM_PM_SLEEP_MODE_POWER_COLLAPSE */
+ qcom,xo = <0>; /* OFF */
+ qcom,l2 = <0>; /* OFF */
+ qcom,vdd-mem-upper-bound = <1050000>; /* ACTIVE */
+ qcom,vdd-mem-lower-bound = <750000>; /* RETENTION HIGH */
+ qcom,vdd-dig-upper-bound = <3>; /* ACTIVE */
+ qcom,vdd-dig-lower-bound = <2>; /* RETIONTION HIGH */
+ qcom,latency-us = <18000>;
+ qcom,ss-power = <10>;
+ qcom,energy-overhead = <3202600>;
+ qcom,time-overhead = <27000>;
+ };
+
+ qcom,lpm-level@8 {
+ reg = <0x8>;
+ qcom,mode= <3>; /* MSM_PM_SLEEP_MODE_POWER_COLLAPSE */
+ qcom,xo = <0>; /* OFF */
+ qcom,l2 = <0>; /* OFF */
+ qcom,vdd-mem-upper-bound = <750000>; /* RETENTION HIGH */
+ qcom,vdd-mem-lower-bound = <750000>; /* RETENTION LOW */
+ qcom,vdd-dig-upper-bound = <2>; /* RETENTION HIGH */
+ qcom,vdd-dig-lower-bound = <0>; /* RETENTION LOW */
+ qcom,latency-us = <20000>;
+ qcom,ss-power = <2>;
+ qcom,energy-overhead = <4252000>;
+ qcom,time-overhead = <32000>;
+ };
+ };
+
+ qcom,pm-boot {
+ compatible = "qcom,pm-boot";
+ qcom,mode = <0>; /* MSM_PM_BOOT_CONFIG_TZ */
+ };
+
+ qcom,mpm@fc4281d0 {
+ compatible = "qcom,mpm-v2";
+ reg = <0xfc4281d0 0x1000>, /* MSM_RPM_MPM_BASE 4K */
+ <0xf9011008 0x4>; /* MSM_APCS_GCC_BASE 4K */
+ reg-names = "vmpm", "ipc";
+ interrupts = <0 171 1>;
+
+ qcom,ipc-bit-offset = <1>;
+
+ qcom,gic-parent = <&intc>;
+ qcom,gic-map = <47 172>, /* usb2_hsic_async_wakeup_irq */
+ <53 104>, /* mdss_irq */
+ <62 222>, /* ee0_krait_hlos_spmi_periph_irq */
+ <0xff 57>, /* mss_to_apps_irq(0) */
+ <0xff 58>, /* mss_to_apps_irq(1) */
+ <0xff 59>, /* mss_to_apps_irq(2) */
+ <0xff 60>, /* mss_to_apps_irq(3) */
+ <0xff 173>, /* o_wcss_apss_smd_hi */
+ <0xff 174>, /* o_wcss_apss_smd_med */
+ <0xff 175>, /* o_wcss_apss_smd_low */
+ <0xff 176>, /* o_wcss_apss_smsm_irq */
+ <0xff 177>, /* o_wcss_apss_wlan_data_xfer_done */
+ <0xff 178>, /* o_wcss_apss_wlan_rx_data_avail */
+ <0xff 179>, /* o_wcss_apss_asic_intr
+
+ <0xff 188>, /* lpass_irq_out_apcs(0) */
+ <0xff 189>, /* lpass_irq_out_apcs(1) */
+ <0xff 190>, /* lpass_irq_out_apcs(2) */
+ <0xff 191>, /* lpass_irq_out_apcs(3) */
+ <0xff 192>, /* lpass_irq_out_apcs(4) */
+ <0xff 193>, /* lpass_irq_out_apcs(5) */
+ <0xff 194>, /* lpass_irq_out_apcs(6) */
+ <0xff 195>, /* lpass_irq_out_apcs(7) */
+ <0xff 196>, /* lpass_irq_out_apcs(8) */
+ <0xff 197>, /* lpass_irq_out_apcs(9) */
+ <0xff 200>, /* rpm_ipc(4) */
+ <0xff 201>, /* rpm_ipc(5) */
+ <0xff 202>, /* rpm_ipc(6) */
+ <0xff 203>, /* rpm_ipc(7) */
+ <0xff 204>, /* rpm_ipc(24) */
+ <0xff 205>, /* rpm_ipc(25) */
+ <0xff 206>, /* rpm_ipc(26) */
+ <0xff 207>, /* rpm_ipc(27) */
+ <0xff 240>; /* summary_irq_kpss */
+
+ qcom,gpio-parent = <&msmgpio>;
+ qcom,gpio-map = <3 102>,
+ <4 1 >,
+ <5 5 >,
+ <6 9 >,
+ <7 18>,
+ <8 20>,
+ <9 24>,
+ <10 27>,
+ <11 28>,
+ <12 34>,
+ <13 35>,
+ <14 37>,
+ <15 42>,
+ <16 44>,
+ <17 46>,
+ <18 50>,
+ <19 54>,
+ <20 59>,
+ <21 61>,
+ <22 62>,
+ <23 64>,
+ <24 65>,
+ <25 66>,
+ <26 67>,
+ <27 68>,
+ <28 71>,
+ <29 72>,
+ <30 73>,
+ <31 74>,
+ <32 75>,
+ <33 77>,
+ <34 79>,
+ <35 80>,
+ <36 82>,
+ <37 86>,
+ <38 92>,
+ <39 93>,
+ <40 95>;
+ };
+
+ qcom,pc-cntr@fe805664 {
+ compatible = "qcom,pc-cntr";
+ reg = <0xfe805664 0x40>;
+ };
+
+ qcom,pm-8x60 {
+ compatible = "qcom,pm-8x60";
+ qcom,pc-mode = <0>; /*MSM_PC_TZ_L2_INT */
+ qcom,use-sync-timer;
+ };
+
+ qcom,rpm-stats@0xfc19dbd0{
+ compatible = "qcom,rpm-stats";
+ reg = <0xfc19dbd0 0x1000>;
+ reg-names = "phys_addr_base";
+ qcom,sleep-stats-version = <2>;
+ };
+};
diff --git a/arch/arm/boot/dts/msm8910-regulator.dtsi b/arch/arm/boot/dts/msm8610-regulator.dtsi
similarity index 98%
rename from arch/arm/boot/dts/msm8910-regulator.dtsi
rename to arch/arm/boot/dts/msm8610-regulator.dtsi
index a32d4ab..a81c082 100644
--- a/arch/arm/boot/dts/msm8910-regulator.dtsi
+++ b/arch/arm/boot/dts/msm8610-regulator.dtsi
@@ -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
diff --git a/arch/arm/boot/dts/msm8910-rumi.dts b/arch/arm/boot/dts/msm8610-rumi.dts
similarity index 75%
rename from arch/arm/boot/dts/msm8910-rumi.dts
rename to arch/arm/boot/dts/msm8610-rumi.dts
index 0d944aa..d889268 100644
--- a/arch/arm/boot/dts/msm8910-rumi.dts
+++ b/arch/arm/boot/dts/msm8610-rumi.dts
@@ -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
@@ -12,11 +12,11 @@
/dts-v1/;
-/include/ "msm8910.dtsi"
+/include/ "msm8610.dtsi"
/ {
- model = "Qualcomm MSM 8910 Rumi";
- compatible = "qcom,msm8910-rumi", "qcom,msm8910";
+ model = "Qualcomm MSM 8610 Rumi";
+ compatible = "qcom,msm8610-rumi", "qcom,msm8610";
qcom,msm-id = <147 1 0>;
serial@f991f000 {
diff --git a/arch/arm/boot/dts/msm8910-sim.dts b/arch/arm/boot/dts/msm8610-sim.dts
similarity index 74%
rename from arch/arm/boot/dts/msm8910-sim.dts
rename to arch/arm/boot/dts/msm8610-sim.dts
index aae88b1..73ba807 100644
--- a/arch/arm/boot/dts/msm8910-sim.dts
+++ b/arch/arm/boot/dts/msm8610-sim.dts
@@ -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
@@ -12,11 +12,11 @@
/dts-v1/;
-/include/ "msm8910.dtsi"
+/include/ "msm8610.dtsi"
/ {
- model = "Qualcomm MSM 8910 Simulator";
- compatible = "qcom,msm8910-sim", "qcom,msm8910";
+ model = "Qualcomm MSM 8610 Simulator";
+ compatible = "qcom,msm8610-sim", "qcom,msm8610";
qcom,msm-id = <147 1 0>;
serial@f991f000 {
diff --git a/arch/arm/boot/dts/msm8910.dtsi b/arch/arm/boot/dts/msm8610.dtsi
similarity index 91%
rename from arch/arm/boot/dts/msm8910.dtsi
rename to arch/arm/boot/dts/msm8610.dtsi
index a08c165..ffc9394 100644
--- a/arch/arm/boot/dts/msm8910.dtsi
+++ b/arch/arm/boot/dts/msm8610.dtsi
@@ -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
@@ -12,12 +12,13 @@
/include/ "skeleton.dtsi"
/include/ "msm-iommu-v1.dtsi"
-/include/ "msm8910-ion.dtsi"
+/include/ "msm8610-ion.dtsi"
/include/ "msm-gdsc.dtsi"
+/include/ "msm8610-pm.dtsi"
/ {
- model = "Qualcomm MSM 8910";
- compatible = "qcom,msm8910";
+ model = "Qualcomm MSM 8610";
+ compatible = "qcom,msm8610";
interrupt-parent = <&intc>;
intc: interrupt-controller@f9000000 {
@@ -46,6 +47,11 @@
clock-frequency = <19200000>;
};
+ qcom,msm-adsp-loader {
+ compatible = "qcom,adsp-loader";
+ qcom,adsp-state = <0>;
+ };
+
serial@f991f000 {
compatible = "qcom,msm-lsuart-v14";
reg = <0xf991f000 0x1000>;
@@ -53,6 +59,11 @@
status = "disabled";
};
+ qcom,vidc@fdc00000 {
+ compatible = "qcom,msm-vidc";
+ hfi = "q6";
+ };
+
usb@f9a55000 {
compatible = "qcom,hsusb-otg";
reg = <0xf9a55000 0x400>;
@@ -210,6 +221,13 @@
};
};
+ rpm_bus: qcom,rpm-smd {
+ compatible = "qcom,rpm-smd";
+ rpm-channel-name = "rpm_requests";
+ rpm-channel-type = <15>; /* SMD_APPS_RPM */
+ rpm-standalone;
+ };
+
qcom,wdt@f9017000 {
compatible = "qcom,msm-watchdog";
reg = <0xf9017000 0x1000>;
@@ -308,6 +326,15 @@
<0x155000cf>; /* LDO_22 */
};
+ qcom,lpass@fe200000 {
+ compatible = "qcom,pil-q6v5-lpass";
+ reg = <0xfe200000 0x00100>,
+ <0xfd485100 0x00010>;
+ reg-names = "qdsp6_base", "halt_base";
+ interrupts = <0 162 1>;
+
+ qcom,firmware-name = "adsp";
+ };
};
&gdsc_vfe {
@@ -342,7 +369,7 @@
status = "ok";
};
-/include/ "msm8910-iommu-domains.dtsi"
+/include/ "msm8610-iommu-domains.dtsi"
-/include/ "msm8910-regulator.dtsi"
+/include/ "msm8610-regulator.dtsi"
/include/ "msm-pm8110.dtsi"
diff --git a/arch/arm/boot/dts/msm8974-camera-sensor-liquid.dtsi b/arch/arm/boot/dts/msm8974-camera-sensor-liquid.dtsi
index c9b999f..7035bb4 100644
--- a/arch/arm/boot/dts/msm8974-camera-sensor-liquid.dtsi
+++ b/arch/arm/boot/dts/msm8974-camera-sensor-liquid.dtsi
@@ -1,6 +1,6 @@
/*
- * 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
@@ -17,8 +17,9 @@
qcom,camera@6e {
compatible = "qcom,s5k3l1yx";
reg = <0x6e 0x0>;
- qcom,csi-if = <1>;
- qcom,csid-core = <0>;
+ qcom,slave-id = <0x6e 0x0 0x3121>;
+ qcom,csiphy-sd-index = <0>;
+ qcom,csid-sd-index = <0>;
qcom,flash-src-index = <&led_flash0>;
qcom,mount-angle = <0>;
qcom,sensor-name = "s5k3l1yx";
@@ -26,46 +27,42 @@
cam_vana-supply = <&pm8941_l17>;
cam_vio-supply = <&pm8941_lvs3>;
cam_vaf-supply = <&pm8941_l23>;
- qcom,cam-vreg-name = "cam_vdig", "cam_vana", "cam_vio",
+ qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana",
"cam_vaf";
- qcom,cam-vreg-type = <0 0 1 0>;
- qcom,cam-vreg-min-voltage = <1225000 2850000 0 3000000>;
- qcom,cam-vreg-max-voltage = <1225000 2850000 0 3000000>;
- qcom,cam-vreg-op-mode = <105000 80000 0 100000>;
+ qcom,cam-vreg-type = <0 1 0 0>;
+ qcom,cam-vreg-min-voltage = <1225000 0 2850000 3000000>;
+ qcom,cam-vreg-max-voltage = <1225000 0 2850000 3000000>;
+ qcom,cam-vreg-op-mode = <105000 0 80000 100000>;
qcom,gpio-no-mux = <0>;
gpios = <&msmgpio 15 0>,
- <&msmgpio 19 0>,
- <&msmgpio 20 0>,
<&msmgpio 90 0>;
- qcom,gpio-common-tbl-num = <0 1 2>;
- qcom,gpio-common-tbl-flags = <1 1 1>;
- qcom,gpio-common-tbl-label = "CAMIF_MCLK",
- "CAMIF_I2C_DATA",
- "CAMIF_I2C_CLK";
- qcom,gpio-req-tbl-num = <3>;
- qcom,gpio-req-tbl-flags = <0>;
- qcom,gpio-req-tbl-label = "CAM_RESET1";
- qcom,gpio-set-tbl-num = <3 3>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-req-tbl-num = <0 1>;
+ qcom,gpio-req-tbl-flags = <1 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK",
+ "CAM_RESET1";
+ qcom,gpio-set-tbl-num = <1 1>;
qcom,gpio-set-tbl-flags = <0 2>;
qcom,gpio-set-tbl-delay = <1000 30000>;
qcom,csi-lane-assign = <0x4320>;
qcom,csi-lane-mask = <0x1F>;
- qcom,csi-phy-sel = <0>;
- qcom,camera-type = <0>;
- qcom,sensor-type = <0>;
+ qcom,sensor-position = <0>;
+ qcom,sensor-mode = <1>;
status = "ok";
};
qcom,camera@6c {
compatible = "qcom,ov2720";
reg = <0x6c 0x0>;
- qcom,csi-if = <1>;
- qcom,csid-core = <0>;
+ qcom,slave-id = <0x6c 0x300A 0x2720>;
+ qcom,led-flash-sd-index = <0>;
+ qcom,csiphy-sd-index = <2>;
+ qcom,csid-sd-index = <0>;
qcom,mount-angle = <180>;
qcom,sensor-name = "ov2720";
cam_vdig-supply = <&pm8941_l3>;
cam_vana-supply = <&pm8941_l17>;
- cam_vio-supply = <&pm8941_lvs2>;
+ cam_vio-supply = <&pm8941_lvs3>;
qcom,cam-vreg-name = "cam_vdig", "cam_vana", "cam_vio";
qcom,cam-vreg-type = <0 0 1>;
qcom,cam-vreg-min-voltage = <1225000 2850000 0>;
@@ -73,25 +70,19 @@
qcom,cam-vreg-op-mode = <105000 80000 0>;
qcom,gpio-no-mux = <0>;
gpios = <&msmgpio 17 0>,
- <&msmgpio 19 0>,
- <&msmgpio 20 0>,
<&msmgpio 18 0>;
- qcom,gpio-common-tbl-num = <0 1 2>;
- qcom,gpio-common-tbl-flags = <1 1 1>;
- qcom,gpio-common-tbl-label = "CAMIF_MCLK",
- "CAMIF_I2C_DATA",
- "CAMIF_I2C_CLK";
- qcom,gpio-req-tbl-num = <3>;
- qcom,gpio-req-tbl-flags = <0>;
- qcom,gpio-req-tbl-label = "CAM_RESET1";
- qcom,gpio-set-tbl-num = <3 3>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-req-tbl-num = <0 1>;
+ qcom,gpio-req-tbl-flags = <1 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK",
+ "CAM_RESET1";
+ qcom,gpio-set-tbl-num = <1 1>;
qcom,gpio-set-tbl-flags = <0 2>;
qcom,gpio-set-tbl-delay = <1000 4000>;
qcom,csi-lane-assign = <0x4320>;
qcom,csi-lane-mask = <0x7>;
- qcom,csi-phy-sel = <2>;
- qcom,camera-type = <1>;
- qcom,sensor-type = <0>;
+ qcom,sensor-position = <1>;
+ qcom,sensor-mode = <1>;
status = "ok";
};
};
diff --git a/arch/arm/boot/dts/msm8974-camera-sensor.dtsi b/arch/arm/boot/dts/msm8974-camera-sensor.dtsi
index 68da844..948cdf5 100644
--- a/arch/arm/boot/dts/msm8974-camera-sensor.dtsi
+++ b/arch/arm/boot/dts/msm8974-camera-sensor.dtsi
@@ -1,5 +1,5 @@
/*
- * 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
@@ -16,8 +16,9 @@
qcom,camera@6e {
compatible = "qcom,s5k3l1yx";
reg = <0x6e 0x0>;
- qcom,csi-if = <1>;
- qcom,csid-core = <0>;
+ qcom,slave-id = <0x6e 0x0 0x3121>;
+ qcom,csiphy-sd-index = <0>;
+ qcom,csid-sd-index = <0>;
qcom,flash-src-index = <&led_flash0>;
qcom,mount-angle = <90>;
qcom,sensor-name = "s5k3l1yx";
@@ -25,41 +26,37 @@
cam_vana-supply = <&pm8941_l17>;
cam_vio-supply = <&pm8941_lvs3>;
cam_vaf-supply = <&pm8941_l23>;
- qcom,cam-vreg-name = "cam_vdig", "cam_vana", "cam_vio",
+ qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana",
"cam_vaf";
- qcom,cam-vreg-type = <0 0 1 0>;
- qcom,cam-vreg-min-voltage = <1225000 2850000 0 3000000>;
- qcom,cam-vreg-max-voltage = <1225000 2850000 0 3000000>;
- qcom,cam-vreg-op-mode = <105000 80000 0 100000>;
+ qcom,cam-vreg-type = <0 1 0 0>;
+ qcom,cam-vreg-min-voltage = <1225000 0 2850000 3000000>;
+ qcom,cam-vreg-max-voltage = <1225000 0 2850000 3000000>;
+ qcom,cam-vreg-op-mode = <105000 0 80000 100000>;
qcom,gpio-no-mux = <0>;
gpios = <&msmgpio 15 0>,
- <&msmgpio 19 0>,
- <&msmgpio 20 0>,
<&msmgpio 90 0>;
- qcom,gpio-common-tbl-num = <0 1 2>;
- qcom,gpio-common-tbl-flags = <1 1 1>;
- qcom,gpio-common-tbl-label = "CAMIF_MCLK",
- "CAMIF_I2C_DATA",
- "CAMIF_I2C_CLK";
- qcom,gpio-req-tbl-num = <3>;
- qcom,gpio-req-tbl-flags = <0>;
- qcom,gpio-req-tbl-label = "CAM_RESET1";
- qcom,gpio-set-tbl-num = <3 3>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-req-tbl-num = <0 1>;
+ qcom,gpio-req-tbl-flags = <1 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK",
+ "CAM_RESET1";
+ qcom,gpio-set-tbl-num = <1 1>;
qcom,gpio-set-tbl-flags = <0 2>;
qcom,gpio-set-tbl-delay = <1000 30000>;
qcom,csi-lane-assign = <0x4320>;
qcom,csi-lane-mask = <0x1F>;
- qcom,csi-phy-sel = <0>;
- qcom,camera-type = <0>;
- qcom,sensor-type = <0>;
+ qcom,sensor-position = <0>;
+ qcom,sensor-mode = <1>;
status = "ok";
};
qcom,camera@6c {
compatible = "qcom,ov2720";
reg = <0x6c 0x0>;
- qcom,csi-if = <1>;
- qcom,csid-core = <0>;
+ qcom,slave-id = <0x6c 0x300A 0x2720>;
+ qcom,led-flash-sd-index = <0>;
+ qcom,csiphy-sd-index = <2>;
+ qcom,csid-sd-index = <0>;
qcom,mount-angle = <180>;
qcom,sensor-name = "ov2720";
cam_vdig-supply = <&pm8941_l3>;
@@ -72,25 +69,19 @@
qcom,cam-vreg-op-mode = <105000 80000 0>;
qcom,gpio-no-mux = <0>;
gpios = <&msmgpio 17 0>,
- <&msmgpio 19 0>,
- <&msmgpio 20 0>,
<&msmgpio 18 0>;
- qcom,gpio-common-tbl-num = <0 1 2>;
- qcom,gpio-common-tbl-flags = <1 1 1>;
- qcom,gpio-common-tbl-label = "CAMIF_MCLK",
- "CAMIF_I2C_DATA",
- "CAMIF_I2C_CLK";
- qcom,gpio-req-tbl-num = <3>;
- qcom,gpio-req-tbl-flags = <0>;
- qcom,gpio-req-tbl-label = "CAM_RESET1";
- qcom,gpio-set-tbl-num = <3 3>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-req-tbl-num = <0 1>;
+ qcom,gpio-req-tbl-flags = <1 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK",
+ "CAM_RESET1";
+ qcom,gpio-set-tbl-num = <1 1>;
qcom,gpio-set-tbl-flags = <0 2>;
qcom,gpio-set-tbl-delay = <1000 4000>;
qcom,csi-lane-assign = <0x4320>;
qcom,csi-lane-mask = <0x7>;
- qcom,csi-phy-sel = <2>;
- qcom,camera-type = <1>;
- qcom,sensor-type = <0>;
+ qcom,sensor-position = <1>;
+ qcom,sensor-mode = <1>;
status = "ok";
};
};
diff --git a/arch/arm/boot/dts/msm8974-camera.dtsi b/arch/arm/boot/dts/msm8974-camera.dtsi
index 48dd4dc..6002f85 100644
--- a/arch/arm/boot/dts/msm8974-camera.dtsi
+++ b/arch/arm/boot/dts/msm8974-camera.dtsi
@@ -1,5 +1,5 @@
/*
- * 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,10 +14,10 @@
/include/ "skeleton.dtsi"
/ {
- qcom,cam_server {
- compatible = "qcom,cam_server";
+ qcom,msm-cam@fd8C0000 {
+ compatible = "qcom,msm-cam";
reg = <0xfd8C0000 0x10000>;
- reg-names = "server";
+ reg-names = "msm-cam";
};
qcom,csiphy@fda0ac00 {
@@ -90,7 +90,7 @@
qcom,ispif@fda0A000 {
cell-index = <0>;
compatible = "qcom,ispif";
- reg = <0xfda0A000 0x300>;
+ reg = <0xfda0A000 0x500>;
reg-names = "ispif";
interrupts = <0 55 0>;
interrupt-names = "ispif";
@@ -182,6 +182,26 @@
reg-names = "cci";
interrupts = <0 50 0>;
interrupt-names = "cci";
+ gpios = <&msmgpio 19 0>,
+ <&msmgpio 20 0>,
+ <&msmgpio 21 0>,
+ <&msmgpio 22 0>;
+ qcom,gpio-tbl-num = <0 1 2 3>;
+ qcom,gpio-tbl-flags = <1 1 1 1>;
+ qcom,gpio-tbl-label = "CCI_I2C_DATA0",
+ "CCI_I2C_CLK0",
+ "CCI_I2C_DATA1",
+ "CCI_I2C_CLK1";
+ qcom,hw-thigh = <78>;
+ qcom,hw-tlow = <114>;
+ qcom,hw-tsu-sto = <28>;
+ qcom,hw-tsu-sta = <28>;
+ qcom,hw-thd-dat = <10>;
+ qcom,hw-thd-sta = <77>;
+ qcom,hw-tbuf = <118>;
+ qcom,hw-scl-stretch-en = <0>;
+ qcom,hw-trdhld = <6>;
+ qcom,hw-tsp = <1>;
qcom,camera@6e {
status = "disable";
@@ -194,8 +214,9 @@
qcom,camera@90 {
compatible = "qcom,mt9m114";
reg = <0x90 0x0>;
- qcom,csi-if = <1>;
- qcom,csid-core = <0>;
+ qcom,slave-id = <0x90 0x0 0x2481>;
+ qcom,csiphy-sd-index = <1>;
+ qcom,csid-sd-index = <0>;
qcom,mount-angle = <0>;
qcom,sensor-name = "mt9m114";
cam_vdig-supply = <&pm8941_l3>;
@@ -209,20 +230,18 @@
qcom,gpio-no-mux = <0>;
gpios = <&msmgpio 16 0>,
<&msmgpio 92 0>;
- qcom,gpio-common-tbl-num = <0>;
- qcom,gpio-common-tbl-flags = <1>;
- qcom,gpio-common-tbl-label = "CAMIF_MCLK";
- qcom,gpio-req-tbl-num = <1>;
- qcom,gpio-req-tbl-flags = <0>;
- qcom,gpio-req-tbl-label = "CAM_RESET1";
+ qcom,gpio-reset = <1>;
+ qcom,gpio-req-tbl-num = <0 1>;
+ qcom,gpio-req-tbl-flags = <1 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK",
+ "CAM_RESET1";
qcom,gpio-set-tbl-num = <1 1>;
qcom,gpio-set-tbl-flags = <0 2>;
qcom,gpio-set-tbl-delay = <1000 4000>;
qcom,csi-lane-assign = <0x4320>;
qcom,csi-lane-mask = <0x3>;
- qcom,csi-phy-sel = <1>;
- qcom,camera-type = <1>;
- qcom,sensor-type = <1>;
+ qcom,sensor-position = <1>;
+ qcom,sensor-mode = <1>;
};
};
};
diff --git a/arch/arm/boot/dts/msm8974-cdp.dtsi b/arch/arm/boot/dts/msm8974-cdp.dtsi
index 5e65ca4..06bab30 100644
--- a/arch/arm/boot/dts/msm8974-cdp.dtsi
+++ b/arch/arm/boot/dts/msm8974-cdp.dtsi
@@ -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
@@ -180,6 +180,15 @@
qcom,model = "msm8974-taiko-cdp-snd-card";
qcom,hdmi-audio-rx;
};
+
+ usb2_otg_sw: regulator-tpd4s214 {
+ compatible = "regulator-fixed";
+ regulator-name = "usb2_otg_sw";
+ gpio = <&pm8941_gpios 18 0>;
+ parent-supply = <&pm8941_boost>;
+ startup-delay-us = <17000>;
+ enable-active-high;
+ };
};
&spmi_bus {
@@ -256,6 +265,15 @@
wp-gpios = <&pm8941_gpios 29 0x1>;
};
+&uart7 {
+ status = "ok";
+};
+
+&ehci {
+ status = "ok";
+ vbus-supply = <&usb2_otg_sw>;
+};
+
&usb3 {
qcom,otg-capability;
};
@@ -374,6 +392,14 @@
};
gpio@d100 { /* GPIO 18 */
+ /* usb2_otg_sw regulator enable */
+ qcom,mode = <1>; /* Digital output */
+ qcom,output-type = <0>; /* CMOS logic */
+ qcom,invert = <0>; /* Output low initially */
+ qcom,vin-sel = <2>; /* PM8941 S3 = 1.8 V */
+ qcom,src-sel = <0>; /* Constant */
+ qcom,out-strength = <2>; /* Medium drive strength */
+ qcom,master-en = <1>; /* Enable GPIO */
};
gpio@d200 { /* GPIO 19 */
diff --git a/arch/arm/boot/dts/msm8974-coresight.dtsi b/arch/arm/boot/dts/msm8974-coresight.dtsi
index ee3df10..6bb7b28 100644
--- a/arch/arm/boot/dts/msm8974-coresight.dtsi
+++ b/arch/arm/boot/dts/msm8974-coresight.dtsi
@@ -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
@@ -190,5 +190,7 @@
coresight-id = <14>;
coresight-name = "coresight-csr";
coresight-nr-inports = <0>;
+
+ qcom,blk-size = <3>;
};
};
diff --git a/arch/arm/boot/dts/msm8974-fluid.dtsi b/arch/arm/boot/dts/msm8974-fluid.dtsi
index d8e15b8..f15f572 100644
--- a/arch/arm/boot/dts/msm8974-fluid.dtsi
+++ b/arch/arm/boot/dts/msm8974-fluid.dtsi
@@ -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
@@ -195,6 +195,12 @@
};
};
+&slim_msm {
+ taiko_codec {
+ qcom,cdc-micbias2-ext-cap;
+ };
+};
+
&spmi_bus {
qcom,pm8941@1 {
qcom,leds@d800 {
@@ -279,8 +285,6 @@
&pm8941_chg {
status = "ok";
- qcom,chg-charging-disabled;
-
qcom,chg-chgr@1000 {
status = "ok";
};
diff --git a/arch/arm/boot/dts/msm8974-liquid.dtsi b/arch/arm/boot/dts/msm8974-liquid.dtsi
index 98ed0bf..f0f79b0 100644
--- a/arch/arm/boot/dts/msm8974-liquid.dtsi
+++ b/arch/arm/boot/dts/msm8974-liquid.dtsi
@@ -26,6 +26,8 @@
battery@b {
compatible = "ti,bq28400-battery";
reg = <0xb>;
+ ti,temp-cold = <2>; /* degree celsius */
+ ti,temp-hot = <43>; /* degree celsius */
};
charger@2b {
@@ -563,6 +565,12 @@
};
};
+&slim_msm {
+ taiko_codec {
+ qcom,cdc-micbias2-ext-cap;
+ };
+};
+
&spi_epm {
epm-adc@0 {
compatible = "cy,epm-adc-cy8c5568lti-114";
diff --git a/arch/arm/boot/dts/msm8974-mtp.dtsi b/arch/arm/boot/dts/msm8974-mtp.dtsi
index c295353..40eaf2d 100644
--- a/arch/arm/boot/dts/msm8974-mtp.dtsi
+++ b/arch/arm/boot/dts/msm8974-mtp.dtsi
@@ -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
@@ -257,6 +257,10 @@
qcom,hsusb-otg-otg-control = <2>;
};
+&uart7 {
+ status = "ok";
+};
+
&usb3 {
qcom,otg-capability;
};
@@ -268,8 +272,6 @@
&pm8941_chg {
status = "ok";
- qcom,chg-charging-disabled;
-
qcom,chg-chgr@1000 {
status = "ok";
};
diff --git a/arch/arm/boot/dts/msm8974-pm.dtsi b/arch/arm/boot/dts/msm8974-pm.dtsi
index b8a977b..ab105a9 100644
--- a/arch/arm/boot/dts/msm8974-pm.dtsi
+++ b/arch/arm/boot/dts/msm8974-pm.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. 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
@@ -179,15 +179,17 @@
#address-cells = <1>;
#size-cells = <0>;
+ qcom,use-qtimer;
+
qcom,lpm-level@0 {
reg = <0x0>;
qcom,mode = <0>; /* MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT */
qcom,xo = <1>; /* ON */
- qcom,l2 = <3>; /* ACTIVE */
- qcom,vdd-mem-upper-bound = <1150000>; /* MAX */
- qcom,vdd-mem-lower-bound = <1050000>; /* ACTIVE */
- qcom,vdd-dig-upper-bound = <5>; /* MAX */
- qcom,vdd-dig-lower-bound = <3>; /* ACTIVE */
+ qcom,l2 = <2>; /* Retention */
+ qcom,vdd-mem-upper-bound = <1050000>; /* SUPER TURBO */
+ qcom,vdd-mem-lower-bound = <950000>; /* NORMAL */
+ qcom,vdd-dig-upper-bound = <6>; /* SUPER TURBO */
+ qcom,vdd-dig-lower-bound = <4>; /* NORMAL */
qcom,latency-us = <1>;
qcom,ss-power = <784>;
qcom,energy-overhead = <190000>;
@@ -198,27 +200,26 @@
reg = <0x1>;
qcom,mode = <4>; /* MSM_PM_SLEEP_MODE_RETENTION*/
qcom,xo = <1>; /* ON */
- qcom,l2 = <3>; /* ACTIVE */
- qcom,vdd-mem-upper-bound = <1150000>; /* MAX */
- qcom,vdd-mem-lower-bound = <1050000>; /* ACTIVE */
- qcom,vdd-dig-upper-bound = <5>; /* MAX */
- qcom,vdd-dig-lower-bound = <3>; /* ACTIVE */
+ qcom,l2 = <2>; /* Retention */
+ qcom,vdd-mem-upper-bound = <1050000>; /* SUPER TURBO */
+ qcom,vdd-mem-lower-bound = <950000>; /* NORMAL */
+ qcom,vdd-dig-upper-bound = <6>; /* SUPER TURBO */
+ qcom,vdd-dig-lower-bound = <4>; /* NORMAL */
qcom,latency-us = <75>;
qcom,ss-power = <735>;
qcom,energy-overhead = <77341>;
qcom,time-overhead = <105>;
};
-
qcom,lpm-level@2 {
reg = <0x2>;
qcom,mode = <2>; /* MSM_PM_SLEEP_MODE_STANDALONE_POWER_COLLAPSE */
qcom,xo = <1>; /* ON */
- qcom,l2 = <3>; /* ACTIVE */
- qcom,vdd-mem-upper-bound = <1150000>; /* MAX */
- qcom,vdd-mem-lower-bound = <1050000>; /* ACTIVE */
- qcom,vdd-dig-upper-bound = <5>; /* MAX */
- qcom,vdd-dig-lower-bound = <3>; /* ACTIVE */
+ qcom,l2 = <2>; /* Retention */
+ qcom,vdd-mem-upper-bound = <1050000>; /* SUPER TURBO */
+ qcom,vdd-mem-lower-bound = <950000>; /* NORMAL */
+ qcom,vdd-dig-upper-bound = <6>; /* SUPER TURBO */
+ qcom,vdd-dig-lower-bound = <4>; /* NORMAL */
qcom,latency-us = <95>;
qcom,ss-power = <725>;
qcom,energy-overhead = <99500>;
@@ -230,10 +231,10 @@
qcom,mode = <3>; /* MSM_PM_SLEEP_MODE_POWER_COLLAPSE */
qcom,xo = <1>; /* ON */
qcom,l2 = <1>; /* GDHS */
- qcom,vdd-mem-upper-bound = <1150000>; /* MAX */
- qcom,vdd-mem-lower-bound = <1050000>; /* ACTIVE */
- qcom,vdd-dig-upper-bound = <5>; /* MAX */
- qcom,vdd-dig-lower-bound = <3>; /* ACTIVE */
+ qcom,vdd-mem-upper-bound = <1050000>; /* SUPER TURBO */
+ qcom,vdd-mem-lower-bound = <950000>; /* NORMAL */
+ qcom,vdd-dig-upper-bound = <6>; /* SUPER TURBO */
+ qcom,vdd-dig-lower-bound = <4>; /* NORMAL */
qcom,latency-us = <2000>;
qcom,ss-power = <138>;
qcom,energy-overhead = <1208400>;
@@ -244,11 +245,11 @@
reg = <0x4>;
qcom,mode = <3>; /* MSM_PM_SLEEP_MODE_POWER_COLLAPSE */
qcom,xo = <1>; /* ON */
- qcom,l2 = <0>; /* OFF */
- qcom,vdd-mem-upper-bound = <1050000>; /* ACTIVE */
- qcom,vdd-mem-lower-bound = <750000>; /* RETENTION HIGH */
- qcom,vdd-dig-upper-bound = <3>; /* ACTIVE */
- qcom,vdd-dig-lower-bound = <2>; /* RETENTION HIGH */
+ qcom,l2 = <1>; /* GDHS */
+ qcom,vdd-mem-upper-bound = <1050000>; /* SUPER TURBO */
+ qcom,vdd-mem-lower-bound = <950000>; /* SVS SOC */
+ qcom,vdd-dig-upper-bound = <6>; /* SUPER TURBO */
+ qcom,vdd-dig-lower-bound = <3>; /* SVS SOC */
qcom,latency-us = <3000>;
qcom,ss-power = <110>;
qcom,energy-overhead = <1250300>;
@@ -260,10 +261,10 @@
qcom,mode = <3>; /* MSM_PM_SLEEP_MODE_POWER_COLLAPSE */
qcom,xo = <0>; /* OFF */
qcom,l2 = <1>; /* GDHS */
- qcom,vdd-mem-upper-bound = <1150000>; /* MAX */
- qcom,vdd-mem-lower-bound = <1050000>; /* ACTIVE */
- qcom,vdd-dig-upper-bound = <5>; /* MAX */
- qcom,vdd-dig-lower-bound = <3>; /* ACTIVE */
+ qcom,vdd-mem-upper-bound = <1050000>; /* SUPER TURBO */
+ qcom,vdd-mem-lower-bound = <950000>; /* NORMAL */
+ qcom,vdd-dig-upper-bound = <6>; /* SUPER TURBO */
+ qcom,vdd-dig-lower-bound = <4>; /* NORMAL */
qcom,latency-us = <3000>;
qcom,ss-power = <68>;
qcom,energy-overhead = <1350200>;
@@ -272,17 +273,17 @@
qcom,lpm-level@6 {
reg = <0x6>;
- qcom,mode = <3>; /* MSM_PM_SLEEP_MODE_POWER_COLLAPSE */
+ qcom,mode= <3>; /* MSM_PM_SLEEP_MODE_POWER_COLLAPSE */
qcom,xo = <0>; /* OFF */
- qcom,l2 = <0>; /* OFF */
- qcom,vdd-mem-upper-bound = <1150000>; /* MAX */
- qcom,vdd-mem-lower-bound = <1050000>; /* ACTIVE */
- qcom,vdd-dig-upper-bound = <5>; /* MAX */
- qcom,vdd-dig-lower-bound = <3>; /* ACTIVE */
- qcom,latency-us = <10300>;
- qcom,ss-power = <63>;
- qcom,energy-overhead = <2128000>;
- qcom,time-overhead = <18200>;
+ qcom,l2 = <1>; /* GDHS */
+ qcom,vdd-mem-upper-bound = <950000>; /* NORMAL */
+ qcom,vdd-mem-lower-bound = <950000>; /* SVS SOC */
+ qcom,vdd-dig-upper-bound = <4>; /* NORMAL */
+ qcom,vdd-dig-lower-bound = <3>; /* SVS SOC */
+ qcom,latency-us = <18000>;
+ qcom,ss-power = <10>;
+ qcom,energy-overhead = <3202600>;
+ qcom,time-overhead = <27000>;
};
qcom,lpm-level@7 {
@@ -290,25 +291,10 @@
qcom,mode= <3>; /* MSM_PM_SLEEP_MODE_POWER_COLLAPSE */
qcom,xo = <0>; /* OFF */
qcom,l2 = <0>; /* OFF */
- qcom,vdd-mem-upper-bound = <1050000>; /* ACTIVE */
- qcom,vdd-mem-lower-bound = <750000>; /* RETENTION HIGH */
- qcom,vdd-dig-upper-bound = <3>; /* ACTIVE */
- qcom,vdd-dig-lower-bound = <2>; /* RETIONTION HIGH */
- qcom,latency-us = <18000>;
- qcom,ss-power = <10>;
- qcom,energy-overhead = <3202600>;
- qcom,time-overhead = <27000>;
- };
-
- qcom,lpm-level@8 {
- reg = <0x8>;
- qcom,mode= <3>; /* MSM_PM_SLEEP_MODE_POWER_COLLAPSE */
- qcom,xo = <0>; /* OFF */
- qcom,l2 = <0>; /* OFF */
- qcom,vdd-mem-upper-bound = <750000>; /* RETENTION HIGH */
- qcom,vdd-mem-lower-bound = <750000>; /* RETENTION LOW */
- qcom,vdd-dig-upper-bound = <2>; /* RETENTION HIGH */
- qcom,vdd-dig-lower-bound = <0>; /* RETENTION LOW */
+ qcom,vdd-mem-upper-bound = <950000>; /* SVS SOC */
+ qcom,vdd-mem-lower-bound = <675000>; /* RETENTION */
+ qcom,vdd-dig-upper-bound = <3>; /* SVS SOC */
+ qcom,vdd-dig-lower-bound = <1>; /* RETENTION */
qcom,latency-us = <20000>;
qcom,ss-power = <2>;
qcom,energy-overhead = <4252000>;
diff --git a/arch/arm/boot/dts/msm8974-regulator.dtsi b/arch/arm/boot/dts/msm8974-regulator.dtsi
index a7a7c88..c6c5452 100644
--- a/arch/arm/boot/dts/msm8974-regulator.dtsi
+++ b/arch/arm/boot/dts/msm8974-regulator.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. 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
@@ -110,9 +110,17 @@
pm8841_s4: regulator-s4 {
regulator-min-microvolt = <815000>;
regulator-max-microvolt = <900000>;
- qcom,init-voltage = <815000>;
status = "okay";
};
+ pm8841_s4_corner: regulator-s4-corner {
+ compatible = "qcom,rpm-regulator-smd";
+ regulator-name = "8841_s4_corner";
+ qcom,set = <3>;
+ qcom,use-voltage-corner;
+ regulator-min-microvolt = <1>;
+ regulator-max-microvolt = <7>;
+ qcom,init-voltage-corner = <3>; /* SVS SOC */
+ };
};
rpm-regulator-smpa1 {
@@ -418,49 +426,61 @@
krait0_vreg: regulator@f9088000 {
compatible = "qcom,krait-regulator";
regulator-name = "krait0";
- reg = <0xf9088000 0x1000>;
+ reg = <0xf9088000 0x1000>, /* APCS_ALIAS0_KPSS_ACS */
+ <0xf908a800 0x1000>; /* APCS_ALIAS0_KPSS_MDD */
+ reg-names = "acs", "mdd";
regulator-min-microvolt = <500000>;
regulator-max-microvolt = <1100000>;
qcom,headroom-voltage = <150000>;
- qcom,retention-voltage = <745000>;
- qcom,ldo-default-voltage = <745000>;
- qcom,ldo-threshold-voltage = <750000>;
+ qcom,retention-voltage = <675000>;
+ qcom,ldo-default-voltage = <750000>;
+ qcom,ldo-threshold-voltage = <850000>;
+ qcom,ldo-delta-voltage = <50000>;
};
krait1_vreg: regulator@f9098000 {
compatible = "qcom,krait-regulator";
regulator-name = "krait1";
- reg = <0xf9098000 0x1000>;
+ reg = <0xf9098000 0x1000>, /* APCS_ALIAS1_KPSS_ACS */
+ <0xf909a800 0x1000>; /* APCS_ALIAS1_KPSS_MDD */
+ reg-names = "acs", "mdd";
regulator-min-microvolt = <500000>;
regulator-max-microvolt = <1100000>;
qcom,headroom-voltage = <150000>;
- qcom,retention-voltage = <745000>;
- qcom,ldo-default-voltage = <745000>;
- qcom,ldo-threshold-voltage = <750000>;
+ qcom,retention-voltage = <675000>;
+ qcom,ldo-default-voltage = <750000>;
+ qcom,ldo-threshold-voltage = <850000>;
+ qcom,ldo-delta-voltage = <50000>;
};
krait2_vreg: regulator@f90a8000 {
compatible = "qcom,krait-regulator";
regulator-name = "krait2";
- reg = <0xf90a8000 0x1000>;
+ reg = <0xf90a8000 0x1000>, /* APCS_ALIAS2_KPSS_ACS */
+ <0xf90aa800 0x1000>; /* APCS_ALIAS2_KPSS_MDD */
+ reg-names = "acs", "mdd";
regulator-min-microvolt = <500000>;
regulator-max-microvolt = <1100000>;
qcom,headroom-voltage = <150000>;
- qcom,retention-voltage = <745000>;
- qcom,ldo-default-voltage = <745000>;
- qcom,ldo-threshold-voltage = <750000>;
+ qcom,retention-voltage = <675000>;
+ qcom,ldo-default-voltage = <750000>;
+ qcom,ldo-threshold-voltage = <850000>;
+ qcom,ldo-delta-voltage = <50000>;
};
krait3_vreg: regulator@f90b8000 {
compatible = "qcom,krait-regulator";
regulator-name = "krait3";
- reg = <0xf90b8000 0x1000>;
+ reg = <0xf90b8000 0x1000>, /* APCS_ALIAS3_KPSS_ACS */
+ <0xf90ba800 0x1000>; /* APCS_ALIAS3_KPSS_MDD */
+ reg-names = "acs", "mdd";
regulator-min-microvolt = <500000>;
regulator-max-microvolt = <1100000>;
qcom,headroom-voltage = <150000>;
- qcom,retention-voltage = <745000>;
- qcom,ldo-default-voltage = <745000>;
- qcom,ldo-threshold-voltage = <750000>;
+ qcom,retention-voltage = <675000>;
+ qcom,ldo-default-voltage = <750000>;
+ qcom,ldo-threshold-voltage = <850000>;
+ qcom,ldo-delta-voltage = <50000>;
};
spi_eth_vreg: spi_eth_phy_vreg {
diff --git a/arch/arm/boot/dts/msm8974-rumi.dtsi b/arch/arm/boot/dts/msm8974-rumi.dtsi
index 4919391..38e552e 100644
--- a/arch/arm/boot/dts/msm8974-rumi.dtsi
+++ b/arch/arm/boot/dts/msm8974-rumi.dtsi
@@ -75,7 +75,7 @@
interrupts = <0 105 0>;
interrupt-names = "qup_err_intr";
qcom,i2c-bus-freq = <100000>;
- qcom,i2c-src-freq = <24000000>;
+ qcom,i2c-src-freq = <19200000>;
gpios = <&msmgpio 83 0>, /* DAT */
<&msmgpio 84 0>; /* CLK */
};
diff --git a/arch/arm/boot/dts/msm8974-smp2p.dtsi b/arch/arm/boot/dts/msm8974-smp2p.dtsi
new file mode 100644
index 0000000..60f63a8
--- /dev/null
+++ b/arch/arm/boot/dts/msm8974-smp2p.dtsi
@@ -0,0 +1,194 @@
+/* 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,smp2p-modem {
+ compatible = "qcom,smp2p";
+ reg = <0xfa006000 0x1000>, <0x8 0x0>;
+ reg-names = "irq-reg-base", "irq-reg-offset";
+ qcom,remote-pid = <1>;
+ qcom,irq-bitmask = <0x4000>;
+ interrupts = <0 27 1>;
+ };
+
+ qcom,smp2p-adsp {
+ compatible = "qcom,smp2p";
+ reg = <0xfa006000 0x1000>, <0x8 0x0>;
+ reg-names = "irq-reg-base", "irq-reg-offset";
+ qcom,remote-pid = <2>;
+ qcom,irq-bitmask = <0x400>;
+ interrupts = <0 158 1>;
+ };
+
+ qcom,smp2p-wcnss {
+ compatible = "qcom,smp2p";
+ reg = <0xfa006000 0x1000>, <0x8 0x0>;
+ reg-names = "irq-reg-base", "irq-reg-offset";
+ qcom,remote-pid = <4>;
+ qcom,irq-bitmask = <0x40000>;
+ interrupts = <0 143 1>;
+ };
+
+ /* SMP2P Test Driver for inbound entries */
+ smp2pgpio_smp2p_7_in: qcom,smp2pgpio-smp2p-7-in {
+ compatible = "qcom,smp2pgpio";
+ qcom,entry-name = "smp2p";
+ qcom,remote-pid = <7>;
+ qcom,is-inbound;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ qcom,smp2pgpio_test_smp2p_7_in {
+ compatible = "qcom,smp2pgpio_test_smp2p_7_in";
+ gpios = <&smp2pgpio_smp2p_7_in 0 0>;
+ };
+
+ /* SMP2P Test Driver for outbound entries */
+ smp2pgpio_smp2p_7_out: qcom,smp2pgpio-smp2p-7-out {
+ compatible = "qcom,smp2pgpio";
+ qcom,entry-name = "smp2p";
+ qcom,remote-pid = <7>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ qcom,smp2pgpio_test_smp2p_7_out {
+ compatible = "qcom,smp2pgpio_test_smp2p_7_out";
+ gpios = <&smp2pgpio_smp2p_7_out 0 0>;
+ };
+
+ /* SMP2P Test Driver for modem inbound */
+ smp2pgpio_smp2p_1_in: qcom,smp2pgpio-smp2p-1-in {
+ compatible = "qcom,smp2pgpio";
+ qcom,entry-name = "smp2p";
+ qcom,remote-pid = <1>;
+ qcom,is-inbound;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ qcom,smp2pgpio_test_smp2p_1_in {
+ compatible = "qcom,smp2pgpio_test_smp2p_1_in";
+ gpios = <&smp2pgpio_smp2p_1_in 0 0>;
+ };
+
+ /* SMP2P Test Driver for modem output */
+ smp2pgpio_smp2p_1_out: qcom,smp2pgpio-smp2p-1-out {
+ compatible = "qcom,smp2pgpio";
+ qcom,entry-name = "smp2p";
+ qcom,remote-pid = <1>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ qcom,smp2pgpio_test_smp2p_1_out {
+ compatible = "qcom,smp2pgpio_test_smp2p_1_out";
+ gpios = <&smp2pgpio_smp2p_1_out 0 0>;
+ };
+
+ /* SMP2P SSR Driver for inbound entry from modem. */
+ smp2pgpio_ssr_smp2p_1_in: qcom,smp2pgpio-ssr-smp2p-1-in {
+ compatible = "qcom,smp2pgpio";
+ qcom,entry-name = "slave-kernel";
+ qcom,remote-pid = <1>;
+ qcom,is-inbound;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ /* SMP2P SSR Driver for outbound entry to modem */
+ smp2pgpio_ssr_smp2p_1_out: qcom,smp2pgpio-ssr-smp2p-1-out {
+ compatible = "qcom,smp2pgpio";
+ qcom,entry-name = "master-kernel";
+ qcom,remote-pid = <1>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ /* SMP2P Test Driver for adsp inbound */
+ smp2pgpio_smp2p_2_in: qcom,smp2pgpio-smp2p-2-in {
+ compatible = "qcom,smp2pgpio";
+ qcom,entry-name = "smp2p";
+ qcom,remote-pid = <2>;
+ qcom,is-inbound;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ qcom,smp2pgpio_test_smp2p_2_in {
+ compatible = "qcom,smp2pgpio_test_smp2p_2_in";
+ gpios = <&smp2pgpio_smp2p_2_in 0 0>;
+ };
+
+ /* SMP2P Test Driver for adsp output */
+ smp2pgpio_smp2p_2_out: qcom,smp2pgpio-smp2p-2-out {
+ compatible = "qcom,smp2pgpio";
+ qcom,entry-name = "smp2p";
+ qcom,remote-pid = <2>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ qcom,smp2pgpio_test_smp2p_2_out {
+ compatible = "qcom,smp2pgpio_test_smp2p_2_out";
+ gpios = <&smp2pgpio_smp2p_2_out 0 0>;
+ };
+
+ /* SMP2P Test Driver for wcnss inbound */
+ smp2pgpio_smp2p_4_in: qcom,smp2pgpio-smp2p-4-in {
+ compatible = "qcom,smp2pgpio";
+ qcom,entry-name = "smp2p";
+ qcom,remote-pid = <4>;
+ qcom,is-inbound;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ qcom,smp2pgpio_test_smp2p_4_in {
+ compatible = "qcom,smp2pgpio_test_smp2p_4_in";
+ gpios = <&smp2pgpio_smp2p_4_in 0 0>;
+ };
+
+ /* SMP2P Test Driver for wcnss output */
+ smp2pgpio_smp2p_4_out: qcom,smp2pgpio-smp2p-4-out {
+ compatible = "qcom,smp2pgpio";
+ qcom,entry-name = "smp2p";
+ qcom,remote-pid = <4>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ qcom,smp2pgpio_test_smp2p_4_out {
+ compatible = "qcom,smp2pgpio_test_smp2p_4_out";
+ gpios = <&smp2pgpio_smp2p_4_out 0 0>;
+ };
+};
diff --git a/arch/arm/boot/dts/msm8974-cdp.dts b/arch/arm/boot/dts/msm8974-v1-cdp.dts
similarity index 86%
rename from arch/arm/boot/dts/msm8974-cdp.dts
rename to arch/arm/boot/dts/msm8974-v1-cdp.dts
index b8b3141..15ff424 100644
--- a/arch/arm/boot/dts/msm8974-cdp.dts
+++ b/arch/arm/boot/dts/msm8974-v1-cdp.dts
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. 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
@@ -12,7 +12,7 @@
/dts-v1/;
-/include/ "msm8974.dtsi"
+/include/ "msm8974-v1.dtsi"
/include/ "msm8974-cdp.dtsi"
/ {
diff --git a/arch/arm/boot/dts/msm8974-fluid.dts b/arch/arm/boot/dts/msm8974-v1-fluid.dts
similarity index 86%
rename from arch/arm/boot/dts/msm8974-fluid.dts
rename to arch/arm/boot/dts/msm8974-v1-fluid.dts
index b014e14..0b435a3 100644
--- a/arch/arm/boot/dts/msm8974-fluid.dts
+++ b/arch/arm/boot/dts/msm8974-v1-fluid.dts
@@ -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
@@ -12,7 +12,7 @@
/dts-v1/;
-/include/ "msm8974.dtsi"
+/include/ "msm8974-v1.dtsi"
/include/ "msm8974-fluid.dtsi"
/ {
diff --git a/arch/arm/boot/dts/msm8974-liquid.dts b/arch/arm/boot/dts/msm8974-v1-liquid.dts
similarity index 86%
rename from arch/arm/boot/dts/msm8974-liquid.dts
rename to arch/arm/boot/dts/msm8974-v1-liquid.dts
index ef38036..5c12569 100644
--- a/arch/arm/boot/dts/msm8974-liquid.dts
+++ b/arch/arm/boot/dts/msm8974-v1-liquid.dts
@@ -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
@@ -12,7 +12,7 @@
/dts-v1/;
-/include/ "msm8974.dtsi"
+/include/ "msm8974-v1.dtsi"
/include/ "msm8974-liquid.dtsi"
/ {
diff --git a/arch/arm/boot/dts/msm8974-mtp.dts b/arch/arm/boot/dts/msm8974-v1-mtp.dts
similarity index 86%
rename from arch/arm/boot/dts/msm8974-mtp.dts
rename to arch/arm/boot/dts/msm8974-v1-mtp.dts
index 9946cf0..01e9fe2 100644
--- a/arch/arm/boot/dts/msm8974-mtp.dts
+++ b/arch/arm/boot/dts/msm8974-v1-mtp.dts
@@ -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
@@ -12,7 +12,7 @@
/dts-v1/;
-/include/ "msm8974.dtsi"
+/include/ "msm8974-v1.dtsi"
/include/ "msm8974-mtp.dtsi"
/ {
diff --git a/arch/arm/boot/dts/msm8974-rumi.dts b/arch/arm/boot/dts/msm8974-v1-rumi.dts
similarity index 86%
rename from arch/arm/boot/dts/msm8974-rumi.dts
rename to arch/arm/boot/dts/msm8974-v1-rumi.dts
index 738ff86..ebb37b7 100644
--- a/arch/arm/boot/dts/msm8974-rumi.dts
+++ b/arch/arm/boot/dts/msm8974-v1-rumi.dts
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. 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
@@ -12,7 +12,7 @@
/dts-v1/;
-/include/ "msm8974.dtsi"
+/include/ "msm8974-v1.dtsi"
/include/ "msm8974-rumi.dtsi"
/ {
diff --git a/arch/arm/boot/dts/msm8974-sim.dts b/arch/arm/boot/dts/msm8974-v1-sim.dts
similarity index 86%
rename from arch/arm/boot/dts/msm8974-sim.dts
rename to arch/arm/boot/dts/msm8974-v1-sim.dts
index 09ea419..29add5d 100644
--- a/arch/arm/boot/dts/msm8974-sim.dts
+++ b/arch/arm/boot/dts/msm8974-v1-sim.dts
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. 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
@@ -12,7 +12,7 @@
/dts-v1/;
-/include/ "msm8974.dtsi"
+/include/ "msm8974-v1.dtsi"
/include/ "msm8974-sim.dtsi"
/ {
diff --git a/arch/arm/boot/dts/msm8974-mtp.dts b/arch/arm/boot/dts/msm8974-v1.dtsi
similarity index 66%
copy from arch/arm/boot/dts/msm8974-mtp.dts
copy to arch/arm/boot/dts/msm8974-v1.dtsi
index 9946cf0..6f91ee8 100644
--- a/arch/arm/boot/dts/msm8974-mtp.dts
+++ b/arch/arm/boot/dts/msm8974-v1.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* 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
@@ -10,13 +10,10 @@
* GNU General Public License for more details.
*/
-/dts-v1/;
+/*
+ * As a general rule, only version-specific property overrides should be placed
+ * inside this file. However, device definitions should be placed inside the
+ * msm8974.dtsi file.
+ */
/include/ "msm8974.dtsi"
-/include/ "msm8974-mtp.dtsi"
-
-/ {
- model = "Qualcomm MSM 8974 MTP";
- compatible = "qcom,msm8974-mtp", "qcom,msm8974";
- qcom,msm-id = <126 8 0>;
-};
diff --git a/arch/arm/boot/dts/msm8974-cdp.dts b/arch/arm/boot/dts/msm8974-v2-cdp.dts
similarity index 77%
copy from arch/arm/boot/dts/msm8974-cdp.dts
copy to arch/arm/boot/dts/msm8974-v2-cdp.dts
index b8b3141..58e172f 100644
--- a/arch/arm/boot/dts/msm8974-cdp.dts
+++ b/arch/arm/boot/dts/msm8974-v2-cdp.dts
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+/* 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
@@ -12,11 +12,11 @@
/dts-v1/;
-/include/ "msm8974.dtsi"
+/include/ "msm8974-v2.dtsi"
/include/ "msm8974-cdp.dtsi"
/ {
- model = "Qualcomm MSM 8974 CDP";
+ model = "Qualcomm MSM 8974v2 CDP";
compatible = "qcom,msm8974-cdp", "qcom,msm8974";
- qcom,msm-id = <126 1 0>;
+ qcom,msm-id = <126 1 0x20000>;
};
diff --git a/arch/arm/boot/dts/msm8974-fluid.dts b/arch/arm/boot/dts/msm8974-v2-fluid.dts
similarity index 77%
copy from arch/arm/boot/dts/msm8974-fluid.dts
copy to arch/arm/boot/dts/msm8974-v2-fluid.dts
index b014e14..5759b56 100644
--- a/arch/arm/boot/dts/msm8974-fluid.dts
+++ b/arch/arm/boot/dts/msm8974-v2-fluid.dts
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* 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
@@ -12,11 +12,11 @@
/dts-v1/;
-/include/ "msm8974.dtsi"
+/include/ "msm8974-v2.dtsi"
/include/ "msm8974-fluid.dtsi"
/ {
- model = "Qualcomm MSM 8974 FLUID";
+ model = "Qualcomm MSM 8974v2 FLUID";
compatible = "qcom,msm8974-fluid", "qcom,msm8974";
- qcom,msm-id = <126 3 0>;
+ qcom,msm-id = <126 3 0x20000>;
};
diff --git a/arch/arm/boot/dts/msm8974-liquid.dts b/arch/arm/boot/dts/msm8974-v2-liquid.dts
similarity index 77%
copy from arch/arm/boot/dts/msm8974-liquid.dts
copy to arch/arm/boot/dts/msm8974-v2-liquid.dts
index ef38036..6812f60 100644
--- a/arch/arm/boot/dts/msm8974-liquid.dts
+++ b/arch/arm/boot/dts/msm8974-v2-liquid.dts
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* 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
@@ -12,11 +12,11 @@
/dts-v1/;
-/include/ "msm8974.dtsi"
+/include/ "msm8974-v2.dtsi"
/include/ "msm8974-liquid.dtsi"
/ {
- model = "Qualcomm MSM 8974 LIQUID";
+ model = "Qualcomm MSM 8974v2 LIQUID";
compatible = "qcom,msm8974-liquid", "qcom,msm8974";
- qcom,msm-id = <126 9 0>;
+ qcom,msm-id = <126 9 0x20000>;
};
diff --git a/arch/arm/boot/dts/msm8974-mtp.dts b/arch/arm/boot/dts/msm8974-v2-mtp.dts
similarity index 77%
copy from arch/arm/boot/dts/msm8974-mtp.dts
copy to arch/arm/boot/dts/msm8974-v2-mtp.dts
index 9946cf0..b29d4ca 100644
--- a/arch/arm/boot/dts/msm8974-mtp.dts
+++ b/arch/arm/boot/dts/msm8974-v2-mtp.dts
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* 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
@@ -12,11 +12,11 @@
/dts-v1/;
-/include/ "msm8974.dtsi"
+/include/ "msm8974-v2.dtsi"
/include/ "msm8974-mtp.dtsi"
/ {
- model = "Qualcomm MSM 8974 MTP";
+ model = "Qualcomm MSM 8974v2 MTP";
compatible = "qcom,msm8974-mtp", "qcom,msm8974";
- qcom,msm-id = <126 8 0>;
+ qcom,msm-id = <126 8 0x20000>;
};
diff --git a/arch/arm/boot/dts/msm8974-mtp.dts b/arch/arm/boot/dts/msm8974-v2.dtsi
similarity index 66%
copy from arch/arm/boot/dts/msm8974-mtp.dts
copy to arch/arm/boot/dts/msm8974-v2.dtsi
index 9946cf0..6f91ee8 100644
--- a/arch/arm/boot/dts/msm8974-mtp.dts
+++ b/arch/arm/boot/dts/msm8974-v2.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* 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
@@ -10,13 +10,10 @@
* GNU General Public License for more details.
*/
-/dts-v1/;
+/*
+ * As a general rule, only version-specific property overrides should be placed
+ * inside this file. However, device definitions should be placed inside the
+ * msm8974.dtsi file.
+ */
/include/ "msm8974.dtsi"
-/include/ "msm8974-mtp.dtsi"
-
-/ {
- model = "Qualcomm MSM 8974 MTP";
- compatible = "qcom,msm8974-mtp", "qcom,msm8974";
- qcom,msm-id = <126 8 0>;
-};
diff --git a/arch/arm/boot/dts/msm8974.dtsi b/arch/arm/boot/dts/msm8974.dtsi
index 9be99af..1072e7e 100644
--- a/arch/arm/boot/dts/msm8974.dtsi
+++ b/arch/arm/boot/dts/msm8974.dtsi
@@ -19,6 +19,7 @@
/include/ "msm8974-ion.dtsi"
/include/ "msm8974-gpu.dtsi"
/include/ "msm8974-mdss.dtsi"
+/include/ "msm8974-smp2p.dtsi"
/ {
model = "Qualcomm MSM 8974";
@@ -70,6 +71,7 @@
<783360 410000000>,
<489600 266670000>,
<244800 133330000>;
+ hfi = "venus";
};
qcom,wfd {
@@ -430,6 +432,10 @@
qcom,cdc-mclk-gpios = <&pm8941_gpios 15 0>;
taiko-mclk-clk = <&pm8941_clkdiv1>;
qcom,taiko-mclk-clk-freq = <9600000>;
+ prim-auxpcm-gpio-clk = <&msmgpio 65 0>;
+ prim-auxpcm-gpio-sync = <&msmgpio 66 0>;
+ prim-auxpcm-gpio-din = <&msmgpio 67 0>;
+ prim-auxpcm-gpio-dout = <&msmgpio 68 0>;
};
spmi_bus: qcom,spmi@fc4c0000 {
@@ -590,7 +596,7 @@
interrupts = <0 105 0>;
interrupt-names = "qup_err_intr";
qcom,i2c-bus-freq = <100000>;
- qcom,i2c-src-freq = <24000000>;
+ qcom,i2c-src-freq = <19200000>;
};
i2c@f9924000 {
@@ -603,7 +609,7 @@
interrupts = <0 96 0>;
interrupt-names = "qup_err_intr";
qcom,i2c-bus-freq = <100000>;
- qcom,i2c-src-freq = <24000000>;
+ qcom,i2c-src-freq = <19200000>;
};
spi@f9923000 {
@@ -655,6 +661,7 @@
vbus_dwc3-supply = <&pm8941_mvs1>;
qcom,dwc-usb3-msm-dbm-eps = <4>;
qcom,vdd-voltage-level = <1 5 7>;
+ qcom,dwc-hsphy-init = <0x00D195A4>;
qcom,msm-bus,name = "usb3";
qcom,msm-bus,num-cases = <2>;
@@ -665,8 +672,21 @@
<61 512 240000 960000>;
};
+ ehci: qcom,ehci-host@f9a55000 {
+ compatible = "qcom,ehci-host";
+ status = "disabled";
+ reg = <0xf9a55000 0x400>;
+ interrupts = <0 134 0>, <0 140 0>;
+ interrupt-names = "core_irq", "async_irq";
+ HSUSB_VDDCX-supply = <&pm8841_s2>;
+ HSUSB_1p8-supply = <&pm8941_l6>;
+ HSUSB_3p3-supply = <&pm8941_l24>;
+ qcom,usb2-enable-hsphy2;
+ qcom,usb2-power-budget = <500>;
+ };
+
gdsc_oxili_gx: qcom,gdsc@fd8c4024 {
- parent-supply = <&pm8841_s4>;
+ parent-supply = <&pm8841_s4_corner>;
};
qcom,lpass@fe200000 {
@@ -821,13 +841,13 @@
qcom,msm-auxpcm {
compatible = "qcom,msm-auxpcm-resource";
qcom,msm-cpudai-auxpcm-clk = "pcm_clk";
- qcom,msm-cpudai-auxpcm-mode = <0>;
- qcom,msm-cpudai-auxpcm-sync = <1>;
- qcom,msm-cpudai-auxpcm-frame = <5>;
- qcom,msm-cpudai-auxpcm-quant = <2>;
- qcom,msm-cpudai-auxpcm-slot = <1>;
- qcom,msm-cpudai-auxpcm-data = <0>;
- qcom,msm-cpudai-auxpcm-pcm-clk-rate = <2048000>;
+ qcom,msm-cpudai-auxpcm-mode = <0>, <0>;
+ qcom,msm-cpudai-auxpcm-sync = <1>, <1>;
+ qcom,msm-cpudai-auxpcm-frame = <5>, <4>;
+ qcom,msm-cpudai-auxpcm-quant = <2>, <2>;
+ qcom,msm-cpudai-auxpcm-slot = <1>, <1>;
+ qcom,msm-cpudai-auxpcm-data = <0>, <0>;
+ qcom,msm-cpudai-auxpcm-pcm-clk-rate = <2048000>, <2048000>;
qcom,msm-auxpcm-rx {
qcom,msm-auxpcm-dev-id = <4106>;
@@ -890,6 +910,12 @@
qcom,is-loadable;
qcom,firmware-name = "mba";
qcom,pil-self-auth = <1>;
+
+ /* GPIO input from mss */
+ qcom,gpio-err-fatal = <&smp2pgpio_ssr_smp2p_1_in 0 0>;
+
+ /* GPIO output to mss */
+ qcom,gpio-force-stop = <&smp2pgpio_ssr_smp2p_1_out 0 0>;
};
qcom,pronto@fb21b000 {
@@ -1089,6 +1115,7 @@
qcom,usb-bam-num-pipes = <16>;
qcom,usb-base-address = <0xf9200000>;
qcom,ignore-core-reset-ack;
+ qcom,disable-clk-gating;
qcom,pipe1 {
label = "usb-to-peri-qdss-dwc3";
@@ -1172,6 +1199,20 @@
qcom,memblock-remove = <0x7f00000 0x8000000>; /* Address and Size of Hole */
};
+ uart7: uart@f995d000 { /*BLSP #2, UART #7 */
+ cell-index = <0>;
+ compatible = "qcom,msm-hsuart-v14";
+ status = "disabled";
+ reg = <0xf995d000 0x1000>,
+ <0xf9944000 0x5000>;
+ reg-names = "core_mem", "bam_mem";
+ interrupts = <0 113 0>, <0 239 0>;
+ interrupt-names = "core_irq", "bam_irq";
+
+ qcom,bam-tx-ep-pipe-index = <0>;
+ qcom,bam-rx-ep-pipe-index = <1>;
+ };
+
qcom,smem@fa00000 {
compatible = "qcom,smem";
reg = <0xfa00000 0x200000>,
diff --git a/arch/arm/boot/dts/msm9625-cdp.dts b/arch/arm/boot/dts/msm9625-cdp.dts
index 232fba7..f2a798a 100644
--- a/arch/arm/boot/dts/msm9625-cdp.dts
+++ b/arch/arm/boot/dts/msm9625-cdp.dts
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. 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
@@ -17,7 +17,7 @@
/ {
model = "Qualcomm MSM 9625 CDP";
compatible = "qcom,msm9625-cdp", "qcom,msm9625";
- qcom,msm-id = <134 1 0>, <152 1 0>;
+ qcom,msm-id = <134 1 0>, <152 1 0>, <149 1 0>, <150 1 0>;
i2c@f9925000 {
charger@57 {
diff --git a/arch/arm/boot/dts/msm9625-coresight.dtsi b/arch/arm/boot/dts/msm9625-coresight.dtsi
index f01fe63..f352c3f 100644
--- a/arch/arm/boot/dts/msm9625-coresight.dtsi
+++ b/arch/arm/boot/dts/msm9625-coresight.dtsi
@@ -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
@@ -107,12 +107,26 @@
coresight-child-ports = <7>;
};
+ etm: etm@fc332000 {
+ compatible = "arm,coresight-etm";
+ reg = <0xfc332000 0x1000>;
+
+ coresight-id = <8>;
+ coresight-name = "coresight-etm";
+ coresight-nr-inports = <0>;
+ coresight-outports = <0>;
+ coresight-child-list = <&funnel_in0>;
+ coresight-child-ports = <4>;
+ };
+
csr: csr@fc302000 {
compatible = "qcom,coresight-csr";
reg = <0xfc302000 0x1000>;
- coresight-id = <8>;
+ coresight-id = <9>;
coresight-name = "coresight-csr";
coresight-nr-inports = <0>;
+
+ qcom,blk-size = <1>;
};
};
diff --git a/arch/arm/boot/dts/msm9625-mtp.dts b/arch/arm/boot/dts/msm9625-mtp.dts
index faf86d4..584fc7f 100644
--- a/arch/arm/boot/dts/msm9625-mtp.dts
+++ b/arch/arm/boot/dts/msm9625-mtp.dts
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. 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
@@ -17,7 +17,7 @@
/ {
model = "Qualcomm MSM 9625 MTP";
compatible = "qcom,msm9625-mtp", "qcom,msm9625";
- qcom,msm-id = <134 7 0>, <152 7 0>;
+ qcom,msm-id = <134 7 0>, <152 7 0>, <149 7 0>, <150 7 0>;
i2c@f9925000 {
charger@57 {
diff --git a/arch/arm/boot/dts/msm9625-pm.dtsi b/arch/arm/boot/dts/msm9625-pm.dtsi
index 71fcfd6..5a925cb 100644
--- a/arch/arm/boot/dts/msm9625-pm.dtsi
+++ b/arch/arm/boot/dts/msm9625-pm.dtsi
@@ -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
@@ -71,6 +71,8 @@
#address-cells = <1>;
#size-cells = <0>;
+ qcom,use-qtimer;
+
qcom,lpm-level@0 {
reg = <0x0>;
qcom,mode = <0>; /* MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT */
diff --git a/arch/arm/boot/dts/msm9625.dtsi b/arch/arm/boot/dts/msm9625.dtsi
index 2fbff6d..c2ed824 100644
--- a/arch/arm/boot/dts/msm9625.dtsi
+++ b/arch/arm/boot/dts/msm9625.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. 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
@@ -81,7 +81,7 @@
vbus_otg-supply = <&usb_vbus>;
qcom,hsusb-otg-phy-type = <2>;
- qcom,hsusb-otg-mode = <3>;
+ qcom,hsusb-otg-mode = <1>;
qcom,hsusb-otg-otg-control = <1>;
qcom,hsusb-otg-disable-reset;
};
@@ -111,6 +111,7 @@
qcom,usb-total-bam-num = <3>;
qcom,usb-bam-num-pipes = <16>;
qcom,ignore-core-reset-ack;
+ qcom,disable-clk-gating;
qcom,pipe0 {
label = "usb-to-ipa";
@@ -602,6 +603,18 @@
qcom,bam-pipe-pair = <2>;
};
+ jtag_mm: jtagmm@fc332000 {
+ compatible = "qcom,jtag-mm";
+ reg = <0xfc332000 0x1000>,
+ <0xfc330000 0x1000>;
+ reg-names = "etm-base","debug-base";
+ };
+
+ qcom,msm-rtb {
+ compatible = "qcom,msm-rtb";
+ qcom,memory-reservation-type = "EBI1";
+ qcom,memory-reservation-size = <0x1000>; /* 4K EBI1 buffer */
+ };
};
/include/ "msm-pm8019-rpm-regulator.dtsi"
diff --git a/arch/arm/configs/msm8910_defconfig b/arch/arm/configs/msm8610_defconfig
similarity index 92%
rename from arch/arm/configs/msm8910_defconfig
rename to arch/arm/configs/msm8610_defconfig
index 4b1e3f2..6793a65 100644
--- a/arch/arm/configs/msm8910_defconfig
+++ b/arch/arm/configs/msm8610_defconfig
@@ -34,7 +34,7 @@
CONFIG_PARTITION_ADVANCED=y
CONFIG_EFI_PARTITION=y
CONFIG_ARCH_MSM=y
-CONFIG_ARCH_MSM8910=y
+CONFIG_ARCH_MSM8610=y
CONFIG_ARCH_MSM8226=y
CONFIG_SND_SOC_MSM8226=y
# CONFIG_MSM_STACKED_MEMORY is not set
@@ -43,7 +43,13 @@
# CONFIG_MSM_PROC_COMM is not set
CONFIG_MSM_SMD=y
CONFIG_MSM_SMD_PKG4=y
+CONFIG_MSM_IPC_ROUTER=y
+CONFIG_MSM_IPC_ROUTER_SMD_XPRT=y
+CONFIG_MSM_QMI_INTERFACE=y
# CONFIG_MSM_HW3D is not set
+CONFIG_MSM_SUBSYSTEM_RESTART=y
+CONFIG_MSM_PIL=y
+CONFIG_MSM_PIL_LPASS_QDSP6V5=y
CONFIG_MSM_DIRECT_SCLK_ACCESS=y
CONFIG_MSM_WATCHDOG_V2=y
CONFIG_MSM_ADSP_LOADER=m
@@ -176,3 +182,9 @@
# CONFIG_CRYPTO_HW is not set
CONFIG_CRC_CCITT=y
CONFIG_LIBCRC32C=y
+CONFIG_MEDIA_SUPPORT=y
+CONFIG_VIDEO_DEV=y
+CONFIG_VIDEO_V4L2_SUBDEV_API=y
+CONFIG_VIDEOBUF2_MSM_MEM=y
+CONFIG_MSM_SUBSYSTEM_RESTART=y
+CONFIG_MSM_OCMEM=y
diff --git a/arch/arm/configs/msm8960-perf_defconfig b/arch/arm/configs/msm8960-perf_defconfig
index f464a55..94009bc 100644
--- a/arch/arm/configs/msm8960-perf_defconfig
+++ b/arch/arm/configs/msm8960-perf_defconfig
@@ -253,7 +253,6 @@
CONFIG_PMIC8XXX_VIBRATOR=y
CONFIG_QSEECOM=y
CONFIG_USB_HSIC_SMSC_HUB=y
-CONFIG_SCSI=y
CONFIG_SCSI_TGT=y
CONFIG_BLK_DEV_SD=y
CONFIG_CHR_DEV_SG=y
@@ -262,6 +261,8 @@
CONFIG_SCSI_CONSTANTS=y
CONFIG_SCSI_LOGGING=y
CONFIG_SCSI_SCAN_ASYNC=y
+CONFIG_ATA=y
+CONFIG_SATA_AHCI_MSM=y
CONFIG_MD=y
CONFIG_BLK_DEV_DM=y
CONFIG_DM_CRYPT=y
@@ -338,6 +339,7 @@
CONFIG_MFD_PM8XXX_BATT_ALARM=y
CONFIG_WCD9304_CODEC=y
CONFIG_WCD9310_CODEC=y
+CONFIG_REGULATOR_FIXED_VOLTAGE=y
CONFIG_REGULATOR_PM8XXX=y
CONFIG_REGULATOR_MSM_GPIO=y
CONFIG_MEDIA_SUPPORT=y
@@ -370,6 +372,8 @@
CONFIG_MSM_CSI20_HEADER=y
CONFIG_S5K3L1YX=y
CONFIG_IMX091=y
+CONFIG_MSM_CSIPHY=y
+CONFIG_MSM_CSID=y
CONFIG_MSM_WFD=y
CONFIG_RADIO_IRIS=y
CONFIG_RADIO_IRIS_TRANSPORT=m
diff --git a/arch/arm/configs/msm8960_defconfig b/arch/arm/configs/msm8960_defconfig
index e54459d..6efdafa 100644
--- a/arch/arm/configs/msm8960_defconfig
+++ b/arch/arm/configs/msm8960_defconfig
@@ -258,7 +258,6 @@
CONFIG_PMIC8XXX_VIBRATOR=y
CONFIG_QSEECOM=y
CONFIG_USB_HSIC_SMSC_HUB=y
-CONFIG_SCSI=y
CONFIG_SCSI_TGT=y
CONFIG_BLK_DEV_SD=y
CONFIG_CHR_DEV_SG=y
@@ -267,6 +266,8 @@
CONFIG_SCSI_CONSTANTS=y
CONFIG_SCSI_LOGGING=y
CONFIG_SCSI_SCAN_ASYNC=y
+CONFIG_ATA=y
+CONFIG_SATA_AHCI_MSM=y
CONFIG_MD=y
CONFIG_BLK_DEV_DM=y
CONFIG_DM_CRYPT=y
@@ -343,6 +344,7 @@
CONFIG_MFD_PM8XXX_BATT_ALARM=y
CONFIG_WCD9304_CODEC=y
CONFIG_WCD9310_CODEC=y
+CONFIG_REGULATOR_FIXED_VOLTAGE=y
CONFIG_REGULATOR_PM8XXX=y
CONFIG_REGULATOR_MSM_GPIO=y
CONFIG_MEDIA_SUPPORT=y
@@ -374,6 +376,8 @@
CONFIG_MSM_CSI20_HEADER=y
CONFIG_S5K3L1YX=y
CONFIG_IMX091=y
+CONFIG_MSM_CSIPHY=y
+CONFIG_MSM_CSID=y
CONFIG_MSM_WFD=y
CONFIG_RADIO_IRIS=y
CONFIG_RADIO_IRIS_TRANSPORT=m
diff --git a/arch/arm/configs/msm8974-perf_defconfig b/arch/arm/configs/msm8974-perf_defconfig
index 136cb5a..76a13f9 100644
--- a/arch/arm/configs/msm8974-perf_defconfig
+++ b/arch/arm/configs/msm8974-perf_defconfig
@@ -45,6 +45,9 @@
CONFIG_MSM_SMD=y
CONFIG_MSM_SMD_PKG4=y
CONFIG_MSM_BAM_DMUX=y
+CONFIG_MSM_SMP2P=y
+CONFIG_MSM_SMP2P_TEST=y
+CONFIG_MSM_IPC_LOGGING=y
CONFIG_MSM_IPC_ROUTER=y
CONFIG_MSM_IPC_ROUTER_SMD_XPRT=y
CONFIG_MSM_IPC_ROUTER_SECURITY=y
@@ -75,6 +78,7 @@
CONFIG_MSM_L1_ERR_LOG=y
CONFIG_MSM_L2_ERP_2BIT_PANIC=y
CONFIG_MSM_ENABLE_WDOG_DEBUG_CONTROL=y
+CONFIG_MSM_UARTDM_Core_v14=y
CONFIG_STRICT_MEMORY_RWX=y
CONFIG_NO_HZ=y
CONFIG_HIGH_RES_TIMERS=y
@@ -259,6 +263,7 @@
CONFIG_TOUCHSCREEN_ATMEL_MXT=y
CONFIG_INPUT_MISC=y
CONFIG_INPUT_UINPUT=y
+CONFIG_SERIAL_MSM_HS=y
CONFIG_SERIAL_MSM_HSL=y
CONFIG_SERIAL_MSM_HSL_CONSOLE=y
CONFIG_DIAG_CHAR=y
@@ -303,12 +308,9 @@
CONFIG_VIDEOBUF2_MSM_MEM=y
CONFIG_USB_VIDEO_CLASS=y
CONFIG_V4L_PLATFORM_DRIVERS=y
-CONFIG_MSM_CAMERA_V4L2=y
-CONFIG_MT9M114=y
-CONFIG_MSM_CAMERA_LED_TRIGGER_FLASH=y
-CONFIG_OV2720=y
+CONFIG_MSM_CAMERA=n
+CONFIG_MSMB_CAMERA=y
CONFIG_MSM_CAMERA_SENSOR=y
-CONFIG_MSM_ACTUATOR=y
CONFIG_MSM_JPEG=y
CONFIG_MSM_CCI=y
CONFIG_MSM_CSI30_HEADER=y
@@ -329,6 +331,7 @@
CONFIG_FB_MSM_MDSS=y
CONFIG_FB_MSM_MDSS_WRITEBACK=y
CONFIG_FB_MSM_MDSS_HDMI_PANEL=y
+CONFIG_FB_MSM_MDSS_HDMI_MHL_SII8334=y
CONFIG_BACKLIGHT_LCD_SUPPORT=y
# CONFIG_LCD_CLASS_DEVICE is not set
CONFIG_BACKLIGHT_CLASS_DEVICE=y
@@ -338,9 +341,11 @@
CONFIG_SND_USB_AUDIO=y
CONFIG_SND_SOC=y
CONFIG_SND_SOC_MSM8974=y
+CONFIG_USB_SUSPEND=y
CONFIG_USB_XHCI_HCD=y
CONFIG_USB_EHCI_HCD=y
CONFIG_USB_EHCI_ROOT_HUB_TT=y
+CONFIG_USB_EHCI_MSM=y
CONFIG_USB_EHCI_MSM_HSIC=y
CONFIG_USB_ACM=y
CONFIG_USB_STORAGE=y
@@ -431,3 +436,5 @@
CONFIG_CRC_CCITT=y
CONFIG_SYNC=y
CONFIG_SW_SYNC=y
+CONFIG_MOBICORE_SUPPORT=m
+CONFIG_MOBICORE_API=m
diff --git a/arch/arm/configs/msm8974_defconfig b/arch/arm/configs/msm8974_defconfig
index eb3c315..f025ab3 100644
--- a/arch/arm/configs/msm8974_defconfig
+++ b/arch/arm/configs/msm8974_defconfig
@@ -44,6 +44,9 @@
CONFIG_MSM_SMD=y
CONFIG_MSM_SMD_PKG4=y
CONFIG_MSM_BAM_DMUX=y
+CONFIG_MSM_SMP2P=y
+CONFIG_MSM_SMP2P_TEST=y
+CONFIG_MSM_IPC_LOGGING=y
CONFIG_MSM_IPC_ROUTER=y
CONFIG_MSM_IPC_ROUTER_SMD_XPRT=y
CONFIG_MSM_IPC_ROUTER_SECURITY=y
@@ -73,10 +76,13 @@
CONFIG_MSM_L1_RECOV_ERR_PANIC=y
CONFIG_MSM_L1_ERR_LOG=y
CONFIG_MSM_L2_ERP_PRINT_ACCESS_ERRORS=y
+CONFIG_MSM_L2_ERP_PORT_PANIC=y
+CONFIG_MSM_L2_ERP_1BIT_PANIC=y
CONFIG_MSM_L2_ERP_2BIT_PANIC=y
CONFIG_MSM_CACHE_DUMP=y
CONFIG_MSM_CACHE_DUMP_ON_PANIC=y
CONFIG_MSM_ENABLE_WDOG_DEBUG_CONTROL=y
+CONFIG_MSM_UARTDM_Core_v14=y
CONFIG_STRICT_MEMORY_RWX=y
CONFIG_NO_HZ=y
CONFIG_HIGH_RES_TIMERS=y
@@ -261,6 +267,7 @@
CONFIG_TOUCHSCREEN_ATMEL_MXT=y
CONFIG_INPUT_MISC=y
CONFIG_INPUT_UINPUT=y
+CONFIG_SERIAL_MSM_HS=y
CONFIG_SERIAL_MSM_HSL=y
CONFIG_SERIAL_MSM_HSL_CONSOLE=y
CONFIG_DIAG_CHAR=y
@@ -304,12 +311,9 @@
CONFIG_VIDEOBUF2_MSM_MEM=y
CONFIG_USB_VIDEO_CLASS=y
CONFIG_V4L_PLATFORM_DRIVERS=y
-CONFIG_MSM_CAMERA_V4L2=y
-CONFIG_MT9M114=y
-CONFIG_MSM_CAMERA_LED_TRIGGER_FLASH=y
-CONFIG_OV2720=y
+CONFIG_MSM_CAMERA=n
+CONFIG_MSMB_CAMERA=y
CONFIG_MSM_CAMERA_SENSOR=y
-CONFIG_MSM_ACTUATOR=y
CONFIG_MSM_JPEG=y
CONFIG_MSM_CCI=y
CONFIG_MSM_CSI30_HEADER=y
@@ -330,6 +334,7 @@
CONFIG_FB_MSM_MDSS=y
CONFIG_FB_MSM_MDSS_WRITEBACK=y
CONFIG_FB_MSM_MDSS_HDMI_PANEL=y
+CONFIG_FB_MSM_MDSS_HDMI_MHL_SII8334=y
CONFIG_BACKLIGHT_LCD_SUPPORT=y
# CONFIG_LCD_CLASS_DEVICE is not set
CONFIG_BACKLIGHT_CLASS_DEVICE=y
@@ -339,9 +344,11 @@
CONFIG_SND_USB_AUDIO=y
CONFIG_SND_SOC=y
CONFIG_SND_SOC_MSM8974=y
+CONFIG_USB_SUSPEND=y
CONFIG_USB_XHCI_HCD=y
CONFIG_USB_EHCI_HCD=y
CONFIG_USB_EHCI_ROOT_HUB_TT=y
+CONFIG_USB_EHCI_MSM=y
CONFIG_USB_EHCI_MSM_HSIC=y
CONFIG_USB_ACM=y
CONFIG_USB_STORAGE=y
@@ -445,3 +452,5 @@
CONFIG_CRC_CCITT=y
CONFIG_SYNC=y
CONFIG_SW_SYNC=y
+CONFIG_MOBICORE_SUPPORT=m
+CONFIG_MOBICORE_API=m
diff --git a/arch/arm/configs/msm9625_defconfig b/arch/arm/configs/msm9625_defconfig
index decd8f2..ecf1c68 100644
--- a/arch/arm/configs/msm9625_defconfig
+++ b/arch/arm/configs/msm9625_defconfig
@@ -40,6 +40,7 @@
CONFIG_MSM_SMD=y
CONFIG_MSM_SMD_PKG4=y
CONFIG_MSM_BAM_DMUX=y
+CONFIG_MSM_IPC_LOGGING=y
CONFIG_MSM_IPC_ROUTER=y
CONFIG_MSM_IPC_ROUTER_SMD_XPRT=y
CONFIG_MSM_RPM_REGULATOR_SMD=y
@@ -98,6 +99,7 @@
CONFIG_NF_CONNTRACK_PPTP=y
CONFIG_NF_CONNTRACK_SIP=y
CONFIG_NF_CONNTRACK_TFTP=y
+CONFIG_NF_CT_NETLINK=y
CONFIG_NETFILTER_XT_MARK=y
CONFIG_NETFILTER_XT_CONNMARK=y
CONFIG_NETFILTER_XT_MATCH_ADDRTYPE=y
@@ -215,6 +217,9 @@
CONFIG_USB_GADGET=y
CONFIG_USB_CI13XXX_MSM=y
CONFIG_USB_G_ANDROID=y
+CONFIG_USB=y
+CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_EHCI_MSM_HSIC=y
CONFIG_MMC=y
CONFIG_MMC_PERF_PROFILING=y
CONFIG_MMC_UNSAFE_RESUME=y
@@ -300,3 +305,4 @@
CONFIG_SCSI_CONSTANTS=y
CONFIG_SCSI_LOGGING=y
CONFIG_SCSI_SCAN_ASYNC=y
+CONFIG_MSM_RTB=y
diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig
index afb9ab6..fd8a0cc 100644
--- a/arch/arm/mach-msm/Kconfig
+++ b/arch/arm/mach-msm/Kconfig
@@ -184,6 +184,7 @@
select ARM_USE_USER_ACCESSIBLE_TIMERS
select MSM_USE_USER_ACCESSIBLE_TIMERS
select MSM_CPU_PWRCTL
+ select MSM_LPM_TEST
config ARCH_MSM8930
bool "MSM8930"
@@ -287,6 +288,8 @@
select QMI_ENCDEC
select DONT_MAP_HOLE_AFTER_MEMBANK0
select SENSORS_ADSP
+ select MSM_ULTRASOUND_B
+ select MSM_LPM_TEST
config ARCH_MPQ8092
bool "MPQ8092"
@@ -374,9 +377,10 @@
select MSM_QDSP6V2_CODECS
select MSM_AUDIO_QDSP6V2 if SND_SOC
select CPU_HAS_L2_PMU
+ select MSM_JTAG_MM if MSM_QDSS
-config ARCH_MSM8910
- bool "MSM8910"
+config ARCH_MSM8610
+ bool "MSM8610"
select ARM_GIC
select GIC_SECURE
select ARCH_MSM_CORTEXMP
@@ -389,6 +393,10 @@
select MSM_GPIOMUX
select MSM_NATIVE_RESTART
select MSM_RESTART_V2
+ select QMI_ENCDEC
+ select MSM_QDSP6_APRV2
+ select MSM_QDSP6V2_CODECS
+ select MSM_AUDIO_QDSP6V2 if SND_SOC
config ARCH_MSM8226
bool "MSM8226"
@@ -407,6 +415,11 @@
select MSM_QDSP6_APRV2
select MSM_QDSP6V2_CODECS
select MSM_AUDIO_QDSP6V2 if SND_SOC
+ select QMI_ENCDEC
+ select MSM_RPM_SMD
+ select MSM_SPM_V2
+ select MSM_L2_SPM
+ select MSM_PM8X60 if PM
endmenu
choice
@@ -522,6 +535,17 @@
enables the MPM driver that supports initialization from a device
tree
+config MSM_LPM_TEST
+ bool "Low Power Mode test framework"
+ depends on MSM_RPM || MSM_RPM_SMD
+ depends on MSM_PM8X60
+ help
+ LPM_TEST is a test framework that assists in exercising the low
+ power mode algorithm on MSM targets. This test framework tracks
+ notifications sent during entry/exit of the low power modes and
+ processes them to measure various stats including latency
+ measurement.
+
config MSM_XO
bool
@@ -928,6 +952,15 @@
help
Support for the Qualcomm APQ8064 CDP device.
+config MACH_FSM8064_EP
+ depends on ARCH_APQ8064
+ bool "FSM8064 EP"
+ help
+ Support for the Qualcomm FSM8064 EP device.
+ This board also known as Femto development platform (FDP)
+ is based on APQ8064 chipset. This board does not support
+ keyboard, display or multimedia.
+
config MACH_APQ8064_MTP
depends on ARCH_APQ8064
bool "APQ8064 MTP"
@@ -987,7 +1020,7 @@
default "0x00000000" if ARCH_MSM8974
default "0x00000000" if ARCH_MPQ8092
default "0x00000000" if ARCH_MSM8226
- default "0x00000000" if ARCH_MSM8910
+ default "0x00000000" if ARCH_MSM8610
default "0x10000000" if ARCH_FSM9XXX
default "0x00200000" if ARCH_MSM9625
default "0x00200000" if !MSM_STACKED_MEMORY
@@ -1427,6 +1460,27 @@
Supports APPS-QDSP SMSM communication along with
normal APPS-MODEM SMSM communication.
+config MSM_SMP2P
+ bool "SMSM Point-to-Point (SMP2P)"
+ depends on MSM_SMD
+ help
+ Provide point-to-point remote signaling support.
+ SMP2P enables transferring 32-bit values between
+ the local and a remote system using shared
+ memory and interrupts. A client can open multiple
+ 32-bit values by specifying a unique string and
+ remote processor ID.
+
+config MSM_SMP2P_TEST
+ bool "SMSM Point-to-Point Test"
+ depends on MSM_SMP2P
+ help
+ Enables loopback and unit testing support for
+ SMP2P. Loopback support is used by other
+ processors to do unit testing. Unit tests
+ are used to verify the local and remote
+ implementations.
+
config MSM_RESET_MODEM
tristate "Reset Modem Driver"
depends on MSM_SMD
@@ -2260,6 +2314,19 @@
For production builds, you should probably say 'N' here to avoid
potential power, performance and memory penalty.
+config MSM_JTAG_MM
+ bool "ETM trace and debug support across power collapse using memory mapped access"
+ help
+ Enables support for kernel debugging (specifically breakpoints) and
+ processor tracing using ETM across power collapse both for JTag and
+ OS hosted software running on the target. Enabling this will ensure
+ debug and ETM registers are saved and restored across power collapse.
+ Needed on targets on which cp14 access to debug and ETM registers is
+ not permitted and so memory mapped access is necessary.
+
+ For production builds, you should probably say 'N' here to avoid
+ potential power, performance and memory penalty.
+
config MSM_ETM
tristate "Enable MSM ETM and ETB"
depends on ARCH_MSM8X60
@@ -2400,6 +2467,16 @@
HW and services, calculating input events
upon the ultrasound data.
+config MSM_ULTRASOUND_B
+ bool "QDSP6V2 HW Ultrasound support"
+ help
+ Enable HW Ultrasound support in QDSP6V2.
+ QDSP6V2 can support HW encoder & decoder and
+ ultrasound processing. It will enable
+ ultrasound data paths between
+ HW and services, calculating input events
+ upon the ultrasound data.
+
config MSM_RPC_VIBRATOR
bool "RPC based MSM Vibrator Support"
depends on MSM_ONCRPCROUTER
diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile
index 2d617a9..c4d9048 100644
--- a/arch/arm/mach-msm/Makefile
+++ b/arch/arm/mach-msm/Makefile
@@ -38,6 +38,7 @@
obj-$(CONFIG_ARCH_MSM9625) += pmu.o perf_event_msm_pl310.o
obj-$(CONFIG_ARCH_MSM8625) += pmu.o perf_event_msm_pl310.o
obj-$(CONFIG_ARCH_MSM9615) += pmu.o perf_event_msm_pl310.o
+obj-$(CONFIG_DEBUG_FS) += perf_debug.o
endif
ifndef CONFIG_MSM_SMP
@@ -60,6 +61,7 @@
obj-$(CONFIG_CPU_V6) += idle-v6.o
obj-$(CONFIG_CPU_V7) += idle-v7.o
obj-$(CONFIG_MSM_JTAG) += jtag.o
+obj-$(CONFIG_MSM_JTAG_MM) += jtag-mm.o
msm-etm-objs := etm.o
obj-$(CONFIG_MSM_ETM) += msm-etm.o
@@ -72,7 +74,8 @@
$(call if_changed,mkrpcsym)
obj-$(CONFIG_MSM_SMD) += smd.o smd_debug.o remote_spinlock.o smd_private.o
-
+obj-$(CONFIG_MSM_SMP2P) += smp2p.o smp2p_debug.o smp2p_gpio.o
+obj-$(CONFIG_MSM_SMP2P_TEST) += smp2p_loopback.o smp2p_test.o smp2p_gpio_test.o smp2p_spinlock_test.o
obj-$(CONFIG_MSM_SCM) += scm.o scm-boot.o
obj-$(CONFIG_MSM_SECURE_IO) += scm-io.o
obj-$(CONFIG_MSM_PIL) += peripheral-loader.o
@@ -112,7 +115,7 @@
ifndef CONFIG_ARCH_MSM8226
ifndef CONFIG_ARCH_MSM9625
ifndef CONFIG_ARCH_MPQ8092
-ifndef CONFIG_ARCH_MSM8910
+ifndef CONFIG_ARCH_MSM8610
obj-y += nand_partitions.o
endif
endif
@@ -209,14 +212,7 @@
endif
obj-$(CONFIG_MSM_SYSMON_COMM) += sysmon.o
-ifdef CONFIG_CPU_IDLE
- obj-$(CONFIG_ARCH_APQ8064) += cpuidle.o
- obj-$(CONFIG_ARCH_MSM8960) += cpuidle.o
- obj-$(CONFIG_ARCH_MSM8X60) += cpuidle.o
- obj-$(CONFIG_ARCH_MSM9615) += cpuidle.o
- obj-$(CONFIG_ARCH_MSM9625) += cpuidle.o
- obj-$(CONFIG_ARCH_MSM8974) += cpuidle.o
-endif
+obj-$(CONFIG_CPU_IDLE) += cpuidle.o
ifdef CONFIG_MSM_CAMERA_V4L2
obj-$(CONFIG_ARCH_MSM8X60) += board-msm8x60-camera.o
@@ -296,15 +292,15 @@
obj-$(CONFIG_ARCH_MSM8974) += gdsc.o
obj-$(CONFIG_ARCH_MSM9625) += gdsc.o
obj-$(CONFIG_ARCH_MSM8226) += gdsc.o
-obj-$(CONFIG_ARCH_MSM8910) += gdsc.o
+obj-$(CONFIG_ARCH_MSM8610) += gdsc.o
obj-$(CONFIG_ARCH_MSM8974) += krait-regulator.o
obj-$(CONFIG_ARCH_MSM9625) += board-9625.o board-9625-gpiomux.o
obj-$(CONFIG_ARCH_MSM9625) += clock-local2.o clock-pll.o clock-9625.o clock-rpm.o clock-voter.o acpuclock-9625.o
obj-$(CONFIG_ARCH_MSM8930) += acpuclock-8930.o acpuclock-8627.o acpuclock-8930aa.o acpuclock-8930ab.o
obj-$(CONFIG_ARCH_MPQ8092) += board-8092.o board-8092-gpiomux.o
obj-$(CONFIG_ARCH_MSM8226) += board-8226.o board-8226-gpiomux.o
-obj-$(CONFIG_ARCH_MSM8910) += board-8910.o board-8910-gpiomux.o
-obj-$(CONFIG_ARCH_MSM8910) += clock-local2.o clock-pll.o clock-8910.o clock-rpm.o clock-voter.o
+obj-$(CONFIG_ARCH_MSM8610) += board-8610.o board-8610-gpiomux.o
+obj-$(CONFIG_ARCH_MSM8610) += clock-local2.o clock-pll.o clock-8610.o clock-rpm.o clock-voter.o
obj-$(CONFIG_MACH_SAPPHIRE) += board-sapphire.o board-sapphire-gpio.o
obj-$(CONFIG_MACH_SAPPHIRE) += board-sapphire-keypad.o board-sapphire-panel.o
@@ -322,17 +318,9 @@
obj-$(CONFIG_HTC_HEADSET) += htc_headset.o
obj-$(CONFIG_MSM_RMT_STORAGE_CLIENT) += rmt_storage_client.o
obj-$(CONFIG_MSM_SDIO_SMEM) += sdio_smem.o
-obj-$(CONFIG_MSM_RPM) += rpm.o
-ifdef CONFIG_MSM_RPM
- obj-$(CONFIG_ARCH_APQ8064) += rpm_resources.o
- obj-$(CONFIG_ARCH_MSM8960) += rpm_resources.o
- obj-$(CONFIG_ARCH_MSM8X60) += rpm_resources.o
- obj-$(CONFIG_ARCH_MSM9615) += rpm_resources.o
-endif
-ifdef CONFIG_MSM_RPM_SMD
- obj-$(CONFIG_ARCH_MSM8974) += lpm_levels.o lpm_resources.o
- obj-$(CONFIG_ARCH_MSM9625) += lpm_levels.o lpm_resources.o
-endif
+obj-$(CONFIG_MSM_RPM) += rpm.o rpm_resources.o
+obj-$(CONFIG_MSM_LPM_TEST) += test-lpm.o
+obj-$(CONFIG_MSM_RPM_SMD) += rpm-smd.o lpm_levels.o lpm_resources.o
obj-$(CONFIG_MSM_MPM_OF) += mpm-of.o
obj-$(CONFIG_MSM_MPM) += mpm.o
obj-$(CONFIG_MSM_RPM_STATS_LOG) += rpm_stats.o rpm_master_stat.o
@@ -370,7 +358,7 @@
obj-$(CONFIG_ARCH_MSM9625) += gpiomux-v2.o gpiomux.o
obj-$(CONFIG_ARCH_MPQ8092) += gpiomux-v2.o gpiomux.o
obj-$(CONFIG_ARCH_MSM8226) += gpiomux-v2.o gpiomux.o
-obj-$(CONFIG_ARCH_MSM8910) += gpiomux-v2.o gpiomux.o
+obj-$(CONFIG_ARCH_MSM8610) += gpiomux-v2.o gpiomux.o
obj-$(CONFIG_MSM_SLEEP_STATS_DEVICE) += idle_stats_device.o
obj-$(CONFIG_MSM_DCVS) += msm_dcvs_scm.o msm_dcvs.o msm_mpdecision.o
diff --git a/arch/arm/mach-msm/Makefile.boot b/arch/arm/mach-msm/Makefile.boot
index e74d61a..e04a9a0 100644
--- a/arch/arm/mach-msm/Makefile.boot
+++ b/arch/arm/mach-msm/Makefile.boot
@@ -47,12 +47,16 @@
# MSM8974
zreladdr-$(CONFIG_ARCH_MSM8974) := 0x00008000
- dtb-$(CONFIG_ARCH_MSM8974) += msm8974-cdp.dtb
- dtb-$(CONFIG_ARCH_MSM8974) += msm8974-fluid.dtb
- dtb-$(CONFIG_ARCH_MSM8974) += msm8974-liquid.dtb
- dtb-$(CONFIG_ARCH_MSM8974) += msm8974-mtp.dtb
- dtb-$(CONFIG_ARCH_MSM8974) += msm8974-rumi.dtb
- dtb-$(CONFIG_ARCH_MSM8974) += msm8974-sim.dtb
+ dtb-$(CONFIG_ARCH_MSM8974) += msm8974-v1-cdp.dtb
+ dtb-$(CONFIG_ARCH_MSM8974) += msm8974-v1-fluid.dtb
+ dtb-$(CONFIG_ARCH_MSM8974) += msm8974-v1-liquid.dtb
+ dtb-$(CONFIG_ARCH_MSM8974) += msm8974-v1-mtp.dtb
+ dtb-$(CONFIG_ARCH_MSM8974) += msm8974-v1-rumi.dtb
+ dtb-$(CONFIG_ARCH_MSM8974) += msm8974-v1-sim.dtb
+ dtb-$(CONFIG_ARCH_MSM8974) += msm8974-v2-cdp.dtb
+ dtb-$(CONFIG_ARCH_MSM8974) += msm8974-v2-fluid.dtb
+ dtb-$(CONFIG_ARCH_MSM8974) += msm8974-v2-liquid.dtb
+ dtb-$(CONFIG_ARCH_MSM8974) += msm8974-v2-mtp.dtb
# MSM9615
zreladdr-$(CONFIG_ARCH_MSM9615) := 0x40808000
@@ -75,7 +79,7 @@
# MPQ8092
zreladdr-$(CONFIG_ARCH_MPQ8092) := 0x00008000
-# MSM8910
- zreladdr-$(CONFIG_ARCH_MSM8910) := 0x00008000
- dtb-$(CONFIG_ARCH_MSM8910) += msm8910-rumi.dtb
- dtb-$(CONFIG_ARCH_MSM8910) += msm8910-sim.dtb
+# MSM8610
+ zreladdr-$(CONFIG_ARCH_MSM8610) := 0x00008000
+ dtb-$(CONFIG_ARCH_MSM8610) += msm8610-rumi.dtb
+ dtb-$(CONFIG_ARCH_MSM8610) += msm8610-sim.dtb
diff --git a/arch/arm/mach-msm/acpuclock-8064.c b/arch/arm/mach-msm/acpuclock-8064.c
index e8c7680..a0727b7 100644
--- a/arch/arm/mach-msm/acpuclock-8064.c
+++ b/arch/arm/mach-msm/acpuclock-8064.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011-2012, Code Aurora Forum. 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
@@ -239,6 +239,118 @@
{ 0, { 0 } }
};
+static struct acpu_level tbl_PVS0_1512MHz[] __initdata = {
+ { 1, { 384000, PLL_8, 0, 0x00 }, L2(0), 950000 },
+ { 1, { 486000, HFPLL, 2, 0x24 }, L2(5), 950000 },
+ { 1, { 594000, HFPLL, 1, 0x16 }, L2(5), 950000 },
+ { 1, { 702000, HFPLL, 1, 0x1A }, L2(5), 962500 },
+ { 1, { 810000, HFPLL, 1, 0x1E }, L2(5), 1000000 },
+ { 1, { 918000, HFPLL, 1, 0x22 }, L2(5), 1025000 },
+ { 1, { 1026000, HFPLL, 1, 0x26 }, L2(5), 1037500 },
+ { 1, { 1134000, HFPLL, 1, 0x2A }, L2(15), 1075000 },
+ { 1, { 1242000, HFPLL, 1, 0x2E }, L2(15), 1087500 },
+ { 1, { 1350000, HFPLL, 1, 0x32 }, L2(15), 1125000 },
+ { 1, { 1458000, HFPLL, 1, 0x36 }, L2(15), 1150000 },
+ { 1, { 1512000, HFPLL, 1, 0x38 }, L2(15), 1162500 },
+ { 0, { 0 } }
+};
+
+static struct acpu_level tbl_PVS1_1512MHz[] __initdata = {
+ { 1, { 384000, PLL_8, 0, 0x00 }, L2(0), 950000 },
+ { 1, { 486000, HFPLL, 2, 0x24 }, L2(5), 950000 },
+ { 1, { 594000, HFPLL, 1, 0x16 }, L2(5), 950000 },
+ { 1, { 702000, HFPLL, 1, 0x1A }, L2(5), 962500 },
+ { 1, { 810000, HFPLL, 1, 0x1E }, L2(5), 975000 },
+ { 1, { 918000, HFPLL, 1, 0x22 }, L2(5), 1000000 },
+ { 1, { 1026000, HFPLL, 1, 0x26 }, L2(5), 1012500 },
+ { 1, { 1134000, HFPLL, 1, 0x2A }, L2(15), 1037500 },
+ { 1, { 1242000, HFPLL, 1, 0x2E }, L2(15), 1050000 },
+ { 1, { 1350000, HFPLL, 1, 0x32 }, L2(15), 1087500 },
+ { 1, { 1458000, HFPLL, 1, 0x36 }, L2(15), 1112500 },
+ { 1, { 1512000, HFPLL, 1, 0x38 }, L2(15), 1125000 },
+ { 0, { 0 } }
+};
+
+static struct acpu_level tbl_PVS2_1512MHz[] __initdata = {
+ { 1, { 384000, PLL_8, 0, 0x00 }, L2(0), 925000 },
+ { 1, { 486000, HFPLL, 2, 0x24 }, L2(5), 925000 },
+ { 1, { 594000, HFPLL, 1, 0x16 }, L2(5), 925000 },
+ { 1, { 702000, HFPLL, 1, 0x1A }, L2(5), 925000 },
+ { 1, { 810000, HFPLL, 1, 0x1E }, L2(5), 937500 },
+ { 1, { 918000, HFPLL, 1, 0x22 }, L2(5), 950000 },
+ { 1, { 1026000, HFPLL, 1, 0x26 }, L2(5), 975000 },
+ { 1, { 1134000, HFPLL, 1, 0x2A }, L2(15), 1000000 },
+ { 1, { 1242000, HFPLL, 1, 0x2E }, L2(15), 1012500 },
+ { 1, { 1350000, HFPLL, 1, 0x32 }, L2(15), 1037500 },
+ { 1, { 1458000, HFPLL, 1, 0x36 }, L2(15), 1075000 },
+ { 1, { 1512000, HFPLL, 1, 0x38 }, L2(15), 1087500 },
+ { 0, { 0 } }
+};
+
+static struct acpu_level tbl_PVS3_1512MHz[] __initdata = {
+ { 1, { 384000, PLL_8, 0, 0x00 }, L2(0), 900000 },
+ { 1, { 486000, HFPLL, 2, 0x24 }, L2(5), 900000 },
+ { 1, { 594000, HFPLL, 1, 0x16 }, L2(5), 900000 },
+ { 1, { 702000, HFPLL, 1, 0x1A }, L2(5), 900000 },
+ { 1, { 810000, HFPLL, 1, 0x1E }, L2(5), 900000 },
+ { 1, { 918000, HFPLL, 1, 0x22 }, L2(5), 925000 },
+ { 1, { 1026000, HFPLL, 1, 0x26 }, L2(5), 950000 },
+ { 1, { 1134000, HFPLL, 1, 0x2A }, L2(15), 975000 },
+ { 1, { 1242000, HFPLL, 1, 0x2E }, L2(15), 987500 },
+ { 1, { 1350000, HFPLL, 1, 0x32 }, L2(15), 1000000 },
+ { 1, { 1458000, HFPLL, 1, 0x36 }, L2(15), 1037500 },
+ { 1, { 1512000, HFPLL, 1, 0x38 }, L2(15), 1050000 },
+ { 0, { 0 } }
+};
+
+static struct acpu_level tbl_PVS4_1512MHz[] __initdata = {
+ { 1, { 384000, PLL_8, 0, 0x00 }, L2(0), 875000 },
+ { 1, { 486000, HFPLL, 2, 0x24 }, L2(5), 875000 },
+ { 1, { 594000, HFPLL, 1, 0x16 }, L2(5), 875000 },
+ { 1, { 702000, HFPLL, 1, 0x1A }, L2(5), 875000 },
+ { 1, { 810000, HFPLL, 1, 0x1E }, L2(5), 887500 },
+ { 1, { 918000, HFPLL, 1, 0x22 }, L2(5), 900000 },
+ { 1, { 1026000, HFPLL, 1, 0x26 }, L2(5), 925000 },
+ { 1, { 1134000, HFPLL, 1, 0x2A }, L2(15), 950000 },
+ { 1, { 1242000, HFPLL, 1, 0x2E }, L2(15), 962500 },
+ { 1, { 1350000, HFPLL, 1, 0x32 }, L2(15), 975000 },
+ { 1, { 1458000, HFPLL, 1, 0x36 }, L2(15), 1000000 },
+ { 1, { 1512000, HFPLL, 1, 0x38 }, L2(15), 1012500 },
+ { 0, { 0 } }
+};
+
+static struct acpu_level tbl_PVS5_1512MHz[] __initdata = {
+ { 1, { 384000, PLL_8, 0, 0x00 }, L2(0), 875000 },
+ { 1, { 486000, HFPLL, 2, 0x24 }, L2(5), 875000 },
+ { 1, { 594000, HFPLL, 1, 0x16 }, L2(5), 875000 },
+ { 1, { 702000, HFPLL, 1, 0x1A }, L2(5), 875000 },
+ { 1, { 810000, HFPLL, 1, 0x1E }, L2(5), 887500 },
+ { 1, { 918000, HFPLL, 1, 0x22 }, L2(5), 900000 },
+ { 1, { 1026000, HFPLL, 1, 0x26 }, L2(5), 925000 },
+ { 1, { 1134000, HFPLL, 1, 0x2A }, L2(15), 937500 },
+ { 1, { 1242000, HFPLL, 1, 0x2E }, L2(15), 950000 },
+ { 1, { 1350000, HFPLL, 1, 0x32 }, L2(15), 962500 },
+ { 1, { 1458000, HFPLL, 1, 0x36 }, L2(15), 987500 },
+ { 1, { 1512000, HFPLL, 1, 0x38 }, L2(15), 1000000 },
+ { 0, { 0 } }
+};
+
+static struct acpu_level tbl_PVS6_1512MHz[] __initdata = {
+ { 1, { 384000, PLL_8, 0, 0x00 }, L2(0), 875000 },
+ { 1, { 486000, HFPLL, 2, 0x24 }, L2(5), 875000 },
+ { 1, { 594000, HFPLL, 1, 0x16 }, L2(5), 875000 },
+ { 1, { 702000, HFPLL, 1, 0x1A }, L2(5), 875000 },
+ { 1, { 810000, HFPLL, 1, 0x1E }, L2(5), 887500 },
+ { 1, { 918000, HFPLL, 1, 0x22 }, L2(5), 900000 },
+ { 1, { 1026000, HFPLL, 1, 0x26 }, L2(5), 925000 },
+ { 1, { 1134000, HFPLL, 1, 0x2A }, L2(15), 937500 },
+ { 1, { 1242000, HFPLL, 1, 0x2E }, L2(15), 950000 },
+ { 1, { 1350000, HFPLL, 1, 0x32 }, L2(15), 962500 },
+ { 1, { 1458000, HFPLL, 1, 0x36 }, L2(15), 975000 },
+ { 1, { 1512000, HFPLL, 1, 0x38 }, L2(15), 987500 },
+ { 0, { 0 } }
+};
+
static struct acpu_level tbl_PVS0_1700MHz[] __initdata = {
{ 1, { 384000, PLL_8, 0, 0x00 }, L2(0), 950000 },
{ 1, { 486000, HFPLL, 2, 0x24 }, L2(5), 950000 },
@@ -519,6 +631,14 @@
[2][4] = { tbl_PVS4_2000MHz, sizeof(tbl_PVS4_2000MHz), 25000 },
[2][5] = { tbl_PVS5_2000MHz, sizeof(tbl_PVS5_2000MHz), 25000 },
[2][6] = { tbl_PVS6_2000MHz, sizeof(tbl_PVS6_2000MHz), 25000 },
+
+ [14][0] = { tbl_PVS0_1512MHz, sizeof(tbl_PVS0_1512MHz), 0 },
+ [14][1] = { tbl_PVS1_1512MHz, sizeof(tbl_PVS1_1512MHz), 25000 },
+ [14][2] = { tbl_PVS2_1512MHz, sizeof(tbl_PVS2_1512MHz), 25000 },
+ [14][3] = { tbl_PVS3_1512MHz, sizeof(tbl_PVS3_1512MHz), 25000 },
+ [14][4] = { tbl_PVS4_1512MHz, sizeof(tbl_PVS4_1512MHz), 25000 },
+ [14][5] = { tbl_PVS5_1512MHz, sizeof(tbl_PVS5_1512MHz), 25000 },
+ [14][6] = { tbl_PVS6_1512MHz, sizeof(tbl_PVS6_1512MHz), 25000 },
};
static struct acpuclk_krait_params acpuclk_8064_params __initdata = {
@@ -530,6 +650,7 @@
.l2_freq_tbl_size = sizeof(l2_freq_tbl),
.bus_scale = &bus_scale_data,
.pte_efuse_phys = 0x007000C0,
+ .get_bin_info = get_krait_bin_format_a,
.stby_khz = 384000,
};
diff --git a/arch/arm/mach-msm/acpuclock-8627.c b/arch/arm/mach-msm/acpuclock-8627.c
index ac29cac..405b26b 100644
--- a/arch/arm/mach-msm/acpuclock-8627.c
+++ b/arch/arm/mach-msm/acpuclock-8627.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011-2012, Code Aurora Forum. 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
@@ -142,6 +142,7 @@
.l2_freq_tbl_size = sizeof(l2_freq_tbl),
.bus_scale = &bus_scale_data,
.pte_efuse_phys = 0x007000C0,
+ .get_bin_info = get_krait_bin_format_a,
.stby_khz = 384000,
};
diff --git a/arch/arm/mach-msm/acpuclock-8930.c b/arch/arm/mach-msm/acpuclock-8930.c
index 40326cb..6915343 100644
--- a/arch/arm/mach-msm/acpuclock-8930.c
+++ b/arch/arm/mach-msm/acpuclock-8930.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011-2012, Code Aurora Forum. 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
@@ -227,6 +227,7 @@
.l2_freq_tbl_size = sizeof(l2_freq_tbl),
.bus_scale = &bus_scale_data,
.pte_efuse_phys = 0x007000C0,
+ .get_bin_info = get_krait_bin_format_a,
.stby_khz = 384000,
};
diff --git a/arch/arm/mach-msm/acpuclock-8930aa.c b/arch/arm/mach-msm/acpuclock-8930aa.c
index 7d1ce09..9aebac9 100644
--- a/arch/arm/mach-msm/acpuclock-8930aa.c
+++ b/arch/arm/mach-msm/acpuclock-8930aa.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011-2012, Code Aurora Forum. 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
@@ -203,6 +203,7 @@
.l2_freq_tbl_size = sizeof(l2_freq_tbl),
.bus_scale = &bus_scale_data,
.pte_efuse_phys = 0x007000C0,
+ .get_bin_info = get_krait_bin_format_a,
.stby_khz = 384000,
};
diff --git a/arch/arm/mach-msm/acpuclock-8930ab.c b/arch/arm/mach-msm/acpuclock-8930ab.c
index 5003862..bddac00 100644
--- a/arch/arm/mach-msm/acpuclock-8930ab.c
+++ b/arch/arm/mach-msm/acpuclock-8930ab.c
@@ -1,5 +1,5 @@
/*
- * 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
@@ -257,6 +257,7 @@
.l2_freq_tbl_size = sizeof(l2_freq_tbl),
.bus_scale = &bus_scale_data,
.pte_efuse_phys = 0x007000C0,
+ .get_bin_info = get_krait_bin_format_a,
.stby_khz = 384000,
};
diff --git a/arch/arm/mach-msm/acpuclock-8960.c b/arch/arm/mach-msm/acpuclock-8960.c
index d7d3edd..317729f 100644
--- a/arch/arm/mach-msm/acpuclock-8960.c
+++ b/arch/arm/mach-msm/acpuclock-8960.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011-2012, Code Aurora Forum. 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
@@ -210,6 +210,7 @@
.l2_freq_tbl_size = sizeof(l2_freq_tbl),
.bus_scale = &bus_scale_data,
.pte_efuse_phys = 0x007000C0,
+ .get_bin_info = get_krait_bin_format_a,
.stby_khz = 384000,
};
diff --git a/arch/arm/mach-msm/acpuclock-8960ab.c b/arch/arm/mach-msm/acpuclock-8960ab.c
index d2e88fb..38658a2 100644
--- a/arch/arm/mach-msm/acpuclock-8960ab.c
+++ b/arch/arm/mach-msm/acpuclock-8960ab.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011-2012, Code Aurora Forum. 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
@@ -252,6 +252,7 @@
.l2_freq_tbl_size = sizeof(l2_freq_tbl),
.bus_scale = &bus_scale_data,
.pte_efuse_phys = 0x007000C0,
+ .get_bin_info = get_krait_bin_format_a,
.stby_khz = 384000,
};
diff --git a/arch/arm/mach-msm/acpuclock-8974.c b/arch/arm/mach-msm/acpuclock-8974.c
index 94a4d3e..8eb4b28 100644
--- a/arch/arm/mach-msm/acpuclock-8974.c
+++ b/arch/arm/mach-msm/acpuclock-8974.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, Code Aurora Forum. 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
@@ -112,14 +112,14 @@
};
static struct l2_level l2_freq_tbl[] __initdata = {
- [0] = { { 300000, PLL_0, 0, 0 }, LVL_LOW, 1050000, 0 },
- [1] = { { 345600, HFPLL, 2, 36 }, LVL_NOM, 1050000, 1 },
- [2] = { { 422400, HFPLL, 2, 44 }, LVL_NOM, 1050000, 1 },
- [3] = { { 499200, HFPLL, 2, 52 }, LVL_NOM, 1050000, 2 },
- [4] = { { 576000, HFPLL, 1, 30 }, LVL_NOM, 1050000, 2 },
- [5] = { { 652800, HFPLL, 1, 34 }, LVL_NOM, 1050000, 3 },
- [6] = { { 729600, HFPLL, 1, 38 }, LVL_NOM, 1050000, 3 },
- [7] = { { 806400, HFPLL, 1, 42 }, LVL_NOM, 1050000, 3 },
+ [0] = { { 300000, PLL_0, 0, 0 }, LVL_LOW, 950000, 0 },
+ [1] = { { 345600, HFPLL, 2, 36 }, LVL_NOM, 950000, 1 },
+ [2] = { { 422400, HFPLL, 2, 44 }, LVL_NOM, 950000, 1 },
+ [3] = { { 499200, HFPLL, 2, 52 }, LVL_NOM, 950000, 2 },
+ [4] = { { 576000, HFPLL, 1, 30 }, LVL_NOM, 950000, 2 },
+ [5] = { { 652800, HFPLL, 1, 34 }, LVL_NOM, 950000, 3 },
+ [6] = { { 729600, HFPLL, 1, 38 }, LVL_NOM, 950000, 3 },
+ [7] = { { 806400, HFPLL, 1, 42 }, LVL_HIGH, 1050000, 3 },
[8] = { { 883200, HFPLL, 1, 46 }, LVL_HIGH, 1050000, 4 },
[9] = { { 960000, HFPLL, 1, 50 }, LVL_HIGH, 1050000, 4 },
[10] = { { 1036800, HFPLL, 1, 54 }, LVL_HIGH, 1050000, 4 },
@@ -129,50 +129,135 @@
[14] = { { 1344000, HFPLL, 1, 70 }, LVL_HIGH, 1050000, 6 },
[15] = { { 1420800, HFPLL, 1, 74 }, LVL_HIGH, 1050000, 7 },
[16] = { { 1497600, HFPLL, 1, 78 }, LVL_HIGH, 1050000, 7 },
- [17] = { { 1574400, HFPLL, 1, 82 }, LVL_HIGH, 1050000, 7 },
- [18] = { { 1651200, HFPLL, 1, 86 }, LVL_HIGH, 1050000, 7 },
- [19] = { { 1728000, HFPLL, 1, 90 }, LVL_HIGH, 1050000, 7 },
- [20] = { { 1804800, HFPLL, 1, 94 }, LVL_HIGH, 1050000, 7 },
- [21] = { { 1881600, HFPLL, 1, 98 }, LVL_HIGH, 1050000, 7 },
- [22] = { { 1958400, HFPLL, 1, 102 }, LVL_HIGH, 1050000, 7 },
- [23] = { { 2035200, HFPLL, 1, 106 }, LVL_HIGH, 1050000, 7 },
- [24] = { { 2112000, HFPLL, 1, 110 }, LVL_HIGH, 1050000, 7 },
- [25] = { { 2188800, HFPLL, 1, 114 }, LVL_HIGH, 1050000, 7 },
{ }
};
-static struct acpu_level acpu_freq_tbl[] __initdata = {
- { 1, { 300000, PLL_0, 0, 0 }, L2(0), 850000, 400000 },
- { 0, { 345600, HFPLL, 2, 36 }, L2(0), 850000, 3200000 },
- { 1, { 422400, HFPLL, 2, 44 }, L2(0), 850000, 3200000 },
- { 0, { 499200, HFPLL, 2, 52 }, L2(0), 850000, 3200000 },
- { 1, { 576000, HFPLL, 1, 30 }, L2(0), 850000, 3200000 },
- { 1, { 652800, HFPLL, 1, 34 }, L2(16), 850000, 3200000 },
- { 0, { 729600, HFPLL, 1, 38 }, L2(16), 850000, 3200000 },
- { 1, { 806400, HFPLL, 1, 42 }, L2(16), 850000, 3200000 },
- { 1, { 883200, HFPLL, 1, 46 }, L2(16), 870000, 3200000 },
- { 1, { 960000, HFPLL, 1, 50 }, L2(16), 880000, 3200000 },
- { 1, { 1036800, HFPLL, 1, 54 }, L2(16), 900000, 3200000 },
- { 1, { 1113600, HFPLL, 1, 58 }, L2(16), 915000, 3200000 },
- { 1, { 1190400, HFPLL, 1, 62 }, L2(16), 935000, 3200000 },
- { 1, { 1267200, HFPLL, 1, 66 }, L2(16), 950000, 3200000 },
- { 1, { 1344000, HFPLL, 1, 70 }, L2(16), 970000, 3200000 },
- { 1, { 1420800, HFPLL, 1, 74 }, L2(16), 985000, 3200000 },
- { 1, { 1497600, HFPLL, 1, 78 }, L2(16), 1000000, 3200000 },
+static struct acpu_level acpu_freq_tbl_pvs0[] __initdata = {
+ { 1, { 300000, PLL_0, 0, 0 }, L2(0), 825000, 400000 },
+ { 0, { 345600, HFPLL, 2, 36 }, L2(3), 825000, 3200000 },
+ { 1, { 422400, HFPLL, 2, 44 }, L2(3), 825000, 3200000 },
+ { 0, { 499200, HFPLL, 2, 52 }, L2(6), 825000, 3200000 },
+ { 1, { 576000, HFPLL, 1, 30 }, L2(6), 825000, 3200000 },
+ { 1, { 652800, HFPLL, 1, 34 }, L2(6), 825000, 3200000 },
+ { 0, { 729600, HFPLL, 1, 38 }, L2(6), 825000, 3200000 },
+ { 1, { 806400, HFPLL, 1, 42 }, L2(8), 835000, 3200000 },
+ { 1, { 883200, HFPLL, 1, 46 }, L2(8), 845000, 3200000 },
+ { 1, { 960000, HFPLL, 1, 50 }, L2(8), 860000, 3200000 },
+ { 1, { 1036800, HFPLL, 1, 54 }, L2(8), 880000, 3200000 },
+ { 1, { 1113600, HFPLL, 1, 58 }, L2(12), 905000, 3200000 },
+ { 1, { 1190400, HFPLL, 1, 62 }, L2(12), 920000, 3200000 },
+ { 1, { 1267200, HFPLL, 1, 66 }, L2(12), 940000, 3200000 },
+ { 1, { 1344000, HFPLL, 1, 70 }, L2(12), 960000, 3200000 },
+ { 1, { 1420800, HFPLL, 1, 74 }, L2(16), 980000, 3200000 },
+ { 1, { 1497600, HFPLL, 1, 78 }, L2(16), 995000, 3200000 },
{ 1, { 1574400, HFPLL, 1, 82 }, L2(16), 1015000, 3200000 },
{ 1, { 1651200, HFPLL, 1, 86 }, L2(16), 1030000, 3200000 },
{ 1, { 1728000, HFPLL, 1, 90 }, L2(16), 1050000, 3200000 },
- { 0, { 1804800, HFPLL, 1, 94 }, L2(16), 1050000, 3200000 },
- { 0, { 1881600, HFPLL, 1, 98 }, L2(16), 1050000, 3200000 },
- { 0, { 1958400, HFPLL, 1, 102 }, L2(16), 1050000, 3200000 },
- { 0, { 1996800, HFPLL, 1, 104 }, L2(16), 1050000, 3200000 },
{ 0, { 0 } }
};
-static struct pvs_table pvs_tables[NUM_SPEED_BINS][NUM_PVS] __initdata = {
- [0][PVS_SLOW] = { acpu_freq_tbl, sizeof(acpu_freq_tbl) },
- [0][PVS_NOMINAL] = { acpu_freq_tbl, sizeof(acpu_freq_tbl) },
- [0][PVS_FAST] = { acpu_freq_tbl, sizeof(acpu_freq_tbl) },
+static struct acpu_level acpu_freq_tbl_pvs1[] __initdata = {
+ { 1, { 300000, PLL_0, 0, 0 }, L2(0), 825000, 400000 },
+ { 0, { 345600, HFPLL, 2, 36 }, L2(3), 825000, 3200000 },
+ { 1, { 422400, HFPLL, 2, 44 }, L2(3), 825000, 3200000 },
+ { 0, { 499200, HFPLL, 2, 52 }, L2(6), 825000, 3200000 },
+ { 1, { 576000, HFPLL, 1, 30 }, L2(6), 825000, 3200000 },
+ { 1, { 652800, HFPLL, 1, 34 }, L2(6), 825000, 3200000 },
+ { 0, { 729600, HFPLL, 1, 38 }, L2(6), 825000, 3200000 },
+ { 1, { 806400, HFPLL, 1, 42 }, L2(8), 835000, 3200000 },
+ { 1, { 883200, HFPLL, 1, 46 }, L2(8), 845000, 3200000 },
+ { 1, { 960000, HFPLL, 1, 50 }, L2(8), 860000, 3200000 },
+ { 1, { 1036800, HFPLL, 1, 54 }, L2(8), 880000, 3200000 },
+ { 1, { 1113600, HFPLL, 1, 58 }, L2(12), 905000, 3200000 },
+ { 1, { 1190400, HFPLL, 1, 62 }, L2(12), 920000, 3200000 },
+ { 1, { 1267200, HFPLL, 1, 66 }, L2(12), 940000, 3200000 },
+ { 1, { 1344000, HFPLL, 1, 70 }, L2(12), 960000, 3200000 },
+ { 1, { 1420800, HFPLL, 1, 74 }, L2(16), 980000, 3200000 },
+ { 1, { 1497600, HFPLL, 1, 78 }, L2(16), 995000, 3200000 },
+ { 1, { 1574400, HFPLL, 1, 82 }, L2(16), 1015000, 3200000 },
+ { 1, { 1651200, HFPLL, 1, 86 }, L2(16), 1030000, 3200000 },
+ { 1, { 1728000, HFPLL, 1, 90 }, L2(16), 1050000, 3200000 },
+ { 0, { 0 } }
+};
+
+static struct acpu_level acpu_freq_tbl_pvs2[] __initdata = {
+ { 1, { 300000, PLL_0, 0, 0 }, L2(0), 825000, 400000 },
+ { 0, { 345600, HFPLL, 2, 36 }, L2(3), 825000, 3200000 },
+ { 1, { 422400, HFPLL, 2, 44 }, L2(3), 825000, 3200000 },
+ { 0, { 499200, HFPLL, 2, 52 }, L2(6), 825000, 3200000 },
+ { 1, { 576000, HFPLL, 1, 30 }, L2(6), 825000, 3200000 },
+ { 1, { 652800, HFPLL, 1, 34 }, L2(6), 825000, 3200000 },
+ { 0, { 729600, HFPLL, 1, 38 }, L2(6), 825000, 3200000 },
+ { 1, { 806400, HFPLL, 1, 42 }, L2(8), 825000, 3200000 },
+ { 1, { 883200, HFPLL, 1, 46 }, L2(8), 825000, 3200000 },
+ { 1, { 960000, HFPLL, 1, 50 }, L2(8), 835000, 3200000 },
+ { 1, { 1036800, HFPLL, 1, 54 }, L2(8), 855000, 3200000 },
+ { 1, { 1113600, HFPLL, 1, 58 }, L2(12), 875000, 3200000 },
+ { 1, { 1190400, HFPLL, 1, 62 }, L2(12), 895000, 3200000 },
+ { 1, { 1267200, HFPLL, 1, 66 }, L2(12), 915000, 3200000 },
+ { 1, { 1344000, HFPLL, 1, 70 }, L2(12), 930000, 3200000 },
+ { 1, { 1420800, HFPLL, 1, 74 }, L2(16), 945000, 3200000 },
+ { 1, { 1497600, HFPLL, 1, 78 }, L2(16), 960000, 3200000 },
+ { 1, { 1574400, HFPLL, 1, 82 }, L2(16), 975000, 3200000 },
+ { 1, { 1651200, HFPLL, 1, 86 }, L2(16), 990000, 3200000 },
+ { 1, { 1728000, HFPLL, 1, 90 }, L2(16), 1000000, 3200000 },
+ { 0, { 0 } }
+};
+
+static struct acpu_level acpu_freq_tbl_pvs3[] __initdata = {
+ { 1, { 300000, PLL_0, 0, 0 }, L2(0), 825000, 400000 },
+ { 0, { 345600, HFPLL, 2, 36 }, L2(3), 825000, 3200000 },
+ { 1, { 422400, HFPLL, 2, 44 }, L2(3), 825000, 3200000 },
+ { 0, { 499200, HFPLL, 2, 52 }, L2(6), 825000, 3200000 },
+ { 1, { 576000, HFPLL, 1, 30 }, L2(6), 825000, 3200000 },
+ { 1, { 652800, HFPLL, 1, 34 }, L2(6), 825000, 3200000 },
+ { 0, { 729600, HFPLL, 1, 38 }, L2(6), 825000, 3200000 },
+ { 1, { 806400, HFPLL, 1, 42 }, L2(8), 825000, 3200000 },
+ { 1, { 883200, HFPLL, 1, 46 }, L2(8), 825000, 3200000 },
+ { 1, { 960000, HFPLL, 1, 50 }, L2(8), 835000, 3200000 },
+ { 1, { 1036800, HFPLL, 1, 54 }, L2(8), 855000, 3200000 },
+ { 1, { 1113600, HFPLL, 1, 58 }, L2(12), 875000, 3200000 },
+ { 1, { 1190400, HFPLL, 1, 62 }, L2(12), 895000, 3200000 },
+ { 1, { 1267200, HFPLL, 1, 66 }, L2(12), 915000, 3200000 },
+ { 1, { 1344000, HFPLL, 1, 70 }, L2(12), 930000, 3200000 },
+ { 1, { 1420800, HFPLL, 1, 74 }, L2(16), 945000, 3200000 },
+ { 1, { 1497600, HFPLL, 1, 78 }, L2(16), 960000, 3200000 },
+ { 1, { 1574400, HFPLL, 1, 82 }, L2(16), 975000, 3200000 },
+ { 1, { 1651200, HFPLL, 1, 86 }, L2(16), 990000, 3200000 },
+ { 1, { 1728000, HFPLL, 1, 90 }, L2(16), 1000000, 3200000 },
+ { 0, { 0 } }
+};
+
+static struct acpu_level acpu_freq_tbl_pvs4[] __initdata = {
+ { 1, { 300000, PLL_0, 0, 0 }, L2(0), 825000, 400000 },
+ { 0, { 345600, HFPLL, 2, 36 }, L2(3), 825000, 3200000 },
+ { 1, { 422400, HFPLL, 2, 44 }, L2(3), 825000, 3200000 },
+ { 0, { 499200, HFPLL, 2, 52 }, L2(6), 825000, 3200000 },
+ { 1, { 576000, HFPLL, 1, 30 }, L2(6), 825000, 3200000 },
+ { 1, { 652800, HFPLL, 1, 34 }, L2(6), 825000, 3200000 },
+ { 0, { 729600, HFPLL, 1, 38 }, L2(6), 825000, 3200000 },
+ { 1, { 806400, HFPLL, 1, 42 }, L2(8), 825000, 3200000 },
+ { 1, { 883200, HFPLL, 1, 46 }, L2(8), 825000, 3200000 },
+ { 1, { 960000, HFPLL, 1, 50 }, L2(8), 825000, 3200000 },
+ { 1, { 1036800, HFPLL, 1, 54 }, L2(8), 825000, 3200000 },
+ { 1, { 1113600, HFPLL, 1, 58 }, L2(12), 835000, 3200000 },
+ { 1, { 1190400, HFPLL, 1, 62 }, L2(12), 855000, 3200000 },
+ { 1, { 1267200, HFPLL, 1, 66 }, L2(12), 870000, 3200000 },
+ { 1, { 1344000, HFPLL, 1, 70 }, L2(12), 885000, 3200000 },
+ { 1, { 1420800, HFPLL, 1, 74 }, L2(16), 900000, 3200000 },
+ { 1, { 1497600, HFPLL, 1, 78 }, L2(16), 910000, 3200000 },
+ { 1, { 1574400, HFPLL, 1, 82 }, L2(16), 925000, 3200000 },
+ { 1, { 1651200, HFPLL, 1, 86 }, L2(16), 940000, 3200000 },
+ { 1, { 1728000, HFPLL, 1, 90 }, L2(16), 950000, 3200000 },
+ { 0, { 0 } }
+};
+
+static struct pvs_table pvs_tables[NUM_SPEED_BINS][NUM_PVS] __initdata = {
+ [0][0] = { acpu_freq_tbl_pvs0, sizeof(acpu_freq_tbl_pvs0) },
+ [0][1] = { acpu_freq_tbl_pvs1, sizeof(acpu_freq_tbl_pvs1) },
+ [0][2] = { acpu_freq_tbl_pvs2, sizeof(acpu_freq_tbl_pvs2) },
+ [0][3] = { acpu_freq_tbl_pvs3, sizeof(acpu_freq_tbl_pvs3) },
+ [0][4] = { acpu_freq_tbl_pvs4, sizeof(acpu_freq_tbl_pvs4) },
};
static struct acpuclk_krait_params acpuclk_8974_params __initdata = {
@@ -184,11 +269,41 @@
.l2_freq_tbl_size = sizeof(l2_freq_tbl),
.bus_scale = &bus_scale_data,
.pte_efuse_phys = 0xFC4B80B0,
+ .get_bin_info = get_krait_bin_format_b,
.stby_khz = 300000,
};
+static void __init apply_l2_workaround(void)
+{
+ static struct l2_level resticted_l2_tbl[] __initdata = {
+ [0] = { { 300000, PLL_0, 0, 0 }, LVL_LOW, 1050000, 0 },
+ [1] = { { 1497600, HFPLL, 1, 78 }, LVL_HIGH, 1050000, 7 },
+ { }
+ };
+ struct acpu_level *l;
+ int s, p;
+
+ for (s = 0; s < NUM_SPEED_BINS; s++)
+ for (p = 0; p < NUM_PVS; p++)
+ for (l = pvs_tables[s][p].table; l && l->speed.khz; l++)
+ l->l2_level = l->l2_level > 5 ? 1 : 0;
+
+ acpuclk_8974_params.l2_freq_tbl = resticted_l2_tbl;
+ acpuclk_8974_params.l2_freq_tbl_size = sizeof(resticted_l2_tbl);
+}
+
static int __init acpuclk_8974_probe(struct platform_device *pdev)
{
+ /*
+ * 8974 hardware revisions older than v1.2 may experience L2 parity
+ * errors when running at some performance points between 300MHz
+ * and 1497.6MHz (non-inclusive), or when vdd_mx is less than 1.05V.
+ * Restrict L2 operation to safe performance points on these devices.
+ */
+ if (SOCINFO_VERSION_MAJOR(socinfo_get_version()) < 2 &&
+ SOCINFO_VERSION_MINOR(socinfo_get_version()) < 2)
+ apply_l2_workaround();
+
return acpuclk_krait_init(&pdev->dev, &acpuclk_8974_params);
}
diff --git a/arch/arm/mach-msm/acpuclock-krait.c b/arch/arm/mach-msm/acpuclock-krait.c
index 10c4d6c..9566cea 100644
--- a/arch/arm/mach-msm/acpuclock-krait.c
+++ b/arch/arm/mach-msm/acpuclock-krait.c
@@ -1,5 +1,5 @@
/*
- * 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
@@ -1019,7 +1019,7 @@
.notifier_call = acpuclk_cpu_callback,
};
-static const int krait_needs_vmin(void)
+static const int __init krait_needs_vmin(void)
{
switch (read_cpuid_id()) {
case 0x511F04D0: /* KR28M2A20 */
@@ -1031,7 +1031,7 @@
};
}
-static void krait_apply_vmin(struct acpu_level *tbl)
+static void __init krait_apply_vmin(struct acpu_level *tbl)
{
for (; tbl->speed.khz != 0; tbl++) {
if (tbl->vdd_core < 1150000)
@@ -1040,62 +1040,78 @@
}
}
-static int __init get_speed_bin(u32 pte_efuse)
+void __init get_krait_bin_format_a(void __iomem *base, struct bin_info *bin)
{
- uint32_t speed_bin;
+ u32 pte_efuse = readl_relaxed(base);
- speed_bin = pte_efuse & 0xF;
- if (speed_bin == 0xF)
- speed_bin = (pte_efuse >> 4) & 0xF;
+ bin->speed = pte_efuse & 0xF;
+ if (bin->speed == 0xF)
+ bin->speed = (pte_efuse >> 4) & 0xF;
+ bin->speed_valid = bin->speed != 0xF;
- if (speed_bin == 0xF) {
- speed_bin = 0;
- dev_warn(drv.dev, "SPEED BIN: Defaulting to %d\n", speed_bin);
- } else {
- dev_info(drv.dev, "SPEED BIN: %d\n", speed_bin);
- }
-
- return speed_bin;
+ bin->pvs = (pte_efuse >> 10) & 0x7;
+ if (bin->pvs == 0x7)
+ bin->pvs = (pte_efuse >> 13) & 0x7;
+ bin->pvs_valid = bin->pvs != 0x7;
}
-static int __init get_pvs_bin(u32 pte_efuse)
+void __init get_krait_bin_format_b(void __iomem *base, struct bin_info *bin)
{
- uint32_t pvs_bin;
+ u32 pte_efuse, redundant_sel;
- pvs_bin = (pte_efuse >> 10) & 0x7;
- if (pvs_bin == 0x7)
- pvs_bin = (pte_efuse >> 13) & 0x7;
+ pte_efuse = readl_relaxed(base);
+ redundant_sel = (pte_efuse >> 24) & 0x7;
+ bin->speed = pte_efuse & 0x7;
+ bin->pvs = (pte_efuse >> 6) & 0x7;
- if (pvs_bin == 0x7) {
- pvs_bin = 0;
- dev_warn(drv.dev, "ACPU PVS: Defaulting to %d\n", pvs_bin);
- } else {
- dev_info(drv.dev, "ACPU PVS: %d\n", pvs_bin);
+ switch (redundant_sel) {
+ case 1:
+ bin->speed = (pte_efuse >> 27) & 0x7;
+ break;
+ case 2:
+ bin->pvs = (pte_efuse >> 27) & 0x7;
+ break;
}
+ bin->speed_valid = true;
- return pvs_bin;
+ /* Check PVS_BLOW_STATUS */
+ pte_efuse = readl_relaxed(base + 0x4);
+ bin->pvs_valid = !!(pte_efuse & BIT(21));
}
-static struct pvs_table * __init select_freq_plan(u32 pte_efuse_phys,
- struct pvs_table (*pvs_tables)[NUM_PVS])
+static struct pvs_table * __init select_freq_plan(
+ const struct acpuclk_krait_params *params)
{
- void __iomem *pte_efuse;
- u32 pte_efuse_val;
+ void __iomem *pte_efuse_base;
+ struct bin_info bin;
- pte_efuse = ioremap(pte_efuse_phys, 4);
- if (!pte_efuse) {
- dev_err(drv.dev, "Unable to map QFPROM base\n");
+ pte_efuse_base = ioremap(params->pte_efuse_phys, 8);
+ if (!pte_efuse_base) {
+ dev_err(drv.dev, "Unable to map PTE eFuse base\n");
return NULL;
}
+ params->get_bin_info(pte_efuse_base, &bin);
+ iounmap(pte_efuse_base);
- pte_efuse_val = readl_relaxed(pte_efuse);
- iounmap(pte_efuse);
+ if (bin.speed_valid) {
+ drv.speed_bin = bin.speed;
+ dev_info(drv.dev, "SPEED BIN: %d\n", drv.speed_bin);
+ } else {
+ drv.speed_bin = 0;
+ dev_warn(drv.dev, "SPEED BIN: Defaulting to %d\n",
+ drv.speed_bin);
+ }
- /* Select frequency tables. */
- drv.speed_bin = get_speed_bin(pte_efuse_val);
- drv.pvs_bin = get_pvs_bin(pte_efuse_val);
+ if (bin.pvs_valid) {
+ drv.pvs_bin = bin.pvs;
+ dev_info(drv.dev, "ACPU PVS: %d\n", drv.pvs_bin);
+ } else {
+ drv.pvs_bin = 0;
+ dev_warn(drv.dev, "ACPU PVS: Defaulting to %d\n",
+ drv.pvs_bin);
+ }
- return &pvs_tables[drv.speed_bin][drv.pvs_bin];
+ return ¶ms->pvs_tables[drv.speed_bin][drv.pvs_bin];
}
static void __init drv_data_init(struct device *dev,
@@ -1124,7 +1140,7 @@
GFP_KERNEL);
BUG_ON(!drv.bus_scale->usecase);
- pvs = select_freq_plan(params->pte_efuse_phys, params->pvs_tables);
+ pvs = select_freq_plan(params);
BUG_ON(!pvs->table);
drv.acpu_freq_tbl = kmemdup(pvs->table, pvs->size, GFP_KERNEL);
diff --git a/arch/arm/mach-msm/acpuclock-krait.h b/arch/arm/mach-msm/acpuclock-krait.h
index ca8013e..00f64fc 100644
--- a/arch/arm/mach-msm/acpuclock-krait.h
+++ b/arch/arm/mach-msm/acpuclock-krait.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011-2012, Code Aurora Forum. 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
@@ -152,7 +152,7 @@
struct acpu_level {
const int use_for_scaling;
const struct core_speed speed;
- const unsigned int l2_level;
+ unsigned int l2_level;
int vdd_core;
int ua_core;
unsigned int avsdscr_setting;
@@ -227,6 +227,20 @@
};
/**
+ * struct bin_info - Hardware speed and voltage binning info.
+ * @speed_valid: @speed field is valid
+ * @pvs_valid: @pvs field is valid
+ * @speed: Speed bin ID
+ * @pvs: PVS bin ID
+ */
+struct bin_info {
+ bool speed_valid;
+ bool pvs_valid;
+ int speed;
+ int pvs;
+};
+
+/**
* struct pvs_table - CPU performance level table and size.
* @table: CPU performance level table
* @size: sizeof(@table)
@@ -247,6 +261,7 @@
* @l2_freq_tbl: L2 frequency table.
* @l2_freq_tbl_size: Size of @l2_freq_tbl.
* @pte_efuse_phys: Physical address of PTE EFUSE.
+ * @get_bin_info: Function to populate bin_info from pte_efuse.
* @bus_scale: MSM bus driver parameters.
* @stby_khz: KHz value corresponding to an always-on clock source.
*/
@@ -258,6 +273,7 @@
struct l2_level *l2_freq_tbl;
size_t l2_freq_tbl_size;
phys_addr_t pte_efuse_phys;
+ void (*get_bin_info)(void __iomem *base, struct bin_info *bin);
struct msm_bus_scale_pdata *bus_scale;
unsigned long stby_khz;
};
@@ -297,6 +313,16 @@
};
/**
+ * get_krait_bin_format_a - Populate bin_info from a 'Format A' pte_efuse
+ */
+void __init get_krait_bin_format_a(void __iomem *base, struct bin_info *bin);
+
+/**
+ * get_krait_bin_format_b - Populate bin_info from a 'Format B' pte_efuse
+ */
+void __init get_krait_bin_format_b(void __iomem *base, struct bin_info *bin);
+
+/**
* acpuclk_krait_init - Initialize the Krait CPU clock driver give SoC params.
*/
extern int acpuclk_krait_init(struct device *dev,
diff --git a/arch/arm/mach-msm/bam_dmux.c b/arch/arm/mach-msm/bam_dmux.c
index 040660a..9da5436 100644
--- a/arch/arm/mach-msm/bam_dmux.c
+++ b/arch/arm/mach-msm/bam_dmux.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, Code Aurora Forum. 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
@@ -891,6 +891,7 @@
write_fail3:
kfree(pkt);
write_fail2:
+ skb_pull(skb, sizeof(struct bam_mux_hdr));
if (new_skb)
dev_kfree_skb_any(new_skb);
write_fail:
diff --git a/arch/arm/mach-msm/board-8064-bt.c b/arch/arm/mach-msm/board-8064-bt.c
index a8ae9fa..963b1a3 100644
--- a/arch/arm/mach-msm/board-8064-bt.c
+++ b/arch/arm/mach-msm/board-8064-bt.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-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
@@ -121,7 +121,7 @@
const struct bahama_config_register *p;
- u8 version;
+ int version;
const struct bahama_config_register v10_bt_on[] = {
{ 0xE9, 0x00, 0xFF },
@@ -203,7 +203,7 @@
u8 offset = 0; /* index into bahama configs */
on = on ? 1 : 0;
version = marimba_read_bahama_ver(&config);
- if ((int)version < 0 || version == BAHAMA_VER_UNSUPPORTED) {
+ if (version < 0 || version == BAHAMA_VER_UNSUPPORTED) {
dev_err(&msm_bt_power_device.dev,
"%s : Bahama version read Error, version = %d\n",
__func__, version);
@@ -237,10 +237,9 @@
__func__, (p+i)->reg,
value, (p+i)->mask);
value = 0;
- rc = marimba_read_bit_mask(&config,
+ if (marimba_read_bit_mask(&config,
(p+i)->reg, &value,
- sizeof((p+i)->value), (p+i)->mask);
- if (rc < 0)
+ sizeof((p+i)->value), (p+i)->mask) < 0)
dev_err(&msm_bt_power_device.dev,
"%s marimba_read_bit_mask- error",
__func__);
@@ -298,15 +297,14 @@
dev_err(&msm_bt_power_device.dev,
"%s: could not %sable regulator %s: %d\n",
__func__, "dis", bt_vregs[i].name, rc);
- goto reg_disable;
}
}
return rc;
reg_disable:
pr_err("bluetooth_switch_regulators - FAIL!!!!\n");
- while (i) {
- if (on) {
+ if (on) {
+ while (i) {
i--;
regulator_disable(bt_vregs[i].reg);
regulator_put(bt_vregs[i].reg);
@@ -419,8 +417,9 @@
int rc = 0;
const char *id = "BTPW";
int cid = 0;
+ int bt_state = 0;
+ struct marimba config = { .mod_id = SLAVE_ID_BAHAMA};
- pr_debug("bluetooth_power entered....\n");
cid = adie_get_detected_connectivity_type();
if (cid != BAHAMA_ID) {
pr_err("%s: unexpected adie connectivity type: %d\n",
@@ -429,6 +428,7 @@
}
if (on) {
+ pr_debug("%s: Powering up the BT module.\n", __func__);
rc = bluetooth_switch_regulators(on);
if (rc < 0) {
pr_err("%s: bluetooth_switch_regulators rc = %d",
@@ -437,11 +437,13 @@
}
/* UART GPIO configuration to be done by by UART module*/
/*Setup BT clocks*/
+ pr_debug("%s: Voting for the 19.2MHz clock\n", __func__);
bt_clock = msm_xo_get(MSM_XO_TCXO_A2, id);
if (IS_ERR(bt_clock)) {
rc = PTR_ERR(bt_clock);
pr_err("%s: failed to get the handle for A2(%d)\n",
__func__, rc);
+ goto fail_power;
}
rc = msm_xo_mode_vote(bt_clock, MSM_XO_MODE_ON);
if (rc < 0) {
@@ -451,20 +453,23 @@
msleep(20);
/*I2C config for Bahama*/
+ pr_debug("%s: BT Turn On sequence in-progress.\n", __func__);
rc = bahama_bt(1);
if (rc < 0) {
pr_err("%s: bahama_bt rc = %d", __func__, rc);
- goto fail_i2c;
+ goto fail_xo_vote;
}
msleep(20);
/*setup BT PCM lines*/
+ pr_debug("%s: Configuring PCM lines.\n", __func__);
rc = config_pcm(BT_PCM_ON);
if (rc < 0) {
- pr_err("%s: config_pcm , rc =%d\n",
+ pr_err("%s: config_pcm , rc = %d\n",
__func__, rc);
- goto fail_power;
+ goto fail_i2c;
}
+ pr_debug("%s: BT Turn On complete.\n", __func__);
/* TO DO - Enable PIN CTRL */
/*
rc = msm_xo_mode_vote(bt_clock, MSM_XO_MODE_PIN_CTRL);
@@ -474,9 +479,12 @@
goto fail_xo_vote;
} */
} else {
- rc = bahama_bt(0);
- if (rc < 0)
- pr_err("%s: bahama_bt rc = %d", __func__, rc);
+ pr_debug("%s: Turning BT Off.\n", __func__);
+ bt_state = marimba_get_bt_status(&config);
+ if (!bt_state) {
+ pr_err("%s: BT is already turned OFF.\n", __func__);
+ return 0;
+ }
rc = config_pcm(BT_PCM_OFF);
if (rc < 0) {
@@ -484,19 +492,21 @@
__func__, rc);
}
fail_i2c:
- pr_err("bluetooth_power...FAIL_I2C\n");
-
+ rc = bahama_bt(0);
+ if (rc < 0)
+ pr_err("%s: bahama_bt rc = %d", __func__, rc);
fail_xo_vote:
- pr_err("bluetooth_power...FAIL_XO_VOTE\n");
+ pr_debug("%s: Voting off the 19.2MHz clk\n", __func__);
msm_xo_put(bt_clock);
fail_power:
- pr_err("bluetooth_power...FAIL POWER\n");
+ pr_debug("%s: Switching off voltage regulators.\n", __func__);
rc = bluetooth_switch_regulators(0);
if (rc < 0) {
pr_err("%s: switch_regulators : rc = %d",\
__func__, rc);
goto exit;
}
+ pr_debug("%s: BT Power Off complete.\n", __func__);
}
return rc;
exit:
diff --git a/arch/arm/mach-msm/board-8064-gpiomux.c b/arch/arm/mach-msm/board-8064-gpiomux.c
index 9e34f47..57d1b6c 100644
--- a/arch/arm/mach-msm/board-8064-gpiomux.c
+++ b/arch/arm/mach-msm/board-8064-gpiomux.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, Code Aurora Forum. 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
@@ -74,259 +74,207 @@
#endif
#ifdef CONFIG_MSM_VCAP
-static struct gpiomux_setting gpio_vcap_config[] = {
- {
- .func = GPIOMUX_FUNC_GPIO,
- .drv = GPIOMUX_DRV_2MA,
- .pull = GPIOMUX_PULL_DOWN,
- },
- {
- .func = GPIOMUX_FUNC_1,
- .drv = GPIOMUX_DRV_2MA,
- .pull = GPIOMUX_PULL_DOWN,
- },
- {
- .func = GPIOMUX_FUNC_2,
- .drv = GPIOMUX_DRV_2MA,
- .pull = GPIOMUX_PULL_DOWN,
- },
- {
- .func = GPIOMUX_FUNC_3,
- .drv = GPIOMUX_DRV_2MA,
- .pull = GPIOMUX_PULL_DOWN,
- },
- {
- .func = GPIOMUX_FUNC_4,
- .drv = GPIOMUX_DRV_2MA,
- .pull = GPIOMUX_PULL_DOWN,
- },
- {
- .func = GPIOMUX_FUNC_5,
- .drv = GPIOMUX_DRV_2MA,
- .pull = GPIOMUX_PULL_DOWN,
- },
- {
- .func = GPIOMUX_FUNC_6,
- .drv = GPIOMUX_DRV_2MA,
- .pull = GPIOMUX_PULL_DOWN,
- },
- {
- .func = GPIOMUX_FUNC_7,
- .drv = GPIOMUX_DRV_2MA,
- .pull = GPIOMUX_PULL_DOWN,
- },
- {
- .func = GPIOMUX_FUNC_8,
- .drv = GPIOMUX_DRV_2MA,
- .pull = GPIOMUX_PULL_DOWN,
- },
- {
- .func = GPIOMUX_FUNC_9,
- .drv = GPIOMUX_DRV_2MA,
- .pull = GPIOMUX_PULL_DOWN,
- },
- {
- .func = GPIOMUX_FUNC_A,
- .drv = GPIOMUX_DRV_2MA,
- .pull = GPIOMUX_PULL_DOWN,
- },
+static struct gpiomux_setting gpio_vcap_config = {
+ .func = GPIOMUX_FUNC_GPIO,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_DOWN,
};
struct msm_gpiomux_config vcap_configs[] = {
{
.gpio = 20,
.settings = {
- [GPIOMUX_SUSPENDED] = &gpio_vcap_config[7],
- [GPIOMUX_ACTIVE] = &gpio_vcap_config[7],
+ [GPIOMUX_SUSPENDED] = &gpio_vcap_config,
+ [GPIOMUX_ACTIVE] = &gpio_vcap_config,
}
},
{
.gpio = 25,
.settings = {
- [GPIOMUX_SUSPENDED] = &gpio_vcap_config[2],
- [GPIOMUX_ACTIVE] = &gpio_vcap_config[2],
+ [GPIOMUX_SUSPENDED] = &gpio_vcap_config,
+ [GPIOMUX_ACTIVE] = &gpio_vcap_config,
}
},
{
.gpio = 24,
.settings = {
- [GPIOMUX_SUSPENDED] = &gpio_vcap_config[1],
- [GPIOMUX_ACTIVE] = &gpio_vcap_config[1],
+ [GPIOMUX_SUSPENDED] = &gpio_vcap_config,
+ [GPIOMUX_ACTIVE] = &gpio_vcap_config,
}
},
{
.gpio = 23,
.settings = {
- [GPIOMUX_SUSPENDED] = &gpio_vcap_config[2],
- [GPIOMUX_ACTIVE] = &gpio_vcap_config[2],
+ [GPIOMUX_SUSPENDED] = &gpio_vcap_config,
+ [GPIOMUX_ACTIVE] = &gpio_vcap_config,
}
},
{
.gpio = 19,
.settings = {
- [GPIOMUX_SUSPENDED] = &gpio_vcap_config[8],
- [GPIOMUX_ACTIVE] = &gpio_vcap_config[8],
+ [GPIOMUX_SUSPENDED] = &gpio_vcap_config,
+ [GPIOMUX_ACTIVE] = &gpio_vcap_config,
}
},
{
.gpio = 22,
.settings = {
- [GPIOMUX_SUSPENDED] = &gpio_vcap_config[2],
- [GPIOMUX_ACTIVE] = &gpio_vcap_config[2],
+ [GPIOMUX_SUSPENDED] = &gpio_vcap_config,
+ [GPIOMUX_ACTIVE] = &gpio_vcap_config,
}
},
{
.gpio = 21,
.settings = {
- [GPIOMUX_SUSPENDED] = &gpio_vcap_config[7],
- [GPIOMUX_ACTIVE] = &gpio_vcap_config[7],
+ [GPIOMUX_SUSPENDED] = &gpio_vcap_config,
+ [GPIOMUX_ACTIVE] = &gpio_vcap_config,
}
},
{
.gpio = 12,
.settings = {
- [GPIOMUX_SUSPENDED] = &gpio_vcap_config[6],
- [GPIOMUX_ACTIVE] = &gpio_vcap_config[6],
+ [GPIOMUX_SUSPENDED] = &gpio_vcap_config,
+ [GPIOMUX_ACTIVE] = &gpio_vcap_config,
}
},
{
.gpio = 18,
.settings = {
- [GPIOMUX_SUSPENDED] = &gpio_vcap_config[9],
- [GPIOMUX_ACTIVE] = &gpio_vcap_config[9],
+ [GPIOMUX_SUSPENDED] = &gpio_vcap_config,
+ [GPIOMUX_ACTIVE] = &gpio_vcap_config,
}
},
{
.gpio = 11,
.settings = {
- [GPIOMUX_SUSPENDED] = &gpio_vcap_config[10],
- [GPIOMUX_ACTIVE] = &gpio_vcap_config[10],
+ [GPIOMUX_SUSPENDED] = &gpio_vcap_config,
+ [GPIOMUX_ACTIVE] = &gpio_vcap_config,
}
},
{
.gpio = 10,
.settings = {
- [GPIOMUX_SUSPENDED] = &gpio_vcap_config[9],
- [GPIOMUX_ACTIVE] = &gpio_vcap_config[9],
+ [GPIOMUX_SUSPENDED] = &gpio_vcap_config,
+ [GPIOMUX_ACTIVE] = &gpio_vcap_config,
}
},
{
.gpio = 9,
.settings = {
- [GPIOMUX_SUSPENDED] = &gpio_vcap_config[2],
- [GPIOMUX_ACTIVE] = &gpio_vcap_config[2],
+ [GPIOMUX_SUSPENDED] = &gpio_vcap_config,
+ [GPIOMUX_ACTIVE] = &gpio_vcap_config,
}
},
{
.gpio = 26,
.settings = {
- [GPIOMUX_SUSPENDED] = &gpio_vcap_config[1],
- [GPIOMUX_ACTIVE] = &gpio_vcap_config[1],
+ [GPIOMUX_SUSPENDED] = &gpio_vcap_config,
+ [GPIOMUX_ACTIVE] = &gpio_vcap_config,
}
},
{
.gpio = 8,
.settings = {
- [GPIOMUX_SUSPENDED] = &gpio_vcap_config[3],
- [GPIOMUX_ACTIVE] = &gpio_vcap_config[3],
+ [GPIOMUX_SUSPENDED] = &gpio_vcap_config,
+ [GPIOMUX_ACTIVE] = &gpio_vcap_config,
}
},
{
.gpio = 7,
.settings = {
- [GPIOMUX_SUSPENDED] = &gpio_vcap_config[7],
- [GPIOMUX_ACTIVE] = &gpio_vcap_config[7],
+ [GPIOMUX_SUSPENDED] = &gpio_vcap_config,
+ [GPIOMUX_ACTIVE] = &gpio_vcap_config,
}
},
{
.gpio = 6,
.settings = {
- [GPIOMUX_SUSPENDED] = &gpio_vcap_config[7],
- [GPIOMUX_ACTIVE] = &gpio_vcap_config[7],
+ [GPIOMUX_SUSPENDED] = &gpio_vcap_config,
+ [GPIOMUX_ACTIVE] = &gpio_vcap_config,
}
},
{
.gpio = 80,
.settings = {
- [GPIOMUX_SUSPENDED] = &gpio_vcap_config[2],
- [GPIOMUX_ACTIVE] = &gpio_vcap_config[2],
+ [GPIOMUX_SUSPENDED] = &gpio_vcap_config,
+ [GPIOMUX_ACTIVE] = &gpio_vcap_config,
}
},
{
.gpio = 86,
.settings = {
- [GPIOMUX_SUSPENDED] = &gpio_vcap_config[1],
- [GPIOMUX_ACTIVE] = &gpio_vcap_config[1],
+ [GPIOMUX_SUSPENDED] = &gpio_vcap_config,
+ [GPIOMUX_ACTIVE] = &gpio_vcap_config,
}
},
{
.gpio = 85,
.settings = {
- [GPIOMUX_SUSPENDED] = &gpio_vcap_config[4],
- [GPIOMUX_ACTIVE] = &gpio_vcap_config[4],
+ [GPIOMUX_SUSPENDED] = &gpio_vcap_config,
+ [GPIOMUX_ACTIVE] = &gpio_vcap_config,
}
},
{
.gpio = 84,
.settings = {
- [GPIOMUX_SUSPENDED] = &gpio_vcap_config[3],
- [GPIOMUX_ACTIVE] = &gpio_vcap_config[3],
+ [GPIOMUX_SUSPENDED] = &gpio_vcap_config,
+ [GPIOMUX_ACTIVE] = &gpio_vcap_config,
}
},
{
.gpio = 5,
.settings = {
- [GPIOMUX_SUSPENDED] = &gpio_vcap_config[2],
- [GPIOMUX_ACTIVE] = &gpio_vcap_config[2],
+ [GPIOMUX_SUSPENDED] = &gpio_vcap_config,
+ [GPIOMUX_ACTIVE] = &gpio_vcap_config,
}
},
{
.gpio = 4,
.settings = {
- [GPIOMUX_SUSPENDED] = &gpio_vcap_config[3],
- [GPIOMUX_ACTIVE] = &gpio_vcap_config[3],
+ [GPIOMUX_SUSPENDED] = &gpio_vcap_config,
+ [GPIOMUX_ACTIVE] = &gpio_vcap_config,
}
},
{
.gpio = 3,
.settings = {
- [GPIOMUX_SUSPENDED] = &gpio_vcap_config[6],
- [GPIOMUX_ACTIVE] = &gpio_vcap_config[6],
+ [GPIOMUX_SUSPENDED] = &gpio_vcap_config,
+ [GPIOMUX_ACTIVE] = &gpio_vcap_config,
}
},
{
.gpio = 2,
.settings = {
- [GPIOMUX_SUSPENDED] = &gpio_vcap_config[5],
- [GPIOMUX_ACTIVE] = &gpio_vcap_config[5],
+ [GPIOMUX_SUSPENDED] = &gpio_vcap_config,
+ [GPIOMUX_ACTIVE] = &gpio_vcap_config,
}
},
{
.gpio = 82,
.settings = {
- [GPIOMUX_SUSPENDED] = &gpio_vcap_config[4],
- [GPIOMUX_ACTIVE] = &gpio_vcap_config[4],
+ [GPIOMUX_SUSPENDED] = &gpio_vcap_config,
+ [GPIOMUX_ACTIVE] = &gpio_vcap_config,
}
},
{
.gpio = 83,
.settings = {
- [GPIOMUX_SUSPENDED] = &gpio_vcap_config[4],
- [GPIOMUX_ACTIVE] = &gpio_vcap_config[4],
+ [GPIOMUX_SUSPENDED] = &gpio_vcap_config,
+ [GPIOMUX_ACTIVE] = &gpio_vcap_config,
}
},
{
.gpio = 87,
.settings = {
- [GPIOMUX_SUSPENDED] = &gpio_vcap_config[2],
- [GPIOMUX_ACTIVE] = &gpio_vcap_config[2],
+ [GPIOMUX_SUSPENDED] = &gpio_vcap_config,
+ [GPIOMUX_ACTIVE] = &gpio_vcap_config,
}
},
{
.gpio = 13,
.settings = {
- [GPIOMUX_SUSPENDED] = &gpio_vcap_config[6],
- [GPIOMUX_ACTIVE] = &gpio_vcap_config[6],
+ [GPIOMUX_SUSPENDED] = &gpio_vcap_config,
+ [GPIOMUX_ACTIVE] = &gpio_vcap_config,
}
},
};
@@ -917,38 +865,38 @@
static struct gpiomux_setting ap2mdm_cfg = {
.func = GPIOMUX_FUNC_GPIO,
- .drv = GPIOMUX_DRV_8MA,
+ .drv = GPIOMUX_DRV_4MA,
.pull = GPIOMUX_PULL_DOWN,
};
static struct gpiomux_setting mdm2ap_status_cfg = {
.func = GPIOMUX_FUNC_GPIO,
- .drv = GPIOMUX_DRV_8MA,
+ .drv = GPIOMUX_DRV_2MA,
.pull = GPIOMUX_PULL_DOWN,
};
static struct gpiomux_setting mdm2ap_errfatal_cfg = {
.func = GPIOMUX_FUNC_GPIO,
- .drv = GPIOMUX_DRV_16MA,
+ .drv = GPIOMUX_DRV_2MA,
.pull = GPIOMUX_PULL_DOWN,
};
static struct gpiomux_setting mdm2ap_pblrdy = {
.func = GPIOMUX_FUNC_GPIO,
- .drv = GPIOMUX_DRV_16MA,
+ .drv = GPIOMUX_DRV_2MA,
.pull = GPIOMUX_PULL_DOWN,
};
static struct gpiomux_setting ap2mdm_soft_reset_cfg = {
.func = GPIOMUX_FUNC_GPIO,
- .drv = GPIOMUX_DRV_8MA,
+ .drv = GPIOMUX_DRV_4MA,
.pull = GPIOMUX_PULL_DOWN,
};
static struct gpiomux_setting ap2mdm_wakeup = {
.func = GPIOMUX_FUNC_GPIO,
- .drv = GPIOMUX_DRV_8MA,
+ .drv = GPIOMUX_DRV_4MA,
.pull = GPIOMUX_PULL_DOWN,
};
@@ -1621,6 +1569,10 @@
} else {
msm_gpiomux_install(apq8064_slimbus_config,
ARRAY_SIZE(apq8064_slimbus_config));
+ }
+
+ if (!(machine_is_mpq8064_cdp() || machine_is_mpq8064_hrd() ||
+ machine_is_mpq8064_dtv()) && !machine_is_apq8064_mtp()) {
msm_gpiomux_install(apq8064_gsbi1_i2c_8ma_configs,
ARRAY_SIZE(apq8064_gsbi1_i2c_8ma_configs));
}
@@ -1705,8 +1657,11 @@
ARRAY_SIZE(apq8064_sdc4_configs));
#endif
- msm_gpiomux_install(apq8064_sdc3_configs,
- ARRAY_SIZE(apq8064_sdc3_configs));
+ if (!(machine_is_mpq8064_cdp() || machine_is_mpq8064_hrd() ||
+ machine_is_mpq8064_dtv())) {
+ msm_gpiomux_install(apq8064_sdc3_configs,
+ ARRAY_SIZE(apq8064_sdc3_configs));
+ }
if (machine_is_mpq8064_hrd() || machine_is_mpq8064_dtv())
msm_gpiomux_install(mpq8064_uartdm_configs,
ARRAY_SIZE(mpq8064_uartdm_configs));
diff --git a/arch/arm/mach-msm/board-8064-regulator.c b/arch/arm/mach-msm/board-8064-regulator.c
index a6f0e32..57ecc27 100644
--- a/arch/arm/mach-msm/board-8064-regulator.c
+++ b/arch/arm/mach-msm/board-8064-regulator.c
@@ -1,5 +1,5 @@
/*
- * 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
@@ -190,6 +190,7 @@
REGULATOR_SUPPLY("vddp", "0-0048"),
REGULATOR_SUPPLY("hdmi_lvl_tsl", "hdmi_msm.0"),
REGULATOR_SUPPLY("vdd-io", "spi0.2"),
+ REGULATOR_SUPPLY("sata_pmp_pwr", "msm_sata.0"),
};
VREG_CONSUMERS(S5) = {
REGULATOR_SUPPLY("8921_s5", NULL),
@@ -260,12 +261,15 @@
REGULATOR_SUPPLY("dsi1_vccs_3p3v", "mipi_dsi.1"),
REGULATOR_SUPPLY("hdmi_mux_vdd", "hdmi_msm.0"),
REGULATOR_SUPPLY("pcie_ext_3p3v", "msm_pcie"),
- REGULATOR_SUPPLY("sata_ext_3p3v", "ahci.0"),
};
VREG_CONSUMERS(EXT_TS_SW) = {
REGULATOR_SUPPLY("ext_ts_sw", NULL),
REGULATOR_SUPPLY("vdd_ana", "3-005b"),
};
+VREG_CONSUMERS(EXT_SATA_PWR) = {
+ REGULATOR_SUPPLY("ext_sata_pwr", NULL),
+ REGULATOR_SUPPLY("sata_ext_3p3v", "msm_sata.0"),
+};
VREG_CONSUMERS(AVC_1P2V) = {
REGULATOR_SUPPLY("avc_1p2v", NULL),
};
@@ -436,7 +440,8 @@
.pin_ctrl = _pin_ctrl, \
}
-#define GPIO_VREG(_id, _reg_name, _gpio_label, _gpio, _supply_regulator) \
+#define GPIO_VREG(_id, _reg_name, _gpio_label, _gpio, _supply_regulator, \
+ _active_low) \
[GPIO_VREG_ID_##_id] = { \
.init_data = { \
.constraints = { \
@@ -450,6 +455,17 @@
.regulator_name = _reg_name, \
.gpio_label = _gpio_label, \
.gpio = _gpio, \
+ .active_low = _active_low, \
+ }
+
+#define FIXED_VREG_INIT(_id, _supply_regulator) \
+ { \
+ .constraints = { \
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS, \
+ }, \
+ .num_consumer_supplies = ARRAY_SIZE(vreg_consumers_##_id), \
+ .consumer_supplies = vreg_consumers_##_id, \
+ .supply_regulator = _supply_regulator, \
}
#define SAW_VREG_INIT(_id, _name, _min_uV, _max_uV) \
@@ -565,25 +581,41 @@
/* GPIO regulator constraints */
struct gpio_regulator_platform_data
apq8064_gpio_regulator_pdata[] __devinitdata = {
- /* ID vreg_name gpio_label gpio supply */
- GPIO_VREG(EXT_5V, "ext_5v", "ext_5v_en", PM8921_MPP_PM_TO_SYS(7), NULL),
+ /* ID vreg_name gpio_label gpio supply active_low */
+ GPIO_VREG(EXT_5V, "ext_5v", "ext_5v_en",
+ PM8921_MPP_PM_TO_SYS(7), NULL, 0),
GPIO_VREG(EXT_3P3V, "ext_3p3v", "ext_3p3v_en",
- APQ8064_EXT_3P3V_REG_EN_GPIO, NULL),
+ APQ8064_EXT_3P3V_REG_EN_GPIO, NULL, 0),
GPIO_VREG(EXT_TS_SW, "ext_ts_sw", "ext_ts_sw_en",
- PM8921_GPIO_PM_TO_SYS(23), "ext_3p3v"),
+ PM8921_GPIO_PM_TO_SYS(23), "ext_3p3v", 0),
GPIO_VREG(EXT_MPP8, "ext_mpp8", "ext_mpp8_en",
- PM8921_MPP_PM_TO_SYS(8), NULL),
+ PM8921_MPP_PM_TO_SYS(8), NULL, 0),
+ GPIO_VREG(EXT_SATA_PWR, "ext_sata_pwr", "ext_sata_pwr_en",
+ PM8921_MPP_PM_TO_SYS(4), "ext_3p3v", 1),
};
struct gpio_regulator_platform_data
mpq8064_gpio_regulator_pdata[] __devinitdata = {
- GPIO_VREG(AVC_1P2V, "avc_1p2v", "avc_1p2v_en", SX150X_GPIO(4, 2), NULL),
- GPIO_VREG(AVC_1P8V, "avc_1p8v", "avc_1p8v_en", SX150X_GPIO(4, 4), NULL),
+ GPIO_VREG(AVC_1P2V, "avc_1p2v", "avc_1p2v_en",
+ SX150X_GPIO(4, 2), NULL, 0),
+ GPIO_VREG(AVC_1P8V, "avc_1p8v", "avc_1p8v_en",
+ SX150X_GPIO(4, 4), NULL, 0),
GPIO_VREG(AVC_2P2V, "avc_2p2v", "avc_2p2v_en",
- SX150X_GPIO(4, 14), NULL),
- GPIO_VREG(AVC_5V, "avc_5v", "avc_5v_en", SX150X_GPIO(4, 3), NULL),
+ SX150X_GPIO(4, 14), NULL, 0),
+ GPIO_VREG(AVC_5V, "avc_5v", "avc_5v_en", SX150X_GPIO(4, 3), NULL, 0),
GPIO_VREG(AVC_3P3V, "avc_3p3v", "avc_3p3v_en",
- SX150X_GPIO(4, 15), "avc_5v"),
+ SX150X_GPIO(4, 15), "avc_5v", 0),
+};
+
+/* Fixed regulator constraints */
+static struct regulator_init_data mpq8064_3p3_regulator_init =
+ /* ID supply */
+ FIXED_VREG_INIT(EXT_3P3V, NULL);
+
+struct fixed_voltage_config mpq8064_3p3_regulator_pdata = {
+ .supply_name = "ext_3p3v",
+ .gpio = -EINVAL,
+ .init_data = &mpq8064_3p3_regulator_init,
};
/* SAW regulator constraints */
diff --git a/arch/arm/mach-msm/board-8064.c b/arch/arm/mach-msm/board-8064.c
index 90315b5..dc9120a 100644
--- a/arch/arm/mach-msm/board-8064.c
+++ b/arch/arm/mach-msm/board-8064.c
@@ -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
@@ -10,6 +10,7 @@
* GNU General Public License for more details.
*
*/
+#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/bitops.h>
#include <linux/platform_device.h>
@@ -142,6 +143,11 @@
#define PCIE_PWR_EN_PMIC_GPIO 13
#define PCIE_RST_N_PMIC_MPP 1
+/* PCIe pmic gpios for fsm8064_ep */
+/* Unused pin. The WAKE feature is not supported on fsm8064_ep */
+#define PCIE_EP_WAKE_N_PMIC_GPIO 11
+#define PCIE_EP_RST_N_PMIC_GPIO 37
+
#ifdef CONFIG_KERNEL_MSM_CONTIG_MEM_REGION
static unsigned msm_contig_mem_size = MSM_CONTIG_MEM_SIZE;
static int __init msm_contig_mem_size_setup(char *p)
@@ -958,7 +964,7 @@
{
if (machine_is_apq8064_liquid() || machine_is_mpq8064_cdp() ||
machine_is_mpq8064_hrd() || machine_is_mpq8064_dtv() ||
- machine_is_apq8064_cdp()) {
+ machine_is_apq8064_cdp() || machine_is_fsm8064_ep()) {
if (machine_is_apq8064_liquid())
msm_ehci_host_pdata3.dock_connect_irq =
PM8921_MPP_IRQ(PM8921_IRQ_BASE, 9);
@@ -2073,7 +2079,7 @@
msm_shared_ram_phys = MSM_SHARED_RAM_PHYS;
msm_map_apq8064_io();
if (socinfo_init() < 0)
- pr_err("socinfo_init() failed!\n");
+ pr_err("%s: socinfo_init() failed\n", __func__);
}
static void __init apq8064_init_irq(void)
@@ -2478,6 +2484,19 @@
.wake_n = PM8921_GPIO_IRQ(PM8921_IRQ_BASE, PCIE_WAKE_N_PMIC_GPIO),
};
+/* FSM8064_EP PCIe gpios */
+static struct msm_pcie_gpio_info_t ep_pcie_gpio_info[MSM_PCIE_MAX_GPIO] = {
+ {"rst_n", PM8921_GPIO_PM_TO_SYS(PCIE_EP_RST_N_PMIC_GPIO), 0},
+ {"pwr_en", PM8921_GPIO_PM_TO_SYS(PCIE_PWR_EN_PMIC_GPIO), 1},
+};
+
+static struct msm_pcie_platform ep_pcie_platform_data = {
+ .gpio = ep_pcie_gpio_info,
+ .axi_addr = PCIE_AXI_BAR_PHYS,
+ .axi_size = PCIE_AXI_BAR_SIZE,
+ .wake_n = PM8921_GPIO_IRQ(PM8921_IRQ_BASE, PCIE_EP_WAKE_N_PMIC_GPIO),
+};
+
static int __init mpq8064_pcie_enabled(void)
{
return !((readl_relaxed(QFPROM_RAW_FEAT_CONFIG_ROW0_MSB) & BIT(21)) ||
@@ -2492,6 +2511,19 @@
}
}
+static void __init fsm8064_ep_pcie_init(void)
+{
+ msm_device_pcie.dev.platform_data = &ep_pcie_platform_data;
+ platform_device_register(&msm_device_pcie);
+}
+
+static struct platform_device mpq8064_device_ext_3p3v_vreg = {
+ .name = "reg-fixed-voltage",
+ .dev = {
+ .platform_data = &mpq8064_3p3_regulator_pdata,
+ },
+};
+
static struct platform_device apq8064_device_ext_5v_vreg __devinitdata = {
.name = GPIO_REGULATOR_DEV_NAME,
.id = PM8921_MPP_PM_TO_SYS(7),
@@ -2528,6 +2560,16 @@
},
};
+static struct platform_device
+apq8064_device_ext_3p3v_mpp4_vreg __devinitdata = {
+ .name = GPIO_REGULATOR_DEV_NAME,
+ .id = PM8921_MPP_PM_TO_SYS(4),
+ .dev = {
+ .platform_data =
+ &apq8064_gpio_regulator_pdata[GPIO_VREG_ID_EXT_SATA_PWR],
+ },
+};
+
static struct platform_device apq8064_device_rpm_regulator __devinitdata = {
.name = "rpm-regulator",
.id = 0,
@@ -2570,6 +2612,100 @@
&mpq_cpudai_pseudo,
};
+static struct platform_device *ep_devices[] __initdata = {
+ &msm_device_smd_apq8064,
+ &apq8064_device_gadget_peripheral,
+ &apq8064_device_hsusb_host,
+ &android_usb_device,
+ &msm_device_wcnss_wlan,
+ &msm_device_iris_fm,
+ &apq8064_fmem_device,
+#ifdef CONFIG_ANDROID_PMEM
+#ifndef CONFIG_MSM_MULTIMEDIA_USE_ION
+ &apq8064_android_pmem_device,
+ &apq8064_android_pmem_adsp_device,
+ &apq8064_android_pmem_audio_device,
+#endif /*CONFIG_MSM_MULTIMEDIA_USE_ION*/
+#endif /*CONFIG_ANDROID_PMEM*/
+#ifdef CONFIG_ION_MSM
+ &apq8064_ion_dev,
+#endif
+ &msm8064_device_watchdog,
+ &msm8064_device_saw_regulator_core0,
+ &msm8064_device_saw_regulator_core1,
+ &msm8064_device_saw_regulator_core2,
+ &msm8064_device_saw_regulator_core3,
+#if defined(CONFIG_QSEECOM)
+ &qseecom_device,
+#endif
+
+ &msm_8064_device_tsif[0],
+ &msm_8064_device_tsif[1],
+
+#if defined(CONFIG_CRYPTO_DEV_QCRYPTO) || \
+ defined(CONFIG_CRYPTO_DEV_QCRYPTO_MODULE)
+ &qcrypto_device,
+#endif
+
+#if defined(CONFIG_CRYPTO_DEV_QCEDEV) || \
+ defined(CONFIG_CRYPTO_DEV_QCEDEV_MODULE)
+ &qcedev_device,
+#endif
+
+#ifdef CONFIG_HW_RANDOM_MSM
+ &apq8064_device_rng,
+#endif
+ &apq_pcm,
+ &apq_pcm_routing,
+ &apq8064_rpm_device,
+ &apq8064_rpm_log_device,
+ &apq8064_rpm_stat_device,
+ &apq8064_rpm_master_stat_device,
+ &apq_device_tz_log,
+ &msm_bus_8064_apps_fabric,
+ &msm_bus_8064_sys_fabric,
+ &msm_bus_8064_mm_fabric,
+ &msm_bus_8064_sys_fpb,
+ &msm_bus_8064_cpss_fpb,
+ &msm_pil_dsps,
+ &msm_8960_q6_lpass,
+ &msm_pil_vidc,
+ &msm_gss,
+ &apq8064_rtb_device,
+ &apq8064_dcvs_device,
+ &apq8064_msm_gov_device,
+ &apq8064_device_cache_erp,
+ &msm8960_device_ebi1_ch0_erp,
+ &msm8960_device_ebi1_ch1_erp,
+ &epm_adc_device,
+ &coresight_tpiu_device,
+ &coresight_etb_device,
+ &apq8064_coresight_funnel_device,
+ &coresight_etm0_device,
+ &coresight_etm1_device,
+ &coresight_etm2_device,
+ &coresight_etm3_device,
+#ifdef CONFIG_MSM_GEMINI
+ &msm8960_gemini_device,
+#endif
+ &msm_tsens_device,
+ &apq8064_cache_dump_device,
+ &msm_8064_device_tspp,
+#ifdef CONFIG_BATTERY_BCL
+ &battery_bcl_device,
+#endif
+ &apq8064_msm_mpd_device,
+ &apq8064_device_qup_i2c_gsbi1,
+ &apq8064_device_uart_gsbi2,
+ &apq8064_device_uart_gsbi1,
+ &apq8064_device_uart_gsbi4,
+ &msm_device_sps_apq8064,
+#ifdef CONFIG_MSM_ROTATOR
+ &msm_rotator_device,
+#endif
+ &msm8064_pc_cntr,
+};
+
static struct platform_device *common_i2s_devices[] __initdata = {
&apq_cpudai_mi2s,
&apq_cpudai_i2s_rx,
@@ -2593,6 +2729,7 @@
static struct platform_device *pm8921_mpq_hrd_common_devices[] __initdata = {
&apq8064_device_ext_5v_vreg,
&apq8064_device_ext_mpp8_vreg,
+ &mpq8064_device_ext_3p3v_vreg,
&apq8064_device_ssbi_pmic1,
&apq8064_device_ssbi_pmic2,
};
@@ -2979,12 +3116,14 @@
wmb();
iounmap(gsbi_mem);
apq8064_i2c_qup_gsbi1_pdata.use_gsbi_shared_mode = 1;
- apq8064_device_qup_i2c_gsbi3.dev.platform_data =
- &apq8064_i2c_qup_gsbi3_pdata;
apq8064_device_qup_i2c_gsbi1.dev.platform_data =
&apq8064_i2c_qup_gsbi1_pdata;
- apq8064_device_qup_i2c_gsbi4.dev.platform_data =
- &apq8064_i2c_qup_gsbi4_pdata;
+ if (!machine_is_fsm8064_ep()) {
+ apq8064_device_qup_i2c_gsbi3.dev.platform_data =
+ &apq8064_i2c_qup_gsbi3_pdata;
+ apq8064_device_qup_i2c_gsbi4.dev.platform_data =
+ &apq8064_i2c_qup_gsbi4_pdata;
+ }
mpq8064_device_qup_i2c_gsbi5.dev.platform_data =
&mpq8064_i2c_qup_gsbi5_pdata;
}
@@ -3500,6 +3639,8 @@
mach_mask = I2C_LIQUID;
else if (PLATFORM_IS_MPQ8064())
mach_mask = I2C_MPQ_CDP;
+ else if (machine_is_fsm8064_ep())
+ mach_mask = I2C_SURF;
else
pr_err("unmatched machine ID in register_i2c_devices\n");
@@ -3625,9 +3766,11 @@
ARRAY_SIZE(pm8917_common_devices));
if (machine_is_apq8064_cdp() || machine_is_apq8064_liquid())
platform_device_register(&apq8064_device_ext_ts_sw_vreg);
- platform_add_devices(common_devices, ARRAY_SIZE(common_devices));
+ if (!machine_is_fsm8064_ep())
+ platform_add_devices(common_devices,
+ ARRAY_SIZE(common_devices));
if (!(machine_is_mpq8064_cdp() || machine_is_mpq8064_hrd() ||
- machine_is_mpq8064_dtv()))
+ machine_is_mpq8064_dtv() || machine_is_fsm8064_ep()))
platform_add_devices(common_not_mpq_devices,
ARRAY_SIZE(common_not_mpq_devices));
@@ -3759,6 +3902,43 @@
if (machine_is_mpq8064_cdp() || machine_is_mpq8064_hrd() ||
machine_is_mpq8064_dtv())
platform_device_register(&msm_dev_avtimer_device);
+
+ if (machine_is_apq8064_cdp() || machine_is_mpq8064_hrd()) {
+ int ret;
+ struct pm8xxx_mpp_config_data sata_pwr_cfg = {
+ .type = PM8XXX_MPP_TYPE_D_OUTPUT,
+ .level = PM8921_MPP_DIG_LEVEL_VPH,
+ .control = PM8XXX_MPP_DOUT_CTRL_HIGH,
+ };
+
+ /* Apply MPP-4 init only when it is used to control SATA PWR */
+ ret = pm8xxx_mpp_config(PM8921_MPP_PM_TO_SYS(4), &sata_pwr_cfg);
+ if (ret)
+ pr_err("%s: pm8921 MPP %d init config failed(%d)\n",
+ __func__, PM8921_MPP_PM_TO_SYS(4), ret);
+ platform_device_register(&apq8064_device_ext_3p3v_mpp4_vreg);
+ platform_device_register(&apq8064_device_sata);
+ }
+}
+
+static void __init fsm8064_ep_init(void)
+{
+ if (meminfo_init(SYS_MEMORY, SZ_256M) < 0)
+ pr_err("meminfo_init() failed!\n");
+
+ apq8064_common_init();
+ ethernet_init();
+ msm_rotator_set_split_iommu_domain();
+ fsm8064_ep_pcie_init();
+ platform_add_devices(ep_devices, ARRAY_SIZE(ep_devices));
+ spi_register_board_info(spi_board_info, ARRAY_SIZE(spi_board_info));
+ apq8064_init_gpu();
+ platform_add_devices(apq8064_footswitch, apq8064_num_footswitch);
+ platform_device_register(&cdp_kp_pdev);
+#ifdef CONFIG_MSM_CAMERA
+ apq8064_init_cam();
+#endif
+ change_memory_power = &apq8064_change_memory_power;
}
MACHINE_START(APQ8064_CDP, "QCT APQ8064 CDP")
@@ -3774,6 +3954,18 @@
.smp = &msm8960_smp_ops,
MACHINE_END
+MACHINE_START(FSM8064_EP, "QCT FSM8064 EP")
+ .map_io = apq8064_map_io,
+ .reserve = apq8064_reserve,
+ .init_irq = apq8064_init_irq,
+ .handle_irq = gic_handle_irq,
+ .timer = &msm_timer,
+ .init_machine = fsm8064_ep_init,
+ .init_early = apq8064_allocate_memory_regions,
+ .init_very_early = apq8064_early_reserve,
+ .restart = msm_restart,
+MACHINE_END
+
MACHINE_START(APQ8064_MTP, "QCT APQ8064 MTP")
.map_io = apq8064_map_io,
.reserve = apq8064_reserve,
diff --git a/arch/arm/mach-msm/board-8064.h b/arch/arm/mach-msm/board-8064.h
index 2bc0981..adc037b 100644
--- a/arch/arm/mach-msm/board-8064.h
+++ b/arch/arm/mach-msm/board-8064.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
@@ -14,9 +14,9 @@
#define __ARCH_ARM_MACH_MSM_BOARD_APQ8064_H
#include <linux/regulator/msm-gpio-regulator.h>
+#include <linux/regulator/fixed.h>
#include <linux/mfd/pm8xxx/pm8921.h>
#include <linux/mfd/pm8xxx/pm8821.h>
-#include <linux/ahci_platform.h>
#include <mach/msm_memtypes.h>
#include <mach/irqs.h>
#include <mach/rpm-regulator.h>
@@ -50,6 +50,7 @@
#define GPIO_VREG_ID_EXT_3P3V 1
#define GPIO_VREG_ID_EXT_TS_SW 2
#define GPIO_VREG_ID_EXT_MPP8 3
+#define GPIO_VREG_ID_EXT_SATA_PWR 4
#define GPIO_VREG_ID_AVC_1P2V 0
#define GPIO_VREG_ID_AVC_1P8V 1
@@ -65,6 +66,8 @@
extern struct gpio_regulator_platform_data
mpq8064_gpio_regulator_pdata[] __devinitdata;
+extern struct fixed_voltage_config mpq8064_3p3_regulator_pdata;
+
extern struct rpm_regulator_platform_data
apq8064_rpm_regulator_pdata __devinitdata;
@@ -81,7 +84,6 @@
struct mmc_platform_data *plat);
void apq8064_init_mmc(void);
-int __init apq8064_add_ahci(struct ahci_platform_data *platd);
void apq8064_init_gpiomux(void);
void apq8064_init_pmic(void);
diff --git a/arch/arm/mach-msm/board-8092.c b/arch/arm/mach-msm/board-8092.c
index cbd257f..b4c63f9 100644
--- a/arch/arm/mach-msm/board-8092.c
+++ b/arch/arm/mach-msm/board-8092.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. 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
@@ -10,6 +10,7 @@
* GNU General Public License for more details.
*/
+#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/of.h>
@@ -78,9 +79,6 @@
static void __init mpq8092_map_io(void)
{
msm_map_mpq8092_io();
- if (socinfo_init() < 0)
- pr_err("%s: socinfo_init() failed\n", __func__);
-
}
static struct of_dev_auxdata mpq8092_auxdata_lookup[] __initdata = {
@@ -99,6 +97,9 @@
{
struct of_dev_auxdata *adata = mpq8092_auxdata_lookup;
+ if (socinfo_init() < 0)
+ pr_err("%s: socinfo_init() failed\n", __func__);
+
mpq8092_init_gpiomux();
msm_clock_init(&mpq8092_clock_init_data);
of_platform_populate(NULL, of_default_bus_match_table, adata, NULL);
diff --git a/arch/arm/mach-msm/board-8226-gpiomux.c b/arch/arm/mach-msm/board-8226-gpiomux.c
index 7a53b24..e58cee7 100644
--- a/arch/arm/mach-msm/board-8226-gpiomux.c
+++ b/arch/arm/mach-msm/board-8226-gpiomux.c
@@ -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
@@ -25,11 +25,6 @@
.drv = GPIOMUX_DRV_2MA,
.func = GPIOMUX_FUNC_GPIO,
};
-static struct gpiomux_setting gpio_spi_config = {
- .func = GPIOMUX_FUNC_1,
- .drv = GPIOMUX_DRV_8MA,
- .pull = GPIOMUX_PULL_NONE,
-};
static struct msm_gpiomux_config msm_eth_configs[] = {
{
@@ -39,6 +34,19 @@
}
},
};
+#endif
+
+static struct gpiomux_setting gpio_spi_config = {
+ .func = GPIOMUX_FUNC_1,
+ .drv = GPIOMUX_DRV_8MA,
+ .pull = GPIOMUX_PULL_NONE,
+};
+
+static struct gpiomux_setting gpio_i2c_config = {
+ .func = GPIOMUX_FUNC_3,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_NONE,
+};
static struct msm_gpiomux_config msm_blsp_configs[] __initdata = {
{
@@ -65,8 +73,19 @@
[GPIOMUX_SUSPENDED] = &gpio_spi_config,
},
},
+ {
+ .gpio = 14, /* BLSP-1 QUP-4 I2C_SDA */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &gpio_i2c_config,
+ },
+ },
+ {
+ .gpio = 15, /* BLSP-1 QUP-4 I2C_SCL */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &gpio_i2c_config,
+ },
+ },
};
-#endif
void __init msm8226_init_gpiomux(void)
{
@@ -77,8 +96,10 @@
pr_err("%s failed %d\n", __func__, rc);
return;
}
+
#if defined(CONFIG_KS8851) || defined(CONFIG_KS8851_MODULE)
msm_gpiomux_install(msm_eth_configs, ARRAY_SIZE(msm_eth_configs));
- msm_gpiomux_install(msm_blsp_configs, ARRAY_SIZE(msm_blsp_configs));
#endif
+
+ msm_gpiomux_install(msm_blsp_configs, ARRAY_SIZE(msm_blsp_configs));
}
diff --git a/arch/arm/mach-msm/board-8226.c b/arch/arm/mach-msm/board-8226.c
index ce6ca92..3a6eb7f 100644
--- a/arch/arm/mach-msm/board-8226.c
+++ b/arch/arm/mach-msm/board-8226.c
@@ -10,6 +10,7 @@
* GNU General Public License for more details.
*/
+#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/platform_device.h>
@@ -41,9 +42,14 @@
#include <mach/socinfo.h>
#include <mach/board.h>
#include <mach/clk-provider.h>
+#include <mach/msm_smd.h>
+#include <mach/rpm-smd.h>
+#include <linux/msm_thermal.h>
#include "board-dt.h"
#include "clock.h"
#include "platsmp.h"
+#include "spm.h"
+#include "lpm_resources.h"
static struct memtype_reserve msm8226_reserve_table[] __initdata = {
[MEMTYPE_SMI] = {
@@ -61,6 +67,8 @@
return MEMTYPE_EBI1;
}
static struct clk_lookup msm_clocks_dummy[] = {
+ CLK_DUMMY("core_clk", NULL, "f9926000.i2c", OFF),
+ CLK_DUMMY("iface_clk", NULL, "f9926000.i2c", OFF),
CLK_DUMMY("core_clk", BLSP1_UART_CLK, "f991f000.serial", OFF),
CLK_DUMMY("iface_clk", BLSP1_UART_CLK, "f991f000.serial", OFF),
CLK_DUMMY("iface_clk", HSUSB_IFACE_CLK, "f9a55000.usb", OFF),
@@ -118,18 +126,27 @@
msm_reserve();
}
+
+void __init msm8226_add_drivers(void)
+{
+ msm_rpm_driver_init();
+ msm_lpmrs_module_init();
+ msm_spm_device_init();
+ msm_thermal_device_init();
+}
+
void __init msm8226_init(void)
{
struct of_dev_auxdata *adata = msm8226_auxdata_lookup;
- msm8226_init_gpiomux();
-
- msm_clock_init(&msm_dummy_clock_init_data);
-
if (socinfo_init() < 0)
pr_err("%s: socinfo_init() failed\n", __func__);
+ msm8226_init_gpiomux();
+ msm8226_add_drivers();
+ msm_clock_init(&msm_dummy_clock_init_data);
of_platform_populate(NULL, of_default_bus_match_table, adata, NULL);
+
}
static const char *msm8226_dt_match[] __initconst = {
@@ -139,7 +156,7 @@
DT_MACHINE_START(MSM8226_DT, "Qualcomm MSM 8226 (Flattened Device Tree)")
.map_io = msm_map_msm8226_io,
- .init_irq = msm_dt_init_irq_nompm,
+ .init_irq = msm_dt_init_irq,
.init_machine = msm8226_init,
.handle_irq = gic_handle_irq,
.timer = &msm_dt_timer,
diff --git a/arch/arm/mach-msm/board-8910-gpiomux.c b/arch/arm/mach-msm/board-8610-gpiomux.c
similarity index 86%
rename from arch/arm/mach-msm/board-8910-gpiomux.c
rename to arch/arm/mach-msm/board-8610-gpiomux.c
index a295a17..49d8236 100644
--- a/arch/arm/mach-msm/board-8910-gpiomux.c
+++ b/arch/arm/mach-msm/board-8610-gpiomux.c
@@ -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
@@ -17,7 +17,7 @@
#include <mach/gpio.h>
#include <mach/gpiomux.h>
-void __init msm8910_init_gpiomux(void)
+void __init msm8610_init_gpiomux(void)
{
int rc;
diff --git a/arch/arm/mach-msm/board-8610.c b/arch/arm/mach-msm/board-8610.c
new file mode 100644
index 0000000..322d696
--- /dev/null
+++ b/arch/arm/mach-msm/board-8610.c
@@ -0,0 +1,134 @@
+/* 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
+ * 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/err.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/of_fdt.h>
+#include <linux/of_irq.h>
+#include <linux/memory.h>
+#include <asm/mach/map.h>
+#include <asm/arch_timer.h>
+#include <asm/hardware/gic.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/time.h>
+#include <mach/board.h>
+#include <mach/gpiomux.h>
+#include <mach/msm_iomap.h>
+#include <mach/restart.h>
+#ifdef CONFIG_ION_MSM
+#include <mach/ion.h>
+#endif
+#include <mach/msm_memtypes.h>
+#include <mach/socinfo.h>
+#include <mach/board.h>
+#include <mach/clk-provider.h>
+#include <mach/msm_smd.h>
+#include <mach/rpm-smd.h>
+#include <linux/msm_thermal.h>
+#include "board-dt.h"
+#include "clock.h"
+#include "platsmp.h"
+#include "spm.h"
+#include "lpm_resources.h"
+
+static struct memtype_reserve msm8610_reserve_table[] __initdata = {
+ [MEMTYPE_SMI] = {
+ },
+ [MEMTYPE_EBI0] = {
+ .flags = MEMTYPE_FLAGS_1M_ALIGN,
+ },
+ [MEMTYPE_EBI1] = {
+ .flags = MEMTYPE_FLAGS_1M_ALIGN,
+ },
+};
+
+static int msm8610_paddr_to_memtype(unsigned int paddr)
+{
+ return MEMTYPE_EBI1;
+}
+
+static struct of_dev_auxdata msm8610_auxdata_lookup[] __initdata = {
+ OF_DEV_AUXDATA("qcom,msm-sdcc", 0xF9824000, \
+ "msm_sdcc.1", NULL),
+ OF_DEV_AUXDATA("qcom,msm-sdcc", 0xF98A4000, \
+ "msm_sdcc.2", NULL),
+ {}
+};
+
+static struct reserve_info msm8610_reserve_info __initdata = {
+ .memtype_reserve_table = msm8610_reserve_table,
+ .paddr_to_memtype = msm8610_paddr_to_memtype,
+};
+
+static void __init msm8610_early_memory(void)
+{
+ reserve_info = &msm8610_reserve_info;
+ of_scan_flat_dt(dt_scan_for_memory_reserve, msm8610_reserve_table);
+}
+
+static void __init msm8610_reserve(void)
+{
+ msm_reserve();
+}
+
+void __init msm8610_add_drivers(void)
+{
+ msm_rpm_driver_init();
+ msm_lpmrs_module_init();
+ msm_spm_device_init();
+ msm_thermal_device_init();
+}
+
+void __init msm8610_init(void)
+{
+ struct of_dev_auxdata *adata = msm8610_auxdata_lookup;
+
+ if (socinfo_init() < 0)
+ pr_err("%s: socinfo_init() failed\n", __func__);
+
+ msm8610_init_gpiomux();
+ msm8610_add_drivers();
+
+ if (machine_is_msm8610_rumi())
+ msm_clock_init(&msm8610_rumi_clock_init_data);
+ else
+ msm_clock_init(&msm8610_clock_init_data);
+
+ of_platform_populate(NULL, of_default_bus_match_table, adata, NULL);
+}
+
+static const char *msm8610_dt_match[] __initconst = {
+ "qcom,msm8610",
+ NULL
+};
+
+DT_MACHINE_START(MSM8610_DT, "Qualcomm MSM 8610 (Flattened Device Tree)")
+ .map_io = msm_map_msm8610_io,
+ .init_irq = msm_dt_init_irq,
+ .init_machine = msm8610_init,
+ .handle_irq = gic_handle_irq,
+ .timer = &msm_dt_timer,
+ .dt_compat = msm8610_dt_match,
+ .restart = msm_restart,
+ .reserve = msm8610_reserve,
+ .init_very_early = msm8610_early_memory,
+ .smp = &arm_smp_ops,
+MACHINE_END
diff --git a/arch/arm/mach-msm/board-8910.c b/arch/arm/mach-msm/board-8910.c
deleted file mode 100644
index 2ff4567..0000000
--- a/arch/arm/mach-msm/board-8910.c
+++ /dev/null
@@ -1,119 +0,0 @@
-/* Copyright (c) 2012, 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/kernel.h>
-#include <linux/errno.h>
-#include <linux/platform_device.h>
-#include <linux/io.h>
-#include <linux/gpio.h>
-#include <linux/irq.h>
-#include <linux/irqdomain.h>
-#include <linux/of.h>
-#include <linux/of_address.h>
-#include <linux/of_platform.h>
-#include <linux/of_fdt.h>
-#include <linux/of_irq.h>
-#include <linux/memory.h>
-#include <asm/mach/map.h>
-#include <asm/arch_timer.h>
-#include <asm/hardware/gic.h>
-#include <asm/mach/arch.h>
-#include <asm/mach/time.h>
-#include <mach/board.h>
-#include <mach/gpiomux.h>
-#include <mach/msm_iomap.h>
-#include <mach/restart.h>
-#ifdef CONFIG_ION_MSM
-#include <mach/ion.h>
-#endif
-#include <mach/msm_memtypes.h>
-#include <mach/socinfo.h>
-#include <mach/board.h>
-#include <mach/clk-provider.h>
-#include "board-dt.h"
-#include "clock.h"
-#include "platsmp.h"
-
-static struct memtype_reserve msm8910_reserve_table[] __initdata = {
- [MEMTYPE_SMI] = {
- },
- [MEMTYPE_EBI0] = {
- .flags = MEMTYPE_FLAGS_1M_ALIGN,
- },
- [MEMTYPE_EBI1] = {
- .flags = MEMTYPE_FLAGS_1M_ALIGN,
- },
-};
-
-static int msm8910_paddr_to_memtype(unsigned int paddr)
-{
- return MEMTYPE_EBI1;
-}
-
-static struct of_dev_auxdata msm8910_auxdata_lookup[] __initdata = {
- OF_DEV_AUXDATA("qcom,msm-sdcc", 0xF9824000, \
- "msm_sdcc.1", NULL),
- OF_DEV_AUXDATA("qcom,msm-sdcc", 0xF98A4000, \
- "msm_sdcc.2", NULL),
- {}
-};
-
-static struct reserve_info msm8910_reserve_info __initdata = {
- .memtype_reserve_table = msm8910_reserve_table,
- .paddr_to_memtype = msm8910_paddr_to_memtype,
-};
-
-static void __init msm8910_early_memory(void)
-{
- reserve_info = &msm8910_reserve_info;
- of_scan_flat_dt(dt_scan_for_memory_reserve, msm8910_reserve_table);
-}
-
-static void __init msm8910_reserve(void)
-{
- msm_reserve();
-}
-
-void __init msm8910_init(void)
-{
- struct of_dev_auxdata *adata = msm8910_auxdata_lookup;
-
- msm8910_init_gpiomux();
-
- if (machine_is_msm8910_rumi())
- msm_clock_init(&msm8910_rumi_clock_init_data);
- else
- msm_clock_init(&msm8910_clock_init_data);
-
- if (socinfo_init() < 0)
- pr_err("%s: socinfo_init() failed\n", __func__);
-
- of_platform_populate(NULL, of_default_bus_match_table, adata, NULL);
-}
-
-static const char *msm8910_dt_match[] __initconst = {
- "qcom,msm8910",
- NULL
-};
-
-DT_MACHINE_START(MSM8910_DT, "Qualcomm MSM 8910 (Flattened Device Tree)")
- .map_io = msm_map_msm8910_io,
- .init_irq = msm_dt_init_irq_nompm,
- .init_machine = msm8910_init,
- .handle_irq = gic_handle_irq,
- .timer = &msm_dt_timer,
- .dt_compat = msm8910_dt_match,
- .restart = msm_restart,
- .reserve = msm8910_reserve,
- .init_very_early = msm8910_early_memory,
- .smp = &arm_smp_ops,
-MACHINE_END
diff --git a/arch/arm/mach-msm/board-8930-pmic.c b/arch/arm/mach-msm/board-8930-pmic.c
index c0402ef..4fb5fe9 100644
--- a/arch/arm/mach-msm/board-8930-pmic.c
+++ b/arch/arm/mach-msm/board-8930-pmic.c
@@ -455,7 +455,7 @@
static struct pm8xxx_spk_platform_data pm8xxx_spk_pdata = {
.spk_add_enable = false,
- .cd_ng_threshold = 0x6,
+ .cd_ng_threshold = 0x0,
.cd_nf_preamp_bias = 0x1,
.cd_ng_hold = 0x6,
.cd_ng_max_atten = 0x0,
diff --git a/arch/arm/mach-msm/board-8930.c b/arch/arm/mach-msm/board-8930.c
index 61b4e13..6323c49 100644
--- a/arch/arm/mach-msm/board-8930.c
+++ b/arch/arm/mach-msm/board-8930.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2012, 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
@@ -10,6 +10,7 @@
* GNU General Public License for more details.
*
*/
+#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/io.h>
@@ -1466,6 +1467,7 @@
if (socinfo_init() < 0)
pr_err("socinfo_init() failed!\n");
+
}
static void __init msm8930_init_irq(void)
@@ -1505,6 +1507,16 @@
#ifdef CONFIG_USB_MSM_OTG_72K
static struct msm_otg_platform_data msm_otg_pdata;
#else
+static int enable_usb_host_mode;
+static int __init usb_host_mode_with_pm8917(char *param)
+{
+ int ret;
+
+ ret = kstrtoint(param, 10, &enable_usb_host_mode);
+ return ret;
+}
+early_param("usb_host_mode_pm8917", usb_host_mode_with_pm8917);
+
#ifdef CONFIG_MSM_BUS_SCALING
/* Bandwidth requests (zero) if no vote placed */
static struct msm_bus_vectors usb_init_vectors[] = {
@@ -2871,9 +2883,20 @@
msm_clock_init(&msm8930_clock_init_data);
if (socinfo_get_pmic_model() == PMIC_MODEL_PM8917) {
- /* MPP01 IRQ number */
- msm_otg_pdata.pmic_id_irq =
+ /*
+ * By default, set USB mode as USB Peripheral only due to
+ * hardware rework requirement for USB Host Mode.
+ * Provide pmic_id_irq number only if host mode is enable
+ * by user assuming that hardware rework is available.
+ */
+ if (enable_usb_host_mode) {
+ /* MPP01 IRQ number */
+ msm_otg_pdata.pmic_id_irq =
PM8921_MPP_IRQ(PM8917_IRQ_BASE, 1);
+ } else {
+ pr_err("Enabling USB Peripheral Only mode.\n");
+ msm_otg_pdata.mode = USB_PERIPHERAL;
+ }
} else {
msm_otg_pdata.pmic_id_irq =
PM8038_USB_ID_IN_IRQ(PM8038_IRQ_BASE);
diff --git a/arch/arm/mach-msm/board-8960.c b/arch/arm/mach-msm/board-8960.c
index f64bf18..3e71725 100644
--- a/arch/arm/mach-msm/board-8960.c
+++ b/arch/arm/mach-msm/board-8960.c
@@ -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
@@ -10,6 +10,7 @@
* GNU General Public License for more details.
*
*/
+#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/io.h>
@@ -1437,26 +1438,49 @@
static struct resource tspp_resources[] = {
[0] = {
+ .name = "TSIF_TSPP_IRQ",
.flags = IORESOURCE_IRQ,
.start = TSIF_TSPP_IRQ,
- .end = TSIF1_IRQ,
+ .end = TSIF_TSPP_IRQ,
},
[1] = {
+ .name = "TSIF0_IRQ",
+ .flags = IORESOURCE_IRQ,
+ .start = TSIF1_IRQ,
+ .end = TSIF1_IRQ,
+ },
+ [2] = {
+ .name = "TSIF1_IRQ",
+ .flags = IORESOURCE_IRQ,
+ .start = TSIF2_IRQ,
+ .end = TSIF2_IRQ,
+ },
+ [3] = {
+ .name = "TSIF_BAM_IRQ",
+ .flags = IORESOURCE_IRQ,
+ .start = TSIF_BAM_IRQ,
+ .end = TSIF_BAM_IRQ,
+ },
+ [4] = {
+ .name = "MSM_TSIF0_PHYS",
.flags = IORESOURCE_MEM,
.start = MSM_TSIF0_PHYS,
.end = MSM_TSIF0_PHYS + MSM_TSIF_SIZE - 1,
},
- [2] = {
+ [5] = {
+ .name = "MSM_TSIF1_PHYS",
.flags = IORESOURCE_MEM,
.start = MSM_TSIF1_PHYS,
.end = MSM_TSIF1_PHYS + MSM_TSIF_SIZE - 1,
},
- [3] = {
+ [6] = {
+ .name = "MSM_TSPP_PHYS",
.flags = IORESOURCE_MEM,
.start = MSM_TSPP_PHYS,
.end = MSM_TSPP_PHYS + MSM_TSPP_SIZE - 1,
},
- [4] = {
+ [7] = {
+ .name = "MSM_TSPP_BAM_PHYS",
.flags = IORESOURCE_MEM,
.start = MSM_TSPP_BAM_PHYS,
.end = MSM_TSPP_BAM_PHYS + MSM_TSPP_BAM_SIZE - 1,
@@ -1486,9 +1510,9 @@
{
msm_shared_ram_phys = MSM_SHARED_RAM_PHYS;
msm_map_msm8960_io();
-
if (socinfo_init() < 0)
pr_err("socinfo_init() failed!\n");
+
}
static void __init msm8960_init_irq(void)
diff --git a/arch/arm/mach-msm/board-8974-gpiomux.c b/arch/arm/mach-msm/board-8974-gpiomux.c
index 6672f49..ac93b00 100644
--- a/arch/arm/mach-msm/board-8974-gpiomux.c
+++ b/arch/arm/mach-msm/board-8974-gpiomux.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. 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
@@ -640,6 +640,51 @@
},
},
};
+
+static struct gpiomux_setting pri_auxpcm_act_cfg = {
+ .func = GPIOMUX_FUNC_1,
+ .drv = GPIOMUX_DRV_8MA,
+ .pull = GPIOMUX_PULL_NONE,
+};
+
+
+static struct gpiomux_setting pri_auxpcm_sus_cfg = {
+ .func = GPIOMUX_FUNC_1,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_DOWN,
+};
+
+static struct msm_gpiomux_config msm8974_pri_auxpcm_configs[] __initdata = {
+ {
+ .gpio = 65,
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &pri_auxpcm_sus_cfg,
+ [GPIOMUX_ACTIVE] = &pri_auxpcm_act_cfg,
+ },
+ },
+ {
+ .gpio = 66,
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &pri_auxpcm_sus_cfg,
+ [GPIOMUX_ACTIVE] = &pri_auxpcm_act_cfg,
+ },
+ },
+ {
+ .gpio = 67,
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &pri_auxpcm_sus_cfg,
+ [GPIOMUX_ACTIVE] = &pri_auxpcm_act_cfg,
+ },
+ },
+ {
+ .gpio = 68,
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &pri_auxpcm_sus_cfg,
+ [GPIOMUX_ACTIVE] = &pri_auxpcm_act_cfg,
+ },
+ },
+};
+
static struct msm_gpiomux_config wcnss_5wire_interface[] = {
{
.gpio = 36,
@@ -902,6 +947,9 @@
msm_gpiomux_install(msm_hdmi_configs, ARRAY_SIZE(msm_hdmi_configs));
msm_gpiomux_install(msm_mhl_configs, ARRAY_SIZE(msm_mhl_configs));
+ msm_gpiomux_install(msm8974_pri_auxpcm_configs,
+ ARRAY_SIZE(msm8974_pri_auxpcm_configs));
+
if (machine_is_msm8974_rumi())
msm_gpiomux_install(msm_rumi_blsp_configs,
ARRAY_SIZE(msm_rumi_blsp_configs));
diff --git a/arch/arm/mach-msm/board-8974.c b/arch/arm/mach-msm/board-8974.c
index 2f6d3d0..2fdceae 100644
--- a/arch/arm/mach-msm/board-8974.c
+++ b/arch/arm/mach-msm/board-8974.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, Code Aurora Forum. 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
@@ -10,6 +10,7 @@
* GNU General Public License for more details.
*/
+#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/io.h>
@@ -299,6 +300,8 @@
static struct of_dev_auxdata msm8974_auxdata_lookup[] __initdata = {
OF_DEV_AUXDATA("qcom,hsusb-otg", 0xF9A55000, \
"msm_otg", NULL),
+ OF_DEV_AUXDATA("qcom,ehci-host", 0xF9A55000, \
+ "msm_ehci_host", NULL),
OF_DEV_AUXDATA("qcom,dwc-usb3-msm", 0xF9200000, \
"msm_dwc3", NULL),
OF_DEV_AUXDATA("qcom,usb-bam-msm", 0xF9304000, \
@@ -358,18 +361,18 @@
static void __init msm8974_map_io(void)
{
msm_map_8974_io();
- if (socinfo_init() < 0)
- pr_err("%s: socinfo_init() failed\n", __func__);
}
void __init msm8974_init(void)
{
struct of_dev_auxdata *adata = msm8974_auxdata_lookup;
+ if (socinfo_init() < 0)
+ pr_err("%s: socinfo_init() failed\n", __func__);
+
msm_8974_init_gpiomux();
regulator_has_full_constraints();
of_platform_populate(NULL, of_default_bus_match_table, adata, NULL);
-
msm8974_add_drivers();
}
diff --git a/arch/arm/mach-msm/board-9625-gpiomux.c b/arch/arm/mach-msm/board-9625-gpiomux.c
index 2f75470..b39dc27 100644
--- a/arch/arm/mach-msm/board-9625-gpiomux.c
+++ b/arch/arm/mach-msm/board-9625-gpiomux.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2012-2013, Code Aurora Forum. 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
@@ -142,34 +142,6 @@
},
},
{
- .gpio = 16, /* Sec mi2s ws */
- .settings = {
- [GPIOMUX_SUSPENDED] = &mi2s_suspend_cfg,
- [GPIOMUX_ACTIVE] = &mi2s_active_cfg,
- },
- },
- {
- .gpio = 17, /* Sec mi2s din */
- .settings = {
- [GPIOMUX_SUSPENDED] = &mi2s_suspend_cfg,
- [GPIOMUX_ACTIVE] = &mi2s_active_cfg,
- },
- },
- {
- .gpio = 18, /* Sec mi2s dout */
- .settings = {
- [GPIOMUX_SUSPENDED] = &mi2s_suspend_cfg,
- [GPIOMUX_ACTIVE] = &mi2s_active_cfg,
- },
- },
- {
- .gpio = 19, /* Sec mi2s clk */
- .settings = {
- [GPIOMUX_SUSPENDED] = &mi2s_suspend_cfg,
- [GPIOMUX_ACTIVE] = &mi2s_active_cfg,
- },
- },
- {
.gpio = 71, /* mi2s mclk */
.settings = {
[GPIOMUX_SUSPENDED] = &mi2s_suspend_cfg,
diff --git a/arch/arm/mach-msm/board-9625.c b/arch/arm/mach-msm/board-9625.c
index f6a354f..9b02a5d 100644
--- a/arch/arm/mach-msm/board-9625.c
+++ b/arch/arm/mach-msm/board-9625.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. 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
@@ -10,6 +10,7 @@
* GNU General Public License for more details.
*/
+#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/io.h>
@@ -111,6 +112,8 @@
"msm-tsens", NULL),
OF_DEV_AUXDATA("qcom,usb-bam-msm", 0xF9A44000, \
"usb_bam", NULL),
+ OF_DEV_AUXDATA("qcom,hsic-host", 0xF9A15000, \
+ "msm_hsic_host", NULL),
{}
};
diff --git a/arch/arm/mach-msm/board-fsm9xxx.c b/arch/arm/mach-msm/board-fsm9xxx.c
index 274b338..cb2e766 100644
--- a/arch/arm/mach-msm/board-fsm9xxx.c
+++ b/arch/arm/mach-msm/board-fsm9xxx.c
@@ -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
@@ -1003,9 +1003,7 @@
msm_shared_ram_phys = 0x00100000;
msm_map_fsm9xxx_io();
if (socinfo_init() < 0)
- pr_err("%s: socinfo_init() failed!\n",
- __func__);
-
+ pr_err("socinfo_init() failed!\n");
}
MACHINE_START(FSM9XXX_SURF, "QCT FSM9XXX")
diff --git a/arch/arm/mach-msm/board-msm7627a-bt.c b/arch/arm/mach-msm/board-msm7627a-bt.c
index 1c2d8a2..3e90a15 100644
--- a/arch/arm/mach-msm/board-msm7627a-bt.c
+++ b/arch/arm/mach-msm/board-msm7627a-bt.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, Code Aurora Forum. 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
@@ -485,7 +485,7 @@
const struct bahama_config_register *p;
- u8 version;
+ int version;
const struct bahama_config_register v10_bt_on[] = {
{ 0xE9, 0x00, 0xFF },
@@ -567,7 +567,7 @@
u8 offset = 0; /* index into bahama configs */
on = on ? 1 : 0;
version = marimba_read_bahama_ver(&config);
- if ((int)version < 0 || version == BAHAMA_VER_UNSUPPORTED) {
+ if (version < 0 || version == BAHAMA_VER_UNSUPPORTED) {
dev_err(&msm_bt_power_device.dev, "%s : Bahama "
"version read Error, version = %d\n",
__func__, version);
@@ -602,10 +602,10 @@
__func__, (p+i)->reg,
value, (p+i)->mask);
value = 0;
- rc = marimba_read_bit_mask(&config,
+ /* Ignoring the read failure as it is only for check */
+ if (marimba_read_bit_mask(&config,
(p+i)->reg, &value,
- sizeof((p+i)->value), (p+i)->mask);
- if (rc < 0)
+ sizeof((p+i)->value), (p+i)->mask) < 0)
dev_err(&msm_bt_power_device.dev,
"%s marimba_read_bit_mask- error",
__func__);
@@ -678,7 +678,6 @@
dev_err(&msm_bt_power_device.dev,
"%s: could not %sable regulator %s: %d\n",
__func__, "dis", bt_vregs[i].name, rc);
- goto reg_disable;
}
}
@@ -687,8 +686,8 @@
if (on)
regulator_disable(bt_vregs[i].reg);
reg_disable:
- while (i) {
- if (on) {
+ if (on) {
+ while (i) {
i--;
regulator_disable(bt_vregs[i].reg);
regulator_put(bt_vregs[i].reg);
@@ -835,7 +834,10 @@
int pin, rc = 0;
const char *id = "BTPW";
int cid = 0;
+ int bt_state = 0;
+ struct marimba config = { .mod_id = SLAVE_ID_BAHAMA};
+ pr_debug("%s: on = %d\n", __func__, on);
cid = adie_get_detected_connectivity_type();
if (cid != BAHAMA_ID) {
pr_err("%s: unexpected adie connectivity type: %d\n",
@@ -854,7 +856,7 @@
if (rc < 0) {
pr_err("%s: bluetooth_switch_regulators rc = %d",
__func__, rc);
- goto exit;
+ goto fail_gpio;
}
/*setup BT GPIO lines*/
for (pin = 0; pin < ARRAY_SIZE(bt_config_power_on);
@@ -874,7 +876,7 @@
PMAPP_CLOCK_VOTE_ON);
if (rc < 0) {
pr_err("Failed to vote for TCXO_D1 ON\n");
- goto fail_clock;
+ goto fail_gpio_cfg;
}
msleep(20);
@@ -882,7 +884,7 @@
rc = bahama_bt(1);
if (rc < 0) {
pr_err("%s: bahama_bt rc = %d", __func__, rc);
- goto fail_i2c;
+ goto fail_clock;
}
msleep(20);
@@ -891,7 +893,7 @@
if (rc < 0) {
pr_err("%s: msm_bahama_setup_pcm_i2s , rc =%d\n",
__func__, rc);
- goto fail_power;
+ goto fail_i2c;
}
rc = pmapp_clock_vote(id, PMAPP_CLOCK_ID_D1,
PMAPP_CLOCK_VOTE_PIN_CTRL);
@@ -900,26 +902,28 @@
__func__, rc);
} else {
- rc = bahama_bt(0);
- if (rc < 0)
- pr_err("%s: bahama_bt rc = %d", __func__, rc);
+ bt_state = marimba_get_bt_status(&config);
+ if (!bt_state) {
+ pr_err("%s: BT is already turned OFF.\n", __func__);
+ return 0;
+ }
rc = msm_bahama_setup_pcm_i2s(BT_PCM_OFF);
if (rc < 0) {
pr_err("%s: msm_bahama_setup_pcm_i2s, rc =%d\n",
__func__, rc);
}
- rc = bt_set_gpio(on);
- if (rc) {
- pr_err("%s: bt_set_gpio = %d\n",
- __func__, rc);
- }
fail_i2c:
+ rc = bahama_bt(0);
+ if (rc < 0)
+ pr_err("%s: bahama_bt rc = %d", __func__, rc);
+
+fail_clock:
rc = pmapp_clock_vote(id, PMAPP_CLOCK_ID_D1,
PMAPP_CLOCK_VOTE_OFF);
if (rc < 0)
pr_err("%s: Failed to vote Off D1\n", __func__);
-fail_clock:
+fail_gpio_cfg:
for (pin = 0; pin < ARRAY_SIZE(bt_config_power_off);
pin++) {
rc = gpio_tlmm_config(bt_config_power_off[pin],
@@ -936,6 +940,12 @@
if (rc < 0) {
pr_err("%s: switch_regulators : rc = %d",\
__func__, rc);
+ }
+fail_gpio:
+ rc = bt_set_gpio(0);
+ if (rc) {
+ pr_err("%s: bt_set_gpio = %d\n",
+ __func__, rc);
goto exit;
}
}
diff --git a/arch/arm/mach-msm/board-msm7x30.c b/arch/arm/mach-msm/board-msm7x30.c
index 9822aa9..0654a0d 100644
--- a/arch/arm/mach-msm/board-msm7x30.c
+++ b/arch/arm/mach-msm/board-msm7x30.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2009-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,6 +11,7 @@
*
*/
+#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/irq.h>
#include <linux/gpio.h>
@@ -7349,8 +7350,7 @@
msm_shared_ram_phys = 0x00100000;
msm_map_msm7x30_io();
if (socinfo_init() < 0)
- printk(KERN_ERR "%s: socinfo_init() failed!\n",
- __func__);
+ pr_err("socinfo_init() failed!\n");
}
static void __init msm7x30_init_early(void)
diff --git a/arch/arm/mach-msm/board-msm8x60.c b/arch/arm/mach-msm/board-msm8x60.c
index d4205bd..c45cf11 100644
--- a/arch/arm/mach-msm/board-msm8x60.c
+++ b/arch/arm/mach-msm/board-msm8x60.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2012, Code Aurora Forum. 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
@@ -11,6 +11,7 @@
*
*/
+#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
@@ -7540,9 +7541,9 @@
{
msm_shared_ram_phys = MSM_SHARED_RAM_PHYS;
msm_map_msm8x60_io();
-
if (socinfo_init() < 0)
pr_err("socinfo_init() failed!\n");
+
}
/*
diff --git a/arch/arm/mach-msm/board-qsd8x50.c b/arch/arm/mach-msm/board-qsd8x50.c
index 146c8a8..e3b42bc 100644
--- a/arch/arm/mach-msm/board-qsd8x50.c
+++ b/arch/arm/mach-msm/board-qsd8x50.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2008-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
@@ -2522,8 +2522,7 @@
msm_map_qsd8x50_io();
qsd8x50_allocate_memory_regions();
if (socinfo_init() < 0)
- printk(KERN_ERR "%s: socinfo_init() failed!\n",
- __func__);
+ pr_err("socinfo_init() failed!\n");
}
MACHINE_START(QSD8X50_SURF, "QCT QSD8X50 SURF")
diff --git a/arch/arm/mach-msm/clock-8910.c b/arch/arm/mach-msm/clock-8610.c
similarity index 98%
rename from arch/arm/mach-msm/clock-8910.c
rename to arch/arm/mach-msm/clock-8610.c
index c5541b4..159f151 100644
--- a/arch/arm/mach-msm/clock-8910.c
+++ b/arch/arm/mach-msm/clock-8610.c
@@ -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
@@ -1272,6 +1272,8 @@
.cbcr_reg = LPASS_Q6_AXI_CBCR,
.has_sibling = 1,
.base = &virt_bases[GCC_BASE],
+ /* FIXME: Remove this once simulation is fixed. */
+ .halt_check = DELAY,
.c = {
.dbg_name = "gcc_lpass_q6_axi_clk",
.ops = &clk_ops_branch,
@@ -1735,6 +1737,8 @@
.cbcr_reg = BIMC_GFX_CBCR,
.has_sibling = 1,
.base = &virt_bases[MMSS_BASE],
+ /* FIXME: Remove this once simulation is fixed. */
+ .halt_check = DELAY,
.c = {
.dbg_name = "bimc_gfx_clk",
.ops = &clk_ops_branch,
@@ -1843,7 +1847,7 @@
.has_sibling = 1,
.base = &virt_bases[MMSS_BASE],
.c = {
- .parent = &csi0_clk_src.c,
+ .parent = &csi1_clk_src.c,
.dbg_name = "csi1pix_clk",
.ops = &clk_ops_branch,
CLK_INIT(csi1pix_clk.c),
@@ -1855,7 +1859,7 @@
.has_sibling = 1,
.base = &virt_bases[MMSS_BASE],
.c = {
- .parent = &csi0_clk_src.c,
+ .parent = &csi1_clk_src.c,
.dbg_name = "csi1rdi_clk",
.ops = &clk_ops_branch,
CLK_INIT(csi1rdi_clk.c),
@@ -1995,6 +1999,8 @@
.cbcr_reg = MDP_AXI_CBCR,
.has_sibling = 1,
.base = &virt_bases[MMSS_BASE],
+ /* FIXME: Remove this once simulation is fixed. */
+ .halt_check = DELAY,
.c = {
.parent = &axi_clk_src.c,
.dbg_name = "mdp_axi_clk",
@@ -2148,6 +2154,8 @@
.cbcr_reg = VFE_AXI_CBCR,
.has_sibling = 1,
.base = &virt_bases[MMSS_BASE],
+ /* FIXME: Remove this once simulation is fixed. */
+ .halt_check = DELAY,
.c = {
.parent = &axi_clk_src.c,
.dbg_name = "vfe_axi_clk",
@@ -2921,7 +2929,7 @@
.multiplier = 1,
};
-static struct clk_lookup msm_clocks_8910[] = {
+static struct clk_lookup msm_clocks_8610[] = {
CLK_LOOKUP("xo", gcc_xo_clk_src.c, "msm_otg"),
CLK_LOOKUP("xo", gcc_xo_clk_src.c, "fe200000.qcom,lpass"),
CLK_LOOKUP("xo", gcc_xo_clk_src.c, "pil-q6v5-mss"),
@@ -3157,7 +3165,7 @@
CLK_LOOKUP("reg_clk", q6ss_ahbm_clk.c, "fe200000.qcom,lpass"),
};
-static struct clk_lookup msm_clocks_8910_rumi[] = {
+static struct clk_lookup msm_clocks_8610_rumi[] = {
CLK_DUMMY("core_clk", BLSP1_UART_CLK, "f991f000.serial", OFF),
CLK_DUMMY("iface_clk", BLSP1_UART_CLK, "f991f000.serial", OFF),
CLK_DUMMY("iface_clk", HSUSB_IFACE_CLK, "f9a55000.usb", OFF),
@@ -3183,9 +3191,9 @@
CLK_DUMMY("core_clk", NULL, "fd010000.qcom,iommu", OFF),
};
-struct clock_init_data msm8910_rumi_clock_init_data __initdata = {
- .table = msm_clocks_8910_rumi,
- .size = ARRAY_SIZE(msm_clocks_8910_rumi),
+struct clock_init_data msm8610_rumi_clock_init_data __initdata = {
+ .table = msm_clocks_8610_rumi,
+ .size = ARRAY_SIZE(msm_clocks_8610_rumi),
};
static struct pll_config_regs gpll0_regs __initdata = {
@@ -3346,7 +3354,7 @@
WARN(ret, "LPASS Audio Core GDSC did not power on.\n");
}
-static void __init msm8910_clock_post_init(void)
+static void __init msm8610_clock_post_init(void)
{
/*
* Hold an active set vote for CXO; this is because CXO is expected
@@ -3377,29 +3385,29 @@
#define APCS_GCC_CC_PHYS 0xF9011000
#define APCS_GCC_CC_SIZE SZ_4K
-static void __init msm8910_clock_pre_init(void)
+static void __init msm8610_clock_pre_init(void)
{
virt_bases[GCC_BASE] = ioremap(GCC_CC_PHYS, GCC_CC_SIZE);
if (!virt_bases[GCC_BASE])
- panic("clock-8910: Unable to ioremap GCC memory!");
+ panic("clock-8610: Unable to ioremap GCC memory!");
virt_bases[MMSS_BASE] = ioremap(MMSS_CC_PHYS, MMSS_CC_SIZE);
if (!virt_bases[MMSS_BASE])
- panic("clock-8910: Unable to ioremap MMSS_CC memory!");
+ panic("clock-8610: Unable to ioremap MMSS_CC memory!");
virt_bases[LPASS_BASE] = ioremap(LPASS_CC_PHYS, LPASS_CC_SIZE);
if (!virt_bases[LPASS_BASE])
- panic("clock-8910: Unable to ioremap LPASS_CC memory!");
+ panic("clock-8610: Unable to ioremap LPASS_CC memory!");
virt_bases[APCS_BASE] = ioremap(APCS_GCC_CC_PHYS, APCS_GCC_CC_SIZE);
if (!virt_bases[APCS_BASE])
- panic("clock-8910: Unable to ioremap APCS_GCC_CC memory!");
+ panic("clock-8610: Unable to ioremap APCS_GCC_CC memory!");
clk_ops_local_pll.enable = sr_hpm_lp_pll_clk_enable;
vdd_dig_reg = rpm_regulator_get(NULL, "vdd_dig");
if (IS_ERR(vdd_dig_reg))
- panic("clock-8910: Unable to get the vdd_dig regulator!");
+ panic("clock-8610: Unable to get the vdd_dig regulator!");
/*
* TODO: Set a voltage and enable vdd_dig, leaving the voltage high
@@ -3429,15 +3437,15 @@
clk_prepare_enable(&audio_core_ixfabric_clk.c);
}
-static int __init msm8910_clock_late_init(void)
+static int __init msm8610_clock_late_init(void)
{
return unvote_vdd_level(&vdd_dig, VDD_DIG_HIGH);
}
-struct clock_init_data msm8910_clock_init_data __initdata = {
- .table = msm_clocks_8910,
- .size = ARRAY_SIZE(msm_clocks_8910),
- .pre_init = msm8910_clock_pre_init,
- .post_init = msm8910_clock_post_init,
- .late_init = msm8910_clock_late_init,
+struct clock_init_data msm8610_clock_init_data __initdata = {
+ .table = msm_clocks_8610,
+ .size = ARRAY_SIZE(msm_clocks_8610),
+ .pre_init = msm8610_clock_pre_init,
+ .post_init = msm8610_clock_post_init,
+ .late_init = msm8610_clock_late_init,
};
diff --git a/arch/arm/mach-msm/clock-8960.c b/arch/arm/mach-msm/clock-8960.c
index 7038b06..14f369df 100644
--- a/arch/arm/mach-msm/clock-8960.c
+++ b/arch/arm/mach-msm/clock-8960.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2009-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
@@ -114,6 +114,7 @@
#define SATA_RXOOB_CLK_CTL_REG REG(0x2C0C)
#define SATA_PMALIVE_CLK_CTL_REG REG(0x2C10)
#define SATA_PHY_REF_CLK_CTL_REG REG(0x2C14)
+#define SATA_RESET REG(0x2C1C)
#define SATA_ACLK_CTL_REG REG(0x2C20)
#define SATA_PHY_CFG_CLK_CTL_REG REG(0x2C40)
#define USB_FSn_HCLK_CTL_REG(n) REG(0x2960+(0x20*((n)-1)))
@@ -2085,6 +2086,8 @@
.en_mask = BIT(4),
.halt_reg = CLK_HALT_MSS_SMPSS_MISC_STATE_REG,
.halt_bit = 27,
+ .reset_reg = SATA_RESET,
+ .reset_mask = BIT(0),
},
.c = {
.dbg_name = "sata_p_clk",
@@ -3046,22 +3049,30 @@
return -ENXIO;
}
-static enum handoff pix_rdi_clk_handoff(struct clk *c)
+static struct clk *pix_rdi_clk_get_parent(struct clk *c)
{
u32 reg;
struct pix_rdi_clk *rdi = to_pix_rdi_clk(c);
+
+ reg = readl_relaxed(rdi->s_reg);
+ rdi->cur_rate = reg & rdi->s_mask ? 1 : 0;
+ reg = readl_relaxed(rdi->s2_reg);
+ rdi->cur_rate = reg & rdi->s2_mask ? 2 : rdi->cur_rate;
+
+ return pix_rdi_mux_map[rdi->cur_rate];
+}
+
+static enum handoff pix_rdi_clk_handoff(struct clk *c)
+{
+ struct pix_rdi_clk *rdi = to_pix_rdi_clk(c);
enum handoff ret;
ret = branch_handoff(&rdi->b, &rdi->c);
if (ret == HANDOFF_DISABLED_CLK)
return ret;
- reg = readl_relaxed(rdi->s_reg);
- rdi->cur_rate = reg & rdi->s_mask ? 1 : 0;
- reg = readl_relaxed(rdi->s2_reg);
- rdi->cur_rate = reg & rdi->s2_mask ? 2 : rdi->cur_rate;
- c->parent = pix_rdi_mux_map[rdi->cur_rate];
-
+ rdi->prepared = true;
+ rdi->enabled = true;
return HANDOFF_ENABLED_CLK;
}
@@ -3075,6 +3086,7 @@
.get_rate = pix_rdi_clk_get_rate,
.list_rate = pix_rdi_clk_list_rate,
.reset = pix_rdi_clk_reset,
+ .get_parent = pix_rdi_clk_get_parent,
};
static struct pix_rdi_clk csi_pix_clk = {
@@ -5280,8 +5292,10 @@
CLK_LOOKUP("core_clk", gp2_clk.c, ""),
CLK_LOOKUP("core_clk", gsbi1_uart_clk.c, "msm_serial_hsl.1"),
CLK_LOOKUP("core_clk", gsbi2_uart_clk.c, ""),
+ CLK_LOOKUP("core_clk", gsbi2_uart_clk.c, "msm_serial_hsl.3"),
CLK_LOOKUP("core_clk", gsbi3_uart_clk.c, ""),
CLK_LOOKUP("core_clk", gsbi4_uart_clk.c, ""),
+ CLK_LOOKUP("core_clk", gsbi4_uart_clk.c, "msm_serial_hsl.4"),
CLK_LOOKUP("core_clk", gsbi5_uart_clk.c, "msm_serial_hsl.2"),
CLK_LOOKUP("core_clk", gsbi6_uart_clk.c, "msm_serial_hs.0"),
CLK_LOOKUP("core_clk", gsbi7_uart_clk.c, "msm_serial_hsl.0"),
@@ -5311,14 +5325,14 @@
CLK_LOOKUP("src_clk", usb_fs1_src_clk.c, ""),
CLK_LOOKUP("alt_core_clk", usb_fs1_xcvr_clk.c, ""),
CLK_LOOKUP("sys_clk", usb_fs1_sys_clk.c, ""),
- CLK_LOOKUP("ref_clk", sata_phy_ref_clk.c, ""),
- CLK_LOOKUP("cfg_clk", sata_phy_cfg_clk.c, ""),
- CLK_LOOKUP("src_clk", sata_src_clk.c, ""),
- CLK_LOOKUP("core_rxoob_clk", sata_rxoob_clk.c, ""),
- CLK_LOOKUP("core_pmalive_clk", sata_pmalive_clk.c, ""),
- CLK_LOOKUP("bus_clk", sata_a_clk.c, ""),
- CLK_LOOKUP("iface_clk", sata_p_clk.c, ""),
- CLK_LOOKUP("slave_iface_clk", sfab_sata_s_p_clk.c, ""),
+ CLK_LOOKUP("ref_clk", sata_phy_ref_clk.c, "msm_sata.0"),
+ CLK_LOOKUP("cfg_clk", sata_phy_cfg_clk.c, "msm_sata.0"),
+ CLK_LOOKUP("src_clk", sata_src_clk.c, "msm_sata.0"),
+ CLK_LOOKUP("core_rxoob_clk", sata_rxoob_clk.c, "msm_sata.0"),
+ CLK_LOOKUP("core_pmalive_clk", sata_pmalive_clk.c, "msm_sata.0"),
+ CLK_LOOKUP("bus_clk", sata_a_clk.c, "msm_sata.0"),
+ CLK_LOOKUP("iface_clk", sata_p_clk.c, "msm_sata.0"),
+ CLK_LOOKUP("slave_iface_clk", sfab_sata_s_p_clk.c, "msm_sata.0"),
CLK_LOOKUP("iface_clk", ce3_p_clk.c, "qce.0"),
CLK_LOOKUP("iface_clk", ce3_p_clk.c, "qcrypto.0"),
CLK_LOOKUP("core_clk", ce3_core_clk.c, "qce.0"),
@@ -5329,6 +5343,7 @@
CLK_LOOKUP("iface_clk", gsbi1_p_clk.c, "msm_serial_hsl.1"),
CLK_LOOKUP("iface_clk", gsbi1_p_clk.c, "qup_i2c.0"),
CLK_LOOKUP("iface_clk", gsbi2_p_clk.c, ""),
+ CLK_LOOKUP("iface_clk", gsbi2_p_clk.c, "msm_serial_hsl.3"),
CLK_LOOKUP("iface_clk", gsbi3_p_clk.c, "qup_i2c.3"),
CLK_LOOKUP("iface_clk", gsbi4_p_clk.c, "qup_i2c.4"),
CLK_LOOKUP("iface_clk", gsbi5_p_clk.c, "msm_serial_hsl.2"),
@@ -5337,6 +5352,7 @@
CLK_LOOKUP("iface_clk", gsbi6_p_clk.c, "msm_serial_hs.0"),
CLK_LOOKUP("iface_clk", gsbi6_p_clk.c, "spi_qsd.1"),
CLK_LOOKUP("iface_clk", gsbi7_p_clk.c, "msm_serial_hsl.0"),
+ CLK_LOOKUP("iface_clk", gsbi7_p_clk.c, "msm_serial_hsl.4"),
CLK_LOOKUP("ref_clk", tsif_ref_clk.c, "msm_tspp.0"),
CLK_LOOKUP("iface_clk", tsif_p_clk.c, "msm_tspp.0"),
CLK_LOOKUP("iface_clk", usb_fs1_p_clk.c, ""),
@@ -6366,7 +6382,7 @@
writel_relaxed(0x3C7097F9, AHB_EN2_REG);
}
- if (cpu_is_apq8064() || cpu_is_apq8064ab())
+ if (soc_class_is_apq8064())
rmwreg(0x00000000, AHB_EN3_REG, 0x00000001);
/* Deassert all locally-owned MM AHB resets. */
@@ -6389,7 +6405,7 @@
rmwreg(0x0027FCFF, MAXI_EN3_REG, 0x003FFFFF);
rmwreg(0x0027FCFF, MAXI_EN4_REG, 0x017FFFFF);
- if (cpu_is_apq8064() || cpu_is_apq8064ab())
+ if (soc_class_is_apq8064())
rmwreg(0x019FECFF, MAXI_EN5_REG, 0x01FFEFFF);
if (cpu_is_msm8930() || cpu_is_msm8930aa() || cpu_is_msm8627() ||
cpu_is_msm8930ab())
@@ -6426,8 +6442,7 @@
rmwreg(0x80FF0000, VFE_CC_REG, 0xE0FF4010);
rmwreg(0x800000FF, VFE_CC2_REG, 0xE00000FF);
rmwreg(0x80FF0000, VPE_CC_REG, 0xE0FF0010);
- if (cpu_is_msm8960ab() || cpu_is_msm8960() || cpu_is_apq8064()
- || cpu_is_apq8064ab()) {
+ if (cpu_is_msm8960ab() || cpu_is_msm8960() || soc_class_is_apq8064()) {
rmwreg(0x80FF0000, DSI2_BYTE_CC_REG, 0xE0FF0010);
rmwreg(0x80FF0000, DSI2_PIXEL_CC_REG, 0xE0FF0010);
rmwreg(0x80FF0000, JPEGD_CC_REG, 0xE0FF0010);
@@ -6445,7 +6460,7 @@
rmwreg(0x80FF0000, GFX2D0_CC_REG, 0xE0FF0010);
rmwreg(0x80FF0000, GFX2D1_CC_REG, 0xE0FF0010);
}
- if (cpu_is_apq8064() || cpu_is_apq8064ab()) {
+ if (soc_class_is_apq8064()) {
rmwreg(0x00000000, TV_CC_REG, 0x00004010);
rmwreg(0x80FF0000, VCAP_CC_REG, 0xE0FF1010);
}
@@ -6456,7 +6471,7 @@
* and wake-up value to max.
*/
rmwreg(0x0000004F, USB_HS1_HCLK_FS_REG, 0x0000007F);
- if (cpu_is_apq8064() || cpu_is_apq8064ab()) {
+ if (soc_class_is_apq8064()) {
rmwreg(0x0000004F, USB_HS3_HCLK_FS_REG, 0x0000007F);
rmwreg(0x0000004F, USB_HS4_HCLK_FS_REG, 0x0000007F);
}
@@ -6478,8 +6493,7 @@
/* Source the dsi_byte_clks from the DSI PHY PLLs */
rmwreg(0x1, DSI1_BYTE_NS_REG, 0x7);
- if (cpu_is_msm8960ab() || cpu_is_msm8960() || cpu_is_apq8064()
- || cpu_is_apq8064ab())
+ if (cpu_is_msm8960ab() || cpu_is_msm8960() || soc_class_is_apq8064())
rmwreg(0x2, DSI2_BYTE_NS_REG, 0x7);
/* Source the dsi1_esc_clk from the DSI1 PHY PLLs */
@@ -6489,7 +6503,7 @@
* Source the sata_phy_ref_clk from PXO and set predivider of
* sata_pmalive_clk to 1.
*/
- if (cpu_is_apq8064() || cpu_is_apq8064ab()) {
+ if (soc_class_is_apq8064()) {
rmwreg(0, SATA_PHY_REF_CLK_CTL_REG, 0x1);
rmwreg(0, SATA_PMALIVE_CLK_CTL_REG, 0x3);
}
@@ -6498,7 +6512,7 @@
* TODO: Programming below PLLs and prng_clk is temporary and
* needs to be removed after bootloaders program them.
*/
- if (cpu_is_apq8064() || cpu_is_apq8064ab()) {
+ if (soc_class_is_apq8064()) {
u32 is_pll_enabled;
/* Program pxo_src_clk to source from PXO */
@@ -6524,7 +6538,7 @@
writel_relaxed(0x2B, PRNG_CLK_NS_REG);
}
- if (cpu_is_apq8064()) {
+ if (cpu_is_apq8064() || cpu_is_apq8064aa()) {
/* Program PLL15 to 975MHz with ref clk = 27MHz */
configure_sr_pll(&pll15_config, &pll15_regs, 0);
} else if (cpu_is_apq8064ab()) {
@@ -6561,7 +6575,7 @@
/* Initialize clock registers. */
reg_init();
- if (cpu_is_apq8064() || cpu_is_apq8064ab())
+ if (soc_class_is_apq8064())
vdd_sr2_hdmi_pll.set_vdd = set_vdd_sr2_hdmi_pll_8064;
/* Detect PLL4 programmed for alternate 491.52MHz clock plan. */
@@ -6605,21 +6619,21 @@
* Change the freq tables for and voltage requirements for
* clocks which differ between chips.
*/
- if (cpu_is_apq8064()) {
+ if (cpu_is_apq8064() || cpu_is_apq8064aa())
gfx3d_clk.c.fmax = fmax_gfx3d_8064;
- }
- if (cpu_is_apq8064ab()) {
+
+ if (cpu_is_apq8064ab())
gfx3d_clk.c.fmax = fmax_gfx3d_8064ab;
- }
+
if ((cpu_is_apq8064() &&
SOCINFO_VERSION_MAJOR(socinfo_get_version()) == 2) ||
- cpu_is_apq8064ab()) {
+ cpu_is_apq8064ab() || cpu_is_apq8064aa()) {
vcodec_clk.c.fmax = fmax_vcodec_8064v2;
ce3_src_clk.c.fmax = fmax_ce3_8064v2;
sdc1_clk.c.fmax = fmax_sdc1_8064v2;
}
- if (cpu_is_apq8064() || cpu_is_apq8064ab()) {
+ if (soc_class_is_apq8064()) {
ijpeg_clk.c.fmax = fmax_ijpeg_8064;
mdp_clk.c.fmax = fmax_mdp_8064;
tv_src_clk.c.fmax = fmax_tv_src_8064;
@@ -6692,7 +6706,7 @@
clk_set_rate(&tsif_ref_clk.c, 105000);
clk_set_rate(&tssc_clk.c, 27000000);
clk_set_rate(&usb_hs1_xcvr_clk.c, 60000000);
- if (cpu_is_apq8064() || cpu_is_apq8064ab()) {
+ if (soc_class_is_apq8064()) {
clk_set_rate(&usb_hs3_xcvr_clk.c, 60000000);
clk_set_rate(&usb_hs4_xcvr_clk.c, 60000000);
}
diff --git a/arch/arm/mach-msm/clock-8974.c b/arch/arm/mach-msm/clock-8974.c
index 43a03a0..a48bf94 100644
--- a/arch/arm/mach-msm/clock-8974.c
+++ b/arch/arm/mach-msm/clock-8974.c
@@ -662,9 +662,9 @@
#define D0_ID 1
#define D1_ID 2
-#define A0_ID 3
-#define A1_ID 4
-#define A2_ID 5
+#define A0_ID 4
+#define A1_ID 5
+#define A2_ID 6
#define DIFF_CLK_ID 7
#define DIV_CLK1_ID 11
#define DIV_CLK2_ID 12
@@ -770,6 +770,8 @@
.dbg_name = "mmpll1_clk_src",
.rate = 846000000,
.ops = &clk_ops_pll_vote,
+ /* May be reassigned at runtime; alloc memory at compile time */
+ VDD_DIG_FMAX_MAP1(LOW, 846000000),
CLK_INIT(mmpll1_clk_src.c),
},
};
@@ -781,7 +783,7 @@
.c = {
.parent = &cxo_clk_src.c,
.dbg_name = "mmpll3_clk_src",
- .rate = 1000000000,
+ .rate = 820000000,
.ops = &clk_ops_local_pll,
CLK_INIT(mmpll3_clk_src.c),
},
@@ -934,6 +936,7 @@
F(56000000, gpll0, 1, 7, 75),
F(58982400, gpll0, 1, 1536, 15625),
F(60000000, gpll0, 10, 0, 0),
+ F(63160000, gpll0, 9.5, 0, 0),
F_END
};
@@ -2392,6 +2395,19 @@
F_END
};
+static struct clk_freq_tbl ftbl_mmss_axi_v2_clk[] = {
+ F_MM( 19200000, cxo, 1, 0, 0),
+ F_MM( 37500000, gpll0, 16, 0, 0),
+ F_MM( 50000000, gpll0, 12, 0, 0),
+ F_MM( 75000000, gpll0, 8, 0, 0),
+ F_MM(100000000, gpll0, 6, 0, 0),
+ F_MM(150000000, gpll0, 4, 0, 0),
+ F_MM(333430000, mmpll1, 3.5, 0, 0),
+ F_MM(400000000, mmpll0, 2, 0, 0),
+ F_MM(466800000, mmpll1, 2.5, 0, 0),
+ F_END
+};
+
static struct rcg_clk axi_clk_src = {
.cmd_rcgr_reg = 0x5040,
.set_rate = set_rate_hid,
@@ -2419,6 +2435,18 @@
F_END
};
+static struct clk_freq_tbl ftbl_ocmemnoc_v2_clk[] = {
+ F_MM( 19200000, cxo, 1, 0, 0),
+ F_MM( 37500000, gpll0, 16, 0, 0),
+ F_MM( 50000000, gpll0, 12, 0, 0),
+ F_MM( 75000000, gpll0, 8, 0, 0),
+ F_MM(100000000, gpll0, 6, 0, 0),
+ F_MM(150000000, gpll0, 4, 0, 0),
+ F_MM(333430000, mmpll1, 3.5, 0, 0),
+ F_MM(400000000, mmpll0, 2, 0, 0),
+ F_END
+};
+
struct rcg_clk ocmemnoc_clk_src = {
.cmd_rcgr_reg = OCMEMNOC_CMD_RCGR,
.set_rate = set_rate_hid,
@@ -2550,6 +2578,7 @@
F_MM(133330000, mmpll0, 6, 0, 0),
F_MM(160000000, mmpll0, 5, 0, 0),
F_MM(200000000, mmpll0, 4, 0, 0),
+ F_MM(240000000, gpll0, 2.5, 0, 0),
F_MM(266670000, mmpll0, 3, 0, 0),
F_MM(320000000, mmpll0, 2.5, 0, 0),
F_END
@@ -3181,6 +3210,16 @@
F_END
};
+static struct clk_freq_tbl ftbl_venus0_vcodec0_v2_clk[] = {
+ F_MM( 50000000, gpll0, 12, 0, 0),
+ F_MM(100000000, gpll0, 6, 0, 0),
+ F_MM(133330000, mmpll0, 6, 0, 0),
+ F_MM(200000000, mmpll0, 4, 0, 0),
+ F_MM(266670000, mmpll0, 3, 0, 0),
+ F_MM(465000000, mmpll3, 2, 0, 0),
+ F_END
+};
+
static struct rcg_clk vcodec0_clk_src = {
.cmd_rcgr_reg = VCODEC0_CMD_RCGR,
.set_rate = set_rate_mnd,
@@ -5055,6 +5094,7 @@
CLK_LOOKUP("iface_clk", gcc_blsp2_ahb_clk.c, "f9967000.i2c"),
CLK_LOOKUP("iface_clk", gcc_blsp2_ahb_clk.c, "f9966000.spi"),
CLK_LOOKUP("iface_clk", gcc_blsp2_ahb_clk.c, "f995e000.serial"),
+ CLK_LOOKUP("iface_clk", gcc_blsp2_ahb_clk.c, "f995d000.uart"),
CLK_LOOKUP("core_clk", gcc_blsp2_qup1_i2c_apps_clk.c, ""),
CLK_LOOKUP("core_clk", gcc_blsp2_qup1_spi_apps_clk.c, ""),
CLK_LOOKUP("core_clk", gcc_blsp2_qup2_i2c_apps_clk.c, ""),
@@ -5067,7 +5107,7 @@
CLK_LOOKUP("core_clk", gcc_blsp2_qup5_spi_apps_clk.c, ""),
CLK_LOOKUP("core_clk", gcc_blsp2_qup6_i2c_apps_clk.c, ""),
CLK_LOOKUP("core_clk", gcc_blsp2_qup6_spi_apps_clk.c, ""),
- CLK_LOOKUP("core_clk", gcc_blsp2_uart1_apps_clk.c, ""),
+ CLK_LOOKUP("core_clk", gcc_blsp2_uart1_apps_clk.c, "f995d000.uart"),
CLK_LOOKUP("core_clk", gcc_blsp2_uart2_apps_clk.c, "f995e000.serial"),
CLK_LOOKUP("core_clk", gcc_blsp2_uart3_apps_clk.c, ""),
CLK_LOOKUP("core_clk", gcc_blsp2_uart4_apps_clk.c, ""),
@@ -5114,8 +5154,8 @@
CLK_LOOKUP("iface_clk", gcc_sdcc4_ahb_clk.c, "msm_sdcc.4"),
CLK_LOOKUP("core_clk", gcc_sdcc4_apps_clk.c, "msm_sdcc.4"),
- CLK_LOOKUP("iface_clk", gcc_tsif_ahb_clk.c, ""),
- CLK_LOOKUP("ref_clk", gcc_tsif_ref_clk.c, ""),
+ CLK_LOOKUP("iface_clk", gcc_tsif_ahb_clk.c, "f99d8000.msm_tspp"),
+ CLK_LOOKUP("ref_clk", gcc_tsif_ref_clk.c, "f99d8000.msm_tspp"),
CLK_LOOKUP("mem_clk", gcc_usb30_master_clk.c, "usb_bam"),
CLK_LOOKUP("mem_iface_clk", gcc_sys_noc_usb3_axi_clk.c, "usb_bam"),
@@ -5134,11 +5174,15 @@
CLK_LOOKUP("cal_clk", gcc_usb_hsic_io_cal_clk.c, "msm_hsic_host"),
CLK_LOOKUP("core_clk", gcc_usb_hsic_system_clk.c, "msm_hsic_host"),
CLK_LOOKUP("ref_clk", div_clk2.c, "msm_smsc_hub"),
+ CLK_LOOKUP("iface_clk", gcc_usb_hs_ahb_clk.c, "msm_ehci_host"),
+ CLK_LOOKUP("core_clk", gcc_usb_hs_system_clk.c, "msm_ehci_host"),
+ CLK_LOOKUP("sleep_clk", gcc_usb2b_phy_sleep_clk.c, "msm_ehci_host"),
CLK_LOOKUP("pwm_clk", div_clk2.c, "0-0048"),
/* Multimedia clocks */
CLK_LOOKUP("bus_clk_src", axi_clk_src.c, ""),
CLK_LOOKUP("bus_clk", mmss_mmssnoc_axi_clk.c, ""),
+ CLK_LOOKUP("bus_clk", mmssnoc_ahb_clk.c, ""),
CLK_LOOKUP("core_clk", mdss_edpaux_clk.c, "fd923400.qcom,mdss_edp"),
CLK_LOOKUP("pixel_clk", mdss_edppixel_clk.c, "fd923400.qcom,mdss_edp"),
CLK_LOOKUP("link_clk", mdss_edplink_clk.c, "fd923400.qcom,mdss_edp"),
@@ -5179,18 +5223,24 @@
CLK_LOOKUP("cci_src_clk", cci_clk_src.c, "fda0c000.qcom,cci"),
CLK_LOOKUP("cci_clk", camss_cci_cci_clk.c, "fda0c000.qcom,cci"),
/* CSIPHY clocks */
+ CLK_LOOKUP("camss_top_ahb_clk", camss_top_ahb_clk.c,
+ "fda0ac00.qcom,csiphy"),
CLK_LOOKUP("ispif_ahb_clk", camss_ispif_ahb_clk.c,
"fda0ac00.qcom,csiphy"),
CLK_LOOKUP("csiphy_timer_src_clk", csi0phytimer_clk_src.c,
"fda0ac00.qcom,csiphy"),
CLK_LOOKUP("csiphy_timer_clk", camss_phy0_csi0phytimer_clk.c,
"fda0ac00.qcom,csiphy"),
+ CLK_LOOKUP("camss_top_ahb_clk", camss_top_ahb_clk.c,
+ "fda0b000.qcom,csiphy"),
CLK_LOOKUP("ispif_ahb_clk", camss_ispif_ahb_clk.c,
"fda0b000.qcom,csiphy"),
CLK_LOOKUP("csiphy_timer_src_clk", csi1phytimer_clk_src.c,
"fda0b000.qcom,csiphy"),
CLK_LOOKUP("csiphy_timer_clk", camss_phy1_csi1phytimer_clk.c,
"fda0b000.qcom,csiphy"),
+ CLK_LOOKUP("camss_top_ahb_clk", camss_top_ahb_clk.c,
+ "fda0b400.qcom,csiphy"),
CLK_LOOKUP("ispif_ahb_clk", camss_ispif_ahb_clk.c,
"fda0b400.qcom,csiphy"),
CLK_LOOKUP("csiphy_timer_src_clk", csi2phytimer_clk_src.c,
@@ -5198,6 +5248,10 @@
CLK_LOOKUP("csiphy_timer_clk", camss_phy2_csi2phytimer_clk.c,
"fda0b400.qcom,csiphy"),
/* CSID clocks */
+ CLK_LOOKUP("camss_top_ahb_clk", camss_top_ahb_clk.c,
+ "fda08000.qcom,csid"),
+ CLK_LOOKUP("ispif_ahb_clk", camss_ispif_ahb_clk.c,
+ "fda08000.qcom,csid"),
CLK_LOOKUP("csi0_ahb_clk", camss_csi0_ahb_clk.c, "fda08000.qcom,csid"),
CLK_LOOKUP("csi0_src_clk", csi0_clk_src.c, "fda08000.qcom,csid"),
CLK_LOOKUP("csi0_phy_clk", camss_csi0phy_clk.c, "fda08000.qcom,csid"),
@@ -5205,6 +5259,10 @@
CLK_LOOKUP("csi0_pix_clk", camss_csi0pix_clk.c, "fda08000.qcom,csid"),
CLK_LOOKUP("csi0_rdi_clk", camss_csi0rdi_clk.c, "fda08000.qcom,csid"),
+ CLK_LOOKUP("camss_top_ahb_clk", camss_top_ahb_clk.c,
+ "fda08400.qcom,csid"),
+ CLK_LOOKUP("ispif_ahb_clk", camss_ispif_ahb_clk.c,
+ "fda08400.qcom,csid"),
CLK_LOOKUP("csi0_ahb_clk", camss_csi0_ahb_clk.c, "fda08400.qcom,csid"),
CLK_LOOKUP("csi1_ahb_clk", camss_csi1_ahb_clk.c, "fda08400.qcom,csid"),
CLK_LOOKUP("csi0_src_clk", csi0_clk_src.c, "fda08400.qcom,csid"),
@@ -5218,6 +5276,10 @@
CLK_LOOKUP("csi0_rdi_clk", camss_csi0rdi_clk.c, "fda08400.qcom,csid"),
CLK_LOOKUP("csi1_rdi_clk", camss_csi1rdi_clk.c, "fda08400.qcom,csid"),
+ CLK_LOOKUP("camss_top_ahb_clk", camss_top_ahb_clk.c,
+ "fda08800.qcom,csid"),
+ CLK_LOOKUP("ispif_ahb_clk", camss_ispif_ahb_clk.c,
+ "fda08800.qcom,csid"),
CLK_LOOKUP("csi0_ahb_clk", camss_csi0_ahb_clk.c, "fda08800.qcom,csid"),
CLK_LOOKUP("csi2_ahb_clk", camss_csi2_ahb_clk.c, "fda08800.qcom,csid"),
CLK_LOOKUP("csi0_src_clk", csi0_clk_src.c, "fda08800.qcom,csid"),
@@ -5231,6 +5293,10 @@
CLK_LOOKUP("csi0_rdi_clk", camss_csi0rdi_clk.c, "fda08800.qcom,csid"),
CLK_LOOKUP("csi2_rdi_clk", camss_csi2rdi_clk.c, "fda08800.qcom,csid"),
+ CLK_LOOKUP("camss_top_ahb_clk", camss_top_ahb_clk.c,
+ "fda08c00.qcom,csid"),
+ CLK_LOOKUP("ispif_ahb_clk", camss_ispif_ahb_clk.c,
+ "fda08c00.qcom,csid"),
CLK_LOOKUP("csi0_ahb_clk", camss_csi0_ahb_clk.c, "fda08c00.qcom,csid"),
CLK_LOOKUP("csi3_ahb_clk", camss_csi3_ahb_clk.c, "fda08c00.qcom,csid"),
CLK_LOOKUP("csi0_src_clk", csi0_clk_src.c, "fda08c00.qcom,csid"),
@@ -5244,6 +5310,16 @@
CLK_LOOKUP("csi0_rdi_clk", camss_csi0rdi_clk.c, "fda08c00.qcom,csid"),
CLK_LOOKUP("csi3_rdi_clk", camss_csi3rdi_clk.c, "fda08c00.qcom,csid"),
+ /* ISPIF clocks */
+ CLK_LOOKUP("camss_vfe_vfe_clk", camss_vfe_vfe0_clk.c,
+ "fda0a000.qcom,ispif"),
+ CLK_LOOKUP("camss_csi_vfe_clk", camss_csi_vfe0_clk.c,
+ "fda0a000.qcom,ispif"),
+ CLK_LOOKUP("camss_vfe_vfe_clk1", camss_vfe_vfe1_clk.c,
+ "fda0a000.qcom,ispif"),
+ CLK_LOOKUP("camss_csi_vfe_clk1", camss_csi_vfe1_clk.c,
+ "fda0a000.qcom,ispif"),
+
/*VFE clocks*/
CLK_LOOKUP("camss_top_ahb_clk", camss_top_ahb_clk.c,
"fda10000.qcom,vfe"),
@@ -5373,6 +5449,14 @@
"msm-dai-q6.4106"),
CLK_LOOKUP("core_oe_clk", audio_core_lpaif_pcmoe_clk.c,
"msm-dai-q6.4106"),
+ CLK_LOOKUP("pcm_clk", audio_core_lpaif_pcm0_clk_src.c,
+ "msm-dai-q6.4107"),
+ CLK_LOOKUP("ibit_clk", audio_core_lpaif_pcm0_ibit_clk.c,
+ "msm-dai-q6.4107"),
+ CLK_LOOKUP("core_oe_src_clk", audio_core_lpaif_pcmoe_clk_src.c,
+ "msm-dai-q6.4107"),
+ CLK_LOOKUP("core_oe_clk", audio_core_lpaif_pcmoe_clk.c,
+ "msm-dai-q6.4107"),
CLK_LOOKUP("br_clk", audio_wrapper_br_clk.c, "fdd00000.qcom,ocmem"),
CLK_LOOKUP("bus_clk", gcc_mss_q6_bimc_axi_clk.c, "fc880000.qcom,mss"),
@@ -5488,7 +5572,7 @@
.base = &virt_bases[MMSS_BASE],
};
-/* MMPLL1 at 1000 MHz, main output enabled. */
+/* MMPLL1 at 846 MHz, main output enabled. */
static struct pll_config mmpll1_config __initdata = {
.l = 0x2C,
.m = 0x1,
@@ -5505,6 +5589,23 @@
.main_output_mask = BIT(0),
};
+/* MMPLL1 at 1167 MHz, main output enabled. */
+static struct pll_config mmpll1_v2_config __initdata = {
+ .l = 60,
+ .m = 25,
+ .n = 32,
+ .vco_val = 0x0,
+ .vco_mask = BM(21, 20),
+ .pre_div_val = 0x0,
+ .pre_div_mask = BM(14, 12),
+ .post_div_val = 0x0,
+ .post_div_mask = BM(9, 8),
+ .mn_ena_val = BIT(24),
+ .mn_ena_mask = BIT(24),
+ .main_output_val = BIT(0),
+ .main_output_mask = BIT(0),
+};
+
static struct pll_config_regs mmpll3_regs __initdata = {
.l_reg = (void __iomem *)MMPLL3_L_REG,
.m_reg = (void __iomem *)MMPLL3_M_REG,
@@ -5531,6 +5632,23 @@
.main_output_mask = BIT(0),
};
+/* MMPLL3 at 930 MHz, main output enabled. */
+static struct pll_config mmpll3_v2_config __initdata = {
+ .l = 48,
+ .m = 7,
+ .n = 16,
+ .vco_val = 0x0,
+ .vco_mask = BM(21, 20),
+ .pre_div_val = 0x0,
+ .pre_div_mask = BM(14, 12),
+ .post_div_val = 0x0,
+ .post_div_mask = BM(9, 8),
+ .mn_ena_val = BIT(24),
+ .mn_ena_mask = BIT(24),
+ .main_output_val = BIT(0),
+ .main_output_mask = BIT(0),
+};
+
static struct pll_config_regs lpapll0_regs __initdata = {
.l_reg = (void __iomem *)LPAPLL_L_REG,
.m_reg = (void __iomem *)LPAPLL_M_REG,
@@ -5577,8 +5695,14 @@
int ret;
configure_sr_hpm_lp_pll(&mmpll0_config, &mmpll0_regs, 1);
- configure_sr_hpm_lp_pll(&mmpll1_config, &mmpll1_regs, 1);
- configure_sr_hpm_lp_pll(&mmpll3_config, &mmpll3_regs, 0);
+
+ if (SOCINFO_VERSION_MAJOR(socinfo_get_version()) == 2) {
+ configure_sr_hpm_lp_pll(&mmpll1_v2_config, &mmpll1_regs, 1);
+ configure_sr_hpm_lp_pll(&mmpll3_v2_config, &mmpll3_regs, 0);
+ } else {
+ configure_sr_hpm_lp_pll(&mmpll1_config, &mmpll1_regs, 1);
+ configure_sr_hpm_lp_pll(&mmpll3_config, &mmpll3_regs, 0);
+ }
configure_sr_hpm_lp_pll(&lpapll0_config, &lpapll0_regs, 1);
/* Vote for GPLL0 to turn on. Needed by acpuclock. */
@@ -5634,8 +5758,13 @@
static void __init msm8974_clock_post_init(void)
{
- clk_set_rate(&axi_clk_src.c, 282000000);
- clk_set_rate(&ocmemnoc_clk_src.c, 282000000);
+ if (SOCINFO_VERSION_MAJOR(socinfo_get_version()) == 2) {
+ clk_set_rate(&axi_clk_src.c, 333430000);
+ clk_set_rate(&ocmemnoc_clk_src.c, 333430000);
+ } else {
+ clk_set_rate(&axi_clk_src.c, 282000000);
+ clk_set_rate(&ocmemnoc_clk_src.c, 282000000);
+ }
/*
* Hold an active set vote at a rate of 40MHz for the MMSS NOC AHB
@@ -5741,6 +5870,25 @@
enable_rpm_scaling();
reg_init();
+
+ /* v2 specific changes */
+ if (SOCINFO_VERSION_MAJOR(socinfo_get_version()) == 2) {
+ mmpll3_clk_src.c.rate = 930000000;
+ mmpll1_clk_src.c.rate = 1167000000;
+ mmpll1_clk_src.c.fmax[VDD_DIG_NOMINAL] = 1167000000;
+
+ ocmemnoc_clk_src.freq_tbl = ftbl_ocmemnoc_v2_clk;
+ ocmemnoc_clk_src.c.fmax[VDD_DIG_NOMINAL] = 333430000;
+
+ axi_clk_src.freq_tbl = ftbl_mmss_axi_v2_clk;
+ axi_clk_src.c.fmax[VDD_DIG_NOMINAL] = 333430000;
+ axi_clk_src.c.fmax[VDD_DIG_HIGH] = 466800000;
+
+ vcodec0_clk_src.freq_tbl = ftbl_venus0_vcodec0_v2_clk;
+ vcodec0_clk_src.c.fmax[VDD_DIG_HIGH] = 465000000;
+
+ mdp_clk_src.c.fmax[VDD_DIG_NOMINAL] = 240000000;
+ }
}
static int __init msm8974_clock_late_init(void)
diff --git a/arch/arm/mach-msm/clock-9625.c b/arch/arm/mach-msm/clock-9625.c
index 151f192..c4cbdfd 100644
--- a/arch/arm/mach-msm/clock-9625.c
+++ b/arch/arm/mach-msm/clock-9625.c
@@ -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
@@ -2097,10 +2097,10 @@
CLK_LOOKUP("iface_clk", gcc_usb_hs_ahb_clk.c, "f9a55000.usb"),
CLK_LOOKUP("core_clk", gcc_usb_hs_system_clk.c, "f9a55000.usb"),
- CLK_LOOKUP("iface_clk", gcc_usb_hsic_ahb_clk.c, "f9a15000.hsic"),
- CLK_LOOKUP("phy_clk", gcc_usb_hsic_clk.c, "f9a15000.hsic"),
- CLK_LOOKUP("cal_clk", gcc_usb_hsic_io_cal_clk.c, "f9a15000.hsic"),
- CLK_LOOKUP("core_clk", gcc_usb_hsic_system_clk.c, "f9a15000.hsic"),
+ CLK_LOOKUP("iface_clk", gcc_usb_hsic_ahb_clk.c, "msm_hsic_host"),
+ CLK_LOOKUP("phy_clk", gcc_usb_hsic_clk.c, "msm_hsic_host"),
+ CLK_LOOKUP("cal_clk", gcc_usb_hsic_io_cal_clk.c, "msm_hsic_host"),
+ CLK_LOOKUP("core_clk", gcc_usb_hsic_system_clk.c, "msm_hsic_host"),
CLK_LOOKUP("alt_core_clk", gcc_usb_hsic_xcvr_fs_clk.c, ""),
CLK_LOOKUP("core_clk", gcc_ce1_clk.c, "fd400000.qcom,qcedev"),
@@ -2175,6 +2175,7 @@
CLK_LOOKUP("core_clk", qdss_clk.c, "fc31a000.funnel"),
CLK_LOOKUP("core_clk", qdss_clk.c, "fc321000.stm"),
CLK_LOOKUP("core_clk", qdss_clk.c, "fc332000.etm"),
+ CLK_LOOKUP("core_clk", qdss_clk.c, "fc332000.jtagmm"),
CLK_LOOKUP("core_a_clk", qdss_clk.c, "fc322000.tmc"),
CLK_LOOKUP("core_a_clk", qdss_clk.c, "fc318000.tpiu"),
@@ -2185,6 +2186,7 @@
CLK_LOOKUP("core_a_clk", qdss_clk.c, "fc31a000.funnel"),
CLK_LOOKUP("core_a_clk", qdss_clk.c, "fc321000.stm"),
CLK_LOOKUP("core_a_clk", qdss_clk.c, "fc332000.etm"),
+ CLK_LOOKUP("core_a_clk", qdss_clk.c, "fc332000.jtagmm"),
};
diff --git a/arch/arm/mach-msm/clock-local.c b/arch/arm/mach-msm/clock-local.c
index 4432795..a173ba9 100644
--- a/arch/arm/mach-msm/clock-local.c
+++ b/arch/arm/mach-msm/clock-local.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2009-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
@@ -598,20 +598,21 @@
static enum handoff branch_clk_handoff(struct clk *c)
{
struct branch_clk *br = to_branch_clk(c);
- return branch_handoff(&br->b, &br->c);
+ if (branch_handoff(&br->b, &br->c) == HANDOFF_ENABLED_CLK) {
+ br->enabled = true;
+ return HANDOFF_ENABLED_CLK;
+ }
+
+ return HANDOFF_DISABLED_CLK;
}
-static enum handoff rcg_clk_handoff(struct clk *c)
+static struct clk *rcg_clk_get_parent(struct clk *c)
{
struct rcg_clk *rcg = to_rcg_clk(c);
uint32_t ctl_val, ns_val, md_val, ns_mask;
struct clk_freq_tbl *freq;
- enum handoff ret;
ctl_val = readl_relaxed(rcg->b.ctl_reg);
- ret = branch_handoff(&rcg->b, &rcg->c);
- if (ret == HANDOFF_DISABLED_CLK)
- return HANDOFF_DISABLED_CLK;
if (rcg->bank_info) {
const struct bank_masks *bank_masks = rcg->bank_info;
@@ -628,21 +629,40 @@
ns_mask = rcg->ns_mask;
md_val = rcg->md_reg ? readl_relaxed(rcg->md_reg) : 0;
}
+
if (!ns_mask)
- return HANDOFF_UNKNOWN_RATE;
+ return NULL;
+
ns_val = readl_relaxed(rcg->ns_reg) & ns_mask;
for (freq = rcg->freq_tbl; freq->freq_hz != FREQ_END; freq++) {
if ((freq->ns_val & ns_mask) == ns_val &&
(!freq->md_val || freq->md_val == md_val))
break;
}
+
if (freq->freq_hz == FREQ_END)
- return HANDOFF_UNKNOWN_RATE;
+ return NULL;
+ /* Cache the results for the handoff code. */
rcg->current_freq = freq;
- c->parent = freq->src_clk;
- c->rate = freq->freq_hz;
+ return freq->src_clk;
+}
+
+static enum handoff rcg_clk_handoff(struct clk *c)
+{
+ struct rcg_clk *rcg = to_rcg_clk(c);
+ enum handoff ret;
+
+ if (rcg->current_freq && rcg->current_freq->freq_hz != FREQ_END)
+ c->rate = rcg->current_freq->freq_hz;
+
+ ret = branch_handoff(&rcg->b, &rcg->c);
+ if (ret == HANDOFF_DISABLED_CLK)
+ return HANDOFF_DISABLED_CLK;
+
+ rcg->prepared = true;
+ rcg->enabled = true;
return HANDOFF_ENABLED_CLK;
}
@@ -861,6 +881,7 @@
.round_rate = rcg_clk_round_rate,
.reset = rcg_clk_reset,
.set_flags = rcg_clk_set_flags,
+ .get_parent = rcg_clk_get_parent,
};
static int cdiv_clk_enable(struct clk *c)
@@ -940,6 +961,7 @@
reg_val >>= cdiv->div_offset;
cdiv->cur_div = (reg_val & (cdiv->max_div - 1)) + 1;
}
+ c->rate = cdiv->cur_div;
return HANDOFF_ENABLED_CLK;
}
diff --git a/arch/arm/mach-msm/clock-local2.c b/arch/arm/mach-msm/clock-local2.c
index cf42355..dd78557 100644
--- a/arch/arm/mach-msm/clock-local2.c
+++ b/arch/arm/mach-msm/clock-local2.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. 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
@@ -69,8 +69,8 @@
#define MND_MODE_MASK BM(13, 12)
#define MND_DUAL_EDGE_MODE_BVAL BVAL(13, 12, 0x2)
#define CMD_RCGR_CONFIG_DIRTY_MASK BM(7, 4)
-#define CBCR_BRANCH_CDIV_MASK BM(24, 16)
-#define CBCR_BRANCH_CDIV_MASKED(val) BVAL(24, 16, (val));
+#define CBCR_CDIV_LSB 16
+#define CBCR_CDIV_MSB 24
enum branch_state {
BRANCH_ON,
@@ -235,21 +235,17 @@
return (rcg->freq_tbl + n)->freq_hz;
}
-static enum handoff _rcg_clk_handoff(struct rcg_clk *rcg, int has_mnd)
+static struct clk *_rcg_clk_get_parent(struct rcg_clk *rcg, int has_mnd)
{
u32 n_regval = 0, m_regval = 0, d_regval = 0;
u32 cfg_regval;
struct clk_freq_tbl *freq;
u32 cmd_rcgr_regval;
- /* Is the root enabled? */
- cmd_rcgr_regval = readl_relaxed(CMD_RCGR_REG(rcg));
- if ((cmd_rcgr_regval & CMD_RCGR_ROOT_STATUS_BIT))
- return HANDOFF_DISABLED_CLK;
-
/* Is there a pending configuration? */
+ cmd_rcgr_regval = readl_relaxed(CMD_RCGR_REG(rcg));
if (cmd_rcgr_regval & CMD_RCGR_CONFIG_DIRTY_MASK)
- return HANDOFF_UNKNOWN_RATE;
+ return NULL;
/* Get values of m, n, d, div and src_sel registers. */
if (has_mnd) {
@@ -299,23 +295,45 @@
/* No known frequency found */
if (freq->freq_hz == FREQ_END)
- return HANDOFF_UNKNOWN_RATE;
+ return NULL;
rcg->current_freq = freq;
- rcg->c.parent = freq->src_clk;
- rcg->c.rate = freq->freq_hz;
+ return freq->src_clk;
+}
+
+static enum handoff _rcg_clk_handoff(struct rcg_clk *rcg)
+{
+ u32 cmd_rcgr_regval;
+
+ if (rcg->current_freq && rcg->current_freq->freq_hz != FREQ_END)
+ rcg->c.rate = rcg->current_freq->freq_hz;
+
+ /* Is the root enabled? */
+ cmd_rcgr_regval = readl_relaxed(CMD_RCGR_REG(rcg));
+ if ((cmd_rcgr_regval & CMD_RCGR_ROOT_STATUS_BIT))
+ return HANDOFF_DISABLED_CLK;
return HANDOFF_ENABLED_CLK;
}
+static struct clk *rcg_mnd_clk_get_parent(struct clk *c)
+{
+ return _rcg_clk_get_parent(to_rcg_clk(c), 1);
+}
+
+static struct clk *rcg_clk_get_parent(struct clk *c)
+{
+ return _rcg_clk_get_parent(to_rcg_clk(c), 0);
+}
+
static enum handoff rcg_mnd_clk_handoff(struct clk *c)
{
- return _rcg_clk_handoff(to_rcg_clk(c), 1);
+ return _rcg_clk_handoff(to_rcg_clk(c));
}
static enum handoff rcg_clk_handoff(struct clk *c)
{
- return _rcg_clk_handoff(to_rcg_clk(c), 0);
+ return _rcg_clk_handoff(to_rcg_clk(c));
}
#define BRANCH_CHECK_MASK BM(31, 28)
@@ -412,8 +430,8 @@
spin_lock_irqsave(&local_clock_reg_lock, flags);
regval = readl_relaxed(CBCR_REG(branch));
- regval &= ~CBCR_BRANCH_CDIV_MASK;
- regval |= CBCR_BRANCH_CDIV_MASKED(rate);
+ regval &= ~BM(CBCR_CDIV_MSB, CBCR_CDIV_LSB);
+ regval |= BVAL(CBCR_CDIV_MSB, CBCR_CDIV_LSB, rate);
writel_relaxed(regval, CBCR_REG(branch));
spin_unlock_irqrestore(&local_clock_reg_lock, flags);
@@ -496,9 +514,12 @@
if ((cbcr_regval & CBCR_BRANCH_OFF_BIT))
return HANDOFF_DISABLED_CLK;
- if (c->parent) {
- if (c->parent->ops->handoff)
- return c->parent->ops->handoff(c->parent);
+ if (branch->max_div) {
+ cbcr_regval &= BM(CBCR_CDIV_MSB, CBCR_CDIV_LSB);
+ cbcr_regval >>= CBCR_CDIV_LSB;
+ c->rate = cbcr_regval;
+ } else if (!branch->has_sibling) {
+ c->rate = clk_get_rate(c->parent);
}
return HANDOFF_ENABLED_CLK;
@@ -611,6 +632,7 @@
.list_rate = rcg_clk_list_rate,
.round_rate = rcg_clk_round_rate,
.handoff = rcg_clk_handoff,
+ .get_parent = rcg_clk_get_parent,
};
struct clk_ops clk_ops_rcg_mnd = {
@@ -619,6 +641,7 @@
.list_rate = rcg_clk_list_rate,
.round_rate = rcg_clk_round_rate,
.handoff = rcg_mnd_clk_handoff,
+ .get_parent = rcg_mnd_clk_get_parent,
};
struct clk_ops clk_ops_branch = {
diff --git a/arch/arm/mach-msm/clock-mdss-8974.c b/arch/arm/mach-msm/clock-mdss-8974.c
index 79bc639..aca6494 100644
--- a/arch/arm/mach-msm/clock-mdss-8974.c
+++ b/arch/arm/mach-msm/clock-mdss-8974.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. 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
@@ -348,6 +348,16 @@
return ret;
}
+static enum handoff mdss_dsi_pll_handoff(struct clk *c)
+{
+ /*
+ * FIXME: Continuous display is not implemented. So the display is
+ * always off. Implement a poor man's handoff by always returning
+ * "disabled".
+ */
+ return HANDOFF_DISABLED_CLK;
+}
+
void hdmi_pll_disable(void)
{
clk_enable(mdss_dsi_ahb_clk);
@@ -766,6 +776,7 @@
.disable = mdss_dsi_pll_disable,
.set_rate = mdss_dsi_pll_pixel_set_rate,
.round_rate = mdss_dsi_pll_pixel_round_rate,
+ .handoff = mdss_dsi_pll_handoff,
};
struct clk_ops clk_ops_dsi_byte_pll = {
@@ -773,4 +784,5 @@
.disable = mdss_dsi_pll_disable,
.set_rate = mdss_dsi_pll_byte_set_rate,
.round_rate = mdss_dsi_pll_byte_round_rate,
+ .handoff = mdss_dsi_pll_handoff,
};
diff --git a/arch/arm/mach-msm/clock-rpm.c b/arch/arm/mach-msm/clock-rpm.c
index a4def28..c1cc27b 100644
--- a/arch/arm/mach-msm/clock-rpm.c
+++ b/arch/arm/mach-msm/clock-rpm.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2012, Code Aurora Forum. 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
@@ -285,6 +285,18 @@
if (rc < 0)
return HANDOFF_DISABLED_CLK;
+ /*
+ * Since RPM handoff code may update the software rate of the clock by
+ * querying the RPM, we need to make sure our request to RPM now
+ * matches the software rate of the clock. When we send the request
+ * to RPM, we also need to update any other state info we would
+ * normally update. So, call the appropriate clock function instead
+ * of directly using the RPM driver APIs.
+ */
+ rc = rpm_clk_prepare(clk);
+ if (rc < 0)
+ return HANDOFF_DISABLED_CLK;
+
return HANDOFF_ENABLED_CLK;
}
diff --git a/arch/arm/mach-msm/clock-voter.c b/arch/arm/mach-msm/clock-voter.c
index 7421ba6..c3145ef 100644
--- a/arch/arm/mach-msm/clock-voter.c
+++ b/arch/arm/mach-msm/clock-voter.c
@@ -1,5 +1,4 @@
-/*
- * Copyright (c) 2010-2011, Code Aurora Forum. 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
@@ -139,11 +138,17 @@
static enum handoff voter_clk_handoff(struct clk *clk)
{
- /* Apply default rate vote */
- if (clk->rate)
- return HANDOFF_ENABLED_CLK;
+ if (!clk->rate)
+ return HANDOFF_DISABLED_CLK;
- return HANDOFF_DISABLED_CLK;
+ /*
+ * Send the default rate to the parent if necessary and update the
+ * software state of the voter clock.
+ */
+ if (voter_clk_prepare(clk) < 0)
+ return HANDOFF_DISABLED_CLK;
+
+ return HANDOFF_ENABLED_CLK;
}
struct clk_ops clk_ops_voter = {
diff --git a/arch/arm/mach-msm/clock.c b/arch/arm/mach-msm/clock.c
index c2bf5ba..e0ee084 100644
--- a/arch/arm/mach-msm/clock.c
+++ b/arch/arm/mach-msm/clock.c
@@ -1,7 +1,7 @@
/* arch/arm/mach-msm/clock.c
*
* Copyright (C) 2007 Google, Inc.
- * Copyright (c) 2007-2012, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2007-2013, The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@@ -467,56 +467,83 @@
}
EXPORT_SYMBOL(msm_clock_register);
-static enum handoff __init __handoff_clk(struct clk *clk)
+static int __init __handoff_clk(struct clk *clk)
{
- enum handoff ret;
- struct handoff_clk *h;
- unsigned long rate;
- int err = 0;
+ enum handoff state = HANDOFF_DISABLED_CLK;
+ struct handoff_clk *h = NULL;
+ int rc;
+
+ if (clk == NULL || clk->flags & CLKFLAG_INIT_DONE ||
+ clk->flags & CLKFLAG_SKIP_HANDOFF)
+ return 0;
+
+ if (clk->flags & CLKFLAG_INIT_ERR)
+ return -ENXIO;
+
+ /* Handoff any 'depends' clock first. */
+ rc = __handoff_clk(clk->depends);
+ if (rc)
+ goto err;
/*
- * Tree roots don't have parents, but need to be handed off. So,
- * terminate recursion by returning "enabled". Also return "enabled"
- * for clocks with non-zero enable counts since they must have already
- * been handed off.
+ * Handoff functions for the parent must be called before the
+ * children can be handed off. Without handing off the parents and
+ * knowing their rate and state (on/off), it's impossible to figure
+ * out the rate and state of the children.
*/
- if (clk == NULL || clk->count)
- return HANDOFF_ENABLED_CLK;
+ if (clk->ops->get_parent)
+ clk->parent = clk->ops->get_parent(clk);
- /* Clocks without handoff functions are assumed to be disabled. */
- if (!clk->ops->handoff || (clk->flags & CLKFLAG_SKIP_HANDOFF))
- return HANDOFF_DISABLED_CLK;
+ if (IS_ERR(clk->parent)) {
+ rc = PTR_ERR(clk->parent);
+ goto err;
+ }
- /*
- * Handoff functions for children must be called before their parents'
- * so that the correct parent is available below.
- */
- ret = clk->ops->handoff(clk);
- if (ret == HANDOFF_ENABLED_CLK) {
- ret = __handoff_clk(clk->parent);
- if (ret == HANDOFF_ENABLED_CLK) {
- h = kmalloc(sizeof(*h), GFP_KERNEL);
- if (!h) {
- err = -ENOMEM;
- goto out;
- }
- err = clk_prepare_enable(clk);
- if (err)
- goto out;
- rate = clk_get_rate(clk);
- if (rate)
- pr_debug("%s rate=%lu\n", clk->dbg_name, rate);
- h->clk = clk;
- list_add_tail(&h->list, &handoff_list);
+ rc = __handoff_clk(clk->parent);
+ if (rc)
+ goto err;
+
+ if (clk->ops->handoff)
+ state = clk->ops->handoff(clk);
+
+ if (state == HANDOFF_ENABLED_CLK) {
+
+ h = kmalloc(sizeof(*h), GFP_KERNEL);
+ if (!h) {
+ rc = -ENOMEM;
+ goto err;
}
+
+ rc = clk_prepare_enable(clk->parent);
+ if (rc)
+ goto err;
+
+ rc = clk_prepare_enable(clk->depends);
+ if (rc)
+ goto err_depends;
+
+ rc = vote_rate_vdd(clk, clk->rate);
+ WARN(rc, "%s unable to vote for voltage!\n", clk->dbg_name);
+
+ clk->count = 1;
+ clk->prepare_count = 1;
+ h->clk = clk;
+ list_add_tail(&h->list, &handoff_list);
+
+ pr_debug("Handed off %s rate=%lu\n", clk->dbg_name, clk->rate);
}
-out:
- if (err) {
- pr_err("%s handoff failed (%d)\n", clk->dbg_name, err);
- kfree(h);
- ret = HANDOFF_DISABLED_CLK;
- }
- return ret;
+
+ clk->flags |= CLKFLAG_INIT_DONE;
+
+ return 0;
+
+err_depends:
+ clk_disable_unprepare(clk->parent);
+err:
+ kfree(h);
+ clk->flags |= CLKFLAG_INIT_ERR;
+ pr_err("%s handoff failed (%d)\n", clk->dbg_name, rc);
+ return rc;
}
/**
diff --git a/arch/arm/mach-msm/clock.h b/arch/arm/mach-msm/clock.h
index 181cf4c..002ee96 100644
--- a/arch/arm/mach-msm/clock.h
+++ b/arch/arm/mach-msm/clock.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@@ -49,8 +49,8 @@
extern struct clock_init_data msm8930_pm8917_clock_init_data;
extern struct clock_init_data msm8974_clock_init_data;
extern struct clock_init_data msm8974_rumi_clock_init_data;
-extern struct clock_init_data msm8910_clock_init_data;
-extern struct clock_init_data msm8910_rumi_clock_init_data;
+extern struct clock_init_data msm8610_clock_init_data;
+extern struct clock_init_data msm8610_rumi_clock_init_data;
int msm_clock_init(struct clock_init_data *data);
int find_vdd_level(struct clk *clk, unsigned long rate);
diff --git a/arch/arm/mach-msm/cpuidle.c b/arch/arm/mach-msm/cpuidle.c
index dd2dc1d..056f19e 100644
--- a/arch/arm/mach-msm/cpuidle.c
+++ b/arch/arm/mach-msm/cpuidle.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2012, Code Aurora Forum. 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
@@ -80,8 +80,7 @@
cpu_pm_enter();
#endif
- pm_mode = msm_pm_idle_prepare(dev, drv, index);
- dev->last_residency = msm_pm_idle_enter(pm_mode);
+ pm_mode = msm_pm_idle_enter(dev, drv, index);
for (i = 0; i < dev->state_count; i++) {
st_usage = &dev->states_usage[i];
if ((enum msm_pm_sleep_mode) cpuidle_get_statedata(st_usage)
diff --git a/arch/arm/mach-msm/devices-8064.c b/arch/arm/mach-msm/devices-8064.c
index dcd90d0..f87c540 100644
--- a/arch/arm/mach-msm/devices-8064.c
+++ b/arch/arm/mach-msm/devices-8064.c
@@ -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,11 +20,11 @@
#include <linux/dma-mapping.h>
#include <linux/coresight.h>
#include <linux/avtimer.h>
-#include <linux/ahci_platform.h>
#include <mach/irqs-8064.h>
#include <mach/board.h>
#include <mach/msm_iomap.h>
#include <mach/usbdiag.h>
+#include <mach/msm_serial_hs_lite.h>
#include <mach/msm_sps.h>
#include <mach/dma.h>
#include <mach/msm_dsps.h>
@@ -52,6 +52,7 @@
/* Address of GSBI blocks */
#define MSM_GSBI1_PHYS 0x12440000
+#define MSM_GSBI2_PHYS 0x12480000
#define MSM_GSBI3_PHYS 0x16200000
#define MSM_GSBI4_PHYS 0x16300000
#define MSM_GSBI5_PHYS 0x1A200000
@@ -60,7 +61,9 @@
/* GSBI UART devices */
#define MSM_UART1DM_PHYS (MSM_GSBI1_PHYS + 0x10000)
+#define MSM_UART2DM_PHYS (MSM_GSBI2_PHYS + 0x10000)
#define MSM_UART3DM_PHYS (MSM_GSBI3_PHYS + 0x40000)
+#define MSM_UART4DM_PHYS (MSM_GSBI4_PHYS + 0x40000)
#define MSM_UART5DM_PHYS (MSM_GSBI5_PHYS + 0x40000)
#define MSM_UART6DM_PHYS (MSM_GSBI6_PHYS + 0x40000)
#define MSM_UART7DM_PHYS (MSM_GSBI7_PHYS + 0x40000)
@@ -204,6 +207,38 @@
.resource = resources_uart_gsbi1,
};
+static struct resource resources_uart_gsbi2[] = {
+ {
+ .start = APQ8064_GSBI2_UARTDM_IRQ,
+ .end = APQ8064_GSBI2_UARTDM_IRQ,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .start = MSM_UART2DM_PHYS,
+ .end = MSM_UART2DM_PHYS + PAGE_SIZE - 1,
+ .name = "uartdm_resource",
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = MSM_GSBI2_PHYS,
+ .end = MSM_GSBI2_PHYS + PAGE_SIZE - 1,
+ .name = "gsbi_resource",
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct msm_serial_hslite_platform_data uart_gsbi2_pdata = {
+ .line = 0,
+};
+
+struct platform_device apq8064_device_uart_gsbi2 = {
+ .name = "msm_serial_hsl",
+ .id = 3,
+ .num_resources = ARRAY_SIZE(resources_uart_gsbi2),
+ .resource = resources_uart_gsbi2,
+ .dev.platform_data = &uart_gsbi2_pdata,
+};
+
static struct resource resources_uart_gsbi3[] = {
{
.start = GSBI3_UARTDM_IRQ,
@@ -351,6 +386,38 @@
.resource = resources_qup_i2c_gsbi4,
};
+static struct resource resources_uart_gsbi4[] = {
+ {
+ .start = GSBI4_UARTDM_IRQ,
+ .end = GSBI4_UARTDM_IRQ,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .start = MSM_UART4DM_PHYS,
+ .end = MSM_UART4DM_PHYS + PAGE_SIZE - 1,
+ .name = "uartdm_resource",
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = MSM_GSBI4_PHYS,
+ .end = MSM_GSBI4_PHYS + PAGE_SIZE - 1,
+ .name = "gsbi_resource",
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct msm_serial_hslite_platform_data uart_gsbi4_pdata = {
+ .line = 2,
+};
+
+struct platform_device apq8064_device_uart_gsbi4 = {
+ .name = "msm_serial_hsl",
+ .id = 4,
+ .num_resources = ARRAY_SIZE(resources_uart_gsbi4),
+ .resource = resources_uart_gsbi4,
+ .dev.platform_data = &uart_gsbi4_pdata,
+};
+
static struct resource resources_qup_spi_gsbi5[] = {
{
.name = "spi_base",
@@ -755,26 +822,49 @@
static struct resource tspp_resources[] = {
[0] = {
+ .name = "TSIF_TSPP_IRQ",
.flags = IORESOURCE_IRQ,
.start = TSIF_TSPP_IRQ,
- .end = TSIF1_IRQ,
+ .end = TSIF_TSPP_IRQ,
},
[1] = {
+ .name = "TSIF0_IRQ",
+ .flags = IORESOURCE_IRQ,
+ .start = TSIF1_IRQ,
+ .end = TSIF1_IRQ,
+ },
+ [2] = {
+ .name = "TSIF1_IRQ",
+ .flags = IORESOURCE_IRQ,
+ .start = TSIF2_IRQ,
+ .end = TSIF2_IRQ,
+ },
+ [3] = {
+ .name = "TSIF_BAM_IRQ",
+ .flags = IORESOURCE_IRQ,
+ .start = TSIF_BAM_IRQ,
+ .end = TSIF_BAM_IRQ,
+ },
+ [4] = {
+ .name = "MSM_TSIF0_PHYS",
.flags = IORESOURCE_MEM,
.start = MSM_TSIF0_PHYS,
.end = MSM_TSIF0_PHYS + MSM_TSIF_SIZE - 1,
},
- [2] = {
+ [5] = {
+ .name = "MSM_TSIF1_PHYS",
.flags = IORESOURCE_MEM,
.start = MSM_TSIF1_PHYS,
.end = MSM_TSIF1_PHYS + MSM_TSIF_SIZE - 1,
},
- [3] = {
+ [6] = {
+ .name = "MSM_TSPP_PHYS",
.flags = IORESOURCE_MEM,
.start = MSM_TSPP_PHYS,
.end = MSM_TSPP_PHYS + MSM_TSPP_SIZE - 1,
},
- [4] = {
+ [7] = {
+ .name = "MSM_TSPP_BAM_PHYS",
.flags = IORESOURCE_MEM,
.start = MSM_TSPP_BAM_PHYS,
.end = MSM_TSPP_BAM_PHYS + MSM_TSPP_BAM_SIZE - 1,
@@ -1779,9 +1869,11 @@
}
#define MSM_SATA_AHCI_BASE 0x29000000
-#define MSM_SATA_AHCI_REGS_SZ 0x17C
+#define MSM_SATA_AHCI_REGS_SZ 0x180
+#define MSM_SATA_PHY_BASE 0x1B400000
+#define MSM_SATA_PHY_REGS_SZ 0x200
-static struct resource resources_ahci[] = {
+static struct resource resources_sata[] = {
{
.name = "ahci_mem",
.flags = IORESOURCE_MEM,
@@ -1794,31 +1886,25 @@
.start = SATA_CONTROLLER_IRQ,
.end = SATA_CONTROLLER_IRQ,
},
-};
-
-static u64 ahci_dma_mask = DMA_BIT_MASK(32);
-static struct platform_device apq8064_device_ahci = {
- .name = "ahci",
- .id = 0,
- .num_resources = ARRAY_SIZE(resources_ahci),
- .resource = resources_ahci,
- .dev = {
- .dma_mask = &ahci_dma_mask,
- .coherent_dma_mask = DMA_BIT_MASK(32),
+ {
+ .name = "phy_mem",
+ .flags = IORESOURCE_MEM,
+ .start = MSM_SATA_PHY_BASE,
+ .end = MSM_SATA_PHY_BASE + MSM_SATA_PHY_REGS_SZ - 1,
},
};
-int __init apq8064_add_ahci(struct ahci_platform_data *platd)
-{
- struct platform_device *pdev;
-
- if (!platd)
- return -EINVAL;
-
- pdev = &apq8064_device_ahci;
- pdev->dev.platform_data = platd;
- return platform_device_register(pdev);
-}
+static u64 sata_dma_mask = DMA_BIT_MASK(32);
+struct platform_device apq8064_device_sata = {
+ .name = "msm_sata",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(resources_sata),
+ .resource = resources_sata,
+ .dev = {
+ .dma_mask = &sata_dma_mask,
+ .coherent_dma_mask = DMA_BIT_MASK(32),
+ },
+};
static struct resource resources_sps[] = {
{
diff --git a/arch/arm/mach-msm/devices-9615.c b/arch/arm/mach-msm/devices-9615.c
index cec57fd..ee6c5cb 100644
--- a/arch/arm/mach-msm/devices-9615.c
+++ b/arch/arm/mach-msm/devices-9615.c
@@ -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
@@ -11,6 +11,7 @@
*
*/
+#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/irq.h>
diff --git a/arch/arm/mach-msm/devices-msm7x27a.c b/arch/arm/mach-msm/devices-msm7x27a.c
index 3c60fa6..907af68 100644
--- a/arch/arm/mach-msm/devices-msm7x27a.c
+++ b/arch/arm/mach-msm/devices-msm7x27a.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, Code Aurora Forum. 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
@@ -2148,7 +2148,7 @@
{
msm_map_common_io();
if (socinfo_init() < 0)
- pr_err("%s: socinfo_init() failed!\n", __func__);
+ pr_err("socinfo_init() failed!\n");
msm7x27x_cache_init();
}
@@ -2164,7 +2164,7 @@
msm_map_msm8625_io();
if (socinfo_init() < 0)
- pr_err("%s: socinfo_init() failed!\n", __func__);
+ pr_err("socinfo_init() failed!\n");
msm7x27x_cache_init();
}
diff --git a/arch/arm/mach-msm/devices.h b/arch/arm/mach-msm/devices.h
index 2339706..8301c29 100644
--- a/arch/arm/mach-msm/devices.h
+++ b/arch/arm/mach-msm/devices.h
@@ -85,7 +85,9 @@
extern struct platform_device msm8960_device_ebi1_ch1_erp;
extern struct platform_device apq8064_device_uart_gsbi1;
+extern struct platform_device apq8064_device_uart_gsbi2;
extern struct platform_device apq8064_device_uart_gsbi3;
+extern struct platform_device apq8064_device_uart_gsbi4;
extern struct platform_device apq8064_device_uart_gsbi7;
extern struct platform_device apq8064_device_qup_i2c_gsbi1;
extern struct platform_device apq8064_device_qup_i2c_gsbi3;
@@ -95,6 +97,7 @@
extern struct platform_device apq8064_device_ssbi_pmic1;
extern struct platform_device apq8064_device_ssbi_pmic2;
extern struct platform_device apq8064_device_cache_erp;
+extern struct platform_device apq8064_device_sata;
extern struct platform_device msm9615_device_uart_gsbi4;
extern struct platform_device msm9615_device_qup_i2c_gsbi5;
diff --git a/arch/arm/mach-msm/idle-v7.S b/arch/arm/mach-msm/idle-v7.S
index 9a22996..6840f1c 100644
--- a/arch/arm/mach-msm/idle-v7.S
+++ b/arch/arm/mach-msm/idle-v7.S
@@ -2,7 +2,7 @@
* Idle processing for ARMv7-based Qualcomm SoCs.
*
* Copyright (C) 2007 Google, Inc.
- * Copyright (c) 2007-2009, 2011-2012 Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2007-2009, 2011-2013 The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@@ -97,7 +97,7 @@
mrc p15, 0, ip, c13, c0, 1 /* context ID */
stmia r0!, {r1-r9, ip}
-#ifdef CONFIG_MSM_JTAG
+#if defined(CONFIG_MSM_JTAG) || defined(CONFIG_MSM_JTAG_MM)
bl msm_jtag_save_state
#endif
@@ -185,7 +185,7 @@
blxne r1
dmb
-#ifdef CONFIG_MSM_JTAG
+#if defined(CONFIG_MSM_JTAG) || defined(CONFIG_MSM_JTAG_MM)
bl msm_jtag_restore_state
#endif
ldr r0, =msm_saved_state /* address of msm_saved_state ptr */
@@ -286,7 +286,7 @@
stmfd sp!, {lr}
blxne r1
dmb
-#ifdef CONFIG_MSM_JTAG
+#if defined(CONFIG_MSM_JTAG) || defined(CONFIG_MSM_JTAG_MM)
bl msm_jtag_restore_state
#endif
ldmfd sp!, {lr}
diff --git a/arch/arm/mach-msm/include/mach/board.h b/arch/arm/mach-msm/include/mach/board.h
index 8b5c70f..56c78c6 100644
--- a/arch/arm/mach-msm/include/mach/board.h
+++ b/arch/arm/mach-msm/include/mach/board.h
@@ -1,7 +1,7 @@
/* arch/arm/mach-msm/include/mach/board.h
*
* Copyright (C) 2007 Google, Inc.
- * Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2008-2013, The Linux Foundation. All rights reserved.
* Author: Brian Swetland <swetland@google.com>
*
* This software is licensed under the terms of the GNU General Public
@@ -182,9 +182,8 @@
uint32_t delay;
};
-struct msm_camera_csi_lane_params {
- uint16_t csi_lane_assign;
- uint16_t csi_lane_mask;
+struct msm_camera_gpio_num_info {
+ uint16_t gpio_num[2];
};
struct msm_camera_gpio_conf {
@@ -201,6 +200,7 @@
uint8_t camera_off_table_size;
uint32_t *camera_on_table;
uint8_t camera_on_table_size;
+ struct msm_camera_gpio_num_info *gpio_num_info;
};
enum msm_camera_i2c_mux_mode {
@@ -215,13 +215,6 @@
enum msm_camera_i2c_mux_mode i2c_mux_mode;
};
-enum msm_camera_vreg_name_t {
- CAM_VDIG,
- CAM_VIO,
- CAM_VANA,
- CAM_VAF,
-};
-
struct msm_camera_sensor_platform_info {
int mount_angle;
int sensor_reset;
@@ -595,9 +588,9 @@
void msm_map_msm8226_io(void);
void msm8226_init_irq(void);
void msm8226_init_gpiomux(void);
-void msm8910_init_gpiomux(void);
-void msm_map_msm8910_io(void);
-void msm8910_init_irq(void);
+void msm8610_init_gpiomux(void);
+void msm_map_msm8610_io(void);
+void msm8610_init_irq(void);
/* Dump debug info (states, rate, etc) of clocks */
#if defined(CONFIG_ARCH_MSM7X27)
diff --git a/arch/arm/mach-msm/include/mach/camera2.h b/arch/arm/mach-msm/include/mach/camera2.h
new file mode 100644
index 0000000..e624131
--- /dev/null
+++ b/arch/arm/mach-msm/include/mach/camera2.h
@@ -0,0 +1,91 @@
+/* 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
+ * 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 __CAMERA2_H__
+#define __CAMERA2_H__
+
+#include <media/msm_cam_sensor.h>
+#include <mach/board.h>
+
+enum msm_sensor_device_type_t {
+ MSM_SENSOR_I2C_DEVICE,
+ MSM_SENSOR_PLATFORM_DEVICE,
+};
+
+enum msm_bus_perf_setting {
+ S_INIT,
+ S_PREVIEW,
+ S_VIDEO,
+ S_CAPTURE,
+ S_ZSL,
+ S_STEREO_VIDEO,
+ S_STEREO_CAPTURE,
+ S_DEFAULT,
+ S_LIVESHOT,
+ S_DUAL,
+ S_EXIT
+};
+
+struct msm_camera_slave_info {
+ uint16_t sensor_slave_addr;
+ uint16_t sensor_id_reg_addr;
+ uint16_t sensor_id;
+};
+
+struct msm_cam_clk_info {
+ const char *clk_name;
+ long clk_rate;
+ uint32_t delay;
+};
+
+struct msm_cam_clk_setting {
+ struct msm_cam_clk_info *clk_info;
+ uint16_t num_clk_info;
+ uint8_t enable;
+};
+
+struct v4l2_subdev_info {
+ enum v4l2_mbus_pixelcode code;
+ enum v4l2_colorspace colorspace;
+ uint16_t fmt;
+ uint16_t order;
+};
+
+struct msm_camera_sensor_board_info {
+ const char *sensor_name;
+ struct msm_camera_slave_info *slave_info;
+ struct msm_camera_csi_lane_params *csi_lane_params;
+ struct camera_vreg_t *cam_vreg;
+ int num_vreg;
+ struct msm_camera_sensor_strobe_flash_data *strobe_flash_data;
+ struct msm_camera_gpio_conf *gpio_conf;
+ struct msm_actuator_info *actuator_info;
+ struct msm_camera_i2c_conf *i2c_conf;
+ struct msm_sensor_info_t *sensor_info;
+ struct msm_sensor_init_params *sensor_init_params;
+};
+
+enum msm_camera_i2c_cmd_type {
+ MSM_CAMERA_I2C_CMD_WRITE,
+ MSM_CAMERA_I2C_CMD_POLL,
+};
+
+struct msm_camera_i2c_reg_conf {
+ uint16_t reg_addr;
+ uint16_t reg_data;
+ enum msm_camera_i2c_data_type dt;
+ enum msm_camera_i2c_cmd_type cmd_type;
+ int16_t mask;
+};
+
+#endif
diff --git a/arch/arm/mach-msm/include/mach/clk-provider.h b/arch/arm/mach-msm/include/mach/clk-provider.h
index 0f2feaa..475b483 100644
--- a/arch/arm/mach-msm/include/mach/clk-provider.h
+++ b/arch/arm/mach-msm/include/mach/clk-provider.h
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2007 Google, Inc.
- * Copyright (c) 2007-2012, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2007-2013, The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@@ -69,7 +69,6 @@
enum handoff {
HANDOFF_ENABLED_CLK,
HANDOFF_DISABLED_CLK,
- HANDOFF_UNKNOWN_RATE,
};
struct clk_ops {
diff --git a/arch/arm/mach-msm/include/mach/clk.h b/arch/arm/mach-msm/include/mach/clk.h
index d69b372..1191bb7 100644
--- a/arch/arm/mach-msm/include/mach/clk.h
+++ b/arch/arm/mach-msm/include/mach/clk.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009, 2012 Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2009, 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
@@ -21,6 +21,8 @@
#define CLKFLAG_SKIP_HANDOFF 0x00000100
#define CLKFLAG_MIN 0x00000400
#define CLKFLAG_MAX 0x00000800
+#define CLKFLAG_INIT_DONE 0x00001000
+#define CLKFLAG_INIT_ERR 0x00002000
struct clk_lookup;
struct clk;
diff --git a/arch/arm/mach-msm/include/mach/gpio.h b/arch/arm/mach-msm/include/mach/gpio.h
index 137d243..2d47e4e 100644
--- a/arch/arm/mach-msm/include/mach/gpio.h
+++ b/arch/arm/mach-msm/include/mach/gpio.h
@@ -16,7 +16,7 @@
#ifndef __ASM_ARCH_MSM_GPIO_H
#define __ASM_ARCH_MSM_GPIO_H
-#define ARCH_NR_GPIOS 512
+#define ARCH_NR_GPIOS 1024
#include <linux/interrupt.h>
#include <asm-generic/gpio.h>
diff --git a/arch/arm/mach-msm/include/mach/iommu.h b/arch/arm/mach-msm/include/mach/iommu.h
index 8b39e34..bbf3153 100644
--- a/arch/arm/mach-msm/include/mach/iommu.h
+++ b/arch/arm/mach-msm/include/mach/iommu.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
@@ -94,6 +94,7 @@
* @bfb_settings: Optional BFB performance tuning parameters
* @dev: Struct device this hardware instance is tied to
* @list: List head to link all iommus together
+ * @clk_reg_virt: Optional clock register virtual address.
*
* A msm_iommu_drvdata holds the global driver data about a single piece
* of an IOMMU hardware instance.
@@ -108,14 +109,18 @@
struct clk *aclk;
const char *name;
struct regulator *gdsc;
+ struct regulator *alt_gdsc;
struct msm_iommu_bfb_settings *bfb_settings;
int sec_id;
struct device *dev;
struct list_head list;
+ void __iomem *clk_reg_virt;
};
void msm_iommu_add_drv(struct msm_iommu_drvdata *drv);
void msm_iommu_remove_drv(struct msm_iommu_drvdata *drv);
+void program_iommu_bfb_settings(void __iomem *base,
+ const struct msm_iommu_bfb_settings *bfb_settings);
/**
* struct msm_iommu_ctx_drvdata - an IOMMU context bank instance
diff --git a/arch/arm/mach-msm/include/mach/iommu_perfmon.h b/arch/arm/mach-msm/include/mach/iommu_perfmon.h
new file mode 100644
index 0000000..b44523f
--- /dev/null
+++ b/arch/arm/mach-msm/include/mach/iommu_perfmon.h
@@ -0,0 +1,177 @@
+/* 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
+ * 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/err.h>
+#include <linux/mutex.h>
+#include <linux/list.h>
+
+#ifndef MSM_IOMMU_PERFMON_H
+#define MSM_IOMMU_PERFMON_H
+
+
+/**
+ * struct iommu_access_ops - Callbacks for accessing IOMMU
+ * @iommu_power_on: Turn on clocks/power to unit
+ * @iommu_power_off: Turn off clocks/power to unit
+ * @iommu_lock_acquire: Acquire any locks needed
+ * @iommu_lock_release: Release locks needed
+ */
+struct iommu_access_ops {
+ int (*iommu_power_on)(void *);
+ int (*iommu_power_off)(void *);
+ void (*iommu_lock_acquire)(void);
+ void (*iommu_lock_release)(void);
+};
+
+/**
+ * struct iommu_pmon_counter - container for a performance counter.
+ * @counter_no: counter number within the group
+ * @absolute_counter_no: counter number within IOMMU PMU
+ * @value: cached counter value
+ * @overflow_count: no of times counter has overflowed
+ * @enabled: indicates whether counter is enabled or not
+ * @current_event_class: current selected event class, -1 if none
+ * @counter_dir: debugfs directory for this counter
+ * @cnt_group: group this counter belongs to
+ */
+struct iommu_pmon_counter {
+ unsigned int counter_no;
+ unsigned int absolute_counter_no;
+ unsigned long value;
+ unsigned long overflow_count;
+ unsigned int enabled;
+ int current_event_class;
+ struct dentry *counter_dir;
+ struct iommu_pmon_cnt_group *cnt_group;
+};
+
+/**
+ * struct iommu_pmon_cnt_group - container for a perf mon counter group.
+ * @grp_no: group number
+ * @num_counters: number of counters in this group
+ * @counters: list of counter in this group
+ * @group_dir: debugfs directory for this group
+ * @pmon: pointer to the iommu_pmon object this group belongs to
+ */
+struct iommu_pmon_cnt_group {
+ unsigned int grp_no;
+ unsigned int num_counters;
+ struct iommu_pmon_counter *counters;
+ struct dentry *group_dir;
+ struct iommu_pmon *pmon;
+};
+
+/**
+ * struct iommu_info - container for a perf mon iommu info.
+ * @iommu_name: name of the iommu from device tree
+ * @base: virtual base address for this iommu
+ * @evt_irq: irq number for event overflow interrupt
+ * @iommu_dev: pointer to iommu device
+ * @ops: iommu access operations pointer.
+ */
+struct iommu_info {
+ const char *iommu_name;
+ void *base;
+ int evt_irq;
+ struct device *iommu_dev;
+ struct iommu_access_ops *ops;
+};
+
+/**
+ * struct iommu_pmon - main container for a perf mon data.
+ * @iommu_dir: debugfs directory for this iommu
+ * @iommu: iommu_info instance
+ * @iommu_list: iommu_list head
+ * @cnt_grp: list of counter groups
+ * @num_groups: number of counter groups
+ * @event_cls_supp_value: event classes supported for this PMU
+ * @enabled: Indicates whether perf. mon is enabled or not
+ * @iommu_attached Indicates whether iommu is attached or not.
+ * @lock: mutex used to synchronize access to shared data
+ */
+struct iommu_pmon {
+ struct dentry *iommu_dir;
+ struct iommu_info iommu;
+ struct list_head iommu_list;
+ struct iommu_pmon_cnt_group *cnt_grp;
+ unsigned int num_groups;
+ unsigned int event_cls_supp_value;
+ unsigned int enabled;
+ unsigned int iommu_attach_count;
+ struct mutex lock;
+};
+
+extern struct iommu_access_ops iommu_access_ops;
+
+#ifdef CONFIG_MSM_IOMMU_PMON
+/**
+ * Allocate memory for performance monitor structure. Must
+ * be called befre iommu_pm_iommu_register
+ */
+struct iommu_info *msm_iommu_pm_alloc(struct device *iommu_dev);
+
+/**
+ * Free memory previously allocated with iommu_pm_alloc
+ */
+void msm_iommu_pm_free(struct device *iommu_dev);
+
+/**
+ * Register iommu with the performance monitor module.
+ */
+int msm_iommu_pm_iommu_register(struct iommu_info *info);
+
+/**
+ * Unregister iommu with the performance monitor module.
+ */
+void msm_iommu_pm_iommu_unregister(struct device *dev);
+
+/**
+ * Called by iommu driver when attaching is complete
+ * Must NOT be called with IOMMU mutexes held.
+ * @param iommu_dev IOMMU device that is attached
+ */
+void msm_iommu_attached(struct device *dev);
+
+/**
+ * Called by iommu driver before detaching.
+ * Must NOT be called with IOMMU mutexes held.
+ * @param iommu_dev IOMMU device that is going to be detached
+ */
+void msm_iommu_detached(struct device *dev);
+#else
+static inline struct iommu_info *msm_iommu_pm_alloc(struct device *iommu_dev)
+{
+ return NULL;
+}
+
+static inline void msm_iommu_pm_free(struct device *iommu_dev)
+{
+ return;
+}
+
+static inline int msm_iommu_pm_iommu_register(struct iommu_info *info)
+{
+ return -EIO;
+}
+
+static inline void msm_iommu_pm_iommu_unregister(struct device *dev)
+{
+}
+
+static inline void msm_iommu_attached(struct device *dev)
+{
+}
+
+static inline void msm_iommu_detached(struct device *dev)
+{
+}
+#endif
+#endif
diff --git a/arch/arm/mach-msm/include/mach/irqs.h b/arch/arm/mach-msm/include/mach/irqs.h
index 6955c80..65d5d02 100644
--- a/arch/arm/mach-msm/include/mach/irqs.h
+++ b/arch/arm/mach-msm/include/mach/irqs.h
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2007 Google, Inc.
- * Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2008-2013, The Linux Foundation. All rights reserved.
* Author: Brian Swetland <swetland@google.com>
*
* This software is licensed under the terms of the GNU General Public
@@ -108,7 +108,7 @@
#define NR_QPNP_IRQS 32768
#define NR_BOARD_IRQS NR_QPNP_IRQS
-#elif defined(CONFIG_ARCH_MSM8910) || defined(CONFIG_ARCH_MSM8226)
+#elif defined(CONFIG_ARCH_MSM8610) || defined(CONFIG_ARCH_MSM8226)
#define NR_MSM_IRQS 256
#define NR_GPIO_IRQS 117
#define NR_QPNP_IRQS 32768
diff --git a/arch/arm/mach-msm/include/mach/jtag.h b/arch/arm/mach-msm/include/mach/jtag.h
index 3850eff..2131be6 100644
--- a/arch/arm/mach-msm/include/mach/jtag.h
+++ b/arch/arm/mach-msm/include/mach/jtag.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. 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
@@ -13,7 +13,7 @@
#ifndef __MACH_JTAG_H
#define __MACH_JTAG_H
-#ifdef CONFIG_MSM_JTAG
+#if defined(CONFIG_MSM_JTAG) || defined(CONFIG_MSM_JTAG_MM)
extern void msm_jtag_save_state(void);
extern void msm_jtag_restore_state(void);
#else
diff --git a/arch/arm/mach-msm/include/mach/msm_iomap-8910.h b/arch/arm/mach-msm/include/mach/msm_iomap-8610.h
similarity index 61%
rename from arch/arm/mach-msm/include/mach/msm_iomap-8910.h
rename to arch/arm/mach-msm/include/mach/msm_iomap-8610.h
index 64990da..05544af 100644
--- a/arch/arm/mach-msm/include/mach/msm_iomap-8910.h
+++ b/arch/arm/mach-msm/include/mach/msm_iomap-8610.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 software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@@ -10,8 +10,8 @@
* GNU General Public License for more details.
*/
-#ifndef __ASM_ARCH_MSM_IOMAP_8910_H
-#define __ASM_ARCH_MSM_IOMAP_8910_H
+#ifndef __ASM_ARCH_MSM_IOMAP_8610_H
+#define __ASM_ARCH_MSM_IOMAP_8610_H
/* Physical base address and size of peripherals.
* Ordered by the virtual base addresses they will be mapped at.
@@ -22,21 +22,21 @@
*
*/
-#define MSM8910_MSM_SHARED_RAM_PHYS 0x0D600000
+#define MSM8610_MSM_SHARED_RAM_PHYS 0x0D600000
-#define MSM8910_APCS_GCC_PHYS 0xF9011000
-#define MSM8910_APCS_GCC_SIZE SZ_4K
+#define MSM8610_APCS_GCC_PHYS 0xF9011000
+#define MSM8610_APCS_GCC_SIZE SZ_4K
-#define MSM8910_TLMM_PHYS 0xFD510000
-#define MSM8910_TLMM_SIZE SZ_16K
+#define MSM8610_TLMM_PHYS 0xFD510000
+#define MSM8610_TLMM_SIZE SZ_16K
-#define MSM8910_IMEM_PHYS 0xFE805000
-#define MSM8910_IMEM_SIZE SZ_4K
+#define MSM8610_IMEM_PHYS 0xFE805000
+#define MSM8610_IMEM_SIZE SZ_4K
-#define MSM8910_MPM2_PSHOLD_PHYS 0xFC4AB000
-#define MSM8910_MPM2_PSHOLD_SIZE SZ_4K
+#define MSM8610_MPM2_PSHOLD_PHYS 0xFC4AB000
+#define MSM8610_MPM2_PSHOLD_SIZE SZ_4K
-#ifdef CONFIG_DEBUG_MSM8910_UART
+#ifdef CONFIG_DEBUG_MSM8610_UART
#define MSM_DEBUG_UART_BASE IOMEM(0xFA71E000)
#define MSM_DEBUG_UART_PHYS 0xF991E000
#endif
diff --git a/arch/arm/mach-msm/include/mach/msm_iomap.h b/arch/arm/mach-msm/include/mach/msm_iomap.h
index f372b1e..8c49539 100644
--- a/arch/arm/mach-msm/include/mach/msm_iomap.h
+++ b/arch/arm/mach-msm/include/mach/msm_iomap.h
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2007 Google, Inc.
- * Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2008-2013, The Linux Foundation. All rights reserved.
* Author: Brian Swetland <swetland@google.com>
*
* This software is licensed under the terms of the GNU General Public
@@ -54,7 +54,7 @@
defined(CONFIG_ARCH_MSM7X25) || defined(CONFIG_ARCH_MSM7X01A) || \
defined(CONFIG_ARCH_MSM8625) || defined(CONFIG_ARCH_MSM7X30) || \
defined(CONFIG_ARCH_MSM9625) || defined(CONFIG_ARCH_MPQ8092) || \
- defined(CONFIG_ARCH_MSM8226) || defined(CONFIG_ARCH_MSM8910)
+ defined(CONFIG_ARCH_MSM8226) || defined(CONFIG_ARCH_MSM8610)
/* Unified iomap */
@@ -122,7 +122,7 @@
#include "msm_iomap-9625.h"
#include "msm_iomap-8092.h"
#include "msm_iomap-8226.h"
-#include "msm_iomap-8910.h"
+#include "msm_iomap-8610.h"
#else
/* Legacy single-target iomap */
diff --git a/arch/arm/mach-msm/include/mach/msm_ipc_logging.h b/arch/arm/mach-msm/include/mach/msm_ipc_logging.h
index 0a203a5..ec9fdb0 100644
--- a/arch/arm/mach-msm/include/mach/msm_ipc_logging.h
+++ b/arch/arm/mach-msm/include/mach/msm_ipc_logging.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. 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
@@ -185,47 +185,49 @@
struct decode_context *));
#else
-void *ipc_log_context_create(int max_num_pages, const char *modname)
+static inline void *ipc_log_context_create(int max_num_pages,
+ const char *modname)
{ return NULL; }
-void msg_encode_start(struct encode_context *ectxt, uint32_t type) { }
+static inline void msg_encode_start(struct encode_context *ectxt,
+ uint32_t type) { }
-int tsv_timestamp_write(struct encode_context *ectxt)
+static inline int tsv_timestamp_write(struct encode_context *ectxt)
{ return -EINVAL; }
-int tsv_pointer_write(struct encode_context *ectxt, void *pointer)
+static inline int tsv_pointer_write(struct encode_context *ectxt, void *pointer)
{ return -EINVAL; }
-int tsv_int32_write(struct encode_context *ectxt, int32_t n)
+static inline int tsv_int32_write(struct encode_context *ectxt, int32_t n)
{ return -EINVAL; }
-int tsv_byte_array_write(struct encode_context *ectxt,
+static inline int tsv_byte_array_write(struct encode_context *ectxt,
void *data, int data_size)
{ return -EINVAL; }
-void msg_encode_end(struct encode_context *ectxt) { }
+static inline void msg_encode_end(struct encode_context *ectxt) { }
-void ipc_log_write(void *ctxt, struct encode_context *ectxt) { }
+static inline void ipc_log_write(void *ctxt, struct encode_context *ectxt) { }
-int ipc_log_string(void *ilctxt, const char *fmt, ...)
+static inline int ipc_log_string(void *ilctxt, const char *fmt, ...)
{ return -EINVAL; }
#define IPC_SPRINTF_DECODE(dctxt, args...) do { } while (0)
-void tsv_timestamp_read(struct encode_context *ectxt,
+static inline void tsv_timestamp_read(struct encode_context *ectxt,
struct decode_context *dctxt, const char *format) { }
-void tsv_pointer_read(struct encode_context *ectxt,
+static inline void tsv_pointer_read(struct encode_context *ectxt,
struct decode_context *dctxt, const char *format) { }
-int32_t tsv_int32_read(struct encode_context *ectxt,
+static inline int32_t tsv_int32_read(struct encode_context *ectxt,
struct decode_context *dctxt, const char *format)
{ return 0; }
-void tsv_byte_array_read(struct encode_context *ectxt,
+static inline void tsv_byte_array_read(struct encode_context *ectxt,
struct decode_context *dctxt, const char *format) { }
-int add_deserialization_func(void *ctxt, int type,
+static inline int add_deserialization_func(void *ctxt, int type,
void (*dfunc)(struct encode_context *,
struct decode_context *))
{ return 0; }
diff --git a/arch/arm/mach-msm/include/mach/msm_serial_hs.h b/arch/arm/mach-msm/include/mach/msm_serial_hs.h
index d2905d4..cc50955 100644
--- a/arch/arm/mach-msm/include/mach/msm_serial_hs.h
+++ b/arch/arm/mach-msm/include/mach/msm_serial_hs.h
@@ -17,15 +17,34 @@
#include<linux/serial_core.h>
-/* Optional platform device data for msm_serial_hs driver.
- * Used to configure low power wakeup */
+/**
+ * struct msm_serial_hs_platform_data - platform device data
+ * for msm hsuart device
+ * @wakeup_irq : IRQ line to be configured as Wakeup source.
+ * @inject_rx_on_wakeup : Set 1 if specific character to be inserted on wakeup
+ * @rx_to_inject : Character to be inserted on wakeup
+ * @gpio_config : Configure gpios that are used for uart communication
+ * @userid : User-defined number to be used to enumerate device as tty<userid>
+ * @uart_tx_gpio: GPIO number for UART Tx Line.
+ * @uart_rx_gpio: GPIO number for UART Rx Line.
+ * @uart_cts_gpio: GPIO number for UART CTS Line.
+ * @uart_rfr_gpio: GPIO number for UART RFR Line.
+ * @bam_tx_ep_pipe_index : BAM TX Endpoint Pipe Index for HSUART
+ * @bam_tx_ep_pipe_index : BAM RX Endpoint Pipe Index for HSUART
+ */
struct msm_serial_hs_platform_data {
int wakeup_irq; /* wakeup irq */
- /* bool: inject char into rx tty on wakeup */
unsigned char inject_rx_on_wakeup;
char rx_to_inject;
int (*gpio_config)(int);
int userid;
+
+ unsigned uart_tx_gpio;
+ unsigned uart_rx_gpio;
+ unsigned uart_cts_gpio;
+ unsigned uart_rfr_gpio;
+ unsigned bam_tx_ep_pipe_index;
+ unsigned bam_rx_ep_pipe_index;
};
unsigned int msm_hs_tx_empty(struct uart_port *uport);
diff --git a/arch/arm/mach-msm/include/mach/msm_smsm.h b/arch/arm/mach-msm/include/mach/msm_smsm.h
index c77c181..9a031b2 100644
--- a/arch/arm/mach-msm/include/mach/msm_smsm.h
+++ b/arch/arm/mach-msm/include/mach/msm_smsm.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, Code Aurora Forum. 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
@@ -198,7 +198,14 @@
SMEM_SSR_REASON_LPASS0,
SMEM_SSR_REASON_DSPS0,
SMEM_SSR_REASON_VCODEC0,
- SMEM_MEM_LAST = SMEM_SSR_REASON_VCODEC0,
+ SMEM_SMP2P_APPS_BASE = 427,
+ SMEM_SMP2P_MODEM_BASE = SMEM_SMP2P_APPS_BASE + 8, /* 435 */
+ SMEM_SMP2P_AUDIO_BASE = SMEM_SMP2P_MODEM_BASE + 8, /* 443 */
+ SMEM_SMP2P_WIRLESS_BASE = SMEM_SMP2P_AUDIO_BASE + 8, /* 451 */
+ SMEM_SMP2P_POWER_BASE = SMEM_SMP2P_WIRLESS_BASE + 8, /* 459 */
+ SMEM_FLASH_DEVICE_INFO = SMEM_SMP2P_POWER_BASE + 8, /* 467 */
+ SMEM_BAM_PIPE_MEMORY, /* 468 */
+ SMEM_IMAGE_VERSION_TABLE, /* 469 */
SMEM_NUM_ITEMS,
};
diff --git a/arch/arm/mach-msm/include/mach/qdsp6v2/apr_us_b.h b/arch/arm/mach-msm/include/mach/qdsp6v2/apr_us_b.h
new file mode 100644
index 0000000..11de6ef
--- /dev/null
+++ b/arch/arm/mach-msm/include/mach/qdsp6v2/apr_us_b.h
@@ -0,0 +1,70 @@
+/* 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
+ * 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 __APR_US_B_H__
+#define __APR_US_B_H__
+
+#include "apr_us.h"
+
+/* ======================================================================= */
+/* Session Level commands */
+#define USM_CMD_SHARED_MEM_MAP_REGION 0x00012728
+struct usm_cmd_memory_map_region {
+ struct apr_hdr hdr;
+ u16 mempool_id;
+ u16 num_regions;
+ u32 flags;
+ u32 shm_addr_lsw;
+ u32 shm_addr_msw;
+ u32 mem_size_bytes;
+} __packed;
+
+#define USM_CMDRSP_SHARED_MEM_MAP_REGION 0x00012729
+struct usm_cmdrsp_memory_map_region {
+ u32 mem_map_handle;
+} __packed;
+
+#define USM_CMD_SHARED_MEM_UNMAP_REGION 0x0001272A
+struct usm_cmd_memory_unmap_region {
+ struct apr_hdr hdr;
+ u32 mem_map_handle;
+} __packed;
+
+#define USM_DATA_CMD_READ 0x00012724
+struct usm_stream_cmd_read {
+ struct apr_hdr hdr;
+ u32 buf_addr_lsw;
+ u32 buf_addr_msw;
+ u32 mem_map_handle;
+ u32 buf_size;
+ u32 seq_id;
+ u32 counter;
+} __packed;
+
+#define USM_DATA_EVENT_READ_DONE 0x00012725
+
+#define USM_DATA_CMD_WRITE 0x00012726
+struct usm_stream_cmd_write {
+ struct apr_hdr hdr;
+ u32 buf_addr_lsw;
+ u32 buf_addr_msw;
+ u32 mem_map_handle;
+ u32 buf_size;
+ u32 seq_id;
+ u32 res0;
+ u32 res1;
+ u32 res2;
+} __packed;
+
+#define USM_DATA_EVENT_WRITE_DONE 0x00012727
+
+#endif /* __APR_US_B_H__ */
diff --git a/arch/arm/mach-msm/include/mach/qdsp6v2/audio_acdb.h b/arch/arm/mach-msm/include/mach/qdsp6v2/audio_acdb.h
index b830134..31dd582 100644
--- a/arch/arm/mach-msm/include/mach/qdsp6v2/audio_acdb.h
+++ b/arch/arm/mach-msm/include/mach/qdsp6v2/audio_acdb.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2012, Code Aurora Forum. 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
@@ -14,13 +14,8 @@
#define _AUDIO_ACDB_H
#include <linux/msm_audio_acdb.h>
-#if defined(CONFIG_ARCH_MSM8974) || defined(CONFIG_ARCH_MSM9625) \
- || defined(CONFIG_ARCH_MSM8226)
-
-#include <sound/q6adm-v2.h>
-#else
#include <sound/q6adm.h>
-#endif
+
enum {
RX_CAL,
TX_CAL,
@@ -62,14 +57,11 @@
void get_all_vocproc_cal(struct acdb_cal_block *cal_block);
void get_all_vocstrm_cal(struct acdb_cal_block *cal_block);
void get_all_vocvol_cal(struct acdb_cal_block *cal_block);
-void get_voice_col_data(uint32_t vocproc_type,
- struct acdb_cal_block *cal_block);
void get_anc_cal(struct acdb_cal_block *cal_block);
void get_afe_cal(int32_t path, struct acdb_cal_block *cal_block);
void get_audproc_cal(int32_t path, struct acdb_cal_block *cal_block);
void get_audstrm_cal(int32_t path, struct acdb_cal_block *cal_block);
void get_audvol_cal(int32_t path, struct acdb_cal_block *cal_block);
-void get_vocproc_dev_cfg_cal(struct acdb_cal_block *cal_block);
void get_vocproc_cal(struct acdb_cal_data *cal_data);
void get_vocstrm_cal(struct acdb_cal_data *cal_data);
void get_vocvol_cal(struct acdb_cal_data *cal_data);
diff --git a/arch/arm/mach-msm/include/mach/remote_spinlock.h b/arch/arm/mach-msm/include/mach/remote_spinlock.h
index 75b70f3..72e7337 100644
--- a/arch/arm/mach-msm/include/mach/remote_spinlock.h
+++ b/arch/arm/mach-msm/include/mach/remote_spinlock.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009, 2011 Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2009, 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
@@ -39,7 +39,7 @@
typedef raw_remote_spinlock_t *_remote_spinlock_t;
#define remote_spinlock_id_t const char *
-#define SMEM_SPINLOCK_PID_APPS 1
+#define SPINLOCK_PID_APPS 1
static inline void __raw_remote_ex_spin_lock(raw_remote_spinlock_t *lock)
{
@@ -52,7 +52,7 @@
" teqeq %0, #0\n"
" bne 1b"
: "=&r" (tmp)
- : "r" (&lock->lock), "r" (1)
+ : "r" (&lock->lock), "r" (SPINLOCK_PID_APPS)
: "cc");
smp_mb();
@@ -67,7 +67,7 @@
" teq %0, #0\n"
" strexeq %0, %2, [%1]\n"
: "=&r" (tmp)
- : "r" (&lock->lock), "r" (1)
+ : "r" (&lock->lock), "r" (SPINLOCK_PID_APPS)
: "cc");
if (tmp == 0) {
@@ -79,7 +79,14 @@
static inline void __raw_remote_ex_spin_unlock(raw_remote_spinlock_t *lock)
{
+ int lock_owner;
+
smp_mb();
+ lock_owner = readl_relaxed(&lock->lock);
+ if (lock_owner != SPINLOCK_PID_APPS) {
+ pr_err("%s: spinlock not owned by Apps (actual owner is %d)\n",
+ __func__, lock_owner);
+ }
__asm__ __volatile__(
" str %1, [%0]\n"
@@ -122,7 +129,14 @@
static inline void __raw_remote_swp_spin_unlock(raw_remote_spinlock_t *lock)
{
+ int lock_owner;
+
smp_mb();
+ lock_owner = readl_relaxed(&lock->lock);
+ if (lock_owner != SPINLOCK_PID_APPS) {
+ pr_err("%s: spinlock not owned by Apps (actual owner is %d)\n",
+ __func__, lock_owner);
+ }
__asm__ __volatile__(
" str %1, [%0]"
@@ -178,15 +192,20 @@
static inline int __raw_remote_dek_spin_release(raw_remote_spinlock_t *lock,
uint32_t pid)
{
- return -EINVAL;
+ return -EPERM;
+}
+
+static inline int __raw_remote_dek_spin_owner(raw_remote_spinlock_t *lock)
+{
+ return -EPERM;
}
static inline void __raw_remote_sfpb_spin_lock(raw_remote_spinlock_t *lock)
{
do {
- writel_relaxed(SMEM_SPINLOCK_PID_APPS, lock);
+ writel_relaxed(SPINLOCK_PID_APPS, lock);
smp_mb();
- } while (readl_relaxed(lock) != SMEM_SPINLOCK_PID_APPS);
+ } while (readl_relaxed(lock) != SPINLOCK_PID_APPS);
}
static inline int __raw_remote_sfpb_spin_trylock(raw_remote_spinlock_t *lock)
@@ -196,6 +215,14 @@
static inline void __raw_remote_sfpb_spin_unlock(raw_remote_spinlock_t *lock)
{
+ int lock_owner;
+
+ lock_owner = readl_relaxed(lock);
+ if (lock_owner != SPINLOCK_PID_APPS) {
+ pr_err("%s: spinlock not owned by Apps (actual owner is %d)\n",
+ __func__, lock_owner);
+ }
+
writel_relaxed(0, lock);
smp_mb();
}
@@ -206,8 +233,8 @@
* This is only to be used for situations where the processor owning
* the spinlock has crashed and the spinlock must be released.
*
- * @lock - lock structure
- * @pid - processor ID of processor to release
+ * @lock: lock structure
+ * @pid: processor ID of processor to release
*/
static inline int __raw_remote_gen_spin_release(raw_remote_spinlock_t *lock,
uint32_t pid)
@@ -222,6 +249,20 @@
return ret;
}
+/**
+ * Return owner of the spinlock.
+ *
+ * @lock: pointer to lock structure
+ * @returns: >= 0 owned PID; < 0 for error case
+ *
+ * Used for testing. PID's are assumed to be 31 bits or less.
+ */
+static inline int __raw_remote_gen_spin_owner(raw_remote_spinlock_t *lock)
+{
+ rmb();
+ return readl_relaxed(&lock->lock);
+}
+
#if defined(CONFIG_MSM_SMD) || defined(CONFIG_MSM_REMOTE_SPINLOCK_SFPB)
int _remote_spin_lock_init(remote_spinlock_id_t, _remote_spinlock_t *lock);
void _remote_spin_release_all(uint32_t pid);
@@ -242,6 +283,7 @@
#define _remote_spin_trylock(lock) __raw_remote_dek_spin_trylock(*lock)
#define _remote_spin_release(lock, pid) __raw_remote_dek_spin_release(*lock,\
pid)
+#define _remote_spin_owner(lock) __raw_remote_dek_spin_owner(*lock)
#elif defined(CONFIG_MSM_REMOTE_SPINLOCK_SWP)
/* Use SWP-based locks when LDREX/STREX are unavailable for shared memory. */
#define _remote_spin_lock(lock) __raw_remote_swp_spin_lock(*lock)
@@ -249,6 +291,7 @@
#define _remote_spin_trylock(lock) __raw_remote_swp_spin_trylock(*lock)
#define _remote_spin_release(lock, pid) __raw_remote_gen_spin_release(*lock,\
pid)
+#define _remote_spin_owner(lock) __raw_remote_gen_spin_owner(*lock)
#elif defined(CONFIG_MSM_REMOTE_SPINLOCK_SFPB)
/* Use SFPB Hardware Mutex Registers */
#define _remote_spin_lock(lock) __raw_remote_sfpb_spin_lock(*lock)
@@ -256,6 +299,7 @@
#define _remote_spin_trylock(lock) __raw_remote_sfpb_spin_trylock(*lock)
#define _remote_spin_release(lock, pid) __raw_remote_gen_spin_release(*lock,\
pid)
+#define _remote_spin_owner(lock) __raw_remote_gen_spin_owner(*lock)
#else
/* Use LDREX/STREX for shared memory locking, when available */
#define _remote_spin_lock(lock) __raw_remote_ex_spin_lock(*lock)
@@ -263,6 +307,7 @@
#define _remote_spin_trylock(lock) __raw_remote_ex_spin_trylock(*lock)
#define _remote_spin_release(lock, pid) __raw_remote_gen_spin_release(*lock, \
pid)
+#define _remote_spin_owner(lock) __raw_remote_gen_spin_owner(*lock)
#endif
/* Remote mutex definitions. */
diff --git a/arch/arm/mach-msm/include/mach/socinfo.h b/arch/arm/mach-msm/include/mach/socinfo.h
index c0624bb..5b4f00e 100644
--- a/arch/arm/mach-msm/include/mach/socinfo.h
+++ b/arch/arm/mach-msm/include/mach/socinfo.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2009-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
@@ -56,14 +56,14 @@
of_machine_is_compatible("qcom,msm8226-sim")
#define machine_is_msm8226_rumi() \
of_machine_is_compatible("qcom,msm8226-rumi")
-#define early_machine_is_msm8910() \
- of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,msm8910")
-#define machine_is_msm8910() \
- of_machine_is_compatible("qcom,msm8910")
-#define machine_is_msm8910_sim() \
- of_machine_is_compatible("qcom,msm8910-sim")
-#define machine_is_msm8910_rumi() \
- of_machine_is_compatible("qcom,msm8910-rumi")
+#define early_machine_is_msm8610() \
+ of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,msm8610")
+#define machine_is_msm8610() \
+ of_machine_is_compatible("qcom,msm8610")
+#define machine_is_msm8610_sim() \
+ of_machine_is_compatible("qcom,msm8610-sim")
+#define machine_is_msm8610_rumi() \
+ of_machine_is_compatible("qcom,msm8610-rumi")
#else
#define early_machine_is_msm8974() 0
#define machine_is_msm8974() 0
@@ -77,10 +77,10 @@
#define machine_is_msm8226() 0
#define machine_is_msm8226_sim() 0
#define machine_is_msm8226_rumi() 0
-#define early_machine_is_msm8910() 0
-#define machine_is_msm8910() 0
-#define machine_is_msm8910_sim() 0
-#define machine_is_msm8910_rumi() 0
+#define early_machine_is_msm8610() 0
+#define machine_is_msm8610() 0
+#define machine_is_msm8610_sim() 0
+#define machine_is_msm8610_rumi() 0
#endif
@@ -105,6 +105,7 @@
MSM_CPU_7X25AB,
MSM_CPU_8064,
MSM_CPU_8064AB,
+ MSM_CPU_8064AA,
MSM_CPU_8930,
MSM_CPU_8930AA,
MSM_CPU_8930AB,
@@ -116,7 +117,7 @@
MSM_CPU_9625,
MSM_CPU_8092,
MSM_CPU_8226,
- MSM_CPU_8910,
+ MSM_CPU_8610,
MSM_CPU_8625Q,
};
@@ -332,6 +333,15 @@
#endif
}
+static inline int cpu_is_apq8064aa(void)
+{
+#ifdef CONFIG_ARCH_APQ8064
+ return read_msm_cpu_type() == MSM_CPU_8064AA;
+#else
+ return 0;
+#endif
+}
+
static inline int cpu_is_msm8930(void)
{
#ifdef CONFIG_ARCH_MSM8930
@@ -442,13 +452,13 @@
#endif
}
-static inline int cpu_is_msm8910(void)
+static inline int cpu_is_msm8610(void)
{
-#ifdef CONFIG_ARCH_MSM8910
+#ifdef CONFIG_ARCH_MSM8610
enum msm_cpu cpu = socinfo_get_msm_cpu();
BUG_ON(cpu == MSM_CPU_UNKNOWN);
- return cpu == MSM_CPU_8910;
+ return cpu == MSM_CPU_8610;
#else
return 0;
#endif
@@ -473,7 +483,7 @@
static inline int soc_class_is_apq8064(void)
{
- return cpu_is_apq8064() || cpu_is_apq8064ab();
+ return cpu_is_apq8064() || cpu_is_apq8064ab() || cpu_is_apq8064aa();
}
static inline int soc_class_is_msm8930(void)
diff --git a/arch/arm/mach-msm/io.c b/arch/arm/mach-msm/io.c
index cd70ae9..013b4b2 100644
--- a/arch/arm/mach-msm/io.c
+++ b/arch/arm/mach-msm/io.c
@@ -3,7 +3,7 @@
* MSM7K, QSD io support
*
* Copyright (C) 2007 Google, Inc.
- * Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2008-2013, The Linux Foundation. All rights reserved.
* Author: Brian Swetland <swetland@google.com>
*
* This software is licensed under the terms of the GNU General Public
@@ -538,12 +538,12 @@
}
#endif /* CONFIG_ARCH_MSM8226 */
-#ifdef CONFIG_ARCH_MSM8910
-static struct map_desc msm8910_io_desc[] __initdata = {
- MSM_CHIP_DEVICE(APCS_GCC, MSM8910),
- MSM_CHIP_DEVICE(TLMM, MSM8910),
- MSM_CHIP_DEVICE(MPM2_PSHOLD, MSM8910),
- MSM_CHIP_DEVICE(IMEM, MSM8910),
+#ifdef CONFIG_ARCH_MSM8610
+static struct map_desc msm8610_io_desc[] __initdata = {
+ MSM_CHIP_DEVICE(APCS_GCC, MSM8610),
+ MSM_CHIP_DEVICE(TLMM, MSM8610),
+ MSM_CHIP_DEVICE(MPM2_PSHOLD, MSM8610),
+ MSM_CHIP_DEVICE(IMEM, MSM8610),
{
.virtual = (unsigned long) MSM_SHARED_RAM_BASE,
.length = MSM_SHARED_RAM_SIZE,
@@ -551,9 +551,9 @@
},
};
-void __init msm_map_msm8910_io(void)
+void __init msm_map_msm8610_io(void)
{
- msm_shared_ram_phys = MSM8910_MSM_SHARED_RAM_PHYS;
- msm_map_io(msm8910_io_desc, ARRAY_SIZE(msm8910_io_desc));
+ msm_shared_ram_phys = MSM8610_MSM_SHARED_RAM_PHYS;
+ msm_map_io(msm8610_io_desc, ARRAY_SIZE(msm8610_io_desc));
}
-#endif /* CONFIG_ARCH_MSM8910 */
+#endif /* CONFIG_ARCH_MSM8610 */
diff --git a/arch/arm/mach-msm/ipc_router.c b/arch/arm/mach-msm/ipc_router.c
index fde43b0..cdacd87 100644
--- a/arch/arm/mach-msm/ipc_router.c
+++ b/arch/arm/mach-msm/ipc_router.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, Code Aurora Forum. 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
@@ -2288,13 +2288,19 @@
RR("x REMOVE_SERVER Name=%d:%08x Id=%d:%08x\n",
msg.srv.service, msg.srv.instance,
msg.srv.node_id, msg.srv.port_id);
- } else if (port_ptr->type == CLIENT_PORT) {
- msg.cmd = IPC_ROUTER_CTRL_CMD_REMOVE_CLIENT;
- msg.cli.node_id = port_ptr->this_port.node_id;
- msg.cli.port_id = port_ptr->this_port.port_id;
- RR("x REMOVE_CLIENT id=%d:%08x\n",
- msg.cli.node_id, msg.cli.port_id);
+ broadcast_ctl_msg(&msg);
+ broadcast_ctl_msg_locally(&msg);
}
+
+ /*
+ * Server port could have been a client port earlier.
+ * Send REMOVE_CLIENT message in either case.
+ */
+ msg.cmd = IPC_ROUTER_CTRL_CMD_REMOVE_CLIENT;
+ msg.cli.node_id = port_ptr->this_port.node_id;
+ msg.cli.port_id = port_ptr->this_port.port_id;
+ RR("x REMOVE_CLIENT id=%d:%08x\n",
+ msg.cli.node_id, msg.cli.port_id);
broadcast_ctl_msg(&msg);
broadcast_ctl_msg_locally(&msg);
} else if (port_ptr->type == CONTROL_PORT) {
diff --git a/arch/arm/mach-msm/ipc_router_smd_xprt.c b/arch/arm/mach-msm/ipc_router_smd_xprt.c
index 8c0bf4b..5d1b5e4 100644
--- a/arch/arm/mach-msm/ipc_router_smd_xprt.c
+++ b/arch/arm/mach-msm/ipc_router_smd_xprt.c
@@ -40,7 +40,7 @@
#define MIN_FRAG_SZ (IPC_ROUTER_HDR_SIZE + sizeof(union rr_control_msg))
-#define NUM_SMD_XPRTS 3
+#define NUM_SMD_XPRTS 4
#define XPRT_NAME_LEN (SMD_MAX_CH_NAME_LEN + 12)
struct msm_ipc_router_smd_xprt {
@@ -76,6 +76,7 @@
{"RPCRPY_CNTL", "ipc_rtr_smd_rpcrpy_cntl", SMD_APPS_MODEM, 1},
{"IPCRTR", "ipc_rtr_smd_ipcrtr", SMD_APPS_MODEM, 1},
{"IPCRTR", "ipc_rtr_q6_ipcrtr", SMD_APPS_QDSP, 1},
+ {"IPCRTR", "ipc_rtr_wcnss_ipcrtr", SMD_APPS_WCNSS, 1},
};
static struct msm_ipc_router_smd_xprt smd_remote_xprt[NUM_SMD_XPRTS];
diff --git a/arch/arm/mach-msm/jtag-mm.c b/arch/arm/mach-msm/jtag-mm.c
new file mode 100644
index 0000000..af05995
--- /dev/null
+++ b/arch/arm/mach-msm/jtag-mm.c
@@ -0,0 +1,747 @@
+/* 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/kernel.h>
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/smp.h>
+#include <linux/export.h>
+#include <linux/printk.h>
+#include <linux/ratelimit.h>
+#include <linux/coresight.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/bitops.h>
+#include <mach/scm.h>
+#include <mach/jtag.h>
+
+/* Coresight management registers */
+#define CORESIGHT_ITCTRL (0xF00)
+#define CORESIGHT_CLAIMSET (0xFA0)
+#define CORESIGHT_CLAIMCLR (0xFA4)
+#define CORESIGHT_LAR (0xFB0)
+#define CORESIGHT_LSR (0xFB4)
+#define CORESIGHT_AUTHSTATUS (0xFB8)
+#define CORESIGHT_DEVID (0xFC8)
+#define CORESIGHT_DEVTYPE (0xFCC)
+
+#define CORESIGHT_UNLOCK (0xC5ACCE55)
+
+#define TIMEOUT_US (100)
+
+#define BM(lsb, msb) ((BIT(msb) - BIT(lsb)) + BIT(msb))
+#define BMVAL(val, lsb, msb) ((val & BM(lsb, msb)) >> lsb)
+#define BVAL(val, n) ((val & BIT(n)) >> n)
+
+/* Trace registers */
+#define ETMCR (0x000)
+#define ETMCCR (0x004)
+#define ETMTRIGGER (0x008)
+#define ETMASICCTLR (0x00C)
+#define ETMSR (0x010)
+#define ETMSCR (0x014)
+#define ETMTSSCR (0x018)
+#define ETMTECR2 (0x01C)
+#define ETMTEEVR (0x020)
+#define ETMTECR1 (0x024)
+#define ETMFFLR (0x02C)
+#define ETMVDEVR (0x030)
+#define ETMVDCR1 (0x034)
+#define ETMVDCR3 (0x03C)
+#define ETMACVRn(n) (0x040 + (n * 4))
+#define ETMACTRn(n) (0x080 + (n * 4))
+#define ETMDCVRn(n) (0x0C0 + (n * 8))
+#define ETMDCMRn(n) (0x100 + (n * 8))
+#define ETMCNTRLDVRn(n) (0x140 + (n * 4))
+#define ETMCNTENRn(n) (0x150 + (n * 4))
+#define ETMCNTRLDEVRn(n) (0x160 + (n * 4))
+#define ETMCNTVRn(n) (0x170 + (n * 4))
+#define ETMSQ12EVR (0x180)
+#define ETMSQ21EVR (0x184)
+#define ETMSQ23EVR (0x188)
+#define ETMSQ31EVR (0x18C)
+#define ETMSQ32EVR (0x190)
+#define ETMSQ13EVR (0x194)
+#define ETMSQR (0x19C)
+#define ETMEXTOUTEVRn(n) (0x1A0 + (n * 4))
+#define ETMCIDCVRn(n) (0x1B0 + (n * 4))
+#define ETMCIDCMR (0x1BC)
+#define ETMIMPSPEC0 (0x1C0)
+#define ETMIMPSPEC1 (0x1C4)
+#define ETMIMPSPEC2 (0x1C8)
+#define ETMIMPSPEC3 (0x1CC)
+#define ETMIMPSPEC4 (0x1D0)
+#define ETMIMPSPEC5 (0x1D4)
+#define ETMIMPSPEC6 (0x1D8)
+#define ETMIMPSPEC7 (0x1DC)
+#define ETMSYNCFR (0x1E0)
+#define ETMIDR (0x1E4)
+#define ETMCCER (0x1E8)
+#define ETMEXTINSELR (0x1EC)
+#define ETMTESSEICR (0x1F0)
+#define ETMEIBCR (0x1F4)
+#define ETMTSEVR (0x1F8)
+#define ETMAUXCR (0x1FC)
+#define ETMTRACEIDR (0x200)
+#define ETMIDR2 (0x208)
+#define ETMVMIDCVR (0x240)
+#define ETMCLAIMSET (0xFA0)
+#define ETMCLAIMCLR (0xFA4)
+/* ETM Management registers */
+#define ETMOSLAR (0x300)
+#define ETMOSLSR (0x304)
+#define ETMOSSRR (0x308)
+#define ETMPDCR (0x310)
+#define ETMPDSR (0x314)
+
+#define ETM_MAX_ADDR_CMP (16)
+#define ETM_MAX_CNTR (4)
+#define ETM_MAX_CTXID_CMP (3)
+
+/* DBG Registers */
+#define DBGDIDR (0x0)
+#define DBGWFAR (0x18)
+#define DBGVCR (0x1C)
+#define DBGDTRRXext (0x80)
+#define DBGDSCRext (0x88)
+#define DBGDTRTXext (0x8C)
+#define DBGDRCR (0x90)
+#define DBGBVRn(n) (0x100 + (n * 4))
+#define DBGBCRn(n) (0x140 + (n * 4))
+#define DBGWVRn(n) (0x180 + (n * 4))
+#define DBGWCRn(n) (0x1C0 + (n * 4))
+#define DBGPRCR (0x310)
+#define DBGITMISCOUT (0xEF8)
+#define DBGITMISCIN (0xEFC)
+#define DBGCLAIMSET (0xFA0)
+#define DBGCLAIMCLR (0xFA4)
+
+#define DBGDSCR_MASK (0x6C30FC3C)
+
+#define MAX_DBG_STATE_SIZE (90)
+#define MAX_ETM_STATE_SIZE (78)
+
+#define TZ_DBG_ETM_FEAT_ID (0x8)
+#define TZ_DBG_ETM_VER (0x400000)
+
+#define ARCH_V3_5 (0x25)
+#define ARM_DEBUG_ARCH_V7B (0x3)
+
+#define etm_write(etm, val, off) \
+ __raw_writel(val, etm->base + off)
+#define etm_read(etm, off) \
+ __raw_readl(etm->base + off)
+
+#define dbg_write(dbg, val, off) \
+ __raw_writel(val, dbg->base + off)
+#define dbg_read(dbg, off) \
+ __raw_readl(dbg->base + off)
+
+#define ETM_LOCK(base) \
+do { \
+ /* recommended by spec to ensure ETM writes are committed prior
+ * to resuming execution
+ */ \
+ mb(); \
+ etm_write(base, 0x0, CORESIGHT_LAR); \
+} while (0)
+
+#define ETM_UNLOCK(base) \
+do { \
+ etm_write(base, CORESIGHT_UNLOCK, CORESIGHT_LAR); \
+ /* ensure unlock and any pending writes are committed prior to
+ * programming ETM registers
+ */ \
+ mb(); \
+} while (0)
+
+#define DBG_LOCK(base) \
+do { \
+ /* recommended by spec to ensure ETM writes are committed prior
+ * to resuming execution
+ */ \
+ mb(); \
+ dbg_write(base, 0x0, CORESIGHT_LAR); \
+} while (0)
+
+#define DBG_UNLOCK(base) \
+do { \
+ dbg_write(base, CORESIGHT_UNLOCK, CORESIGHT_LAR); \
+ /* ensure unlock and any pending writes are committed prior to
+ * programming ETM registers
+ */ \
+ mb(); \
+} while (0)
+
+uint32_t msm_jtag_save_cntr[NR_CPUS];
+uint32_t msm_jtag_restore_cntr[NR_CPUS];
+
+struct dbg_cpu_ctx {
+ void __iomem *base;
+ uint32_t *state;
+};
+
+struct dbg_ctx {
+ uint8_t arch;
+ uint8_t nr_wp;
+ uint8_t nr_bp;
+ uint8_t nr_ctx_cmp;
+ struct dbg_cpu_ctx *cpu_ctx[NR_CPUS];
+ bool save_restore_enabled[NR_CPUS];
+};
+static struct dbg_ctx dbg;
+
+struct etm_cpu_ctx {
+ void __iomem *base;
+ struct device *dev;
+ uint32_t *state;
+};
+
+struct etm_ctx {
+ uint8_t arch;
+ uint8_t nr_addr_cmp;
+ uint8_t nr_data_cmp;
+ uint8_t nr_cntr;
+ uint8_t nr_ext_inp;
+ uint8_t nr_ext_out;
+ uint8_t nr_ctxid_cmp;
+ struct etm_cpu_ctx *cpu_ctx[NR_CPUS];
+ bool save_restore_enabled[NR_CPUS];
+};
+
+static struct etm_ctx etm;
+
+static struct clk *clock[NR_CPUS];
+
+static void etm_set_pwrdwn(struct etm_cpu_ctx *etmdata)
+{
+ uint32_t etmcr;
+
+ /* ensure all writes are complete before setting pwrdwn */
+ mb();
+ etmcr = etm_read(etmdata, ETMCR);
+ etmcr |= BIT(0);
+ etm_write(etmdata, etmcr, ETMCR);
+}
+
+static void etm_clr_pwrdwn(struct etm_cpu_ctx *etmdata)
+{
+ uint32_t etmcr;
+
+ etmcr = etm_read(etmdata, ETMCR);
+ etmcr &= ~BIT(0);
+ etm_write(etmdata, etmcr, ETMCR);
+ /* ensure pwrup completes before subsequent register accesses */
+ mb();
+}
+
+static void etm_set_prog(struct etm_cpu_ctx *etmdata)
+{
+ uint32_t etmcr;
+ int count;
+
+ etmcr = etm_read(etmdata, ETMCR);
+ etmcr |= BIT(10);
+ etm_write(etmdata, etmcr, ETMCR);
+ for (count = TIMEOUT_US; BVAL(etm_read(etmdata, ETMSR), 1) != 1
+ && count > 0; count--)
+ udelay(1);
+ WARN(count == 0, "timeout while setting prog bit, ETMSR: %#x\n",
+ etm_read(etmdata, ETMSR));
+}
+
+static inline void etm_save_state(struct etm_cpu_ctx *etmdata)
+{
+ int i, j;
+
+ i = 0;
+ ETM_UNLOCK(etmdata);
+
+ switch (etm.arch) {
+ case ETM_ARCH_V3_5:
+ etmdata->state[i++] = etm_read(etmdata, ETMTRIGGER);
+ etmdata->state[i++] = etm_read(etmdata, ETMASICCTLR);
+ etmdata->state[i++] = etm_read(etmdata, ETMSR);
+ etmdata->state[i++] = etm_read(etmdata, ETMTSSCR);
+ etmdata->state[i++] = etm_read(etmdata, ETMTECR2);
+ etmdata->state[i++] = etm_read(etmdata, ETMTEEVR);
+ etmdata->state[i++] = etm_read(etmdata, ETMTECR1);
+ etmdata->state[i++] = etm_read(etmdata, ETMFFLR);
+ etmdata->state[i++] = etm_read(etmdata, ETMVDEVR);
+ etmdata->state[i++] = etm_read(etmdata, ETMVDCR1);
+ etmdata->state[i++] = etm_read(etmdata, ETMVDCR3);
+ for (j = 0; j < etm.nr_addr_cmp; j++) {
+ etmdata->state[i++] = etm_read(etmdata,
+ ETMACVRn(j));
+ etmdata->state[i++] = etm_read(etmdata,
+ ETMACTRn(j));
+ }
+ for (j = 0; j < etm.nr_data_cmp; j++) {
+ etmdata->state[i++] = etm_read(etmdata,
+ ETMDCVRn(j));
+ etmdata->state[i++] = etm_read(etmdata,
+ ETMDCMRn(j));
+ }
+ for (j = 0; j < etm.nr_cntr; j++) {
+ etmdata->state[i++] = etm_read(etmdata,
+ ETMCNTRLDVRn(j));
+ etmdata->state[i++] = etm_read(etmdata,
+ ETMCNTENRn(j));
+ etmdata->state[i++] = etm_read(etmdata,
+ ETMCNTRLDEVRn(j));
+ etmdata->state[i++] = etm_read(etmdata,
+ ETMCNTVRn(j));
+ }
+ etmdata->state[i++] = etm_read(etmdata, ETMSQ12EVR);
+ etmdata->state[i++] = etm_read(etmdata, ETMSQ21EVR);
+ etmdata->state[i++] = etm_read(etmdata, ETMSQ23EVR);
+ etmdata->state[i++] = etm_read(etmdata, ETMSQ31EVR);
+ etmdata->state[i++] = etm_read(etmdata, ETMSQ32EVR);
+ etmdata->state[i++] = etm_read(etmdata, ETMSQ13EVR);
+ etmdata->state[i++] = etm_read(etmdata, ETMSQR);
+ for (j = 0; j < etm.nr_ext_out; j++)
+ etmdata->state[i++] = etm_read(etmdata,
+ ETMEXTOUTEVRn(j));
+ for (j = 0; j < etm.nr_ctxid_cmp; j++)
+ etmdata->state[i++] = etm_read(etmdata,
+ ETMCIDCVRn(j));
+ etmdata->state[i++] = etm_read(etmdata, ETMCIDCMR);
+ etmdata->state[i++] = etm_read(etmdata, ETMSYNCFR);
+ etmdata->state[i++] = etm_read(etmdata, ETMEXTINSELR);
+ etmdata->state[i++] = etm_read(etmdata, ETMTSEVR);
+ etmdata->state[i++] = etm_read(etmdata, ETMAUXCR);
+ etmdata->state[i++] = etm_read(etmdata, ETMTRACEIDR);
+ etmdata->state[i++] = etm_read(etmdata, ETMVMIDCVR);
+ etmdata->state[i++] = etm_read(etmdata, ETMCLAIMCLR);
+ etmdata->state[i++] = etm_read(etmdata, ETMCR);
+ break;
+ default:
+ pr_err_ratelimited("unsupported etm arch %d in %s\n", etm.arch,
+ __func__);
+ }
+
+ ETM_LOCK(etmdata);
+}
+
+static inline void etm_restore_state(struct etm_cpu_ctx *etmdata)
+{
+ int i, j;
+
+ i = 0;
+ ETM_UNLOCK(etmdata);
+
+ switch (etm.arch) {
+ case ETM_ARCH_V3_5:
+ etm_clr_pwrdwn(etmdata);
+ etm_write(etmdata, etmdata->state[i++], ETMTRIGGER);
+ etm_write(etmdata, etmdata->state[i++], ETMASICCTLR);
+ etm_write(etmdata, etmdata->state[i++], ETMSR);
+ etm_write(etmdata, etmdata->state[i++], ETMTSSCR);
+ etm_write(etmdata, etmdata->state[i++], ETMTECR2);
+ etm_write(etmdata, etmdata->state[i++], ETMTEEVR);
+ etm_write(etmdata, etmdata->state[i++], ETMTECR1);
+ etm_write(etmdata, etmdata->state[i++], ETMFFLR);
+ etm_write(etmdata, etmdata->state[i++], ETMVDEVR);
+ etm_write(etmdata, etmdata->state[i++], ETMVDCR1);
+ etm_write(etmdata, etmdata->state[i++], ETMVDCR3);
+ for (j = 0; j < etm.nr_addr_cmp; j++) {
+ etm_write(etmdata, etmdata->state[i++],
+ ETMACVRn(j));
+ etm_write(etmdata, etmdata->state[i++],
+ ETMACTRn(j));
+ }
+ for (j = 0; j < etm.nr_data_cmp; j++) {
+ etm_write(etmdata, etmdata->state[i++],
+ ETMDCVRn(j));
+ etm_write(etmdata, etmdata->state[i++],
+ ETMDCMRn(j));
+ }
+ for (j = 0; j < etm.nr_cntr; j++) {
+ etm_write(etmdata, etmdata->state[i++],
+ ETMCNTRLDVRn(j));
+ etm_write(etmdata, etmdata->state[i++],
+ ETMCNTENRn(j));
+ etm_write(etmdata, etmdata->state[i++],
+ ETMCNTRLDEVRn(j));
+ etm_write(etmdata, etmdata->state[i++],
+ ETMCNTVRn(j));
+ }
+ etm_write(etmdata, etmdata->state[i++], ETMSQ12EVR);
+ etm_write(etmdata, etmdata->state[i++], ETMSQ21EVR);
+ etm_write(etmdata, etmdata->state[i++], ETMSQ23EVR);
+ etm_write(etmdata, etmdata->state[i++], ETMSQ31EVR);
+ etm_write(etmdata, etmdata->state[i++], ETMSQ32EVR);
+ etm_write(etmdata, etmdata->state[i++], ETMSQ13EVR);
+ etm_write(etmdata, etmdata->state[i++], ETMSQR);
+ for (j = 0; j < etm.nr_ext_out; j++)
+ etm_write(etmdata, etmdata->state[i++],
+ ETMEXTOUTEVRn(j));
+ for (j = 0; j < etm.nr_ctxid_cmp; j++)
+ etm_write(etmdata, etmdata->state[i++],
+ ETMCIDCVRn(j));
+ etm_write(etmdata, etmdata->state[i++], ETMCIDCMR);
+ etm_write(etmdata, etmdata->state[i++], ETMSYNCFR);
+ etm_write(etmdata, etmdata->state[i++], ETMEXTINSELR);
+ etm_write(etmdata, etmdata->state[i++], ETMTSEVR);
+ etm_write(etmdata, etmdata->state[i++], ETMAUXCR);
+ etm_write(etmdata, etmdata->state[i++], ETMTRACEIDR);
+ etm_write(etmdata, etmdata->state[i++], ETMVMIDCVR);
+ etm_write(etmdata, etmdata->state[i++], ETMCLAIMSET);
+ /*
+ * Set ETMCR at last as we dont know the saved status of pwrdwn
+ * bit
+ */
+ etm_write(etmdata, etmdata->state[i++], ETMCR);
+ break;
+ default:
+ pr_err_ratelimited("unsupported etm arch %d in %s\n", etm.arch,
+ __func__);
+ }
+
+ ETM_LOCK(etmdata);
+}
+
+static inline void dbg_save_state(struct dbg_cpu_ctx *dbgdata)
+{
+ int i, j;
+
+ 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));
+ }
+ 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);
+}
+
+static inline void dbg_restore_state(struct dbg_cpu_ctx *dbgdata)
+{
+ int i, j;
+
+ 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,
+ DBGDSCRext);
+
+ DBG_LOCK(dbgdata);
+}
+
+void msm_jtag_save_state(void)
+{
+ int cpu;
+
+ cpu = raw_smp_processor_id();
+
+ msm_jtag_save_cntr[cpu]++;
+ /* ensure counter is updated before moving forward */
+ mb();
+
+ if (dbg.save_restore_enabled[cpu])
+ dbg_save_state(dbg.cpu_ctx[cpu]);
+ if (etm.save_restore_enabled[cpu])
+ etm_save_state(etm.cpu_ctx[cpu]);
+}
+EXPORT_SYMBOL(msm_jtag_save_state);
+
+void msm_jtag_restore_state(void)
+{
+ int cpu;
+
+ cpu = raw_smp_processor_id();
+
+ /* Attempt restore only if save has been done. If power collapse
+ * is disabled, hotplug off of non-boot core will result in WFI
+ * and hence msm_jtag_save_state will not occur. Subsequently,
+ * during hotplug on of non-boot core when msm_jtag_restore_state
+ * is called via msm_platform_secondary_init, this check will help
+ * bail us out without restoring.
+ */
+ if (msm_jtag_save_cntr[cpu] == msm_jtag_restore_cntr[cpu])
+ return;
+ else if (msm_jtag_save_cntr[cpu] != msm_jtag_restore_cntr[cpu] + 1)
+ pr_err_ratelimited("jtag imbalance, save:%lu, restore:%lu\n",
+ (unsigned long)msm_jtag_save_cntr[cpu],
+ (unsigned long)msm_jtag_restore_cntr[cpu]);
+
+ msm_jtag_restore_cntr[cpu]++;
+ /* ensure counter is updated before moving forward */
+ mb();
+
+ if (dbg.save_restore_enabled[cpu])
+ dbg_restore_state(dbg.cpu_ctx[cpu]);
+ if (etm.save_restore_enabled[cpu])
+ etm_restore_state(etm.cpu_ctx[cpu]);
+}
+EXPORT_SYMBOL(msm_jtag_restore_state);
+
+static inline bool etm_arch_supported(uint8_t arch)
+{
+ switch (arch) {
+ case ETM_ARCH_V3_5:
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+
+static void __devinit etm_init_arch_data(void *info)
+{
+ uint32_t etmidr;
+ uint32_t etmccr;
+ struct etm_cpu_ctx *etmdata = info;
+
+ /*
+ * Clear power down bit since when this bit is set writes to
+ * certain registers might be ignored.
+ */
+ ETM_UNLOCK(etmdata);
+
+ etm_clr_pwrdwn(etmdata);
+ /* Set prog bit. It will be set from reset but this is included to
+ * ensure it is set
+ */
+ etm_set_prog(etmdata);
+
+ /* find all capabilities */
+ etmidr = etm_read(etmdata, ETMIDR);
+ etm.arch = BMVAL(etmidr, 4, 11);
+
+ etmccr = etm_read(etmdata, ETMCCR);
+ etm.nr_addr_cmp = BMVAL(etmccr, 0, 3) * 2;
+ etm.nr_data_cmp = BMVAL(etmccr, 4, 7);
+ etm.nr_cntr = BMVAL(etmccr, 13, 15);
+ etm.nr_ext_inp = BMVAL(etmccr, 17, 19);
+ etm.nr_ext_out = BMVAL(etmccr, 20, 22);
+ etm.nr_ctxid_cmp = BMVAL(etmccr, 24, 25);
+
+ etm_set_pwrdwn(etmdata);
+
+ ETM_LOCK(etmdata);
+}
+
+static int __devinit jtag_mm_etm_probe(struct platform_device *pdev,
+ uint32_t cpu)
+{
+ struct etm_cpu_ctx *etmdata;
+ struct resource *res;
+ struct device *dev = &pdev->dev;
+
+ /* Allocate memory per cpu */
+ etmdata = devm_kzalloc(dev, sizeof(struct etm_cpu_ctx), GFP_KERNEL);
+ if (!etmdata)
+ return -ENOMEM;
+
+ etm.cpu_ctx[cpu] = etmdata;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ etmdata->base = devm_ioremap(dev, res->start, resource_size(res));
+ if (!etmdata->base)
+ return -EINVAL;
+
+ /* Allocate etm state save space per core */
+ etmdata->state = devm_kzalloc(dev,
+ (MAX_ETM_STATE_SIZE * sizeof(uint32_t)), GFP_KERNEL);
+ if (!etmdata->state)
+ return -ENOMEM;
+
+ smp_call_function_single(0, etm_init_arch_data, etmdata, 1);
+
+ 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;
+ else
+ pr_info("etm save-restore supported by TZ\n");
+ } else
+ pr_info("etm arch %u not supported\n", etm.arch);
+ return 0;
+}
+
+static inline bool dbg_arch_supported(uint8_t arch)
+{
+ switch (arch) {
+ case ARM_DEBUG_ARCH_V7B:
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+
+static void __devinit dbg_init_arch_data(void *info)
+{
+ uint32_t dbgdidr;
+ struct dbg_cpu_ctx *dbgdata = info;
+
+ /* This will run on core0 so use it to populate parameters */
+ dbgdidr = dbg_read(dbgdata, DBGDIDR);
+ dbg.arch = BMVAL(dbgdidr, 16, 19);
+ dbg.nr_ctx_cmp = BMVAL(dbgdidr, 20, 23) + 1;
+ dbg.nr_bp = BMVAL(dbgdidr, 24, 27) + 1;
+ dbg.nr_wp = BMVAL(dbgdidr, 28, 31) + 1;
+}
+
+
+
+static int __devinit jtag_mm_dbg_probe(struct platform_device *pdev,
+ uint32_t cpu)
+{
+ struct dbg_cpu_ctx *dbgdata;
+ struct resource *res;
+ struct device *dev = &pdev->dev;
+
+ /* Allocate memory per cpu */
+ dbgdata = devm_kzalloc(dev, sizeof(struct dbg_cpu_ctx), GFP_KERNEL);
+ if (!dbgdata)
+ return -ENOMEM;
+
+ dbg.cpu_ctx[cpu] = dbgdata;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+
+ dbgdata->base = devm_ioremap(dev, res->start, resource_size(res));
+ if (!dbgdata->base)
+ return -EINVAL;
+
+ /* Allocate etm state save space per core */
+ dbgdata->state = devm_kzalloc(dev,
+ (MAX_DBG_STATE_SIZE * sizeof(uint32_t)), GFP_KERNEL);
+ if (!dbgdata->state)
+ return -ENOMEM;
+
+ smp_call_function_single(0, dbg_init_arch_data, dbgdata, 1);
+
+ 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;
+ else
+ pr_info("dbg save-restore supported by TZ\n");
+ } else
+ pr_info("dbg arch %u not supported\n", dbg.arch);
+ return 0;
+}
+
+static int __devinit jtag_mm_probe(struct platform_device *pdev)
+{
+ int etm_ret, dbg_ret, ret;
+ static uint32_t cpu;
+ static uint32_t count;
+ struct device *dev = &pdev->dev;
+
+ cpu = count;
+ count++;
+
+ clock[cpu] = devm_clk_get(dev, "core_clk");
+ if (IS_ERR(clock[cpu])) {
+ ret = PTR_ERR(clock[cpu]);
+ return ret;
+ }
+
+ ret = clk_set_rate(clock[cpu], CORESIGHT_CLK_RATE_TRACE);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(clock[cpu]);
+ if (ret)
+ return ret;
+
+ platform_set_drvdata(pdev, clock[cpu]);
+
+ etm_ret = jtag_mm_etm_probe(pdev, cpu);
+
+ dbg_ret = jtag_mm_dbg_probe(pdev, cpu);
+
+ /* The probe succeeds even when only one of the etm and dbg probes
+ * succeeds. This allows us to save-restore etm and dbg registers
+ * independently.
+ */
+ if (etm_ret && dbg_ret) {
+ clk_disable_unprepare(clock[cpu]);
+ ret = etm_ret;
+ } else
+ ret = 0;
+ return ret;
+}
+
+static int __devexit jtag_mm_remove(struct platform_device *pdev)
+{
+ struct clk *clock = platform_get_drvdata(pdev);
+
+ clk_disable_unprepare(clock);
+ return 0;
+}
+
+static struct of_device_id msm_qdss_mm_match[] = {
+ { .compatible = "qcom,jtag-mm"},
+ {}
+};
+
+static struct platform_driver jtag_mm_driver = {
+ .probe = jtag_mm_probe,
+ .remove = __devexit_p(jtag_mm_remove),
+ .driver = {
+ .name = "msm-jtag-mm",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_qdss_mm_match,
+ },
+};
+
+static int __init jtag_mm_init(void)
+{
+ return platform_driver_register(&jtag_mm_driver);
+}
+module_init(jtag_mm_init);
+
+static void __exit jtag_mm_exit(void)
+{
+ platform_driver_unregister(&jtag_mm_driver);
+}
+module_exit(jtag_mm_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Coresight debug and ETM save-restore driver");
diff --git a/arch/arm/mach-msm/krait-regulator.c b/arch/arm/mach-msm/krait-regulator.c
index f7b2b1e..aa03a6a 100644
--- a/arch/arm/mach-msm/krait-regulator.c
+++ b/arch/arm/mach-msm/krait-regulator.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. 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
@@ -10,7 +10,7 @@
* GNU General Public License for more details.
*/
-#define pr_fmt(fmt) "%s: " fmt, __func__
+#define pr_fmt(fmt) "PDN %s: " fmt, __func__
#include <linux/err.h>
#include <linux/kernel.h>
@@ -27,28 +27,28 @@
#include <linux/regulator/machine.h>
#include <linux/regulator/of_regulator.h>
#include <linux/regulator/krait-regulator.h>
+#include <linux/debugfs.h>
#include <mach/msm_iomap.h>
#include "spm.h"
+#include "pm.h"
/*
* supply
* from
* pmic
* gang
- * | LDO BYP [6]
- * | /
- * | /
- * |_______/ _____
- * | |
- * ___|___ |
- * | | |
- * | | /
- * | LDO | /
- * | | / BHS[6]
- * |_______| |
- * | |
- * |________________|
+ * |
+ * |________________________________
+ * | | |
+ * ___|___ | |
+ * | | | |
+ * | | / /
+ * | LDO | / /LDO BYP [6]
+ * | | / BHS[6] /(bypass is a weak BHS
+ * |_______| | | needs to be on when in
+ * | | | BHS mode)
+ * |________________|_______________|
* |
* ________|________
* | |
@@ -88,6 +88,10 @@
#define PWR_GATE_CONFIG 0x00000044
#define VERSION 0x00000FD0
+/* MDD register group */
+#define MDD_CONFIG_CTL 0x00000000
+#define MDD_MODE 0x00000010
+
/* bit definitions for APC_PWR_GATE_CTL */
#define BHS_CNT_BIT_POS 24
#define BHS_CNT_MASK KRAIT_MASK(31, 24)
@@ -117,6 +121,18 @@
#define VREF_LDO_BIT_POS 0
#define VREF_LDO_MASK KRAIT_MASK(6, 0)
+#define LDO_HDROOM_MIN 50000
+#define LDO_HDROOM_MAX 250000
+
+#define LDO_UV_MIN 465000
+#define LDO_UV_MAX 750000
+
+#define LDO_TH_MIN 600000
+#define LDO_TH_MAX 900000
+
+#define LDO_DELTA_MIN 10000
+#define LDO_DELTA_MAX 100000
+
/**
* struct pmic_gang_vreg -
* @name: the string used to represent the gang
@@ -136,6 +152,8 @@
struct list_head krait_power_vregs;
struct mutex krait_power_vregs_lock;
bool pfm_mode;
+ int pmic_min_uV_for_retention;
+ bool retention_enabled;
};
static struct pmic_gang_vreg *the_gang;
@@ -155,15 +173,26 @@
int load_uA;
enum krait_supply_mode mode;
void __iomem *reg_base;
+ void __iomem *mdd_base;
int ldo_default_uV;
int retention_uV;
int headroom_uV;
int ldo_threshold_uV;
+ int ldo_delta_uV;
bool online;
};
static u32 version;
+static int is_between(int left, int right, int value)
+{
+ if (left >= right && left >= value && value >= right)
+ return 1;
+ if (left <= right && left <= value && value <= right)
+ return 1;
+ return 0;
+}
+
static void krait_masked_write(struct krait_power_vreg *kvreg,
int reg, uint32_t mask, uint32_t val)
{
@@ -182,6 +211,23 @@
mb();
}
+static int get_krait_retention_ldo_uv(struct krait_power_vreg *kvreg)
+{
+ uint32_t reg_val;
+ int uV;
+
+ reg_val = readl_relaxed(kvreg->reg_base + APC_LDO_VREF_SET);
+ reg_val &= VREF_RET_MASK;
+ reg_val >>= VREF_RET_POS;
+
+ if (reg_val == 0)
+ uV = 0;
+ else
+ uV = KRAIT_LDO_VOLTAGE_OFFSET + reg_val * KRAIT_LDO_STEP;
+
+ return uV;
+}
+
static int get_krait_ldo_uv(struct krait_power_vreg *kvreg)
{
uint32_t reg_val;
@@ -225,15 +271,13 @@
{
if (kvreg->mode == HS_MODE)
return 0;
-
- /*
- * enable ldo bypass - the krait is powered still by LDO since
- * LDO is enabled and BHS is disabled
- */
- krait_masked_write(kvreg, APC_PWR_GATE_CTL, LDO_BYP_MASK, LDO_BYP_MASK);
-
/* enable bhs */
- krait_masked_write(kvreg, APC_PWR_GATE_CTL, BHS_EN_MASK, BHS_EN_MASK);
+ krait_masked_write(kvreg, APC_PWR_GATE_CTL,
+ BHS_SEG_EN_MASK | BHS_EN_MASK,
+ BHS_SEG_EN_DEFAULT << BHS_SEG_EN_BIT_POS | BHS_EN_MASK);
+
+ /* complete the above write before the delay */
+ mb();
/*
* wait for the bhs to settle - note that
@@ -242,17 +286,25 @@
*/
udelay(BHS_SETTLING_DELAY_US);
+ /*
+ * enable ldo bypass - the krait is powered still by LDO since
+ * LDO is enabled
+ */
+ krait_masked_write(kvreg, APC_PWR_GATE_CTL, LDO_BYP_MASK, LDO_BYP_MASK);
+
/* disable ldo - only the BHS provides voltage to the cpu after this */
krait_masked_write(kvreg, APC_PWR_GATE_CTL,
LDO_PWR_DWN_MASK, LDO_PWR_DWN_MASK);
kvreg->mode = HS_MODE;
+ pr_debug("%s using BHS\n", kvreg->name);
return 0;
}
static int switch_to_using_ldo(struct krait_power_vreg *kvreg)
{
- if (kvreg->mode == LDO_MODE && get_krait_ldo_uv(kvreg) == kvreg->uV)
+ if (kvreg->mode == LDO_MODE
+ && get_krait_ldo_uv(kvreg) == kvreg->uV - kvreg->ldo_delta_uV)
return 0;
/*
@@ -262,7 +314,7 @@
if (kvreg->mode == LDO_MODE)
switch_to_using_hs(kvreg);
- set_krait_ldo_uv(kvreg, kvreg->uV);
+ set_krait_ldo_uv(kvreg, kvreg->uV - kvreg->ldo_delta_uV);
/*
* enable ldo - note that both LDO and BHS are are supplying voltage to
@@ -271,6 +323,9 @@
*/
krait_masked_write(kvreg, APC_PWR_GATE_CTL, LDO_PWR_DWN_MASK, 0);
+ /* complete the writes before the delay */
+ mb();
+
/* wait for the ldo to settle */
udelay(LDO_SETTLING_DELAY_US);
@@ -280,8 +335,10 @@
*/
krait_masked_write(kvreg, APC_PWR_GATE_CTL,
BHS_EN_MASK | LDO_BYP_MASK, 0);
+ krait_masked_write(kvreg, APC_PWR_GATE_CTL, BHS_SEG_EN_MASK, 0);
kvreg->mode = LDO_MODE;
+ pr_debug("%s using LDO\n", kvreg->name);
return 0;
}
@@ -294,9 +351,15 @@
return 0;
}
-static int set_pmic_gang_voltage(int uV)
+static int set_pmic_gang_voltage(struct pmic_gang_vreg *pvreg, int uV)
{
int setpoint;
+ int rc;
+
+ if (pvreg->pmic_vmax_uV == uV)
+ return 0;
+
+ pr_debug("%d\n", uV);
if (uV < PMIC_VOLTAGE_MIN) {
pr_err("requested %d < %d, restricting it to %d\n",
@@ -309,12 +372,35 @@
uV = PMIC_VOLTAGE_MAX;
}
+ if (uV < pvreg->pmic_min_uV_for_retention) {
+ if (pvreg->retention_enabled) {
+ pr_debug("Disabling Retention pmic = %duV, pmic_min_uV_for_retention = %duV",
+ uV, pvreg->pmic_min_uV_for_retention);
+ msm_pm_enable_retention(false);
+ pvreg->retention_enabled = false;
+ }
+ } else {
+ if (!pvreg->retention_enabled) {
+ pr_debug("Enabling Retention pmic = %duV, pmic_min_uV_for_retention = %duV",
+ uV, pvreg->pmic_min_uV_for_retention);
+ msm_pm_enable_retention(true);
+ pvreg->retention_enabled = true;
+ }
+ }
+
setpoint = DIV_ROUND_UP(uV, LV_RANGE_STEP);
- return msm_spm_apcs_set_vdd(setpoint);
+ rc = msm_spm_apcs_set_vdd(setpoint);
+ if (rc < 0)
+ pr_err("could not set %duV setpt = 0x%x rc = %d\n",
+ uV, setpoint, rc);
+ else
+ pvreg->pmic_vmax_uV = uV;
+
+ return rc;
}
-static int configure_ldo_or_hs(struct krait_power_vreg *from, int vmax)
+static int configure_ldo_or_hs_all(struct krait_power_vreg *from, int vmax)
{
struct pmic_gang_vreg *pvreg = from->pvreg;
struct krait_power_vreg *kvreg;
@@ -323,18 +409,19 @@
list_for_each_entry(kvreg, &pvreg->krait_power_vregs, link) {
if (!kvreg->online)
continue;
- if (kvreg->uV > kvreg->ldo_threshold_uV
- || kvreg->uV > vmax - kvreg->headroom_uV) {
- rc = switch_to_using_hs(kvreg);
+ if (kvreg->uV <= kvreg->ldo_threshold_uV
+ && kvreg->uV - kvreg->ldo_delta_uV + kvreg->headroom_uV
+ <= vmax) {
+ rc = switch_to_using_ldo(kvreg);
if (rc < 0) {
- pr_err("could not switch %s to hs rc = %d\n",
+ pr_err("could not switch %s to ldo rc = %d\n",
kvreg->name, rc);
return rc;
}
} else {
- rc = switch_to_using_ldo(kvreg);
+ rc = switch_to_using_hs(kvreg);
if (rc < 0) {
- pr_err("could not switch %s to ldo rc = %d\n",
+ pr_err("could not switch %s to hs rc = %d\n",
kvreg->name, rc);
return rc;
}
@@ -345,7 +432,7 @@
}
#define SLEW_RATE 2994
-static int pmic_gang_set_voltage_increase(struct krait_power_vreg *from,
+static int krait_voltage_increase(struct krait_power_vreg *from,
int vmax)
{
struct pmic_gang_vreg *pvreg = from->pvreg;
@@ -353,20 +440,25 @@
int settling_us;
/*
- * since pmic gang voltage is increasing set the gang voltage
+ * since krait voltage is increasing set the gang voltage
* prior to changing ldo/hs states of the requesting krait
*/
- rc = set_pmic_gang_voltage(vmax);
+ rc = set_pmic_gang_voltage(pvreg, vmax);
if (rc < 0) {
dev_err(&from->rdev->dev, "%s failed set voltage %d rc = %d\n",
pvreg->name, vmax, rc);
+ return rc;
}
+
+ /* complete the above writes before the delay */
+ mb();
+
/* delay until the voltage is settled when it is raised */
settling_us = DIV_ROUND_UP(vmax - pvreg->pmic_vmax_uV, SLEW_RATE);
udelay(settling_us);
- rc = configure_ldo_or_hs(from, vmax);
+ rc = configure_ldo_or_hs_all(from, vmax);
if (rc < 0) {
dev_err(&from->rdev->dev, "%s failed ldo/hs conf %d rc = %d\n",
pvreg->name, vmax, rc);
@@ -375,25 +467,25 @@
return rc;
}
-static int pmic_gang_set_voltage_decrease(struct krait_power_vreg *from,
+static int krait_voltage_decrease(struct krait_power_vreg *from,
int vmax)
{
struct pmic_gang_vreg *pvreg = from->pvreg;
int rc = 0;
/*
- * since pmic gang voltage is decreasing ldos might get out of their
+ * since krait voltage is decreasing ldos might get out of their
* operating range. Hence configure such kraits to be in hs mode prior
* to setting the pmic gang voltage
*/
- rc = configure_ldo_or_hs(from, vmax);
+ rc = configure_ldo_or_hs_all(from, vmax);
if (rc < 0) {
dev_err(&from->rdev->dev, "%s failed ldo/hs conf %d rc = %d\n",
pvreg->name, vmax, rc);
return rc;
}
- rc = set_pmic_gang_voltage(vmax);
+ rc = set_pmic_gang_voltage(pvreg, vmax);
if (rc < 0) {
dev_err(&from->rdev->dev, "%s failed set voltage %d rc = %d\n",
pvreg->name, vmax, rc);
@@ -402,19 +494,6 @@
return rc;
}
-static int pmic_gang_set_voltage(struct krait_power_vreg *from,
- int vmax)
-{
- struct pmic_gang_vreg *pvreg = from->pvreg;
-
- if (pvreg->pmic_vmax_uV == vmax)
- return 0;
- else if (vmax < pvreg->pmic_vmax_uV)
- return pmic_gang_set_voltage_decrease(from, vmax);
-
- return pmic_gang_set_voltage_increase(from, vmax);
-}
-
#define PHASE_SETTLING_TIME_US 10
static unsigned int pmic_gang_set_phases(struct krait_power_vreg *from,
int load_uA)
@@ -435,6 +514,9 @@
return rc;
}
+ /* complete the writes before the delay */
+ mb();
+
/*
* delay until the phases are settled when
* the count is raised
@@ -461,6 +543,8 @@
pvreg->name = "pmic_gang";
pvreg->pmic_vmax_uV = PMIC_VOLTAGE_MIN;
pvreg->pmic_phase_count = 1;
+ pvreg->retention_enabled = true;
+ pvreg->pmic_min_uV_for_retention = INT_MAX;
mutex_init(&pvreg->krait_power_vregs_lock);
INIT_LIST_HEAD(&pvreg->krait_power_vregs);
@@ -478,12 +562,11 @@
return kvreg->uV;
}
-static int get_vmax(struct krait_power_vreg *from, int min_uV)
+static int get_vmax(struct pmic_gang_vreg *pvreg)
{
int vmax = 0;
int v;
struct krait_power_vreg *kvreg;
- struct pmic_gang_vreg *pvreg = from->pvreg;
list_for_each_entry(kvreg, &pvreg->krait_power_vregs, link) {
if (!kvreg->online)
@@ -491,9 +574,6 @@
v = kvreg->uV;
- if (kvreg == from)
- v = min_uV;
-
if (vmax < v)
vmax = v;
}
@@ -518,28 +598,35 @@
#define ROUND_UP_VOLTAGE(v, res) (DIV_ROUND_UP(v, res) * res)
static int _set_voltage(struct regulator_dev *rdev,
- int min_uV, int max_uV, unsigned *selector)
+ int orig_krait_uV, int requested_uV)
{
struct krait_power_vreg *kvreg = rdev_get_drvdata(rdev);
struct pmic_gang_vreg *pvreg = kvreg->pvreg;
int rc;
int vmax;
- vmax = get_vmax(kvreg, min_uV);
+ pr_debug("%s: %d to %d\n", kvreg->name, orig_krait_uV, requested_uV);
+ /*
+ * Assign the voltage before updating the gang voltage as we iterate
+ * over all the core voltages and choose HS or LDO for each of them
+ */
+ kvreg->uV = requested_uV;
+
+ vmax = get_vmax(pvreg);
/* round up the pmic voltage as per its resolution */
vmax = ROUND_UP_VOLTAGE(vmax, LV_RANGE_STEP);
- rc = pmic_gang_set_voltage(kvreg, vmax);
+ if (requested_uV > orig_krait_uV)
+ rc = krait_voltage_increase(kvreg, vmax);
+ else
+ rc = krait_voltage_decrease(kvreg, vmax);
+
if (rc < 0) {
- dev_err(&rdev->dev, "%s failed set voltage (%d, %d) rc = %d\n",
- kvreg->name, min_uV, max_uV, rc);
- goto out;
+ dev_err(&rdev->dev, "%s failed to set %duV from %duV rc = %d\n",
+ kvreg->name, requested_uV, orig_krait_uV, rc);
}
- pvreg->pmic_vmax_uV = vmax;
-
-out:
return rc;
}
@@ -562,14 +649,13 @@
}
mutex_lock(&pvreg->krait_power_vregs_lock);
- kvreg->uV = min_uV;
-
if (!kvreg->online) {
+ kvreg->uV = min_uV;
mutex_unlock(&pvreg->krait_power_vregs_lock);
return 0;
}
- rc = _set_voltage(rdev, min_uV, max_uV, selector);
+ rc = _set_voltage(rdev, kvreg->uV, min_uV);
mutex_unlock(&pvreg->krait_power_vregs_lock);
return rc;
@@ -673,12 +759,14 @@
mutex_lock(&pvreg->krait_power_vregs_lock);
kvreg->online = true;
- rc = _get_optimum_mode(rdev, kvreg->uV, kvreg->uV,
- kvreg->load_uA);
+ rc = _get_optimum_mode(rdev, kvreg->uV, kvreg->uV, kvreg->load_uA);
if (rc < 0)
goto en_err;
- rc = _set_voltage(rdev, kvreg->uV,
- rdev->constraints->max_uV, NULL);
+ /*
+ * since the core is being enabled, behave as if it is increasing
+ * the core voltage
+ */
+ rc = _set_voltage(rdev, 0, kvreg->uV);
en_err:
mutex_unlock(&pvreg->krait_power_vregs_lock);
return rc;
@@ -698,8 +786,7 @@
if (rc < 0)
goto dis_err;
- rc = _set_voltage(rdev, kvreg->uV,
- rdev->constraints->max_uV, NULL);
+ rc = _set_voltage(rdev, kvreg->uV, kvreg->uV);
dis_err:
mutex_unlock(&pvreg->krait_power_vregs_lock);
return rc;
@@ -716,24 +803,57 @@
.is_enabled = krait_power_is_enabled,
};
+static struct dentry *dent;
+static int get_retention_dbg_uV(void *data, u64 *val)
+{
+ struct pmic_gang_vreg *pvreg = data;
+ struct krait_power_vreg *kvreg;
+
+ mutex_lock(&pvreg->krait_power_vregs_lock);
+ if (!list_empty(&pvreg->krait_power_vregs)) {
+ /* return the retention voltage on just the first cpu */
+ kvreg = list_entry((&pvreg->krait_power_vregs)->next,
+ typeof(*kvreg), link);
+ *val = get_krait_retention_ldo_uv(kvreg);
+ }
+ mutex_unlock(&pvreg->krait_power_vregs_lock);
+ return 0;
+}
+
+static int set_retention_dbg_uV(void *data, u64 val)
+{
+ struct pmic_gang_vreg *pvreg = data;
+ struct krait_power_vreg *kvreg;
+ int retention_uV = val;
+
+ if (!is_between(LDO_UV_MIN, LDO_UV_MAX, retention_uV))
+ return -EINVAL;
+
+ mutex_lock(&pvreg->krait_power_vregs_lock);
+ list_for_each_entry(kvreg, &pvreg->krait_power_vregs, link) {
+ kvreg->retention_uV = retention_uV;
+ set_krait_retention_uv(kvreg, retention_uV);
+ }
+ mutex_unlock(&pvreg->krait_power_vregs_lock);
+ return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(retention_fops,
+ get_retention_dbg_uV, set_retention_dbg_uV, "%llu\n");
+
static void kvreg_hw_init(struct krait_power_vreg *kvreg)
{
/*
* bhs_cnt value sets the ramp-up time from power collapse,
* initialize the ramp up time
*/
- krait_masked_write(kvreg, APC_PWR_GATE_CTL,
- BHS_CNT_MASK, BHS_CNT_DEFAULT << BHS_CNT_BIT_POS);
-
- krait_masked_write(kvreg, APC_PWR_GATE_CTL,
- CLK_SRC_SEL_MASK, CLK_SRC_DEFAULT << CLK_SRC_SEL_BIT_POS);
-
- /* BHS has six different segments, turn them all on */
- krait_masked_write(kvreg, APC_PWR_GATE_CTL,
- BHS_SEG_EN_MASK, BHS_SEG_EN_DEFAULT << BHS_SEG_EN_BIT_POS);
-
set_krait_retention_uv(kvreg, kvreg->retention_uV);
set_krait_ldo_uv(kvreg, kvreg->ldo_default_uV);
+
+ /* setup the bandgap that configures the reference to the LDO */
+ writel_relaxed(0x00000190, kvreg->mdd_base + MDD_CONFIG_CTL);
+ /* Enable MDD */
+ writel_relaxed(0x00000002, kvreg->mdd_base + MDD_MODE);
+ mb();
}
static void glb_init(struct platform_device *pdev)
@@ -745,31 +865,14 @@
pr_debug("version= 0x%x\n", version);
}
-static int is_between(int left, int right, int value)
-{
- if (left >= right && left >= value && value >= right)
- return 1;
- if (left <= right && left <= value && value <= right)
- return 1;
- return 0;
-}
-
-#define LDO_HDROOM_MIN 50000
-#define LDO_HDROOM_MAX 250000
-
-#define LDO_UV_MIN 465000
-#define LDO_UV_MAX 750000
-
-#define LDO_TH_MIN 600000
-#define LDO_TH_MAX 800000
-
static int __devinit krait_power_probe(struct platform_device *pdev)
{
struct krait_power_vreg *kvreg;
- struct resource *res;
+ struct resource *res, *res_mdd;
struct regulator_init_data *init_data = pdev->dev.platform_data;
int rc = 0;
int headroom_uV, retention_uV, ldo_default_uV, ldo_threshold_uV;
+ int ldo_delta_uV;
/* Initialize the pmic gang if it hasn't been initialized already */
if (the_gang == NULL) {
@@ -783,6 +886,12 @@
glb_init(pdev);
}
+ if (dent == NULL) {
+ dent = debugfs_create_dir(KRAIT_REGULATOR_DRIVER_NAME, NULL);
+ debugfs_create_file("retention_uV",
+ 0644, dent, the_gang, &retention_fops);
+ }
+
if (pdev->dev.of_node) {
/* Get init_data from device tree. */
init_data = of_get_regulator_init_data(&pdev->dev,
@@ -845,6 +954,19 @@
ldo_threshold_uV);
return -EINVAL;
}
+
+ rc = of_property_read_u32(pdev->dev.of_node,
+ "qcom,ldo-delta-voltage",
+ &ldo_delta_uV);
+ if (rc < 0) {
+ pr_err("ldo-delta-voltage missing rc=%d\n", rc);
+ return rc;
+ }
+ if (!is_between(LDO_DELTA_MIN, LDO_DELTA_MAX, ldo_delta_uV)) {
+ pr_err("bad ldo-delta-voltage = %d specified\n",
+ ldo_delta_uV);
+ return -EINVAL;
+ }
}
if (!init_data) {
@@ -858,12 +980,18 @@
return -EINVAL;
}
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "acs");
if (!res) {
dev_err(&pdev->dev, "missing physical register addresses\n");
return -EINVAL;
}
+ res_mdd = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mdd");
+ if (!res_mdd) {
+ dev_err(&pdev->dev, "missing mdd register addresses\n");
+ return -EINVAL;
+ }
+
kvreg = devm_kzalloc(&pdev->dev,
sizeof(struct krait_power_vreg), GFP_KERNEL);
if (!kvreg) {
@@ -874,6 +1002,9 @@
kvreg->reg_base = devm_ioremap(&pdev->dev,
res->start, resource_size(res));
+ kvreg->mdd_base = devm_ioremap(&pdev->dev,
+ res_mdd->start, resource_size(res));
+
kvreg->pvreg = the_gang;
kvreg->name = init_data->constraints.name;
kvreg->desc.name = kvreg->name;
@@ -887,10 +1018,14 @@
kvreg->retention_uV = retention_uV;
kvreg->ldo_default_uV = ldo_default_uV;
kvreg->ldo_threshold_uV = ldo_threshold_uV;
+ kvreg->ldo_delta_uV = ldo_delta_uV;
platform_set_drvdata(pdev, kvreg);
mutex_lock(&the_gang->krait_power_vregs_lock);
+ the_gang->pmic_min_uV_for_retention
+ = min(the_gang->pmic_min_uV_for_retention,
+ kvreg->retention_uV + kvreg->headroom_uV);
list_add_tail(&kvreg->link, &the_gang->krait_power_vregs);
mutex_unlock(&the_gang->krait_power_vregs_lock);
@@ -953,25 +1088,36 @@
{
platform_driver_unregister(&krait_power_driver);
}
-
module_exit(krait_power_exit);
void secondary_cpu_hs_init(void *base_ptr)
{
- /* 605mV retention and 705mV operational voltage */
- writel_relaxed(0x1C30, base_ptr + APC_LDO_VREF_SET);
- /* HS_EN_DLY=3; LDO_BYP_DLY=1; */
- writel_relaxed(0x430000, base_ptr + APC_PWR_GATE_DLY);
- /* MODE = BHS; EN=1; */
- writel_relaxed(0x21, base_ptr + APC_PWR_GATE_MODE);
-
/* Turn on the BHS, turn off LDO Bypass and power down LDO */
- writel_relaxed(0x403F007F, base_ptr + APC_PWR_GATE_CTL);
+ writel_relaxed(
+ BHS_CNT_DEFAULT << BHS_CNT_BIT_POS
+ | LDO_PWR_DWN_MASK
+ | CLK_SRC_DEFAULT << CLK_SRC_SEL_BIT_POS
+ | BHS_SEG_EN_DEFAULT << BHS_SEG_EN_BIT_POS
+ | BHS_EN_MASK,
+ base_ptr + APC_PWR_GATE_CTL);
+
+ /* complete the above write before the delay */
mb();
- udelay(1);
+
+ /*
+ * wait for the bhs to settle
+ */
+ udelay(BHS_SETTLING_DELAY_US);
/* Finally turn on the bypass so that BHS supplies power */
- writel_relaxed(0x403F3F7F, base_ptr + APC_PWR_GATE_CTL);
+ writel_relaxed(
+ BHS_CNT_DEFAULT << BHS_CNT_BIT_POS
+ | LDO_PWR_DWN_MASK
+ | CLK_SRC_DEFAULT << CLK_SRC_SEL_BIT_POS
+ | LDO_BYP_MASK
+ | BHS_SEG_EN_DEFAULT << BHS_SEG_EN_BIT_POS
+ | BHS_EN_MASK,
+ base_ptr + APC_PWR_GATE_CTL);
}
MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/mach-msm/lpm_levels.c b/arch/arm/mach-msm/lpm_levels.c
index 61c2aa8..fcb4299 100644
--- a/arch/arm/mach-msm/lpm_levels.c
+++ b/arch/arm/mach-msm/lpm_levels.c
@@ -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
@@ -37,6 +37,10 @@
static struct msm_rpmrs_level *msm_lpm_levels;
static int msm_lpm_level_count;
+static DEFINE_PER_CPU(uint32_t , msm_lpm_sleep_time);
+static DEFINE_PER_CPU(int , lpm_permitted_level);
+static DEFINE_PER_CPU(struct atomic_notifier_head, lpm_notify_head);
+
static void msm_lpm_level_update(void)
{
unsigned int lpm_level;
@@ -53,31 +57,41 @@
bool from_idle, bool notify_rpm)
{
int ret = 0;
- int debug_mask;
- struct msm_rpmrs_limits *l = (struct msm_rpmrs_limits *)limits;
+ struct msm_lpm_sleep_data sleep_data;
- ret = msm_rpm_enter_sleep();
- if (ret) {
- pr_warn("%s(): RPM failed to enter sleep err:%d\n",
- __func__, ret);
- goto bail;
+ sleep_data.limits = limits;
+ sleep_data.kernel_sleep = __get_cpu_var(msm_lpm_sleep_time);
+ atomic_notifier_call_chain(&__get_cpu_var(lpm_notify_head),
+ MSM_LPM_STATE_ENTER, &sleep_data);
+
+ if (notify_rpm) {
+ int debug_mask;
+ struct msm_rpmrs_limits *l = (struct msm_rpmrs_limits *)limits;
+
+ ret = msm_rpm_enter_sleep();
+ if (ret) {
+ pr_warn("%s(): RPM failed to enter sleep err:%d\n",
+ __func__, ret);
+ goto bail;
+ }
+ if (from_idle)
+ debug_mask = msm_lpm_lvl_dbg_msk &
+ MSM_LPM_LVL_DBG_IDLE_LIMITS;
+ else
+ debug_mask = msm_lpm_lvl_dbg_msk &
+ MSM_LPM_LVL_DBG_SUSPEND_LIMITS;
+
+ if (debug_mask)
+ pr_info("%s(): pxo:%d l2:%d mem:0x%x(0x%x) dig:0x%x(0x%x)\n",
+ __func__, l->pxo, l->l2_cache,
+ l->vdd_mem_lower_bound,
+ l->vdd_mem_upper_bound,
+ l->vdd_dig_lower_bound,
+ l->vdd_dig_upper_bound);
+
+ ret = msm_lpmrs_enter_sleep(sclk_count, l, from_idle,
+ notify_rpm);
}
- if (from_idle)
- debug_mask = msm_lpm_lvl_dbg_msk &
- MSM_LPM_LVL_DBG_IDLE_LIMITS;
- else
- debug_mask = msm_lpm_lvl_dbg_msk &
- MSM_LPM_LVL_DBG_SUSPEND_LIMITS;
-
- if (debug_mask)
- pr_info("%s(): pxo:%d l2:%d mem:0x%x(0x%x) dig:0x%x(0x%x)\n",
- __func__, l->pxo, l->l2_cache,
- l->vdd_mem_lower_bound,
- l->vdd_mem_upper_bound,
- l->vdd_dig_lower_bound,
- l->vdd_dig_upper_bound);
-
- ret = msm_lpmrs_enter_sleep(sclk_count, l, from_idle, notify_rpm);
bail:
return ret;
}
@@ -88,6 +102,8 @@
msm_rpm_exit_sleep();
msm_lpmrs_exit_sleep((struct msm_rpmrs_limits *)limits,
from_idle, notify_rpm, collapsed);
+ atomic_notifier_call_chain(&__get_cpu_var(lpm_notify_head),
+ MSM_LPM_STATE_EXIT, NULL);
}
void msm_lpm_show_resources(void)
@@ -96,6 +112,48 @@
return;
}
+uint32_t msm_pm_get_pxo(struct msm_rpmrs_limits *limits)
+{
+ return limits->pxo;
+}
+
+uint32_t msm_pm_get_l2_cache(struct msm_rpmrs_limits *limits)
+{
+ return limits->l2_cache;
+}
+
+uint32_t msm_pm_get_vdd_mem(struct msm_rpmrs_limits *limits)
+{
+ return limits->vdd_mem_upper_bound;
+}
+
+uint32_t msm_pm_get_vdd_dig(struct msm_rpmrs_limits *limits)
+{
+ return limits->vdd_dig_upper_bound;
+}
+
+static bool lpm_level_permitted(int cur_level_count)
+{
+ if (__get_cpu_var(lpm_permitted_level) == msm_lpm_level_count + 1)
+ return true;
+ return (__get_cpu_var(lpm_permitted_level) == cur_level_count);
+}
+
+int msm_lpm_register_notifier(int cpu, int level_iter,
+ struct notifier_block *nb, bool is_latency_measure)
+{
+ per_cpu(lpm_permitted_level, cpu) = level_iter;
+ return atomic_notifier_chain_register(&per_cpu(lpm_notify_head,
+ cpu), nb);
+}
+
+int msm_lpm_unregister_notifier(int cpu, struct notifier_block *nb)
+{
+ per_cpu(lpm_permitted_level, cpu) = msm_lpm_level_count + 1;
+ return atomic_notifier_chain_unregister(&per_cpu(lpm_notify_head, cpu),
+ nb);
+}
+
s32 msm_cpuidle_get_deep_idle_latency(void)
{
int i;
@@ -127,7 +185,7 @@
struct msm_rpmrs_level *best_level = NULL;
uint32_t pwr;
int i;
-
+ int best_level_iter = msm_lpm_level_count + 1;
if (!msm_lpm_levels)
return NULL;
@@ -170,14 +228,31 @@
level->rs_limits.latency_us[cpu] = level->latency_us;
level->rs_limits.power[cpu] = pwr;
best_level = level;
-
+ best_level_iter = i;
if (power)
*power = pwr;
}
}
+ if (best_level && !lpm_level_permitted(best_level_iter))
+ best_level = NULL;
+ else
+ per_cpu(msm_lpm_sleep_time, cpu) =
+ time_param->modified_time_us ?
+ time_param->modified_time_us : time_param->sleep_us;
return best_level ? &best_level->rs_limits : NULL;
}
+
+static struct lpm_test_platform_data lpm_test_pdata;
+
+static struct platform_device msm_lpm_test_device = {
+ .name = "lpm_test",
+ .id = -1,
+ .dev = {
+ .platform_data = &lpm_test_pdata,
+ },
+};
+
static struct msm_pm_sleep_ops msm_lpm_ops = {
.lowest_limits = msm_lpm_lowest_limits,
.enter_sleep = msm_lpm_enter_sleep,
@@ -194,6 +269,7 @@
int ret = 0;
uint32_t num_levels = 0;
int idx = 0;
+ unsigned int m_cpu = 0;
for_each_child_of_node(pdev->dev.of_node, node)
num_levels++;
@@ -279,6 +355,17 @@
msm_lpm_levels = levels;
msm_lpm_level_count = idx;
+ lpm_test_pdata.msm_lpm_test_levels = msm_lpm_levels;
+ lpm_test_pdata.msm_lpm_test_level_count = msm_lpm_level_count;
+ key = "qcom,use-qtimer";
+ lpm_test_pdata.use_qtimer =
+ of_property_read_bool(pdev->dev.of_node, key);
+
+ for_each_possible_cpu(m_cpu)
+ per_cpu(lpm_permitted_level, m_cpu) =
+ msm_lpm_level_count + 1;
+
+ platform_device_register(&msm_lpm_test_device);
msm_pm_set_sleep_ops(&msm_lpm_ops);
return 0;
diff --git a/arch/arm/mach-msm/lpm_resources.h b/arch/arm/mach-msm/lpm_resources.h
index 120832f..42e43c2 100644
--- a/arch/arm/mach-msm/lpm_resources.h
+++ b/arch/arm/mach-msm/lpm_resources.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. 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
@@ -15,6 +15,7 @@
#define __ARCH_ARM_MACH_MSM_LPM_RESOURCES_H
#include "pm.h"
+#include "test-lpm.h"
enum {
MSM_LPM_PXO_OFF = 0,
@@ -50,6 +51,93 @@
uint32_t time_overhead_us;
};
+enum {
+ MSM_LPM_STATE_ENTER = 0,
+ MSM_LPM_STATE_EXIT = 1,
+};
+
+#define MSM_PM(field) MSM_LPM_##field
+
+/**
+ * msm_pm_get_pxo() - get the limits for pxo
+ * @limits: pointer to the msm_rpmrs_limits structure
+ *
+ * This function gets the limits to the resource pxo on
+ * 8974
+ */
+
+uint32_t msm_pm_get_pxo(struct msm_rpmrs_limits *limits);
+
+/**
+ * msm_pm_get_l2_cache() - get the limits for l2 cache
+ * @limits: pointer to the msm_rpmrs_limits structure
+ *
+ * This function gets the limits to the resource l2 cache
+ * on 8974
+ */
+
+uint32_t msm_pm_get_l2_cache(struct msm_rpmrs_limits *limits);
+
+/**
+ * msm_pm_get_vdd_mem() - get the limits for pxo
+ * @limits: pointer to the msm_rpmrs_limits structure
+ *
+ * This function gets the limits to the resource vdd mem
+ * on 8974
+ */
+
+uint32_t msm_pm_get_vdd_mem(struct msm_rpmrs_limits *limits);
+
+/**
+ * msm_pm_get_vdd_dig() - get the limits for vdd dig
+ * @limits: pointer to the msm_rpmrs_limits structure
+ *
+ * This function gets the limits to the resource on 8974
+ */
+
+uint32_t msm_pm_get_vdd_dig(struct msm_rpmrs_limits *limits);
+
+/**
+ * struct msm_lpm_sleep_data - abstraction to get sleep data
+ * @limits: pointer to the msm_rpmrs_limits structure
+ * @kernel_sleep: kernel sleep time as decided by the power calculation
+ * algorithm
+ *
+ * This structure is an abstraction to get the limits and kernel sleep time
+ * during enter sleep.
+ */
+
+struct msm_lpm_sleep_data {
+ struct msm_rpmrs_limits *limits;
+ uint32_t kernel_sleep;
+};
+
+/**
+ * msm_lpm_register_notifier() - register for notifications
+ * @cpu: cpu to debug
+ * @level_iter: low power level index to debug
+ * @nb: notifier block to callback on notifications
+ * @is_latency_measure: is it latency measure
+ *
+ * This function sets the permitted level to the index of the
+ * level under test and registers notifier for callback.
+ */
+
+int msm_lpm_register_notifier(int cpu, int level_iter,
+ struct notifier_block *nb, bool is_latency_measure);
+
+/**
+ * msm_lpm_unregister_notifier() - unregister from notifications
+ * @cpu: cpu to debug
+ * @nb: notifier block to callback on notifications
+ *
+ * This function sets the permitted level to a value one more than
+ * available levels count which indicates that all levels are
+ * permitted and it also unregisters notifier for callback.
+ */
+
+int msm_lpm_unregister_notifier(int cpu, struct notifier_block *nb);
+
#ifdef CONFIG_MSM_RPM_SMD
/**
diff --git a/arch/arm/mach-msm/msm_bus/msm_bus_bimc.c b/arch/arm/mach-msm/msm_bus/msm_bus_bimc.c
index ea17efe..d416c19 100644
--- a/arch/arm/mach-msm/msm_bus/msm_bus_bimc.c
+++ b/arch/arm/mach-msm/msm_bus/msm_bus_bimc.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. 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
@@ -1932,17 +1932,18 @@
}
for (i = 0; i < info->node_info->num_mports; i++) {
- /* If in bypass mode, update priority */
- if (info->node_info->mode != BIMC_QOS_MODE_BYPASS)
+ /* If not in bypass mode, update priority */
+ if (info->node_info->mode != BIMC_QOS_MODE_BYPASS) {
msm_bus_bimc_set_qos_prio(binfo, info->node_info->
qport[i], info->node_info->mode, qmode);
- /* If in fixed mode, update bandwidth */
- if (info->node_info->mode != BIMC_QOS_MODE_FIXED) {
- struct msm_bus_bimc_qos_bw qbw;
- qbw.ws = info->node_info->ws;
- msm_bus_bimc_set_qos_bw(binfo,
- info->node_info->qport[i], &qbw);
+ /* If not in fixed mode, update bandwidth */
+ if (info->node_info->mode != BIMC_QOS_MODE_FIXED) {
+ struct msm_bus_bimc_qos_bw qbw;
+ qbw.ws = info->node_info->ws;
+ msm_bus_bimc_set_qos_bw(binfo,
+ info->node_info->qport[i], &qbw);
+ }
}
/* set mode */
diff --git a/arch/arm/mach-msm/msm_bus/msm_bus_board_8974.c b/arch/arm/mach-msm/msm_bus/msm_bus_board_8974.c
index dbfa5ec..7b67157 100644
--- a/arch/arm/mach-msm/msm_bus/msm_bus_board_8974.c
+++ b/arch/arm/mach-msm/msm_bus/msm_bus_board_8974.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. 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
@@ -539,7 +539,6 @@
static int qports_venus_p1[] = {5};
static int qports_vfe[] = {6};
static int qports_gemini_ocmem[] = {0};
-static int qports_mdp_ocmem[] = {1};
static int qports_venus_p0_ocmem[] = {2};
static int qports_venus_p1_ocmem[] = {3};
static int qports_vfe_ocmem[] = {4};
@@ -1154,7 +1153,6 @@
.num_tiers = ARRAY_SIZE(tier2),
.perm_mode = NOC_QOS_PERM_MODE_FIXED,
.mode = NOC_QOS_MODE_FIXED,
- .qport = qports_mdp_ocmem,
.mas_hw_id = MAS_MDP_OCMEM,
.hw_sel = MSM_BUS_NOC,
},
diff --git a/arch/arm/mach-msm/msm_bus/msm_bus_rpm_smd.c b/arch/arm/mach-msm/msm_bus/msm_bus_rpm_smd.c
index 88fab96..7c2b4c9 100644
--- a/arch/arm/mach-msm/msm_bus/msm_bus_rpm_smd.c
+++ b/arch/arm/mach-msm/msm_bus/msm_bus_rpm_smd.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. 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
@@ -101,7 +101,7 @@
if (ret) {
MSM_BUS_WARN("RPM: Add KVP failed for RPM Req:%u\n",
rsc_type);
- return ret;
+ goto free_rpm_request;
}
MSM_BUS_DBG("Added Key: %d, Val: %llu, size: %d\n", key,
@@ -112,22 +112,26 @@
if (ret) {
MSM_BUS_WARN("RPM: Add KVP failed for RPM Req:%u\n",
rsc_type);
- return ret;
+ goto free_rpm_request;
}
}
msg_id = msm_rpm_send_request(rpm_req);
if (!msg_id) {
MSM_BUS_WARN("RPM: No message ID for req\n");
- return -ENXIO;
+ ret = -ENXIO;
+ goto free_rpm_request;
}
ret = msm_rpm_wait_for_ack(msg_id);
if (ret) {
MSM_BUS_WARN("RPM: Ack failed\n");
- return ret;
+ goto free_rpm_request;
}
+free_rpm_request:
+ msm_rpm_free_request(rpm_req);
+
return ret;
}
diff --git a/arch/arm/mach-msm/msm_watchdog_v2.c b/arch/arm/mach-msm/msm_watchdog_v2.c
index 6aa14a6..ef10cdc 100644
--- a/arch/arm/mach-msm/msm_watchdog_v2.c
+++ b/arch/arm/mach-msm/msm_watchdog_v2.c
@@ -333,7 +333,14 @@
wdog_dd->last_pet, nanosec_rem / 1000);
if (wdog_dd->do_ipi_ping)
dump_cpu_alive_mask(wdog_dd);
- panic("Apps watchdog bark received!");
+ printk(KERN_INFO "Causing a watchdog bite!");
+ __raw_writel(1, wdog_dd->base + WDT0_BITE_TIME);
+ mb();
+ __raw_writel(1, wdog_dd->base + WDT0_RST);
+ mb();
+ /* Delay to make sure bite occurs */
+ mdelay(1);
+ panic("Failed to cause a watchdog bite! - Falling back to kernel panic!");
return IRQ_HANDLED;
}
diff --git a/arch/arm/mach-msm/no-pm.c b/arch/arm/mach-msm/no-pm.c
index d460c70..a8d4fdb 100644
--- a/arch/arm/mach-msm/no-pm.c
+++ b/arch/arm/mach-msm/no-pm.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2011, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2010-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
@@ -37,14 +37,11 @@
void msm_pm_set_irq_extns(struct msm_pm_irq_calls *irq_calls) {}
-int msm_pm_idle_prepare(struct cpuidle_device *dev,
+enum msm_pm_sleep_mode msm_pm_idle_enter(struct cpuidle_device *dev,
struct cpuidle_driver *drv, int index)
{
return -ENOSYS;
}
-int msm_pm_idle_enter(enum msm_pm_sleep_mode sleep_mode)
-{
- return -ENOSYS;
-}
+void msm_pm_enable_retention(bool enable) {}
diff --git a/arch/arm/mach-msm/ocmem_core.c b/arch/arm/mach-msm/ocmem_core.c
index 3d9639f..9782b90 100644
--- a/arch/arm/mach-msm/ocmem_core.c
+++ b/arch/arm/mach-msm/ocmem_core.c
@@ -488,7 +488,7 @@
if (mpu_start < 0)
/* Avoid underflow */
mpu_start = 0;
- mpu_end = ((offset+len) >> GFX_MPU_SHIFT) - 1;
+ mpu_end = ((offset+len) >> GFX_MPU_SHIFT);
BUG_ON(mpu_end < 0);
pr_debug("ocmem: mpu: start %x end %x\n", mpu_start, mpu_end);
diff --git a/arch/arm/mach-msm/perf_debug.c b/arch/arm/mach-msm/perf_debug.c
new file mode 100644
index 0000000..52e58a2
--- /dev/null
+++ b/arch/arm/mach-msm/perf_debug.c
@@ -0,0 +1,64 @@
+/* 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/types.h>
+#include <linux/uaccess.h>
+#include <linux/debugfs.h>
+
+/*
+ * Subsequent patches should add an entry to end of this string.
+ * Format is incrementing sequence number followed by text of
+ * patch commit title with newline.
+ * Note trailing ';' is on its own line to simplify addition of
+ * future strings.
+ */
+static char *descriptions =
+ "0 msm: perf: add debug patch logging framework\n"
+;
+
+static ssize_t desc_read(struct file *fp, char __user *buf,
+ size_t count, loff_t *pos)
+{
+ return simple_read_from_buffer(buf, count, pos, descriptions,
+ strlen(descriptions));
+}
+
+static const struct file_operations perf_debug_desc_fops = {
+ .read = desc_read,
+};
+
+static int msm_perf_debugfs_init(void)
+{
+ int ret = 0;
+ struct dentry *dir;
+ struct dentry *file;
+
+ dir = debugfs_create_dir("msm-perf-patches", NULL);
+ if (IS_ERR_OR_NULL(dir)) {
+ pr_err("failed to create msm-perf-patches dir in debugfs\n");
+ ret = PTR_ERR(dir);
+ goto init_exit;
+ }
+
+ file = debugfs_create_file("descriptions", 0444, dir, NULL,
+ &perf_debug_desc_fops);
+ if (IS_ERR_OR_NULL(file)) {
+ debugfs_remove(dir);
+ pr_err("failed to create descriptions file for msm-perf-patches\n");
+ ret = PTR_ERR(file);
+ goto init_exit;
+ }
+
+init_exit:
+ return ret;
+}
+late_initcall(msm_perf_debugfs_init);
diff --git a/arch/arm/mach-msm/pil-pronto.c b/arch/arm/mach-msm/pil-pronto.c
index cfcf5dc..9c89f2d 100644
--- a/arch/arm/mach-msm/pil-pronto.c
+++ b/arch/arm/mach-msm/pil-pronto.c
@@ -235,6 +235,12 @@
return pas_init_image(PAS_WCNSS, metadata, size);
}
+static int pil_pronto_mem_setup_trusted(struct pil_desc *pil, phys_addr_t addr,
+ size_t size)
+{
+ return pas_mem_setup(PAS_WCNSS, addr, size);
+}
+
static int pil_pronto_reset_trusted(struct pil_desc *pil)
{
return pas_auth_and_reset(PAS_WCNSS);
@@ -247,6 +253,7 @@
static struct pil_reset_ops pil_pronto_ops_trusted = {
.init_image = pil_pronto_init_image_trusted,
+ .mem_setup = pil_pronto_mem_setup_trusted,
.auth_and_reset = pil_pronto_reset_trusted,
.shutdown = pil_pronto_shutdown_trusted,
.proxy_vote = pil_pronto_make_proxy_vote,
diff --git a/arch/arm/mach-msm/pil-q6v5-lpass.c b/arch/arm/mach-msm/pil-q6v5-lpass.c
index 5e03aa8..5c498ec 100644
--- a/arch/arm/mach-msm/pil-q6v5-lpass.c
+++ b/arch/arm/mach-msm/pil-q6v5-lpass.c
@@ -153,6 +153,12 @@
return pas_init_image(PAS_Q6, metadata, size);
}
+static int pil_lpass_mem_setup_trusted(struct pil_desc *pil, phys_addr_t addr,
+ size_t size)
+{
+ return pas_mem_setup(PAS_Q6, addr, size);
+}
+
static int pil_lpass_reset_trusted(struct pil_desc *pil)
{
return pas_auth_and_reset(PAS_Q6);
@@ -165,6 +171,7 @@
static struct pil_reset_ops pil_lpass_ops_trusted = {
.init_image = pil_lpass_init_image_trusted,
+ .mem_setup = pil_lpass_mem_setup_trusted,
.proxy_vote = pil_q6v5_make_proxy_votes,
.proxy_unvote = pil_q6v5_remove_proxy_votes,
.auth_and_reset = pil_lpass_reset_trusted,
diff --git a/arch/arm/mach-msm/pil-q6v5-mss.c b/arch/arm/mach-msm/pil-q6v5-mss.c
index ed85c95..7cf61ab 100644
--- a/arch/arm/mach-msm/pil-q6v5-mss.c
+++ b/arch/arm/mach-msm/pil-q6v5-mss.c
@@ -1,5 +1,5 @@
/*
- * 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
@@ -24,6 +24,7 @@
#include <linux/of.h>
#include <linux/regulator/consumer.h>
#include <linux/interrupt.h>
+#include <linux/of_gpio.h>
#include <mach/subsystem_restart.h>
#include <mach/clk.h>
@@ -87,6 +88,8 @@
bool crash_shutdown;
bool ignore_errors;
int is_loadable;
+ int err_fatal_irq;
+ int force_stop_gpio;
};
static int pbl_mba_boot_timeout_ms = 100;
@@ -442,18 +445,17 @@
subsystem_restart_dev(drv->subsys);
}
-static void smsm_state_cb(void *data, uint32_t old_state, uint32_t new_state)
+static irqreturn_t modem_err_fatal_intr_handler(int irq, void *dev_id)
{
- struct mba_data *drv = data;
+ struct mba_data *drv = dev_id;
- /* Ignore if we're the one that set SMSM_RESET */
+ /* Ignore if we're the one that set the force stop GPIO */
if (drv->crash_shutdown)
- return;
+ return IRQ_HANDLED;
- if (new_state & SMSM_RESET) {
- pr_err("Probable fatal error on the modem.\n");
- restart_modem(drv);
- }
+ pr_err("Fatal error on the modem.\n");
+ restart_modem(drv);
+ return IRQ_HANDLED;
}
static int modem_shutdown(const struct subsys_desc *subsys)
@@ -493,7 +495,7 @@
{
struct mba_data *drv = subsys_to_drv(subsys);
drv->crash_shutdown = true;
- smsm_reset_modem(SMSM_RESET);
+ gpio_set_value(drv->force_stop_gpio, 1);
}
static struct ramdump_segment smem_segments[] = {
@@ -630,10 +632,11 @@
goto err_irq;
}
- ret = smsm_state_cb_register(SMSM_MODEM_STATE, SMSM_RESET,
- smsm_state_cb, drv);
+ ret = devm_request_irq(&pdev->dev, drv->err_fatal_irq,
+ modem_err_fatal_intr_handler,
+ IRQF_TRIGGER_RISING, "pil-mss", drv);
if (ret < 0) {
- dev_err(&pdev->dev, "Unable to register SMSM callback!\n");
+ dev_err(&pdev->dev, "Unable to register SMP2P err fatal handler!\n");
goto err_irq;
}
@@ -643,14 +646,11 @@
ret = PTR_ERR(drv->adsp_state_notifier);
dev_err(&pdev->dev, "%s: Registration with the SSR notification driver failed (%d)",
__func__, ret);
- goto err_smsm;
+ goto err_irq;
}
return 0;
-err_smsm:
- smsm_state_cb_deregister(SMSM_MODEM_STATE, SMSM_RESET, smsm_state_cb,
- drv);
err_irq:
destroy_ramdump_device(drv->smem_ramdump_dev);
err_ramdump_smem:
@@ -759,7 +759,7 @@
static int __devinit pil_mss_driver_probe(struct platform_device *pdev)
{
struct mba_data *drv;
- int ret;
+ int ret, err_fatal_gpio;
drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
if (!drv)
@@ -774,6 +774,22 @@
return ret;
}
+ /* Get the IRQ from the GPIO for registering inbound handler */
+ err_fatal_gpio = of_get_named_gpio(pdev->dev.of_node,
+ "qcom,gpio-err-fatal", 0);
+ if (err_fatal_gpio < 0)
+ return err_fatal_gpio;
+
+ drv->err_fatal_irq = gpio_to_irq(err_fatal_gpio);
+ if (drv->err_fatal_irq < 0)
+ return drv->err_fatal_irq;
+
+ /* Get the GPIO pin for writing the outbound bits: add more as needed */
+ drv->force_stop_gpio = of_get_named_gpio(pdev->dev.of_node,
+ "qcom,gpio-force-stop", 0);
+ if (drv->force_stop_gpio < 0)
+ return drv->force_stop_gpio;
+
return pil_subsys_init(drv, pdev);
}
@@ -783,8 +799,6 @@
subsys_notif_unregister_notifier(drv->adsp_state_notifier,
&adsp_state_notifier_block);
- smsm_state_cb_deregister(SMSM_MODEM_STATE, SMSM_RESET,
- smsm_state_cb, drv);
subsys_unregister(drv->subsys);
destroy_ramdump_device(drv->smem_ramdump_dev);
destroy_ramdump_device(drv->ramdump_dev);
diff --git a/arch/arm/mach-msm/pil-venus.c b/arch/arm/mach-msm/pil-venus.c
index eb222e3..1fcd3ba 100644
--- a/arch/arm/mach-msm/pil-venus.c
+++ b/arch/arm/mach-msm/pil-venus.c
@@ -401,6 +401,12 @@
return pas_init_image(PAS_VIDC, metadata, size);
}
+static int pil_venus_mem_setup_trusted(struct pil_desc *pil, phys_addr_t addr,
+ size_t size)
+{
+ return pas_mem_setup(PAS_VIDC, addr, size);
+}
+
static int pil_venus_reset_trusted(struct pil_desc *pil)
{
int rc;
@@ -442,6 +448,7 @@
static struct pil_reset_ops pil_venus_ops_trusted = {
.init_image = pil_venus_init_image_trusted,
+ .mem_setup = pil_venus_mem_setup_trusted,
.auth_and_reset = pil_venus_reset_trusted,
.shutdown = pil_venus_shutdown_trusted,
.proxy_vote = pil_venus_make_proxy_vote,
diff --git a/arch/arm/mach-msm/platsmp.c b/arch/arm/mach-msm/platsmp.c
index 80f6014..b034525 100644
--- a/arch/arm/mach-msm/platsmp.c
+++ b/arch/arm/mach-msm/platsmp.c
@@ -1,7 +1,7 @@
/*
* Copyright (C) 2002 ARM Ltd.
* All Rights Reserved
- * Copyright (c) 2010-2012, Code Aurora Forum. 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 as
@@ -263,7 +263,7 @@
pr_debug("Starting secondary CPU %d\n", cpu);
if (per_cpu(cold_boot_done, cpu) == false) {
- if (machine_is_msm8226_sim() || machine_is_msm8910_sim())
+ if (machine_is_msm8226_sim() || machine_is_msm8610_sim())
release_secondary_sim(0xf9088000, cpu);
per_cpu(cold_boot_done, cpu) = true;
diff --git a/arch/arm/mach-msm/pm-8x60.c b/arch/arm/mach-msm/pm-8x60.c
index b42ad94..af6f8af 100644
--- a/arch/arm/mach-msm/pm-8x60.c
+++ b/arch/arm/mach-msm/pm-8x60.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
@@ -118,6 +118,7 @@
static struct msm_pm_init_data_type msm_pm_init_data;
static struct hrtimer pm_hrtimer;
static struct msm_pm_sleep_ops pm_sleep_ops;
+static bool msm_pm_ldo_retention_enabled = true;
/*
* Write out the attribute.
*/
@@ -474,8 +475,6 @@
}
}
-static void *msm_pm_idle_rs_limits;
-
static void msm_pm_swfi(void)
{
msm_pm_config_hw_before_swfi();
@@ -774,12 +773,13 @@
}
}
-int msm_pm_idle_prepare(struct cpuidle_device *dev,
- struct cpuidle_driver *drv, int index)
+static int msm_pm_idle_prepare(struct cpuidle_device *dev,
+ struct cpuidle_driver *drv, int index,
+ void **msm_pm_idle_rs_limits)
{
int i;
unsigned int power_usage = -1;
- int ret = 0;
+ int ret = MSM_PM_SLEEP_MODE_NOT_SELECTED;
uint32_t modified_time_us = 0;
struct msm_pm_time_params time_param;
@@ -802,7 +802,6 @@
struct cpuidle_state_usage *st_usage = &dev->states_usage[i];
enum msm_pm_sleep_mode mode;
bool allow;
- void *rs_limits = NULL;
uint32_t power;
int idx;
@@ -820,6 +819,14 @@
}
/* fall through */
case MSM_PM_SLEEP_MODE_RETENTION:
+ /*
+ * The Krait BHS regulator doesn't have enough head
+ * room to drive the retention voltage on LDO and so
+ * has disabled retention
+ */
+ if (!msm_pm_ldo_retention_enabled)
+ break;
+
if (!allow)
break;
@@ -841,17 +848,20 @@
/* fall through */
if (pm_sleep_ops.lowest_limits)
- rs_limits = pm_sleep_ops.lowest_limits(true,
- mode, &time_param, &power);
+ *msm_pm_idle_rs_limits =
+ pm_sleep_ops.lowest_limits(
+ true, mode,
+ &time_param, &power);
if (MSM_PM_DEBUG_IDLE & msm_pm_debug_mask)
pr_info("CPU%u: %s: %s, latency %uus, "
"sleep %uus, limit %p\n",
dev->cpu, __func__, state->desc,
time_param.latency_us,
- time_param.sleep_us, rs_limits);
+ time_param.sleep_us,
+ *msm_pm_idle_rs_limits);
- if (!rs_limits)
+ if (!*msm_pm_idle_rs_limits)
allow = false;
break;
@@ -871,8 +881,6 @@
ret = mode;
}
- if (MSM_PM_SLEEP_MODE_POWER_COLLAPSE == mode)
- msm_pm_idle_rs_limits = rs_limits;
}
}
@@ -886,11 +894,27 @@
return ret;
}
-int msm_pm_idle_enter(enum msm_pm_sleep_mode sleep_mode)
+enum msm_pm_sleep_mode msm_pm_idle_enter(struct cpuidle_device *dev,
+ struct cpuidle_driver *drv, int index)
{
int64_t time;
- int exit_stat;
bool collapsed = 1;
+ int exit_stat = -1;
+ enum msm_pm_sleep_mode sleep_mode;
+ void *msm_pm_idle_rs_limits = NULL;
+ int sleep_delay = 1;
+ int ret = -ENODEV;
+ int64_t timer_expiration = 0;
+ int notify_rpm = false;
+ bool timer_halted = false;
+
+ sleep_mode = msm_pm_idle_prepare(dev, drv, index,
+ &msm_pm_idle_rs_limits);
+
+ if (!msm_pm_idle_rs_limits) {
+ sleep_mode = MSM_PM_SLEEP_MODE_NOT_SELECTED;
+ goto cpuidle_enter_bail;
+ }
if (MSM_PM_DEBUG_IDLE & msm_pm_debug_mask)
pr_info("CPU%u: %s: mode %d\n",
@@ -898,71 +922,80 @@
time = ktime_to_ns(ktime_get());
- switch (sleep_mode) {
- case MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT:
- msm_pm_swfi();
- exit_stat = MSM_PM_STAT_IDLE_WFI;
- break;
-
- case MSM_PM_SLEEP_MODE_RETENTION:
- msm_pm_retention();
- exit_stat = MSM_PM_STAT_RETENTION;
- break;
-
- case MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE:
- collapsed = msm_pm_power_collapse_standalone(true);
- exit_stat = MSM_PM_STAT_IDLE_STANDALONE_POWER_COLLAPSE;
- break;
-
- case MSM_PM_SLEEP_MODE_POWER_COLLAPSE: {
- int64_t timer_expiration = 0;
- bool timer_halted = false;
- uint32_t sleep_delay;
- int ret = -ENODEV;
- int notify_rpm =
- (sleep_mode == MSM_PM_SLEEP_MODE_POWER_COLLAPSE);
+ if (sleep_mode == MSM_PM_SLEEP_MODE_POWER_COLLAPSE) {
+ notify_rpm = true;
timer_expiration = msm_pm_timer_enter_idle();
sleep_delay = (uint32_t) msm_pm_convert_and_cap_time(
timer_expiration, MSM_PM_SLEEP_TICK_LIMIT);
if (sleep_delay == 0) /* 0 would mean infinite time */
sleep_delay = 1;
+ }
- if (MSM_PM_DEBUG_IDLE_CLK & msm_pm_debug_mask)
- clock_debug_print_enabled();
+ if (pm_sleep_ops.enter_sleep)
+ ret = pm_sleep_ops.enter_sleep(sleep_delay,
+ msm_pm_idle_rs_limits,
+ true, notify_rpm);
+ if (!ret) {
- if (pm_sleep_ops.enter_sleep)
- ret = pm_sleep_ops.enter_sleep(sleep_delay,
- msm_pm_idle_rs_limits,
- true, notify_rpm);
- if (!ret) {
+ switch (sleep_mode) {
+ case MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT:
+ msm_pm_swfi();
+ exit_stat = MSM_PM_STAT_IDLE_WFI;
+ break;
+
+ case MSM_PM_SLEEP_MODE_RETENTION:
+ msm_pm_retention();
+ exit_stat = MSM_PM_STAT_RETENTION;
+ break;
+
+ case MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE:
+ collapsed = msm_pm_power_collapse_standalone(true);
+ exit_stat = MSM_PM_STAT_IDLE_STANDALONE_POWER_COLLAPSE;
+ break;
+
+ case MSM_PM_SLEEP_MODE_POWER_COLLAPSE:
+ if (MSM_PM_DEBUG_IDLE_CLK & msm_pm_debug_mask)
+ clock_debug_print_enabled();
+
collapsed = msm_pm_power_collapse(true);
timer_halted = true;
- if (pm_sleep_ops.exit_sleep)
- pm_sleep_ops.exit_sleep(msm_pm_idle_rs_limits,
- true, notify_rpm, collapsed);
+ exit_stat = MSM_PM_STAT_IDLE_POWER_COLLAPSE;
+ msm_pm_timer_exit_idle(timer_halted);
+ break;
+
+ case MSM_PM_SLEEP_MODE_NOT_SELECTED:
+ goto cpuidle_enter_bail;
+ break;
+
+ default:
+ __WARN();
+ goto cpuidle_enter_bail;
+ break;
}
+ if (pm_sleep_ops.exit_sleep)
+ pm_sleep_ops.exit_sleep(msm_pm_idle_rs_limits,
+ true, notify_rpm, collapsed);
+
+ time = ktime_to_ns(ktime_get()) - time;
+ msm_pm_ftrace_lpm_exit(smp_processor_id(), sleep_mode,
+ collapsed);
+ if (exit_stat >= 0)
+ msm_pm_add_stat(exit_stat, time);
+ do_div(time, 1000);
+ dev->last_residency = (int) time;
+ return sleep_mode;
+
+ } else if (sleep_mode == MSM_PM_SLEEP_MODE_POWER_COLLAPSE) {
msm_pm_timer_exit_idle(timer_halted);
- exit_stat = MSM_PM_STAT_IDLE_POWER_COLLAPSE;
- break;
- }
-
- default:
- __WARN();
- goto cpuidle_enter_bail;
- }
-
- time = ktime_to_ns(ktime_get()) - time;
- msm_pm_add_stat(exit_stat, time);
- msm_pm_ftrace_lpm_exit(smp_processor_id(), sleep_mode,
- collapsed);
-
- do_div(time, 1000);
- return (int) time;
+ sleep_mode = MSM_PM_SLEEP_MODE_NOT_SELECTED;
+ } else
+ sleep_mode = MSM_PM_SLEEP_MODE_NOT_SELECTED;
cpuidle_enter_bail:
- return 0;
+ dev->last_residency = 0;
+ return sleep_mode;
}
void msm_pm_cpu_enter_lowpower(unsigned int cpu)
@@ -990,6 +1023,34 @@
msm_pm_swfi();
}
+static void msm_pm_ack_retention_disable(void *data)
+{
+ /*
+ * This is a NULL function to ensure that the core has woken up
+ * and is safe to disable retention.
+ */
+}
+/**
+ * msm_pm_enable_retention() - Disable/Enable retention on all cores
+ * @enable: Enable/Disable retention
+ *
+ */
+void msm_pm_enable_retention(bool enable)
+{
+ msm_pm_ldo_retention_enabled = enable;
+ /*
+ * If retention is being disabled, wakeup all online core to ensure
+ * that it isn't executing retention. Offlined cores need not be woken
+ * up as they enter the deepest sleep mode, namely RPM assited power
+ * collapse
+ */
+ if (!enable)
+ smp_call_function_many(cpu_online_mask,
+ msm_pm_ack_retention_disable,
+ NULL, true);
+}
+EXPORT_SYMBOL(msm_pm_enable_retention);
+
static int msm_pm_enter(suspend_state_t state)
{
bool allow[MSM_PM_SLEEP_MODE_NR];
diff --git a/arch/arm/mach-msm/pm-data.c b/arch/arm/mach-msm/pm-data.c
index 6f4743f..ccc2519 100644
--- a/arch/arm/mach-msm/pm-data.c
+++ b/arch/arm/mach-msm/pm-data.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. 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
@@ -30,7 +30,7 @@
[MSM_PM_MODE(0, MSM_PM_SLEEP_MODE_RETENTION)] = {
.idle_supported = 1,
- .suspend_supported = 1,
+ .suspend_supported = 0,
.idle_enabled = 0,
.suspend_enabled = 0,
},
diff --git a/arch/arm/mach-msm/pm.h b/arch/arm/mach-msm/pm.h
index bd61feb..43bb7de 100644
--- a/arch/arm/mach-msm/pm.h
+++ b/arch/arm/mach-msm/pm.h
@@ -1,7 +1,7 @@
/* arch/arm/mach-msm/pm.h
*
* Copyright (C) 2007 Google, Inc.
- * Copyright (c) 2009-2012, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2009-2013, The Linux Foundation. All rights reserved.
* Author: San Mehat <san@android.com>
*
* This software is licensed under the terms of the GNU General Public
@@ -52,7 +52,8 @@
MSM_PM_SLEEP_MODE_RETENTION = MSM_PM_SLEEP_MODE_APPS_SLEEP,
MSM_PM_SLEEP_MODE_POWER_COLLAPSE_SUSPEND = 5,
MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN = 6,
- MSM_PM_SLEEP_MODE_NR
+ MSM_PM_SLEEP_MODE_NR = 7,
+ MSM_PM_SLEEP_MODE_NOT_SELECTED,
};
#define MSM_PM_MODE(cpu, mode_nr) ((cpu) * MSM_PM_SLEEP_MODE_NR + (mode_nr))
@@ -107,12 +108,12 @@
};
void msm_pm_set_platform_data(struct msm_pm_platform_data *data, int count);
-int msm_pm_idle_prepare(struct cpuidle_device *dev,
+enum msm_pm_sleep_mode msm_pm_idle_enter(struct cpuidle_device *dev,
struct cpuidle_driver *drv, int index);
void msm_pm_set_irq_extns(struct msm_pm_irq_calls *irq_calls);
-int msm_pm_idle_enter(enum msm_pm_sleep_mode sleep_mode);
void msm_pm_cpu_enter_lowpower(unsigned int cpu);
void __init msm_pm_set_tz_retention_flag(unsigned int flag);
+void msm_pm_enable_retention(bool enable);
#ifdef CONFIG_MSM_PM8X60
void msm_pm_set_rpm_wakeup_irq(unsigned int irq);
diff --git a/arch/arm/mach-msm/pmu.c b/arch/arm/mach-msm/pmu.c
index cb191fc..21f65f8 100644
--- a/arch/arm/mach-msm/pmu.c
+++ b/arch/arm/mach-msm/pmu.c
@@ -181,7 +181,7 @@
* and point to the appropriate 'struct resource'.
*/
#ifdef CONFIG_ARCH_MSM8625
- if (cpu_is_msm8625()) {
+ if (cpu_is_msm8625() || cpu_is_msm8625q()) {
pmu_devices[0] = &msm8625_cpu_pmu_device;
pmu_devices[1] = &msm8625_l2_pmu_device;
msm8625_cpu_pmu_device.dev.platform_data = &multicore_data;
diff --git a/arch/arm/mach-msm/qdsp5/audmgr.c b/arch/arm/mach-msm/qdsp5/audmgr.c
index f2a84aa..fb51240 100644
--- a/arch/arm/mach-msm/qdsp5/audmgr.c
+++ b/arch/arm/mach-msm/qdsp5/audmgr.c
@@ -158,8 +158,10 @@
return;
if (am->state != STATE_ENABLED)
am->state = STATE_ENABLED;
- if (!amg->cad)
+ if (!amg->cad) {
+ wake_up(&am->wait);
break;
+ }
if (am->evt.session_info == SESSION_PLAYBACK &&
am->evt.dev_type.rx_device != amg->rx_device) {
diff --git a/arch/arm/mach-msm/qdsp6v2/Makefile b/arch/arm/mach-msm/qdsp6v2/Makefile
index 08a6de6..34d336e 100644
--- a/arch/arm/mach-msm/qdsp6v2/Makefile
+++ b/arch/arm/mach-msm/qdsp6v2/Makefile
@@ -13,17 +13,18 @@
endif
obj-$(CONFIG_MSM_QDSP6_APR) += apr.o apr_v1.o apr_tal.o q6core.o dsp_debug.o
obj-$(CONFIG_MSM_QDSP6_APRV2) += apr.o apr_v2.o apr_tal.o q6core.o dsp_debug.o
-obj-y += audio_acdb.o
ifdef CONFIG_ARCH_MSM9615
+obj-y += audio_acdb.o
obj-y += rtac.o
endif
obj-$(CONFIG_MSM_QDSP6_CODECS) += aac_in.o qcelp_in.o evrc_in.o amrnb_in.o audio_utils.o
obj-$(CONFIG_MSM_QDSP6_CODECS) += audio_wma.o audio_wmapro.o audio_aac.o audio_multi_aac.o audio_utils_aio.o
-obj-$(CONFIG_MSM_QDSP6_CODECS) += rtac.o q6audio_v1.o q6audio_v1_aio.o
+obj-$(CONFIG_MSM_QDSP6_CODECS) += audio_acdb.o rtac.o q6audio_v1.o q6audio_v1_aio.o
obj-$(CONFIG_MSM_QDSP6_CODECS) += audio_mp3.o audio_amrnb.o audio_amrwb.o audio_amrwbplus.o audio_evrc.o audio_qcelp.o amrwb_in.o
obj-$(CONFIG_MSM_QDSP6V2_CODECS) += aac_in.o qcelp_in.o evrc_in.o amrnb_in.o audio_utils.o
obj-$(CONFIG_MSM_QDSP6V2_CODECS) += audio_wma.o audio_wmapro.o audio_aac.o audio_multi_aac.o audio_utils_aio.o
-obj-$(CONFIG_MSM_QDSP6V2_CODECS) += rtac_v2.o q6audio_v2.o q6audio_v2_aio.o
+obj-$(CONFIG_MSM_QDSP6V2_CODECS) += q6audio_v2.o q6audio_v2_aio.o
obj-$(CONFIG_MSM_QDSP6V2_CODECS) += audio_mp3.o audio_amrnb.o audio_amrwb.o audio_evrc.o audio_qcelp.o amrwb_in.o
obj-$(CONFIG_MSM_ADSP_LOADER) += adsp-loader.o
obj-$(CONFIG_MSM_ULTRASOUND_A) += ultrasound/version_a/
+obj-$(CONFIG_MSM_ULTRASOUND_B) += ultrasound/version_b/
diff --git a/arch/arm/mach-msm/qdsp6v2/apr_tal.c b/arch/arm/mach-msm/qdsp6v2/apr_tal.c
index 03f0513..d734739 100644
--- a/arch/arm/mach-msm/qdsp6v2/apr_tal.c
+++ b/arch/arm/mach-msm/qdsp6v2/apr_tal.c
@@ -133,12 +133,12 @@
spin_unlock_irqrestore(&apr_ch->lock, flags);
break;
case SMD_EVENT_OPEN:
- pr_info("apr_tal: SMD_EVENT_OPEN\n");
+ pr_debug("apr_tal: SMD_EVENT_OPEN\n");
apr_ch->smd_state = 1;
wake_up(&apr_ch->wait);
break;
case SMD_EVENT_CLOSE:
- pr_info("apr_tal: SMD_EVENT_CLOSE\n");
+ pr_debug("apr_tal: SMD_EVENT_CLOSE\n");
break;
}
}
diff --git a/arch/arm/mach-msm/qdsp6v2/apr_v2.c b/arch/arm/mach-msm/qdsp6v2/apr_v2.c
index ed494e4..f554cf3 100644
--- a/arch/arm/mach-msm/qdsp6v2/apr_v2.c
+++ b/arch/arm/mach-msm/qdsp6v2/apr_v2.c
@@ -50,16 +50,16 @@
pr_err("%s: adsp not up\n", __func__);
return NULL;
}
- pr_info("%s: adsp Up\n", __func__);
+ pr_debug("%s: adsp Up\n", __func__);
} else if ((dest_id == APR_DEST_MODEM) &&
(apr_get_modem_state() == APR_SUBSYS_DOWN)) {
- pr_info("%s: Wait for modem to bootup\n", __func__);
+ pr_debug("%s: Wait for modem to bootup\n", __func__);
rc = apr_wait_for_device_up(dest_id);
if (rc == 0) {
pr_err("%s: Modem is not Up\n", __func__);
return NULL;
}
- pr_info("%s: modem Up\n", __func__);
+ pr_debug("%s: modem Up\n", __func__);
}
if (apr_get_svc(svc_name, dest_id, &client_id, &svc_idx, &svc_id)) {
diff --git a/arch/arm/mach-msm/qdsp6v2/audio_aac.c b/arch/arm/mach-msm/qdsp6v2/audio_aac.c
index 44ab611..2a8d5c8 100644
--- a/arch/arm/mach-msm/qdsp6v2/audio_aac.c
+++ b/arch/arm/mach-msm/qdsp6v2/audio_aac.c
@@ -2,7 +2,7 @@
*
* Copyright (C) 2008 Google, Inc.
* Copyright (C) 2008 HTC Corporation
- * Copyright (c) 2010-2012, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2010-2013, The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@@ -135,10 +135,9 @@
} else {
uint16_t sce_left = 1, sce_right = 2;
aac_config = audio->codec_cfg;
- if ((aac_config->dual_mono_mode <
- AUDIO_AAC_DUAL_MONO_PL_PR) ||
- (aac_config->dual_mono_mode >
- AUDIO_AAC_DUAL_MONO_PL_SR)) {
+ /* PL_PR is 0 only need to check PL_SR */
+ if (aac_config->dual_mono_mode >
+ AUDIO_AAC_DUAL_MONO_PL_SR) {
pr_err("%s:AUDIO_SET_AAC_CONFIG: Invalid"
"dual_mono mode =%d\n", __func__,
aac_config->dual_mono_mode);
diff --git a/arch/arm/mach-msm/qdsp6v2/audio_acdb.c b/arch/arm/mach-msm/qdsp6v2/audio_acdb.c
index cad845f..ea22c12 100644
--- a/arch/arm/mach-msm/qdsp6v2/audio_acdb.c
+++ b/arch/arm/mach-msm/qdsp6v2/audio_acdb.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2012, Code Aurora Forum. 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
@@ -64,15 +64,6 @@
atomic_t vocstrm_total_cal_size;
atomic_t vocvol_total_cal_size;
- /* Voice Column data */
- struct acdb_atomic_cal_block vocproc_col_cal[MAX_VOCPROC_TYPES];
- uint32_t *col_data[MAX_VOCPROC_TYPES];
-
- /* VocProc dev cfg cal*/
- struct acdb_atomic_cal_block vocproc_dev_cal[MAX_NETWORKS];
- atomic_t vocproc_dev_cal_size;
- atomic_t vocproc_dev_total_cal_size;
-
/* AFE cal */
struct acdb_atomic_cal_block afe_cal[MAX_AUDPROC_TYPES];
@@ -203,45 +194,6 @@
atomic_read(&acdb_data.vocvol_total_cal_size);
}
-void get_voice_col_data(uint32_t vocproc_type,
- struct acdb_cal_block *cal_block)
-{
- if (cal_block == NULL) {
- pr_err("ACDB=> NULL pointer sent to %s\n", __func__);
- goto done;
- }
-
- cal_block->cal_kvaddr = atomic_read(&acdb_data.
- vocproc_col_cal[vocproc_type].cal_kvaddr);
- cal_block->cal_paddr = atomic_read(&acdb_data.
- vocproc_col_cal[vocproc_type].cal_paddr);
- cal_block->cal_size = atomic_read(&acdb_data.
- vocproc_col_cal[vocproc_type].cal_size);
-done:
- return;
-}
-
-void store_voice_col_data(uint32_t vocproc_type, uint32_t cal_size,
- uint32_t *cal_data)
-{
- if (cal_size > MAX_COL_SIZE) {
- pr_err("%s: col size is to big %d\n", __func__,
- cal_size);
- goto done;
- }
- if (copy_from_user(acdb_data.col_data[vocproc_type],
- (void *)((uint8_t *)cal_data + sizeof(cal_size)),
- cal_size)) {
- pr_err("%s: fail to copy col size %d\n",
- __func__, cal_size);
- goto done;
- }
- atomic_set(&acdb_data.vocproc_col_cal[vocproc_type].cal_size,
- cal_size);
-done:
- return;
-}
-
void get_anc_cal(struct acdb_cal_block *cal_block)
{
pr_debug("%s\n", __func__);
@@ -482,7 +434,7 @@
return;
}
-void store_vocproc_dev_cfg_cal(int32_t len, struct cal_block *cal_blocks)
+void store_vocproc_cal(int32_t len, struct cal_block *cal_blocks)
{
int i;
pr_debug("%s\n", __func__);
@@ -493,57 +445,6 @@
goto done;
}
- atomic_set(&acdb_data.vocproc_dev_total_cal_size, 0);
- for (i = 0; i < len; i++) {
- if (cal_blocks[i].cal_offset >
- atomic64_read(&acdb_data.mem_len)) {
- pr_err("%s: offset %d is > mem_len %ld\n",
- __func__, cal_blocks[i].cal_offset,
- (long)atomic64_read(&acdb_data.mem_len));
- atomic_set(&acdb_data.vocproc_dev_cal[i].cal_size, 0);
- } else {
- atomic_add(cal_blocks[i].cal_size,
- &acdb_data.vocproc_dev_total_cal_size);
- atomic_set(&acdb_data.vocproc_dev_cal[i].cal_size,
- cal_blocks[i].cal_size);
- atomic_set(&acdb_data.vocproc_dev_cal[i].cal_paddr,
- cal_blocks[i].cal_offset +
- atomic64_read(&acdb_data.paddr));
- atomic_set(&acdb_data.vocproc_dev_cal[i].cal_kvaddr,
- cal_blocks[i].cal_offset +
- atomic64_read(&acdb_data.kvaddr));
- }
- }
- atomic_set(&acdb_data.vocproc_dev_cal_size, len);
-done:
- return;
-}
-
-void get_vocproc_dev_cfg_cal(struct acdb_cal_block *cal_block)
-{
- pr_debug("%s\n", __func__);
-
- cal_block->cal_kvaddr =
- atomic_read(&acdb_data.vocproc_dev_cal[0].cal_kvaddr);
- cal_block->cal_paddr =
- atomic_read(&acdb_data.vocproc_dev_cal[0].cal_paddr);
- cal_block->cal_size =
- atomic_read(&acdb_data.vocproc_dev_total_cal_size);
-}
-
-
-
-void store_vocproc_cal(int32_t len, struct cal_block *cal_blocks)
-{
- int i;
- pr_debug("%s\n", __func__);
-
- if (len > MAX_NETWORKS) {
- pr_err("%s: Calibration sent for %d networks, only %d are "
- "supported!\n", __func__, len, MAX_NETWORKS);
- goto done;
- }
-
atomic_set(&acdb_data.vocproc_total_cal_size, 0);
for (i = 0; i < len; i++) {
if (cal_blocks[i].cal_offset >
@@ -591,8 +492,8 @@
pr_debug("%s\n", __func__);
if (len > MAX_NETWORKS) {
- pr_err("%s: Calibration sent for %d networks, only %d are "
- "supported!\n", __func__, len, MAX_NETWORKS);
+ pr_err("%s: Calibration sent for %d networks, only %d are supported!\n",
+ __func__, len, MAX_NETWORKS);
goto done;
}
@@ -643,8 +544,8 @@
pr_debug("%s\n", __func__);
if (len > MAX_NETWORKS) {
- pr_err("%s: Calibration sent for %d networks, only %d are "
- "supported!\n", __func__, len, MAX_NETWORKS);
+ pr_err("%s: Calibration sent for %d networks, only %d are supported!\n",
+ __func__, len, MAX_NETWORKS);
goto done;
}
@@ -719,8 +620,7 @@
pr_debug("%s\n", __func__);
if (atomic64_read(&acdb_data.mem_len)) {
- pr_debug("%s: ACDB opened but memory allocated, "
- "using existing allocation!\n",
+ pr_debug("%s: ACDB opened but memory allocated, using existing allocation!\n",
__func__);
}
@@ -730,19 +630,12 @@
static int deregister_memory(void)
{
- int i;
-
if (atomic64_read(&acdb_data.mem_len)) {
mutex_lock(&acdb_data.acdb_mutex);
atomic64_set(&acdb_data.mem_len, 0);
atomic_set(&acdb_data.vocstrm_total_cal_size, 0);
atomic_set(&acdb_data.vocproc_total_cal_size, 0);
atomic_set(&acdb_data.vocvol_total_cal_size, 0);
-
- for (i = 0; i < MAX_VOCPROC_TYPES; i++) {
- kfree(acdb_data.col_data[i]);
- acdb_data.col_data[i] = NULL;
- }
ion_unmap_kernel(acdb_data.ion_client, acdb_data.ion_handle);
ion_free(acdb_data.ion_client, acdb_data.ion_handle);
ion_client_destroy(acdb_data.ion_client);
@@ -754,18 +647,12 @@
static int register_memory(void)
{
int result;
- int i;
unsigned long paddr;
void *kvptr;
unsigned long kvaddr;
unsigned long mem_len;
mutex_lock(&acdb_data.acdb_mutex);
- for (i = 0; i < MAX_VOCPROC_TYPES; i++) {
- acdb_data.col_data[i] = kmalloc(MAX_COL_SIZE, GFP_KERNEL);
- atomic_set(&acdb_data.vocproc_col_cal[i].cal_kvaddr,
- (uint32_t)acdb_data.col_data[i]);
- }
acdb_data.ion_client =
msm_ion_client_create(UINT_MAX, "audio_acdb_client");
@@ -803,8 +690,7 @@
atomic64_set(&acdb_data.mem_len, mem_len);
mutex_unlock(&acdb_data.acdb_mutex);
- pr_debug("%s done! paddr = 0x%lx, "
- "kvaddr = 0x%lx, len = x%lx\n",
+ pr_debug("%s done! paddr = 0x%lx, kvaddr = 0x%lx, len = x%lx\n",
__func__,
(long)atomic64_read(&acdb_data.paddr),
(long)atomic64_read(&acdb_data.kvaddr),
@@ -820,6 +706,7 @@
mutex_unlock(&acdb_data.acdb_mutex);
return result;
}
+
static long acdb_ioctl(struct file *f,
unsigned int cmd, unsigned long arg)
{
@@ -906,18 +793,6 @@
goto done;
}
- switch (cmd) {
- case AUDIO_SET_VOCPROC_COL_CAL:
- store_voice_col_data(VOCPROC_CAL, size, (uint32_t *)arg);
- goto done;
- case AUDIO_SET_VOCSTRM_COL_CAL:
- store_voice_col_data(VOCSTRM_CAL, size, (uint32_t *)arg);
- goto done;
- case AUDIO_SET_VOCVOL_COL_CAL:
- store_voice_col_data(VOCVOL_CAL, size, (uint32_t *)arg);
- goto done;
- }
-
if (copy_from_user(data, (void *)(arg + sizeof(size)), size)) {
pr_err("%s: fail to copy table size %d\n", __func__, size);
@@ -934,50 +809,50 @@
switch (cmd) {
case AUDIO_SET_AUDPROC_TX_CAL:
if (size > sizeof(struct cal_block))
- pr_err("%s: More Audproc Cal then expected, "
- "size received: %d\n", __func__, size);
+ pr_err("%s: More Audproc Cal then expected, size received: %d\n",
+ __func__, size);
store_audproc_cal(TX_CAL, (struct cal_block *)data);
break;
case AUDIO_SET_AUDPROC_RX_CAL:
if (size > sizeof(struct cal_block))
- pr_err("%s: More Audproc Cal then expected, "
- "size received: %d\n", __func__, size);
+ pr_err("%s: More Audproc Cal then expected, size received: %d\n",
+ __func__, size);
store_audproc_cal(RX_CAL, (struct cal_block *)data);
break;
case AUDIO_SET_AUDPROC_TX_STREAM_CAL:
if (size > sizeof(struct cal_block))
- pr_err("%s: More Audproc Cal then expected, "
- "size received: %d\n", __func__, size);
+ pr_err("%s: More Audproc Cal then expected, size received: %d\n",
+ __func__, size);
store_audstrm_cal(TX_CAL, (struct cal_block *)data);
break;
case AUDIO_SET_AUDPROC_RX_STREAM_CAL:
if (size > sizeof(struct cal_block))
- pr_err("%s: More Audproc Cal then expected, "
- "size received: %d\n", __func__, size);
+ pr_err("%s: More Audproc Cal then expected, size received: %d\n",
+ __func__, size);
store_audstrm_cal(RX_CAL, (struct cal_block *)data);
break;
case AUDIO_SET_AUDPROC_TX_VOL_CAL:
if (size > sizeof(struct cal_block))
- pr_err("%s: More Audproc Cal then expected, "
- "size received: %d\n", __func__, size);
+ pr_err("%s: More Audproc Cal then expected, size received: %d\n",
+ __func__, size);
store_audvol_cal(TX_CAL, (struct cal_block *)data);
break;
case AUDIO_SET_AUDPROC_RX_VOL_CAL:
if (size > sizeof(struct cal_block))
- pr_err("%s: More Audproc Cal then expected, "
- "size received: %d\n", __func__, size);
+ pr_err("%s: More Audproc Cal then expected, size received: %d\n",
+ __func__, size);
store_audvol_cal(RX_CAL, (struct cal_block *)data);
break;
case AUDIO_SET_AFE_TX_CAL:
if (size > sizeof(struct cal_block))
- pr_err("%s: More AFE Cal then expected, "
- "size received: %d\n", __func__, size);
+ pr_err("%s: More AFE Cal then expected, size received: %d\n",
+ __func__, size);
store_afe_cal(TX_CAL, (struct cal_block *)data);
break;
case AUDIO_SET_AFE_RX_CAL:
if (size > sizeof(struct cal_block))
- pr_err("%s: More AFE Cal then expected, "
- "size received: %d\n", __func__, size);
+ pr_err("%s: More AFE Cal then expected, size received: %d\n",
+ __func__, size);
store_afe_cal(RX_CAL, (struct cal_block *)data);
break;
case AUDIO_SET_VOCPROC_CAL:
@@ -992,14 +867,10 @@
store_vocvol_cal(size / sizeof(struct cal_block),
(struct cal_block *)data);
break;
- case AUDIO_SET_VOCPROC_DEV_CFG_CAL:
- store_vocproc_dev_cfg_cal(size / sizeof(struct cal_block),
- (struct cal_block *)data);
- break;
case AUDIO_SET_SIDETONE_CAL:
if (size > sizeof(struct sidetone_cal))
- pr_err("%s: More sidetone cal then expected, "
- "size received: %d\n", __func__, size);
+ pr_err("%s: More sidetone cal then expected, size received: %d\n",
+ __func__, size);
store_sidetone_cal((struct sidetone_cal *)data);
break;
case AUDIO_SET_ANC_CAL:
diff --git a/arch/arm/mach-msm/qdsp6v2/audio_multi_aac.c b/arch/arm/mach-msm/qdsp6v2/audio_multi_aac.c
index b53edd9..658c07b 100644
--- a/arch/arm/mach-msm/qdsp6v2/audio_multi_aac.c
+++ b/arch/arm/mach-msm/qdsp6v2/audio_multi_aac.c
@@ -2,7 +2,7 @@
*
* Copyright (C) 2008 Google, Inc.
* Copyright (C) 2008 HTC Corporation
- * Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@@ -147,10 +147,8 @@
} else {
uint16_t sce_left = 1, sce_right = 2;
aac_config = audio->codec_cfg;
- if ((aac_config->dual_mono_mode <
- AUDIO_AAC_DUAL_MONO_PL_PR) ||
- (aac_config->dual_mono_mode >
- AUDIO_AAC_DUAL_MONO_PL_SR)) {
+ if (aac_config->dual_mono_mode >
+ AUDIO_AAC_DUAL_MONO_PL_SR) {
pr_err("%s:AUDIO_SET_AAC_CONFIG: Invalid dual_mono mode =%d\n",
__func__, aac_config->dual_mono_mode);
} else {
diff --git a/arch/arm/mach-msm/qdsp6v2/audio_utils.c b/arch/arm/mach-msm/qdsp6v2/audio_utils.c
index 6a23e37..33bbac0 100644
--- a/arch/arm/mach-msm/qdsp6v2/audio_utils.c
+++ b/arch/arm/mach-msm/qdsp6v2/audio_utils.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2012, Code Aurora Forum. 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
@@ -19,7 +19,7 @@
#include <linux/wait.h>
#include <linux/dma-mapping.h>
#include <linux/slab.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include <asm/ioctls.h>
#include "audio_utils.h"
@@ -109,8 +109,8 @@
rc = q6asm_cmd(audio->ac, CMD_CLOSE);
if (rc < 0)
- pr_err("%s:session id %d: Failed to close the"
- "session rc=%d\n", __func__, audio->ac->session,
+ pr_err("%s:session id %d: Failed to close the session rc=%d\n",
+ __func__, audio->ac->session,
rc);
audio->stopped = 1;
memset(audio->out_frame_info, 0,
@@ -135,8 +135,8 @@
ALIGN_BUF_SIZE(audio->pcm_cfg.buffer_size),
audio->pcm_cfg.buffer_count);
if (rc < 0) {
- pr_err("%s:session id %d: Buffer Alloc"
- "failed\n", __func__,
+ pr_err("%s:session id %d: Buffer Alloc failed\n",
+ __func__,
audio->ac->session);
rc = -ENOMEM;
break;
@@ -172,8 +172,8 @@
ALIGN_BUF_SIZE(audio->pcm_cfg.buffer_size),
audio->pcm_cfg.buffer_count);
if (rc < 0) {
- pr_err("%s:session id %d: Buffer Alloc"
- "failed\n", __func__,
+ pr_err("%s:session id %d: Buffer Alloc failed\n",
+ __func__,
audio->ac->session);
rc = -ENOMEM;
break;
@@ -303,15 +303,15 @@
}
audio->buf_cfg.meta_info_enable = cfg.meta_info_enable;
audio->buf_cfg.frames_per_buf = cfg.frames_per_buf;
- pr_debug("%s:session id %d: Set-buf-cfg: meta[%d]"
- "framesperbuf[%d]\n", __func__,
+ pr_debug("%s:session id %d: Set-buf-cfg: meta[%d] framesperbuf[%d]\n",
+ __func__,
audio->ac->session, cfg.meta_info_enable,
cfg.frames_per_buf);
break;
}
case AUDIO_GET_BUF_CFG: {
- pr_debug("%s:session id %d: Get-buf-cfg: meta[%d]"
- "framesperbuf[%d]\n", __func__,
+ pr_debug("%s:session id %d: Get-buf-cfg: meta[%d] framesperbuf[%d]\n",
+ __func__,
audio->ac->session, audio->buf_cfg.meta_info_enable,
audio->buf_cfg.frames_per_buf);
@@ -334,8 +334,8 @@
break;
}
if (audio->feedback != NON_TUNNEL_MODE) {
- pr_err("%s:session id %d: Not sufficient permission to"
- "change the record mode\n", __func__,
+ pr_err("%s:session id %d: Not sufficient permission to change the record mode\n",
+ __func__,
audio->ac->session);
rc = -EACCES;
break;
@@ -399,15 +399,22 @@
audio->read_wait,
((atomic_read(&audio->out_count) > 0) ||
(audio->stopped) ||
- audio->rflush || audio->eos_rsp));
+ audio->rflush || audio->eos_rsp ||
+ audio->event_abort));
+
+ if (audio->event_abort) {
+ rc = -EIO;
+ break;
+ }
+
if (rc < 0)
break;
if ((audio->stopped && !(atomic_read(&audio->out_count))) ||
audio->rflush) {
- pr_debug("%s:session id %d: driver in stop state or"
- "flush,No more buf to read", __func__,
+ pr_debug("%s:session id %d: driver in stop state or flush,No more buf to read",
+ __func__,
audio->ac->session);
rc = 0;/* End of File */
break;
@@ -473,8 +480,8 @@
count -= bytes_to_copy;
buf += bytes_to_copy;
} else {
- pr_err("%s:session id %d: short read data[%p]"
- "bytesavail[%d]bytesrequest[%d]\n", __func__,
+ pr_err("%s:session id %d: short read data[%p] bytesavail[%d]bytesrequest[%d]\n",
+ __func__,
audio->ac->session,
data, size, count);
}
@@ -529,7 +536,13 @@
rc = wait_event_interruptible(audio->write_wait,
((atomic_read(&audio->in_count) > 0) ||
(audio->stopped) ||
- (audio->wflush)));
+ (audio->wflush) || (audio->event_abort)));
+
+ if (audio->event_abort) {
+ rc = -EIO;
+ break;
+ }
+
if (rc < 0)
break;
if (audio->stopped || audio->wflush) {
@@ -554,8 +567,8 @@
&nflags);
buf += mfield_size;
/* send the EOS and return */
- pr_debug("%s:session id %d: send EOS"
- "0x%8x\n", __func__,
+ pr_debug("%s:session id %d: send EOS 0x%8x\n",
+ __func__,
audio->ac->session, nflags);
break;
}
@@ -582,8 +595,8 @@
buf += mfield_size;
count -= mfield_size;
} else {
- pr_debug("%s:session id %d: continuous"
- "buffer\n", __func__, audio->ac->session);
+ pr_debug("%s:session id %d: continuous buffer\n",
+ __func__, audio->ac->session);
}
}
xfer = (count > (audio->pcm_cfg.buffer_size)) ?
@@ -603,8 +616,8 @@
buf += xfer;
}
mutex_unlock(&audio->write_lock);
- pr_debug("%s:session id %d: eos_condition 0x%8x buf[0x%x]"
- "start[0x%x]\n", __func__, audio->ac->session,
+ pr_debug("%s:session id %d: eos_condition 0x%8x buf[0x%x] start[0x%x]\n",
+ __func__, audio->ac->session,
nflags, (int) buf, (int) start);
if (nflags & AUD_EOS_SET) {
rc = q6asm_cmd(audio->ac, CMD_EOS);
diff --git a/arch/arm/mach-msm/qdsp6v2/audio_utils.h b/arch/arm/mach-msm/qdsp6v2/audio_utils.h
index df963f9..7209724 100644
--- a/arch/arm/mach-msm/qdsp6v2/audio_utils.h
+++ b/arch/arm/mach-msm/qdsp6v2/audio_utils.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2012, Code Aurora Forum. 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
@@ -30,13 +30,13 @@
struct timestamp {
unsigned long lowpart;
unsigned long highpart;
-} __attribute__ ((packed));
+} __packed;
struct meta_in {
unsigned short offset;
struct timestamp ntimestamp;
unsigned int nflags;
-} __attribute__ ((packed));
+} __packed;
struct meta_out_dsp {
u32 offset_to_frame;
@@ -45,12 +45,12 @@
u32 msw_ts;
u32 lsw_ts;
u32 nflags;
-} __attribute__ ((packed));
+} __packed;
struct meta_out {
unsigned char num_of_frames;
struct meta_out_dsp meta_out_dsp[];
-} __attribute__ ((packed));
+} __packed;
struct q6audio_in {
spinlock_t dsp_lock;
@@ -80,6 +80,7 @@
int opened;
int enabled;
int stopped;
+ int event_abort;
int feedback; /* Flag indicates whether used
in Non Tunnel mode */
int rflush;
diff --git a/arch/arm/mach-msm/qdsp6v2/audio_utils_aio.c b/arch/arm/mach-msm/qdsp6v2/audio_utils_aio.c
index 02eb383..fc0de3b 100644
--- a/arch/arm/mach-msm/qdsp6v2/audio_utils_aio.c
+++ b/arch/arm/mach-msm/qdsp6v2/audio_utils_aio.c
@@ -861,10 +861,9 @@
}
param.msw_ts = buf_node->meta_info.meta_in.ntimestamp.highpart;
param.lsw_ts = buf_node->meta_info.meta_in.ntimestamp.lowpart;
+ param.flags = buf_node->meta_info.meta_in.nflags;
/* If no meta_info enaled, indicate no time stamp valid */
- if (audio->buf_cfg.meta_info_enable)
- param.flags = 0;
- else
+ if (!audio->buf_cfg.meta_info_enable)
param.flags = 0xFF00;
if ((buf_node != NULL) &&
diff --git a/arch/arm/mach-msm/qdsp6v2/dsp_debug.c b/arch/arm/mach-msm/qdsp6v2/dsp_debug.c
index 3635fbd..26c8f75 100644
--- a/arch/arm/mach-msm/qdsp6v2/dsp_debug.c
+++ b/arch/arm/mach-msm/qdsp6v2/dsp_debug.c
@@ -38,12 +38,14 @@
void q6audio_dsp_not_responding(void)
{
+ int i;
+
if (cb_ptr)
cb_ptr(DSP_STATE_CRASHED);
if (atomic_add_return(1, &dsp_crash_count) != 1) {
pr_err("q6audio_dsp_not_responding() \
- parking additional crasher...\n");
- for (;;)
+ for (i = 0; i < 600; i++)
msleep(1000);
}
if (dsp_wait_count) {
diff --git a/arch/arm/mach-msm/qdsp6v2/evrc_in.c b/arch/arm/mach-msm/qdsp6v2/evrc_in.c
index b95d659..a785266 100644
--- a/arch/arm/mach-msm/qdsp6v2/evrc_in.c
+++ b/arch/arm/mach-msm/qdsp6v2/evrc_in.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2012, Code Aurora Forum. 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
@@ -20,7 +20,7 @@
#include <linux/dma-mapping.h>
#include <linux/slab.h>
#include <linux/msm_audio_qcp.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include <asm/ioctls.h>
#include "audio_utils.h"
@@ -64,8 +64,8 @@
enc_cfg->max_bit_rate, 0);
if (rc < 0) {
- pr_err("%s:session id %d: cmd evrc media format block"
- "failed\n", __func__, audio->ac->session);
+ pr_err("%s:session id %d: cmd evrc media format block failed\n",
+ __func__, audio->ac->session);
break;
}
if (audio->feedback == NON_TUNNEL_MODE) {
@@ -74,8 +74,8 @@
audio->pcm_cfg.channel_count);
if (rc < 0) {
- pr_err("%s:session id %d: media format block"
- "failed\n", __func__, audio->ac->session);
+ pr_err("%s:session id %d: media format block failed\n",
+ __func__, audio->ac->session);
break;
}
}
@@ -86,8 +86,8 @@
audio->enabled = 1;
} else {
audio->enabled = 0;
- pr_err("%s:session id %d: Audio Start procedure failed"
- "rc=%d\n", __func__, audio->ac->session, rc);
+ pr_err("%s:session id %d: Audio Start procedure failed rc=%d\n",
+ __func__, audio->ac->session, rc);
break;
}
while (cnt++ < audio->str_cfg.buffer_count)
@@ -102,8 +102,8 @@
audio->ac->session);
rc = audio_in_disable(audio);
if (rc < 0) {
- pr_err("%s:session id %d: Audio Stop procedure failed"
- "rc=%d\n", __func__, audio->ac->session, rc);
+ pr_err("%s:session id %d: Audio Stop procedure failed rc=%d\n",
+ __func__, audio->ac->session, rc);
break;
}
break;
@@ -143,8 +143,8 @@
}
enc_cfg->min_bit_rate = cfg.min_bit_rate;
enc_cfg->max_bit_rate = cfg.max_bit_rate;
- pr_debug("%s:session id %d: min_bit_rate= 0x%x"
- "max_bit_rate=0x%x\n", __func__,
+ pr_debug("%s:session id %d: min_bit_rate= 0x%x max_bit_rate=0x%x\n",
+ __func__,
audio->ac->session, enc_cfg->min_bit_rate,
enc_cfg->max_bit_rate);
break;
@@ -164,16 +164,16 @@
audio = kzalloc(sizeof(struct q6audio_in), GFP_KERNEL);
if (audio == NULL) {
- pr_err("%s: Could not allocate memory for evrc"
- "driver\n", __func__);
+ pr_err("%s: Could not allocate memory for evrc driver\n",
+ __func__);
return -ENOMEM;
}
/* Allocate memory for encoder config param */
audio->enc_cfg = kzalloc(sizeof(struct msm_audio_evrc_enc_config),
GFP_KERNEL);
if (audio->enc_cfg == NULL) {
- pr_err("%s:session id %d: Could not allocate memory for aac"
- "config param\n", __func__, audio->ac->session);
+ pr_err("%s:session id %d: Could not allocate memory for aac config param\n",
+ __func__, audio->ac->session);
kfree(audio);
return -ENOMEM;
}
@@ -200,13 +200,14 @@
audio->pcm_cfg.sample_rate = 8000;
audio->buf_cfg.meta_info_enable = 0x01;
audio->buf_cfg.frames_per_buf = 0x01;
+ audio->event_abort = 0;
audio->ac = q6asm_audio_client_alloc((app_cb)q6asm_in_cb,
(void *)audio);
if (!audio->ac) {
- pr_err("%s: Could not allocate memory for audio"
- "client\n", __func__);
+ pr_err("%s: Could not allocate memory for audio client\n",
+ __func__);
kfree(audio->enc_cfg);
kfree(audio);
return -ENOMEM;
@@ -239,8 +240,8 @@
/* register for tx overflow (valid for tunnel mode only) */
rc = q6asm_reg_tx_overflow(audio->ac, 0x01);
if (rc < 0) {
- pr_err("%s:session id %d: TX Overflow registration"
- "failed rc=%d\n", __func__,
+ pr_err("%s:session id %d: TX Overflow registration failed rc=%d\n",
+ __func__,
audio->ac->session, rc);
rc = -ENODEV;
goto fail;
diff --git a/arch/arm/mach-msm/qdsp6v2/q6audio_common.h b/arch/arm/mach-msm/qdsp6v2/q6audio_common.h
index 5ffffd2..de4e1f0 100644
--- a/arch/arm/mach-msm/qdsp6v2/q6audio_common.h
+++ b/arch/arm/mach-msm/qdsp6v2/q6audio_common.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2012-2013, Code Aurora Forum. 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
@@ -16,7 +16,7 @@
#define __Q6_AUDIO_COMMON_H__
#if defined(CONFIG_ARCH_MSM8974) || defined(CONFIG_ARCH_MSM9625) \
- || defined(CONFIG_ARCH_MSM8226)
+ || defined(CONFIG_ARCH_MSM8226) || defined(CONFIG_ARCH_MSM8610)
#include <sound/apr_audio-v2.h>
#include <sound/q6asm-v2.h>
diff --git a/arch/arm/mach-msm/qdsp6v2/q6audio_v2.c b/arch/arm/mach-msm/qdsp6v2/q6audio_v2.c
index 0db1ef4..7786fc0 100644
--- a/arch/arm/mach-msm/qdsp6v2/q6audio_v2.c
+++ b/arch/arm/mach-msm/qdsp6v2/q6audio_v2.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. 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
@@ -19,7 +19,7 @@
#include <linux/wait.h>
#include <linux/dma-mapping.h>
#include <linux/slab.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include <asm/ioctls.h>
#include "audio_utils.h"
@@ -51,6 +51,14 @@
pr_err("%s:session id %d: ASM_SESSION_EVENT_TX_OVERFLOW\n",
__func__, audio->ac->session);
break;
+ case RESET_EVENTS:
+ pr_debug("%s:received RESET EVENTS\n", __func__);
+ audio->enabled = 0;
+ audio->stopped = 1;
+ audio->event_abort = 1;
+ wake_up(&audio->read_wait);
+ wake_up(&audio->write_wait);
+ break;
default:
pr_debug("%s:session id %d: Ignore opcode[0x%x]\n", __func__,
audio->ac->session, opcode);
diff --git a/arch/arm/mach-msm/qdsp6v2/q6core.c b/arch/arm/mach-msm/qdsp6v2/q6core.c
index 9dd66e1..f23ba67 100644
--- a/arch/arm/mach-msm/qdsp6v2/q6core.c
+++ b/arch/arm/mach-msm/qdsp6v2/q6core.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2011, Code Aurora Forum. 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
@@ -253,8 +253,6 @@
int len;
static int t_len;
- if (count < 0)
- return 0;
len = count > 63 ? 63 : count;
if (copy_from_user(l_buf + 20 , buf, len)) {
pr_info("Unable to copy data from user space\n");
diff --git a/arch/arm/mach-msm/qdsp6v2/qcelp_in.c b/arch/arm/mach-msm/qdsp6v2/qcelp_in.c
index a48df39..3a5411e 100644
--- a/arch/arm/mach-msm/qdsp6v2/qcelp_in.c
+++ b/arch/arm/mach-msm/qdsp6v2/qcelp_in.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2012, Code Aurora Forum. 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
@@ -20,7 +20,7 @@
#include <linux/dma-mapping.h>
#include <linux/slab.h>
#include <linux/msm_audio_qcp.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include <asm/ioctls.h>
#include "audio_utils.h"
@@ -64,8 +64,8 @@
enc_cfg->max_bit_rate, 0, 0);
if (rc < 0) {
- pr_err("%s:session id %d: cmd qcelp media format block"
- "failed\n", __func__, audio->ac->session);
+ pr_err("%s:session id %d: cmd qcelp media format block failed\n",
+ __func__, audio->ac->session);
break;
}
if (audio->feedback == NON_TUNNEL_MODE) {
@@ -74,8 +74,8 @@
audio->pcm_cfg.channel_count);
if (rc < 0) {
- pr_err("%s:session id %d: media format block"
- "failed\n", __func__, audio->ac->session);
+ pr_err("%s:session id %d: media format block failed\n",
+ __func__, audio->ac->session);
break;
}
}
@@ -86,8 +86,8 @@
audio->enabled = 1;
} else {
audio->enabled = 0;
- pr_err("%s:session id %d: Audio Start procedure failed"
- "rc=%d\n", __func__, audio->ac->session, rc);
+ pr_err("%s:session id %d: Audio Start procedure failed rc=%d\n",
+ __func__, audio->ac->session, rc);
break;
}
while (cnt++ < audio->str_cfg.buffer_count)
@@ -102,8 +102,8 @@
audio->ac->session);
rc = audio_in_disable(audio);
if (rc < 0) {
- pr_err("%s:session id %d: Audio Stop procedure failed"
- "rc=%d\n", __func__, audio->ac->session,
+ pr_err("%s:session id %d: Audio Stop procedure failed rc=%d\n",
+ __func__, audio->ac->session,
rc);
break;
}
@@ -141,8 +141,8 @@
}
enc_cfg->min_bit_rate = cfg.min_bit_rate;
enc_cfg->max_bit_rate = cfg.max_bit_rate;
- pr_debug("%s:session id %d: min_bit_rate= 0x%x"
- "max_bit_rate=0x%x\n", __func__,
+ pr_debug("%s:session id %d: min_bit_rate= 0x%x max_bit_rate=0x%x\n",
+ __func__,
audio->ac->session, enc_cfg->min_bit_rate,
enc_cfg->max_bit_rate);
break;
@@ -162,16 +162,16 @@
audio = kzalloc(sizeof(struct q6audio_in), GFP_KERNEL);
if (audio == NULL) {
- pr_err("%s: Could not allocate memory for qcelp"
- "driver\n", __func__);
+ pr_err("%s: Could not allocate memory for qcelp driver\n",
+ __func__);
return -ENOMEM;
}
/* Allocate memory for encoder config param */
audio->enc_cfg = kzalloc(sizeof(struct msm_audio_qcelp_enc_config),
GFP_KERNEL);
if (audio->enc_cfg == NULL) {
- pr_err("%s:session id %d: Could not allocate memory for aac"
- "config param\n", __func__, audio->ac->session);
+ pr_err("%s:session id %d: Could not allocate memory for aac config param\n",
+ __func__, audio->ac->session);
kfree(audio);
return -ENOMEM;
}
@@ -199,13 +199,14 @@
audio->pcm_cfg.sample_rate = 8000;
audio->buf_cfg.meta_info_enable = 0x01;
audio->buf_cfg.frames_per_buf = 0x01;
+ audio->event_abort = 0;
audio->ac = q6asm_audio_client_alloc((app_cb)q6asm_in_cb,
(void *)audio);
if (!audio->ac) {
- pr_err("%s: Could not allocate memory for audio"
- "client\n", __func__);
+ pr_err("%s: Could not allocate memory for audio client\n",
+ __func__);
kfree(audio->enc_cfg);
kfree(audio);
return -ENOMEM;
@@ -238,8 +239,8 @@
/* register for tx overflow (valid for tunnel mode only) */
rc = q6asm_reg_tx_overflow(audio->ac, 0x01);
if (rc < 0) {
- pr_err("%s:session id %d: TX Overflow registration"
- "failed rc=%d\n", __func__, audio->ac->session, rc);
+ pr_err("%s:session id %d: TX Overflow registration failed rc=%d\n",
+ __func__, audio->ac->session, rc);
rc = -ENODEV;
goto fail;
}
diff --git a/arch/arm/mach-msm/qdsp6v2/ultrasound/q6usm.h b/arch/arm/mach-msm/qdsp6v2/ultrasound/q6usm.h
index c68ad68..bf47366 100644
--- a/arch/arm/mach-msm/qdsp6v2/ultrasound/q6usm.h
+++ b/arch/arm/mach-msm/qdsp6v2/ultrasound/q6usm.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, Code Aurora Forum. 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
@@ -62,7 +62,7 @@
struct mutex lock;
spinlock_t dsp_lock;
/* extended parameters, related to q6 variants */
- void *ext;
+ void *ext;
};
struct us_client {
diff --git a/arch/arm/mach-msm/qdsp6v2/ultrasound/version_b/Makefile b/arch/arm/mach-msm/qdsp6v2/ultrasound/version_b/Makefile
new file mode 100644
index 0000000..23de73f
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp6v2/ultrasound/version_b/Makefile
@@ -0,0 +1,2 @@
+obj-y += q6usm_b.o ../usf.o ../usfcdev.o
+ccflags-y := -I$(src)/.. -I$(src)/../..
diff --git a/arch/arm/mach-msm/qdsp6v2/ultrasound/version_b/q6usm_b.c b/arch/arm/mach-msm/qdsp6v2/ultrasound/version_b/q6usm_b.c
new file mode 100644
index 0000000..11b1405
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp6v2/ultrasound/version_b/q6usm_b.c
@@ -0,0 +1,1208 @@
+/* Copyright (c) 2012, 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/mutex.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/msm_audio.h>
+#include <sound/apr_audio.h>
+#include <mach/qdsp6v2/apr_us_b.h>
+#include "q6usm.h"
+
+#define ADSP_MEMORY_MAP_SHMEM8_4K_POOL 3
+
+#define MEM_4K_OFFSET 4095
+#define MEM_4K_MASK 0xfffff000
+
+#define SESSION_MAX 0x02 /* aDSP:USM limit */
+
+#define READDONE_IDX_STATUS 0
+
+#define WRITEDONE_IDX_STATUS 0
+
+/* Standard timeout in the asynchronous ops */
+#define Q6USM_TIMEOUT_JIFFIES (1*HZ) /* 1 sec */
+
+static DEFINE_MUTEX(session_lock);
+
+static struct us_client *session[SESSION_MAX];
+static int32_t q6usm_mmapcallback(struct apr_client_data *data, void *priv);
+static int32_t q6usm_callback(struct apr_client_data *data, void *priv);
+static void q6usm_add_hdr(struct us_client *usc, struct apr_hdr *hdr,
+ uint32_t pkt_size, bool cmd_flg);
+
+struct usm_mmap {
+ atomic_t ref_cnt;
+ atomic_t cmd_state;
+ wait_queue_head_t cmd_wait;
+ void *apr;
+ int mem_handle;
+};
+
+static struct usm_mmap this_mmap;
+
+static void q6usm_add_mmaphdr(struct us_client *usc, struct apr_hdr *hdr,
+ uint32_t pkt_size, bool cmd_flg, u32 token)
+{
+ hdr->hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, \
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ hdr->src_port = 0;
+ hdr->dest_port = 0;
+ if (cmd_flg) {
+ hdr->token = token;
+ atomic_set(&this_mmap.cmd_state, 1);
+ }
+ hdr->pkt_size = pkt_size;
+ return;
+}
+
+static int q6usm_memory_map(struct us_client *usc, uint32_t buf_add, int dir,
+ uint32_t bufsz, uint32_t bufcnt)
+{
+ struct usm_cmd_memory_map_region mem_region_map;
+ int rc = 0;
+
+ if ((usc == NULL) || (usc->apr == NULL) || (this_mmap.apr == NULL)) {
+ pr_err("%s: APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ q6usm_add_mmaphdr(usc, &mem_region_map.hdr,
+ sizeof(struct usm_cmd_memory_map_region), true,
+ ((usc->session << 8) | dir));
+
+ mem_region_map.hdr.opcode = USM_CMD_SHARED_MEM_MAP_REGION;
+ mem_region_map.mempool_id = ADSP_MEMORY_MAP_SHMEM8_4K_POOL;
+
+ mem_region_map.num_regions = 1;
+ mem_region_map.flags = 0;
+
+ mem_region_map.shm_addr_lsw = buf_add;
+ mem_region_map.shm_addr_msw = 0;
+ mem_region_map.mem_size_bytes = bufsz * bufcnt;
+
+ rc = apr_send_pkt(this_mmap.apr, (uint32_t *) &mem_region_map);
+ if (rc < 0) {
+ pr_err("%s: mem_map op[0x%x]rc[%d]\n",
+ __func__, mem_region_map.hdr.opcode, rc);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+
+ rc = wait_event_timeout(this_mmap.cmd_wait,
+ (atomic_read(&this_mmap.cmd_state) == 0),
+ Q6USM_TIMEOUT_JIFFIES);
+ if (!rc) {
+ rc = -ETIME;
+ pr_err("%s: timeout. waited for memory_map\n", __func__);
+ } else {
+ struct us_port_data *port = &usc->port[dir];
+
+ *((uint32_t *)(port->ext)) = this_mmap.mem_handle;
+ rc = 0;
+ }
+fail_cmd:
+ return rc;
+}
+
+int q6usm_memory_unmap(struct us_client *usc, uint32_t buf_add, int dir)
+{
+ struct usm_cmd_memory_unmap_region mem_unmap;
+ struct us_port_data *port = &usc->port[dir];
+ int rc = 0;
+
+ if ((usc == NULL) || (usc->apr == NULL) || (this_mmap.apr == NULL)) {
+ pr_err("%s: APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ port = &usc->port[dir];
+ q6usm_add_mmaphdr(usc, &mem_unmap.hdr,
+ sizeof(struct usm_cmd_memory_unmap_region), true,
+ ((usc->session << 8) | dir));
+ mem_unmap.hdr.opcode = USM_CMD_SHARED_MEM_UNMAP_REGION;
+ mem_unmap.mem_map_handle = *((uint32_t *)(port->ext));
+
+ rc = apr_send_pkt(this_mmap.apr, (uint32_t *) &mem_unmap);
+ if (rc < 0) {
+ pr_err("%s: mem_unmap op[0x%x] rc[%d]\n",
+ __func__, mem_unmap.hdr.opcode, rc);
+ goto fail_cmd;
+ }
+
+ rc = wait_event_timeout(this_mmap.cmd_wait,
+ (atomic_read(&this_mmap.cmd_state) == 0),
+ Q6USM_TIMEOUT_JIFFIES);
+ if (!rc) {
+ rc = -ETIME;
+ pr_err("%s: timeout. waited for memory_unmap\n", __func__);
+ } else
+ rc = 0;
+fail_cmd:
+ return rc;
+}
+
+static int q6usm_session_alloc(struct us_client *usc)
+{
+ int ind = 0;
+
+ mutex_lock(&session_lock);
+ for (ind = 0; ind < SESSION_MAX; ++ind) {
+ if (!session[ind]) {
+ session[ind] = usc;
+ mutex_unlock(&session_lock);
+ ++ind; /* session id: 0 reserved */
+ pr_debug("%s: session[%d] was allocated\n",
+ __func__, ind);
+ return ind;
+ }
+ }
+ mutex_unlock(&session_lock);
+ return -ENOMEM;
+}
+
+static void q6usm_session_free(struct us_client *usc)
+{
+ /* Session index was incremented during allocation */
+ uint16_t ind = (uint16_t)usc->session - 1;
+
+ pr_debug("%s: to free session[%d]\n", __func__, ind);
+ if (ind < SESSION_MAX) {
+ mutex_lock(&session_lock);
+ session[ind] = 0;
+ mutex_unlock(&session_lock);
+ }
+}
+
+int q6usm_us_client_buf_free(unsigned int dir,
+ struct us_client *usc)
+{
+ struct us_port_data *port;
+ int rc = 0;
+ uint32_t size = 0;
+
+ if ((usc == NULL) ||
+ ((dir != IN) && (dir != OUT)))
+ return -EINVAL;
+
+ mutex_lock(&usc->cmd_lock);
+ port = &usc->port[dir];
+ if (port == NULL) {
+ mutex_unlock(&usc->cmd_lock);
+ return -EINVAL;
+ }
+
+ if (port->data == NULL) {
+ mutex_unlock(&usc->cmd_lock);
+ return 0;
+ }
+
+ rc = q6usm_memory_unmap(usc, port->phys, dir);
+ pr_debug("%s: data[%p]phys[%p][%p]\n", __func__,
+ (void *)port->data, (void *)port->phys, (void *)&port->phys);
+ /* 4K boundary is required by the API with QDSP6 */
+ size = (port->buf_size * port->buf_cnt + MEM_4K_OFFSET) & MEM_4K_MASK;
+ dma_free_coherent(NULL, size, port->data, port->phys);
+ port->data = NULL;
+ port->phys = 0;
+ port->buf_size = 0;
+ port->buf_cnt = 0;
+
+ mutex_unlock(&usc->cmd_lock);
+ return rc;
+}
+
+void q6usm_us_client_free(struct us_client *usc)
+{
+ int loopcnt = 0;
+ struct us_port_data *port;
+ uint32_t *p_mem_handle = NULL;
+
+ if ((usc == NULL) ||
+ !(usc->session))
+ return;
+
+ for (loopcnt = 0; loopcnt <= OUT; ++loopcnt) {
+ port = &usc->port[loopcnt];
+ if (port->data == NULL)
+ continue;
+ pr_debug("%s: loopcnt = %d\n", __func__, loopcnt);
+ q6usm_us_client_buf_free(loopcnt, usc);
+ }
+ q6usm_session_free(usc);
+ apr_deregister(usc->apr);
+
+ pr_debug("%s: APR De-Register\n", __func__);
+
+ if (atomic_read(&this_mmap.ref_cnt) <= 0) {
+ pr_err("%s: APR Common Port Already Closed\n", __func__);
+ goto done;
+ }
+
+ atomic_dec(&this_mmap.ref_cnt);
+ if (atomic_read(&this_mmap.ref_cnt) == 0) {
+ apr_deregister(this_mmap.apr);
+ pr_debug("%s: APR De-Register common port\n", __func__);
+ }
+
+done:
+ p_mem_handle = (uint32_t *)usc->port[IN].ext;
+ kfree(p_mem_handle);
+ kfree(usc);
+ pr_debug("%s:\n", __func__);
+ return;
+}
+
+struct us_client *q6usm_us_client_alloc(
+ void (*cb)(uint32_t, uint32_t, uint32_t *, void *),
+ void *priv)
+{
+ struct us_client *usc;
+ uint32_t *p_mem_handle = NULL;
+ int n;
+ int lcnt = 0;
+
+ usc = kzalloc(sizeof(struct us_client), GFP_KERNEL);
+ if (usc == NULL) {
+ pr_err("%s: us_client allocation failed\n", __func__);
+ return NULL;
+ }
+ p_mem_handle = kzalloc(sizeof(uint32_t) * 2, GFP_KERNEL);
+ if (p_mem_handle == NULL) {
+ pr_err("%s: p_mem_handle allocation failed\n", __func__);
+ kfree(usc);
+ return NULL;
+ }
+
+ n = q6usm_session_alloc(usc);
+ if (n <= 0)
+ goto fail_session;
+ usc->session = n;
+ usc->cb = cb;
+ usc->priv = priv;
+ usc->apr = apr_register("ADSP", "USM", \
+ (apr_fn)q6usm_callback,\
+ ((usc->session) << 8 | 0x0001),\
+ usc);
+
+ if (usc->apr == NULL) {
+ pr_err("%s: Registration with APR failed\n", __func__);
+ goto fail;
+ }
+ pr_debug("%s: Registering the common port with APR\n", __func__);
+ if (atomic_read(&this_mmap.ref_cnt) == 0) {
+ this_mmap.apr = apr_register("ADSP", "USM",
+ (apr_fn)q6usm_mmapcallback,
+ 0x0FFFFFFFF, &this_mmap);
+ if (this_mmap.apr == NULL) {
+ pr_err("%s: USM port registration failed\n",
+ __func__);
+ goto fail;
+ }
+ }
+
+ atomic_inc(&this_mmap.ref_cnt);
+ init_waitqueue_head(&usc->cmd_wait);
+ mutex_init(&usc->cmd_lock);
+ for (lcnt = 0; lcnt <= OUT; ++lcnt) {
+ mutex_init(&usc->port[lcnt].lock);
+ spin_lock_init(&usc->port[lcnt].dsp_lock);
+ usc->port[lcnt].ext = (void *)p_mem_handle++;
+ pr_err("%s: usc->port[%d].ext=%p;\n",
+ __func__, lcnt, usc->port[lcnt].ext);
+ }
+ atomic_set(&usc->cmd_state, 0);
+
+ return usc;
+fail:
+ q6usm_us_client_free(usc);
+ return NULL;
+fail_session:
+ kfree(p_mem_handle);
+ kfree(usc);
+ return NULL;
+}
+
+int q6usm_us_client_buf_alloc(unsigned int dir,
+ struct us_client *usc,
+ unsigned int bufsz,
+ unsigned int bufcnt)
+{
+ int rc = 0;
+ struct us_port_data *port = NULL;
+ unsigned int size = bufsz*bufcnt;
+
+ if ((usc == NULL) ||
+ ((dir != IN) && (dir != OUT)) || (size == 0) ||
+ (usc->session <= 0 || usc->session > SESSION_MAX)) {
+ pr_err("%s: wrong parameters: size=%d; bufcnt=%d\n",
+ __func__, size, bufcnt);
+ return -EINVAL;
+ }
+
+ mutex_lock(&usc->cmd_lock);
+
+ port = &usc->port[dir];
+
+ port->data = dma_alloc_coherent(NULL, size, &(port->phys), GFP_KERNEL);
+ if (port->data == NULL) {
+ pr_err("%s: US region allocation failed\n", __func__);
+ mutex_unlock(&usc->cmd_lock);
+ return -ENOMEM;
+ }
+
+ port->buf_cnt = bufcnt;
+ port->buf_size = bufsz;
+ pr_debug("%s: data[%p]; phys[%p]; [%p]\n", __func__,
+ (void *)port->data,
+ (void *)port->phys,
+ (void *)&port->phys);
+
+ size = (size + MEM_4K_OFFSET) & MEM_4K_MASK;
+ rc = q6usm_memory_map(usc, port->phys, dir, size, 1);
+ if (rc < 0) {
+ pr_err("%s: CMD Memory_map failed\n", __func__);
+ mutex_unlock(&usc->cmd_lock);
+ q6usm_us_client_buf_free(dir, usc);
+ } else {
+ mutex_unlock(&usc->cmd_lock);
+ rc = 0;
+ }
+
+ return rc;
+}
+
+static int32_t q6usm_mmapcallback(struct apr_client_data *data, void *priv)
+{
+ uint32_t token;
+ uint32_t *payload = data->payload;
+
+ pr_debug("%s: ptr0[0x%x]; ptr1[0x%x]; opcode[0x%x]\n",
+ __func__, payload[0], payload[1], data->opcode);
+ pr_debug("%s: token[0x%x]; payload_size[%d]; src[%d]; dest[%d];\n",
+ __func__, data->token, data->payload_size,
+ data->src_port, data->dest_port);
+
+ if (data->opcode == APR_BASIC_RSP_RESULT) {
+ /* status field check */
+ if (payload[1]) {
+ pr_err("%s: wrong response[%d] on cmd [%d]\n",
+ __func__, payload[1], payload[0]);
+ } else {
+ token = data->token;
+ switch (payload[0]) {
+ case USM_CMD_SHARED_MEM_UNMAP_REGION:
+ if (atomic_read(&this_mmap.cmd_state)) {
+ atomic_set(&this_mmap.cmd_state, 0);
+ wake_up(&this_mmap.cmd_wait);
+ }
+ case USM_CMD_SHARED_MEM_MAP_REGION:
+ /* For MEM_MAP, additional answer is waited, */
+ /* therfore, no wake-up here */
+ pr_debug("%s: cmd[0x%x]; result[0x%x]\n",
+ __func__, payload[0], payload[1]);
+ break;
+ default:
+ pr_debug("%s: wrong command[0x%x]\n",
+ __func__, payload[0]);
+ break;
+ }
+ }
+ } else {
+ if (data->opcode == USM_CMDRSP_SHARED_MEM_MAP_REGION) {
+ this_mmap.mem_handle = payload[0];
+ pr_debug("%s: memory map handle = 0x%x",
+ __func__, payload[0]);
+ if (atomic_read(&this_mmap.cmd_state)) {
+ atomic_set(&this_mmap.cmd_state, 0);
+ wake_up(&this_mmap.cmd_wait);
+ }
+ }
+ }
+ return 0;
+}
+
+
+static int32_t q6usm_callback(struct apr_client_data *data, void *priv)
+{
+ struct us_client *usc = (struct us_client *)priv;
+ unsigned long dsp_flags;
+ uint32_t *payload = data->payload;
+ uint32_t token = data->token;
+ uint32_t opcode = Q6USM_EVENT_UNDEF;
+
+ if (usc == NULL) {
+ pr_err("%s: client info is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ if (data->opcode == APR_BASIC_RSP_RESULT) {
+ /* status field check */
+ if (payload[1]) {
+ pr_err("%s: wrong response[%d] on cmd [%d]\n",
+ __func__, payload[1], payload[0]);
+ if (usc->cb)
+ usc->cb(data->opcode, token,
+ (uint32_t *)data->payload, usc->priv);
+ } else {
+ switch (payload[0]) {
+ case USM_SESSION_CMD_RUN:
+ case USM_STREAM_CMD_CLOSE:
+ if (token != usc->session) {
+ pr_err("%s: wrong token[%d]",
+ __func__, token);
+ break;
+ }
+ case USM_STREAM_CMD_OPEN_READ:
+ case USM_STREAM_CMD_OPEN_WRITE:
+ case USM_STREAM_CMD_SET_ENC_PARAM:
+ case USM_DATA_CMD_MEDIA_FORMAT_UPDATE:
+ case USM_SESSION_CMD_SIGNAL_DETECT_MODE:
+ if (atomic_read(&usc->cmd_state)) {
+ atomic_set(&usc->cmd_state, 0);
+ wake_up(&usc->cmd_wait);
+ }
+ if (usc->cb)
+ usc->cb(data->opcode, token,
+ (uint32_t *)data->payload,
+ usc->priv);
+ break;
+ default:
+ break;
+ }
+ }
+ return 0;
+ }
+
+ switch (data->opcode) {
+ case USM_DATA_EVENT_READ_DONE: {
+ struct us_port_data *port = &usc->port[OUT];
+
+ opcode = Q6USM_EVENT_READ_DONE;
+ spin_lock_irqsave(&port->dsp_lock, dsp_flags);
+ if (payload[READDONE_IDX_STATUS]) {
+ pr_err("%s: wrong READDONE[%d]; token[%d]\n",
+ __func__,
+ payload[READDONE_IDX_STATUS],
+ token);
+ token = USM_WRONG_TOKEN;
+ spin_unlock_irqrestore(&port->dsp_lock,
+ dsp_flags);
+ break;
+ }
+
+ if (port->expected_token != token) {
+ u32 cpu_buf = port->cpu_buf;
+ pr_err("%s: expected[%d] != token[%d]\n",
+ __func__, port->expected_token, token);
+ pr_debug("%s: dsp_buf=%d; cpu_buf=%d;\n",
+ __func__, port->dsp_buf, cpu_buf);
+
+ token = USM_WRONG_TOKEN;
+ /* To prevent data handle continiue */
+ port->expected_token = USM_WRONG_TOKEN;
+ spin_unlock_irqrestore(&port->dsp_lock,
+ dsp_flags);
+ break;
+ } /* port->expected_token != data->token */
+
+ port->expected_token = token + 1;
+ if (port->expected_token == port->buf_cnt)
+ port->expected_token = 0;
+
+ /* gap support */
+ if (port->expected_token != port->cpu_buf) {
+ port->dsp_buf = port->expected_token;
+ token = port->dsp_buf; /* for callback */
+ } else
+ port->dsp_buf = token;
+
+ spin_unlock_irqrestore(&port->dsp_lock, dsp_flags);
+ break;
+ } /* case USM_DATA_EVENT_READ_DONE */
+
+ case USM_DATA_EVENT_WRITE_DONE: {
+ struct us_port_data *port = &usc->port[IN];
+
+ opcode = Q6USM_EVENT_WRITE_DONE;
+ if (payload[WRITEDONE_IDX_STATUS]) {
+ pr_err("%s: wrong WRITEDONE_IDX_STATUS[%d]\n",
+ __func__,
+ payload[WRITEDONE_IDX_STATUS]);
+ break;
+ }
+
+ spin_lock_irqsave(&port->dsp_lock, dsp_flags);
+ port->dsp_buf = token + 1;
+ if (port->dsp_buf == port->buf_cnt)
+ port->dsp_buf = 0;
+ spin_unlock_irqrestore(&port->dsp_lock, dsp_flags);
+
+ break;
+ } /* case USM_DATA_EVENT_WRITE_DONE */
+
+ case USM_SESSION_EVENT_SIGNAL_DETECT_RESULT: {
+ pr_debug("%s: US detect result: result=%d",
+ __func__,
+ payload[0]);
+ opcode = Q6USM_EVENT_SIGNAL_DETECT_RESULT;
+
+ break;
+ } /* case USM_SESSION_EVENT_SIGNAL_DETECT_RESULT */
+
+ default:
+ return 0;
+
+ } /* switch */
+
+ if (usc->cb)
+ usc->cb(opcode, token,
+ data->payload, usc->priv);
+
+ return 0;
+}
+
+uint32_t q6usm_get_virtual_address(int dir,
+ struct us_client *usc,
+ struct vm_area_struct *vms)
+{
+ uint32_t ret = 0xffffffff;
+
+ if (vms && (usc != NULL) && ((dir == IN) || (dir == OUT))) {
+ struct us_port_data *port = &usc->port[dir];
+ int size = (port->buf_size * port->buf_cnt + MEM_4K_OFFSET)
+ & MEM_4K_MASK;
+
+ ret = dma_mmap_coherent(NULL, vms,
+ port->data, port->phys,
+ size);
+ }
+ return ret;
+}
+
+static void q6usm_add_hdr(struct us_client *usc, struct apr_hdr *hdr,
+ uint32_t pkt_size, bool cmd_flg)
+{
+ mutex_lock(&usc->cmd_lock);
+ hdr->hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, \
+ APR_HDR_LEN(sizeof(struct apr_hdr)),\
+ APR_PKT_VER);
+ hdr->src_svc = ((struct apr_svc *)usc->apr)->id;
+ hdr->src_domain = APR_DOMAIN_APPS;
+ hdr->dest_svc = APR_SVC_USM;
+ hdr->dest_domain = APR_DOMAIN_ADSP;
+ hdr->src_port = (usc->session << 8) | 0x0001;
+ hdr->dest_port = (usc->session << 8) | 0x0001;
+ if (cmd_flg) {
+ hdr->token = usc->session;
+ atomic_set(&usc->cmd_state, 1);
+ }
+ hdr->pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, pkt_size);
+ mutex_unlock(&usc->cmd_lock);
+ return;
+}
+
+static uint32_t q6usm_ext2int_format(uint32_t ext_format)
+{
+ uint32_t int_format = INVALID_FORMAT;
+ switch (ext_format) {
+ case FORMAT_USPS_EPOS:
+ int_format = US_POINT_EPOS_FORMAT;
+ break;
+ case FORMAT_USRAW:
+ int_format = US_RAW_FORMAT;
+ break;
+ case FORMAT_USPROX:
+ int_format = US_PROX_FORMAT;
+ break;
+ default:
+ pr_err("%s: Invalid format[%d]\n", __func__, ext_format);
+ break;
+ }
+
+ return int_format;
+}
+
+int q6usm_open_read(struct us_client *usc,
+ uint32_t format)
+{
+ uint32_t int_format = INVALID_FORMAT;
+ int rc = 0x00;
+ struct usm_stream_cmd_open_read open;
+
+ pr_debug("%s: session[%d]", __func__, usc->session);
+
+ if ((usc == NULL) || (usc->apr == NULL)) {
+ pr_err("%s: client or its apr is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ q6usm_add_hdr(usc, &open.hdr, sizeof(open), true);
+ open.hdr.opcode = USM_STREAM_CMD_OPEN_READ;
+ open.src_endpoint = 0; /* AFE */
+ open.pre_proc_top = 0; /* No preprocessing required */
+
+ int_format = q6usm_ext2int_format(format);
+ if (int_format == INVALID_FORMAT)
+ return -EINVAL;
+
+ open.uMode = STREAM_PRIORITY_NORMAL;
+ open.format = int_format;
+
+ rc = apr_send_pkt(usc->apr, (uint32_t *) &open);
+ if (rc < 0) {
+ pr_err("%s: open failed op[0x%x]rc[%d]\n",
+ __func__, open.hdr.opcode, rc);
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(usc->cmd_wait,
+ (atomic_read(&usc->cmd_state) == 0),
+ Q6USM_TIMEOUT_JIFFIES);
+ if (!rc) {
+ rc = -ETIME;
+ pr_err("%s: timeout, waited for OPEN_READ rc[%d]\n",
+ __func__, rc);
+ goto fail_cmd;
+ } else
+ rc = 0;
+fail_cmd:
+ return rc;
+}
+
+
+int q6usm_enc_cfg_blk(struct us_client *usc, struct us_encdec_cfg *us_cfg)
+{
+ uint32_t int_format = INVALID_FORMAT;
+ struct usm_stream_cmd_encdec_cfg_blk enc_cfg_obj;
+ struct usm_stream_cmd_encdec_cfg_blk *enc_cfg = &enc_cfg_obj;
+ int rc = 0;
+ uint32_t total_cfg_size =
+ sizeof(struct usm_stream_cmd_encdec_cfg_blk);
+ uint32_t round_params_size = 0;
+ uint8_t is_allocated = 0;
+
+
+ if ((usc == NULL) || (us_cfg == NULL)) {
+ pr_err("%s: wrong input", __func__);
+ return -EINVAL;
+ }
+
+ int_format = q6usm_ext2int_format(us_cfg->format_id);
+ if (int_format == INVALID_FORMAT) {
+ pr_err("%s: wrong input format[%d]",
+ __func__, us_cfg->format_id);
+ return -EINVAL;
+ }
+
+ /* Transparent configuration data is after enc_cfg */
+ /* Integer number of u32s is requred */
+ round_params_size = ((us_cfg->params_size + 3)/4) * 4;
+ if (round_params_size > USM_MAX_CFG_DATA_SIZE) {
+ /* Dynamic allocated encdec_cfg_blk is required */
+ /* static part use */
+ round_params_size -= USM_MAX_CFG_DATA_SIZE;
+ total_cfg_size += round_params_size;
+ enc_cfg = kzalloc(total_cfg_size, GFP_KERNEL);
+ if (enc_cfg == NULL) {
+ pr_err("%s: enc_cfg[%d] allocation failed\n",
+ __func__, total_cfg_size);
+ return -ENOMEM;
+ }
+ is_allocated = 1;
+ } else
+ round_params_size = 0;
+
+ q6usm_add_hdr(usc, &enc_cfg->hdr, total_cfg_size - APR_HDR_SIZE, true);
+
+ enc_cfg->hdr.opcode = USM_STREAM_CMD_SET_ENC_PARAM;
+ enc_cfg->param_id = USM_PARAM_ID_ENCDEC_ENC_CFG_BLK;
+ enc_cfg->param_size = sizeof(struct usm_encode_cfg_blk)+
+ round_params_size;
+ enc_cfg->enc_blk.frames_per_buf = 1;
+ enc_cfg->enc_blk.format_id = int_format;
+ enc_cfg->enc_blk.cfg_size = sizeof(struct usm_cfg_common)+
+ USM_MAX_CFG_DATA_SIZE +
+ round_params_size;
+ memcpy(&(enc_cfg->enc_blk.cfg_common), &(us_cfg->cfg_common),
+ sizeof(struct usm_cfg_common));
+
+ /* Transparent data copy */
+ memcpy(enc_cfg->enc_blk.transp_data, us_cfg->params,
+ us_cfg->params_size);
+ pr_debug("%s: cfg_size[%d], params_size[%d]\n",
+ __func__,
+ enc_cfg->enc_blk.cfg_size,
+ us_cfg->params_size);
+ pr_debug("%s: params[%d,%d,%d,%d, %d,%d,%d,%d]\n",
+ __func__,
+ enc_cfg->enc_blk.transp_data[0],
+ enc_cfg->enc_blk.transp_data[1],
+ enc_cfg->enc_blk.transp_data[2],
+ enc_cfg->enc_blk.transp_data[3],
+ enc_cfg->enc_blk.transp_data[4],
+ enc_cfg->enc_blk.transp_data[5],
+ enc_cfg->enc_blk.transp_data[6],
+ enc_cfg->enc_blk.transp_data[7]
+ );
+ pr_debug("%s: srate:%d, ch=%d, bps= %d; dmap:0x%x; dev_id=0x%x\n",
+ __func__, enc_cfg->enc_blk.cfg_common.sample_rate,
+ enc_cfg->enc_blk.cfg_common.ch_cfg,
+ enc_cfg->enc_blk.cfg_common.bits_per_sample,
+ enc_cfg->enc_blk.cfg_common.data_map,
+ enc_cfg->enc_blk.cfg_common.dev_id);
+
+ rc = apr_send_pkt(usc->apr, (uint32_t *) enc_cfg);
+ if (rc < 0) {
+ pr_err("%s:Comamnd open failed\n", __func__);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(usc->cmd_wait,
+ (atomic_read(&usc->cmd_state) == 0),
+ Q6USM_TIMEOUT_JIFFIES);
+ if (!rc) {
+ rc = -ETIME;
+ pr_err("%s: timeout opcode[0x%x]\n",
+ __func__, enc_cfg->hdr.opcode);
+ } else
+ rc = 0;
+
+fail_cmd:
+ if (is_allocated == 1)
+ kfree(enc_cfg);
+
+ return rc;
+}
+
+int q6usm_dec_cfg_blk(struct us_client *usc, struct us_encdec_cfg *us_cfg)
+{
+
+ uint32_t int_format = INVALID_FORMAT;
+ struct usm_stream_media_format_update dec_cfg_obj;
+ struct usm_stream_media_format_update *dec_cfg = &dec_cfg_obj;
+
+ int rc = 0;
+ uint32_t total_cfg_size = sizeof(struct usm_stream_media_format_update);
+ uint32_t round_params_size = 0;
+ uint8_t is_allocated = 0;
+
+
+ if ((usc == NULL) || (us_cfg == NULL)) {
+ pr_err("%s: wrong input", __func__);
+ return -EINVAL;
+ }
+
+ int_format = q6usm_ext2int_format(us_cfg->format_id);
+ if (int_format == INVALID_FORMAT) {
+ pr_err("%s: wrong input format[%d]",
+ __func__, us_cfg->format_id);
+ return -EINVAL;
+ }
+
+ /* Transparent configuration data is after enc_cfg */
+ /* Integer number of u32s is requred */
+ round_params_size = ((us_cfg->params_size + 3)/4) * 4;
+ if (round_params_size > USM_MAX_CFG_DATA_SIZE) {
+ /* Dynamic allocated encdec_cfg_blk is required */
+ /* static part use */
+ round_params_size -= USM_MAX_CFG_DATA_SIZE;
+ total_cfg_size += round_params_size;
+ dec_cfg = kzalloc(total_cfg_size, GFP_KERNEL);
+ if (dec_cfg == NULL) {
+ pr_err("%s:dec_cfg[%d] allocation failed\n",
+ __func__, total_cfg_size);
+ return -ENOMEM;
+ }
+ is_allocated = 1;
+ } else { /* static transp_data is enough */
+ round_params_size = 0;
+ }
+
+ q6usm_add_hdr(usc, &dec_cfg->hdr, total_cfg_size - APR_HDR_SIZE, true);
+
+ dec_cfg->hdr.opcode = USM_DATA_CMD_MEDIA_FORMAT_UPDATE;
+ dec_cfg->format_id = int_format;
+ dec_cfg->cfg_size = sizeof(struct usm_cfg_common) +
+ USM_MAX_CFG_DATA_SIZE +
+ round_params_size;
+ memcpy(&(dec_cfg->cfg_common), &(us_cfg->cfg_common),
+ sizeof(struct usm_cfg_common));
+ /* Transparent data copy */
+ memcpy(dec_cfg->transp_data, us_cfg->params, us_cfg->params_size);
+ pr_debug("%s: cfg_size[%d], params_size[%d]; parambytes[%d,%d,%d,%d]\n",
+ __func__,
+ dec_cfg->cfg_size,
+ us_cfg->params_size,
+ dec_cfg->transp_data[0],
+ dec_cfg->transp_data[1],
+ dec_cfg->transp_data[2],
+ dec_cfg->transp_data[3]
+ );
+
+ rc = apr_send_pkt(usc->apr, (uint32_t *) dec_cfg);
+ if (rc < 0) {
+ pr_err("%s:Comamnd open failed\n", __func__);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(usc->cmd_wait,
+ (atomic_read(&usc->cmd_state) == 0),
+ Q6USM_TIMEOUT_JIFFIES);
+ if (!rc) {
+ rc = -ETIME;
+ pr_err("%s: timeout opcode[0x%x]\n",
+ __func__, dec_cfg->hdr.opcode);
+ } else
+ rc = 0;
+
+fail_cmd:
+ if (is_allocated == 1)
+ kfree(dec_cfg);
+
+ return rc;
+}
+
+int q6usm_open_write(struct us_client *usc,
+ uint32_t format)
+{
+ int rc = 0;
+ uint32_t int_format = INVALID_FORMAT;
+ struct usm_stream_cmd_open_write open;
+
+ pr_debug("%s: session[%d]", __func__, usc->session);
+
+ if ((usc == NULL) || (usc->apr == NULL)) {
+ pr_err("%s: APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ q6usm_add_hdr(usc, &open.hdr, sizeof(open), true);
+ open.hdr.opcode = USM_STREAM_CMD_OPEN_WRITE;
+
+ int_format = q6usm_ext2int_format(format);
+ if (int_format == INVALID_FORMAT) {
+ pr_err("%s: wrong format[%d]", __func__, format);
+ return -EINVAL;
+ }
+
+ open.format = int_format;
+
+ rc = apr_send_pkt(usc->apr, (uint32_t *) &open);
+ if (rc < 0) {
+ pr_err("%s:open failed op[0x%x]rc[%d]\n", \
+ __func__, open.hdr.opcode, rc);
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(usc->cmd_wait,
+ (atomic_read(&usc->cmd_state) == 0),
+ Q6USM_TIMEOUT_JIFFIES);
+ if (!rc) {
+ rc = -ETIME;
+ pr_err("%s:timeout. waited for OPEN_WRITR rc[%d]\n",
+ __func__, rc);
+ goto fail_cmd;
+ } else
+ rc = 0;
+
+fail_cmd:
+ return rc;
+}
+
+int q6usm_run(struct us_client *usc, uint32_t flags,
+ uint32_t msw_ts, uint32_t lsw_ts)
+{
+ struct usm_stream_cmd_run run;
+ int rc = 0;
+
+ if ((usc == NULL) || (usc->apr == NULL)) {
+ pr_err("%s: APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+ q6usm_add_hdr(usc, &run.hdr, sizeof(run), true);
+
+ run.hdr.opcode = USM_SESSION_CMD_RUN;
+ run.flags = flags;
+ run.msw_ts = msw_ts;
+ run.lsw_ts = lsw_ts;
+
+ rc = apr_send_pkt(usc->apr, (uint32_t *) &run);
+ if (rc < 0) {
+ pr_err("%s: Commmand run failed[%d]\n", __func__, rc);
+ goto fail_cmd;
+ }
+
+ rc = wait_event_timeout(usc->cmd_wait,
+ (atomic_read(&usc->cmd_state) == 0),
+ Q6USM_TIMEOUT_JIFFIES);
+ if (!rc) {
+ rc = -ETIME;
+ pr_err("%s: timeout. waited for run success rc[%d]\n",
+ __func__, rc);
+ } else
+ rc = 0;
+
+fail_cmd:
+ return rc;
+}
+
+
+
+int q6usm_read(struct us_client *usc, uint32_t read_ind)
+{
+ struct usm_stream_cmd_read read;
+ struct us_port_data *port = NULL;
+ int rc = 0;
+ u32 read_counter = 0;
+ u32 loop_ind = 0;
+
+ if ((usc == NULL) || (usc->apr == NULL)) {
+ pr_err("%s: APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+ port = &usc->port[OUT];
+
+ if (read_ind > port->buf_cnt) {
+ pr_err("%s: wrong read_ind[%d]\n",
+ __func__, read_ind);
+ return -EINVAL;
+ }
+ if (read_ind == port->cpu_buf) {
+ pr_err("%s: no free region\n", __func__);
+ return 0;
+ }
+
+ if (read_ind > port->cpu_buf) { /* 1 range */
+ read_counter = read_ind - port->cpu_buf;
+ } else { /* 2 ranges */
+ read_counter = (port->buf_cnt - port->cpu_buf) + read_ind;
+ }
+
+ q6usm_add_hdr(usc, &read.hdr, (sizeof(read) - APR_HDR_SIZE), false);
+
+ read.hdr.opcode = USM_DATA_CMD_READ;
+ read.buf_size = port->buf_size;
+ read.buf_addr_msw = 0;
+ read.mem_map_handle = *((uint32_t *)(port->ext));
+
+ for (loop_ind = 0; loop_ind < read_counter; ++loop_ind) {
+ u32 temp_cpu_buf = port->cpu_buf;
+
+ read.buf_addr_lsw = (uint32_t)(port->phys) +
+ port->buf_size * (port->cpu_buf);
+ read.seq_id = port->cpu_buf;
+ read.hdr.token = port->cpu_buf;
+ read.counter = 1;
+
+ ++(port->cpu_buf);
+ if (port->cpu_buf == port->buf_cnt)
+ port->cpu_buf = 0;
+
+ rc = apr_send_pkt(usc->apr, (uint32_t *) &read);
+
+ if (rc < 0) {
+ port->cpu_buf = temp_cpu_buf;
+
+ pr_err("%s:read op[0x%x]rc[%d]\n",
+ __func__, read.hdr.opcode, rc);
+ break;
+ } else
+ rc = 0;
+ } /* bufs loop */
+
+ return rc;
+}
+
+int q6usm_write(struct us_client *usc, uint32_t write_ind)
+{
+ int rc = 0;
+ struct usm_stream_cmd_write cmd_write;
+ struct us_port_data *port = NULL;
+ u32 current_dsp_buf = 0;
+
+ if ((usc == NULL) || (usc->apr == NULL)) {
+ pr_err("%s: APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+ port = &usc->port[IN];
+
+ current_dsp_buf = port->dsp_buf;
+ /* free region, caused by new dsp_buf report from DSP, */
+ /* can be only extended */
+ if (port->cpu_buf >= current_dsp_buf) {
+ /* 2 -part free region, including empty buffer */
+ if ((write_ind <= port->cpu_buf) &&
+ (write_ind > current_dsp_buf)) {
+ pr_err("%s: wrong w_ind[%d]; d_buf=%d; c_buf=%d\n",
+ __func__, write_ind,
+ current_dsp_buf, port->cpu_buf);
+ return -EINVAL;
+ }
+ } else {
+ /* 1 -part free region */
+ if ((write_ind <= port->cpu_buf) ||
+ (write_ind > current_dsp_buf)) {
+ pr_err("%s: wrong w_ind[%d]; d_buf=%d; c_buf=%d\n",
+ __func__, write_ind,
+ current_dsp_buf, port->cpu_buf);
+ return -EINVAL;
+ }
+ }
+
+ q6usm_add_hdr(usc, &cmd_write.hdr,
+ (sizeof(cmd_write) - APR_HDR_SIZE), false);
+
+ cmd_write.hdr.opcode = USM_DATA_CMD_WRITE;
+ cmd_write.buf_size = port->buf_size;
+ cmd_write.buf_addr_msw = 0;
+ cmd_write.mem_map_handle = *((uint32_t *)(port->ext));
+ cmd_write.res0 = 0;
+ cmd_write.res1 = 0;
+ cmd_write.res2 = 0;
+
+ while (port->cpu_buf != write_ind) {
+ u32 temp_cpu_buf = port->cpu_buf;
+
+ cmd_write.buf_addr_lsw = (uint32_t)(port->phys) +
+ port->buf_size * (port->cpu_buf);
+ cmd_write.seq_id = port->cpu_buf;
+ cmd_write.hdr.token = port->cpu_buf;
+
+ ++(port->cpu_buf);
+ if (port->cpu_buf == port->buf_cnt)
+ port->cpu_buf = 0;
+
+ rc = apr_send_pkt(usc->apr, (uint32_t *) &cmd_write);
+
+ if (rc < 0) {
+ port->cpu_buf = temp_cpu_buf;
+ pr_err("%s:write op[0x%x];rc[%d];cpu_buf[%d]\n",
+ __func__, cmd_write.hdr.opcode,
+ rc, port->cpu_buf);
+ break;
+ }
+
+ rc = 0;
+ }
+
+ return rc;
+}
+
+bool q6usm_is_write_buf_full(struct us_client *usc, uint32_t *free_region)
+{
+ struct us_port_data *port = NULL;
+ u32 cpu_buf = 0;
+
+ if ((usc == NULL) || !free_region) {
+ pr_err("%s: input data wrong\n", __func__);
+ return false;
+ }
+ port = &usc->port[IN];
+ cpu_buf = port->cpu_buf + 1;
+ if (cpu_buf == port->buf_cnt)
+ cpu_buf = 0;
+
+ *free_region = port->dsp_buf;
+
+ return cpu_buf == *free_region;
+}
+
+int q6usm_cmd(struct us_client *usc, int cmd)
+{
+ struct apr_hdr hdr;
+ int rc = 0;
+ atomic_t *state;
+
+ if ((usc == NULL) || (usc->apr == NULL)) {
+ pr_err("%s: APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+ q6usm_add_hdr(usc, &hdr, (sizeof(hdr) - APR_HDR_SIZE), true);
+ switch (cmd) {
+ case CMD_CLOSE:
+ hdr.opcode = USM_STREAM_CMD_CLOSE;
+ state = &usc->cmd_state;
+ break;
+
+ default:
+ pr_err("%s:Invalid format[%d]\n", __func__, cmd);
+ goto fail_cmd;
+ }
+
+ rc = apr_send_pkt(usc->apr, (uint32_t *) &hdr);
+ if (rc < 0) {
+ pr_err("%s: Command 0x%x failed\n", __func__, hdr.opcode);
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(usc->cmd_wait, (atomic_read(state) == 0),
+ Q6USM_TIMEOUT_JIFFIES);
+ if (!rc) {
+ rc = -ETIME;
+ pr_err("%s:timeout. waited for response opcode[0x%x]\n",
+ __func__, hdr.opcode);
+ } else
+ rc = 0;
+fail_cmd:
+ return rc;
+}
+
+int q6usm_set_us_detection(struct us_client *usc,
+ struct usm_session_cmd_detect_info *detect_info,
+ uint16_t detect_info_size)
+{
+ int rc = 0;
+
+ if ((usc == NULL) ||
+ (detect_info_size == 0) ||
+ (detect_info == NULL)) {
+ pr_err("%s: wrong input: usc=0x%p, inf_size=%d; info=0x%p",
+ __func__,
+ usc,
+ detect_info_size,
+ detect_info);
+ return -EINVAL;
+ }
+
+ q6usm_add_hdr(usc, &detect_info->hdr,
+ detect_info_size - APR_HDR_SIZE, true);
+
+ detect_info->hdr.opcode = USM_SESSION_CMD_SIGNAL_DETECT_MODE;
+
+ rc = apr_send_pkt(usc->apr, (uint32_t *)detect_info);
+ if (rc < 0) {
+ pr_err("%s:Comamnd signal detect failed\n", __func__);
+ return -EINVAL;
+ }
+ rc = wait_event_timeout(usc->cmd_wait,
+ (atomic_read(&usc->cmd_state) == 0),
+ Q6USM_TIMEOUT_JIFFIES);
+ if (!rc) {
+ rc = -ETIME;
+ pr_err("%s: CMD_SIGNAL_DETECT_MODE: timeout=%d\n",
+ __func__, Q6USM_TIMEOUT_JIFFIES);
+ } else
+ rc = 0;
+
+ return rc;
+}
+
+static int __init q6usm_init(void)
+{
+ pr_debug("%s\n", __func__);
+ init_waitqueue_head(&this_mmap.cmd_wait);
+ memset(session, 0, sizeof(session));
+ return 0;
+}
+
+device_initcall(q6usm_init);
diff --git a/arch/arm/mach-msm/rpm-smd.c b/arch/arm/mach-msm/rpm-smd.c
index ccd0861..4295fd4 100644
--- a/arch/arm/mach-msm/rpm-smd.c
+++ b/arch/arm/mach-msm/rpm-smd.c
@@ -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
@@ -960,6 +960,9 @@
*/
int msm_rpm_enter_sleep(void)
{
+ if (standalone)
+ return 0;
+
return smd_mask_receive_interrupt(msm_rpm_data.ch_info, true);
}
EXPORT_SYMBOL(msm_rpm_enter_sleep);
@@ -970,20 +973,13 @@
*/
void msm_rpm_exit_sleep(void)
{
+ if (standalone)
+ return;
+
smd_mask_receive_interrupt(msm_rpm_data.ch_info, false);
}
EXPORT_SYMBOL(msm_rpm_exit_sleep);
-static bool msm_rpm_set_standalone(void)
-{
- if (machine_is_msm9625() || machine_is_msm8974_rumi()) {
- pr_warn("%s(): Running in standalone mode, requests "
- "will not be sent to RPM\n", __func__);
- standalone = true;
- }
- return standalone;
-}
-
static int __devinit msm_rpm_dev_probe(struct platform_device *pdev)
{
char *key = NULL;
@@ -1001,6 +997,9 @@
if (ret)
goto fail;
+ key = "rpm-standalone";
+ standalone = of_property_read_bool(pdev->dev.of_node, key);
+
init_completion(&msm_rpm_data.smd_open);
spin_lock_init(&msm_rpm_data.smd_lock_write);
spin_lock_init(&msm_rpm_data.smd_lock_read);
@@ -1012,9 +1011,14 @@
pr_info("Cannot open RPM channel %s %d\n", msm_rpm_data.ch_name,
msm_rpm_data.ch_type);
- msm_rpm_set_standalone();
BUG_ON(!standalone);
complete(&msm_rpm_data.smd_open);
+ } else {
+ /*
+ * Override DT's suggestion to try standalone; since we have an
+ * SMD channel.
+ */
+ standalone = false;
}
wait_for_completion(&msm_rpm_data.smd_open);
@@ -1029,6 +1033,10 @@
}
of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
+
+ if (standalone)
+ pr_info("%s(): RPM running in standalone mode\n", __func__);
+
return 0;
fail:
pr_err("%s(): Failed to read node: %s, key=%s\n", __func__,
diff --git a/arch/arm/mach-msm/rpm_resources.c b/arch/arm/mach-msm/rpm_resources.c
index 43073d3..78c5ae0 100644
--- a/arch/arm/mach-msm/rpm_resources.c
+++ b/arch/arm/mach-msm/rpm_resources.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2010-2012, 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
@@ -21,6 +21,7 @@
#include <linux/spinlock.h>
#include <linux/cpu.h>
#include <linux/hrtimer.h>
+#include <linux/platform_device.h>
#include <mach/rpm.h>
#include <mach/msm_iomap.h>
#include <asm/mach-types.h>
@@ -71,6 +72,10 @@
static int vdd_mem_vlevels[MSM_RPMRS_VDD_MEM_LAST];
static int vdd_mask;
+static DEFINE_PER_CPU(uint32_t , msm_lpm_sleep_time);
+static DEFINE_PER_CPU(int , lpm_permitted_level);
+static DEFINE_PER_CPU(struct atomic_notifier_head, lpm_notify_head);
+
#define MSM_RPMRS_MAX_RS_REGISTER_COUNT 2
#define RPMRS_ATTR(_name) \
@@ -869,6 +874,13 @@
spin_unlock_irqrestore(&msm_rpmrs_lock, flags);
}
+static bool lpm_level_permitted(int cur_level_count)
+{
+ if (__get_cpu_var(lpm_permitted_level) == msm_rpmrs_level_count + 1)
+ return true;
+ return (__get_cpu_var(lpm_permitted_level) == cur_level_count);
+}
+
s32 msm_cpuidle_get_deep_idle_latency(void)
{
int i;
@@ -904,6 +916,7 @@
uint32_t pwr;
uint32_t next_wakeup_us = time_param->sleep_us;
bool modify_event_timer;
+ int best_level_iter = msm_rpmrs_level_count + 1;
if (sleep_mode == MSM_PM_SLEEP_MODE_POWER_COLLAPSE) {
irqs_detectable = msm_mpm_irqs_detectable(from_idle);
@@ -968,6 +981,7 @@
level->rs_limits.latency_us[cpu] = level->latency_us;
level->rs_limits.power[cpu] = pwr;
best_level = level;
+ best_level_iter = i;
if (power)
*power = pwr;
if (modify_event_timer && best_level->latency_us > 1)
@@ -978,6 +992,12 @@
time_param->modified_time_us = 0;
}
}
+ if (best_level && !lpm_level_permitted(best_level_iter))
+ best_level = NULL;
+ else
+ per_cpu(msm_lpm_sleep_time, cpu) =
+ time_param->modified_time_us ?
+ time_param->modified_time_us : time_param->sleep_us;
return best_level ? &best_level->rs_limits : NULL;
}
@@ -986,6 +1006,12 @@
bool from_idle, bool notify_rpm)
{
int rc = 0;
+ struct msm_lpm_sleep_data sleep_data;
+
+ sleep_data.limits = limits;
+ sleep_data.kernel_sleep = __get_cpu_var(msm_lpm_sleep_time);
+ atomic_notifier_call_chain(&__get_cpu_var(lpm_notify_head),
+ MSM_LPM_STATE_ENTER, &sleep_data);
if (notify_rpm) {
rc = msm_rpmrs_flush_buffer(sclk_count, limits, from_idle);
@@ -1009,6 +1035,9 @@
if (msm_rpmrs_use_mpm(limits))
msm_mpm_exit_sleep(from_idle);
+
+ atomic_notifier_call_chain(&__get_cpu_var(lpm_notify_head),
+ MSM_LPM_STATE_EXIT, NULL);
}
static int rpmrs_cpu_callback(struct notifier_block *nfb,
@@ -1033,6 +1062,16 @@
return NOTIFY_OK;
}
+static struct lpm_test_platform_data lpm_test_pdata;
+
+static struct platform_device msm_lpm_test_device = {
+ .name = "lpm_test",
+ .id = -1,
+ .dev = {
+ .platform_data = &lpm_test_pdata,
+ },
+};
+
static struct notifier_block __refdata rpmrs_cpu_notifier = {
.notifier_call = rpmrs_cpu_callback,
};
@@ -1041,6 +1080,7 @@
{
int i, k;
struct msm_rpmrs_level *levels = data->levels;
+ unsigned int m_cpu = 0;
msm_rpmrs_level_count = data->num_levels;
@@ -1052,6 +1092,16 @@
memcpy(msm_rpmrs_levels, levels,
msm_rpmrs_level_count * sizeof(struct msm_rpmrs_level));
+ lpm_test_pdata.use_qtimer = 0;
+ lpm_test_pdata.msm_lpm_test_levels = msm_rpmrs_levels,
+ lpm_test_pdata.msm_lpm_test_level_count = msm_rpmrs_level_count;
+
+ for_each_possible_cpu(m_cpu)
+ per_cpu(lpm_permitted_level, m_cpu) =
+ msm_rpmrs_level_count + 1;
+
+ platform_device_register(&msm_lpm_test_device);
+
memcpy(vdd_dig_vlevels, data->vdd_dig_levels,
(MSM_RPMRS_VDD_DIG_MAX + 1) * sizeof(vdd_dig_vlevels[0]));
@@ -1087,6 +1137,41 @@
return 0;
}
+uint32_t msm_pm_get_pxo(struct msm_rpmrs_limits *limits)
+{
+ return limits->pxo;
+}
+
+uint32_t msm_pm_get_l2_cache(struct msm_rpmrs_limits *limits)
+{
+ return limits->l2_cache;
+}
+
+uint32_t msm_pm_get_vdd_mem(struct msm_rpmrs_limits *limits)
+{
+ return limits->vdd_mem;
+}
+
+uint32_t msm_pm_get_vdd_dig(struct msm_rpmrs_limits *limits)
+{
+ return limits->vdd_dig;
+}
+
+int msm_lpm_register_notifier(int cpu, int level_iter,
+ struct notifier_block *nb, bool is_latency_measure)
+{
+ per_cpu(lpm_permitted_level, cpu) = level_iter;
+ return atomic_notifier_chain_register(&per_cpu(lpm_notify_head,
+ cpu), nb);
+}
+
+int msm_lpm_unregister_notifier(int cpu, struct notifier_block *nb)
+{
+ per_cpu(lpm_permitted_level, cpu) = msm_rpmrs_level_count + 1;
+ return atomic_notifier_chain_unregister(&per_cpu(lpm_notify_head, cpu),
+ nb);
+}
+
static int __init msm_rpmrs_init(void)
{
struct msm_rpm_iv_pair req;
diff --git a/arch/arm/mach-msm/rpm_resources.h b/arch/arm/mach-msm/rpm_resources.h
index 46d6d94..0a180fb 100644
--- a/arch/arm/mach-msm/rpm_resources.h
+++ b/arch/arm/mach-msm/rpm_resources.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2010-2012, 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
@@ -15,7 +15,9 @@
#define __ARCH_ARM_MACH_MSM_RPM_RESOURCES_H
#include <mach/rpm.h>
+#include <linux/notifier.h>
#include "pm.h"
+#include "test-lpm.h"
enum {
MSM_RPMRS_ID_PXO_CLK = 0,
@@ -102,6 +104,94 @@
unsigned int rpmrs_target_id[MSM_RPMRS_ID_LAST];
};
+enum {
+ MSM_LPM_STATE_ENTER = 0,
+ MSM_LPM_STATE_EXIT = 1,
+};
+
+/**
+ * struct msm_lpm_sleep_data - abstraction to get sleep data
+ * @limits: pointer to the msm_rpmrs_limits structure
+ * @kernel_sleep: kernel sleep time as decided by the power calculation
+ * algorithm
+ *
+ * This structure is an abstraction to get the limits and kernel sleep time
+ * during enter sleep.
+ */
+
+struct msm_lpm_sleep_data {
+ struct msm_rpmrs_limits *limits;
+ uint32_t kernel_sleep;
+};
+
+#define MSM_PM(field) MSM_RPMRS_##field
+
+/**
+ * msm_pm_get_pxo() - get the limits for pxo
+ * @limits: pointer to the msm_rpmrs_limits structure
+ *
+ * This function gets the limits to the resource pxo on
+ * 8960
+ */
+
+uint32_t msm_pm_get_pxo(struct msm_rpmrs_limits *limits);
+
+/**
+ * msm_pm_get_l2_cache() - get the limits for l2 cache
+ * @limits: pointer to the msm_rpmrs_limits structure
+ *
+ * This function gets the limits to the resource l2 cache
+ * on 8960
+ */
+
+uint32_t msm_pm_get_l2_cache(struct msm_rpmrs_limits *limits);
+
+/**
+ * msm_pm_get_vdd_mem() - get the limits for pxo
+ * @limits: pointer to the msm_rpmrs_limits structure
+ *
+ * This function gets the limits to the resource vdd mem
+ * on 8960
+ */
+
+uint32_t msm_pm_get_vdd_mem(struct msm_rpmrs_limits *limits);
+
+/**
+ * msm_pm_get_vdd_dig() - get the limits for vdd dig
+ * @limits: pointer to the msm_rpmrs_limits structure
+ *
+ * This function gets the limits to the resource vdd dig
+ * on 8960
+ */
+
+uint32_t msm_pm_get_vdd_dig(struct msm_rpmrs_limits *limits);
+
+/**
+ * msm_lpm_register_notifier() - register for notifications
+ * @cpu: cpu to debug
+ * @level_iter: low power level index to debug
+ * @nb: notifier block to callback on notifications
+ * @is_latency_measure: is it latency measure
+ *
+ * This function sets the permitted level to the index of the
+ * level under test and registers notifier for callback.
+ */
+
+int msm_lpm_register_notifier(int cpu, int level_iter,
+ struct notifier_block *nb, bool is_latency_measure);
+
+/**
+ * msm_lpm_unregister_notifier() - unregister from notifications
+ * @cpu: cpu to debug
+ * @nb: notifier block to callback on notifications
+ *
+ * This function sets the permitted level to a value one more than
+ * available levels count which indicates that all levels are
+ * permitted and it also unregisters notifier for callback.
+ */
+
+int msm_lpm_unregister_notifier(int cpu, struct notifier_block *nb);
+
#if defined(CONFIG_MSM_RPM)
int msm_rpmrs_set(int ctx, struct msm_rpm_iv_pair *req, int count);
diff --git a/arch/arm/mach-msm/sdio_al_dloader.c b/arch/arm/mach-msm/sdio_al_dloader.c
index f48c32b..b0cb88f 100644
--- a/arch/arm/mach-msm/sdio_al_dloader.c
+++ b/arch/arm/mach-msm/sdio_al_dloader.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2012, 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
@@ -21,6 +21,7 @@
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/wakelock.h>
+#include <linux/workqueue.h>
#include <linux/mmc/card.h>
#include <linux/dma-mapping.h>
#include <mach/dma.h>
@@ -87,6 +88,8 @@
const char __user *buf, size_t count, loff_t *ppos);
#endif
+static void sdio_dld_tear_down(struct work_struct *work);
+DECLARE_WORK(cleanup, sdio_dld_tear_down);
/* STRUCTURES AND TYPES */
enum sdio_dld_op_mode {
@@ -224,6 +227,8 @@
static DEFINE_SPINLOCK(lock2);
static unsigned long lock_flags2;
+static atomic_t sdio_dld_in_use = ATOMIC_INIT(0);
+
/*
* sdio_op_mode sets the operation mode of the sdio_dloader -
* it may be in NORMAL_MODE, BOOT_TEST_MODE or AMSS_TEST_MODE
@@ -1108,6 +1113,10 @@
REAL_FUNC_TO_FUNC_IN_ARRAY(sdio_dld->sdioc_boot_func);
struct sdio_func *str_func = sdio_dld->card->sdio_func[func_in_array];
+ if (atomic_read(&sdio_dld_in_use) == 1)
+ return -EBUSY;
+
+ atomic_set(&sdio_dld_in_use, 1);
sdio_dld->tty_str = tty;
sdio_dld->tty_str->low_latency = 1;
sdio_dld->tty_str->icanon = 0;
@@ -1185,7 +1194,6 @@
*/
static void sdio_dld_close(struct tty_struct *tty, struct file *file)
{
- int status = 0;
struct sdioc_reg_chunk *reg = &sdio_dld->sdio_dloader_data.sdioc_reg;
/* informing the SDIOC that it can exit boot phase */
@@ -1200,20 +1208,6 @@
sdio_dld->dld_main_thread.exit_wait.wake_up_signal);
pr_debug(MODULE_NAME ": %s - CLOSING - WOKE UP...", __func__);
- del_timer_sync(&sdio_dld->timer);
- del_timer_sync(&sdio_dld->push_timer);
-
- sdio_dld_dealloc_local_buffers();
-
- tty_unregister_device(sdio_dld->tty_drv, 0);
-
- status = tty_unregister_driver(sdio_dld->tty_drv);
-
- if (status) {
- pr_err(MODULE_NAME ": %s - tty_unregister_driver() failed\n",
- __func__);
- }
-
#ifdef CONFIG_DEBUG_FS
gd.curr_i = curr_index;
gd.duration_ms = sdio_dld_info.time_msec;
@@ -1263,9 +1257,8 @@
if (sdio_dld->done_callback)
sdio_dld->done_callback();
- pr_info(MODULE_NAME ": %s - Freeing sdio_dld data structure, and "
- " returning...", __func__);
- kfree(sdio_dld);
+ schedule_work(&cleanup);
+ pr_info(MODULE_NAME ": %s - Bootloader done, returning...", __func__);
}
/**
@@ -2392,6 +2385,9 @@
struct sdio_func *str_func = NULL;
struct device *tty_dev;
+ if (atomic_read(&sdio_dld_in_use) == 1)
+ return -EBUSY;
+
if (num_of_devices == 0 || num_of_devices > MAX_NUM_DEVICES) {
pr_err(MODULE_NAME ": %s - invalid number of devices\n",
__func__);
@@ -2534,6 +2530,28 @@
return status;
}
+static void sdio_dld_tear_down(struct work_struct *work)
+{
+ int status = 0;
+
+ del_timer_sync(&sdio_dld->timer);
+ del_timer_sync(&sdio_dld->push_timer);
+
+ sdio_dld_dealloc_local_buffers();
+
+ tty_unregister_device(sdio_dld->tty_drv, 0);
+
+ status = tty_unregister_driver(sdio_dld->tty_drv);
+
+ if (status) {
+ pr_err(MODULE_NAME ": %s - tty_unregister_driver() failed\n",
+ __func__);
+ }
+
+ kfree(sdio_dld);
+ atomic_set(&sdio_dld_in_use, 0);
+}
+
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("SDIO Downloader");
MODULE_AUTHOR("Yaniv Gardi <ygardi@codeaurora.org>");
diff --git a/arch/arm/mach-msm/smd.c b/arch/arm/mach-msm/smd.c
index c607202..eda8950 100644
--- a/arch/arm/mach-msm/smd.c
+++ b/arch/arm/mach-msm/smd.c
@@ -1,7 +1,7 @@
/* arch/arm/mach-msm/smd.c
*
* Copyright (C) 2007 Google, Inc.
- * Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2008-2013, The Linux Foundation. All rights reserved.
* Author: Brian Swetland <swetland@google.com>
*
* This software is licensed under the terms of the GNU General Public
@@ -3153,6 +3153,17 @@
}
EXPORT_SYMBOL(smsm_state_cb_deregister);
+/**
+ * smem_get_remote_spinlock - Remote spinlock pointer for unit testing.
+ *
+ * @returns: pointer to SMEM remote spinlock
+ */
+remote_spinlock_t *smem_get_remote_spinlock(void)
+{
+ return &remote_spinlock;
+}
+EXPORT_SYMBOL(smem_get_remote_spinlock);
+
int smd_module_init_notifier_register(struct notifier_block *nb)
{
int ret;
diff --git a/arch/arm/mach-msm/smd_private.h b/arch/arm/mach-msm/smd_private.h
index 2033fbe..50bfb13 100644
--- a/arch/arm/mach-msm/smd_private.h
+++ b/arch/arm/mach-msm/smd_private.h
@@ -1,7 +1,7 @@
/* arch/arm/mach-msm/smd_private.h
*
* Copyright (C) 2007 Google, Inc.
- * Copyright (c) 2007-2012, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2007-2013, The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@@ -18,6 +18,8 @@
#include <linux/types.h>
#include <linux/spinlock.h>
+#include <linux/errno.h>
+#include <linux/remote_spinlock.h>
#include <mach/msm_smsm.h>
#include <mach/msm_smd.h>
@@ -271,4 +273,6 @@
};
extern struct interrupt_stat interrupt_stats[NUM_SMD_SUBSYSTEMS];
+/* used for unit testing spinlocks */
+remote_spinlock_t *smem_get_remote_spinlock(void);
#endif
diff --git a/arch/arm/mach-msm/smp2p.c b/arch/arm/mach-msm/smp2p.c
new file mode 100644
index 0000000..6d2df2a
--- /dev/null
+++ b/arch/arm/mach-msm/smp2p.c
@@ -0,0 +1,1706 @@
+/* arch/arm/mach-msm/smp2p.c
+ *
+ * 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/list.h>
+#include <linux/ctype.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <mach/msm_smsm.h>
+#include <mach/msm_ipc_logging.h>
+#include "smp2p_private_api.h"
+#include "smp2p_private.h"
+
+#define NUM_LOG_PAGES 3
+
+/**
+ * struct msm_smp2p_out - This structure represents the outbound SMP2P entry.
+ *
+ * @remote_pid: Outbound processor ID.
+ * @name: Entry name.
+ * @out_edge_list: Adds this structure into smp2p_out_list_item::list.
+ * @msm_smp2p_notifier_list: Notifier block head used to notify for open event.
+ * @open_nb: Notifier block used to notify for open event.
+ * @l_smp2p_entry: Pointer to the actual entry in the SMEM item.
+ */
+struct msm_smp2p_out {
+ int remote_pid;
+ char name[SMP2P_MAX_ENTRY_NAME];
+ struct list_head out_edge_list;
+ struct raw_notifier_head msm_smp2p_notifier_list;
+ struct notifier_block *open_nb;
+ uint32_t *l_smp2p_entry;
+};
+
+/**
+ * struct smp2p_out_list_item - Maintains the state of outbound edge.
+ *
+ * @out_item_lock_lha1: Lock protecting all elements of the structure.
+ * @list: list of outbound entries (struct msm_smp2p_out).
+ * @smem_edge_out: Pointer to outbound smem item.
+ * @smem_edge_state: State of the outbound edge.
+ * @ops_ptr: Pointer to internal version-specific SMEM item access functions.
+ */
+struct smp2p_out_list_item {
+ spinlock_t out_item_lock_lha1;
+
+ struct list_head list;
+ struct smp2p_smem *smem_edge_out;
+ enum msm_smp2p_edge_state smem_edge_state;
+ struct smp2p_version_if *ops_ptr;
+};
+static struct smp2p_out_list_item out_list[SMP2P_NUM_PROCS];
+
+static void *log_ctx;
+static int smp2p_debug_mask = MSM_SMP2P_INFO;
+module_param_named(debug_mask, smp2p_debug_mask,
+ int, S_IRUGO | S_IWUSR | S_IWGRP);
+
+/**
+ * struct smp2p_in - Represents the entry on remote processor.
+ *
+ * @name: Name of the entry.
+ * @remote_pid: Outbound processor ID.
+ * @in_edge_list: Adds this structure into smp2p_in_list_item::list.
+ * @in_notifier_list: List for notifier block for entry opening/updates.
+ * @entry_val: Previous value of the entry.
+ * @entry_ptr: Points to the current value in smem item.
+ * @notifier_count: Counts the number of notifier registered per pid,entry.
+ */
+struct smp2p_in {
+ int remote_pid;
+ char name[SMP2P_MAX_ENTRY_NAME];
+ struct list_head in_edge_list;
+ struct raw_notifier_head in_notifier_list;
+ uint32_t entry_val;
+ uint32_t *entry_ptr;
+ uint32_t notifier_count;
+};
+
+/**
+ * struct smp2p_in_list_item - Maintains the inbound edge state.
+ *
+ * @in_item_lock_lhb1: Lock protecting all elements of the structure.
+ * @list: List head for the entries on remote processor.
+ * @smem_edge_in: Pointer to the remote smem item.
+ */
+struct smp2p_in_list_item {
+ spinlock_t in_item_lock_lhb1;
+ struct list_head list;
+ struct smp2p_smem *smem_edge_in;
+ uint32_t item_size;
+ uint32_t safe_total_entries;
+};
+static struct smp2p_in_list_item in_list[SMP2P_NUM_PROCS];
+
+/**
+ * SMEM Item access function interface.
+ *
+ * This interface is used to help isolate the implementation of
+ * the functionality from any changes in the shared data structures
+ * that may happen as versions are changed.
+ *
+ * @is_supported: True if this version is supported by SMP2P
+ * @negotiate_features: Returns (sub)set of supported features
+ * @find_entry: Finds existing / next empty entry
+ * @create_entry: Creates a new entry
+ * @read_entry: Reads the value of an entry
+ * @write_entry: Writes a new value to an entry
+ * @modify_entry: Does a read/modify/write of an entry
+ * validate_size: Verifies the size of the remote SMEM item to ensure that
+ * an invalid item size doesn't result in an out-of-bounds
+ * memory access.
+ */
+struct smp2p_version_if {
+ /* common functions */
+ bool is_supported;
+ uint32_t (*negotiate_features)(uint32_t features);
+ void (*find_entry)(struct smp2p_smem *item, uint32_t entries_total,
+ char *name, uint32_t **entry_ptr, int *empty_spot);
+
+ /* outbound entry functions */
+ int (*create_entry)(struct msm_smp2p_out *);
+ int (*read_entry)(struct msm_smp2p_out *, uint32_t *);
+ int (*write_entry)(struct msm_smp2p_out *, uint32_t);
+ int (*modify_entry)(struct msm_smp2p_out *, uint32_t, uint32_t);
+
+ /* inbound entry functions */
+ struct smp2p_smem *(*validate_size)(int remote_pid,
+ struct smp2p_smem *, uint32_t);
+};
+
+static int smp2p_do_negotiation(int remote_pid, struct smp2p_out_list_item *p);
+static void smp2p_send_interrupt(int remote_pid);
+
+/* v0 (uninitialized SMEM item) interface functions */
+static uint32_t smp2p_negotiate_features_v0(uint32_t features);
+static void smp2p_find_entry_v0(struct smp2p_smem *item,
+ uint32_t entries_total, char *name, uint32_t **entry_ptr,
+ int *empty_spot);
+static int smp2p_out_create_v0(struct msm_smp2p_out *);
+static int smp2p_out_read_v0(struct msm_smp2p_out *, uint32_t *);
+static int smp2p_out_write_v0(struct msm_smp2p_out *, uint32_t);
+static int smp2p_out_modify_v0(struct msm_smp2p_out *, uint32_t, uint32_t);
+static struct smp2p_smem *smp2p_in_validate_size_v0(int remote_pid,
+ struct smp2p_smem *smem_item, uint32_t size);
+
+/* v1 interface functions */
+static uint32_t smp2p_negotiate_features_v1(uint32_t features);
+static void smp2p_find_entry_v1(struct smp2p_smem *item,
+ uint32_t entries_total, char *name, uint32_t **entry_ptr,
+ int *empty_spot);
+static int smp2p_out_create_v1(struct msm_smp2p_out *);
+static int smp2p_out_read_v1(struct msm_smp2p_out *, uint32_t *);
+static int smp2p_out_write_v1(struct msm_smp2p_out *, uint32_t);
+static int smp2p_out_modify_v1(struct msm_smp2p_out *, uint32_t, uint32_t);
+static struct smp2p_smem *smp2p_in_validate_size_v1(int remote_pid,
+ struct smp2p_smem *smem_item, uint32_t size);
+
+/* Version interface functions */
+static struct smp2p_version_if version_if[] = {
+ [0] = {
+ .negotiate_features = smp2p_negotiate_features_v0,
+ .find_entry = smp2p_find_entry_v0,
+ .create_entry = smp2p_out_create_v0,
+ .read_entry = smp2p_out_read_v0,
+ .write_entry = smp2p_out_write_v0,
+ .modify_entry = smp2p_out_modify_v0,
+ .validate_size = smp2p_in_validate_size_v0,
+ },
+ [1] = {
+ .is_supported = true,
+ .negotiate_features = smp2p_negotiate_features_v1,
+ .find_entry = smp2p_find_entry_v1,
+ .create_entry = smp2p_out_create_v1,
+ .read_entry = smp2p_out_read_v1,
+ .write_entry = smp2p_out_write_v1,
+ .modify_entry = smp2p_out_modify_v1,
+ .validate_size = smp2p_in_validate_size_v1,
+ },
+};
+
+/* interrupt configuration (filled by device tree) */
+static struct smp2p_interrupt_config smp2p_int_cfgs[SMP2P_NUM_PROCS] = {
+ [SMP2P_MODEM_PROC].name = "modem",
+ [SMP2P_AUDIO_PROC].name = "lpass",
+ [SMP2P_WIRELESS_PROC].name = "wcnss",
+ [SMP2P_REMOTE_MOCK_PROC].name = "mock",
+};
+
+/**
+ * smp2p_get_log_ctx - Return log context for other SMP2P modules.
+ *
+ * @returns: Log context or NULL if none.
+ */
+void *smp2p_get_log_ctx(void)
+{
+ return log_ctx;
+}
+
+/**
+ * smp2p_get_debug_mask - Return debug mask.
+ *
+ * @returns: Current debug mask.
+ */
+int smp2p_get_debug_mask(void)
+{
+ return smp2p_debug_mask;
+}
+
+/**
+ * smp2p_interrupt_config - Return interrupt configuration.
+ *
+ * @returns interrupt configuration array for usage by debugfs.
+ */
+struct smp2p_interrupt_config *smp2p_get_interrupt_config(void)
+{
+ return smp2p_int_cfgs;
+}
+
+/**
+ * smp2p_pid_to_name - Lookup name for remote pid.
+ *
+ * @returns: name (may be NULL).
+ */
+const char *smp2p_pid_to_name(int remote_pid)
+{
+ if (remote_pid >= SMP2P_NUM_PROCS)
+ return NULL;
+
+ return smp2p_int_cfgs[remote_pid].name;
+}
+
+/**
+ * smp2p_get_in_item - Return pointer to remote smem item.
+ *
+ * @remote_pid: Processor ID of the remote system.
+ * @returns: Pointer to inbound SMEM item
+ *
+ * This is used by debugfs to print the smem items.
+ */
+struct smp2p_smem *smp2p_get_in_item(int remote_pid)
+{
+ void *ret = NULL;
+ unsigned long flags;
+
+ spin_lock_irqsave(&in_list[remote_pid].in_item_lock_lhb1, flags);
+ if (remote_pid < SMP2P_NUM_PROCS)
+ ret = in_list[remote_pid].smem_edge_in;
+ spin_unlock_irqrestore(&in_list[remote_pid].in_item_lock_lhb1,
+ flags);
+
+ return ret;
+}
+
+/**
+ * smp2p_get_out_item - Return pointer to outbound SMEM item.
+ *
+ * @remote_pid: Processor ID of remote system.
+ * @state: Edge state of the outbound SMEM item.
+ * @returns: Pointer to outbound (remote) SMEM item.
+ */
+struct smp2p_smem *smp2p_get_out_item(int remote_pid, int *state)
+{
+ void *ret = NULL;
+ unsigned long flags;
+
+ spin_lock_irqsave(&out_list[remote_pid].out_item_lock_lha1, flags);
+ if (remote_pid < SMP2P_NUM_PROCS) {
+ ret = out_list[remote_pid].smem_edge_out;
+ if (state)
+ *state = out_list[remote_pid].smem_edge_state;
+ }
+ spin_unlock_irqrestore(&out_list[remote_pid].out_item_lock_lha1, flags);
+
+ return ret;
+}
+
+/**
+ * smp2p_get_smem_item_id - Return the proper SMEM item ID.
+ *
+ * @write_id: Processor that will write to the item.
+ * @read_id: Processor that will read from the item.
+ * @returns: SMEM ID
+ */
+static int smp2p_get_smem_item_id(int write_pid, int read_pid)
+{
+ int ret = -EINVAL;
+
+ switch (write_pid) {
+ case SMP2P_APPS_PROC:
+ ret = SMEM_SMP2P_APPS_BASE + read_pid;
+ break;
+ case SMP2P_MODEM_PROC:
+ ret = SMEM_SMP2P_MODEM_BASE + read_pid;
+ break;
+ case SMP2P_AUDIO_PROC:
+ ret = SMEM_SMP2P_AUDIO_BASE + read_pid;
+ break;
+ case SMP2P_WIRELESS_PROC:
+ ret = SMEM_SMP2P_WIRLESS_BASE + read_pid;
+ break;
+ case SMP2P_POWER_PROC:
+ ret = SMEM_SMP2P_POWER_BASE + read_pid;
+ break;
+ }
+
+ return ret;
+}
+
+/**
+ * Return pointer to SMEM item owned by the local processor.
+ *
+ * @remote_pid: Remote processor ID
+ * @returns: NULL for failure; otherwise pointer to SMEM item
+ *
+ * Must be called with out_item_lock_lha1 locked for mock proc.
+ */
+static void *smp2p_get_local_smem_item(int remote_pid)
+{
+ struct smp2p_smem *item_ptr = NULL;
+
+ if (remote_pid < SMP2P_REMOTE_MOCK_PROC) {
+ unsigned size;
+ int smem_id;
+
+ /* lookup or allocate SMEM item */
+ smem_id = smp2p_get_smem_item_id(SMP2P_APPS_PROC, remote_pid);
+ if (smem_id >= 0) {
+ item_ptr = smem_get_entry(smem_id, &size);
+
+ if (!item_ptr) {
+ size = sizeof(struct smp2p_smem_item);
+ item_ptr = smem_alloc2(smem_id, size);
+ }
+ }
+ } else if (remote_pid == SMP2P_REMOTE_MOCK_PROC) {
+ /*
+ * This path is only used during unit testing so
+ * the GFP_ATOMIC allocation should not be a
+ * concern.
+ */
+ if (!out_list[SMP2P_REMOTE_MOCK_PROC].smem_edge_out)
+ item_ptr = kzalloc(
+ sizeof(struct smp2p_smem_item),
+ GFP_ATOMIC);
+ }
+ return item_ptr;
+}
+
+/**
+ * smp2p_get_remote_smem_item - Return remote SMEM item.
+ *
+ * @remote_pid: Remote processor ID
+ * @out_item: Pointer to the output item structure
+ * @returns: NULL for failure; otherwise pointer to SMEM item
+ *
+ * Return pointer to SMEM item owned by the remote processor.
+ *
+ * Note that this function does an SMEM lookup which uses a remote spinlock,
+ * so this function should not be called more than necessary.
+ *
+ * Must be called with out_item_lock_lha1 and in_item_lock_lhb1 locked.
+ */
+static void *smp2p_get_remote_smem_item(int remote_pid,
+ struct smp2p_out_list_item *out_item)
+{
+ void *item_ptr = NULL;
+ unsigned size;
+
+ if (!out_item)
+ return item_ptr;
+
+ if (remote_pid < SMP2P_REMOTE_MOCK_PROC) {
+ int smem_id;
+
+ smem_id = smp2p_get_smem_item_id(remote_pid, SMP2P_APPS_PROC);
+ if (smem_id >= 0)
+ item_ptr = smem_get_entry(smem_id, &size);
+ } else if (remote_pid == SMP2P_REMOTE_MOCK_PROC) {
+ item_ptr = msm_smp2p_get_remote_mock_smem_item(&size);
+ }
+ item_ptr = out_item->ops_ptr->validate_size(remote_pid, item_ptr, size);
+
+ return item_ptr;
+}
+
+/**
+ * smp2p_negotiate_features_v0 - Initial feature negotiation.
+ *
+ * @features: Inbound feature set.
+ * @returns: Supported features (will be a same/subset of @features).
+ */
+static uint32_t smp2p_negotiate_features_v1(uint32_t features)
+{
+ /* no supported features */
+ return 0;
+}
+
+/**
+ * smp2p_find_entry_v1 - Search for an entry in SMEM item.
+ *
+ * @item: Pointer to the smem item.
+ * @entries_total: Total number of entries in @item.
+ * @name: Name of the entry.
+ * @entry_ptr: Set to pointer of entry if found, NULL otherwise.
+ * @empty_spot: If non-null, set to the value of the next empty entry.
+ *
+ * Searches for entry @name in the SMEM item. If found, a pointer
+ * to the item is returned. If it isn't found, the first empty
+ * index is returned in @empty_spot.
+ */
+static void smp2p_find_entry_v1(struct smp2p_smem *item,
+ uint32_t entries_total, char *name, uint32_t **entry_ptr,
+ int *empty_spot)
+{
+ int i;
+ struct smp2p_entry_v1 *pos;
+
+ if (!item || !name || !entry_ptr) {
+ SMP2P_ERR("%s: invalid arguments %p, %p, %p\n",
+ __func__, item, name, entry_ptr);
+ return;
+ }
+
+ *entry_ptr = NULL;
+ if (empty_spot)
+ *empty_spot = -1;
+
+ pos = (struct smp2p_entry_v1 *)(char *)(item + 1);
+ for (i = 0; i < entries_total; i++, ++pos) {
+ if (pos->name[0]) {
+ if (!strncmp(pos->name, name, SMP2P_MAX_ENTRY_NAME)) {
+ *entry_ptr = &pos->entry;
+ break;
+ }
+ } else if (empty_spot && *empty_spot < 0) {
+ *empty_spot = i;
+ }
+ }
+}
+
+/**
+ * smp2p_out_create_v1 - Creates a outbound SMP2P entry.
+ *
+ * @out_entry: Pointer to the SMP2P entry structure.
+ * @returns: 0 on success, standard Linux error code otherwise.
+ *
+ * Must be called with out_item_lock_lha1 locked.
+ */
+static int smp2p_out_create_v1(struct msm_smp2p_out *out_entry)
+{
+ struct smp2p_smem *smp2p_h_ptr;
+ struct smp2p_out_list_item *p_list;
+ uint32_t *state_entry_ptr;
+ uint32_t empty_spot;
+ uint32_t entries_total;
+ uint32_t entries_valid;
+
+ if (!out_entry)
+ return -EINVAL;
+
+ p_list = &out_list[out_entry->remote_pid];
+ if (p_list->smem_edge_state != SMP2P_EDGE_STATE_OPENED) {
+ SMP2P_ERR("%s: item '%s':%d opened - wrong create called\n",
+ __func__, out_entry->name, out_entry->remote_pid);
+ return -ENODEV;
+ }
+
+ smp2p_h_ptr = p_list->smem_edge_out;
+ entries_total = SMP2P_GET_ENT_TOTAL(smp2p_h_ptr->valid_total_ent);
+ entries_valid = SMP2P_GET_ENT_VALID(smp2p_h_ptr->valid_total_ent);
+
+ p_list->ops_ptr->find_entry(smp2p_h_ptr, entries_total,
+ out_entry->name, &state_entry_ptr, &empty_spot);
+ if (state_entry_ptr) {
+ /* re-use existing entry */
+ out_entry->l_smp2p_entry = state_entry_ptr;
+
+ SMP2P_DBG("%s: item '%s':%d reused\n", __func__,
+ out_entry->name, out_entry->remote_pid);
+ } else if (entries_valid >= entries_total) {
+ /* need to allocate entry, but not more space */
+ SMP2P_ERR("%s: no space for item '%s':%d\n",
+ __func__, out_entry->name, out_entry->remote_pid);
+ return -ENOMEM;
+ } else {
+ /* allocate a new entry */
+ struct smp2p_entry_v1 *entry_ptr;
+
+ entry_ptr = (struct smp2p_entry_v1 *)((char *)(smp2p_h_ptr + 1)
+ + empty_spot * sizeof(struct smp2p_entry_v1));
+ strlcpy(entry_ptr->name, out_entry->name,
+ sizeof(entry_ptr->name));
+ out_entry->l_smp2p_entry = &entry_ptr->entry;
+ ++entries_valid;
+ SMP2P_DBG("%s: item '%s':%d fully created as entry %d of %d\n",
+ __func__, out_entry->name,
+ out_entry->remote_pid,
+ entries_valid, entries_total);
+ SMP2P_SET_ENT_VALID(smp2p_h_ptr->valid_total_ent,
+ entries_valid);
+ smp2p_send_interrupt(out_entry->remote_pid);
+ }
+ raw_notifier_call_chain(&out_entry->msm_smp2p_notifier_list,
+ SMP2P_OPEN, 0);
+
+ return 0;
+}
+
+/**
+ * smp2p_out_read_v1 - Read the data from an outbound entry.
+ *
+ * @out_entry: Pointer to the SMP2P entry structure.
+ * @data: Out pointer, the data is available in this argument on success.
+ * @returns: 0 on success, standard Linux error code otherwise.
+ *
+ * Must be called with out_item_lock_lha1 locked.
+ */
+static int smp2p_out_read_v1(struct msm_smp2p_out *out_entry, uint32_t *data)
+{
+ struct smp2p_smem *smp2p_h_ptr;
+ uint32_t remote_pid;
+
+ if (!out_entry)
+ return -EINVAL;
+
+ smp2p_h_ptr = out_list[out_entry->remote_pid].smem_edge_out;
+ remote_pid = SMP2P_GET_REMOTE_PID(smp2p_h_ptr->rem_loc_proc_id);
+
+ if (remote_pid != out_entry->remote_pid)
+ return -EINVAL;
+
+ if (out_entry->l_smp2p_entry) {
+ *data = readl_relaxed(out_entry->l_smp2p_entry);
+ } else {
+ SMP2P_ERR("%s: '%s':%d not yet OPEN\n", __func__,
+ out_entry->name, remote_pid);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+/**
+ * smp2p_out_write_v1 - Writes an outbound entry value.
+ *
+ * @out_entry: Pointer to the SMP2P entry structure.
+ * @data: The data to be written.
+ * @returns: 0 on success, standard Linux error code otherwise.
+ *
+ * Must be called with out_item_lock_lha1 locked.
+ */
+static int smp2p_out_write_v1(struct msm_smp2p_out *out_entry, uint32_t data)
+{
+ struct smp2p_smem *smp2p_h_ptr;
+ uint32_t remote_pid;
+
+ if (!out_entry)
+ return -EINVAL;
+
+ smp2p_h_ptr = out_list[out_entry->remote_pid].smem_edge_out;
+ remote_pid = SMP2P_GET_REMOTE_PID(smp2p_h_ptr->rem_loc_proc_id);
+
+ if (remote_pid != out_entry->remote_pid)
+ return -EINVAL;
+
+ if (out_entry->l_smp2p_entry) {
+ writel_relaxed(data, out_entry->l_smp2p_entry);
+ smp2p_send_interrupt(remote_pid);
+ } else {
+ SMP2P_ERR("%s: '%s':%d not yet OPEN\n", __func__,
+ out_entry->name, remote_pid);
+ return -ENODEV;
+ }
+ return 0;
+}
+
+/**
+ * smp2p_out_modify_v1 - Modifies and outbound value.
+ *
+ * @set_mask: Mask containing the bits that needs to be set.
+ * @clear_mask: Mask containing the bits that needs to be cleared.
+ * @returns: 0 on success, standard Linux error code otherwise.
+ *
+ * The clear mask is applied first, so if a bit is set in both clear and
+ * set mask, the result will be that the bit is set.
+ *
+ * Must be called with out_item_lock_lha1 locked.
+ */
+static int smp2p_out_modify_v1(struct msm_smp2p_out *out_entry,
+ uint32_t set_mask, uint32_t clear_mask)
+{
+ struct smp2p_smem *smp2p_h_ptr;
+ uint32_t remote_pid;
+
+ if (!out_entry)
+ return -EINVAL;
+
+ smp2p_h_ptr = out_list[out_entry->remote_pid].smem_edge_out;
+ remote_pid = SMP2P_GET_REMOTE_PID(smp2p_h_ptr->rem_loc_proc_id);
+
+ if (remote_pid != out_entry->remote_pid)
+ return -EINVAL;
+
+ if (out_entry->l_smp2p_entry) {
+ uint32_t curr_value;
+
+ curr_value = readl_relaxed(out_entry->l_smp2p_entry);
+ writel_relaxed((curr_value & ~clear_mask) | set_mask,
+ out_entry->l_smp2p_entry);
+ } else {
+ SMP2P_ERR("%s: '%s':%d not yet OPEN\n", __func__,
+ out_entry->name, remote_pid);
+ return -ENODEV;
+ }
+
+ smp2p_send_interrupt(remote_pid);
+ return 0;
+}
+
+/**
+ * smp2p_in_validate_size_v1 - Size validation for version 1.
+ *
+ * @remote_pid: Remote processor ID.
+ * @smem_item: Pointer to the inbound SMEM item.
+ * @size: Size of the SMEM item.
+ * @returns: Validated smem_item pointer (or NULL if size is too small).
+ *
+ * Validates we don't end up with out-of-bounds array access due to invalid
+ * smem item size. If out-of-bound array access can't be avoided, then an
+ * error message is printed and NULL is returned to prevent usage of the
+ * item.
+ *
+ * Must be called with in_item_lock_lhb1 locked.
+ */
+static struct smp2p_smem *smp2p_in_validate_size_v1(int remote_pid,
+ struct smp2p_smem *smem_item, uint32_t size)
+{
+ uint32_t total_entries;
+ unsigned expected_size;
+ struct smp2p_smem *item_ptr;
+ struct smp2p_in_list_item *in_item;
+
+ if (remote_pid >= SMP2P_NUM_PROCS || !smem_item)
+ return NULL;
+
+ in_item = &in_list[remote_pid];
+ item_ptr = (struct smp2p_smem *)smem_item;
+
+ total_entries = SMP2P_GET_ENT_TOTAL(item_ptr->valid_total_ent);
+ if (total_entries > 0) {
+ in_item->safe_total_entries = total_entries;
+ in_item->item_size = size;
+
+ expected_size = sizeof(struct smp2p_smem) +
+ (total_entries * sizeof(struct smp2p_entry_v1));
+
+ if (size < expected_size) {
+ unsigned new_size;
+
+ new_size = size;
+ new_size -= sizeof(struct smp2p_smem);
+ new_size /= sizeof(struct smp2p_entry_v1);
+ in_item->safe_total_entries = new_size;
+
+ SMP2P_ERR(
+ "%s pid %d item too small for %d entries; expected: %d actual: %d; reduced to %d entries\n",
+ __func__, remote_pid, total_entries,
+ expected_size, size, new_size);
+ }
+ } else {
+ /*
+ * Total entries is 0, so the entry is still being initialized
+ * or is invalid. Either way, treat it as if the item does
+ * not exist yet.
+ */
+ in_item->safe_total_entries = 0;
+ in_item->item_size = 0;
+ }
+ return item_ptr;
+}
+
+/**
+ * smp2p_negotiate_features_v0 - Initial feature negotiation.
+ *
+ * @features: Inbound feature set.
+ * @returns: 0 (no features supported for v0).
+ */
+static uint32_t smp2p_negotiate_features_v0(uint32_t features)
+{
+ /* no supported features */
+ return 0;
+}
+
+/**
+ * smp2p_find_entry_v0 - Stub function.
+ *
+ * @item: Pointer to the smem item.
+ * @entries_total: Total number of entries in @item.
+ * @name: Name of the entry.
+ * @entry_ptr: Set to pointer of entry if found, NULL otherwise.
+ * @empty_spot: If non-null, set to the value of the next empty entry.
+ *
+ * Entries cannot be searched for until item negotiation has been completed.
+ */
+static void smp2p_find_entry_v0(struct smp2p_smem *item,
+ uint32_t entries_total, char *name, uint32_t **entry_ptr,
+ int *empty_spot)
+{
+ if (entry_ptr)
+ *entry_ptr = NULL;
+
+ if (empty_spot)
+ *empty_spot = -1;
+
+ SMP2P_ERR("%s: invalid - item negotiation incomplete\n", __func__);
+}
+
+/**
+ * smp2p_out_create_v0 - Initial creation function.
+ *
+ * @out_entry: Pointer to the SMP2P entry structure.
+ * @returns: 0 on success, standard Linux error code otherwise.
+ *
+ * If the outbound SMEM item negotiation is not complete, then
+ * this function is called to start the negotiation process.
+ * Eventually when the negotiation process is complete, this
+ * function pointer is switched with the appropriate function
+ * for the version of SMP2P being created.
+ *
+ * Must be called with out_item_lock_lha1 locked.
+ */
+static int smp2p_out_create_v0(struct msm_smp2p_out *out_entry)
+{
+ int edge_state;
+ struct smp2p_out_list_item *item_ptr;
+
+ if (!out_entry)
+ return -EINVAL;
+
+ edge_state = out_list[out_entry->remote_pid].smem_edge_state;
+
+ switch (edge_state) {
+ case SMP2P_EDGE_STATE_CLOSED:
+ /* start negotiation */
+ item_ptr = &out_list[out_entry->remote_pid];
+ edge_state = smp2p_do_negotiation(out_entry->remote_pid,
+ item_ptr);
+ break;
+
+ case SMP2P_EDGE_STATE_OPENING:
+ /* still negotiating */
+ break;
+
+ case SMP2P_EDGE_STATE_OPENED:
+ SMP2P_ERR("%s: item '%s':%d opened - wrong create called\n",
+ __func__, out_entry->name, out_entry->remote_pid);
+ break;
+
+ default:
+ SMP2P_ERR("%s: item '%s':%d invalid SMEM item state %d\n",
+ __func__, out_entry->name, out_entry->remote_pid,
+ edge_state);
+ break;
+ }
+ return 0;
+}
+
+/**
+ * smp2p_out_read_v0 - Stub function.
+ *
+ * @out_entry: Pointer to the SMP2P entry structure.
+ * @data: Out pointer, the data is available in this argument on success.
+ * @returns: -ENODEV
+ */
+static int smp2p_out_read_v0(struct msm_smp2p_out *out_entry, uint32_t *data)
+{
+ SMP2P_ERR("%s: item '%s':%d not OPEN\n",
+ __func__, out_entry->name, out_entry->remote_pid);
+
+ return -ENODEV;
+}
+
+/**
+ * smp2p_out_write_v0 - Stub function.
+ *
+ * @out_entry: Pointer to the SMP2P entry structure.
+ * @data: The data to be written.
+ * @returns: -ENODEV
+ */
+static int smp2p_out_write_v0(struct msm_smp2p_out *out_entry, uint32_t data)
+{
+ SMP2P_ERR("%s: item '%s':%d not yet OPEN\n",
+ __func__, out_entry->name, out_entry->remote_pid);
+
+ return -ENODEV;
+}
+
+/**
+ * smp2p_out_modify_v0 - Stub function.
+ *
+ * @set_mask: Mask containing the bits that needs to be set.
+ * @clear_mask: Mask containing the bits that needs to be cleared.
+ * @returns: -ENODEV
+ */
+static int smp2p_out_modify_v0(struct msm_smp2p_out *out_entry,
+ uint32_t set_mask, uint32_t clear_mask)
+{
+ SMP2P_ERR("%s: item '%s':%d not yet OPEN\n",
+ __func__, out_entry->name, out_entry->remote_pid);
+
+ return -ENODEV;
+}
+
+/**
+ * smp2p_in_validate_size_v0 - Stub function.
+ *
+ * @remote_pid: Remote processor ID.
+ * @smem_item: Pointer to the inbound SMEM item.
+ * @size: Size of the SMEM item.
+ * @returns: Validated smem_item pointer (or NULL if size is too small).
+ *
+ * Validates we don't end up with out-of-bounds array access due to invalid
+ * smem item size. If out-of-bound array access can't be avoided, then an
+ * error message is printed and NULL is returned to prevent usage of the
+ * item.
+ *
+ * Must be called with in_item_lock_lhb1 locked.
+ */
+static struct smp2p_smem *smp2p_in_validate_size_v0(int remote_pid,
+ struct smp2p_smem *smem_item, uint32_t size)
+{
+ struct smp2p_in_list_item *in_item;
+
+ if (remote_pid >= SMP2P_NUM_PROCS || !smem_item)
+ return NULL;
+
+ in_item = &in_list[remote_pid];
+
+ if (size < sizeof(struct smp2p_smem)) {
+ SMP2P_ERR(
+ "%s pid %d item size too small; expected: %d actual: %d\n",
+ __func__, remote_pid,
+ sizeof(struct smp2p_smem), size);
+ smem_item = NULL;
+ in_item->item_size = 0;
+ } else {
+ in_item->item_size = size;
+ }
+ return smem_item;
+}
+
+/**
+ * smp2p_init_header - Initializes the header of the smem item.
+ *
+ * @header_ptr: Pointer to the smp2p header.
+ * @local_pid: Local processor ID.
+ * @remote_pid: Remote processor ID.
+ * @feature: Features of smp2p implementation.
+ * @version: Version of smp2p implementation.
+ *
+ * Initializes the header as defined in the protocol specification.
+ */
+void smp2p_init_header(struct smp2p_smem *header_ptr,
+ int local_pid, int remote_pid,
+ uint32_t features, uint32_t version)
+{
+ header_ptr->magic = SMP2P_MAGIC;
+ SMP2P_SET_LOCAL_PID(header_ptr->rem_loc_proc_id, local_pid);
+ SMP2P_SET_REMOTE_PID(header_ptr->rem_loc_proc_id, remote_pid);
+ SMP2P_SET_FEATURES(header_ptr->feature_version, features);
+ SMP2P_SET_ENT_TOTAL(header_ptr->valid_total_ent, SMP2P_MAX_ENTRY);
+ SMP2P_SET_ENT_VALID(header_ptr->valid_total_ent, 0);
+ header_ptr->reserved = 0;
+
+ /* ensure that all fields are valid before version is written */
+ wmb();
+ SMP2P_SET_VERSION(header_ptr->feature_version, version);
+}
+
+/**
+ * smp2p_do_negotiation - Implements negotiation algorithm.
+ *
+ * @remote_pid: Remote processor ID.
+ * @out_item: Pointer to the outbound list item.
+ * @returns: 0 on success, standard Linux error code otherwise.
+ *
+ * Must be called with out_item_lock_lha1 locked. Will internally lock
+ * in_item_lock_lhb1.
+ */
+static int smp2p_do_negotiation(int remote_pid,
+ struct smp2p_out_list_item *out_item)
+{
+ struct smp2p_smem *r_smem_ptr, *l_smem_ptr;
+ uint32_t r_version;
+ uint32_t r_feature;
+ uint32_t l_version, l_feature;
+ int prev_state;
+
+ if (remote_pid >= SMP2P_NUM_PROCS || !out_item)
+ return -EINVAL;
+ if (out_item->smem_edge_state == SMP2P_EDGE_STATE_FAILED)
+ return -EPERM;
+
+ prev_state = out_item->smem_edge_state;
+
+ /* create local item */
+ if (!out_item->smem_edge_out) {
+ out_item->smem_edge_out = smp2p_get_local_smem_item(remote_pid);
+ if (!out_item->smem_edge_out) {
+ SMP2P_ERR(
+ "%s unable to allocate SMEM item for pid %d\n",
+ __func__, remote_pid);
+ return -ENODEV;
+ }
+ out_item->smem_edge_state = SMP2P_EDGE_STATE_OPENING;
+ }
+ l_smem_ptr = out_item->smem_edge_out;
+
+ /* retrieve remote side and version */
+ spin_lock(&in_list[remote_pid].in_item_lock_lhb1);
+ r_smem_ptr = smp2p_get_remote_smem_item(remote_pid, out_item);
+ spin_unlock(&in_list[remote_pid].in_item_lock_lhb1);
+
+ r_version = 0;
+ if (r_smem_ptr) {
+ r_version = SMP2P_GET_VERSION(r_smem_ptr->feature_version);
+ r_feature = SMP2P_GET_FEATURES(r_smem_ptr->feature_version);
+ }
+
+ if (r_version == 0) {
+ /*
+ * Either remote side doesn't exist, or is in the
+ * process of being initialized (the version is set last).
+ *
+ * In either case, treat as if the other side doesn't exist
+ * and write out our maximum supported version.
+ */
+ r_smem_ptr = NULL;
+ r_version = ARRAY_SIZE(version_if) - 1;
+ r_feature = ~0U;
+ }
+
+ /* find maximum supported version and feature set */
+ l_version = min(r_version, ARRAY_SIZE(version_if) - 1);
+ for (; l_version > 0; --l_version) {
+ if (!version_if[l_version].is_supported)
+ continue;
+
+ /* found valid version */
+ l_feature = version_if[l_version].negotiate_features(~0U);
+ if (l_version == r_version)
+ l_feature &= r_feature;
+ break;
+ }
+
+ if (l_version == 0) {
+ SMP2P_ERR(
+ "%s: negotiation failure pid %d: RV %d RF %x\n",
+ __func__, remote_pid, r_version, r_feature
+ );
+ SMP2P_SET_VERSION(l_smem_ptr->feature_version,
+ SMP2P_EDGE_STATE_FAILED);
+ smp2p_send_interrupt(remote_pid);
+ out_item->smem_edge_state = SMP2P_EDGE_STATE_FAILED;
+ return -EPERM;
+ }
+
+ /* update header and notify remote side */
+ smp2p_init_header(l_smem_ptr, SMP2P_APPS_PROC, remote_pid,
+ l_feature, l_version);
+ smp2p_send_interrupt(remote_pid);
+
+ /* handle internal state changes */
+ if (r_smem_ptr && l_version == r_version &&
+ l_feature == r_feature) {
+ struct msm_smp2p_out *pos;
+
+ /* negotiation complete */
+ out_item->smem_edge_state = SMP2P_EDGE_STATE_OPENED;
+ out_item->ops_ptr = &version_if[l_version];
+ SMP2P_INFO(
+ "%s: negotiation complete pid %d: State %d->%d F0x%08x\n",
+ __func__, remote_pid, prev_state,
+ out_item->smem_edge_state, l_feature);
+
+ /* create any pending outbound entries */
+ list_for_each_entry(pos, &out_item->list, out_edge_list) {
+ out_item->ops_ptr->create_entry(pos);
+ }
+
+ /* update inbound edge */
+ spin_lock(&in_list[remote_pid].in_item_lock_lhb1);
+ (void)out_item->ops_ptr->validate_size(remote_pid, r_smem_ptr,
+ in_list[remote_pid].item_size);
+ in_list[remote_pid].smem_edge_in = r_smem_ptr;
+ spin_unlock(&in_list[remote_pid].in_item_lock_lhb1);
+ } else {
+ SMP2P_INFO("%s: negotiation pid %d: State %d->%d F0x%08x\n",
+ __func__, remote_pid, prev_state,
+ out_item->smem_edge_state, l_feature);
+ }
+ return 0;
+}
+
+/**
+ * msm_smp2p_out_open - Opens an outbound entry.
+ *
+ * @remote_pid: Outbound processor ID.
+ * @name: Name of the entry.
+ * @open_notifier: Notifier block for the open notification.
+ * @handle: Handle to the smem entry structure.
+ * @returns: 0 on success, standard Linux error code otherwise.
+ *
+ * Opens an outbound entry with the name specified by entry, from the
+ * local processor to the remote processor(remote_pid). If the entry, remote_pid
+ * and open_notifier are valid, then handle will be set and zero will be
+ * returned. The smem item that holds this entry will be created if it has
+ * not been created according to the version negotiation algorithm.
+ * The open_notifier will be used to notify the clients about the
+ * availability of the entry.
+ */
+int msm_smp2p_out_open(int remote_pid, const char *name,
+ struct notifier_block *open_notifier,
+ struct msm_smp2p_out **handle)
+{
+ struct msm_smp2p_out *out_entry;
+ struct msm_smp2p_out *pos;
+ int ret = 0;
+ unsigned long flags;
+
+ if (handle)
+ *handle = NULL;
+
+ if (remote_pid >= SMP2P_NUM_PROCS || !name || !open_notifier || !handle)
+ return -EINVAL;
+
+ /* Allocate the smp2p object and node */
+ out_entry = kzalloc(sizeof(*out_entry), GFP_KERNEL);
+ if (!out_entry)
+ return -ENOMEM;
+
+ /* Handle duplicate registration */
+ spin_lock_irqsave(&out_list[remote_pid].out_item_lock_lha1, flags);
+ list_for_each_entry(pos, &out_list[remote_pid].list,
+ out_edge_list) {
+ if (!strcmp(pos->name, name)) {
+ spin_unlock_irqrestore(
+ &out_list[remote_pid].out_item_lock_lha1,
+ flags);
+ kfree(out_entry);
+ SMP2P_ERR("%s: duplicate registration '%s':%d\n",
+ __func__, name, remote_pid);
+ return -EBUSY;
+ }
+ }
+
+ out_entry->remote_pid = remote_pid;
+ RAW_INIT_NOTIFIER_HEAD(&out_entry->msm_smp2p_notifier_list);
+ strlcpy(out_entry->name, name, SMP2P_MAX_ENTRY_NAME);
+ out_entry->open_nb = open_notifier;
+ raw_notifier_chain_register(&out_entry->msm_smp2p_notifier_list,
+ out_entry->open_nb);
+ list_add(&out_entry->out_edge_list, &out_list[remote_pid].list);
+
+ ret = out_list[remote_pid].ops_ptr->create_entry(out_entry);
+ if (ret) {
+ list_del(&out_entry->out_edge_list);
+ raw_notifier_chain_unregister(
+ &out_entry->msm_smp2p_notifier_list,
+ out_entry->open_nb);
+ spin_unlock_irqrestore(
+ &out_list[remote_pid].out_item_lock_lha1, flags);
+ kfree(out_entry);
+ SMP2P_ERR("%s: unable to open '%s':%d error %d\n",
+ __func__, name, remote_pid, ret);
+ return ret;
+ }
+ spin_unlock_irqrestore(&out_list[remote_pid].out_item_lock_lha1,
+ flags);
+ *handle = out_entry;
+
+ return 0;
+}
+EXPORT_SYMBOL(msm_smp2p_out_open);
+
+/**
+ * msm_smp2p_out_close - Closes the handle to an outbound entry.
+ *
+ * @handle: Pointer to smp2p out entry handle.
+ * @returns: 0 on success, standard Linux error code otherwise.
+ *
+ * The actual entry will not be deleted and can be re-opened at a later
+ * time. The handle will be set to NULL.
+ */
+int msm_smp2p_out_close(struct msm_smp2p_out **handle)
+{
+ unsigned long flags;
+ struct msm_smp2p_out *out_entry;
+ struct smp2p_out_list_item *out_item;
+
+ if (!handle || !*handle)
+ return -EINVAL;
+
+ out_entry = *handle;
+ *handle = NULL;
+
+ out_item = &out_list[out_entry->remote_pid];
+ spin_lock_irqsave(&out_item->out_item_lock_lha1, flags);
+ list_del(&out_entry->out_edge_list);
+ raw_notifier_chain_unregister(&out_entry->msm_smp2p_notifier_list,
+ out_entry->open_nb);
+ spin_unlock_irqrestore(&out_item->out_item_lock_lha1, flags);
+
+ kfree(out_entry);
+
+ return 0;
+}
+EXPORT_SYMBOL(msm_smp2p_out_close);
+
+/**
+ * msm_smp2p_out_read - Allows reading the entry.
+ *
+ * @handle: Handle to the smem entry structure.
+ * @data: Out pointer that holds the read data.
+ * @returns: 0 on success, standard Linux error code otherwise.
+ *
+ * Allows reading of the outbound entry for read-modify-write
+ * operation.
+ */
+int msm_smp2p_out_read(struct msm_smp2p_out *handle, uint32_t *data)
+{
+ int ret = -EINVAL;
+ unsigned long flags;
+ struct smp2p_out_list_item *out_item;
+
+ if (!handle || !data)
+ return ret;
+
+ out_item = &out_list[handle->remote_pid];
+ spin_lock_irqsave(&out_item->out_item_lock_lha1, flags);
+ ret = out_item->ops_ptr->read_entry(handle, data);
+ spin_unlock_irqrestore(&out_item->out_item_lock_lha1, flags);
+
+ return ret;
+}
+EXPORT_SYMBOL(msm_smp2p_out_read);
+
+/**
+ * msm_smp2p_out_write - Allows writing to the entry.
+ *
+ * @handle: Handle to smem entry structure.
+ * @data: Data that has to be written.
+ * @returns: 0 on success, standard Linux error code otherwise.
+ *
+ * Writes a new value to the output entry. Multiple back-to-back writes
+ * may overwrite previous writes before the remote processor get a chance
+ * to see them leading to ABA race condition. The client must implement
+ * their own synchronization mechanism (such as echo mechanism) if this is
+ * not acceptable.
+ */
+int msm_smp2p_out_write(struct msm_smp2p_out *handle, uint32_t data)
+{
+ int ret = -EINVAL;
+ unsigned long flags;
+ struct smp2p_out_list_item *out_item;
+
+ if (!handle)
+ return ret;
+
+ out_item = &out_list[handle->remote_pid];
+ spin_lock_irqsave(&out_item->out_item_lock_lha1, flags);
+ ret = out_item->ops_ptr->write_entry(handle, data);
+ spin_unlock_irqrestore(&out_item->out_item_lock_lha1, flags);
+
+ return ret;
+
+}
+EXPORT_SYMBOL(msm_smp2p_out_write);
+
+/**
+ * msm_smp2p_out_modify - Modifies the entry.
+ *
+ * @handle: Handle to the smem entry structure.
+ * @set_mask: Specifies the bits that needs to be set.
+ * @clear_mask: Specifies the bits that needs to be cleared.
+ * @returns: 0 on success, standard Linux error code otherwise.
+ *
+ * The modification is done by doing a bitwise AND of clear mask followed by
+ * the bit wise OR of set mask. The clear bit mask is applied first to the
+ * data, so if a bit is set in both the clear mask and the set mask, then in
+ * the result is a set bit. Multiple back-to-back modifications may overwrite
+ * previous values before the remote processor gets a chance to see them
+ * leading to ABA race condition. The client must implement their own
+ * synchronization mechanism (such as echo mechanism) if this is not
+ * acceptable.
+ */
+int msm_smp2p_out_modify(struct msm_smp2p_out *handle, uint32_t set_mask,
+ uint32_t clear_mask)
+{
+ int ret = -EINVAL;
+ unsigned long flags;
+ struct smp2p_out_list_item *out_item;
+
+ if (!handle)
+ return ret;
+
+ out_item = &out_list[handle->remote_pid];
+ spin_lock_irqsave(&out_item->out_item_lock_lha1, flags);
+ ret = out_item->ops_ptr->modify_entry(handle, set_mask, clear_mask);
+ spin_unlock_irqrestore(&out_item->out_item_lock_lha1, flags);
+
+ return ret;
+}
+EXPORT_SYMBOL(msm_smp2p_out_modify);
+
+/**
+ * msm_smp2p_in_read - Read an entry on a remote processor.
+ *
+ * @remote_pid: Processor ID of the remote processor.
+ * @name: Name of the entry that is to be read.
+ * @data: Output pointer, the value will be placed here if successful.
+ * @returns: 0 on success, standard Linux error code otherwise.
+ */
+int msm_smp2p_in_read(int remote_pid, const char *name, uint32_t *data)
+{
+ unsigned long flags;
+ struct smp2p_out_list_item *out_item;
+ uint32_t *entry_ptr;
+
+ if (remote_pid >= SMP2P_NUM_PROCS)
+ return -EINVAL;
+
+ out_item = &out_list[remote_pid];
+ spin_lock_irqsave(&out_item->out_item_lock_lha1, flags);
+ spin_lock(&in_list[remote_pid].in_item_lock_lhb1);
+
+ if (in_list[remote_pid].smem_edge_in)
+ out_item->ops_ptr->find_entry(
+ in_list[remote_pid].smem_edge_in,
+ in_list[remote_pid].safe_total_entries,
+ (char *)name, &entry_ptr, NULL);
+
+ spin_unlock(&in_list[remote_pid].in_item_lock_lhb1);
+ spin_unlock_irqrestore(&out_item->out_item_lock_lha1, flags);
+
+ if (!entry_ptr)
+ return -ENODEV;
+
+ *data = readl_relaxed(entry_ptr);
+ return 0;
+}
+EXPORT_SYMBOL(msm_smp2p_in_read);
+
+/**
+ * msm_smp2p_in_register - Notifies the change in value of the entry.
+ *
+ * @pid: Remote processor ID.
+ * @name: Name of the entry.
+ * @in_notifier: Notifier block used to notify about the event.
+ * @returns: 0 on success, standard Linux error code otherwise.
+ *
+ * Register for change notifications for a remote entry. If the remote entry
+ * does not exist yet, then the registration request will be held until the
+ * remote side opens. Once the entry is open, then the SMP2P_OPEN notification
+ * will be sent. Any changes to the entry will trigger a call to the notifier
+ * block with an SMP2P_ENTRY_UPDATE event and the data field will point to an
+ * msm_smp2p_update_notif structure containing the current and previous value.
+ */
+int msm_smp2p_in_register(int pid, const char *name,
+ struct notifier_block *in_notifier)
+{
+ struct smp2p_in *pos;
+ struct smp2p_in *in = NULL;
+ int ret;
+ unsigned long flags;
+ struct msm_smp2p_update_notif data;
+ uint32_t *entry_ptr;
+
+ if (pid >= SMP2P_NUM_PROCS || !name || !in_notifier)
+ return -EINVAL;
+
+ /* Pre-allocate before spinlock since we will likely needed it */
+ in = kzalloc(sizeof(*in), GFP_KERNEL);
+ if (!in)
+ return -ENOMEM;
+
+ /* Search for existing entry */
+ spin_lock_irqsave(&out_list[pid].out_item_lock_lha1, flags);
+ spin_lock(&in_list[pid].in_item_lock_lhb1);
+
+ list_for_each_entry(pos, &in_list[pid].list, in_edge_list) {
+ if (!strncmp(pos->name, name,
+ SMP2P_MAX_ENTRY_NAME)) {
+ kfree(in);
+ in = pos;
+ break;
+ }
+ }
+
+ /* Create and add it to the list */
+ in->remote_pid = pid;
+ strlcpy(in->name, name, SMP2P_MAX_ENTRY_NAME);
+ RAW_INIT_NOTIFIER_HEAD(&in->in_notifier_list);
+ list_add(&in->in_edge_list, &in_list[pid].list);
+ ret = raw_notifier_chain_register(&in->in_notifier_list,
+ in_notifier);
+ if (ret) {
+ SMP2P_DBG("%s: '%s':%d failed %d\n", __func__, name, pid, ret);
+ goto bail;
+ }
+ in->notifier_count++;
+
+ if (out_list[pid].smem_edge_state == SMP2P_EDGE_STATE_OPENED) {
+ out_list[pid].ops_ptr->find_entry(
+ in_list[pid].smem_edge_in,
+ in_list[pid].safe_total_entries, (char *)name,
+ &entry_ptr, NULL);
+ if (entry_ptr) {
+ in->entry_ptr = entry_ptr;
+ in->entry_val = *(entry_ptr);
+
+ data.previous_value = in->entry_val;
+ data.current_value = *(in->entry_ptr);
+ in_notifier->notifier_call(in_notifier, SMP2P_OPEN,
+ (void *)&data);
+ }
+ }
+ SMP2P_DBG("%s: '%s':%d registered\n", __func__, name, pid);
+
+bail:
+ spin_unlock(&in_list[pid].in_item_lock_lhb1);
+ spin_unlock_irqrestore(&out_list[pid].out_item_lock_lha1, flags);
+ return ret;
+
+}
+EXPORT_SYMBOL(msm_smp2p_in_register);
+
+/**
+ * msm_smp2p_in_unregister - Unregister the notifier for remote entry.
+ *
+ * @remote_pid: Processor Id of the remote processor.
+ * @name: The name of the entry.
+ * @in_notifier: Notifier block passed during registration.
+ * @returns: 0 on success, standard Linux error code otherwise.
+ */
+int msm_smp2p_in_unregister(int remote_pid, const char *name,
+ struct notifier_block *in_notifier)
+{
+ struct smp2p_in *pos;
+ struct smp2p_in *in = NULL;
+ int ret = -ENODEV;
+ unsigned long flags;
+
+ if (remote_pid >= SMP2P_NUM_PROCS || !name || !in_notifier)
+ return -EINVAL;
+
+ spin_lock_irqsave(&in_list[remote_pid].in_item_lock_lhb1, flags);
+ list_for_each_entry(pos, &in_list[remote_pid].list,
+ in_edge_list) {
+ if (!strncmp(pos->name, name, SMP2P_MAX_ENTRY_NAME)) {
+ in = pos;
+ break;
+ }
+ }
+ if (!in)
+ goto fail;
+
+ ret = raw_notifier_chain_unregister(&pos->in_notifier_list,
+ in_notifier);
+ if (ret == 0) {
+ pos->notifier_count--;
+ if (!pos->notifier_count) {
+ list_del(&pos->in_edge_list);
+ kfree(pos);
+ ret = 0;
+ }
+ } else {
+ SMP2P_ERR("%s: unregister failure '%s':%d\n", __func__,
+ name, remote_pid);
+ ret = -ENODEV;
+ }
+
+fail:
+ spin_unlock_irqrestore(&in_list[remote_pid].in_item_lock_lhb1, flags);
+
+ return ret;
+}
+EXPORT_SYMBOL(msm_smp2p_in_unregister);
+
+/**
+ * smp2p_send_interrupt - Send interrupt to remote system.
+ *
+ * @remote_pid: Processor ID of the remote system
+ *
+ * Must be called with out_item_lock_lha1 locked.
+ */
+static void smp2p_send_interrupt(int remote_pid)
+{
+ if (smp2p_int_cfgs[remote_pid].name)
+ SMP2P_DBG("SMP2P Int Apps->%s(%d)\n",
+ smp2p_int_cfgs[remote_pid].name, remote_pid);
+
+ ++smp2p_int_cfgs[remote_pid].out_interrupt_count;
+ if (remote_pid != SMP2P_REMOTE_MOCK_PROC &&
+ smp2p_int_cfgs[remote_pid].out_int_mask) {
+ /* flush any pending writes before triggering interrupt */
+ wmb();
+ writel_relaxed(smp2p_int_cfgs[remote_pid].out_int_mask,
+ smp2p_int_cfgs[remote_pid].out_int_ptr);
+ } else {
+ smp2p_remote_mock_rx_interrupt();
+ }
+}
+
+/**
+ * smp2p_in_edge_notify - Notifies the entry changed on remote processor.
+ *
+ * @pid: Processor ID of the remote processor.
+ *
+ * This function is invoked on an incoming interrupt, it scans
+ * the list of the clients registered for the entries on the remote
+ * processor and notifies them if the data changes.
+ *
+ * Must be called with out_item_lock_lha1 locked.
+ */
+static void smp2p_in_edge_notify(int pid)
+{
+ struct smp2p_in *pos;
+ uint32_t *entry_ptr;
+ unsigned long flags;
+ struct smp2p_smem *smem_h_ptr;
+ uint32_t curr_data;
+ struct msm_smp2p_update_notif data;
+
+ spin_lock_irqsave(&in_list[pid].in_item_lock_lhb1, flags);
+ smem_h_ptr = in_list[pid].smem_edge_in;
+ if (!smem_h_ptr) {
+ SMP2P_DBG("%s: No remote SMEM item for pid %d\n",
+ __func__, pid);
+ spin_unlock_irqrestore(&in_list[pid].in_item_lock_lhb1, flags);
+ return;
+ }
+
+ list_for_each_entry(pos, &in_list[pid].list, in_edge_list) {
+ if (pos->entry_ptr != NULL) {
+ /* entry already open */
+ curr_data = *(pos->entry_ptr);
+ if (curr_data != pos->entry_val) {
+ data.previous_value = pos->entry_val;
+ data.current_value = curr_data;
+ pos->entry_val = curr_data;
+ raw_notifier_call_chain(
+ &pos->in_notifier_list,
+ SMP2P_ENTRY_UPDATE, (void *)&data);
+ }
+ } else {
+ /* entry not open - try to open it */
+ out_list[pid].ops_ptr->find_entry(smem_h_ptr,
+ in_list[pid].safe_total_entries, pos->name,
+ &entry_ptr, NULL);
+
+ if (entry_ptr) {
+ pos->entry_ptr = entry_ptr;
+ data.previous_value = 0;
+ data.current_value =
+ *(entry_ptr);
+ raw_notifier_call_chain(
+ &pos->in_notifier_list,
+ SMP2P_OPEN, (void *)&data);
+ }
+ }
+ }
+ spin_unlock_irqrestore(&in_list[pid].in_item_lock_lhb1, flags);
+}
+
+/**
+ * smp2p_interrupt_handler - Incoming interrupt handler.
+ *
+ * @irq: Interrupt ID
+ * @data: Edge
+ * @returns: IRQ_HANDLED or IRQ_NONE for invalid interrupt
+ */
+static irqreturn_t smp2p_interrupt_handler(int irq, void *data)
+{
+ unsigned long flags;
+ uint32_t remote_pid = (uint32_t)data;
+
+ if (remote_pid >= SMP2P_NUM_PROCS) {
+ SMP2P_ERR("%s: invalid interrupt pid %d\n",
+ __func__, remote_pid);
+ return IRQ_NONE;
+ }
+
+ if (smp2p_int_cfgs[remote_pid].name)
+ SMP2P_DBG("SMP2P Int %s(%d)->Apps\n",
+ smp2p_int_cfgs[remote_pid].name, remote_pid);
+
+ spin_lock_irqsave(&out_list[remote_pid].out_item_lock_lha1, flags);
+ ++smp2p_int_cfgs[remote_pid].in_interrupt_count;
+
+ if (out_list[remote_pid].smem_edge_state != SMP2P_EDGE_STATE_OPENED)
+ smp2p_do_negotiation(remote_pid, &out_list[remote_pid]);
+
+ if (out_list[remote_pid].smem_edge_state == SMP2P_EDGE_STATE_OPENED)
+ smp2p_in_edge_notify(remote_pid);
+ spin_unlock_irqrestore(&out_list[remote_pid].out_item_lock_lha1, flags);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * smp2p_reset_mock_edge - Reinitializes the mock edge.
+ *
+ * @returns: 0 on success, -EAGAIN to retry later.
+ *
+ * Reinitializes the mock edge to initial power-up state values.
+ */
+int smp2p_reset_mock_edge(void)
+{
+ const int rpid = SMP2P_REMOTE_MOCK_PROC;
+ unsigned long flags;
+ int ret = 0;
+
+ spin_lock_irqsave(&out_list[rpid].out_item_lock_lha1, flags);
+ spin_lock(&in_list[rpid].in_item_lock_lhb1);
+
+ if (!list_empty(&out_list[rpid].list) ||
+ !list_empty(&in_list[rpid].list)) {
+ ret = -EAGAIN;
+ goto fail;
+ }
+
+ kfree(out_list[rpid].smem_edge_out);
+ out_list[rpid].smem_edge_out = NULL;
+ out_list[rpid].ops_ptr = &version_if[0];
+ out_list[rpid].smem_edge_state = SMP2P_EDGE_STATE_CLOSED;
+
+ in_list[rpid].smem_edge_in = NULL;
+ in_list[rpid].item_size = 0;
+ in_list[rpid].safe_total_entries = 0;
+
+fail:
+ spin_unlock(&in_list[rpid].in_item_lock_lhb1);
+ spin_unlock_irqrestore(&out_list[rpid].out_item_lock_lha1, flags);
+
+ return ret;
+}
+
+/**
+ * msm_smp2p_interrupt_handler - Triggers incoming interrupt.
+ *
+ * @remote_pid: Remote processor ID
+ *
+ * This function is used with the remote mock infrastructure
+ * used for testing. It simulates triggering of interrupt in
+ * a testing environment.
+ */
+void msm_smp2p_interrupt_handler(int remote_pid)
+{
+ smp2p_interrupt_handler(0, (void *)remote_pid);
+}
+
+/**
+ * msm_smp2p_probe - Device tree probe function.
+ *
+ * @pdev: Pointer to device tree data.
+ * @returns: 0 on success; -ENODEV otherwise
+ */
+static int __devinit msm_smp2p_probe(struct platform_device *pdev)
+{
+ struct resource *irq_out_base;
+ struct resource *irq_offset;
+ char *key;
+ uint32_t edge;
+ int ret;
+ struct device_node *node;
+ uint32_t irq_bitmask;
+ uint32_t irq_line;
+
+ node = pdev->dev.of_node;
+
+ key = "qcom,remote-pid";
+ ret = of_property_read_u32(node, key, &edge);
+ if (ret) {
+ SMP2P_ERR("%s: missing edge '%s'\n", __func__, key);
+ goto fail;
+ }
+
+ key = "irq-reg-base";
+ irq_out_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, key);
+ if (!irq_out_base)
+ goto missing_key;
+
+ key = "irq-reg-offset";
+ irq_offset = platform_get_resource_byname(pdev, IORESOURCE_MEM, key);
+ if (!irq_offset)
+ goto missing_key;
+
+ key = "qcom,irq-bitmask";
+ ret = of_property_read_u32(node, key, &irq_bitmask);
+ if (ret)
+ goto missing_key;
+
+ key = "interrupts";
+ irq_line = platform_get_irq(pdev, 0);
+ if (irq_line == -ENXIO)
+ goto missing_key;
+
+ ret = request_irq(irq_line, smp2p_interrupt_handler,
+ IRQF_TRIGGER_RISING, "smp2p", (void *)edge);
+ if (ret < 0) {
+ SMP2P_ERR("%s: request_irq() failed on %d (edge %d)\n",
+ __func__, irq_line, edge);
+ goto fail;
+ }
+
+ ret = enable_irq_wake(irq_line);
+ if (ret < 0)
+ SMP2P_ERR("%s: enable_irq_wake() failed on %d (edge %d)\n",
+ __func__, irq_line, edge);
+
+ /*
+ * Set entry (keep is_configured last to prevent usage before
+ * initialization).
+ */
+ smp2p_int_cfgs[edge].in_int_id = irq_line;
+ smp2p_int_cfgs[edge].out_int_mask = irq_bitmask;
+ smp2p_int_cfgs[edge].out_int_ptr =
+ (uint32_t *)((uint32_t)irq_out_base->start +
+ (uint32_t)irq_offset->start);
+ smp2p_int_cfgs[edge].is_configured = true;
+ return 0;
+
+missing_key:
+ SMP2P_ERR("%s: missing '%s' for edge %d\n", __func__, key, edge);
+fail:
+ return -ENODEV;
+}
+
+static struct of_device_id msm_smp2p_match_table[] = {
+ { .compatible = "qcom,smp2p" },
+ {},
+};
+
+static struct platform_driver msm_smp2p_driver = {
+ .probe = msm_smp2p_probe,
+ .driver = {
+ .name = "msm_smp2p",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_smp2p_match_table,
+ },
+};
+
+/**
+ * msm_smp2p_init - Initialization function for the module.
+ *
+ * @returns: 0 on success, standard Linux error code otherwise.
+ */
+static int __init msm_smp2p_init(void)
+{
+ int i;
+ int rc;
+
+ for (i = 0; i < SMP2P_NUM_PROCS; i++) {
+ spin_lock_init(&out_list[i].out_item_lock_lha1);
+ INIT_LIST_HEAD(&out_list[i].list);
+ out_list[i].smem_edge_out = NULL;
+ out_list[i].smem_edge_state = SMP2P_EDGE_STATE_CLOSED;
+ out_list[i].ops_ptr = &version_if[0];
+
+ spin_lock_init(&in_list[i].in_item_lock_lhb1);
+ INIT_LIST_HEAD(&in_list[i].list);
+ in_list[i].smem_edge_in = NULL;
+ }
+
+ log_ctx = ipc_log_context_create(NUM_LOG_PAGES, "smp2p");
+ if (!log_ctx)
+ SMP2P_ERR("%s: unable to create log context\n", __func__);
+
+ rc = platform_driver_register(&msm_smp2p_driver);
+ if (rc) {
+ SMP2P_ERR("%s: msm_smp2p_driver register failed %d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ return 0;
+}
+module_init(msm_smp2p_init);
+
+MODULE_DESCRIPTION("MSM Shared Memory Point to Point");
+MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/mach-msm/smp2p_debug.c b/arch/arm/mach-msm/smp2p_debug.c
new file mode 100644
index 0000000..1a5c96e
--- /dev/null
+++ b/arch/arm/mach-msm/smp2p_debug.c
@@ -0,0 +1,328 @@
+/* arch/arm/mach-msm/smp2p_debug.c
+ *
+ * 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/ctype.h>
+#include <linux/list.h>
+#include <linux/debugfs.h>
+#include "smp2p_private.h"
+
+#if defined(CONFIG_DEBUG_FS)
+
+/**
+ * Dump interrupt statistics.
+ *
+ * @s: pointer to output file
+ */
+static void smp2p_int_stats(struct seq_file *s)
+{
+ struct smp2p_interrupt_config *int_cfg;
+ int pid;
+
+ int_cfg = smp2p_get_interrupt_config();
+ if (!int_cfg)
+ return;
+
+ seq_printf(s, "| Processor | Incoming Id | Incoming # |");
+ seq_printf(s, " Outgoing # | Base Ptr | Mask |\n");
+
+ for (pid = 0; pid < SMP2P_NUM_PROCS; ++pid) {
+ if (!int_cfg[pid].is_configured &&
+ pid != SMP2P_REMOTE_MOCK_PROC)
+ continue;
+
+ seq_printf(s,
+ "| %5s (%d) | %11u | %10u | %10u | %p | %08x |\n",
+ int_cfg[pid].name,
+ pid, int_cfg[pid].in_int_id,
+ int_cfg[pid].in_interrupt_count,
+ int_cfg[pid].out_interrupt_count,
+ int_cfg[pid].out_int_ptr,
+ int_cfg[pid].out_int_mask);
+ }
+}
+
+/**
+ * Dump item header line 1.
+ *
+ * @buf: output buffer
+ * @max: length of output buffer
+ * @item_ptr: SMEM item pointer
+ * @state: item state
+ * @returns: Number of bytes written to output buffer
+ */
+static int smp2p_item_header1(char *buf, int max, struct smp2p_smem *item_ptr,
+ enum msm_smp2p_edge_state state)
+{
+ int i = 0;
+ const char *state_text;
+
+ if (!item_ptr) {
+ i += scnprintf(buf + i, max - i, "None");
+ return i;
+ }
+
+ switch (state) {
+ case SMP2P_EDGE_STATE_CLOSED:
+ state_text = "State: Closed";
+ break;
+ case SMP2P_EDGE_STATE_OPENING:
+ state_text = "State: Opening";
+ break;
+ case SMP2P_EDGE_STATE_OPENED:
+ state_text = "State: Opened";
+ break;
+ default:
+ state_text = "";
+ break;
+ }
+
+ i += scnprintf(buf + i, max - i,
+ "%-14s LPID %d RPID %d",
+ state_text,
+ SMP2P_GET_LOCAL_PID(item_ptr->rem_loc_proc_id),
+ SMP2P_GET_REMOTE_PID(item_ptr->rem_loc_proc_id)
+ );
+
+ return i;
+}
+
+/**
+ * Dump item header line 2.
+ *
+ * @buf: output buffer
+ * @max: length of output buffer
+ * @item_ptr: SMEM item pointer
+ * @returns: Number of bytes written to output buffer
+ */
+static int smp2p_item_header2(char *buf, int max, struct smp2p_smem *item_ptr)
+{
+ int i = 0;
+
+ if (!item_ptr) {
+ i += scnprintf(buf + i, max - i, "None");
+ return i;
+ }
+
+ i += scnprintf(buf + i, max - i,
+ "Version: %08x Features: %08x",
+ SMP2P_GET_VERSION(item_ptr->feature_version),
+ SMP2P_GET_FEATURES(item_ptr->feature_version)
+ );
+
+ return i;
+}
+
+/**
+ * Dump item header line 3.
+ *
+ * @buf: output buffer
+ * @max: length of output buffer
+ * @item_ptr: SMEM item pointer
+ * @state: item state
+ * @returns: Number of bytes written to output buffer
+ */
+static int smp2p_item_header3(char *buf, int max, struct smp2p_smem *item_ptr)
+{
+ int i = 0;
+
+ if (!item_ptr) {
+ i += scnprintf(buf + i, max - i, "None");
+ return i;
+ }
+
+ i += scnprintf(buf + i, max - i,
+ "Entries Valid/Max: %d/%d",
+ SMP2P_GET_ENT_VALID(item_ptr->valid_total_ent),
+ SMP2P_GET_ENT_TOTAL(item_ptr->valid_total_ent)
+ );
+
+ return i;
+}
+
+/**
+ * Dump individual input/output item pair.
+ *
+ * @s: pointer to output file
+ */
+static void smp2p_item(struct seq_file *s, int remote_pid)
+{
+ struct smp2p_smem *out_ptr;
+ struct smp2p_smem *in_ptr;
+ struct smp2p_interrupt_config *int_cfg;
+ char tmp_buff[64];
+ int state;
+ int entry;
+ struct smp2p_entry_v1 *out_entries = NULL;
+ struct smp2p_entry_v1 *in_entries = NULL;
+ int out_valid = 0;
+ int in_valid = 0;
+
+ int_cfg = smp2p_get_interrupt_config();
+ if (!int_cfg)
+ return;
+ if (!int_cfg[remote_pid].is_configured &&
+ remote_pid != SMP2P_REMOTE_MOCK_PROC)
+ return;
+
+ out_ptr = smp2p_get_out_item(remote_pid, &state);
+ in_ptr = smp2p_get_in_item(remote_pid);
+
+ if (!out_ptr && !in_ptr)
+ return;
+
+ /* print item headers */
+ seq_printf(s, "%s%s\n",
+ " ====================================== ",
+ "======================================");
+ scnprintf(tmp_buff, sizeof(tmp_buff),
+ "Apps(%d)->%s(%d)",
+ SMP2P_APPS_PROC, int_cfg[remote_pid].name, remote_pid);
+ seq_printf(s, "| %-37s", tmp_buff);
+
+ scnprintf(tmp_buff, sizeof(tmp_buff),
+ "%s(%d)->Apps(%d)",
+ int_cfg[remote_pid].name, remote_pid, SMP2P_APPS_PROC);
+ seq_printf(s, "| %-37s|\n", tmp_buff);
+ seq_printf(s, "%s%s\n",
+ " ====================================== ",
+ "======================================");
+
+ smp2p_item_header1(tmp_buff, sizeof(tmp_buff), out_ptr, state);
+ seq_printf(s, "| %-37s", tmp_buff);
+ smp2p_item_header1(tmp_buff, sizeof(tmp_buff), in_ptr, -1);
+ seq_printf(s, "| %-37s|\n", tmp_buff);
+
+ smp2p_item_header2(tmp_buff, sizeof(tmp_buff), out_ptr);
+ seq_printf(s, "| %-37s", tmp_buff);
+ smp2p_item_header2(tmp_buff, sizeof(tmp_buff), in_ptr);
+ seq_printf(s, "| %-37s|\n", tmp_buff);
+
+ smp2p_item_header3(tmp_buff, sizeof(tmp_buff), out_ptr);
+ seq_printf(s, "| %-37s", tmp_buff);
+ smp2p_item_header3(tmp_buff, sizeof(tmp_buff), in_ptr);
+ seq_printf(s, "| %-37s|\n", tmp_buff);
+
+ seq_printf(s, " %s%s\n",
+ "-------------------------------------- ",
+ "--------------------------------------");
+ seq_printf(s, "| %-37s",
+ "Entry Name Value");
+ seq_printf(s, "| %-37s|\n",
+ "Entry Name Value");
+ seq_printf(s, " %s%s\n",
+ "-------------------------------------- ",
+ "--------------------------------------");
+
+ /* print entries */
+ if (out_ptr) {
+ out_entries = (struct smp2p_entry_v1 *)((void *)out_ptr +
+ sizeof(struct smp2p_smem));
+ out_valid = SMP2P_GET_ENT_VALID(out_ptr->valid_total_ent);
+ }
+
+ if (in_ptr) {
+ in_entries = (struct smp2p_entry_v1 *)((void *)in_ptr +
+ sizeof(struct smp2p_smem));
+ in_valid = SMP2P_GET_ENT_VALID(out_ptr->valid_total_ent);
+ }
+
+ for (entry = 0; out_entries || in_entries; ++entry) {
+ if (out_entries && entry < out_valid) {
+ scnprintf(tmp_buff, sizeof(tmp_buff),
+ "%-16s 0x%08x",
+ out_entries->name,
+ out_entries->entry);
+ ++out_entries;
+ } else {
+ out_entries = NULL;
+ scnprintf(tmp_buff, sizeof(tmp_buff), "None");
+ }
+ seq_printf(s, "| %-37s", tmp_buff);
+
+ if (in_entries && entry < in_valid) {
+ scnprintf(tmp_buff, sizeof(tmp_buff),
+ "%-16s 0x%08x",
+ in_entries->name,
+ in_entries->entry);
+ ++in_entries;
+ } else {
+ in_entries = NULL;
+ scnprintf(tmp_buff, sizeof(tmp_buff), "None");
+ }
+ seq_printf(s, "| %-37s|\n", tmp_buff);
+ }
+ seq_printf(s, " %s%s\n\n",
+ "-------------------------------------- ",
+ "--------------------------------------");
+}
+
+/**
+ * Dump item state.
+ *
+ * @s: pointer to output file
+ */
+static void smp2p_items(struct seq_file *s)
+{
+ int pid;
+
+ for (pid = 0; pid < SMP2P_NUM_PROCS; ++pid)
+ smp2p_item(s, pid);
+}
+
+static struct dentry *dent;
+
+static int debugfs_show(struct seq_file *s, void *data)
+{
+ void (*show)(struct seq_file *) = s->private;
+
+ show(s);
+
+ return 0;
+}
+
+static int debug_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, debugfs_show, inode->i_private);
+}
+
+static const struct file_operations debug_ops = {
+ .open = debug_open,
+ .release = single_release,
+ .read = seq_read,
+ .llseek = seq_lseek,
+};
+
+void debug_create(const char *name,
+ void (*show)(struct seq_file *))
+{
+ struct dentry *file;
+
+ file = debugfs_create_file(name, 0444, dent, show, &debug_ops);
+ if (!file)
+ pr_err("%s: unable to create file '%s'\n", __func__, name);
+}
+
+static int __init smp2p_debugfs_init(void)
+{
+ dent = debugfs_create_dir("smp2p", 0);
+ if (IS_ERR(dent))
+ return PTR_ERR(dent);
+
+ debug_create("int_stats", smp2p_int_stats);
+ debug_create("items", smp2p_items);
+
+ return 0;
+}
+
+late_initcall(smp2p_debugfs_init);
+#endif /* CONFIG_DEBUG_FS */
diff --git a/arch/arm/mach-msm/smp2p_gpio.c b/arch/arm/mach-msm/smp2p_gpio.c
new file mode 100644
index 0000000..2a85e5f
--- /dev/null
+++ b/arch/arm/mach-msm/smp2p_gpio.c
@@ -0,0 +1,757 @@
+/* arch/arm/mach-msm/smp2p_gpio.c
+ *
+ * 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/platform_device.h>
+#include <linux/bitmap.h>
+#include <linux/of.h>
+#include <linux/gpio.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <mach/msm_ipc_logging.h>
+#include "smp2p_private_api.h"
+#include "smp2p_private.h"
+
+/* GPIO device - one per SMP2P entry. */
+struct smp2p_chip_dev {
+ struct list_head entry_list;
+ char name[SMP2P_MAX_ENTRY_NAME];
+ int remote_pid;
+ bool is_inbound;
+ bool is_open;
+ struct notifier_block out_notifier;
+ struct notifier_block in_notifier;
+ struct msm_smp2p_out *out_handle;
+
+ struct gpio_chip gpio;
+ struct irq_domain *irq_domain;
+ int irq_base;
+
+ spinlock_t irq_lock;
+ DECLARE_BITMAP(irq_enabled, SMP2P_BITS_PER_ENTRY);
+ DECLARE_BITMAP(irq_rising_edge, SMP2P_BITS_PER_ENTRY);
+ DECLARE_BITMAP(irq_falling_edge, SMP2P_BITS_PER_ENTRY);
+};
+
+static struct platform_driver smp2p_gpio_driver;
+static struct lock_class_key smp2p_gpio_lock_class;
+static struct irq_chip smp2p_gpio_irq_chip;
+static DEFINE_SPINLOCK(smp2p_entry_lock_lha1);
+static LIST_HEAD(smp2p_entry_list);
+
+/* Used for mapping edge to name for logging. */
+static const char * const edge_names[] = {
+ "-",
+ "0->1",
+ "1->0",
+ "-",
+};
+
+/* Used for mapping edge to value for logging. */
+static const char * const edge_name_rising[] = {
+ "-",
+ "0->1",
+};
+
+/* Used for mapping edge to value for logging. */
+static const char * const edge_name_falling[] = {
+ "-",
+ "1->0",
+};
+
+static int smp2p_gpio_to_irq(struct gpio_chip *cp,
+ unsigned offset);
+
+/**
+ * smp2p_get_value - Retrieves GPIO value.
+ *
+ * @cp: GPIO chip pointer
+ * @offset: Pin offset
+ * @returns: >=0: value of GPIO Pin; < 0 for error
+ *
+ * Error codes:
+ * -ENODEV - chip/entry invalid
+ * -ENETDOWN - valid entry, but entry not yet created
+ */
+static int smp2p_get_value(struct gpio_chip *cp,
+ unsigned offset)
+{
+ struct smp2p_chip_dev *chip;
+ int ret = 0;
+ uint32_t data;
+
+ if (!cp)
+ return -ENODEV;
+
+ chip = container_of(cp, struct smp2p_chip_dev, gpio);
+ if (!chip->is_open)
+ return -ENETDOWN;
+
+ if (chip->is_inbound)
+ ret = msm_smp2p_in_read(chip->remote_pid, chip->name, &data);
+ else
+ ret = msm_smp2p_out_read(chip->out_handle, &data);
+
+ if (!ret)
+ ret = (data & (1 << offset)) ? 1 : 0;
+
+ return ret;
+}
+
+/**
+ * smp2p_set_value - Sets GPIO value.
+ *
+ * @cp: GPIO chip pointer
+ * @offset: Pin offset
+ * @value: New value
+ */
+static void smp2p_set_value(struct gpio_chip *cp, unsigned offset, int value)
+{
+ struct smp2p_chip_dev *chip;
+ uint32_t data_set;
+ uint32_t data_clear;
+ int ret;
+
+ if (!cp)
+ return;
+
+ chip = container_of(cp, struct smp2p_chip_dev, gpio);
+ if (!chip->is_open)
+ return;
+
+ if (chip->is_inbound) {
+ SMP2P_ERR("%s: '%s':%d virq %d invalid operation\n",
+ __func__, chip->name, chip->remote_pid,
+ chip->irq_base + offset);
+ return;
+ }
+
+ if (value) {
+ data_set = 1 << offset;
+ data_clear = 0;
+ } else {
+ data_set = 0;
+ data_clear = 1 << offset;
+ }
+
+ ret = msm_smp2p_out_modify(chip->out_handle,
+ data_set, data_clear);
+
+ if (ret)
+ SMP2P_GPIO("'%s':%d gpio %d set to %d failed (%d)\n",
+ chip->name, chip->remote_pid,
+ chip->gpio.base + offset, value, ret);
+ else
+ SMP2P_GPIO("'%s':%d gpio %d set to %d\n",
+ chip->name, chip->remote_pid,
+ chip->gpio.base + offset, value);
+}
+
+/**
+ * smp2p_direction_input - Sets GPIO direction to input.
+ *
+ * @cp: GPIO chip pointer
+ * @offset: Pin offset
+ * @returns: 0 for success; < 0 for failure
+ */
+static int smp2p_direction_input(struct gpio_chip *cp, unsigned offset)
+{
+ struct smp2p_chip_dev *chip;
+
+ if (!cp)
+ return -ENODEV;
+
+ chip = container_of(cp, struct smp2p_chip_dev, gpio);
+ if (!chip->is_inbound)
+ return -EPERM;
+
+ return 0;
+}
+
+/**
+ * smp2p_direction_output - Sets GPIO direction to output.
+ *
+ * @cp: GPIO chip pointer
+ * @offset: Pin offset
+ * @value: Direction
+ * @returns: 0 for success; < 0 for failure
+ */
+static int smp2p_direction_output(struct gpio_chip *cp,
+ unsigned offset, int value)
+{
+ struct smp2p_chip_dev *chip;
+
+ if (!cp)
+ return -ENODEV;
+
+ chip = container_of(cp, struct smp2p_chip_dev, gpio);
+ if (chip->is_inbound)
+ return -EPERM;
+
+ return 0;
+}
+
+/**
+ * smp2p_gpio_to_irq - Convert GPIO pin to virtual IRQ pin.
+ *
+ * @cp: GPIO chip pointer
+ * @offset: Pin offset
+ * @returns: >0 for virtual irq value; < 0 for failure
+ */
+static int smp2p_gpio_to_irq(struct gpio_chip *cp, unsigned offset)
+{
+ struct smp2p_chip_dev *chip;
+
+ chip = container_of(cp, struct smp2p_chip_dev, gpio);
+ if (!cp || chip->irq_base <= 0)
+ return -ENODEV;
+
+ return chip->irq_base + offset;
+}
+
+/**
+ * smp2p_gpio_irq_mask_helper - Mask/Unmask interrupt.
+ *
+ * @d: IRQ data
+ * @mask: true to mask (disable), false to unmask (enable)
+ */
+static void smp2p_gpio_irq_mask_helper(struct irq_data *d, bool mask)
+{
+ struct smp2p_chip_dev *chip;
+ int offset;
+ unsigned long flags;
+
+ chip = (struct smp2p_chip_dev *)irq_get_chip_data(d->irq);
+ if (!chip || chip->irq_base <= 0)
+ return;
+
+ offset = d->irq - chip->irq_base;
+ spin_lock_irqsave(&chip->irq_lock, flags);
+ if (mask)
+ clear_bit(offset, chip->irq_enabled);
+ else
+ set_bit(offset, chip->irq_enabled);
+ spin_unlock_irqrestore(&chip->irq_lock, flags);
+}
+
+/**
+ * smp2p_gpio_irq_mask - Mask interrupt.
+ *
+ * @d: IRQ data
+ */
+static void smp2p_gpio_irq_mask(struct irq_data *d)
+{
+ smp2p_gpio_irq_mask_helper(d, true);
+}
+
+/**
+ * smp2p_gpio_irq_unmask - Unmask interrupt.
+ *
+ * @d: IRQ data
+ */
+static void smp2p_gpio_irq_unmask(struct irq_data *d)
+{
+ smp2p_gpio_irq_mask_helper(d, false);
+}
+
+/**
+ * smp2p_gpio_irq_set_type - Set interrupt edge type.
+ *
+ * @d: IRQ data
+ * @type: Edge type for interrupt
+ * @returns 0 for success; < 0 for failure
+ */
+static int smp2p_gpio_irq_set_type(struct irq_data *d, unsigned int type)
+{
+ struct smp2p_chip_dev *chip;
+ int offset;
+ unsigned long flags;
+ int ret = 0;
+
+ chip = (struct smp2p_chip_dev *)irq_get_chip_data(d->irq);
+ if (!chip)
+ return -ENODEV;
+
+ if (chip->irq_base <= 0) {
+ SMP2P_ERR("%s: '%s':%d virqbase %d invalid\n",
+ __func__, chip->name, chip->remote_pid,
+ chip->irq_base);
+ return -ENODEV;
+ }
+
+ offset = d->irq - chip->irq_base;
+
+ spin_lock_irqsave(&chip->irq_lock, flags);
+ clear_bit(offset, chip->irq_rising_edge);
+ clear_bit(offset, chip->irq_falling_edge);
+ switch (type) {
+ case IRQ_TYPE_EDGE_RISING:
+ set_bit(offset, chip->irq_rising_edge);
+ break;
+
+ case IRQ_TYPE_EDGE_FALLING:
+ set_bit(offset, chip->irq_falling_edge);
+ break;
+
+ case IRQ_TYPE_NONE:
+ case IRQ_TYPE_DEFAULT:
+ case IRQ_TYPE_EDGE_BOTH:
+ set_bit(offset, chip->irq_rising_edge);
+ set_bit(offset, chip->irq_falling_edge);
+ break;
+
+ default:
+ SMP2P_ERR("%s: unsupported interrupt type 0x%x\n",
+ __func__, type);
+ ret = -EINVAL;
+ break;
+ }
+ spin_unlock_irqrestore(&chip->irq_lock, flags);
+ return ret;
+}
+
+/**
+ * smp2p_irq_map - Creates or updates binding of virtual IRQ
+ *
+ * @domain_ptr: Interrupt domain pointer
+ * @virq: Virtual IRQ
+ * @hw: Hardware IRQ (same as virq for nomap)
+ * @returns: 0 for success
+ */
+static int smp2p_irq_map(struct irq_domain *domain_ptr, unsigned int virq,
+ irq_hw_number_t hw)
+{
+ struct smp2p_chip_dev *chip;
+
+ chip = domain_ptr->host_data;
+ if (!chip) {
+ SMP2P_ERR("%s: invalid domain ptr %p\n", __func__, domain_ptr);
+ return -ENODEV;
+ }
+
+ /* map chip structures to device */
+ irq_set_lockdep_class(virq, &smp2p_gpio_lock_class);
+ irq_set_chip_and_handler(virq, &smp2p_gpio_irq_chip,
+ handle_level_irq);
+ irq_set_chip_data(virq, chip);
+ set_irq_flags(virq, IRQF_VALID);
+
+ return 0;
+}
+
+static struct irq_chip smp2p_gpio_irq_chip = {
+ .name = "smp2p_gpio",
+ .irq_mask = smp2p_gpio_irq_mask,
+ .irq_unmask = smp2p_gpio_irq_unmask,
+ .irq_set_type = smp2p_gpio_irq_set_type,
+};
+
+/* No-map interrupt Domain */
+static const struct irq_domain_ops smp2p_irq_domain_ops = {
+ .map = smp2p_irq_map,
+};
+
+/**
+ * msm_summary_irq_handler - Handles inbound entry change notification.
+ *
+ * @chip: GPIO chip pointer
+ * @entry: Change notification data
+ *
+ * Whenever an entry changes, this callback is triggered to determine
+ * which bits changed and if the corresponding interrupts need to be
+ * triggered.
+ */
+static void msm_summary_irq_handler(struct smp2p_chip_dev *chip,
+ struct msm_smp2p_update_notif *entry)
+{
+ int i;
+ uint32_t cur_val;
+ uint32_t prev_val;
+ uint32_t edge;
+ unsigned long flags;
+ bool trigger_interrrupt;
+ bool irq_rising;
+ bool irq_falling;
+
+ cur_val = entry->current_value;
+ prev_val = entry->previous_value;
+
+ if (chip->irq_base <= 0)
+ return;
+
+ SMP2P_GPIO("'%s':%d GPIO Summary IRQ Change %08x->%08x\n",
+ chip->name, chip->remote_pid, prev_val, cur_val);
+
+ for (i = 0; i < SMP2P_BITS_PER_ENTRY; ++i) {
+ spin_lock_irqsave(&chip->irq_lock, flags);
+ trigger_interrrupt = false;
+ edge = (prev_val & 0x1) << 1 | (cur_val & 0x1);
+ irq_rising = test_bit(i, chip->irq_rising_edge);
+ irq_falling = test_bit(i, chip->irq_falling_edge);
+
+ if (test_bit(i, chip->irq_enabled)) {
+ if (edge == 0x1 && irq_rising)
+ /* 0->1 transition */
+ trigger_interrrupt = true;
+ else if (edge == 0x2 && irq_falling)
+ /* 1->0 transition */
+ trigger_interrrupt = true;
+ } else {
+ SMP2P_GPIO(
+ "'%s':%d GPIO bit %d virq %d (%s,%s) - edge %s disabled\n",
+ chip->name, chip->remote_pid, i,
+ chip->irq_base + i,
+ edge_name_rising[irq_rising],
+ edge_name_falling[irq_falling],
+ edge_names[edge]);
+ }
+ spin_unlock_irqrestore(&chip->irq_lock, flags);
+
+ if (trigger_interrrupt) {
+ SMP2P_GPIO(
+ "'%s':%d GPIO bit %d virq %d (%s,%s) - edge %s triggering\n",
+ chip->name, chip->remote_pid, i,
+ chip->irq_base + i,
+ edge_name_rising[irq_rising],
+ edge_name_falling[irq_falling],
+ edge_names[edge]);
+ (void)generic_handle_irq(chip->irq_base + i);
+ }
+
+ cur_val >>= 1;
+ prev_val >>= 1;
+ }
+}
+
+/**
+ * Adds an interrupt domain based upon the DT node.
+ *
+ * @chip: pointer to GPIO chip
+ * @node: pointer to Device Tree node
+ */
+static void smp2p_add_irq_domain(struct smp2p_chip_dev *chip,
+ struct device_node *node)
+{
+ int i;
+
+ /* map GPIO pins to interrupts */
+ chip->irq_domain = irq_domain_add_nomap(node, 0,
+ &smp2p_irq_domain_ops, chip);
+ if (!chip->irq_domain) {
+ SMP2P_ERR("%s: unable to create interrupt domain '%s':%d\n",
+ __func__, chip->name, chip->remote_pid);
+ return;
+ }
+
+ for (i = 0; i < SMP2P_BITS_PER_ENTRY; ++i) {
+ unsigned int virt_irq;
+
+ virt_irq = irq_create_direct_mapping(chip->irq_domain);
+ if (virt_irq == NO_IRQ) {
+ SMP2P_ERR("%s: gpio->virt IRQ mapping failed '%s':%d\n",
+ __func__, chip->name, chip->remote_pid);
+ } else if (!chip->irq_base) {
+ chip->irq_base = virt_irq;
+ }
+ }
+}
+
+/**
+ * Notifier function passed into smp2p API for out bound entries.
+ *
+ * @self: Pointer to calling notifier block
+ * @event: Event
+ * @data: Event-specific data
+ * @returns: 0
+ */
+static int smp2p_gpio_out_notify(struct notifier_block *self,
+ unsigned long event, void *data)
+{
+ struct smp2p_chip_dev *chip;
+
+ chip = container_of(self, struct smp2p_chip_dev, out_notifier);
+
+ switch (event) {
+ case SMP2P_OPEN:
+ chip->is_open = 1;
+ SMP2P_GPIO("%s: Opened out '%s':%d\n", __func__,
+ chip->name, chip->remote_pid);
+ break;
+ case SMP2P_ENTRY_UPDATE:
+ break;
+ default:
+ SMP2P_ERR("%s: Unknown event\n", __func__);
+ break;
+ }
+ return 0;
+}
+
+/**
+ * Notifier function passed into smp2p API for in bound entries.
+ *
+ * @self: Pointer to calling notifier block
+ * @event: Event
+ * @data: Event-specific data
+ * @returns: 0
+ */
+static int smp2p_gpio_in_notify(struct notifier_block *self,
+ unsigned long event, void *data)
+{
+ struct smp2p_chip_dev *chip;
+
+ chip = container_of(self, struct smp2p_chip_dev, in_notifier);
+
+ switch (event) {
+ case SMP2P_OPEN:
+ chip->is_open = 1;
+ SMP2P_GPIO("%s: Opened in '%s':%d\n", __func__,
+ chip->name, chip->remote_pid);
+ break;
+ case SMP2P_ENTRY_UPDATE:
+ msm_summary_irq_handler(chip, data);
+ break;
+ default:
+ SMP2P_ERR("%s: Unknown event\n", __func__);
+ break;
+ }
+ return 0;
+}
+
+/**
+ * Device tree probe function.
+ *
+ * @pdev: Pointer to device tree data.
+ * @returns: 0 on success; -ENODEV otherwise
+ *
+ * Called for each smp2pgpio entry in the device tree.
+ */
+static int __devinit smp2p_gpio_probe(struct platform_device *pdev)
+{
+ struct device_node *node;
+ char *key;
+ struct smp2p_chip_dev *chip;
+ const char *name_tmp;
+ unsigned long flags;
+ bool is_test_entry = false;
+ int ret;
+
+ chip = kzalloc(sizeof(struct smp2p_chip_dev), GFP_KERNEL);
+ if (!chip) {
+ SMP2P_ERR("%s: out of memory\n", __func__);
+ ret = -ENOMEM;
+ goto fail;
+ }
+ spin_lock_init(&chip->irq_lock);
+
+ /* parse device tree */
+ node = pdev->dev.of_node;
+ key = "qcom,entry-name";
+ ret = of_property_read_string(node, key, &name_tmp);
+ if (ret) {
+ SMP2P_ERR("%s: missing DT key '%s'\n", __func__, key);
+ goto fail;
+ }
+ strlcpy(chip->name, name_tmp, sizeof(chip->name));
+
+ key = "qcom,remote-pid";
+ ret = of_property_read_u32(node, key, &chip->remote_pid);
+ if (ret) {
+ SMP2P_ERR("%s: missing DT key '%s'\n", __func__, key);
+ goto fail;
+ }
+
+ key = "qcom,is-inbound";
+ chip->is_inbound = of_property_read_bool(node, key);
+
+ /* create virtual GPIO controller */
+ chip->gpio.label = chip->name;
+ chip->gpio.dev = &pdev->dev;
+ chip->gpio.owner = THIS_MODULE;
+ chip->gpio.direction_input = smp2p_direction_input,
+ chip->gpio.get = smp2p_get_value;
+ chip->gpio.direction_output = smp2p_direction_output,
+ chip->gpio.set = smp2p_set_value;
+ chip->gpio.to_irq = smp2p_gpio_to_irq,
+ chip->gpio.base = -1; /* use dynamic GPIO pin allocation */
+ chip->gpio.ngpio = SMP2P_BITS_PER_ENTRY;
+ ret = gpiochip_add(&chip->gpio);
+ if (ret) {
+ SMP2P_ERR("%s: unable to register GPIO '%s' ret %d\n",
+ __func__, chip->name, ret);
+ goto fail;
+ }
+
+ /*
+ * Test entries opened by GPIO Test conflict with loopback
+ * support, so the test entries must be explicitly opened
+ * in the unit test framework.
+ */
+ if (strncmp("smp2p", chip->name, SMP2P_MAX_ENTRY_NAME) == 0)
+ is_test_entry = true;
+
+ if (!chip->is_inbound) {
+ chip->out_notifier.notifier_call = smp2p_gpio_out_notify;
+ if (!is_test_entry) {
+ ret = msm_smp2p_out_open(chip->remote_pid, chip->name,
+ &chip->out_notifier,
+ &chip->out_handle);
+ if (ret < 0)
+ goto fail;
+ }
+ } else {
+ chip->in_notifier.notifier_call = smp2p_gpio_in_notify;
+ if (!is_test_entry) {
+ ret = msm_smp2p_in_register(chip->remote_pid,
+ chip->name,
+ &chip->in_notifier);
+ if (ret < 0)
+ goto fail;
+ }
+ }
+
+ spin_lock_irqsave(&smp2p_entry_lock_lha1, flags);
+ list_add(&chip->entry_list, &smp2p_entry_list);
+ spin_unlock_irqrestore(&smp2p_entry_lock_lha1, flags);
+
+ /*
+ * Create interrupt domain - note that chip can't be removed from the
+ * interrupt domain, so chip cannot be deleted after this point.
+ */
+ if (chip->is_inbound)
+ smp2p_add_irq_domain(chip, node);
+ else
+ chip->irq_base = -1;
+
+ SMP2P_GPIO("%s: added %s%s entry '%s':%d gpio %d irq %d",
+ __func__,
+ is_test_entry ? "test " : "",
+ chip->is_inbound ? "in" : "out",
+ chip->name, chip->remote_pid,
+ chip->gpio.base, chip->irq_base);
+
+ return 0;
+
+fail:
+ kfree(chip);
+ return ret;
+}
+
+/**
+ * smp2p_gpio_open_close - Opens or closes entry.
+ *
+ * @entry: Entry to open or close
+ * @do_open: true = open port; false = close
+ */
+static void smp2p_gpio_open_close(struct smp2p_chip_dev *entry,
+ bool do_open)
+{
+ int ret;
+
+ if (do_open) {
+ /* open entry */
+ if (entry->is_inbound)
+ ret = msm_smp2p_in_register(entry->remote_pid,
+ entry->name, &entry->in_notifier);
+ else
+ ret = msm_smp2p_out_open(entry->remote_pid,
+ entry->name, &entry->out_notifier,
+ &entry->out_handle);
+ SMP2P_GPIO("%s: opened %s '%s':%d ret %d\n",
+ __func__,
+ entry->is_inbound ? "in" : "out",
+ entry->name, entry->remote_pid,
+ ret);
+ } else {
+ /* close entry */
+ if (entry->is_inbound)
+ ret = msm_smp2p_in_unregister(entry->remote_pid,
+ entry->name, &entry->in_notifier);
+ else
+ ret = msm_smp2p_out_close(&entry->out_handle);
+ entry->is_open = false;
+ SMP2P_GPIO("%s: closed %s '%s':%d ret %d\n",
+ __func__,
+ entry->is_inbound ? "in" : "out",
+ entry->name, entry->remote_pid, ret);
+ }
+}
+
+/**
+ * smp2p_gpio_open_test_entry - Opens or closes test entries for unit testing.
+ *
+ * @name: Name of the entry
+ * @remote_pid: Remote processor ID
+ * @do_open: true = open port; false = close
+ */
+void smp2p_gpio_open_test_entry(const char *name, int remote_pid, bool do_open)
+{
+ struct smp2p_chip_dev *entry;
+ struct smp2p_chip_dev *start_entry;
+ unsigned long flags;
+
+ spin_lock_irqsave(&smp2p_entry_lock_lha1, flags);
+ if (list_empty(&smp2p_entry_list)) {
+ spin_unlock_irqrestore(&smp2p_entry_lock_lha1, flags);
+ return;
+ }
+ start_entry = list_first_entry(&smp2p_entry_list,
+ struct smp2p_chip_dev,
+ entry_list);
+ entry = start_entry;
+ do {
+ if (!strncmp(entry->name, name, SMP2P_MAX_ENTRY_NAME)
+ && entry->remote_pid == remote_pid) {
+ /* found entry to change */
+ spin_unlock_irqrestore(&smp2p_entry_lock_lha1, flags);
+ smp2p_gpio_open_close(entry, do_open);
+ spin_lock_irqsave(&smp2p_entry_lock_lha1, flags);
+ }
+ list_rotate_left(&smp2p_entry_list);
+ entry = list_first_entry(&smp2p_entry_list,
+ struct smp2p_chip_dev,
+ entry_list);
+ } while (entry != start_entry);
+ spin_unlock_irqrestore(&smp2p_entry_lock_lha1, flags);
+}
+
+static struct of_device_id msm_smp2p_match_table[] __devinitdata = {
+ {.compatible = "qcom,smp2pgpio", },
+ {},
+};
+
+static struct platform_driver smp2p_gpio_driver = {
+ .probe = smp2p_gpio_probe,
+ .driver = {
+ .name = "smp2pgpio",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_smp2p_match_table,
+ },
+};
+
+static int __devinit smp2p_init(void)
+{
+ INIT_LIST_HEAD(&smp2p_entry_list);
+ return platform_driver_register(&smp2p_gpio_driver);
+}
+module_init(smp2p_init);
+
+static void __exit smp2p_exit(void)
+{
+ platform_driver_unregister(&smp2p_gpio_driver);
+}
+module_exit(smp2p_exit);
+
+MODULE_DESCRIPTION("SMP2P GPIO");
+MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/mach-msm/smp2p_gpio_test.c b/arch/arm/mach-msm/smp2p_gpio_test.c
new file mode 100644
index 0000000..70de20a
--- /dev/null
+++ b/arch/arm/mach-msm/smp2p_gpio_test.c
@@ -0,0 +1,624 @@
+/* arch/arm/mach-msm/smp2p_gpio_test.c
+ *
+ * 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/platform_device.h>
+#include <linux/of_gpio.h>
+#include <linux/of_irq.h>
+#include <linux/gpio.h>
+#include <linux/debugfs.h>
+#include <linux/completion.h>
+#include <linux/irq.h>
+#include <linux/bitmap.h>
+#include "smp2p_private.h"
+#include "smp2p_test_common.h"
+
+/* Interrupt callback data */
+struct gpio_info {
+ int gpio_base_id;
+ int irq_base_id;
+
+ bool initialized;
+ struct completion cb_completion;
+ int cb_count;
+ DECLARE_BITMAP(triggered_irqs, SMP2P_BITS_PER_ENTRY);
+};
+
+/* GPIO Inbound/Outbound callback info */
+struct gpio_inout {
+ struct gpio_info in;
+ struct gpio_info out;
+};
+
+static struct gpio_inout gpio_info[SMP2P_NUM_PROCS];
+
+/**
+ * Init/reset the callback data.
+ *
+ * @info: Pointer to callback data
+ */
+static void cb_data_reset(struct gpio_info *info)
+{
+ int n;
+
+ if (!info)
+ return;
+
+ if (!info->initialized) {
+ init_completion(&info->cb_completion);
+ info->initialized = true;
+ }
+ info->cb_count = 0;
+
+ for (n = 0; n < SMP2P_BITS_PER_ENTRY; ++n)
+ clear_bit(n, info->triggered_irqs);
+
+ INIT_COMPLETION(info->cb_completion);
+}
+
+static int __devinit smp2p_gpio_test_probe(struct platform_device *pdev)
+{
+ int id;
+ int cnt;
+ struct device_node *node = pdev->dev.of_node;
+ struct gpio_info *gpio_info_ptr = NULL;
+
+ /*
+ * NOTE: This does a string-lookup of the GPIO pin name and doesn't
+ * actually directly link to the SMP2P GPIO driver since all
+ * GPIO/Interrupt access must be through standard
+ * Linux GPIO / Interrupt APIs.
+ */
+ if (strcmp("qcom,smp2pgpio_test_smp2p_1_in", node->name) == 0) {
+ gpio_info_ptr = &gpio_info[SMP2P_MODEM_PROC].in;
+ } else if (strcmp("qcom,smp2pgpio_test_smp2p_1_out", node->name) == 0) {
+ gpio_info_ptr = &gpio_info[SMP2P_MODEM_PROC].out;
+ } else if (strcmp("qcom,smp2pgpio_test_smp2p_2_in", node->name) == 0) {
+ gpio_info_ptr = &gpio_info[SMP2P_AUDIO_PROC].in;
+ } else if (strcmp("qcom,smp2pgpio_test_smp2p_2_out", node->name) == 0) {
+ gpio_info_ptr = &gpio_info[SMP2P_AUDIO_PROC].out;
+ } else if (strcmp("qcom,smp2pgpio_test_smp2p_4_in", node->name) == 0) {
+ gpio_info_ptr = &gpio_info[SMP2P_WIRELESS_PROC].in;
+ } else if (strcmp("qcom,smp2pgpio_test_smp2p_4_out", node->name) == 0) {
+ gpio_info_ptr = &gpio_info[SMP2P_WIRELESS_PROC].out;
+ } else if (strcmp("qcom,smp2pgpio_test_smp2p_7_in", node->name) == 0) {
+ gpio_info_ptr = &gpio_info[SMP2P_REMOTE_MOCK_PROC].in;
+ } else if (strcmp("qcom,smp2pgpio_test_smp2p_7_out", node->name) == 0) {
+ gpio_info_ptr = &gpio_info[SMP2P_REMOTE_MOCK_PROC].out;
+ } else {
+ pr_err("%s: unable to match device type '%s'\n",
+ __func__, node->name);
+ return -ENODEV;
+ }
+
+ /* retrieve the GPIO and interrupt ID's */
+ cnt = of_gpio_count(node);
+ if (cnt && gpio_info_ptr) {
+ /*
+ * Instead of looping through all 32-bits, we can just get the
+ * first pin to get the base IDs. This saves on the verbosity
+ * of the device tree nodes as well.
+ */
+ id = of_get_gpio(node, 0);
+ gpio_info_ptr->gpio_base_id = id;
+ gpio_info_ptr->irq_base_id = gpio_to_irq(id);
+ }
+ return 0;
+}
+
+/*
+ * NOTE: Instead of match table and device driver, you may be able to just
+ * call of_find_compatible_node() in your init function.
+ */
+static struct of_device_id msm_smp2p_match_table[] __devinitdata = {
+ /* modem */
+ {.compatible = "qcom,smp2pgpio_test_smp2p_1_out", },
+ {.compatible = "qcom,smp2pgpio_test_smp2p_1_in", },
+
+ /* audio (adsp) */
+ {.compatible = "qcom,smp2pgpio_test_smp2p_2_out", },
+ {.compatible = "qcom,smp2pgpio_test_smp2p_2_in", },
+
+ /* wcnss */
+ {.compatible = "qcom,smp2pgpio_test_smp2p_4_out", },
+ {.compatible = "qcom,smp2pgpio_test_smp2p_4_in", },
+
+ /* mock loopback */
+ {.compatible = "qcom,smp2pgpio_test_smp2p_7_out", },
+ {.compatible = "qcom,smp2pgpio_test_smp2p_7_in", },
+ {},
+};
+
+static struct platform_driver smp2p_gpio_driver = {
+ .probe = smp2p_gpio_test_probe,
+ .driver = {
+ .name = "smp2pgpio_test",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_smp2p_match_table,
+ },
+};
+
+/**
+ * smp2p_ut_local_gpio_out - Verify outbound functionality.
+ *
+ * @s: pointer to output file
+ */
+static void smp2p_ut_local_gpio_out(struct seq_file *s)
+{
+ int failed = 0;
+ struct gpio_info *cb_info = &gpio_info[SMP2P_REMOTE_MOCK_PROC].out;
+ int ret;
+ int id;
+ struct msm_smp2p_remote_mock *mock;
+
+ seq_printf(s, "Running %s\n", __func__);
+ do {
+ /* initialize mock edge */
+ ret = smp2p_reset_mock_edge();
+ UT_ASSERT_INT(ret, ==, 0);
+
+ mock = msm_smp2p_get_remote_mock();
+ UT_ASSERT_PTR(mock, !=, NULL);
+
+ mock->rx_interrupt_count = 0;
+ memset(&mock->remote_item, 0,
+ sizeof(struct smp2p_smem_item));
+ smp2p_init_header((struct smp2p_smem *)&mock->remote_item,
+ SMP2P_REMOTE_MOCK_PROC, SMP2P_APPS_PROC,
+ 0, 1);
+ strlcpy(mock->remote_item.entries[0].name, "smp2p",
+ SMP2P_MAX_ENTRY_NAME);
+ SMP2P_SET_ENT_VALID(
+ mock->remote_item.header.valid_total_ent, 1);
+ msm_smp2p_set_remote_mock_exists(true);
+ mock->tx_interrupt();
+
+ /* open GPIO entry */
+ smp2p_gpio_open_test_entry("smp2p",
+ SMP2P_REMOTE_MOCK_PROC, true);
+
+ /* verify set/get functions */
+ UT_ASSERT_INT(0, <, cb_info->gpio_base_id);
+ for (id = 0; id < SMP2P_BITS_PER_ENTRY && !failed; ++id) {
+ int pin = cb_info->gpio_base_id + id;
+
+ mock->rx_interrupt_count = 0;
+ gpio_set_value(pin, 1);
+ UT_ASSERT_INT(1, ==, mock->rx_interrupt_count);
+ UT_ASSERT_INT(1, ==, gpio_get_value(pin));
+
+ gpio_set_value(pin, 0);
+ UT_ASSERT_INT(2, ==, mock->rx_interrupt_count);
+ UT_ASSERT_INT(0, ==, gpio_get_value(pin));
+ }
+ if (failed)
+ break;
+
+ seq_printf(s, "\tOK\n");
+ } while (0);
+
+ if (failed) {
+ pr_err("%s: Failed\n", __func__);
+ seq_printf(s, "\tFailed\n");
+ }
+
+ smp2p_gpio_open_test_entry("smp2p",
+ SMP2P_REMOTE_MOCK_PROC, false);
+}
+
+/**
+ * smp2p_gpio_irq - Interrupt handler for inbound entries.
+ *
+ * @irq: Virtual IRQ being triggered
+ * @data: Cookie data (struct gpio_info * in this case)
+ * @returns: Number of bytes written
+ */
+static irqreturn_t smp2p_gpio_irq(int irq, void *data)
+{
+ struct gpio_info *gpio_ptr = (struct gpio_info *)data;
+ int offset;
+
+ if (!gpio_ptr) {
+ pr_err("%s: gpio_ptr is NULL for irq %d\n", __func__, irq);
+ return IRQ_HANDLED;
+ }
+
+ offset = irq - gpio_ptr->irq_base_id;
+ if (offset >= 0 && offset < SMP2P_BITS_PER_ENTRY)
+ set_bit(offset, gpio_ptr->triggered_irqs);
+ else
+ pr_err("%s: invalid irq offset base %d; irq %d\n",
+ __func__, gpio_ptr->irq_base_id, irq);
+
+ ++gpio_ptr->cb_count;
+ complete(&gpio_ptr->cb_completion);
+ return IRQ_HANDLED;
+}
+
+/**
+ * smp2p_ut_local_gpio_in - Verify inbound functionality.
+ *
+ * @s: pointer to output file
+ */
+static void smp2p_ut_local_gpio_in(struct seq_file *s)
+{
+ int failed = 0;
+ struct gpio_info *cb_info = &gpio_info[SMP2P_REMOTE_MOCK_PROC].in;
+ int id;
+ int ret;
+ int virq;
+ struct msm_smp2p_remote_mock *mock;
+
+ seq_printf(s, "Running %s\n", __func__);
+
+ cb_data_reset(cb_info);
+ do {
+ /* initialize mock edge */
+ ret = smp2p_reset_mock_edge();
+ UT_ASSERT_INT(ret, ==, 0);
+
+ mock = msm_smp2p_get_remote_mock();
+ UT_ASSERT_PTR(mock, !=, NULL);
+
+ mock->rx_interrupt_count = 0;
+ memset(&mock->remote_item, 0,
+ sizeof(struct smp2p_smem_item));
+ smp2p_init_header((struct smp2p_smem *)&mock->remote_item,
+ SMP2P_REMOTE_MOCK_PROC, SMP2P_APPS_PROC,
+ 0, 1);
+ strlcpy(mock->remote_item.entries[0].name, "smp2p",
+ SMP2P_MAX_ENTRY_NAME);
+ SMP2P_SET_ENT_VALID(
+ mock->remote_item.header.valid_total_ent, 1);
+ msm_smp2p_set_remote_mock_exists(true);
+ mock->tx_interrupt();
+
+ smp2p_gpio_open_test_entry("smp2p",
+ SMP2P_REMOTE_MOCK_PROC, true);
+
+ /* verify set/get functions locally */
+ UT_ASSERT_INT(0, <, cb_info->gpio_base_id);
+ for (id = 0; id < SMP2P_BITS_PER_ENTRY && !failed; ++id) {
+ int pin;
+ int current_value;
+
+ /* verify pin value cannot be set */
+ pin = cb_info->gpio_base_id + id;
+ current_value = gpio_get_value(pin);
+
+ gpio_set_value(pin, 0);
+ UT_ASSERT_INT(current_value, ==, gpio_get_value(pin));
+ gpio_set_value(pin, 1);
+ UT_ASSERT_INT(current_value, ==, gpio_get_value(pin));
+
+ /* verify no interrupts */
+ UT_ASSERT_INT(0, ==, cb_info->cb_count);
+ }
+ if (failed)
+ break;
+
+ /* register for interrupts */
+ UT_ASSERT_INT(0, <, cb_info->irq_base_id);
+ for (id = 0; id < SMP2P_BITS_PER_ENTRY && !failed; ++id) {
+ virq = cb_info->irq_base_id + id;
+ UT_ASSERT_INT(0, >, (unsigned int)irq_to_desc(virq));
+ ret = request_irq(virq,
+ smp2p_gpio_irq, IRQF_TRIGGER_RISING,
+ "smp2p_test", cb_info);
+ UT_ASSERT_INT(0, ==, ret);
+ }
+ if (failed)
+ break;
+
+ /* verify both rising and falling edge interrupts */
+ for (id = 0; id < SMP2P_BITS_PER_ENTRY && !failed; ++id) {
+ virq = cb_info->irq_base_id + id;
+ irq_set_irq_type(virq, IRQ_TYPE_EDGE_BOTH);
+ cb_data_reset(cb_info);
+
+ /* verify rising-edge interrupt */
+ mock->remote_item.entries[0].entry = 1 << id;
+ mock->tx_interrupt();
+ UT_ASSERT_INT(cb_info->cb_count, ==, 1);
+ UT_ASSERT_INT(0, <,
+ test_bit(id, cb_info->triggered_irqs));
+ test_bit(id, cb_info->triggered_irqs);
+
+ /* verify falling-edge interrupt */
+ mock->remote_item.entries[0].entry = 0;
+ mock->tx_interrupt();
+ UT_ASSERT_INT(cb_info->cb_count, ==, 2);
+ UT_ASSERT_INT(0, <,
+ test_bit(id, cb_info->triggered_irqs));
+ }
+ if (failed)
+ break;
+
+ /* verify rising-edge interrupts */
+ for (id = 0; id < SMP2P_BITS_PER_ENTRY && !failed; ++id) {
+ virq = cb_info->irq_base_id + id;
+ irq_set_irq_type(virq, IRQ_TYPE_EDGE_RISING);
+ cb_data_reset(cb_info);
+
+ /* verify only rising-edge interrupt is triggered */
+ mock->remote_item.entries[0].entry = 1 << id;
+ mock->tx_interrupt();
+ UT_ASSERT_INT(cb_info->cb_count, ==, 1);
+ UT_ASSERT_INT(0, <,
+ test_bit(id, cb_info->triggered_irqs));
+ test_bit(id, cb_info->triggered_irqs);
+
+ mock->remote_item.entries[0].entry = 0;
+ mock->tx_interrupt();
+ UT_ASSERT_INT(cb_info->cb_count, ==, 1);
+ UT_ASSERT_INT(0, <,
+ test_bit(id, cb_info->triggered_irqs));
+ }
+ if (failed)
+ break;
+
+ /* verify falling-edge interrupts */
+ for (id = 0; id < SMP2P_BITS_PER_ENTRY && !failed; ++id) {
+ virq = cb_info->irq_base_id + id;
+ irq_set_irq_type(virq, IRQ_TYPE_EDGE_FALLING);
+ cb_data_reset(cb_info);
+
+ /* verify only rising-edge interrupt is triggered */
+ mock->remote_item.entries[0].entry = 1 << id;
+ mock->tx_interrupt();
+ UT_ASSERT_INT(cb_info->cb_count, ==, 0);
+ UT_ASSERT_INT(0, ==,
+ test_bit(id, cb_info->triggered_irqs));
+
+ mock->remote_item.entries[0].entry = 0;
+ mock->tx_interrupt();
+ UT_ASSERT_INT(cb_info->cb_count, ==, 1);
+ UT_ASSERT_INT(0, <,
+ test_bit(id, cb_info->triggered_irqs));
+ }
+ if (failed)
+ break;
+
+ seq_printf(s, "\tOK\n");
+ } while (0);
+
+ if (failed) {
+ pr_err("%s: Failed\n", __func__);
+ seq_printf(s, "\tFailed\n");
+ }
+
+ /* unregister for interrupts */
+ if (cb_info->irq_base_id) {
+ for (id = 0; id < SMP2P_BITS_PER_ENTRY; ++id)
+ free_irq(cb_info->irq_base_id + id, cb_info);
+ }
+
+ smp2p_gpio_open_test_entry("smp2p",
+ SMP2P_REMOTE_MOCK_PROC, false);
+}
+
+/**
+ * smp2p_gpio_write_bits - writes value to each GPIO pin specified in mask.
+ *
+ * @gpio: gpio test structure
+ * @mask: 1 = write gpio_value to this GPIO pin
+ * @gpio_value: value to write to GPIO pin
+ */
+static void smp2p_gpio_write_bits(struct gpio_info *gpio, uint32_t mask,
+ int gpio_value)
+{
+ int n;
+
+ for (n = 0; n < SMP2P_BITS_PER_ENTRY; ++n) {
+ if (mask & 0x1)
+ gpio_set_value(gpio->gpio_base_id + n, gpio_value);
+ mask >>= 1;
+ }
+}
+
+static void smp2p_gpio_set_bits(struct gpio_info *gpio, uint32_t mask)
+{
+ smp2p_gpio_write_bits(gpio, mask, 1);
+}
+
+static void smp2p_gpio_clr_bits(struct gpio_info *gpio, uint32_t mask)
+{
+ smp2p_gpio_write_bits(gpio, mask, 0);
+}
+
+/**
+ * smp2p_gpio_get_value - reads entire 32-bits of GPIO
+ *
+ * @gpio: gpio structure
+ * @returns: 32 bit value of GPIO pins
+ */
+static uint32_t smp2p_gpio_get_value(struct gpio_info *gpio)
+{
+ int n;
+ uint32_t value = 0;
+
+ for (n = 0; n < SMP2P_BITS_PER_ENTRY; ++n) {
+ if (gpio_get_value(gpio->gpio_base_id + n))
+ value |= 1 << n;
+ }
+ return value;
+}
+
+/**
+ * smp2p_ut_remote_inout_core - Verify inbound/outbound functionality.
+ *
+ * @s: pointer to output file
+ * @remote_pid: Remote processor to test
+ * @name: Name of the test for reporting
+ *
+ * This test verifies inbound/outbound functionality for the remote processor.
+ */
+static void smp2p_ut_remote_inout_core(struct seq_file *s, int remote_pid,
+ const char *name)
+{
+ int failed = 0;
+ uint32_t request;
+ uint32_t response;
+ struct gpio_info *cb_in;
+ struct gpio_info *cb_out;
+ int id;
+ int ret;
+
+ seq_printf(s, "Running %s for '%s' remote pid %d\n",
+ __func__, smp2p_pid_to_name(remote_pid), remote_pid);
+
+ cb_in = &gpio_info[remote_pid].in;
+ cb_out = &gpio_info[remote_pid].out;
+ cb_data_reset(cb_in);
+ cb_data_reset(cb_out);
+ do {
+ /* open test entries */
+ msm_smp2p_deinit_rmt_lpb_proc(remote_pid);
+ smp2p_gpio_open_test_entry("smp2p", remote_pid, true);
+
+ /* register for interrupts */
+ UT_ASSERT_INT(0, <, cb_in->gpio_base_id);
+ UT_ASSERT_INT(0, <, cb_in->irq_base_id);
+ for (id = 0; id < SMP2P_BITS_PER_ENTRY && !failed; ++id) {
+ int virq = cb_in->irq_base_id + id;
+ UT_ASSERT_INT(0, >, (unsigned int)irq_to_desc(virq));
+ ret = request_irq(virq,
+ smp2p_gpio_irq,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ "smp2p_test", cb_in);
+ UT_ASSERT_INT(0, ==, ret);
+ }
+ if (failed)
+ break;
+
+ /* write echo of data value 0 */
+ UT_ASSERT_INT(0, <, cb_out->gpio_base_id);
+ request = 0x0;
+ SMP2P_SET_RMT_CMD_TYPE(request, 1);
+ SMP2P_SET_RMT_CMD(request, SMP2P_LB_CMD_ECHO);
+ SMP2P_SET_RMT_DATA(request, 0x0);
+
+ smp2p_gpio_set_bits(cb_out, SMP2P_RMT_IGNORE_MASK);
+ smp2p_gpio_clr_bits(cb_out, ~SMP2P_RMT_IGNORE_MASK);
+ smp2p_gpio_set_bits(cb_out, request);
+
+ UT_ASSERT_INT(cb_in->cb_count, ==, 0);
+ smp2p_gpio_clr_bits(cb_out, SMP2P_RMT_IGNORE_MASK);
+
+ /* verify response */
+ do {
+ /* wait for up to 32 changes */
+ if (wait_for_completion_timeout(
+ &cb_in->cb_completion, HZ / 2) == 0)
+ break;
+ INIT_COMPLETION(cb_in->cb_completion);
+ } while (cb_in->cb_count < 32);
+ UT_ASSERT_INT(cb_in->cb_count, >, 0);
+ response = smp2p_gpio_get_value(cb_in);
+ SMP2P_SET_RMT_CMD_TYPE(request, 0);
+ UT_ASSERT_HEX(request, ==, response);
+
+ /* write echo of data value of all 1's */
+ request = 0x0;
+ SMP2P_SET_RMT_CMD_TYPE(request, 1);
+ SMP2P_SET_RMT_CMD(request, SMP2P_LB_CMD_ECHO);
+ SMP2P_SET_RMT_DATA(request, ~0);
+
+ smp2p_gpio_set_bits(cb_out, SMP2P_RMT_IGNORE_MASK);
+ cb_data_reset(cb_in);
+ smp2p_gpio_clr_bits(cb_out, ~SMP2P_RMT_IGNORE_MASK);
+ smp2p_gpio_set_bits(cb_out, request);
+
+ UT_ASSERT_INT(cb_in->cb_count, ==, 0);
+ smp2p_gpio_clr_bits(cb_out, SMP2P_RMT_IGNORE_MASK);
+
+ /* verify response including 24 interrupts */
+ do {
+ UT_ASSERT_INT(
+ (int)wait_for_completion_timeout(
+ &cb_in->cb_completion, HZ / 2),
+ >, 0);
+ INIT_COMPLETION(cb_in->cb_completion);
+ } while (cb_in->cb_count < 24);
+ response = smp2p_gpio_get_value(cb_in);
+ SMP2P_SET_RMT_CMD_TYPE(request, 0);
+ UT_ASSERT_HEX(request, ==, response);
+ UT_ASSERT_INT(24, ==, cb_in->cb_count);
+
+ seq_printf(s, "\tOK\n");
+ } while (0);
+
+ if (failed) {
+ pr_err("%s: Failed\n", name);
+ seq_printf(s, "\tFailed\n");
+ }
+
+ /* unregister for interrupts */
+ if (cb_in->irq_base_id) {
+ for (id = 0; id < SMP2P_BITS_PER_ENTRY; ++id)
+ free_irq(cb_in->irq_base_id + id, cb_in);
+ }
+
+ smp2p_gpio_open_test_entry("smp2p", remote_pid, false);
+ msm_smp2p_init_rmt_lpb_proc(remote_pid);
+}
+
+/**
+ * smp2p_ut_remote_inout - Verify inbound/outbound functionality for all.
+ *
+ * @s: pointer to output file
+ *
+ * This test verifies inbound and outbound functionality for all
+ * configured remote processor.
+ */
+static void smp2p_ut_remote_inout(struct seq_file *s)
+{
+ struct smp2p_interrupt_config *int_cfg;
+ int pid;
+
+ int_cfg = smp2p_get_interrupt_config();
+ if (!int_cfg) {
+ seq_printf(s, "Remote processor config unavailable\n");
+ return;
+ }
+
+ for (pid = 0; pid < SMP2P_NUM_PROCS; ++pid) {
+ if (!int_cfg[pid].is_configured)
+ continue;
+
+ smp2p_ut_remote_inout_core(s, pid, __func__);
+ }
+}
+
+static int __init smp2p_debugfs_init(void)
+{
+ /* register GPIO pins */
+ (void)platform_driver_register(&smp2p_gpio_driver);
+
+ /*
+ * Add Unit Test entries.
+ *
+ * The idea with unit tests is that you can run all of them
+ * from ADB shell by doing:
+ * adb shell
+ * cat ut*
+ *
+ * And if particular tests fail, you can then repeatedly run the
+ * failing tests as you debug and resolve the failing test.
+ */
+ smp2p_debug_create("ut_local_gpio_out", smp2p_ut_local_gpio_out);
+ smp2p_debug_create("ut_local_gpio_in", smp2p_ut_local_gpio_in);
+ smp2p_debug_create("ut_remote_gpio_inout", smp2p_ut_remote_inout);
+ return 0;
+}
+late_initcall(smp2p_debugfs_init);
diff --git a/arch/arm/mach-msm/smp2p_loopback.c b/arch/arm/mach-msm/smp2p_loopback.c
new file mode 100644
index 0000000..d95c93f
--- /dev/null
+++ b/arch/arm/mach-msm/smp2p_loopback.c
@@ -0,0 +1,440 @@
+/* arch/arm/mach-msm/smp2p_loopback.c
+ *
+ * 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/debugfs.h>
+#include <linux/list.h>
+#include <linux/ctype.h>
+#include <linux/jiffies.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/completion.h>
+#include <linux/termios.h>
+#include <linux/module.h>
+#include <linux/remote_spinlock.h>
+#include "smd_private.h"
+#include "smp2p_private.h"
+
+/**
+ * struct smp2p_loopback_ctx - Representation of remote loopback object.
+ *
+ * @proc_id: Processor id of the processor that sends the loopback commands.
+ * @out: Handle to the smem entry structure for providing the response.
+ * @out_nb: Notifies the opening of local entry.
+ * @out_is_active: Outbound entry events should be processed.
+ * @in_nb: Notifies changes in the remote entry.
+ * @in_is_active: Inbound entry events should be processed.
+ * @rmt_lpb_work: Work item that handles the incoming loopback commands.
+ * @rmt_cmd: Structure that holds the current and previous value of the entry.
+ */
+struct smp2p_loopback_ctx {
+ int proc_id;
+ struct msm_smp2p_out *out;
+ struct notifier_block out_nb;
+ bool out_is_active;
+ struct notifier_block in_nb;
+ bool in_is_active;
+ struct work_struct rmt_lpb_work;
+ struct msm_smp2p_update_notif rmt_cmd;
+};
+
+static struct smp2p_loopback_ctx remote_loopback[SMP2P_NUM_PROCS];
+static struct msm_smp2p_remote_mock remote_mock;
+
+/**
+ * remote_spinlock_test - Handles remote spinlock test.
+ *
+ * @ctx: Loopback context
+ */
+static void remote_spinlock_test(struct smp2p_loopback_ctx *ctx)
+{
+ uint32_t test_request;
+ uint32_t test_response;
+ unsigned long flags;
+ int n;
+ unsigned lock_count = 0;
+ remote_spinlock_t *smem_spinlock;
+
+ test_request = 0x0;
+ SMP2P_SET_RMT_CMD_TYPE_REQ(test_request);
+ smem_spinlock = smem_get_remote_spinlock();
+ if (!smem_spinlock) {
+ pr_err("%s: unable to get remote spinlock\n", __func__);
+ return;
+ }
+
+ for (;;) {
+ remote_spin_lock_irqsave(smem_spinlock, flags);
+ ++lock_count;
+ SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_RSPIN_LOCKED);
+ (void)msm_smp2p_out_write(ctx->out, test_request);
+
+ for (n = 0; n < 10000; ++n) {
+ (void)msm_smp2p_in_read(ctx->proc_id,
+ "smp2p", &test_response);
+ test_response = SMP2P_GET_RMT_CMD(test_response);
+
+ if (test_response == SMP2P_LB_CMD_RSPIN_END)
+ break;
+
+ if (test_response != SMP2P_LB_CMD_RSPIN_UNLOCKED)
+ SMP2P_ERR("%s: invalid spinlock command %x\n",
+ __func__, test_response);
+ }
+
+ if (test_response == SMP2P_LB_CMD_RSPIN_END) {
+ SMP2P_SET_RMT_CMD_TYPE_RESP(test_request);
+ SMP2P_SET_RMT_CMD(test_request,
+ SMP2P_LB_CMD_RSPIN_END);
+ SMP2P_SET_RMT_DATA(test_request, lock_count);
+ (void)msm_smp2p_out_write(ctx->out, test_request);
+ break;
+ }
+
+ SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_RSPIN_UNLOCKED);
+ (void)msm_smp2p_out_write(ctx->out, test_request);
+ remote_spin_unlock_irqrestore(smem_spinlock, flags);
+ }
+ remote_spin_unlock_irqrestore(smem_spinlock, flags);
+}
+
+/**
+ * smp2p_rmt_lpb_worker - Handles incoming remote loopback commands.
+ *
+ * @work: Work Item scheduled to handle the incoming commands.
+ */
+static void smp2p_rmt_lpb_worker(struct work_struct *work)
+{
+ struct smp2p_loopback_ctx *ctx;
+ int lpb_cmd;
+ int lpb_cmd_type;
+ int lpb_data;
+
+ ctx = container_of(work, struct smp2p_loopback_ctx, rmt_lpb_work);
+
+ if (!ctx->in_is_active || !ctx->out_is_active)
+ return;
+
+ if (ctx->rmt_cmd.previous_value == ctx->rmt_cmd.current_value)
+ return;
+
+ lpb_cmd_type = SMP2P_GET_RMT_CMD_TYPE(ctx->rmt_cmd.current_value);
+ lpb_cmd = SMP2P_GET_RMT_CMD(ctx->rmt_cmd.current_value);
+ lpb_data = SMP2P_GET_RMT_DATA(ctx->rmt_cmd.current_value);
+
+ if (lpb_cmd & SMP2P_RLPB_IGNORE)
+ return;
+
+ switch (lpb_cmd) {
+ case SMP2P_LB_CMD_NOOP:
+ /* Do nothing */
+ break;
+
+ case SMP2P_LB_CMD_ECHO:
+ SMP2P_SET_RMT_CMD_TYPE(ctx->rmt_cmd.current_value, 0);
+ SMP2P_SET_RMT_DATA(ctx->rmt_cmd.current_value,
+ lpb_data);
+ (void)msm_smp2p_out_write(ctx->out,
+ ctx->rmt_cmd.current_value);
+ break;
+
+ case SMP2P_LB_CMD_CLEARALL:
+ ctx->rmt_cmd.current_value = 0;
+ (void)msm_smp2p_out_write(ctx->out,
+ ctx->rmt_cmd.current_value);
+ break;
+
+ case SMP2P_LB_CMD_PINGPONG:
+ SMP2P_SET_RMT_CMD_TYPE(ctx->rmt_cmd.current_value, 0);
+ if (lpb_data) {
+ lpb_data--;
+ SMP2P_SET_RMT_DATA(ctx->rmt_cmd.current_value,
+ lpb_data);
+ (void)msm_smp2p_out_write(ctx->out,
+ ctx->rmt_cmd.current_value);
+ }
+ break;
+
+ case SMP2P_LB_CMD_RSPIN_START:
+ remote_spinlock_test(ctx);
+ break;
+
+ case SMP2P_LB_CMD_RSPIN_LOCKED:
+ case SMP2P_LB_CMD_RSPIN_UNLOCKED:
+ case SMP2P_LB_CMD_RSPIN_END:
+ /* not used for remote spinlock test */
+ break;
+
+ default:
+ SMP2P_DBG("%s: Unknown loopback command %x\n",
+ __func__, lpb_cmd);
+ break;
+ }
+}
+
+/**
+ * smp2p_rmt_in_edge_notify - Schedules a work item to handle the commands.
+ *
+ * @nb: Notifier block, this is called when the value in remote entry changes.
+ * @event: Takes value SMP2P_ENTRY_UPDATE or SMP2P_OPEN based on the event.
+ * @data: Consists of previous and current value in case of entry update.
+ * @returns: 0 for success (return value required for notifier chains).
+ */
+static int smp2p_rmt_in_edge_notify(struct notifier_block *nb,
+ unsigned long event, void *data)
+{
+ struct smp2p_loopback_ctx *ctx;
+
+ if (!(event == SMP2P_ENTRY_UPDATE || event == SMP2P_OPEN))
+ return 0;
+
+ ctx = container_of(nb, struct smp2p_loopback_ctx, in_nb);
+ if (data && ctx->in_is_active) {
+ ctx->rmt_cmd =
+ *(struct msm_smp2p_update_notif *)data;
+ schedule_work(&ctx->rmt_lpb_work);
+ }
+
+ return 0;
+}
+
+/**
+ * smp2p_rmt_out_edge_notify - Notifies on the opening of the outbound entry.
+ *
+ * @nb: Notifier block, this is called when the local entry is open.
+ * @event: Takes on value SMP2P_OPEN when the local entry is open.
+ * @data: Consist of current value of the remote entry, if entry is open.
+ * @returns: 0 for success (return value required for notifier chains).
+ */
+static int smp2p_rmt_out_edge_notify(struct notifier_block *nb,
+ unsigned long event, void *data)
+{
+ struct smp2p_loopback_ctx *ctx;
+
+ ctx = container_of(nb, struct smp2p_loopback_ctx, out_nb);
+ if (event == SMP2P_OPEN)
+ SMP2P_DBG("%s: 'smp2p':%d opened\n", __func__,
+ ctx->proc_id);
+
+ return 0;
+}
+
+/**
+ * msm_smp2p_init_rmt_lpb - Initializes the remote loopback object.
+ *
+ * @ctx: Pointer to remote loopback object that needs to be initialized.
+ * @pid: Processor id of the processor that is sending the commands.
+ * @entry: Name of the entry that needs to be opened locally.
+ * @returns: 0 on success, standard Linux error code otherwise.
+ */
+static int msm_smp2p_init_rmt_lpb(struct smp2p_loopback_ctx *ctx,
+ int pid, const char *entry)
+{
+ int ret = 0;
+ int tmp;
+
+ if (!ctx || !entry || pid > SMP2P_NUM_PROCS)
+ return -EINVAL;
+
+ ctx->in_nb.notifier_call = smp2p_rmt_in_edge_notify;
+ ctx->out_nb.notifier_call = smp2p_rmt_out_edge_notify;
+ ctx->proc_id = pid;
+ ctx->in_is_active = true;
+ ctx->out_is_active = true;
+ tmp = msm_smp2p_out_open(pid, entry, &ctx->out_nb,
+ &ctx->out);
+ if (tmp) {
+ SMP2P_ERR("%s: open failed outbound entry '%s':%d - ret %d\n",
+ __func__, entry, pid, tmp);
+ ret = tmp;
+ }
+
+ tmp = msm_smp2p_in_register(ctx->proc_id,
+ SMP2P_RLPB_ENTRY_NAME,
+ &ctx->in_nb);
+ if (tmp) {
+ SMP2P_ERR("%s: unable to open inbound entry '%s':%d - ret %d\n",
+ __func__, entry, pid, tmp);
+ ret = tmp;
+ }
+
+ return ret;
+}
+
+/**
+ * msm_smp2p_init_rmt_lpb_proc - Wrapper over msm_smp2p_init_rmt_lpb
+ *
+ * @remote_pid: Processor ID of the processor that sends loopback command.
+ * @returns: Pointer to outbound entry handle.
+ */
+void *msm_smp2p_init_rmt_lpb_proc(int remote_pid)
+{
+ int tmp;
+ void *ret = NULL;
+
+ tmp = msm_smp2p_init_rmt_lpb(&remote_loopback[remote_pid],
+ remote_pid, SMP2P_RLPB_ENTRY_NAME);
+ if (!tmp)
+ ret = remote_loopback[remote_pid].out;
+
+ return ret;
+}
+EXPORT_SYMBOL(msm_smp2p_init_rmt_lpb_proc);
+
+/**
+ * msm_smp2p_deinit_rmt_lpb_proc - Unregister support for remote processor.
+ *
+ * @remote_pid: Processor ID of the remote system.
+ * @returns: 0 on success, standard Linux error code otherwise.
+ *
+ * Unregister loopback support for remote processor.
+ */
+int msm_smp2p_deinit_rmt_lpb_proc(int remote_pid)
+{
+ int ret = 0;
+ int tmp;
+ struct smp2p_loopback_ctx *ctx;
+
+ if (remote_pid >= SMP2P_NUM_PROCS)
+ return -EINVAL;
+
+ ctx = &remote_loopback[remote_pid];
+
+ /* abort any pending notifications */
+ remote_loopback[remote_pid].out_is_active = false;
+ remote_loopback[remote_pid].in_is_active = false;
+ flush_work(&ctx->rmt_lpb_work);
+
+ /* unregister entries */
+ tmp = msm_smp2p_out_close(&remote_loopback[remote_pid].out);
+ remote_loopback[remote_pid].out = NULL;
+ if (tmp) {
+ SMP2P_ERR("%s: outbound 'smp2p':%d close failed %d\n",
+ __func__, remote_pid, tmp);
+ ret = tmp;
+ }
+
+ tmp = msm_smp2p_in_unregister(remote_pid,
+ SMP2P_RLPB_ENTRY_NAME, &remote_loopback[remote_pid].in_nb);
+ if (tmp) {
+ SMP2P_ERR("%s: inbound 'smp2p':%d close failed %d\n",
+ __func__, remote_pid, tmp);
+ ret = tmp;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL(msm_smp2p_deinit_rmt_lpb_proc);
+
+/**
+ * msm_smp2p_set_remote_mock_exists - Sets the remote mock configuration.
+ *
+ * @item_exists: true = Remote mock SMEM item exists
+ *
+ * This is used in the testing environment to simulate the existence of the
+ * remote smem item in order to test the negotiation algorithm.
+ */
+void msm_smp2p_set_remote_mock_exists(bool item_exists)
+{
+ remote_mock.item_exists = item_exists;
+}
+EXPORT_SYMBOL(msm_smp2p_set_remote_mock_exists);
+
+/**
+ * msm_smp2p_get_remote_mock - Get remote mock object.
+ *
+ * @returns: Point to the remote mock object.
+ */
+void *msm_smp2p_get_remote_mock(void)
+{
+ return &remote_mock;
+}
+EXPORT_SYMBOL(msm_smp2p_get_remote_mock);
+
+/**
+ * msm_smp2p_get_remote_mock_smem_item - Returns a pointer to remote item.
+ *
+ * @size: Size of item.
+ * @returns: Pointer to mock remote smem item.
+ */
+void *msm_smp2p_get_remote_mock_smem_item(uint32_t *size)
+{
+ void *ptr = NULL;
+ if (remote_mock.item_exists) {
+ *size = sizeof(remote_mock.remote_item);
+ ptr = &(remote_mock.remote_item);
+ }
+
+ return ptr;
+}
+EXPORT_SYMBOL(msm_smp2p_get_remote_mock_smem_item);
+
+/**
+ * smp2p_remote_mock_rx_interrupt - Triggers receive interrupt for mock proc.
+ *
+ * @returns: 0 for success
+ *
+ * This function simulates the receiving of interrupt by the mock remote
+ * processor in a testing environment.
+ */
+int smp2p_remote_mock_rx_interrupt(void)
+{
+ remote_mock.rx_interrupt_count++;
+ if (remote_mock.initialized)
+ complete(&remote_mock.cb_completion);
+ return 0;
+}
+EXPORT_SYMBOL(smp2p_remote_mock_rx_interrupt);
+
+/**
+ * smp2p_remote_mock_tx_interrupt - Calls the SMP2P interrupt handler.
+ *
+ * This function calls the interrupt handler of the Apps processor to simulate
+ * receiving interrupts from a remote processor.
+ */
+static void smp2p_remote_mock_tx_interrupt(void)
+{
+ msm_smp2p_interrupt_handler(SMP2P_REMOTE_MOCK_PROC);
+}
+
+/**
+ * smp2p_remote_mock_init - Initialize the remote mock and loopback objects.
+ *
+ * @returns: 0 for success
+ */
+static int __init smp2p_remote_mock_init(void)
+{
+ int i;
+
+ smp2p_init_header(&remote_mock.remote_item.header,
+ SMP2P_REMOTE_MOCK_PROC, SMP2P_APPS_PROC,
+ 0, 0);
+ remote_mock.rx_interrupt_count = 0;
+ remote_mock.rx_interrupt = smp2p_remote_mock_rx_interrupt;
+ remote_mock.tx_interrupt = smp2p_remote_mock_tx_interrupt;
+ remote_mock.item_exists = false;
+ init_completion(&remote_mock.cb_completion);
+ remote_mock.initialized = true;
+
+ for (i = 0; i < SMP2P_NUM_PROCS; i++) {
+ INIT_WORK(&(remote_loopback[i].rmt_lpb_work),
+ smp2p_rmt_lpb_worker);
+ if (i == SMP2P_REMOTE_MOCK_PROC)
+ /* do not register loopback for remote mock proc */
+ continue;
+
+ msm_smp2p_init_rmt_lpb(&remote_loopback[i],
+ i, SMP2P_RLPB_ENTRY_NAME);
+ }
+ return 0;
+}
+module_init(smp2p_remote_mock_init);
diff --git a/arch/arm/mach-msm/smp2p_private.h b/arch/arm/mach-msm/smp2p_private.h
new file mode 100644
index 0000000..b9a5cfe
--- /dev/null
+++ b/arch/arm/mach-msm/smp2p_private.h
@@ -0,0 +1,226 @@
+/* arch/arm/mach-msm/smp2p_private.h
+ *
+ * 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 _ARCH_ARM_MACH_MSM_MSM_SMP2P_PRIVATE_H_
+#define _ARCH_ARM_MACH_MSM_MSM_SMP2P_PRIVATE_H_
+
+#include <linux/types.h>
+#include <linux/spinlock.h>
+#include <mach/msm_ipc_logging.h>
+#include "smp2p_private_api.h"
+
+#define SMP2P_MAX_ENTRY 16
+#define SMP2P_LOCAL_VERSION 1
+#define SMP2P_LOCAL_FEATURE 0x0
+
+/* SMEM Item Header Macros */
+#define SMP2P_MAGIC 0x504D5324
+#define SMP2P_LOCAL_PID_MASK 0x0000ffff
+#define SMP2P_LOCAL_PID_BIT 0
+#define SMP2P_REMOTE_PID_MASK 0xffff0000
+#define SMP2P_REMOTE_PID_BIT 16
+#define SMP2P_VERSION_MASK 0x000000ff
+#define SMP2P_VERSION_BIT 0
+#define SMP2P_FEATURE_MASK 0xffffff00
+#define SMP2P_FEATURE_BIT 8
+#define SMP2P_ENT_TOTAL_MASK 0x0000ffff
+#define SMP2P_ENT_TOTAL_BIT 0
+#define SMP2P_ENT_VALID_MASK 0xffff0000
+#define SMP2P_ENT_VALID_BIT 16
+
+#define SMP2P_GET_BITS(hdr_val, mask, bit) \
+ (((hdr_val) & (mask)) >> (bit))
+#define SMP2P_SET_BITS(hdr_val, mask, bit, new_value) \
+ do {\
+ hdr_val = (hdr_val & ~(mask)) \
+ | (((new_value) << (bit)) & (mask)); \
+ } while (0)
+
+#define SMP2P_GET_LOCAL_PID(hdr) \
+ SMP2P_GET_BITS(hdr, SMP2P_LOCAL_PID_MASK, SMP2P_LOCAL_PID_BIT)
+#define SMP2P_SET_LOCAL_PID(hdr, pid) \
+ SMP2P_SET_BITS(hdr, SMP2P_LOCAL_PID_MASK, SMP2P_LOCAL_PID_BIT, pid)
+
+#define SMP2P_GET_REMOTE_PID(hdr) \
+ SMP2P_GET_BITS(hdr, SMP2P_REMOTE_PID_MASK, SMP2P_REMOTE_PID_BIT)
+#define SMP2P_SET_REMOTE_PID(hdr, pid) \
+ SMP2P_SET_BITS(hdr, SMP2P_REMOTE_PID_MASK, SMP2P_REMOTE_PID_BIT, pid)
+
+#define SMP2P_GET_VERSION(hdr) \
+ SMP2P_GET_BITS(hdr, SMP2P_VERSION_MASK, SMP2P_VERSION_BIT)
+#define SMP2P_SET_VERSION(hdr, version) \
+ SMP2P_SET_BITS(hdr, SMP2P_VERSION_MASK, SMP2P_VERSION_BIT, version)
+
+#define SMP2P_GET_FEATURES(hdr) \
+ SMP2P_GET_BITS(hdr, SMP2P_FEATURE_MASK, SMP2P_FEATURE_BIT)
+#define SMP2P_SET_FEATURES(hdr, features) \
+ SMP2P_SET_BITS(hdr, SMP2P_FEATURE_MASK, SMP2P_FEATURE_BIT, features)
+
+#define SMP2P_GET_ENT_TOTAL(hdr) \
+ SMP2P_GET_BITS(hdr, SMP2P_ENT_TOTAL_MASK, SMP2P_ENT_TOTAL_BIT)
+#define SMP2P_SET_ENT_TOTAL(hdr, entries) \
+ SMP2P_SET_BITS(hdr, SMP2P_ENT_TOTAL_MASK, SMP2P_ENT_TOTAL_BIT, entries)
+
+#define SMP2P_GET_ENT_VALID(hdr) \
+ SMP2P_GET_BITS(hdr, SMP2P_ENT_VALID_MASK, SMP2P_ENT_VALID_BIT)
+#define SMP2P_SET_ENT_VALID(hdr, entries) \
+ SMP2P_SET_BITS(hdr, SMP2P_ENT_VALID_MASK, SMP2P_ENT_VALID_BIT,\
+ entries)
+
+/* Loopback Command Macros */
+#define SMP2P_RMT_CMD_TYPE_MASK 0x80000000
+#define SMP2P_RMT_CMD_TYPE_BIT 31
+#define SMP2P_RMT_IGNORE_MASK 0x40000000
+#define SMP2P_RMT_IGNORE_BIT 30
+#define SMP2P_RMT_CMD_MASK 0x3f000000
+#define SMP2P_RMT_CMD_BIT 24
+#define SMP2P_RMT_DATA_MASK 0x00ffffff
+#define SMP2P_RMT_DATA_BIT 0
+
+#define SMP2P_GET_RMT_CMD_TYPE(val) \
+ SMP2P_GET_BITS(val, SMP2P_RMT_CMD_TYPE_MASK, SMP2P_RMT_CMD_TYPE_BIT)
+#define SMP2P_GET_RMT_CMD(val) \
+ SMP2P_GET_BITS(val, SMP2P_RMT_CMD_MASK, SMP2P_RMT_CMD_BIT)
+
+#define SMP2P_GET_RMT_DATA(val) \
+ SMP2P_GET_BITS(val, SMP2P_RMT_DATA_MASK, SMP2P_RMT_DATA_BIT)
+
+#define SMP2P_SET_RMT_CMD_TYPE(val, cmd_type) \
+ SMP2P_SET_BITS(val, SMP2P_RMT_CMD_TYPE_MASK, SMP2P_RMT_CMD_TYPE_BIT, \
+ cmd_type)
+#define SMP2P_SET_RMT_CMD_TYPE_REQ(val) \
+ SMP2P_SET_RMT_CMD_TYPE(val, 1)
+#define SMP2P_SET_RMT_CMD_TYPE_RESP(val) \
+ SMP2P_SET_RMT_CMD_TYPE(val, 0)
+
+#define SMP2P_SET_RMT_CMD(val, cmd) \
+ SMP2P_SET_BITS(val, SMP2P_RMT_CMD_MASK, SMP2P_RMT_CMD_BIT, \
+ cmd)
+#define SMP2P_SET_RMT_DATA(val, data) \
+ SMP2P_SET_BITS(val, SMP2P_RMT_DATA_MASK, SMP2P_RMT_DATA_BIT, data)
+
+enum {
+ SMP2P_LB_CMD_NOOP = 0x0,
+ SMP2P_LB_CMD_ECHO,
+ SMP2P_LB_CMD_CLEARALL,
+ SMP2P_LB_CMD_PINGPONG,
+ SMP2P_LB_CMD_RSPIN_START,
+ SMP2P_LB_CMD_RSPIN_LOCKED,
+ SMP2P_LB_CMD_RSPIN_UNLOCKED,
+ SMP2P_LB_CMD_RSPIN_END,
+};
+#define SMP2P_RLPB_IGNORE 0x40
+#define SMP2P_RLPB_ENTRY_NAME "smp2p"
+
+/* Debug Logging Macros */
+enum {
+ MSM_SMP2P_INFO = 1U << 0,
+ MSM_SMP2P_DEBUG = 1U << 1,
+ MSM_SMP2P_GPIO = 1U << 2,
+};
+
+#define SMP2P_IPC_LOG_STR(x...) do { \
+ if (smp2p_get_log_ctx()) \
+ ipc_log_string(smp2p_get_log_ctx(), x); \
+} while (0)
+
+#define SMP2P_DBG(x...) do { \
+ if (smp2p_get_debug_mask() & MSM_SMP2P_DEBUG) \
+ SMP2P_IPC_LOG_STR(x); \
+} while (0)
+
+#define SMP2P_INFO(x...) do { \
+ if (smp2p_get_debug_mask() & MSM_SMP2P_INFO) \
+ SMP2P_IPC_LOG_STR(x); \
+} while (0)
+
+#define SMP2P_ERR(x...) do { \
+ pr_err(x); \
+ SMP2P_IPC_LOG_STR(x); \
+} while (0)
+
+#define SMP2P_GPIO(x...) do { \
+ if (smp2p_get_debug_mask() & MSM_SMP2P_GPIO) \
+ SMP2P_IPC_LOG_STR(x); \
+} while (0)
+
+
+enum msm_smp2p_edge_state {
+ SMP2P_EDGE_STATE_CLOSED,
+ SMP2P_EDGE_STATE_OPENING,
+ SMP2P_EDGE_STATE_OPENED,
+ SMP2P_EDGE_STATE_FAILED = 0xff,
+};
+
+struct smp2p_smem {
+ uint32_t magic;
+ uint32_t feature_version;
+ uint32_t rem_loc_proc_id;
+ uint32_t valid_total_ent;
+ uint32_t reserved;
+};
+
+struct smp2p_entry_v1 {
+ char name[SMP2P_MAX_ENTRY_NAME];
+ uint32_t entry;
+};
+
+struct smp2p_smem_item {
+ struct smp2p_smem header;
+ struct smp2p_entry_v1 entries[SMP2P_MAX_ENTRY];
+};
+
+/* Mock object for internal loopback testing. */
+struct msm_smp2p_remote_mock {
+ struct smp2p_smem_item remote_item;
+ int rx_interrupt_count;
+ int (*rx_interrupt)(void);
+ void (*tx_interrupt)(void);
+
+ bool item_exists;
+ bool initialized;
+ struct completion cb_completion;
+};
+
+void smp2p_init_header(struct smp2p_smem *header_ptr, int local_pid,
+ int remote_pid, uint32_t features, uint32_t version);
+void *msm_smp2p_get_remote_mock(void);
+int smp2p_remote_mock_rx_interrupt(void);
+int smp2p_reset_mock_edge(void);
+void msm_smp2p_interrupt_handler(int);
+void msm_smp2p_set_remote_mock_exists(bool item_exists);
+void *msm_smp2p_get_remote_mock_smem_item(uint32_t *size);
+void *msm_smp2p_init_rmt_lpb_proc(int remote_pid);
+int msm_smp2p_deinit_rmt_lpb_proc(int remote_pid);
+void *smp2p_get_log_ctx(void);
+int smp2p_get_debug_mask(void);
+
+/* Inbound / outbound Interrupt configuration. */
+struct smp2p_interrupt_config {
+ bool is_configured;
+ uint32_t *out_int_ptr;
+ uint32_t out_int_mask;
+ int in_int_id;
+ const char *name;
+
+ /* interrupt stats */
+ unsigned in_interrupt_count;
+ unsigned out_interrupt_count;
+};
+
+struct smp2p_interrupt_config *smp2p_get_interrupt_config(void);
+const char *smp2p_pid_to_name(int remote_pid);
+struct smp2p_smem *smp2p_get_in_item(int remote_pid);
+struct smp2p_smem *smp2p_get_out_item(int remote_pid, int *state);
+void smp2p_gpio_open_test_entry(const char *name, int remote_pid, bool do_open);
+#endif
diff --git a/arch/arm/mach-msm/smp2p_private_api.h b/arch/arm/mach-msm/smp2p_private_api.h
new file mode 100644
index 0000000..c757eec
--- /dev/null
+++ b/arch/arm/mach-msm/smp2p_private_api.h
@@ -0,0 +1,79 @@
+/* arch/arm/mach-msm/smp2p_private_api.h
+ *
+ * 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 _ARCH_ARM_MACH_MSM_SMP2P_PRIVATE_API_H_
+#define _ARCH_ARM_MACH_MSM_SMP2P_PRIVATE_API_H_
+
+#include <linux/notifier.h>
+
+struct msm_smp2p_out;
+
+/* Maximum size of the entry name and trailing null. */
+#define SMP2P_MAX_ENTRY_NAME 16
+
+/* Bits per entry */
+#define SMP2P_BITS_PER_ENTRY 32
+
+/* Processor ID's */
+enum {
+ SMP2P_APPS_PROC = 0,
+ SMP2P_MODEM_PROC = 1,
+ SMP2P_AUDIO_PROC = 2,
+ SMP2P_RESERVED_PROC_1 = 3,
+ SMP2P_WIRELESS_PROC = 4,
+ SMP2P_RESERVED_PROC_2 = 5,
+ SMP2P_POWER_PROC = 6,
+ /* add new processors here */
+
+ SMP2P_REMOTE_MOCK_PROC,
+ SMP2P_NUM_PROCS,
+};
+
+/**
+ * Notification events that are passed to notifier for incoming and outgoing
+ * entries.
+ *
+ * If the @metadata argument in the notifier is non-null, then it will
+ * point to the associated struct smux_meta_* structure.
+ */
+enum msm_smp2p_events {
+ SMP2P_OPEN, /* data is NULL */
+ SMP2P_ENTRY_UPDATE, /* data => struct msm_smp2p_update_notif */
+};
+
+/**
+ * Passed in response to a SMP2P_ENTRY_UPDATE event.
+ *
+ * @prev_value: previous value of entry
+ * @current_value: latest value of entry
+ */
+struct msm_smp2p_update_notif {
+ uint32_t previous_value;
+ uint32_t current_value;
+};
+
+int msm_smp2p_out_open(int remote_pid, const char *entry,
+ struct notifier_block *open_notifier,
+ struct msm_smp2p_out **handle);
+int msm_smp2p_out_close(struct msm_smp2p_out **handle);
+int msm_smp2p_out_read(struct msm_smp2p_out *handle, uint32_t *data);
+int msm_smp2p_out_write(struct msm_smp2p_out *handle, uint32_t data);
+int msm_smp2p_out_modify(struct msm_smp2p_out *handle, uint32_t set_mask,
+ uint32_t clear_mask);
+int msm_smp2p_in_read(int remote_pid, const char *entry, uint32_t *data);
+int msm_smp2p_in_register(int remote_pid, const char *entry,
+ struct notifier_block *in_notifier);
+int msm_smp2p_in_unregister(int remote_pid, const char *entry,
+ struct notifier_block *in_notifier);
+
+#endif /* _ARCH_ARM_MACH_MSM_SMP2P_PRIVATE_API_H_ */
diff --git a/arch/arm/mach-msm/smp2p_spinlock_test.c b/arch/arm/mach-msm/smp2p_spinlock_test.c
new file mode 100644
index 0000000..09d7c0d
--- /dev/null
+++ b/arch/arm/mach-msm/smp2p_spinlock_test.c
@@ -0,0 +1,487 @@
+/* arch/arm/mach-msm/smp2p_spinlock_test.c
+ *
+ * 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/debugfs.h>
+#include <linux/ctype.h>
+#include <linux/jiffies.h>
+#include <linux/delay.h>
+#include <linux/completion.h>
+#include <linux/remote_spinlock.h>
+#include <mach/msm_smsm.h>
+#include "smd_private.h"
+#include "smp2p_private.h"
+#include "smp2p_test_common.h"
+
+#define REMOTE_SPIN_PID 1
+#define RS_END_THIEF_PID_BIT 20
+#define RS_END_THIEF_MASK 0x00f00000
+
+/* Spinlock commands used for testing Apps<->RPM spinlocks. */
+enum RPM_SPINLOCK_CMDS {
+ RPM_CMD_INVALID,
+ RPM_CMD_START,
+ RPM_CMD_LOCKED,
+ RPM_CMD_UNLOCKED,
+ RPM_CMD_END,
+};
+
+/* Shared structure for testing Apps<->RPM spinlocks. */
+struct rpm_spinlock_test {
+ uint32_t apps_cmd;
+ uint32_t apps_lock_count;
+ uint32_t rpm_cmd;
+ uint32_t rpm_lock_count;
+};
+
+/**
+ * smp2p_ut_remote_spinlock_core - Verify remote spinlock.
+ *
+ * @s: Pointer to output file
+ * @remote_pid: Remote processor to test
+ * @use_trylock: Use trylock to prevent an Apps deadlock if the
+ * remote spinlock fails.
+ */
+static void smp2p_ut_remote_spinlock_core(struct seq_file *s, int remote_pid,
+ bool use_trylock)
+{
+ int failed = 0;
+ unsigned lock_count = 0;
+ struct msm_smp2p_out *handle = NULL;
+ int ret;
+ uint32_t test_request;
+ uint32_t test_response;
+ struct mock_cb_data cb_out;
+ struct mock_cb_data cb_in;
+ unsigned long flags;
+ unsigned n;
+ unsigned test_num;
+ bool have_lock;
+ bool timeout;
+ int failed_tmp;
+ int spinlock_owner;
+ remote_spinlock_t *smem_spinlock;
+
+ seq_printf(s, "Running %s for '%s' remote pid %d\n",
+ __func__, smp2p_pid_to_name(remote_pid), remote_pid);
+
+ cb_out.initialized = false;
+ cb_in.initialized = false;
+ mock_cb_data_init(&cb_out);
+ mock_cb_data_init(&cb_in);
+ do {
+ smem_spinlock = smem_get_remote_spinlock();
+ UT_ASSERT_PTR(smem_spinlock, !=, NULL);
+
+ /* Open output entry */
+ ret = msm_smp2p_out_open(remote_pid, SMP2P_RLPB_ENTRY_NAME,
+ &cb_out.nb, &handle);
+ UT_ASSERT_INT(ret, ==, 0);
+ UT_ASSERT_INT(
+ (int)wait_for_completion_timeout(
+ &cb_out.cb_completion, HZ * 2),
+ >, 0);
+ UT_ASSERT_INT(cb_out.cb_count, ==, 1);
+ UT_ASSERT_INT(cb_out.event_open, ==, 1);
+
+ /* Open inbound entry */
+ ret = msm_smp2p_in_register(remote_pid, SMP2P_RLPB_ENTRY_NAME,
+ &cb_in.nb);
+ UT_ASSERT_INT(ret, ==, 0);
+ UT_ASSERT_INT(
+ (int)wait_for_completion_timeout(
+ &cb_in.cb_completion, HZ * 2),
+ >, 0);
+ UT_ASSERT_INT(cb_in.cb_count, ==, 1);
+ UT_ASSERT_INT(cb_in.event_open, ==, 1);
+
+ /* Send start */
+ mock_cb_data_reset(&cb_in);
+ mock_cb_data_reset(&cb_out);
+ test_request = 0x0;
+ SMP2P_SET_RMT_CMD_TYPE_REQ(test_request);
+ SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_RSPIN_START);
+ SMP2P_SET_RMT_DATA(test_request, 0x0);
+ ret = msm_smp2p_out_write(handle, test_request);
+ UT_ASSERT_INT(ret, ==, 0);
+
+ UT_ASSERT_INT(
+ (int)wait_for_completion_timeout(
+ &cb_in.cb_completion, HZ * 2),
+ >, 0);
+ UT_ASSERT_INT(cb_in.cb_count, ==, 1);
+ UT_ASSERT_INT(cb_in.event_entry_update, ==, 1);
+ ret = msm_smp2p_in_read(remote_pid, SMP2P_RLPB_ENTRY_NAME,
+ &test_response);
+ UT_ASSERT_INT(ret, ==, 0);
+
+ test_response = SMP2P_GET_RMT_CMD(test_response);
+ if (test_response != SMP2P_LB_CMD_RSPIN_LOCKED &&
+ test_response != SMP2P_LB_CMD_RSPIN_UNLOCKED) {
+ /* invalid response from remote - abort test */
+ test_request = 0x0;
+ SMP2P_SET_RMT_CMD_TYPE(test_request, 1);
+ SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_RSPIN_END);
+ SMP2P_SET_RMT_DATA(test_request, 0x0);
+ ret = msm_smp2p_out_write(handle, test_request);
+ UT_ASSERT_HEX(SMP2P_LB_CMD_RSPIN_LOCKED, ==,
+ test_response);
+ }
+
+ /* Run spinlock test */
+ if (use_trylock)
+ seq_printf(s, "\tUsing remote_spin_trylock\n");
+ else
+ seq_printf(s, "\tUsing remote_spin_lock\n");
+
+ flags = 0;
+ have_lock = false;
+ timeout = false;
+ spinlock_owner = 0;
+ test_request = 0x0;
+ SMP2P_SET_RMT_CMD_TYPE_REQ(test_request);
+ for (test_num = 0; !failed && test_num < 10000; ++test_num) {
+ /* try to acquire spinlock */
+ if (use_trylock) {
+ unsigned long j_start = jiffies;
+ while (!remote_spin_trylock_irqsave(
+ smem_spinlock, flags)) {
+ if (jiffies_to_msecs(jiffies - j_start)
+ > 1000) {
+ seq_printf(s,
+ "\tFail: Timeout trying to get the lock\n");
+ timeout = true;
+ break;
+ }
+ }
+ if (timeout)
+ break;
+ } else {
+ remote_spin_lock_irqsave(smem_spinlock, flags);
+ }
+ have_lock = true;
+ ++lock_count;
+
+ /* tell the remote side that we have the lock */
+ SMP2P_SET_RMT_DATA(test_request, lock_count);
+ SMP2P_SET_RMT_CMD(test_request,
+ SMP2P_LB_CMD_RSPIN_LOCKED);
+ ret = msm_smp2p_out_write(handle, test_request);
+ UT_ASSERT_INT(ret, ==, 0);
+
+ /* verify the other side doesn't say it has the lock */
+ for (n = 0; n < 1000; ++n) {
+ spinlock_owner =
+ remote_spin_owner(smem_spinlock);
+ if (spinlock_owner != REMOTE_SPIN_PID) {
+ /* lock stolen by remote side */
+ seq_printf(s,
+ "\tFail: Remote side (%d) stole lock (pid %d)\n",
+ remote_pid, spinlock_owner);
+ failed = true;
+ break;
+ }
+ spinlock_owner = 0;
+
+ ret = msm_smp2p_in_read(remote_pid,
+ SMP2P_RLPB_ENTRY_NAME, &test_response);
+ UT_ASSERT_INT(ret, ==, 0);
+ test_response =
+ SMP2P_GET_RMT_CMD(test_response);
+ UT_ASSERT_HEX(SMP2P_LB_CMD_RSPIN_UNLOCKED, ==,
+ test_response);
+ }
+ if (failed)
+ break;
+
+ /* tell remote side we are unlocked and release lock */
+ SMP2P_SET_RMT_CMD(test_request,
+ SMP2P_LB_CMD_RSPIN_UNLOCKED);
+ (void)msm_smp2p_out_write(handle, test_request);
+ have_lock = false;
+ remote_spin_unlock_irqrestore(smem_spinlock, flags);
+ }
+ if (have_lock)
+ remote_spin_unlock_irqrestore(smem_spinlock, flags);
+
+ /* End test */
+ mock_cb_data_reset(&cb_in);
+ SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_RSPIN_END);
+ SMP2P_SET_RMT_DATA(test_request, lock_count |
+ (spinlock_owner << RS_END_THIEF_PID_BIT));
+ (void)msm_smp2p_out_write(handle, test_request);
+
+ failed_tmp = failed;
+ failed = false;
+ do {
+ UT_ASSERT_INT(
+ (int)wait_for_completion_timeout(
+ &cb_in.cb_completion, HZ * 2),
+ >, 0);
+ INIT_COMPLETION(cb_in.cb_completion);
+ ret = msm_smp2p_in_read(remote_pid,
+ SMP2P_RLPB_ENTRY_NAME, &test_response);
+ UT_ASSERT_INT(ret, ==, 0);
+ } while (!failed &&
+ SMP2P_GET_RMT_CMD(test_response) !=
+ SMP2P_LB_CMD_RSPIN_END);
+ if (failed)
+ break;
+ failed = failed_tmp;
+
+ test_response = SMP2P_GET_RMT_DATA(test_response);
+ seq_printf(s,
+ "\tLocked spinlock local %u times; remote %u times",
+ lock_count,
+ test_response & ((1 << RS_END_THIEF_PID_BIT) - 1)
+ );
+ if (test_response & RS_END_THIEF_MASK) {
+ seq_printf(s,
+ "Remote side reporting lock stolen by pid %d.\n",
+ SMP2P_GET_BITS(test_response,
+ RS_END_THIEF_MASK,
+ RS_END_THIEF_PID_BIT));
+ failed = 1;
+ }
+ seq_printf(s, "\n");
+
+ /* Cleanup */
+ ret = msm_smp2p_out_close(&handle);
+ UT_ASSERT_INT(ret, ==, 0);
+ UT_ASSERT_PTR(handle, ==, NULL);
+ ret = msm_smp2p_in_unregister(remote_pid,
+ SMP2P_RLPB_ENTRY_NAME, &cb_in.nb);
+ UT_ASSERT_INT(ret, ==, 0);
+
+ if (!failed && !timeout)
+ seq_printf(s, "\tOK\n");
+ } while (0);
+
+ if (failed) {
+ if (handle) {
+ /* send end command */
+ test_request = 0;
+ SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_RSPIN_END);
+ SMP2P_SET_RMT_DATA(test_request, lock_count);
+ (void)msm_smp2p_out_write(handle, test_request);
+ (void)msm_smp2p_out_close(&handle);
+ }
+ (void)msm_smp2p_in_unregister(remote_pid,
+ SMP2P_RLPB_ENTRY_NAME, &cb_in.nb);
+
+ pr_err("%s: Failed\n", __func__);
+ seq_printf(s, "\tFailed\n");
+ }
+}
+
+/**
+ * smp2p_ut_remote_spinlock_pid - Verify remote spinlock for a processor.
+ *
+ * @s: Pointer to output file
+ * @pid: Processor to test
+ * @use_trylock: Use trylock to prevent an Apps deadlock if the
+ * remote spinlock fails.
+ */
+static void smp2p_ut_remote_spinlock_pid(struct seq_file *s, int pid,
+ bool use_trylock)
+{
+ struct smp2p_interrupt_config *int_cfg;
+
+ int_cfg = smp2p_get_interrupt_config();
+ if (!int_cfg) {
+ seq_printf(s, "Remote processor config unavailable\n");
+ return;
+ }
+
+ if (pid >= SMP2P_NUM_PROCS || !int_cfg[pid].is_configured)
+ return;
+
+ msm_smp2p_deinit_rmt_lpb_proc(pid);
+ smp2p_ut_remote_spinlock_core(s, pid, use_trylock);
+ msm_smp2p_init_rmt_lpb_proc(pid);
+}
+
+/**
+ * smp2p_ut_remote_spinlock - Verify remote spinlock for all processors.
+ *
+ * @s: pointer to output file
+ */
+static void smp2p_ut_remote_spinlock(struct seq_file *s)
+{
+ int pid;
+
+ for (pid = 0; pid < SMP2P_NUM_PROCS; ++pid)
+ smp2p_ut_remote_spinlock_pid(s, pid, false);
+}
+
+/**
+ * smp2p_ut_remote_spin_trylock - Verify remote trylock for all processors.
+ *
+ * @s: Pointer to output file
+ */
+static void smp2p_ut_remote_spin_trylock(struct seq_file *s)
+{
+ int pid;
+
+ for (pid = 0; pid < SMP2P_NUM_PROCS; ++pid)
+ smp2p_ut_remote_spinlock_pid(s, pid, true);
+}
+
+/**
+ * smp2p_ut_remote_spinlock - Verify remote spinlock for all processors.
+ *
+ * @s: pointer to output file
+ *
+ * This test verifies inbound and outbound functionality for all
+ * configured remote processor.
+ */
+static void smp2p_ut_remote_spinlock_modem(struct seq_file *s)
+{
+ smp2p_ut_remote_spinlock_pid(s, SMP2P_MODEM_PROC, false);
+}
+
+static void smp2p_ut_remote_spinlock_adsp(struct seq_file *s)
+{
+ smp2p_ut_remote_spinlock_pid(s, SMP2P_AUDIO_PROC, false);
+}
+
+static void smp2p_ut_remote_spinlock_wcnss(struct seq_file *s)
+{
+ smp2p_ut_remote_spinlock_pid(s, SMP2P_WIRELESS_PROC, false);
+}
+
+/**
+ * smp2p_ut_remote_spinlock_rpm - Verify remote spinlock.
+ *
+ * @s: pointer to output file
+ * @remote_pid: Remote processor to test
+ */
+static void smp2p_ut_remote_spinlock_rpm(struct seq_file *s)
+{
+ int failed = 0;
+ unsigned long flags;
+ unsigned n;
+ unsigned test_num;
+ struct rpm_spinlock_test *data_ptr;
+ remote_spinlock_t *smem_spinlock;
+ bool have_lock;
+
+ seq_printf(s, "Running %s for Apps<->RPM Test\n",
+ __func__);
+ do {
+ smem_spinlock = smem_get_remote_spinlock();
+ UT_ASSERT_PTR(smem_spinlock, !=, NULL);
+
+ data_ptr = smem_alloc2(SMEM_ID_VENDOR0,
+ sizeof(struct rpm_spinlock_test));
+ UT_ASSERT_PTR(0, !=, data_ptr);
+
+ /* Send start */
+ writel_relaxed(0, &data_ptr->apps_lock_count);
+ writel_relaxed(RPM_CMD_START, &data_ptr->apps_cmd);
+
+ seq_printf(s, "\tWaiting for RPM to start test\n");
+ for (n = 0; n < 1000; ++n) {
+ if (readl_relaxed(&data_ptr->rpm_cmd) !=
+ RPM_CMD_INVALID)
+ break;
+ usleep(1000);
+ }
+ if (readl_relaxed(&data_ptr->rpm_cmd) == RPM_CMD_INVALID) {
+ /* timeout waiting for RPM */
+ writel_relaxed(RPM_CMD_INVALID, &data_ptr->apps_cmd);
+ UT_ASSERT_INT(RPM_CMD_LOCKED, !=, RPM_CMD_INVALID);
+ }
+
+ /* Run spinlock test */
+ flags = 0;
+ have_lock = false;
+ for (test_num = 0; !failed && test_num < 10000; ++test_num) {
+ /* acquire spinlock */
+ remote_spin_lock_irqsave(smem_spinlock, flags);
+ have_lock = true;
+ writel_relaxed(++data_ptr->apps_lock_count,
+ &data_ptr->apps_lock_count);
+ writel_relaxed(RPM_CMD_LOCKED, &data_ptr->apps_cmd);
+ /*
+ * Ensure that the remote side sees our lock has
+ * been acquired before we start polling their status.
+ */
+ wmb();
+
+ /* verify the other side doesn't say it has the lock */
+ for (n = 0; n < 1000; ++n) {
+ UT_ASSERT_HEX(RPM_CMD_UNLOCKED, ==,
+ readl_relaxed(&data_ptr->rpm_cmd));
+ }
+ if (failed)
+ break;
+
+ /* release spinlock */
+ have_lock = false;
+ writel_relaxed(RPM_CMD_UNLOCKED, &data_ptr->apps_cmd);
+ /*
+ * Ensure that our status-update write was committed
+ * before we unlock the spinlock.
+ */
+ wmb();
+ remote_spin_unlock_irqrestore(smem_spinlock, flags);
+ }
+ if (have_lock)
+ remote_spin_unlock_irqrestore(smem_spinlock, flags);
+
+ /* End test */
+ writel_relaxed(RPM_CMD_INVALID, &data_ptr->apps_cmd);
+ seq_printf(s,
+ "\tLocked spinlock local %u remote %u\n",
+ readl_relaxed(&data_ptr->apps_lock_count),
+ readl_relaxed(&data_ptr->rpm_lock_count));
+
+ if (!failed)
+ seq_printf(s, "\tOK\n");
+ } while (0);
+
+ if (failed) {
+ pr_err("%s: Failed\n", __func__);
+ seq_printf(s, "\tFailed\n");
+ }
+}
+
+static int __init smp2p_debugfs_init(void)
+{
+ /*
+ * Add Unit Test entries.
+ *
+ * The idea with unit tests is that you can run all of them
+ * from ADB shell by doing:
+ * adb shell
+ * cat ut*
+ *
+ * And if particular tests fail, you can then repeatedly run the
+ * failing tests as you debug and resolve the failing test.
+ */
+ smp2p_debug_create("ut_remote_spinlock",
+ smp2p_ut_remote_spinlock);
+ smp2p_debug_create("ut_remote_spin_trylock",
+ smp2p_ut_remote_spin_trylock);
+ smp2p_debug_create("ut_remote_spinlock_modem",
+ smp2p_ut_remote_spinlock_modem);
+ smp2p_debug_create("ut_remote_spinlock_adsp",
+ smp2p_ut_remote_spinlock_adsp);
+ smp2p_debug_create("ut_remote_spinlock_wcnss",
+ smp2p_ut_remote_spinlock_wcnss);
+ smp2p_debug_create("ut_remote_spinlock_rpm",
+ smp2p_ut_remote_spinlock_rpm);
+
+ return 0;
+}
+module_init(smp2p_debugfs_init);
diff --git a/arch/arm/mach-msm/smp2p_test.c b/arch/arm/mach-msm/smp2p_test.c
new file mode 100644
index 0000000..10f7575
--- /dev/null
+++ b/arch/arm/mach-msm/smp2p_test.c
@@ -0,0 +1,913 @@
+/* arch/arm/mach-msm/smp2p_test.c
+ *
+ * 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/debugfs.h>
+#include <linux/ctype.h>
+#include <linux/jiffies.h>
+#include <linux/delay.h>
+#include <linux/completion.h>
+#include "smp2p_private.h"
+#include "smp2p_test_common.h"
+
+/**
+ * smp2p_ut_local_basic - Basic sanity test using local loopback.
+ *
+ * @s: pointer to output file
+ *
+ * This test simulates a simple write and read
+ * when remote processor does not exist.
+ */
+static void smp2p_ut_local_basic(struct seq_file *s)
+{
+ int failed = 0;
+ struct msm_smp2p_out *smp2p_obj;
+ struct msm_smp2p_remote_mock *rmp = NULL;
+ int ret;
+ uint32_t test_request;
+ uint32_t test_response = 0;
+ static struct mock_cb_data cb_data;
+
+ seq_printf(s, "Running %s\n", __func__);
+ mock_cb_data_init(&cb_data);
+ do {
+ /* initialize mock edge and start opening */
+ ret = smp2p_reset_mock_edge();
+ UT_ASSERT_INT(ret, ==, 0);
+
+ rmp = msm_smp2p_get_remote_mock();
+ UT_ASSERT_PTR(rmp, !=, NULL);
+
+ rmp->rx_interrupt_count = 0;
+ memset(&rmp->remote_item, 0,
+ sizeof(struct smp2p_smem_item));
+
+ msm_smp2p_set_remote_mock_exists(false);
+
+ ret = msm_smp2p_out_open(SMP2P_REMOTE_MOCK_PROC, "smp2p",
+ &cb_data.nb, &smp2p_obj);
+ UT_ASSERT_INT(ret, ==, 0);
+
+ UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 1);
+ UT_ASSERT_INT(cb_data.cb_count, ==, 0);
+ rmp->rx_interrupt_count = 0;
+
+ /* simulate response from remote side */
+ rmp->remote_item.header.magic = SMP2P_MAGIC;
+ SMP2P_SET_LOCAL_PID(
+ rmp->remote_item.header.rem_loc_proc_id,
+ SMP2P_REMOTE_MOCK_PROC);
+ SMP2P_SET_REMOTE_PID(
+ rmp->remote_item.header.rem_loc_proc_id,
+ SMP2P_APPS_PROC);
+ SMP2P_SET_VERSION(
+ rmp->remote_item.header.feature_version, 1);
+ SMP2P_SET_FEATURES(
+ rmp->remote_item.header.feature_version, 0);
+ SMP2P_SET_ENT_TOTAL(
+ rmp->remote_item.header.valid_total_ent, SMP2P_MAX_ENTRY);
+ SMP2P_SET_ENT_VALID(
+ rmp->remote_item.header.valid_total_ent, 0);
+ rmp->remote_item.header.reserved = 0x0;
+ msm_smp2p_set_remote_mock_exists(true);
+ rmp->tx_interrupt();
+
+ /* verify port was opened */
+ UT_ASSERT_INT(
+ (int)wait_for_completion_timeout(
+ &cb_data.cb_completion, HZ / 2), >, 0);
+ UT_ASSERT_INT(cb_data.cb_count, ==, 1);
+ UT_ASSERT_INT(cb_data.event_open, ==, 1);
+ UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 2);
+
+ /* do write (test outbound entries) */
+ rmp->rx_interrupt_count = 0;
+ test_request = 0xC0DE;
+ ret = msm_smp2p_out_write(smp2p_obj, test_request);
+ UT_ASSERT_INT(ret, ==, 0);
+ UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 1);
+
+ /* do read (test inbound entries) */
+ ret = msm_smp2p_out_read(smp2p_obj, &test_response);
+ UT_ASSERT_INT(ret, ==, 0);
+ UT_ASSERT_INT(test_request, ==, test_response);
+
+ ret = msm_smp2p_out_close(&smp2p_obj);
+ UT_ASSERT_INT(ret, ==, 0);
+ UT_ASSERT_PTR(smp2p_obj, ==, 0);
+
+ seq_printf(s, "\tOK\n");
+ } while (0);
+
+ if (failed) {
+ pr_err("%s: Failed\n", __func__);
+ seq_printf(s, "\tFailed\n");
+ (void)msm_smp2p_out_close(&smp2p_obj);
+ }
+}
+
+/**
+ * smp2p_ut_local_late_open - Verify post-negotiation opening.
+ *
+ * @s: pointer to output file
+ *
+ * Verify entry creation for opening entries after negotiation is complete.
+ */
+static void smp2p_ut_local_late_open(struct seq_file *s)
+{
+ int failed = 0;
+ struct msm_smp2p_out *smp2p_obj;
+ struct msm_smp2p_remote_mock *rmp = NULL;
+ int ret;
+ uint32_t test_request;
+ uint32_t test_response = 0;
+ static struct mock_cb_data cb_data;
+
+ seq_printf(s, "Running %s\n", __func__);
+ mock_cb_data_init(&cb_data);
+ do {
+ /* initialize mock edge */
+ ret = smp2p_reset_mock_edge();
+ UT_ASSERT_INT(ret, ==, 0);
+
+ rmp = msm_smp2p_get_remote_mock();
+ UT_ASSERT_PTR(rmp, !=, NULL);
+
+ rmp->rx_interrupt_count = 0;
+ memset(&rmp->remote_item, 0,
+ sizeof(struct smp2p_smem_item));
+ rmp->remote_item.header.magic = SMP2P_MAGIC;
+ SMP2P_SET_LOCAL_PID(
+ rmp->remote_item.header.rem_loc_proc_id,
+ SMP2P_REMOTE_MOCK_PROC);
+ SMP2P_SET_REMOTE_PID(
+ rmp->remote_item.header.rem_loc_proc_id,
+ SMP2P_APPS_PROC);
+ SMP2P_SET_VERSION(
+ rmp->remote_item.header.feature_version, 1);
+ SMP2P_SET_FEATURES(
+ rmp->remote_item.header.feature_version, 0);
+ SMP2P_SET_ENT_TOTAL(
+ rmp->remote_item.header.valid_total_ent,
+ SMP2P_MAX_ENTRY);
+ SMP2P_SET_ENT_VALID(
+ rmp->remote_item.header.valid_total_ent, 0);
+ rmp->remote_item.header.reserved = 0x0;
+
+ msm_smp2p_set_remote_mock_exists(true);
+
+ ret = msm_smp2p_out_open(SMP2P_REMOTE_MOCK_PROC, "smp2p",
+ &cb_data.nb, &smp2p_obj);
+ UT_ASSERT_INT(ret, ==, 0);
+
+ /* verify port was opened */
+ UT_ASSERT_INT(
+ (int)wait_for_completion_timeout(
+ &cb_data.cb_completion, HZ / 2),
+ >, 0);
+ UT_ASSERT_INT(cb_data.cb_count, ==, 1);
+ UT_ASSERT_INT(cb_data.event_open, ==, 1);
+ UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 2);
+
+ /* do write (test outbound entries) */
+ rmp->rx_interrupt_count = 0;
+ test_request = 0xC0DE;
+ ret = msm_smp2p_out_write(smp2p_obj, test_request);
+ UT_ASSERT_INT(ret, ==, 0);
+ UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 1);
+
+ /* do read (test inbound entries) */
+ ret = msm_smp2p_out_read(smp2p_obj, &test_response);
+ UT_ASSERT_INT(ret, ==, 0);
+ UT_ASSERT_INT(test_request, ==, test_response);
+
+ ret = msm_smp2p_out_close(&smp2p_obj);
+ UT_ASSERT_INT(ret, ==, 0);
+ UT_ASSERT_PTR(smp2p_obj, ==, 0);
+
+ seq_printf(s, "\tOK\n");
+ } while (0);
+
+ if (failed) {
+ pr_err("%s: Failed\n", __func__);
+ seq_printf(s, "\tFailed\n");
+ (void)msm_smp2p_out_close(&smp2p_obj);
+ }
+}
+
+/**
+ * smp2p_ut_local_early_open - Verify pre-negotiation opening.
+ *
+ * @s: pointer to output file
+ *
+ * Verify entry creation for opening entries before negotiation is complete.
+ */
+static void smp2p_ut_local_early_open(struct seq_file *s)
+{
+ int failed = 0;
+ struct msm_smp2p_out *smp2p_obj;
+ struct msm_smp2p_remote_mock *rmp = NULL;
+ struct smp2p_smem *outbound_item;
+ int negotiation_state;
+ int ret;
+ uint32_t test_request;
+ uint32_t test_response = 0;
+ static struct mock_cb_data cb_data;
+
+ seq_printf(s, "Running %s\n", __func__);
+ mock_cb_data_init(&cb_data);
+ do {
+ /* initialize mock edge, but don't enable, yet */
+ ret = smp2p_reset_mock_edge();
+ UT_ASSERT_INT(ret, ==, 0);
+
+ rmp = msm_smp2p_get_remote_mock();
+ UT_ASSERT_PTR(rmp, !=, NULL);
+
+ rmp->rx_interrupt_count = 0;
+ memset(&rmp->remote_item, 0,
+ sizeof(struct smp2p_smem_item));
+ rmp->remote_item.header.magic = SMP2P_MAGIC;
+ SMP2P_SET_LOCAL_PID(
+ rmp->remote_item.header.rem_loc_proc_id,
+ SMP2P_REMOTE_MOCK_PROC);
+ SMP2P_SET_REMOTE_PID(
+ rmp->remote_item.header.rem_loc_proc_id,
+ SMP2P_APPS_PROC);
+ SMP2P_SET_VERSION(
+ rmp->remote_item.header.feature_version, 1);
+ SMP2P_SET_FEATURES(
+ rmp->remote_item.header.feature_version, 0);
+ SMP2P_SET_ENT_TOTAL(
+ rmp->remote_item.header.valid_total_ent, SMP2P_MAX_ENTRY);
+ SMP2P_SET_ENT_VALID(
+ rmp->remote_item.header.valid_total_ent, 0);
+ rmp->remote_item.header.reserved = 0x0;
+
+ msm_smp2p_set_remote_mock_exists(false);
+ UT_ASSERT_PTR(NULL, ==,
+ smp2p_get_in_item(SMP2P_REMOTE_MOCK_PROC));
+
+ /* initiate open, but verify it doesn't complete */
+ ret = msm_smp2p_out_open(SMP2P_REMOTE_MOCK_PROC, "smp2p",
+ &cb_data.nb, &smp2p_obj);
+ UT_ASSERT_INT(ret, ==, 0);
+
+ UT_ASSERT_INT(
+ (int)wait_for_completion_timeout(
+ &cb_data.cb_completion, HZ / 8),
+ ==, 0);
+ UT_ASSERT_INT(cb_data.cb_count, ==, 0);
+ UT_ASSERT_INT(cb_data.event_open, ==, 0);
+ UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 1);
+
+ outbound_item = smp2p_get_out_item(SMP2P_REMOTE_MOCK_PROC,
+ &negotiation_state);
+ UT_ASSERT_PTR(outbound_item, !=, NULL);
+ UT_ASSERT_INT(negotiation_state, ==, SMP2P_EDGE_STATE_OPENING);
+ UT_ASSERT_INT(0, ==,
+ SMP2P_GET_ENT_VALID(outbound_item->valid_total_ent));
+
+ /* verify that read/write don't work yet */
+ rmp->rx_interrupt_count = 0;
+ test_request = 0x0;
+ ret = msm_smp2p_out_write(smp2p_obj, test_request);
+ UT_ASSERT_INT(ret, ==, -ENODEV);
+ UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 0);
+
+ ret = msm_smp2p_out_read(smp2p_obj, &test_response);
+ UT_ASSERT_INT(ret, ==, -ENODEV);
+
+ /* allocate remote entry and verify open */
+ msm_smp2p_set_remote_mock_exists(true);
+ rmp->tx_interrupt();
+
+ UT_ASSERT_INT(
+ (int)wait_for_completion_timeout(
+ &cb_data.cb_completion, HZ / 2),
+ >, 0);
+ UT_ASSERT_INT(cb_data.cb_count, ==, 1);
+ UT_ASSERT_INT(cb_data.event_open, ==, 1);
+ UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 2);
+
+ /* do write (test outbound entries) */
+ rmp->rx_interrupt_count = 0;
+ test_request = 0xC0DE;
+ ret = msm_smp2p_out_write(smp2p_obj, test_request);
+ UT_ASSERT_INT(ret, ==, 0);
+ UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 1);
+
+ /* do read (test inbound entries) */
+ ret = msm_smp2p_out_read(smp2p_obj, &test_response);
+ UT_ASSERT_INT(ret, ==, 0);
+ UT_ASSERT_INT(test_request, ==, test_response);
+
+ ret = msm_smp2p_out_close(&smp2p_obj);
+ UT_ASSERT_INT(ret, ==, 0);
+ UT_ASSERT_PTR(smp2p_obj, ==, 0);
+
+ seq_printf(s, "\tOK\n");
+ } while (0);
+
+ if (failed) {
+ pr_err("%s: Failed\n", __func__);
+ seq_printf(s, "\tFailed\n");
+ (void)msm_smp2p_out_close(&smp2p_obj);
+ }
+}
+
+/**
+ * smp2p_ut_mock_loopback - Exercise the remote loopback using remote mock.
+ *
+ * @s: pointer to output file
+ *
+ * This test exercises the remote loopback code using
+ * remote mock object. The remote mock object simulates the remote
+ * processor sending remote loopback commands to the local processor.
+ */
+static void smp2p_ut_mock_loopback(struct seq_file *s)
+{
+ int failed = 0;
+ struct msm_smp2p_remote_mock *rmp = NULL;
+ int ret;
+ uint32_t test_request = 0;
+ uint32_t test_response = 0;
+ struct msm_smp2p_out *local;
+
+ seq_printf(s, "Running %s\n", __func__);
+ do {
+ /* Initialize the mock edge */
+ ret = smp2p_reset_mock_edge();
+ UT_ASSERT_INT(ret, ==, 0);
+
+ rmp = msm_smp2p_get_remote_mock();
+ UT_ASSERT_PTR(rmp, !=, NULL);
+
+ memset(&rmp->remote_item, 0,
+ sizeof(struct smp2p_smem_item));
+ rmp->remote_item.header.magic = SMP2P_MAGIC;
+ SMP2P_SET_LOCAL_PID(
+ rmp->remote_item.header.rem_loc_proc_id,
+ SMP2P_REMOTE_MOCK_PROC);
+ SMP2P_SET_REMOTE_PID(
+ rmp->remote_item.header.rem_loc_proc_id,
+ SMP2P_APPS_PROC);
+ SMP2P_SET_VERSION(
+ rmp->remote_item.header.feature_version, 1);
+ SMP2P_SET_FEATURES(
+ rmp->remote_item.header.feature_version, 0);
+ SMP2P_SET_ENT_TOTAL(
+ rmp->remote_item.header.valid_total_ent, SMP2P_MAX_ENTRY);
+ SMP2P_SET_ENT_VALID(
+ rmp->remote_item.header.valid_total_ent, 1);
+ rmp->remote_item.header.reserved = 0x0;
+ msm_smp2p_set_remote_mock_exists(true);
+
+ /* Create test entry and attach loopback server */
+ rmp->rx_interrupt_count = 0;
+ INIT_COMPLETION(rmp->cb_completion);
+ strlcpy(rmp->remote_item.entries[0].name, "smp2p",
+ SMP2P_MAX_ENTRY_NAME);
+ rmp->remote_item.entries[0].entry = 0;
+ rmp->tx_interrupt();
+
+ local = msm_smp2p_init_rmt_lpb_proc(SMP2P_REMOTE_MOCK_PROC);
+ UT_ASSERT_INT(
+ (int)wait_for_completion_timeout(
+ &rmp->cb_completion, HZ / 2),
+ >, 0);
+ UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 2);
+
+ /* Send Echo Command */
+ rmp->rx_interrupt_count = 0;
+ INIT_COMPLETION(rmp->cb_completion);
+ SMP2P_SET_RMT_CMD_TYPE(test_request, 1);
+ SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_ECHO);
+ SMP2P_SET_RMT_DATA(test_request, 10);
+ rmp->remote_item.entries[0].entry = test_request;
+ rmp->tx_interrupt();
+ UT_ASSERT_INT(
+ (int)wait_for_completion_timeout(
+ &rmp->cb_completion, HZ / 2),
+ >, 0);
+
+ /* Verify Echo Response */
+ UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 1);
+ ret = msm_smp2p_out_read(local,
+ &test_response);
+ UT_ASSERT_INT(ret, ==, 0);
+ test_response = SMP2P_GET_RMT_DATA(test_response);
+ UT_ASSERT_INT(test_response, ==, 10);
+
+ /* Send PINGPONG command */
+ test_request = 0;
+ test_response = 0;
+ rmp->rx_interrupt_count = 0;
+ INIT_COMPLETION(rmp->cb_completion);
+ SMP2P_SET_RMT_CMD_TYPE(test_request, 1);
+ SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_PINGPONG);
+ SMP2P_SET_RMT_DATA(test_request, 10);
+ rmp->remote_item.entries[0].entry = test_request;
+ rmp->tx_interrupt();
+ UT_ASSERT_INT(
+ (int)wait_for_completion_timeout(
+ &rmp->cb_completion, HZ / 2),
+ >, 0);
+
+ /* Verify PINGPONG Response */
+ UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 1);
+ ret = msm_smp2p_out_read(local, &test_response);
+ UT_ASSERT_INT(ret, ==, 0);
+ test_response = SMP2P_GET_RMT_DATA(test_response);
+ UT_ASSERT_INT(test_response, ==, 9);
+
+ /* Send CLEARALL command */
+ test_request = 0;
+ test_response = 0;
+ rmp->rx_interrupt_count = 0;
+ INIT_COMPLETION(rmp->cb_completion);
+ SMP2P_SET_RMT_CMD_TYPE(test_request, 1);
+ SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_CLEARALL);
+ SMP2P_SET_RMT_DATA(test_request, 10);
+ rmp->remote_item.entries[0].entry = test_request;
+ rmp->tx_interrupt();
+ UT_ASSERT_INT(
+ (int)wait_for_completion_timeout(
+ &rmp->cb_completion, HZ / 2),
+ >, 0);
+
+ /* Verify CLEARALL response */
+ UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 1);
+ ret = msm_smp2p_out_read(local, &test_response);
+ UT_ASSERT_INT(ret, ==, 0);
+ test_response = SMP2P_GET_RMT_DATA(test_response);
+ UT_ASSERT_INT(test_response, ==, 0);
+
+ ret = msm_smp2p_deinit_rmt_lpb_proc(SMP2P_REMOTE_MOCK_PROC);
+ UT_ASSERT_INT(ret, ==, 0);
+ seq_printf(s, "\tOK\n");
+ } while (0);
+
+ if (failed) {
+ pr_err("%s: Failed\n", __func__);
+ seq_printf(s, "\tFailed\n");
+ msm_smp2p_deinit_rmt_lpb_proc(SMP2P_REMOTE_MOCK_PROC);
+ }
+}
+
+/**
+ * smp2p_ut_remote_inout_core - Verify inbound/outbound functionality.
+ *
+ * @s: pointer to output file
+ * @remote_pid: Remote processor to test
+ *
+ * This test verifies inbound/outbound functionality for the remote processor.
+ */
+static void smp2p_ut_remote_inout_core(struct seq_file *s, int remote_pid)
+{
+ int failed = 0;
+ struct msm_smp2p_out *handle;
+ int ret;
+ uint32_t test_request;
+ uint32_t test_response = 0;
+ static struct mock_cb_data cb_out;
+ static struct mock_cb_data cb_in;
+
+ seq_printf(s, "Running %s for '%s' remote pid %d\n",
+ __func__, smp2p_pid_to_name(remote_pid), remote_pid);
+ mock_cb_data_init(&cb_out);
+ mock_cb_data_init(&cb_in);
+ do {
+ /* Open output entry */
+ ret = msm_smp2p_out_open(remote_pid, "smp2p",
+ &cb_out.nb, &handle);
+ UT_ASSERT_INT(ret, ==, 0);
+ UT_ASSERT_INT(
+ (int)wait_for_completion_timeout(
+ &cb_out.cb_completion, HZ / 2),
+ >, 0);
+ UT_ASSERT_INT(cb_out.cb_count, ==, 1);
+ UT_ASSERT_INT(cb_out.event_open, ==, 1);
+
+ /* Open inbound entry */
+ ret = msm_smp2p_in_register(remote_pid, "smp2p",
+ &cb_in.nb);
+ UT_ASSERT_INT(ret, ==, 0);
+ UT_ASSERT_INT(
+ (int)wait_for_completion_timeout(
+ &cb_in.cb_completion, HZ / 2),
+ >, 0);
+ UT_ASSERT_INT(cb_in.cb_count, ==, 1);
+ UT_ASSERT_INT(cb_in.event_open, ==, 1);
+
+ /* Write an echo request */
+ mock_cb_data_reset(&cb_out);
+ mock_cb_data_reset(&cb_in);
+ test_request = 0x0;
+ SMP2P_SET_RMT_CMD_TYPE(test_request, 1);
+ SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_ECHO);
+ SMP2P_SET_RMT_DATA(test_request, 0xAA55);
+ ret = msm_smp2p_out_write(handle, test_request);
+ UT_ASSERT_INT(ret, ==, 0);
+
+ /* Verify inbound reply */
+ UT_ASSERT_INT(
+ (int)wait_for_completion_timeout(
+ &cb_in.cb_completion, HZ / 2),
+ >, 0);
+ UT_ASSERT_INT(cb_in.cb_count, ==, 1);
+ UT_ASSERT_INT(cb_in.event_entry_update, ==, 1);
+ UT_ASSERT_INT(SMP2P_GET_RMT_DATA(
+ cb_in.entry_data.current_value), ==, 0xAA55);
+
+ ret = msm_smp2p_in_read(remote_pid, "smp2p", &test_response);
+ UT_ASSERT_INT(ret, ==, 0);
+ UT_ASSERT_INT(0, ==, SMP2P_GET_RMT_CMD_TYPE(test_response));
+ UT_ASSERT_INT(SMP2P_LB_CMD_ECHO, ==,
+ SMP2P_GET_RMT_CMD(test_response));
+ UT_ASSERT_INT(0xAA55, ==, SMP2P_GET_RMT_DATA(test_response));
+
+ /* Write a clear all request */
+ mock_cb_data_reset(&cb_in);
+ test_request = 0x0;
+ SMP2P_SET_RMT_CMD_TYPE(test_request, 1);
+ SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_CLEARALL);
+ SMP2P_SET_RMT_DATA(test_request, 0xAA55);
+ ret = msm_smp2p_out_write(handle, test_request);
+ UT_ASSERT_INT(ret, ==, 0);
+
+ /* Verify inbound reply */
+ UT_ASSERT_INT(
+ (int)wait_for_completion_timeout(
+ &cb_in.cb_completion, HZ / 2),
+ >, 0);
+ UT_ASSERT_INT(cb_in.cb_count, ==, 1);
+ UT_ASSERT_INT(cb_in.event_entry_update, ==, 1);
+ UT_ASSERT_INT(SMP2P_GET_RMT_DATA(
+ cb_in.entry_data.current_value), ==, 0x0000);
+
+ ret = msm_smp2p_in_read(remote_pid, "smp2p", &test_response);
+ UT_ASSERT_INT(ret, ==, 0);
+ UT_ASSERT_INT(0, ==, SMP2P_GET_RMT_CMD_TYPE(test_response));
+ UT_ASSERT_INT(0x0000, ==, SMP2P_GET_RMT_DATA(test_response));
+
+ /* Write a decrement request */
+ mock_cb_data_reset(&cb_in);
+ test_request = 0x0;
+ SMP2P_SET_RMT_CMD_TYPE(test_request, 1);
+ SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_PINGPONG);
+ SMP2P_SET_RMT_DATA(test_request, 0xAA55);
+ ret = msm_smp2p_out_write(handle, test_request);
+ UT_ASSERT_INT(ret, ==, 0);
+
+ /* Verify inbound reply */
+ UT_ASSERT_INT(
+ (int)wait_for_completion_timeout(
+ &cb_in.cb_completion, HZ / 2),
+ >, 0);
+ UT_ASSERT_INT(cb_in.cb_count, ==, 1);
+ UT_ASSERT_INT(cb_in.event_entry_update, ==, 1);
+ UT_ASSERT_INT(SMP2P_GET_RMT_DATA(
+ cb_in.entry_data.current_value), ==, 0xAA54);
+
+ ret = msm_smp2p_in_read(remote_pid, "smp2p", &test_response);
+ UT_ASSERT_INT(ret, ==, 0);
+ UT_ASSERT_INT(0, ==, SMP2P_GET_RMT_CMD_TYPE(test_response));
+ UT_ASSERT_INT(SMP2P_LB_CMD_PINGPONG, ==,
+ SMP2P_GET_RMT_CMD(test_response));
+ UT_ASSERT_INT(0xAA54, ==, SMP2P_GET_RMT_DATA(test_response));
+
+ /* Test the ignore flag */
+ mock_cb_data_reset(&cb_in);
+ test_request = 0x0;
+ SMP2P_SET_RMT_CMD_TYPE(test_request, 1);
+ SMP2P_SET_RMT_CMD(test_request, SMP2P_RLPB_IGNORE);
+ SMP2P_SET_RMT_DATA(test_request, 0xAA55);
+ ret = msm_smp2p_out_write(handle, test_request);
+ UT_ASSERT_INT(ret, ==, 0);
+
+ UT_ASSERT_INT(
+ (int)wait_for_completion_timeout(
+ &cb_in.cb_completion, HZ / 2),
+ ==, 0);
+ UT_ASSERT_INT(cb_in.cb_count, ==, 0);
+ UT_ASSERT_INT(cb_in.event_entry_update, ==, 0);
+ ret = msm_smp2p_in_read(remote_pid, "smp2p", &test_response);
+ UT_ASSERT_INT(ret, ==, 0);
+ UT_ASSERT_INT(0xAA54, ==, SMP2P_GET_RMT_DATA(test_response));
+
+ /* Cleanup */
+ ret = msm_smp2p_out_close(&handle);
+ UT_ASSERT_INT(ret, ==, 0);
+ UT_ASSERT_PTR(handle, ==, 0);
+ ret = msm_smp2p_in_unregister(remote_pid, "smp2p", &cb_in.nb);
+ UT_ASSERT_INT(ret, ==, 0);
+
+ seq_printf(s, "\tOK\n");
+ } while (0);
+
+ if (failed) {
+ if (handle)
+ (void)msm_smp2p_out_close(&handle);
+ (void)msm_smp2p_in_unregister(remote_pid, "smp2p", &cb_in.nb);
+
+ pr_err("%s: Failed\n", __func__);
+ seq_printf(s, "\tFailed\n");
+ }
+}
+
+/**
+ * smp2p_ut_remote_inout - Verify inbound/outbound functionality for all.
+ *
+ * @s: pointer to output file
+ *
+ * This test verifies inbound and outbound functionality for all
+ * configured remote processor.
+ */
+static void smp2p_ut_remote_inout(struct seq_file *s)
+{
+ struct smp2p_interrupt_config *int_cfg;
+ int pid;
+
+ int_cfg = smp2p_get_interrupt_config();
+ if (!int_cfg) {
+ seq_printf(s,
+ "Remote processor config unavailable\n");
+ return;
+ }
+
+ for (pid = 0; pid < SMP2P_NUM_PROCS; ++pid) {
+ if (!int_cfg[pid].is_configured)
+ continue;
+
+ msm_smp2p_deinit_rmt_lpb_proc(pid);
+ smp2p_ut_remote_inout_core(s, pid);
+ msm_smp2p_init_rmt_lpb_proc(pid);
+ }
+}
+
+/**
+ * smp2p_ut_remote_out_max_entries_core - Verify open functionality.
+ *
+ * @s: pointer to output file
+ * @remote_pid: Remote processor for which the test is executed.
+ *
+ * This test verifies open functionality by creating maximum outbound entries.
+ */
+static void smp2p_ut_remote_out_max_entries_core(struct seq_file *s,
+ int remote_pid)
+{
+ int j = 0;
+ int failed = 0;
+ struct msm_smp2p_out *handle[SMP2P_MAX_ENTRY];
+ int ret;
+ static struct mock_cb_data cb_out[SMP2P_MAX_ENTRY];
+ char entry_name[SMP2P_MAX_ENTRY_NAME];
+ int num_created;
+
+ seq_printf(s, "Running %s for '%s' remote pid %d\n",
+ __func__, smp2p_pid_to_name(remote_pid), remote_pid);
+
+ for (j = 0; j < SMP2P_MAX_ENTRY; j++) {
+ handle[j] = NULL;
+ mock_cb_data_init(&cb_out[j]);
+ }
+
+ do {
+ num_created = 0;
+ for (j = 0; j < SMP2P_MAX_ENTRY; j++) {
+ /* Open as many output entries as possible */
+ scnprintf((char *)entry_name, SMP2P_MAX_ENTRY_NAME,
+ "smp2p%d", j);
+ ret = msm_smp2p_out_open(remote_pid, entry_name,
+ &cb_out[j].nb, &handle[j]);
+ if (ret == -ENOMEM)
+ /* hit max number */
+ break;
+ UT_ASSERT_INT(ret, ==, 0);
+ ++num_created;
+ }
+ if (failed)
+ break;
+
+ /* verify we created more than 1 entry */
+ UT_ASSERT_INT(num_created, <=, SMP2P_MAX_ENTRY);
+ UT_ASSERT_INT(num_created, >, 0);
+
+ seq_printf(s, "\tOK\n");
+ } while (0);
+
+ if (failed) {
+ pr_err("%s: Failed\n", __func__);
+ seq_printf(s, "\tFailed\n");
+ }
+
+ /* cleanup */
+ for (j = 0; j < SMP2P_MAX_ENTRY; j++)
+ ret = msm_smp2p_out_close(&handle[j]);
+}
+
+/**
+ * smp2p_ut_remote_out_max_entries - Verify open for all configured processors.
+ *
+ * @s: pointer to output file
+ *
+ * This test verifies creating max number of entries for
+ * all configured remote processor.
+ */
+static void smp2p_ut_remote_out_max_entries(struct seq_file *s)
+{
+ struct smp2p_interrupt_config *int_cfg;
+ int pid;
+
+ int_cfg = smp2p_get_interrupt_config();
+ if (!int_cfg) {
+ seq_printf(s,
+ "Remote processor config unavailable\n");
+ return;
+ }
+
+ for (pid = 0; pid < SMP2P_NUM_PROCS; ++pid) {
+ if (!int_cfg[pid].is_configured)
+ continue;
+
+ smp2p_ut_remote_out_max_entries_core(s, pid);
+ }
+}
+
+/**
+ * smp2p_ut_local_in_max_entries - Verify registering and unregistering.
+ *
+ * @s: pointer to output file
+ *
+ * This test verifies registering and unregistering for inbound entries using
+ * the remote mock processor.
+ */
+static void smp2p_ut_local_in_max_entries(struct seq_file *s)
+{
+ int j = 0;
+ int failed = 0;
+ struct msm_smp2p_remote_mock *rmp = NULL;
+ int ret;
+ static struct mock_cb_data cb_in[SMP2P_MAX_ENTRY];
+ static struct mock_cb_data cb_out;
+
+ seq_printf(s, "Running %s\n", __func__);
+
+ for (j = 0; j < SMP2P_MAX_ENTRY; j++)
+ mock_cb_data_init(&cb_in[j]);
+
+ mock_cb_data_init(&cb_out);
+
+ do {
+ /* Initialize mock edge */
+ ret = smp2p_reset_mock_edge();
+ UT_ASSERT_INT(ret, ==, 0);
+
+ rmp = msm_smp2p_get_remote_mock();
+ UT_ASSERT_PTR(rmp, !=, NULL);
+
+ rmp->rx_interrupt_count = 0;
+ memset(&rmp->remote_item, 0,
+ sizeof(struct smp2p_smem_item));
+ rmp->remote_item.header.magic = SMP2P_MAGIC;
+ SMP2P_SET_LOCAL_PID(
+ rmp->remote_item.header.rem_loc_proc_id,
+ SMP2P_REMOTE_MOCK_PROC);
+ SMP2P_SET_REMOTE_PID(
+ rmp->remote_item.header.rem_loc_proc_id,
+ SMP2P_APPS_PROC);
+ SMP2P_SET_VERSION(
+ rmp->remote_item.header.feature_version, 1);
+ SMP2P_SET_FEATURES(
+ rmp->remote_item.header.feature_version, 0);
+ SMP2P_SET_ENT_TOTAL(
+ rmp->remote_item.header.valid_total_ent, SMP2P_MAX_ENTRY);
+ SMP2P_SET_ENT_VALID(
+ rmp->remote_item.header.valid_total_ent, 0);
+ rmp->remote_item.header.reserved = 0x0;
+ msm_smp2p_set_remote_mock_exists(true);
+
+ /* Create Max Entries in the remote mock object */
+ for (j = 0; j < SMP2P_MAX_ENTRY; j++) {
+ scnprintf(rmp->remote_item.entries[j].name,
+ SMP2P_MAX_ENTRY_NAME, "smp2p%d", j);
+ rmp->remote_item.entries[j].entry = 0;
+ rmp->tx_interrupt();
+ }
+
+ /* Register for in entries */
+ for (j = 0; j < SMP2P_MAX_ENTRY; j++) {
+ ret = msm_smp2p_in_register(SMP2P_REMOTE_MOCK_PROC,
+ rmp->remote_item.entries[j].name,
+ &(cb_in[j].nb));
+ UT_ASSERT_INT(ret, ==, 0);
+ UT_ASSERT_INT(
+ (int)wait_for_completion_timeout(
+ &(cb_in[j].cb_completion), HZ / 2),
+ >, 0);
+ UT_ASSERT_INT(cb_in[j].cb_count, ==, 1);
+ UT_ASSERT_INT(cb_in[j].event_entry_update, ==, 0);
+ }
+ UT_ASSERT_INT(j, ==, SMP2P_MAX_ENTRY);
+
+ /* Unregister */
+ for (j = 0; j < SMP2P_MAX_ENTRY; j++) {
+ ret = msm_smp2p_in_unregister(SMP2P_REMOTE_MOCK_PROC,
+ rmp->remote_item.entries[j].name,
+ &(cb_in[j].nb));
+ UT_ASSERT_INT(ret, ==, 0);
+ }
+ UT_ASSERT_INT(j, ==, SMP2P_MAX_ENTRY);
+
+ seq_printf(s, "\tOK\n");
+ } while (0);
+
+ if (failed) {
+ pr_err("%s: Failed\n", __func__);
+ seq_printf(s, "\tFailed\n");
+
+ for (j = 0; j < SMP2P_MAX_ENTRY; j++)
+ ret = msm_smp2p_in_unregister(SMP2P_REMOTE_MOCK_PROC,
+ rmp->remote_item.entries[j].name,
+ &(cb_in[j].nb));
+ }
+}
+
+static struct dentry *dent;
+
+static int debugfs_show(struct seq_file *s, void *data)
+{
+ void (*show)(struct seq_file *) = s->private;
+
+ show(s);
+
+ return 0;
+}
+
+static int debug_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, debugfs_show, inode->i_private);
+}
+
+static const struct file_operations debug_ops = {
+ .open = debug_open,
+ .release = single_release,
+ .read = seq_read,
+ .llseek = seq_lseek,
+};
+
+void smp2p_debug_create(const char *name,
+ void (*show)(struct seq_file *))
+{
+ struct dentry *file;
+
+ file = debugfs_create_file(name, 0444, dent, show, &debug_ops);
+ if (!file)
+ pr_err("%s: unable to create file '%s'\n", __func__, name);
+}
+
+static int __init smp2p_debugfs_init(void)
+{
+ dent = debugfs_create_dir("smp2p_test", 0);
+ if (IS_ERR(dent))
+ return PTR_ERR(dent);
+
+ /*
+ * Add Unit Test entries.
+ *
+ * The idea with unit tests is that you can run all of them
+ * from ADB shell by doing:
+ * adb shell
+ * cat ut*
+ *
+ * And if particular tests fail, you can then repeatedly run the
+ * failing tests as you debug and resolve the failing test.
+ */
+ smp2p_debug_create("ut_local_basic",
+ smp2p_ut_local_basic);
+ smp2p_debug_create("ut_local_late_open",
+ smp2p_ut_local_late_open);
+ smp2p_debug_create("ut_local_early_open",
+ smp2p_ut_local_early_open);
+ smp2p_debug_create("ut_mock_loopback",
+ smp2p_ut_mock_loopback);
+ smp2p_debug_create("ut_remote_inout",
+ smp2p_ut_remote_inout);
+ smp2p_debug_create("ut_local_in_max_entries",
+ smp2p_ut_local_in_max_entries);
+ smp2p_debug_create("ut_remote_out_max_entries",
+ smp2p_ut_remote_out_max_entries);
+
+ return 0;
+}
+module_init(smp2p_debugfs_init);
diff --git a/arch/arm/mach-msm/smp2p_test_common.h b/arch/arm/mach-msm/smp2p_test_common.h
new file mode 100644
index 0000000..b9cddc4
--- /dev/null
+++ b/arch/arm/mach-msm/smp2p_test_common.h
@@ -0,0 +1,217 @@
+/* arch/arm/mach-msm/smp2p_test_common.h
+ *
+ * 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 _ARCH_ARM_MACH_MSM_SMP2P_TEST_COMMON_H_
+#define _ARCH_ARM_MACH_MSM_SMP2P_TEST_COMMON_H_
+
+#include <linux/debugfs.h>
+
+/**
+ * Unit test assertion for logging test cases.
+ *
+ * @a lval
+ * @b rval
+ * @cmp comparison operator
+ *
+ * Assertion fails if (@a cmp @b) is not true which then
+ * logs the function and line number where the error occurred
+ * along with the values of @a and @b.
+ *
+ * Assumes that the following local variables exist:
+ * @s - sequential output file pointer
+ * @failed - set to true if test fails
+ */
+#define UT_ASSERT_INT(a, cmp, b) \
+ { \
+ int a_tmp = (a); \
+ int b_tmp = (b); \
+ if (!((a_tmp)cmp(b_tmp))) { \
+ seq_printf(s, \
+ "%s:%d Fail: " #a "(%d) " #cmp " " #b "(%d)\n", \
+ __func__, __LINE__, \
+ a_tmp, b_tmp); \
+ failed = 1; \
+ break; \
+ } \
+ }
+
+#define UT_ASSERT_PTR(a, cmp, b) \
+ { \
+ void *a_tmp = (a); \
+ void *b_tmp = (b); \
+ if (!((a_tmp)cmp(b_tmp))) { \
+ seq_printf(s, \
+ "%s:%d Fail: " #a "(%p) " #cmp " " #b "(%p)\n", \
+ __func__, __LINE__, \
+ a_tmp, b_tmp); \
+ failed = 1; \
+ break; \
+ } \
+ }
+
+#define UT_ASSERT_UINT(a, cmp, b) \
+ { \
+ unsigned a_tmp = (a); \
+ unsigned b_tmp = (b); \
+ if (!((a_tmp)cmp(b_tmp))) { \
+ seq_printf(s, \
+ "%s:%d Fail: " #a "(%u) " #cmp " " #b "(%u)\n", \
+ __func__, __LINE__, \
+ a_tmp, b_tmp); \
+ failed = 1; \
+ break; \
+ } \
+ }
+
+#define UT_ASSERT_HEX(a, cmp, b) \
+ { \
+ unsigned a_tmp = (a); \
+ unsigned b_tmp = (b); \
+ if (!((a_tmp)cmp(b_tmp))) { \
+ seq_printf(s, \
+ "%s:%d Fail: " #a "(%x) " #cmp " " #b "(%x)\n", \
+ __func__, __LINE__, \
+ a_tmp, b_tmp); \
+ failed = 1; \
+ break; \
+ } \
+ }
+
+/**
+ * In-range unit test assertion for test cases.
+ *
+ * @a lval
+ * @minv Minimum value
+ * @maxv Maximum value
+ *
+ * Assertion fails if @a is not on the exclusive range minv, maxv
+ * ((@a < @minv) or (@a > @maxv)). In the failure case, the macro
+ * logs the function and line number where the error occurred along
+ * with the values of @a and @minv, @maxv.
+ *
+ * Assumes that the following local variables exist:
+ * @s - sequential output file pointer
+ * @failed - set to true if test fails
+ */
+#define UT_ASSERT_INT_IN_RANGE(a, minv, maxv) \
+ { \
+ int a_tmp = (a); \
+ int minv_tmp = (minv); \
+ int maxv_tmp = (maxv); \
+ if (((a_tmp) < (minv_tmp)) || ((a_tmp) > (maxv_tmp))) { \
+ seq_printf(s, \
+ "%s:%d Fail: " #a "(%d) < " #minv "(%d) or " \
+ #a "(%d) > " #maxv "(%d)\n", \
+ __func__, __LINE__, \
+ a_tmp, minv_tmp, a_tmp, maxv_tmp); \
+ failed = 1; \
+ break; \
+ } \
+ }
+
+/* Structure to track state changes for the notifier callback. */
+struct mock_cb_data {
+ bool initialized;
+ spinlock_t lock;
+ struct notifier_block nb;
+
+ /* events */
+ struct completion cb_completion;
+ int cb_count;
+ int event_open;
+ int event_entry_update;
+ struct msm_smp2p_update_notif entry_data;
+};
+
+void smp2p_debug_create(const char *name, void (*show)(struct seq_file *));
+static inline int smp2p_test_notify(struct notifier_block *self,
+ unsigned long event, void *data);
+
+/**
+ * Reset mock callback data to default values.
+ *
+ * @cb: Mock callback data
+ */
+static inline void mock_cb_data_reset(struct mock_cb_data *cb)
+{
+ INIT_COMPLETION(cb->cb_completion);
+ cb->cb_count = 0;
+ cb->event_open = 0;
+ cb->event_entry_update = 0;
+ memset(&cb->entry_data, 0,
+ sizeof(struct msm_smp2p_update_notif));
+}
+
+
+/**
+ * Initialize mock callback data.
+ *
+ * @cb: Mock callback data
+ */
+static inline void mock_cb_data_init(struct mock_cb_data *cb)
+{
+ if (!cb->initialized) {
+ init_completion(&cb->cb_completion);
+ spin_lock_init(&cb->lock);
+ cb->initialized = true;
+ cb->nb.notifier_call = smp2p_test_notify;
+ memset(&cb->entry_data, 0,
+ sizeof(struct msm_smp2p_update_notif));
+ }
+ mock_cb_data_reset(cb);
+}
+
+/**
+ * Notifier function passed into SMP2P for testing.
+ *
+ * @self: Pointer to calling notifier block
+ * @event: Event
+ * @data: Event-specific data
+ * @returns: 0
+ */
+static inline int smp2p_test_notify(struct notifier_block *self,
+ unsigned long event, void *data)
+{
+ struct mock_cb_data *cb_data_ptr;
+ unsigned long flags;
+
+ cb_data_ptr = container_of(self, struct mock_cb_data, nb);
+
+ spin_lock_irqsave(&cb_data_ptr->lock, flags);
+
+ switch (event) {
+ case SMP2P_OPEN:
+ ++cb_data_ptr->event_open;
+ if (data) {
+ cb_data_ptr->entry_data =
+ *(struct msm_smp2p_update_notif *)(data);
+ }
+ break;
+ case SMP2P_ENTRY_UPDATE:
+ ++cb_data_ptr->event_entry_update;
+ if (data) {
+ cb_data_ptr->entry_data =
+ *(struct msm_smp2p_update_notif *)(data);
+ }
+ break;
+ default:
+ pr_err("%s Unknown event\n", __func__);
+ break;
+ }
+
+ ++cb_data_ptr->cb_count;
+ complete(&cb_data_ptr->cb_completion);
+ spin_unlock_irqrestore(&cb_data_ptr->lock, flags);
+ return 0;
+}
+#endif /* _ARCH_ARM_MACH_MSM_SMP2P_TEST_COMMON_H_ */
diff --git a/arch/arm/mach-msm/socinfo.c b/arch/arm/mach-msm/socinfo.c
index 2743547..ee459d6 100644
--- a/arch/arm/mach-msm/socinfo.c
+++ b/arch/arm/mach-msm/socinfo.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2009-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
@@ -15,9 +15,17 @@
*
*/
-#include <linux/types.h>
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/sys_soc.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
#include <linux/sysdev.h>
+#include <linux/types.h>
+
#include <asm/mach-types.h>
+
#include <mach/socinfo.h>
#include "smd_private.h"
@@ -274,6 +282,8 @@
/* 9625 IDs */
[134] = MSM_CPU_9625,
[152] = MSM_CPU_9625,
+ [149] = MSM_CPU_9625,
+ [150] = MSM_CPU_9625,
/* 8960AB IDs */
[138] = MSM_CPU_8960AB,
@@ -293,8 +303,8 @@
/* 8092 IDs */
[146] = MSM_CPU_8092,
- /* 8910 IDs */
- [147] = MSM_CPU_8910,
+ /* 8610 IDs */
+ [147] = MSM_CPU_8610,
/* 8064AB IDs */
[153] = MSM_CPU_8064AB,
@@ -310,6 +320,8 @@
[169] = MSM_CPU_8625Q,
[170] = MSM_CPU_8625Q,
+ /* 8064AA IDs */
+ [172] = MSM_CPU_8064AA,
/* Uninitialized IDs are not known to run Linux.
MSM_CPU_UNKNOWN is set to 0 to ensure these IDs are
@@ -399,6 +411,11 @@
: 0;
}
+static uint32_t socinfo_get_format(void)
+{
+ return socinfo ? socinfo->v1.format : 0;
+}
+
enum msm_cpu socinfo_get_msm_cpu(void)
{
return cur_cpu;
@@ -607,6 +624,100 @@
socinfo_get_pmic_die_revision());
}
+static ssize_t
+msm_get_vendor(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "Qualcomm\n");
+}
+
+static ssize_t
+msm_get_raw_id(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ socinfo_get_raw_id());
+}
+
+static ssize_t
+msm_get_raw_version(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ socinfo_get_raw_version());
+}
+
+static ssize_t
+msm_get_build_id(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%-.32s\n",
+ socinfo_get_build_id());
+}
+
+static ssize_t
+msm_get_hw_platform(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ uint32_t hw_type;
+ hw_type = socinfo_get_platform_type();
+
+ return snprintf(buf, PAGE_SIZE, "%-.32s\n",
+ hw_platform[hw_type]);
+}
+
+static ssize_t
+msm_get_platform_version(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ socinfo_get_platform_version());
+}
+
+static ssize_t
+msm_get_accessory_chip(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ socinfo_get_accessory_chip());
+}
+
+static ssize_t
+msm_get_platform_subtype(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ uint32_t hw_subtype;
+ hw_subtype = socinfo_get_platform_subtype();
+ return snprintf(buf, PAGE_SIZE, "%-.32s\n",
+ hw_platform_subtype[hw_subtype]);
+}
+
+static ssize_t
+msm_get_pmic_model(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ socinfo_get_pmic_model());
+}
+
+static ssize_t
+msm_get_pmic_die_revision(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ socinfo_get_pmic_die_revision());
+}
+
static struct sysdev_attribute socinfo_v1_files[] = {
_SYSDEV_ATTR(id, 0444, socinfo_show_id, NULL),
_SYSDEV_ATTR(version, 0444, socinfo_show_version, NULL),
@@ -644,6 +755,42 @@
socinfo_show_pmic_die_revision, NULL),
};
+static struct device_attribute msm_soc_attr_raw_version =
+ __ATTR(raw_version, S_IRUGO, msm_get_raw_version, NULL);
+
+static struct device_attribute msm_soc_attr_raw_id =
+ __ATTR(raw_id, S_IRUGO, msm_get_raw_id, NULL);
+
+static struct device_attribute msm_soc_attr_vendor =
+ __ATTR(vendor, S_IRUGO, msm_get_vendor, NULL);
+
+static struct device_attribute msm_soc_attr_build_id =
+ __ATTR(build_id, S_IRUGO, msm_get_build_id, NULL);
+
+static struct device_attribute msm_soc_attr_hw_platform =
+ __ATTR(hw_platform, S_IRUGO, msm_get_hw_platform, NULL);
+
+
+static struct device_attribute msm_soc_attr_platform_version =
+ __ATTR(platform_version, S_IRUGO,
+ msm_get_platform_version, NULL);
+
+static struct device_attribute msm_soc_attr_accessory_chip =
+ __ATTR(accessory_chip, S_IRUGO,
+ msm_get_accessory_chip, NULL);
+
+static struct device_attribute msm_soc_attr_platform_subtype =
+ __ATTR(platform_subtype, S_IRUGO,
+ msm_get_platform_subtype, NULL);
+
+static struct device_attribute msm_soc_attr_pmic_model =
+ __ATTR(pmic_model, S_IRUGO,
+ msm_get_pmic_model, NULL);
+
+static struct device_attribute msm_soc_attr_pmic_die_revision =
+ __ATTR(pmic_die_revision, S_IRUGO,
+ msm_get_pmic_die_revision, NULL);
+
static struct sysdev_class soc_sysdev_class = {
.name = "soc",
};
@@ -669,15 +816,123 @@
return 0;
}
+static void * __init setup_dummy_socinfo(void)
+{
+ if (machine_is_msm8960_cdp())
+ dummy_socinfo.id = 87;
+ else if (machine_is_msm9615_mtp() || machine_is_msm9615_cdp())
+ dummy_socinfo.id = 104;
+ else if (early_machine_is_msm8974()) {
+ dummy_socinfo.id = 126;
+ strlcpy(dummy_socinfo.build_id, "msm8974 - ",
+ sizeof(dummy_socinfo.build_id));
+ } else if (early_machine_is_msm9625()) {
+ dummy_socinfo.id = 134;
+ strlcpy(dummy_socinfo.build_id, "msm9625 - ",
+ sizeof(dummy_socinfo.build_id));
+ } else if (early_machine_is_msm8226()) {
+ dummy_socinfo.id = 145;
+ strlcpy(dummy_socinfo.build_id, "msm8226 - ",
+ sizeof(dummy_socinfo.build_id));
+ } else if (machine_is_msm8625_rumi3())
+ dummy_socinfo.id = 127;
+ else if (early_machine_is_mpq8092()) {
+ dummy_socinfo.id = 146;
+ strlcpy(dummy_socinfo.build_id, "mpq8092 - ",
+ sizeof(dummy_socinfo.build_id));
+ } else if (early_machine_is_msm8610()) {
+ dummy_socinfo.id = 147;
+ strlcpy(dummy_socinfo.build_id, "msm8610 - ",
+ sizeof(dummy_socinfo.build_id));
+ }
+ strlcat(dummy_socinfo.build_id, "Dummy socinfo",
+ sizeof(dummy_socinfo.build_id));
+ return (void *) &dummy_socinfo;
+}
+
+static void __init populate_soc_sysfs_files(struct device *msm_soc_device)
+{
+ uint32_t legacy_format = socinfo_get_format();
+
+ device_create_file(msm_soc_device, &msm_soc_attr_vendor);
+
+ switch (legacy_format) {
+ case 7:
+ device_create_file(msm_soc_device,
+ &msm_soc_attr_pmic_model);
+ device_create_file(msm_soc_device,
+ &msm_soc_attr_pmic_die_revision);
+ case 6:
+ device_create_file(msm_soc_device,
+ &msm_soc_attr_platform_subtype);
+ case 5:
+ device_create_file(msm_soc_device,
+ &msm_soc_attr_accessory_chip);
+ case 4:
+ device_create_file(msm_soc_device,
+ &msm_soc_attr_platform_version);
+ case 3:
+ device_create_file(msm_soc_device,
+ &msm_soc_attr_hw_platform);
+ case 2:
+ device_create_file(msm_soc_device,
+ &msm_soc_attr_raw_id);
+ device_create_file(msm_soc_device,
+ &msm_soc_attr_raw_version);
+ case 1:
+ device_create_file(msm_soc_device,
+ &msm_soc_attr_build_id);
+ break;
+ default:
+ pr_err("%s:Unknown socinfo format:%u\n", __func__,
+ legacy_format);
+ break;
+ }
+
+ return;
+}
+
+static void __init soc_info_populate(struct soc_device_attribute *soc_dev_attr)
+{
+ uint32_t soc_version = socinfo_get_version();
+
+ soc_dev_attr->soc_id = kasprintf(GFP_KERNEL, "%d", socinfo_get_id());
+ soc_dev_attr->machine = "Snapdragon";
+ soc_dev_attr->revision = kasprintf(GFP_KERNEL, "%u.%u",
+ SOCINFO_VERSION_MAJOR(soc_version),
+ SOCINFO_VERSION_MINOR(soc_version));
+ return;
+
+}
+
static int __init socinfo_init_sysdev(void)
{
int err;
+ struct device *msm_soc_device;
+ struct soc_device *soc_dev;
+ struct soc_device_attribute *soc_dev_attr;
if (!socinfo) {
pr_err("%s: No socinfo found!\n", __func__);
return -ENODEV;
}
+ soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL);
+ if (!soc_dev_attr) {
+ pr_err("%s: Soc Device alloc failed!\n", __func__);
+ return -ENOMEM;
+ }
+
+ soc_info_populate(soc_dev_attr);
+ soc_dev = soc_device_register(soc_dev_attr);
+ if (IS_ERR_OR_NULL(soc_dev)) {
+ kfree(soc_dev_attr);
+ pr_err("%s: Soc device register failed\n", __func__);
+ return -EIO;
+ }
+
+ msm_soc_device = soc_device_to_device(soc_dev);
+ populate_soc_sysfs_files(msm_soc_device);
err = sysdev_class_register(&soc_sysdev_class);
if (err) {
pr_err("%s: sysdev_class_register fail (%d)\n",
@@ -724,87 +979,16 @@
if (socinfo->v1.format < 7)
return err;
- return socinfo_create_files(&soc_sys_device, socinfo_v7_files,
+ socinfo_create_files(&soc_sys_device, socinfo_v7_files,
ARRAY_SIZE(socinfo_v7_files));
+
+ return 0;
}
arch_initcall(socinfo_init_sysdev);
-static void * __init setup_dummy_socinfo(void)
+static void socinfo_print(void)
{
- if (machine_is_msm8960_cdp())
- dummy_socinfo.id = 87;
- else if (machine_is_msm9615_mtp() || machine_is_msm9615_cdp())
- dummy_socinfo.id = 104;
- else if (early_machine_is_msm8974()) {
- dummy_socinfo.id = 126;
- strlcpy(dummy_socinfo.build_id, "msm8974 - ",
- sizeof(dummy_socinfo.build_id));
- } else if (early_machine_is_msm9625()) {
- dummy_socinfo.id = 134;
- strlcpy(dummy_socinfo.build_id, "msm9625 - ",
- sizeof(dummy_socinfo.build_id));
- } else if (early_machine_is_msm8226()) {
- dummy_socinfo.id = 145;
- strlcpy(dummy_socinfo.build_id, "msm8226 - ",
- sizeof(dummy_socinfo.build_id));
- } else if (machine_is_msm8625_rumi3())
- dummy_socinfo.id = 127;
- else if (early_machine_is_mpq8092()) {
- dummy_socinfo.id = 146;
- strlcpy(dummy_socinfo.build_id, "mpq8092 - ",
- sizeof(dummy_socinfo.build_id));
- } else if (early_machine_is_msm8910()) {
- dummy_socinfo.id = 147;
- strlcpy(dummy_socinfo.build_id, "msm8910 - ",
- sizeof(dummy_socinfo.build_id));
- }
- strlcat(dummy_socinfo.build_id, "Dummy socinfo",
- sizeof(dummy_socinfo.build_id));
- return (void *) &dummy_socinfo;
-}
-
-int __init socinfo_init(void)
-{
- socinfo = smem_alloc(SMEM_HW_SW_BUILD_ID, sizeof(struct socinfo_v7));
-
- if (!socinfo)
- socinfo = smem_alloc(SMEM_HW_SW_BUILD_ID,
- sizeof(struct socinfo_v6));
-
- if (!socinfo)
- socinfo = smem_alloc(SMEM_HW_SW_BUILD_ID,
- sizeof(struct socinfo_v5));
-
- if (!socinfo)
- socinfo = smem_alloc(SMEM_HW_SW_BUILD_ID,
- sizeof(struct socinfo_v4));
-
- if (!socinfo)
- socinfo = smem_alloc(SMEM_HW_SW_BUILD_ID,
- sizeof(struct socinfo_v3));
-
- if (!socinfo)
- socinfo = smem_alloc(SMEM_HW_SW_BUILD_ID,
- sizeof(struct socinfo_v2));
-
- if (!socinfo)
- socinfo = smem_alloc(SMEM_HW_SW_BUILD_ID,
- sizeof(struct socinfo_v1));
-
- if (!socinfo) {
- pr_warn("%s: Can't find SMEM_HW_SW_BUILD_ID; falling back on "
- "dummy values.\n", __func__);
- socinfo = setup_dummy_socinfo();
- }
-
- WARN(!socinfo_get_id(), "Unknown SOC ID!\n");
- WARN(socinfo_get_id() >= ARRAY_SIZE(cpu_of_id),
- "New IDs added! ID => CPU mapping might need an update.\n");
-
- if (socinfo->v1.id < ARRAY_SIZE(cpu_of_id))
- cur_cpu = cpu_of_id[socinfo->v1.id];
-
switch (socinfo->v1.format) {
case 1:
pr_info("%s: v%u, id=%u, ver=%u.%u\n",
@@ -880,6 +1064,50 @@
pr_err("%s: Unknown format found\n", __func__);
break;
}
+}
+
+int __init socinfo_init(void)
+{
+ socinfo = smem_alloc(SMEM_HW_SW_BUILD_ID, sizeof(struct socinfo_v7));
+
+ if (!socinfo)
+ socinfo = smem_alloc(SMEM_HW_SW_BUILD_ID,
+ sizeof(struct socinfo_v6));
+
+ if (!socinfo)
+ socinfo = smem_alloc(SMEM_HW_SW_BUILD_ID,
+ sizeof(struct socinfo_v5));
+
+ if (!socinfo)
+ socinfo = smem_alloc(SMEM_HW_SW_BUILD_ID,
+ sizeof(struct socinfo_v4));
+
+ if (!socinfo)
+ socinfo = smem_alloc(SMEM_HW_SW_BUILD_ID,
+ sizeof(struct socinfo_v3));
+
+ if (!socinfo)
+ socinfo = smem_alloc(SMEM_HW_SW_BUILD_ID,
+ sizeof(struct socinfo_v2));
+
+ if (!socinfo)
+ socinfo = smem_alloc(SMEM_HW_SW_BUILD_ID,
+ sizeof(struct socinfo_v1));
+
+ if (!socinfo) {
+ pr_warn("%s: Can't find SMEM_HW_SW_BUILD_ID; falling back on dummy values.\n",
+ __func__);
+ socinfo = setup_dummy_socinfo();
+ }
+
+ WARN(!socinfo_get_id(), "Unknown SOC ID!\n");
+ WARN(socinfo_get_id() >= ARRAY_SIZE(cpu_of_id),
+ "New IDs added! ID => CPU mapping might need an update.\n");
+
+ if (socinfo->v1.id < ARRAY_SIZE(cpu_of_id))
+ cur_cpu = cpu_of_id[socinfo->v1.id];
+
+ socinfo_print();
return 0;
}
@@ -921,6 +1149,7 @@
return MSM_CPU_8064;
case 0x511F06F1:
+ case 0x511F06F2:
case 0x512F06F0:
return MSM_CPU_8974;
@@ -972,6 +1201,7 @@
case 0x512F04D0:
case 0x511F06F0:
case 0x511F06F1:
+ case 0x511F06F2:
case 0x510F05D0:
return 1;
diff --git a/arch/arm/mach-msm/test-lpm.c b/arch/arm/mach-msm/test-lpm.c
new file mode 100644
index 0000000..031b2dc
--- /dev/null
+++ b/arch/arm/mach-msm/test-lpm.c
@@ -0,0 +1,699 @@
+/* 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
+ * 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/pm.h>
+#include <linux/mutex.h>
+#include <linux/uaccess.h>
+#include <linux/debugfs.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/ctype.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <mach/socinfo.h>
+#if defined(CONFIG_MSM_RPM)
+#include "rpm_resources.h"
+#endif
+#if defined(CONFIG_MSM_RPM_SMD)
+#include "lpm_resources.h"
+#endif
+#include "timer.h"
+#include "test-lpm.h"
+
+#define LPM_STATS_RESET "reset"
+#define LPM_TEST_ALL_LEVELS "lpm"
+#define LPM_TEST_LATENCIES "latency"
+#define LPM_TEST_CLEAR "clear"
+#define BUF_SIZE 200
+#define STAT_BUF_EXTRA_SIZE 500
+#define WAIT_FOR_XO 1
+#define COMM_BUF_SIZE 15
+#define INPUT_COUNT_BUF 10
+#define LPM_DEFAULT_CPU 0
+
+#define SNPRINTF(buf, size, format, ...) \
+{ \
+ if (size > 0) { \
+ int ret; \
+ ret = snprintf(buf, size, format, ## __VA_ARGS__); \
+ if (ret > size) { \
+ buf += size; \
+ size = 0; \
+ } else { \
+ buf += ret; \
+ size -= ret; \
+ } \
+ } \
+} \
+
+static DEFINE_MUTEX(lpm_stats_mutex);
+
+struct lpm_level_stat {
+ char level_name[BUF_SIZE];
+ int64_t min_time;
+ int64_t max_time;
+ int64_t avg_time;
+ int64_t exit_early;
+ int64_t count;
+ unsigned long min_threshold;
+ uint32_t kernel_sleep_time;
+ bool entered;
+};
+
+static DEFINE_PER_CPU(struct lpm_level_stat *, lpm_levels);
+
+static struct dentry *lpm_stat;
+static struct dentry *lpm_ext_comm;
+static struct msm_rpmrs_level *lpm_supp_level;
+static int lpm_level_count;
+static int lpm_level_iter;
+static bool msm_lpm_use_qtimer;
+static unsigned long lpm_sleep_time;
+static bool lpm_latency_test;
+
+static unsigned int timer_interval = 5000;
+module_param_named(lpm_timer_interval_msec, timer_interval, uint,
+ S_IRUGO | S_IWUSR | S_IWGRP);
+
+static unsigned int latency_test_interval = 50;
+module_param_named(lpm_latency_timer_interval_usec, latency_test_interval, uint,
+ S_IRUGO | S_IWUSR | S_IWGRP);
+
+static unsigned int cpu_to_debug = LPM_DEFAULT_CPU;
+static int lpm_cpu_update(const char *val, const struct kernel_param *kp)
+{
+ int ret = 0;
+ unsigned int debug_val;
+
+ ret = kstrtouint(val, 10, &debug_val);
+ if ((ret < 0) || (debug_val >= num_possible_cpus()))
+ return -EINVAL;
+ cpu_to_debug = debug_val;
+ return ret;
+}
+
+static struct kernel_param_ops cpu_debug_events = {
+ .set = lpm_cpu_update,
+};
+
+module_param_cb(cpu_to_debug, &cpu_debug_events, &cpu_to_debug,
+ S_IRUGO | S_IWUSR | S_IWGRP);
+
+static void lpm_populate_name(struct lpm_level_stat *stat,
+ struct msm_rpmrs_level *supp)
+{
+ char nm[BUF_SIZE] = {0};
+ char default_buf[20];
+
+ switch (supp->sleep_mode) {
+ case MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT:
+ strlcat(nm, "WFI ", BUF_SIZE);
+ break;
+ case MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT:
+ strlcat(nm, "WFI voltage Rampdown ", BUF_SIZE);
+ break;
+ case MSM_PM_SLEEP_MODE_RETENTION:
+ strlcat(nm, "Retention ", BUF_SIZE);
+ break;
+ case MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE:
+ strlcat(nm, "Standalone Power collapse ", BUF_SIZE);
+ break;
+ case MSM_PM_SLEEP_MODE_POWER_COLLAPSE:
+ strlcat(nm, "Idle Power collapse ", BUF_SIZE);
+ break;
+ case MSM_PM_SLEEP_MODE_POWER_COLLAPSE_SUSPEND:
+ strlcat(nm, "Suspend Power collapse ", BUF_SIZE);
+ break;
+ default:
+ strlcat(nm, "Invalid Mode ", BUF_SIZE);
+ break;
+ }
+
+ switch (msm_pm_get_pxo(&(supp->rs_limits))) {
+ case MSM_PM(PXO_OFF):
+ strlcat(nm, "XO: OFF ", BUF_SIZE);
+ break;
+ case MSM_PM(PXO_ON):
+ strlcat(nm, "XO: ON ", BUF_SIZE);
+ break;
+ default:
+ snprintf(default_buf, sizeof(default_buf),
+ "XO : %d ", msm_pm_get_pxo(&(supp->rs_limits)));
+ strlcat(nm, default_buf , BUF_SIZE);
+ break;
+ }
+
+ switch (msm_pm_get_l2_cache(&(supp->rs_limits))) {
+ case MSM_PM(L2_CACHE_HSFS_OPEN):
+ strlcat(nm, "L2: HSFS ", BUF_SIZE);
+ break;
+ case MSM_PM(L2_CACHE_GDHS):
+ strlcat(nm, "L2: GDHS ", BUF_SIZE);
+ break;
+ case MSM_PM(L2_CACHE_RETENTION):
+ strlcat(nm, "L2: Retention ", BUF_SIZE);
+ break;
+ case MSM_PM(L2_CACHE_ACTIVE):
+ strlcat(nm, "L2: Active ", BUF_SIZE);
+ break;
+ default:
+ snprintf(default_buf, sizeof(default_buf),
+ "L2 : %d ", msm_pm_get_l2_cache(&(supp->rs_limits)));
+ strlcat(nm, default_buf , BUF_SIZE);
+ break;
+ }
+
+ snprintf(default_buf, sizeof(default_buf),
+ "Vdd_mem : %d ", msm_pm_get_vdd_mem(&(supp->rs_limits)));
+ strlcat(nm, default_buf , BUF_SIZE);
+
+ snprintf(default_buf, sizeof(default_buf),
+ "Vdd_dig : %d ", msm_pm_get_vdd_dig(&(supp->rs_limits)));
+ strlcat(nm, default_buf , BUF_SIZE);
+
+ strlcpy(stat->level_name, nm, strnlen(nm, BUF_SIZE));
+}
+
+static int64_t msm_lpm_get_time(void)
+{
+ if (msm_lpm_use_qtimer)
+ return ktime_to_ns(ktime_get());
+
+ return msm_timer_get_sclk_time(NULL);
+}
+
+static bool lpm_get_level(void *v, unsigned int *ct)
+{
+ bool ret = false;
+ int it;
+ struct msm_rpmrs_level *level_enter;
+
+ level_enter = container_of(((struct msm_lpm_sleep_data *)v)->limits,
+ struct msm_rpmrs_level, rs_limits);
+ if (level_enter) {
+ for (it = 0; it < lpm_level_count; it++)
+ if (!memcmp(level_enter , lpm_supp_level + it,
+ sizeof(struct msm_rpmrs_level))) {
+ *ct = it;
+ ret = true;
+ break;
+ }
+ }
+ return ret;
+}
+
+static int lpm_callback(struct notifier_block *self, unsigned long cmd,
+ void *sleep_data)
+{
+ static int64_t time;
+ unsigned int ct;
+ struct lpm_level_stat *stats;
+ stats = per_cpu(lpm_levels, cpu_to_debug);
+ /* Update the stats and get the start/stop time */
+ if (cmd == MSM_LPM_STATE_ENTER && !lpm_latency_test) {
+ time = msm_lpm_get_time();
+ stats[lpm_level_iter].entered = true;
+ } else if ((cmd == MSM_LPM_STATE_EXIT) && (time)
+ && (!lpm_latency_test)) {
+ int64_t time1;
+ time1 = msm_lpm_get_time();
+ time = time1 - time;
+
+ if ((time < stats[lpm_level_iter].min_time) ||
+ (!stats[lpm_level_iter].min_time))
+ stats[lpm_level_iter].min_time = time;
+
+ if (time > stats[lpm_level_iter].max_time)
+ stats[lpm_level_iter].max_time = time;
+
+ time1 = stats[lpm_level_iter].avg_time *
+ stats[lpm_level_iter].count + time;
+ do_div(time1, ++(stats[lpm_level_iter].count));
+
+ stats[lpm_level_iter].avg_time = time1;
+ do_div(time, NSEC_PER_USEC);
+ if (time < lpm_supp_level[lpm_level_iter].
+ time_overhead_us)
+ stats[lpm_level_iter].exit_early++;
+ time = 0;
+ } else if (cmd == MSM_LPM_STATE_ENTER && lpm_latency_test) {
+
+ struct msm_lpm_sleep_data *data = sleep_data;
+ if ((lpm_get_level(sleep_data, &ct)) &&
+ (stats[ct].min_threshold == 0) &&
+ data->kernel_sleep <= lpm_sleep_time) {
+
+ stats[ct].min_threshold = lpm_sleep_time;
+ stats[ct].kernel_sleep_time =
+ data->kernel_sleep;
+ }
+ }
+ return 0;
+}
+
+static struct notifier_block lpm_idle_nb = {
+ .notifier_call = lpm_callback,
+};
+
+static void lpm_test_initiate(int lpm_level_test)
+{
+ int test_ret;
+
+ /* This will communicate to 'stat' debugfs to skip latency printing*/
+ lpm_sleep_time = 0;
+ lpm_latency_test = false;
+ /* Unregister any infinitely registered level*/
+ msm_lpm_unregister_notifier(cpu_to_debug, &lpm_idle_nb);
+
+ /* Register/Unregister for Notification */
+ while (lpm_level_iter < lpm_level_count) {
+ test_ret = msm_lpm_register_notifier(cpu_to_debug,
+ lpm_level_iter, &lpm_idle_nb, false);
+ if (test_ret < 0) {
+ pr_err("%s: Registering notifier failed\n", __func__);
+ return;
+ }
+ if (!timer_interval)
+ break;
+ msleep(timer_interval);
+ msm_lpm_unregister_notifier(cpu_to_debug, &lpm_idle_nb);
+ if (lpm_level_test == lpm_level_count)
+ lpm_level_iter++;
+ else
+ break;
+ }
+}
+
+static void lpm_latency_test_initiate(unsigned long max_time)
+{
+ int test_ret;
+ lpm_latency_test = true;
+ lpm_sleep_time = latency_test_interval;
+
+ msm_lpm_unregister_notifier(cpu_to_debug, &lpm_idle_nb);
+ if (max_time > lpm_sleep_time) {
+
+ do {
+ test_ret = msm_lpm_register_notifier(cpu_to_debug,
+ lpm_level_count + 1,
+ &lpm_idle_nb, true);
+ if (test_ret) {
+ pr_err("%s: Registering notifier failed\n",
+ __func__);
+ return;
+ }
+ usleep(lpm_sleep_time);
+ /*Unregister to ensure that we dont update the latency
+ during the timer value transistion*/
+ msm_lpm_unregister_notifier(cpu_to_debug,
+ &lpm_idle_nb);
+ lpm_sleep_time += latency_test_interval;
+ } while (lpm_sleep_time < max_time);
+ } else
+ pr_err("%s: Invalid time interval specified\n", __func__);
+
+ lpm_latency_test = false;
+}
+
+static ssize_t lpm_test_comm_read(struct file *fp, char __user *user_buffer,
+ size_t buffer_length, loff_t *position)
+{
+ int i = 0;
+ int count = buffer_length;
+ int alloc_size = 100 * lpm_level_count;
+ char *temp_buf;
+ char *comm_buf;
+ ssize_t ret;
+
+ comm_buf = kzalloc(alloc_size, GFP_KERNEL);
+ if (!comm_buf) {
+ pr_err("%s:Memory alloc failed\n", __func__);
+ ret = 0;
+ goto com_read_failed;
+ }
+ temp_buf = comm_buf;
+
+ SNPRINTF(temp_buf, count, "Low power modes available:\n");
+
+ for (i = 0; i < lpm_level_count; i++)
+ SNPRINTF(temp_buf, count, "%d. %s\n", i,
+ per_cpu(lpm_levels, cpu_to_debug)[i].level_name);
+
+ SNPRINTF(temp_buf, count, "%d. MSM test all lpm\n", i++);
+ SNPRINTF(temp_buf, count, "%d. MSM determine latency\n", i);
+
+ ret = simple_read_from_buffer(user_buffer, buffer_length - count,
+ position, comm_buf, alloc_size);
+ kfree(comm_buf);
+
+com_read_failed:
+ return ret;
+}
+
+char *trimspaces(char *time_buf)
+{
+ int len;
+ char *tail;
+
+ len = strnlen(time_buf, INPUT_COUNT_BUF);
+ tail = time_buf + len;
+ while (isspace(*time_buf) && (time_buf != tail))
+ time_buf++;
+ if (time_buf == tail) {
+ time_buf = NULL;
+ goto exit_trim_spaces;
+ }
+ len = strnlen(time_buf, INPUT_COUNT_BUF);
+ tail = time_buf + len - 1;
+ while (isspace(*tail) && tail != time_buf) {
+ *tail = '\0';
+ tail--;
+ }
+exit_trim_spaces:
+ return time_buf;
+}
+
+static ssize_t lpm_test_comm_write(struct file *fp, const char __user
+ *user_buffer, size_t count, loff_t *position)
+{
+ ssize_t ret;
+ int str_ret;
+ int lpm_level_test;
+ char *new_ptr;
+ char *comm_buf;
+
+ comm_buf = kzalloc(COMM_BUF_SIZE, GFP_KERNEL);
+ if (!comm_buf) {
+ pr_err("\'%s\': kzalloc failed\n", __func__);
+ return -EINVAL;
+ }
+
+ memset(comm_buf, '\0', COMM_BUF_SIZE);
+
+ ret = simple_write_to_buffer(comm_buf, COMM_BUF_SIZE, position,
+ user_buffer, count);
+ new_ptr = trimspaces(comm_buf);
+ if (!new_ptr) {
+ pr_err("%s: Test case number input invalid\n", __func__);
+ goto write_com_failed;
+ }
+
+ if (!memcmp(comm_buf, LPM_TEST_ALL_LEVELS,
+ sizeof(LPM_TEST_ALL_LEVELS) - 1)) {
+ lpm_level_test = lpm_level_count;
+ lpm_level_iter = 0;
+ lpm_test_initiate(lpm_level_test);
+ goto write_com_success;
+ } else if (!memcmp(comm_buf, LPM_TEST_LATENCIES,
+ sizeof(LPM_TEST_LATENCIES) - 1)) {
+ lpm_level_test = lpm_level_count + 1;
+ lpm_latency_test_initiate(timer_interval * USEC_PER_MSEC);
+ goto write_com_success;
+ } else if (!memcmp(comm_buf, LPM_TEST_CLEAR,
+ sizeof(LPM_TEST_CLEAR) - 1)) {
+ msm_lpm_unregister_notifier(cpu_to_debug, &lpm_idle_nb);
+ goto write_com_success;
+ }
+
+ str_ret = kstrtoint(new_ptr, 10, &lpm_level_test);
+ if ((str_ret) || (lpm_level_test > (lpm_level_count + 1)) ||
+ (lpm_level_test < 0))
+ goto write_com_failed;
+
+ lpm_level_iter = lpm_level_test;
+ lpm_test_initiate(lpm_level_test);
+ goto write_com_success;
+
+write_com_failed:
+ ret = -EINVAL;
+write_com_success:
+ kfree(comm_buf);
+ return ret;
+}
+
+static ssize_t lpm_test_stat_read(struct file *fp, char __user *user_buffer,
+ size_t buffer_length, loff_t *position)
+{
+ int i = 0;
+ int j = 0;
+ int count = buffer_length;
+ char *stat_buf;
+ char *stat_buf_start;
+ size_t stat_buf_size;
+ ssize_t ret;
+ int64_t min_ns;
+ int64_t max_ns;
+ int64_t avg_ns;
+ uint32_t min_ms;
+ uint32_t max_ms;
+ uint32_t avg_ms;
+
+ stat_buf_size = ((sizeof(struct lpm_level_stat) * lpm_level_count) +
+ STAT_BUF_EXTRA_SIZE);
+ stat_buf = kzalloc(stat_buf_size, GFP_KERNEL);
+ if (!stat_buf) {
+ pr_err("\'%s\': kzalloc failed\n", __func__);
+ return -EINVAL;
+ }
+ stat_buf_start = stat_buf;
+ mutex_lock(&lpm_stats_mutex);
+ memset(stat_buf, '\0', stat_buf_size);
+ SNPRINTF(stat_buf, count, "\n\nStats for CPU: %d\nTotal Levels: %d\n",
+ cpu_to_debug, lpm_level_count);
+ if (!lpm_sleep_time) {
+ SNPRINTF(stat_buf, count, "Level(s) failed: ");
+ for (i = 0 ; i < lpm_level_count; i++) {
+ if (per_cpu(lpm_levels, cpu_to_debug)[i].entered)
+ continue;
+ else {
+ SNPRINTF(stat_buf, count,
+ "\n%d. %s", ++j, per_cpu(lpm_levels,
+ cpu_to_debug)[i].level_name);
+ }
+ }
+ SNPRINTF(stat_buf, count, "\n\nSTATS:");
+ for (i = 0; i < lpm_level_count; i++) {
+ min_ns = per_cpu(lpm_levels, cpu_to_debug)[i].min_time;
+ min_ms = do_div(min_ns, NSEC_PER_MSEC);
+ max_ns = per_cpu(lpm_levels, cpu_to_debug)[i].max_time;
+ max_ms = do_div(max_ns, NSEC_PER_MSEC);
+ avg_ns = per_cpu(lpm_levels, cpu_to_debug)[i].avg_time;
+ avg_ms = do_div(avg_ns, NSEC_PER_MSEC);
+ SNPRINTF(stat_buf, count, "\nLEVEL: %s\n"
+ "Entered : %lld\n"
+ "Early wakeup : %lld\n"
+ "Min Time (mSec): %lld.%06u\n"
+ "Max Time (mSec): %lld.%06u\n"
+ "Avg Time (mSec): %lld.%06u\n",
+ per_cpu(lpm_levels, cpu_to_debug)[i].level_name,
+ per_cpu(lpm_levels, cpu_to_debug)[i].count,
+ per_cpu(lpm_levels, cpu_to_debug)[i].exit_early,
+ min_ns, min_ms,
+ max_ns, max_ms,
+ avg_ns, avg_ms);
+ }
+ } else {
+ for (i = 0; i < lpm_level_count; i++) {
+ SNPRINTF(stat_buf, count, "\nLEVEL: %s\n"
+ "Min Timer value (uSec): %lu\n"
+ "Kernel sleep time (uSec): %u\n",
+ per_cpu(lpm_levels, cpu_to_debug)[i].level_name,
+ per_cpu(lpm_levels, cpu_to_debug)[i].
+ min_threshold,
+ per_cpu(lpm_levels,
+ cpu_to_debug)[i].kernel_sleep_time);
+ }
+ }
+
+ ret = simple_read_from_buffer(user_buffer, buffer_length - count,
+ position, stat_buf_start, stat_buf_size);
+
+ mutex_unlock(&lpm_stats_mutex);
+ kfree(stat_buf_start);
+ return ret;
+}
+
+static ssize_t lpm_test_stat_write(struct file *fp, const char __user
+ *user_buffer, size_t count, loff_t *position)
+{
+ char buf[sizeof(LPM_STATS_RESET)];
+ int ret;
+ int i;
+ struct lpm_level_stat *stats;
+
+ if (count > sizeof(LPM_STATS_RESET)) {
+ ret = -EINVAL;
+ goto write_debug_failed;
+ }
+
+ simple_write_to_buffer(buf, sizeof(LPM_STATS_RESET), position,
+ user_buffer, count);
+
+ if (memcmp(buf, LPM_STATS_RESET, sizeof(LPM_STATS_RESET) - 1)) {
+ ret = -EINVAL;
+ goto write_debug_failed;
+ }
+
+ mutex_lock(&lpm_stats_mutex);
+ stats = per_cpu(lpm_levels, cpu_to_debug);
+ for (i = 0 ; i < lpm_level_count; i++) {
+ stats[i].entered = 0;
+ stats[i].min_time = 0;
+ stats[i].max_time = 0;
+ stats[i].avg_time = 0;
+ stats[i].count = 0;
+ stats[i].exit_early = 0;
+ stats[i].min_threshold = 0;
+ stats[i].kernel_sleep_time = 0;
+ }
+ mutex_unlock(&lpm_stats_mutex);
+ return count;
+write_debug_failed:
+ return ret;
+}
+
+static void lpm_init_rpm_levels(int test_lpm_level_count,
+ struct msm_rpmrs_level *test_levels)
+{
+ int i = 0;
+ unsigned int m_cpu = 0;
+ struct lpm_level_stat *stat_levels = NULL;
+
+ if (test_lpm_level_count < 0)
+ return;
+
+ lpm_level_count = test_lpm_level_count;
+
+ lpm_supp_level = test_levels;
+ for_each_possible_cpu(m_cpu) {
+ stat_levels = kzalloc(sizeof(struct lpm_level_stat) *
+ lpm_level_count, GFP_KERNEL);
+ if (!stat_levels) {
+ for (i = m_cpu - 1; i >= 0; i--)
+ kfree(per_cpu(lpm_levels, i));
+ return;
+ }
+
+ for (i = 0; i < lpm_level_count; i++)
+ lpm_populate_name(&stat_levels[i], &lpm_supp_level[i]);
+
+ per_cpu(lpm_levels, m_cpu) = stat_levels;
+ }
+}
+
+static const struct file_operations fops_stat = {
+ .read = lpm_test_stat_read,
+ .write = lpm_test_stat_write,
+};
+
+static const struct file_operations fops_comm = {
+ .read = lpm_test_comm_read,
+ .write = lpm_test_comm_write,
+};
+
+static int __devinit lpm_test_init(int test_lpm_level_count,
+ struct msm_rpmrs_level *test_levels)
+{
+ int filevalue;
+ int lpm_comm;
+ int ret = -EINVAL;
+ struct dentry *parent_dir = NULL;
+
+ parent_dir = debugfs_create_dir("msm_lpm_debug", NULL);
+ if (!parent_dir) {
+ pr_err("%s: debugfs directory creation failed\n",
+ __func__);
+ goto init_err;
+ }
+
+ lpm_stat = debugfs_create_file("stat",
+ S_IRUGO | S_IWUSR | S_IWGRP, parent_dir,
+ &filevalue, &fops_stat);
+ if (!lpm_stat) {
+ pr_err("%s: lpm_stats debugfs creation failed\n",
+ __func__);
+ goto init_err;
+ }
+
+ lpm_ext_comm = debugfs_create_file("comm",
+ S_IRUGO | S_IWUSR | S_IWGRP, parent_dir, &lpm_comm,
+ &fops_comm);
+ if (!lpm_ext_comm) {
+ pr_err("%s: lpm_comm debugfs creation failed\n",
+ __func__);
+ debugfs_remove(lpm_stat);
+ goto init_err;
+ }
+
+ /*Query RPM resources and allocate the data sturctures*/
+ lpm_init_rpm_levels(test_lpm_level_count, test_levels);
+ ret = 0;
+
+init_err:
+ return ret;
+}
+
+static int __devexit lpm_test_exit(struct platform_device *pdev)
+{
+ unsigned int m_cpu = 0;
+
+ kfree(lpm_supp_level);
+ for_each_possible_cpu(m_cpu)
+ kfree(per_cpu(lpm_levels, m_cpu));
+ debugfs_remove(lpm_stat);
+ debugfs_remove(lpm_ext_comm);
+ return 0;
+}
+
+static int __devinit lpm_test_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct lpm_test_platform_data *pdata;
+ struct msm_rpmrs_level *test_levels;
+ int test_lpm_level_count;
+
+ pdata = pdev->dev.platform_data;
+
+ if (!pdata) {
+ dev_err(dev, "no platform data specified\n");
+ return -EINVAL;
+ }
+
+ test_levels = pdata->msm_lpm_test_levels;
+ test_lpm_level_count = pdata->msm_lpm_test_level_count;
+
+ if (pdata->use_qtimer)
+ msm_lpm_use_qtimer = true;
+
+ lpm_test_init(test_lpm_level_count, test_levels);
+
+ return 0;
+}
+
+static struct platform_driver lpm_test_driver = {
+ .probe = lpm_test_probe,
+ .remove = lpm_test_exit,
+ .driver = {
+ .name = "lpm_test",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init lpm_test_platform_driver_init(void)
+{
+ return platform_driver_register(&lpm_test_driver);
+}
+
+late_initcall(lpm_test_platform_driver_init);
diff --git a/arch/arm/boot/dts/msm8974-mtp.dts b/arch/arm/mach-msm/test-lpm.h
similarity index 70%
copy from arch/arm/boot/dts/msm8974-mtp.dts
copy to arch/arm/mach-msm/test-lpm.h
index 9946cf0..1486f88 100644
--- a/arch/arm/boot/dts/msm8974-mtp.dts
+++ b/arch/arm/mach-msm/test-lpm.h
@@ -8,15 +8,15 @@
* 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.
+ *
*/
-/dts-v1/;
+#ifndef __ARCH_ARM_MACH_MSM_TEST_LPM_H
+#define __ARCH_ARM_MACH_MSM_TEST_LPM_H
-/include/ "msm8974.dtsi"
-/include/ "msm8974-mtp.dtsi"
-
-/ {
- model = "Qualcomm MSM 8974 MTP";
- compatible = "qcom,msm8974-mtp", "qcom,msm8974";
- qcom,msm-id = <126 8 0>;
+struct lpm_test_platform_data {
+ struct msm_rpmrs_level *msm_lpm_test_levels;
+ int msm_lpm_test_level_count;
+ bool use_qtimer;
};
+#endif
diff --git a/block/blk-merge.c b/block/blk-merge.c
index 4fced1b..5d74cc3 100644
--- a/block/blk-merge.c
+++ b/block/blk-merge.c
@@ -42,6 +42,9 @@
goto new_segment;
if (!BIOVEC_SEG_BOUNDARY(q, bvprv, bv))
goto new_segment;
+ if ((bvprv->bv_page != bv->bv_page) &&
+ (bvprv->bv_page + 1) != bv->bv_page)
+ goto new_segment;
seg_size += bv->bv_len;
bvprv = bv;
@@ -141,6 +144,9 @@
goto new_segment;
if (!BIOVEC_SEG_BOUNDARY(q, bvprv, bvec))
goto new_segment;
+ if ((bvprv->bv_page != bvec->bv_page) &&
+ ((bvprv->bv_page + 1) != bvec->bv_page))
+ goto new_segment;
sg->length += nbytes;
} else {
diff --git a/block/row-iosched.c b/block/row-iosched.c
index a4184da..b8c16e7 100644
--- a/block/row-iosched.c
+++ b/block/row-iosched.c
@@ -1,7 +1,7 @@
/*
* ROW (Read Over Write) I/O scheduler.
*
- * 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
@@ -47,37 +47,38 @@
ROWQ_MAX_PRIO,
};
-/* Flags indicating whether idling is enabled on the queue */
-static const bool queue_idling_enabled[] = {
- true, /* ROWQ_PRIO_HIGH_READ */
- true, /* ROWQ_PRIO_REG_READ */
- false, /* ROWQ_PRIO_HIGH_SWRITE */
- false, /* ROWQ_PRIO_REG_SWRITE */
- false, /* ROWQ_PRIO_REG_WRITE */
- false, /* ROWQ_PRIO_LOW_READ */
- false, /* ROWQ_PRIO_LOW_SWRITE */
+/**
+ * struct row_queue_params - ROW queue parameters
+ * @idling_enabled: Flag indicating whether idling is enable on
+ * the queue
+ * @quantum: Number of requests to be dispatched from this queue
+ * in a dispatch cycle
+ * @is_urgent: Flags indicating whether the queue can notify on
+ * urgent requests
+ *
+ */
+struct row_queue_params {
+ bool idling_enabled;
+ int quantum;
+ bool is_urgent;
};
-/* Flags indicating whether the queue can notify on urgent requests */
-static const bool urgent_queues[] = {
- true, /* ROWQ_PRIO_HIGH_READ */
- true, /* ROWQ_PRIO_REG_READ */
- false, /* ROWQ_PRIO_HIGH_SWRITE */
- false, /* ROWQ_PRIO_REG_SWRITE */
- false, /* ROWQ_PRIO_REG_WRITE */
- false, /* ROWQ_PRIO_LOW_READ */
- false, /* ROWQ_PRIO_LOW_SWRITE */
-};
-
-/* Default values for row queues quantums in each dispatch cycle */
-static const int queue_quantum[] = {
- 100, /* ROWQ_PRIO_HIGH_READ */
- 100, /* ROWQ_PRIO_REG_READ */
- 2, /* ROWQ_PRIO_HIGH_SWRITE */
- 1, /* ROWQ_PRIO_REG_SWRITE */
- 1, /* ROWQ_PRIO_REG_WRITE */
- 1, /* ROWQ_PRIO_LOW_READ */
- 1 /* ROWQ_PRIO_LOW_SWRITE */
+/*
+ * This array holds the default values of the different configurables
+ * for each ROW queue. Each row of the array holds the following values:
+ * {idling_enabled, quantum, is_urgent}
+ * Each row corresponds to a queue with the same index (according to
+ * enum row_queue_prio)
+ */
+static const struct row_queue_params row_queues_def[] = {
+/* idling_enabled, quantum, is_urgent */
+ {true, 100, true}, /* ROWQ_PRIO_HIGH_READ */
+ {true, 100, true}, /* ROWQ_PRIO_REG_READ */
+ {false, 2, false}, /* ROWQ_PRIO_HIGH_SWRITE */
+ {false, 1, false}, /* ROWQ_PRIO_REG_SWRITE */
+ {false, 1, false}, /* ROWQ_PRIO_REG_WRITE */
+ {false, 1, false}, /* ROWQ_PRIO_LOW_READ */
+ {false, 1, false} /* ROWQ_PRIO_LOW_SWRITE */
};
/* Default values for idling on read queues (in msec) */
@@ -104,6 +105,9 @@
* @nr_dispatched: number of requests already dispatched in
* the current dispatch cycle
* @slice: number of requests to dispatch in a cycle
+ * @nr_req: number of requests in queue
+ * @dispatch quantum: number of requests this queue may
+ * dispatch in a dispatch cycle
* @idle_data: data for idling on queues
*
*/
@@ -115,6 +119,9 @@
unsigned int nr_dispatched;
unsigned int slice;
+ unsigned int nr_req;
+ int disp_quantum;
+
/* used only for READ queues */
struct rowq_idling_data idle_data;
};
@@ -138,8 +145,7 @@
/**
* struct row_queue - Per block device rqueue structure
* @dispatch_queue: dispatch rqueue
- * @row_queues: array of priority request queues with
- * dispatch quantum per rqueue
+ * @row_queues: array of priority request queues
* @curr_queue: index in the row_queues array of the
* currently serviced rqueue
* @read_idle: data for idling after READ request
@@ -152,10 +158,7 @@
struct row_data {
struct request_queue *dispatch_queue;
- struct {
- struct row_queue rqueue;
- int disp_quantum;
- } row_queues[ROWQ_MAX_PRIO];
+ struct row_queue row_queues[ROWQ_MAX_PRIO];
enum row_queue_prio curr_queue;
@@ -191,6 +194,18 @@
return rd->cycle_flags & (1 << qnum);
}
+static inline void __maybe_unused row_dump_queues_stat(struct row_data *rd)
+{
+ int i;
+
+ row_log(rd->dispatch_queue, " Queues status:");
+ for (i = 0; i < ROWQ_MAX_PRIO; i++)
+ row_log(rd->dispatch_queue,
+ "queue%d: dispatched= %d, nr_req=%d", i,
+ rd->row_queues[i].nr_dispatched,
+ rd->row_queues[i].nr_req);
+}
+
/******************** Static helper functions ***********************/
/*
* kick_queue() - Wake up device driver queue thread
@@ -210,7 +225,7 @@
row_log_rowq(rd, rd->curr_queue, "Performing delayed work");
/* Mark idling process as done */
- rd->row_queues[rd->curr_queue].rqueue.idle_data.begin_idling = false;
+ rd->row_queues[rd->curr_queue].idle_data.begin_idling = false;
if (!(rd->nr_reqs[0] + rd->nr_reqs[1]))
row_log(rd->dispatch_queue, "No requests in scheduler");
@@ -235,7 +250,7 @@
int i;
for (i = 0; i < ROWQ_MAX_PRIO; i++)
- rd->row_queues[i].rqueue.nr_dispatched = 0;
+ rd->row_queues[i].nr_dispatched = 0;
rd->curr_queue = ROWQ_PRIO_HIGH_READ;
row_log(rd->dispatch_queue, "Restarting cycle");
@@ -264,9 +279,10 @@
list_add_tail(&rq->queuelist, &rqueue->fifo);
rd->nr_reqs[rq_data_dir(rq)]++;
+ rqueue->nr_req++;
rq_set_fifo_time(rq, jiffies); /* for statistics*/
- if (queue_idling_enabled[rqueue->prio]) {
+ if (row_queues_def[rqueue->prio].idling_enabled) {
if (delayed_work_pending(&rd->read_idle.idle_work))
(void)cancel_delayed_work(
&rd->read_idle.idle_work);
@@ -282,13 +298,14 @@
rqueue->idle_data.last_insert_time = ktime_get();
}
- if (urgent_queues[rqueue->prio] &&
+ if (row_queues_def[rqueue->prio].is_urgent &&
row_rowq_unserved(rd, rqueue->prio)) {
row_log_rowq(rd, rqueue->prio,
- "added urgent req curr_queue = %d",
- rd->curr_queue);
+ "added urgent request (total on queue=%d)",
+ rqueue->nr_req);
} else
- row_log_rowq(rd, rqueue->prio, "added request");
+ row_log_rowq(rd, rqueue->prio,
+ "added request (total on queue=%d)", rqueue->nr_req);
}
/**
@@ -317,8 +334,10 @@
list_add(&rq->queuelist, &rqueue->fifo);
rd->nr_reqs[rq_data_dir(rq)]++;
+ rqueue->nr_req++;
- row_log_rowq(rd, rqueue->prio, "request reinserted");
+ row_log_rowq(rd, rqueue->prio,
+ "request reinserted (total on queue=%d)", rqueue->nr_req);
return 0;
}
@@ -334,8 +353,8 @@
int i;
for (i = 0; i < ROWQ_MAX_PRIO; i++)
- if (urgent_queues[i] && row_rowq_unserved(rd, i) &&
- !list_empty(&rd->row_queues[i].rqueue.fifo)) {
+ if (row_queues_def[i].is_urgent && row_rowq_unserved(rd, i) &&
+ !list_empty(&rd->row_queues[i].fifo)) {
row_log_rowq(rd, i,
"Urgent request pending (curr=%i)",
rd->curr_queue);
@@ -355,8 +374,10 @@
struct request *rq)
{
struct row_data *rd = (struct row_data *)q->elevator->elevator_data;
+ struct row_queue *rqueue = RQ_ROWQ(rq);
rq_fifo_clear(rq);
+ rqueue->nr_req--;
rd->nr_reqs[rq_data_dir(rq)]--;
}
@@ -372,13 +393,13 @@
{
struct request *rq;
- rq = rq_entry_fifo(rd->row_queues[rd->curr_queue].rqueue.fifo.next);
+ rq = rq_entry_fifo(rd->row_queues[rd->curr_queue].fifo.next);
row_remove_request(rd->dispatch_queue, rq);
elv_dispatch_add_tail(rd->dispatch_queue, rq);
- rd->row_queues[rd->curr_queue].rqueue.nr_dispatched++;
+ rd->row_queues[rd->curr_queue].nr_dispatched++;
row_clear_rowq_unserved(rd, rd->curr_queue);
row_log_rowq(rd, rd->curr_queue, " Dispatched request nr_disp = %d",
- rd->row_queues[rd->curr_queue].rqueue.nr_dispatched);
+ rd->row_queues[rd->curr_queue].nr_dispatched);
}
/*
@@ -404,7 +425,7 @@
* Loop over all queues to find the next queue that is not empty.
* Stop when you get back to curr_queue
*/
- while (list_empty(&rd->row_queues[rd->curr_queue].rqueue.fifo)
+ while (list_empty(&rd->row_queues[rd->curr_queue].fifo)
&& rd->curr_queue != prev_curr_queue) {
/* Mark rqueue as unserved */
row_mark_rowq_unserved(rd, rd->curr_queue);
@@ -436,9 +457,10 @@
*/
for (i = 0; i < currq; i++) {
if (row_rowq_unserved(rd, i) &&
- !list_empty(&rd->row_queues[i].rqueue.fifo)) {
+ !list_empty(&rd->row_queues[i].fifo)) {
row_log_rowq(rd, currq,
- " Preemting for unserved rowq%d", i);
+ " Preemting for unserved rowq%d. (nr_req=%u)",
+ i, rd->row_queues[currq].nr_req);
rd->curr_queue = i;
row_dispatch_insert(rd);
ret = 1;
@@ -446,9 +468,9 @@
}
}
- if (rd->row_queues[currq].rqueue.nr_dispatched >=
+ if (rd->row_queues[currq].nr_dispatched >=
rd->row_queues[currq].disp_quantum) {
- rd->row_queues[currq].rqueue.nr_dispatched = 0;
+ rd->row_queues[currq].nr_dispatched = 0;
row_log_rowq(rd, currq, "Expiring rqueue");
ret = row_choose_queue(rd);
if (ret)
@@ -457,7 +479,7 @@
}
/* Dispatch from curr_queue */
- if (list_empty(&rd->row_queues[currq].rqueue.fifo)) {
+ if (list_empty(&rd->row_queues[currq].fifo)) {
/* check idling */
if (delayed_work_pending(&rd->read_idle.idle_work)) {
if (force) {
@@ -472,8 +494,8 @@
}
}
- if (!force && queue_idling_enabled[currq] &&
- rd->row_queues[currq].rqueue.idle_data.begin_idling) {
+ if (!force && row_queues_def[currq].idling_enabled &&
+ rd->row_queues[currq].idle_data.begin_idling) {
if (!queue_delayed_work(rd->read_idle.idle_workqueue,
&rd->read_idle.idle_work,
rd->read_idle.idle_time)) {
@@ -520,12 +542,12 @@
return NULL;
for (i = 0; i < ROWQ_MAX_PRIO; i++) {
- INIT_LIST_HEAD(&rdata->row_queues[i].rqueue.fifo);
- rdata->row_queues[i].disp_quantum = queue_quantum[i];
- rdata->row_queues[i].rqueue.rdata = rdata;
- rdata->row_queues[i].rqueue.prio = i;
- rdata->row_queues[i].rqueue.idle_data.begin_idling = false;
- rdata->row_queues[i].rqueue.idle_data.last_insert_time =
+ INIT_LIST_HEAD(&rdata->row_queues[i].fifo);
+ rdata->row_queues[i].disp_quantum = row_queues_def[i].quantum;
+ rdata->row_queues[i].rdata = rdata;
+ rdata->row_queues[i].prio = i;
+ rdata->row_queues[i].idle_data.begin_idling = false;
+ rdata->row_queues[i].idle_data.last_insert_time =
ktime_set(0, 0);
}
@@ -564,7 +586,7 @@
int i;
for (i = 0; i < ROWQ_MAX_PRIO; i++)
- BUG_ON(!list_empty(&rd->row_queues[i].rqueue.fifo));
+ BUG_ON(!list_empty(&rd->row_queues[i].fifo));
(void)cancel_delayed_work_sync(&rd->read_idle.idle_work);
BUG_ON(delayed_work_pending(&rd->read_idle.idle_work));
destroy_workqueue(rd->read_idle.idle_workqueue);
@@ -583,6 +605,7 @@
struct row_queue *rqueue = RQ_ROWQ(next);
list_del_init(&next->queuelist);
+ rqueue->nr_req--;
rqueue->rdata->nr_reqs[rq_data_dir(rq)]--;
}
@@ -668,7 +691,7 @@
rowd->row_queues[ROWQ_PRIO_LOW_READ].disp_quantum, 0);
SHOW_FUNCTION(row_lp_swrite_quantum_show,
rowd->row_queues[ROWQ_PRIO_LOW_SWRITE].disp_quantum, 0);
-SHOW_FUNCTION(row_read_idle_show, rowd->read_idle.idle_time, 1);
+SHOW_FUNCTION(row_read_idle_show, rowd->read_idle.idle_time, 0);
SHOW_FUNCTION(row_read_idle_freq_show, rowd->read_idle.freq, 0);
#undef SHOW_FUNCTION
@@ -708,7 +731,7 @@
STORE_FUNCTION(row_lp_swrite_quantum_store,
&rowd->row_queues[ROWQ_PRIO_LOW_SWRITE].disp_quantum,
1, INT_MAX, 1);
-STORE_FUNCTION(row_read_idle_store, &rowd->read_idle.idle_time, 1, INT_MAX, 1);
+STORE_FUNCTION(row_read_idle_store, &rowd->read_idle.idle_time, 1, INT_MAX, 0);
STORE_FUNCTION(row_read_idle_freq_store, &rowd->read_idle.freq, 1, INT_MAX, 0);
#undef STORE_FUNCTION
diff --git a/block/test-iosched.c b/block/test-iosched.c
index d2716c84..3b3d485 100644
--- a/block/test-iosched.c
+++ b/block/test-iosched.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2012-2013, Code Aurora Forum. 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
@@ -44,7 +44,6 @@
static struct test_data *ptd;
-
/**
* test_iosched_get_req_queue() - returns the request queue
* served by the scheduler
@@ -76,17 +75,20 @@
}
EXPORT_SYMBOL(test_iosched_mark_test_completion);
-/* Check if all the queued test requests were completed */
-static void check_test_completion(void)
+/**
+ * check_test_completion() - Check if all the queued test
+ * requests were completed
+ */
+void check_test_completion(void)
{
struct test_request *test_rq;
if (!ptd)
- return;
+ goto exit;
list_for_each_entry(test_rq, &ptd->dispatched_queue, queuelist)
if (!test_rq->req_completed)
- return;
+ goto exit;
if (!list_empty(&ptd->test_queue)
|| !list_empty(&ptd->reinsert_queue)
@@ -96,7 +98,7 @@
__func__, ptd->test_count, ptd->reinsert_count);
test_pr_info("%s: dispatched_count=%d, urgent_count=%d",
__func__, ptd->dispatched_count, ptd->urgent_count);
- return;
+ goto exit;
}
ptd->test_info.test_duration = jiffies -
@@ -108,7 +110,11 @@
__func__, ptd->dispatched_count);
test_iosched_mark_test_completion();
+
+exit:
+ return;
}
+EXPORT_SYMBOL(check_test_completion);
/*
* A callback to be called per bio completion.
@@ -348,7 +354,7 @@
rq->end_io = end_test_req;
rq->__sector = start_sec;
rq->cmd_type |= REQ_TYPE_FS;
- rq->cmd_flags |= REQ_SORTED; /* do we need this?*/
+ rq->cmd_flags |= REQ_SORTED;
if (rq->bio) {
rq->bio->bi_sector = start_sec;
@@ -364,6 +370,8 @@
test_rq->req_completed = false;
test_rq->req_result = -EINVAL;
test_rq->rq = rq;
+ if (ptd->test_info.get_rq_disk_fn)
+ test_rq->rq->rq_disk = ptd->test_info.get_rq_disk_fn();
test_rq->is_err_expected = is_err_expcted;
rq->elv.priv[0] = (void *)test_rq;
@@ -682,6 +690,8 @@
/**
* test_iosched_start_test() - Prepares and runs the test.
+ * The members test_duration and test_byte_count of the input
+ * parameter t_info are modified by this function.
* @t_info: the current test testcase and callbacks
* functions
*
@@ -770,6 +780,7 @@
wait_event(ptd->wait_q, ptd->test_state == TEST_COMPLETED);
t_info->test_duration = ptd->test_info.test_duration;
+ t_info->test_byte_count = ptd->test_info.test_byte_count;
del_timer_sync(&ptd->timeout_timer);
ret = check_test_result(ptd);
@@ -1004,6 +1015,7 @@
print_req(rq);
elv_dispatch_sort(q, rq);
+ ptd->test_info.test_byte_count += test_rq->buf_size;
ret = 1;
goto err;
}
diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig
index 6bdedd7..83ef121 100644
--- a/drivers/ata/Kconfig
+++ b/drivers/ata/Kconfig
@@ -83,6 +83,17 @@
If unsure, say N.
+config SATA_AHCI_MSM
+ tristate "Qualcomm MSM AHCI SATA support"
+ depends on ARCH_MSM
+ select SATA_AHCI_PLATFORM
+ help
+ This option enables support for AHCI SATA controller
+ integrated into Qualcomm MSM chipsets. For more
+ information please refer to http://www.qualcomm.com/chipsets.
+
+ If unsure, say N.
+
config SATA_FSL
tristate "Freescale 3.0Gbps SATA support"
depends on FSL_SOC
diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile
index 6ece5b7..bc40152 100644
--- a/drivers/ata/Makefile
+++ b/drivers/ata/Makefile
@@ -9,6 +9,7 @@
obj-$(CONFIG_SATA_INIC162X) += sata_inic162x.o
obj-$(CONFIG_SATA_SIL24) += sata_sil24.o
obj-$(CONFIG_SATA_DWC) += sata_dwc_460ex.o
+obj-$(CONFIG_SATA_AHCI_MSM) += ahci_msm.o
# SFF w/ custom DMA
obj-$(CONFIG_PDC_ADMA) += pdc_adma.o
diff --git a/drivers/ata/ahci_msm.c b/drivers/ata/ahci_msm.c
new file mode 100644
index 0000000..8536040
--- /dev/null
+++ b/drivers/ata/ahci_msm.c
@@ -0,0 +1,778 @@
+/*
+ * 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
+ * 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.
+ */
+
+/*
+ * SATA init module.
+ * To be used with SATA interface on MSM targets.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/iopoll.h>
+#include <linux/regulator/consumer.h>
+#include <linux/ahci_platform.h>
+#include <mach/clk.h>
+
+/* PHY registers */
+#define UNIPHY_PLL_REFCLK_CFG 0x000
+#define UNIPHY_PLL_POSTDIV1_CFG 0x004
+#define UNIPHY_PLL_CHGPUMP_CFG 0x008
+#define UNIPHY_PLL_VCOLPF_CFG 0x00C
+#define UNIPHY_PLL_VREG_CFG 0x010
+#define UNIPHY_PLL_PWRGEN_CFG 0x014
+#define UNIPHY_PLL_DMUX_CFG 0x018
+#define UNIPHY_PLL_AMUX_CFG 0x01C
+#define UNIPHY_PLL_GLB_CFG 0x020
+#define UNIPHY_PLL_POSTDIV2_CFG 0x024
+#define UNIPHY_PLL_POSTDIV3_CFG 0x028
+#define UNIPHY_PLL_LPFR_CFG 0x02C
+#define UNIPHY_PLL_LPFC1_CFG 0x030
+#define UNIPHY_PLL_LPFC2_CFG 0x034
+#define UNIPHY_PLL_SDM_CFG0 0x038
+#define UNIPHY_PLL_SDM_CFG1 0x03C
+#define UNIPHY_PLL_SDM_CFG2 0x040
+#define UNIPHY_PLL_SDM_CFG3 0x044
+#define UNIPHY_PLL_SDM_CFG4 0x048
+#define UNIPHY_PLL_SSC_CFG0 0x04C
+#define UNIPHY_PLL_SSC_CFG1 0x050
+#define UNIPHY_PLL_SSC_CFG2 0x054
+#define UNIPHY_PLL_SSC_CFG3 0x058
+#define UNIPHY_PLL_LKDET_CFG0 0x05C
+#define UNIPHY_PLL_LKDET_CFG1 0x060
+#define UNIPHY_PLL_LKDET_CFG2 0x064
+#define UNIPHY_PLL_TEST_CFG 0x068
+#define UNIPHY_PLL_CAL_CFG0 0x06C
+#define UNIPHY_PLL_CAL_CFG1 0x070
+#define UNIPHY_PLL_CAL_CFG2 0x074
+#define UNIPHY_PLL_CAL_CFG3 0x078
+#define UNIPHY_PLL_CAL_CFG4 0x07C
+#define UNIPHY_PLL_CAL_CFG5 0x080
+#define UNIPHY_PLL_CAL_CFG6 0x084
+#define UNIPHY_PLL_CAL_CFG7 0x088
+#define UNIPHY_PLL_CAL_CFG8 0x08C
+#define UNIPHY_PLL_CAL_CFG9 0x090
+#define UNIPHY_PLL_CAL_CFG10 0x094
+#define UNIPHY_PLL_CAL_CFG11 0x098
+#define UNIPHY_PLL_EFUSE_CFG 0x09C
+#define UNIPHY_PLL_DEBUG_BUS_SEL 0x0A0
+#define UNIPHY_PLL_CTRL_42 0x0A4
+#define UNIPHY_PLL_CTRL_43 0x0A8
+#define UNIPHY_PLL_CTRL_44 0x0AC
+#define UNIPHY_PLL_CTRL_45 0x0B0
+#define UNIPHY_PLL_CTRL_46 0x0B4
+#define UNIPHY_PLL_CTRL_47 0x0B8
+#define UNIPHY_PLL_CTRL_48 0x0BC
+#define UNIPHY_PLL_STATUS 0x0C0
+#define UNIPHY_PLL_DEBUG_BUS0 0x0C4
+#define UNIPHY_PLL_DEBUG_BUS1 0x0C8
+#define UNIPHY_PLL_DEBUG_BUS2 0x0CC
+#define UNIPHY_PLL_DEBUG_BUS3 0x0D0
+#define UNIPHY_PLL_CTRL_54 0x0D4
+
+#define SATA_PHY_SER_CTRL 0x100
+#define SATA_PHY_TX_DRIV_CTRL0 0x104
+#define SATA_PHY_TX_DRIV_CTRL1 0x108
+#define SATA_PHY_TX_DRIV_CTRL2 0x10C
+#define SATA_PHY_TX_DRIV_CTRL3 0x110
+#define SATA_PHY_TX_RESV0 0x114
+#define SATA_PHY_TX_RESV1 0x118
+#define SATA_PHY_TX_IMCAL0 0x11C
+#define SATA_PHY_TX_IMCAL1 0x120
+#define SATA_PHY_TX_IMCAL2 0x124
+#define SATA_PHY_RX_IMCAL0 0x128
+#define SATA_PHY_RX_IMCAL1 0x12C
+#define SATA_PHY_RX_IMCAL2 0x130
+#define SATA_PHY_RX_TERM 0x134
+#define SATA_PHY_RX_TERM_RESV 0x138
+#define SATA_PHY_EQUAL 0x13C
+#define SATA_PHY_EQUAL_RESV 0x140
+#define SATA_PHY_OOB_TERM 0x144
+#define SATA_PHY_CDR_CTRL0 0x148
+#define SATA_PHY_CDR_CTRL1 0x14C
+#define SATA_PHY_CDR_CTRL2 0x150
+#define SATA_PHY_CDR_CTRL3 0x154
+#define SATA_PHY_CDR_CTRL4 0x158
+#define SATA_PHY_FA_LOAD0 0x15C
+#define SATA_PHY_FA_LOAD1 0x160
+#define SATA_PHY_CDR_CTRL_RESV 0x164
+#define SATA_PHY_PI_CTRL0 0x168
+#define SATA_PHY_PI_CTRL1 0x16C
+#define SATA_PHY_DESER_RESV 0x170
+#define SATA_PHY_RX_RESV0 0x174
+#define SATA_PHY_AD_TPA_CTRL 0x178
+#define SATA_PHY_REFCLK_CTRL 0x17C
+#define SATA_PHY_POW_DWN_CTRL0 0x180
+#define SATA_PHY_POW_DWN_CTRL1 0x184
+#define SATA_PHY_TX_DATA_CTRL 0x188
+#define SATA_PHY_BIST_GEN0 0x18C
+#define SATA_PHY_BIST_GEN1 0x190
+#define SATA_PHY_BIST_GEN2 0x194
+#define SATA_PHY_BIST_GEN3 0x198
+#define SATA_PHY_LBK_CTRL 0x19C
+#define SATA_PHY_TEST_DEBUG_CTRL 0x1A0
+#define SATA_PHY_ALIGNP 0x1A4
+#define SATA_PHY_PRBS_CFG0 0x1A8
+#define SATA_PHY_PRBS_CFG1 0x1AC
+#define SATA_PHY_PRBS_CFG2 0x1B0
+#define SATA_PHY_PRBS_CFG3 0x1B4
+#define SATA_PHY_CHAN_COMP_CHK_CNT 0x1B8
+#define SATA_PHY_RESET_CTRL 0x1BC
+#define SATA_PHY_RX_CLR 0x1C0
+#define SATA_PHY_RX_EBUF_CTRL 0x1C4
+#define SATA_PHY_ID0 0x1C8
+#define SATA_PHY_ID1 0x1CC
+#define SATA_PHY_ID2 0x1D0
+#define SATA_PHY_ID3 0x1D4
+#define SATA_PHY_RX_CHK_ERR_CNT0 0x1D8
+#define SATA_PHY_RX_CHK_ERR_CNT1 0x1DC
+#define SATA_PHY_RX_CHK_STAT 0x1E0
+#define SATA_PHY_TX_IMCAL_STAT 0x1E4
+#define SATA_PHY_RX_IMCAL_STAT 0x1E8
+#define SATA_PHY_RX_EBUF_STAT 0x1EC
+#define SATA_PHY_DEBUG_BUS_STAT0 0x1F0
+#define SATA_PHY_DEBUG_BUS_STAT1 0x1F4
+#define SATA_PHY_DEBUG_BUS_STAT2 0x1F8
+#define SATA_PHY_DEBUG_BUS_STAT3 0x1FC
+
+#define AHCI_HOST_CAP 0x00
+#define AHCI_HOST_CAP_MASK 0x1F
+#define AHCI_HOST_CAP_PMP (1 << 17)
+
+struct msm_sata_hba {
+ struct platform_device *ahci_pdev;
+ struct clk *slave_iface_clk;
+ struct clk *bus_clk;
+ struct clk *iface_clk;
+ struct clk *src_clk;
+ struct clk *rxoob_clk;
+ struct clk *pmalive_clk;
+ struct clk *cfg_clk;
+ struct regulator *clk_pwr;
+ struct regulator *pmp_pwr;
+ void __iomem *phy_base;
+ void __iomem *ahci_base;
+};
+
+static inline void msm_sata_delay_us(unsigned int delay)
+{
+ /* sleep for max. 50us more to combine processor wakeups */
+ usleep_range(delay, delay + 50);
+}
+
+static int msm_sata_clk_get_prepare_enable_set_rate(struct device *dev,
+ const char *name, struct clk **out_clk, int rate)
+{
+ int ret = 0;
+ struct clk *clk;
+
+ clk = devm_clk_get(dev, name);
+ if (IS_ERR(clk)) {
+ ret = PTR_ERR(clk);
+ dev_err(dev, "failed to get clk: %s err = %d\n", name, ret);
+ goto out;
+ }
+
+ if (rate >= 0) {
+ ret = clk_set_rate(clk, rate);
+ if (ret) {
+ dev_err(dev, "failed to set rate: %d clk: %s err = %d\n",
+ rate, name, ret);
+ goto out;
+ }
+ }
+
+ ret = clk_prepare_enable(clk);
+ if (ret)
+ dev_err(dev, "failed to enable clk: %s err = %d\n", name, ret);
+out:
+ if (!ret)
+ *out_clk = clk;
+
+ return ret;
+}
+
+static int msm_sata_clk_get_prepare_enable(struct device *dev,
+ const char *name, struct clk **out_clk)
+{
+ return msm_sata_clk_get_prepare_enable_set_rate(dev, name, out_clk, -1);
+}
+
+static void msm_sata_clk_put_unprepare_disable(struct clk **clk)
+{
+ if (*clk) {
+ clk_disable_unprepare(*clk);
+ clk_put(*clk);
+ *clk = NULL;
+ }
+}
+
+static int msm_sata_hard_reset(struct device *dev)
+{
+ int ret;
+ struct msm_sata_hba *hba = dev_get_drvdata(dev);
+
+ ret = clk_reset(hba->iface_clk, CLK_RESET_ASSERT);
+ if (ret) {
+ dev_err(dev, "iface_clk assert failed %d\n", ret);
+ goto out;
+ }
+
+ ret = clk_reset(hba->iface_clk, CLK_RESET_DEASSERT);
+ if (ret) {
+ dev_err(dev, "iface_clk de-assert failed %d\n", ret);
+ goto out;
+ }
+out:
+ return ret;
+}
+
+static int msm_sata_clk_init(struct device *dev)
+{
+ int ret = 0;
+ struct msm_sata_hba *hba = dev_get_drvdata(dev);
+
+ /* Enable AHB clock for system fabric slave port connected to SATA */
+ ret = msm_sata_clk_get_prepare_enable(dev,
+ "slave_iface_clk", &hba->slave_iface_clk);
+ if (ret)
+ goto out;
+
+ /* Enable AHB clock for system fabric and SATA core interface */
+ ret = msm_sata_clk_get_prepare_enable(dev,
+ "iface_clk", &hba->iface_clk);
+ if (ret)
+ goto put_dis_slave_iface_clk;
+
+ /* Enable AXI clock for SATA AXI master and slave interfaces */
+ ret = msm_sata_clk_get_prepare_enable(dev,
+ "bus_clk", &hba->bus_clk);
+ if (ret)
+ goto put_dis_iface_clk;
+
+ /* Enable the source clock for pmalive, rxoob and phy ref clocks */
+ ret = msm_sata_clk_get_prepare_enable_set_rate(dev,
+ "src_clk", &hba->src_clk, 100000000);
+ if (ret)
+ goto put_dis_bus_clk;
+
+ /*
+ * Enable RX OOB detection clock. The clock rate is
+ * same as PHY reference clock (100MHz).
+ */
+ ret = msm_sata_clk_get_prepare_enable(dev,
+ "core_rxoob_clk", &hba->rxoob_clk);
+ if (ret)
+ goto put_dis_src_clk;
+
+ /*
+ * Enable power management always-on clock. The clock rate
+ * is same as PHY reference clock (100MHz).
+ */
+ ret = msm_sata_clk_get_prepare_enable(dev,
+ "core_pmalive_clk", &hba->pmalive_clk);
+ if (ret)
+ goto put_dis_rxoob_clk;
+
+ /* Enable PHY configuration AHB clock, fixed 64MHz clock */
+ ret = msm_sata_clk_get_prepare_enable(dev,
+ "cfg_clk", &hba->cfg_clk);
+ if (ret)
+ goto put_dis_pmalive_clk;
+
+ return ret;
+
+put_dis_pmalive_clk:
+ msm_sata_clk_put_unprepare_disable(&hba->pmalive_clk);
+put_dis_rxoob_clk:
+ msm_sata_clk_put_unprepare_disable(&hba->rxoob_clk);
+put_dis_src_clk:
+ msm_sata_clk_put_unprepare_disable(&hba->src_clk);
+put_dis_bus_clk:
+ msm_sata_clk_put_unprepare_disable(&hba->bus_clk);
+put_dis_iface_clk:
+ msm_sata_clk_put_unprepare_disable(&hba->iface_clk);
+put_dis_slave_iface_clk:
+ msm_sata_clk_put_unprepare_disable(&hba->slave_iface_clk);
+out:
+ return ret;
+}
+
+static void msm_sata_clk_deinit(struct device *dev)
+{
+ struct msm_sata_hba *hba = dev_get_drvdata(dev);
+
+ msm_sata_clk_put_unprepare_disable(&hba->cfg_clk);
+ msm_sata_clk_put_unprepare_disable(&hba->pmalive_clk);
+ msm_sata_clk_put_unprepare_disable(&hba->rxoob_clk);
+ msm_sata_clk_put_unprepare_disable(&hba->src_clk);
+ msm_sata_clk_put_unprepare_disable(&hba->bus_clk);
+ msm_sata_clk_put_unprepare_disable(&hba->iface_clk);
+ msm_sata_clk_put_unprepare_disable(&hba->slave_iface_clk);
+}
+
+static int msm_sata_vreg_get_enable_set_vdd(struct device *dev,
+ const char *name, struct regulator **out_vreg,
+ int min_uV, int max_uV, int hpm_uA)
+{
+ int ret = 0;
+ struct regulator *vreg;
+
+ vreg = devm_regulator_get(dev, name);
+ if (IS_ERR(vreg)) {
+ ret = PTR_ERR(vreg);
+ dev_err(dev, "Regulator: %s get failed, err=%d\n", name, ret);
+ goto out;
+ }
+
+ if (regulator_count_voltages(vreg) > 0) {
+ ret = regulator_set_voltage(vreg, min_uV, max_uV);
+ if (ret) {
+ dev_err(dev, "Regulator: %s set voltage failed, err=%d\n",
+ name, ret);
+ goto err;
+ }
+
+ ret = regulator_set_optimum_mode(vreg, hpm_uA);
+ if (ret < 0) {
+ dev_err(dev, "Regulator: %s set optimum mode(uA_load=%d) failed, err=%d\n",
+ name, hpm_uA, ret);
+ goto err;
+ } else {
+ /*
+ * regulator_set_optimum_mode() can return non zero
+ * value even for success case.
+ */
+ ret = 0;
+ }
+ }
+
+ ret = regulator_enable(vreg);
+ if (ret)
+ dev_err(dev, "Regulator: %s enable failed, err=%d\n",
+ name, ret);
+err:
+ if (!ret)
+ *out_vreg = vreg;
+ else
+ devm_regulator_put(vreg);
+out:
+ return ret;
+}
+
+static int msm_sata_vreg_put_disable(struct device *dev,
+ struct regulator *reg, const char *name, int max_uV)
+{
+ int ret;
+
+ if (!reg)
+ return 0;
+
+ ret = regulator_disable(reg);
+ if (ret) {
+ dev_err(dev, "Regulator: %s disable failed err=%d\n",
+ name, ret);
+ goto err;
+ }
+
+ if (regulator_count_voltages(reg) > 0) {
+ ret = regulator_set_voltage(reg, 0, max_uV);
+ if (ret < 0) {
+ dev_err(dev, "Regulator: %s set voltage to 0 failed, err=%d\n",
+ name, ret);
+ goto err;
+ }
+
+ ret = regulator_set_optimum_mode(reg, 0);
+ if (ret < 0) {
+ dev_err(dev, "Regulator: %s set optimum mode(uA_load = 0) failed, err=%d\n",
+ name, ret);
+ goto err;
+ } else {
+ /*
+ * regulator_set_optimum_mode() can return non zero
+ * value even for success case.
+ */
+ ret = 0;
+ }
+ }
+
+err:
+ devm_regulator_put(reg);
+ return ret;
+}
+
+static int msm_sata_vreg_init(struct device *dev)
+{
+ int ret = 0;
+ struct msm_sata_hba *hba = dev_get_drvdata(dev);
+
+ /*
+ * The SATA clock generator needs 3.3V supply and can consume
+ * max. 850mA during functional mode.
+ */
+ ret = msm_sata_vreg_get_enable_set_vdd(dev, "sata_ext_3p3v",
+ &hba->clk_pwr, 3300000, 3300000, 850000);
+ if (ret)
+ goto out;
+
+ /* Add 1ms regulator ramp-up delay */
+ msm_sata_delay_us(1000);
+
+ /* Read AHCI capability register to check if PMP is supported.*/
+ if (readl_relaxed(hba->ahci_base +
+ AHCI_HOST_CAP) & AHCI_HOST_CAP_PMP) {
+ /* Power up port-multiplier */
+ ret = msm_sata_vreg_get_enable_set_vdd(dev, "sata_pmp_pwr",
+ &hba->pmp_pwr, 1800000, 1800000, 200000);
+ if (ret) {
+ msm_sata_vreg_put_disable(dev, hba->clk_pwr,
+ "sata_ext_3p3v", 3300000);
+ goto out;
+ }
+
+ /* Add 1ms regulator ramp-up delay */
+ msm_sata_delay_us(1000);
+ }
+
+out:
+ return ret;
+}
+
+static void msm_sata_vreg_deinit(struct device *dev)
+{
+ struct msm_sata_hba *hba = dev_get_drvdata(dev);
+
+ msm_sata_vreg_put_disable(dev, hba->clk_pwr,
+ "sata_ext_3p3v", 3300000);
+
+ if (hba->pmp_pwr)
+ msm_sata_vreg_put_disable(dev, hba->pmp_pwr,
+ "sata_pmp_pwr", 1800000);
+}
+
+static void msm_sata_phy_deinit(struct device *dev)
+{
+ struct msm_sata_hba *hba = dev_get_drvdata(dev);
+
+ /* Power down PHY */
+ writel_relaxed(0xF8, hba->phy_base + SATA_PHY_POW_DWN_CTRL0);
+ writel_relaxed(0xFE, hba->phy_base + SATA_PHY_POW_DWN_CTRL1);
+
+ /* Power down PLL block */
+ writel_relaxed(0x00, hba->phy_base + UNIPHY_PLL_GLB_CFG);
+ mb();
+
+ devm_iounmap(dev, hba->phy_base);
+}
+
+static int msm_sata_phy_init(struct device *dev)
+{
+ int ret = 0;
+ u32 reg = 0;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct msm_sata_hba *hba = dev_get_drvdata(dev);
+ struct resource *mem;
+
+ mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy_mem");
+ if (!mem) {
+ dev_err(dev, "no mmio space\n");
+ return -EINVAL;
+ }
+
+ hba->phy_base = devm_ioremap(dev, mem->start, resource_size(mem));
+ if (!hba->phy_base) {
+ dev_err(dev, "failed to allocate memory for SATA PHY\n");
+ return -ENOMEM;
+ }
+
+ /* SATA phy initialization */
+
+ writel_relaxed(0x01, hba->phy_base + SATA_PHY_SER_CTRL);
+
+ writel_relaxed(0xB1, hba->phy_base + SATA_PHY_POW_DWN_CTRL0);
+ mb();
+ msm_sata_delay_us(10);
+
+ writel_relaxed(0x01, hba->phy_base + SATA_PHY_POW_DWN_CTRL0);
+ writel_relaxed(0x3E, hba->phy_base + SATA_PHY_POW_DWN_CTRL1);
+ writel_relaxed(0x01, hba->phy_base + SATA_PHY_RX_IMCAL0);
+ writel_relaxed(0x01, hba->phy_base + SATA_PHY_TX_IMCAL0);
+ writel_relaxed(0x02, hba->phy_base + SATA_PHY_TX_IMCAL2);
+
+ /* Write UNIPHYPLL registers to configure PLL */
+ writel_relaxed(0x04, hba->phy_base + UNIPHY_PLL_REFCLK_CFG);
+ writel_relaxed(0x00, hba->phy_base + UNIPHY_PLL_PWRGEN_CFG);
+
+ writel_relaxed(0x0A, hba->phy_base + UNIPHY_PLL_CAL_CFG0);
+ writel_relaxed(0xF3, hba->phy_base + UNIPHY_PLL_CAL_CFG8);
+ writel_relaxed(0x01, hba->phy_base + UNIPHY_PLL_CAL_CFG9);
+ writel_relaxed(0xED, hba->phy_base + UNIPHY_PLL_CAL_CFG10);
+ writel_relaxed(0x02, hba->phy_base + UNIPHY_PLL_CAL_CFG11);
+
+ writel_relaxed(0x36, hba->phy_base + UNIPHY_PLL_SDM_CFG0);
+ writel_relaxed(0x0D, hba->phy_base + UNIPHY_PLL_SDM_CFG1);
+ writel_relaxed(0xA3, hba->phy_base + UNIPHY_PLL_SDM_CFG2);
+ writel_relaxed(0xF0, hba->phy_base + UNIPHY_PLL_SDM_CFG3);
+ writel_relaxed(0x00, hba->phy_base + UNIPHY_PLL_SDM_CFG4);
+
+ writel_relaxed(0x19, hba->phy_base + UNIPHY_PLL_SSC_CFG0);
+ writel_relaxed(0xE1, hba->phy_base + UNIPHY_PLL_SSC_CFG1);
+ writel_relaxed(0x00, hba->phy_base + UNIPHY_PLL_SSC_CFG2);
+ writel_relaxed(0x11, hba->phy_base + UNIPHY_PLL_SSC_CFG3);
+
+ writel_relaxed(0x04, hba->phy_base + UNIPHY_PLL_LKDET_CFG0);
+ writel_relaxed(0xFF, hba->phy_base + UNIPHY_PLL_LKDET_CFG1);
+
+ writel_relaxed(0x02, hba->phy_base + UNIPHY_PLL_GLB_CFG);
+ mb();
+ msm_sata_delay_us(40);
+
+ writel_relaxed(0x03, hba->phy_base + UNIPHY_PLL_GLB_CFG);
+ mb();
+ msm_sata_delay_us(400);
+
+ writel_relaxed(0x05, hba->phy_base + UNIPHY_PLL_LKDET_CFG2);
+ mb();
+
+ /* poll for ready status, timeout after 1 sec */
+ ret = readl_poll_timeout(hba->phy_base + UNIPHY_PLL_STATUS, reg,
+ (reg & 1 << 0), 100, 1000000);
+ if (ret) {
+ dev_err(dev, "poll timeout UNIPHY_PLL_STATUS\n");
+ goto out;
+ }
+
+ ret = readl_poll_timeout(hba->phy_base + SATA_PHY_TX_IMCAL_STAT, reg,
+ (reg & 1 << 0), 100, 1000000);
+ if (ret) {
+ dev_err(dev, "poll timeout SATA_PHY_TX_IMCAL_STAT\n");
+ goto out;
+ }
+
+ ret = readl_poll_timeout(hba->phy_base + SATA_PHY_RX_IMCAL_STAT, reg,
+ (reg & 1 << 0), 100, 1000000);
+ if (ret) {
+ dev_err(dev, "poll timeout SATA_PHY_RX_IMCAL_STAT\n");
+ goto out;
+ }
+
+ /* SATA phy calibrated succesfully, power up to functional mode */
+ writel_relaxed(0x3E, hba->phy_base + SATA_PHY_POW_DWN_CTRL1);
+ writel_relaxed(0x01, hba->phy_base + SATA_PHY_RX_IMCAL0);
+ writel_relaxed(0x01, hba->phy_base + SATA_PHY_TX_IMCAL0);
+
+ writel_relaxed(0x00, hba->phy_base + SATA_PHY_POW_DWN_CTRL1);
+ writel_relaxed(0x59, hba->phy_base + SATA_PHY_CDR_CTRL0);
+ writel_relaxed(0x04, hba->phy_base + SATA_PHY_CDR_CTRL1);
+ writel_relaxed(0x00, hba->phy_base + SATA_PHY_CDR_CTRL2);
+ writel_relaxed(0x00, hba->phy_base + SATA_PHY_PI_CTRL0);
+ writel_relaxed(0x00, hba->phy_base + SATA_PHY_CDR_CTRL3);
+ writel_relaxed(0x01, hba->phy_base + SATA_PHY_POW_DWN_CTRL0);
+
+ writel_relaxed(0x11, hba->phy_base + SATA_PHY_TX_DATA_CTRL);
+ writel_relaxed(0x43, hba->phy_base + SATA_PHY_ALIGNP);
+ writel_relaxed(0x04, hba->phy_base + SATA_PHY_OOB_TERM);
+
+ writel_relaxed(0x01, hba->phy_base + SATA_PHY_EQUAL);
+ writel_relaxed(0x09, hba->phy_base + SATA_PHY_TX_DRIV_CTRL0);
+ writel_relaxed(0x09, hba->phy_base + SATA_PHY_TX_DRIV_CTRL1);
+ mb();
+
+ dev_dbg(dev, "SATA PHY powered up in functional mode\n");
+
+out:
+ /* power down PHY in case of failure */
+ if (ret)
+ msm_sata_phy_deinit(dev);
+
+ return ret;
+}
+
+int msm_sata_init(struct device *ahci_dev, void __iomem *mmio)
+{
+ int ret;
+ struct device *dev = ahci_dev->parent;
+ struct msm_sata_hba *hba = dev_get_drvdata(dev);
+
+ /* Save ahci mmio to access vendor specific registers */
+ hba->ahci_base = mmio;
+
+ ret = msm_sata_clk_init(dev);
+ if (ret) {
+ dev_err(dev, "SATA clk init failed with err=%d\n", ret);
+ goto out;
+ }
+
+ ret = msm_sata_vreg_init(dev);
+ if (ret) {
+ dev_err(dev, "SATA vreg init failed with err=%d\n", ret);
+ msm_sata_clk_deinit(dev);
+ goto out;
+ }
+
+ ret = msm_sata_phy_init(dev);
+ if (ret) {
+ dev_err(dev, "SATA PHY init failed with err=%d\n", ret);
+ msm_sata_vreg_deinit(dev);
+ msm_sata_clk_deinit(dev);
+ goto out;
+ }
+
+out:
+ return ret;
+}
+
+void msm_sata_deinit(struct device *ahci_dev)
+{
+ struct device *dev = ahci_dev->parent;
+
+ msm_sata_phy_deinit(dev);
+ msm_sata_vreg_deinit(dev);
+ msm_sata_clk_deinit(dev);
+}
+
+static int msm_sata_suspend(struct device *ahci_dev)
+{
+ msm_sata_deinit(ahci_dev);
+
+ return 0;
+}
+
+static int msm_sata_resume(struct device *ahci_dev)
+{
+ int ret;
+ struct device *dev = ahci_dev->parent;
+
+ ret = msm_sata_clk_init(dev);
+ if (ret) {
+ dev_err(dev, "SATA clk init failed with err=%d\n", ret);
+ /*
+ * If clock initialization failed, that means ahci driver
+ * cannot access any register going further. Since there is
+ * no check within ahci driver to check for clock failures,
+ * panic here instead of making an unclocked register access.
+ */
+ BUG();
+ }
+
+ /* Issue asynchronous reset to reset PHY */
+ ret = msm_sata_hard_reset(dev);
+ if (ret)
+ goto out;
+
+ ret = msm_sata_vreg_init(dev);
+ if (ret) {
+ dev_err(dev, "SATA vreg init failed with err=%d\n", ret);
+ /* Do not turn off clks, AHCI driver might do register access */
+ goto out;
+ }
+
+ ret = msm_sata_phy_init(dev);
+ if (ret) {
+ dev_err(dev, "SATA PHY init failed with err=%d\n", ret);
+ /* Do not turn off clks, AHCI driver might do register access */
+ msm_sata_vreg_deinit(dev);
+ goto out;
+ }
+out:
+ return ret;
+}
+
+static struct ahci_platform_data msm_ahci_pdata = {
+ .init = msm_sata_init,
+ .exit = msm_sata_deinit,
+ .suspend = msm_sata_suspend,
+ .resume = msm_sata_resume,
+};
+
+static int __devinit msm_sata_probe(struct platform_device *pdev)
+{
+ struct platform_device *ahci;
+ struct msm_sata_hba *hba;
+ int ret = 0;
+
+ hba = devm_kzalloc(&pdev->dev, sizeof(struct msm_sata_hba), GFP_KERNEL);
+ if (!hba) {
+ dev_err(&pdev->dev, "no memory\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ platform_set_drvdata(pdev, hba);
+
+ ahci = platform_device_alloc("ahci", pdev->id);
+ if (!ahci) {
+ dev_err(&pdev->dev, "couldn't allocate ahci device\n");
+ ret = -ENOMEM;
+ goto err_free;
+ }
+
+ dma_set_coherent_mask(&ahci->dev, pdev->dev.coherent_dma_mask);
+
+ ahci->dev.parent = &pdev->dev;
+ ahci->dev.dma_mask = pdev->dev.dma_mask;
+ ahci->dev.dma_parms = pdev->dev.dma_parms;
+ hba->ahci_pdev = ahci;
+
+ ret = platform_device_add_resources(ahci, pdev->resource,
+ pdev->num_resources);
+ if (ret) {
+ dev_err(&pdev->dev, "couldn't add resources to ahci device\n");
+ goto err_put_device;
+ }
+
+ ahci->dev.platform_data = &msm_ahci_pdata;
+ ret = platform_device_add(ahci);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register ahci device\n");
+ goto err_put_device;
+ }
+
+ return 0;
+
+err_put_device:
+ platform_device_put(ahci);
+err_free:
+ devm_kfree(&pdev->dev, hba);
+err:
+ return ret;
+}
+
+static int __devexit msm_sata_remove(struct platform_device *pdev)
+{
+ struct msm_sata_hba *hba = platform_get_drvdata(pdev);
+
+ platform_device_unregister(hba->ahci_pdev);
+
+ return 0;
+}
+
+static struct platform_driver msm_sata_driver = {
+ .probe = msm_sata_probe,
+ .remove = __devexit_p(msm_sata_remove),
+ .driver = {
+ .name = "msm_sata",
+ },
+};
+
+module_platform_driver(msm_sata_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("AHCI platform MSM Glue Layer");
diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c
index 82d19e0..0f82142 100644
--- a/drivers/ata/ahci_platform.c
+++ b/drivers/ata/ahci_platform.c
@@ -21,6 +21,7 @@
#include <linux/platform_device.h>
#include <linux/libata.h>
#include <linux/ahci_platform.h>
+#include <linux/pm_runtime.h>
#include "ahci.h"
enum ahci_type {
@@ -73,7 +74,7 @@
AHCI_SHT("ahci_platform"),
};
-static int __init ahci_probe(struct platform_device *pdev)
+static int __devinit ahci_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct ahci_platform_data *pdata = dev_get_platdata(dev);
@@ -192,6 +193,14 @@
if (rc)
goto err0;
+ rc = pm_runtime_set_active(dev);
+ if (rc) {
+ dev_warn(dev, "Unable to set runtime pm active err=%d\n", rc);
+ } else {
+ pm_runtime_enable(dev);
+ pm_runtime_forbid(dev);
+ }
+
return 0;
err0:
if (pdata && pdata->exit)
@@ -275,6 +284,8 @@
static struct dev_pm_ops ahci_pm_ops = {
.suspend = &ahci_suspend,
.resume = &ahci_resume,
+ .runtime_suspend = &ahci_suspend,
+ .runtime_resume = &ahci_resume,
};
#endif
@@ -287,6 +298,7 @@
MODULE_DEVICE_TABLE(of, ahci_of_match);
static struct platform_driver ahci_driver = {
+ .probe = ahci_probe,
.remove = __devexit_p(ahci_remove),
.driver = {
.name = "ahci",
@@ -301,7 +313,7 @@
static int __init ahci_init(void)
{
- return platform_driver_probe(&ahci_driver, ahci_probe);
+ return platform_driver_register(&ahci_driver);
}
module_init(ahci_init);
diff --git a/drivers/base/sync.c b/drivers/base/sync.c
index c0690c8..202c40d 100644
--- a/drivers/base/sync.c
+++ b/drivers/base/sync.c
@@ -28,8 +28,13 @@
#include <linux/anon_inodes.h>
+#define CREATE_TRACE_POINTS
+#include <trace/events/sync.h>
+
static void sync_fence_signal_pt(struct sync_pt *pt);
static int _sync_pt_has_signaled(struct sync_pt *pt);
+static void sync_fence_free(struct kref *kref);
+static void sync_dump(void);
static LIST_HEAD(sync_timeline_list_head);
static DEFINE_SPINLOCK(sync_timeline_list_lock);
@@ -50,6 +55,7 @@
if (obj == NULL)
return NULL;
+ kref_init(&obj->kref);
obj->ops = ops;
strlcpy(obj->name, name, sizeof(obj->name));
@@ -67,8 +73,10 @@
}
EXPORT_SYMBOL(sync_timeline_create);
-static void sync_timeline_free(struct sync_timeline *obj)
+static void sync_timeline_free(struct kref *kref)
{
+ struct sync_timeline *obj =
+ container_of(kref, struct sync_timeline, kref);
unsigned long flags;
if (obj->ops->release_obj)
@@ -83,17 +91,14 @@
void sync_timeline_destroy(struct sync_timeline *obj)
{
- unsigned long flags;
- bool needs_freeing;
-
- spin_lock_irqsave(&obj->child_list_lock, flags);
obj->destroyed = true;
- needs_freeing = list_empty(&obj->child_list_head);
- spin_unlock_irqrestore(&obj->child_list_lock, flags);
- if (needs_freeing)
- sync_timeline_free(obj);
- else
+ /*
+ * If this is not the last reference, signal any children
+ * that their parent is going away.
+ */
+
+ if (!kref_put(&obj->kref, sync_timeline_free))
sync_timeline_signal(obj);
}
EXPORT_SYMBOL(sync_timeline_destroy);
@@ -113,7 +118,6 @@
{
struct sync_timeline *obj = pt->parent;
unsigned long flags;
- bool needs_freeing;
spin_lock_irqsave(&obj->active_list_lock, flags);
if (!list_empty(&pt->active_list))
@@ -121,12 +125,10 @@
spin_unlock_irqrestore(&obj->active_list_lock, flags);
spin_lock_irqsave(&obj->child_list_lock, flags);
- list_del(&pt->child_list);
- needs_freeing = obj->destroyed && list_empty(&obj->child_list_head);
+ if (!list_empty(&pt->child_list)) {
+ list_del_init(&pt->child_list);
+ }
spin_unlock_irqrestore(&obj->child_list_lock, flags);
-
- if (needs_freeing)
- sync_timeline_free(obj);
}
void sync_timeline_signal(struct sync_timeline *obj)
@@ -135,24 +137,30 @@
LIST_HEAD(signaled_pts);
struct list_head *pos, *n;
+ trace_sync_timeline(obj);
+
spin_lock_irqsave(&obj->active_list_lock, flags);
list_for_each_safe(pos, n, &obj->active_list_head) {
struct sync_pt *pt =
container_of(pos, struct sync_pt, active_list);
- if (_sync_pt_has_signaled(pt))
- list_move(pos, &signaled_pts);
+ if (_sync_pt_has_signaled(pt)) {
+ list_del_init(pos);
+ list_add(&pt->signaled_list, &signaled_pts);
+ kref_get(&pt->fence->kref);
+ }
}
spin_unlock_irqrestore(&obj->active_list_lock, flags);
list_for_each_safe(pos, n, &signaled_pts) {
struct sync_pt *pt =
- container_of(pos, struct sync_pt, active_list);
+ container_of(pos, struct sync_pt, signaled_list);
list_del_init(pos);
sync_fence_signal_pt(pt);
+ kref_put(&pt->fence->kref, sync_fence_free);
}
}
EXPORT_SYMBOL(sync_timeline_signal);
@@ -169,6 +177,7 @@
return NULL;
INIT_LIST_HEAD(&pt->active_list);
+ kref_get(&parent->kref);
sync_timeline_add_pt(parent, pt);
return pt;
@@ -182,6 +191,8 @@
sync_timeline_remove_pt(pt);
+ kref_put(&pt->parent->kref, sync_timeline_free);
+
kfree(pt);
}
EXPORT_SYMBOL(sync_pt_free);
@@ -255,6 +266,7 @@
if (fence->file == NULL)
goto err;
+ kref_init(&fence->kref);
strlcpy(fence->name, name, sizeof(fence->name));
INIT_LIST_HEAD(&fence->pt_list_head);
@@ -290,6 +302,12 @@
list_add(&pt->pt_list, &fence->pt_list_head);
sync_pt_activate(pt);
+ /*
+ * signal the fence in case pt was activated before
+ * sync_pt_activate(pt) was called
+ */
+ sync_fence_signal_pt(pt);
+
return fence;
}
EXPORT_SYMBOL(sync_fence_create);
@@ -314,6 +332,65 @@
return 0;
}
+static int sync_fence_merge_pts(struct sync_fence *dst, struct sync_fence *src)
+{
+ struct list_head *src_pos, *dst_pos, *n;
+
+ list_for_each(src_pos, &src->pt_list_head) {
+ struct sync_pt *src_pt =
+ container_of(src_pos, struct sync_pt, pt_list);
+ bool collapsed = false;
+
+ list_for_each_safe(dst_pos, n, &dst->pt_list_head) {
+ struct sync_pt *dst_pt =
+ container_of(dst_pos, struct sync_pt, pt_list);
+ /* collapse two sync_pts on the same timeline
+ * to a single sync_pt that will signal at
+ * the later of the two
+ */
+ if (dst_pt->parent == src_pt->parent) {
+ if (dst_pt->parent->ops->compare(dst_pt, src_pt) == -1) {
+ struct sync_pt *new_pt =
+ sync_pt_dup(src_pt);
+ if (new_pt == NULL)
+ return -ENOMEM;
+
+ new_pt->fence = dst;
+ list_replace(&dst_pt->pt_list,
+ &new_pt->pt_list);
+ sync_pt_activate(new_pt);
+ sync_pt_free(dst_pt);
+ }
+ collapsed = true;
+ break;
+ }
+ }
+
+ if (!collapsed) {
+ struct sync_pt *new_pt = sync_pt_dup(src_pt);
+
+ if (new_pt == NULL)
+ return -ENOMEM;
+
+ new_pt->fence = dst;
+ list_add(&new_pt->pt_list, &dst->pt_list_head);
+ sync_pt_activate(new_pt);
+ }
+ }
+
+ return 0;
+}
+
+static void sync_fence_detach_pts(struct sync_fence *fence)
+{
+ struct list_head *pos, *n;
+
+ list_for_each_safe(pos, n, &fence->pt_list_head) {
+ struct sync_pt *pt = container_of(pos, struct sync_pt, pt_list);
+ sync_timeline_remove_pt(pt);
+ }
+}
+
static void sync_fence_free_pts(struct sync_fence *fence)
{
struct list_head *pos, *n;
@@ -388,11 +465,17 @@
if (err < 0)
goto err;
- err = sync_fence_copy_pts(fence, b);
+ err = sync_fence_merge_pts(fence, b);
if (err < 0)
goto err;
- fence->status = sync_fence_get_status(fence);
+ /*
+ * signal the fence in case one of it's pts were activated before
+ * they were activated
+ */
+ sync_fence_signal_pt(list_first_entry(&fence->pt_list_head,
+ struct sync_pt,
+ pt_list));
return fence;
err:
@@ -491,44 +574,87 @@
}
EXPORT_SYMBOL(sync_fence_cancel_async);
+static bool sync_fence_check(struct sync_fence *fence)
+{
+ /*
+ * Make sure that reads to fence->status are ordered with the
+ * wait queue event triggering
+ */
+ smp_rmb();
+ return fence->status != 0;
+}
+
int sync_fence_wait(struct sync_fence *fence, long timeout)
{
- int err;
+ int err = 0;
+ struct sync_pt *pt;
- if (timeout) {
+ trace_sync_wait(fence, 1);
+ list_for_each_entry(pt, &fence->pt_list_head, pt_list)
+ trace_sync_pt(pt);
+
+ if (timeout > 0) {
timeout = msecs_to_jiffies(timeout);
err = wait_event_interruptible_timeout(fence->wq,
- fence->status != 0,
+ sync_fence_check(fence),
timeout);
- } else {
- err = wait_event_interruptible(fence->wq, fence->status != 0);
+ } else if (timeout < 0) {
+ err = wait_event_interruptible(fence->wq,
+ sync_fence_check(fence));
}
+ trace_sync_wait(fence, 0);
if (err < 0)
return err;
- if (fence->status < 0)
+ if (fence->status < 0) {
+ pr_info("fence error %d on [%p]\n", fence->status, fence);
+ sync_dump();
return fence->status;
+ }
- if (fence->status == 0)
+ if (fence->status == 0) {
+ pr_info("fence timeout on [%p] after %dms\n", fence,
+ jiffies_to_msecs(timeout));
+ sync_dump();
return -ETIME;
+ }
return 0;
}
EXPORT_SYMBOL(sync_fence_wait);
+static void sync_fence_free(struct kref *kref)
+{
+ struct sync_fence *fence = container_of(kref, struct sync_fence, kref);
+
+ sync_fence_free_pts(fence);
+
+ kfree(fence);
+}
+
static int sync_fence_release(struct inode *inode, struct file *file)
{
struct sync_fence *fence = file->private_data;
unsigned long flags;
- sync_fence_free_pts(fence);
-
+ /*
+ * We need to remove all ways to access this fence before droping
+ * our ref.
+ *
+ * start with its membership in the global fence list
+ */
spin_lock_irqsave(&sync_fence_list_lock, flags);
list_del(&fence->sync_fence_list);
spin_unlock_irqrestore(&sync_fence_list_lock, flags);
- kfree(fence);
+ /*
+ * remove its pts from their parents so that sync_timeline_signal()
+ * can't reference the fence.
+ */
+ sync_fence_detach_pts(fence);
+
+ kref_put(&fence->kref, sync_fence_free);
return 0;
}
@@ -539,6 +665,12 @@
poll_wait(file, &fence->wq, wait);
+ /*
+ * Make sure that reads to fence->status are ordered with the
+ * wait queue event triggering
+ */
+ smp_rmb();
+
if (fence->status == 1)
return POLLIN;
else if (fence->status < 0)
@@ -549,7 +681,7 @@
static long sync_fence_ioctl_wait(struct sync_fence *fence, unsigned long arg)
{
- __u32 value;
+ __s32 value;
if (copy_from_user(&value, (void __user *)arg, sizeof(value)))
return -EFAULT;
@@ -564,8 +696,13 @@
struct sync_fence *fence2, *fence3;
struct sync_merge_data data;
- if (copy_from_user(&data, (void __user *)arg, sizeof(data)))
- return -EFAULT;
+ if (fd < 0)
+ return fd;
+
+ if (copy_from_user(&data, (void __user *)arg, sizeof(data))) {
+ err = -EFAULT;
+ goto err_put_fd;
+ }
fence2 = sync_fence_fdget(data.fd2);
if (fence2 == NULL) {
@@ -722,7 +859,17 @@
seq_printf(s, "@%ld.%06ld", tv.tv_sec, tv.tv_usec);
}
- if (pt->parent->ops->print_pt) {
+ if (pt->parent->ops->timeline_value_str &&
+ pt->parent->ops->pt_value_str) {
+ char value[64];
+ pt->parent->ops->pt_value_str(pt, value, sizeof(value));
+ seq_printf(s, ": %s", value);
+ if (fence) {
+ pt->parent->ops->timeline_value_str(pt->parent, value,
+ sizeof(value));
+ seq_printf(s, " / %s", value);
+ }
+ } else if (pt->parent->ops->print_pt) {
seq_printf(s, ": ");
pt->parent->ops->print_pt(s, pt);
}
@@ -737,7 +884,11 @@
seq_printf(s, "%s %s", obj->name, obj->ops->driver_name);
- if (obj->ops->print_obj) {
+ if (obj->ops->timeline_value_str) {
+ char value[64];
+ obj->ops->timeline_value_str(obj, value, sizeof(value));
+ seq_printf(s, ": %s", value);
+ } else if (obj->ops->print_obj) {
seq_printf(s, ": ");
obj->ops->print_obj(s, obj);
}
@@ -758,7 +909,8 @@
struct list_head *pos;
unsigned long flags;
- seq_printf(s, "%s: %s\n", fence->name, sync_status_str(fence->status));
+ seq_printf(s, "[%p] %s: %s\n", fence, fence->name,
+ sync_status_str(fence->status));
list_for_each(pos, &fence->pt_list_head) {
struct sync_pt *pt =
@@ -826,7 +978,34 @@
debugfs_create_file("sync", S_IRUGO, NULL, NULL, &sync_debugfs_fops);
return 0;
}
-
late_initcall(sync_debugfs_init);
+#define DUMP_CHUNK 256
+static char sync_dump_buf[64 * 1024];
+void sync_dump(void)
+{
+ struct seq_file s = {
+ .buf = sync_dump_buf,
+ .size = sizeof(sync_dump_buf) - 1,
+ };
+ int i;
+
+ sync_debugfs_show(&s, NULL);
+
+ for (i = 0; i < s.count; i += DUMP_CHUNK) {
+ if ((s.count - i) > DUMP_CHUNK) {
+ char c = s.buf[i + DUMP_CHUNK];
+ s.buf[i + DUMP_CHUNK] = 0;
+ pr_cont("%s", s.buf + i);
+ s.buf[i + DUMP_CHUNK] = c;
+ } else {
+ s.buf[s.count] = 0;
+ pr_cont("%s", s.buf + i);
+ }
+ }
+}
+#else
+static void sync_dump(void)
+{
+}
#endif
diff --git a/drivers/char/diag/diagchar.h b/drivers/char/diag/diagchar.h
index 79f20a4..0d5ad6a 100644
--- a/drivers/char/diag/diagchar.h
+++ b/drivers/char/diag/diagchar.h
@@ -81,7 +81,6 @@
#define SMD_DATA_TYPE 0
#define SMD_CNTL_TYPE 1
#define SMD_DCI_TYPE 2
-#define MAX_PROC 10
/* Maximum number of pkt reg supported at initialization*/
extern unsigned int diag_max_reg;
diff --git a/drivers/char/diag/diagchar_core.c b/drivers/char/diag/diagchar_core.c
index f6c26c3..d2454f4 100644
--- a/drivers/char/diag/diagchar_core.c
+++ b/drivers/char/diag/diagchar_core.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2008-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
@@ -80,9 +80,6 @@
means that the diag packet has a delayed response. */
static uint16_t delayed_rsp_id = 1;
-/* Array of valid token ids */
-static int token_list[MAX_PROC] = {0, -1, -2, -3, -4, -5, -6, -7, -8, -9};
-
#define DIAGPKT_MAX_DELAYED_RSP 0xFFFF
/* returns the next delayed rsp id - rollsover the id if wrapping is
@@ -250,6 +247,10 @@
pr_alert("diag: Invalid file pointer");
return -ENOMEM;
}
+
+ if (!driver)
+ return -ENOMEM;
+
/* clean up any DCI registrations, if this is a DCI client
* This will specially help in case of ungraceful exit of any DCI client
* This call will remove any pending registrations of such client
@@ -289,26 +290,23 @@
if (driver->table[i].process_id == current->tgid)
driver->table[i].process_id = 0;
- if (driver) {
- mutex_lock(&driver->diagchar_mutex);
- driver->ref_count--;
- /* On Client exit, try to destroy all 3 pools */
- diagmem_exit(driver, POOL_TYPE_COPY);
- diagmem_exit(driver, POOL_TYPE_HDLC);
- diagmem_exit(driver, POOL_TYPE_WRITE_STRUCT);
- for (i = 0; i < driver->num_clients; i++) {
- if (NULL != diagpriv_data && diagpriv_data->pid ==
- driver->client_map[i].pid) {
- driver->client_map[i].pid = 0;
- kfree(diagpriv_data);
- diagpriv_data = NULL;
- break;
- }
+ mutex_lock(&driver->diagchar_mutex);
+ driver->ref_count--;
+ /* On Client exit, try to destroy all 3 pools */
+ diagmem_exit(driver, POOL_TYPE_COPY);
+ diagmem_exit(driver, POOL_TYPE_HDLC);
+ diagmem_exit(driver, POOL_TYPE_WRITE_STRUCT);
+ for (i = 0; i < driver->num_clients; i++) {
+ if (NULL != diagpriv_data && diagpriv_data->pid ==
+ driver->client_map[i].pid) {
+ driver->client_map[i].pid = 0;
+ kfree(diagpriv_data);
+ diagpriv_data = NULL;
+ break;
}
- mutex_unlock(&driver->diagchar_mutex);
- return 0;
}
- return -ENOMEM;
+ mutex_unlock(&driver->diagchar_mutex);
+ return 0;
}
int diag_find_polling_reg(int i)
@@ -383,10 +381,13 @@
{
uint16_t remote_dev = 0;
+ /* Check for MDM processor */
if (driver->hsic_inited)
- remote_dev |= (1 << 0);
+ remote_dev |= 1 << 0;
+
+ /* Check for QSC processor */
if (driver->diag_smux_enabled)
- remote_dev |= (1 << 1);
+ remote_dev |= 1 << 1;
return remote_dev;
}
@@ -394,15 +395,22 @@
inline uint16_t diag_get_remote_device_mask(void) { return 0; }
#endif
-static int diag_get_token(int token)
+static int diag_get_remote(int remote_info)
{
- int i;
+ int val = (remote_info < 0) ? -remote_info : remote_info;
+ int remote_val;
- for (i = 0; i < MAX_PROC; i++)
- if (token_list[i] == token)
- return 1 << i;
+ switch (val) {
+ case MDM:
+ case QSC:
+ remote_val = -remote_info;
+ break;
+ default:
+ remote_val = 0;
+ break;
+ }
- return 0;
+ return remote_val;
}
long diagchar_ioctl(struct file *filp,
@@ -810,6 +818,7 @@
struct diag_dci_client_tbl *entry;
int index = -1, i = 0, ret = 0;
int num_data = 0, data_type;
+ int remote_token;
for (i = 0; i < driver->num_clients; i++)
if (driver->client_map[i].pid == current->tgid)
@@ -830,7 +839,7 @@
unsigned long spin_lock_flags;
struct diag_write_device hsic_buf_tbl[NUM_HSIC_BUF_TBL_ENTRIES];
#endif
-
+ remote_token = 0;
pr_debug("diag: process woken up\n");
/*Copy the type of data being passed*/
data_type = driver->data_ready[index] & USER_SPACE_DATA_TYPE;
@@ -906,9 +915,12 @@
#ifdef CONFIG_DIAG_SDIO_PIPE
/* copy 9K data over SDIO */
if (driver->in_busy_sdio == 1) {
+ remote_token = diag_get_remote(MDM);
num_data++;
- /*Copy the negative token of data being passed*/
- COPY_USER_SPACE_OR_EXIT(buf+ret, token_list[MDM], 4);
+
+ /*Copy the negative token of data being passed*/
+ COPY_USER_SPACE_OR_EXIT(buf+ret,
+ remote_token, 4);
/*Copy the length of data being passed*/
COPY_USER_SPACE_OR_EXIT(buf+ret,
(driver->write_ptr_mdm->length), 4);
@@ -932,6 +944,7 @@
spin_unlock_irqrestore(&driver->hsic_spinlock,
spin_lock_flags);
+ remote_token = diag_get_remote(MDM);
for (i = 0; i < driver->poolsize_hsic_write; i++) {
if (hsic_buf_tbl[i].length > 0) {
pr_debug("diag: HSIC copy to user, i: %d, buf: %x, len: %d\n",
@@ -940,8 +953,8 @@
num_data++;
/* Copy the negative token */
- if (copy_to_user(buf+ret, &token_list[MDM],
- 4)) {
+ if (copy_to_user(buf+ret,
+ &remote_token, 4)) {
num_data--;
goto drop_hsic;
}
@@ -976,10 +989,12 @@
}
}
if (driver->in_busy_smux == 1) {
+ remote_token = diag_get_remote(QSC);
num_data++;
/* Copy the negative token of data being passed */
- COPY_USER_SPACE_OR_EXIT(buf+ret, token_list[QSC], 4);
+ COPY_USER_SPACE_OR_EXIT(buf+ret,
+ remote_token, 4);
/* Copy the length of data being passed */
COPY_USER_SPACE_OR_EXIT(buf+ret,
(driver->write_ptr_mdm->length), 4);
@@ -1067,10 +1082,9 @@
COPY_USER_SPACE_OR_EXIT(buf, data_type, 4);
/* check the current client and copy its data */
for (i = 0; i < MAX_DCI_CLIENTS; i++) {
- if (driver->dci_client_tbl[i].client != NULL) {
- entry = &(driver->dci_client_tbl[i]);
- if (entry && (current->tgid ==
- entry->client->tgid)) {
+ entry = &(driver->dci_client_tbl[i]);
+ if (entry && entry->client) {
+ if (current->tgid == entry->client->tgid) {
COPY_USER_SPACE_OR_EXIT(buf+4,
entry->data_len, 4);
COPY_USER_SPACE_OR_EXIT(buf+8,
@@ -1142,7 +1156,7 @@
err = copy_from_user(driver->user_space_data, buf + 4,
payload_size);
/* Check for proc_type */
- remote_proc = diag_get_token(*(int *)driver->user_space_data);
+ remote_proc = diag_get_remote(*(int *)driver->user_space_data);
if (remote_proc) {
token_offset = 4;
@@ -1167,7 +1181,7 @@
#endif
#ifdef CONFIG_DIAG_SDIO_PIPE
/* send masks to 9k too */
- if (driver->sdio_ch && (remote_proc & MDM)) {
+ if (driver->sdio_ch && (remote_proc == MDM)) {
wait_event_interruptible(driver->wait_q,
(sdio_write_avail(driver->sdio_ch) >=
payload_size));
@@ -1181,7 +1195,7 @@
#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
/* send masks to 9k too */
if (driver->hsic_ch && (payload_size > 0) &&
- (remote_proc & MDM)) {
+ (remote_proc == MDM)) {
/* wait sending mask updates if HSIC ch not ready */
if (driver->in_busy_hsic_write)
wait_event_interruptible(driver->wait_q,
@@ -1204,7 +1218,7 @@
driver->in_busy_hsic_write = 0;
}
}
- if (driver->diag_smux_enabled && (remote_proc & QSC)
+ if (driver->diag_smux_enabled && (remote_proc == QSC)
&& driver->lcid) {
if (payload_size > 0) {
err = msm_smux_write(driver->lcid, NULL,
@@ -1273,6 +1287,13 @@
ret = -ENOMEM;
goto fail_free_hdlc;
}
+ if (HDLC_OUT_BUF_SIZE < (2*payload_size) + 3) {
+ pr_err("diag: Dropping packet, HDLC encoded packet payload size crosses buffer limit. Current payload size %d\n",
+ ((2*payload_size) + 3));
+ driver->dropped_count++;
+ ret = -EBADMSG;
+ goto fail_free_hdlc;
+ }
if (HDLC_OUT_BUF_SIZE - driver->used <= (2*payload_size) + 3) {
err = diag_device_write(buf_hdlc, APPS_DATA, NULL);
if (err) {
@@ -1343,8 +1364,8 @@
driver->used = 0;
}
- mutex_unlock(&driver->diagchar_mutex);
diagmem_free(driver, buf_copy, POOL_TYPE_COPY);
+ mutex_unlock(&driver->diagchar_mutex);
if (!timer_in_progress) {
timer_in_progress = 1;
ret = mod_timer(&drain_timer, jiffies + msecs_to_jiffies(500));
diff --git a/drivers/coresight/coresight-csr.c b/drivers/coresight/coresight-csr.c
index 1f6bd1d..e734ece 100644
--- a/drivers/coresight/coresight-csr.c
+++ b/drivers/coresight/coresight-csr.c
@@ -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
@@ -18,6 +18,7 @@
#include <linux/io.h>
#include <linux/err.h>
#include <linux/slab.h>
+#include <linux/of.h>
#include <linux/of_coresight.h>
#include <linux/coresight.h>
@@ -74,6 +75,7 @@
void __iomem *base;
struct device *dev;
struct coresight_device *csdev;
+ uint32_t blksize;
};
static struct csr_drvdata *csrdrvdata;
@@ -86,7 +88,7 @@
CSR_UNLOCK(drvdata);
usbbamctrl = csr_readl(drvdata, CSR_USBBAMCTRL);
- usbbamctrl = (usbbamctrl & ~0x3) | BLKSIZE_2048;
+ usbbamctrl = (usbbamctrl & ~0x3) | drvdata->blksize;
csr_writel(drvdata, usbbamctrl, CSR_USBBAMCTRL);
usbflshctrl = csr_readl(drvdata, CSR_USBFLSHCTRL);
@@ -119,6 +121,7 @@
static int __devinit csr_probe(struct platform_device *pdev)
{
+ int ret;
struct device *dev = &pdev->dev;
struct coresight_platform_data *pdata;
struct csr_drvdata *drvdata;
@@ -148,6 +151,13 @@
if (!drvdata->base)
return -ENOMEM;
+ if (pdev->dev.of_node) {
+ ret = of_property_read_u32(pdev->dev.of_node, "qcom,blk-size",
+ &drvdata->blksize);
+ if (ret)
+ drvdata->blksize = BLKSIZE_256;
+ }
+
desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
if (!desc)
return -ENOMEM;
diff --git a/drivers/coresight/coresight-etm.c b/drivers/coresight/coresight-etm.c
index 9f96b19..b569aed 100644
--- a/drivers/coresight/coresight-etm.c
+++ b/drivers/coresight/coresight-etm.c
@@ -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
@@ -91,75 +91,87 @@
*/
/* Trace registers (0x000-0x2FC) */
-#define ETMCR (0x000)
-#define ETMCCR (0x004)
-#define ETMTRIGGER (0x008)
-#define ETMSR (0x010)
-#define ETMSCR (0x014)
-#define ETMTSSCR (0x018)
-#define ETMTEEVR (0x020)
-#define ETMTECR1 (0x024)
-#define ETMFFLR (0x02C)
-#define ETMACVRn(n) (0x040 + (n * 4))
-#define ETMACTRn(n) (0x080 + (n * 4))
-#define ETMCNTRLDVRn(n) (0x140 + (n * 4))
-#define ETMCNTENRn(n) (0x150 + (n * 4))
-#define ETMCNTRLDEVRn(n) (0x160 + (n * 4))
-#define ETMCNTVRn(n) (0x170 + (n * 4))
-#define ETMSQ12EVR (0x180)
-#define ETMSQ21EVR (0x184)
-#define ETMSQ23EVR (0x188)
-#define ETMSQ31EVR (0x18C)
-#define ETMSQ32EVR (0x190)
-#define ETMSQ13EVR (0x194)
-#define ETMSQR (0x19C)
-#define ETMEXTOUTEVRn(n) (0x1A0 + (n * 4))
-#define ETMCIDCVRn(n) (0x1B0 + (n * 4))
-#define ETMCIDCMR (0x1BC)
-#define ETMIMPSPEC0 (0x1C0)
-#define ETMIMPSPEC1 (0x1C4)
-#define ETMIMPSPEC2 (0x1C8)
-#define ETMIMPSPEC3 (0x1CC)
-#define ETMIMPSPEC4 (0x1D0)
-#define ETMIMPSPEC5 (0x1D4)
-#define ETMIMPSPEC6 (0x1D8)
-#define ETMIMPSPEC7 (0x1DC)
-#define ETMSYNCFR (0x1E0)
-#define ETMIDR (0x1E4)
-#define ETMCCER (0x1E8)
-#define ETMEXTINSELR (0x1EC)
-#define ETMTESSEICR (0x1F0)
-#define ETMEIBCR (0x1F4)
-#define ETMTSEVR (0x1F8)
-#define ETMAUXCR (0x1FC)
-#define ETMTRACEIDR (0x200)
-#define ETMVMIDCVR (0x240)
+#define ETMCR (0x000)
+#define ETMCCR (0x004)
+#define ETMTRIGGER (0x008)
+#define ETMASSICCTLR (0x00C)
+#define ETMSR (0x010)
+#define ETMSCR (0x014)
+#define ETMTSSCR (0x018)
+#define ETMTECR2 (0x01C)
+#define ETMTEEVR (0x020)
+#define ETMTECR1 (0x024)
+#define ETMFFLR (0x02C)
+#define ETMVDEVR (0x030)
+#define ETMVDCR1 (0x034)
+#define ETMVDCR3 (0x03C)
+#define ETMACVRn(n) (0x040 + (n * 4))
+#define ETMACTRn(n) (0x080 + (n * 4))
+#define ETMDCVRn(n) (0x0C0 + (n * 8))
+#define ETMDCMRn(n) (0x100 + (n * 8))
+#define ETMCNTRLDVRn(n) (0x140 + (n * 4))
+#define ETMCNTENRn(n) (0x150 + (n * 4))
+#define ETMCNTRLDEVRn(n) (0x160 + (n * 4))
+#define ETMCNTVRn(n) (0x170 + (n * 4))
+#define ETMSQ12EVR (0x180)
+#define ETMSQ21EVR (0x184)
+#define ETMSQ23EVR (0x188)
+#define ETMSQ31EVR (0x18C)
+#define ETMSQ32EVR (0x190)
+#define ETMSQ13EVR (0x194)
+#define ETMSQR (0x19C)
+#define ETMEXTOUTEVRn(n) (0x1A0 + (n * 4))
+#define ETMCIDCVRn(n) (0x1B0 + (n * 4))
+#define ETMCIDCMR (0x1BC)
+#define ETMIMPSPEC0 (0x1C0)
+#define ETMIMPSPEC1 (0x1C4)
+#define ETMIMPSPEC2 (0x1C8)
+#define ETMIMPSPEC3 (0x1CC)
+#define ETMIMPSPEC4 (0x1D0)
+#define ETMIMPSPEC5 (0x1D4)
+#define ETMIMPSPEC6 (0x1D8)
+#define ETMIMPSPEC7 (0x1DC)
+#define ETMSYNCFR (0x1E0)
+#define ETMIDR (0x1E4)
+#define ETMCCER (0x1E8)
+#define ETMEXTINSELR (0x1EC)
+#define ETMTESSEICR (0x1F0)
+#define ETMEIBCR (0x1F4)
+#define ETMTSEVR (0x1F8)
+#define ETMAUXCR (0x1FC)
+#define ETMTRACEIDR (0x200)
+#define ETMIDR2 (0x208)
+#define ETMVMIDCVR (0x240)
/* Management registers (0x300-0x314) */
-#define ETMOSLAR (0x300)
-#define ETMOSLSR (0x304)
-#define ETMOSSRR (0x308)
-#define ETMPDCR (0x310)
-#define ETMPDSR (0x314)
+#define ETMOSLAR (0x300)
+#define ETMOSLSR (0x304)
+#define ETMOSSRR (0x308)
+#define ETMPDCR (0x310)
+#define ETMPDSR (0x314)
-#define ETM_MAX_ADDR_CMP (16)
-#define ETM_MAX_CNTR (4)
-#define ETM_MAX_CTXID_CMP (3)
+#define ETM_MAX_ADDR_CMP (16)
+#define ETM_MAX_CNTR (4)
+#define ETM_MAX_CTXID_CMP (3)
-#define ETM_MODE_EXCLUDE BIT(0)
-#define ETM_MODE_CYCACC BIT(1)
-#define ETM_MODE_STALL BIT(2)
-#define ETM_MODE_TIMESTAMP BIT(3)
-#define ETM_MODE_CTXID BIT(4)
-#define ETM_MODE_ALL (0x1F)
+#define ETM_MODE_EXCLUDE BIT(0)
+#define ETM_MODE_CYCACC BIT(1)
+#define ETM_MODE_STALL BIT(2)
+#define ETM_MODE_TIMESTAMP BIT(3)
+#define ETM_MODE_CTXID BIT(4)
+#define ETM_MODE_DATA_TRACE_VAL BIT(5)
+#define ETM_MODE_DATA_TRACE_ADDR BIT(6)
+#define ETM_MODE_ALL (0x7F)
-#define ETM_EVENT_MASK (0x1FFFF)
-#define ETM_SYNC_MASK (0xFFF)
-#define ETM_ALL_MASK (0xFFFFFFFF)
+#define ETM_DATACMP_ENABLE (0x2)
-#define ETM_SEQ_STATE_MAX_VAL (0x2)
+#define ETM_EVENT_MASK (0x1FFFF)
+#define ETM_SYNC_MASK (0xFFF)
+#define ETM_ALL_MASK (0xFFFFFFFF)
-#define ETM_REG_DUMP_VER_OFF (4)
-#define ETM_REG_DUMP_VER (1)
+#define ETM_SEQ_STATE_MAX_VAL (0x2)
+
+#define ETM_REG_DUMP_VER_OFF (4)
+#define ETM_REG_DUMP_VER (1)
enum etm_addr_type {
ETM_ADDR_TYPE_NONE,
@@ -203,6 +215,7 @@
uint8_t nr_ext_inp;
uint8_t nr_ext_out;
uint8_t nr_ctxid_cmp;
+ uint8_t nr_data_cmp;
uint8_t reset;
uint32_t mode;
uint32_t ctrl;
@@ -210,11 +223,18 @@
uint32_t startstop_ctrl;
uint32_t enable_event;
uint32_t enable_ctrl1;
+ uint32_t enable_ctrl2;
uint32_t fifofull_level;
uint8_t addr_idx;
uint32_t addr_val[ETM_MAX_ADDR_CMP];
uint32_t addr_acctype[ETM_MAX_ADDR_CMP];
uint32_t addr_type[ETM_MAX_ADDR_CMP];
+ bool data_trace_support;
+ uint32_t data_val[ETM_MAX_ADDR_CMP];
+ uint32_t data_mask[ETM_MAX_ADDR_CMP];
+ uint32_t viewdata_event;
+ uint32_t viewdata_ctrl1;
+ uint32_t viewdata_ctrl3;
uint8_t cntr_idx;
uint32_t cntr_rld_val[ETM_MAX_CNTR];
uint32_t cntr_event[ETM_MAX_CNTR];
@@ -247,8 +267,10 @@
*/
static void etm_os_unlock(void *info)
{
- etm_writel_cp14(0x0, ETMOSLAR);
- isb();
+ if (cpu_is_krait()) {
+ etm_writel_cp14(0x0, ETMOSLAR);
+ isb();
+ }
}
/*
@@ -382,6 +404,14 @@
ETM_LOCK(drvdata);
}
+static bool etm_version_gte(uint8_t arch, uint8_t base_arch)
+{
+ if (arch >= base_arch && ((arch & PFT_ARCH_MAJOR) != PFT_ARCH_MAJOR))
+ return true;
+ else
+ return false;
+}
+
static void __etm_enable(void *info)
{
int i;
@@ -409,13 +439,24 @@
etm_writel(drvdata, drvdata->ctrl | etmcr, ETMCR);
etm_writel(drvdata, drvdata->trigger_event, ETMTRIGGER);
etm_writel(drvdata, drvdata->startstop_ctrl, ETMTSSCR);
+ if (etm_version_gte(drvdata->arch, ETM_ARCH_V1_2))
+ etm_writel(drvdata, drvdata->enable_ctrl2, ETMTECR2);
etm_writel(drvdata, drvdata->enable_event, ETMTEEVR);
etm_writel(drvdata, drvdata->enable_ctrl1, ETMTECR1);
etm_writel(drvdata, drvdata->fifofull_level, ETMFFLR);
+ if (drvdata->data_trace_support == true) {
+ etm_writel(drvdata, drvdata->viewdata_event, ETMVDEVR);
+ etm_writel(drvdata, drvdata->viewdata_ctrl1, ETMVDCR1);
+ etm_writel(drvdata, drvdata->viewdata_ctrl3, ETMVDCR3);
+ }
for (i = 0; i < drvdata->nr_addr_cmp; i++) {
etm_writel(drvdata, drvdata->addr_val[i], ETMACVRn(i));
etm_writel(drvdata, drvdata->addr_acctype[i], ETMACTRn(i));
}
+ for (i = 0; i < drvdata->nr_data_cmp; i++) {
+ etm_writel(drvdata, drvdata->data_val[i], ETMDCVRn(i));
+ etm_writel(drvdata, drvdata->data_mask[i], ETMDCMRn(i));
+ }
for (i = 0; i < drvdata->nr_cntr; i++) {
etm_writel(drvdata, drvdata->cntr_rld_val[i], ETMCNTRLDVRn(i));
etm_writel(drvdata, drvdata->cntr_event[i], ETMCNTENRn(i));
@@ -594,21 +635,37 @@
if (val) {
drvdata->mode = ETM_MODE_EXCLUDE;
drvdata->ctrl = 0x0;
+ if (etm_version_gte(drvdata->arch, ETM_ARCH_V1_0))
+ drvdata->ctrl |= BIT(11);
if (cpu_is_krait_v1()) {
drvdata->mode |= ETM_MODE_CYCACC;
drvdata->ctrl |= BIT(12);
}
drvdata->trigger_event = 0x406F;
drvdata->startstop_ctrl = 0x0;
+ if (etm_version_gte(drvdata->arch, ETM_ARCH_V1_2))
+ drvdata->enable_ctrl2 = 0x0;
drvdata->enable_event = 0x6F;
drvdata->enable_ctrl1 = 0x1000000;
drvdata->fifofull_level = 0x28;
+ if (drvdata->data_trace_support == true) {
+ drvdata->mode |= (ETM_MODE_DATA_TRACE_VAL |
+ ETM_MODE_DATA_TRACE_ADDR);
+ drvdata->ctrl |= BIT(2) | BIT(3);
+ drvdata->viewdata_event = 0x6F;
+ drvdata->viewdata_ctrl1 = 0x0;
+ drvdata->viewdata_ctrl3 = 0x10000;
+ }
drvdata->addr_idx = 0x0;
for (i = 0; i < drvdata->nr_addr_cmp; i++) {
drvdata->addr_val[i] = 0x0;
drvdata->addr_acctype[i] = 0x0;
drvdata->addr_type[i] = ETM_ADDR_TYPE_NONE;
}
+ for (i = 0; i < drvdata->nr_data_cmp; i++) {
+ drvdata->data_val[i] = 0;
+ drvdata->data_mask[i] = ~(0);
+ }
drvdata->cntr_idx = 0x0;
for (i = 0; i < drvdata->nr_cntr; i++) {
drvdata->cntr_rld_val[i] = 0x0;
@@ -684,6 +741,17 @@
drvdata->ctrl |= (BIT(14) | BIT(15));
else
drvdata->ctrl &= ~(BIT(14) | BIT(15));
+ if (etm_version_gte(drvdata->arch, ETM_ARCH_V1_0)) {
+ if (drvdata->mode & ETM_MODE_DATA_TRACE_VAL)
+ drvdata->ctrl |= BIT(2);
+ else
+ drvdata->ctrl &= ~(BIT(2));
+
+ if (drvdata->mode & ETM_MODE_DATA_TRACE_ADDR)
+ drvdata->ctrl |= (BIT(3));
+ else
+ drvdata->ctrl &= ~(BIT(3));
+ }
spin_unlock(&drvdata->spinlock);
return size;
@@ -839,6 +907,8 @@
drvdata->addr_val[idx] = val;
drvdata->addr_type[idx] = ETM_ADDR_TYPE_SINGLE;
+ if (etm_version_gte(drvdata->arch, ETM_ARCH_V1_2))
+ drvdata->enable_ctrl2 |= (1 << idx);
spin_unlock(&drvdata->spinlock);
return size;
}
@@ -1039,6 +1109,138 @@
static DEVICE_ATTR(addr_acctype, S_IRUGO | S_IWUSR, etm_show_addr_acctype,
etm_store_addr_acctype);
+static ssize_t etm_show_data_val(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+ unsigned long val;
+ uint8_t idx;
+
+ spin_lock(&drvdata->spinlock);
+ idx = drvdata->addr_idx;
+ if (idx % 2 != 0) {
+ spin_unlock(&drvdata->spinlock);
+ return -EPERM;
+ }
+ idx = idx >> 1;
+ if (idx >= drvdata->nr_data_cmp) {
+ spin_unlock(&drvdata->spinlock);
+ return -EPERM;
+ }
+
+ val = drvdata->data_val[idx];
+ spin_unlock(&drvdata->spinlock);
+ return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
+}
+
+static ssize_t etm_store_data_val(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+ unsigned long val;
+ uint8_t idx, data_idx;
+
+ if (sscanf(buf, "%lx", &val) != 1)
+ return -EINVAL;
+
+ spin_lock(&drvdata->spinlock);
+ idx = drvdata->addr_idx;
+ /* Adjust index to use the correct data comparator */
+ data_idx = idx >> 1;
+ /* Only idx = 0, 2, 4, 6... are valid */
+ if (idx % 2 != 0) {
+ spin_unlock(&drvdata->spinlock);
+ return -EPERM;
+ }
+ if (data_idx >= drvdata->nr_data_cmp) {
+ spin_unlock(&drvdata->spinlock);
+ return -EPERM;
+ }
+ if (!BVAL(drvdata->addr_acctype[idx], ETM_DATACMP_ENABLE)) {
+ spin_unlock(&drvdata->spinlock);
+ return -EPERM;
+ }
+ if (drvdata->addr_type[idx] == ETM_ADDR_TYPE_RANGE) {
+ if (!BVAL(drvdata->addr_acctype[idx + 1], ETM_DATACMP_ENABLE)) {
+ spin_unlock(&drvdata->spinlock);
+ return -EPERM;
+ }
+ }
+
+ drvdata->data_val[data_idx] = val;
+ spin_unlock(&drvdata->spinlock);
+ return size;
+}
+static DEVICE_ATTR(data_val, S_IRUGO | S_IWUSR, etm_show_data_val,
+ etm_store_data_val);
+
+static ssize_t etm_show_data_mask(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+ unsigned long mask;
+ uint8_t idx;
+
+ spin_lock(&drvdata->spinlock);
+ idx = drvdata->addr_idx;
+ if (idx % 2 != 0) {
+ spin_unlock(&drvdata->spinlock);
+ return -EPERM;
+ }
+ idx = idx >> 1;
+ if (idx >= drvdata->nr_data_cmp) {
+ spin_unlock(&drvdata->spinlock);
+ return -EPERM;
+ }
+
+ mask = drvdata->data_mask[idx];
+ spin_unlock(&drvdata->spinlock);
+ return scnprintf(buf, PAGE_SIZE, "%#lx\n", mask);
+}
+
+static ssize_t etm_store_data_mask(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
+ unsigned long mask;
+ uint8_t idx, data_idx;
+
+ if (sscanf(buf, "%lx", &mask) != 1)
+ return -EINVAL;
+
+ spin_lock(&drvdata->spinlock);
+ idx = drvdata->addr_idx;
+ /* Adjust index to use the correct data comparator */
+ data_idx = idx >> 1;
+ /* Only idx = 0, 2, 4, 6... are valid */
+ if (idx % 2 != 0) {
+ spin_unlock(&drvdata->spinlock);
+ return -EPERM;
+ }
+ if (data_idx >= drvdata->nr_data_cmp) {
+ spin_unlock(&drvdata->spinlock);
+ return -EPERM;
+ }
+ if (!BVAL(drvdata->addr_acctype[idx], ETM_DATACMP_ENABLE)) {
+ spin_unlock(&drvdata->spinlock);
+ return -EPERM;
+ }
+ if (drvdata->addr_type[idx] == ETM_ADDR_TYPE_RANGE) {
+ if (!BVAL(drvdata->addr_acctype[idx + 1], ETM_DATACMP_ENABLE)) {
+ spin_unlock(&drvdata->spinlock);
+ return -EPERM;
+ }
+ }
+
+ drvdata->data_mask[data_idx] = mask;
+ spin_unlock(&drvdata->spinlock);
+ return size;
+}
+static DEVICE_ATTR(data_mask, S_IRUGO | S_IWUSR, etm_show_data_mask,
+ etm_store_data_mask);
+
static ssize_t etm_show_cntr_idx(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -1604,6 +1806,8 @@
&dev_attr_addr_start.attr,
&dev_attr_addr_stop.attr,
&dev_attr_addr_acctype.attr,
+ &dev_attr_data_val.attr,
+ &dev_attr_data_mask.attr,
&dev_attr_cntr_idx.attr,
&dev_attr_cntr_rld_val.attr,
&dev_attr_cntr_event.attr,
@@ -1681,6 +1885,8 @@
switch (arch) {
case PFT_ARCH_V1_1:
break;
+ case ETM_ARCH_V3_5:
+ break;
default:
return false;
}
@@ -1691,6 +1897,7 @@
{
uint32_t etmidr;
uint32_t etmccr;
+ uint32_t etmcr;
struct etm_drvdata *drvdata = info;
ETM_UNLOCK(drvdata);
@@ -1721,6 +1928,19 @@
drvdata->nr_ext_inp = BMVAL(etmccr, 17, 19);
drvdata->nr_ext_out = BMVAL(etmccr, 20, 22);
drvdata->nr_ctxid_cmp = BMVAL(etmccr, 24, 25);
+ drvdata->nr_data_cmp = BMVAL(etmccr, 4, 7);
+
+ if (etm_version_gte(drvdata->arch, ETM_ARCH_V1_0)) {
+ etmcr = etm_readl(drvdata, ETMCR);
+ etmcr |= (BIT(2) | BIT(3));
+ etm_writel(drvdata, etmcr, ETMCR);
+ etmcr = etm_readl(drvdata, ETMCR);
+ if (BVAL(etmcr, 2) || BVAL(etmcr, 3))
+ drvdata->data_trace_support = true;
+ else
+ drvdata->data_trace_support = false;
+ } else
+ drvdata->data_trace_support = false;
etm_set_pwrdwn(drvdata);
ETM_LOCK(drvdata);
@@ -1734,6 +1954,8 @@
drvdata->nr_ext_inp = etmdrvdata[0]->nr_ext_inp;
drvdata->nr_ext_out = etmdrvdata[0]->nr_ext_out;
drvdata->nr_ctxid_cmp = etmdrvdata[0]->nr_ctxid_cmp;
+ drvdata->nr_data_cmp = etmdrvdata[0]->nr_data_cmp;
+ drvdata->data_trace_support = etmdrvdata[0]->data_trace_support;
}
static void __devinit etm_init_default_data(struct etm_drvdata *drvdata)
@@ -1749,6 +1971,10 @@
drvdata->addr_val[1] = (uint32_t) _etext;
drvdata->addr_type[0] = ETM_ADDR_TYPE_RANGE;
drvdata->addr_type[1] = ETM_ADDR_TYPE_RANGE;
+ if (etm_version_gte(drvdata->arch, ETM_ARCH_V1_0)) {
+ drvdata->addr_acctype[0] = 0x19;
+ drvdata->addr_acctype[1] = 0x19;
+ }
}
for (i = 0; i < drvdata->nr_cntr; i++) {
drvdata->cntr_event[i] = 0x406F;
@@ -1781,6 +2007,23 @@
drvdata->addr_type[i] = ETM_ADDR_TYPE_NONE;
}
}
+
+ if (etm_version_gte(drvdata->arch, ETM_ARCH_V1_0))
+ drvdata->ctrl |= BIT(11);
+ if (etm_version_gte(drvdata->arch, ETM_ARCH_V1_2))
+ drvdata->enable_ctrl2 = 0x0;
+ if (drvdata->data_trace_support == true) {
+ drvdata->mode |= (ETM_MODE_DATA_TRACE_VAL |
+ ETM_MODE_DATA_TRACE_ADDR);
+ drvdata->ctrl |= BIT(2) | BIT(3);
+ drvdata->viewdata_ctrl1 = 0x0;
+ drvdata->viewdata_ctrl3 = 0x10000;
+ drvdata->viewdata_event = 0x6F;
+ }
+ for (i = 0; i < drvdata->nr_data_cmp; i++) {
+ drvdata->data_val[i] = 0;
+ drvdata->data_mask[i] = ~(0);
+ }
}
static int __devinit etm_probe(struct platform_device *pdev)
diff --git a/drivers/coresight/coresight-tmc.c b/drivers/coresight/coresight-tmc.c
index 3bb9ec7..d49652f 100644
--- a/drivers/coresight/coresight-tmc.c
+++ b/drivers/coresight/coresight-tmc.c
@@ -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
@@ -26,6 +26,7 @@
#include <linux/delay.h>
#include <linux/spinlock.h>
#include <linux/clk.h>
+#include <linux/of.h>
#include <linux/of_coresight.h>
#include <linux/coresight.h>
#include <linux/usb/usb_qdss.h>
@@ -50,41 +51,45 @@
mb(); \
} while (0)
-#define TMC_RSZ (0x004)
-#define TMC_STS (0x00C)
-#define TMC_RRD (0x010)
-#define TMC_RRP (0x014)
-#define TMC_RWP (0x018)
-#define TMC_TRG (0x01C)
-#define TMC_CTL (0x020)
-#define TMC_RWD (0x024)
-#define TMC_MODE (0x028)
-#define TMC_LBUFLEVEL (0x02C)
-#define TMC_CBUFLEVEL (0x030)
-#define TMC_BUFWM (0x034)
-#define TMC_RRPHI (0x038)
-#define TMC_RWPHI (0x03C)
-#define TMC_AXICTL (0x110)
-#define TMC_DBALO (0x118)
-#define TMC_DBAHI (0x11C)
-#define TMC_FFSR (0x300)
-#define TMC_FFCR (0x304)
-#define TMC_PSCR (0x308)
-#define TMC_ITMISCOP0 (0xEE0)
-#define TMC_ITTRFLIN (0xEE8)
-#define TMC_ITATBDATA0 (0xEEC)
-#define TMC_ITATBCTR2 (0xEF0)
-#define TMC_ITATBCTR1 (0xEF4)
-#define TMC_ITATBCTR0 (0xEF8)
+#define TMC_RSZ (0x004)
+#define TMC_STS (0x00C)
+#define TMC_RRD (0x010)
+#define TMC_RRP (0x014)
+#define TMC_RWP (0x018)
+#define TMC_TRG (0x01C)
+#define TMC_CTL (0x020)
+#define TMC_RWD (0x024)
+#define TMC_MODE (0x028)
+#define TMC_LBUFLEVEL (0x02C)
+#define TMC_CBUFLEVEL (0x030)
+#define TMC_BUFWM (0x034)
+#define TMC_RRPHI (0x038)
+#define TMC_RWPHI (0x03C)
+#define TMC_AXICTL (0x110)
+#define TMC_DBALO (0x118)
+#define TMC_DBAHI (0x11C)
+#define TMC_FFSR (0x300)
+#define TMC_FFCR (0x304)
+#define TMC_PSCR (0x308)
+#define TMC_ITMISCOP0 (0xEE0)
+#define TMC_ITTRFLIN (0xEE8)
+#define TMC_ITATBDATA0 (0xEEC)
+#define TMC_ITATBCTR2 (0xEF0)
+#define TMC_ITATBCTR1 (0xEF4)
+#define TMC_ITATBCTR0 (0xEF8)
-#define BYTES_PER_WORD 4
-#define TMC_ETR_BAM_PIPE_INDEX 0
-#define TMC_ETR_BAM_NR_PIPES 2
+#define BYTES_PER_WORD 4
+#define TMC_ETR_BAM_PIPE_INDEX 0
+#define TMC_ETR_BAM_NR_PIPES 2
-#define TMC_ETFETB_DUMP_VER_OFF (4)
-#define TMC_ETFETB_DUMP_VER (1)
-#define TMC_REG_DUMP_VER_OFF (4)
-#define TMC_REG_DUMP_VER (1)
+#define TMC_ETFETB_DUMP_MAGIC_OFF (0)
+#define TMC_ETFETB_DUMP_MAGIC (0x5D1DB1BF)
+#define TMC_ETFETB_DUMP_VER_OFF (4)
+#define TMC_ETFETB_DUMP_VER (1)
+#define TMC_REG_DUMP_MAGIC_OFF (0)
+#define TMC_REG_DUMP_MAGIC (0x5D1DB1BF)
+#define TMC_REG_DUMP_VER_OFF (4)
+#define TMC_REG_DUMP_VER (1)
enum tmc_config_type {
TMC_CONFIG_TYPE_ETB,
@@ -134,6 +139,8 @@
struct mutex read_lock;
int read_count;
bool reading;
+ bool aborting;
+ char *reg_buf;
char *buf;
unsigned long paddr;
void __iomem *vaddr;
@@ -462,10 +469,64 @@
return tmc_enable(drvdata, TMC_MODE_HARDWARE_FIFO);
}
+static void __tmc_reg_dump(struct tmc_drvdata *drvdata)
+{
+ char *reg_hdr;
+ uint32_t *reg_buf;
+
+ if (!drvdata->reg_buf || !drvdata->aborting)
+ return;
+
+ reg_hdr = drvdata->reg_buf - PAGE_SIZE;
+ reg_buf = (uint32_t *)drvdata->reg_buf;
+
+ reg_buf[1] = tmc_readl(drvdata, TMC_RSZ);
+ reg_buf[3] = tmc_readl(drvdata, TMC_STS);
+ reg_buf[5] = tmc_readl(drvdata, TMC_RRP);
+ reg_buf[6] = tmc_readl(drvdata, TMC_RWP);
+ reg_buf[7] = tmc_readl(drvdata, TMC_TRG);
+ reg_buf[8] = tmc_readl(drvdata, TMC_CTL);
+ reg_buf[10] = tmc_readl(drvdata, TMC_MODE);
+ reg_buf[11] = tmc_readl(drvdata, TMC_LBUFLEVEL);
+ reg_buf[12] = tmc_readl(drvdata, TMC_CBUFLEVEL);
+ reg_buf[13] = tmc_readl(drvdata, TMC_BUFWM);
+ if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) {
+ reg_buf[14] = tmc_readl(drvdata, TMC_RRPHI);
+ reg_buf[15] = tmc_readl(drvdata, TMC_RWPHI);
+ reg_buf[68] = tmc_readl(drvdata, TMC_AXICTL);
+ reg_buf[70] = tmc_readl(drvdata, TMC_DBALO);
+ reg_buf[71] = tmc_readl(drvdata, TMC_DBAHI);
+ }
+ reg_buf[192] = tmc_readl(drvdata, TMC_FFSR);
+ reg_buf[193] = tmc_readl(drvdata, TMC_FFCR);
+ reg_buf[194] = tmc_readl(drvdata, TMC_PSCR);
+ reg_buf[1000] = tmc_readl(drvdata, CORESIGHT_CLAIMSET);
+ reg_buf[1001] = tmc_readl(drvdata, CORESIGHT_CLAIMCLR);
+ reg_buf[1005] = tmc_readl(drvdata, CORESIGHT_LSR);
+ reg_buf[1006] = tmc_readl(drvdata, CORESIGHT_AUTHSTATUS);
+ reg_buf[1010] = tmc_readl(drvdata, CORESIGHT_DEVID);
+ reg_buf[1011] = tmc_readl(drvdata, CORESIGHT_DEVTYPE);
+ reg_buf[1012] = tmc_readl(drvdata, CORESIGHT_PERIPHIDR4);
+ reg_buf[1013] = tmc_readl(drvdata, CORESIGHT_PERIPHIDR5);
+ reg_buf[1014] = tmc_readl(drvdata, CORESIGHT_PERIPHIDR6);
+ reg_buf[1015] = tmc_readl(drvdata, CORESIGHT_PERIPHIDR7);
+ reg_buf[1016] = tmc_readl(drvdata, CORESIGHT_PERIPHIDR0);
+ reg_buf[1017] = tmc_readl(drvdata, CORESIGHT_PERIPHIDR1);
+ reg_buf[1018] = tmc_readl(drvdata, CORESIGHT_PERIPHIDR2);
+ reg_buf[1019] = tmc_readl(drvdata, CORESIGHT_PERIPHIDR3);
+ reg_buf[1020] = tmc_readl(drvdata, CORESIGHT_COMPIDR0);
+ reg_buf[1021] = tmc_readl(drvdata, CORESIGHT_COMPIDR1);
+ reg_buf[1022] = tmc_readl(drvdata, CORESIGHT_COMPIDR2);
+ reg_buf[1023] = tmc_readl(drvdata, CORESIGHT_COMPIDR3);
+
+ *(uint32_t *)(reg_hdr + TMC_REG_DUMP_MAGIC_OFF) = TMC_REG_DUMP_MAGIC;
+}
+
static void __tmc_etb_dump(struct tmc_drvdata *drvdata)
{
enum tmc_mem_intf_width memwidth;
uint8_t memwords;
+ char *hdr;
char *bufp;
uint32_t read_data;
int i;
@@ -485,11 +546,18 @@
for (i = 0; i < memwords; i++) {
read_data = tmc_readl(drvdata, TMC_RRD);
if (read_data == 0xFFFFFFFF)
- return;
+ goto out;
memcpy(bufp, &read_data, BYTES_PER_WORD);
bufp += BYTES_PER_WORD;
}
}
+
+out:
+ if (drvdata->aborting) {
+ hdr = drvdata->buf - PAGE_SIZE;
+ *(uint32_t *)(hdr + TMC_ETFETB_DUMP_MAGIC_OFF) =
+ TMC_ETFETB_DUMP_MAGIC;
+ }
}
static void __tmc_etb_disable(struct tmc_drvdata *drvdata)
@@ -498,6 +566,7 @@
tmc_flush_and_stop(drvdata);
__tmc_etb_dump(drvdata);
+ __tmc_reg_dump(drvdata);
__tmc_disable(drvdata);
TMC_LOCK(drvdata);
@@ -522,6 +591,7 @@
tmc_flush_and_stop(drvdata);
__tmc_etr_dump(drvdata);
+ __tmc_reg_dump(drvdata);
__tmc_disable(drvdata);
TMC_LOCK(drvdata);
@@ -604,6 +674,8 @@
unsigned long flags;
enum tmc_mode mode;
+ drvdata->aborting = true;
+
spin_lock_irqsave(&drvdata->spinlock, flags);
if (drvdata->reading)
goto out0;
@@ -1040,10 +1112,18 @@
devid = tmc_readl(drvdata, CORESIGHT_DEVID);
drvdata->config_type = BMVAL(devid, 6, 7);
- if (drvdata->config_type == TMC_CONFIG_TYPE_ETR)
- drvdata->size = SZ_1M;
- else
+ if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) {
+ if (pdev->dev.of_node) {
+ ret = of_property_read_u32(pdev->dev.of_node,
+ "qcom,memory-reservation-size", &drvdata->size);
+ if (ret) {
+ clk_disable_unprepare(drvdata->clk);
+ return ret;
+ }
+ }
+ } else {
drvdata->size = tmc_readl(drvdata, TMC_RSZ) * BYTES_PER_WORD;
+ }
clk_disable_unprepare(drvdata->clk);
@@ -1077,8 +1157,10 @@
dump.start_addr = virt_to_phys(baddr);
dump.end_addr = dump.start_addr + PAGE_SIZE + drvdata->size;
ret = msm_dump_table_register(&dump);
- /* Don't free the buffer in case of error since it can still
- * be used to provide dump collection via the device node
+ /*
+ * Don't free the buffer in case of error since it can still
+ * be used to provide dump collection via the device node or
+ * as part of abort.
*/
if (ret)
dev_info(dev, "TMC ETF-ETB dump setup failed\n");
@@ -1087,15 +1169,19 @@
baddr = devm_kzalloc(dev, PAGE_SIZE + reg_size, GFP_KERNEL);
if (baddr) {
+ drvdata->reg_buf = baddr + PAGE_SIZE;
*(uint32_t *)(baddr + TMC_REG_DUMP_VER_OFF) = TMC_REG_DUMP_VER;
dump.id = MSM_TMC0_REG + count;
dump.start_addr = virt_to_phys(baddr);
dump.end_addr = dump.start_addr + PAGE_SIZE + reg_size;
ret = msm_dump_table_register(&dump);
- if (ret) {
- devm_kfree(dev, baddr);
+ /*
+ * Don't free the buffer in case of error since it can still
+ * be used to dump registers as part of abort to aid post crash
+ * parsing.
+ */
+ if (ret)
dev_info(dev, "TMC REG dump setup failed\n");
- }
} else {
dev_info(dev, "TMC REG dump space allocation failed\n");
}
diff --git a/drivers/coresight/coresight.c b/drivers/coresight/coresight.c
index 3974f2e..39bc9ff 100644
--- a/drivers/coresight/coresight.c
+++ b/drivers/coresight/coresight.c
@@ -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
@@ -566,6 +566,9 @@
struct coresight_device *csdev;
struct coresight_connection *conns;
+ if (IS_ERR_OR_NULL(desc))
+ return ERR_PTR(-EINVAL);
+
csdev = kzalloc(sizeof(*csdev), GFP_KERNEL);
if (!csdev) {
ret = -ENOMEM;
diff --git a/drivers/crypto/msm/qce.c b/drivers/crypto/msm/qce.c
index 4a9729c..7f8460b 100644
--- a/drivers/crypto/msm/qce.c
+++ b/drivers/crypto/msm/qce.c
@@ -1,6 +1,6 @@
/* Qualcomm Crypto Engine driver.
*
- * Copyright (c) 2010-2012, Code Aurora Forum. 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
@@ -474,11 +474,37 @@
{
int i;
- for (i = 0; nbytes > 0; i++, sg = sg_next(sg))
+ for (i = 0; nbytes > 0; i++, sg = scatterwalk_sg_next(sg))
nbytes -= sg->length;
return i;
}
+static int qce_dma_map_sg(struct device *dev, struct scatterlist *sg, int nents,
+ enum dma_data_direction direction)
+{
+ int i;
+
+ for (i = 0; i < nents; ++i) {
+ dma_map_sg(dev, sg, 1, direction);
+ sg = scatterwalk_sg_next(sg);
+ }
+
+ return nents;
+}
+
+static int qce_dma_unmap_sg(struct device *dev, struct scatterlist *sg,
+ int nents, enum dma_data_direction direction)
+{
+ int i;
+
+ for (i = 0; i < nents; ++i) {
+ dma_unmap_sg(dev, sg, 1, direction);
+ sg = scatterwalk_sg_next(sg);
+ }
+
+ return nents;
+}
+
static int dma_map_pmem_sg(struct buf_info *pmem, unsigned entries,
struct scatterlist *sg)
{
@@ -917,15 +943,15 @@
ivsize = crypto_aead_ivsize(aead);
if (areq->src != areq->dst) {
- dma_unmap_sg(pce_dev->pdev, areq->dst, pce_dev->dst_nents,
+ qce_dma_unmap_sg(pce_dev->pdev, areq->dst, pce_dev->dst_nents,
DMA_FROM_DEVICE);
}
- dma_unmap_sg(pce_dev->pdev, areq->src, pce_dev->src_nents,
+ qce_dma_unmap_sg(pce_dev->pdev, areq->src, pce_dev->src_nents,
(areq->src == areq->dst) ? DMA_BIDIRECTIONAL :
DMA_TO_DEVICE);
dma_unmap_single(pce_dev->pdev, pce_dev->phy_iv_in,
ivsize, DMA_TO_DEVICE);
- dma_unmap_sg(pce_dev->pdev, areq->assoc, pce_dev->assoc_nents,
+ qce_dma_unmap_sg(pce_dev->pdev, areq->assoc, pce_dev->assoc_nents,
DMA_TO_DEVICE);
/* check ce error status */
@@ -975,7 +1001,7 @@
uint32_t status;
areq = (struct ahash_request *) pce_dev->areq;
- dma_unmap_sg(pce_dev->pdev, areq->src, pce_dev->src_nents,
+ qce_dma_unmap_sg(pce_dev->pdev, areq->src, pce_dev->src_nents,
DMA_TO_DEVICE);
/* check ce error status */
@@ -1014,10 +1040,10 @@
areq = (struct ablkcipher_request *) pce_dev->areq;
if (areq->src != areq->dst) {
- dma_unmap_sg(pce_dev->pdev, areq->dst,
+ qce_dma_unmap_sg(pce_dev->pdev, areq->dst,
pce_dev->dst_nents, DMA_FROM_DEVICE);
}
- dma_unmap_sg(pce_dev->pdev, areq->src, pce_dev->src_nents,
+ qce_dma_unmap_sg(pce_dev->pdev, areq->src, pce_dev->src_nents,
(areq->src == areq->dst) ? DMA_BIDIRECTIONAL :
DMA_TO_DEVICE);
@@ -1178,7 +1204,7 @@
}
}
if (nbytes > 0)
- sg = sg_next(sg);
+ sg = scatterwalk_sg_next(sg);
}
return 0;
}
@@ -1341,7 +1367,7 @@
}
}
if (nbytes > 0)
- sg = sg_next(sg);
+ sg = scatterwalk_sg_next(sg);
}
return 0;
}
@@ -2041,7 +2067,7 @@
pce_dev->dst_nents = 0;
pce_dev->assoc_nents = count_sg(areq->assoc, areq->assoclen);
- dma_map_sg(pce_dev->pdev, areq->assoc, pce_dev->assoc_nents,
+ qce_dma_map_sg(pce_dev->pdev, areq->assoc, pce_dev->assoc_nents,
DMA_TO_DEVICE);
if (_chain_sg_buffer_in(pce_dev, areq->assoc, areq->assoclen) < 0) {
rc = -ENOMEM;
@@ -2065,7 +2091,7 @@
/* cipher input */
pce_dev->src_nents = count_sg(areq->src, q_req->cryptlen);
- dma_map_sg(pce_dev->pdev, areq->src, pce_dev->src_nents,
+ qce_dma_map_sg(pce_dev->pdev, areq->src, pce_dev->src_nents,
(areq->src == areq->dst) ? DMA_BIDIRECTIONAL :
DMA_TO_DEVICE);
if (_chain_sg_buffer_in(pce_dev, areq->src, q_req->cryptlen) < 0) {
@@ -2076,7 +2102,7 @@
/* cipher output */
if (areq->src != areq->dst) {
pce_dev->dst_nents = count_sg(areq->dst, q_req->cryptlen);
- dma_map_sg(pce_dev->pdev, areq->dst, pce_dev->dst_nents,
+ qce_dma_map_sg(pce_dev->pdev, areq->dst, pce_dev->dst_nents,
DMA_FROM_DEVICE);
};
if (_chain_sg_buffer_out(pce_dev, areq->dst, q_req->cryptlen) < 0) {
@@ -2119,20 +2145,20 @@
return 0;
bad:
if (pce_dev->assoc_nents) {
- dma_unmap_sg(pce_dev->pdev, areq->assoc, pce_dev->assoc_nents,
- DMA_TO_DEVICE);
+ qce_dma_unmap_sg(pce_dev->pdev, areq->assoc,
+ pce_dev->assoc_nents, DMA_TO_DEVICE);
}
if (pce_dev->phy_iv_in) {
dma_unmap_single(pce_dev->pdev, pce_dev->phy_iv_in,
ivsize, DMA_TO_DEVICE);
}
if (pce_dev->src_nents) {
- dma_unmap_sg(pce_dev->pdev, areq->src, pce_dev->src_nents,
+ qce_dma_unmap_sg(pce_dev->pdev, areq->src, pce_dev->src_nents,
(areq->src == areq->dst) ? DMA_BIDIRECTIONAL :
DMA_TO_DEVICE);
}
if (pce_dev->dst_nents) {
- dma_unmap_sg(pce_dev->pdev, areq->dst, pce_dev->dst_nents,
+ qce_dma_unmap_sg(pce_dev->pdev, areq->dst, pce_dev->dst_nents,
DMA_FROM_DEVICE);
}
return rc;
@@ -2158,7 +2184,7 @@
pce_dev->src_nents = count_sg(areq->src, areq->nbytes);
if (c_req->use_pmem != 1)
- dma_map_sg(pce_dev->pdev, areq->src, pce_dev->src_nents,
+ qce_dma_map_sg(pce_dev->pdev, areq->src, pce_dev->src_nents,
(areq->src == areq->dst) ? DMA_BIDIRECTIONAL :
DMA_TO_DEVICE);
else
@@ -2174,8 +2200,8 @@
if (areq->src != areq->dst) {
pce_dev->dst_nents = count_sg(areq->dst, areq->nbytes);
if (c_req->use_pmem != 1)
- dma_map_sg(pce_dev->pdev, areq->dst, pce_dev->dst_nents,
- DMA_FROM_DEVICE);
+ qce_dma_map_sg(pce_dev->pdev, areq->dst,
+ pce_dev->dst_nents, DMA_FROM_DEVICE);
else
dma_map_pmem_sg(&c_req->pmem->dst[0],
pce_dev->dst_nents, areq->dst);
@@ -2233,11 +2259,11 @@
bad:
if (c_req->use_pmem != 1) {
if (pce_dev->dst_nents) {
- dma_unmap_sg(pce_dev->pdev, areq->dst,
+ qce_dma_unmap_sg(pce_dev->pdev, areq->dst,
pce_dev->dst_nents, DMA_FROM_DEVICE);
}
if (pce_dev->src_nents) {
- dma_unmap_sg(pce_dev->pdev, areq->src,
+ qce_dma_unmap_sg(pce_dev->pdev, areq->src,
pce_dev->src_nents,
(areq->src == areq->dst) ?
DMA_BIDIRECTIONAL :
@@ -2257,7 +2283,7 @@
_chain_buffer_in_init(pce_dev);
pce_dev->src_nents = count_sg(sreq->src, sreq->size);
- dma_map_sg(pce_dev->pdev, sreq->src, pce_dev->src_nents,
+ qce_dma_map_sg(pce_dev->pdev, sreq->src, pce_dev->src_nents,
DMA_TO_DEVICE);
if (_chain_sg_buffer_in(pce_dev, sreq->src, sreq->size) < 0) {
@@ -2293,7 +2319,7 @@
return 0;
bad:
if (pce_dev->src_nents) {
- dma_unmap_sg(pce_dev->pdev, sreq->src,
+ qce_dma_unmap_sg(pce_dev->pdev, sreq->src,
pce_dev->src_nents, DMA_TO_DEVICE);
}
diff --git a/drivers/crypto/msm/qce40.c b/drivers/crypto/msm/qce40.c
index de060cc..84d41da 100644
--- a/drivers/crypto/msm/qce40.c
+++ b/drivers/crypto/msm/qce40.c
@@ -1,6 +1,6 @@
/* Qualcomm Crypto Engine driver.
*
- * Copyright (c) 2011 - 2012, Code Aurora Forum. 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
@@ -107,11 +107,37 @@
{
int i;
- for (i = 0; nbytes > 0; i++, sg = sg_next(sg))
+ for (i = 0; nbytes > 0; i++, sg = scatterwalk_sg_next(sg))
nbytes -= sg->length;
return i;
}
+static int qce_dma_map_sg(struct device *dev, struct scatterlist *sg, int nents,
+ enum dma_data_direction direction)
+{
+ int i;
+
+ for (i = 0; i < nents; ++i) {
+ dma_map_sg(dev, sg, 1, direction);
+ sg = scatterwalk_sg_next(sg);
+ }
+
+ return nents;
+}
+
+static int qce_dma_unmap_sg(struct device *dev, struct scatterlist *sg,
+ int nents, enum dma_data_direction direction)
+{
+ int i;
+
+ for (i = 0; i < nents; ++i) {
+ dma_unmap_sg(dev, sg, 1, direction);
+ sg = scatterwalk_sg_next(sg);
+ }
+
+ return nents;
+}
+
static int dma_map_pmem_sg(struct buf_info *pmem, unsigned entries,
struct scatterlist *sg)
{
@@ -670,14 +696,14 @@
areq = (struct aead_request *) pce_dev->areq;
if (areq->src != areq->dst) {
- dma_unmap_sg(pce_dev->pdev, areq->dst, pce_dev->dst_nents,
+ qce_dma_unmap_sg(pce_dev->pdev, areq->dst, pce_dev->dst_nents,
DMA_FROM_DEVICE);
}
- dma_unmap_sg(pce_dev->pdev, areq->src, pce_dev->src_nents,
+ qce_dma_unmap_sg(pce_dev->pdev, areq->src, pce_dev->src_nents,
(areq->src == areq->dst) ? DMA_BIDIRECTIONAL :
DMA_TO_DEVICE);
- dma_unmap_sg(pce_dev->pdev, areq->assoc, pce_dev->assoc_nents,
+ qce_dma_unmap_sg(pce_dev->pdev, areq->assoc, pce_dev->assoc_nents,
DMA_TO_DEVICE);
/* check MAC */
@@ -700,7 +726,7 @@
struct ahash_request *areq;
areq = (struct ahash_request *) pce_dev->areq;
- dma_unmap_sg(pce_dev->pdev, areq->src, pce_dev->src_nents,
+ qce_dma_unmap_sg(pce_dev->pdev, areq->src, pce_dev->src_nents,
DMA_TO_DEVICE);
pce_dev->qce_cb(areq, pce_dev->ce_dm.buffer.auth_result,
@@ -716,10 +742,10 @@
areq = (struct ablkcipher_request *) pce_dev->areq;
if (areq->src != areq->dst) {
- dma_unmap_sg(pce_dev->pdev, areq->dst,
+ qce_dma_unmap_sg(pce_dev->pdev, areq->dst,
pce_dev->dst_nents, DMA_FROM_DEVICE);
}
- dma_unmap_sg(pce_dev->pdev, areq->src, pce_dev->src_nents,
+ qce_dma_unmap_sg(pce_dev->pdev, areq->src, pce_dev->src_nents,
(areq->src == areq->dst) ? DMA_BIDIRECTIONAL :
DMA_TO_DEVICE);
@@ -826,7 +852,7 @@
&pce_dev->ce_dm.ce_in_src_desc_index);
}
if (nbytes > 0)
- sg = sg_next(sg);
+ sg = scatterwalk_sg_next(sg);
}
return 0;
}
@@ -989,7 +1015,7 @@
}
if (nbytes > 0)
- sg = sg_next(sg);
+ sg = scatterwalk_sg_next(sg);
}
return 0;
}
@@ -2149,7 +2175,7 @@
/* associated data input */
pce_dev->assoc_nents = count_sg(areq->assoc, areq->assoclen);
- dma_map_sg(pce_dev->pdev, areq->assoc, pce_dev->assoc_nents,
+ qce_dma_map_sg(pce_dev->pdev, areq->assoc, pce_dev->assoc_nents,
DMA_TO_DEVICE);
if (_chain_sg_buffer_in(pce_dev, areq->assoc, areq->assoclen) < 0) {
rc = -ENOMEM;
@@ -2157,7 +2183,7 @@
}
/* cipher input */
pce_dev->src_nents = count_sg(areq->src, areq->cryptlen);
- dma_map_sg(pce_dev->pdev, areq->src, pce_dev->src_nents,
+ qce_dma_map_sg(pce_dev->pdev, areq->src, pce_dev->src_nents,
(areq->src == areq->dst) ? DMA_BIDIRECTIONAL :
DMA_TO_DEVICE);
if (_chain_sg_buffer_in(pce_dev, areq->src, areq->cryptlen) < 0) {
@@ -2182,7 +2208,7 @@
/* cipher + mac output for encryption */
if (areq->src != areq->dst) {
pce_dev->dst_nents = count_sg(areq->dst, out_len);
- dma_map_sg(pce_dev->pdev, areq->dst, pce_dev->dst_nents,
+ qce_dma_map_sg(pce_dev->pdev, areq->dst, pce_dev->dst_nents,
DMA_FROM_DEVICE);
};
if (_chain_sg_buffer_out(pce_dev, areq->dst, out_len) < 0) {
@@ -2222,17 +2248,17 @@
return 0;
bad:
if (pce_dev->assoc_nents) {
- dma_unmap_sg(pce_dev->pdev, areq->assoc, pce_dev->assoc_nents,
- DMA_TO_DEVICE);
+ qce_dma_unmap_sg(pce_dev->pdev, areq->assoc,
+ pce_dev->assoc_nents, DMA_TO_DEVICE);
}
if (pce_dev->src_nents) {
- dma_unmap_sg(pce_dev->pdev, areq->src, pce_dev->src_nents,
+ qce_dma_unmap_sg(pce_dev->pdev, areq->src, pce_dev->src_nents,
(areq->src == areq->dst) ? DMA_BIDIRECTIONAL :
DMA_TO_DEVICE);
}
if (pce_dev->dst_nents) {
- dma_unmap_sg(pce_dev->pdev, areq->dst, pce_dev->dst_nents,
+ qce_dma_unmap_sg(pce_dev->pdev, areq->dst, pce_dev->dst_nents,
DMA_FROM_DEVICE);
}
return rc;
@@ -2259,7 +2285,7 @@
pce_dev->src_nents = count_sg(areq->src, areq->nbytes);
if (c_req->use_pmem != 1)
- dma_map_sg(pce_dev->pdev, areq->src, pce_dev->src_nents,
+ qce_dma_map_sg(pce_dev->pdev, areq->src, pce_dev->src_nents,
(areq->src == areq->dst) ? DMA_BIDIRECTIONAL :
DMA_TO_DEVICE);
else
@@ -2275,8 +2301,8 @@
if (areq->src != areq->dst) {
pce_dev->dst_nents = count_sg(areq->dst, areq->nbytes);
if (c_req->use_pmem != 1)
- dma_map_sg(pce_dev->pdev, areq->dst, pce_dev->dst_nents,
- DMA_FROM_DEVICE);
+ qce_dma_map_sg(pce_dev->pdev, areq->dst,
+ pce_dev->dst_nents, DMA_FROM_DEVICE);
else
dma_map_pmem_sg(&c_req->pmem->dst[0],
pce_dev->dst_nents, areq->dst);
@@ -2333,11 +2359,11 @@
bad:
if (c_req->use_pmem != 1) {
if (pce_dev->dst_nents) {
- dma_unmap_sg(pce_dev->pdev, areq->dst,
+ qce_dma_unmap_sg(pce_dev->pdev, areq->dst,
pce_dev->dst_nents, DMA_FROM_DEVICE);
}
if (pce_dev->src_nents) {
- dma_unmap_sg(pce_dev->pdev, areq->src,
+ qce_dma_unmap_sg(pce_dev->pdev, areq->src,
pce_dev->src_nents,
(areq->src == areq->dst) ?
DMA_BIDIRECTIONAL :
@@ -2358,7 +2384,7 @@
_chain_buffer_in_init(pce_dev);
pce_dev->src_nents = count_sg(sreq->src, sreq->size);
- dma_map_sg(pce_dev->pdev, sreq->src, pce_dev->src_nents,
+ qce_dma_map_sg(pce_dev->pdev, sreq->src, pce_dev->src_nents,
DMA_TO_DEVICE);
if (_chain_sg_buffer_in(pce_dev, sreq->src, sreq->size) < 0) {
@@ -2392,7 +2418,7 @@
return 0;
bad:
if (pce_dev->src_nents) {
- dma_unmap_sg(pce_dev->pdev, sreq->src,
+ qce_dma_unmap_sg(pce_dev->pdev, sreq->src,
pce_dev->src_nents, DMA_TO_DEVICE);
}
diff --git a/drivers/crypto/msm/qce50.c b/drivers/crypto/msm/qce50.c
index d605a61..ab9e83a 100644
--- a/drivers/crypto/msm/qce50.c
+++ b/drivers/crypto/msm/qce50.c
@@ -1,6 +1,6 @@
/* Qualcomm Crypto Engine driver.
*
- * Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2012-2013, Code Aurora Forum. 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
@@ -1248,8 +1248,27 @@
notify->data.transfer.iovec.addr,
notify->data.transfer.iovec.size,
notify->data.transfer.iovec.flags);
- /* done */
- _aead_complete(pce_dev);
+
+ if (pce_dev->ce_sps.producer_state == QCE_PIPE_STATE_COMP) {
+ pce_dev->ce_sps.producer_state = QCE_PIPE_STATE_IDLE;
+ /* done */
+ _aead_complete(pce_dev);
+ } else {
+ int rc = 0;
+ pce_dev->ce_sps.producer_state = QCE_PIPE_STATE_COMP;
+ pce_dev->ce_sps.out_transfer.iovec_count = 0;
+ _qce_sps_add_data(GET_PHYS_ADDR(pce_dev->ce_sps.result_dump),
+ CRYPTO_RESULT_DUMP_SIZE,
+ &pce_dev->ce_sps.out_transfer);
+ _qce_set_flag(&pce_dev->ce_sps.out_transfer,
+ SPS_IOVEC_FLAG_EOT|SPS_IOVEC_FLAG_INT);
+ rc = sps_transfer(pce_dev->ce_sps.producer.pipe,
+ &pce_dev->ce_sps.out_transfer);
+ if (rc) {
+ pr_err("sps_xfr() fail (producer pipe=0x%x) rc = %d,",
+ (u32)pce_dev->ce_sps.producer.pipe, rc);
+ }
+ }
};
static void _aead_sps_consumer_callback(struct sps_event_notify *notify)
@@ -1304,8 +1323,27 @@
notify->data.transfer.iovec.addr,
notify->data.transfer.iovec.size,
notify->data.transfer.iovec.flags);
- /* done */
- _ablk_cipher_complete(pce_dev);
+
+ if (pce_dev->ce_sps.producer_state == QCE_PIPE_STATE_COMP) {
+ pce_dev->ce_sps.producer_state = QCE_PIPE_STATE_IDLE;
+ /* done */
+ _ablk_cipher_complete(pce_dev);
+ } else {
+ int rc = 0;
+ pce_dev->ce_sps.producer_state = QCE_PIPE_STATE_COMP;
+ pce_dev->ce_sps.out_transfer.iovec_count = 0;
+ _qce_sps_add_data(GET_PHYS_ADDR(pce_dev->ce_sps.result_dump),
+ CRYPTO_RESULT_DUMP_SIZE,
+ &pce_dev->ce_sps.out_transfer);
+ _qce_set_flag(&pce_dev->ce_sps.out_transfer,
+ SPS_IOVEC_FLAG_EOT|SPS_IOVEC_FLAG_INT);
+ rc = sps_transfer(pce_dev->ce_sps.producer.pipe,
+ &pce_dev->ce_sps.out_transfer);
+ if (rc) {
+ pr_err("sps_xfr() fail (producer pipe=0x%x) rc = %d,",
+ (u32)pce_dev->ce_sps.producer.pipe, rc);
+ }
+ }
};
static void _ablk_cipher_sps_consumer_callback(struct sps_event_notify *notify)
@@ -2255,7 +2293,6 @@
pr_err("Producer callback registration failed rc = %d\n", rc);
goto bad;
}
-
/* Register callback event for EOT (End of transfer) event. */
pce_dev->ce_sps.consumer.event.callback = _aead_sps_consumer_callback;
rc = sps_register_event(pce_dev->ce_sps.consumer.pipe,
@@ -2280,11 +2317,21 @@
_qce_sps_add_sg_data(pce_dev, areq->dst, out_len +
areq->assoclen + hw_pad_out,
&pce_dev->ce_sps.out_transfer);
- _qce_sps_add_data(GET_PHYS_ADDR(pce_dev->ce_sps.result_dump),
+ if (totallen_in > SPS_MAX_PKT_SIZE) {
+ _qce_set_flag(&pce_dev->ce_sps.out_transfer,
+ SPS_IOVEC_FLAG_INT);
+ pce_dev->ce_sps.producer.event.options =
+ SPS_O_DESC_DONE;
+ pce_dev->ce_sps.producer_state = QCE_PIPE_STATE_IDLE;
+ } else {
+ _qce_sps_add_data(GET_PHYS_ADDR(
+ pce_dev->ce_sps.result_dump),
CRYPTO_RESULT_DUMP_SIZE,
- &pce_dev->ce_sps.out_transfer);
- _qce_set_flag(&pce_dev->ce_sps.out_transfer,
- SPS_IOVEC_FLAG_INT);
+ &pce_dev->ce_sps.out_transfer);
+ _qce_set_flag(&pce_dev->ce_sps.out_transfer,
+ SPS_IOVEC_FLAG_INT);
+ pce_dev->ce_sps.producer_state = QCE_PIPE_STATE_COMP;
+ }
} else {
_qce_sps_add_sg_data(pce_dev, areq->assoc, areq->assoclen,
&pce_dev->ce_sps.in_transfer);
@@ -2304,11 +2351,21 @@
/* Pass through to ignore hw_pad (padding of the MAC data) */
_qce_sps_add_data(GET_PHYS_ADDR(pce_dev->ce_sps.ignore_buffer),
hw_pad_out, &pce_dev->ce_sps.out_transfer);
-
- _qce_sps_add_data(GET_PHYS_ADDR(pce_dev->ce_sps.result_dump),
- CRYPTO_RESULT_DUMP_SIZE, &pce_dev->ce_sps.out_transfer);
- _qce_set_flag(&pce_dev->ce_sps.out_transfer,
- SPS_IOVEC_FLAG_INT);
+ if (totallen_in > SPS_MAX_PKT_SIZE) {
+ _qce_set_flag(&pce_dev->ce_sps.out_transfer,
+ SPS_IOVEC_FLAG_INT);
+ pce_dev->ce_sps.producer.event.options =
+ SPS_O_DESC_DONE;
+ pce_dev->ce_sps.producer_state = QCE_PIPE_STATE_IDLE;
+ } else {
+ _qce_sps_add_data(
+ GET_PHYS_ADDR(pce_dev->ce_sps.result_dump),
+ CRYPTO_RESULT_DUMP_SIZE,
+ &pce_dev->ce_sps.out_transfer);
+ _qce_set_flag(&pce_dev->ce_sps.out_transfer,
+ SPS_IOVEC_FLAG_INT);
+ pce_dev->ce_sps.producer_state = QCE_PIPE_STATE_COMP;
+ }
}
rc = _qce_sps_transfer(pce_dev);
if (rc)
@@ -2392,7 +2449,6 @@
pr_err("Producer callback registration failed rc = %d\n", rc);
goto bad;
}
-
/* Register callback event for EOT (End of transfer) event. */
pce_dev->ce_sps.consumer.event.callback =
_ablk_cipher_sps_consumer_callback;
@@ -2414,10 +2470,19 @@
_qce_sps_add_sg_data(pce_dev, areq->dst, areq->nbytes,
&pce_dev->ce_sps.out_transfer);
- _qce_sps_add_data(GET_PHYS_ADDR(pce_dev->ce_sps.result_dump),
+ if (areq->nbytes > SPS_MAX_PKT_SIZE) {
+ _qce_set_flag(&pce_dev->ce_sps.out_transfer,
+ SPS_IOVEC_FLAG_INT);
+ pce_dev->ce_sps.producer.event.options = SPS_O_DESC_DONE;
+ pce_dev->ce_sps.producer_state = QCE_PIPE_STATE_IDLE;
+ } else {
+ pce_dev->ce_sps.producer_state = QCE_PIPE_STATE_COMP;
+ _qce_sps_add_data(GET_PHYS_ADDR(pce_dev->ce_sps.result_dump),
CRYPTO_RESULT_DUMP_SIZE,
&pce_dev->ce_sps.out_transfer);
- _qce_set_flag(&pce_dev->ce_sps.out_transfer, SPS_IOVEC_FLAG_INT);
+ _qce_set_flag(&pce_dev->ce_sps.out_transfer,
+ SPS_IOVEC_FLAG_INT);
+ }
rc = _qce_sps_transfer(pce_dev);
if (rc)
goto bad;
diff --git a/drivers/crypto/msm/qcrypto.c b/drivers/crypto/msm/qcrypto.c
index 10f83f3..b64368d 100644
--- a/drivers/crypto/msm/qcrypto.c
+++ b/drivers/crypto/msm/qcrypto.c
@@ -1,6 +1,6 @@
/* Qualcomm Crypto driver
*
- * Copyright (c) 2010-2012, Code Aurora Forum. 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
@@ -1043,7 +1043,7 @@
}
adata += len;
qreq->assoclen = ALIGN((alen + len), 16);
- for (l = alen; l > 0; sg = sg_next(sg)) {
+ for (l = alen; l > 0; sg = scatterwalk_sg_next(sg)) {
memcpy(adata, sg_virt(sg), sg->length);
l -= sg->length;
adata += sg->length;
@@ -2118,7 +2118,7 @@
{
int i;
- for (i = 0; nbytes > 0; i++, sg = sg_next(sg))
+ for (i = 0; nbytes > 0; i++, sg = scatterwalk_sg_next(sg))
nbytes -= sg->length;
return i;
diff --git a/drivers/crypto/msm/qcryptohw_50.h b/drivers/crypto/msm/qcryptohw_50.h
index d77311d..0fb924f 100644
--- a/drivers/crypto/msm/qcryptohw_50.h
+++ b/drivers/crypto/msm/qcryptohw_50.h
@@ -59,77 +59,78 @@
#define CRYPTO_ENCR_XTS_KEY6_REG 0x1D038
#define CRYPTO_ENCR_XTS_KEY7_REG 0x1D03C
-#define CRYPTO_ENCR_PIP0_KEY0_REG 0x1E000
-#define CRYPTO_ENCR_PIP0_KEY1_REG 0x1E004
-#define CRYPTO_ENCR_PIP0_KEY2_REG 0x1E008
-#define CRYPTO_ENCR_PIP0_KEY3_REG 0x1E00C
-#define CRYPTO_ENCR_PIP0_KEY4_REG 0x1E010
-#define CRYPTO_ENCR_PIP0_KEY5_REG 0x1E004
-#define CRYPTO_ENCR_PIP0_KEY6_REG 0x1E008
-#define CRYPTO_ENCR_PIP0_KEY7_REG 0x1E00C
+#define CRYPTO_ENCR_PIPE0_KEY0_REG 0x1E000
+#define CRYPTO_ENCR_PIPE0_KEY1_REG 0x1E004
+#define CRYPTO_ENCR_PIPE0_KEY2_REG 0x1E008
+#define CRYPTO_ENCR_PIPE0_KEY3_REG 0x1E00C
+#define CRYPTO_ENCR_PIPE0_KEY4_REG 0x1E010
+#define CRYPTO_ENCR_PIPE0_KEY5_REG 0x1E004
+#define CRYPTO_ENCR_PIPE0_KEY6_REG 0x1E008
+#define CRYPTO_ENCR_PIPE0_KEY7_REG 0x1E00C
-#define CRYPTO_ENCR_PIP1_KEY0_REG 0x1E000
-#define CRYPTO_ENCR_PIP1_KEY1_REG 0x1E004
-#define CRYPTO_ENCR_PIP1_KEY2_REG 0x1E008
-#define CRYPTO_ENCR_PIP1_KEY3_REG 0x1E00C
-#define CRYPTO_ENCR_PIP1_KEY4_REG 0x1E010
-#define CRYPTO_ENCR_PIP1_KEY5_REG 0x1E014
-#define CRYPTO_ENCR_PIP1_KEY6_REG 0x1E018
-#define CRYPTO_ENCR_PIP1_KEY7_REG 0x1E01C
+#define CRYPTO_ENCR_PIPE1_KEY0_REG 0x1E020
+#define CRYPTO_ENCR_PIPE1_KEY1_REG 0x1E024
+#define CRYPTO_ENCR_PIPE1_KEY2_REG 0x1E028
+#define CRYPTO_ENCR_PIPE1_KEY3_REG 0x1E02C
+#define CRYPTO_ENCR_PIPE1_KEY4_REG 0x1E030
+#define CRYPTO_ENCR_PIPE1_KEY5_REG 0x1E034
+#define CRYPTO_ENCR_PIPE1_KEY6_REG 0x1E038
+#define CRYPTO_ENCR_PIPE1_KEY7_REG 0x1E03C
-#define CRYPTO_ENCR_PIP2_KEY0_REG 0x1E020
-#define CRYPTO_ENCR_PIP2_KEY1_REG 0x1E024
-#define CRYPTO_ENCR_PIP2_KEY2_REG 0x1E028
-#define CRYPTO_ENCR_PIP2_KEY3_REG 0x1E02C
-#define CRYPTO_ENCR_PIP2_KEY4_REG 0x1E030
-#define CRYPTO_ENCR_PIP2_KEY5_REG 0x1E034
-#define CRYPTO_ENCR_PIP2_KEY6_REG 0x1E038
-#define CRYPTO_ENCR_PIP2_KEY7_REG 0x1E03C
+#define CRYPTO_ENCR_PIPE2_KEY0_REG 0x1E040
+#define CRYPTO_ENCR_PIPE2_KEY1_REG 0x1E044
+#define CRYPTO_ENCR_PIPE2_KEY2_REG 0x1E048
+#define CRYPTO_ENCR_PIPE2_KEY3_REG 0x1E04C
+#define CRYPTO_ENCR_PIPE2_KEY4_REG 0x1E050
+#define CRYPTO_ENCR_PIPE2_KEY5_REG 0x1E054
+#define CRYPTO_ENCR_PIPE2_KEY6_REG 0x1E058
+#define CRYPTO_ENCR_PIPE2_KEY7_REG 0x1E05C
-#define CRYPTO_ENCR_PIP3_KEY0_REG 0x1E040
-#define CRYPTO_ENCR_PIP3_KEY1_REG 0x1E044
-#define CRYPTO_ENCR_PIP3_KEY2_REG 0x1E048
-#define CRYPTO_ENCR_PIP3_KEY3_REG 0x1E04C
-#define CRYPTO_ENCR_PIP3_KEY4_REG 0x1E050
-#define CRYPTO_ENCR_PIP3_KEY5_REG 0x1E054
-#define CRYPTO_ENCR_PIP3_KEY6_REG 0x1E058
-#define CRYPTO_ENCR_PIP3_KEY7_REG 0x1E05C
+#define CRYPTO_ENCR_PIPE3_KEY0_REG 0x1E060
+#define CRYPTO_ENCR_PIPE3_KEY1_REG 0x1E064
+#define CRYPTO_ENCR_PIPE3_KEY2_REG 0x1E068
+#define CRYPTO_ENCR_PIPE3_KEY3_REG 0x1E06C
+#define CRYPTO_ENCR_PIPE3_KEY4_REG 0x1E070
+#define CRYPTO_ENCR_PIPE3_KEY5_REG 0x1E074
+#define CRYPTO_ENCR_PIPE3_KEY6_REG 0x1E078
+#define CRYPTO_ENCR_PIPE3_KEY7_REG 0x1E07C
-#define CRYPTO_ENCR_PIP0_XTS_KEY0_REG 0x1E200
-#define CRYPTO_ENCR_PIP0_XTS_KEY1_REG 0x1E204
-#define CRYPTO_ENCR_PIP0_XTS_KEY2_REG 0x1E208
-#define CRYPTO_ENCR_PIP0_XTS_KEY3_REG 0x1E20C
-#define CRYPTO_ENCR_PIP0_XTS_KEY4_REG 0x1E210
-#define CRYPTO_ENCR_PIP0_XTS_KEY5_REG 0x1E214
-#define CRYPTO_ENCR_PIP0_XTS_KEY6_REG 0x1E218
-#define CRYPTO_ENCR_PIP0_XTS_KEY7_REG 0x1E21C
-#define CRYPTO_ENCR_PIP1_XTS_KEY0_REG 0x1E220
-#define CRYPTO_ENCR_PIP1_XTS_KEY1_REG 0x1E224
-#define CRYPTO_ENCR_PIP1_XTS_KEY2_REG 0x1E228
-#define CRYPTO_ENCR_PIP1_XTS_KEY3_REG 0x1E22C
-#define CRYPTO_ENCR_PIP1_XTS_KEY4_REG 0x1E230
-#define CRYPTO_ENCR_PIP1_XTS_KEY5_REG 0x1E234
-#define CRYPTO_ENCR_PIP1_XTS_KEY6_REG 0x1E238
-#define CRYPTO_ENCR_PIP1_XTS_KEY7_REG 0x1E23C
+#define CRYPTO_ENCR_PIPE0_XTS_KEY0_REG 0x1E200
+#define CRYPTO_ENCR_PIPE0_XTS_KEY1_REG 0x1E204
+#define CRYPTO_ENCR_PIPE0_XTS_KEY2_REG 0x1E208
+#define CRYPTO_ENCR_PIPE0_XTS_KEY3_REG 0x1E20C
+#define CRYPTO_ENCR_PIPE0_XTS_KEY4_REG 0x1E210
+#define CRYPTO_ENCR_PIPE0_XTS_KEY5_REG 0x1E214
+#define CRYPTO_ENCR_PIPE0_XTS_KEY6_REG 0x1E218
+#define CRYPTO_ENCR_PIPE0_XTS_KEY7_REG 0x1E21C
-#define CRYPTO_ENCR_PIP2_XTS_KEY0_REG 0x1E240
-#define CRYPTO_ENCR_PIP2_XTS_KEY1_REG 0x1E244
-#define CRYPTO_ENCR_PIP2_XTS_KEY2_REG 0x1E248
-#define CRYPTO_ENCR_PIP2_XTS_KEY3_REG 0x1E24C
-#define CRYPTO_ENCR_PIP2_XTS_KEY4_REG 0x1E250
-#define CRYPTO_ENCR_PIP2_XTS_KEY5_REG 0x1E254
-#define CRYPTO_ENCR_PIP2_XTS_KEY6_REG 0x1E258
-#define CRYPTO_ENCR_PIP2_XTS_KEY7_REG 0x1E25C
+#define CRYPTO_ENCR_PIPE1_XTS_KEY0_REG 0x1E220
+#define CRYPTO_ENCR_PIPE1_XTS_KEY1_REG 0x1E224
+#define CRYPTO_ENCR_PIPE1_XTS_KEY2_REG 0x1E228
+#define CRYPTO_ENCR_PIPE1_XTS_KEY3_REG 0x1E22C
+#define CRYPTO_ENCR_PIPE1_XTS_KEY4_REG 0x1E230
+#define CRYPTO_ENCR_PIPE1_XTS_KEY5_REG 0x1E234
+#define CRYPTO_ENCR_PIPE1_XTS_KEY6_REG 0x1E238
+#define CRYPTO_ENCR_PIPE1_XTS_KEY7_REG 0x1E23C
-#define CRYPTO_ENCR_PIP3_XTS_KEY0_REG 0x1E260
-#define CRYPTO_ENCR_PIP3_XTS_KEY1_REG 0x1E264
-#define CRYPTO_ENCR_PIP3_XTS_KEY2_REG 0x1E268
-#define CRYPTO_ENCR_PIP3_XTS_KEY3_REG 0x1E26C
-#define CRYPTO_ENCR_PIP3_XTS_KEY4_REG 0x1E270
-#define CRYPTO_ENCR_PIP3_XTS_KEY5_REG 0x1E274
-#define CRYPTO_ENCR_PIP3_XTS_KEY6_REG 0x1E278
-#define CRYPTO_ENCR_PIP3_XTS_KEY7_REG 0x1E27C
+#define CRYPTO_ENCR_PIPE2_XTS_KEY0_REG 0x1E240
+#define CRYPTO_ENCR_PIPE2_XTS_KEY1_REG 0x1E244
+#define CRYPTO_ENCR_PIPE2_XTS_KEY2_REG 0x1E248
+#define CRYPTO_ENCR_PIPE2_XTS_KEY3_REG 0x1E24C
+#define CRYPTO_ENCR_PIPE2_XTS_KEY4_REG 0x1E250
+#define CRYPTO_ENCR_PIPE2_XTS_KEY5_REG 0x1E254
+#define CRYPTO_ENCR_PIPE2_XTS_KEY6_REG 0x1E258
+#define CRYPTO_ENCR_PIPE2_XTS_KEY7_REG 0x1E25C
+
+#define CRYPTO_ENCR_PIPE3_XTS_KEY0_REG 0x1E260
+#define CRYPTO_ENCR_PIPE3_XTS_KEY1_REG 0x1E264
+#define CRYPTO_ENCR_PIPE3_XTS_KEY2_REG 0x1E268
+#define CRYPTO_ENCR_PIPE3_XTS_KEY3_REG 0x1E26C
+#define CRYPTO_ENCR_PIPE3_XTS_KEY4_REG 0x1E270
+#define CRYPTO_ENCR_PIPE3_XTS_KEY5_REG 0x1E274
+#define CRYPTO_ENCR_PIPE3_XTS_KEY6_REG 0x1E278
+#define CRYPTO_ENCR_PIPE3_XTS_KEY7_REG 0x1E27C
#define CRYPTO_CNTR0_IV0_REG 0x1A20C
diff --git a/drivers/gpu/ion/ion_cp_heap.c b/drivers/gpu/ion/ion_cp_heap.c
index d96b755..8f5addd 100644
--- a/drivers/gpu/ion/ion_cp_heap.c
+++ b/drivers/gpu/ion/ion_cp_heap.c
@@ -2,7 +2,7 @@
* drivers/gpu/ion/ion_cp_heap.c
*
* Copyright (C) 2011 Google, Inc.
- * Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@@ -469,6 +469,7 @@
{
unsigned long offset;
unsigned long secure_allocation = flags & ION_SECURE;
+ unsigned long force_contig = flags & ION_FORCE_CONTIGUOUS;
struct ion_cp_heap *cp_heap =
container_of(heap, struct ion_cp_heap, heap);
@@ -481,7 +482,8 @@
return ION_CP_ALLOCATE_FAIL;
}
- if (!secure_allocation && cp_heap->disallow_non_secure_allocation) {
+ if (!force_contig && !secure_allocation &&
+ cp_heap->disallow_non_secure_allocation) {
mutex_unlock(&cp_heap->lock);
pr_debug("%s: non-secure allocation disallowed from this heap\n",
__func__);
diff --git a/drivers/gpu/msm/Makefile b/drivers/gpu/msm/Makefile
index aacd355..fec5363 100644
--- a/drivers/gpu/msm/Makefile
+++ b/drivers/gpu/msm/Makefile
@@ -9,7 +9,8 @@
kgsl_mmu.o \
kgsl_gpummu.o \
kgsl_iommu.o \
- kgsl_snapshot.o
+ kgsl_snapshot.o \
+ kgsl_events.o
msm_kgsl_core-$(CONFIG_DEBUG_FS) += kgsl_debugfs.o
msm_kgsl_core-$(CONFIG_MSM_KGSL_CFF_DUMP) += kgsl_cffdump.o
diff --git a/drivers/gpu/msm/a3xx_reg.h b/drivers/gpu/msm/a3xx_reg.h
index 90f14e6..4a47c59 100644
--- a/drivers/gpu/msm/a3xx_reg.h
+++ b/drivers/gpu/msm/a3xx_reg.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. 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
@@ -67,6 +67,10 @@
#define A3XX_RBBM_INT_0_STATUS 0x064
#define A3XX_RBBM_PERFCTR_CTL 0x80
#define A3XX_RBBM_GPU_BUSY_MASKED 0x88
+#define A3XX_RBBM_PERFCTR_SP_5_LO 0xDC
+#define A3XX_RBBM_PERFCTR_SP_5_HI 0xDD
+#define A3XX_RBBM_PERFCTR_SP_6_LO 0xDE
+#define A3XX_RBBM_PERFCTR_SP_6_HI 0xDF
#define A3XX_RBBM_PERFCTR_SP_7_LO 0xE0
#define A3XX_RBBM_PERFCTR_SP_7_HI 0xE1
#define A3XX_RBBM_RBBM_CTL 0x100
@@ -164,6 +168,8 @@
#define A3XX_VPC_VPC_DEBUG_RAM_READ 0xE62
#define A3XX_UCHE_CACHE_MODE_CONTROL_REG 0xE82
#define A3XX_UCHE_CACHE_INVALIDATE0_REG 0xEA0
+#define A3XX_SP_PERFCOUNTER5_SELECT 0xEC9
+#define A3XX_SP_PERFCOUNTER6_SELECT 0xECA
#define A3XX_SP_PERFCOUNTER7_SELECT 0xECB
#define A3XX_GRAS_CL_CLIP_CNTL 0x2040
#define A3XX_GRAS_CL_GB_CLIP_ADJ 0x2044
@@ -539,5 +545,8 @@
/* COUNTABLE FOR SP PERFCOUNTER */
#define SP_FS_FULL_ALU_INSTRUCTIONS 0x0E
+#define SP_ALU_ACTIVE_CYCLES 0x1D
+#define SP0_ICL1_MISSES 0x1A
+#define SP_FS_CFLOW_INSTRUCTIONS 0x0C
#endif
diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c
index 24be1b0..5c98599 100644
--- a/drivers/gpu/msm/adreno.c
+++ b/drivers/gpu/msm/adreno.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2002,2007-2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2002,2007-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
@@ -130,6 +130,10 @@
REG_CP_IB2_BASE,
REG_CP_IB2_BUFSZ,
0,
+ 0,
+ 0,
+ 0,
+ 0,
0
};
@@ -304,6 +308,8 @@
num_iommu_units = kgsl_mmu_get_num_iommu_units(&device->mmu);
context = idr_find(&device->context_idr, context_id);
+ if (context == NULL)
+ return;
adreno_ctx = context->devctxt;
if (kgsl_mmu_enable_clk(&device->mmu,
@@ -454,6 +460,8 @@
*/
if (!kgsl_cff_dump_enable && adreno_dev->drawctxt_active) {
context = idr_find(&device->context_idr, context_id);
+ if (context == NULL)
+ return;
adreno_ctx = context->devctxt;
if (flags & KGSL_MMUFLAGS_PTUPDATE) {
@@ -973,9 +981,17 @@
goto err;
}
- if (adreno_of_read_property(child, "qcom,iommu-ctx-sids",
- &ctxs[ctx_index].ctx_id))
+ ret = of_property_read_u32_array(child, "reg", reg_val, 2);
+ if (ret) {
+ KGSL_CORE_ERR("Unable to read KGSL IOMMU 'reg'\n");
goto err;
+ }
+ if (msm_soc_version_supports_iommu_v1())
+ ctxs[ctx_index].ctx_id = (reg_val[0] -
+ data->physstart) >> KGSL_IOMMU_CTX_SHIFT;
+ else
+ ctxs[ctx_index].ctx_id = ((reg_val[0] -
+ data->physstart) >> KGSL_IOMMU_CTX_SHIFT) - 8;
ctx_index++;
}
@@ -1263,6 +1279,10 @@
if (adreno_is_a3xx(adreno_dev)) {
hang_detect_regs[6] = A3XX_RBBM_PERFCTR_SP_7_LO;
hang_detect_regs[7] = A3XX_RBBM_PERFCTR_SP_7_HI;
+ hang_detect_regs[8] = A3XX_RBBM_PERFCTR_SP_6_LO;
+ hang_detect_regs[9] = A3XX_RBBM_PERFCTR_SP_6_HI;
+ hang_detect_regs[10] = A3XX_RBBM_PERFCTR_SP_5_LO;
+ hang_detect_regs[11] = A3XX_RBBM_PERFCTR_SP_5_HI;
}
status = kgsl_mmu_start(device);
@@ -1282,14 +1302,22 @@
device->ftbl->irqctrl(device, 1);
status = adreno_ringbuffer_start(&adreno_dev->ringbuffer, init_ram);
- if (status == 0) {
- /* While recovery is on we do not want timer to
- * fire and attempt to change any device state */
- if (KGSL_STATE_DUMP_AND_RECOVER != device->state)
- mod_timer(&device->idle_timer, jiffies + FIRST_TIMEOUT);
- return 0;
- }
+ if (status)
+ goto error_irq_off;
+ /*
+ * While recovery is on we do not want timer to
+ * fire and attempt to change any device state
+ */
+
+ if (KGSL_STATE_DUMP_AND_RECOVER != device->state)
+ mod_timer(&device->idle_timer, jiffies + FIRST_TIMEOUT);
+
+ device->reset_counter++;
+
+ return 0;
+
+error_irq_off:
kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_OFF);
error_mmu_off:
@@ -1342,7 +1370,7 @@
adreno_context->flags |= CTXT_FLAGS_GPU_HANG;
} else if (KGSL_CTX_STAT_GUILTY_CONTEXT_RESET_EXT !=
context->reset_status) {
- if (adreno_context->flags & (CTXT_FLAGS_GPU_HANG ||
+ if (adreno_context->flags & (CTXT_FLAGS_GPU_HANG |
CTXT_FLAGS_GPU_HANG_RECOVERED))
context->reset_status =
KGSL_CTX_STAT_GUILTY_CONTEXT_RESET_EXT;
@@ -1622,6 +1650,11 @@
KGSL_MEMSTORE_OFFSET(KGSL_MEMSTORE_GLOBAL,
eoptimestamp),
rb->timestamp[KGSL_MEMSTORE_GLOBAL]);
+
+ /* switch to NULL ctxt */
+ if (adreno_dev->drawctxt_active != NULL)
+ adreno_drawctxt_switch(adreno_dev, NULL, 0);
+
done:
adreno_set_max_ts_for_bad_ctxs(device);
adreno_mark_context_status(device, ret);
@@ -1784,6 +1817,7 @@
unsigned int sizebytes)
{
int status = -EINVAL;
+ struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
switch (type) {
case KGSL_PROP_PWRCTRL: {
@@ -1803,10 +1837,11 @@
if (enable) {
if (pdata->nap_allowed)
device->pwrctrl.nap_allowed = true;
-
+ adreno_dev->fast_hang_detect = 1;
kgsl_pwrscale_enable(device);
} else {
device->pwrctrl.nap_allowed = false;
+ adreno_dev->fast_hang_detect = 0;
kgsl_pwrscale_disable(device);
}
@@ -2142,125 +2177,89 @@
return context_id;
}
-static void adreno_next_event(struct kgsl_device *device,
- struct kgsl_event *event)
-{
- int status;
- unsigned int ref_ts, enableflag;
- unsigned int context_id = _get_context_id(event->context);
- struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
-
- status = kgsl_check_timestamp(device, event->context, event->timestamp);
- if (!status) {
- kgsl_sharedmem_readl(&device->memstore, &enableflag,
- KGSL_MEMSTORE_OFFSET(context_id, ts_cmp_enable));
- /*
- * Barrier is needed here to make sure the read from memstore
- * has posted
- */
-
- mb();
-
- if (enableflag) {
- kgsl_sharedmem_readl(&device->memstore, &ref_ts,
- KGSL_MEMSTORE_OFFSET(context_id,
- ref_wait_ts));
-
- /* Make sure the memstore read has posted */
- mb();
- if (timestamp_cmp(ref_ts, event->timestamp) >= 0) {
- kgsl_sharedmem_writel(&device->memstore,
- KGSL_MEMSTORE_OFFSET(context_id,
- ref_wait_ts), event->timestamp);
- /* Make sure the memstore write is posted */
- wmb();
- }
- } else {
- unsigned int cmds[2];
- kgsl_sharedmem_writel(&device->memstore,
- KGSL_MEMSTORE_OFFSET(context_id,
- ref_wait_ts), event->timestamp);
- enableflag = 1;
- kgsl_sharedmem_writel(&device->memstore,
- KGSL_MEMSTORE_OFFSET(context_id,
- ts_cmp_enable), enableflag);
-
- /* Make sure the memstore write gets posted */
- wmb();
-
- /*
- * submit a dummy packet so that even if all
- * commands upto timestamp get executed we will still
- * get an interrupt
- */
- cmds[0] = cp_type3_packet(CP_NOP, 1);
- cmds[1] = 0;
-
- if (adreno_dev->drawctxt_active)
- adreno_ringbuffer_issuecmds_intr(device,
- event->context, &cmds[0], 2);
- }
- }
-}
-
-static int kgsl_check_interrupt_timestamp(struct kgsl_device *device,
+static unsigned int adreno_check_hw_ts(struct kgsl_device *device,
struct kgsl_context *context, unsigned int timestamp)
{
- int status;
+ int status = 0;
unsigned int ref_ts, enableflag;
- unsigned int context_id;
+ unsigned int context_id = _get_context_id(context);
- mutex_lock(&device->mutex);
- context_id = _get_context_id(context);
/*
* If the context ID is invalid, we are in a race with
* the context being destroyed by userspace so bail.
*/
if (context_id == KGSL_CONTEXT_INVALID) {
KGSL_DRV_WARN(device, "context was detached");
- status = -EINVAL;
- goto unlock;
+ return -EINVAL;
}
status = kgsl_check_timestamp(device, context, timestamp);
- if (!status) {
- kgsl_sharedmem_readl(&device->memstore, &enableflag,
- KGSL_MEMSTORE_OFFSET(context_id, ts_cmp_enable));
- mb();
+ if (status)
+ return status;
- if (enableflag) {
- kgsl_sharedmem_readl(&device->memstore, &ref_ts,
+ kgsl_sharedmem_readl(&device->memstore, &enableflag,
+ KGSL_MEMSTORE_OFFSET(context_id, ts_cmp_enable));
+ /*
+ * Barrier is needed here to make sure the read from memstore
+ * has posted
+ */
+
+ mb();
+
+ if (enableflag) {
+ kgsl_sharedmem_readl(&device->memstore, &ref_ts,
KGSL_MEMSTORE_OFFSET(context_id,
ref_wait_ts));
- mb();
- if (timestamp_cmp(ref_ts, timestamp) >= 0) {
- kgsl_sharedmem_writel(&device->memstore,
+
+ /* Make sure the memstore read has posted */
+ mb();
+ if (timestamp_cmp(ref_ts, timestamp) >= 0) {
+ kgsl_sharedmem_writel(&device->memstore,
+ KGSL_MEMSTORE_OFFSET(context_id,
+ ref_wait_ts), timestamp);
+ /* Make sure the memstore write is posted */
+ wmb();
+ }
+ } else {
+ kgsl_sharedmem_writel(&device->memstore,
KGSL_MEMSTORE_OFFSET(context_id,
ref_wait_ts), timestamp);
- wmb();
- }
- } else {
- unsigned int cmds[2];
- kgsl_sharedmem_writel(&device->memstore,
- KGSL_MEMSTORE_OFFSET(context_id,
- ref_wait_ts), timestamp);
- enableflag = 1;
- kgsl_sharedmem_writel(&device->memstore,
+ enableflag = 1;
+ kgsl_sharedmem_writel(&device->memstore,
KGSL_MEMSTORE_OFFSET(context_id,
ts_cmp_enable), enableflag);
- wmb();
- /* submit a dummy packet so that even if all
- * commands upto timestamp get executed we will still
- * get an interrupt */
- cmds[0] = cp_type3_packet(CP_NOP, 1);
- cmds[1] = 0;
- if (context)
- adreno_ringbuffer_issuecmds_intr(device,
- context, &cmds[0], 2);
+ /* Make sure the memstore write gets posted */
+ wmb();
+
+ /*
+ * submit a dummy packet so that even if all
+ * commands upto timestamp get executed we will still
+ * get an interrupt
+ */
+
+ if (context) {
+ adreno_ringbuffer_issuecmds(device, context->devctxt,
+ KGSL_CMD_FLAGS_NONE, NULL, 0);
}
}
-unlock:
+
+ return 0;
+}
+
+static void adreno_next_event(struct kgsl_device *device,
+ struct kgsl_event *event)
+{
+ adreno_check_hw_ts(device, event->context, event->timestamp);
+}
+
+static int adreno_check_interrupt_timestamp(struct kgsl_device *device,
+ struct kgsl_context *context, unsigned int timestamp)
+{
+ int status;
+
+ mutex_lock(&device->mutex);
+ status = adreno_check_hw_ts(device, context, timestamp);
mutex_unlock(&device->mutex);
return status;
@@ -2497,7 +2496,7 @@
/* Wait for a timestamp event */
status = kgsl_wait_event_interruptible_timeout(
device->wait_queue,
- kgsl_check_interrupt_timestamp(device, context,
+ adreno_check_interrupt_timestamp(device, context,
timestamp), msecs_to_jiffies(wait), io);
mutex_lock(&device->mutex);
diff --git a/drivers/gpu/msm/adreno_a3xx.c b/drivers/gpu/msm/adreno_a3xx.c
index 2466a5c..c408f1b 100644
--- a/drivers/gpu/msm/adreno_a3xx.c
+++ b/drivers/gpu/msm/adreno_a3xx.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. 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
@@ -2873,12 +2873,22 @@
adreno_regwrite(device, A3XX_RBBM_PERFCTR_CTL, 0x01);
/*
- * Set SP perfcounter 7 to count SP_FS_FULL_ALU_INSTRUCTIONS
+ * Set SP perfcounter 5 to count SP_ALU_ACTIVE_CYCLES, it includes
+ * all ALU instruction execution regardless precision or shader ID.
+ * Set SP perfcounter 6 to count SP0_ICL1_MISSES, It counts
+ * USP L1 instruction miss request.
+ * Set SP perfcounter 7 to count SP_FS_FULL_ALU_INSTRUCTIONS, it
+ * counts USP flow control instruction execution.
* we will use this to augment our hang detection
*/
-
- adreno_regwrite(device, A3XX_SP_PERFCOUNTER7_SELECT,
- SP_FS_FULL_ALU_INSTRUCTIONS);
+ if (adreno_dev->fast_hang_detect) {
+ adreno_regwrite(device, A3XX_SP_PERFCOUNTER5_SELECT,
+ SP_ALU_ACTIVE_CYCLES);
+ adreno_regwrite(device, A3XX_SP_PERFCOUNTER6_SELECT,
+ SP0_ICL1_MISSES);
+ adreno_regwrite(device, A3XX_SP_PERFCOUNTER7_SELECT,
+ SP_FS_CFLOW_INSTRUCTIONS);
+ }
}
/* Defined in adreno_a3xx_snapshot.c */
diff --git a/drivers/gpu/msm/adreno_a3xx_snapshot.c b/drivers/gpu/msm/adreno_a3xx_snapshot.c
index de95951..713ef1a 100644
--- a/drivers/gpu/msm/adreno_a3xx_snapshot.c
+++ b/drivers/gpu/msm/adreno_a3xx_snapshot.c
@@ -24,6 +24,22 @@
#define SHADER_MEMORY_SIZE 0x4000
/**
+ * _rbbm_debug_bus_read - Helper function to read data from the RBBM
+ * debug bus.
+ * @device - GPU device to read/write registers
+ * @block_id - Debug bus block to read from
+ * @index - Index in the debug bus block to read
+ * @ret - Value of the register read
+ */
+static void _rbbm_debug_bus_read(struct kgsl_device *device,
+ unsigned int block_id, unsigned int index, unsigned int *val)
+{
+ unsigned int block = (block_id << 8) | 1 << 16;
+ adreno_regwrite(device, A3XX_RBBM_DEBUG_BUS_CTL, block | index);
+ adreno_regread(device, A3XX_RBBM_DEBUG_BUS_DATA_STATUS, val);
+}
+
+/**
* a3xx_snapshot_shader_memory - Helper function to dump the GPU shader
* memory to the snapshot buffer.
* @device - GPU device whose shader memory is to be dumped
@@ -260,7 +276,6 @@
struct kgsl_snapshot_debugbus *header = snapshot;
struct debugbus_block *block = priv;
- unsigned int val;
int i;
unsigned int *data = snapshot + sizeof(*header);
unsigned int dwords;
@@ -282,16 +297,11 @@
return 0;
}
- val = (block->block_id << 8) | (1 << 16);
-
header->id = block->block_id;
header->count = dwords;
- for (i = 0; i < dwords; i++) {
- adreno_regwrite(device, A3XX_RBBM_DEBUG_BUS_CTL, val | i);
- adreno_regread(device, A3XX_RBBM_DEBUG_BUS_DATA_STATUS,
- &data[i]);
- }
+ for (i = 0; i < dwords; i++)
+ _rbbm_debug_bus_read(device, block->block_id, i, &data[i]);
return size;
}
@@ -353,18 +363,58 @@
struct kgsl_snapshot_registers_list *list,
struct adreno_device *adreno_dev)
{
- /* HLSQ specific registers */
+ struct kgsl_device *device = &adreno_dev->dev;
+
/*
- * Don't dump any a3xx HLSQ registers just yet. Reading the HLSQ
- * registers can cause the device to hang if the HLSQ block is
- * busy. Add specific checks for each a3xx core as the requirements
- * are discovered. Disable by default for now.
+ * Trying to read HLSQ registers when the HLSQ block is busy
+ * will cause the device to hang. The RBBM_DEBUG_BUS has information
+ * that will tell us if the HLSQ block is busy or not. Read values
+ * from the debug bus to ensure the HLSQ block is not busy (this
+ * is hardware dependent). If the HLSQ block is busy do not
+ * dump the registers, otherwise dump the HLSQ registers.
*/
- if (!adreno_is_a3xx(adreno_dev)) {
- regs[list->count].regs = (unsigned int *) a3xx_hlsq_registers;
- regs[list->count].count = a3xx_hlsq_registers_count;
- list->count++;
+
+ if (adreno_is_a330(adreno_dev)) {
+ /*
+ * stall_ctxt_full status bit: RBBM_BLOCK_ID_HLSQ index 49 [27]
+ *
+ * if (!stall_context_full)
+ * then dump HLSQ registers
+ */
+ unsigned int stall_context_full = 0;
+
+ _rbbm_debug_bus_read(device, RBBM_BLOCK_ID_HLSQ, 49,
+ &stall_context_full);
+ stall_context_full &= 0x08000000;
+
+ if (stall_context_full)
+ return;
+ } else {
+ /*
+ * tpif status bits: RBBM_BLOCK_ID_HLSQ index 4 [4:0]
+ * spif status bits: RBBM_BLOCK_ID_HLSQ index 7 [5:0]
+ *
+ * if ((tpif == 0, 1, 28) && (spif == 0, 1, 10))
+ * then dump HLSQ registers
+ */
+ unsigned int next_pif = 0;
+
+ /* check tpif */
+ _rbbm_debug_bus_read(device, RBBM_BLOCK_ID_HLSQ, 4, &next_pif);
+ next_pif &= 0x1f;
+ if (next_pif != 0 && next_pif != 1 && next_pif != 28)
+ return;
+
+ /* check spif */
+ _rbbm_debug_bus_read(device, RBBM_BLOCK_ID_HLSQ, 7, &next_pif);
+ next_pif &= 0x3f;
+ if (next_pif != 0 && next_pif != 1 && next_pif != 10)
+ return;
}
+
+ regs[list->count].regs = (unsigned int *) a3xx_hlsq_registers;
+ regs[list->count].count = a3xx_hlsq_registers_count;
+ list->count++;
}
static void _snapshot_a330_regs(struct kgsl_snapshot_registers *regs,
diff --git a/drivers/gpu/msm/adreno_ringbuffer.c b/drivers/gpu/msm/adreno_ringbuffer.c
index 97b35b0..03828c6 100644
--- a/drivers/gpu/msm/adreno_ringbuffer.c
+++ b/drivers/gpu/msm/adreno_ringbuffer.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2002,2007-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2002,2007-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
@@ -745,30 +745,6 @@
return timestamp;
}
-void
-adreno_ringbuffer_issuecmds_intr(struct kgsl_device *device,
- struct kgsl_context *k_ctxt,
- unsigned int *cmds,
- int sizedwords)
-{
- struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
- struct adreno_ringbuffer *rb = &adreno_dev->ringbuffer;
- struct adreno_context *a_ctxt = NULL;
-
- if (!k_ctxt)
- return;
-
- a_ctxt = k_ctxt->devctxt;
-
- if (k_ctxt->id == KGSL_CONTEXT_INVALID ||
- a_ctxt == NULL ||
- device->state & KGSL_STATE_HUNG)
- return;
-
- adreno_ringbuffer_addcmds(rb, a_ctxt, KGSL_CMD_FLAGS_INTERNAL_ISSUE,
- cmds, sizedwords, 0);
-}
-
unsigned int
adreno_ringbuffer_issuecmds(struct kgsl_device *device,
struct adreno_context *drawctxt,
@@ -1071,10 +1047,6 @@
0,
&link[0], (cmds - link), *timestamp);
- KGSL_CMD_INFO(device, "<%d:0x%x> g %08x numibs %d\n",
- context->id, *timestamp, (unsigned int)ibdesc, numibs);
-
-
#ifdef CONFIG_MSM_KGSL_CFF_DUMP
/*
* insert wait for idle after every IB1
diff --git a/drivers/gpu/msm/adreno_ringbuffer.h b/drivers/gpu/msm/adreno_ringbuffer.h
index 50d9c25..e87b506 100644
--- a/drivers/gpu/msm/adreno_ringbuffer.h
+++ b/drivers/gpu/msm/adreno_ringbuffer.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2002,2007-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2002,2007-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
@@ -110,11 +110,6 @@
unsigned int *cmdaddr,
int sizedwords);
-void adreno_ringbuffer_issuecmds_intr(struct kgsl_device *device,
- struct kgsl_context *k_ctxt,
- unsigned int *cmdaddr,
- int sizedwords);
-
void adreno_ringbuffer_submit(struct adreno_ringbuffer *rb);
void kgsl_cp_intrcallback(struct kgsl_device *device);
diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c
index 9cde1d7..f234d67 100644
--- a/drivers/gpu/msm/kgsl.c
+++ b/drivers/gpu/msm/kgsl.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2008-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
@@ -53,147 +53,6 @@
static struct ion_client *kgsl_ion_client;
-/**
- * kgsl_add_event - Add a new timstamp event for the KGSL device
- * @device - KGSL device for the new event
- * @ts - the timestamp to trigger the event on
- * @cb - callback function to call when the timestamp expires
- * @priv - private data for the specific event type
- * @owner - driver instance that owns this event
- *
- * @returns - 0 on success or error code on failure
- */
-
-int kgsl_add_event(struct kgsl_device *device, u32 id, u32 ts,
- void (*cb)(struct kgsl_device *, void *, u32, u32), void *priv,
- void *owner)
-{
- struct kgsl_event *event;
- struct list_head *n;
- unsigned int cur_ts;
- struct kgsl_context *context = NULL;
-
- if (cb == NULL)
- return -EINVAL;
-
- if (id != KGSL_MEMSTORE_GLOBAL) {
- context = idr_find(&device->context_idr, id);
- if (context == NULL)
- return -EINVAL;
- }
- cur_ts = kgsl_readtimestamp(device, context, KGSL_TIMESTAMP_RETIRED);
-
- /* Check to see if the requested timestamp has already fired */
-
- if (timestamp_cmp(cur_ts, ts) >= 0) {
- cb(device, priv, id, cur_ts);
- return 0;
- }
-
- event = kzalloc(sizeof(*event), GFP_KERNEL);
- if (event == NULL)
- return -ENOMEM;
-
- event->context = context;
- event->timestamp = ts;
- event->priv = priv;
- event->func = cb;
- event->owner = owner;
-
- /*
- * Add the event in order to the list. Order is by context id
- * first and then by timestamp for that context.
- */
-
- for (n = device->events.next ; n != &device->events; n = n->next) {
- struct kgsl_event *e =
- list_entry(n, struct kgsl_event, list);
-
- if (e->context != context)
- continue;
-
- if (timestamp_cmp(e->timestamp, ts) > 0) {
- list_add(&event->list, n->prev);
- break;
- }
- }
-
- if (n == &device->events)
- list_add_tail(&event->list, &device->events);
-
- queue_work(device->work_queue, &device->ts_expired_ws);
- return 0;
-}
-EXPORT_SYMBOL(kgsl_add_event);
-
-/**
- * kgsl_cancel_events_ctxt - Cancel all events for a context
- * @device - KGSL device for the events to cancel
- * @ctxt - context whose events we want to cancel
- *
- */
-static void kgsl_cancel_events_ctxt(struct kgsl_device *device,
- struct kgsl_context *context)
-{
- struct kgsl_event *event, *event_tmp;
- unsigned int id, cur;
-
- cur = kgsl_readtimestamp(device, context, KGSL_TIMESTAMP_RETIRED);
- id = context->id;
-
- list_for_each_entry_safe(event, event_tmp, &device->events, list) {
- if (event->context != context)
- continue;
-
- /*
- * "cancel" the events by calling their callback.
- * Currently, events are used for lock and memory
- * management, so if the process is dying the right
- * thing to do is release or free.
- */
- if (event->func)
- event->func(device, event->priv, id, cur);
-
- list_del(&event->list);
- kfree(event);
- }
-}
-
-/**
- * kgsl_cancel_events - Cancel all events for a process
- * @device - KGSL device for the events to cancel
- * @owner - driver instance that owns the events to cancel
- *
- */
-void kgsl_cancel_events(struct kgsl_device *device,
- void *owner)
-{
- struct kgsl_event *event, *event_tmp;
- unsigned int id, cur;
-
- list_for_each_entry_safe(event, event_tmp, &device->events, list) {
- if (event->owner != owner)
- continue;
-
- cur = kgsl_readtimestamp(device, event->context,
- KGSL_TIMESTAMP_RETIRED);
-
- id = event->context ? event->context->id : KGSL_MEMSTORE_GLOBAL;
- /*
- * "cancel" the events by calling their callback.
- * Currently, events are used for lock and memory
- * management, so if the process is dying the right
- * thing to do is release or free.
- */
- if (event->func)
- event->func(device, event->priv, id, cur);
-
- list_del(&event->list);
- kfree(event);
- }
-}
-EXPORT_SYMBOL(kgsl_cancel_events);
-
int kgsl_memfree_hist_init(void)
{
void *base;
@@ -486,6 +345,20 @@
goto func_end;
}
+ /* Initialize the pending event list */
+ INIT_LIST_HEAD(&context->events);
+
+ /*
+ * Initialize the node that is used to maintain the master list of
+ * contexts with pending events in the device structure. Normally we
+ * wouldn't take the time to initalize a node but at event add time we
+ * call list_empty() on the node as a quick way of determining if the
+ * context is already in the master list so it needs to always be either
+ * active or in an unused but initialized state
+ */
+
+ INIT_LIST_HEAD(&context->events_list);
+
func_end:
if (ret) {
kfree(context);
@@ -540,52 +413,6 @@
kfree(context);
}
-void kgsl_timestamp_expired(struct work_struct *work)
-{
- struct kgsl_device *device = container_of(work, struct kgsl_device,
- ts_expired_ws);
- struct kgsl_event *event, *event_tmp;
- uint32_t ts_processed;
- unsigned int id;
-
- mutex_lock(&device->mutex);
-
- /* Process expired events */
- list_for_each_entry_safe(event, event_tmp, &device->events, list) {
- ts_processed = kgsl_readtimestamp(device, event->context,
- KGSL_TIMESTAMP_RETIRED);
- if (timestamp_cmp(ts_processed, event->timestamp) < 0)
- continue;
-
- id = event->context ? event->context->id : KGSL_MEMSTORE_GLOBAL;
-
- if (event->func)
- event->func(device, event->priv, id, ts_processed);
-
- list_del(&event->list);
- kfree(event);
- }
-
- /* Send the next pending event for each context to the device */
- if (device->ftbl->next_event) {
- unsigned int id = KGSL_MEMSTORE_GLOBAL;
-
- list_for_each_entry(event, &device->events, list) {
-
- if (!event->context)
- continue;
-
- if (event->context->id != id) {
- device->ftbl->next_event(device, event);
- id = event->context->id;
- }
- }
- }
-
- mutex_unlock(&device->mutex);
-}
-EXPORT_SYMBOL(kgsl_timestamp_expired);
-
static void kgsl_check_idle_locked(struct kgsl_device *device)
{
if (device->pwrctrl.nap_allowed == true &&
@@ -690,7 +517,7 @@
INIT_COMPLETION(device->hwaccess_gate);
device->ftbl->suspend_context(device);
device->ftbl->stop(device);
- pm_qos_update_request(&device->pm_qos_req_dma,
+ pm_qos_update_request(&device->pwrctrl.pm_qos_req_dma,
PM_QOS_DEFAULT_VALUE);
kgsl_pwrctrl_set_state(device, KGSL_STATE_SUSPEND);
break;
@@ -3136,7 +2963,8 @@
goto error_close_mmu;
}
- pm_qos_add_request(&device->pm_qos_req_dma, PM_QOS_CPU_DMA_LATENCY,
+ pm_qos_add_request(&device->pwrctrl.pm_qos_req_dma,
+ PM_QOS_CPU_DMA_LATENCY,
PM_QOS_DEFAULT_VALUE);
/* Initalize the snapshot engine */
@@ -3247,7 +3075,7 @@
kgsl_cffdump_close(device->id);
kgsl_pwrctrl_uninit_sysfs(device);
- pm_qos_remove_request(&device->pm_qos_req_dma);
+ pm_qos_remove_request(&device->pwrctrl.pm_qos_req_dma);
idr_destroy(&device->context_idr);
diff --git a/drivers/gpu/msm/kgsl.h b/drivers/gpu/msm/kgsl.h
index 72e7776..3eb8831 100644
--- a/drivers/gpu/msm/kgsl.h
+++ b/drivers/gpu/msm/kgsl.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2008-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
@@ -89,6 +89,7 @@
struct kgsl_device;
+struct kgsl_context;
struct kgsl_driver {
struct cdev cdev;
@@ -217,6 +218,9 @@
void kgsl_cancel_events(struct kgsl_device *device,
void *owner);
+void kgsl_cancel_events_ctxt(struct kgsl_device *device,
+ struct kgsl_context *context);
+
extern const struct dev_pm_ops kgsl_pm_ops;
struct early_suspend;
diff --git a/drivers/gpu/msm/kgsl_device.h b/drivers/gpu/msm/kgsl_device.h
index ce3820c..65598ba 100644
--- a/drivers/gpu/msm/kgsl_device.h
+++ b/drivers/gpu/msm/kgsl_device.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2002,2007-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2002,2007-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,7 +26,7 @@
#define KGSL_TIMEOUT_NONE 0
#define KGSL_TIMEOUT_DEFAULT 0xFFFFFFFF
-#define KGSL_TIMEOUT_PART 2000 /* 2 sec */
+#define KGSL_TIMEOUT_PART 50 /* 50 msec */
#define FIRST_TIMEOUT (HZ / 2)
@@ -212,17 +212,19 @@
int pwr_log;
struct kgsl_pwrscale pwrscale;
struct kobject pwrscale_kobj;
- struct pm_qos_request pm_qos_req_dma;
struct work_struct ts_expired_ws;
struct list_head events;
+ struct list_head events_pending_list;
s64 on_time;
/* Postmortem Control switches */
int pm_regs_enabled;
int pm_ib_enabled;
+
+ int reset_counter; /* Track how many GPU core resets have occured */
};
-void kgsl_timestamp_expired(struct work_struct *work);
+void kgsl_process_events(struct work_struct *work);
#define KGSL_DEVICE_COMMON_INIT(_dev) \
.hwaccess_gate = COMPLETION_INITIALIZER((_dev).hwaccess_gate),\
@@ -231,36 +233,42 @@
.idle_check_ws = __WORK_INITIALIZER((_dev).idle_check_ws,\
kgsl_idle_check),\
.ts_expired_ws = __WORK_INITIALIZER((_dev).ts_expired_ws,\
- kgsl_timestamp_expired),\
+ kgsl_process_events),\
.context_idr = IDR_INIT((_dev).context_idr),\
.events = LIST_HEAD_INIT((_dev).events),\
+ .events_pending_list = LIST_HEAD_INIT((_dev).events_pending_list), \
.wait_queue = __WAIT_QUEUE_HEAD_INITIALIZER((_dev).wait_queue),\
.mutex = __MUTEX_INITIALIZER((_dev).mutex),\
.state = KGSL_STATE_INIT,\
.ver_major = DRIVER_VERSION_MAJOR,\
.ver_minor = DRIVER_VERSION_MINOR
+
+/**
+ * struct kgsl_context - Master structure for a KGSL context object
+ * @refcount - kref object for reference counting the context
+ * @id - integer identifier for the context
+ * @dev_priv - pointer to the owning device instance
+ * @devctxt - pointer to the device specific context information
+ * @reset_status - status indication whether a gpu reset occured and whether
+ * this context was responsible for causing it
+ * @wait_on_invalid_ts - flag indicating if this context has tried to wait on a
+ * bad timestamp
+ * @timeline - sync timeline used to create fences that can be signaled when a
+ * sync_pt timestamp expires
+ * @events - list head of pending events for this context
+ * @events_list - list node for the list of all contexts that have pending events
+ */
struct kgsl_context {
struct kref refcount;
uint32_t id;
-
- /* Pointer to the owning device instance */
struct kgsl_device_private *dev_priv;
-
- /* Pointer to the device specific context information */
void *devctxt;
- /*
- * Status indicating whether a gpu reset occurred and whether this
- * context was responsible for causing it
- */
unsigned int reset_status;
- /* Flag indicating if we tried to wait for bad timestamp for this ctx */
bool wait_on_invalid_ts;
- /*
- * Timeline used to create fences that can be signaled when a
- * sync_pt timestamp expires.
- */
struct sync_timeline *timeline;
+ struct list_head events;
+ struct list_head events_list;
};
struct kgsl_process_private {
diff --git a/drivers/gpu/msm/kgsl_events.c b/drivers/gpu/msm/kgsl_events.c
new file mode 100644
index 0000000..2002537
--- /dev/null
+++ b/drivers/gpu/msm/kgsl_events.c
@@ -0,0 +1,251 @@
+/* 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
+ * 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/slab.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <kgsl_device.h>
+
+static void _add_event_to_list(struct list_head *head, struct kgsl_event *event)
+{
+ struct list_head *n;
+
+ for (n = head->next; n != head; n = n->next) {
+ struct kgsl_event *e =
+ list_entry(n, struct kgsl_event, list);
+
+ if (timestamp_cmp(e->timestamp, event->timestamp) > 0) {
+ list_add(&event->list, n->prev);
+ break;
+ }
+ }
+
+ if (n == head)
+ list_add_tail(&event->list, head);
+}
+
+/**
+ * kgsl_add_event - Add a new timstamp event for the KGSL device
+ * @device - KGSL device for the new event
+ * @id - the context ID that the event should be added to
+ * @ts - the timestamp to trigger the event on
+ * @cb - callback function to call when the timestamp expires
+ * @priv - private data for the specific event type
+ * @owner - driver instance that owns this event
+ *
+ * @returns - 0 on success or error code on failure
+ */
+int kgsl_add_event(struct kgsl_device *device, u32 id, u32 ts,
+ void (*cb)(struct kgsl_device *, void *, u32, u32), void *priv,
+ void *owner)
+{
+ struct kgsl_event *event;
+ unsigned int cur_ts;
+ struct kgsl_context *context = NULL;
+
+ if (cb == NULL)
+ return -EINVAL;
+
+ if (id != KGSL_MEMSTORE_GLOBAL) {
+ context = idr_find(&device->context_idr, id);
+ if (context == NULL)
+ return -EINVAL;
+ }
+ cur_ts = kgsl_readtimestamp(device, context, KGSL_TIMESTAMP_RETIRED);
+
+ /* Check to see if the requested timestamp has already fired */
+
+ if (timestamp_cmp(cur_ts, ts) >= 0) {
+ cb(device, priv, id, cur_ts);
+ return 0;
+ }
+
+ event = kzalloc(sizeof(*event), GFP_KERNEL);
+ if (event == NULL)
+ return -ENOMEM;
+
+ event->context = context;
+ event->timestamp = ts;
+ event->priv = priv;
+ event->func = cb;
+ event->owner = owner;
+
+ /* Add the event to either the owning context or the global list */
+
+ if (context) {
+ _add_event_to_list(&context->events, event);
+
+ /*
+ * Add it to the master list of contexts with pending events if
+ * it isn't already there
+ */
+
+ if (list_empty(&context->events_list))
+ list_add_tail(&context->events_list,
+ &device->events_pending_list);
+
+ } else
+ _add_event_to_list(&device->events, event);
+
+ queue_work(device->work_queue, &device->ts_expired_ws);
+ return 0;
+}
+EXPORT_SYMBOL(kgsl_add_event);
+
+/**
+ * kgsl_cancel_events_ctxt - Cancel all events for a context
+ * @device - KGSL device for the events to cancel
+ * @context - context whose events we want to cancel
+ *
+ */
+void kgsl_cancel_events_ctxt(struct kgsl_device *device,
+ struct kgsl_context *context)
+{
+ struct kgsl_event *event, *event_tmp;
+ unsigned int id, cur;
+
+ cur = kgsl_readtimestamp(device, context, KGSL_TIMESTAMP_RETIRED);
+ id = context->id;
+
+ list_for_each_entry_safe(event, event_tmp, &context->events, list) {
+ /*
+ * "cancel" the events by calling their callback.
+ * Currently, events are used for lock and memory
+ * management, so if the process is dying the right
+ * thing to do is release or free.
+ */
+ if (event->func)
+ event->func(device, event->priv, id, cur);
+
+ list_del(&event->list);
+ kfree(event);
+ }
+
+ /* Remove ourselves from the master pending list */
+ list_del_init(&context->events_list);
+}
+
+/**
+ * kgsl_cancel_events - Cancel all generic events for a process
+ * @device - KGSL device for the events to cancel
+ * @owner - driver instance that owns the events to cancel
+ *
+ */
+void kgsl_cancel_events(struct kgsl_device *device,
+ void *owner)
+{
+ struct kgsl_event *event, *event_tmp;
+ unsigned int cur;
+
+ cur = kgsl_readtimestamp(device, NULL, KGSL_TIMESTAMP_RETIRED);
+
+ list_for_each_entry_safe(event, event_tmp, &device->events, list) {
+ if (event->owner != owner)
+ continue;
+
+ /*
+ * "cancel" the events by calling their callback.
+ * Currently, events are used for lock and memory
+ * management, so if the process is dying the right
+ * thing to do is release or free.
+ */
+ if (event->func)
+ event->func(device, event->priv, KGSL_MEMSTORE_GLOBAL,
+ cur);
+
+ list_del(&event->list);
+ kfree(event);
+ }
+}
+EXPORT_SYMBOL(kgsl_cancel_events);
+
+static void _process_event_list(struct kgsl_device *device,
+ struct list_head *head, unsigned int timestamp)
+{
+ struct kgsl_event *event, *tmp;
+ unsigned int id;
+
+ list_for_each_entry_safe(event, tmp, head, list) {
+ if (timestamp_cmp(timestamp, event->timestamp) < 0)
+ break;
+
+ id = event->context ? event->context->id : KGSL_MEMSTORE_GLOBAL;
+
+ if (event->func)
+ event->func(device, event->priv, id, timestamp);
+
+ list_del(&event->list);
+ kfree(event);
+ }
+}
+
+static inline void _mark_next_event(struct kgsl_device *device,
+ struct list_head *head)
+{
+ struct kgsl_event *event;
+
+ if (!list_empty(head)) {
+ event = list_first_entry(head, struct kgsl_event, list);
+ device->ftbl->next_event(device, event);
+ }
+}
+
+static int kgsl_process_context_events(struct kgsl_device *device,
+ struct kgsl_context *context)
+{
+ unsigned int timestamp = kgsl_readtimestamp(device, context,
+ KGSL_TIMESTAMP_RETIRED);
+
+ _process_event_list(device, &context->events, timestamp);
+
+ /* Mark the next pending event on the list to fire an interrupt */
+ _mark_next_event(device, &context->events);
+
+ /*
+ * Return 0 if the list is empty so the calling function can remove the
+ * context from the pending list
+ */
+
+ return list_empty(&context->events) ? 0 : 1;
+}
+
+void kgsl_process_events(struct work_struct *work)
+{
+ struct kgsl_device *device = container_of(work, struct kgsl_device,
+ ts_expired_ws);
+ struct kgsl_context *context, *tmp;
+ uint32_t timestamp;
+
+ mutex_lock(&device->mutex);
+
+ /* Process expired global events */
+ timestamp = kgsl_readtimestamp(device, NULL, KGSL_TIMESTAMP_RETIRED);
+ _process_event_list(device, &device->events, timestamp);
+ _mark_next_event(device, &device->events);
+
+ /* Now process all of the pending contexts */
+ list_for_each_entry_safe(context, tmp, &device->events_pending_list,
+ events_list) {
+
+ /*
+ * If kgsl_timestamp_expired_context returns 0 then it no longer
+ * has any pending events and can be removed from the list
+ */
+
+ if (kgsl_process_context_events(device, context) == 0)
+ list_del_init(&context->events_list);
+ }
+
+ mutex_unlock(&device->mutex);
+}
+EXPORT_SYMBOL(kgsl_process_events);
diff --git a/drivers/gpu/msm/kgsl_iommu.c b/drivers/gpu/msm/kgsl_iommu.c
index 818bd63..abf125f 100644
--- a/drivers/gpu/msm/kgsl_iommu.c
+++ b/drivers/gpu/msm/kgsl_iommu.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, Code Aurora Forum. 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
@@ -11,6 +11,7 @@
*
*/
#include <linux/types.h>
+#include <linux/delay.h>
#include <linux/device.h>
#include <linux/spinlock.h>
#include <linux/genalloc.h>
@@ -40,6 +41,8 @@
{ 0x20, 0, 0 }, /* FSR */
{ 0x800, 0, 0 }, /* TLBIALL */
{ 0x820, 0, 0 }, /* RESUME */
+ { 0x03C, 0, 0 }, /* TLBLKCR */
+ { 0x818, 0, 0 }, /* V2PUR */
};
static struct kgsl_iommu_register_list kgsl_iommuv2_reg[KGSL_IOMMU_REG_MAX] = {
@@ -782,16 +785,26 @@
*/
static int kgsl_iommu_init_sync_lock(struct kgsl_mmu *mmu)
{
- struct kgsl_iommu *iommu = mmu->device->mmu.priv;
+ struct kgsl_iommu *iommu = mmu->priv;
int status = 0;
uint32_t lock_phy_addr = 0;
uint32_t page_offset = 0;
- if (KGSL_DEVICE_3D0 != mmu->device->id ||
- !msm_soc_version_supports_iommu_v1() ||
+ if (!msm_soc_version_supports_iommu_v1() ||
!kgsl_mmu_is_perprocess())
return status;
+ /*
+ * For 2D devices cpu side sync lock is required. For 3D device,
+ * since we only have a single 3D core and we always ensure that
+ * 3D core is idle while writing to IOMMU register using CPU this
+ * lock is not required
+ */
+ if (KGSL_DEVICE_2D0 == mmu->device->id ||
+ KGSL_DEVICE_2D1 == mmu->device->id) {
+ return status;
+ }
+
/* Return if already initialized */
if (iommu->sync_lock_initialized)
return status;
@@ -1260,6 +1273,111 @@
return status;
}
+/*
+ * kgsl_iommu_lock_rb_in_tlb - Allocates tlb entries and locks the
+ * virtual to physical address translation of ringbuffer for 3D
+ * device into tlb.
+ * @mmu - Pointer to mmu structure
+ *
+ * Return - void
+ */
+static void kgsl_iommu_lock_rb_in_tlb(struct kgsl_mmu *mmu)
+{
+ struct kgsl_device *device = mmu->device;
+ struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+ struct adreno_ringbuffer *rb;
+ struct kgsl_iommu *iommu = mmu->priv;
+ unsigned int num_tlb_entries;
+ unsigned int tlblkcr = 0;
+ unsigned int v2pxx = 0;
+ unsigned int vaddr = 0;
+ int i, j, k, l;
+
+ if (!iommu->sync_lock_initialized)
+ return;
+
+ rb = &adreno_dev->ringbuffer;
+ num_tlb_entries = rb->buffer_desc.size / PAGE_SIZE;
+
+ for (i = 0; i < iommu->unit_count; i++) {
+ struct kgsl_iommu_unit *iommu_unit = &iommu->iommu_units[i];
+ for (j = 0; j < iommu_unit->dev_count; j++) {
+ tlblkcr = 0;
+ if (cpu_is_msm8960())
+ tlblkcr |= ((num_tlb_entries &
+ KGSL_IOMMU_TLBLKCR_FLOOR_MASK) <<
+ KGSL_IOMMU_TLBLKCR_FLOOR_SHIFT);
+ else
+ tlblkcr |= (((num_tlb_entries *
+ iommu_unit->dev_count) &
+ KGSL_IOMMU_TLBLKCR_FLOOR_MASK) <<
+ KGSL_IOMMU_TLBLKCR_FLOOR_SHIFT);
+ /* Do not invalidate locked entries on tlbiall flush */
+ tlblkcr |= ((1 & KGSL_IOMMU_TLBLKCR_TLBIALLCFG_MASK)
+ << KGSL_IOMMU_TLBLKCR_TLBIALLCFG_SHIFT);
+ tlblkcr |= ((1 & KGSL_IOMMU_TLBLKCR_TLBIASIDCFG_MASK)
+ << KGSL_IOMMU_TLBLKCR_TLBIASIDCFG_SHIFT);
+ tlblkcr |= ((1 & KGSL_IOMMU_TLBLKCR_TLBIVAACFG_MASK)
+ << KGSL_IOMMU_TLBLKCR_TLBIVAACFG_SHIFT);
+ /* Enable tlb locking */
+ tlblkcr |= ((1 & KGSL_IOMMU_TLBLKCR_LKE_MASK)
+ << KGSL_IOMMU_TLBLKCR_LKE_SHIFT);
+ KGSL_IOMMU_SET_CTX_REG(iommu, iommu_unit,
+ iommu_unit->dev[j].ctx_id,
+ TLBLKCR, tlblkcr);
+ }
+ for (j = 0; j < iommu_unit->dev_count; j++) {
+ /* skip locking entries for private bank on 8960 */
+ if (cpu_is_msm8960() && KGSL_IOMMU_CONTEXT_PRIV == j)
+ continue;
+ /* Lock the ringbuffer virtual address into tlb */
+ vaddr = rb->buffer_desc.gpuaddr;
+ for (k = 0; k < num_tlb_entries; k++) {
+ v2pxx = 0;
+ v2pxx |= (((k + j * num_tlb_entries) &
+ KGSL_IOMMU_V2PXX_INDEX_MASK)
+ << KGSL_IOMMU_V2PXX_INDEX_SHIFT);
+ v2pxx |= vaddr & (KGSL_IOMMU_V2PXX_VA_MASK <<
+ KGSL_IOMMU_V2PXX_VA_SHIFT);
+
+ KGSL_IOMMU_SET_CTX_REG(iommu, iommu_unit,
+ iommu_unit->dev[j].ctx_id,
+ V2PUR, v2pxx);
+ vaddr += PAGE_SIZE;
+ for (l = 0; l < iommu_unit->dev_count; l++) {
+ tlblkcr = KGSL_IOMMU_GET_CTX_REG(iommu,
+ iommu_unit,
+ iommu_unit->dev[l].ctx_id,
+ TLBLKCR);
+ mb();
+ tlblkcr &=
+ ~(KGSL_IOMMU_TLBLKCR_VICTIM_MASK
+ << KGSL_IOMMU_TLBLKCR_VICTIM_SHIFT);
+ tlblkcr |= (((k + 1 +
+ (j * num_tlb_entries)) &
+ KGSL_IOMMU_TLBLKCR_VICTIM_MASK) <<
+ KGSL_IOMMU_TLBLKCR_VICTIM_SHIFT);
+ KGSL_IOMMU_SET_CTX_REG(iommu,
+ iommu_unit,
+ iommu_unit->dev[l].ctx_id,
+ TLBLKCR, tlblkcr);
+ }
+ }
+ }
+ for (j = 0; j < iommu_unit->dev_count; j++) {
+ tlblkcr = KGSL_IOMMU_GET_CTX_REG(iommu, iommu_unit,
+ iommu_unit->dev[j].ctx_id,
+ TLBLKCR);
+ mb();
+ /* Disable tlb locking */
+ tlblkcr &= ~(KGSL_IOMMU_TLBLKCR_LKE_MASK
+ << KGSL_IOMMU_TLBLKCR_LKE_SHIFT);
+ KGSL_IOMMU_SET_CTX_REG(iommu, iommu_unit,
+ iommu_unit->dev[j].ctx_id, TLBLKCR, tlblkcr);
+ }
+ }
+}
+
static int kgsl_iommu_start(struct kgsl_mmu *mmu)
{
int status;
@@ -1280,7 +1398,7 @@
/* We use the GPU MMU to control access to IOMMU registers on 8960 with
* a225, hence we still keep the MMU active on 8960 */
- if (cpu_is_msm8960()) {
+ if (cpu_is_msm8960() && KGSL_DEVICE_3D0 == mmu->device->id) {
struct kgsl_mh *mh = &(mmu->device->mh);
BUG_ON(iommu->iommu_units[0].reg_map.gpuaddr != 0 &&
mh->mpu_base > iommu->iommu_units[0].reg_map.gpuaddr);
@@ -1312,6 +1430,7 @@
* changing pagetables we can use this lsb value of the pagetable w/o
* having to read it again
*/
+ msm_iommu_lock();
for (i = 0; i < iommu->unit_count; i++) {
struct kgsl_iommu_unit *iommu_unit = &iommu->iommu_units[i];
for (j = 0; j < iommu_unit->dev_count; j++) {
@@ -1322,6 +1441,9 @@
TTBR0));
}
}
+ kgsl_iommu_lock_rb_in_tlb(mmu);
+ msm_iommu_unlock();
+
kgsl_iommu_disable_clk_on_ts(mmu, 0, false);
mmu->flags |= KGSL_FLAGS_STARTED;
@@ -1408,7 +1530,6 @@
*
* call this with the global lock held
*/
-
if (mmu->flags & KGSL_FLAGS_STARTED) {
/* detach iommu attachment */
kgsl_detach_pagetable_iommu_domain(mmu);
@@ -1423,10 +1544,12 @@
for (j = 0; j < iommu_unit->dev_count; j++) {
if (iommu_unit->dev[j].fault) {
kgsl_iommu_enable_clk(mmu, j);
+ msm_iommu_lock();
KGSL_IOMMU_SET_CTX_REG(iommu,
iommu_unit,
iommu_unit->dev[j].ctx_id,
RESUME, 1);
+ msm_iommu_unlock();
iommu_unit->dev[j].fault = 0;
}
}
diff --git a/drivers/gpu/msm/kgsl_iommu.h b/drivers/gpu/msm/kgsl_iommu.h
index f539b07..25f0d45 100644
--- a/drivers/gpu/msm/kgsl_iommu.h
+++ b/drivers/gpu/msm/kgsl_iommu.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. 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
@@ -19,6 +19,26 @@
#define KGSL_IOMMU_CTX_OFFSET_V2 0x8000
#define KGSL_IOMMU_CTX_SHIFT 12
+/* TLBLKCR feilds */
+#define KGSL_IOMMU_TLBLKCR_LKE_MASK 0x00000001
+#define KGSL_IOMMU_TLBLKCR_LKE_SHIFT 0
+#define KGSL_IOMMU_TLBLKCR_TLBIALLCFG_MASK 0x00000001
+#define KGSL_IOMMU_TLBLKCR_TLBIALLCFG_SHIFT 1
+#define KGSL_IOMMU_TLBLKCR_TLBIASIDCFG_MASK 0x00000001
+#define KGSL_IOMMU_TLBLKCR_TLBIASIDCFG_SHIFT 2
+#define KGSL_IOMMU_TLBLKCR_TLBIVAACFG_MASK 0x00000001
+#define KGSL_IOMMU_TLBLKCR_TLBIVAACFG_SHIFT 3
+#define KGSL_IOMMU_TLBLKCR_FLOOR_MASK 0x000000FF
+#define KGSL_IOMMU_TLBLKCR_FLOOR_SHIFT 8
+#define KGSL_IOMMU_TLBLKCR_VICTIM_MASK 0x000000FF
+#define KGSL_IOMMU_TLBLKCR_VICTIM_SHIFT 16
+
+/* V2PXX feilds */
+#define KGSL_IOMMU_V2PXX_INDEX_MASK 0x000000FF
+#define KGSL_IOMMU_V2PXX_INDEX_SHIFT 0
+#define KGSL_IOMMU_V2PXX_VA_MASK 0x000FFFFF
+#define KGSL_IOMMU_V2PXX_VA_SHIFT 12
+
enum kgsl_iommu_reg_map {
KGSL_IOMMU_GLOBAL_BASE = 0,
KGSL_IOMMU_CTX_TTBR0,
@@ -26,6 +46,8 @@
KGSL_IOMMU_CTX_FSR,
KGSL_IOMMU_CTX_TLBIALL,
KGSL_IOMMU_CTX_RESUME,
+ KGSL_IOMMU_CTX_TLBLKCR,
+ KGSL_IOMMU_CTX_V2PUR,
KGSL_IOMMU_REG_MAX
};
diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c
index 73c8df1..10f8ae1 100644
--- a/drivers/gpu/msm/kgsl_pwrctrl.c
+++ b/drivers/gpu/msm/kgsl_pwrctrl.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2012, Code Aurora Forum. 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
@@ -30,7 +30,6 @@
#define KGSL_PWRFLAGS_AXI_ON 2
#define KGSL_PWRFLAGS_IRQ_ON 3
-#define GPU_SWFI_LATENCY 3
#define UPDATE_BUSY_VAL 1000000
#define UPDATE_BUSY 50
@@ -555,6 +554,42 @@
device->pwrctrl.interval_timeout);
}
+static int kgsl_pwrctrl_pmqos_latency_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ char temp[20];
+ unsigned long val;
+ struct kgsl_device *device = kgsl_device_from_dev(dev);
+ int rc;
+
+ if (device == NULL)
+ return 0;
+
+ snprintf(temp, sizeof(temp), "%.*s",
+ (int)min(count, sizeof(temp) - 1), buf);
+ rc = kstrtoul(temp, 0, &val);
+ if (rc)
+ return rc;
+
+ mutex_lock(&device->mutex);
+ device->pwrctrl.pm_qos_latency = val;
+ mutex_unlock(&device->mutex);
+
+ return count;
+}
+
+static int kgsl_pwrctrl_pmqos_latency_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct kgsl_device *device = kgsl_device_from_dev(dev);
+ if (device == NULL)
+ return 0;
+ return snprintf(buf, PAGE_SIZE, "%d\n",
+ device->pwrctrl.pm_qos_latency);
+}
+
static int kgsl_pwrctrl_gpubusy_show(struct device *dev,
struct device_attribute *attr,
char *buf)
@@ -616,6 +651,14 @@
return num_chars;
}
+static int kgsl_pwrctrl_reset_count_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct kgsl_device *device = kgsl_device_from_dev(dev);
+ return snprintf(buf, PAGE_SIZE, "%d\n", device->reset_counter);
+}
+
DEVICE_ATTR(gpuclk, 0644, kgsl_pwrctrl_gpuclk_show, kgsl_pwrctrl_gpuclk_store);
DEVICE_ATTR(max_gpuclk, 0644, kgsl_pwrctrl_max_gpuclk_show,
kgsl_pwrctrl_max_gpuclk_store);
@@ -641,6 +684,12 @@
DEVICE_ATTR(num_pwrlevels, 0444,
kgsl_pwrctrl_num_pwrlevels_show,
NULL);
+DEVICE_ATTR(pmqos_latency, 0644,
+ kgsl_pwrctrl_pmqos_latency_show,
+ kgsl_pwrctrl_pmqos_latency_store);
+DEVICE_ATTR(reset_count, 0444,
+ kgsl_pwrctrl_reset_count_show,
+ NULL);
static const struct device_attribute *pwrctrl_attr_list[] = {
&dev_attr_gpuclk,
@@ -654,6 +703,8 @@
&dev_attr_min_pwrlevel,
&dev_attr_thermal_pwrlevel,
&dev_attr_num_pwrlevels,
+ &dev_attr_pmqos_latency,
+ &dev_attr_reset_count,
NULL
};
@@ -951,6 +1002,8 @@
}
}
+ /* Set the CPU latency to 501usec to allow low latency PC modes */
+ pwr->pm_qos_latency = 3;
pm_runtime_enable(device->parentdev);
register_early_suspend(&device->display_off);
@@ -1147,7 +1200,7 @@
_sleep_accounting(device);
kgsl_pwrctrl_clk(device, KGSL_PWRFLAGS_OFF, KGSL_STATE_SLEEP);
kgsl_pwrctrl_set_state(device, KGSL_STATE_SLEEP);
- pm_qos_update_request(&device->pm_qos_req_dma,
+ pm_qos_update_request(&device->pwrctrl.pm_qos_req_dma,
PM_QOS_DEFAULT_VALUE);
break;
case KGSL_STATE_SLEEP:
@@ -1181,7 +1234,7 @@
device->ftbl->stop(device);
_sleep_accounting(device);
kgsl_pwrctrl_set_state(device, KGSL_STATE_SLUMBER);
- pm_qos_update_request(&device->pm_qos_req_dma,
+ pm_qos_update_request(&device->pwrctrl.pm_qos_req_dma,
PM_QOS_DEFAULT_VALUE);
break;
case KGSL_STATE_SLUMBER:
@@ -1267,8 +1320,8 @@
/* Re-enable HW access */
mod_timer(&device->idle_timer,
jiffies + device->pwrctrl.interval_timeout);
- pm_qos_update_request(&device->pm_qos_req_dma,
- GPU_SWFI_LATENCY);
+ pm_qos_update_request(&device->pwrctrl.pm_qos_req_dma,
+ device->pwrctrl.pm_qos_latency);
case KGSL_STATE_ACTIVE:
kgsl_pwrctrl_request_state(device, KGSL_STATE_NONE);
break;
diff --git a/drivers/gpu/msm/kgsl_pwrctrl.h b/drivers/gpu/msm/kgsl_pwrctrl.h
index e51ec54..72ad4d1 100644
--- a/drivers/gpu/msm/kgsl_pwrctrl.h
+++ b/drivers/gpu/msm/kgsl_pwrctrl.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2012, Code Aurora Forum. 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
@@ -60,6 +60,8 @@
* @irq_name - resource name for the IRQ
* @restore_slumber - Flag to indicate that we are in a suspend/restore sequence
* @clk_stats - structure of clock statistics
+ * @pm_qos_req_dma - the power management quality of service structure
+ * @pm_qos_latency - allowed CPU latency in microseconds
*/
struct kgsl_pwrctrl {
@@ -85,6 +87,8 @@
s64 time;
unsigned int restore_slumber;
struct kgsl_clk_stats clk_stats;
+ struct pm_qos_request pm_qos_req_dma;
+ unsigned int pm_qos_latency;
};
void kgsl_pwrctrl_irq(struct kgsl_device *device, int state);
diff --git a/drivers/gpu/msm/z180.c b/drivers/gpu/msm/z180.c
index 258dcfa..8f03ccb 100644
--- a/drivers/gpu/msm/z180.c
+++ b/drivers/gpu/msm/z180.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2002,2007-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2002,2007-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
@@ -576,6 +576,9 @@
mod_timer(&device->idle_timer, jiffies + FIRST_TIMEOUT);
kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_ON);
device->ftbl->irqctrl(device, 1);
+
+ device->reset_counter++;
+
return 0;
error_clk_off:
diff --git a/drivers/hwmon/qpnp-adc-current.c b/drivers/hwmon/qpnp-adc-current.c
index c28c61d..11f351c 100644
--- a/drivers/hwmon/qpnp-adc-current.c
+++ b/drivers/hwmon/qpnp-adc-current.c
@@ -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
@@ -537,6 +537,7 @@
return rc;
}
+EXPORT_SYMBOL(qpnp_iadc_get_rsense);
int32_t qpnp_check_pmic_temp(void)
{
diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
index 86703e0..4f54a0c 100644
--- a/drivers/iommu/Kconfig
+++ b/drivers/iommu/Kconfig
@@ -16,7 +16,7 @@
# MSM IOMMU support
config MSM_IOMMU
bool "MSM IOMMU Support"
- depends on ARCH_MSM8X60 || ARCH_MSM8960 || ARCH_APQ8064 || ARCH_MSM8974 || ARCH_MPQ8092 || ARCH_MSM8910
+ depends on ARCH_MSM8X60 || ARCH_MSM8960 || ARCH_APQ8064 || ARCH_MSM8974 || ARCH_MPQ8092 || ARCH_MSM8910 || ARCH_MSM8226
select IOMMU_API
help
Support for the IOMMUs found on certain Qualcomm SOCs.
@@ -36,6 +36,16 @@
If unsure, say N here.
+config MSM_IOMMU_PMON
+ bool "MSM IOMMU Perfomance Monitoring Support"
+ depends on ARCH_MSM8974 && MSM_IOMMU
+ help
+ Support for monitoring IOMMUs performance on certain Qualcomm SOCs.
+ It captures TLB statistics per context bank of the IOMMU as an
+ indication of its performance metric.
+
+ If unsure, say N here.
+
config IOMMU_PGTABLES_L2
bool "Allow SMMU page tables in the L2 cache (Experimental)"
depends on MSM_IOMMU && MMU && SMP && CPU_DCACHE_DISABLE=n
diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile
index 11d50c1..5db4258 100644
--- a/drivers/iommu/Makefile
+++ b/drivers/iommu/Makefile
@@ -3,6 +3,7 @@
ifdef CONFIG_OF
obj-$(CONFIG_MSM_IOMMU) += msm_iommu-v2.o msm_iommu_dev-v2.o msm_iommu_pagetable.o msm_iommu_sec.o
endif
+obj-$(CONFIG_MSM_IOMMU_PMON) += msm_iommu_perfmon.o
obj-$(CONFIG_AMD_IOMMU) += amd_iommu.o amd_iommu_init.o
obj-$(CONFIG_AMD_IOMMU_V2) += amd_iommu_v2.o
obj-$(CONFIG_DMAR_TABLE) += dmar.o
diff --git a/drivers/iommu/msm_iommu-v2.c b/drivers/iommu/msm_iommu-v2.c
index e3aa30c..687269e 100644
--- a/drivers/iommu/msm_iommu-v2.c
+++ b/drivers/iommu/msm_iommu-v2.c
@@ -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
@@ -30,7 +30,7 @@
#include <mach/iommu_hw-v2.h>
#include <mach/iommu.h>
-
+#include <mach/iommu_perfmon.h>
#include "msm_iommu_pagetable.h"
/* bitmap of the page sizes currently supported */
@@ -43,6 +43,29 @@
struct list_head list_attached;
};
+static int __enable_regulators(struct msm_iommu_drvdata *drvdata)
+{
+ int ret = regulator_enable(drvdata->gdsc);
+ if (ret)
+ goto fail;
+
+ if (drvdata->alt_gdsc)
+ ret = regulator_enable(drvdata->alt_gdsc);
+
+ if (ret)
+ regulator_disable(drvdata->gdsc);
+fail:
+ return ret;
+}
+
+static void __disable_regulators(struct msm_iommu_drvdata *drvdata)
+{
+ if (drvdata->alt_gdsc)
+ regulator_disable(drvdata->alt_gdsc);
+
+ regulator_disable(drvdata->gdsc);
+}
+
static int __enable_clocks(struct msm_iommu_drvdata *drvdata)
{
int ret;
@@ -62,6 +85,14 @@
clk_disable_unprepare(drvdata->pclk);
}
}
+
+ if (drvdata->clk_reg_virt) {
+ unsigned int value;
+
+ value = readl_relaxed(drvdata->clk_reg_virt);
+ value &= ~0x1;
+ writel_relaxed(value, drvdata->clk_reg_virt);
+ }
fail:
return ret;
}
@@ -74,6 +105,53 @@
clk_disable_unprepare(drvdata->pclk);
}
+static int _iommu_power_off(void *data)
+{
+ struct msm_iommu_drvdata *drvdata;
+
+ drvdata = (struct msm_iommu_drvdata *)data;
+ __disable_clocks(drvdata);
+ __disable_regulators(drvdata);
+ return 0;
+}
+
+static int _iommu_power_on(void *data)
+{
+ int ret;
+ struct msm_iommu_drvdata *drvdata;
+
+ drvdata = (struct msm_iommu_drvdata *)data;
+ ret = __enable_regulators(drvdata);
+ if (ret)
+ goto fail;
+
+ ret = __enable_clocks(drvdata);
+ if (ret) {
+ __disable_regulators(drvdata);
+ goto fail;
+ }
+ return 0;
+fail:
+ return -EIO;
+}
+
+static void _iommu_lock_acquire(void)
+{
+ mutex_lock(&msm_iommu_lock);
+}
+
+static void _iommu_lock_release(void)
+{
+ mutex_unlock(&msm_iommu_lock);
+}
+
+struct iommu_access_ops iommu_access_ops = {
+ .iommu_power_on = _iommu_power_on,
+ .iommu_power_off = _iommu_power_off,
+ .iommu_lock_acquire = _iommu_lock_acquire,
+ .iommu_lock_release = _iommu_lock_release,
+};
+
static void __sync_tlb(void __iomem *base, int ctx)
{
SET_TLBSYNC(base, ctx, 0);
@@ -160,7 +238,6 @@
SET_GFAR(base, 0);
SET_GFSRRESTORE(base, 0);
SET_TLBIALLNSNH(base, 0);
- SET_PMCR(base, 0);
SET_SCR1(base, 0);
SET_SSDR_N(base, 0, 0);
smt_size = GET_IDR0_NUMSMRG(base);
@@ -174,10 +251,8 @@
/*
* May only be called for non-secure iommus
*/
-static void __program_iommu(void __iomem *base,
- struct msm_iommu_bfb_settings *bfb_settings)
+static void __program_iommu(void __iomem *base)
{
- int i;
__reset_iommu(base);
SET_CR0_SMCFCFG(base, 1);
@@ -189,12 +264,19 @@
SET_CR0_GFRE(base, 1);
SET_CR0_CLIENTPD(base, 0);
+ mb(); /* Make sure writes complete before returning */
+}
+
+void program_iommu_bfb_settings(void __iomem *base,
+ const struct msm_iommu_bfb_settings *bfb_settings)
+{
+ unsigned int i;
if (bfb_settings)
for (i = 0; i < bfb_settings->length; i++)
SET_GLOBAL_REG(base, bfb_settings->regs[i],
bfb_settings->data[i]);
- mb(); /* Make sure writes complete before returning */
+ mb(); /* Make sure writes complete before returning */
}
static void __reset_context(void __iomem *base, int ctx)
@@ -478,29 +560,30 @@
is_secure = iommu_drvdata->sec_id != -1;
- ret = regulator_enable(iommu_drvdata->gdsc);
+ ret = __enable_regulators(iommu_drvdata);
if (ret)
goto fail;
ret = __enable_clocks(iommu_drvdata);
if (ret) {
- regulator_disable(iommu_drvdata->gdsc);
+ __disable_regulators(iommu_drvdata);
goto fail;
}
if (!msm_iommu_ctx_attached(dev->parent)) {
if (!is_secure) {
- __program_iommu(iommu_drvdata->base,
- iommu_drvdata->bfb_settings);
+ __program_iommu(iommu_drvdata->base);
} else {
ret = msm_iommu_sec_program_iommu(
iommu_drvdata->sec_id);
if (ret) {
- regulator_disable(iommu_drvdata->gdsc);
+ __disable_regulators(iommu_drvdata);
__disable_clocks(iommu_drvdata);
goto fail;
}
}
+ program_iommu_bfb_settings(iommu_drvdata->base,
+ iommu_drvdata->bfb_settings);
}
__program_context(iommu_drvdata, ctx_drvdata, __pa(priv->pt.fl_table),
@@ -511,6 +594,10 @@
list_add(&(ctx_drvdata->attached_elm), &priv->list_attached);
ctx_drvdata->attached_domain = domain;
+ mutex_unlock(&msm_iommu_lock);
+
+ msm_iommu_attached(dev->parent);
+ return ret;
fail:
mutex_unlock(&msm_iommu_lock);
return ret;
@@ -525,6 +612,8 @@
int ret;
int is_secure;
+ msm_iommu_detached(dev->parent);
+
mutex_lock(&msm_iommu_lock);
priv = domain->priv;
if (!priv || !dev)
@@ -550,7 +639,7 @@
__disable_clocks(iommu_drvdata);
- regulator_disable(iommu_drvdata->gdsc);
+ __disable_regulators(iommu_drvdata);
list_del_init(&ctx_drvdata->attached_elm);
ctx_drvdata->attached_domain = NULL;
diff --git a/drivers/iommu/msm_iommu_dev-v2.c b/drivers/iommu/msm_iommu_dev-v2.c
index 7961280..4571595 100644
--- a/drivers/iommu/msm_iommu_dev-v2.c
+++ b/drivers/iommu/msm_iommu_dev-v2.c
@@ -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
@@ -27,6 +27,7 @@
#include <mach/iommu_hw-v2.h>
#include <mach/iommu.h>
+#include <mach/iommu_perfmon.h>
static int msm_iommu_parse_bfb_settings(struct platform_device *pdev,
struct msm_iommu_drvdata *drvdata)
@@ -93,6 +94,7 @@
{
struct device_node *child;
int ret = 0;
+ struct resource *r;
drvdata->dev = &pdev->dev;
msm_iommu_add_drv(drvdata);
@@ -107,17 +109,50 @@
pr_err("Failed to create %s device\n", child->name);
}
- drvdata->name = dev_name(&pdev->dev);
+ ret = of_property_read_string(pdev->dev.of_node, "label",
+ &drvdata->name);
+ if (ret)
+ goto fail;
+
drvdata->sec_id = -1;
of_property_read_u32(pdev->dev.of_node, "qcom,iommu-secure-id",
&drvdata->sec_id);
+
+ r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "clk_base");
+ if (r) {
+ drvdata->clk_reg_virt = devm_ioremap(&pdev->dev, r->start,
+ resource_size(r));
+ if (!drvdata->clk_reg_virt) {
+ pr_err("Failed to map 0x%x for iommu clk\n",
+ r->start);
+ ret = -ENOMEM;
+ goto fail;
+ }
+ }
+
return 0;
fail:
return ret;
}
+static int msm_iommu_pmon_parse_dt(struct platform_device *pdev,
+ struct iommu_info *pmon_info)
+{
+ int ret = 0;
+ int irq = platform_get_irq(pdev, 0);
+
+ if (irq > 0) {
+ pmon_info->evt_irq = platform_get_irq(pdev, 0);
+ } else {
+ pmon_info->evt_irq = -1;
+ ret = irq;
+ }
+ return ret;
+}
+
static int __devinit msm_iommu_probe(struct platform_device *pdev)
{
+ struct iommu_info *pmon_info;
struct msm_iommu_drvdata *drvdata;
struct resource *r;
int ret, needs_alt_core_clk;
@@ -126,7 +161,7 @@
if (!drvdata)
return -ENOMEM;
- r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "iommu_base");
if (!r)
return -EINVAL;
@@ -140,6 +175,10 @@
if (IS_ERR(drvdata->gdsc))
return -EINVAL;
+ drvdata->alt_gdsc = devm_regulator_get(&pdev->dev, "qcom,alt-vdd");
+ if (IS_ERR(drvdata->alt_gdsc))
+ drvdata->alt_gdsc = NULL;
+
drvdata->pclk = devm_clk_get(&pdev->dev, "iface_clk");
if (IS_ERR(drvdata->pclk))
return PTR_ERR(drvdata->pclk);
@@ -170,11 +209,32 @@
if (ret)
return ret;
- pr_info("device %s mapped at %p, with %d ctx banks\n",
+ dev_info(&pdev->dev, "device %s mapped at %p, with %d ctx banks\n",
drvdata->name, drvdata->base, drvdata->ncb);
platform_set_drvdata(pdev, drvdata);
+ pmon_info = msm_iommu_pm_alloc(&pdev->dev);
+ if (pmon_info != NULL) {
+ ret = msm_iommu_pmon_parse_dt(pdev, pmon_info);
+ if (ret) {
+ msm_iommu_pm_free(&pdev->dev);
+ pr_info("%s: pmon not available.\n", drvdata->name);
+ } else {
+ pmon_info->base = drvdata->base;
+ pmon_info->ops = &iommu_access_ops;
+ pmon_info->iommu_name = drvdata->name;
+ ret = msm_iommu_pm_iommu_register(pmon_info);
+ if (ret) {
+ pr_err("%s iommu register fail\n",
+ drvdata->name);
+ msm_iommu_pm_free(&pdev->dev);
+ } else {
+ pr_debug("%s iommu registered for pmon\n",
+ pmon_info->iommu_name);
+ }
+ }
+ }
return 0;
}
@@ -182,6 +242,9 @@
{
struct msm_iommu_drvdata *drv = NULL;
+ msm_iommu_pm_iommu_unregister(&pdev->dev);
+ msm_iommu_pm_free(&pdev->dev);
+
drv = platform_get_drvdata(pdev);
if (drv) {
msm_iommu_remove_drv(drv);
diff --git a/drivers/iommu/msm_iommu_perfmon.c b/drivers/iommu/msm_iommu_perfmon.c
new file mode 100644
index 0000000..15302b1
--- /dev/null
+++ b/drivers/iommu/msm_iommu_perfmon.c
@@ -0,0 +1,1104 @@
+/* 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
+ * 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.
+ */
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/iommu.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/bitops.h>
+#include <linux/debugfs.h>
+#include <mach/iommu_hw-v2.h>
+#include <mach/iommu.h>
+#include <mach/iommu_perfmon.h>
+
+#define PMCR_P_MASK (0x1)
+#define PMCR_P_SHIFT (1)
+#define PMCR_P (PMCR_P_MASK << PMCR_P_SHIFT)
+#define PMCFGR_NCG_MASK (0xFF)
+#define PMCFGR_NCG_SHIFT (24)
+#define PMCFGR_NCG (PMCFGR_NCG_MASK << PMCFGR_NCG_SHIFT)
+#define PMCFGR_N_MASK (0xFF)
+#define PMCFGR_N_SHIFT (0)
+#define PMCFGR_N (PMCFGR_N_MASK << PMCFGR_N_SHIFT)
+#define CR_E 0x1
+#define CGCR_CEN 0x800
+#define CGCR_CEN_SHFT (1 << 11)
+#define PMCGCR_CGNC_MASK (0x0F)
+#define PMCGCR_CGNC_SHIFT (24)
+#define PMCGCR_CGNC (PMCGCR_CGNC_MASK << PMCGCR_CGNC_SHIFT)
+#define PMCGCR_(group) (PMCGCR_N + group*4)
+
+#define PMOVSCLR_(n) (PMOVSCLR_N + n*4)
+#define PMCNTENSET_(n) (PMCNTENSET_N + n*4)
+#define PMCNTENCLR_(n) (PMCNTENCLR_N + n*4)
+#define PMINTENSET_(n) (PMINTENSET_N + n*4)
+#define PMINTENCLR_(n) (PMINTENCLR_N + n*4)
+
+#define PMEVCNTR_(n) (PMEVCNTR_N + n*4)
+#define PMEVTYPER_(n) (PMEVTYPER_N + n*4)
+
+static LIST_HEAD(iommu_list);
+static struct dentry *msm_iommu_root_debugfs_dir;
+static const char *NO_EVENT_CLASS_NAME = "none";
+static int NO_EVENT_CLASS = -1;
+static const unsigned int MAX_EVEN_CLASS_NAME_LEN = 36;
+
+struct event_class {
+ unsigned int event_number;
+ const char *desc;
+};
+
+static struct event_class pmu_event_classes[] = {
+ { 0x00, "cycle_count" },
+ { 0x01, "cycle_count64" },
+ { 0x08, "tlb_refill" },
+ { 0x09, "tlb_refill_read" },
+ { 0x0A, "tlb_refill_write" },
+ { 0x10, "access" },
+ { 0x11, "access_read" },
+ { 0x12, "access_write" },
+};
+
+static struct event_class pmu_event_classes_impl_defined[] = {
+ { 0x80, "full_misses" },
+ { 0x81, "partial_miss_1lbfb_hit" },
+ { 0x82, "partial_miss_2lbfb_hit" },
+ { 0x83, "full_hit" },
+ { 0x90, "pred_req_full_miss" },
+ { 0x91, "pred_req_partial_miss_1lbfb_hit" },
+ { 0x92, "pred_req_partial_miss_2lbfb_hit" },
+ { 0xb0, "tot_num_miss_axi_htw_read_req" },
+ { 0xb1, "tot_num_pred_axi_htw_read_req" },
+};
+
+static unsigned int iommu_pm_is_hw_access_OK(const struct iommu_pmon *pmon)
+{
+ return pmon->enabled && (pmon->iommu_attach_count > 0);
+}
+
+static unsigned int iommu_pm_create_sup_cls_str(char **buf,
+ unsigned int event_cls)
+{
+ unsigned long buf_size = (ARRAY_SIZE(pmu_event_classes) +
+ ARRAY_SIZE(pmu_event_classes_impl_defined)) *
+ MAX_EVEN_CLASS_NAME_LEN;
+ unsigned int pos = 0;
+
+ *buf = kzalloc(buf_size, GFP_KERNEL);
+ if (*buf) {
+ int i;
+ struct event_class *ptr;
+ size_t array_len = ARRAY_SIZE(pmu_event_classes);
+ ptr = pmu_event_classes;
+
+ for (i = 0; i < array_len; ++i) {
+ if ((1 << ptr[i].event_number) & event_cls) {
+ if (pos < buf_size) {
+ pos += snprintf(&(*buf)[pos],
+ buf_size-pos,
+ "[%u] %s\n",
+ ptr[i].event_number,
+ ptr[i].desc);
+ }
+
+ }
+ }
+
+ /*
+ * No way to read a register to check if impl. defined
+ * classes are supported or not so we just assume all of them
+ * are
+ */
+ array_len = ARRAY_SIZE(pmu_event_classes_impl_defined);
+ ptr = pmu_event_classes_impl_defined;
+
+ for (i = 0; i < array_len; ++i) {
+ if (buf_size > pos) {
+ pos += snprintf(&(*buf)[pos],
+ buf_size-pos,
+ "[%u] %s\n",
+ ptr[i].event_number,
+ ptr[i].desc);
+ }
+ }
+ }
+ return pos;
+}
+
+static const char *iommu_pm_find_event_class_name(int event_class)
+{
+ size_t array_len;
+ struct event_class *ptr;
+ int i;
+ const char *event_class_name = NO_EVENT_CLASS_NAME;
+ if (event_class < 0) {
+ goto out;
+ } else if (event_class < 0x80) {
+ array_len = ARRAY_SIZE(pmu_event_classes);
+ ptr = pmu_event_classes;
+ } else {
+ /* All implementation defined classes are above 0x7F */
+ array_len = ARRAY_SIZE(pmu_event_classes_impl_defined);
+ ptr = pmu_event_classes_impl_defined;
+ }
+
+ for (i = 0; i < array_len; ++i) {
+ if (ptr[i].event_number == event_class) {
+ event_class_name = ptr[i].desc;
+ break;
+ }
+ }
+
+out:
+ return event_class_name;
+}
+
+static int iommu_pm_find_event_class(const char *event_class_name)
+{
+ size_t array_len;
+ struct event_class *ptr;
+ int i;
+ int event_class = NO_EVENT_CLASS;
+
+ if (strcmp(event_class_name, NO_EVENT_CLASS_NAME) == 0)
+ goto out;
+
+ array_len = ARRAY_SIZE(pmu_event_classes);
+ ptr = pmu_event_classes;
+
+ for (i = 0; i < array_len; ++i) {
+ if (strcmp(ptr[i].desc, event_class_name) == 0) {
+ event_class = ptr[i].event_number;
+ goto out;
+ }
+ }
+
+ array_len = ARRAY_SIZE(pmu_event_classes_impl_defined);
+ ptr = pmu_event_classes_impl_defined;
+
+ for (i = 0; i < array_len; ++i) {
+ if (strcmp(ptr[i].desc, event_class_name) == 0) {
+ event_class = ptr[i].event_number;
+ goto out;
+ }
+ }
+
+out:
+ return event_class;
+}
+
+static inline void iommu_pm_add_to_iommu_list(struct iommu_pmon *iommu_pmon)
+{
+ list_add(&iommu_pmon->iommu_list, &iommu_list);
+}
+
+static inline void iommu_pm_del_from_iommu_list(struct iommu_pmon *iommu_pmon)
+{
+ list_del(&iommu_pmon->iommu_list);
+}
+
+static struct iommu_pmon *iommu_pm_get_pm_by_dev(struct device *dev)
+{
+ struct iommu_pmon *pmon;
+ struct iommu_info *info;
+ struct list_head *ent;
+ list_for_each(ent, &iommu_list) {
+ pmon = list_entry(ent, struct iommu_pmon, iommu_list);
+ info = &pmon->iommu;
+ if (dev == info->iommu_dev)
+ return pmon;
+ }
+ return NULL;
+}
+
+static void iommu_pm_grp_enable(struct iommu_info *iommu, unsigned int grp_no)
+{
+ unsigned int pmcgcr;
+ pmcgcr = readl_relaxed(iommu->base + PMCGCR_(grp_no));
+ pmcgcr |= CGCR_CEN;
+ writel_relaxed(pmcgcr, iommu->base + PMCGCR_(grp_no));
+}
+
+static void iommu_pm_grp_disable(struct iommu_info *iommu, unsigned int grp_no)
+{
+ unsigned int pmcgcr;
+ pmcgcr = readl_relaxed(iommu->base + PMCGCR_(grp_no));
+ pmcgcr &= ~CGCR_CEN;
+ writel_relaxed(pmcgcr, iommu->base + PMCGCR_(grp_no));
+}
+
+static void iommu_pm_enable(struct iommu_info *iommu)
+{
+ unsigned int pmcr;
+ pmcr = readl_relaxed(iommu->base + PMCR);
+ pmcr |= CR_E;
+ writel_relaxed(pmcr, iommu->base + PMCR);
+}
+
+void iommu_pm_disable(struct iommu_info *iommu)
+{
+ unsigned int pmcr;
+ pmcr = readl_relaxed(iommu->base + PMCR);
+ pmcr &= ~CR_E;
+ writel_relaxed(pmcr, iommu->base + PMCR);
+}
+
+static void iommu_pm_reset_counters(const struct iommu_info *iommu)
+{
+ unsigned int pmcr;
+ pmcr = readl_relaxed(iommu->base + PMCR);
+ pmcr |= PMCR_P;
+ writel_relaxed(pmcr, iommu->base + PMCR);
+}
+
+static void iommu_pm_check_for_overflow(struct iommu_pmon *pmon)
+{
+ struct iommu_pmon_counter *counter;
+ struct iommu_info *iommu = &pmon->iommu;
+ unsigned int reg_no = 0;
+ unsigned int bit_no;
+ unsigned int reg_value;
+ unsigned int i;
+ unsigned int j;
+ unsigned int curr_reg = 0;
+
+ reg_value = readl_relaxed(iommu->base + PMOVSCLR_(curr_reg));
+
+ for (i = 0; i < pmon->num_groups; ++i) {
+ struct iommu_pmon_cnt_group *cnt_grp = &pmon->cnt_grp[i];
+ for (j = 0; j < cnt_grp->num_counters; ++j) {
+ counter = &cnt_grp->counters[j];
+ reg_no = counter->absolute_counter_no / 32;
+ bit_no = counter->absolute_counter_no % 32;
+ if (reg_no != curr_reg) {
+ /* Clear overflow bits */
+ writel_relaxed(reg_value, iommu->base +
+ PMOVSCLR_(reg_no));
+ curr_reg = reg_no;
+ reg_value = readl_relaxed(iommu->base +
+ PMOVSCLR_(curr_reg));
+ }
+
+ if (counter->enabled) {
+ if (reg_value & (1 << bit_no))
+ counter->overflow_count++;
+ }
+ }
+ }
+
+ /* Clear overflow */
+ writel_relaxed(reg_value, iommu->base + PMOVSCLR_(reg_no));
+}
+
+irqreturn_t iommu_pm_evt_ovfl_int_handler(int irq, void *dev_id)
+{
+ struct iommu_pmon *pmon = dev_id;
+ struct iommu_info *iommu = &pmon->iommu;
+
+ mutex_lock(&pmon->lock);
+
+ if (!iommu_pm_is_hw_access_OK(pmon)) {
+ mutex_unlock(&pmon->lock);
+ goto out;
+ }
+
+ iommu->ops->iommu_lock_acquire();
+ iommu_pm_check_for_overflow(pmon);
+ iommu->ops->iommu_lock_release();
+
+ mutex_unlock(&pmon->lock);
+
+out:
+ return IRQ_HANDLED;
+}
+
+static void iommu_pm_counter_enable(struct iommu_info *iommu,
+ struct iommu_pmon_counter *counter)
+{
+ unsigned int reg_no = counter->absolute_counter_no / 32;
+ unsigned int bit_no = counter->absolute_counter_no % 32;
+ unsigned int reg_value;
+
+ /* Clear overflow of counter */
+ reg_value = 1 << bit_no;
+ writel_relaxed(reg_value, iommu->base + PMOVSCLR_(reg_no));
+
+ /* Enable counter */
+ writel_relaxed(reg_value, iommu->base + PMCNTENSET_(reg_no));
+ counter->enabled = 1;
+}
+
+static void iommu_pm_counter_disable(struct iommu_info *iommu,
+ struct iommu_pmon_counter *counter)
+{
+ unsigned int reg_no = counter->absolute_counter_no / 32;
+ unsigned int bit_no = counter->absolute_counter_no % 32;
+ unsigned int reg_value;
+
+ counter->enabled = 0;
+
+ /* Disable counter */
+ reg_value = 1 << bit_no;
+ writel_relaxed(reg_value, iommu->base + PMCNTENCLR_(reg_no));
+
+ /* Clear overflow of counter */
+ writel_relaxed(reg_value, iommu->base + PMOVSCLR_(reg_no));
+}
+
+/*
+ * Must be called after iommu_start_access() is called
+ */
+static void iommu_pm_ovfl_int_enable(struct iommu_info *iommu,
+ const struct iommu_pmon_counter *counter)
+{
+ unsigned int reg_no = counter->absolute_counter_no / 32;
+ unsigned int bit_no = counter->absolute_counter_no % 32;
+ unsigned int reg_value;
+
+ /* Enable overflow interrupt for counter */
+ reg_value = (1 << bit_no);
+ writel_relaxed(reg_value, iommu->base + PMINTENSET_(reg_no));
+}
+
+/*
+ * Must be called after iommu_start_access() is called
+ */
+static void iommu_pm_ovfl_int_disable(struct iommu_info *iommu,
+ const struct iommu_pmon_counter *counter)
+{
+ unsigned int reg_no = counter->absolute_counter_no / 32;
+ unsigned int bit_no = counter->absolute_counter_no % 32;
+ unsigned int reg_value;
+
+ /* Disable overflow interrupt for counter */
+ reg_value = 1 << bit_no;
+ writel_relaxed(reg_value, iommu->base + PMINTENCLR_(reg_no));
+}
+
+static void iommu_pm_set_event_type(struct iommu_pmon *pmon,
+ struct iommu_pmon_counter *counter)
+{
+ int event_class;
+ unsigned int count_no;
+ struct iommu_info *iommu = &pmon->iommu;
+
+ event_class = counter->current_event_class;
+ count_no = counter->absolute_counter_no;
+
+ if (event_class == NO_EVENT_CLASS) {
+ if (iommu_pm_is_hw_access_OK(pmon)) {
+ iommu->ops->iommu_lock_acquire();
+ iommu_pm_counter_disable(iommu, counter);
+ iommu_pm_ovfl_int_disable(iommu, counter);
+ writel_relaxed(0, iommu->base + PMEVTYPER_(count_no));
+ iommu->ops->iommu_lock_release();
+ }
+ counter->overflow_count = 0;
+ counter->value = 0;
+ } else {
+ counter->overflow_count = 0;
+ counter->value = 0;
+ if (iommu_pm_is_hw_access_OK(pmon)) {
+ iommu->ops->iommu_lock_acquire();
+ writel_relaxed(event_class,
+ iommu->base + PMEVTYPER_(count_no));
+ iommu_pm_ovfl_int_enable(iommu, counter);
+ iommu_pm_counter_enable(iommu, counter);
+ iommu->ops->iommu_lock_release();
+ }
+ }
+}
+
+static void iommu_pm_reset_counts(struct iommu_pmon *pmon)
+{
+ unsigned int i;
+ unsigned int j;
+ for (i = 0; i < pmon->num_groups; ++i) {
+ struct iommu_pmon_cnt_group *cnt_grp = &pmon->cnt_grp[i];
+ for (j = 0; j < cnt_grp->num_counters; ++j) {
+ cnt_grp->counters[j].value = 0;
+ cnt_grp->counters[j].overflow_count = 0;
+ }
+ }
+}
+
+static unsigned int iommu_pm_read_counter(struct iommu_pmon_counter *counter)
+{
+ struct iommu_pmon *pmon = counter->cnt_group->pmon;
+ struct iommu_info *info = &pmon->iommu;
+ unsigned int cnt_no = counter->absolute_counter_no;
+ unsigned int pmevcntr;
+
+ pmevcntr = readl_relaxed(info->base + PMEVCNTR_(cnt_no));
+
+ return pmevcntr;
+
+}
+
+static void iommu_pm_set_all_counters(struct iommu_pmon *pmon)
+{
+ unsigned int i;
+ unsigned int j;
+ for (i = 0; i < pmon->num_groups; ++i) {
+ struct iommu_pmon_cnt_group *cnt_grp = &pmon->cnt_grp[i];
+ for (j = 0; j < cnt_grp->num_counters; ++j)
+ iommu_pm_set_event_type(pmon, &cnt_grp->counters[j]);
+ }
+}
+
+static void iommu_pm_read_all_counters(struct iommu_pmon *pmon)
+{
+ unsigned int i;
+ unsigned int j;
+ for (i = 0; i < pmon->num_groups; ++i) {
+ struct iommu_pmon_cnt_group *cnt_grp = &pmon->cnt_grp[i];
+ for (j = 0; j < cnt_grp->num_counters; ++j) {
+ struct iommu_pmon_counter *counter;
+ counter = &cnt_grp->counters[j];
+ counter->value = iommu_pm_read_counter(counter);
+ }
+ }
+}
+
+static void iommu_pm_on(struct iommu_pmon *pmon)
+{
+ unsigned int i;
+ struct iommu_info *iommu = &pmon->iommu;
+ struct msm_iommu_drvdata *iommu_drvdata =
+ dev_get_drvdata(iommu->iommu_dev);
+
+ iommu->ops->iommu_power_on(iommu_drvdata);
+
+ iommu_pm_reset_counts(pmon);
+
+ pmon->enabled = 1;
+
+ iommu_pm_set_all_counters(pmon);
+
+ iommu->ops->iommu_lock_acquire();
+
+ /* enable all counter group */
+ for (i = 0; i < pmon->num_groups; ++i)
+ iommu_pm_grp_enable(iommu, i);
+
+ /* enable global counters */
+ iommu_pm_enable(iommu);
+ iommu->ops->iommu_lock_release();
+
+ pr_info("%s: TLB performance monitoring turned ON\n",
+ pmon->iommu.iommu_name);
+}
+
+static void iommu_pm_off(struct iommu_pmon *pmon)
+{
+ unsigned int i;
+ struct iommu_info *iommu = &pmon->iommu;
+ struct msm_iommu_drvdata *iommu_drvdata =
+ dev_get_drvdata(iommu->iommu_dev);
+
+ pmon->enabled = 0;
+
+ iommu->ops->iommu_lock_acquire();
+
+ /* disable global counters */
+ iommu_pm_disable(iommu);
+
+ /* Check if we overflowed just before turning off pmon */
+ iommu_pm_check_for_overflow(pmon);
+
+ /* disable all counter group */
+ for (i = 0; i < pmon->num_groups; ++i)
+ iommu_pm_grp_disable(iommu, i);
+
+ /* Update cached copy of counters before turning off power */
+ iommu_pm_read_all_counters(pmon);
+
+ iommu->ops->iommu_lock_release();
+ iommu->ops->iommu_power_off(iommu_drvdata);
+
+ pr_info("%s: TLB performance monitoring turned OFF\n",
+ pmon->iommu.iommu_name);
+}
+
+static unsigned int iommu_pm_get_num_groups(struct iommu_pmon *iommu_pmon)
+{
+ unsigned int pmcfgr;
+ unsigned int num_cntgrp;
+ struct iommu_info *iommu = &iommu_pmon->iommu;
+
+ pmcfgr = readl_relaxed(iommu->base + PMCFGR);
+
+ /* Due to a bug in IOMMU hardware the counter register is returning
+ * the wrong information. num_cntgrp should return "total number
+ * of counter groups - 1". However, it returns "total number
+ * of counter groups". Thus we do not add 1 to the total number of
+ * counter groups. If we find that the number of counter group is 0
+ * then we assume the bug has been fixed and add 1 to the count.
+ */
+ num_cntgrp = ((pmcfgr & PMCFGR_NCG) >> PMCFGR_NCG_SHIFT);
+ if (num_cntgrp == 0)
+ num_cntgrp++;
+
+ return num_cntgrp;
+
+}
+
+static unsigned int iommu_pm_get_num_counters(struct iommu_pmon *iommu_pmon,
+ unsigned int group_no)
+{
+ unsigned int num_counters;
+ unsigned int tmp;
+ struct iommu_info *iommu = &iommu_pmon->iommu;
+
+ tmp = readl_relaxed(iommu->base + PMCGCR_(group_no));
+ num_counters = ((tmp & PMCGCR_CGNC) >> PMCGCR_CGNC_SHIFT);
+
+ return num_counters;
+
+}
+
+static unsigned int iommu_pm_get_sup_ev_cls(struct iommu_info *iommu)
+{
+ return readl_relaxed(iommu->base + PMCEID0);
+}
+
+static int iommu_pm_debug_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+static ssize_t iommu_pm_count_value_read(struct file *fp,
+ char __user *user_buff,
+ size_t count, loff_t *pos)
+{
+ size_t rd_cnt;
+ unsigned long long full_count;
+
+ struct iommu_pmon_counter *counter = fp->private_data;
+ struct iommu_pmon *pmon = counter->cnt_group->pmon;
+ struct iommu_info *iommu = &pmon->iommu;
+ char buf[50];
+ size_t len;
+
+ mutex_lock(&pmon->lock);
+
+ if (iommu_pm_is_hw_access_OK(pmon)) {
+ iommu->ops->iommu_lock_acquire();
+ counter->value = iommu_pm_read_counter(counter);
+ iommu->ops->iommu_lock_release();
+ }
+ full_count = (unsigned long long) counter->value +
+ ((unsigned long long)counter->overflow_count *
+ 0x100000000ULL);
+
+ len = snprintf(buf, 50, "%llu\n", full_count);
+ rd_cnt = simple_read_from_buffer(user_buff, count, pos, buf, len);
+ mutex_unlock(&pmon->lock);
+
+ return rd_cnt;
+}
+
+static const struct file_operations cnt_value_file_ops = {
+ .open = iommu_pm_debug_open,
+ .read = iommu_pm_count_value_read,
+};
+
+static ssize_t iommu_pm_event_class_read(struct file *fp,
+ char __user *user_buff,
+ size_t count, loff_t *pos)
+{
+ size_t rd_cnt;
+ struct iommu_pmon_counter *counter = fp->private_data;
+ struct iommu_pmon *pmon = counter->cnt_group->pmon;
+ char buf[50];
+ const char *event_class_name;
+ size_t len;
+
+ mutex_lock(&pmon->lock);
+ event_class_name = iommu_pm_find_event_class_name(
+ counter->current_event_class);
+ len = snprintf(buf, 50, "%s\n", event_class_name);
+
+ rd_cnt = simple_read_from_buffer(user_buff, count, pos, buf, len);
+ mutex_unlock(&pmon->lock);
+ return rd_cnt;
+}
+
+static ssize_t iommu_pm_event_class_write(struct file *fp,
+ const char __user *user_buff,
+ size_t count, loff_t *pos)
+{
+ size_t wr_cnt;
+ char buf[50];
+ size_t buf_size = sizeof(buf);
+ struct iommu_pmon_counter *counter = fp->private_data;
+ struct iommu_pmon *pmon = counter->cnt_group->pmon;
+ int current_event_class;
+
+ if ((count + *pos) >= buf_size)
+ return -EINVAL;
+
+ mutex_lock(&pmon->lock);
+ current_event_class = counter->current_event_class;
+ wr_cnt = simple_write_to_buffer(buf, buf_size, pos, user_buff, count);
+ if (wr_cnt >= 1) {
+ int rv;
+ long value;
+ buf[wr_cnt-1] = '\0';
+ rv = kstrtol(buf, 50, &value);
+ if (!rv) {
+ counter->current_event_class =
+ iommu_pm_find_event_class(
+ iommu_pm_find_event_class_name(value));
+ } else {
+ counter->current_event_class =
+ iommu_pm_find_event_class(buf);
+ } }
+
+ if (current_event_class != counter->current_event_class)
+ iommu_pm_set_event_type(pmon, counter);
+
+ mutex_unlock(&pmon->lock);
+ return wr_cnt;
+}
+
+static const struct file_operations event_class_file_ops = {
+ .open = iommu_pm_debug_open,
+ .read = iommu_pm_event_class_read,
+ .write = iommu_pm_event_class_write,
+};
+
+static ssize_t iommu_reset_counters_write(struct file *fp,
+ const char __user *user_buff,
+ size_t count, loff_t *pos)
+{
+ size_t wr_cnt;
+ char buf[10];
+ size_t buf_size = sizeof(buf);
+ struct iommu_pmon *pmon = fp->private_data;
+ struct iommu_info *iommu = &pmon->iommu;
+
+ if ((count + *pos) >= buf_size)
+ return -EINVAL;
+
+ mutex_lock(&pmon->lock);
+ wr_cnt = simple_write_to_buffer(buf, buf_size, pos, user_buff, count);
+ if (wr_cnt >= 1) {
+ unsigned long cmd = 0;
+ int rv;
+ buf[wr_cnt-1] = '\0';
+ rv = kstrtoul(buf, 10, &cmd);
+ if (!rv && (cmd == 1)) {
+ if (iommu_pm_is_hw_access_OK(pmon)) {
+ iommu->ops->iommu_lock_acquire();
+ iommu_pm_reset_counters(&pmon->iommu);
+ iommu->ops->iommu_lock_release();
+ }
+ iommu_pm_reset_counts(pmon);
+ pr_info("TLB performance counters reset\n");
+ } else {
+ pr_err("Unknown performance monitor command: %lu\n",
+ cmd);
+ }
+ }
+ mutex_unlock(&pmon->lock);
+ return wr_cnt;
+}
+
+static const struct file_operations reset_file_ops = {
+ .open = iommu_pm_debug_open,
+ .write = iommu_reset_counters_write,
+};
+
+static ssize_t iommu_pm_enable_counters_read(struct file *fp,
+ char __user *user_buff,
+ size_t count, loff_t *pos)
+{
+ size_t rd_cnt;
+ char buf[5];
+ size_t len;
+ struct iommu_pmon *pmon = fp->private_data;
+
+ mutex_lock(&pmon->lock);
+ len = snprintf(buf, 5, "%u\n", pmon->enabled);
+ rd_cnt = simple_read_from_buffer(user_buff, count, pos, buf, len);
+ mutex_unlock(&pmon->lock);
+ return rd_cnt;
+}
+
+static ssize_t iommu_pm_enable_counters_write(struct file *fp,
+ const char __user *user_buff,
+ size_t count, loff_t *pos)
+{
+ size_t wr_cnt;
+ char buf[10];
+ size_t buf_size = sizeof(buf);
+ struct iommu_pmon *pmon = fp->private_data;
+
+ if ((count + *pos) >= buf_size)
+ return -EINVAL;
+
+ mutex_lock(&pmon->lock);
+ wr_cnt = simple_write_to_buffer(buf, buf_size, pos, user_buff, count);
+ if (wr_cnt >= 1) {
+ unsigned long cmd;
+ int rv;
+ buf[wr_cnt-1] = '\0';
+ rv = kstrtoul(buf, 10, &cmd);
+ if (!rv && (cmd < 2)) {
+ if (pmon->enabled == 1 && cmd == 0) {
+ if (pmon->iommu_attach_count > 0)
+ iommu_pm_off(pmon);
+ } else if (pmon->enabled == 0 && cmd == 1) {
+ /* We can only turn on perf. monitoring if
+ * iommu is attached. Delay turning on perf.
+ * monitoring until we are attached.
+ */
+ if (pmon->iommu_attach_count > 0)
+ iommu_pm_on(pmon);
+ else
+ pmon->enabled = 1;
+ }
+ } else {
+ pr_err("Unknown performance monitor command: %lu\n",
+ cmd);
+ }
+ }
+ mutex_unlock(&pmon->lock);
+ return wr_cnt;
+}
+
+static const struct file_operations event_enable_file_ops = {
+ .open = iommu_pm_debug_open,
+ .read = iommu_pm_enable_counters_read,
+ .write = iommu_pm_enable_counters_write,
+};
+
+static ssize_t iommu_pm_avail_event_cls_read(struct file *fp,
+ char __user *user_buff,
+ size_t count, loff_t *pos)
+{
+ size_t rd_cnt = 0;
+ struct iommu_pmon *pmon = fp->private_data;
+ char *buf;
+ size_t len;
+
+ mutex_lock(&pmon->lock);
+
+ len = iommu_pm_create_sup_cls_str(&buf, pmon->event_cls_supp_value);
+ if (buf) {
+ rd_cnt = simple_read_from_buffer(user_buff, count, pos,
+ buf, len);
+ kfree(buf);
+ }
+ mutex_unlock(&pmon->lock);
+ return rd_cnt;
+}
+
+static const struct file_operations available_event_cls_file_ops = {
+ .open = iommu_pm_debug_open,
+ .read = iommu_pm_avail_event_cls_read,
+};
+
+
+
+static int iommu_pm_create_grp_debugfs_counters_hierarchy(
+ struct iommu_pmon_cnt_group *cnt_grp,
+ unsigned int *abs_counter_no)
+{
+ int ret = 0;
+ int j;
+ char name[20];
+
+ for (j = 0; j < cnt_grp->num_counters; ++j) {
+ struct dentry *grp_dir = cnt_grp->group_dir;
+ struct dentry *counter_dir;
+ cnt_grp->counters[j].cnt_group = cnt_grp;
+ cnt_grp->counters[j].counter_no = j;
+ cnt_grp->counters[j].absolute_counter_no = *abs_counter_no;
+ (*abs_counter_no)++;
+ cnt_grp->counters[j].value = 0;
+ cnt_grp->counters[j].overflow_count = 0;
+ cnt_grp->counters[j].current_event_class = NO_EVENT_CLASS;
+
+ snprintf(name, 20, "counter%u", j);
+
+ counter_dir = debugfs_create_dir(name, grp_dir);
+
+ if (IS_ERR_OR_NULL(counter_dir)) {
+ pr_err("unable to create counter debugfs dir %s\n",
+ name);
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ cnt_grp->counters[j].counter_dir = counter_dir;
+
+ if (!debugfs_create_file("value", 0644, counter_dir,
+ &cnt_grp->counters[j],
+ &cnt_value_file_ops)) {
+ ret = -EIO;
+ goto out;
+ }
+
+ if (!debugfs_create_file("current_event_class", 0644,
+ counter_dir, &cnt_grp->counters[j],
+ &event_class_file_ops)) {
+ ret = -EIO;
+ goto out;
+ }
+ }
+out:
+ return ret;
+}
+
+static int iommu_pm_create_group_debugfs_hierarchy(struct iommu_info *iommu,
+ struct iommu_pmon *pmon_entry)
+{
+ int i;
+ int ret = 0;
+ char name[20];
+ unsigned int abs_counter_no = 0;
+
+ for (i = 0; i < pmon_entry->num_groups; ++i) {
+ pmon_entry->cnt_grp[i].pmon = pmon_entry;
+ pmon_entry->cnt_grp[i].grp_no = i;
+ pmon_entry->cnt_grp[i].num_counters =
+ iommu_pm_get_num_counters(pmon_entry, i);
+ pmon_entry->cnt_grp[i].counters =
+ kzalloc(sizeof(*pmon_entry->cnt_grp[i].counters)
+ * pmon_entry->cnt_grp[i].num_counters, GFP_KERNEL);
+
+ if (!pmon_entry->cnt_grp[i].counters) {
+ pr_err("Unable to allocate memory for counters\n");
+ ret = -ENOMEM;
+ goto out;
+ }
+ snprintf(name, 20, "group%u", i);
+ pmon_entry->cnt_grp[i].group_dir = debugfs_create_dir(name,
+ pmon_entry->iommu_dir);
+ if (IS_ERR_OR_NULL(pmon_entry->cnt_grp[i].group_dir)) {
+ pr_err("unable to create group debugfs dir %s\n", name);
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ ret = iommu_pm_create_grp_debugfs_counters_hierarchy(
+ &pmon_entry->cnt_grp[i],
+ &abs_counter_no);
+ if (ret)
+ goto out;
+ }
+out:
+ return ret;
+}
+
+int msm_iommu_pm_iommu_register(struct iommu_info *iommu)
+{
+ struct iommu_pmon *pmon_entry;
+ int ret = 0;
+ struct msm_iommu_drvdata *iommu_drvdata;
+ int i;
+
+ if (!iommu->ops || !iommu->iommu_name || !iommu->base
+ || !iommu->iommu_dev) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (!msm_iommu_root_debugfs_dir) {
+ msm_iommu_root_debugfs_dir = debugfs_create_dir("iommu", NULL);
+ if (IS_ERR_OR_NULL(msm_iommu_root_debugfs_dir)) {
+ pr_err("Failed creating iommu debugfs dir \"iommu\"\n");
+ ret = -EIO;
+ goto out;
+ }
+ }
+ pmon_entry = (struct iommu_pmon *)container_of(iommu,
+ struct iommu_pmon, iommu);
+ iommu_drvdata = dev_get_drvdata(iommu->iommu_dev);
+
+ iommu->ops->iommu_power_on(iommu_drvdata);
+ iommu->ops->iommu_lock_acquire();
+
+ pmon_entry->num_groups = iommu_pm_get_num_groups(pmon_entry);
+ pmon_entry->cnt_grp = kzalloc(sizeof(*pmon_entry->cnt_grp)
+ * pmon_entry->num_groups, GFP_KERNEL);
+ if (!pmon_entry->cnt_grp) {
+ pr_err("Unable to allocate memory for counter groups\n");
+ ret = -ENOMEM;
+ goto file_err;
+ }
+ pmon_entry->event_cls_supp_value = iommu_pm_get_sup_ev_cls(iommu);
+
+ pmon_entry->iommu_dir = debugfs_create_dir(iommu->iommu_name,
+ msm_iommu_root_debugfs_dir);
+ if (IS_ERR_OR_NULL(pmon_entry->iommu_dir)) {
+ pr_err("unable to create iommu debugfs dir %s\n",
+ iommu->iommu_name);
+ ret = -ENOMEM;
+ goto free_mem;
+ }
+
+ if (!debugfs_create_file("reset_counters", 0644,
+ pmon_entry->iommu_dir, pmon_entry, &reset_file_ops)) {
+ ret = -EIO;
+ goto free_mem;
+ }
+
+ if (!debugfs_create_file("enable_counters", 0644,
+ pmon_entry->iommu_dir, pmon_entry, &event_enable_file_ops)) {
+ ret = -EIO;
+ goto free_mem;
+ }
+
+ if (!debugfs_create_file("available_event_classes", 0644,
+ pmon_entry->iommu_dir, pmon_entry,
+ &available_event_cls_file_ops)) {
+ ret = -EIO;
+ goto free_mem;
+ }
+
+ ret = iommu_pm_create_group_debugfs_hierarchy(iommu, pmon_entry);
+ if (ret)
+ goto free_mem;
+
+ iommu->ops->iommu_lock_release();
+ iommu->ops->iommu_power_off(iommu_drvdata);
+
+ if (iommu->evt_irq > 0) {
+ ret = request_threaded_irq(iommu->evt_irq, NULL,
+ iommu_pm_evt_ovfl_int_handler,
+ IRQF_ONESHOT | IRQF_SHARED,
+ "msm_iommu_nonsecure_irq", pmon_entry);
+ if (ret) {
+ pr_err("Request IRQ %d failed with ret=%d\n",
+ iommu->evt_irq,
+ ret);
+ goto free_mem;
+ }
+ } else {
+ pr_info("%s: Overflow interrupt not available\n", __func__);
+ }
+
+ dev_dbg(iommu->iommu_dev, "%s iommu registered\n",
+ iommu->iommu_name);
+
+ goto out;
+free_mem:
+ if (pmon_entry->cnt_grp) {
+ for (i = 0; i < pmon_entry->num_groups; ++i) {
+ kfree(pmon_entry->cnt_grp[i].counters);
+ pmon_entry->cnt_grp[i].counters = 0;
+ }
+ }
+ kfree(pmon_entry->cnt_grp);
+ pmon_entry->cnt_grp = 0;
+file_err:
+ debugfs_remove_recursive(msm_iommu_root_debugfs_dir);
+out:
+ return ret;
+}
+EXPORT_SYMBOL(msm_iommu_pm_iommu_register);
+
+void msm_iommu_pm_iommu_unregister(struct device *dev)
+{
+ int i;
+ struct iommu_pmon *pmon_entry = iommu_pm_get_pm_by_dev(dev);
+
+ if (!pmon_entry)
+ return;
+
+ free_irq(pmon_entry->iommu.evt_irq, pmon_entry->iommu.iommu_dev);
+
+ if (!pmon_entry)
+ goto remove_debugfs;
+
+ if (pmon_entry->cnt_grp) {
+ for (i = 0; i < pmon_entry->num_groups; ++i)
+ kfree(pmon_entry->cnt_grp[i].counters);
+ }
+
+ kfree(pmon_entry->cnt_grp);
+
+remove_debugfs:
+ debugfs_remove_recursive(msm_iommu_root_debugfs_dir);
+
+ return;
+}
+EXPORT_SYMBOL(msm_iommu_pm_iommu_unregister);
+
+struct iommu_info *msm_iommu_pm_alloc(struct device *dev)
+{
+ struct iommu_pmon *pmon_entry;
+ struct iommu_info *info;
+ pmon_entry = devm_kzalloc(dev, sizeof(*pmon_entry), GFP_KERNEL);
+ if (!pmon_entry)
+ return NULL;
+ info = &pmon_entry->iommu;
+ info->iommu_dev = dev;
+ mutex_init(&pmon_entry->lock);
+ iommu_pm_add_to_iommu_list(pmon_entry);
+ return &pmon_entry->iommu;
+}
+EXPORT_SYMBOL(msm_iommu_pm_alloc);
+
+void msm_iommu_pm_free(struct device *dev)
+{
+ struct iommu_pmon *pmon = iommu_pm_get_pm_by_dev(dev);
+ if (pmon)
+ iommu_pm_del_from_iommu_list(pmon);
+}
+EXPORT_SYMBOL(msm_iommu_pm_free);
+
+void msm_iommu_attached(struct device *dev)
+{
+ struct iommu_pmon *pmon = iommu_pm_get_pm_by_dev(dev);
+ if (pmon) {
+ mutex_lock(&pmon->lock);
+ ++pmon->iommu_attach_count;
+ if (pmon->iommu_attach_count == 1) {
+ /* If perf. mon was enabled before we attached we do
+ * the actual after we attach.
+ */
+ if (pmon->enabled)
+ iommu_pm_on(pmon);
+ }
+ mutex_unlock(&pmon->lock);
+ }
+}
+EXPORT_SYMBOL(msm_iommu_attached);
+
+void msm_iommu_detached(struct device *dev)
+{
+ struct iommu_pmon *pmon = iommu_pm_get_pm_by_dev(dev);
+ if (pmon) {
+ mutex_lock(&pmon->lock);
+ if (pmon->iommu_attach_count == 1) {
+ /* If perf. mon is still enabled we have to disable
+ * before we do the detach.
+ */
+ if (pmon->enabled)
+ iommu_pm_off(pmon);
+ }
+ BUG_ON(pmon->iommu_attach_count == 0);
+ --pmon->iommu_attach_count;
+ mutex_unlock(&pmon->lock);
+ }
+}
+EXPORT_SYMBOL(msm_iommu_detached);
+
diff --git a/drivers/iommu/msm_iommu_sec.c b/drivers/iommu/msm_iommu_sec.c
index a6483b9..e57fcd8 100644
--- a/drivers/iommu/msm_iommu_sec.c
+++ b/drivers/iommu/msm_iommu_sec.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012 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
@@ -25,6 +25,7 @@
#include <linux/scatterlist.h>
#include <linux/of.h>
#include <linux/of_device.h>
+#include <linux/kmemleak.h>
#include <asm/sizes.h>
@@ -120,6 +121,8 @@
goto fail_mem;
}
+ kmemleak_not_leak(buf);
+
return 0;
fail_mem:
@@ -362,6 +365,11 @@
}
ret = msm_iommu_sec_program_iommu(iommu_drvdata->sec_id);
+
+ /* bfb settings are always programmed by HLOS */
+ program_iommu_bfb_settings(iommu_drvdata->base,
+ iommu_drvdata->bfb_settings);
+
__disable_clocks(iommu_drvdata);
if (ret) {
regulator_disable(iommu_drvdata->gdsc);
diff --git a/drivers/leds/leds-qpnp.c b/drivers/leds/leds-qpnp.c
index e7d514e..dc0f7a1 100644
--- a/drivers/leds/leds-qpnp.c
+++ b/drivers/leds/leds-qpnp.c
@@ -1,5 +1,5 @@
-/* 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
@@ -703,7 +703,8 @@
if (led->wled_cfg->cs_out_en) {
rc = qpnp_led_masked_write(led, WLED_CURR_SINK_REG(led->base),
WLED_CURR_SINK_MASK,
- (led->wled_cfg->num_strings << WLED_CURR_SINK_SHFT));
+ (((1 << led->wled_cfg->num_strings) - 1)
+ << WLED_CURR_SINK_SHFT));
if (rc) {
dev_err(&led->spmi_dev->dev,
"WLED curr sink reg write failed(%d)\n", rc);
diff --git a/drivers/media/dvb/dvb-core/demux.h b/drivers/media/dvb/dvb-core/demux.h
index 042ba37..844c65f 100644
--- a/drivers/media/dvb/dvb-core/demux.h
+++ b/drivers/media/dvb/dvb-core/demux.h
@@ -7,7 +7,7 @@
* Copyright (c) 2000 Nokia Research Center
* Tampere, FINLAND
*
- * Copyright (c) 2012, Code Aurora Forum. 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 Lesser General Public License
@@ -100,6 +100,9 @@
int disc_indicator_set;
int pes_length_mismatch;
u64 stc;
+ u32 tei_counter;
+ u32 cont_err_counter;
+ u32 ts_packets_num;
} pes_end;
struct {
diff --git a/drivers/media/dvb/dvb-core/dmxdev.c b/drivers/media/dvb/dvb-core/dmxdev.c
index 82b6aa0..bddbb5f 100644
--- a/drivers/media/dvb/dvb-core/dmxdev.c
+++ b/drivers/media/dvb/dvb-core/dmxdev.c
@@ -1680,7 +1680,8 @@
* Decoder filters have no data in the data buffer and their
* events can be removed now from the queue.
*/
- if (dmxdevfilter->params.pes.output == DMX_OUT_DECODER)
+ if ((dmxdevfilter->type == DMXDEV_TYPE_PES) &&
+ (dmxdevfilter->params.pes.output == DMX_OUT_DECODER))
dmxdevfilter->events.read_index =
dvb_dmxdev_advance_event_idx(
dmxdevfilter->events.read_index);
@@ -1860,6 +1861,9 @@
event.params.pes.flags = 0;
event.params.pes.stc = 0;
+ event.params.pes.transport_error_indicator_counter = 0;
+ event.params.pes.continuity_error_counter = 0;
+ event.params.pes.ts_packets_num = 0;
dvb_dmxdev_add_event(events, &event);
events->current_event_data_size = 0;
@@ -2108,6 +2112,13 @@
DMX_FILTER_PES_LENGTH_ERROR;
event.params.pes.stc = dmx_data_ready->pes_end.stc;
+ event.params.pes.transport_error_indicator_counter =
+ dmx_data_ready->pes_end.tei_counter;
+ event.params.pes.continuity_error_counter =
+ dmx_data_ready->pes_end.cont_err_counter;
+ event.params.pes.ts_packets_num =
+ dmx_data_ready->pes_end.ts_packets_num;
+
dvb_dmxdev_add_event(events, &event);
events->current_event_data_size = 0;
diff --git a/drivers/media/dvb/dvb-core/dvb_demux.c b/drivers/media/dvb/dvb-core/dvb_demux.c
index eea83c2..488f42c 100644
--- a/drivers/media/dvb/dvb-core/dvb_demux.c
+++ b/drivers/media/dvb/dvb-core/dvb_demux.c
@@ -5,7 +5,7 @@
* & Marcus Metzler <marcus@convergence.de>
* for convergence integrated media GmbH
*
- * Copyright (c) 2012, Code Aurora Forum. 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 Lesser General Public License
@@ -127,15 +127,15 @@
{
int count = payload(buf);
int p;
- //int ccok;
- //u8 cc;
+ int ccok;
+ u8 cc;
+ struct dmx_data_ready data;
if (count == 0)
return -1;
p = 188 - count;
- /*
cc = buf[3] & 0x0f;
if (feed->first_cc)
ccok = 1;
@@ -144,26 +144,41 @@
feed->first_cc = 0;
feed->cc = cc;
- if (!ccok)
- printk("missed packet!\n");
- */
/* PUSI ? */
if (buf[1] & 0x40) {
- if (feed->pusi_seen)
+ if (feed->pusi_seen) {
/* We had seen PUSI before, this means
* that previous PES can be closed now.
*/
- feed->cb.ts(NULL, 0, NULL, 0,
- &feed->feed.ts, DMX_OK_PES_END);
+ data.status = DMX_OK_PES_END;
+ data.data_length = 0;
+ data.pes_end.start_gap = 0;
+ data.pes_end.actual_length = feed->peslen;
+ data.pes_end.disc_indicator_set = 0;
+ data.pes_end.pes_length_mismatch = 0;
+ data.pes_end.stc = 0;
+ data.pes_end.tei_counter = feed->pes_tei_counter;
+ data.pes_end.cont_err_counter =
+ feed->pes_cont_err_counter;
+ data.pes_end.ts_packets_num = feed->pes_ts_packets_num;
+ feed->data_ready_cb.ts(&feed->feed.ts, &data);
+ }
feed->pusi_seen = 1;
feed->peslen = 0;
+ feed->pes_tei_counter = 0;
+ feed->pes_ts_packets_num = 0;
+ feed->pes_cont_err_counter = 0;
}
if (feed->pusi_seen == 0)
return 0;
+ feed->pes_ts_packets_num++;
+ feed->pes_cont_err_counter += !ccok;
+ feed->pes_tei_counter += (buf[1] & 0x80) ? 1 : 0;
+
feed->peslen += count;
return feed->cb.ts(&buf[p], count, NULL, 0, &feed->feed.ts, DMX_OK);
@@ -1243,6 +1258,9 @@
feed->demux = demux;
feed->pid = 0xffff;
feed->peslen = 0;
+ feed->pes_tei_counter = 0;
+ feed->pes_ts_packets_num = 0;
+ feed->pes_cont_err_counter = 0;
feed->buffer = NULL;
feed->tsp_out_format = DMX_TSP_FORMAT_188;
memset(&feed->indexing_params, 0,
diff --git a/drivers/media/dvb/dvb-core/dvb_demux.h b/drivers/media/dvb/dvb-core/dvb_demux.h
index 2e4a468..5e98ea1 100644
--- a/drivers/media/dvb/dvb-core/dvb_demux.h
+++ b/drivers/media/dvb/dvb-core/dvb_demux.h
@@ -4,7 +4,7 @@
* Copyright (C) 2000-2001 Marcus Metzler & Ralph Metzler
* for convergence integrated media GmbH
*
- * Copyright (c) 2012, Code Aurora Forum. 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 Lesser General Public License
@@ -105,6 +105,9 @@
int pusi_seen; /* prevents feeding of garbage from previous section */
u32 peslen;
+ u32 pes_tei_counter;
+ u32 pes_cont_err_counter;
+ u32 pes_ts_packets_num;
struct list_head list_head;
unsigned int index; /* a unique index for each feed (can be used as hardware pid filter index) */
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index 0d2d91c..830ba81 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -1195,8 +1195,28 @@
help
Enable printk() debug for msm camera
-
+if MSM_CAMERA
source "drivers/media/video/msm/Kconfig"
+endif # MSM_CAMERA
+
+menuconfig MSMB_CAMERA
+ bool "Qualcomm MSM camera and video capture 2.0 support"
+ depends on ARCH_MSM && VIDEO_V4L2 && I2C
+ ---help---
+ Say Y here to enable selecting the video adapters for
+ Qualcomm msm camera and video capture 2.0, enabling this
+ adds support for the camera driver stack including sensor, isp
+ and postprocessing drivers.
+
+config MSMB_CAMERA_DEBUG
+ bool "Qualcomm MSM camera 2.0 debugging with printk"
+ depends on MSMB_CAMERA
+ ---help---
+ Enable printk() debug for msm camera 2.0
+
+if MSMB_CAMERA
+source "drivers/media/video/msmb/Kconfig"
+endif # MSMB_CAMERA
endif # V4L_PLATFORM_DRIVERS
endif # VIDEO_CAPTURE_DRIVERS
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index fd736c3..e732773 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -213,6 +213,7 @@
obj-y += davinci/
obj-$(CONFIG_MSM_CAMERA) += msm/
+obj-$(CONFIG_MSMB_CAMERA) += msmb/
obj-$(CONFIG_ARCH_OMAP) += omap/
obj-$(CONFIG_MSM_VIDC_V4L2) += msm_vidc/
obj-$(CONFIG_MSM_WFD) += msm_wfd/
diff --git a/drivers/media/video/msm/Makefile b/drivers/media/video/msm/Makefile
index 56d3e0c..67ac906 100644
--- a/drivers/media/video/msm/Makefile
+++ b/drivers/media/video/msm/Makefile
@@ -22,7 +22,8 @@
obj-$(CONFIG_MSM_CAMERA) += msm_camera.o
endif
obj-$(CONFIG_MSM_CAMERA) += vfe/
-obj-$(CONFIG_MSM_CAMERA) += msm_axi_qos.o gemini/ mercury/ jpeg_10/
+obj-$(CONFIG_MSM_CAMERA) += msm_axi_qos.o gemini/ mercury/
+obj-$(CONFIG_MSM_JPEG) += jpeg_10/
ifeq ($(CONFIG_MSM_CAMERA_V4L2),y)
obj-$(CONFIG_ARCH_MSM8X60) += msm_vpe.o
obj-$(CONFIG_ARCH_MSM7X30) += msm_vpe.o msm_axi_qos.o
diff --git a/drivers/media/video/msm/msm_mctl_buf.c b/drivers/media/video/msm/msm_mctl_buf.c
index 2919d23..f4bb5b3 100644
--- a/drivers/media/video/msm/msm_mctl_buf.c
+++ b/drivers/media/video/msm/msm_mctl_buf.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2013, Code Aurora Forum. 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
@@ -122,6 +122,11 @@
}
for (i = 0; i < vb->num_planes; i++) {
mem = vb2_plane_cookie(vb, i);
+ if (mem == NULL) {
+ pr_err("%s Inst %p Buffer %d Plane %d cookie is null",
+ __func__, pcam_inst, buf_idx, i);
+ return -EINVAL;
+ }
if (buf_type == VIDEOBUF2_MULTIPLE_PLANES)
offset.data_offset =
pcam_inst->plane_info.plane[i].offset;
@@ -266,8 +271,14 @@
}
for (i = 0; i < vb->num_planes; i++) {
mem = vb2_plane_cookie(vb, i);
- videobuf2_pmem_contig_user_put(mem, pmctl->client,
- pmctl->domain_num);
+ if (mem) {
+ videobuf2_pmem_contig_user_put(mem, pmctl->client,
+ pmctl->domain_num);
+ } else {
+ pr_err("%s Inst %p buffer plane cookie is null",
+ __func__, pcam_inst);
+ return;
+ }
}
buf->state = MSM_BUFFER_STATE_UNUSED;
}
@@ -385,7 +396,13 @@
&pcam_inst->free_vq, list) {
buf_idx = buf->vidbuf.v4l2_buf.index;
mem = vb2_plane_cookie(&buf->vidbuf, 0);
- if (mem->buffer_type == VIDEOBUF2_MULTIPLE_PLANES)
+ if (mem == NULL) {
+ pr_err("%s Inst %p plane cookie is null",
+ __func__, pcam_inst);
+ spin_unlock_irqrestore(&pcam_inst->vq_irqlock, flags);
+ return NULL;
+ }
+ if (mem->buffer_type == VIDEOBUF2_MULTIPLE_PLANES)
offset = mem->offset.data_offset +
pcam_inst->buf_offset[buf_idx][0].data_offset;
else
@@ -690,7 +707,19 @@
return rc;
}
spin_lock_irqsave(&pcam_inst->vq_irqlock, flags);
+ if (pcam_inst->free_vq.next == NULL) {
+ pr_err("%s Inst %p Free queue head is null",
+ __func__, pcam_inst);
+ spin_unlock_irqrestore(&pcam_inst->vq_irqlock, flags);
+ return rc;
+ }
list_for_each_entry(buf, &pcam_inst->free_vq, list) {
+ if (buf == NULL) {
+ pr_err("%s Inst %p Invalid buffer ptr",
+ __func__, pcam_inst);
+ spin_unlock_irqrestore(&pcam_inst->vq_irqlock, flags);
+ return rc;
+ }
if (buf->state != MSM_BUFFER_STATE_QUEUED)
continue;
@@ -701,6 +730,13 @@
pcam_inst->plane_info.num_planes;
for (i = 0; i < free_buf->num_planes; i++) {
mem = vb2_plane_cookie(&buf->vidbuf, i);
+ if (mem == NULL) {
+ pr_err("%s Inst %p %d invalid cookie",
+ __func__, pcam_inst, buf_idx);
+ spin_unlock_irqrestore(
+ &pcam_inst->vq_irqlock, flags);
+ return rc;
+ }
if (mem->buffer_type ==
VIDEOBUF2_MULTIPLE_PLANES)
plane_offset =
@@ -721,6 +757,13 @@
}
} else {
mem = vb2_plane_cookie(&buf->vidbuf, 0);
+ if (mem == NULL) {
+ pr_err("%s Inst %p %d invalid cookie",
+ __func__, pcam_inst, buf_idx);
+ spin_unlock_irqrestore(
+ &pcam_inst->vq_irqlock, flags);
+ return rc;
+ }
free_buf->ch_paddr[0] = (uint32_t)
videobuf2_to_pmem_contig(&buf->vidbuf, 0) +
mem->offset.sp_off.y_off;
diff --git a/drivers/media/video/msm/msm_mctl_pp.c b/drivers/media/video/msm/msm_mctl_pp.c
index 7155d4c..7f0fd94 100644
--- a/drivers/media/video/msm/msm_mctl_pp.c
+++ b/drivers/media/video/msm/msm_mctl_pp.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2013, Code Aurora Forum. 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
@@ -138,6 +138,12 @@
&pcam_inst->free_vq, list) {
buf_idx = buf->vidbuf.v4l2_buf.index;
mem = vb2_plane_cookie(&buf->vidbuf, 0);
+ if (mem == NULL) {
+ pr_err("%s Inst %p Buffer %d invalid plane cookie",
+ __func__, pcam_inst, buf_idx);
+ spin_unlock_irqrestore(&pcam_inst->vq_irqlock, flags);
+ return 0;
+ }
if (mem->buffer_type == VIDEOBUF2_MULTIPLE_PLANES)
offset = mem->offset.data_offset +
pcam_inst->buf_offset[buf_idx][0].data_offset;
@@ -272,6 +278,11 @@
* Also use this to check the number of planes in
* this buffer.*/
mem = vb2_plane_cookie(&vb->vidbuf, 0);
+ if (mem == NULL) {
+ pr_err("%s Inst %p Buffer %d, invalid plane cookie ", __func__,
+ pcam_inst, buf_idx);
+ return -EINVAL;
+ }
div.frame.path = mem->path;
div.frame.node_type = node;
if (mem->buffer_type == VIDEOBUF2_SINGLE_PLANE) {
@@ -296,6 +307,11 @@
* fill out the plane info. */
for (i = 0; i < div.frame.num_planes; i++) {
mem = vb2_plane_cookie(&vb->vidbuf, i);
+ if (mem == NULL) {
+ pr_err("%s Inst %p %d invalid plane cookie ",
+ __func__, pcam_inst, buf_idx);
+ return -EINVAL;
+ }
div.frame.mp[i].phy_addr =
videobuf2_to_pmem_contig(&vb->vidbuf, i);
if (!pcam_inst->buf_offset)
@@ -338,6 +354,11 @@
* Also use this to check the number of planes in
* this buffer.*/
mem = vb2_plane_cookie(&vb->vidbuf, 0);
+ if (mem == NULL) {
+ pr_err("%s Inst %p Buffer %d, invalid plane cookie ", __func__,
+ pcam_inst, buf_idx);
+ return -EINVAL;
+ }
pp_frame->image_type = (unsigned short)mem->path;
if (mem->buffer_type == VIDEOBUF2_SINGLE_PLANE) {
pp_frame->num_planes = 1;
diff --git a/drivers/media/video/msm_vidc/Makefile b/drivers/media/video/msm_vidc/Makefile
index c8e7076..da829cc 100644
--- a/drivers/media/video/msm_vidc/Makefile
+++ b/drivers/media/video/msm_vidc/Makefile
@@ -8,3 +8,4 @@
venus_hfi.o \
hfi_response_handler.o \
hfi_packetization.o \
+ vidc_hfi.o \
diff --git a/drivers/media/video/msm_vidc/hfi_packetization.c b/drivers/media/video/msm_vidc/hfi_packetization.c
index 509b013..06e230d 100644
--- a/drivers/media/video/msm_vidc/hfi_packetization.c
+++ b/drivers/media/video/msm_vidc/hfi_packetization.c
@@ -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
@@ -794,6 +794,7 @@
HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE;
hfi = (struct hfi_bitrate *) &pkt->rg_property_data[1];
hfi->bit_rate = ((struct hal_bitrate *)pdata)->bit_rate;
+ hfi->layer_id = ((struct hal_bitrate *)pdata)->layer_id;
pkt->size += sizeof(u32) * 2;
break;
}
@@ -943,11 +944,15 @@
case HAL_PARAM_VENC_SESSION_QP:
{
struct hfi_quantization *hfi;
+ struct hal_quantization *hal_quant =
+ (struct hal_quantization *) pdata;
pkt->rg_property_data[0] =
HFI_PROPERTY_PARAM_VENC_SESSION_QP;
hfi = (struct hfi_quantization *) &pkt->rg_property_data[1];
- memcpy(hfi, (struct hfi_quantization *) pdata,
- sizeof(struct hfi_quantization));
+ hfi->qp_i = hal_quant->qpi;
+ hfi->qp_p = hal_quant->qpp;
+ hfi->qp_b = hal_quant->qpb;
+ hfi->layer_id = hal_quant->layer_id;
pkt->size += sizeof(u32) + sizeof(struct hfi_quantization);
break;
}
@@ -1099,3 +1104,36 @@
}
return rc;
}
+
+static int get_hfi_ssr_type(enum hal_ssr_trigger_type type)
+{
+ int rc = HFI_TEST_SSR_HW_WDOG_IRQ;
+ switch (type) {
+ case SSR_ERR_FATAL:
+ rc = HFI_TEST_SSR_SW_ERR_FATAL;
+ break;
+ case SSR_SW_DIV_BY_ZERO:
+ rc = HFI_TEST_SSR_SW_DIV_BY_ZERO;
+ break;
+ case SSR_HW_WDOG_IRQ:
+ rc = HFI_TEST_SSR_HW_WDOG_IRQ;
+ break;
+ default:
+ dprintk(VIDC_WARN,
+ "SSR trigger type not recognized, using WDOG.\n");
+ }
+ return rc;
+}
+
+int create_pkt_ssr_cmd(enum hal_ssr_trigger_type type,
+ struct hfi_cmd_sys_test_ssr_packet *pkt)
+{
+ if (!pkt) {
+ dprintk(VIDC_ERR, "Invalid params, device: %p\n", pkt);
+ return -EINVAL;
+ }
+ pkt->size = sizeof(struct hfi_cmd_sys_test_ssr_packet);
+ pkt->packet_type = HFI_CMD_SYS_TEST_SSR;
+ pkt->trigger_type = get_hfi_ssr_type(type);
+ return 0;
+}
diff --git a/drivers/media/video/msm_vidc/hfi_packetization.h b/drivers/media/video/msm_vidc/hfi_packetization.h
index 541654f..b2c6e08 100644
--- a/drivers/media/video/msm_vidc/hfi_packetization.h
+++ b/drivers/media/video/msm_vidc/hfi_packetization.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
@@ -15,8 +15,8 @@
#include <linux/types.h>
#include "vidc_hfi_helper.h"
+#include "vidc_hfi.h"
#include "vidc_hfi_api.h"
-#include "venus_hfi.h"
int create_pkt_cmd_sys_init(struct hfi_cmd_sys_init_packet *pkt,
u32 arch_type);
@@ -79,5 +79,6 @@
struct hfi_cmd_session_set_property_packet *pkt,
u32 session_id, enum hal_property ptype, void *pdata);
+int create_pkt_ssr_cmd(enum hal_ssr_trigger_type type,
+ struct hfi_cmd_sys_test_ssr_packet *pkt);
#endif
-
diff --git a/drivers/media/video/msm_vidc/hfi_response_handler.c b/drivers/media/video/msm_vidc/hfi_response_handler.c
index 50970cb..cedd789 100644
--- a/drivers/media/video/msm_vidc/hfi_response_handler.c
+++ b/drivers/media/video/msm_vidc/hfi_response_handler.c
@@ -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,9 +14,10 @@
#include <linux/slab.h>
#include <linux/list.h>
#include <linux/interrupt.h>
-#include "venus_hfi.h"
+#include "vidc_hfi_helper.h"
#include "vidc_hfi_io.h"
#include "msm_vidc_debug.h"
+#include "vidc_hfi.h"
static enum vidc_status hfi_map_err_status(int hfi_err)
{
@@ -75,8 +76,9 @@
return vidc_err;
}
-static void hfi_process_sess_evt_seq_changed(struct venus_hfi_device *device,
- struct hfi_msg_event_notify_packet *pkt)
+static void hfi_process_sess_evt_seq_changed(
+ msm_vidc_callback callback, u32 device_id,
+ struct hfi_msg_event_notify_packet *pkt)
{
struct msm_vidc_cb_cmd_done cmd_done;
struct msm_vidc_cb_event event_notify;
@@ -95,7 +97,7 @@
memset(&event_notify, 0, sizeof(struct
msm_vidc_cb_event));
- cmd_done.device_id = device->device_id;
+ cmd_done.device_id = device_id;
cmd_done.session_id = ((struct hal_session *) pkt->session_id)->
session_id;
cmd_done.status = VIDC_ERR_NONE;
@@ -136,39 +138,35 @@
} while (num_properties_changed > 0);
}
cmd_done.data = &event_notify;
- device->callback(VIDC_EVENT_CHANGE, &cmd_done);
+ callback(VIDC_EVENT_CHANGE, &cmd_done);
}
-static void hfi_process_sys_watchdog_timeout(struct venus_hfi_device *device)
-{
- struct msm_vidc_cb_cmd_done cmd_done;
- device->intr_status &= ~VIDC_WRAPPER_INTR_STATUS_A2HWD_BMSK;
- memset(&cmd_done, 0, sizeof(struct msm_vidc_cb_cmd_done));
- cmd_done.device_id = device->device_id;
- device->callback(SYS_WATCHDOG_TIMEOUT, &cmd_done);
-}
-static void hfi_process_sys_error(struct venus_hfi_device *device)
+
+static void hfi_process_sys_error(
+ msm_vidc_callback callback, u32 device_id)
{
struct msm_vidc_cb_cmd_done cmd_done;
memset(&cmd_done, 0, sizeof(struct msm_vidc_cb_cmd_done));
- cmd_done.device_id = device->device_id;
- device->callback(SYS_ERROR, &cmd_done);
+ cmd_done.device_id = device_id;
+ callback(SYS_ERROR, &cmd_done);
}
-static void hfi_process_session_error(struct venus_hfi_device *device,
+static void hfi_process_session_error(
+ msm_vidc_callback callback, u32 device_id,
struct hfi_msg_event_notify_packet *pkt)
{
struct msm_vidc_cb_cmd_done cmd_done;
memset(&cmd_done, 0, sizeof(struct msm_vidc_cb_cmd_done));
- cmd_done.device_id = device->device_id;
+ cmd_done.device_id = device_id;
cmd_done.session_id = ((struct hal_session *) pkt->session_id)->
session_id;
- device->callback(SESSION_ERROR, &cmd_done);
+ callback(SESSION_ERROR, &cmd_done);
}
-static void hfi_process_event_notify(struct venus_hfi_device *device,
- struct hfi_msg_event_notify_packet *pkt)
+static void hfi_process_event_notify(
+ msm_vidc_callback callback, u32 device_id,
+ struct hfi_msg_event_notify_packet *pkt)
{
dprintk(VIDC_DBG, "RECVD:EVENT_NOTIFY");
- if (!device || !pkt ||
+ if (!callback || !pkt ||
pkt->size < sizeof(struct hfi_msg_event_notify_packet)) {
dprintk(VIDC_ERR, "Invalid Params");
return;
@@ -178,15 +176,15 @@
case HFI_EVENT_SYS_ERROR:
dprintk(VIDC_ERR, "HFI_EVENT_SYS_ERROR: %d\n",
pkt->event_data1);
- hfi_process_sys_error(device);
+ hfi_process_sys_error(callback, device_id);
break;
case HFI_EVENT_SESSION_ERROR:
dprintk(VIDC_ERR, "HFI_EVENT_SESSION_ERROR");
- hfi_process_session_error(device, pkt);
+ hfi_process_session_error(callback, device_id, pkt);
break;
case HFI_EVENT_SESSION_SEQUENCE_CHANGED:
dprintk(VIDC_INFO, "HFI_EVENT_SESSION_SEQUENCE_CHANGED");
- hfi_process_sess_evt_seq_changed(device, pkt);
+ hfi_process_sess_evt_seq_changed(callback, device_id, pkt);
break;
case HFI_EVENT_SESSION_PROPERTY_CHANGED:
dprintk(VIDC_INFO, "HFI_EVENT_SESSION_PROPERTY_CHANGED");
@@ -196,7 +194,8 @@
break;
}
}
-static void hfi_process_sys_init_done(struct venus_hfi_device *device,
+static void hfi_process_sys_init_done(
+ msm_vidc_callback callback, u32 device_id,
struct hfi_msg_sys_init_done_packet *pkt)
{
struct msm_vidc_cb_cmd_done cmd_done;
@@ -273,16 +272,17 @@
}
}
err_no_prop:
- cmd_done.device_id = device->device_id;
+ cmd_done.device_id = device_id;
cmd_done.session_id = 0;
cmd_done.status = (u32) status;
cmd_done.size = sizeof(struct vidc_hal_sys_init_done);
cmd_done.data = (void *) &sys_init_done;
- device->callback(SYS_INIT_DONE, &cmd_done);
+ callback(SYS_INIT_DONE, &cmd_done);
}
-static void hfi_process_sys_rel_resource_done(struct venus_hfi_device *device,
- struct hfi_msg_sys_release_resource_done_packet *pkt)
+static void hfi_process_sys_rel_resource_done(
+ msm_vidc_callback callback, u32 device_id,
+ struct hfi_msg_sys_release_resource_done_packet *pkt)
{
struct msm_vidc_cb_cmd_done cmd_done;
enum vidc_status status = VIDC_ERR_NONE;
@@ -297,12 +297,12 @@
return;
}
status = hfi_map_err_status((u32)pkt->error_type);
- cmd_done.device_id = device->device_id;
+ cmd_done.device_id = device_id;
cmd_done.session_id = 0;
cmd_done.status = (u32) status;
cmd_done.size = 0;
cmd_done.data = NULL;
- device->callback(RELEASE_RESOURCE_DONE, &cmd_done);
+ callback(RELEASE_RESOURCE_DONE, &cmd_done);
}
enum vidc_status hfi_process_sess_init_done_prop_read(
@@ -408,8 +408,9 @@
}
}
-static void hfi_process_session_prop_info(struct venus_hfi_device *device,
- struct hfi_msg_session_property_info_packet *pkt)
+static void hfi_process_session_prop_info(
+ msm_vidc_callback callback, u32 device_id,
+ struct hfi_msg_session_property_info_packet *pkt)
{
struct msm_vidc_cb_cmd_done cmd_done;
struct buffer_requirements buff_req;
@@ -433,13 +434,13 @@
switch (pkt->rg_property_data[0]) {
case HFI_PROPERTY_CONFIG_BUFFER_REQUIREMENTS:
hfi_process_sess_get_prop_buf_req(pkt, &buff_req);
- cmd_done.device_id = device->device_id;
+ cmd_done.device_id = device_id;
cmd_done.session_id =
((struct hal_session *) pkt->session_id)->session_id;
cmd_done.status = VIDC_ERR_NONE;
cmd_done.data = &buff_req;
cmd_done.size = sizeof(struct buffer_requirements);
- device->callback(SESSION_PROPERTY_INFO, &cmd_done);
+ callback(SESSION_PROPERTY_INFO, &cmd_done);
break;
default:
dprintk(VIDC_ERR, "hal_process_session_prop_info:"
@@ -449,8 +450,9 @@
}
}
-static void hfi_process_session_init_done(struct venus_hfi_device *device,
- struct hfi_msg_sys_session_init_done_packet *pkt)
+static void hfi_process_session_init_done(
+ msm_vidc_callback callback, u32 device_id,
+ struct hfi_msg_sys_session_init_done_packet *pkt)
{
struct msm_vidc_cb_cmd_done cmd_done;
struct vidc_hal_session_init_done session_init_done;
@@ -466,7 +468,7 @@
memset(&session_init_done, 0, sizeof(struct
vidc_hal_session_init_done));
- cmd_done.device_id = device->device_id;
+ cmd_done.device_id = device_id;
cmd_done.session_id =
((struct hal_session *) pkt->session_id)->session_id;
cmd_done.status = hfi_map_err_status((u32)pkt->error_type);
@@ -476,11 +478,12 @@
pkt, &cmd_done);
}
cmd_done.size = sizeof(struct vidc_hal_session_init_done);
- device->callback(SESSION_INIT_DONE, &cmd_done);
+ callback(SESSION_INIT_DONE, &cmd_done);
}
-static void hfi_process_session_load_res_done(struct venus_hfi_device *device,
- struct hfi_msg_session_load_resources_done_packet *pkt)
+static void hfi_process_session_load_res_done(
+ msm_vidc_callback callback, u32 device_id,
+ struct hfi_msg_session_load_resources_done_packet *pkt)
{
struct msm_vidc_cb_cmd_done cmd_done;
dprintk(VIDC_DBG, "RECEIVED:SESSION_LOAD_RESOURCES_DONE");
@@ -494,17 +497,18 @@
memset(&cmd_done, 0, sizeof(struct msm_vidc_cb_cmd_done));
- cmd_done.device_id = device->device_id;
+ cmd_done.device_id = device_id;
cmd_done.session_id =
((struct hal_session *) pkt->session_id)->session_id;
cmd_done.status = hfi_map_err_status((u32)pkt->error_type);
cmd_done.data = NULL;
cmd_done.size = 0;
- device->callback(SESSION_LOAD_RESOURCE_DONE, &cmd_done);
+ callback(SESSION_LOAD_RESOURCE_DONE, &cmd_done);
}
-static void hfi_process_session_flush_done(struct venus_hfi_device *device,
- struct hfi_msg_session_flush_done_packet *pkt)
+static void hfi_process_session_flush_done(
+ msm_vidc_callback callback, u32 device_id,
+ struct hfi_msg_session_flush_done_packet *pkt)
{
struct msm_vidc_cb_cmd_done cmd_done;
@@ -517,17 +521,18 @@
}
memset(&cmd_done, 0, sizeof(struct msm_vidc_cb_cmd_done));
- cmd_done.device_id = device->device_id;
+ cmd_done.device_id = device_id;
cmd_done.session_id =
((struct hal_session *) pkt->session_id)->session_id;
cmd_done.status = hfi_map_err_status((u32)pkt->error_type);
cmd_done.data = (void *) pkt->flush_type;
cmd_done.size = sizeof(u32);
- device->callback(SESSION_FLUSH_DONE, &cmd_done);
+ callback(SESSION_FLUSH_DONE, &cmd_done);
}
-static void hfi_process_session_etb_done(struct venus_hfi_device *device,
- struct hfi_msg_session_empty_buffer_done_packet *pkt)
+static void hfi_process_session_etb_done(
+ msm_vidc_callback callback, u32 device_id,
+ struct hfi_msg_session_empty_buffer_done_packet *pkt)
{
struct msm_vidc_cb_data_done data_done;
@@ -541,7 +546,7 @@
memset(&data_done, 0, sizeof(struct msm_vidc_cb_data_done));
- data_done.device_id = device->device_id;
+ data_done.device_id = device_id;
data_done.session_id =
((struct hal_session *) pkt->session_id)->session_id;
data_done.status = hfi_map_err_status((u32) pkt->error_type);
@@ -550,11 +555,12 @@
data_done.input_done.offset = pkt->offset;
data_done.input_done.filled_len = pkt->filled_len;
data_done.input_done.packet_buffer = pkt->packet_buffer;
- device->callback(SESSION_ETB_DONE, &data_done);
+ callback(SESSION_ETB_DONE, &data_done);
}
-static void hfi_process_session_ftb_done(struct venus_hfi_device *device,
- void *msg_hdr)
+static void hfi_process_session_ftb_done(
+ msm_vidc_callback callback, u32 device_id,
+ void *msg_hdr)
{
struct msm_vidc_cb_data_done data_done;
struct hfi_msg_session_fill_buffer_done_compressed_packet *pack =
@@ -590,7 +596,7 @@
/* Proceed with the FBD */
}
- data_done.device_id = device->device_id;
+ data_done.device_id = device_id;
data_done.session_id = (u32) session;
data_done.status = hfi_map_err_status((u32)
pkt->error_type);
@@ -624,7 +630,7 @@
return;
}
- data_done.device_id = device->device_id;
+ data_done.device_id = device_id;
data_done.session_id = (u32) session;
data_done.status = hfi_map_err_status((u32)
pkt->error_type);
@@ -644,8 +650,8 @@
data_done.output_done.offset1 = pkt->offset;
data_done.output_done.frame_width = pkt->frame_width;
data_done.output_done.frame_height = pkt->frame_height;
- data_done.output_done.start_xCoord = pkt->start_x_coord;
- data_done.output_done.start_yCoord = pkt->start_y_coord;
+ data_done.output_done.start_x_coord = pkt->start_x_coord;
+ data_done.output_done.start_y_coord = pkt->start_y_coord;
data_done.output_done.input_tag1 = pkt->input_tag;
data_done.output_done.picture_type = pkt->picture_type;
data_done.output_done.packet_buffer1 = pkt->packet_buffer;
@@ -657,11 +663,12 @@
else if (pkt->stream_id == 1)
data_done.output_done.buffer_type = HAL_BUFFER_OUTPUT2;
}
- device->callback(SESSION_FTB_DONE, &data_done);
+ callback(SESSION_FTB_DONE, &data_done);
}
-static void hfi_process_session_start_done(struct venus_hfi_device *device,
- struct hfi_msg_session_start_done_packet *pkt)
+static void hfi_process_session_start_done(
+ msm_vidc_callback callback, u32 device_id,
+ struct hfi_msg_session_start_done_packet *pkt)
{
struct msm_vidc_cb_cmd_done cmd_done;
@@ -675,17 +682,18 @@
}
memset(&cmd_done, 0, sizeof(struct msm_vidc_cb_cmd_done));
- cmd_done.device_id = device->device_id;
+ cmd_done.device_id = device_id;
cmd_done.session_id =
((struct hal_session *) pkt->session_id)->session_id;
cmd_done.status = hfi_map_err_status((u32)pkt->error_type);
cmd_done.data = NULL;
cmd_done.size = 0;
- device->callback(SESSION_START_DONE, &cmd_done);
+ callback(SESSION_START_DONE, &cmd_done);
}
-static void hfi_process_session_stop_done(struct venus_hfi_device *device,
- struct hfi_msg_session_stop_done_packet *pkt)
+static void hfi_process_session_stop_done(
+ msm_vidc_callback callback, u32 device_id,
+ struct hfi_msg_session_stop_done_packet *pkt)
{
struct msm_vidc_cb_cmd_done cmd_done;
@@ -699,17 +707,18 @@
}
memset(&cmd_done, 0, sizeof(struct msm_vidc_cb_cmd_done));
- cmd_done.device_id = device->device_id;
+ cmd_done.device_id = device_id;
cmd_done.session_id =
((struct hal_session *) pkt->session_id)->session_id;
cmd_done.status = hfi_map_err_status((u32)pkt->error_type);
cmd_done.data = NULL;
cmd_done.size = 0;
- device->callback(SESSION_STOP_DONE, &cmd_done);
+ callback(SESSION_STOP_DONE, &cmd_done);
}
-static void hfi_process_session_rel_res_done(struct venus_hfi_device *device,
- struct hfi_msg_session_release_resources_done_packet *pkt)
+static void hfi_process_session_rel_res_done(
+ msm_vidc_callback callback, u32 device_id,
+ struct hfi_msg_session_release_resources_done_packet *pkt)
{
struct msm_vidc_cb_cmd_done cmd_done;
@@ -723,17 +732,18 @@
}
memset(&cmd_done, 0, sizeof(struct msm_vidc_cb_cmd_done));
- cmd_done.device_id = device->device_id;
+ cmd_done.device_id = device_id;
cmd_done.session_id =
((struct hal_session *) pkt->session_id)->session_id;
cmd_done.status = hfi_map_err_status((u32)pkt->error_type);
cmd_done.data = NULL;
cmd_done.size = 0;
- device->callback(SESSION_RELEASE_RESOURCE_DONE, &cmd_done);
+ callback(SESSION_RELEASE_RESOURCE_DONE, &cmd_done);
}
-static void hfi_process_session_rel_buf_done(struct venus_hfi_device *device,
- struct hfi_msg_session_release_buffers_done_packet *pkt)
+static void hfi_process_session_rel_buf_done(
+ msm_vidc_callback callback, u32 device_id,
+ struct hfi_msg_session_release_buffers_done_packet *pkt)
{
struct msm_vidc_cb_cmd_done cmd_done;
if (!pkt || pkt->size !=
@@ -743,7 +753,7 @@
return;
}
memset(&cmd_done, 0, sizeof(struct msm_vidc_cb_cmd_done));
- cmd_done.device_id = device->device_id;
+ cmd_done.device_id = device_id;
cmd_done.size = sizeof(struct msm_vidc_cb_cmd_done);
cmd_done.session_id =
((struct hal_session *) pkt->session_id)->session_id;
@@ -754,11 +764,12 @@
} else {
dprintk(VIDC_ERR, "invalid payload in rel_buff_done\n");
}
- device->callback(SESSION_RELEASE_BUFFER_DONE, &cmd_done);
+ callback(SESSION_RELEASE_BUFFER_DONE, &cmd_done);
}
-static void hfi_process_session_end_done(struct venus_hfi_device *device,
- struct hfi_msg_sys_session_end_done_packet *pkt)
+static void hfi_process_session_end_done(
+ msm_vidc_callback callback, u32 device_id,
+ struct hfi_msg_sys_session_end_done_packet *pkt)
{
struct msm_vidc_cb_cmd_done cmd_done;
struct hal_session *sess_close;
@@ -779,17 +790,17 @@
kfree(sess_close);
memset(&cmd_done, 0, sizeof(struct msm_vidc_cb_cmd_done));
- cmd_done.device_id = device->device_id;
+ cmd_done.device_id = device_id;
cmd_done.session_id =
((struct hal_session *) pkt->session_id)->session_id;
cmd_done.status = hfi_map_err_status((u32)pkt->error_type);
cmd_done.data = NULL;
cmd_done.size = 0;
- device->callback(SESSION_END_DONE, &cmd_done);
+ callback(SESSION_END_DONE, &cmd_done);
}
static void hfi_process_session_get_seq_hdr_done(
- struct venus_hfi_device *device,
+ msm_vidc_callback callback, u32 device_id,
struct hfi_msg_session_get_sequence_header_done_packet *pkt)
{
struct msm_vidc_cb_data_done data_done;
@@ -800,7 +811,7 @@
return;
}
memset(&data_done, 0, sizeof(struct msm_vidc_cb_data_done));
- data_done.device_id = device->device_id;
+ data_done.device_id = device_id;
data_done.size = sizeof(struct msm_vidc_cb_data_done);
data_done.session_id =
((struct hal_session *) pkt->session_id)->session_id;
@@ -809,121 +820,99 @@
data_done.output_done.filled_len1 = pkt->header_len;
dprintk(VIDC_INFO, "seq_hdr: %p, Length: %d",
pkt->sequence_header, pkt->header_len);
- device->callback(SESSION_GET_SEQ_HDR_DONE, &data_done);
+ callback(SESSION_GET_SEQ_HDR_DONE, &data_done);
}
-static void hfi_process_msg_packet(struct venus_hfi_device *device,
- struct vidc_hal_msg_pkt_hdr *msg_hdr)
+void hfi_process_msg_packet(
+ msm_vidc_callback callback, u32 device_id,
+ struct vidc_hal_msg_pkt_hdr *msg_hdr)
{
- if (!device || !msg_hdr || msg_hdr->size <
- VIDC_IFACEQ_MIN_PKT_SIZE) {
+ if (!callback || !msg_hdr || msg_hdr->size <
+ HFI_MIN_PKT_SIZE) {
dprintk(VIDC_ERR, "hal_process_msg_packet:bad"
"packet/packet size: %d", msg_hdr->size);
return;
}
dprintk(VIDC_INFO, "Received: 0x%x in ", msg_hdr->packet);
- if ((device->intr_status & VIDC_WRAPPER_INTR_CLEAR_A2HWD_BMSK)) {
- dprintk(VIDC_ERR, "Received: Watchdog timeout %s", __func__);
- hfi_process_sys_watchdog_timeout(device);
- return;
- }
switch (msg_hdr->packet) {
case HFI_MSG_EVENT_NOTIFY:
- hfi_process_event_notify(device,
+ hfi_process_event_notify(callback, device_id,
(struct hfi_msg_event_notify_packet *) msg_hdr);
break;
case HFI_MSG_SYS_INIT_DONE:
- hfi_process_sys_init_done(device,
+ hfi_process_sys_init_done(callback, device_id,
(struct hfi_msg_sys_init_done_packet *)
msg_hdr);
break;
case HFI_MSG_SYS_SESSION_INIT_DONE:
- hfi_process_session_init_done(device,
+ hfi_process_session_init_done(callback, device_id,
(struct hfi_msg_sys_session_init_done_packet *)
msg_hdr);
break;
case HFI_MSG_SYS_SESSION_END_DONE:
- hfi_process_session_end_done(device,
+ hfi_process_session_end_done(callback, device_id,
(struct hfi_msg_sys_session_end_done_packet *)
msg_hdr);
break;
case HFI_MSG_SESSION_LOAD_RESOURCES_DONE:
- hfi_process_session_load_res_done(device,
+ hfi_process_session_load_res_done(callback, device_id,
(struct hfi_msg_session_load_resources_done_packet *)
msg_hdr);
break;
case HFI_MSG_SESSION_START_DONE:
- hfi_process_session_start_done(device,
+ hfi_process_session_start_done(callback, device_id,
(struct hfi_msg_session_start_done_packet *)
msg_hdr);
break;
case HFI_MSG_SESSION_STOP_DONE:
- hfi_process_session_stop_done(device,
+ hfi_process_session_stop_done(callback, device_id,
(struct hfi_msg_session_stop_done_packet *)
msg_hdr);
break;
case HFI_MSG_SESSION_EMPTY_BUFFER_DONE:
- hfi_process_session_etb_done(device,
+ hfi_process_session_etb_done(callback, device_id,
(struct hfi_msg_session_empty_buffer_done_packet *)
msg_hdr);
break;
case HFI_MSG_SESSION_FILL_BUFFER_DONE:
- hfi_process_session_ftb_done(device, msg_hdr);
+ hfi_process_session_ftb_done(callback, device_id, msg_hdr);
break;
case HFI_MSG_SESSION_FLUSH_DONE:
- hfi_process_session_flush_done(device,
+ hfi_process_session_flush_done(callback, device_id,
(struct hfi_msg_session_flush_done_packet *)
msg_hdr);
break;
case HFI_MSG_SESSION_PROPERTY_INFO:
- hfi_process_session_prop_info(device,
+ hfi_process_session_prop_info(callback, device_id,
(struct hfi_msg_session_property_info_packet *)
msg_hdr);
break;
case HFI_MSG_SESSION_RELEASE_RESOURCES_DONE:
- hfi_process_session_rel_res_done(device,
+ hfi_process_session_rel_res_done(callback, device_id,
(struct hfi_msg_session_release_resources_done_packet *)
msg_hdr);
break;
case HFI_MSG_SYS_RELEASE_RESOURCE:
- hfi_process_sys_rel_resource_done(device,
+ hfi_process_sys_rel_resource_done(callback, device_id,
(struct hfi_msg_sys_release_resource_done_packet *)
msg_hdr);
break;
case HFI_MSG_SESSION_GET_SEQUENCE_HEADER_DONE:
- hfi_process_session_get_seq_hdr_done(device, (struct
- hfi_msg_session_get_sequence_header_done_packet
- *) msg_hdr);
+ hfi_process_session_get_seq_hdr_done(
+ callback, device_id, (struct
+ hfi_msg_session_get_sequence_header_done_packet*)
+ msg_hdr);
break;
case HFI_MSG_SESSION_RELEASE_BUFFERS_DONE:
- hfi_process_session_rel_buf_done(device, (struct
- hfi_msg_session_release_buffers_done_packet
- *) msg_hdr);
+ hfi_process_session_rel_buf_done(
+ callback, device_id, (struct
+ hfi_msg_session_release_buffers_done_packet*)
+ msg_hdr);
break;
default:
dprintk(VIDC_ERR, "UNKNOWN_MSG_TYPE : %d", msg_hdr->packet);
break;
}
}
-
-void hfi_response_handler(struct venus_hfi_device *device)
-{
- u8 packet[VIDC_IFACEQ_MED_PKT_SIZE];
-
- dprintk(VIDC_INFO, "#####vidc_hal_response_handler#####\n");
- if (device) {
- while (!venus_hfi_iface_msgq_read(device, packet)) {
- hfi_process_msg_packet(device,
- (struct vidc_hal_msg_pkt_hdr *) packet);
- }
- while (!venus_hfi_iface_dbgq_read(device, packet)) {
- struct hfi_msg_sys_debug_packet *pkt =
- (struct hfi_msg_sys_debug_packet *) packet;
- dprintk(VIDC_FW, "FW-SAYS: %s", pkt->rg_msg_data);
- }
- } else {
- dprintk(VIDC_ERR, "SPURIOUS_INTERRUPT");
- }
-}
diff --git a/drivers/media/video/msm_vidc/msm_v4l2_vidc.c b/drivers/media/video/msm_vidc/msm_v4l2_vidc.c
index e51896c..4f39357 100644
--- a/drivers/media/video/msm_vidc/msm_v4l2_vidc.c
+++ b/drivers/media/video/msm_vidc/msm_v4l2_vidc.c
@@ -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
@@ -29,7 +29,7 @@
#include "msm_vidc_debug.h"
#include "vidc_hfi_api.h"
#include "msm_smem.h"
-#include "venus_hfi.h"
+#include "vidc_hfi_api.h"
#define BASE_DEVICE_NUMBER 32
@@ -355,7 +355,8 @@
int i, rc = 0;
int smem_flags = 0;
int domain;
- struct venus_hfi_device *device;
+ struct hfi_device *hdev;
+
vidc_inst = get_vidc_inst(file, fh);
v4l2_inst = get_v4l2_inst(file, fh);
if (!v4l2_inst || !vidc_inst || !vidc_inst->core
@@ -364,7 +365,8 @@
goto exit;
}
- device = vidc_inst->core->device;
+ hdev = vidc_inst->core->device;
+
if (!v4l2_inst->mem_client) {
dprintk(VIDC_ERR, "Failed to get memory client\n");
rc = -ENOMEM;
@@ -404,9 +406,11 @@
&& (!EXTRADATA_IDX(b->length)
|| (i != EXTRADATA_IDX(b->length)))) {
smem_flags |= SMEM_SECURE;
- domain = venus_hfi_get_domain(device, CP_MAP);
+ domain = hdev->get_domain(hdev->hfi_device_data,
+ CP_MAP);
} else
- domain = venus_hfi_get_domain(device, NS_MAP);
+ domain = hdev->get_domain(hdev->hfi_device_data,
+ NS_MAP);
if (b->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
smem_flags |= SMEM_INPUT;
@@ -667,6 +671,30 @@
{
}
+static int msm_vidc_get_hfi(struct platform_device *pdev,
+ struct msm_vidc_core *core)
+{
+ struct device_node *np = pdev->dev.of_node;
+ int rc = 0;
+ const char *hfi_name = NULL;
+
+ rc = of_property_read_string(np, "hfi", &hfi_name);
+ if (rc) {
+ dprintk(VIDC_ERR, "Failed to read hfi from device tree\n");
+ goto err_hfi_read;
+ }
+
+ if (!strcmp(hfi_name, "venus"))
+ core->hfi_type = VIDC_HFI_VENUS;
+ else if (!strcmp(hfi_name, "q6"))
+ core->hfi_type = VIDC_HFI_Q6;
+
+ dprintk(VIDC_INFO, "hfi_type = %d\n", core->hfi_type);
+
+err_hfi_read:
+ return rc;
+}
+
static int msm_vidc_initialize_core(struct platform_device *pdev,
struct msm_vidc_core *core)
{
@@ -685,6 +713,11 @@
init_completion(&core->completions[i]);
}
+ rc = msm_vidc_get_hfi(pdev, core);
+ if (rc)
+ dprintk(VIDC_ERR,
+ "Failed to read Host-Firmware Interface rc: %d\n", rc);
+
return rc;
}
@@ -736,8 +769,8 @@
}
video_set_drvdata(&core->vdev[MSM_VIDC_ENCODER].vdev, core);
- core->device =
- venus_hfi_get_device(core->id, pdev, &handle_cmd_response);
+ core->device = vidc_hfi_initialize(core->hfi_type, core->id,
+ pdev, &handle_cmd_response);
if (!core->device) {
dprintk(VIDC_ERR, "Failed to create HFI device\n");
goto err_cores_exceeded;
@@ -774,9 +807,20 @@
static int __devexit msm_vidc_remove(struct platform_device *pdev)
{
int rc = 0;
- struct msm_vidc_core *core = pdev->dev.platform_data;
+ struct msm_vidc_core *core;
- venus_hfi_delete_device(core->device);
+ if (!pdev) {
+ dprintk(VIDC_ERR, "%s invalid input %p", __func__, pdev);
+ return -EINVAL;
+ }
+ core = pdev->dev.platform_data;
+
+ if (!core) {
+ dprintk(VIDC_ERR, "%s invalid core", __func__);
+ return -EINVAL;
+ }
+
+ vidc_hfi_deinitialize(core->hfi_type, core->device);
video_unregister_device(&core->vdev[MSM_VIDC_ENCODER].vdev);
video_unregister_device(&core->vdev[MSM_VIDC_DECODER].vdev);
v4l2_device_unregister(&core->v4l2_dev);
diff --git a/drivers/media/video/msm_vidc/msm_vdec.c b/drivers/media/video/msm_vidc/msm_vdec.c
index 525bad8..24407dd 100644
--- a/drivers/media/video/msm_vidc/msm_vdec.c
+++ b/drivers/media/video/msm_vidc/msm_vdec.c
@@ -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
@@ -377,6 +377,14 @@
struct vidc_buffer_addr_info buffer_info;
int extra_idx = 0;
int i;
+ struct hfi_device *hdev;
+
+ if (!inst || !inst->core || !inst->core->device) {
+ dprintk(VIDC_ERR, "%s invalid parameters", __func__);
+ return -EINVAL;
+ }
+ hdev = inst->core->device;
+
switch (b->type) {
case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
break;
@@ -415,7 +423,7 @@
buffer_info.extradata_addr = 0;
buffer_info.extradata_size = 0;
}
- rc = venus_hfi_session_set_buffers(
+ rc = hdev->session_set_buffers(
(void *)inst->session, &buffer_info);
if (rc)
dprintk(VIDC_ERR,
@@ -436,6 +444,15 @@
struct msm_vidc_core *core = inst->core;
int extra_idx = 0;
int i;
+ struct hfi_device *hdev;
+
+ if (!inst || !inst->core || !inst->core->device) {
+ dprintk(VIDC_ERR, "%s invalid parameters", __func__);
+ return -EINVAL;
+ }
+
+ hdev = inst->core->device;
+
if (inst->state == MSM_VIDC_CORE_INVALID ||
core->state == VIDC_CORE_INVALID) {
dprintk(VIDC_ERR,
@@ -484,7 +501,7 @@
else
buffer_info.extradata_addr = 0;
buffer_info.response_required = false;
- rc = venus_hfi_session_release_buffers(
+ rc = hdev->session_release_buffers(
(void *)inst->session, &buffer_info);
if (rc)
dprintk(VIDC_ERR,
@@ -809,6 +826,7 @@
unsigned long flags;
struct hal_buffer_requirements *bufreq;
int extra_idx = 0;
+ struct hfi_device *hdev;
if (!q || !num_buffers || !num_planes
|| !sizes || !q->drv_priv) {
dprintk(VIDC_ERR, "Invalid input, q = %p, %p, %p\n",
@@ -816,6 +834,14 @@
return -EINVAL;
}
inst = q->drv_priv;
+
+ if (!inst || !inst->core || !inst->core->device) {
+ dprintk(VIDC_ERR, "%s invalid parameters", __func__);
+ return -EINVAL;
+ }
+
+ hdev = inst->core->device;
+
switch (q->type) {
case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
*num_planes = inst->fmts[OUTPUT_PORT]->num_planes;
@@ -851,7 +877,7 @@
new_buf_count.buffer_type = HAL_BUFFER_OUTPUT;
new_buf_count.buffer_count_actual = *num_buffers;
- rc = venus_hfi_session_set_property(inst->session,
+ rc = hdev->session_set_property(inst->session,
property_id, &new_buf_count);
}
@@ -1088,6 +1114,13 @@
enum hal_property property_id = 0;
u32 property_val = 0;
void *pdata;
+ struct hfi_device *hdev;
+
+ if (!inst || !inst->core || !inst->core->device) {
+ dprintk(VIDC_ERR, "%s invalid parameters", __func__);
+ return -EINVAL;
+ }
+ hdev = inst->core->device;
switch (ctrl->id) {
case V4L2_CID_MPEG_VIDC_VIDEO_STREAM_FORMAT:
@@ -1166,7 +1199,7 @@
property_id,
msm_vdec_ctrls[control_idx].id,
control.value);
- rc = venus_hfi_session_set_property((void *)
+ rc = hdev->session_set_property((void *)
inst->session, property_id,
pdata);
}
diff --git a/drivers/media/video/msm_vidc/msm_venc.c b/drivers/media/video/msm_vidc/msm_venc.c
index 341e06f..05d3570 100644
--- a/drivers/media/video/msm_vidc/msm_venc.c
+++ b/drivers/media/video/msm_vidc/msm_venc.c
@@ -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
@@ -390,10 +390,16 @@
.name = "Slice Mode",
.type = V4L2_CTRL_TYPE_MENU,
.minimum = V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE,
- .maximum = V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES,
+ .maximum = V4L2_MPEG_VIDEO_MULTI_SLICE_GOB,
.default_value = V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE,
.step = 1,
- .menu_skip_mask = 0,
+ .menu_skip_mask = ~(
+ (1 << V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE) |
+ (1 << V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_MB) |
+ (1 << V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES) |
+ (1 << V4L2_MPEG_VIDEO_MULTI_SLICE_GOB)
+ ),
+ .qmenu = NULL,
.cluster = MSM_VENC_CTRL_CLUSTER_SLICING,
},
{
@@ -421,6 +427,18 @@
.cluster = MSM_VENC_CTRL_CLUSTER_SLICING,
},
{
+ .id = V4L2_CID_MPEG_VIDEO_MULTI_SLICE_GOB,
+ .name = "Slice GOB",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 1,
+ .maximum = MAX_SLICE_MB_SIZE,
+ .default_value = 1,
+ .step = 1,
+ .menu_skip_mask = 0,
+ .qmenu = NULL,
+ .cluster = MSM_VENC_CTRL_CLUSTER_SLICING,
+ },
+ {
.id = V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_MODE,
.name = "Intra Refresh Mode",
.type = V4L2_CTRL_TYPE_MENU,
@@ -611,11 +629,19 @@
struct hal_buffer_count_actual new_buf_count;
enum hal_property property_id;
unsigned long flags;
+ struct hfi_device *hdev;
if (!q || !q->drv_priv) {
dprintk(VIDC_ERR, "Invalid input, q = %p\n", q);
return -EINVAL;
}
inst = q->drv_priv;
+
+ if (!inst || !inst->core || !inst->core->device) {
+ dprintk(VIDC_ERR, "%s invalid parameters", __func__);
+ return -EINVAL;
+ }
+ hdev = inst->core->device;
+
switch (q->type) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
*num_planes = 1;
@@ -648,7 +674,7 @@
property_id = HAL_PARAM_BUFFER_COUNT_ACTUAL;
new_buf_count.buffer_type = HAL_BUFFER_INPUT;
new_buf_count.buffer_count_actual = *num_buffers;
- rc = venus_hfi_session_set_property(inst->session,
+ rc = hdev->session_set_property(inst->session,
property_id, &new_buf_count);
dprintk(VIDC_DBG, "size = %d, alignment = %d, count = %d\n",
inst->buff_req.buffer[0].buffer_size,
@@ -966,6 +992,13 @@
u32 property_id = 0, property_val = 0;
void *pdata;
struct v4l2_ctrl *temp_ctrl = NULL;
+ struct hfi_device *hdev;
+
+ if (!inst || !inst->core || !inst->core->device) {
+ dprintk(VIDC_ERR, "%s invalid parameters", __func__);
+ return -EINVAL;
+ }
+ hdev = inst->core->device;
/* Small helper macro for quickly getting a control and err checking */
#define TRY_GET_CTRL(__ctrl_id) ({ \
@@ -1104,6 +1137,7 @@
property_id =
HAL_CONFIG_VENC_TARGET_BITRATE;
bitrate.bit_rate = ctrl->val;
+ bitrate.layer_id = 0;
pdata = &bitrate;
break;
case V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE:
@@ -1218,6 +1252,7 @@
quantization.qpi = ctrl->val;
quantization.qpp = qpp->val;
quantization.qpb = qpb->val;
+ quantization.layer_id = 0;
pdata = &quantization;
break;
@@ -1233,6 +1268,7 @@
quantization.qpp = ctrl->val;
quantization.qpi = qpi->val;
quantization.qpb = qpb->val;
+ quantization.layer_id = 0;
pdata = &quantization;
break;
@@ -1248,6 +1284,7 @@
quantization.qpb = ctrl->val;
quantization.qpi = qpi->val;
quantization.qpp = qpp->val;
+ quantization.layer_id = 0;
pdata = &quantization;
break;
@@ -1262,6 +1299,9 @@
case V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES:
temp = V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES;
break;
+ case V4L2_MPEG_VIDEO_MULTI_SLICE_GOB:
+ temp = V4L2_CID_MPEG_VIDEO_MULTI_SLICE_GOB;
+ break;
case V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE:
default:
temp = 0;
@@ -1281,6 +1321,7 @@
}
case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES:
case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB:
+ case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_GOB:
temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE);
property_id =
@@ -1405,7 +1446,7 @@
dprintk(VIDC_DBG, "Control: HAL property=%d,ctrl_value=%d\n",
property_id,
ctrl->val);
- rc = venus_hfi_session_set_property((void *)inst->session,
+ rc = hdev->session_set_property((void *)inst->session,
property_id, pdata);
}
@@ -1570,6 +1611,14 @@
void *pdata;
int rc = 0;
struct hal_frame_rate frame_rate;
+ struct hfi_device *hdev;
+
+ if (!inst || !inst->core || !inst->core->device) {
+ dprintk(VIDC_ERR, "%s invalid parameters", __func__);
+ return -EINVAL;
+ }
+ hdev = inst->core->device;
+
property_id = HAL_CONFIG_FRAME_RATE;
if (a->parm.output.timeperframe.denominator) {
switch (a->type) {
@@ -1597,7 +1646,7 @@
frame_rate.frame_rate = inst->prop.fps * (0x1<<16);
frame_rate.buffer_type = HAL_BUFFER_OUTPUT;
pdata = &frame_rate;
- rc = venus_hfi_session_set_property((void *)inst->session,
+ rc = hdev->session_set_property((void *)inst->session,
property_id, pdata);
if (rc) {
dprintk(VIDC_WARN,
@@ -1614,11 +1663,19 @@
struct hal_frame_size frame_sz;
int rc = 0;
int i;
+ struct hfi_device *hdev;
if (!inst || !f) {
dprintk(VIDC_ERR,
"Invalid input, inst = %p, format = %p\n", inst, f);
return -EINVAL;
}
+
+ if (!inst->core || !inst->core->device) {
+ dprintk(VIDC_ERR, "%s invalid parameters", __func__);
+ return -EINVAL;
+ }
+ hdev = inst->core->device;
+
if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
fmt = msm_comm_get_pixel_fmt_fourcc(venc_formats,
ARRAY_SIZE(venc_formats), f->fmt.pix_mp.pixelformat,
@@ -1638,7 +1695,7 @@
frame_sz.height = inst->prop.height;
dprintk(VIDC_DBG, "width = %d, height = %d\n",
frame_sz.width, frame_sz.height);
- rc = venus_hfi_session_set_property((void *)inst->session,
+ rc = hdev->session_set_property((void *)inst->session,
HAL_PARAM_FRAME_SIZE, &frame_sz);
if (rc) {
dprintk(VIDC_ERR,
@@ -1646,7 +1703,7 @@
goto exit;
}
frame_sz.buffer_type = HAL_BUFFER_OUTPUT;
- rc = venus_hfi_session_set_property((void *)inst->session,
+ rc = hdev->session_set_property((void *)inst->session,
HAL_PARAM_FRAME_SIZE, &frame_sz);
if (rc) {
dprintk(VIDC_ERR,
@@ -1752,6 +1809,14 @@
int rc = 0;
int i;
struct vidc_buffer_addr_info buffer_info;
+ struct hfi_device *hdev;
+
+ if (!inst || !inst->core || !inst->core->device) {
+ dprintk(VIDC_ERR, "%s invalid parameters", __func__);
+ return -EINVAL;
+ }
+
+ hdev = inst->core->device;
switch (b->type) {
case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
@@ -1769,7 +1834,7 @@
b->m.planes[i].m.userptr;
buffer_info.extradata_size = 0;
buffer_info.extradata_addr = 0;
- rc = venus_hfi_session_set_buffers(
+ rc = hdev->session_set_buffers(
(void *)inst->session, &buffer_info);
if (rc)
dprintk(VIDC_ERR,
@@ -1790,6 +1855,15 @@
int rc = 0;
int i;
struct vidc_buffer_addr_info buffer_info;
+ struct hfi_device *hdev;
+
+ if (!inst || !inst->core || !inst->core->device) {
+ dprintk(VIDC_ERR, "%s invalid parameters", __func__);
+ return -EINVAL;
+ }
+
+ hdev = inst->core->device;
+
rc = msm_comm_try_state(inst, MSM_VIDC_RELEASE_RESOURCES_DONE);
if (rc) {
dprintk(VIDC_ERR,
@@ -1814,7 +1888,7 @@
buffer_info.extradata_size = 0;
buffer_info.extradata_addr = 0;
buffer_info.response_required = false;
- rc = venus_hfi_session_release_buffers(
+ rc = hdev->session_release_buffers(
(void *)inst->session, &buffer_info);
if (rc)
dprintk(VIDC_ERR,
diff --git a/drivers/media/video/msm_vidc/msm_vidc.c b/drivers/media/video/msm_vidc/msm_vidc.c
index b9f1508..73c9860 100644
--- a/drivers/media/video/msm_vidc/msm_vidc.c
+++ b/drivers/media/video/msm_vidc/msm_vidc.c
@@ -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
@@ -21,7 +21,7 @@
#include "msm_vidc_common.h"
#include "msm_smem.h"
#include <linux/delay.h>
-#include "venus_hfi.h"
+#include "vidc_hfi_api.h"
#define MAX_EVENTS 30
@@ -86,11 +86,14 @@
struct msm_vidc_iommu_info maps[MAX_MAP])
{
struct msm_vidc_inst *inst = instance;
+ struct hfi_device *hdev;
- if (!inst || !maps || !inst->core)
+ if (!inst || !maps || !inst->core || !inst->core->device)
return -EINVAL;
- return venus_hfi_iommu_get_map(inst->core->device, maps);
+ hdev = inst->core->device;
+
+ return hdev->iommu_get_map(hdev->hfi_device_data, maps);
}
int msm_vidc_querycap(void *instance, struct v4l2_capability *cap)
diff --git a/drivers/media/video/msm_vidc/msm_vidc_common.c b/drivers/media/video/msm_vidc/msm_vidc_common.c
index eac715f..7e8732a 100644
--- a/drivers/media/video/msm_vidc/msm_vidc_common.c
+++ b/drivers/media/video/msm_vidc/msm_vidc_common.c
@@ -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
@@ -20,7 +20,6 @@
#include "vidc_hfi_api.h"
#include "msm_smem.h"
#include "msm_vidc_debug.h"
-#include "venus_hfi.h"
#define HW_RESPONSE_TIMEOUT (5 * 60 * 1000)
@@ -77,14 +76,22 @@
{
int load;
int rc = 0;
+ struct hfi_device *hdev;
if (!core || type >= MSM_VIDC_MAX_DEVICES) {
dprintk(VIDC_ERR, "Invalid args: %p, %d\n", core, type);
return -EINVAL;
}
+
+ hdev = core->device;
+ if (!hdev) {
+ dprintk(VIDC_ERR, "Invalid device handle %p\n", hdev);
+ return -EINVAL;
+ }
+
load = msm_comm_get_load(core, type);
- rc = venus_hfi_scale_bus(core->device, load, type, mtype);
+ rc = hdev->scale_bus(hdev->hfi_device_data, load, type, mtype);
if (rc)
dprintk(VIDC_ERR, "Failed to scale bus: %d\n", rc);
@@ -95,14 +102,23 @@
enum mem_type mtype)
{
int i;
+ struct hfi_device *hdev;
+
+ if (!core || !core->device) {
+ dprintk(VIDC_ERR, "%s invalid parameters", __func__);
+ return;
+ }
+ hdev = core->device;
+
for (i = 0; i < MSM_VIDC_MAX_DEVICES; i++) {
if ((mtype & DDR_MEM) &&
- venus_hfi_scale_bus(core->device, 0, i, DDR_MEM)) {
+ hdev->scale_bus(hdev->hfi_device_data, 0, i, DDR_MEM)) {
dprintk(VIDC_WARN,
"Failed to unvote for DDR accesses\n");
}
if ((mtype & OCMEM_MEM) &&
- venus_hfi_scale_bus(core->device, 0, i, OCMEM_MEM)) {
+ hdev->scale_bus(hdev->hfi_device_data, 0, i,
+ OCMEM_MEM)) {
dprintk(VIDC_WARN,
"Failed to unvote for OCMEM accesses\n");
}
@@ -406,6 +422,9 @@
inst->buff_req.buffer[i].buffer_count_actual,
inst->buff_req.buffer[i].buffer_size);
}
+ dprintk(VIDC_PROF, "Input buffers: %d, Output buffers: %d\n",
+ inst->buff_req.buffer[0].buffer_count_actual,
+ inst->buff_req.buffer[1].buffer_count_actual);
signal_session_msg_receipt(cmd, inst);
}
@@ -700,8 +719,13 @@
(u32)fill_buf_done->packet_buffer1);
if (vb) {
vb->v4l2_planes[0].bytesused = fill_buf_done->filled_len1;
+ vb->v4l2_planes[0].reserved[2] = fill_buf_done->start_x_coord;
+ vb->v4l2_planes[0].reserved[3] = fill_buf_done->start_y_coord;
+ vb->v4l2_planes[0].reserved[4] = fill_buf_done->frame_width;
+ vb->v4l2_planes[0].reserved[5] = fill_buf_done->frame_height;
if (!(fill_buf_done->flags1 &
- HAL_BUFFERFLAG_TIMESTAMPINVALID)) {
+ HAL_BUFFERFLAG_TIMESTAMPINVALID) &&
+ fill_buf_done->filled_len1) {
int64_t time_usec = fill_buf_done->timestamp_hi;
time_usec = (time_usec << 32) |
fill_buf_done->timestamp_lo;
@@ -876,15 +900,23 @@
{
int num_mbs_per_sec;
int rc = 0;
+ struct hfi_device *hdev;
if (!core) {
- dprintk(VIDC_ERR, "Invalid args: %p\n", core);
+ dprintk(VIDC_ERR, "%s Invalid args: %p\n", __func__, core);
+ return -EINVAL;
+ }
+
+ hdev = core->device;
+ if (!hdev) {
+ dprintk(VIDC_ERR, "%s Invalid device handle: %p\n",
+ __func__, hdev);
return -EINVAL;
}
num_mbs_per_sec = msm_comm_get_load(core, MSM_VIDC_ENCODER);
num_mbs_per_sec += msm_comm_get_load(core, MSM_VIDC_DECODER);
dprintk(VIDC_INFO, "num_mbs_per_sec = %d\n", num_mbs_per_sec);
- rc = venus_hfi_scale_clocks(core->device, num_mbs_per_sec);
+ rc = hdev->scale_clocks(hdev->hfi_device_data, num_mbs_per_sec);
if (rc)
dprintk(VIDC_ERR, "Failed to set clock rate: %d\n", rc);
return rc;
@@ -892,12 +924,16 @@
void msm_comm_scale_clocks_and_bus(struct msm_vidc_inst *inst)
{
- struct msm_vidc_core *core = inst->core;
+ struct msm_vidc_core *core;
+ struct hfi_device *hdev;
- if (!inst) {
- dprintk(VIDC_WARN, "Invalid params\n");
+ if (!inst || !inst->core || !inst->core->device) {
+ dprintk(VIDC_ERR, "%s Invalid params\n", __func__);
return;
}
+ core = inst->core;
+ hdev = core->device;
+
if (msm_comm_scale_clocks(core)) {
dprintk(VIDC_WARN,
"Failed to scale clocks. Performance might be impacted\n");
@@ -906,7 +942,7 @@
dprintk(VIDC_WARN,
"Failed to scale DDR bus. Performance might be impacted\n");
}
- if (venus_hfi_is_ocmem_present(core->device)) {
+ if (hdev->is_ocmem_present(hdev->hfi_device_data)) {
if (msm_comm_scale_bus(core, inst->session_type,
OCMEM_MEM))
dprintk(VIDC_WARN,
@@ -926,6 +962,14 @@
static int msm_comm_unset_ocmem(struct msm_vidc_core *core)
{
int rc = 0;
+ struct hfi_device *hdev;
+
+ if (!core || !core->device) {
+ dprintk(VIDC_ERR, "%s invalid parameters", __func__);
+ return -EINVAL;
+ }
+ hdev = core->device;
+
if (core->state == VIDC_CORE_INVALID) {
dprintk(VIDC_ERR,
"Core is in bad state. Cannot unset ocmem\n");
@@ -935,7 +979,7 @@
init_completion(
&core->completions[SYS_MSG_INDEX(RELEASE_RESOURCE_DONE)]);
- rc = venus_hfi_unset_ocmem(core->device);
+ rc = hdev->unset_ocmem(hdev->hfi_device_data);
if (rc) {
dprintk(VIDC_ERR, "Failed to set OCMEM on driver\n");
goto release_ocmem_failed;
@@ -951,39 +995,6 @@
return rc;
}
-int msm_vidc_ocmem_notify_handler(struct notifier_block *this,
- unsigned long event, void *data)
-{
- struct ocmem_buf *buff = data;
- struct msm_vidc_core *core;
- struct venus_hfi_device *device;
- struct venus_resources *resources;
- struct on_chip_mem *ocmem;
- int rc = NOTIFY_DONE;
- if (event == OCMEM_ALLOC_GROW) {
- ocmem = container_of(this, struct on_chip_mem, vidc_ocmem_nb);
- if (!ocmem) {
- dprintk(VIDC_ERR, "Wrong handler passed\n");
- rc = NOTIFY_BAD;
- goto bad_notfier;
- }
- resources = container_of(ocmem,
- struct venus_resources, ocmem);
- device = container_of(resources,
- struct venus_hfi_device, resources);
- core = container_of((void *)device,
- struct msm_vidc_core, device);
- if (venus_hfi_set_ocmem(core->device, buff)) {
- dprintk(VIDC_ERR, "Failed to set ocmem: %d\n", rc);
- goto ocmem_set_failed;
- }
- rc = NOTIFY_OK;
- }
-ocmem_set_failed:
-bad_notfier:
- return rc;
-}
-
static int msm_comm_init_core_done(struct msm_vidc_inst *inst)
{
struct msm_vidc_core *core = inst->core;
@@ -1022,11 +1033,11 @@
int rc = 0;
struct msm_vidc_core *core = inst->core;
unsigned long flags;
- struct venus_hfi_device *device;
+ struct hfi_device *hdev;
if (!core || !core->device)
return -EINVAL;
- device = core->device;
+ hdev = core->device;
mutex_lock(&core->sync_lock);
if (core->state >= VIDC_CORE_INIT) {
@@ -1041,7 +1052,7 @@
goto fail_scale_bus;
}
- rc = venus_hfi_load_fw(core->device);
+ rc = hdev->load_fw(hdev->hfi_device_data);
if (rc) {
dprintk(VIDC_ERR, "Failed to load video firmware\n");
goto fail_load_fw;
@@ -1053,7 +1064,7 @@
}
init_completion(&core->completions[SYS_MSG_INDEX(SYS_INIT_DONE)]);
- rc = venus_hfi_core_init(core->device);
+ rc = hdev->core_init(hdev->hfi_device_data);
if (rc) {
dprintk(VIDC_ERR, "Failed to init core, id = %d\n", core->id);
goto fail_core_init;
@@ -1066,7 +1077,7 @@
mutex_unlock(&core->sync_lock);
return rc;
fail_core_init:
- venus_hfi_unload_fw(core->device);
+ hdev->unload_fw(hdev->hfi_device_data);
fail_load_fw:
msm_comm_unvote_buses(core, DDR_MEM);
fail_scale_bus:
@@ -1077,8 +1088,18 @@
static int msm_vidc_deinit_core(struct msm_vidc_inst *inst)
{
int rc = 0;
- struct msm_vidc_core *core = inst->core;
+ struct msm_vidc_core *core;
+ struct hfi_device *hdev;
unsigned long flags;
+
+ if (!inst || !inst->core || !inst->core->device) {
+ dprintk(VIDC_ERR, "%s invalid parameters", __func__);
+ return -EINVAL;
+ }
+
+ core = inst->core;
+ hdev = core->device;
+
mutex_lock(&core->sync_lock);
if (core->state == VIDC_CORE_UNINIT) {
dprintk(VIDC_INFO, "Video core: %d is already in state: %d\n",
@@ -1087,10 +1108,11 @@
}
msm_comm_scale_clocks_and_bus(inst);
if (list_empty(&core->instances)) {
- msm_comm_unset_ocmem(core);
- venus_hfi_free_ocmem(core->device);
+ if (inst->state != MSM_VIDC_CORE_INVALID)
+ msm_comm_unset_ocmem(core);
+ hdev->free_ocmem(hdev->hfi_device_data);
dprintk(VIDC_DBG, "Calling vidc_hal_core_release\n");
- rc = venus_hfi_core_release(core->device);
+ rc = hdev->core_release(hdev->hfi_device_data);
if (rc) {
dprintk(VIDC_ERR, "Failed to release core, id = %d\n",
core->id);
@@ -1099,7 +1121,7 @@
spin_lock_irqsave(&core->lock, flags);
core->state = VIDC_CORE_UNINIT;
spin_unlock_irqrestore(&core->lock, flags);
- venus_hfi_unload_fw(core->device);
+ hdev->unload_fw(hdev->hfi_device_data);
msm_comm_unvote_buses(core, DDR_MEM|OCMEM_MEM);
}
core_already_uninited:
@@ -1182,6 +1204,14 @@
{
int rc = 0;
int fourcc = 0;
+ struct hfi_device *hdev;
+
+ if (!inst || !inst->core || !inst->core->device) {
+ dprintk(VIDC_ERR, "%s invalid parameters", __func__);
+ return -EINVAL;
+ }
+ hdev = inst->core->device;
+
if (IS_ALREADY_IN_STATE(flipped_state, MSM_VIDC_OPEN)) {
dprintk(VIDC_INFO, "inst: %p is already in state: %d\n",
inst, inst->state);
@@ -1197,7 +1227,7 @@
}
init_completion(
&inst->completions[SESSION_MSG_INDEX(SESSION_INIT_DONE)]);
- inst->session = venus_hfi_session_init(inst->core->device, (u32) inst,
+ inst->session = hdev->session_init(hdev->hfi_device_data, (u32) inst,
get_hal_domain(inst->session_type),
get_hal_codec_type(fourcc));
if (!inst->session) {
@@ -1218,6 +1248,14 @@
{
int rc = 0;
u32 ocmem_sz = 0;
+ struct hfi_device *hdev;
+
+ if (!inst || !inst->core || !inst->core->device) {
+ dprintk(VIDC_ERR, "%s invalid parameters", __func__);
+ return -EINVAL;
+ }
+ hdev = inst->core->device;
+
if (IS_ALREADY_IN_STATE(flipped_state, MSM_VIDC_LOAD_RESOURCES)) {
dprintk(VIDC_INFO, "inst: %p is already in state: %d\n",
inst, inst->state);
@@ -1227,7 +1265,7 @@
rc = msm_comm_scale_bus(inst->core, inst->session_type, OCMEM_MEM);
if (!rc) {
mutex_lock(&inst->core->sync_lock);
- rc = venus_hfi_alloc_ocmem(inst->core->device, ocmem_sz);
+ rc = hdev->alloc_ocmem(hdev->hfi_device_data, ocmem_sz);
mutex_unlock(&inst->core->sync_lock);
if (rc) {
dprintk(VIDC_WARN,
@@ -1238,7 +1276,7 @@
dprintk(VIDC_WARN,
"Failed to vote for OCMEM BW. Performance will be impacted\n");
}
- rc = venus_hfi_session_load_res((void *) inst->session);
+ rc = hdev->session_load_res((void *) inst->session);
if (rc) {
dprintk(VIDC_ERR,
"Failed to send load resources\n");
@@ -1252,6 +1290,15 @@
static int msm_vidc_start(int flipped_state, struct msm_vidc_inst *inst)
{
int rc = 0;
+ struct hfi_device *hdev;
+
+ if (!inst || !inst->core || !inst->core->device) {
+ dprintk(VIDC_ERR, "%s invalid parameters", __func__);
+ return -EINVAL;
+ }
+
+ hdev = inst->core->device;
+
if (IS_ALREADY_IN_STATE(flipped_state, MSM_VIDC_START)) {
dprintk(VIDC_INFO,
"inst: %p is already in state: %d\n",
@@ -1260,7 +1307,7 @@
}
init_completion(
&inst->completions[SESSION_MSG_INDEX(SESSION_START_DONE)]);
- rc = venus_hfi_session_start((void *) inst->session);
+ rc = hdev->session_start((void *) inst->session);
if (rc) {
dprintk(VIDC_ERR,
"Failed to send start\n");
@@ -1274,6 +1321,14 @@
static int msm_vidc_stop(int flipped_state, struct msm_vidc_inst *inst)
{
int rc = 0;
+ struct hfi_device *hdev;
+
+ if (!inst || !inst->core || !inst->core->device) {
+ dprintk(VIDC_ERR, "%s invalid parameters", __func__);
+ return -EINVAL;
+ }
+ hdev = inst->core->device;
+
if (IS_ALREADY_IN_STATE(flipped_state, MSM_VIDC_STOP)) {
dprintk(VIDC_INFO,
"inst: %p is already in state: %d\n",
@@ -1283,7 +1338,7 @@
dprintk(VIDC_DBG, "Send Stop to hal\n");
init_completion(
&inst->completions[SESSION_MSG_INDEX(SESSION_STOP_DONE)]);
- rc = venus_hfi_session_stop((void *) inst->session);
+ rc = hdev->session_stop((void *) inst->session);
if (rc) {
dprintk(VIDC_ERR, "Failed to send stop\n");
goto exit;
@@ -1296,6 +1351,14 @@
static int msm_vidc_release_res(int flipped_state, struct msm_vidc_inst *inst)
{
int rc = 0;
+ struct hfi_device *hdev;
+
+ if (!inst || !inst->core || !inst->core->device) {
+ dprintk(VIDC_ERR, "%s invalid parameters", __func__);
+ return -EINVAL;
+ }
+ hdev = inst->core->device;
+
if (IS_ALREADY_IN_STATE(flipped_state, MSM_VIDC_RELEASE_RESOURCES)) {
dprintk(VIDC_INFO,
"inst: %p is already in state: %d\n",
@@ -1306,7 +1369,7 @@
"Send release res to hal\n");
init_completion(
&inst->completions[SESSION_MSG_INDEX(SESSION_RELEASE_RESOURCE_DONE)]);
- rc = venus_hfi_session_release_res((void *) inst->session);
+ rc = hdev->session_release_res((void *) inst->session);
if (rc) {
dprintk(VIDC_ERR,
"Failed to send release resources\n");
@@ -1317,9 +1380,17 @@
return rc;
}
-static int msm_comm_session_close(int flipped_state, struct msm_vidc_inst *inst)
+static int msm_comm_session_close(int flipped_state,
+ struct msm_vidc_inst *inst)
{
int rc = 0;
+ struct hfi_device *hdev;
+
+ if (!inst || !inst->core || !inst->core->device) {
+ dprintk(VIDC_ERR, "%s invalid params", __func__);
+ return -EINVAL;
+ }
+ hdev = inst->core->device;
if (IS_ALREADY_IN_STATE(flipped_state, MSM_VIDC_CLOSE)) {
dprintk(VIDC_INFO,
"inst: %p is already in state: %d\n",
@@ -1330,7 +1401,7 @@
"Send session close to hal\n");
init_completion(
&inst->completions[SESSION_MSG_INDEX(SESSION_END_DONE)]);
- rc = venus_hfi_session_end((void *) inst->session);
+ rc = hdev->session_end((void *) inst->session);
if (rc) {
dprintk(VIDC_ERR,
"Failed to send close\n");
@@ -1479,6 +1550,7 @@
struct vb2_buf_entry *entry;
struct vidc_frame_data frame_data;
struct msm_vidc_core *core;
+ struct hfi_device *hdev;
q = vb->vb2_queue;
inst = q->drv_priv;
if (!inst || !vb) {
@@ -1491,6 +1563,11 @@
"Invalid input: %p, %p, %p\n", inst, core, vb);
return -EINVAL;
}
+ hdev = core->device;
+ if (!hdev) {
+ dprintk(VIDC_ERR, "Invalid input: %p", hdev);
+ return -EINVAL;
+ }
if (inst->state == MSM_VIDC_CORE_INVALID ||
core->state == VIDC_CORE_INVALID) {
@@ -1534,7 +1611,7 @@
dprintk(VIDC_DBG,
"Sending etb to hal: Alloc: %d :filled: %d\n",
frame_data.alloc_len, frame_data.filled_len);
- rc = venus_hfi_session_etb((void *) inst->session,
+ rc = hdev->session_etb((void *) inst->session,
&frame_data);
if (!rc)
msm_vidc_debugfs_update(inst,
@@ -1564,7 +1641,7 @@
seq_hdr.seq_hdr = (u8 *) vb->v4l2_planes[0].
m.userptr;
seq_hdr.seq_hdr_len = vb->v4l2_planes[0].length;
- rc = venus_hfi_session_get_seq_hdr((void *)
+ rc = hdev->session_get_seq_hdr((void *)
inst->session, &seq_hdr);
if (!rc) {
inst->vb2_seq_hdr = vb;
@@ -1572,7 +1649,7 @@
inst->vb2_seq_hdr);
}
} else {
- rc = venus_hfi_session_ftb((void *)
+ rc = hdev->session_ftb((void *)
inst->session, &frame_data);
if (!rc)
msm_vidc_debugfs_update(inst,
@@ -1595,6 +1672,14 @@
int msm_comm_try_get_bufreqs(struct msm_vidc_inst *inst)
{
int rc = 0;
+ struct hfi_device *hdev;
+
+ if (!inst || !inst->core || !inst->core->device) {
+ dprintk(VIDC_ERR, "%s invalid parameters", __func__);
+ return -EINVAL;
+ }
+ hdev = inst->core->device;
+
mutex_lock(&inst->sync_lock);
if (inst->state < MSM_VIDC_OPEN_DONE || inst->state >= MSM_VIDC_CLOSE) {
dprintk(VIDC_ERR,
@@ -1604,7 +1689,7 @@
}
init_completion(
&inst->completions[SESSION_MSG_INDEX(SESSION_PROPERTY_INFO)]);
- rc = venus_hfi_session_get_buf_req((void *) inst->session);
+ rc = hdev->session_get_buf_req((void *) inst->session);
if (rc) {
dprintk(VIDC_ERR, "Failed to get property\n");
goto exit;
@@ -1632,6 +1717,7 @@
int rc = 0;
unsigned long flags;
struct msm_vidc_core *core;
+ struct hfi_device *hdev;
if (!inst) {
dprintk(VIDC_ERR,
"Invalid instance pointer = %p\n", inst);
@@ -1643,6 +1729,11 @@
"Invalid core pointer = %p\n", core);
return -EINVAL;
}
+ hdev = core->device;
+ if (!hdev) {
+ dprintk(VIDC_ERR, "Invalid device pointer = %p\n", hdev);
+ return -EINVAL;
+ }
spin_lock_irqsave(&inst->lock, flags);
if (!list_empty(&inst->internalbufs)) {
list_for_each_safe(ptr, next, &inst->internalbufs) {
@@ -1659,7 +1750,7 @@
init_completion(
&inst->completions[SESSION_MSG_INDEX
(SESSION_RELEASE_BUFFER_DONE)]);
- rc = venus_hfi_session_release_buffers(
+ rc = hdev->session_release_buffers(
(void *) inst->session,
&buffer_info);
if (rc)
@@ -1692,6 +1783,7 @@
int rc = 0;
unsigned long flags;
struct msm_vidc_core *core;
+ struct hfi_device *hdev;
if (!inst) {
dprintk(VIDC_ERR,
"Invalid instance pointer = %p\n", inst);
@@ -1703,6 +1795,11 @@
"Invalid core pointer = %p\n", core);
return -EINVAL;
}
+ hdev = core->device;
+ if (!hdev) {
+ dprintk(VIDC_ERR, "Invalid device pointer = %p\n", hdev);
+ return -EINVAL;
+ }
spin_lock_irqsave(&inst->lock, flags);
if (!list_empty(&inst->persistbufs)) {
list_for_each_safe(ptr, next, &inst->persistbufs) {
@@ -1719,7 +1816,7 @@
init_completion(
&inst->completions[SESSION_MSG_INDEX
(SESSION_RELEASE_BUFFER_DONE)]);
- rc = venus_hfi_session_release_buffers(
+ rc = hdev->session_release_buffers(
(void *) inst->session,
&buffer_info);
if (rc)
@@ -1747,17 +1844,25 @@
enum hal_property ptype, void *pdata)
{
int rc = 0;
+ struct hfi_device *hdev;
if (!inst) {
dprintk(VIDC_ERR, "Invalid input: %p\n", inst);
return -EINVAL;
}
+
+ if (!inst->core || !inst->core->device) {
+ dprintk(VIDC_ERR, "%s invalid parameters", __func__);
+ return -EINVAL;
+ }
+ hdev = inst->core->device;
+
mutex_lock(&inst->sync_lock);
if (inst->state < MSM_VIDC_OPEN_DONE || inst->state >= MSM_VIDC_CLOSE) {
dprintk(VIDC_ERR, "Not in proper state to set property\n");
rc = -EAGAIN;
goto exit;
}
- rc = venus_hfi_session_set_property((void *)inst->session,
+ rc = hdev->session_set_property((void *)inst->session,
ptype, pdata);
if (rc)
dprintk(VIDC_ERR, "Failed to set hal property for framesize\n");
@@ -1777,12 +1882,15 @@
unsigned long smem_flags = 0;
struct hal_buffer_requirements *scratch_buf;
int i;
- struct venus_hfi_device *device;
+ struct hfi_device *hdev;
- if (!inst || !inst->core || !inst->core->device)
+ if (!inst || !inst->core || !inst->core->device) {
+ dprintk(VIDC_ERR, "%s invalid parameters", __func__);
return -EINVAL;
+ }
- device = inst->core->device;
+ hdev = inst->core->device;
+
scratch_buf =
&inst->buff_req.buffer[HAL_BUFFER_INTERNAL_SCRATCH];
dprintk(VIDC_DBG,
@@ -1792,10 +1900,10 @@
if (msm_comm_release_scratch_buffers(inst))
dprintk(VIDC_WARN, "Failed to release scratch buffers\n");
if (inst->mode == VIDC_SECURE) {
- domain = venus_hfi_get_domain(device, CP_MAP);
+ domain = hdev->get_domain(hdev->hfi_device_data, CP_MAP);
smem_flags |= SMEM_SECURE;
} else
- domain = venus_hfi_get_domain(device, NS_MAP);
+ domain = hdev->get_domain(hdev->hfi_device_data, NS_MAP);
if (scratch_buf->buffer_size) {
for (i = 0; i < scratch_buf->buffer_count_actual;
@@ -1822,7 +1930,7 @@
buffer_info.align_device_addr = handle->device_addr;
dprintk(VIDC_DBG, "Scratch buffer address: %x",
buffer_info.align_device_addr);
- rc = venus_hfi_session_set_buffers(
+ rc = hdev->session_set_buffers(
(void *) inst->session, &buffer_info);
if (rc) {
dprintk(VIDC_ERR,
@@ -1854,12 +1962,14 @@
int domain;
struct hal_buffer_requirements *persist_buf;
int i;
- struct venus_hfi_device *device;
+ struct hfi_device *hdev;
- if (!inst || !inst->core || !inst->core->device)
+ if (!inst || !inst->core || !inst->core->device) {
+ dprintk(VIDC_ERR, "%s invalid parameters", __func__);
return -EINVAL;
+ }
- device = inst->core->device;
+ hdev = inst->core->device;
persist_buf =
&inst->buff_req.buffer[HAL_BUFFER_INTERNAL_PERSIST];
@@ -1874,10 +1984,10 @@
}
if (inst->mode == VIDC_SECURE) {
- domain = venus_hfi_get_domain(device, CP_MAP);
+ domain = hdev->get_domain(hdev->hfi_device_data, CP_MAP);
flags |= SMEM_SECURE;
} else
- domain = venus_hfi_get_domain(device, NS_MAP);
+ domain = hdev->get_domain(hdev->hfi_device_data, NS_MAP);
if (persist_buf->buffer_size) {
for (i = 0; i < persist_buf->buffer_count_actual; i++) {
@@ -1903,7 +2013,7 @@
buffer_info.align_device_addr = handle->device_addr;
dprintk(VIDC_DBG, "Persist buffer address: %x",
buffer_info.align_device_addr);
- rc = venus_hfi_session_set_buffers(
+ rc = hdev->session_set_buffers(
(void *) inst->session, &buffer_info);
if (rc) {
dprintk(VIDC_ERR,
@@ -1977,6 +2087,7 @@
struct vb2_buf_entry *temp;
struct mutex *lock;
struct msm_vidc_core *core;
+ struct hfi_device *hdev;
if (!inst) {
dprintk(VIDC_ERR,
"Invalid instance pointer = %p\n", inst);
@@ -1988,12 +2099,17 @@
"Invalid core pointer = %p\n", core);
return -EINVAL;
}
+ hdev = core->device;
+ if (!hdev) {
+ dprintk(VIDC_ERR, "Invalid device pointer = %p", hdev);
+ return -EINVAL;
+ }
ip_flush = flags & V4L2_QCOM_CMD_FLUSH_OUTPUT;
op_flush = flags & V4L2_QCOM_CMD_FLUSH_CAPTURE;
if (ip_flush && !op_flush) {
- dprintk(VIDC_WARN, "Input only flush not supported\n");
+ dprintk(VIDC_INFO, "Input only flush not supported\n");
return 0;
}
if (inst->state == MSM_VIDC_CORE_INVALID ||
@@ -2014,7 +2130,7 @@
dprintk(VIDC_WARN,
"FLUSH BUG: Pending q not empty! It should be empty\n");
}
- rc = venus_hfi_session_flush(inst->session,
+ rc = hdev->session_flush(inst->session,
HAL_FLUSH_OUTPUT);
} else {
if (!list_empty(&inst->pendingq)) {
@@ -2035,7 +2151,7 @@
kfree(temp);
}
}
- rc = venus_hfi_session_flush(inst->session,
+ rc = hdev->session_flush(inst->session,
HAL_FLUSH_ALL);
}
mutex_unlock(&inst->sync_lock);
@@ -2099,3 +2215,18 @@
}
return ret;
};
+
+int msm_vidc_trigger_ssr(struct msm_vidc_core *core,
+ enum hal_ssr_trigger_type type)
+{
+ int rc = 0;
+ struct hfi_device *hdev;
+ if (!core && !core->device) {
+ dprintk(VIDC_WARN, "Invalid parameters: %p\n", core);
+ return -EINVAL;
+ }
+ hdev = core->device;
+ if (core->state == VIDC_CORE_INIT_DONE)
+ rc = hdev->core_trigger_ssr(hdev->hfi_device_data, type);
+ return rc;
+}
diff --git a/drivers/media/video/msm_vidc/msm_vidc_debug.c b/drivers/media/video/msm_vidc/msm_vidc_debug.c
index bdf146e..0a2075d 100644
--- a/drivers/media/video/msm_vidc/msm_vidc_debug.c
+++ b/drivers/media/video/msm_vidc/msm_vidc_debug.c
@@ -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
@@ -12,11 +12,12 @@
*/
#include "msm_vidc_debug.h"
-#include "venus_hfi.h"
+#include "vidc_hfi_api.h"
#define MAX_DBG_BUF_SIZE 4096
int msm_vidc_debug = 0x3;
int msm_fw_debug = 0x18;
+int msm_fw_debug_mode = 0x1;
struct debug_buffer {
char ptr[MAX_DBG_BUF_SIZE];
@@ -53,22 +54,26 @@
size_t count, loff_t *ppos)
{
struct msm_vidc_core *core = file->private_data;
- struct venus_hfi_device *device;
+ struct hfi_device *hdev;
int i = 0;
if (!core || !core->device) {
dprintk(VIDC_ERR, "Invalid params, core: %p\n", core);
return 0;
}
- device = core->device;
+ hdev = core->device;
INIT_DBG_BUF(dbg_buf);
write_str(&dbg_buf, "===============================\n");
write_str(&dbg_buf, "CORE %d: 0x%p\n", core->id, core);
write_str(&dbg_buf, "===============================\n");
write_str(&dbg_buf, "state: %d\n", core->state);
- write_str(&dbg_buf, "base addr: 0x%x\n", device->base_addr);
- write_str(&dbg_buf, "register_base: 0x%x\n", device->register_base);
- write_str(&dbg_buf, "register_size: %u\n", device->register_size);
- write_str(&dbg_buf, "irq: %u\n", device->irq);
+ write_str(&dbg_buf, "base addr: 0x%x\n",
+ hdev->get_fw_info(hdev->hfi_device_data, FW_BASE_ADDRESS));
+ write_str(&dbg_buf, "register_base: 0x%x\n",
+ hdev->get_fw_info(hdev->hfi_device_data, FW_REGISTER_BASE));
+ write_str(&dbg_buf, "register_size: %u\n",
+ hdev->get_fw_info(hdev->hfi_device_data, FW_REGISTER_SIZE));
+ write_str(&dbg_buf, "irq: %u\n",
+ hdev->get_fw_info(hdev->hfi_device_data, FW_IRQ));
for (i = SYS_MSG_START; i < SYS_MSG_END; i++) {
write_str(&dbg_buf, "completions[%d]: %s\n", i,
completion_done(&core->completions[SYS_MSG_INDEX(i)]) ?
@@ -83,6 +88,33 @@
.read = core_info_read,
};
+static int trigger_ssr_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+static ssize_t trigger_ssr_write(struct file *filp, const char __user *buf,
+ size_t count, loff_t *ppos) {
+ u32 ssr_trigger_val;
+ int rc;
+ struct msm_vidc_core *core = filp->private_data;
+ rc = sscanf(buf, "%d", &ssr_trigger_val);
+ if (rc < 0) {
+ dprintk(VIDC_WARN, "returning error err %d\n", rc);
+ rc = -EINVAL;
+ } else {
+ msm_vidc_trigger_ssr(core, ssr_trigger_val);
+ rc = count;
+ }
+ return rc;
+}
+
+static const struct file_operations ssr_fops = {
+ .open = trigger_ssr_open,
+ .write = trigger_ssr_write,
+};
+
struct dentry *msm_vidc_debugfs_init_core(struct msm_vidc_core *core,
struct dentry *parent)
{
@@ -113,6 +145,16 @@
dprintk(VIDC_ERR, "debugfs_create_file: fail\n");
goto failed_create_dir;
}
+ if (!debugfs_create_file("trigger_ssr", S_IWUSR,
+ dir, core, &ssr_fops)) {
+ dprintk(VIDC_ERR, "debugfs_create_file: fail\n");
+ goto failed_create_dir;
+ }
+ if (!debugfs_create_u32("fw_debug_mode", S_IRUGO | S_IWUSR,
+ parent, &msm_fw_debug_mode)) {
+ dprintk(VIDC_ERR, "debugfs_create_file: fail\n");
+ goto failed_create_dir;
+ }
failed_create_dir:
return dir;
}
@@ -161,6 +203,10 @@
completion_done(&inst->completions[SESSION_MSG_INDEX(i)]) ?
"pending" : "done");
}
+ write_str(&dbg_buf, "ETB Count: %d\n", inst->count.etb);
+ write_str(&dbg_buf, "EBD Count: %d\n", inst->count.ebd);
+ write_str(&dbg_buf, "FTB Count: %d\n", inst->count.ftb);
+ write_str(&dbg_buf, "FBD Count: %d\n", inst->count.fbd);
return simple_read_from_buffer(buf, count, ppos,
dbg_buf.ptr, dbg_buf.filled_size);
}
@@ -209,8 +255,12 @@
break;
case MSM_VIDC_DEBUGFS_EVENT_EBD:
inst->count.ebd++;
- if (inst->count.ebd && inst->count.ebd == inst->count.etb)
+ if (inst->count.ebd && inst->count.ebd == inst->count.etb) {
toc(inst, FRAME_PROCESSING);
+ dprintk(VIDC_PROF, "EBD: FW needs input buffers\n");
+ }
+ if (inst->count.ftb == inst->count.fbd)
+ dprintk(VIDC_PROF, "EBD: FW needs output buffers\n");
break;
case MSM_VIDC_DEBUGFS_EVENT_FTB: {
inst->count.ftb++;
@@ -222,8 +272,12 @@
break;
case MSM_VIDC_DEBUGFS_EVENT_FBD:
inst->debug.samples++;
- if (inst->count.ebd && inst->count.fbd == inst->count.ftb)
+ if (inst->count.ebd && inst->count.fbd == inst->count.ftb) {
toc(inst, FRAME_PROCESSING);
+ dprintk(VIDC_PROF, "FBD: FW needs output buffers\n");
+ }
+ if (inst->count.etb == inst->count.ebd)
+ dprintk(VIDC_PROF, "FBD: FW needs input buffers\n");
break;
default:
dprintk(VIDC_ERR, "Invalid state in debugfs: %d\n", e);
diff --git a/drivers/media/video/msm_vidc/msm_vidc_debug.h b/drivers/media/video/msm_vidc/msm_vidc_debug.h
index b3bb6e1..07568ef 100644
--- a/drivers/media/video/msm_vidc/msm_vidc_debug.h
+++ b/drivers/media/video/msm_vidc/msm_vidc_debug.h
@@ -43,6 +43,7 @@
extern int msm_vidc_debug;
extern int msm_fw_debug;
+extern int msm_fw_debug_mode;
#define dprintk(__level, __fmt, arg...) \
do { \
diff --git a/drivers/media/video/msm_vidc/msm_vidc_internal.h b/drivers/media/video/msm_vidc/msm_vidc_internal.h
index 14cef1e..16bf753 100644
--- a/drivers/media/video/msm_vidc/msm_vidc_internal.h
+++ b/drivers/media/video/msm_vidc/msm_vidc_internal.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
@@ -32,6 +32,7 @@
#include <media/msm_media_info.h>
#include "vidc_hfi_api.h"
+#include "vidc_hfi_api.h"
#define MSM_VIDC_DRV_NAME "msm_vidc_driver"
#define MSM_VIDC_VERSION KERNEL_VERSION(0, 0, 1);
@@ -177,6 +178,7 @@
struct dentry *debugfs_root;
enum vidc_core_state state;
struct completion completions[SYS_MSG_END - SYS_MSG_START + 1];
+ enum msm_vidc_hfi_type hfi_type;
};
struct msm_vidc_inst {
@@ -235,6 +237,6 @@
};
void handle_cmd_response(enum command_response cmd, void *data);
-int msm_vidc_ocmem_notify_handler(struct notifier_block *this,
- unsigned long event, void *data);
+int msm_vidc_trigger_ssr(struct msm_vidc_core *core,
+ enum hal_ssr_trigger_type type);
#endif
diff --git a/drivers/media/video/msm_vidc/venus_hfi.c b/drivers/media/video/msm_vidc/venus_hfi.c
index 8f0902f..bffba30 100644
--- a/drivers/media/video/msm_vidc/venus_hfi.c
+++ b/drivers/media/video/msm_vidc/venus_hfi.c
@@ -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
@@ -37,7 +37,16 @@
#define SHARED_QSIZE 0x1000000
-struct hal_device_data hal_ctxt;
+static const u32 venus_qdss_entries[][2] = {
+ {0xFC307000, 0x1000},
+ {0xFC322000, 0x1000},
+ {0xFC319000, 0x1000},
+ {0xFC31A000, 0x1000},
+ {0xFC31B000, 0x1000},
+ {0xFC321000, 0x1000},
+ {0xFA180000, 0x1000},
+ {0xFA181000, 0x1000},
+};
static struct msm_bus_vectors enc_ocmem_init_vectors[] = {
{
@@ -840,7 +849,7 @@
return result;
}
-int venus_hfi_iface_msgq_read(struct venus_hfi_device *device, void *pkt)
+static int venus_hfi_iface_msgq_read(struct venus_hfi_device *device, void *pkt)
{
u32 tx_req_is_set = 0;
int rc = 0;
@@ -875,7 +884,7 @@
return rc;
}
-int venus_hfi_iface_dbgq_read(struct venus_hfi_device *device, void *pkt)
+static int venus_hfi_iface_dbgq_read(struct venus_hfi_device *device, void *pkt)
{
u32 tx_req_is_set = 0;
int rc = 0;
@@ -928,8 +937,28 @@
static void venus_hfi_interface_queues_release(struct venus_hfi_device *device)
{
int i;
+ struct hfi_mem_map_table *qdss;
+ struct hfi_mem_map *mem_map;
+ int num_entries = sizeof(venus_qdss_entries)/(2 * sizeof(u32));
- venus_hfi_free(device->hal_client, device->mem_addr.mem_data);
+ if (device->qdss.mem_data) {
+ qdss = (struct hfi_mem_map_table *)
+ device->qdss.align_virtual_addr;
+ qdss->mem_map_num_entries = num_entries;
+ qdss->mem_map_table_base_addr =
+ (u32 *)((u32)device->qdss.align_device_addr +
+ sizeof(struct hfi_mem_map_table));
+ mem_map = (struct hfi_mem_map *)(qdss + 1);
+ for (i = 0; i < num_entries; i++) {
+ msm_iommu_unmap_contig_buffer(
+ (unsigned long)(mem_map[i].virtual_addr),
+ device->resources.io_map[NS_MAP].domain,
+ 1, SZ_4K);
+ }
+ venus_hfi_free(device->hal_client, device->qdss.mem_data);
+ }
+ venus_hfi_free(device->hal_client, device->iface_q_table.mem_data);
+ venus_hfi_free(device->hal_client, device->sfr.mem_data);
for (i = 0; i < VIDC_IFACEQ_NUMQ; i++) {
device->iface_queues[i].q_hdr = NULL;
@@ -952,6 +981,41 @@
msm_smem_delete_client(device->hal_client);
device->hal_client = NULL;
}
+static int venus_hfi_get_qdss_iommu_virtual_addr(struct hfi_mem_map *mem_map,
+ int domain)
+{
+ int i;
+ int rc = 0;
+ unsigned long iova = 0;
+ int num_entries = sizeof(venus_qdss_entries)/(2 * sizeof(u32));
+
+ for (i = 0; i < num_entries; i++) {
+ rc = msm_iommu_map_contig_buffer(venus_qdss_entries[i][0],
+ domain, 1 , venus_qdss_entries[i][1],
+ SZ_4K, 0, &iova);
+ if (rc) {
+ dprintk(VIDC_ERR,
+ "IOMMU QDSS mapping failed for addr 0x%x",
+ venus_qdss_entries[i][0]);
+ rc = -ENOMEM;
+ break;
+ }
+ mem_map[i].virtual_addr = (u32) iova;
+ mem_map[i].physical_addr = venus_qdss_entries[i][0];
+ mem_map[i].size = venus_qdss_entries[i][1];
+ mem_map[i].attr = 0x0;
+ }
+ if (i < num_entries) {
+ dprintk(VIDC_ERR,
+ "IOMMU QDSS mapping failed, Freeing entries %d", i);
+ for (--i; i >= 0; i--) {
+ msm_iommu_unmap_contig_buffer(
+ (unsigned long)(mem_map[i].virtual_addr),
+ domain, 1, SZ_4K);
+ }
+ }
+ return rc;
+}
static int venus_hfi_interface_queues_init(struct venus_hfi_device *dev,
int domain)
@@ -960,24 +1024,25 @@
struct hfi_queue_header *q_hdr;
u8 i;
int rc = 0;
+ struct hfi_mem_map_table *qdss;
+ struct hfi_mem_map *mem_map;
struct vidc_iface_q_info *iface_q;
struct hfi_sfr_struct *vsfr;
struct vidc_mem_addr *mem_addr;
int offset = 0;
- int size_1m = 1024 * 1024;
- int uc_size = (UC_SIZE + size_1m - 1) & (~(size_1m - 1));
+ int num_entries = sizeof(venus_qdss_entries)/(2 * sizeof(u32));
mem_addr = &dev->mem_addr;
rc = venus_hfi_alloc((void *) mem_addr,
- dev->hal_client, uc_size, 1,
+ dev->hal_client, QUEUE_SIZE, 1,
0, domain);
if (rc) {
dprintk(VIDC_ERR, "iface_q_table_alloc_fail");
- return -ENOMEM;
+ goto fail_alloc_queue;
}
dev->iface_q_table.align_virtual_addr = mem_addr->align_virtual_addr;
dev->iface_q_table.align_device_addr = mem_addr->align_device_addr;
dev->iface_q_table.mem_size = VIDC_IFACEQ_TABLE_SIZE;
- dev->iface_q_table.mem_data = NULL;
+ dev->iface_q_table.mem_data = mem_addr->mem_data;
offset += dev->iface_q_table.mem_size;
for (i = 0; i < VIDC_IFACEQ_NUMQ; i++) {
@@ -994,18 +1059,31 @@
venus_hfi_set_queue_hdr_defaults(iface_q->q_hdr);
}
- dev->qdss.align_device_addr = mem_addr->align_device_addr + offset;
- dev->qdss.align_virtual_addr = mem_addr->align_virtual_addr + offset;
- dev->qdss.mem_size = QDSS_SIZE;
- dev->qdss.mem_data = NULL;
- offset += dev->qdss.mem_size;
-
- dev->sfr.align_device_addr = mem_addr->align_device_addr + offset;
- dev->sfr.align_virtual_addr = mem_addr->align_virtual_addr + offset;
- dev->sfr.mem_size = SFR_SIZE;
- dev->sfr.mem_data = NULL;
- offset += dev->sfr.mem_size;
-
+ rc = venus_hfi_alloc((void *) mem_addr,
+ dev->hal_client, QDSS_SIZE, 1,
+ 0, domain);
+ if (rc) {
+ dprintk(VIDC_WARN,
+ "qdss_alloc_fail: QDSS messages logging will not work");
+ dev->qdss.align_device_addr = NULL;
+ } else {
+ dev->qdss.align_device_addr = mem_addr->align_device_addr;
+ dev->qdss.align_virtual_addr = mem_addr->align_virtual_addr;
+ dev->qdss.mem_size = QDSS_SIZE;
+ dev->qdss.mem_data = mem_addr->mem_data;
+ }
+ rc = venus_hfi_alloc((void *) mem_addr,
+ dev->hal_client, SFR_SIZE, 1,
+ 0, domain);
+ if (rc) {
+ dprintk(VIDC_WARN, "sfr_alloc_fail: SFR not will work");
+ dev->sfr.align_device_addr = NULL;
+ } else {
+ dev->sfr.align_device_addr = mem_addr->align_device_addr;
+ dev->sfr.align_virtual_addr = mem_addr->align_virtual_addr;
+ dev->sfr.mem_size = SFR_SIZE;
+ dev->sfr.mem_data = mem_addr->mem_data;
+ }
q_tbl_hdr = (struct hfi_queue_table_header *)
dev->iface_q_table.align_virtual_addr;
q_tbl_hdr->qtbl_version = 0;
@@ -1037,9 +1115,9 @@
venus_hfi_write_register(dev->hal_data->register_base_addr,
VIDC_UC_REGION_ADDR,
- (u32) mem_addr->align_device_addr, 0);
+ (u32) dev->iface_q_table.align_device_addr, 0);
venus_hfi_write_register(dev->hal_data->register_base_addr,
- VIDC_UC_REGION_SIZE, mem_addr->mem_size, 0);
+ VIDC_UC_REGION_SIZE, SHARED_QSIZE, 0);
venus_hfi_write_register(dev->hal_data->register_base_addr,
VIDC_CPU_CS_SCIACMDARG2,
(u32) dev->iface_q_table.align_device_addr,
@@ -1047,16 +1125,33 @@
venus_hfi_write_register(dev->hal_data->register_base_addr,
VIDC_CPU_CS_SCIACMDARG1, 0x01,
dev->iface_q_table.align_virtual_addr);
- venus_hfi_write_register(dev->hal_data->register_base_addr,
+
+ qdss = (struct hfi_mem_map_table *) dev->qdss.align_virtual_addr;
+ qdss->mem_map_num_entries = num_entries;
+ qdss->mem_map_table_base_addr =
+ (u32 *) ((u32)dev->qdss.align_device_addr +
+ sizeof(struct hfi_mem_map_table));
+ mem_map = (struct hfi_mem_map *)(qdss + 1);
+ rc = venus_hfi_get_qdss_iommu_virtual_addr(mem_map, domain);
+ if (rc) {
+ dprintk(VIDC_ERR,
+ "IOMMU mapping failed, Freeing qdss memdata");
+ venus_hfi_free(dev->hal_client, dev->qdss.mem_data);
+ dev->qdss.mem_data = NULL;
+ }
+ if (!IS_ERR_OR_NULL(dev->qdss.align_device_addr))
+ venus_hfi_write_register(dev->hal_data->register_base_addr,
VIDC_MMAP_ADDR,
(u32) dev->qdss.align_device_addr, 0);
vsfr = (struct hfi_sfr_struct *) dev->sfr.align_virtual_addr;
vsfr->bufSize = SFR_SIZE;
-
- venus_hfi_write_register(dev->hal_data->register_base_addr,
+ if (!IS_ERR_OR_NULL(dev->sfr.align_device_addr))
+ venus_hfi_write_register(dev->hal_data->register_base_addr,
VIDC_SFR_ADDR, (u32)dev->sfr.align_device_addr , 0);
return 0;
+fail_alloc_queue:
+ return -ENOMEM;
}
static int venus_hfi_core_start_cpu(struct venus_hfi_device *device)
@@ -1141,12 +1236,14 @@
hfi = (struct hfi_debug_config *) &pkt->rg_property_data[1];
hfi->debug_config = debug;
hfi->debug_mode = HFI_DEBUG_MODE_QUEUE;
+ if (msm_fw_debug_mode <= HFI_DEBUG_MODE_QDSS)
+ hfi->debug_mode = msm_fw_debug_mode;
if (venus_hfi_iface_cmdq_write(device, pkt))
return -ENOTEMPTY;
return 0;
}
-int venus_hfi_core_init(void *device)
+static int venus_hfi_core_init(void *device)
{
struct hfi_cmd_sys_init_packet pkt;
int rc = 0;
@@ -1214,7 +1311,7 @@
return rc;
}
-int venus_hfi_core_release(void *device)
+static int venus_hfi_core_release(void *device)
{
struct venus_hfi_device *dev;
if (device) {
@@ -1226,7 +1323,9 @@
if (dev->hal_client) {
venus_hfi_write_register(dev->hal_data->register_base_addr,
VIDC_CPU_CS_SCIACMDARG3, 0, 0);
- disable_irq_nosync(dev->hal_data->irq);
+ if (!(dev->intr_status & VIDC_WRAPPER_INTR_STATUS_A2HWD_BMSK))
+ disable_irq_nosync(dev->hal_data->irq);
+ dev->intr_status = 0;
venus_hfi_interface_queues_release(dev);
}
dprintk(VIDC_INFO, "HAL exited\n");
@@ -1288,7 +1387,7 @@
dprintk(VIDC_DBG, "Cleared WRAPPER/A2H interrupt");
}
-int venus_hfi_core_set_resource(void *device,
+static int venus_hfi_core_set_resource(void *device,
struct vidc_resource_hdr *resource_hdr, void *resource_value)
{
struct hfi_cmd_sys_set_resource_packet *pkt;
@@ -1318,7 +1417,7 @@
return rc;
}
-int venus_hfi_core_release_resource(void *device,
+static int venus_hfi_core_release_resource(void *device,
struct vidc_resource_hdr *resource_hdr)
{
struct hfi_cmd_sys_release_resource_packet pkt;
@@ -1345,7 +1444,7 @@
return rc;
}
-int venus_hfi_core_ping(void *device)
+static int venus_hfi_core_ping(void *device)
{
struct hfi_cmd_sys_ping_packet pkt;
int rc = 0;
@@ -1371,8 +1470,35 @@
return rc;
}
-int venus_hfi_session_set_property(void *sess,
- enum hal_property ptype, void *pdata)
+static int venus_hfi_core_trigger_ssr(void *device,
+ enum hal_ssr_trigger_type type)
+{
+ struct hfi_cmd_sys_test_ssr_packet pkt;
+ int rc = 0;
+ struct venus_hfi_device *dev;
+
+ if (device) {
+ dev = device;
+ } else {
+ dprintk(VIDC_ERR, "invalid device");
+ return -ENODEV;
+ }
+
+ rc = create_pkt_ssr_cmd(type, &pkt);
+ if (rc) {
+ dprintk(VIDC_ERR, "core_ping: failed to create packet");
+ goto err_create_pkt;
+ }
+
+ if (venus_hfi_iface_cmdq_write(dev, &pkt))
+ rc = -ENOTEMPTY;
+
+err_create_pkt:
+ return rc;
+}
+
+static int venus_hfi_session_set_property(void *sess,
+ enum hal_property ptype, void *pdata)
{
u8 packet[VIDC_IFACEQ_VAR_LARGE_PKT_SIZE];
struct hfi_cmd_session_set_property_packet *pkt =
@@ -1401,8 +1527,8 @@
return rc;
}
-int venus_hfi_session_get_property(void *sess,
- enum hal_property ptype, void *pdata)
+static int venus_hfi_session_get_property(void *sess,
+ enum hal_property ptype, void *pdata)
{
struct hal_session *session;
@@ -1524,8 +1650,8 @@
return 0;
}
-void *venus_hfi_session_init(void *device, u32 session_id,
- enum hal_domain session_type, enum hal_video_codec codec_type)
+static void *venus_hfi_session_init(void *device, u32 session_id,
+ enum hal_domain session_type, enum hal_video_codec codec_type)
{
struct hfi_cmd_sys_session_init_packet pkt;
struct hal_session *new_session;
@@ -1592,20 +1718,20 @@
return rc;
}
-int venus_hfi_session_end(void *session)
+static int venus_hfi_session_end(void *session)
{
return venus_hfi_send_session_cmd(session,
HFI_CMD_SYS_SESSION_END);
}
-int venus_hfi_session_abort(void *session)
+static int venus_hfi_session_abort(void *session)
{
return venus_hfi_send_session_cmd(session,
HFI_CMD_SYS_SESSION_ABORT);
}
-int venus_hfi_session_set_buffers(void *sess,
- struct vidc_buffer_addr_info *buffer_info)
+static int venus_hfi_session_set_buffers(void *sess,
+ struct vidc_buffer_addr_info *buffer_info)
{
struct hfi_cmd_session_set_buffers_packet *pkt;
u8 packet[VIDC_IFACEQ_VAR_LARGE_PKT_SIZE];
@@ -1638,8 +1764,8 @@
return rc;
}
-int venus_hfi_session_release_buffers(void *sess,
- struct vidc_buffer_addr_info *buffer_info)
+static int venus_hfi_session_release_buffers(void *sess,
+ struct vidc_buffer_addr_info *buffer_info)
{
struct hfi_cmd_session_release_buffer_packet *pkt;
u8 packet[VIDC_IFACEQ_VAR_LARGE_PKT_SIZE];
@@ -1672,43 +1798,44 @@
return rc;
}
-int venus_hfi_session_load_res(void *sess)
+static int venus_hfi_session_load_res(void *sess)
{
return venus_hfi_send_session_cmd(sess,
HFI_CMD_SESSION_LOAD_RESOURCES);
}
-int venus_hfi_session_release_res(void *sess)
+static int venus_hfi_session_release_res(void *sess)
{
return venus_hfi_send_session_cmd(sess,
HFI_CMD_SESSION_RELEASE_RESOURCES);
}
-int venus_hfi_session_start(void *sess)
+static int venus_hfi_session_start(void *sess)
{
return venus_hfi_send_session_cmd(sess,
HFI_CMD_SESSION_START);
}
-int venus_hfi_session_stop(void *sess)
+static int venus_hfi_session_stop(void *sess)
{
return venus_hfi_send_session_cmd(sess,
HFI_CMD_SESSION_STOP);
}
-int venus_hfi_session_suspend(void *sess)
+static int venus_hfi_session_suspend(void *sess)
{
return venus_hfi_send_session_cmd(sess,
HFI_CMD_SESSION_SUSPEND);
}
-int venus_hfi_session_resume(void *sess)
+static int venus_hfi_session_resume(void *sess)
{
return venus_hfi_send_session_cmd(sess,
HFI_CMD_SESSION_RESUME);
}
-int venus_hfi_session_etb(void *sess, struct vidc_frame_data *input_frame)
+static int venus_hfi_session_etb(void *sess,
+ struct vidc_frame_data *input_frame)
{
int rc = 0;
struct hal_session *session;
@@ -1752,8 +1879,8 @@
return rc;
}
-int venus_hfi_session_ftb(void *sess,
- struct vidc_frame_data *output_frame)
+static int venus_hfi_session_ftb(void *sess,
+ struct vidc_frame_data *output_frame)
{
struct hfi_cmd_session_fill_buffer_packet pkt;
int rc = 0;
@@ -1778,8 +1905,8 @@
return rc;
}
-int venus_hfi_session_parse_seq_hdr(void *sess,
- struct vidc_seq_hdr *seq_hdr)
+static int venus_hfi_session_parse_seq_hdr(void *sess,
+ struct vidc_seq_hdr *seq_hdr)
{
struct hfi_cmd_session_parse_sequence_header_packet *pkt;
int rc = 0;
@@ -1809,8 +1936,8 @@
return rc;
}
-int venus_hfi_session_get_seq_hdr(void *sess,
- struct vidc_seq_hdr *seq_hdr)
+static int venus_hfi_session_get_seq_hdr(void *sess,
+ struct vidc_seq_hdr *seq_hdr)
{
struct hfi_cmd_session_get_sequence_header_packet *pkt;
int rc = 0;
@@ -1837,7 +1964,7 @@
return rc;
}
-int venus_hfi_session_get_buf_req(void *sess)
+static int venus_hfi_session_get_buf_req(void *sess)
{
struct hfi_cmd_session_get_property_packet pkt;
int rc = 0;
@@ -1862,7 +1989,7 @@
return rc;
}
-int venus_hfi_session_flush(void *sess, enum hal_flush flush_mode)
+static int venus_hfi_session_flush(void *sess, enum hal_flush flush_mode)
{
struct hfi_cmd_session_flush_packet pkt;
int rc = 0;
@@ -1937,6 +2064,43 @@
return -EINVAL;
}
+static void venus_hfi_process_sys_watchdog_timeout(
+ struct venus_hfi_device *device)
+{
+ struct msm_vidc_cb_cmd_done cmd_done;
+ memset(&cmd_done, 0, sizeof(struct msm_vidc_cb_cmd_done));
+ cmd_done.device_id = device->device_id;
+ device->callback(SYS_WATCHDOG_TIMEOUT, &cmd_done);
+}
+
+static void venus_hfi_response_handler(struct venus_hfi_device *device)
+{
+ u8 packet[VIDC_IFACEQ_MED_PKT_SIZE];
+
+ dprintk(VIDC_INFO, "#####venus_hfi_response_handler#####\n");
+ if (device) {
+ if ((device->intr_status &
+ VIDC_WRAPPER_INTR_CLEAR_A2HWD_BMSK)) {
+ dprintk(VIDC_ERR, "Received: Watchdog timeout %s",
+ __func__);
+ venus_hfi_process_sys_watchdog_timeout(device);
+ }
+
+ while (!venus_hfi_iface_msgq_read(device, packet)) {
+ hfi_process_msg_packet(device->callback,
+ device->device_id,
+ (struct vidc_hal_msg_pkt_hdr *) packet);
+ }
+ while (!venus_hfi_iface_dbgq_read(device, packet)) {
+ struct hfi_msg_sys_debug_packet *pkt =
+ (struct hfi_msg_sys_debug_packet *) packet;
+ dprintk(VIDC_FW, "FW-SAYS: %s", pkt->rg_msg_data);
+ }
+ } else {
+ dprintk(VIDC_ERR, "SPURIOUS_INTERRUPT");
+ }
+}
+
static void venus_hfi_core_work_handler(struct work_struct *work)
{
struct venus_hfi_device *device = list_first_entry(
@@ -1949,8 +2113,9 @@
return;
}
venus_hfi_core_clear_interrupt(device);
- hfi_response_handler(device);
- enable_irq(device->hal_data->irq);
+ venus_hfi_response_handler(device);
+ if (!(device->intr_status & VIDC_WRAPPER_INTR_STATUS_A2HWD_BMSK))
+ enable_irq(device->hal_data->irq);
}
static DECLARE_WORK(venus_hfi_work, venus_hfi_core_work_handler);
@@ -2153,13 +2318,14 @@
break;
ret = table[i].freq;
}
- dprintk(VIDC_DBG, "Required clock rate = %lu\n", ret);
+ dprintk(VIDC_PROF, "Required clock rate = %lu\n", ret);
return ret;
}
-int venus_hfi_scale_clocks(struct venus_hfi_device *device, int load)
+static int venus_hfi_scale_clocks(void *dev, int load)
{
int rc = 0;
+ struct venus_hfi_device *device = dev;
if (!device) {
dprintk(VIDC_ERR, "Invalid args: %p\n", device);
return -EINVAL;
@@ -2364,21 +2530,28 @@
static int venus_hfi_get_bus_vector(int load)
{
int num_rows = sizeof(venus_hfi_bus_table)/(sizeof(u32));
- int i;
+ int i, j;
for (i = 0; i < num_rows; i++) {
if (load <= venus_hfi_bus_table[i])
break;
}
- i++;
- dprintk(VIDC_DBG, "Required bus = %d\n", i);
- return i;
+ j = clamp(i, 0, num_rows-1) + 1;
+ dprintk(VIDC_DBG, "Required bus = %d\n", j);
+ return j;
}
-int venus_hfi_scale_bus(struct venus_hfi_device *device, int load,
+static int venus_hfi_scale_bus(void *dev, int load,
enum session_type type, enum mem_type mtype)
{
int rc = 0;
u32 handle = 0;
+ struct venus_hfi_device *device = dev;
+
+ if (!device) {
+ dprintk(VIDC_ERR, "%s invalid device handle %p",
+ __func__, device);
+ return -EINVAL;
+ }
if (mtype & DDR_MEM)
handle = device->resources.bus_info.ddr_handle[type];
@@ -2399,24 +2572,10 @@
return rc;
}
-static void venus_hfi_ocmem_init(struct venus_hfi_device *device)
-{
- struct on_chip_mem *ocmem;
-
- ocmem = &device->resources.ocmem;
- ocmem->vidc_ocmem_nb.notifier_call = msm_vidc_ocmem_notify_handler;
- ocmem->handle =
- ocmem_notifier_register(OCMEM_VIDEO, &ocmem->vidc_ocmem_nb);
- if (!ocmem->handle) {
- dprintk(VIDC_WARN, "Failed to register OCMEM notifier.");
- dprintk(VIDC_INFO, " Performance will be impacted\n");
- }
-}
-
-int venus_hfi_set_ocmem(struct venus_hfi_device *device,
- struct ocmem_buf *ocmem)
+static int venus_hfi_set_ocmem(void *dev, struct ocmem_buf *ocmem)
{
struct vidc_resource_hdr rhdr;
+ struct venus_hfi_device *device = dev;
int rc = 0;
if (!device || !ocmem) {
dprintk(VIDC_ERR, "Invalid params, core:%p, ocmem: %p\n",
@@ -2437,12 +2596,14 @@
return rc;
}
-int venus_hfi_unset_ocmem(struct venus_hfi_device *device)
+static int venus_hfi_unset_ocmem(void *dev)
{
struct vidc_resource_hdr rhdr;
+ struct venus_hfi_device *device = dev;
int rc = 0;
if (!device || !device->resources.ocmem.buf) {
- dprintk(VIDC_ERR, "Invalid params, device:%p\n", device);
+ dprintk(VIDC_ERR, "%s Invalid params, device:%p\n",
+ __func__, device);
return -EINVAL;
}
rhdr.resource_id = VIDC_RESOURCE_OCMEM;
@@ -2454,15 +2615,59 @@
return rc;
}
-int venus_hfi_alloc_ocmem(struct venus_hfi_device *device,
- unsigned long size)
+static int venus_hfi_ocmem_notify_handler(struct notifier_block *this,
+ unsigned long event, void *data)
+{
+ struct ocmem_buf *buff = data;
+ struct venus_hfi_device *device;
+ struct venus_resources *resources;
+ struct on_chip_mem *ocmem;
+ int rc = NOTIFY_DONE;
+ if (event == OCMEM_ALLOC_GROW) {
+ ocmem = container_of(this, struct on_chip_mem, vidc_ocmem_nb);
+ if (!ocmem) {
+ dprintk(VIDC_ERR, "Wrong handler passed\n");
+ rc = NOTIFY_BAD;
+ goto err_ocmem_notify;
+ }
+ resources = container_of(ocmem,
+ struct venus_resources, ocmem);
+ device = container_of(resources,
+ struct venus_hfi_device, resources);
+ if (venus_hfi_set_ocmem(device, buff)) {
+ dprintk(VIDC_ERR, "Failed to set ocmem: %d\n", rc);
+ goto err_ocmem_notify;
+ }
+ rc = NOTIFY_OK;
+ }
+
+err_ocmem_notify:
+ return rc;
+}
+
+static void venus_hfi_ocmem_init(struct venus_hfi_device *device)
+{
+ struct on_chip_mem *ocmem;
+
+ ocmem = &device->resources.ocmem;
+ ocmem->vidc_ocmem_nb.notifier_call = venus_hfi_ocmem_notify_handler;
+ ocmem->handle =
+ ocmem_notifier_register(OCMEM_VIDEO, &ocmem->vidc_ocmem_nb);
+ if (!ocmem->handle) {
+ dprintk(VIDC_WARN, "Failed to register OCMEM notifier.");
+ dprintk(VIDC_INFO, " Performance will be impacted\n");
+ }
+}
+
+static int venus_hfi_alloc_ocmem(void *dev, unsigned long size)
{
int rc = 0;
struct ocmem_buf *ocmem_buffer;
+ struct venus_hfi_device *device = dev;
if (!device || !size) {
- dprintk(VIDC_ERR,
- "Invalid param, core: %p, size: %lu\n", device, size);
+ dprintk(VIDC_ERR, "%s Invalid param, core: %p, size: %lu\n",
+ __func__, device, size);
return -EINVAL;
}
ocmem_buffer = device->resources.ocmem.buf;
@@ -2490,21 +2695,35 @@
return rc;
}
-int venus_hfi_free_ocmem(struct venus_hfi_device *device)
+static int venus_hfi_free_ocmem(void *dev)
{
+ struct venus_hfi_device *device = dev;
int rc = 0;
+ if (!device) {
+ dprintk(VIDC_ERR, "%s invalid device handle %p",
+ __func__, device);
+ return -EINVAL;
+ }
+
if (device->resources.ocmem.buf) {
rc = ocmem_free(OCMEM_VIDEO, device->resources.ocmem.buf);
if (rc)
dprintk(VIDC_ERR, "Failed to free ocmem\n");
+ device->resources.ocmem.buf = NULL;
}
- device->resources.ocmem.buf = NULL;
return rc;
}
-int venus_hfi_is_ocmem_present(struct venus_hfi_device *device)
+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;
}
@@ -2515,6 +2734,7 @@
&device->resources.ocmem.vidc_ocmem_nb);
}
+
static int venus_hfi_init_resources(struct venus_hfi_device *device,
struct platform_device *pdev)
{
@@ -2619,9 +2839,9 @@
}
}
-int venus_hfi_get_domain(struct venus_hfi_device *device,
- enum msm_vidc_io_maps iomap)
+static int venus_hfi_get_domain(void *dev, enum msm_vidc_io_maps iomap)
{
+ struct venus_hfi_device *device = dev;
if (!device || iomap < CP_MAP || iomap >= MAX_MAP) {
dprintk(VIDC_ERR, "%s: Invalid parameter: %p iomap: %d\n",
__func__, device, iomap);
@@ -2630,10 +2850,11 @@
return device->resources.io_map[iomap].domain;
}
-int venus_hfi_iommu_get_map(struct venus_hfi_device *device,
+static int venus_hfi_iommu_get_map(void *dev,
struct msm_vidc_iommu_info maps[MAX_MAP])
{
int i = 0;
+ struct venus_hfi_device *device = dev;
if (!device || !maps) {
dprintk(VIDC_ERR, "%s: Invalid param device: %p maps: %p\n",
@@ -2677,12 +2898,14 @@
return rc;
}
-int venus_hfi_load_fw(struct venus_hfi_device *device)
+static int venus_hfi_load_fw(void *dev)
{
int rc = 0;
+ struct venus_hfi_device *device = dev;
if (!device) {
- dprintk(VIDC_ERR, "Invalid paramter: %p\n", device);
+ dprintk(VIDC_ERR, "%s Invalid paramter: %p\n",
+ __func__, device);
return -EINVAL;
}
@@ -2723,10 +2946,12 @@
return rc;
}
-void venus_hfi_unload_fw(struct venus_hfi_device *device)
+static void venus_hfi_unload_fw(void *dev)
{
+ struct venus_hfi_device *device = dev;
if (!device) {
- dprintk(VIDC_ERR, "Invalid paramter: %p\n", device);
+ dprintk(VIDC_ERR, "%s Invalid paramter: %p\n",
+ __func__, device);
return;
}
if (device->resources.fw.cookie) {
@@ -2737,6 +2962,40 @@
}
}
+static int venus_hfi_get_fw_info(void *dev, enum fw_info info)
+{
+ int rc = 0;
+ struct venus_hfi_device *device = dev;
+
+ if (!device) {
+ dprintk(VIDC_ERR, "%s Invalid paramter: %p\n",
+ __func__, device);
+ return -EINVAL;
+ }
+
+ switch (info) {
+ case FW_BASE_ADDRESS:
+ rc = device->base_addr;
+ break;
+
+ case FW_REGISTER_BASE:
+ rc = device->register_base;
+ break;
+
+ case FW_REGISTER_SIZE:
+ rc = device->register_size;
+ break;
+
+ case FW_IRQ:
+ rc = device->irq;
+ break;
+
+ default:
+ dprintk(VIDC_ERR, "Invalid fw info requested");
+ }
+ return rc;
+}
+
static void *venus_hfi_add_device(u32 device_id, struct platform_device *pdev,
void (*callback) (enum command_response cmd, void *data))
{
@@ -2786,9 +3045,9 @@
return NULL;
}
-void *venus_hfi_get_device(u32 device_id,
- struct platform_device *pdev,
- void (*callback) (enum command_response cmd, void *data))
+static void *venus_hfi_get_device(u32 device_id,
+ struct platform_device *pdev,
+ hfi_cmd_response_callback callback)
{
struct venus_hfi_device *device;
int rc = 0;
@@ -2837,3 +3096,61 @@
}
}
+
+static void venus_init_hfi_callbacks(struct hfi_device *hdev)
+{
+ hdev->core_init = venus_hfi_core_init;
+ hdev->core_release = venus_hfi_core_release;
+ hdev->core_pc_prep = venus_hfi_core_pc_prep;
+ hdev->core_ping = venus_hfi_core_ping;
+ hdev->core_trigger_ssr = venus_hfi_core_trigger_ssr;
+ hdev->session_init = venus_hfi_session_init;
+ hdev->session_end = venus_hfi_session_end;
+ hdev->session_abort = venus_hfi_session_abort;
+ hdev->session_set_buffers = venus_hfi_session_set_buffers;
+ hdev->session_release_buffers = venus_hfi_session_release_buffers;
+ hdev->session_load_res = venus_hfi_session_load_res;
+ hdev->session_release_res = venus_hfi_session_release_res;
+ hdev->session_start = venus_hfi_session_start;
+ hdev->session_stop = venus_hfi_session_stop;
+ hdev->session_suspend = venus_hfi_session_suspend;
+ hdev->session_resume = venus_hfi_session_resume;
+ hdev->session_etb = venus_hfi_session_etb;
+ hdev->session_ftb = venus_hfi_session_ftb;
+ hdev->session_parse_seq_hdr = venus_hfi_session_parse_seq_hdr;
+ hdev->session_get_seq_hdr = venus_hfi_session_get_seq_hdr;
+ hdev->session_get_buf_req = venus_hfi_session_get_buf_req;
+ hdev->session_flush = venus_hfi_session_flush;
+ hdev->session_set_property = venus_hfi_session_set_property;
+ hdev->session_get_property = venus_hfi_session_get_property;
+ hdev->scale_clocks = venus_hfi_scale_clocks;
+ hdev->scale_bus = venus_hfi_scale_bus;
+ 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->get_domain = venus_hfi_get_domain;
+ hdev->iommu_get_map = venus_hfi_iommu_get_map;
+ hdev->load_fw = venus_hfi_load_fw;
+ hdev->unload_fw = venus_hfi_unload_fw;
+ hdev->get_fw_info = venus_hfi_get_fw_info;
+}
+
+int venus_hfi_initialize(struct hfi_device *hdev, u32 device_id,
+ struct platform_device *pdev, hfi_cmd_response_callback callback)
+{
+ int rc = 0;
+
+ if (!hdev || !callback) {
+ dprintk(VIDC_ERR, "Invalid params: %p %p\n", pdev, callback);
+ rc = -EINVAL;
+ goto err_venus_hfi_init;
+ }
+ hdev->hfi_device_data = venus_hfi_get_device(device_id, pdev, callback);
+
+ venus_init_hfi_callbacks(hdev);
+
+err_venus_hfi_init:
+ return rc;
+}
+
diff --git a/drivers/media/video/msm_vidc/venus_hfi.h b/drivers/media/video/msm_vidc/venus_hfi.h
index 93b9ae3..8770ace 100644
--- a/drivers/media/video/msm_vidc/venus_hfi.h
+++ b/drivers/media/video/msm_vidc/venus_hfi.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
@@ -19,10 +19,13 @@
#include <linux/platform_device.h>
#include <linux/spinlock.h>
#include <mach/ocmem.h>
-#include <media/msm_vidc.h>
+#include <mach/iommu_domains.h>
+
#include "vidc_hfi_api.h"
#include "msm_smem.h"
#include "vidc_hfi_helper.h"
+#include "vidc_hfi_api.h"
+#include "vidc_hfi.h"
#define HFI_MASK_QHDR_TX_TYPE 0xFF000000
#define HFI_MASK_QHDR_RX_TYPE 0x00FF0000
@@ -77,6 +80,18 @@
u32 qhdr_write_idx;
};
+struct hfi_mem_map_table {
+ u32 mem_map_num_entries;
+ u32 *mem_map_table_base_addr;
+};
+
+struct hfi_mem_map {
+ u32 virtual_addr;
+ u32 physical_addr;
+ u32 size;
+ u32 attr;
+};
+
#define VIDC_IFACEQ_TABLE_SIZE (sizeof(struct hfi_queue_table_header) \
+ sizeof(struct hfi_queue_header) * VIDC_IFACEQ_NUMQ)
@@ -90,8 +105,8 @@
#define QDSS_SIZE 4096
#define SFR_SIZE 4096
-#define UC_SIZE (VIDC_IFACEQ_TABLE_SIZE + \
- (VIDC_IFACEQ_QUEUE_SIZE * VIDC_IFACEQ_NUMQ) + QDSS_SIZE + SFR_SIZE)
+#define QUEUE_SIZE (VIDC_IFACEQ_TABLE_SIZE + \
+ (VIDC_IFACEQ_QUEUE_SIZE * VIDC_IFACEQ_NUMQ))
enum vidc_hw_reg {
VIDC_HWREG_CTRL_STATUS = 0x1,
@@ -106,793 +121,6 @@
VIDC_HWREG_HVI_SOFTINTEN = 0xA,
};
-#define HFI_EVENT_SESSION_SEQUENCE_CHANGED (HFI_OX_BASE + 0x3)
-#define HFI_EVENT_SESSION_PROPERTY_CHANGED (HFI_OX_BASE + 0x4)
-
-#define HFI_EVENT_DATA_SEQUENCE_CHANGED_SUFFICIENT_BUFFER_RESOURCES \
- (HFI_OX_BASE + 0x1)
-#define HFI_EVENT_DATA_SEQUENCE_CHANGED_INSUFFICIENT_BUFFER_RESOURCES \
- (HFI_OX_BASE + 0x2)
-
-#define HFI_BUFFERFLAG_EOS 0x00000001
-#define HFI_BUFFERFLAG_STARTTIME 0x00000002
-#define HFI_BUFFERFLAG_DECODEONLY 0x00000004
-#define HFI_BUFFERFLAG_DATACORRUPT 0x00000008
-#define HFI_BUFFERFLAG_ENDOFFRAME 0x00000010
-#define HFI_BUFFERFLAG_SYNCFRAME 0x00000020
-#define HFI_BUFFERFLAG_EXTRADATA 0x00000040
-#define HFI_BUFFERFLAG_CODECCONFIG 0x00000080
-#define HFI_BUFFERFLAG_TIMESTAMPINVALID 0x00000100
-#define HFI_BUFFERFLAG_READONLY 0x00000200
-#define HFI_BUFFERFLAG_ENDOFSUBFRAME 0x00000400
-#define HFI_BUFFERFLAG_EOSEQ 0x00200000
-#define HFI_BUFFERFLAG_DISCONTINUITY 0x80000000
-#define HFI_BUFFERFLAG_TEI 0x40000000
-
-#define HFI_ERR_SESSION_EMPTY_BUFFER_DONE_OUTPUT_PENDING \
- (HFI_OX_BASE + 0x1001)
-#define HFI_ERR_SESSION_SAME_STATE_OPERATION \
- (HFI_OX_BASE + 0x1002)
-#define HFI_ERR_SESSION_SYNC_FRAME_NOT_DETECTED \
- (HFI_OX_BASE + 0x1003)
-#define HFI_ERR_SESSION_START_CODE_NOT_FOUND \
- (HFI_OX_BASE + 0x1004)
-
-#define HFI_BUFFER_INTERNAL_SCRATCH (HFI_OX_BASE + 0x1)
-#define HFI_BUFFER_EXTRADATA_INPUT (HFI_OX_BASE + 0x2)
-#define HFI_BUFFER_EXTRADATA_OUTPUT (HFI_OX_BASE + 0x3)
-#define HFI_BUFFER_EXTRADATA_OUTPUT2 (HFI_OX_BASE + 0x4)
-
-#define HFI_BUFFER_MODE_STATIC (HFI_OX_BASE + 0x1)
-#define HFI_BUFFER_MODE_RING (HFI_OX_BASE + 0x2)
-
-struct hfi_buffer_alloc_mode {
- u32 buffer_type;
- u32 buffer_mode;
-};
-
-#define HFI_FLUSH_INPUT (HFI_OX_BASE + 0x1)
-#define HFI_FLUSH_OUTPUT (HFI_OX_BASE + 0x2)
-#define HFI_FLUSH_OUTPUT2 (HFI_OX_BASE + 0x3)
-#define HFI_FLUSH_ALL (HFI_OX_BASE + 0x4)
-
-#define HFI_EXTRADATA_NONE 0x00000000
-#define HFI_EXTRADATA_MB_QUANTIZATION 0x00000001
-#define HFI_EXTRADATA_INTERLACE_VIDEO 0x00000002
-#define HFI_EXTRADATA_VC1_FRAMEDISP 0x00000003
-#define HFI_EXTRADATA_VC1_SEQDISP 0x00000004
-#define HFI_EXTRADATA_TIMESTAMP 0x00000005
-#define HFI_EXTRADATA_S3D_FRAME_PACKING 0x00000006
-#define HFI_EXTRADATA_FRAME_RATE 0x00000007
-#define HFI_EXTRADATA_PANSCAN_WINDOW 0x00000008
-#define HFI_EXTRADATA_RECOVERY_POINT_SEI 0x00000009
-#define HFI_EXTRADATA_CLOSED_CAPTION_UD 0x0000000A
-#define HFI_EXTRADATA_AFD_UD 0x0000000B
-#define HFI_EXTRADATA_MULTISLICE_INFO 0x7F100000
-#define HFI_EXTRADATA_NUM_CONCEALED_MB 0x7F100001
-#define HFI_EXTRADATA_INDEX 0x7F100002
-#define HFI_EXTRADATA_METADATA_FILLER 0x7FE00002
-
-#define HFI_INDEX_EXTRADATA_INPUT_CROP 0x0700000E
-#define HFI_INDEX_EXTRADATA_DIGITAL_ZOOM 0x07000010
-#define HFI_INDEX_EXTRADATA_ASPECT_RATIO 0x7F100003
-
-struct hfi_index_extradata_config {
- int enable;
- u32 index_extra_data_id;
-};
-
-struct hfi_extradata_header {
- u32 size;
- u32 version;
- u32 port_index;
- u32 type;
- u32 data_size;
- u8 rg_data[1];
-};
-
-#define HFI_INTERLACE_FRAME_PROGRESSIVE 0x01
-#define HFI_INTERLACE_INTERLEAVE_FRAME_TOPFIELDFIRST 0x02
-#define HFI_INTERLACE_INTERLEAVE_FRAME_BOTTOMFIELDFIRST 0x04
-#define HFI_INTERLACE_FRAME_TOPFIELDFIRST 0x08
-#define HFI_INTERLACE_FRAME_BOTTOMFIELDFIRST 0x10
-
-#define HFI_PROPERTY_SYS_OX_START \
- (HFI_DOMAIN_BASE_COMMON + HFI_ARCH_OX_OFFSET + 0x0000)
-#define HFI_PROPERTY_SYS_IDLE_INDICATOR \
- (HFI_PROPERTY_SYS_OX_START + 0x001)
-
-#define HFI_PROPERTY_PARAM_OX_START \
- (HFI_DOMAIN_BASE_COMMON + HFI_ARCH_OX_OFFSET + 0x1000)
-#define HFI_PROPERTY_PARAM_BUFFER_COUNT_ACTUAL \
- (HFI_PROPERTY_PARAM_OX_START + 0x001)
-#define HFI_PROPERTY_PARAM_UNCOMPRESSED_PLANE_ACTUAL_CONSTRAINTS_INFO \
- (HFI_PROPERTY_PARAM_OX_START + 0x002)
-#define HFI_PROPERTY_PARAM_INTERLACE_FORMAT_SUPPORTED \
- (HFI_PROPERTY_PARAM_OX_START + 0x003)
-#define HFI_PROPERTY_PARAM_CHROMA_SITE \
-(HFI_PROPERTY_PARAM_OX_START + 0x004)
-#define HFI_PROPERTY_PARAM_EXTRA_DATA_HEADER_CONFIG \
- (HFI_PROPERTY_PARAM_OX_START + 0x005)
-#define HFI_PROPERTY_PARAM_INDEX_EXTRADATA \
- (HFI_PROPERTY_PARAM_OX_START + 0x006)
-#define HFI_PROPERTY_PARAM_DIVX_FORMAT \
- (HFI_PROPERTY_PARAM_OX_START + 0x007)
-#define HFI_PROPERTY_PARAM_BUFFER_ALLOC_MODE \
- (HFI_PROPERTY_PARAM_OX_START + 0x008)
-#define HFI_PROPERTY_PARAM_S3D_FRAME_PACKING_EXTRADATA \
- (HFI_PROPERTY_PARAM_OX_START + 0x009)
-
-#define HFI_PROPERTY_CONFIG_OX_START \
- (HFI_DOMAIN_BASE_COMMON + HFI_ARCH_OX_OFFSET + 0x02000)
-#define HFI_PROPERTY_CONFIG_BUFFER_REQUIREMENTS \
- (HFI_PROPERTY_CONFIG_OX_START + 0x001)
-#define HFI_PROPERTY_CONFIG_REALTIME \
- (HFI_PROPERTY_CONFIG_OX_START + 0x002)
-#define HFI_PROPERTY_CONFIG_PRIORITY \
- (HFI_PROPERTY_CONFIG_OX_START + 0x003)
-#define HFI_PROPERTY_CONFIG_BATCH_INFO \
- (HFI_PROPERTY_CONFIG_OX_START + 0x004)
-
-#define HFI_PROPERTY_PARAM_VDEC_OX_START \
- (HFI_DOMAIN_BASE_VDEC + HFI_ARCH_OX_OFFSET + 0x3000)
-#define HFI_PROPERTY_PARAM_VDEC_CONTINUE_DATA_TRANSFER \
- (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x001)
-#define HFI_PROPERTY_PARAM_VDEC_DISPLAY_PICTURE_BUFFER_COUNT\
- (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x002)
-#define HFI_PROPERTY_PARAM_VDEC_MULTI_VIEW_SELECT \
- (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x003)
-#define HFI_PROPERTY_PARAM_VDEC_PICTURE_TYPE_DECODE \
- (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x004)
-#define HFI_PROPERTY_PARAM_VDEC_OUTPUT_ORDER \
- (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x005)
-#define HFI_PROPERTY_PARAM_VDEC_MB_QUANTIZATION \
- (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x006)
-#define HFI_PROPERTY_PARAM_VDEC_NUM_CONCEALED_MB \
- (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x007)
-#define HFI_PROPERTY_PARAM_VDEC_H264_ENTROPY_SWITCHING \
- (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x008)
-#define HFI_PROPERTY_PARAM_VDEC_OUTPUT2_KEEP_ASPECT_RATIO\
- (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x009)
-#define HFI_PROPERTY_PARAM_VDEC_FRAME_RATE_EXTRADATA \
- (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x00A)
-#define HFI_PROPERTY_PARAM_VDEC_PANSCAN_WNDW_EXTRADATA \
- (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x00B)
-#define HFI_PROPERTY_PARAM_VDEC_RECOVERY_POINT_SEI_EXTRADATA \
- (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x00C)
-#define HFI_PROPERTY_PARAM_VDEC_THUMBNAIL_MODE \
- (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x00D)
-
-#define HFI_PROPERTY_PARAM_VDEC_FRAME_ASSEMBLY \
- (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x00E)
-#define HFI_PROPERTY_PARAM_VDEC_CLOSED_CAPTION_EXTRADATA \
- (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x00F)
-#define HFI_PROPERTY_PARAM_VDEC_AFD_EXTRADATA \
- (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x010)
-#define HFI_PROPERTY_PARAM_VDEC_VC1_FRAMEDISP_EXTRADATA \
- (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x011)
-#define HFI_PROPERTY_PARAM_VDEC_VC1_SEQDISP_EXTRADATA \
- (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x012)
-#define HFI_PROPERTY_PARAM_VDEC_TIMESTAMP_EXTRADATA \
- (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x013)
-#define HFI_PROPERTY_PARAM_VDEC_INTERLACE_VIDEO_EXTRADATA \
- (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x014)
-#define HFI_PROPERTY_PARAM_VDEC_AVC_SESSION_SELECT \
- (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x015)
-
-#define HFI_PROPERTY_CONFIG_VDEC_OX_START \
- (HFI_DOMAIN_BASE_VDEC + HFI_ARCH_OX_OFFSET + 0x0000)
-#define HFI_PROPERTY_CONFIG_VDEC_POST_LOOP_DEBLOCKER \
- (HFI_PROPERTY_CONFIG_VDEC_OX_START + 0x001)
-#define HFI_PROPERTY_CONFIG_VDEC_MB_ERROR_MAP_REPORTING \
- (HFI_PROPERTY_CONFIG_VDEC_OX_START + 0x002)
-#define HFI_PROPERTY_CONFIG_VDEC_MB_ERROR_MAP \
- (HFI_PROPERTY_CONFIG_VDEC_OX_START + 0x003)
-
-#define HFI_PROPERTY_PARAM_VENC_OX_START \
- (HFI_DOMAIN_BASE_VENC + HFI_ARCH_OX_OFFSET + 0x5000)
-#define HFI_PROPERTY_PARAM_VENC_MULTI_SLICE_INFO \
- (HFI_PROPERTY_PARAM_VENC_OX_START + 0x001)
-#define HFI_PROPERTY_PARAM_VENC_H264_IDR_S3D_FRAME_PACKING_NAL \
- (HFI_PROPERTY_PARAM_VENC_OX_START + 0x002)
-
-#define HFI_PROPERTY_CONFIG_VENC_OX_START \
- (HFI_DOMAIN_BASE_VENC + HFI_ARCH_OX_OFFSET + 0x6000)
-#define HFI_PROPERTY_CONFIG_VENC_FRAME_QP \
- (HFI_PROPERTY_CONFIG_VENC_OX_START + 0x001)
-
-#define HFI_PROPERTY_PARAM_VPE_OX_START \
- (HFI_DOMAIN_BASE_VPE + HFI_ARCH_OX_OFFSET + 0x7000)
-#define HFI_PROPERTY_CONFIG_VPE_OX_START \
- (HFI_DOMAIN_BASE_VPE + HFI_ARCH_OX_OFFSET + 0x8000)
-
-struct hfi_batch_info {
- u32 input_batch_count;
- u32 output_batch_count;
-};
-
-struct hfi_buffer_count_actual {
- u32 buffer_type;
- u32 buffer_count_actual;
-};
-
-struct hfi_buffer_requirements {
- u32 buffer_type;
- u32 buffer_size;
- u32 buffer_region_size;
- u32 buffer_hold_count;
- u32 buffer_count_min;
- u32 buffer_count_actual;
- u32 contiguous;
- u32 buffer_alignment;
-};
-
-#define HFI_CHROMA_SITE_0 (HFI_OX_BASE + 0x1)
-#define HFI_CHROMA_SITE_1 (HFI_OX_BASE + 0x2)
-#define HFI_CHROMA_SITE_2 (HFI_OX_BASE + 0x3)
-#define HFI_CHROMA_SITE_3 (HFI_OX_BASE + 0x4)
-#define HFI_CHROMA_SITE_4 (HFI_OX_BASE + 0x5)
-#define HFI_CHROMA_SITE_5 (HFI_OX_BASE + 0x6)
-
-struct hfi_data_payload {
- u32 size;
- u8 rg_data[1];
-};
-
-struct hfi_enable_picture {
- u32 picture_type;
-};
-
-struct hfi_display_picture_buffer_count {
- int enable;
- u32 count;
-};
-
-struct hfi_extra_data_header_config {
- u32 type;
- u32 buffer_type;
- u32 version;
- u32 port_index;
- u32 client_extra_data_id;
-};
-
-struct hfi_interlace_format_supported {
- u32 buffer_type;
- u32 format;
-};
-
-struct hfi_mb_error_map {
- u32 error_map_size;
- u8 rg_error_map[1];
-};
-
-struct hfi_metadata_pass_through {
- int enable;
- u32 size;
-};
-
-struct hfi_multi_view_select {
- u32 view_index;
-};
-
-#define HFI_PRIORITY_LOW 10
-#define HFI_PRIOIRTY_MEDIUM 20
-#define HFI_PRIORITY_HIGH 30
-
-#define HFI_OUTPUT_ORDER_DISPLAY (HFI_OX_BASE + 0x1)
-#define HFI_OUTPUT_ORDER_DECODE (HFI_OX_BASE + 0x2)
-
-#define HFI_RATE_CONTROL_OFF (HFI_OX_BASE + 0x1)
-#define HFI_RATE_CONTROL_VBR_VFR (HFI_OX_BASE + 0x2)
-#define HFI_RATE_CONTROL_VBR_CFR (HFI_OX_BASE + 0x3)
-#define HFI_RATE_CONTROL_CBR_VFR (HFI_OX_BASE + 0x4)
-#define HFI_RATE_CONTROL_CBR_CFR (HFI_OX_BASE + 0x5)
-
-struct hfi_uncompressed_plane_actual_constraints_info {
- u32 buffer_type;
- u32 num_planes;
- struct hfi_uncompressed_plane_constraints rg_plane_format[1];
-};
-
-#define HFI_CMD_SYS_OX_START \
-(HFI_DOMAIN_BASE_COMMON + HFI_ARCH_OX_OFFSET + HFI_CMD_START_OFFSET + 0x0000)
-#define HFI_CMD_SYS_SESSION_ABORT (HFI_CMD_SYS_OX_START + 0x001)
-#define HFI_CMD_SYS_PING (HFI_CMD_SYS_OX_START + 0x002)
-
-#define HFI_CMD_SESSION_OX_START \
-(HFI_DOMAIN_BASE_COMMON + HFI_ARCH_OX_OFFSET + HFI_CMD_START_OFFSET + 0x1000)
-#define HFI_CMD_SESSION_LOAD_RESOURCES (HFI_CMD_SESSION_OX_START + 0x001)
-#define HFI_CMD_SESSION_START (HFI_CMD_SESSION_OX_START + 0x002)
-#define HFI_CMD_SESSION_STOP (HFI_CMD_SESSION_OX_START + 0x003)
-#define HFI_CMD_SESSION_EMPTY_BUFFER (HFI_CMD_SESSION_OX_START + 0x004)
-#define HFI_CMD_SESSION_FILL_BUFFER (HFI_CMD_SESSION_OX_START + 0x005)
-#define HFI_CMD_SESSION_SUSPEND (HFI_CMD_SESSION_OX_START + 0x006)
-#define HFI_CMD_SESSION_RESUME (HFI_CMD_SESSION_OX_START + 0x007)
-#define HFI_CMD_SESSION_FLUSH (HFI_CMD_SESSION_OX_START + 0x008)
-#define HFI_CMD_SESSION_GET_PROPERTY (HFI_CMD_SESSION_OX_START + 0x009)
-#define HFI_CMD_SESSION_PARSE_SEQUENCE_HEADER \
- (HFI_CMD_SESSION_OX_START + 0x00A)
-#define HFI_CMD_SESSION_RELEASE_BUFFERS \
- (HFI_CMD_SESSION_OX_START + 0x00B)
-#define HFI_CMD_SESSION_RELEASE_RESOURCES \
- (HFI_CMD_SESSION_OX_START + 0x00C)
-
-#define HFI_MSG_SYS_OX_START \
-(HFI_DOMAIN_BASE_COMMON + HFI_ARCH_OX_OFFSET + HFI_MSG_START_OFFSET + 0x0000)
-#define HFI_MSG_SYS_IDLE (HFI_MSG_SYS_OX_START + 0x1)
-#define HFI_MSG_SYS_PING_ACK (HFI_MSG_SYS_OX_START + 0x2)
-#define HFI_MSG_SYS_PROPERTY_INFO (HFI_MSG_SYS_OX_START + 0x3)
-#define HFI_MSG_SYS_SESSION_ABORT_DONE (HFI_MSG_SYS_OX_START + 0x4)
-
-#define HFI_MSG_SESSION_OX_START \
-(HFI_DOMAIN_BASE_COMMON + HFI_ARCH_OX_OFFSET + HFI_MSG_START_OFFSET + 0x1000)
-#define HFI_MSG_SESSION_LOAD_RESOURCES_DONE (HFI_MSG_SESSION_OX_START + 0x1)
-#define HFI_MSG_SESSION_START_DONE (HFI_MSG_SESSION_OX_START + 0x2)
-#define HFI_MSG_SESSION_STOP_DONE (HFI_MSG_SESSION_OX_START + 0x3)
-#define HFI_MSG_SESSION_SUSPEND_DONE (HFI_MSG_SESSION_OX_START + 0x4)
-#define HFI_MSG_SESSION_RESUME_DONE (HFI_MSG_SESSION_OX_START + 0x5)
-#define HFI_MSG_SESSION_FLUSH_DONE (HFI_MSG_SESSION_OX_START + 0x6)
-#define HFI_MSG_SESSION_EMPTY_BUFFER_DONE (HFI_MSG_SESSION_OX_START + 0x7)
-#define HFI_MSG_SESSION_FILL_BUFFER_DONE (HFI_MSG_SESSION_OX_START + 0x8)
-#define HFI_MSG_SESSION_PROPERTY_INFO (HFI_MSG_SESSION_OX_START + 0x9)
-#define HFI_MSG_SESSION_RELEASE_RESOURCES_DONE \
- (HFI_MSG_SESSION_OX_START + 0xA)
-#define HFI_MSG_SESSION_PARSE_SEQUENCE_HEADER_DONE \
- (HFI_MSG_SESSION_OX_START + 0xB)
-#define HFI_MSG_SESSION_RELEASE_BUFFERS_DONE \
- (HFI_MSG_SESSION_OX_START + 0xC)
-
-struct hfi_cmd_sys_session_abort_packet {
- u32 size;
- u32 packet_type;
- u32 session_id;
-};
-
-struct hfi_cmd_sys_ping_packet {
- u32 size;
- u32 packet_type;
- u32 client_data;
-};
-
-struct hfi_cmd_session_load_resources_packet {
- u32 size;
- u32 packet_type;
- u32 session_id;
-};
-
-struct hfi_cmd_session_start_packet {
- u32 size;
- u32 packet_type;
- u32 session_id;
-};
-
-struct hfi_cmd_session_stop_packet {
- u32 size;
- u32 packet_type;
- u32 session_id;
-};
-
-struct hfi_cmd_session_empty_buffer_compressed_packet {
- u32 size;
- u32 packet_type;
- u32 session_id;
- u32 time_stamp_hi;
- u32 time_stamp_lo;
- u32 flags;
- u32 mark_target;
- u32 mark_data;
- u32 offset;
- u32 alloc_len;
- u32 filled_len;
- u32 input_tag;
- u8 *packet_buffer;
- u8 *extra_data_buffer;
- u32 rgData[0];
-};
-
-struct hfi_cmd_session_empty_buffer_uncompressed_plane0_packet {
- u32 size;
- u32 packet_type;
- u32 session_id;
- u32 view_id;
- u32 time_stamp_hi;
- u32 time_stamp_lo;
- u32 flags;
- u32 mark_target;
- u32 mark_data;
- u32 alloc_len;
- u32 filled_len;
- u32 offset;
- u32 input_tag;
- u8 *packet_buffer;
- u8 *extra_data_buffer;
- u32 rgData[0];
-};
-
-struct hfi_cmd_session_empty_buffer_uncompressed_plane1_packet {
- u32 flags;
- u32 alloc_len;
- u32 filled_len;
- u32 offset;
- u8 *packet_buffer2;
- u32 rgData[0];
-};
-
-struct hfi_cmd_session_empty_buffer_uncompressed_plane2_packet {
- u32 flags;
- u32 alloc_len;
- u32 filled_len;
- u32 offset;
- u8 *packet_buffer3;
- u32 rgData[0];
-};
-
-struct hfi_cmd_session_fill_buffer_packet {
- u32 size;
- u32 packet_type;
- u32 session_id;
- u32 stream_id;
- u32 offset;
- u32 alloc_len;
- u32 filled_len;
- u32 output_tag;
- u8 *packet_buffer;
- u8 *extra_data_buffer;
- u32 rgData[0];
-};
-
-struct hfi_cmd_session_flush_packet {
- u32 size;
- u32 packet_type;
- u32 session_id;
- u32 flush_type;
-};
-
-struct hfi_cmd_session_suspend_packet {
- u32 size;
- u32 packet_type;
- u32 session_id;
-};
-
-struct hfi_cmd_session_resume_packet {
- u32 size;
- u32 packet_type;
- u32 session_id;
-};
-
-struct hfi_cmd_session_get_property_packet {
- u32 size;
- u32 packet_type;
- u32 session_id;
- u32 num_properties;
- u32 rg_property_data[1];
-};
-
-struct hfi_cmd_session_release_buffer_packet {
- u32 size;
- u32 packet_type;
- u32 session_id;
- u32 buffer_type;
- u32 buffer_size;
- u32 extra_data_size;
- int response_req;
- u32 num_buffers;
- u32 rg_buffer_info[1];
-};
-
-struct hfi_cmd_session_release_resources_packet {
- u32 size;
- u32 packet_type;
- u32 session_id;
-};
-
-struct hfi_cmd_session_parse_sequence_header_packet {
- u32 size;
- u32 packet_type;
- u32 session_id;
- u32 header_len;
- u8 *packet_buffer;
-};
-
-struct hfi_msg_sys_session_abort_done_packet {
- u32 size;
- u32 packet_type;
- u32 session_id;
- u32 error_type;
-};
-
-struct hfi_msg_sys_idle_packet {
- u32 size;
- u32 packet_type;
-};
-
-struct hfi_msg_sys_ping_ack_packet {
- u32 size;
- u32 packet_type;
- u32 client_data;
-};
-
-struct hfi_msg_sys_property_info_packet {
- u32 size;
- u32 packet_type;
- u32 num_properties;
- u32 rg_property_data[1];
-};
-
-struct hfi_msg_session_load_resources_done_packet {
- u32 size;
- u32 packet_type;
- u32 session_id;
- u32 error_type;
-};
-
-struct hfi_msg_session_start_done_packet {
- u32 size;
- u32 packet_type;
- u32 session_id;
- u32 error_type;
-};
-
-struct hfi_msg_session_stop_done_packet {
- u32 size;
- u32 packet_type;
- u32 session_id;
- u32 error_type;
-};
-
-struct hfi_msg_session_suspend_done_packet {
- u32 size;
- u32 packet_type;
- u32 session_id;
- u32 error_type;
-};
-
-struct hfi_msg_session_resume_done_packet {
- u32 size;
- u32 packet_type;
- u32 session_id;
- u32 error_type;
-};
-
-struct hfi_msg_session_flush_done_packet {
- u32 size;
- u32 packet_type;
- u32 session_id;
- u32 error_type;
- u32 flush_type;
-};
-
-struct hfi_msg_session_empty_buffer_done_packet {
- u32 size;
- u32 packet_type;
- u32 session_id;
- u32 error_type;
- u32 offset;
- u32 filled_len;
- u32 input_tag;
- u8 *packet_buffer;
- u8 *extra_data_buffer;
- u32 rgData[0];
-};
-
-struct hfi_msg_session_fill_buffer_done_compressed_packet {
- u32 size;
- u32 packet_type;
- u32 session_id;
- u32 time_stamp_hi;
- u32 time_stamp_lo;
- u32 error_type;
- u32 flags;
- u32 mark_target;
- u32 mark_data;
- u32 stats;
- u32 offset;
- u32 alloc_len;
- u32 filled_len;
- u32 input_tag;
- u32 output_tag;
- u32 picture_type;
- u8 *packet_buffer;
- u8 *extra_data_buffer;
- u32 rgData[0];
-};
-
-struct hfi_msg_session_fbd_uncompressed_plane0_packet {
- u32 size;
- u32 packet_type;
- u32 session_id;
- u32 stream_id;
- u32 view_id;
- u32 error_type;
- u32 time_stamp_hi;
- u32 time_stamp_lo;
- u32 flags;
- u32 mark_target;
- u32 mark_data;
- u32 stats;
- u32 alloc_len;
- u32 filled_len;
- u32 offset;
- u32 frame_width;
- u32 frame_height;
- u32 start_x_coord;
- u32 start_y_coord;
- u32 input_tag;
- u32 input_tag2;
- u32 output_tag;
- u32 picture_type;
- u8 *packet_buffer;
- u8 *extra_data_buffer;
- u32 rgData[0];
-};
-
-struct hfi_msg_session_fill_buffer_done_uncompressed_plane1_packet {
- u32 flags;
- u32 alloc_len;
- u32 filled_len;
- u32 offset;
- u8 *packet_buffer2;
- u32 rgData[0];
-};
-
-struct hfi_msg_session_fill_buffer_done_uncompressed_plane2_packet {
- u32 flags;
- u32 alloc_len;
- u32 filled_len;
- u32 offset;
- u8 *packet_buffer3;
- u32 rgData[0];
-};
-
-struct hfi_msg_session_parse_sequence_header_done_packet {
- u32 size;
- u32 packet_type;
- u32 session_id;
- u32 error_type;
- u32 num_properties;
- u32 rg_property_data[1];
-};
-
-struct hfi_msg_session_property_info_packet {
- u32 size;
- u32 packet_type;
- u32 session_id;
- u32 num_properties;
- u32 rg_property_data[1];
-};
-
-struct hfi_msg_session_release_resources_done_packet {
- u32 size;
- u32 packet_type;
- u32 session_id;
- u32 error_type;
-};
-
-struct hfi_msg_session_release_buffers_done_packet {
- u32 size;
- u32 packet_type;
- u32 session_id;
- u32 error_type;
- u32 num_buffers;
- u32 rg_buffer_info[1];
-};
-
-struct hfi_extradata_mb_quantization_payload {
- u8 rg_mb_qp[1];
-};
-
-struct hfi_extradata_vc1_pswnd {
- u32 ps_wnd_h_offset;
- u32 ps_wnd_v_offset;
- u32 ps_wnd_width;
- u32 ps_wnd_height;
-};
-
-struct hfi_extradata_vc1_framedisp_payload {
- u32 res_pic;
- u32 ref;
- u32 range_map_present;
- u32 range_map_y;
- u32 range_map_uv;
- u32 num_pan_scan_wnds;
- struct hfi_extradata_vc1_pswnd rg_ps_wnd[1];
-};
-
-struct hfi_extradata_vc1_seqdisp_payload {
- u32 prog_seg_frm;
- u32 uv_sampling_fmt;
- u32 color_fmt_flag;
- u32 color_primaries;
- u32 transfer_char;
- u32 mat_coeff;
- u32 aspect_ratio;
- u32 aspect_horiz;
- u32 aspect_vert;
-};
-
-struct hfi_extradata_timestamp_payload {
- u32 time_stamp_low;
- u32 time_stamp_high;
-};
-
-
-struct hfi_extradata_s3d_frame_packing_payload {
- u32 fpa_id;
- int cancel_flag;
- u32 fpa_type;
- int quin_cunx_flag;
- u32 content_interprtation_type;
- int spatial_flipping_flag;
- int frame0_flipped_flag;
- int field_views_flag;
- int current_frame_isFrame0_flag;
- int frame0_self_contained_flag;
- int frame1_self_contained_flag;
- u32 frame0_graid_pos_x;
- u32 frame0_graid_pos_y;
- u32 frame1_graid_pos_x;
- u32 frame1_graid_pos_y;
- u32 fpa_reserved_byte;
- u32 fpa_repetition_period;
- int fpa_extension_flag;
-};
-
-struct hfi_extradata_interlace_video_payload {
- u32 format;
-};
-
-struct hfi_extradata_num_concealed_mb_payload {
- u32 num_mb_concealed;
-};
-
-struct hfi_extradata_sliceinfo {
- u32 offset_in_stream;
- u32 slice_length;
-};
-
-struct hfi_extradata_multislice_info_payload {
- u32 num_slices;
- struct hfi_extradata_sliceinfo rg_slice_info[1];
-};
-
-struct hfi_index_extradata_input_crop_payload {
- u32 size;
- u32 version;
- u32 port_index;
- u32 left;
- u32 top;
- u32 width;
- u32 height;
-};
-
-struct hfi_index_extradata_digital_zoom_payload {
- u32 size;
- u32 version;
- u32 port_index;
- int width;
- int height;
-};
-
-struct hfi_index_extradata_aspect_ratio_payload {
- u32 size;
- u32 version;
- u32 port_index;
- u32 aspect_width;
- u32 aspect_height;
-};
-struct hfi_extradata_panscan_wndw_payload {
- u32 num_window;
- struct hfi_extradata_vc1_pswnd wnd[1];
-};
-
-struct hfi_extradata_frame_type_payload {
- u32 frame_rate;
-};
-
-struct hfi_extradata_recovery_point_sei_payload {
- u32 flag;
-};
-
struct vidc_mem_addr {
u8 *align_device_addr;
u8 *align_virtual_addr;
@@ -921,11 +149,6 @@
VCODEC_MAX_CLKS
};
-enum mem_type {
- DDR_MEM = 0x1,
- OCMEM_MEM = 0x2,
-};
-
struct load_freq_table {
u32 load;
u32 freq;
@@ -968,7 +191,7 @@
u32 device_id;
spinlock_t read_lock;
spinlock_t write_lock;
- void (*callback) (u32 response, void *callback);
+ msm_vidc_callback callback;
struct vidc_mem_addr iface_q_table;
struct vidc_mem_addr qdss;
struct vidc_mem_addr sfr;
@@ -986,49 +209,7 @@
struct venus_resources resources;
};
-struct hal_session {
- struct list_head list;
- u32 session_id;
- u32 is_decoder;
- struct venus_hfi_device *device;
-};
-
-struct hal_device_data {
- struct list_head dev_head;
- int dev_count;
-};
-
-extern struct hal_device_data hal_ctxt;
-
-int venus_hfi_iface_msgq_read(struct venus_hfi_device *device, void *pkt);
-int venus_hfi_iface_dbgq_read(struct venus_hfi_device *device, void *pkt);
-
-/* Interrupt Processing:*/
-void hfi_response_handler(struct venus_hfi_device *device);
-
void venus_hfi_delete_device(void *device);
-
-int venus_hfi_scale_clocks(struct venus_hfi_device *device, int load);
-
-void *venus_hfi_get_device(u32 device_id,
- struct platform_device *pdev,
- void (*callback) (enum command_response cmd, void *data));
-
-int venus_hfi_scale_bus(struct venus_hfi_device *device, int load,
- enum session_type type, enum mem_type mtype);
-int venus_hfi_set_ocmem(struct venus_hfi_device *device,
- struct ocmem_buf *ocmem);
-int venus_hfi_unset_ocmem(struct venus_hfi_device *device);
-int venus_hfi_alloc_ocmem(struct venus_hfi_device *device,
- unsigned long size);
-int venus_hfi_free_ocmem(struct venus_hfi_device *device);
-int venus_hfi_is_ocmem_present(struct venus_hfi_device *device);
-
-int venus_hfi_get_domain(struct venus_hfi_device *device,
- enum msm_vidc_io_maps iomap);
-int venus_hfi_iommu_get_map(struct venus_hfi_device *device,
- struct msm_vidc_iommu_info maps[MAX_MAP]);
-int venus_hfi_load_fw(struct venus_hfi_device *device);
-void venus_hfi_unload_fw(struct venus_hfi_device *device);
-
+int venus_hfi_initialize(struct hfi_device *hdev, u32 device_id,
+ struct platform_device *pdev, hfi_cmd_response_callback callback);
#endif
diff --git a/drivers/media/video/msm_vidc/vidc_hfi.c b/drivers/media/video/msm_vidc/vidc_hfi.c
new file mode 100644
index 0000000..f09d3d5
--- /dev/null
+++ b/drivers/media/video/msm_vidc/vidc_hfi.c
@@ -0,0 +1,68 @@
+/* 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
+ * 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/slab.h>
+#include "msm_vidc_debug.h"
+#include "vidc_hfi_api.h"
+#include "venus_hfi.h"
+
+struct hal_device_data hal_ctxt;
+
+void *vidc_hfi_initialize(enum msm_vidc_hfi_type hfi_type, u32 device_id,
+ struct platform_device *pdev,
+ hfi_cmd_response_callback callback)
+{
+ struct hfi_device *hdev = NULL;
+ hdev = (struct hfi_device *)
+ kzalloc(sizeof(struct hfi_device), GFP_KERNEL);
+ if (!hdev) {
+ dprintk(VIDC_ERR, "%s: failed to allocate hdev\n", __func__);
+ return NULL;
+ }
+
+ switch (hfi_type) {
+ case VIDC_HFI_VENUS:
+ venus_hfi_initialize(hdev, device_id, pdev, callback);
+ break;
+
+ case VIDC_HFI_Q6:
+ default:
+ dprintk(VIDC_ERR, "Unsupported host-firmware interface\n");
+ goto err_hfi_init;
+ }
+ return hdev;
+
+err_hfi_init:
+ kfree(hdev);
+ return NULL;
+}
+
+void vidc_hfi_deinitialize(enum msm_vidc_hfi_type hfi_type,
+ struct hfi_device *hdev)
+{
+ if (!hdev) {
+ dprintk(VIDC_ERR, "%s invalid device %p", __func__, hdev);
+ return;
+ }
+
+ switch (hfi_type) {
+ case VIDC_HFI_VENUS:
+ venus_hfi_delete_device(hdev->hfi_device_data);
+ break;
+
+ case VIDC_HFI_Q6:
+ default:
+ dprintk(VIDC_ERR, "Unsupported host-firmware interface\n");
+ }
+ kfree(hdev);
+}
+
diff --git a/drivers/media/video/msm_vidc/vidc_hfi.h b/drivers/media/video/msm_vidc/vidc_hfi.h
new file mode 100644
index 0000000..6ff0921
--- /dev/null
+++ b/drivers/media/video/msm_vidc/vidc_hfi.h
@@ -0,0 +1,826 @@
+/* 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
+ * 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 __H_VIDC_HFI_H__
+#define __H_VIDC_HFI_H__
+
+#include "vidc_hfi_helper.h"
+#include "vidc_hfi_api.h"
+
+#define HFI_EVENT_SESSION_SEQUENCE_CHANGED (HFI_OX_BASE + 0x3)
+#define HFI_EVENT_SESSION_PROPERTY_CHANGED (HFI_OX_BASE + 0x4)
+
+#define HFI_EVENT_DATA_SEQUENCE_CHANGED_SUFFICIENT_BUFFER_RESOURCES \
+ (HFI_OX_BASE + 0x1)
+#define HFI_EVENT_DATA_SEQUENCE_CHANGED_INSUFFICIENT_BUFFER_RESOURCES \
+ (HFI_OX_BASE + 0x2)
+
+#define HFI_BUFFERFLAG_EOS 0x00000001
+#define HFI_BUFFERFLAG_STARTTIME 0x00000002
+#define HFI_BUFFERFLAG_DECODEONLY 0x00000004
+#define HFI_BUFFERFLAG_DATACORRUPT 0x00000008
+#define HFI_BUFFERFLAG_ENDOFFRAME 0x00000010
+#define HFI_BUFFERFLAG_SYNCFRAME 0x00000020
+#define HFI_BUFFERFLAG_EXTRADATA 0x00000040
+#define HFI_BUFFERFLAG_CODECCONFIG 0x00000080
+#define HFI_BUFFERFLAG_TIMESTAMPINVALID 0x00000100
+#define HFI_BUFFERFLAG_READONLY 0x00000200
+#define HFI_BUFFERFLAG_ENDOFSUBFRAME 0x00000400
+#define HFI_BUFFERFLAG_EOSEQ 0x00200000
+#define HFI_BUFFERFLAG_DISCONTINUITY 0x80000000
+#define HFI_BUFFERFLAG_TEI 0x40000000
+
+#define HFI_ERR_SESSION_EMPTY_BUFFER_DONE_OUTPUT_PENDING \
+ (HFI_OX_BASE + 0x1001)
+#define HFI_ERR_SESSION_SAME_STATE_OPERATION \
+ (HFI_OX_BASE + 0x1002)
+#define HFI_ERR_SESSION_SYNC_FRAME_NOT_DETECTED \
+ (HFI_OX_BASE + 0x1003)
+#define HFI_ERR_SESSION_START_CODE_NOT_FOUND \
+ (HFI_OX_BASE + 0x1004)
+
+#define HFI_BUFFER_INTERNAL_SCRATCH (HFI_OX_BASE + 0x1)
+#define HFI_BUFFER_EXTRADATA_INPUT (HFI_OX_BASE + 0x2)
+#define HFI_BUFFER_EXTRADATA_OUTPUT (HFI_OX_BASE + 0x3)
+#define HFI_BUFFER_EXTRADATA_OUTPUT2 (HFI_OX_BASE + 0x4)
+
+#define HFI_BUFFER_MODE_STATIC (HFI_OX_BASE + 0x1)
+#define HFI_BUFFER_MODE_RING (HFI_OX_BASE + 0x2)
+
+#define HFI_FLUSH_INPUT (HFI_OX_BASE + 0x1)
+#define HFI_FLUSH_OUTPUT (HFI_OX_BASE + 0x2)
+#define HFI_FLUSH_OUTPUT2 (HFI_OX_BASE + 0x3)
+#define HFI_FLUSH_ALL (HFI_OX_BASE + 0x4)
+
+#define HFI_EXTRADATA_NONE 0x00000000
+#define HFI_EXTRADATA_MB_QUANTIZATION 0x00000001
+#define HFI_EXTRADATA_INTERLACE_VIDEO 0x00000002
+#define HFI_EXTRADATA_VC1_FRAMEDISP 0x00000003
+#define HFI_EXTRADATA_VC1_SEQDISP 0x00000004
+#define HFI_EXTRADATA_TIMESTAMP 0x00000005
+#define HFI_EXTRADATA_S3D_FRAME_PACKING 0x00000006
+#define HFI_EXTRADATA_FRAME_RATE 0x00000007
+#define HFI_EXTRADATA_PANSCAN_WINDOW 0x00000008
+#define HFI_EXTRADATA_RECOVERY_POINT_SEI 0x00000009
+#define HFI_EXTRADATA_CLOSED_CAPTION_UD 0x0000000A
+#define HFI_EXTRADATA_AFD_UD 0x0000000B
+#define HFI_EXTRADATA_MULTISLICE_INFO 0x7F100000
+#define HFI_EXTRADATA_NUM_CONCEALED_MB 0x7F100001
+#define HFI_EXTRADATA_INDEX 0x7F100002
+#define HFI_EXTRADATA_METADATA_FILLER 0x7FE00002
+
+#define HFI_INDEX_EXTRADATA_INPUT_CROP 0x0700000E
+#define HFI_INDEX_EXTRADATA_DIGITAL_ZOOM 0x07000010
+#define HFI_INDEX_EXTRADATA_ASPECT_RATIO 0x7F100003
+
+struct hfi_buffer_alloc_mode {
+ u32 buffer_type;
+ u32 buffer_mode;
+};
+
+
+struct hfi_index_extradata_config {
+ int enable;
+ u32 index_extra_data_id;
+};
+
+struct hfi_extradata_header {
+ u32 size;
+ u32 version;
+ u32 port_index;
+ u32 type;
+ u32 data_size;
+ u8 rg_data[1];
+};
+
+#define HFI_INTERLACE_FRAME_PROGRESSIVE 0x01
+#define HFI_INTERLACE_INTERLEAVE_FRAME_TOPFIELDFIRST 0x02
+#define HFI_INTERLACE_INTERLEAVE_FRAME_BOTTOMFIELDFIRST 0x04
+#define HFI_INTERLACE_FRAME_TOPFIELDFIRST 0x08
+#define HFI_INTERLACE_FRAME_BOTTOMFIELDFIRST 0x10
+
+#define HFI_PROPERTY_SYS_OX_START \
+ (HFI_DOMAIN_BASE_COMMON + HFI_ARCH_OX_OFFSET + 0x0000)
+#define HFI_PROPERTY_SYS_IDLE_INDICATOR \
+ (HFI_PROPERTY_SYS_OX_START + 0x001)
+
+#define HFI_PROPERTY_PARAM_OX_START \
+ (HFI_DOMAIN_BASE_COMMON + HFI_ARCH_OX_OFFSET + 0x1000)
+#define HFI_PROPERTY_PARAM_BUFFER_COUNT_ACTUAL \
+ (HFI_PROPERTY_PARAM_OX_START + 0x001)
+#define HFI_PROPERTY_PARAM_UNCOMPRESSED_PLANE_ACTUAL_CONSTRAINTS_INFO \
+ (HFI_PROPERTY_PARAM_OX_START + 0x002)
+#define HFI_PROPERTY_PARAM_INTERLACE_FORMAT_SUPPORTED \
+ (HFI_PROPERTY_PARAM_OX_START + 0x003)
+#define HFI_PROPERTY_PARAM_CHROMA_SITE \
+(HFI_PROPERTY_PARAM_OX_START + 0x004)
+#define HFI_PROPERTY_PARAM_EXTRA_DATA_HEADER_CONFIG \
+ (HFI_PROPERTY_PARAM_OX_START + 0x005)
+#define HFI_PROPERTY_PARAM_INDEX_EXTRADATA \
+ (HFI_PROPERTY_PARAM_OX_START + 0x006)
+#define HFI_PROPERTY_PARAM_DIVX_FORMAT \
+ (HFI_PROPERTY_PARAM_OX_START + 0x007)
+#define HFI_PROPERTY_PARAM_BUFFER_ALLOC_MODE \
+ (HFI_PROPERTY_PARAM_OX_START + 0x008)
+#define HFI_PROPERTY_PARAM_S3D_FRAME_PACKING_EXTRADATA \
+ (HFI_PROPERTY_PARAM_OX_START + 0x009)
+
+#define HFI_PROPERTY_CONFIG_OX_START \
+ (HFI_DOMAIN_BASE_COMMON + HFI_ARCH_OX_OFFSET + 0x02000)
+#define HFI_PROPERTY_CONFIG_BUFFER_REQUIREMENTS \
+ (HFI_PROPERTY_CONFIG_OX_START + 0x001)
+#define HFI_PROPERTY_CONFIG_REALTIME \
+ (HFI_PROPERTY_CONFIG_OX_START + 0x002)
+#define HFI_PROPERTY_CONFIG_PRIORITY \
+ (HFI_PROPERTY_CONFIG_OX_START + 0x003)
+#define HFI_PROPERTY_CONFIG_BATCH_INFO \
+ (HFI_PROPERTY_CONFIG_OX_START + 0x004)
+
+#define HFI_PROPERTY_PARAM_VDEC_OX_START \
+ (HFI_DOMAIN_BASE_VDEC + HFI_ARCH_OX_OFFSET + 0x3000)
+#define HFI_PROPERTY_PARAM_VDEC_CONTINUE_DATA_TRANSFER \
+ (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x001)
+#define HFI_PROPERTY_PARAM_VDEC_DISPLAY_PICTURE_BUFFER_COUNT\
+ (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x002)
+#define HFI_PROPERTY_PARAM_VDEC_MULTI_VIEW_SELECT \
+ (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x003)
+#define HFI_PROPERTY_PARAM_VDEC_PICTURE_TYPE_DECODE \
+ (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x004)
+#define HFI_PROPERTY_PARAM_VDEC_OUTPUT_ORDER \
+ (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x005)
+#define HFI_PROPERTY_PARAM_VDEC_MB_QUANTIZATION \
+ (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x006)
+#define HFI_PROPERTY_PARAM_VDEC_NUM_CONCEALED_MB \
+ (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x007)
+#define HFI_PROPERTY_PARAM_VDEC_H264_ENTROPY_SWITCHING \
+ (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x008)
+#define HFI_PROPERTY_PARAM_VDEC_OUTPUT2_KEEP_ASPECT_RATIO\
+ (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x009)
+#define HFI_PROPERTY_PARAM_VDEC_FRAME_RATE_EXTRADATA \
+ (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x00A)
+#define HFI_PROPERTY_PARAM_VDEC_PANSCAN_WNDW_EXTRADATA \
+ (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x00B)
+#define HFI_PROPERTY_PARAM_VDEC_RECOVERY_POINT_SEI_EXTRADATA \
+ (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x00C)
+#define HFI_PROPERTY_PARAM_VDEC_THUMBNAIL_MODE \
+ (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x00D)
+
+#define HFI_PROPERTY_PARAM_VDEC_FRAME_ASSEMBLY \
+ (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x00E)
+#define HFI_PROPERTY_PARAM_VDEC_CLOSED_CAPTION_EXTRADATA \
+ (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x00F)
+#define HFI_PROPERTY_PARAM_VDEC_AFD_EXTRADATA \
+ (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x010)
+#define HFI_PROPERTY_PARAM_VDEC_VC1_FRAMEDISP_EXTRADATA \
+ (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x011)
+#define HFI_PROPERTY_PARAM_VDEC_VC1_SEQDISP_EXTRADATA \
+ (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x012)
+#define HFI_PROPERTY_PARAM_VDEC_TIMESTAMP_EXTRADATA \
+ (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x013)
+#define HFI_PROPERTY_PARAM_VDEC_INTERLACE_VIDEO_EXTRADATA \
+ (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x014)
+#define HFI_PROPERTY_PARAM_VDEC_AVC_SESSION_SELECT \
+ (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x015)
+
+#define HFI_PROPERTY_CONFIG_VDEC_OX_START \
+ (HFI_DOMAIN_BASE_VDEC + HFI_ARCH_OX_OFFSET + 0x0000)
+#define HFI_PROPERTY_CONFIG_VDEC_POST_LOOP_DEBLOCKER \
+ (HFI_PROPERTY_CONFIG_VDEC_OX_START + 0x001)
+#define HFI_PROPERTY_CONFIG_VDEC_MB_ERROR_MAP_REPORTING \
+ (HFI_PROPERTY_CONFIG_VDEC_OX_START + 0x002)
+#define HFI_PROPERTY_CONFIG_VDEC_MB_ERROR_MAP \
+ (HFI_PROPERTY_CONFIG_VDEC_OX_START + 0x003)
+
+#define HFI_PROPERTY_PARAM_VENC_OX_START \
+ (HFI_DOMAIN_BASE_VENC + HFI_ARCH_OX_OFFSET + 0x5000)
+#define HFI_PROPERTY_PARAM_VENC_MULTI_SLICE_INFO \
+ (HFI_PROPERTY_PARAM_VENC_OX_START + 0x001)
+#define HFI_PROPERTY_PARAM_VENC_H264_IDR_S3D_FRAME_PACKING_NAL \
+ (HFI_PROPERTY_PARAM_VENC_OX_START + 0x002)
+
+#define HFI_PROPERTY_CONFIG_VENC_OX_START \
+ (HFI_DOMAIN_BASE_VENC + HFI_ARCH_OX_OFFSET + 0x6000)
+#define HFI_PROPERTY_CONFIG_VENC_FRAME_QP \
+ (HFI_PROPERTY_CONFIG_VENC_OX_START + 0x001)
+
+#define HFI_PROPERTY_PARAM_VPE_OX_START \
+ (HFI_DOMAIN_BASE_VPE + HFI_ARCH_OX_OFFSET + 0x7000)
+#define HFI_PROPERTY_CONFIG_VPE_OX_START \
+ (HFI_DOMAIN_BASE_VPE + HFI_ARCH_OX_OFFSET + 0x8000)
+
+struct hfi_batch_info {
+ u32 input_batch_count;
+ u32 output_batch_count;
+};
+
+struct hfi_buffer_count_actual {
+ u32 buffer_type;
+ u32 buffer_count_actual;
+};
+
+struct hfi_buffer_requirements {
+ u32 buffer_type;
+ u32 buffer_size;
+ u32 buffer_region_size;
+ u32 buffer_hold_count;
+ u32 buffer_count_min;
+ u32 buffer_count_actual;
+ u32 contiguous;
+ u32 buffer_alignment;
+};
+
+#define HFI_CHROMA_SITE_0 (HFI_OX_BASE + 0x1)
+#define HFI_CHROMA_SITE_1 (HFI_OX_BASE + 0x2)
+#define HFI_CHROMA_SITE_2 (HFI_OX_BASE + 0x3)
+#define HFI_CHROMA_SITE_3 (HFI_OX_BASE + 0x4)
+#define HFI_CHROMA_SITE_4 (HFI_OX_BASE + 0x5)
+#define HFI_CHROMA_SITE_5 (HFI_OX_BASE + 0x6)
+
+struct hfi_data_payload {
+ u32 size;
+ u8 rg_data[1];
+};
+
+struct hfi_enable_picture {
+ u32 picture_type;
+};
+
+struct hfi_display_picture_buffer_count {
+ int enable;
+ u32 count;
+};
+
+struct hfi_extra_data_header_config {
+ u32 type;
+ u32 buffer_type;
+ u32 version;
+ u32 port_index;
+ u32 client_extra_data_id;
+};
+
+struct hfi_interlace_format_supported {
+ u32 buffer_type;
+ u32 format;
+};
+
+struct hfi_mb_error_map {
+ u32 error_map_size;
+ u8 rg_error_map[1];
+};
+
+struct hfi_metadata_pass_through {
+ int enable;
+ u32 size;
+};
+
+struct hfi_multi_view_select {
+ u32 view_index;
+};
+
+#define HFI_PRIORITY_LOW 10
+#define HFI_PRIOIRTY_MEDIUM 20
+#define HFI_PRIORITY_HIGH 30
+
+#define HFI_OUTPUT_ORDER_DISPLAY (HFI_OX_BASE + 0x1)
+#define HFI_OUTPUT_ORDER_DECODE (HFI_OX_BASE + 0x2)
+
+#define HFI_RATE_CONTROL_OFF (HFI_OX_BASE + 0x1)
+#define HFI_RATE_CONTROL_VBR_VFR (HFI_OX_BASE + 0x2)
+#define HFI_RATE_CONTROL_VBR_CFR (HFI_OX_BASE + 0x3)
+#define HFI_RATE_CONTROL_CBR_VFR (HFI_OX_BASE + 0x4)
+#define HFI_RATE_CONTROL_CBR_CFR (HFI_OX_BASE + 0x5)
+
+struct hfi_uncompressed_plane_actual_constraints_info {
+ u32 buffer_type;
+ u32 num_planes;
+ struct hfi_uncompressed_plane_constraints rg_plane_format[1];
+};
+
+#define HFI_CMD_SYS_OX_START \
+(HFI_DOMAIN_BASE_COMMON + HFI_ARCH_OX_OFFSET + HFI_CMD_START_OFFSET + 0x0000)
+#define HFI_CMD_SYS_SESSION_ABORT (HFI_CMD_SYS_OX_START + 0x001)
+#define HFI_CMD_SYS_PING (HFI_CMD_SYS_OX_START + 0x002)
+
+#define HFI_CMD_SESSION_OX_START \
+(HFI_DOMAIN_BASE_COMMON + HFI_ARCH_OX_OFFSET + HFI_CMD_START_OFFSET + 0x1000)
+#define HFI_CMD_SESSION_LOAD_RESOURCES (HFI_CMD_SESSION_OX_START + 0x001)
+#define HFI_CMD_SESSION_START (HFI_CMD_SESSION_OX_START + 0x002)
+#define HFI_CMD_SESSION_STOP (HFI_CMD_SESSION_OX_START + 0x003)
+#define HFI_CMD_SESSION_EMPTY_BUFFER (HFI_CMD_SESSION_OX_START + 0x004)
+#define HFI_CMD_SESSION_FILL_BUFFER (HFI_CMD_SESSION_OX_START + 0x005)
+#define HFI_CMD_SESSION_SUSPEND (HFI_CMD_SESSION_OX_START + 0x006)
+#define HFI_CMD_SESSION_RESUME (HFI_CMD_SESSION_OX_START + 0x007)
+#define HFI_CMD_SESSION_FLUSH (HFI_CMD_SESSION_OX_START + 0x008)
+#define HFI_CMD_SESSION_GET_PROPERTY (HFI_CMD_SESSION_OX_START + 0x009)
+#define HFI_CMD_SESSION_PARSE_SEQUENCE_HEADER \
+ (HFI_CMD_SESSION_OX_START + 0x00A)
+#define HFI_CMD_SESSION_RELEASE_BUFFERS \
+ (HFI_CMD_SESSION_OX_START + 0x00B)
+#define HFI_CMD_SESSION_RELEASE_RESOURCES \
+ (HFI_CMD_SESSION_OX_START + 0x00C)
+
+#define HFI_MSG_SYS_OX_START \
+(HFI_DOMAIN_BASE_COMMON + HFI_ARCH_OX_OFFSET + HFI_MSG_START_OFFSET + 0x0000)
+#define HFI_MSG_SYS_IDLE (HFI_MSG_SYS_OX_START + 0x1)
+#define HFI_MSG_SYS_PING_ACK (HFI_MSG_SYS_OX_START + 0x2)
+#define HFI_MSG_SYS_PROPERTY_INFO (HFI_MSG_SYS_OX_START + 0x3)
+#define HFI_MSG_SYS_SESSION_ABORT_DONE (HFI_MSG_SYS_OX_START + 0x4)
+
+#define HFI_MSG_SESSION_OX_START \
+(HFI_DOMAIN_BASE_COMMON + HFI_ARCH_OX_OFFSET + HFI_MSG_START_OFFSET + 0x1000)
+#define HFI_MSG_SESSION_LOAD_RESOURCES_DONE (HFI_MSG_SESSION_OX_START + 0x1)
+#define HFI_MSG_SESSION_START_DONE (HFI_MSG_SESSION_OX_START + 0x2)
+#define HFI_MSG_SESSION_STOP_DONE (HFI_MSG_SESSION_OX_START + 0x3)
+#define HFI_MSG_SESSION_SUSPEND_DONE (HFI_MSG_SESSION_OX_START + 0x4)
+#define HFI_MSG_SESSION_RESUME_DONE (HFI_MSG_SESSION_OX_START + 0x5)
+#define HFI_MSG_SESSION_FLUSH_DONE (HFI_MSG_SESSION_OX_START + 0x6)
+#define HFI_MSG_SESSION_EMPTY_BUFFER_DONE (HFI_MSG_SESSION_OX_START + 0x7)
+#define HFI_MSG_SESSION_FILL_BUFFER_DONE (HFI_MSG_SESSION_OX_START + 0x8)
+#define HFI_MSG_SESSION_PROPERTY_INFO (HFI_MSG_SESSION_OX_START + 0x9)
+#define HFI_MSG_SESSION_RELEASE_RESOURCES_DONE \
+ (HFI_MSG_SESSION_OX_START + 0xA)
+#define HFI_MSG_SESSION_PARSE_SEQUENCE_HEADER_DONE \
+ (HFI_MSG_SESSION_OX_START + 0xB)
+#define HFI_MSG_SESSION_RELEASE_BUFFERS_DONE \
+ (HFI_MSG_SESSION_OX_START + 0xC)
+
+#define HFI_MIN_PKT_SIZE 8
+
+struct hfi_cmd_sys_session_abort_packet {
+ u32 size;
+ u32 packet_type;
+ u32 session_id;
+};
+
+struct hfi_cmd_sys_ping_packet {
+ u32 size;
+ u32 packet_type;
+ u32 client_data;
+};
+
+struct hfi_cmd_session_load_resources_packet {
+ u32 size;
+ u32 packet_type;
+ u32 session_id;
+};
+
+struct hfi_cmd_session_start_packet {
+ u32 size;
+ u32 packet_type;
+ u32 session_id;
+};
+
+struct hfi_cmd_session_stop_packet {
+ u32 size;
+ u32 packet_type;
+ u32 session_id;
+};
+
+struct hfi_cmd_session_empty_buffer_compressed_packet {
+ u32 size;
+ u32 packet_type;
+ u32 session_id;
+ u32 time_stamp_hi;
+ u32 time_stamp_lo;
+ u32 flags;
+ u32 mark_target;
+ u32 mark_data;
+ u32 offset;
+ u32 alloc_len;
+ u32 filled_len;
+ u32 input_tag;
+ u8 *packet_buffer;
+ u8 *extra_data_buffer;
+ u32 rgData[0];
+};
+
+struct hfi_cmd_session_empty_buffer_uncompressed_plane0_packet {
+ u32 size;
+ u32 packet_type;
+ u32 session_id;
+ u32 view_id;
+ u32 time_stamp_hi;
+ u32 time_stamp_lo;
+ u32 flags;
+ u32 mark_target;
+ u32 mark_data;
+ u32 alloc_len;
+ u32 filled_len;
+ u32 offset;
+ u32 input_tag;
+ u8 *packet_buffer;
+ u8 *extra_data_buffer;
+ u32 rgData[0];
+};
+
+struct hfi_cmd_session_empty_buffer_uncompressed_plane1_packet {
+ u32 flags;
+ u32 alloc_len;
+ u32 filled_len;
+ u32 offset;
+ u8 *packet_buffer2;
+ u32 rgData[0];
+};
+
+struct hfi_cmd_session_empty_buffer_uncompressed_plane2_packet {
+ u32 flags;
+ u32 alloc_len;
+ u32 filled_len;
+ u32 offset;
+ u8 *packet_buffer3;
+ u32 rgData[0];
+};
+
+struct hfi_cmd_session_fill_buffer_packet {
+ u32 size;
+ u32 packet_type;
+ u32 session_id;
+ u32 stream_id;
+ u32 offset;
+ u32 alloc_len;
+ u32 filled_len;
+ u32 output_tag;
+ u8 *packet_buffer;
+ u8 *extra_data_buffer;
+ u32 rgData[0];
+};
+
+struct hfi_cmd_session_flush_packet {
+ u32 size;
+ u32 packet_type;
+ u32 session_id;
+ u32 flush_type;
+};
+
+struct hfi_cmd_session_suspend_packet {
+ u32 size;
+ u32 packet_type;
+ u32 session_id;
+};
+
+struct hfi_cmd_session_resume_packet {
+ u32 size;
+ u32 packet_type;
+ u32 session_id;
+};
+
+struct hfi_cmd_session_get_property_packet {
+ u32 size;
+ u32 packet_type;
+ u32 session_id;
+ u32 num_properties;
+ u32 rg_property_data[1];
+};
+
+struct hfi_cmd_session_release_buffer_packet {
+ u32 size;
+ u32 packet_type;
+ u32 session_id;
+ u32 buffer_type;
+ u32 buffer_size;
+ u32 extra_data_size;
+ int response_req;
+ u32 num_buffers;
+ u32 rg_buffer_info[1];
+};
+
+struct hfi_cmd_session_release_resources_packet {
+ u32 size;
+ u32 packet_type;
+ u32 session_id;
+};
+
+struct hfi_cmd_session_parse_sequence_header_packet {
+ u32 size;
+ u32 packet_type;
+ u32 session_id;
+ u32 header_len;
+ u8 *packet_buffer;
+};
+
+struct hfi_msg_sys_session_abort_done_packet {
+ u32 size;
+ u32 packet_type;
+ u32 session_id;
+ u32 error_type;
+};
+
+struct hfi_msg_sys_idle_packet {
+ u32 size;
+ u32 packet_type;
+};
+
+struct hfi_msg_sys_ping_ack_packet {
+ u32 size;
+ u32 packet_type;
+ u32 client_data;
+};
+
+struct hfi_msg_sys_property_info_packet {
+ u32 size;
+ u32 packet_type;
+ u32 num_properties;
+ u32 rg_property_data[1];
+};
+
+struct hfi_msg_session_load_resources_done_packet {
+ u32 size;
+ u32 packet_type;
+ u32 session_id;
+ u32 error_type;
+};
+
+struct hfi_msg_session_start_done_packet {
+ u32 size;
+ u32 packet_type;
+ u32 session_id;
+ u32 error_type;
+};
+
+struct hfi_msg_session_stop_done_packet {
+ u32 size;
+ u32 packet_type;
+ u32 session_id;
+ u32 error_type;
+};
+
+struct hfi_msg_session_suspend_done_packet {
+ u32 size;
+ u32 packet_type;
+ u32 session_id;
+ u32 error_type;
+};
+
+struct hfi_msg_session_resume_done_packet {
+ u32 size;
+ u32 packet_type;
+ u32 session_id;
+ u32 error_type;
+};
+
+struct hfi_msg_session_flush_done_packet {
+ u32 size;
+ u32 packet_type;
+ u32 session_id;
+ u32 error_type;
+ u32 flush_type;
+};
+
+struct hfi_msg_session_empty_buffer_done_packet {
+ u32 size;
+ u32 packet_type;
+ u32 session_id;
+ u32 error_type;
+ u32 offset;
+ u32 filled_len;
+ u32 input_tag;
+ u8 *packet_buffer;
+ u8 *extra_data_buffer;
+ u32 rgData[0];
+};
+
+struct hfi_msg_session_fill_buffer_done_compressed_packet {
+ u32 size;
+ u32 packet_type;
+ u32 session_id;
+ u32 time_stamp_hi;
+ u32 time_stamp_lo;
+ u32 error_type;
+ u32 flags;
+ u32 mark_target;
+ u32 mark_data;
+ u32 stats;
+ u32 offset;
+ u32 alloc_len;
+ u32 filled_len;
+ u32 input_tag;
+ u32 output_tag;
+ u32 picture_type;
+ u8 *packet_buffer;
+ u8 *extra_data_buffer;
+ u32 rgData[0];
+};
+
+struct hfi_msg_session_fbd_uncompressed_plane0_packet {
+ u32 size;
+ u32 packet_type;
+ u32 session_id;
+ u32 stream_id;
+ u32 view_id;
+ u32 error_type;
+ u32 time_stamp_hi;
+ u32 time_stamp_lo;
+ u32 flags;
+ u32 mark_target;
+ u32 mark_data;
+ u32 stats;
+ u32 alloc_len;
+ u32 filled_len;
+ u32 offset;
+ u32 frame_width;
+ u32 frame_height;
+ u32 start_x_coord;
+ u32 start_y_coord;
+ u32 input_tag;
+ u32 input_tag2;
+ u32 output_tag;
+ u32 picture_type;
+ u8 *packet_buffer;
+ u8 *extra_data_buffer;
+ u32 rgData[0];
+};
+
+struct hfi_msg_session_fill_buffer_done_uncompressed_plane1_packet {
+ u32 flags;
+ u32 alloc_len;
+ u32 filled_len;
+ u32 offset;
+ u8 *packet_buffer2;
+ u32 rgData[0];
+};
+
+struct hfi_msg_session_fill_buffer_done_uncompressed_plane2_packet {
+ u32 flags;
+ u32 alloc_len;
+ u32 filled_len;
+ u32 offset;
+ u8 *packet_buffer3;
+ u32 rgData[0];
+};
+
+struct hfi_msg_session_parse_sequence_header_done_packet {
+ u32 size;
+ u32 packet_type;
+ u32 session_id;
+ u32 error_type;
+ u32 num_properties;
+ u32 rg_property_data[1];
+};
+
+struct hfi_msg_session_property_info_packet {
+ u32 size;
+ u32 packet_type;
+ u32 session_id;
+ u32 num_properties;
+ u32 rg_property_data[1];
+};
+
+struct hfi_msg_session_release_resources_done_packet {
+ u32 size;
+ u32 packet_type;
+ u32 session_id;
+ u32 error_type;
+};
+
+struct hfi_msg_session_release_buffers_done_packet {
+ u32 size;
+ u32 packet_type;
+ u32 session_id;
+ u32 error_type;
+ u32 num_buffers;
+ u32 rg_buffer_info[1];
+};
+
+struct hfi_extradata_mb_quantization_payload {
+ u8 rg_mb_qp[1];
+};
+
+struct hfi_extradata_vc1_pswnd {
+ u32 ps_wnd_h_offset;
+ u32 ps_wnd_v_offset;
+ u32 ps_wnd_width;
+ u32 ps_wnd_height;
+};
+
+struct hfi_extradata_vc1_framedisp_payload {
+ u32 res_pic;
+ u32 ref;
+ u32 range_map_present;
+ u32 range_map_y;
+ u32 range_map_uv;
+ u32 num_pan_scan_wnds;
+ struct hfi_extradata_vc1_pswnd rg_ps_wnd[1];
+};
+
+struct hfi_extradata_vc1_seqdisp_payload {
+ u32 prog_seg_frm;
+ u32 uv_sampling_fmt;
+ u32 color_fmt_flag;
+ u32 color_primaries;
+ u32 transfer_char;
+ u32 mat_coeff;
+ u32 aspect_ratio;
+ u32 aspect_horiz;
+ u32 aspect_vert;
+};
+
+struct hfi_extradata_timestamp_payload {
+ u32 time_stamp_low;
+ u32 time_stamp_high;
+};
+
+
+struct hfi_extradata_s3d_frame_packing_payload {
+ u32 fpa_id;
+ int cancel_flag;
+ u32 fpa_type;
+ int quin_cunx_flag;
+ u32 content_interprtation_type;
+ int spatial_flipping_flag;
+ int frame0_flipped_flag;
+ int field_views_flag;
+ int current_frame_isFrame0_flag;
+ int frame0_self_contained_flag;
+ int frame1_self_contained_flag;
+ u32 frame0_graid_pos_x;
+ u32 frame0_graid_pos_y;
+ u32 frame1_graid_pos_x;
+ u32 frame1_graid_pos_y;
+ u32 fpa_reserved_byte;
+ u32 fpa_repetition_period;
+ int fpa_extension_flag;
+};
+
+struct hfi_extradata_interlace_video_payload {
+ u32 format;
+};
+
+struct hfi_extradata_num_concealed_mb_payload {
+ u32 num_mb_concealed;
+};
+
+struct hfi_extradata_sliceinfo {
+ u32 offset_in_stream;
+ u32 slice_length;
+};
+
+struct hfi_extradata_multislice_info_payload {
+ u32 num_slices;
+ struct hfi_extradata_sliceinfo rg_slice_info[1];
+};
+
+struct hfi_index_extradata_input_crop_payload {
+ u32 size;
+ u32 version;
+ u32 port_index;
+ u32 left;
+ u32 top;
+ u32 width;
+ u32 height;
+};
+
+struct hfi_index_extradata_digital_zoom_payload {
+ u32 size;
+ u32 version;
+ u32 port_index;
+ int width;
+ int height;
+};
+
+struct hfi_index_extradata_aspect_ratio_payload {
+ u32 size;
+ u32 version;
+ u32 port_index;
+ u32 aspect_width;
+ u32 aspect_height;
+};
+struct hfi_extradata_panscan_wndw_payload {
+ u32 num_window;
+ struct hfi_extradata_vc1_pswnd wnd[1];
+};
+
+struct hfi_extradata_frame_type_payload {
+ u32 frame_rate;
+};
+
+struct hfi_extradata_recovery_point_sei_payload {
+ u32 flag;
+};
+
+struct hal_session {
+ struct list_head list;
+ u32 session_id;
+ u32 is_decoder;
+ void *device;
+};
+
+struct hal_device_data {
+ struct list_head dev_head;
+ int dev_count;
+};
+
+extern struct hal_device_data hal_ctxt;
+
+void hfi_process_msg_packet(msm_vidc_callback callback,
+ u32 device_id, struct vidc_hal_msg_pkt_hdr *msg_hdr);
+#endif
+
diff --git a/drivers/media/video/msm_vidc/vidc_hfi_api.h b/drivers/media/video/msm_vidc/vidc_hfi_api.h
index 79fee90..e12153a 100644
--- a/drivers/media/video/msm_vidc/vidc_hfi_api.h
+++ b/drivers/media/video/msm_vidc/vidc_hfi_api.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,7 +14,9 @@
#ifndef __VIDC_HFI_API_H__
#define __VIDC_HFI_API_H__
+#include <linux/platform_device.h>
#include <linux/types.h>
+#include <media/msm_vidc.h>
#define CONTAINS(__a, __sz, __t) ({\
int __rc = __t >= __a && \
@@ -405,6 +407,12 @@
HAL_UNUSED_COLOR = 0x10000000,
};
+enum hal_ssr_trigger_type {
+ SSR_ERR_FATAL = 1,
+ SSR_SW_DIV_BY_ZERO,
+ SSR_HW_WDOG_IRQ,
+};
+
struct hal_uncompressed_format_select {
enum hal_buffer buffer_type;
enum hal_uncompressed_format format;
@@ -513,6 +521,7 @@
struct hal_bitrate {
u32 bit_rate;
+ u32 layer_id;
};
struct hal_profile_level {
@@ -587,6 +596,7 @@
u32 qpi;
u32 qpp;
u32 qpb;
+ u32 layer_id;
};
struct hal_intra_period {
@@ -920,8 +930,8 @@
u32 offset1;
u32 frame_width;
u32 frame_height;
- u32 start_xCoord;
- u32 start_yCoord;
+ u32 start_x_coord;
+ u32 start_y_coord;
u32 input_tag;
u32 input_tag1;
enum hal_picture picture_type;
@@ -979,44 +989,85 @@
struct hal_buffer_requirements buffer[HAL_BUFFER_MAX];
};
-/* VIDC_HAL CORE API's */
-int venus_hfi_core_init(void *device);
-int venus_hfi_core_release(void *device);
-int venus_hfi_core_pc_prep(void *device);
-int venus_hfi_core_set_resource(void *device,
- struct vidc_resource_hdr *resource_hdr, void *resource_value);
-int venus_hfi_core_release_resource(void *device,
- struct vidc_resource_hdr *resource_hdr);
-int venus_hfi_core_ping(void *device);
+enum msm_vidc_hfi_type {
+ VIDC_HFI_VENUS,
+ VIDC_HFI_Q6,
+};
-/* VIDC_HAL SESSION API's */
-void *venus_hfi_session_init(void *device, u32 session_id,
- enum hal_domain session_type, enum hal_video_codec codec_type);
-int venus_hfi_session_end(void *session);
-int venus_hfi_session_abort(void *session);
-int venus_hfi_session_set_buffers(void *sess,
- struct vidc_buffer_addr_info *buffer_info);
-int venus_hfi_session_release_buffers(void *sess,
- struct vidc_buffer_addr_info *buffer_info);
-int venus_hfi_session_load_res(void *sess);
-int venus_hfi_session_release_res(void *sess);
-int venus_hfi_session_start(void *sess);
-int venus_hfi_session_stop(void *sess);
-int venus_hfi_session_suspend(void *sess);
-int venus_hfi_session_resume(void *sess);
-int venus_hfi_session_etb(void *sess,
- struct vidc_frame_data *input_frame);
-int venus_hfi_session_ftb(void *sess,
- struct vidc_frame_data *output_frame);
-int venus_hfi_session_parse_seq_hdr(void *sess,
- struct vidc_seq_hdr *seq_hdr);
-int venus_hfi_session_get_seq_hdr(void *sess,
- struct vidc_seq_hdr *seq_hdr);
-int venus_hfi_session_get_buf_req(void *sess);
-int venus_hfi_session_flush(void *sess, enum hal_flush flush_mode);
-int venus_hfi_session_set_property(void *sess, enum hal_property ptype,
- void *pdata);
-int venus_hfi_session_get_property(void *sess, enum hal_property ptype,
- void *pdata);
+enum mem_type {
+ DDR_MEM = 0x1,
+ OCMEM_MEM = 0x2,
+};
+
+enum fw_info {
+ FW_BASE_ADDRESS,
+ FW_REGISTER_BASE,
+ FW_REGISTER_SIZE,
+ FW_IRQ,
+ FW_INFO_MAX,
+};
+
+struct hfi_device {
+ void *hfi_device_data;
+
+ /*Add function pointers for all the hfi functions below*/
+ int (*core_init)(void *device);
+ int (*core_release)(void *device);
+ int (*core_pc_prep)(void *device);
+ int (*core_ping)(void *device);
+ int (*core_trigger_ssr)(void *device, enum hal_ssr_trigger_type);
+ void *(*session_init)(void *device, u32 session_id,
+ enum hal_domain session_type, enum hal_video_codec codec_type);
+ int (*session_end)(void *session);
+ int (*session_abort)(void *session);
+ int (*session_set_buffers)(void *sess,
+ struct vidc_buffer_addr_info *buffer_info);
+ int (*session_release_buffers)(void *sess,
+ struct vidc_buffer_addr_info *buffer_info);
+ int (*session_load_res)(void *sess);
+ int (*session_release_res)(void *sess);
+ int (*session_start)(void *sess);
+ int (*session_stop)(void *sess);
+ int (*session_suspend)(void *sess);
+ int (*session_resume)(void *sess);
+ int (*session_etb)(void *sess,
+ struct vidc_frame_data *input_frame);
+ int (*session_ftb)(void *sess,
+ struct vidc_frame_data *output_frame);
+ int (*session_parse_seq_hdr)(void *sess,
+ struct vidc_seq_hdr *seq_hdr);
+ int (*session_get_seq_hdr)(void *sess,
+ struct vidc_seq_hdr *seq_hdr);
+ int (*session_get_buf_req)(void *sess);
+ int (*session_flush)(void *sess, enum hal_flush flush_mode);
+ int (*session_set_property)(void *sess, enum hal_property ptype,
+ void *pdata);
+ int (*session_get_property)(void *sess, enum hal_property ptype,
+ void *pdata);
+ int (*scale_clocks)(void *dev, int load);
+ int (*scale_bus)(void *dev, int load,
+ enum session_type type, enum mem_type mtype);
+ 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 (*get_domain)(void *dev, enum msm_vidc_io_maps iomap);
+ int (*iommu_get_map)(void *dev,
+ struct msm_vidc_iommu_info maps[MAX_MAP]);
+ int (*load_fw)(void *dev);
+ void (*unload_fw)(void *dev);
+ int (*get_fw_info)(void *dev, enum fw_info info);
+};
+
+typedef void (*hfi_cmd_response_callback) (enum command_response cmd,
+ void *data);
+typedef void (*msm_vidc_callback) (u32 response, void *callback);
+
+void *vidc_hfi_initialize(enum msm_vidc_hfi_type hfi_type, u32 device_id,
+ struct platform_device *pdev,
+ hfi_cmd_response_callback callback);
+void vidc_hfi_deinitialize(enum msm_vidc_hfi_type hfi_type,
+ struct hfi_device *hdev);
+
#endif /*__VIDC_HFI_API_H__ */
diff --git a/drivers/media/video/msm_vidc/vidc_hfi_helper.h b/drivers/media/video/msm_vidc/vidc_hfi_helper.h
index 7531811..37c051e 100644
--- a/drivers/media/video/msm_vidc/vidc_hfi_helper.h
+++ b/drivers/media/video/msm_vidc/vidc_hfi_helper.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
@@ -685,6 +685,11 @@
#define HFI_MSG_SESSION_GET_SEQUENCE_HEADER_DONE \
(HFI_MSG_SESSION_COMMON_START + 0x2)
+#define HFI_CMD_SYS_TEST_SSR (HFI_CMD_SYS_TEST_START + 0x1)
+#define HFI_TEST_SSR_SW_ERR_FATAL 0x1
+#define HFI_TEST_SSR_SW_DIV_BY_ZERO 0x2
+#define HFI_TEST_SSR_HW_WDOG_IRQ 0x3
+
struct vidc_hal_msg_pkt_hdr {
u32 size;
u32 packet;
@@ -871,4 +876,10 @@
u8 rg_data[1];
};
+struct hfi_cmd_sys_test_ssr_packet {
+ u32 size;
+ u32 packet_type;
+ u32 trigger_type;
+};
+
#endif
diff --git a/drivers/media/video/msm_wfd/mdp-4-subdev.c b/drivers/media/video/msm_wfd/mdp-4-subdev.c
index c68d5d4..d2ecd22 100644
--- a/drivers/media/video/msm_wfd/mdp-4-subdev.c
+++ b/drivers/media/video/msm_wfd/mdp-4-subdev.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, Code Aurora Forum. 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
@@ -11,6 +11,7 @@
*
*/
#include <linux/msm_mdp.h>
+#include <linux/switch.h>
#include <mach/iommu_domains.h>
#include <media/videobuf2-core.h>
#include "enc-subdev.h"
@@ -23,6 +24,7 @@
u32 width;
bool secure;
bool uses_iommu_split_domain;
+ struct switch_dev sdev;
};
int mdp_init(struct v4l2_subdev *sd, u32 val)
@@ -54,14 +56,13 @@
rc = -ENODEV;
goto mdp_open_fail;
}
-
- /*Tell HDMI daemon to open fb2*/
- rc = kobject_uevent(&fbi->dev->kobj, KOBJ_ADD);
+ inst->sdev.name = "wfd";
+ /* Register wfd node to switch driver */
+ rc = switch_dev_register(&inst->sdev);
if (rc) {
- WFD_MSG_ERR("Failed add to kobj");
+ WFD_MSG_ERR("WFD switch registration failed\n");
goto mdp_open_fail;
}
-
msm_fb_writeback_init(fbi);
inst->mdp = fbi;
inst->secure = mops->secure;
@@ -91,9 +92,8 @@
rc = -ENODEV;
goto exit;
}
- rc = kobject_uevent(&fbi->dev->kobj, KOBJ_ONLINE);
- if (rc)
- WFD_MSG_ERR("Failed to send ONLINE event\n");
+ switch_set_state(&inst->sdev, true);
+ WFD_MSG_DBG("wfd state switched to %d\n", inst->sdev.state);
}
exit:
return rc;
@@ -110,11 +110,8 @@
return rc;
}
fbi = (struct fb_info *)inst->mdp;
- rc = kobject_uevent(&fbi->dev->kobj, KOBJ_OFFLINE);
- if (rc) {
- WFD_MSG_ERR("Failed to send offline event\n");
- return -EIO;
- }
+ switch_set_state(&inst->sdev, false);
+ WFD_MSG_DBG("wfd state switched to %d\n", inst->sdev.state);
}
return 0;
}
@@ -126,6 +123,8 @@
fbi = (struct fb_info *)inst->mdp;
msm_fb_writeback_terminate(fbi);
kfree(inst);
+ /* Unregister wfd node from switch driver */
+ switch_dev_unregister(&inst->sdev);
}
return 0;
}
diff --git a/drivers/media/video/msm_wfd/mdp-5-subdev.c b/drivers/media/video/msm_wfd/mdp-5-subdev.c
index 4f29389..5b49498 100644
--- a/drivers/media/video/msm_wfd/mdp-5-subdev.c
+++ b/drivers/media/video/msm_wfd/mdp-5-subdev.c
@@ -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
@@ -11,18 +11,19 @@
*
*/
#include <linux/msm_mdp.h>
+#include <linux/switch.h>
#include <mach/iommu_domains.h>
#include <media/videobuf2-core.h>
#include "enc-subdev.h"
#include "mdp-subdev.h"
#include "wfd-util.h"
-
struct mdp_instance {
struct fb_info *mdp;
u32 height;
u32 width;
bool secure;
+ struct switch_dev sdev;
};
int mdp_init(struct v4l2_subdev *sd, u32 val)
@@ -54,12 +55,13 @@
rc = -ENODEV;
goto mdp_open_fail;
}
-
- /*Tell HDMI daemon to open fb2*/
- rc = kobject_uevent(&fbi->dev->kobj, KOBJ_ADD);
- if (rc)
- WFD_MSG_ERR("Failed add to kobj");
-
+ inst->sdev.name = "wfd";
+ /* Register wfd node to switch driver */
+ rc = switch_dev_register(&inst->sdev);
+ if (rc) {
+ WFD_MSG_ERR("WFD switch registration failed\n");
+ goto mdp_open_fail;
+ }
msm_fb_writeback_init(fbi);
inst->mdp = fbi;
inst->secure = mops->secure;
@@ -88,9 +90,8 @@
rc = -ENODEV;
goto exit;
}
- rc = kobject_uevent(&fbi->dev->kobj, KOBJ_ONLINE);
- if (rc)
- WFD_MSG_ERR("Failed to send ONLINE event\n");
+ switch_set_state(&inst->sdev, true);
+ WFD_MSG_DBG("wfd state switched to %d\n", inst->sdev.state);
}
exit:
return rc;
@@ -108,11 +109,8 @@
return rc;
}
fbi = (struct fb_info *)inst->mdp;
- rc = kobject_uevent(&fbi->dev->kobj, KOBJ_OFFLINE);
- if (rc) {
- WFD_MSG_ERR("Failed to send offline event\n");
- return -EIO;
- }
+ switch_set_state(&inst->sdev, false);
+ WFD_MSG_DBG("wfd state switched to %d\n", inst->sdev.state);
}
return 0;
}
@@ -124,6 +122,8 @@
fbi = (struct fb_info *)inst->mdp;
msm_fb_writeback_terminate(fbi);
kfree(inst);
+ /* Unregister wfd node from switch driver */
+ switch_dev_unregister(&inst->sdev);
}
return 0;
}
diff --git a/drivers/media/video/msmb/Kconfig b/drivers/media/video/msmb/Kconfig
new file mode 100644
index 0000000..c128b0d
--- /dev/null
+++ b/drivers/media/video/msmb/Kconfig
@@ -0,0 +1,93 @@
+config MSM_CAMERA_SENSOR
+ bool "Qualcomm MSM camera sensor support"
+ depends on MSMB_CAMERA
+ ---help---
+ This flag enables support for Camera Sensor.
+ The sensor driver is capable of providing real time
+ data for camera support. The driver support V4L2
+ subdev APIs.
+
+config MSM_CPP
+ bool "Qualcomm MSM Camera Post Processing Engine support"
+ depends on MSMB_CAMERA
+ ---help---
+ Enable support for Camera Post-processing Engine
+ The Post processing engine is capable of scaling
+ and cropping image. The driver support V4L2 subdev
+ APIs.
+
+config MSM_CCI
+ bool "Qualcomm MSM Camera Control Interface support"
+ depends on MSMB_CAMERA
+ ---help---
+ Enable support for Camera Control Interface driver only
+ for those platforms that have hardware support. This driver
+ is responsible for handling I2C read and write on the I2C
+ bus. It is also responsible for synchronization with
+ GPIO and data frames.
+
+config MSM_CSI20_HEADER
+ bool "Qualcomm MSM CSI 2.0 Header"
+ depends on MSMB_CAMERA
+ ---help---
+ Enable support for CSI drivers to include 2.0
+ header. This header has register macros and its
+ values and bit mask for register configuration bits
+ This config macro is required targets based on 8960,
+ 8930 and 8064 platforms.
+
+config MSM_CSI30_HEADER
+ bool "Qualcomm MSM CSI 3.0 Header"
+ depends on MSMB_CAMERA
+ ---help---
+ Enable support for CSI drivers to include 3.0
+ header. This header has register macros and its
+ values and bit mask for register configuration bits
+ This config macro is required for targets based on
+ 8064 platforms.
+
+config MSM_CSIPHY
+ bool "Qualcomm MSM Camera Serial Interface Physical receiver support"
+ depends on MSMB_CAMERA
+ ---help---
+ Enable support for Camera Serial Interface
+ Physical receiver. It deserializes packets and
+ supports detection of packet start and stop
+ signalling.
+
+config MSM_CSID
+ bool "Qualcomm MSM Camera Serial Interface decoder support"
+ depends on MSMB_CAMERA
+ ---help---
+ Enable support for Camera Serial Interface decoder.
+ It supports lane merging and decoding of packets
+ based on cid which is mapped to a virtual channel
+ and datatype.
+
+config MSM_ISPIF
+ bool "Qualcomm MSM Image Signal Processing interface support"
+ depends on MSMB_CAMERA
+ ---help---
+ Enable support for Image Signal Processing interface module.
+ This module acts as a crossbar between CSID and VFE. Output
+ of any CID of CSID can be routed to of of pixel or raw
+ data interface in VFE.
+
+config S5K3L1YX
+ bool "Sensor S5K3L1YX (BAYER 12M)"
+ depends on MSMB_CAMERA
+ ---help---
+ Samsung 12 MP Bayer Sensor with auto focus, uses
+ 4 mipi lanes, preview config = 1984 * 1508 at 30 fps,
+ snapshot config = 4000 * 3000 at 20 fps,
+ hfr video at 60, 90 and 120 fps.
+
+config MSM_V4L2_VIDEO_OVERLAY_DEVICE
+ tristate "Qualcomm MSM V4l2 video overlay device"
+ ---help---
+ Enables support for the MSM V4L2 video
+ overlay driver. This allows video rendering
+ apps to render overlaid video using Video4Linux2
+ APIs, by using /dev/videoX device
+
+
diff --git a/drivers/media/video/msmb/Makefile b/drivers/media/video/msmb/Makefile
new file mode 100644
index 0000000..3986128
--- /dev/null
+++ b/drivers/media/video/msmb/Makefile
@@ -0,0 +1,14 @@
+ccflags-y += -Idrivers/media/video/msmb
+ccflags-y += -Idrivers/media/video/msmb/sensor
+ccflags-y += -Idrivers/media/video/msmb/codecs
+ccflags-y += -Idrivers/media/video/msmb/isps
+ccflags-y += -Idrivers/media/video/msmb/pps
+ccflags-y += -Idrivers/media/video/msmb/msm_vb2
+ccflags-y += -Idrivers/media/video/msmb/camera
+
+obj-$(CONFIG_MSMB_CAMERA) += msm.o
+obj-$(CONFIG_MSMB_CAMERA) += camera/
+obj-$(CONFIG_MSMB_CAMERA) += msm_vb2/
+obj-$(CONFIG_MSMB_CAMERA) += sensor/
+obj-$(CONFIG_MSMB_CAMERA) += isp/
+obj-$(CONFIG_MSMB_CAMERA) += ispif/
diff --git a/drivers/media/video/msmb/camera/Makefile b/drivers/media/video/msmb/camera/Makefile
new file mode 100644
index 0000000..89ff167
--- /dev/null
+++ b/drivers/media/video/msmb/camera/Makefile
@@ -0,0 +1,3 @@
+ccflags-y += -Idrivers/media/video/msmb
+ccflags-y += -Idrivers/media/video/msmb/msm_vb2
+obj-$(CONFIG_MSMB_CAMERA) += camera.o
diff --git a/drivers/media/video/msmb/camera/camera.c b/drivers/media/video/msmb/camera/camera.c
new file mode 100644
index 0000000..c726958
--- /dev/null
+++ b/drivers/media/video/msmb/camera/camera.c
@@ -0,0 +1,706 @@
+/* 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
+ * 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/of.h>
+#include <linux/module.h>
+#include <linux/workqueue.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/ioctl.h>
+#include <linux/spinlock.h>
+#include <linux/proc_fs.h>
+#include <linux/atomic.h>
+#include <linux/wait.h>
+#include <linux/videodev2.h>
+#include <linux/msm_ion.h>
+#include <linux/iommu.h>
+#include <linux/platform_device.h>
+#include <media/v4l2-fh.h>
+
+#include "camera.h"
+#include "msm.h"
+#include "msm_vb2.h"
+
+#define fh_to_private(__fh) \
+ container_of(__fh, struct camera_v4l2_private, fh)
+
+struct camera_v4l2_private {
+ struct v4l2_fh fh;
+ unsigned int stream_id;
+ struct vb2_queue vb2_q;
+};
+
+static void camera_pack_event(struct file *filep, int evt_id,
+ int command, struct v4l2_event *event)
+{
+ struct msm_v4l2_event_data *event_data =
+ (struct msm_v4l2_event_data *)&event->u.data[0];
+ struct msm_video_device *pvdev = video_drvdata(filep);
+ struct camera_v4l2_private *sp = fh_to_private(filep->private_data);
+
+ /* always MSM_CAMERA_V4L2_EVENT_TYPE */
+ event->type = MSM_CAMERA_V4L2_EVENT_TYPE;
+ event->id = evt_id;
+ event_data->command = command;
+ event_data->session_id = pvdev->vdev->num;
+ event_data->stream_id = sp->stream_id;
+}
+
+static int camera_check_event_status(struct v4l2_event *event)
+{
+ struct msm_v4l2_event_data *event_data =
+ (struct msm_v4l2_event_data *)&event->u.data[0];
+
+ if (event_data->status > MSM_CAMERA_ERR_EVT_BASE)
+ return -EFAULT;
+
+ return 0;
+}
+
+static int camera_v4l2_querycap(struct file *filep, void *fh,
+ struct v4l2_capability *cap)
+{
+ int rc;
+ struct v4l2_event event;
+
+ /* can use cap->driver to make differentiation */
+ camera_pack_event(filep, MSM_CAMERA_GET_PARM,
+ MSM_CAMERA_PRIV_QUERY_CAP, &event);
+
+ rc = msm_post_event(&event, MSM_POST_EVT_TIMEOUT);
+ if (rc < 0)
+ return rc;
+
+ rc = camera_check_event_status(&event);
+
+ return rc;
+}
+
+static int camera_v4l2_s_crop(struct file *filep, void *fh,
+ struct v4l2_crop *crop)
+{
+ int rc = 0;
+ struct v4l2_event event;
+
+ if (crop->type == V4L2_BUF_TYPE_PRIVATE) {
+
+ camera_pack_event(filep, MSM_CAMERA_SET_PARM,
+ MSM_CAMERA_PRIV_S_CROP, &event);
+
+ rc = msm_post_event(&event, MSM_POST_EVT_TIMEOUT);
+ if (rc < 0)
+ return rc;
+
+ rc = camera_check_event_status(&event);
+ }
+
+ return rc;
+}
+
+static int camera_v4l2_g_crop(struct file *filep, void *fh,
+ struct v4l2_crop *crop)
+{
+ int rc = 0;
+ struct v4l2_event event;
+
+ if (crop->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ camera_pack_event(filep, MSM_CAMERA_GET_PARM,
+ MSM_CAMERA_PRIV_G_CROP, &event);
+
+ rc = msm_post_event(&event, MSM_POST_EVT_TIMEOUT);
+ if (rc < 0)
+ return rc;
+
+ rc = camera_check_event_status(&event);
+ }
+
+ return rc;
+}
+
+static int camera_v4l2_queryctrl(struct file *filep, void *fh,
+ struct v4l2_queryctrl *ctrl)
+{
+ int rc = 0;
+ struct v4l2_event event;
+
+ if (ctrl->type == V4L2_CTRL_TYPE_MENU) {
+
+ camera_pack_event(filep, MSM_CAMERA_GET_PARM,
+ ctrl->id, &event);
+
+ rc = msm_post_event(&event, MSM_POST_EVT_TIMEOUT);
+ if (rc < 0)
+ return rc;
+
+ rc = camera_check_event_status(&event);
+ }
+
+ return rc;
+}
+
+static int camera_v4l2_g_ctrl(struct file *filep, void *fh,
+ struct v4l2_control *ctrl)
+{
+ int rc = 0;
+ struct v4l2_event event;
+
+ if (ctrl->id >= V4L2_CID_PRIVATE_BASE) {
+ camera_pack_event(filep, MSM_CAMERA_GET_PARM, ctrl->id, &event);
+
+ rc = msm_post_event(&event, MSM_POST_EVT_TIMEOUT);
+ if (rc < 0)
+ return rc;
+
+ rc = camera_check_event_status(&event);
+ }
+
+ return rc;
+}
+
+static int camera_v4l2_s_ctrl(struct file *filep, void *fh,
+ struct v4l2_control *ctrl)
+{
+ int rc = 0;
+ struct v4l2_event event;
+ if (ctrl->id >= V4L2_CID_PRIVATE_BASE) {
+ camera_pack_event(filep, MSM_CAMERA_SET_PARM, ctrl->id, &event);
+
+ rc = msm_post_event(&event, MSM_POST_EVT_TIMEOUT);
+ if (rc < 0)
+ return rc;
+
+ rc = camera_check_event_status(&event);
+ }
+
+ return rc;
+}
+
+static int camera_v4l2_reqbufs(struct file *filep, void *fh,
+ struct v4l2_requestbuffers *req)
+{
+ struct camera_v4l2_private *sp = fh_to_private(fh);
+
+ return vb2_reqbufs(&sp->vb2_q, req);
+}
+
+static int camera_v4l2_querybuf(struct file *filep, void *fh,
+ struct v4l2_buffer *pb)
+{
+ return 0;
+}
+
+static int camera_v4l2_qbuf(struct file *filep, void *fh,
+ struct v4l2_buffer *pb)
+{
+ struct camera_v4l2_private *sp = fh_to_private(fh);
+
+ return vb2_qbuf(&sp->vb2_q, pb);
+}
+
+static int camera_v4l2_dqbuf(struct file *filep, void *fh,
+ struct v4l2_buffer *pb)
+{
+ struct camera_v4l2_private *sp = fh_to_private(fh);
+
+ return vb2_dqbuf(&sp->vb2_q, pb, filep->f_flags & O_NONBLOCK);
+}
+
+static int camera_v4l2_streamon(struct file *filep, void *fh,
+ enum v4l2_buf_type buf_type)
+{
+ struct v4l2_event event;
+ int rc;
+ struct camera_v4l2_private *sp = fh_to_private(fh);
+
+ rc = vb2_streamon(&sp->vb2_q, buf_type);
+ camera_pack_event(filep, MSM_CAMERA_SET_PARM,
+ MSM_CAMERA_PRIV_STREAM_ON, &event);
+
+ rc = msm_post_event(&event, MSM_POST_EVT_TIMEOUT);
+ if (rc < 0)
+ return rc;
+
+ rc = camera_check_event_status(&event);
+ return rc;
+}
+
+static int camera_v4l2_streamoff(struct file *filep, void *fh,
+ enum v4l2_buf_type buf_type)
+{
+ struct v4l2_event event;
+ int rc;
+ struct camera_v4l2_private *sp = fh_to_private(fh);
+
+ camera_pack_event(filep, MSM_CAMERA_SET_PARM,
+ MSM_CAMERA_PRIV_STREAM_OFF, &event);
+
+ rc = msm_post_event(&event, MSM_POST_EVT_TIMEOUT);
+ if (rc < 0)
+ return rc;
+
+ rc = camera_check_event_status(&event);
+ vb2_streamoff(&sp->vb2_q, buf_type);
+ return rc;
+}
+
+static int camera_v4l2_g_fmt_cap_private(struct file *filep, void *fh,
+ struct v4l2_format *pfmt)
+{
+ int rc = -EINVAL;
+
+ if (pfmt->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ struct v4l2_event event;
+
+ camera_pack_event(filep, MSM_CAMERA_GET_PARM,
+ MSM_CAMERA_PRIV_G_FMT, &event);
+
+ rc = msm_post_event(&event, MSM_POST_EVT_TIMEOUT);
+ if (rc < 0)
+ return rc;
+
+ rc = camera_check_event_status(&event);
+ }
+
+ return rc;
+}
+
+static int camera_v4l2_s_fmt_cap_private(struct file *filep, void *fh,
+ struct v4l2_format *pfmt)
+{
+ int rc = 0;
+ int i = 0;
+ struct v4l2_event event;
+ struct camera_v4l2_private *sp = fh_to_private(fh);
+ struct msm_v4l2_format_data *user_fmt;
+
+ if (pfmt->type == V4L2_BUF_TYPE_PRIVATE) {
+
+ if (WARN_ON(!sp->vb2_q.drv_priv))
+ return -ENOMEM;
+
+ memcpy(sp->vb2_q.drv_priv, pfmt->fmt.raw_data,
+ sizeof(struct msm_v4l2_format_data));
+ user_fmt = (struct msm_v4l2_format_data *)sp->vb2_q.drv_priv;
+
+ pr_debug("%s: num planes :%c\n", __func__,
+ user_fmt->num_planes);
+ for (i = 0; i < user_fmt->num_planes; i++)
+ pr_debug("%s: plane size[%d]\n", __func__,
+ user_fmt->plane_sizes[i]);
+
+ camera_pack_event(filep, MSM_CAMERA_SET_PARM,
+ MSM_CAMERA_PRIV_S_FMT, &event);
+
+ rc = msm_post_event(&event, MSM_POST_EVT_TIMEOUT);
+ if (rc < 0)
+ goto set_fmt_fail;
+
+ rc = camera_check_event_status(&event);
+ if (rc < 0)
+ goto set_fmt_fail;
+ }
+
+ return rc;
+
+set_fmt_fail:
+ kfree(sp->vb2_q.drv_priv);
+ return rc;
+}
+
+static int camera_v4l2_try_fmt_cap_private(struct file *filep, void *fh,
+ struct v4l2_format *pfmt)
+{
+ return 0;
+}
+
+int camera_v4l2_g_fmt_vid_cap_mplane(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ return 0;
+}
+
+static int camera_v4l2_g_parm(struct file *filep, void *fh,
+ struct v4l2_streamparm *a)
+{
+ /* TODO */
+ return 0;
+}
+
+static int camera_v4l2_s_parm(struct file *filep, void *fh,
+ struct v4l2_streamparm *parm)
+{
+ int rc = 0;
+ struct v4l2_event event;
+ struct msm_v4l2_event_data *event_data =
+ (struct msm_v4l2_event_data *)&event.u.data[0];
+ struct camera_v4l2_private *sp = fh_to_private(fh);
+
+ camera_pack_event(filep, MSM_CAMERA_SET_PARM,
+ MSM_CAMERA_PRIV_NEW_STREAM, &event);
+
+ rc = msm_create_stream(event_data->session_id,
+ event_data->stream_id, &sp->vb2_q);
+ if (rc < 0)
+ return rc;
+
+ rc = msm_post_event(&event, MSM_POST_EVT_TIMEOUT);
+ if (rc < 0)
+ goto error;
+
+ rc = camera_check_event_status(&event);
+ if (rc < 0)
+ goto error;
+
+ /* use stream_id as stream index */
+ parm->parm.capture.extendedmode = sp->stream_id;
+
+ return rc;
+
+error:
+ msm_delete_stream(event_data->session_id,
+ event_data->stream_id);
+ return rc;
+}
+
+static int camera_v4l2_subscribe_event(struct v4l2_fh *fh,
+ struct v4l2_event_subscription *sub)
+{
+ int rc = 0;
+ struct camera_v4l2_private *sp = fh_to_private(fh);
+
+ rc = v4l2_event_subscribe(&sp->fh, sub, 5);
+
+ return rc;
+}
+
+static int camera_v4l2_unsubscribe_event(struct v4l2_fh *fh,
+ struct v4l2_event_subscription *sub)
+{
+ int rc = 0;
+ struct camera_v4l2_private *sp = fh_to_private(fh);
+
+ rc = v4l2_event_unsubscribe(&sp->fh, sub);
+
+ return rc;
+}
+
+static const struct v4l2_ioctl_ops camera_v4l2_ioctl_ops = {
+ .vidioc_querycap = camera_v4l2_querycap,
+ .vidioc_s_crop = camera_v4l2_s_crop,
+ .vidioc_g_crop = camera_v4l2_g_crop,
+ .vidioc_queryctrl = camera_v4l2_queryctrl,
+ .vidioc_g_ctrl = camera_v4l2_g_ctrl,
+ .vidioc_s_ctrl = camera_v4l2_s_ctrl,
+ .vidioc_reqbufs = camera_v4l2_reqbufs,
+ .vidioc_querybuf = camera_v4l2_querybuf,
+ .vidioc_qbuf = camera_v4l2_qbuf,
+ .vidioc_dqbuf = camera_v4l2_dqbuf,
+ .vidioc_streamon = camera_v4l2_streamon,
+ .vidioc_streamoff = camera_v4l2_streamoff,
+ .vidioc_g_fmt_type_private = camera_v4l2_g_fmt_cap_private,
+ .vidioc_s_fmt_type_private = camera_v4l2_s_fmt_cap_private,
+ .vidioc_try_fmt_type_private = camera_v4l2_try_fmt_cap_private,
+ .vidioc_g_fmt_vid_cap_mplane = camera_v4l2_g_fmt_vid_cap_mplane,
+
+ /* Stream type-dependent parameter ioctls */
+ .vidioc_g_parm = camera_v4l2_g_parm,
+ .vidioc_s_parm = camera_v4l2_s_parm,
+
+ /* event subscribe/unsubscribe */
+ .vidioc_subscribe_event = camera_v4l2_subscribe_event,
+ .vidioc_unsubscribe_event = camera_v4l2_unsubscribe_event,
+};
+
+static int camera_v4l2_fh_open(struct file *filep)
+{
+ struct msm_video_device *pvdev = video_drvdata(filep);
+ struct camera_v4l2_private *sp;
+
+ sp = kzalloc(sizeof(*sp), GFP_KERNEL);
+ if (!sp)
+ return -ENOMEM;
+
+ filep->private_data = &sp->fh;
+
+ /* stream_id = open id */
+ sp->stream_id = atomic_read(&pvdev->opened);
+
+ v4l2_fh_init(&sp->fh, pvdev->vdev);
+ v4l2_fh_add(&sp->fh);
+
+ return 0;
+}
+
+static int camera_v4l2_fh_release(struct file *filep)
+{
+ struct camera_v4l2_private *sp = fh_to_private(filep->private_data);
+
+ if (sp) {
+ v4l2_fh_del(&sp->fh);
+ v4l2_fh_exit(&sp->fh);
+ }
+
+ kfree(sp);
+ return 0;
+}
+
+static int camera_v4l2_vb2_q_init(struct file *filep)
+{
+ struct camera_v4l2_private *sp = fh_to_private(filep->private_data);
+ struct vb2_queue *q = &sp->vb2_q;
+
+ memset(q, 0, sizeof(struct vb2_queue));
+
+ /* free up this buffer when stream is done */
+ q->drv_priv =
+ kzalloc(sizeof(struct msm_v4l2_format_data), GFP_KERNEL);
+ if (!q->drv_priv)
+ return -ENOMEM;
+
+ q->mem_ops = msm_vb2_get_q_mem_ops();
+ q->ops = msm_vb2_get_q_ops();
+
+ /* default queue type */
+ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ q->io_modes = VB2_USERPTR;
+ q->io_flags = 0;
+ q->buf_struct_size = sizeof(struct msm_vb2_buffer);
+ vb2_queue_init(q);
+
+ return 0;
+}
+
+static void camera_v4l2_vb2_q_release(struct file *filep)
+{
+ struct camera_v4l2_private *sp = filep->private_data;
+
+ kfree(sp->vb2_q.drv_priv);
+ vb2_queue_release(&sp->vb2_q);
+}
+
+static int camera_v4l2_open(struct file *filep)
+{
+ int rc = 0;
+ struct v4l2_event event;
+ struct msm_video_device *pvdev = video_drvdata(filep);
+ BUG_ON(!pvdev);
+
+ rc = camera_v4l2_fh_open(filep);
+ if (rc < 0)
+ goto fh_open_fail;
+
+ /* every stream has a vb2 queue */
+ rc = camera_v4l2_vb2_q_init(filep);
+ if (rc < 0)
+ goto vb2_q_fail;
+
+ if (!atomic_read(&pvdev->opened)) {
+
+ /* create a new session when first opened */
+ rc = msm_create_session(pvdev->vdev->num, pvdev->vdev);
+ if (rc < 0)
+ goto session_fail;
+
+ rc = msm_create_command_ack_q(pvdev->vdev->num, 0);
+ if (rc < 0)
+ goto command_ack_q_fail;
+
+ camera_pack_event(filep, MSM_CAMERA_NEW_SESSION, 0, &event);
+ rc = msm_post_event(&event, MSM_POST_EVT_TIMEOUT);
+ if (rc < 0)
+ goto post_fail;
+
+ rc = camera_check_event_status(&event);
+ if (rc < 0)
+ goto post_fail;
+ } else {
+ rc = msm_create_command_ack_q(pvdev->vdev->num,
+ atomic_read(&pvdev->opened));
+ if (rc < 0)
+ goto session_fail;
+ }
+
+ atomic_add(1, &pvdev->opened);
+ return rc;
+
+post_fail:
+ msm_delete_command_ack_q(pvdev->vdev->num, 0);
+command_ack_q_fail:
+ msm_destroy_session(pvdev->vdev->num);
+session_fail:
+ camera_v4l2_vb2_q_release(filep);
+vb2_q_fail:
+ camera_v4l2_fh_release(filep);
+fh_open_fail:
+ return rc;
+}
+
+static unsigned int camera_v4l2_poll(struct file *filep,
+ struct poll_table_struct *wait)
+{
+ int rc = 0;
+ struct camera_v4l2_private *sp = fh_to_private(filep->private_data);
+
+ rc = vb2_poll(&sp->vb2_q, filep, wait);
+
+ poll_wait(filep, &sp->fh.wait, wait);
+ if (v4l2_event_pending(&sp->fh))
+ rc |= POLLPRI;
+
+ return rc;
+}
+
+static int camera_v4l2_close(struct file *filep)
+{
+ int rc = 0;
+ struct v4l2_event event;
+ struct msm_video_device *pvdev = video_drvdata(filep);
+ struct camera_v4l2_private *sp = fh_to_private(filep->private_data);
+
+ BUG_ON(!pvdev);
+
+ atomic_sub_return(1, &pvdev->opened);
+
+ if (atomic_read(&pvdev->opened) == 0) {
+
+ camera_pack_event(filep, MSM_CAMERA_DEL_SESSION, 0, &event);
+
+ /* Donot wait, imaging server may have crashed */
+ msm_post_event(&event, -1);
+
+ /* This should take care of both normal close
+ * and application crashes */
+ msm_destroy_session(pvdev->vdev->num);
+
+ } else {
+ camera_pack_event(filep, MSM_CAMERA_SET_PARM,
+ MSM_CAMERA_PRIV_DEL_STREAM, &event);
+
+ /* Donot wait, imaging server may have crashed */
+ msm_post_event(&event, MSM_POST_EVT_TIMEOUT);
+
+ msm_delete_command_ack_q(pvdev->vdev->num,
+ sp->stream_id);
+
+ msm_delete_stream(pvdev->vdev->num, sp->stream_id);
+ }
+
+ camera_v4l2_vb2_q_release(filep);
+ camera_v4l2_fh_release(filep);
+
+ return rc;
+}
+
+static struct v4l2_file_operations camera_v4l2_fops = {
+ .owner = THIS_MODULE,
+ .open = camera_v4l2_open,
+ .poll = camera_v4l2_poll,
+ .release = camera_v4l2_close,
+ .ioctl = video_ioctl2,
+};
+
+int camera_init_v4l2(struct device *dev, unsigned int *session)
+{
+ struct msm_video_device *pvdev;
+ struct v4l2_device *v4l2_dev;
+ int rc = 0;
+
+ pvdev = kzalloc(sizeof(struct msm_video_device),
+ GFP_KERNEL);
+ if (WARN_ON(!pvdev)) {
+ rc = -ENOMEM;
+ goto init_end;
+ }
+
+ pvdev->vdev = video_device_alloc();
+ if (WARN_ON(!pvdev->vdev)) {
+ rc = -ENOMEM;
+ goto video_fail;
+ }
+
+ v4l2_dev = kzalloc(sizeof(struct v4l2_device), GFP_KERNEL);
+ if (WARN_ON(!v4l2_dev)) {
+ rc = -ENOMEM;
+ goto v4l2_fail;
+ }
+
+#if defined(CONFIG_MEDIA_CONTROLLER)
+ v4l2_dev->mdev = kzalloc(sizeof(struct media_device),
+ GFP_KERNEL);
+ if (!v4l2_dev->mdev) {
+ rc = -ENOMEM;
+ goto mdev_fail;
+ }
+ strlcpy(v4l2_dev->mdev->model, MSM_CAMERA_NAME,
+ sizeof(v4l2_dev->mdev->model));
+
+ v4l2_dev->mdev->dev = dev;
+
+ rc = media_device_register(v4l2_dev->mdev);
+ if (WARN_ON(rc < 0))
+ goto media_fail;
+
+ rc = media_entity_init(&pvdev->vdev->entity, 0, NULL, 0);
+ if (WARN_ON(rc < 0))
+ goto entity_fail;
+ pvdev->vdev->entity.type = MEDIA_ENT_T_DEVNODE_V4L;
+ pvdev->vdev->entity.group_id = QCAMERA_VNODE_GROUP_ID;
+#endif
+
+ v4l2_dev->notify = NULL;
+ pvdev->vdev->v4l2_dev = v4l2_dev;
+
+ rc = v4l2_device_register(dev, pvdev->vdev->v4l2_dev);
+ if (WARN_ON(rc < 0))
+ goto register_fail;
+
+ strlcpy(pvdev->vdev->name, "msm-sensor", sizeof(pvdev->vdev->name));
+ pvdev->vdev->release = video_device_release;
+ pvdev->vdev->fops = &camera_v4l2_fops;
+ pvdev->vdev->ioctl_ops = &camera_v4l2_ioctl_ops;
+ pvdev->vdev->minor = -1;
+ pvdev->vdev->vfl_type = VFL_TYPE_GRABBER;
+ rc = video_register_device(pvdev->vdev,
+ VFL_TYPE_GRABBER, -1);
+ if (WARN_ON(rc < 0))
+ goto video_register_fail;
+#if defined(CONFIG_MEDIA_CONTROLLER)
+ /* FIXME: How to get rid of this messy? */
+ pvdev->vdev->entity.name = video_device_node_name(pvdev->vdev);
+#endif
+
+ *session = pvdev->vdev->num;
+ atomic_set(&pvdev->opened, 0);
+ video_set_drvdata(pvdev->vdev, pvdev);
+ goto init_end;
+
+video_register_fail:
+ v4l2_device_unregister(pvdev->vdev->v4l2_dev);
+register_fail:
+#if defined(CONFIG_MEDIA_CONTROLLER)
+ media_entity_cleanup(&pvdev->vdev->entity);
+entity_fail:
+ media_device_unregister(v4l2_dev->mdev);
+media_fail:
+ kfree(v4l2_dev->mdev);
+mdev_fail:
+#endif
+ kfree(v4l2_dev);
+v4l2_fail:
+ video_device_release(pvdev->vdev);
+video_fail:
+ kfree(pvdev);
+init_end:
+ return rc;
+}
diff --git a/arch/arm/boot/dts/msm8974-mtp.dts b/drivers/media/video/msmb/camera/camera.h
similarity index 64%
copy from arch/arm/boot/dts/msm8974-mtp.dts
copy to drivers/media/video/msmb/camera/camera.h
index 9946cf0..ac860a4 100644
--- a/arch/arm/boot/dts/msm8974-mtp.dts
+++ b/drivers/media/video/msmb/camera/camera.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
@@ -10,13 +10,14 @@
* GNU General Public License for more details.
*/
-/dts-v1/;
+#ifndef _CAMERA_H
+#define _CAMERA_H
-/include/ "msm8974.dtsi"
-/include/ "msm8974-mtp.dtsi"
-
-/ {
- model = "Qualcomm MSM 8974 MTP";
- compatible = "qcom,msm8974-mtp", "qcom,msm8974";
- qcom,msm-id = <126 8 0>;
+enum stream_state {
+ START_STREAM = 0,
+ STOP_STREAM,
};
+
+int camera_init_v4l2(struct device *dev, unsigned int *session);
+
+#endif /*_CAMERA_H */
diff --git a/drivers/media/video/msmb/isp/Makefile b/drivers/media/video/msmb/isp/Makefile
new file mode 100644
index 0000000..e517798
--- /dev/null
+++ b/drivers/media/video/msmb/isp/Makefile
@@ -0,0 +1,4 @@
+ccflags-y += -Idrivers/media/video/msmb
+ccflags-y += -Idrivers/media/video/msmb/sensor/io
+obj-$(CONFIG_MSMB_CAMERA) += msm_isp.o msm_buf_mgr.o msm_isp_util.o msm_isp_axi_util.o msm_isp_stats_util.o
+obj-$(CONFIG_MSMB_CAMERA) += msm_isp40.o
diff --git a/drivers/media/video/msmb/isp/msm_buf_mgr.c b/drivers/media/video/msmb/isp/msm_buf_mgr.c
new file mode 100644
index 0000000..aab97d7
--- /dev/null
+++ b/drivers/media/video/msmb/isp/msm_buf_mgr.c
@@ -0,0 +1,604 @@
+/* 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/workqueue.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/ioctl.h>
+#include <linux/spinlock.h>
+#include <linux/videodev2.h>
+#include <linux/proc_fs.h>
+#include <linux/videodev2.h>
+#include <linux/vmalloc.h>
+#include <linux/android_pmem.h>
+
+#include <media/v4l2-dev.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-device.h>
+#include <media/videobuf2-core.h>
+#include <media/msm_camera.h>
+#include <media/msm_isp.h>
+
+#include <mach/iommu.h>
+
+#include "msm.h"
+#include "msm_buf_mgr.h"
+
+/*#define CONFIG_MSM_ISP_DBG*/
+#undef CDBG
+#ifdef CONFIG_MSM_ISP_DBG
+#define CDBG(fmt, args...) pr_err(fmt, ##args)
+#else
+#define CDBG(fmt, args...) do { } while (0)
+#endif
+
+static struct msm_isp_bufq *msm_isp_get_bufq(
+ struct msm_isp_buf_mgr *buf_mgr,
+ uint32_t bufq_handle)
+{
+ struct msm_isp_bufq *bufq = NULL;
+ uint32_t bufq_index = bufq_handle & 0xFF;
+ if (bufq_index > buf_mgr->num_buf_q)
+ return bufq;
+
+ bufq = &buf_mgr->bufq[bufq_index];
+ if (bufq->bufq_handle == bufq_handle)
+ return bufq;
+
+ return NULL;
+}
+
+static struct msm_isp_buffer *msm_isp_get_buf_ptr(
+ struct msm_isp_buf_mgr *buf_mgr,
+ uint32_t bufq_handle, uint32_t buf_index)
+{
+ struct msm_isp_bufq *bufq = NULL;
+ struct msm_isp_buffer *buf_info = NULL;
+
+ bufq = msm_isp_get_bufq(buf_mgr, bufq_handle);
+ if (!bufq) {
+ pr_err("%s: Invalid bufq\n", __func__);
+ return buf_info;
+ }
+
+ if (bufq->num_bufs <= buf_index) {
+ pr_err("%s: Invalid buf index\n", __func__);
+ return buf_info;
+ }
+ buf_info = &bufq->bufs[buf_index];
+ return buf_info;
+}
+
+static uint32_t msm_isp_get_buf_handle(
+ struct msm_isp_buf_mgr *buf_mgr)
+{
+ int i;
+ if ((buf_mgr->buf_handle_cnt << 8) == 0)
+ buf_mgr->buf_handle_cnt++;
+
+ for (i = 0; i < buf_mgr->num_buf_q; i++) {
+ if (buf_mgr->bufq[i].bufq_handle == 0) {
+ memset(&buf_mgr->bufq[i],
+ 0, sizeof(struct msm_isp_bufq));
+ buf_mgr->bufq[i].bufq_handle =
+ (++buf_mgr->buf_handle_cnt) << 8 | i;
+ return buf_mgr->bufq[i].bufq_handle;
+ }
+ }
+ return 0;
+}
+
+static int msm_isp_free_buf_handle(struct msm_isp_buf_mgr *buf_mgr,
+ uint32_t bufq_handle)
+{
+ struct msm_isp_bufq *bufq =
+ msm_isp_get_bufq(buf_mgr, bufq_handle);
+ if (!bufq)
+ return -EINVAL;
+ memset(bufq, 0, sizeof(struct msm_isp_bufq));
+ return 0;
+}
+
+static int msm_isp_prepare_v4l2_buf(struct msm_isp_buf_mgr *buf_mgr,
+ struct msm_isp_buffer *buf_info,
+ struct v4l2_buffer *v4l2_buf)
+{
+ int i, rc = -1;
+ struct msm_isp_buffer_mapped_info *mapped_info;
+ for (i = 0; i < v4l2_buf->length; i++) {
+ mapped_info = &buf_info->mapped_info[i];
+ mapped_info->handle =
+ ion_import_dma_buf(buf_mgr->client,
+ v4l2_buf->m.planes[i].m.userptr);
+ if (IS_ERR_OR_NULL(mapped_info->handle)) {
+ pr_err("%s: buf has null/error ION handle %p\n",
+ __func__, mapped_info->handle);
+ goto ion_map_error;
+ }
+ if (ion_map_iommu(buf_mgr->client, mapped_info->handle,
+ buf_mgr->iommu_domain_num, 0, SZ_4K,
+ 0, &(mapped_info->paddr),
+ &(mapped_info->len), 0, 0) < 0) {
+ rc = -EINVAL;
+ pr_err("%s: cannot map address", __func__);
+ ion_free(buf_mgr->client, mapped_info->handle);
+ goto ion_map_error;
+ }
+ mapped_info->paddr += v4l2_buf->m.planes[i].data_offset;
+ CDBG("%s: plane: %d addr:%lu\n",
+ __func__, i, mapped_info->paddr);
+ }
+ buf_info->num_planes = v4l2_buf->length;
+ return 0;
+ion_map_error:
+ for (--i; i >= 0; i--) {
+ mapped_info = &buf_info->mapped_info[i];
+ ion_unmap_iommu(buf_mgr->client, mapped_info->handle,
+ buf_mgr->iommu_domain_num, 0);
+ ion_free(buf_mgr->client, mapped_info->handle);
+ }
+ return rc;
+}
+
+static void msm_isp_unprepare_v4l2_buf(
+ struct msm_isp_buf_mgr *buf_mgr,
+ struct msm_isp_buffer *buf_info)
+{
+ int i;
+ struct msm_isp_buffer_mapped_info *mapped_info;
+ for (i = 0; i < buf_info->num_planes; i++) {
+ mapped_info = &buf_info->mapped_info[i];
+ ion_unmap_iommu(buf_mgr->client, mapped_info->handle,
+ buf_mgr->iommu_domain_num, 0);
+ ion_free(buf_mgr->client, mapped_info->handle);
+ }
+ return;
+}
+
+static int msm_isp_buf_prepare(struct msm_isp_buf_mgr *buf_mgr,
+ struct msm_isp_qbuf_info *info, struct vb2_buffer *vb2_buf)
+{
+ int rc = -1;
+ struct msm_isp_buffer *buf_info = NULL;
+ struct v4l2_buffer *buf = NULL;
+ struct v4l2_plane *plane = NULL;
+
+ buf_info = msm_isp_get_buf_ptr(buf_mgr,
+ info->handle, info->buf_idx);
+ if (!buf_info) {
+ pr_err("Invalid buffer prepare\n");
+ return rc;
+ }
+
+ if (buf_info->state == MSM_ISP_BUFFER_STATE_UNUSED ||
+ buf_info->state != MSM_ISP_BUFFER_STATE_INITIALIZED) {
+ pr_err("%s: Invalid buffer state: %d\n",
+ __func__, buf_info->state);
+ return rc;
+ }
+
+ if (vb2_buf) {
+ buf = &vb2_buf->v4l2_buf;
+ buf_info->vb2_buf = vb2_buf;
+ } else {
+ buf = &info->buffer;
+ plane =
+ kzalloc(sizeof(struct v4l2_plane) * buf->length,
+ GFP_KERNEL);
+ if (!plane) {
+ pr_err("%s: Cannot alloc plane: %d\n",
+ __func__, buf_info->state);
+ return rc;
+ }
+ if (copy_from_user(plane,
+ (void __user *)(buf->m.planes),
+ sizeof(struct v4l2_plane) * buf->length)) {
+ kfree(plane);
+ return rc;
+ }
+ buf->m.planes = plane;
+ }
+
+ rc = msm_isp_prepare_v4l2_buf(buf_mgr, buf_info, buf);
+ if (rc < 0) {
+ pr_err("%s: Prepare buffer error\n", __func__);
+ kfree(plane);
+ return rc;
+ }
+ buf_info->state = MSM_ISP_BUFFER_STATE_PREPARED;
+ kfree(plane);
+ return rc;
+}
+
+static int msm_isp_buf_unprepare(struct msm_isp_buf_mgr *buf_mgr,
+ uint32_t buf_handle)
+{
+ int rc = -1, i;
+ struct msm_isp_bufq *bufq = NULL;
+ struct msm_isp_buffer *buf_info = NULL;
+ bufq = msm_isp_get_bufq(buf_mgr, buf_handle);
+ if (!bufq) {
+ pr_err("%s: Invalid bufq\n", __func__);
+ return rc;
+ }
+
+ for (i = 0; i < bufq->num_bufs; i++) {
+ buf_info = msm_isp_get_buf_ptr(buf_mgr, buf_handle, i);
+ if (buf_info->state == MSM_ISP_BUFFER_STATE_UNUSED ||
+ buf_info->state ==
+ MSM_ISP_BUFFER_STATE_INITIALIZED)
+ continue;
+ msm_isp_unprepare_v4l2_buf(buf_mgr, buf_info);
+ }
+ return 0;
+}
+
+static int msm_isp_get_buf(struct msm_isp_buf_mgr *buf_mgr,
+ uint32_t bufq_handle, struct msm_isp_buffer **buf_info)
+{
+ int rc = -1;
+ struct msm_isp_buffer *temp_buf_info;
+ struct msm_isp_bufq *bufq = NULL;
+ struct vb2_buffer *vb2_buf = NULL;
+ bufq = msm_isp_get_bufq(buf_mgr, bufq_handle);
+ if (!bufq) {
+ pr_err("%s: Invalid bufq\n", __func__);
+ return rc;
+ }
+
+ *buf_info = NULL;
+ if (BUF_SRC(bufq->stream_id)) {
+ list_for_each_entry(temp_buf_info, &bufq->head, list) {
+ if (temp_buf_info->state ==
+ MSM_ISP_BUFFER_STATE_QUEUED) {
+ /* found one buf */
+ list_del_init(&temp_buf_info->list);
+ *buf_info = temp_buf_info;
+ break;
+ }
+ }
+ } else {
+ vb2_buf = buf_mgr->vb2_ops->get_buf(
+ bufq->session_id, bufq->stream_id);
+ if (vb2_buf) {
+ if (vb2_buf->v4l2_buf.index < bufq->num_bufs) {
+ *buf_info =
+ &bufq->bufs[vb2_buf->v4l2_buf.index];
+ (*buf_info)->vb2_buf = vb2_buf;
+ } else {
+ pr_err("%s: Incorrect buf index %d\n",
+ __func__, vb2_buf->v4l2_buf.index);
+ return -EINVAL;
+ }
+ }
+ }
+
+ if (!(*buf_info)) {
+ pr_err("%s: No free buffer\n", __func__);
+ return rc;
+ }
+
+ (*buf_info)->state = MSM_ISP_BUFFER_STATE_DEQUEUED;
+ return 0;
+}
+
+static int msm_isp_put_buf(struct msm_isp_buf_mgr *buf_mgr,
+ uint32_t bufq_handle, uint32_t buf_index)
+{
+ int rc = -1;
+ struct msm_isp_bufq *bufq = NULL;
+ struct msm_isp_buffer *buf_info = NULL;
+
+ bufq = msm_isp_get_bufq(buf_mgr, bufq_handle);
+ if (!bufq) {
+ pr_err("%s: Invalid bufq\n", __func__);
+ return rc;
+ }
+
+ buf_info = msm_isp_get_buf_ptr(buf_mgr, bufq_handle, buf_index);
+ if (!buf_info) {
+ pr_err("%s: buf not found\n", __func__);
+ return rc;
+ }
+
+ switch (buf_info->state) {
+ case MSM_ISP_BUFFER_STATE_PREPARED:
+ case MSM_ISP_BUFFER_STATE_DEQUEUED:
+ case MSM_ISP_BUFFER_STATE_DISPATCHED:
+ if (BUF_SRC(bufq->stream_id))
+ list_add_tail(&buf_info->list, &bufq->head);
+ else
+ buf_mgr->vb2_ops->put_buf(buf_info->vb2_buf);
+ buf_info->state = MSM_ISP_BUFFER_STATE_QUEUED;
+ rc = 0;
+ break;
+ default:
+ pr_err("%s: incorrect state = %d",
+ __func__, buf_info->state);
+ break;
+ }
+
+ return rc;
+}
+
+static int msm_isp_buf_done(struct msm_isp_buf_mgr *buf_mgr,
+ uint32_t bufq_handle, uint32_t buf_index,
+ struct timeval *tv, uint32_t frame_id)
+{
+ int rc = -1;
+ struct msm_isp_bufq *bufq = NULL;
+ struct msm_isp_buffer *buf_info = NULL;
+
+ bufq = msm_isp_get_bufq(buf_mgr, bufq_handle);
+ if (!bufq) {
+ pr_err("Invalid bufq\n");
+ return rc;
+ }
+
+ buf_info = msm_isp_get_buf_ptr(buf_mgr, bufq_handle, buf_index);
+ if (!buf_info) {
+ pr_err("%s: buf not found\n", __func__);
+ return rc;
+ }
+
+ if (buf_info->state == MSM_ISP_BUFFER_STATE_DEQUEUED) {
+ buf_info->state = MSM_ISP_BUFFER_STATE_DISPATCHED;
+ if (!(BUF_SRC(bufq->stream_id))) {
+ buf_info->vb2_buf->v4l2_buf.timestamp = *tv;
+ buf_info->vb2_buf->v4l2_buf.sequence = frame_id;
+ buf_mgr->vb2_ops->buf_done(buf_info->vb2_buf);
+ }
+ }
+
+ return 0;
+}
+
+static int msm_isp_buf_enqueue(struct msm_isp_buf_mgr *buf_mgr,
+ struct msm_isp_qbuf_info *info)
+{
+ int rc;
+ struct msm_isp_bufq *bufq = NULL;
+ rc = msm_isp_buf_prepare(buf_mgr, info, NULL);
+ if (rc < 0) {
+ pr_err("%s: Buf prepare failed\n", __func__);
+ return rc;
+ }
+
+ bufq = msm_isp_get_bufq(buf_mgr, info->handle);
+ if (BUF_SRC(bufq->stream_id)) {
+ rc = msm_isp_put_buf(buf_mgr, info->handle, info->buf_idx);
+ if (rc < 0) {
+ pr_err("%s: Buf put failed\n", __func__);
+ return rc;
+ }
+ }
+
+ return rc;
+}
+
+static int msm_isp_get_bufq_handle(struct msm_isp_buf_mgr *buf_mgr,
+ uint32_t session_id, uint32_t stream_id)
+{
+ int i;
+ for (i = 0; i < buf_mgr->num_buf_q; i++) {
+ if (buf_mgr->bufq[i].session_id == session_id &&
+ buf_mgr->bufq[i].stream_id == stream_id) {
+ return buf_mgr->bufq[i].bufq_handle;
+ }
+ }
+ return 0;
+}
+
+static int msm_isp_request_bufq(struct msm_isp_buf_mgr *buf_mgr,
+ struct msm_isp_buf_request *buf_request)
+{
+ int rc = -1, i;
+ struct msm_isp_bufq *bufq = NULL;
+ CDBG("%s: E\n", __func__);
+
+ if (!buf_request->num_buf) {
+ pr_err("Invalid buffer request\n");
+ return rc;
+ }
+
+ buf_request->handle = msm_isp_get_buf_handle(buf_mgr);
+ if (!buf_request->handle) {
+ pr_err("Invalid buffer handle\n");
+ return rc;
+ }
+
+ bufq = msm_isp_get_bufq(buf_mgr, buf_request->handle);
+ if (!bufq) {
+ pr_err("Invalid buffer queue\n");
+ return rc;
+ }
+
+ bufq->bufs = kzalloc(sizeof(struct msm_isp_buffer) *
+ buf_request->num_buf, GFP_KERNEL);
+ if (!bufq->bufs) {
+ pr_err("No free memory for buf info\n");
+ msm_isp_free_buf_handle(buf_mgr, buf_request->handle);
+ return rc;
+ }
+
+ bufq->bufq_handle = buf_request->handle;
+ bufq->session_id = buf_request->session_id;
+ bufq->stream_id = buf_request->stream_id;
+ bufq->num_bufs = buf_request->num_buf;
+ INIT_LIST_HEAD(&bufq->head);
+ for (i = 0; i < buf_request->num_buf; i++) {
+ bufq->bufs[i].state = MSM_ISP_BUFFER_STATE_INITIALIZED;
+ bufq->bufs[i].bufq_handle = bufq->bufq_handle;
+ bufq->bufs[i].buf_idx = i;
+ }
+
+ return 0;
+}
+
+static int msm_isp_release_bufq(struct msm_isp_buf_mgr *buf_mgr,
+ uint32_t bufq_handle)
+{
+ struct msm_isp_bufq *bufq = NULL;
+ int rc = -1;
+ bufq = msm_isp_get_bufq(buf_mgr, bufq_handle);
+ if (!bufq) {
+ pr_err("Invalid bufq release\n");
+ return rc;
+ }
+
+ msm_isp_buf_unprepare(buf_mgr, bufq_handle);
+
+ kfree(bufq->bufs);
+ msm_isp_free_buf_handle(buf_mgr, bufq_handle);
+ return 0;
+}
+
+static void msm_isp_release_all_bufq(
+ struct msm_isp_buf_mgr *buf_mgr)
+{
+ struct msm_isp_bufq *bufq = NULL;
+ int i;
+ for (i = 0; i < buf_mgr->num_buf_q; i++) {
+ bufq = &buf_mgr->bufq[i];
+ if (!bufq->bufq_handle)
+ continue;
+ msm_isp_buf_unprepare(buf_mgr, bufq->bufq_handle);
+ kfree(bufq->bufs);
+ msm_isp_free_buf_handle(buf_mgr, bufq->bufq_handle);
+ }
+}
+
+static int msm_isp_attach_ctx(struct msm_isp_buf_mgr *buf_mgr,
+ struct device *iommu_ctx)
+{
+ int rc;
+ rc = iommu_attach_device(buf_mgr->iommu_domain, iommu_ctx);
+ if (rc) {
+ pr_err("%s: Iommu attach error\n", __func__);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static void msm_isp_detach_ctx(struct msm_isp_buf_mgr *buf_mgr,
+ struct device *iommu_ctx)
+{
+ iommu_detach_device(buf_mgr->iommu_domain, iommu_ctx);
+}
+
+static int msm_isp_init_isp_buf_mgr(
+ struct msm_isp_buf_mgr *buf_mgr,
+ const char *ctx_name, uint16_t num_buf_q)
+{
+ int rc = -1;
+ if (!num_buf_q) {
+ pr_err("Invalid buffer queue number\n");
+ return rc;
+ }
+
+ CDBG("%s: E\n", __func__);
+ buf_mgr->num_buf_q = num_buf_q;
+ buf_mgr->bufq =
+ kzalloc(sizeof(struct msm_isp_bufq) * num_buf_q,
+ GFP_KERNEL);
+ if (!buf_mgr->bufq) {
+ pr_err("Bufq malloc error\n");
+ goto bufq_error;
+ }
+ buf_mgr->client = msm_ion_client_create(-1, ctx_name);
+ buf_mgr->buf_handle_cnt = 0;
+
+ return 0;
+bufq_error:
+ return rc;
+}
+
+static int msm_isp_deinit_isp_buf_mgr(
+ struct msm_isp_buf_mgr *buf_mgr)
+{
+ msm_isp_release_all_bufq(buf_mgr);
+ ion_client_destroy(buf_mgr->client);
+ kfree(buf_mgr->bufq);
+ buf_mgr->num_buf_q = 0;
+ return 0;
+}
+
+int msm_isp_proc_buf_cmd(struct msm_isp_buf_mgr *buf_mgr,
+ unsigned int cmd, void *arg)
+{
+ switch (cmd) {
+ case VIDIOC_MSM_ISP_REQUEST_BUF: {
+ struct msm_isp_buf_request *buf_req = arg;
+ buf_mgr->ops->request_buf(buf_mgr, buf_req);
+ break;
+ }
+ case VIDIOC_MSM_ISP_ENQUEUE_BUF: {
+ struct msm_isp_qbuf_info *qbuf_info = arg;
+ buf_mgr->ops->enqueue_buf(buf_mgr, qbuf_info);
+ break;
+ }
+ case VIDIOC_MSM_ISP_RELEASE_BUF: {
+ struct msm_isp_buf_request *buf_req = arg;
+ buf_mgr->ops->release_buf(buf_mgr, buf_req->handle);
+ break;
+ }
+ }
+ return 0;
+}
+
+static struct msm_isp_buf_ops isp_buf_ops = {
+ .request_buf = msm_isp_request_bufq,
+ .enqueue_buf = msm_isp_buf_enqueue,
+ .release_buf = msm_isp_release_bufq,
+ .get_bufq_handle = msm_isp_get_bufq_handle,
+ .get_buf = msm_isp_get_buf,
+ .put_buf = msm_isp_put_buf,
+ .buf_done = msm_isp_buf_done,
+ .attach_ctx = msm_isp_attach_ctx,
+ .detach_ctx = msm_isp_detach_ctx,
+ .buf_mgr_init = msm_isp_init_isp_buf_mgr,
+ .buf_mgr_deinit = msm_isp_deinit_isp_buf_mgr,
+};
+
+int msm_isp_create_isp_buf_mgr(
+ struct msm_isp_buf_mgr *buf_mgr,
+ struct msm_sd_req_vb2_q *vb2_ops,
+ struct msm_iova_layout *iova_layout)
+{
+ int rc = 0;
+ if (buf_mgr->init_done)
+ return rc;
+
+ buf_mgr->iommu_domain_num = msm_register_domain(iova_layout);
+ if (buf_mgr->iommu_domain_num < 0) {
+ pr_err("%s: Invalid iommu domain number\n", __func__);
+ rc = -1;
+ goto iommu_domain_error;
+ }
+
+ buf_mgr->iommu_domain = msm_get_iommu_domain(
+ buf_mgr->iommu_domain_num);
+ if (!buf_mgr->iommu_domain) {
+ pr_err("%s: Invalid iommu domain\n", __func__);
+ rc = -1;
+ goto iommu_domain_error;
+ }
+
+ buf_mgr->ops = &isp_buf_ops;
+ buf_mgr->vb2_ops = vb2_ops;
+ buf_mgr->init_done = 1;
+ buf_mgr->ref_count = 0;
+ return 0;
+iommu_domain_error:
+ return rc;
+}
diff --git a/drivers/media/video/msmb/isp/msm_buf_mgr.h b/drivers/media/video/msmb/isp/msm_buf_mgr.h
new file mode 100644
index 0000000..b96d9fe
--- /dev/null
+++ b/drivers/media/video/msmb/isp/msm_buf_mgr.h
@@ -0,0 +1,122 @@
+/* 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 _MSM_ISP_BUF_H_
+#define _MSM_ISP_BUF_H_
+
+#include <media/msmb_isp.h>
+#include <mach/iommu_domains.h>
+#include "msm_sd.h"
+
+/*Buffer source can be from userspace / HAL*/
+#define BUF_SRC(id) (id & ISP_NATIVE_BUF_BIT)
+
+struct msm_isp_buf_mgr;
+
+enum msm_isp_buffer_state {
+ MSM_ISP_BUFFER_STATE_UNUSED, /* not used */
+ MSM_ISP_BUFFER_STATE_INITIALIZED, /* REQBUF done */
+ MSM_ISP_BUFFER_STATE_PREPARED, /* BUF mapped */
+ MSM_ISP_BUFFER_STATE_QUEUED, /* buf queued */
+ MSM_ISP_BUFFER_STATE_DEQUEUED, /* in use in VFE */
+ MSM_ISP_BUFFER_STATE_DISPATCHED, /* sent to userspace */
+};
+
+struct msm_isp_buffer_mapped_info {
+ unsigned long len;
+ unsigned long paddr;
+ struct ion_handle *handle;
+};
+
+struct msm_isp_buffer {
+ /*Common Data structure*/
+ int num_planes;
+ struct msm_isp_buffer_mapped_info mapped_info[VIDEO_MAX_PLANES];
+ int buf_idx;
+ uint32_t bufq_handle;
+
+ /*Native buffer*/
+ struct list_head list;
+ enum msm_isp_buffer_state state;
+
+ /*Vb2 buffer data*/
+ struct vb2_buffer *vb2_buf;
+
+};
+
+struct msm_isp_bufq {
+ uint32_t session_id;
+ uint32_t stream_id;
+ uint32_t num_bufs;
+ uint32_t bufq_handle;
+ struct msm_isp_buffer *bufs;
+
+ /*Native buffer queue*/
+ struct list_head head;
+};
+
+struct msm_isp_buf_ops {
+ int (*request_buf) (struct msm_isp_buf_mgr *buf_mgr,
+ struct msm_isp_buf_request *buf_request);
+
+ int (*enqueue_buf) (struct msm_isp_buf_mgr *buf_mgr,
+ struct msm_isp_qbuf_info *info);
+
+ int (*release_buf) (struct msm_isp_buf_mgr *buf_mgr,
+ uint32_t bufq_handle);
+
+ int (*get_bufq_handle) (struct msm_isp_buf_mgr *buf_mgr,
+ uint32_t session_id, uint32_t stream_id);
+
+ int (*get_buf) (struct msm_isp_buf_mgr *buf_mgr,
+ uint32_t bufq_handle, struct msm_isp_buffer **buf_info);
+
+ int (*put_buf) (struct msm_isp_buf_mgr *buf_mgr,
+ uint32_t bufq_handle, uint32_t buf_index);
+
+ int (*buf_done) (struct msm_isp_buf_mgr *buf_mgr,
+ uint32_t bufq_handle, uint32_t buf_index,
+ struct timeval *tv, uint32_t frame_id);
+ int (*attach_ctx) (struct msm_isp_buf_mgr *buf_mgr,
+ struct device *iommu_ctx);
+ void (*detach_ctx) (struct msm_isp_buf_mgr *buf_mgr,
+ struct device *iommu_ctx);
+ int (*buf_mgr_init) (struct msm_isp_buf_mgr *buf_mgr,
+ const char *ctx_name, uint16_t num_buf_q);
+ int (*buf_mgr_deinit) (struct msm_isp_buf_mgr *buf_mgr);
+};
+
+struct msm_isp_buf_mgr {
+ int init_done;
+ uint32_t ref_count;
+ spinlock_t lock;
+ uint16_t num_buf_q;
+ struct msm_isp_bufq *bufq;
+
+ struct ion_client *client;
+ struct msm_isp_buf_ops *ops;
+ uint32_t buf_handle_cnt;
+
+ struct msm_sd_req_vb2_q *vb2_ops;
+
+ /*IOMMU specific*/
+ int iommu_domain_num;
+ struct iommu_domain *iommu_domain;
+};
+
+int msm_isp_create_isp_buf_mgr(struct msm_isp_buf_mgr *buf_mgr,
+ struct msm_sd_req_vb2_q *vb2_ops, struct msm_iova_layout *iova_layout);
+
+int msm_isp_proc_buf_cmd(struct msm_isp_buf_mgr *buf_mgr,
+ unsigned int cmd, void *arg);
+
+#endif /* _MSM_ISP_BUF_H_ */
diff --git a/drivers/media/video/msmb/isp/msm_isp.c b/drivers/media/video/msmb/isp/msm_isp.c
new file mode 100644
index 0000000..960144e
--- /dev/null
+++ b/drivers/media/video/msmb/isp/msm_isp.c
@@ -0,0 +1,166 @@
+/* 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/delay.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/proc_fs.h>
+#include <linux/debugfs.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-subdev.h>
+#include <media/v4l2-device.h>
+#include <mach/board.h>
+#include <mach/vreg.h>
+#include <mach/iommu.h>
+
+#include "msm_isp.h"
+#include "msm_isp_util.h"
+#include "msm_isp_axi_util.h"
+#include "msm_isp_stats_util.h"
+#include "msm_sd.h"
+#include "msm_isp40.h"
+
+static struct msm_sd_req_vb2_q vfe_vb2_ops;
+
+static const struct of_device_id msm_vfe_dt_match[] = {
+ {
+ .compatible = "qcom,vfe40",
+ .data = &vfe40_hw_info,
+ },
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, msm_vfe_dt_match);
+
+static const struct platform_device_id msm_vfe_dev_id[] = {
+ {"msm_vfe32"},
+ {}
+};
+
+static struct msm_isp_buf_mgr vfe_buf_mgr;
+
+static int __devinit vfe_probe(struct platform_device *pdev)
+{
+ struct vfe_device *vfe_dev;
+ /*struct msm_cam_subdev_info sd_info;*/
+ const struct of_device_id *match_dev;
+ int rc = 0;
+
+ struct msm_iova_partition vfe_partition = {
+ .start = SZ_128K,
+ .size = SZ_2G - SZ_128K,
+ };
+ struct msm_iova_layout vfe_layout = {
+ .partitions = &vfe_partition,
+ .npartitions = 1,
+ .client_name = "vfe",
+ .domain_flags = 0,
+ };
+
+ vfe_dev = kzalloc(sizeof(struct vfe_device), GFP_KERNEL);
+ if (!vfe_dev) {
+ pr_err("%s: no enough memory\n", __func__);
+ return -ENOMEM;
+ }
+
+ if (pdev->dev.of_node) {
+ of_property_read_u32((&pdev->dev)->of_node,
+ "cell-index", &pdev->id);
+ match_dev = of_match_device(msm_vfe_dt_match, &pdev->dev);
+ vfe_dev->hw_info =
+ (struct msm_vfe_hardware_info *) match_dev->data;
+ } else {
+ vfe_dev->hw_info = platform_get_drvdata(pdev);
+ }
+
+ if (!vfe_dev->hw_info) {
+ pr_err("%s: No vfe hardware info\n", __func__);
+ return -EINVAL;
+ }
+ ISP_DBG("%s: device id = %d\n", __func__, pdev->id);
+
+ vfe_dev->pdev = pdev;
+ rc = vfe_dev->hw_info->vfe_ops.core_ops.get_platform_data(vfe_dev);
+ if (rc < 0) {
+ pr_err("%s: failed to get platform resources\n", __func__);
+ kfree(vfe_dev);
+ return -ENOMEM;
+ }
+
+ INIT_LIST_HEAD(&vfe_dev->tasklet_q);
+ tasklet_init(&vfe_dev->vfe_tasklet,
+ msm_isp_do_tasklet, (unsigned long)vfe_dev);
+
+ v4l2_subdev_init(&vfe_dev->subdev.sd, vfe_dev->hw_info->subdev_ops);
+ vfe_dev->subdev.sd.internal_ops =
+ vfe_dev->hw_info->subdev_internal_ops;
+ snprintf(vfe_dev->subdev.sd.name,
+ ARRAY_SIZE(vfe_dev->subdev.sd.name),
+ "vfe");
+ vfe_dev->subdev.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ vfe_dev->subdev.sd.flags |= V4L2_SUBDEV_FL_HAS_EVENTS;
+ v4l2_set_subdevdata(&vfe_dev->subdev.sd, vfe_dev);
+ platform_set_drvdata(pdev, &vfe_dev->subdev.sd);
+ mutex_init(&vfe_dev->mutex);
+ spin_lock_init(&vfe_dev->tasklet_lock);
+ spin_lock_init(&vfe_dev->shared_data_lock);
+ media_entity_init(&vfe_dev->subdev.sd.entity, 0, NULL, 0);
+ vfe_dev->subdev.sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV;
+ vfe_dev->subdev.sd.entity.group_id = MSM_CAMERA_SUBDEV_VFE;
+ vfe_dev->subdev.sd.entity.name = pdev->name;
+ rc = msm_sd_register(&vfe_dev->subdev);
+ if (rc != 0) {
+ pr_err("%s: msm_sd_register error = %d\n", __func__, rc);
+ kfree(vfe_dev);
+ goto end;
+ }
+
+ vfe_dev->buf_mgr = &vfe_buf_mgr;
+ v4l2_subdev_notify(&vfe_dev->subdev.sd,
+ MSM_SD_NOTIFY_REQ_CB, &vfe_vb2_ops);
+ rc = msm_isp_create_isp_buf_mgr(vfe_dev->buf_mgr,
+ &vfe_vb2_ops, &vfe_layout);
+ if (rc < 0) {
+ pr_err("%s: Unable to create buffer manager\n", __func__);
+ kfree(vfe_dev);
+ return -EINVAL;
+ }
+ vfe_dev->vfe_open_cnt = 0;
+end:
+ return rc;
+}
+
+static struct platform_driver vfe_driver = {
+ .probe = vfe_probe,
+ .driver = {
+ .name = "msm_vfe",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_vfe_dt_match,
+ },
+ .id_table = msm_vfe_dev_id,
+};
+
+static int __init msm_vfe_init_module(void)
+{
+ return platform_driver_register(&vfe_driver);
+}
+
+static void __exit msm_vfe_exit_module(void)
+{
+ platform_driver_unregister(&vfe_driver);
+}
+
+module_init(msm_vfe_init_module);
+module_exit(msm_vfe_exit_module);
+MODULE_DESCRIPTION("MSM VFE driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/msmb/isp/msm_isp.h b/drivers/media/video/msmb/isp/msm_isp.h
new file mode 100644
index 0000000..1c82f45
--- /dev/null
+++ b/drivers/media/video/msmb/isp/msm_isp.h
@@ -0,0 +1,366 @@
+/* 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 __MSM_VFE_H__
+#define __MSM_VFE_H__
+
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/completion.h>
+#include <linux/io.h>
+#include <linux/list.h>
+#include <linux/delay.h>
+#include <media/v4l2-subdev.h>
+#include <media/msmb_isp.h>
+#include <mach/msm_bus.h>
+#include <mach/msm_bus_board.h>
+
+#include "msm_buf_mgr.h"
+
+#define MAX_NUM_WM 7
+#define MAX_NUM_RDI 3
+#define MAX_NUM_RDI_MASTER 3
+#define MAX_NUM_COMPOSITE_MASK 4
+#define MAX_NUM_STATS_COMP_MASK 2
+#define MAX_INIT_FRAME_DROP 31
+#define ISP_SUB(a) ((a > 0) ? a-1 : 0)
+
+struct vfe_device;
+struct msm_vfe_axi_stream;
+struct msm_vfe_stats_stream;
+
+struct vfe_subscribe_info {
+ struct v4l2_fh *vfh;
+ uint32_t active;
+};
+
+enum msm_isp_camif_update_state {
+ NO_UPDATE,
+ ENABLE_CAMIF,
+ DISABLE_CAMIF,
+ DISABLE_CAMIF_IMMEDIATELY
+};
+
+struct msm_vfe_irq_ops {
+ void (*read_irq_status) (struct vfe_device *vfe_dev,
+ uint32_t *irq_status0, uint32_t *irq_status1);
+ void (*process_reg_update) (struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1);
+ void (*process_reset_irq) (struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1);
+ void (*process_halt_irq) (struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1);
+ void (*process_camif_irq) (struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1);
+ void (*process_axi_irq) (struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1,
+ struct timeval *tv);
+ void (*process_error_irq) (struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1);
+ void (*process_stats_irq) (struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1,
+ struct timeval *tv);
+};
+
+struct msm_vfe_axi_ops {
+ void (*reload_wm) (struct vfe_device *vfe_dev,
+ uint32_t reload_mask);
+ void (*enable_wm) (struct vfe_device *vfe_dev,
+ uint8_t wm_idx, uint8_t enable);
+
+ void (*cfg_framedrop) (struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info);
+ void (*clear_framedrop) (struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info);
+ void (*cfg_comp_mask) (struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info);
+ void (*clear_comp_mask) (struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info);
+ void (*cfg_wm_irq_mask) (struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info);
+ void (*clear_wm_irq_mask) (struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info);
+
+ void (*cfg_wm_reg) (struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd,
+ uint8_t plane_idx);
+ void (*clear_wm_reg) (struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info, uint8_t plane_idx);
+
+ void (*cfg_wm_xbar_reg) (struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd,
+ uint8_t plane_idx);
+ void (*clear_wm_xbar_reg) (struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info, uint8_t plane_idx);
+
+ void (*cfg_rdi_reg) (struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd,
+ uint8_t plane_idx);
+
+ void (*cfg_ub) (struct vfe_device *vfe_dev);
+
+ void (*update_ping_pong_addr) (struct vfe_device *vfe_dev,
+ uint8_t wm_idx, uint32_t pingpong_status, unsigned long paddr);
+
+ uint32_t (*get_wm_mask) (uint32_t irq_status0, uint32_t irq_status1);
+ uint32_t (*get_comp_mask) (uint32_t irq_status0, uint32_t irq_status1);
+ uint32_t (*get_pingpong_status) (struct vfe_device *vfe_dev);
+ long (*halt) (struct vfe_device *vfe_dev);
+};
+
+struct msm_vfe_core_ops {
+ void (*epoch_irq) (struct vfe_device *vfe_dev,
+ uint32_t epoch_line0, uint32_t epoch_line1);
+ void (*reg_update) (struct vfe_device *vfe_dev, uint32_t update_mask);
+ long (*reset_hw) (struct vfe_device *vfe_dev);
+ int (*init_hw) (struct vfe_device *vfe_dev);
+ void (*init_hw_reg) (struct vfe_device *vfe_dev);
+ void (*release_hw) (struct vfe_device *vfe_dev);
+ void (*cfg_camif) (struct vfe_device *vfe_dev,
+ struct msm_vfe_pix_cfg *pix_cfg);
+ void (*update_camif_state) (struct vfe_device *vfe_dev,
+ enum msm_isp_camif_update_state update_state);
+ int (*get_platform_data) (struct vfe_device *vfe_dev);
+};
+struct msm_vfe_stats_ops {
+ void (*cfg_framedrop) (struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream *stream_info);
+ void (*clear_framedrop) (struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream *stream_info);
+ void (*cfg_comp_mask) (struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream *stream_info);
+ void (*clear_comp_mask) (struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream *stream_info);
+ void (*cfg_wm_irq_mask) (struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream *stream_info);
+ void (*clear_wm_irq_mask) (struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream *stream_info);
+
+ void (*cfg_wm_reg) (struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream *stream_info);
+ void (*clear_wm_reg) (struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream *stream_info);
+
+ void (*cfg_ub) (struct vfe_device *vfe_dev);
+
+ void (*stats_enable) (struct vfe_device *vfe_dev,
+ uint32_t stats_mask, uint8_t enable);
+
+ void (*update_ping_pong_addr) (struct vfe_device *vfe_dev,
+ enum msm_isp_stats_type stats_type, uint32_t pingpong_status,
+ unsigned long paddr);
+
+ uint32_t (*get_frame_id) (struct vfe_device *vfe_dev);
+ uint32_t (*get_wm_mask) (uint32_t irq_status0, uint32_t irq_status1);
+ uint32_t (*get_comp_mask) (uint32_t irq_status0, uint32_t irq_status1);
+ uint32_t (*get_pingpong_status) (struct vfe_device *vfe_dev);
+ uint32_t (*get_active_pingpong_idx) (uint32_t pingpong_status,
+ enum msm_isp_stats_type stats_type);
+
+};
+
+struct msm_vfe_ops {
+ struct msm_vfe_irq_ops irq_ops;
+ struct msm_vfe_axi_ops axi_ops;
+ struct msm_vfe_core_ops core_ops;
+ struct msm_vfe_stats_ops stats_ops;
+};
+
+struct msm_vfe_hardware_info {
+ struct msm_vfe_ops vfe_ops;
+ struct msm_vfe_axi_hardware_info *axi_hw_info;
+ struct v4l2_subdev_internal_ops *subdev_internal_ops;
+ struct v4l2_subdev_ops *subdev_ops;
+};
+
+struct msm_vfe_axi_hardware_info {
+ uint8_t num_wm;
+ uint8_t num_rdi;
+ uint8_t num_rdi_master;
+ uint8_t num_comp_mask;
+ uint32_t min_wm_ub;
+ uint8_t num_stats_comp_mask;
+};
+
+enum msm_vfe_axi_state {
+ AVALIABLE,
+ INACTIVE,
+ ACTIVE,
+ PAUSE,
+ START_PENDING,
+ STOP_PENDING,
+ STOPPING,
+ PAUSE_PENDING,
+};
+
+#define VFE_NO_DROP 0xFFFFFFFF
+#define VFE_DROP_EVERY_2FRAME 0x55555555
+#define VFE_DROP_EVERY_4FRAME 0x11111111
+#define VFE_DROP_EVERY_8FRAME 0x01010101
+#define VFE_DROP_EVERY_16FRAME 0x00010001
+#define VFE_DROP_EVERY_32FRAME 0x00000001
+
+enum msm_vfe_axi_stream_type {
+ CONTINUOUS_STREAM,
+ BURST_STREAM,
+};
+
+struct msm_vfe_axi_stream {
+ uint32_t frame_id;
+ enum msm_vfe_axi_state state;
+ enum msm_vfe_axi_stream_src stream_src;
+ uint8_t num_planes;
+ uint8_t wm[MAX_PLANES_PER_STREAM];
+ uint8_t rdi[MAX_PLANES_PER_STREAM];
+ uint8_t rdi_master[MAX_PLANES_PER_STREAM];
+ uint8_t comp_mask_index;
+ struct msm_isp_buffer *buf[2];
+ uint32_t session_id;
+ uint32_t stream_id;
+ uint32_t bufq_handle;
+ uint32_t stream_handle;
+ uint8_t buf_divert;
+ enum msm_vfe_axi_stream_type stream_type;
+
+ uint32_t framedrop_pattern;
+ uint32_t init_frame_drop;
+ uint32_t burst_frame_count;/*number of sof before burst stop*/
+ uint8_t auto_trigger_stop;
+ uint8_t framedrop_update;
+};
+
+struct msm_vfe_axi_composite_info {
+ uint32_t stream_handle;
+ uint32_t stream_composite_mask;
+};
+struct msm_vfe_src_info {
+ unsigned long frame_id;
+ uint8_t active;
+ uint8_t pix_stream_count;
+ uint8_t raw_stream_count;
+ enum msm_vfe_inputmux input_mux;
+};
+
+enum msm_vfe_stats_state {
+ STATE_AVALIABLE,
+ STATE_INACTIVE,
+ STATE_ACTIVE,
+ STATE_START_PENDING,
+ STATE_STOP_PENDING,
+ STATE_STOPPING,
+};
+struct msm_vfe_stats_stream {
+ uint32_t frame_id;
+ uint8_t enable;
+ enum msm_isp_stats_type stats_type;
+ int8_t comp_mask_index;
+ struct msm_isp_buffer *buf[2];
+ uint32_t session_id;
+ uint32_t stream_id;
+ uint32_t bufq_handle;
+ uint32_t stream_handle;
+ uint32_t framedrop_pattern;
+ uint8_t comp_flag;
+ uint8_t comp_idx;
+ enum msm_vfe_stats_state state;
+};
+
+struct msm_vfe_stats_composite_info {
+ uint32_t stats_mask;
+ uint8_t comp_flag;
+};
+
+enum msm_wm_ub_cfg_type {
+ MSM_WM_UB_CFG_DEFAULT,
+ MSM_WM_UB_EQUAL_SLICING,
+ MSM_WM_UB_CFG_MAX_NUM
+};
+struct msm_vfe_axi_shared_data {
+ struct msm_vfe_axi_hardware_info *hw_info;
+ struct msm_vfe_axi_stream stream_info[MAX_NUM_STREAM];
+ uint32_t free_wm[MAX_NUM_WM];
+ uint32_t wm_image_size[MAX_NUM_WM];
+ enum msm_wm_ub_cfg_type wm_ub_cfg_policy;
+ uint8_t num_used_wm;
+ uint8_t free_rdi[MAX_NUM_RDI];
+ uint8_t free_rdi_master[MAX_NUM_RDI][MAX_NUM_RDI_MASTER];
+ uint8_t num_used_rdi;
+ uint8_t num_active_stream;
+ struct msm_vfe_axi_composite_info
+ composite_info[MAX_NUM_COMPOSITE_MASK];
+ uint8_t num_used_composite_mask;
+ uint32_t stream_update;
+ struct msm_vfe_src_info src_info[VFE_SRC_MAX];
+ uint16_t stream_handle_cnt;
+ unsigned long event_mask;
+};
+
+struct msm_vfe_stats_shared_data {
+ struct msm_vfe_stats_stream stream_info[MSM_ISP_STATS_MAX];
+ struct msm_vfe_stats_composite_info
+ composite_info[MAX_NUM_STATS_COMP_MASK];
+ uint8_t num_active_stream;
+ uint8_t num_used_composite_mask;
+ uint16_t stream_handle_cnt;
+};
+struct msm_vfe_tasklet_queue_cmd {
+ struct list_head list;
+ uint32_t vfeInterruptStatus0;
+ uint32_t vfeInterruptStatus1;
+ struct timeval tv;
+ uint8_t cmd_used;
+};
+
+#define MSM_VFE_TASKLETQ_SIZE 200
+
+struct vfe_device {
+ struct platform_device *pdev;
+ struct msm_sd_subdev subdev;
+ struct resource *vfe_irq;
+ struct resource *vfe_mem;
+ struct resource *vfe_vbif_mem;
+ struct resource *vfe_io;
+ struct resource *vfe_vbif_io;
+ void __iomem *vfe_base;
+ void __iomem *vfe_vbif_base;
+
+ struct device *iommu_ctx;
+
+ struct regulator *fs_vfe;
+ struct clk *vfe_clk[7];
+
+ uint32_t bus_perf_client;
+
+ struct completion reset_complete;
+ struct completion halt_complete;
+ struct completion stream_config_complete;
+ struct mutex mutex;
+
+ atomic_t irq_cnt;
+ uint8_t taskletq_idx;
+ spinlock_t tasklet_lock;
+ spinlock_t shared_data_lock;
+ struct list_head tasklet_q;
+ struct tasklet_struct vfe_tasklet;
+ struct msm_vfe_tasklet_queue_cmd
+ tasklet_queue_cmd[MSM_VFE_TASKLETQ_SIZE];
+
+ struct msm_vfe_hardware_info *hw_info;
+
+ struct msm_vfe_axi_shared_data axi_data;
+ struct msm_vfe_stats_shared_data stats_data;
+ struct msm_isp_buf_mgr *buf_mgr;
+ int dump_reg;
+ uint32_t vfe_open_cnt;
+};
+
+#endif
diff --git a/drivers/media/video/msmb/isp/msm_isp32.c b/drivers/media/video/msmb/isp/msm_isp32.c
new file mode 100644
index 0000000..d8ac4e0
--- /dev/null
+++ b/drivers/media/video/msmb/isp/msm_isp32.c
@@ -0,0 +1,822 @@
+/* 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 "msm_isp32.h"
+#include "msm_isp_util.h"
+#include "msm_isp_axi_util.h"
+#include "msm_isp.h"
+#include "msm.h"
+
+#define VFE32_BURST_LEN 4
+#define VFE32_EQUAL_SLICE_UB 117
+#define VFE32_WM_BASE(idx) (0x4C + 0x18 * idx)
+#define VFE32_RDI_BASE(idx) (0x734 + 0x4 * idx)
+#define VFE32_RDI_MN_BASE(m) (0x734 + 0x4 * m/3)
+#define VFE32_RDI_MN_SEL_SHIFT(m) (4*(m%3) + 4)
+#define VFE32_RDI_MN_FB_SHIFT(m) ((m%3) + 16)
+#define VFE32_XBAR_BASE(idx) (0x40 + 0x4 * (idx / 4))
+#define VFE32_XBAR_SHIFT(idx) ((idx % 4) * 8)
+#define VFE32_PING_PONG_BASE(wm, ping_pong) \
+ (VFE32_WM_BASE(wm) + 0x4 * (1 + (~(ping_pong >> wm) & 0x1)))
+
+/*Temporary use fixed bus vectors in VFE */
+static struct msm_bus_vectors msm_vfe32_init_vectors[] = {
+ {
+ .src = MSM_BUS_MASTER_VFE,
+ .dst = MSM_BUS_SLAVE_EBI_CH0,
+ .ab = 0,
+ .ib = 0,
+ },
+};
+
+static struct msm_bus_vectors msm_vfe32_preview_vectors[] = {
+ {
+ .src = MSM_BUS_MASTER_VFE,
+ .dst = MSM_BUS_SLAVE_EBI_CH0,
+ .ab = 1027648000,
+ .ib = 1105920000,
+ },
+};
+
+static struct msm_bus_paths msm_vfe32_bus_client_config[] = {
+ {
+ ARRAY_SIZE(msm_vfe32_init_vectors),
+ msm_vfe32_init_vectors,
+ },
+ {
+ ARRAY_SIZE(msm_vfe32_preview_vectors),
+ msm_vfe32_preview_vectors,
+ },
+};
+
+static struct msm_bus_scale_pdata msm_vfe32_bus_client_pdata = {
+ msm_vfe32_bus_client_config,
+ ARRAY_SIZE(msm_vfe32_bus_client_config),
+ .name = "msm_camera_vfe",
+};
+
+static struct msm_cam_clk_info msm_vfe32_clk_info[] = {
+ {"vfe_clk", 228570000},
+ {"vfe_pclk", -1},
+ {"csi_vfe_clk", -1},
+};
+
+static int msm_vfe32_init_hardware(struct vfe_device *vfe_dev)
+{
+ int rc = -1;
+
+ vfe_dev->bus_perf_client =
+ msm_bus_scale_register_client(&msm_vfe32_bus_client_pdata);
+ if (!vfe_dev->bus_perf_client) {
+ pr_err("%s: Registration Failed!\n", __func__);
+ vfe_dev->bus_perf_client = 0;
+ goto bus_scale_register_failed;
+ }
+ msm_bus_scale_client_update_request(vfe_dev->bus_perf_client, 1);
+
+ if (vfe_dev->fs_vfe) {
+ rc = regulator_enable(vfe_dev->fs_vfe);
+ if (rc) {
+ pr_err("%s: Regulator enable failed\n", __func__);
+ goto fs_failed;
+ }
+ }
+
+ rc = msm_cam_clk_enable(&vfe_dev->pdev->dev, msm_vfe32_clk_info,
+ vfe_dev->vfe_clk, ARRAY_SIZE(msm_vfe32_clk_info), 1);
+ if (rc < 0)
+ goto clk_enable_failed;
+
+ vfe_dev->vfe_base = ioremap(vfe_dev->vfe_mem->start,
+ resource_size(vfe_dev->vfe_mem));
+ if (!vfe_dev->vfe_base) {
+ rc = -ENOMEM;
+ pr_err("%s: vfe ioremap failed\n", __func__);
+ goto vfe_remap_failed;
+ }
+
+ rc = request_irq(vfe_dev->vfe_irq->start, msm_isp_process_irq,
+ IRQF_TRIGGER_RISING, "vfe", vfe_dev);
+ if (rc < 0) {
+ pr_err("%s: irq request failed\n", __func__);
+ goto irq_req_failed;
+ }
+
+ return rc;
+irq_req_failed:
+ iounmap(vfe_dev->vfe_base);
+vfe_remap_failed:
+ msm_cam_clk_enable(&vfe_dev->pdev->dev, msm_vfe32_clk_info,
+ vfe_dev->vfe_clk, ARRAY_SIZE(msm_vfe32_clk_info), 0);
+clk_enable_failed:
+ regulator_disable(vfe_dev->fs_vfe);
+fs_failed:
+ msm_bus_scale_client_update_request(vfe_dev->bus_perf_client, 0);
+ msm_bus_scale_unregister_client(vfe_dev->bus_perf_client);
+bus_scale_register_failed:
+ return rc;
+}
+
+static void msm_vfe32_release_hardware(struct vfe_device *vfe_dev)
+{
+ free_irq(vfe_dev->vfe_irq->start, vfe_dev);
+ tasklet_kill(&vfe_dev->vfe_tasklet);
+ iounmap(vfe_dev->vfe_base);
+ msm_cam_clk_enable(&vfe_dev->pdev->dev, msm_vfe32_clk_info,
+ vfe_dev->vfe_clk, ARRAY_SIZE(msm_vfe32_clk_info), 0);
+ regulator_disable(vfe_dev->fs_vfe);
+ msm_bus_scale_client_update_request(vfe_dev->bus_perf_client, 0);
+ msm_bus_scale_unregister_client(vfe_dev->bus_perf_client);
+}
+
+static void msm_vfe32_init_hardware_reg(struct vfe_device *vfe_dev)
+{
+ /* CGC_OVERRIDE */
+ msm_camera_io_w(0x07FFFFFF, vfe_dev->vfe_base + 0xC);
+ /* BUS_CFG */
+ msm_camera_io_w(0x00000001, vfe_dev->vfe_base + 0x3C);
+ msm_camera_io_w(0x00000025, vfe_dev->vfe_base + 0x1C);
+ msm_camera_io_w_mb(0x1DFFFFFF, vfe_dev->vfe_base + 0x20);
+ msm_camera_io_w(0xFFFFFFFF, vfe_dev->vfe_base + 0x24);
+ msm_camera_io_w_mb(0x1FFFFFFF, vfe_dev->vfe_base + 0x28);
+}
+
+static void msm_vfe32_process_reset_irq(struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1)
+{
+ if (irq_status1 & (1 << 23))
+ complete(&vfe_dev->reset_complete);
+}
+
+static void msm_vfe32_process_halt_irq(struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1)
+{
+ if (irq_status1 & (1 << 24))
+ complete(&vfe_dev->halt_complete);
+}
+
+static void msm_vfe32_process_camif_irq(struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1)
+{
+ if (!(irq_status0 & 0x1F))
+ return;
+
+ if (irq_status0 & (1 << 0)) {
+ ISP_DBG("%s: PIX0 frame id: %lu\n", __func__,
+ vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id);
+ msm_isp_update_framedrop_count(vfe_dev);
+ vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id++;
+ }
+}
+
+static void msm_vfe32_process_violation_irq(struct vfe_device *vfe_dev)
+{
+ uint32_t violation_status;
+ violation_status = msm_camera_io_r(vfe_dev->vfe_base + 0x48);
+ if (!violation_status)
+ return;
+
+ if (violation_status & (1 << 0))
+ pr_err("%s: black violation\n", __func__);
+ if (violation_status & (1 << 1))
+ pr_err("%s: rolloff violation\n", __func__);
+ if (violation_status & (1 << 2))
+ pr_err("%s: demux violation\n", __func__);
+ if (violation_status & (1 << 3))
+ pr_err("%s: demosaic violation\n", __func__);
+ if (violation_status & (1 << 4))
+ pr_err("%s: crop violation\n", __func__);
+ if (violation_status & (1 << 5))
+ pr_err("%s: scale violation\n", __func__);
+ if (violation_status & (1 << 6))
+ pr_err("%s: wb violation\n", __func__);
+ if (violation_status & (1 << 7))
+ pr_err("%s: clf violation\n", __func__);
+ if (violation_status & (1 << 8))
+ pr_err("%s: matrix violation\n", __func__);
+ if (violation_status & (1 << 9))
+ pr_err("%s: rgb lut violation\n", __func__);
+ if (violation_status & (1 << 10))
+ pr_err("%s: la violation\n", __func__);
+ if (violation_status & (1 << 11))
+ pr_err("%s: chroma enhance violation\n", __func__);
+ if (violation_status & (1 << 12))
+ pr_err("%s: chroma supress mce violation\n", __func__);
+ if (violation_status & (1 << 13))
+ pr_err("%s: skin enhance violation\n", __func__);
+ if (violation_status & (1 << 14))
+ pr_err("%s: asf violation\n", __func__);
+ if (violation_status & (1 << 15))
+ pr_err("%s: scale y violation\n", __func__);
+ if (violation_status & (1 << 16))
+ pr_err("%s: scale cbcr violation\n", __func__);
+ if (violation_status & (1 << 17))
+ pr_err("%s: chroma subsample violation\n", __func__);
+ if (violation_status & (1 << 18))
+ pr_err("%s: framedrop enc y violation\n", __func__);
+ if (violation_status & (1 << 19))
+ pr_err("%s: framedrop enc cbcr violation\n", __func__);
+ if (violation_status & (1 << 20))
+ pr_err("%s: framedrop view y violation\n", __func__);
+ if (violation_status & (1 << 21))
+ pr_err("%s: framedrop view cbcr violation\n", __func__);
+ if (violation_status & (1 << 22))
+ pr_err("%s: realign buf y violation\n", __func__);
+ if (violation_status & (1 << 23))
+ pr_err("%s: realign buf cb violation\n", __func__);
+ if (violation_status & (1 << 24))
+ pr_err("%s: realign buf cr violation\n", __func__);
+}
+
+static void msm_vfe32_process_error_irq(struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1)
+{
+ uint32_t camif_status;
+ if (!(irq_status1 & 0x7FFFFF))
+ return;
+
+ if (irq_status1 & (1 << 0)) {
+ camif_status = msm_camera_io_r(vfe_dev->vfe_base + 0x204);
+ pr_err("%s: camif error status: 0x%x\n",
+ __func__, camif_status);
+ }
+ if (irq_status1 & (1 << 1))
+ pr_err("%s: stats bhist overwrite\n", __func__);
+ if (irq_status1 & (1 << 2))
+ pr_err("%s: stats cs overwrite\n", __func__);
+ if (irq_status1 & (1 << 3))
+ pr_err("%s: stats ihist overwrite\n", __func__);
+ if (irq_status1 & (1 << 4))
+ pr_err("%s: realign buf y overflow\n", __func__);
+ if (irq_status1 & (1 << 5))
+ pr_err("%s: realign buf cb overflow\n", __func__);
+ if (irq_status1 & (1 << 6))
+ pr_err("%s: realign buf cr overflow\n", __func__);
+ if (irq_status1 & (1 << 7)) {
+ pr_err("%s: violation\n", __func__);
+ msm_vfe32_process_violation_irq(vfe_dev);
+ }
+ if (irq_status1 & (1 << 8))
+ pr_err("%s: image master 0 bus overflow\n", __func__);
+ if (irq_status1 & (1 << 9))
+ pr_err("%s: image master 1 bus overflow\n", __func__);
+ if (irq_status1 & (1 << 10))
+ pr_err("%s: image master 2 bus overflow\n", __func__);
+ if (irq_status1 & (1 << 11))
+ pr_err("%s: image master 3 bus overflow\n", __func__);
+ if (irq_status1 & (1 << 12))
+ pr_err("%s: image master 4 bus overflow\n", __func__);
+ if (irq_status1 & (1 << 13))
+ pr_err("%s: image master 5 bus overflow\n", __func__);
+ if (irq_status1 & (1 << 14))
+ pr_err("%s: image master 6 bus overflow\n", __func__);
+ if (irq_status1 & (1 << 15))
+ pr_err("%s: status ae/bg bus overflow\n", __func__);
+ if (irq_status1 & (1 << 16))
+ pr_err("%s: status af/bf bus overflow\n", __func__);
+ if (irq_status1 & (1 << 17))
+ pr_err("%s: status awb bus overflow\n", __func__);
+ if (irq_status1 & (1 << 18))
+ pr_err("%s: status rs bus overflow\n", __func__);
+ if (irq_status1 & (1 << 19))
+ pr_err("%s: status cs bus overflow\n", __func__);
+ if (irq_status1 & (1 << 20))
+ pr_err("%s: status ihist bus overflow\n", __func__);
+ if (irq_status1 & (1 << 21))
+ pr_err("%s: status skin bhist bus overflow\n", __func__);
+ if (irq_status1 & (1 << 22))
+ pr_err("%s: axi error\n", __func__);
+}
+
+static void msm_vfe32_read_irq_status(struct vfe_device *vfe_dev,
+ uint32_t *irq_status0, uint32_t *irq_status1)
+{
+ *irq_status0 = msm_camera_io_r(vfe_dev->vfe_base + 0x2C);
+ *irq_status1 = msm_camera_io_r(vfe_dev->vfe_base + 0x30);
+ msm_camera_io_w(*irq_status0, vfe_dev->vfe_base + 0x24);
+ msm_camera_io_w(*irq_status1, vfe_dev->vfe_base + 0x28);
+ msm_camera_io_w_mb(1, vfe_dev->vfe_base + 0x18);
+}
+
+static void msm_vfe32_process_reg_update(struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1)
+{
+ if (!(irq_status0 & 0x20) && !(irq_status1 & 0x1C000000))
+ return;
+
+ if (vfe_dev->axi_data.stream_update)
+ msm_isp_axi_stream_update(vfe_dev);
+
+ msm_isp_update_framedrop_reg(vfe_dev);
+
+ return;
+}
+
+static void msm_vfe32_reg_update(
+ struct vfe_device *vfe_dev, uint32_t update_mask)
+{
+ msm_camera_io_w_mb(update_mask, vfe_dev->vfe_base + 0x260);
+}
+
+static long msm_vfe32_reset_hardware(struct vfe_device *vfe_dev)
+{
+ init_completion(&vfe_dev->reset_complete);
+ msm_camera_io_w_mb(0x3FF, vfe_dev->vfe_base + 0x4);
+ return wait_for_completion_interruptible_timeout(
+ &vfe_dev->reset_complete, msecs_to_jiffies(50));
+}
+
+static void msm_vfe32_axi_reload_wm(
+ struct vfe_device *vfe_dev, uint32_t reload_mask)
+{
+ msm_camera_io_w_mb(reload_mask, vfe_dev->vfe_base + 0x38);
+}
+
+static void msm_vfe32_axi_enable_wm(struct vfe_device *vfe_dev,
+ uint8_t wm_idx, uint8_t enable)
+{
+ if (enable)
+ msm_camera_io_w_mb(0x1,
+ vfe_dev->vfe_base + VFE32_WM_BASE(wm_idx));
+ else
+ msm_camera_io_w_mb(0x0,
+ vfe_dev->vfe_base + VFE32_WM_BASE(wm_idx));
+}
+
+static void msm_vfe32_axi_cfg_comp_mask(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info)
+{
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
+ uint32_t comp_mask, comp_mask_index =
+ stream_info->comp_mask_index;
+ uint32_t irq_mask;
+
+ comp_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x34);
+ comp_mask &= ~(0x7F << (comp_mask_index * 8));
+ comp_mask |= (axi_data->composite_info[comp_mask_index].
+ stream_composite_mask << (comp_mask_index * 8));
+ msm_camera_io_w(comp_mask, vfe_dev->vfe_base + 0x34);
+
+ irq_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x1C);
+ irq_mask |= 1 << (comp_mask_index + 21);
+ msm_camera_io_w(irq_mask, vfe_dev->vfe_base + 0x1C);
+}
+
+static void msm_vfe32_axi_clear_comp_mask(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info)
+{
+ uint32_t comp_mask, comp_mask_index = stream_info->comp_mask_index;
+ uint32_t irq_mask;
+
+ comp_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x34);
+ comp_mask &= ~(0x7F << (comp_mask_index * 8));
+ msm_camera_io_w(comp_mask, vfe_dev->vfe_base + 0x34);
+
+ irq_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x1C);
+ irq_mask &= ~(1 << (comp_mask_index + 21));
+ msm_camera_io_w(irq_mask, vfe_dev->vfe_base + 0x1C);
+}
+
+static void msm_vfe32_axi_cfg_wm_irq_mask(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info)
+{
+ uint32_t irq_mask;
+ irq_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x1C);
+ irq_mask |= 1 << (stream_info->wm[0] + 6);
+ msm_camera_io_w(irq_mask, vfe_dev->vfe_base + 0x1C);
+}
+
+static void msm_vfe32_axi_clear_wm_irq_mask(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info)
+{
+ uint32_t irq_mask;
+ irq_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x1C);
+ irq_mask &= ~(1 << (stream_info->wm[0] + 6));
+ msm_camera_io_w(irq_mask, vfe_dev->vfe_base + 0x1C);
+}
+
+static void msm_vfe32_cfg_framedrop(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info)
+{
+ uint32_t framedrop_pattern = 0;
+
+ if (stream_info->init_frame_drop == 0)
+ framedrop_pattern = stream_info->framedrop_pattern;
+
+ if (stream_info->stream_type == BURST_STREAM &&
+ stream_info->burst_frame_count == 0)
+ framedrop_pattern = 0;
+
+ if (stream_info->stream_src == PIX_ENCODER) {
+ msm_camera_io_w(0x1F, vfe_dev->vfe_base + 0x504);
+ msm_camera_io_w(0x1F, vfe_dev->vfe_base + 0x508);
+ msm_camera_io_w(framedrop_pattern, vfe_dev->vfe_base + 0x50C);
+ msm_camera_io_w(framedrop_pattern, vfe_dev->vfe_base + 0x510);
+ } else if (stream_info->stream_src == PIX_VIEWFINDER) {
+ msm_camera_io_w(0x1F, vfe_dev->vfe_base + 0x514);
+ msm_camera_io_w(0x1F, vfe_dev->vfe_base + 0x518);
+ msm_camera_io_w(framedrop_pattern, vfe_dev->vfe_base + 0x51C);
+ msm_camera_io_w(framedrop_pattern, vfe_dev->vfe_base + 0x520);
+ }
+ msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x260);
+}
+
+static void msm_vfe32_clear_framedrop(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info)
+{
+ if (stream_info->stream_src == PIX_ENCODER) {
+ msm_camera_io_w(0, vfe_dev->vfe_base + 0x50C);
+ msm_camera_io_w(0, vfe_dev->vfe_base + 0x510);
+ } else if (stream_info->stream_src == PIX_VIEWFINDER) {
+ msm_camera_io_w(0, vfe_dev->vfe_base + 0x51C);
+ msm_camera_io_w(0, vfe_dev->vfe_base + 0x520);
+ }
+}
+
+static void msm_vfe32_cfg_camif(struct vfe_device *vfe_dev,
+ struct msm_vfe_pix_cfg *pix_cfg)
+{
+ uint16_t first_pixel, last_pixel, first_line, last_line;
+ struct msm_vfe_camif_cfg *camif_cfg = &pix_cfg->camif_cfg;
+ uint32_t val;
+
+ first_pixel = camif_cfg->left_crop;
+ last_pixel = camif_cfg->pixels_per_line -
+ camif_cfg->left_crop -
+ camif_cfg->right_crop;
+ first_line = camif_cfg->left_crop;
+ last_line = camif_cfg->lines_per_frame -
+ camif_cfg->top_crop -
+ camif_cfg->bottom_crop;
+
+ msm_camera_io_w(pix_cfg->input_mux << 16 | pix_cfg->pixel_pattern,
+ vfe_dev->vfe_base + 0x14);
+
+ msm_camera_io_w(camif_cfg->lines_per_frame << 16 |
+ camif_cfg->pixels_per_line,
+ vfe_dev->vfe_base + 0x1EC);
+
+ msm_camera_io_w(ISP_SUB(first_pixel) << 16 | ISP_SUB(last_pixel),
+ vfe_dev->vfe_base + 0x1F0);
+
+ msm_camera_io_w(ISP_SUB(first_line) << 16 | ISP_SUB(last_line),
+ vfe_dev->vfe_base + 0x1F4);
+
+ val = msm_camera_io_r(vfe_dev->vfe_base + 0x6FC);
+ val &= 0xFFFFFFFC;
+ val |= camif_cfg->camif_input;
+ msm_camera_io_w(val, vfe_dev->vfe_base + 0x6FC);
+}
+
+static void msm_vfe32_update_camif_state(
+ struct vfe_device *vfe_dev,
+ enum msm_isp_camif_update_state update_state)
+{
+ uint32_t val;
+ bool bus_en, vfe_en;
+ if (update_state == NO_UPDATE)
+ return;
+
+ val = msm_camera_io_r(vfe_dev->vfe_base + 0x1E4);
+ if (update_state == ENABLE_CAMIF) {
+ bus_en =
+ ((vfe_dev->axi_data.src_info[
+ VFE_PIX_0].raw_stream_count > 0) ? 1 : 0);
+ vfe_en =
+ ((vfe_dev->axi_data.src_info[
+ VFE_PIX_0].pix_stream_count > 0) ? 1 : 0);
+ val &= 0xFFFFFF3F;
+ val = val | bus_en << 7 | vfe_en << 6;
+ msm_camera_io_w(val, vfe_dev->vfe_base + 0x1E4);
+ msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x1E0);
+ vfe_dev->axi_data.src_info[VFE_PIX_0].active = 1;
+ } else if (update_state == DISABLE_CAMIF) {
+ msm_camera_io_w_mb(0x0, vfe_dev->vfe_base + 0x1E0);
+ vfe_dev->axi_data.src_info[VFE_PIX_0].active = 0;
+ }
+}
+
+static void msm_vfe32_axi_cfg_wm_reg(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd,
+ uint8_t plane_idx)
+{
+ uint32_t val;
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
+ struct msm_vfe_axi_stream *stream_info =
+ &axi_data->stream_info[
+ (stream_cfg_cmd->axi_stream_handle & 0xFF)];
+ uint32_t wm_base = VFE32_WM_BASE(stream_info->wm[plane_idx]);
+
+ /*WR_IMAGE_SIZE*/
+ val =
+ ((msm_isp_cal_word_per_line(stream_cfg_cmd->output_format,
+ stream_cfg_cmd->plane_cfg[
+ plane_idx].output_width)+1)/2 - 1) << 16 |
+ (stream_cfg_cmd->plane_cfg[plane_idx].output_height - 1);
+ msm_camera_io_w(val, vfe_dev->vfe_base + wm_base + 0x10);
+
+ /*WR_BUFFER_CFG*/
+ val =
+ msm_isp_cal_word_per_line(stream_cfg_cmd->output_format,
+ stream_cfg_cmd->plane_cfg[plane_idx].output_stride) << 16 |
+ (stream_cfg_cmd->plane_cfg[
+ plane_idx].output_scan_lines - 1) << 4 |
+ VFE32_BURST_LEN >> 2;
+ msm_camera_io_w(val, vfe_dev->vfe_base + wm_base + 0x14);
+ return;
+}
+
+static void msm_vfe32_axi_clear_wm_reg(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info, uint8_t plane_idx)
+{
+ uint32_t val = 0;
+ uint32_t wm_base = VFE32_WM_BASE(stream_info->wm[plane_idx]);
+ /*WR_IMAGE_SIZE*/
+ msm_camera_io_w(val, vfe_dev->vfe_base + wm_base + 0x10);
+ /*WR_BUFFER_CFG*/
+ msm_camera_io_w(val, vfe_dev->vfe_base + wm_base + 0x14);
+ return;
+}
+
+static void msm_vfe32_axi_cfg_rdi_reg(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd,
+ uint8_t plane_idx)
+{
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
+ struct msm_vfe_axi_stream *stream_info =
+ &axi_data->stream_info[(stream_cfg_cmd->axi_stream_handle & 0xFF)];
+ struct msm_vfe_axi_plane_cfg *plane_cfg =
+ &stream_cfg_cmd->plane_cfg[plane_idx];
+ uint8_t rdi = stream_info->rdi[plane_idx];
+ uint8_t rdi_master = stream_info->rdi_master[plane_idx];
+ uint32_t rdi_reg_cfg;
+
+ rdi_reg_cfg = msm_camera_io_r(vfe_dev->vfe_base + VFE32_RDI_BASE(rdi));
+ rdi_reg_cfg = (rdi_reg_cfg & 0xFFFFFFF) | rdi_master << 28;
+ msm_camera_io_w(rdi_reg_cfg, vfe_dev->vfe_base + VFE32_RDI_BASE(rdi));
+
+ rdi_reg_cfg = msm_camera_io_r(
+ vfe_dev->vfe_base + VFE32_RDI_MN_BASE(rdi_master));
+ rdi_reg_cfg &= ~((0xF << VFE32_RDI_MN_SEL_SHIFT(rdi_master)) |
+ (0x1 << VFE32_RDI_MN_FB_SHIFT(rdi_master)));
+ rdi_reg_cfg |= (plane_cfg->rdi_cid <<
+ VFE32_RDI_MN_SEL_SHIFT(rdi_master) |
+ (stream_cfg_cmd->frame_base <<
+ VFE32_RDI_MN_FB_SHIFT(rdi_master)));
+ msm_camera_io_w(rdi_reg_cfg,
+ vfe_dev->vfe_base +
+ VFE32_RDI_MN_BASE(rdi_master));
+}
+
+static void msm_vfe32_axi_cfg_wm_xbar_reg(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd,
+ uint8_t plane_idx)
+{
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
+ struct msm_vfe_axi_stream *stream_info =
+ &axi_data->stream_info[(stream_cfg_cmd->axi_stream_handle & 0xFF)];
+ struct msm_vfe_axi_plane_cfg *plane_cfg =
+ &stream_cfg_cmd->plane_cfg[plane_idx];
+ uint8_t wm = stream_info->wm[plane_idx];
+ uint32_t xbar_cfg = 0;
+ uint32_t xbar_reg_cfg = 0;
+
+ switch (stream_cfg_cmd->stream_src) {
+ case PIX_ENCODER:
+ case PIX_VIEWFINDER: {
+ if (plane_cfg->output_plane_format != CRCB_PLANE &&
+ plane_cfg->output_plane_format != CBCR_PLANE) {
+ /*SINGLE_STREAM_SEL*/
+ xbar_cfg |= plane_cfg->output_plane_format << 5;
+ } else {
+ switch (stream_cfg_cmd->output_format) {
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV16:
+ xbar_cfg |= 0x3 << 3; /*PAIR_STREAM_SWAP_CTRL*/
+ break;
+ }
+ xbar_cfg |= 0x1 << 1; /*PAIR_STREAM_EN*/
+ }
+ if (stream_cfg_cmd->stream_src == PIX_VIEWFINDER)
+ xbar_cfg |= 0x1; /*VIEW_STREAM_EN*/
+ break;
+ }
+ case CAMIF_RAW:
+ xbar_cfg = 0x60;
+ break;
+ case IDEAL_RAW:
+ xbar_cfg = 0x80;
+ break;
+ case RDI:
+ if (stream_info->rdi[plane_idx] == 0)
+ xbar_cfg = 0xA0;
+ else if (stream_info->rdi[plane_idx] == 1)
+ xbar_cfg = 0xC0;
+ else if (stream_info->rdi[plane_idx] == 2)
+ xbar_cfg = 0xE0;
+ break;
+ default:
+ pr_err("%s: Invalid stream src\n", __func__);
+ }
+ xbar_reg_cfg = msm_camera_io_r(vfe_dev->vfe_base + VFE32_XBAR_BASE(wm));
+ xbar_reg_cfg &= ~(0xFF << VFE32_XBAR_SHIFT(wm));
+ xbar_reg_cfg |= (xbar_cfg << VFE32_XBAR_SHIFT(wm));
+ msm_camera_io_w(xbar_reg_cfg, vfe_dev->vfe_base + VFE32_XBAR_BASE(wm));
+ return;
+}
+
+static void msm_vfe32_axi_clear_wm_xbar_reg(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info, uint8_t plane_idx)
+{
+ uint8_t wm = stream_info->wm[plane_idx];
+ uint32_t xbar_reg_cfg = 0;
+
+ xbar_reg_cfg = msm_camera_io_r(vfe_dev->vfe_base + VFE32_XBAR_BASE(wm));
+ xbar_reg_cfg &= ~(0xFF << VFE32_XBAR_SHIFT(wm));
+ msm_camera_io_w(xbar_reg_cfg, vfe_dev->vfe_base + VFE32_XBAR_BASE(wm));
+}
+
+static void msm_vfe32_cfg_axi_ub(struct vfe_device *vfe_dev)
+{
+ int i;
+ uint32_t ub_offset = 0;
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
+ for (i = 0; i < axi_data->hw_info->num_wm; i++) {
+ msm_camera_io_w(ub_offset << 16 | (VFE32_EQUAL_SLICE_UB - 1),
+ vfe_dev->vfe_base + VFE32_WM_BASE(i) + 0xC);
+ ub_offset += VFE32_EQUAL_SLICE_UB;
+ }
+}
+
+static void msm_vfe32_update_ping_pong_addr(struct vfe_device *vfe_dev,
+ uint8_t wm_idx, uint32_t pingpong_status, unsigned long paddr)
+{
+ msm_camera_io_w(paddr, vfe_dev->vfe_base +
+ VFE32_PING_PONG_BASE(wm_idx, pingpong_status));
+}
+
+static long msm_vfe32_axi_halt(struct vfe_device *vfe_dev)
+{
+ uint32_t halt_mask;
+ halt_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x20);
+ halt_mask |= (1 << 24);
+ msm_camera_io_w_mb(halt_mask, vfe_dev->vfe_base + 0x20);
+ init_completion(&vfe_dev->halt_complete);
+ /*TD: Need to fix crashes with this*/
+ /*msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x1D8);*/
+ return wait_for_completion_interruptible_timeout(
+ &vfe_dev->halt_complete, msecs_to_jiffies(500));
+}
+
+static uint32_t msm_vfe32_get_wm_mask(
+ uint32_t irq_status0, uint32_t irq_status1)
+{
+ return (irq_status0 >> 6) & 0x7F;
+}
+
+static uint32_t msm_vfe32_get_comp_mask(
+ uint32_t irq_status0, uint32_t irq_status1)
+{
+ return (irq_status0 >> 21) & 0x7;
+}
+
+static uint32_t msm_vfe32_get_pingpong_status(struct vfe_device *vfe_dev)
+{
+ return msm_camera_io_r(vfe_dev->vfe_base + 0x180);
+}
+
+static int msm_vfe32_get_platform_data(struct vfe_device *vfe_dev)
+{
+ int rc = 0;
+ vfe_dev->vfe_mem = platform_get_resource_byname(vfe_dev->pdev,
+ IORESOURCE_MEM, "vfe");
+ if (!vfe_dev->vfe_mem) {
+ pr_err("%s: no mem resource?\n", __func__);
+ rc = -ENODEV;
+ goto vfe_no_resource;
+ }
+
+ vfe_dev->vfe_irq = platform_get_resource_byname(vfe_dev->pdev,
+ IORESOURCE_IRQ, "vfe");
+ if (!vfe_dev->vfe_irq) {
+ pr_err("%s: no irq resource?\n", __func__);
+ rc = -ENODEV;
+ goto vfe_no_resource;
+ }
+
+ vfe_dev->fs_vfe = regulator_get(&vfe_dev->pdev->dev, "vdd");
+ if (IS_ERR(vfe_dev->fs_vfe)) {
+ pr_err("%s: Regulator get failed %ld\n", __func__,
+ PTR_ERR(vfe_dev->fs_vfe));
+ vfe_dev->fs_vfe = NULL;
+ rc = -ENODEV;
+ goto vfe_no_resource;
+ }
+
+ vfe_dev->iommu_ctx[0] = msm_iommu_get_ctx("vfe_imgwr");
+ if (!vfe_dev->iommu_ctx[0]) {
+ pr_err("%s: no iommux ctx resource?\n", __func__);
+ rc = -ENODEV;
+ goto vfe_no_resource;
+ }
+
+ vfe_dev->iommu_ctx[1] = msm_iommu_get_ctx("vfe_misc");
+ if (!vfe_dev->iommu_ctx[1]) {
+ pr_err("%s: no iommux ctx resource?\n", __func__);
+ rc = -ENODEV;
+ goto vfe_no_resource;
+ }
+ vfe_dev->num_ctx = 2;
+
+vfe_no_resource:
+ return rc;
+}
+
+struct msm_vfe_axi_hardware_info msm_vfe32_axi_hw_info = {
+ .num_wm = 7,
+ .num_comp_mask = 3,
+ .num_rdi = 3,
+ .num_rdi_master = 3,
+};
+
+static struct v4l2_subdev_core_ops msm_vfe32_subdev_core_ops = {
+ .ioctl = msm_isp_ioctl,
+ .subscribe_event = msm_isp_subscribe_event,
+ .unsubscribe_event = msm_isp_unsubscribe_event,
+};
+
+static struct v4l2_subdev_ops msm_vfe32_subdev_ops = {
+ .core = &msm_vfe32_subdev_core_ops,
+};
+
+static struct v4l2_subdev_internal_ops msm_vfe32_internal_ops = {
+ .open = msm_isp_open_node,
+ .close = msm_isp_close_node,
+};
+
+struct msm_vfe_hardware_info vfe32_hw_info = {
+ .vfe_ops = {
+ .irq_ops = {
+ .read_irq_status = msm_vfe32_read_irq_status,
+ .process_camif_irq = msm_vfe32_process_camif_irq,
+ .process_reset_irq = msm_vfe32_process_reset_irq,
+ .process_halt_irq = msm_vfe32_process_halt_irq,
+ .process_reset_irq = msm_vfe32_process_reset_irq,
+ .process_error_irq = msm_vfe32_process_error_irq,
+ .process_reg_update = msm_vfe32_process_reg_update,
+ .process_axi_irq = msm_isp_process_axi_irq,
+ },
+ .axi_ops = {
+ .reload_wm = msm_vfe32_axi_reload_wm,
+ .enable_wm = msm_vfe32_axi_enable_wm,
+ .cfg_comp_mask = msm_vfe32_axi_cfg_comp_mask,
+ .clear_comp_mask = msm_vfe32_axi_clear_comp_mask,
+ .cfg_wm_irq_mask = msm_vfe32_axi_cfg_wm_irq_mask,
+ .clear_wm_irq_mask = msm_vfe32_axi_clear_wm_irq_mask,
+ .cfg_framedrop = msm_vfe32_cfg_framedrop,
+ .clear_framedrop = msm_vfe32_clear_framedrop,
+ .cfg_wm_reg = msm_vfe32_axi_cfg_wm_reg,
+ .clear_wm_reg = msm_vfe32_axi_clear_wm_reg,
+ .cfg_wm_xbar_reg = msm_vfe32_axi_cfg_wm_xbar_reg,
+ .clear_wm_xbar_reg = msm_vfe32_axi_clear_wm_xbar_reg,
+ .cfg_rdi_reg = msm_vfe32_axi_cfg_rdi_reg,
+ .cfg_ub = msm_vfe32_cfg_axi_ub,
+ .update_ping_pong_addr =
+ msm_vfe32_update_ping_pong_addr,
+ .get_comp_mask = msm_vfe32_get_comp_mask,
+ .get_wm_mask = msm_vfe32_get_wm_mask,
+ .get_pingpong_status = msm_vfe32_get_pingpong_status,
+ .halt = msm_vfe32_axi_halt,
+ },
+ .core_ops = {
+ .reg_update = msm_vfe32_reg_update,
+ .cfg_camif = msm_vfe32_cfg_camif,
+ .update_camif_state = msm_vfe32_update_camif_state,
+ .reset_hw = msm_vfe32_reset_hardware,
+ .init_hw = msm_vfe32_init_hardware,
+ .init_hw_reg = msm_vfe32_init_hardware_reg,
+ .release_hw = msm_vfe32_release_hardware,
+ .get_platform_data = msm_vfe32_get_platform_data,
+ },
+ },
+ .axi_hw_info = &msm_vfe32_axi_hw_info,
+ .subdev_ops = &msm_vfe32_subdev_ops,
+ .subdev_internal_ops = &msm_vfe32_internal_ops,
+};
+EXPORT_SYMBOL(vfe32_hw_info);
diff --git a/arch/arm/boot/dts/msm8974-mtp.dts b/drivers/media/video/msmb/isp/msm_isp32.h
similarity index 64%
copy from arch/arm/boot/dts/msm8974-mtp.dts
copy to drivers/media/video/msmb/isp/msm_isp32.h
index 9946cf0..0535048 100644
--- a/arch/arm/boot/dts/msm8974-mtp.dts
+++ b/drivers/media/video/msmb/isp/msm_isp32.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* 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
@@ -10,13 +10,8 @@
* GNU General Public License for more details.
*/
-/dts-v1/;
+#ifndef __MSM_ISP32_H__
+#define __MSM_ISP32_H__
-/include/ "msm8974.dtsi"
-/include/ "msm8974-mtp.dtsi"
-
-/ {
- model = "Qualcomm MSM 8974 MTP";
- compatible = "qcom,msm8974-mtp", "qcom,msm8974";
- qcom,msm-id = <126 8 0>;
-};
+extern struct msm_vfe_hardware_info vfe32_hw_info;
+#endif /* __MSM_ISP32_H__ */
diff --git a/drivers/media/video/msmb/isp/msm_isp40.c b/drivers/media/video/msmb/isp/msm_isp40.c
new file mode 100644
index 0000000..6c90763
--- /dev/null
+++ b/drivers/media/video/msmb/isp/msm_isp40.c
@@ -0,0 +1,1181 @@
+/* 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 <mach/iommu.h>
+
+#include "msm_isp40.h"
+#include "msm_isp_util.h"
+#include "msm_isp_axi_util.h"
+#include "msm_isp_stats_util.h"
+#include "msm_isp.h"
+#include "msm.h"
+#include "msm_camera_io_util.h"
+
+/*#define CONFIG_MSM_ISP_DBG*/
+#undef CDBG
+#ifdef CONFIG_MSM_ISP_DBG
+#define CDBG(fmt, args...) pr_err(fmt, ##args)
+#else
+#define CDBG(fmt, args...) do { } while (0)
+#endif
+
+#define VFE40_BURST_LEN 4
+#define VFE40_EQUAL_SLICE_UB 117
+#define VFE40_WM_BASE(idx) (0x6C + 0x24 * idx)
+#define VFE40_RDI_BASE(idx) (0x2E8 + 0x4 * idx)
+#define VFE40_RDI_MN_BASE(m) (0x2E8 + 0x4 * m/3)
+#define VFE40_RDI_MN_SEL_SHIFT(m) (4*(m%3) + 4)
+#define VFE40_RDI_MN_FB_SHIFT(m) ((m%3) + 16)
+#define VFE40_XBAR_BASE(idx) (0x58 + 0x4 * (idx / 2))
+#define VFE40_XBAR_SHIFT(idx) ((idx%2) ? 16 : 0)
+#define VFE40_PING_PONG_BASE(wm, ping_pong) \
+ (VFE40_WM_BASE(wm) + 0x4 * (1 + (~(ping_pong >> wm) & 0x1)))
+
+#define VFE40_VBIF_CLKON 0x4
+#define VFE40_VBIF_IN_RD_LIM_CONF0 0xB0
+#define VFE40_VBIF_IN_RD_LIM_CONF1 0xB4
+#define VFE40_VBIF_IN_RD_LIM_CONF2 0xB8
+#define VFE40_VBIF_IN_WR_LIM_CONF0 0xC0
+#define VFE40_VBIF_IN_WR_LIM_CONF1 0xC4
+#define VFE40_VBIF_IN_WR_LIM_CONF2 0xC8
+#define VFE40_VBIF_OUT_RD_LIM_CONF0 0xD0
+#define VFE40_VBIF_OUT_WR_LIM_CONF0 0xD4
+#define VFE40_VBIF_DDR_OUT_MAX_BURST 0xD8
+#define VFE40_VBIF_ARB_CTL 0xF0
+#define VFE40_VBIF_DDR_ARB_CONF0 0xF4
+#define VFE40_VBIF_DDR_ARB_CONF1 0xF8
+#define VFE40_VBIF_ROUND_ROBIN_QOS_ARB 0x124
+#define VFE40_VBIF_OUT_AXI_AOOO_EN 0x178
+#define VFE40_VBIF_OUT_AXI_AOOO 0x17C
+
+/*Temporary use fixed bus vectors in VFE */
+static struct msm_bus_vectors msm_vfe40_init_vectors[] = {
+ {
+ .src = MSM_BUS_MASTER_VFE,
+ .dst = MSM_BUS_SLAVE_EBI_CH0,
+ .ab = 0,
+ .ib = 0,
+ },
+};
+
+static struct msm_bus_vectors msm_vfe40_preview_vectors[] = {
+ {
+ .src = MSM_BUS_MASTER_VFE,
+ .dst = MSM_BUS_SLAVE_EBI_CH0,
+ .ab = 1027648000,
+ .ib = 1105920000,
+ },
+};
+
+static struct msm_bus_paths msm_vfe40_bus_client_config[] = {
+ {
+ ARRAY_SIZE(msm_vfe40_init_vectors),
+ msm_vfe40_init_vectors,
+ },
+ {
+ ARRAY_SIZE(msm_vfe40_preview_vectors),
+ msm_vfe40_preview_vectors,
+ },
+};
+
+static struct msm_bus_scale_pdata msm_vfe40_bus_client_pdata = {
+ msm_vfe40_bus_client_config,
+ ARRAY_SIZE(msm_vfe40_bus_client_config),
+ .name = "msm_camera_vfe",
+};
+
+static struct msm_cam_clk_info msm_vfe40_clk_info[] = {
+ {"camss_top_ahb_clk", -1},
+ {"vfe_clk_src", 266670000},
+ {"camss_vfe_vfe_clk", -1},
+ {"camss_csi_vfe_clk", -1},
+ {"iface_clk", -1},
+ {"bus_clk", -1},
+ {"alt_bus_clk", -1},
+};
+
+static void msm_vfe40_init_vbif_parms(
+ void __iomem *vfe_vbif_base)
+{
+ msm_camera_io_w_mb(0x1,
+ vfe_vbif_base + VFE40_VBIF_CLKON);
+ msm_camera_io_w_mb(0x1,
+ vfe_vbif_base + VFE40_VBIF_ROUND_ROBIN_QOS_ARB);
+ msm_camera_io_w_mb(0xFFFF,
+ vfe_vbif_base + VFE40_VBIF_OUT_AXI_AOOO_EN);
+ msm_camera_io_w_mb(0xFFFFFFFF,
+ vfe_vbif_base + VFE40_VBIF_OUT_AXI_AOOO);
+
+ msm_camera_io_w_mb(0x10101010,
+ vfe_vbif_base + VFE40_VBIF_IN_RD_LIM_CONF0);
+ msm_camera_io_w_mb(0x10101010,
+ vfe_vbif_base + VFE40_VBIF_IN_RD_LIM_CONF1);
+ msm_camera_io_w_mb(0x10101010,
+ vfe_vbif_base + VFE40_VBIF_IN_RD_LIM_CONF2);
+ msm_camera_io_w_mb(0x10101010,
+ vfe_vbif_base + VFE40_VBIF_IN_WR_LIM_CONF0);
+ msm_camera_io_w_mb(0x10101010,
+ vfe_vbif_base + VFE40_VBIF_IN_WR_LIM_CONF1);
+ msm_camera_io_w_mb(0x10101010,
+ vfe_vbif_base + VFE40_VBIF_IN_WR_LIM_CONF2);
+ msm_camera_io_w_mb(0x00001010,
+ vfe_vbif_base + VFE40_VBIF_OUT_RD_LIM_CONF0);
+ msm_camera_io_w_mb(0x00001010,
+ vfe_vbif_base + VFE40_VBIF_OUT_WR_LIM_CONF0);
+ msm_camera_io_w_mb(0x00000707,
+ vfe_vbif_base + VFE40_VBIF_DDR_OUT_MAX_BURST);
+ msm_camera_io_w_mb(0x00000030,
+ vfe_vbif_base + VFE40_VBIF_ARB_CTL);
+ msm_camera_io_w_mb(0x04210842,
+ vfe_vbif_base + VFE40_VBIF_DDR_ARB_CONF0);
+ msm_camera_io_w_mb(0x04210842,
+ vfe_vbif_base + VFE40_VBIF_DDR_ARB_CONF1);
+}
+
+static int msm_vfe40_init_hardware(struct vfe_device *vfe_dev)
+{
+ int rc = -1;
+
+ vfe_dev->bus_perf_client =
+ msm_bus_scale_register_client(&msm_vfe40_bus_client_pdata);
+ if (!vfe_dev->bus_perf_client) {
+ pr_err("%s: Registration Failed!\n", __func__);
+ vfe_dev->bus_perf_client = 0;
+ goto bus_scale_register_failed;
+ }
+ msm_bus_scale_client_update_request(
+ vfe_dev->bus_perf_client, 1);
+
+ if (vfe_dev->fs_vfe) {
+ rc = regulator_enable(vfe_dev->fs_vfe);
+ if (rc) {
+ pr_err("%s: Regulator enable failed\n", __func__);
+ goto fs_failed;
+ }
+ }
+
+ rc = msm_cam_clk_enable(&vfe_dev->pdev->dev, msm_vfe40_clk_info,
+ vfe_dev->vfe_clk, ARRAY_SIZE(msm_vfe40_clk_info), 1);
+ if (rc < 0)
+ goto clk_enable_failed;
+
+ vfe_dev->vfe_base = ioremap(vfe_dev->vfe_mem->start,
+ resource_size(vfe_dev->vfe_mem));
+ if (!vfe_dev->vfe_base) {
+ rc = -ENOMEM;
+ pr_err("%s: vfe ioremap failed\n", __func__);
+ goto vfe_remap_failed;
+ }
+
+ vfe_dev->vfe_vbif_base = ioremap(vfe_dev->vfe_vbif_mem->start,
+ resource_size(vfe_dev->vfe_vbif_mem));
+ if (!vfe_dev->vfe_vbif_base) {
+ rc = -ENOMEM;
+ pr_err("%s: vfe ioremap failed\n", __func__);
+ goto vbif_remap_failed;
+ }
+
+ rc = request_irq(vfe_dev->vfe_irq->start, msm_isp_process_irq,
+ IRQF_TRIGGER_RISING, "vfe", vfe_dev);
+ if (rc < 0) {
+ pr_err("%s: irq request failed\n", __func__);
+ goto irq_req_failed;
+ }
+
+ msm_vfe40_init_vbif_parms(vfe_dev->vfe_vbif_base);
+ return rc;
+irq_req_failed:
+ iounmap(vfe_dev->vfe_vbif_base);
+vbif_remap_failed:
+ iounmap(vfe_dev->vfe_base);
+vfe_remap_failed:
+ msm_cam_clk_enable(&vfe_dev->pdev->dev, msm_vfe40_clk_info,
+ vfe_dev->vfe_clk, ARRAY_SIZE(msm_vfe40_clk_info), 0);
+clk_enable_failed:
+ regulator_disable(vfe_dev->fs_vfe);
+fs_failed:
+ msm_bus_scale_client_update_request(vfe_dev->bus_perf_client, 0);
+ msm_bus_scale_unregister_client(vfe_dev->bus_perf_client);
+bus_scale_register_failed:
+ return rc;
+}
+
+static void msm_vfe40_release_hardware(struct vfe_device *vfe_dev)
+{
+ free_irq(vfe_dev->vfe_irq->start, vfe_dev);
+ tasklet_kill(&vfe_dev->vfe_tasklet);
+ iounmap(vfe_dev->vfe_vbif_base);
+ iounmap(vfe_dev->vfe_base);
+ msm_cam_clk_enable(&vfe_dev->pdev->dev, msm_vfe40_clk_info,
+ vfe_dev->vfe_clk, ARRAY_SIZE(msm_vfe40_clk_info), 0);
+ regulator_disable(vfe_dev->fs_vfe);
+ msm_bus_scale_client_update_request(vfe_dev->bus_perf_client, 0);
+ msm_bus_scale_unregister_client(vfe_dev->bus_perf_client);
+}
+
+static void msm_vfe40_init_hardware_reg(struct vfe_device *vfe_dev)
+{
+ /* CGC_OVERRIDE */
+ msm_camera_io_w(0x3FFFFFFF, vfe_dev->vfe_base + 0x14);
+ /* BUS_CFG */
+ msm_camera_io_w(0x00000001, vfe_dev->vfe_base + 0x50);
+ msm_camera_io_w(0x800000F3, vfe_dev->vfe_base + 0x28);
+ msm_camera_io_w_mb(0xFEFFFFFF, vfe_dev->vfe_base + 0x2C);
+ msm_camera_io_w(0xFFFFFFFF, vfe_dev->vfe_base + 0x30);
+ msm_camera_io_w_mb(0xFEFFFFFF, vfe_dev->vfe_base + 0x34);
+}
+
+static void msm_vfe40_process_reset_irq(struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1)
+{
+ if (irq_status0 & (1 << 31))
+ complete(&vfe_dev->reset_complete);
+}
+
+static void msm_vfe40_process_halt_irq(struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1)
+{
+ if (irq_status1 & (1 << 8))
+ complete(&vfe_dev->halt_complete);
+}
+
+static void msm_vfe40_process_camif_irq(struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1)
+{
+ if (!(irq_status0 & 0xF))
+ return;
+
+ if (vfe_dev->hw_info->vfe_ops.core_ops.epoch_irq) {
+ if (irq_status0 & (1 << 2)) {
+ ISP_DBG("%s: EPOCH0 IRQ, PIX0_frameid = 0x%lu\n",
+ __func__,
+ vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id);
+ msm_isp_new_frame_notify(vfe_dev, VFE_PIX_0);
+ }
+ } else {
+ if (irq_status0 & (1 << 0)) {
+ ISP_DBG("%s: SOF: PIX0 frame id: %lu\n", __func__,
+ vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id);
+ msm_isp_new_frame_notify(vfe_dev, VFE_PIX_0);
+ }
+ }
+}
+
+static void msm_vfe40_process_violation_irq(
+ struct vfe_device *vfe_dev)
+{
+ uint32_t violation_status;
+ violation_status = msm_camera_io_r(vfe_dev->vfe_base + 0x48);
+ if (!violation_status)
+ return;
+
+ if (violation_status & (1 << 0))
+ pr_err("%s: camif violation\n", __func__);
+ if (violation_status & (1 << 1))
+ pr_err("%s: black violation\n", __func__);
+ if (violation_status & (1 << 2))
+ pr_err("%s: rolloff violation\n", __func__);
+ if (violation_status & (1 << 3))
+ pr_err("%s: demux violation\n", __func__);
+ if (violation_status & (1 << 4))
+ pr_err("%s: demosaic violation\n", __func__);
+ if (violation_status & (1 << 5))
+ pr_err("%s: wb violation\n", __func__);
+ if (violation_status & (1 << 6))
+ pr_err("%s: clf violation\n", __func__);
+ if (violation_status & (1 << 7))
+ pr_err("%s: color correct violation\n", __func__);
+ if (violation_status & (1 << 8))
+ pr_err("%s: rgb lut violation\n", __func__);
+ if (violation_status & (1 << 9))
+ pr_err("%s: la violation\n", __func__);
+ if (violation_status & (1 << 10))
+ pr_err("%s: chroma enhance violation\n", __func__);
+ if (violation_status & (1 << 11))
+ pr_err("%s: chroma supress mce violation\n", __func__);
+ if (violation_status & (1 << 12))
+ pr_err("%s: skin enhance violation\n", __func__);
+ if (violation_status & (1 << 13))
+ pr_err("%s: color tranform enc violation\n", __func__);
+ if (violation_status & (1 << 14))
+ pr_err("%s: color tranform view violation\n", __func__);
+ if (violation_status & (1 << 15))
+ pr_err("%s: scale enc y violation\n", __func__);
+ if (violation_status & (1 << 16))
+ pr_err("%s: scale enc cbcr violation\n", __func__);
+ if (violation_status & (1 << 17))
+ pr_err("%s: scale view y violation\n", __func__);
+ if (violation_status & (1 << 18))
+ pr_err("%s: scale view cbcr violation\n", __func__);
+ if (violation_status & (1 << 19))
+ pr_err("%s: asf enc violation\n", __func__);
+ if (violation_status & (1 << 20))
+ pr_err("%s: asf view violation\n", __func__);
+ if (violation_status & (1 << 21))
+ pr_err("%s: crop enc y violation\n", __func__);
+ if (violation_status & (1 << 22))
+ pr_err("%s: crop enc cbcr violation\n", __func__);
+ if (violation_status & (1 << 23))
+ pr_err("%s: crop view y violation\n", __func__);
+ if (violation_status & (1 << 24))
+ pr_err("%s: crop view cbcr violation\n", __func__);
+ if (violation_status & (1 << 25))
+ pr_err("%s: realign buf y violation\n", __func__);
+ if (violation_status & (1 << 26))
+ pr_err("%s: realign buf cb violation\n", __func__);
+ if (violation_status & (1 << 27))
+ pr_err("%s: realign buf cr violation\n", __func__);
+}
+
+static void msm_vfe40_process_error_irq(struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1)
+{
+ uint32_t camif_status;
+ if (!(irq_status1 & 0x00FFFEFF))
+ return;
+
+ if (irq_status1 & (1 << 0)) {
+ camif_status = msm_camera_io_r(vfe_dev->vfe_base + 0x31C);
+ pr_err("%s: camif error status: 0x%x\n",
+ __func__, camif_status);
+ }
+ if (irq_status1 & (1 << 1))
+ pr_err("%s: stats bhist overwrite\n", __func__);
+ if (irq_status1 & (1 << 2))
+ pr_err("%s: stats cs overwrite\n", __func__);
+ if (irq_status1 & (1 << 3))
+ pr_err("%s: stats ihist overwrite\n", __func__);
+ if (irq_status1 & (1 << 4))
+ pr_err("%s: realign buf y overflow\n", __func__);
+ if (irq_status1 & (1 << 5))
+ pr_err("%s: realign buf cb overflow\n", __func__);
+ if (irq_status1 & (1 << 6))
+ pr_err("%s: realign buf cr overflow\n", __func__);
+ if (irq_status1 & (1 << 7)) {
+ pr_err("%s: violation\n", __func__);
+ msm_vfe40_process_violation_irq(vfe_dev);
+ }
+ if (irq_status1 & (1 << 9))
+ pr_err("%s: image master 0 bus overflow\n", __func__);
+ if (irq_status1 & (1 << 10))
+ pr_err("%s: image master 1 bus overflow\n", __func__);
+ if (irq_status1 & (1 << 11))
+ pr_err("%s: image master 2 bus overflow\n", __func__);
+ if (irq_status1 & (1 << 12))
+ pr_err("%s: image master 3 bus overflow\n", __func__);
+ if (irq_status1 & (1 << 13))
+ pr_err("%s: image master 4 bus overflow\n", __func__);
+ if (irq_status1 & (1 << 14))
+ pr_err("%s: image master 5 bus overflow\n", __func__);
+ if (irq_status1 & (1 << 15))
+ pr_err("%s: image master 6 bus overflow\n", __func__);
+ if (irq_status1 & (1 << 16))
+ pr_err("%s: status be bus overflow\n", __func__);
+ if (irq_status1 & (1 << 17))
+ pr_err("%s: status bg bus overflow\n", __func__);
+ if (irq_status1 & (1 << 18))
+ pr_err("%s: status bf bus overflow\n", __func__);
+ if (irq_status1 & (1 << 19))
+ pr_err("%s: status awb bus overflow\n", __func__);
+ if (irq_status1 & (1 << 20))
+ pr_err("%s: status rs bus overflow\n", __func__);
+ if (irq_status1 & (1 << 21))
+ pr_err("%s: status cs bus overflow\n", __func__);
+ if (irq_status1 & (1 << 22))
+ pr_err("%s: status ihist bus overflow\n", __func__);
+ if (irq_status1 & (1 << 23))
+ pr_err("%s: status skin bhist bus overflow\n", __func__);
+}
+
+static void msm_vfe40_read_irq_status(struct vfe_device *vfe_dev,
+ uint32_t *irq_status0, uint32_t *irq_status1)
+{
+ *irq_status0 = msm_camera_io_r(vfe_dev->vfe_base + 0x38);
+ *irq_status1 = msm_camera_io_r(vfe_dev->vfe_base + 0x3C);
+ msm_camera_io_w(*irq_status0, vfe_dev->vfe_base + 0x30);
+ msm_camera_io_w(*irq_status1, vfe_dev->vfe_base + 0x34);
+ msm_camera_io_w_mb(1, vfe_dev->vfe_base + 0x24);
+}
+
+static void msm_vfe40_process_reg_update(struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1)
+{
+ uint32_t update_mask = 0xF;
+
+ if (!(irq_status0 & 0xF0))
+ return;
+
+ if (vfe_dev->axi_data.stream_update)
+ msm_isp_axi_stream_update(vfe_dev);
+ msm_isp_update_framedrop_reg(vfe_dev);
+
+ vfe_dev->hw_info->vfe_ops.core_ops.
+ reg_update(vfe_dev, update_mask);
+ return;
+}
+
+static void msm_vfe40_epoch_irq_enb(struct vfe_device *vfe_dev,
+ uint32_t epoch_line0, uint32_t epoch_line1)
+{
+ uint32_t irq_mask = 0;
+ uint32_t epoch_val = 0;
+ irq_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x28);
+ if (epoch_line0 > 0) {
+ irq_mask |= 0x4;
+ epoch_val |= (epoch_line0 - 1) << 16;
+ } else {
+ irq_mask &= ~0x4;
+ epoch_val &= 0xFFFF;
+ }
+ if (epoch_line1 > 0) {
+ irq_mask |= 0x8;
+ epoch_val |= epoch_line1 - 1;
+ } else {
+ irq_mask &= ~0x8;
+ epoch_val &= 0xFFFF0000;
+ }
+ msm_camera_io_w_mb(epoch_val, vfe_dev->vfe_base + 0x318);
+ msm_camera_io_w_mb(irq_mask, vfe_dev->vfe_base + 0x28);
+}
+
+static void msm_vfe40_reg_update(
+ struct vfe_device *vfe_dev, uint32_t update_mask)
+{
+ msm_camera_io_w_mb(update_mask, vfe_dev->vfe_base + 0x378);
+}
+
+static long msm_vfe40_reset_hardware(struct vfe_device *vfe_dev)
+{
+ init_completion(&vfe_dev->reset_complete);
+ msm_camera_io_w_mb(0x1FF, vfe_dev->vfe_base + 0xC);
+ return wait_for_completion_interruptible_timeout(
+ &vfe_dev->reset_complete, msecs_to_jiffies(50));
+}
+
+static void msm_vfe40_axi_reload_wm(
+ struct vfe_device *vfe_dev, uint32_t reload_mask)
+{
+ msm_camera_io_w_mb(reload_mask, vfe_dev->vfe_base + 0x4C);
+}
+
+static void msm_vfe40_axi_enable_wm(struct vfe_device *vfe_dev,
+ uint8_t wm_idx, uint8_t enable)
+{
+ if (enable)
+ msm_camera_io_w_mb(0x1,
+ vfe_dev->vfe_base + VFE40_WM_BASE(wm_idx));
+ else
+ msm_camera_io_w_mb(0x0,
+ vfe_dev->vfe_base + VFE40_WM_BASE(wm_idx));
+}
+
+static void msm_vfe40_axi_cfg_comp_mask(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info)
+{
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
+ uint32_t comp_mask, comp_mask_index =
+ stream_info->comp_mask_index;
+ uint32_t irq_mask;
+
+ comp_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x40);
+ comp_mask &= ~(0x7F << (comp_mask_index * 8));
+ comp_mask |= (axi_data->composite_info[comp_mask_index].
+ stream_composite_mask << (comp_mask_index * 8));
+ msm_camera_io_w(comp_mask, vfe_dev->vfe_base + 0x40);
+
+ irq_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x28);
+ irq_mask |= 1 << (comp_mask_index + 25);
+ msm_camera_io_w(irq_mask, vfe_dev->vfe_base + 0x28);
+}
+
+static void msm_vfe40_axi_clear_comp_mask(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info)
+{
+ uint32_t comp_mask, comp_mask_index = stream_info->comp_mask_index;
+ uint32_t irq_mask;
+
+ comp_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x40);
+ comp_mask &= ~(0x7F << (comp_mask_index * 8));
+ msm_camera_io_w(comp_mask, vfe_dev->vfe_base + 0x40);
+
+ irq_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x28);
+ irq_mask &= ~(1 << (comp_mask_index + 25));
+ msm_camera_io_w(irq_mask, vfe_dev->vfe_base + 0x28);
+}
+
+static void msm_vfe40_axi_cfg_wm_irq_mask(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info)
+{
+ uint32_t irq_mask;
+ irq_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x28);
+ irq_mask |= 1 << (stream_info->wm[0] + 8);
+ msm_camera_io_w(irq_mask, vfe_dev->vfe_base + 0x28);
+}
+
+static void msm_vfe40_axi_clear_wm_irq_mask(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info)
+{
+ uint32_t irq_mask;
+ irq_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x28);
+ irq_mask &= ~(1 << (stream_info->wm[0] + 8));
+ msm_camera_io_w(irq_mask, vfe_dev->vfe_base + 0x28);
+}
+
+static void msm_vfe40_cfg_framedrop(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info)
+{
+ uint32_t i;
+ uint32_t framedrop_pattern = 0;
+
+ if (stream_info->init_frame_drop == 0)
+ framedrop_pattern = stream_info->framedrop_pattern;
+
+ if (stream_info->stream_type == BURST_STREAM &&
+ stream_info->burst_frame_count == 0)
+ framedrop_pattern = 0;
+
+ for (i = 0; i < stream_info->num_planes; i++)
+ msm_camera_io_w(framedrop_pattern, vfe_dev->vfe_base +
+ VFE40_WM_BASE(stream_info->wm[i]) + 0x1C);
+
+ msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x378);
+}
+
+static void msm_vfe40_clear_framedrop(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info)
+{
+ uint32_t i;
+ for (i = 0; i < stream_info->num_planes; i++)
+ msm_camera_io_w(0, vfe_dev->vfe_base +
+ VFE40_WM_BASE(stream_info->wm[i]) + 0x1C);
+}
+
+static void msm_vfe40_cfg_camif(struct vfe_device *vfe_dev,
+ struct msm_vfe_pix_cfg *pix_cfg)
+{
+ uint16_t first_pixel, last_pixel, first_line, last_line;
+ struct msm_vfe_camif_cfg *camif_cfg = &pix_cfg->camif_cfg;
+ uint32_t val;
+
+ first_pixel = camif_cfg->first_pixel;
+ last_pixel = camif_cfg->last_pixel;
+ first_line = camif_cfg->first_line;
+ last_line = camif_cfg->last_line;
+
+ msm_camera_io_w(pix_cfg->input_mux << 16 | pix_cfg->pixel_pattern,
+ vfe_dev->vfe_base + 0x1C);
+
+ msm_camera_io_w(camif_cfg->lines_per_frame << 16 |
+ camif_cfg->pixels_per_line, vfe_dev->vfe_base + 0x300);
+
+ msm_camera_io_w(first_pixel << 16 | last_pixel,
+ vfe_dev->vfe_base + 0x304);
+
+ msm_camera_io_w(first_line << 16 | last_line,
+ vfe_dev->vfe_base + 0x308);
+
+ msm_camera_io_w(0xFFFFFFFF, vfe_dev->vfe_base + 0x314);
+
+ val = msm_camera_io_r(vfe_dev->vfe_base + 0x2E8);
+ val |= camif_cfg->camif_input;
+ msm_camera_io_w(val, vfe_dev->vfe_base + 0x2E8);
+
+ vfe_dev->hw_info->vfe_ops.core_ops.epoch_irq(vfe_dev,
+ camif_cfg->epoch_line0, camif_cfg->epoch_line1);
+
+ switch (pix_cfg->input_mux) {
+ case CAMIF:
+ val = 0x01;
+ msm_camera_io_w(val, vfe_dev->vfe_base + 0x2F4);
+ break;
+ case TESTGEN:
+ val = 0x01;
+ msm_camera_io_w(val, vfe_dev->vfe_base + 0x93C);
+ break;
+ case EXTERNAL_READ:
+ default:
+ pr_err("%s: not supported input_mux %d\n",
+ __func__, pix_cfg->input_mux);
+ break;
+ }
+}
+
+static void msm_vfe40_update_camif_state(struct vfe_device *vfe_dev,
+ enum msm_isp_camif_update_state update_state)
+{
+ uint32_t val;
+ bool bus_en, vfe_en;
+ if (update_state == NO_UPDATE)
+ return;
+
+ val = msm_camera_io_r(vfe_dev->vfe_base + 0x2F8);
+ if (update_state == ENABLE_CAMIF) {
+ bus_en =
+ ((vfe_dev->axi_data.
+ src_info[VFE_PIX_0].raw_stream_count > 0) ? 1 : 0);
+ vfe_en =
+ ((vfe_dev->axi_data.
+ src_info[VFE_PIX_0].pix_stream_count > 0) ? 1 : 0);
+ val &= 0xFFFFFF3F;
+ val = val | bus_en << 7 | vfe_en << 6;
+ msm_camera_io_w(val, vfe_dev->vfe_base + 0x2F8);
+ msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x2F4);
+ vfe_dev->axi_data.src_info[VFE_PIX_0].active = 1;
+ } else if (update_state == DISABLE_CAMIF) {
+ msm_camera_io_w_mb(0x0, vfe_dev->vfe_base + 0x2F4);
+ vfe_dev->axi_data.src_info[VFE_PIX_0].active = 0;
+ } else if (update_state == DISABLE_CAMIF_IMMEDIATELY) {
+ msm_camera_io_w_mb(0x2, vfe_dev->vfe_base + 0x2F4);
+ vfe_dev->axi_data.src_info[VFE_PIX_0].active = 0;
+ }
+}
+
+static void msm_vfe40_axi_cfg_wm_reg(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd,
+ uint8_t plane_idx)
+{
+ uint32_t val;
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
+ struct msm_vfe_axi_stream *stream_info =
+ &axi_data->stream_info[
+ (stream_cfg_cmd->axi_stream_handle & 0xFF)];
+ uint32_t wm_base = VFE40_WM_BASE(stream_info->wm[plane_idx]);
+
+ /*WR_ADDR_CFG*/
+ msm_camera_io_w(0x7C, vfe_dev->vfe_base + wm_base + 0xC);
+
+ /*WR_IMAGE_SIZE*/
+ val =
+ ((msm_isp_cal_word_per_line(stream_cfg_cmd->output_format,
+ stream_cfg_cmd->plane_cfg[plane_idx].
+ output_width)+1)/2 - 1) << 16 |
+ (stream_cfg_cmd->plane_cfg[plane_idx].
+ output_height - 1);
+ msm_camera_io_w(val, vfe_dev->vfe_base + wm_base + 0x14);
+
+ /*WR_BUFFER_CFG*/
+ val =
+ msm_isp_cal_word_per_line(stream_cfg_cmd->output_format,
+ stream_cfg_cmd->plane_cfg[
+ plane_idx].output_stride) << 16 |
+ (stream_cfg_cmd->plane_cfg[
+ plane_idx].output_scan_lines - 1) << 4 |
+ VFE40_BURST_LEN >> 2;
+ msm_camera_io_w(val, vfe_dev->vfe_base + wm_base + 0x18);
+
+ /*WR_IRQ_SUBSAMPLE_PATTERN*/
+ msm_camera_io_w(0xFFFFFFFF,
+ vfe_dev->vfe_base + wm_base + 0x20);
+ /* TD: Add IRQ subsample pattern */
+ return;
+}
+
+static void msm_vfe40_axi_clear_wm_reg(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info, uint8_t plane_idx)
+{
+ uint32_t val = 0;
+ uint32_t wm_base = VFE40_WM_BASE(stream_info->wm[plane_idx]);
+ /*WR_ADDR_CFG*/
+ msm_camera_io_w(val, vfe_dev->vfe_base + wm_base + 0xC);
+ /*WR_IMAGE_SIZE*/
+ msm_camera_io_w(val, vfe_dev->vfe_base + wm_base + 0x14);
+ /*WR_BUFFER_CFG*/
+ msm_camera_io_w(val, vfe_dev->vfe_base + wm_base + 0x18);
+ /*WR_IRQ_SUBSAMPLE_PATTERN*/
+ msm_camera_io_w(val, vfe_dev->vfe_base + wm_base + 0x20);
+ return;
+}
+
+static void msm_vfe40_axi_cfg_rdi_reg(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd,
+ uint8_t plane_idx)
+{
+ struct msm_vfe_axi_shared_data *axi_data =
+ &vfe_dev->axi_data;
+ struct msm_vfe_axi_stream *stream_info =
+ &axi_data->stream_info[
+ (stream_cfg_cmd->axi_stream_handle & 0xFF)];
+ struct msm_vfe_axi_plane_cfg *plane_cfg =
+ &stream_cfg_cmd->plane_cfg[plane_idx];
+ uint8_t rdi = stream_info->rdi[plane_idx];
+ uint8_t rdi_master = stream_info->rdi_master[plane_idx];
+ uint32_t rdi_reg_cfg;
+
+ rdi_reg_cfg = msm_camera_io_r(
+ vfe_dev->vfe_base + VFE40_RDI_BASE(rdi));
+ rdi_reg_cfg = (rdi_reg_cfg & 0xFFFFFFF) | rdi_master << 28;
+ msm_camera_io_w(
+ rdi_reg_cfg, vfe_dev->vfe_base + VFE40_RDI_BASE(rdi));
+
+ rdi_reg_cfg = msm_camera_io_r(
+ vfe_dev->vfe_base + VFE40_RDI_MN_BASE(rdi_master));
+ rdi_reg_cfg &= ~((0xF << VFE40_RDI_MN_SEL_SHIFT(rdi_master)) |
+ (0x1 << VFE40_RDI_MN_FB_SHIFT(rdi_master)));
+ rdi_reg_cfg |=
+ (plane_cfg->rdi_cid << VFE40_RDI_MN_SEL_SHIFT(rdi_master) |
+ (stream_cfg_cmd->frame_base <<
+ VFE40_RDI_MN_FB_SHIFT(rdi_master)));
+ msm_camera_io_w(rdi_reg_cfg,
+ vfe_dev->vfe_base + VFE40_RDI_MN_BASE(rdi_master));
+}
+
+static void msm_vfe40_axi_cfg_wm_xbar_reg(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd,
+ uint8_t plane_idx)
+{
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
+ struct msm_vfe_axi_stream *stream_info =
+ &axi_data->stream_info[
+ (stream_cfg_cmd->axi_stream_handle & 0xFF)];
+ struct msm_vfe_axi_plane_cfg *plane_cfg =
+ &stream_cfg_cmd->plane_cfg[plane_idx];
+ uint8_t wm = stream_info->wm[plane_idx];
+ uint32_t xbar_cfg = 0;
+ uint32_t xbar_reg_cfg = 0;
+
+ switch (stream_cfg_cmd->stream_src) {
+ case PIX_ENCODER:
+ case PIX_VIEWFINDER: {
+ if (plane_cfg->output_plane_format != CRCB_PLANE &&
+ plane_cfg->output_plane_format != CBCR_PLANE) {
+ /*SINGLE_STREAM_SEL*/
+ xbar_cfg |= plane_cfg->output_plane_format << 8;
+ } else {
+ switch (stream_cfg_cmd->output_format) {
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV16:
+ xbar_cfg |= 0x3 << 4; /*PAIR_STREAM_SWAP_CTRL*/
+ break;
+ }
+ xbar_cfg |= 0x1 << 1; /*PAIR_STREAM_EN*/
+ }
+ if (stream_cfg_cmd->stream_src == PIX_VIEWFINDER)
+ xbar_cfg |= 0x1; /*VIEW_STREAM_EN*/
+ break;
+ }
+ case CAMIF_RAW:
+ xbar_cfg = 0x300;
+ break;
+ case IDEAL_RAW:
+ xbar_cfg = 0x400;
+ break;
+ case RDI:
+ if (stream_info->rdi[plane_idx] == 0)
+ xbar_cfg = 0x500;
+ else if (stream_info->rdi[plane_idx] == 1)
+ xbar_cfg = 0x600;
+ else if (stream_info->rdi[plane_idx] == 2)
+ xbar_cfg = 0x700;
+ break;
+ default:
+ pr_err("%s: Invalid stream src\n", __func__);
+ break;
+ }
+ xbar_reg_cfg =
+ msm_camera_io_r(vfe_dev->vfe_base + VFE40_XBAR_BASE(wm));
+ xbar_reg_cfg &= ~(0xFFFF << VFE40_XBAR_SHIFT(wm));
+ xbar_reg_cfg |= (xbar_cfg << VFE40_XBAR_SHIFT(wm));
+ msm_camera_io_w(xbar_reg_cfg,
+ vfe_dev->vfe_base + VFE40_XBAR_BASE(wm));
+ return;
+}
+
+static void msm_vfe40_axi_clear_wm_xbar_reg(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info, uint8_t plane_idx)
+{
+ uint8_t wm = stream_info->wm[plane_idx];
+ uint32_t xbar_reg_cfg = 0;
+
+ xbar_reg_cfg =
+ msm_camera_io_r(vfe_dev->vfe_base + VFE40_XBAR_BASE(wm));
+ xbar_reg_cfg &= ~(0xFFFF << VFE40_XBAR_SHIFT(wm));
+ msm_camera_io_w(xbar_reg_cfg,
+ vfe_dev->vfe_base + VFE40_XBAR_BASE(wm));
+}
+
+#define MSM_ISP40_TOTAL_WM_UB 819
+
+static void msm_vfe40_cfg_axi_ub_equal_default(
+ struct vfe_device *vfe_dev)
+{
+ int i;
+ uint32_t ub_offset = 0;
+ struct msm_vfe_axi_shared_data *axi_data =
+ &vfe_dev->axi_data;
+ uint32_t total_image_size = 0;
+ uint8_t num_used_wms = 0;
+ uint32_t prop_size = 0;
+ uint32_t wm_ub_size;
+ uint32_t delta;
+
+ for (i = 0; i < axi_data->hw_info->num_wm; i++) {
+ if (axi_data->free_wm[i] > 0) {
+ num_used_wms++;
+ total_image_size += axi_data->wm_image_size[i];
+ }
+ }
+ prop_size = MSM_ISP40_TOTAL_WM_UB -
+ axi_data->hw_info->min_wm_ub * num_used_wms;
+ for (i = 0; i < axi_data->hw_info->num_wm; i++) {
+ if (axi_data->free_wm[i]) {
+ delta =
+ (axi_data->wm_image_size[i] *
+ prop_size)/total_image_size;
+ wm_ub_size = axi_data->hw_info->min_wm_ub + delta;
+ msm_camera_io_w(ub_offset << 16 | (wm_ub_size - 1),
+ vfe_dev->vfe_base + VFE40_WM_BASE(i) + 0x10);
+ ub_offset += wm_ub_size;
+ } else
+ msm_camera_io_w(0,
+ vfe_dev->vfe_base + VFE40_WM_BASE(i) + 0x10);
+ }
+}
+
+static void msm_vfe40_cfg_axi_ub_equal_slicing(
+ struct vfe_device *vfe_dev)
+{
+ int i;
+ uint32_t ub_offset = 0;
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
+ for (i = 0; i < axi_data->hw_info->num_wm; i++) {
+ msm_camera_io_w(ub_offset << 16 | (VFE40_EQUAL_SLICE_UB - 1),
+ vfe_dev->vfe_base + VFE40_WM_BASE(i) + 0x10);
+ ub_offset += VFE40_EQUAL_SLICE_UB;
+ }
+}
+
+static void msm_vfe40_cfg_axi_ub(struct vfe_device *vfe_dev)
+{
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
+ axi_data->wm_ub_cfg_policy = MSM_WM_UB_EQUAL_SLICING;
+ if (axi_data->wm_ub_cfg_policy == MSM_WM_UB_EQUAL_SLICING)
+ msm_vfe40_cfg_axi_ub_equal_slicing(vfe_dev);
+ else
+ msm_vfe40_cfg_axi_ub_equal_default(vfe_dev);
+}
+
+static void msm_vfe40_update_ping_pong_addr(
+ struct vfe_device *vfe_dev,
+ uint8_t wm_idx, uint32_t pingpong_status, unsigned long paddr)
+{
+ msm_camera_io_w(paddr, vfe_dev->vfe_base +
+ VFE40_PING_PONG_BASE(wm_idx, pingpong_status));
+}
+
+static long msm_vfe40_axi_halt(struct vfe_device *vfe_dev)
+{
+ uint32_t halt_mask;
+ halt_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x2C);
+ halt_mask |= (1 << 8);
+ msm_camera_io_w_mb(halt_mask, vfe_dev->vfe_base + 0x2C);
+ init_completion(&vfe_dev->halt_complete);
+ msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x2C0);
+ return wait_for_completion_interruptible_timeout(
+ &vfe_dev->halt_complete, msecs_to_jiffies(500));
+}
+
+static uint32_t msm_vfe40_get_wm_mask(
+ uint32_t irq_status0, uint32_t irq_status1)
+{
+ return (irq_status0 >> 8) & 0x7F;
+}
+
+static uint32_t msm_vfe40_get_comp_mask(
+ uint32_t irq_status0, uint32_t irq_status1)
+{
+ return (irq_status0 >> 25) & 0xF;
+}
+
+static uint32_t msm_vfe40_get_pingpong_status(
+ struct vfe_device *vfe_dev)
+{
+ return msm_camera_io_r(vfe_dev->vfe_base + 0x268);
+}
+
+static void msm_vfe40_stats_cfg_comp_mask(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream *stream_info)
+{
+
+}
+
+static void msm_vfe40_stats_clear_comp_mask(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream *stream_info)
+{
+
+}
+
+static void msm_vfe40_stats_cfg_wm_irq_mask(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream *stream_info)
+{
+
+}
+
+static void msm_vfe40_stats_clear_wm_irq_mask(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream *stream_info)
+{
+
+}
+
+static void msm_vfe40_stats_cfg_wm_reg(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream *stream_info)
+{
+
+}
+
+static void msm_vfe40_stats_clear_wm_reg(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream *stream_info)
+{
+
+}
+
+static void msm_vfe40_stats_cfg_ub(struct vfe_device *vfe_dev)
+{
+}
+
+static void msm_vfe40_stats_enable(struct vfe_device *vfe_dev,
+ uint32_t stats_mask, uint8_t enable)
+{
+
+}
+
+static void msm_vfe40_stats_update_ping_pong_addr(
+ struct vfe_device *vfe_dev,
+ enum msm_isp_stats_type stats_type,
+ uint32_t pingpong_status,
+ unsigned long paddr)
+{
+}
+
+static uint32_t msm_vfe40_stats_get_wm_mask(
+ uint32_t irq_status0, uint32_t irq_status1)
+{
+ return (irq_status0 >> 16) & 0xFF;
+}
+
+static uint32_t msm_vfe40_stats_get_comp_mask(
+ uint32_t irq_status0, uint32_t irq_status1)
+{
+ return (irq_status0 >> 29) & 0x2;
+}
+
+static uint32_t msm_vfe40_stats_get_frame_id(
+ struct vfe_device *vfe_dev)
+{
+ return vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id;
+}
+
+static uint32_t msm_vfe40_stats_get_active_pingpong_idx(
+ uint32_t pingpong_status,
+ enum msm_isp_stats_type stats_type)
+{
+ int pingpong_bit = 0;
+ switch (stats_type) {
+ case MSM_ISP_STATS_AWB:
+ if (pingpong_status & (1 << 11))
+ pingpong_bit = 1; /* pong is active */
+ break;
+ case MSM_ISP_STATS_RS:
+ if (pingpong_status & (1 << 12))
+ pingpong_bit = 1; /* pong is active */
+ break;
+ case MSM_ISP_STATS_CS:
+ if (pingpong_status & (1 << 13))
+ pingpong_bit = 1; /* pong is active */
+ break;
+ case MSM_ISP_STATS_IHIST:
+ if (pingpong_status & (1 << 14))
+ pingpong_bit = 1; /* pong is active */
+ break;
+ case MSM_ISP_STATS_BG:
+ if (pingpong_status & (1 << 9))
+ pingpong_bit = 1; /* pong is active */
+ break;
+ case MSM_ISP_STATS_BF:
+ if (pingpong_status & (1 << 10))
+ pingpong_bit = 1; /* pong is active */
+ break;
+ case MSM_ISP_STATS_BE:
+ if (pingpong_status & (1 << 8))
+ pingpong_bit = 1; /* pong is active */
+ break;
+ case MSM_ISP_STATS_BHIST:
+ if (pingpong_status & (1 << 15))
+ pingpong_bit = 1; /* pong is active */
+ break;
+ case MSM_ISP_STATS_SKIN:
+ case MSM_ISP_STATS_AEC:
+ case MSM_ISP_STATS_AF:
+ default:
+ pr_err("%s: not supported stats type = %d\n",
+ __func__, stats_type);
+ return -EINVAL;
+ }
+ return pingpong_bit;
+}
+
+static int msm_vfe40_get_platform_data(struct vfe_device *vfe_dev)
+{
+ int rc = 0;
+ vfe_dev->vfe_mem = platform_get_resource_byname(vfe_dev->pdev,
+ IORESOURCE_MEM, "vfe");
+ if (!vfe_dev->vfe_mem) {
+ pr_err("%s: no mem resource?\n", __func__);
+ rc = -ENODEV;
+ goto vfe_no_resource;
+ }
+
+ vfe_dev->vfe_vbif_mem = platform_get_resource_byname(
+ vfe_dev->pdev,
+ IORESOURCE_MEM, "vfe_vbif");
+ if (!vfe_dev->vfe_vbif_mem) {
+ pr_err("%s: no mem resource?\n", __func__);
+ rc = -ENODEV;
+ goto vfe_no_resource;
+ }
+
+ vfe_dev->vfe_irq = platform_get_resource_byname(vfe_dev->pdev,
+ IORESOURCE_IRQ, "vfe");
+ if (!vfe_dev->vfe_irq) {
+ pr_err("%s: no irq resource?\n", __func__);
+ rc = -ENODEV;
+ goto vfe_no_resource;
+ }
+
+ vfe_dev->fs_vfe = regulator_get(&vfe_dev->pdev->dev, "vdd");
+ if (IS_ERR(vfe_dev->fs_vfe)) {
+ pr_err("%s: Regulator get failed %ld\n", __func__,
+ PTR_ERR(vfe_dev->fs_vfe));
+ vfe_dev->fs_vfe = NULL;
+ rc = -ENODEV;
+ goto vfe_no_resource;
+ }
+
+ if (vfe_dev->pdev->id == 0)
+ vfe_dev->iommu_ctx = msm_iommu_get_ctx("vfe0");
+ else if (vfe_dev->pdev->id == 1)
+ vfe_dev->iommu_ctx = msm_iommu_get_ctx("vfe1");
+ if (!vfe_dev->iommu_ctx) {
+ pr_err("%s: no irq resource?\n", __func__);
+ rc = -ENODEV;
+ goto vfe_no_resource;
+ }
+
+vfe_no_resource:
+ return rc;
+}
+
+struct msm_vfe_axi_hardware_info msm_vfe40_axi_hw_info = {
+ .num_wm = 7,
+ .num_comp_mask = 4,
+ .num_rdi = 3,
+ .num_rdi_master = 3,
+ .min_wm_ub = 64,
+ .num_stats_comp_mask = 2,
+};
+
+static struct v4l2_subdev_core_ops msm_vfe40_subdev_core_ops = {
+ .ioctl = msm_isp_ioctl,
+ .subscribe_event = msm_isp_subscribe_event,
+ .unsubscribe_event = msm_isp_unsubscribe_event,
+};
+
+static struct v4l2_subdev_ops msm_vfe40_subdev_ops = {
+ .core = &msm_vfe40_subdev_core_ops,
+};
+
+static struct v4l2_subdev_internal_ops msm_vfe40_internal_ops = {
+ .open = msm_isp_open_node,
+ .close = msm_isp_close_node,
+};
+
+struct msm_vfe_hardware_info vfe40_hw_info = {
+ .vfe_ops = {
+ .irq_ops = {
+ .read_irq_status = msm_vfe40_read_irq_status,
+ .process_camif_irq = msm_vfe40_process_camif_irq,
+ .process_reset_irq = msm_vfe40_process_reset_irq,
+ .process_halt_irq = msm_vfe40_process_halt_irq,
+ .process_reset_irq = msm_vfe40_process_reset_irq,
+ .process_error_irq = msm_vfe40_process_error_irq,
+ .process_reg_update = msm_vfe40_process_reg_update,
+ .process_axi_irq = msm_isp_process_axi_irq,
+ .process_stats_irq = msm_isp_process_stats_irq,
+ },
+ .axi_ops = {
+ .reload_wm = msm_vfe40_axi_reload_wm,
+ .enable_wm = msm_vfe40_axi_enable_wm,
+ .cfg_comp_mask = msm_vfe40_axi_cfg_comp_mask,
+ .clear_comp_mask = msm_vfe40_axi_clear_comp_mask,
+ .cfg_wm_irq_mask = msm_vfe40_axi_cfg_wm_irq_mask,
+ .clear_wm_irq_mask = msm_vfe40_axi_clear_wm_irq_mask,
+ .cfg_framedrop = msm_vfe40_cfg_framedrop,
+ .clear_framedrop = msm_vfe40_clear_framedrop,
+ .cfg_wm_reg = msm_vfe40_axi_cfg_wm_reg,
+ .clear_wm_reg = msm_vfe40_axi_clear_wm_reg,
+ .cfg_wm_xbar_reg = msm_vfe40_axi_cfg_wm_xbar_reg,
+ .clear_wm_xbar_reg = msm_vfe40_axi_clear_wm_xbar_reg,
+ .cfg_rdi_reg = msm_vfe40_axi_cfg_rdi_reg,
+ .cfg_ub = msm_vfe40_cfg_axi_ub,
+ .update_ping_pong_addr =
+ msm_vfe40_update_ping_pong_addr,
+ .get_comp_mask = msm_vfe40_get_comp_mask,
+ .get_wm_mask = msm_vfe40_get_wm_mask,
+ .get_pingpong_status = msm_vfe40_get_pingpong_status,
+ .halt = msm_vfe40_axi_halt,
+ },
+ .core_ops = {
+ .epoch_irq = msm_vfe40_epoch_irq_enb,
+ .reg_update = msm_vfe40_reg_update,
+ .cfg_camif = msm_vfe40_cfg_camif,
+ .update_camif_state = msm_vfe40_update_camif_state,
+ .reset_hw = msm_vfe40_reset_hardware,
+ .init_hw = msm_vfe40_init_hardware,
+ .init_hw_reg = msm_vfe40_init_hardware_reg,
+ .release_hw = msm_vfe40_release_hardware,
+ .get_platform_data = msm_vfe40_get_platform_data,
+ },
+ .stats_ops = {
+ .cfg_comp_mask = msm_vfe40_stats_cfg_comp_mask,
+ .clear_comp_mask = msm_vfe40_stats_clear_comp_mask,
+ .cfg_wm_irq_mask = msm_vfe40_stats_cfg_wm_irq_mask,
+ .clear_wm_irq_mask = msm_vfe40_stats_clear_wm_irq_mask,
+ .cfg_wm_reg = msm_vfe40_stats_cfg_wm_reg,
+ .clear_wm_reg = msm_vfe40_stats_clear_wm_reg,
+ .cfg_ub = msm_vfe40_stats_cfg_ub,
+ .stats_enable = msm_vfe40_stats_enable,
+ .update_ping_pong_addr =
+ msm_vfe40_stats_update_ping_pong_addr,
+ .get_comp_mask = msm_vfe40_stats_get_comp_mask,
+ .get_wm_mask = msm_vfe40_stats_get_wm_mask,
+ .get_frame_id = msm_vfe40_stats_get_frame_id,
+ .get_pingpong_status = msm_vfe40_get_pingpong_status,
+ .get_active_pingpong_idx =
+ msm_vfe40_stats_get_active_pingpong_idx,
+ },
+ },
+ .axi_hw_info = &msm_vfe40_axi_hw_info,
+ .subdev_ops = &msm_vfe40_subdev_ops,
+ .subdev_internal_ops = &msm_vfe40_internal_ops,
+};
+EXPORT_SYMBOL(vfe40_hw_info);
diff --git a/arch/arm/boot/dts/msm8974-mtp.dts b/drivers/media/video/msmb/isp/msm_isp40.h
similarity index 64%
copy from arch/arm/boot/dts/msm8974-mtp.dts
copy to drivers/media/video/msmb/isp/msm_isp40.h
index 9946cf0..e9b1518 100644
--- a/arch/arm/boot/dts/msm8974-mtp.dts
+++ b/drivers/media/video/msmb/isp/msm_isp40.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* 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
@@ -10,13 +10,8 @@
* GNU General Public License for more details.
*/
-/dts-v1/;
+#ifndef __MSM_ISP40_H__
+#define __MSM_ISP40_H__
-/include/ "msm8974.dtsi"
-/include/ "msm8974-mtp.dtsi"
-
-/ {
- model = "Qualcomm MSM 8974 MTP";
- compatible = "qcom,msm8974-mtp", "qcom,msm8974";
- qcom,msm-id = <126 8 0>;
-};
+extern struct msm_vfe_hardware_info vfe40_hw_info;
+#endif /* __MSM_ISP40_H__ */
diff --git a/drivers/media/video/msmb/isp/msm_isp_axi_util.c b/drivers/media/video/msmb/isp/msm_isp_axi_util.c
new file mode 100644
index 0000000..7e62071
--- /dev/null
+++ b/drivers/media/video/msmb/isp/msm_isp_axi_util.c
@@ -0,0 +1,967 @@
+/* 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/io.h>
+#include <media/v4l2-subdev.h>
+#include "msm_isp_util.h"
+#include "msm_isp_axi_util.h"
+
+int msm_isp_axi_create_stream(
+ struct msm_vfe_axi_shared_data *axi_data,
+ struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd)
+{
+ int i, rc = -1;
+ for (i = 0; i < MAX_NUM_STREAM; i++) {
+ if (axi_data->stream_info[i].state == AVALIABLE)
+ break;
+ }
+
+ if (i == MAX_NUM_STREAM) {
+ pr_err("%s: No free stream\n", __func__);
+ return rc;
+ }
+
+ axi_data->stream_info[i].session_id = stream_cfg_cmd->session_id;
+ axi_data->stream_info[i].stream_id = stream_cfg_cmd->stream_id;
+ axi_data->stream_info[i].buf_divert = stream_cfg_cmd->buf_divert;
+ axi_data->stream_info[i].state = INACTIVE;
+
+ if ((axi_data->stream_handle_cnt << 8) == 0)
+ axi_data->stream_handle_cnt++;
+
+ stream_cfg_cmd->axi_stream_handle =
+ (++axi_data->stream_handle_cnt) << 8 | i;
+
+ return 0;
+}
+
+void msm_isp_axi_destroy_stream(
+ struct msm_vfe_axi_shared_data *axi_data, int stream_idx)
+{
+ if (axi_data->stream_info[stream_idx].state != AVALIABLE)
+ axi_data->stream_info[stream_idx].state = AVALIABLE;
+ else
+ pr_err("%s: stream does not exist\n", __func__);
+}
+
+int msm_isp_validate_axi_request(struct msm_vfe_axi_shared_data *axi_data,
+ struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd)
+{
+ int rc = -1;
+ struct msm_vfe_axi_stream *stream_info =
+ &axi_data->stream_info[
+ (stream_cfg_cmd->axi_stream_handle & 0xFF)];
+
+ switch (stream_cfg_cmd->output_format) {
+ case V4L2_PIX_FMT_SBGGR8:
+ case V4L2_PIX_FMT_SGBRG8:
+ case V4L2_PIX_FMT_SGRBG8:
+ case V4L2_PIX_FMT_SRGGB8:
+ case V4L2_PIX_FMT_SBGGR10:
+ case V4L2_PIX_FMT_SGBRG10:
+ case V4L2_PIX_FMT_SGRBG10:
+ case V4L2_PIX_FMT_SRGGB10:
+ case V4L2_PIX_FMT_SBGGR12:
+ case V4L2_PIX_FMT_SGBRG12:
+ case V4L2_PIX_FMT_SGRBG12:
+ case V4L2_PIX_FMT_SRGGB12:
+ stream_info->num_planes = 1;
+ break;
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV21:
+ stream_info->num_planes = 2;
+ break;
+ /*TD: Add more image format*/
+ default:
+ pr_err("%s: Invalid output format\n", __func__);
+ return rc;
+ }
+
+ if (axi_data->hw_info->num_wm - axi_data->num_used_wm <
+ stream_info->num_planes) {
+ pr_err("%s: No free write masters\n", __func__);
+ return rc;
+ }
+
+ if ((stream_info->num_planes > 1) &&
+ (axi_data->hw_info->num_comp_mask -
+ axi_data->num_used_composite_mask < 1)) {
+ pr_err("%s: No free composite mask\n", __func__);
+ return rc;
+ }
+
+ if (stream_cfg_cmd->stream_src == RDI) {
+ if (axi_data->hw_info->num_rdi -
+ axi_data->num_used_rdi < stream_info->num_planes) {
+ pr_err("%s: No free RDI\n", __func__);
+ return rc;
+ }
+ }
+
+ if (stream_cfg_cmd->init_frame_drop >= MAX_INIT_FRAME_DROP) {
+ pr_err("%s: Invalid skip pattern\n", __func__);
+ return rc;
+ }
+
+ if (stream_cfg_cmd->frame_skip_pattern >= MAX_SKIP) {
+ pr_err("%s: Invalid skip pattern\n", __func__);
+ return rc;
+ }
+
+ return 0;
+}
+
+static uint32_t msm_isp_axi_get_plane_size(
+ struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd, int plane_idx)
+{
+ uint32_t size = 0;
+ struct msm_vfe_axi_plane_cfg *plane_cfg = stream_cfg_cmd->plane_cfg;
+ switch (stream_cfg_cmd->output_format) {
+ case V4L2_PIX_FMT_SBGGR8:
+ case V4L2_PIX_FMT_SGBRG8:
+ case V4L2_PIX_FMT_SGRBG8:
+ case V4L2_PIX_FMT_SRGGB8:
+ size = plane_cfg[plane_idx].output_height *
+ plane_cfg[plane_idx].output_width;
+ break;
+ case V4L2_PIX_FMT_SBGGR10:
+ case V4L2_PIX_FMT_SGBRG10:
+ case V4L2_PIX_FMT_SGRBG10:
+ case V4L2_PIX_FMT_SRGGB10:
+ /* TODO: fix me */
+ size = plane_cfg[plane_idx].output_height *
+ plane_cfg[plane_idx].output_width;
+ break;
+ case V4L2_PIX_FMT_SBGGR12:
+ case V4L2_PIX_FMT_SGBRG12:
+ case V4L2_PIX_FMT_SGRBG12:
+ case V4L2_PIX_FMT_SRGGB12:
+ /* TODO: fix me */
+ size = plane_cfg[plane_idx].output_height *
+ plane_cfg[plane_idx].output_width;
+ break;
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV21:
+ if (plane_cfg[plane_idx].output_plane_format == Y_PLANE)
+ size = plane_cfg[plane_idx].output_height *
+ plane_cfg[plane_idx].output_width;
+ else
+ size = plane_cfg[plane_idx].output_height *
+ plane_cfg[plane_idx].output_width / 2;
+ break;
+ /*TD: Add more image format*/
+ default:
+ pr_err("%s: Invalid output format\n", __func__);
+ break;
+ }
+ return size;
+}
+
+void msm_isp_axi_reserve_wm(struct msm_vfe_axi_shared_data *axi_data,
+ struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd)
+{
+ int i, j;
+ struct msm_vfe_axi_stream *stream_info =
+ &axi_data->stream_info[
+ (stream_cfg_cmd->axi_stream_handle & 0xFF)];
+
+ for (i = 0; i < stream_info->num_planes; i++) {
+ for (j = 0; j < axi_data->hw_info->num_wm; j++) {
+ if (!axi_data->free_wm[j]) {
+ axi_data->free_wm[j] =
+ stream_cfg_cmd->axi_stream_handle;
+ axi_data->wm_image_size[j] =
+ msm_isp_axi_get_plane_size(
+ stream_cfg_cmd, i);
+ axi_data->num_used_wm++;
+ break;
+ }
+ }
+ stream_info->wm[i] = j;
+ }
+}
+
+void msm_isp_axi_free_wm(struct msm_vfe_axi_shared_data *axi_data,
+ struct msm_vfe_axi_stream *stream_info)
+{
+ int i;
+ for (i = 0; i < stream_info->num_planes; i++) {
+ axi_data->free_wm[stream_info->wm[i]] = 0;
+ axi_data->num_used_wm--;
+ }
+}
+
+void msm_isp_axi_reserve_rdi(struct msm_vfe_axi_shared_data *axi_data,
+ struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd)
+{
+ int i, j;
+ struct msm_vfe_axi_stream *stream_info =
+ &axi_data->stream_info[(stream_cfg_cmd->axi_stream_handle & 0xFF)];
+
+ for (i = 0; i < stream_info->num_planes; i++) {
+ uint8_t csid = stream_cfg_cmd->plane_cfg[i].csid_src;
+
+ for (j = 0; j < axi_data->hw_info->num_rdi; j++) {
+ if (!axi_data->free_rdi[j]) {
+ axi_data->free_rdi[j] = 1;
+ axi_data->num_used_rdi++;
+ break;
+ }
+ }
+ stream_info->rdi[i] = j;
+
+ for (j = 0; j < axi_data->hw_info->num_rdi; j++) {
+ if (!axi_data->free_rdi_master[csid][j]) {
+ axi_data->free_rdi_master[csid][j] = 1;
+ axi_data->num_used_rdi++;
+ break;
+ }
+ }
+ stream_info->rdi_master[i] =
+ csid * axi_data->hw_info->num_rdi_master + j;
+ }
+ return;
+}
+
+void msm_isp_axi_reserve_comp_mask(
+ struct msm_vfe_axi_shared_data *axi_data,
+ struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd)
+{
+ int i;
+ uint8_t comp_mask = 0;
+ struct msm_vfe_axi_stream *stream_info =
+ &axi_data->stream_info[
+ (stream_cfg_cmd->axi_stream_handle & 0xFF)];
+ for (i = 0; i < stream_info->num_planes; i++)
+ comp_mask |= 1 << stream_info->wm[i];
+
+ for (i = 0; i < axi_data->hw_info->num_comp_mask; i++) {
+ if (!axi_data->composite_info[i].stream_handle) {
+ axi_data->composite_info[i].stream_handle =
+ stream_cfg_cmd->axi_stream_handle;
+ axi_data->composite_info[i].
+ stream_composite_mask = comp_mask;
+ axi_data->num_used_composite_mask++;
+ break;
+ }
+ }
+ stream_info->comp_mask_index = i;
+ return;
+}
+
+void msm_isp_axi_free_comp_mask(struct msm_vfe_axi_shared_data *axi_data,
+ struct msm_vfe_axi_stream *stream_info)
+{
+ axi_data->composite_info[stream_info->comp_mask_index].
+ stream_composite_mask = 0;
+ axi_data->composite_info[stream_info->comp_mask_index].
+ stream_handle = 0;
+ axi_data->num_used_composite_mask--;
+}
+
+int msm_isp_axi_check_stream_state(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream_cfg_cmd *stream_cfg_cmd)
+{
+ int rc = 0, i, j;
+ uint8_t src_state;
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
+ struct msm_vfe_axi_stream *stream_info;
+ enum msm_vfe_axi_state valid_state =
+ (stream_cfg_cmd->cmd == START_STREAM) ? INACTIVE : ACTIVE;
+
+ for (i = 0; i < stream_cfg_cmd->num_streams; i++) {
+ stream_info = &axi_data->stream_info[
+ (stream_cfg_cmd->stream_handle[i] & 0xFF)];
+ if (stream_info->state != valid_state) {
+ pr_err("%s: Invalid stream state\n", __func__);
+ rc = -EINVAL;
+ break;
+ }
+ /*
+ * For RDI stream, if multiple RDIs are used
+ * check if all the RDI srcs are in the same state, on/off
+ */
+ if (stream_info->stream_src == RDI) {
+ src_state = axi_data->src_info[
+ stream_info->rdi[0]+1].active;
+ for (j = 0; j < stream_info->num_planes; j++) {
+ if (src_state !=
+ axi_data->src_info[
+ stream_info->rdi[j]+1].active) {
+ pr_err("%s: RDI stream has inconsistent state\n",
+ __func__);
+ rc = -EINVAL;
+ break;
+ }
+ }
+ }
+
+ if (stream_cfg_cmd->cmd == START_STREAM) {
+ stream_info->bufq_handle =
+ vfe_dev->buf_mgr->ops->get_bufq_handle(
+ vfe_dev->buf_mgr, stream_info->session_id,
+ stream_info->stream_id);
+ if (stream_info->bufq_handle == 0) {
+ pr_err("%s: Stream has no valid buffer queue\n",
+ __func__);
+ rc = -EINVAL;
+ break;
+ }
+ }
+ }
+ return rc;
+}
+
+void msm_isp_update_framedrop_reg(struct vfe_device *vfe_dev)
+{
+ int i;
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
+ struct msm_vfe_axi_stream *stream_info;
+ for (i = 0; i < MAX_NUM_STREAM; i++) {
+ stream_info = &axi_data->stream_info[i];
+ if (stream_info->framedrop_update) {
+ if (stream_info->init_frame_drop == 0) {
+ stream_info->framedrop_update = 0;
+ vfe_dev->hw_info->vfe_ops.axi_ops.
+ cfg_framedrop(vfe_dev, stream_info);
+ }
+ }
+ if (stream_info->stream_type == BURST_STREAM) {
+ if (stream_info->burst_frame_count == 0 &&
+ stream_info->state == ACTIVE) {
+ vfe_dev->hw_info->vfe_ops.axi_ops.
+ cfg_framedrop(vfe_dev, stream_info);
+ if (stream_info->stream_src == RDI) {
+ uint32_t wm_reload_mask = 0,
+ reg_update_mask = 0;
+ stream_info->state = STOP_PENDING;
+ msm_isp_axi_stream_enable_cfg(
+ vfe_dev, stream_info,
+ &wm_reload_mask,
+ ®_update_mask);
+ }
+ }
+ }
+ }
+}
+
+void msm_isp_update_framedrop_count(
+ struct vfe_device *vfe_dev)
+{
+ int i;
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
+ struct msm_vfe_axi_stream *stream_info;
+ for (i = 0; i < MAX_NUM_STREAM; i++) {
+ stream_info = &axi_data->stream_info[i];
+ if (stream_info->framedrop_update) {
+ stream_info->init_frame_drop--;
+ if (stream_info->init_frame_drop == 1) {
+ vfe_dev->hw_info->vfe_ops.core_ops.
+ reg_update(vfe_dev, 0xF);
+ }
+ }
+ if (stream_info->stream_type == BURST_STREAM) {
+ stream_info->burst_frame_count--;
+ if (stream_info->burst_frame_count == 1) {
+ vfe_dev->hw_info->vfe_ops.core_ops.
+ reg_update(vfe_dev, 0xF);
+ } else if (stream_info->burst_frame_count == 0) {
+ if (stream_info->stream_src != RDI) {
+ vfe_dev->hw_info->vfe_ops.core_ops.
+ update_camif_state(vfe_dev,
+ DISABLE_CAMIF);
+ pr_err("%s: pending burst_cnt = %d, disable camif\n",
+ __func__,
+ stream_info->burst_frame_count);
+ }
+ }
+ }
+ }
+}
+
+void msm_isp_new_frame_notify(struct vfe_device *vfe_dev,
+ enum msm_vfe_input_src frame_src) {
+ switch (frame_src) {
+ case VFE_PIX_0:
+ ISP_DBG("%s: PIX0 frame id: %lu\n", __func__,
+ vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id);
+ msm_isp_update_framedrop_count(vfe_dev);
+ vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id++;
+ if (vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id == 0)
+ vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id = 1;
+ break;
+ case VFE_RAW_0:
+ case VFE_RAW_1:
+ case VFE_RAW_2:
+ break;
+ default:
+ pr_err("%s: invalid frame src %d received\n",
+ __func__, frame_src);
+ break;
+ }
+}
+void msm_isp_calculate_framedrop(
+ struct msm_vfe_axi_shared_data *axi_data,
+ struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd)
+{
+ struct msm_vfe_axi_stream *stream_info =
+ &axi_data->stream_info[
+ (stream_cfg_cmd->axi_stream_handle & 0xFF)];
+ uint32_t framedrop_period = 1;
+
+ switch (stream_cfg_cmd->frame_skip_pattern) {
+ case NO_SKIP:
+ stream_info->framedrop_pattern = VFE_NO_DROP;
+ framedrop_period = 1;
+ break;
+ case EVERY_2FRAME:
+ stream_info->framedrop_pattern = VFE_DROP_EVERY_2FRAME;
+ framedrop_period = 2;
+ break;
+ case EVERY_4FRAME:
+ stream_info->framedrop_pattern = VFE_DROP_EVERY_4FRAME;
+ framedrop_period = 4;
+ break;
+ case EVERY_8FRAME:
+ stream_info->framedrop_pattern = VFE_DROP_EVERY_8FRAME;
+ framedrop_period = 8;
+ break;
+ case EVERY_16FRAME:
+ stream_info->framedrop_pattern = VFE_DROP_EVERY_16FRAME;
+ framedrop_period = 16;
+ break;
+ case EVERY_32FRAME:
+ stream_info->framedrop_pattern = VFE_DROP_EVERY_32FRAME;
+ framedrop_period = 32;
+ break;
+ default:
+ stream_info->framedrop_pattern = VFE_NO_DROP;
+ framedrop_period = 1;
+ break;
+ }
+
+ if (stream_cfg_cmd->init_frame_drop < framedrop_period) {
+ stream_info->framedrop_pattern <<=
+ stream_cfg_cmd->init_frame_drop;
+ stream_info->init_frame_drop = 0;
+ stream_info->framedrop_update = 0;
+ } else {
+ stream_info->init_frame_drop = stream_cfg_cmd->init_frame_drop;
+ stream_info->framedrop_update = 1;
+ }
+
+ if (stream_cfg_cmd->burst_count > 0) {
+ stream_info->stream_type = BURST_STREAM;
+ stream_info->burst_frame_count =
+ stream_cfg_cmd->init_frame_drop +
+ (stream_cfg_cmd->burst_count - 1) *
+ framedrop_period + 1;
+ } else {
+ stream_info->stream_type = CONTINUOUS_STREAM;
+ stream_info->burst_frame_count = 0;
+ }
+}
+
+int msm_isp_request_axi_stream(struct vfe_device *vfe_dev, void *arg)
+{
+ int rc = 0, i;
+ struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd = arg;
+ struct msm_vfe_axi_stream *stream_info;
+
+ rc = msm_isp_axi_create_stream(
+ &vfe_dev->axi_data, stream_cfg_cmd);
+ if (rc) {
+ pr_err("%s: create stream failed\n", __func__);
+ return rc;
+ }
+
+ rc = msm_isp_validate_axi_request(
+ &vfe_dev->axi_data, stream_cfg_cmd);
+ if (rc) {
+ pr_err("%s: Request validation failed\n", __func__);
+ msm_isp_axi_destroy_stream(&vfe_dev->axi_data,
+ (stream_cfg_cmd->axi_stream_handle & 0xFF));
+ return rc;
+ }
+
+ msm_isp_axi_reserve_wm(&vfe_dev->axi_data, stream_cfg_cmd);
+ if (stream_cfg_cmd->stream_src == RDI)
+ msm_isp_axi_reserve_rdi(&vfe_dev->axi_data, stream_cfg_cmd);
+
+ stream_info =
+ &vfe_dev->axi_data.
+ stream_info[(stream_cfg_cmd->axi_stream_handle & 0xFF)];
+
+ msm_isp_calculate_framedrop(&vfe_dev->axi_data, stream_cfg_cmd);
+ vfe_dev->hw_info->vfe_ops.axi_ops.cfg_framedrop(vfe_dev, stream_info);
+
+ if (stream_info->num_planes > 1) {
+ msm_isp_axi_reserve_comp_mask(
+ &vfe_dev->axi_data, stream_cfg_cmd);
+ vfe_dev->hw_info->vfe_ops.axi_ops.
+ cfg_comp_mask(vfe_dev, stream_info);
+ } else {
+ vfe_dev->hw_info->vfe_ops.axi_ops.
+ cfg_wm_irq_mask(vfe_dev, stream_info);
+ }
+
+ for (i = 0; i < stream_info->num_planes; i++) {
+ vfe_dev->hw_info->vfe_ops.axi_ops.
+ cfg_wm_reg(vfe_dev, stream_cfg_cmd, i);
+
+ vfe_dev->hw_info->vfe_ops.axi_ops.
+ cfg_wm_xbar_reg(vfe_dev, stream_cfg_cmd, i);
+
+ if (stream_cfg_cmd->stream_src == RDI)
+ vfe_dev->hw_info->vfe_ops.axi_ops.
+ cfg_rdi_reg(vfe_dev, stream_cfg_cmd, i);
+ }
+ return rc;
+}
+
+int msm_isp_release_axi_stream(struct vfe_device *vfe_dev, void *arg)
+{
+ int rc = 0, i;
+ struct msm_vfe_axi_stream_release_cmd *stream_release_cmd = arg;
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
+ struct msm_vfe_axi_stream *stream_info =
+ &axi_data->stream_info[
+ (stream_release_cmd->stream_handle & 0xFF)];
+ struct msm_vfe_axi_stream_cfg_cmd stream_cfg;
+
+ if (stream_info->state == AVALIABLE) {
+ pr_err("%s: Stream already released\n", __func__);
+ return -EINVAL;
+ } else if (stream_info->state != INACTIVE) {
+ stream_cfg.cmd = STOP_STREAM;
+ stream_cfg.num_streams = 1;
+ stream_cfg.stream_handle[0] = stream_release_cmd->stream_handle;
+ msm_isp_cfg_axi_stream(vfe_dev, (void *) &stream_cfg);
+ }
+
+ for (i = 0; i < stream_info->num_planes; i++) {
+ vfe_dev->hw_info->vfe_ops.axi_ops.
+ clear_wm_reg(vfe_dev, stream_info, i);
+
+ vfe_dev->hw_info->vfe_ops.axi_ops.
+ clear_wm_xbar_reg(vfe_dev, stream_info, i);
+ }
+
+ if (stream_info->num_planes > 1) {
+ msm_isp_axi_free_comp_mask(&vfe_dev->axi_data, stream_info);
+ vfe_dev->hw_info->vfe_ops.axi_ops.
+ clear_comp_mask(vfe_dev, stream_info);
+ } else {
+ vfe_dev->hw_info->vfe_ops.axi_ops.
+ clear_wm_irq_mask(vfe_dev, stream_info);
+ }
+
+ vfe_dev->hw_info->vfe_ops.axi_ops.clear_framedrop(vfe_dev, stream_info);
+ msm_isp_axi_free_wm(axi_data, stream_info);
+
+ msm_isp_axi_destroy_stream(&vfe_dev->axi_data,
+ (stream_release_cmd->stream_handle & 0xFF));
+
+ return rc;
+}
+
+void msm_isp_axi_stream_enable_cfg(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info,
+ uint32_t *wm_reload_mask, uint32_t *reg_update_mask)
+{
+ int i;
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
+ if (stream_info->state == INACTIVE)
+ return;
+ for (i = 0; i < stream_info->num_planes; i++) {
+ /*TD: Frame base command*/
+ if (stream_info->state == START_PENDING)
+ vfe_dev->hw_info->vfe_ops.axi_ops.
+ enable_wm(vfe_dev, stream_info->wm[i], 1);
+ else
+ vfe_dev->hw_info->vfe_ops.axi_ops.
+ enable_wm(vfe_dev, stream_info->wm[i], 0);
+
+ *wm_reload_mask |= (1 << stream_info->wm[i]);
+ if (stream_info->stream_src == RDI)
+ *reg_update_mask |= (1 << stream_info->rdi[i]);
+ }
+ if (stream_info->state == START_PENDING) {
+ axi_data->num_active_stream++;
+ stream_info->state = ACTIVE;
+ } else {
+ axi_data->num_active_stream--;
+ stream_info->state = INACTIVE;
+ }
+}
+
+void msm_isp_axi_stream_update(struct vfe_device *vfe_dev)
+{
+ int i;
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
+ uint32_t wm_reload_mask = 0x0, reg_update_mask = 0x1;
+ int send_update_complete = 0;
+ for (i = 0; i < MAX_NUM_STREAM; i++) {
+ if (axi_data->stream_info[i].state == START_PENDING ||
+ axi_data->stream_info[i].state ==
+ STOP_PENDING) {
+ msm_isp_axi_stream_enable_cfg(
+ vfe_dev, &axi_data->stream_info[i],
+ &wm_reload_mask, ®_update_mask);
+ if (axi_data->stream_info[i].state == STOP_PENDING)
+ axi_data->stream_info[i].state = STOPPING;
+ } else if (axi_data->stream_info[i].state == STOPPING) {
+ send_update_complete = 1;
+ axi_data->stream_info[i].state = INACTIVE;
+ }
+ }
+ /*Reload AXI*/
+ vfe_dev->hw_info->vfe_ops.axi_ops.
+ reload_wm(vfe_dev, wm_reload_mask);
+ /*Reg update per src*/
+ vfe_dev->hw_info->vfe_ops.core_ops.
+ reg_update(vfe_dev, reg_update_mask);
+ if (send_update_complete) {
+ ISP_DBG("%s: send update complete\n", __func__);
+ vfe_dev->axi_data.stream_update = 0;
+ complete(&vfe_dev->stream_config_complete);
+ }
+}
+#define VFE_PING_FLAG 0xFFFFFFFF
+#define VFE_PONG_FLAG 0x0
+
+int msm_isp_cfg_ping_pong_address(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info, uint32_t pingpong_status,
+ struct timeval *tv)
+{
+ int i, rc = -1;
+ struct msm_isp_buffer *buf;
+ struct msm_isp_event_data buf_event;
+ uint32_t pingpong_bit = 0;
+ uint32_t bufq_handle = stream_info->bufq_handle;
+
+ pingpong_bit = (~(pingpong_status >> stream_info->wm[0]) & 0x1);
+ rc = vfe_dev->buf_mgr->ops->get_buf(
+ vfe_dev->buf_mgr, bufq_handle, &buf);
+ if (rc < 0) {
+ if (stream_info->stream_type == BURST_STREAM &&
+ stream_info->burst_frame_count <= 1) {
+ rc = 0;
+ if (pingpong_bit)
+ buf = stream_info->buf[0];
+ else
+ buf = stream_info->buf[1];
+ } else {
+ pr_err("%s: No free buffer, stream_type = %d, burst_cnt = %d\n",
+ __func__, stream_info->stream_type,
+ stream_info->burst_frame_count);
+ return rc;
+ }
+ }
+
+ if (buf->num_planes != stream_info->num_planes) {
+ pr_err("%s: Invalid buffer\n", __func__);
+ rc = -EINVAL;
+ goto buf_error;
+ }
+ for (i = 0; i < stream_info->num_planes; i++) {
+ if (pingpong_bit !=
+ (~(pingpong_status >> stream_info->wm[i]) & 0x1)) {
+ pr_warn("%s: Write master ping pong mismatch. Status: 0x%x\n",
+ __func__, pingpong_status);
+ }
+ }
+ for (i = 0; i < stream_info->num_planes; i++)
+ vfe_dev->hw_info->vfe_ops.axi_ops.update_ping_pong_addr(
+ vfe_dev, stream_info->wm[i],
+ pingpong_status, buf->mapped_info[i].paddr);
+
+ if (stream_info->buf[pingpong_bit] && tv) {
+ if (stream_info->buf_divert) {
+ buf_event.frame_id = stream_info->frame_id;
+ buf_event.timestamp = *tv;
+ buf_event.u.buf_done.session_id =
+ stream_info->session_id;
+ buf_event.u.buf_done.stream_id =
+ stream_info->stream_id;
+ buf_event.u.buf_done.handle =
+ stream_info->bufq_handle;
+ buf_event.u.buf_done.buf_idx =
+ stream_info->buf[pingpong_bit]->buf_idx;
+ msm_isp_send_event(
+ vfe_dev, ISP_EVENT_BUF_DIVERT, &buf_event);
+ } else {
+ vfe_dev->buf_mgr->ops->buf_done(vfe_dev->buf_mgr,
+ stream_info->buf[pingpong_bit]->bufq_handle,
+ stream_info->buf[pingpong_bit]->buf_idx,
+ tv, stream_info->frame_id);
+ }
+ }
+
+ stream_info->buf[pingpong_bit] = buf;
+ return 0;
+buf_error:
+ vfe_dev->buf_mgr->ops->put_buf(vfe_dev->buf_mgr,
+ buf->bufq_handle, buf->buf_idx);
+ return rc;
+}
+
+enum msm_isp_camif_update_state
+ msm_isp_update_camif_output_count(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream_cfg_cmd *stream_cfg_cmd)
+{
+ int i;
+ struct msm_vfe_axi_stream *stream_info;
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
+ uint8_t cur_pix_count = axi_data->src_info[VFE_PIX_0].
+ pix_stream_count;
+ uint8_t cur_raw_count = axi_data->src_info[VFE_PIX_0].
+ raw_stream_count;
+ uint8_t pix_stream_cnt = 0;
+ for (i = 0; i < stream_cfg_cmd->num_streams; i++) {
+ stream_info =
+ &axi_data->stream_info[
+ (stream_cfg_cmd->stream_handle[i] & 0xFF)];
+ if (stream_info->stream_src != RDI)
+ pix_stream_cnt++;
+ if (stream_info->stream_src == PIX_ENCODER ||
+ stream_info->stream_src == PIX_VIEWFINDER) {
+ if (stream_cfg_cmd->cmd == START_STREAM)
+ vfe_dev->axi_data.src_info[VFE_PIX_0].
+ pix_stream_count++;
+ else
+ vfe_dev->axi_data.src_info[VFE_PIX_0].
+ pix_stream_count--;
+ } else if (stream_info->stream_src == CAMIF_RAW ||
+ stream_info->stream_src == IDEAL_RAW) {
+ if (stream_cfg_cmd->cmd == START_STREAM)
+ vfe_dev->axi_data.src_info[VFE_PIX_0].
+ raw_stream_count++;
+ else
+ vfe_dev->axi_data.src_info[VFE_PIX_0].
+ raw_stream_count--;
+ }
+ }
+ if (pix_stream_cnt) {
+ if ((cur_pix_count + cur_raw_count == 0) &&
+ (axi_data->src_info[VFE_PIX_0].
+ pix_stream_count +
+ axi_data->src_info[VFE_PIX_0].
+ raw_stream_count != 0)) {
+ return ENABLE_CAMIF;
+ }
+
+ if ((cur_pix_count + cur_raw_count != 0) &&
+ (axi_data->src_info[VFE_PIX_0].
+ pix_stream_count +
+ axi_data->src_info[VFE_PIX_0].
+ raw_stream_count == 0)) {
+ return DISABLE_CAMIF;
+ }
+ }
+
+ return NO_UPDATE;
+}
+
+void msm_camera_io_dump_2(void __iomem *addr, int size)
+{
+ char line_str[128], *p_str;
+ int i;
+ u32 *p = (u32 *) addr;
+ u32 data;
+ ISP_DBG("%s: %p %d\n", __func__, addr, size);
+ line_str[0] = '\0';
+ p_str = line_str;
+ for (i = 0; i < size/4; i++) {
+ if (i % 4 == 0) {
+ snprintf(p_str, 12, "%08x: ", (u32) p);
+ p_str += 10;
+ }
+ data = readl_relaxed(p++);
+ snprintf(p_str, 12, "%08x ", data);
+ p_str += 9;
+ if ((i + 1) % 4 == 0) {
+ ISP_DBG("%s\n", line_str);
+ line_str[0] = '\0';
+ p_str = line_str;
+ }
+ }
+ if (line_str[0] != '\0')
+ ISP_DBG("%s\n", line_str);
+}
+
+int msm_isp_cfg_axi_stream(struct vfe_device *vfe_dev, void *arg)
+{
+ int rc = 0, i;
+ struct msm_vfe_axi_stream_cfg_cmd *stream_cfg_cmd = arg;
+ uint32_t wm_reload_mask = 0x0, reg_update_mask = 0x1;
+ struct msm_vfe_axi_stream *stream_info;
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
+ uint8_t src_state;
+ enum msm_isp_camif_update_state camif_update;
+ uint8_t wait_for_complete = 0;
+ rc = msm_isp_axi_check_stream_state(vfe_dev, stream_cfg_cmd);
+ if (rc < 0) {
+ pr_err("%s: Invalid stream state\n", __func__);
+ return rc;
+ }
+
+ if (axi_data->num_active_stream == 0) {
+ /*Configure UB*/
+ vfe_dev->hw_info->vfe_ops.axi_ops.cfg_ub(vfe_dev);
+ }
+
+ camif_update =
+ msm_isp_update_camif_output_count(vfe_dev, stream_cfg_cmd);
+
+ if (camif_update == DISABLE_CAMIF)
+ vfe_dev->hw_info->vfe_ops.core_ops.
+ update_camif_state(vfe_dev, DISABLE_CAMIF_IMMEDIATELY);
+
+ /*
+ * Stream start either immediately or at reg update
+ * Depends on whether the stream src is active
+ * If source is on, start and stop have to be done during reg update
+ * If source is off, start can happen immediately or during reg update
+ * stop has to be done immediately.
+ */
+ for (i = 0; i < stream_cfg_cmd->num_streams; i++) {
+ stream_info =
+ &axi_data->stream_info[
+ (stream_cfg_cmd->stream_handle[i] & 0xFF)];
+
+
+ if (stream_info->stream_src == RDI)
+ src_state =
+ axi_data->src_info[
+ stream_info->rdi[0]+1].active;
+ else
+ src_state = axi_data->src_info[0].active;
+
+ stream_info->state = (stream_cfg_cmd->cmd == START_STREAM) ?
+ START_PENDING : STOP_PENDING;
+
+ if (stream_cfg_cmd->cmd == START_STREAM) {
+ /*Set address for both PING & PONG register */
+ rc = msm_isp_cfg_ping_pong_address(vfe_dev,
+ stream_info, VFE_PING_FLAG, NULL);
+ rc = msm_isp_cfg_ping_pong_address(vfe_dev,
+ stream_info, VFE_PONG_FLAG, NULL);
+ }
+ if (src_state && camif_update != DISABLE_CAMIF) {
+ /*On the fly stream start/stop */
+ wait_for_complete = 1;
+ reg_update_mask = 0xF; /*TD: Maybe set this per src*/
+ } else {
+ if (vfe_dev->dump_reg)
+ msm_camera_io_dump_2(vfe_dev->vfe_base, 0x900);
+ /*Configure AXI start bits to start immediately*/
+ msm_isp_axi_stream_enable_cfg(
+ vfe_dev, stream_info,
+ &wm_reload_mask, ®_update_mask);
+ }
+ }
+ if (!wait_for_complete) {
+ /*Reload AXI*/
+ vfe_dev->hw_info->vfe_ops.axi_ops.
+ reload_wm(vfe_dev, wm_reload_mask);
+ /*Reg update per src*/
+ vfe_dev->hw_info->vfe_ops.core_ops.
+ reg_update(vfe_dev, reg_update_mask);
+
+ if (camif_update == ENABLE_CAMIF)
+ vfe_dev->hw_info->vfe_ops.core_ops.
+ update_camif_state(vfe_dev, camif_update);
+ } else {
+ unsigned long flags;
+ spin_lock_irqsave(&vfe_dev->shared_data_lock, flags);
+ init_completion(&vfe_dev->stream_config_complete);
+ axi_data->stream_update = 1;
+ spin_unlock_irqrestore(&vfe_dev->shared_data_lock, flags);
+ /*Reload AXI*/
+ vfe_dev->hw_info->vfe_ops.axi_ops.
+ reload_wm(vfe_dev, wm_reload_mask);
+ /*Reg update per src*/
+ vfe_dev->hw_info->vfe_ops.core_ops.
+ reg_update(vfe_dev, reg_update_mask);
+ rc = wait_for_completion_interruptible_timeout(
+ &vfe_dev->stream_config_complete,
+ msecs_to_jiffies(500));
+ if (rc == 0) {
+ pr_err("%s: wait timeout\n", __func__);
+ rc = -1;
+ }
+ }
+ return rc;
+}
+
+void msm_isp_process_axi_irq(struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1,
+ struct timeval *tv)
+{
+ int i;
+ uint32_t comp_mask = 0, wm_mask = 0;
+ uint32_t pingpong_status, stream_idx;
+ struct msm_vfe_axi_stream *stream_info;
+ struct msm_vfe_axi_composite_info *comp_info;
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
+
+ ISP_DBG("%s: status: 0x%x\n", __func__, irq_status0);
+ comp_mask = vfe_dev->hw_info->vfe_ops.axi_ops.
+ get_comp_mask(irq_status0, irq_status1);
+ wm_mask = vfe_dev->hw_info->vfe_ops.axi_ops.
+ get_wm_mask(irq_status0, irq_status1);
+ if (!(comp_mask || wm_mask))
+ return;
+
+ pingpong_status =
+ vfe_dev->hw_info->vfe_ops.axi_ops.get_pingpong_status(vfe_dev);
+
+ for (i = 0; i < axi_data->hw_info->num_comp_mask; i++) {
+ comp_info = &axi_data->composite_info[i];
+ if (comp_mask & (1 << i)) {
+ if (!comp_info->stream_handle) {
+ pr_err("%s: Invalid handle for composite irq\n",
+ __func__);
+ } else {
+ stream_idx = comp_info->stream_handle & 0xFF;
+ stream_info =
+ &axi_data->stream_info[stream_idx];
+ ISP_DBG("%s: stream%d frame id: 0x%x\n",
+ __func__,
+ stream_idx, stream_info->frame_id);
+ stream_info->frame_id++;
+ msm_isp_cfg_ping_pong_address(vfe_dev,
+ stream_info, pingpong_status, tv);
+ }
+ }
+ wm_mask &= ~(comp_info->stream_composite_mask);
+ }
+
+ for (i = 0; i < axi_data->hw_info->num_wm; i++) {
+ if (wm_mask & (1 << i)) {
+ if (!axi_data->free_wm[i]) {
+ pr_err("%s: Invalid handle for wm irq\n",
+ __func__);
+ continue;
+ }
+ stream_idx = axi_data->free_wm[i] & 0xFF;
+ stream_info = &axi_data->stream_info[stream_idx];
+ stream_info->frame_id++;
+ msm_isp_cfg_ping_pong_address(vfe_dev,
+ stream_info, pingpong_status, tv);
+ }
+ }
+ return;
+}
diff --git a/drivers/media/video/msmb/isp/msm_isp_axi_util.h b/drivers/media/video/msmb/isp/msm_isp_axi_util.h
new file mode 100644
index 0000000..4847c06
--- /dev/null
+++ b/drivers/media/video/msmb/isp/msm_isp_axi_util.h
@@ -0,0 +1,65 @@
+/* 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 __MSM_ISP_AXI_UTIL_H__
+#define __MSM_ISP_AXI_UTIL_H__
+
+#include "msm_isp.h"
+
+int msm_isp_axi_create_stream(
+ struct msm_vfe_axi_shared_data *axi_data,
+ struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd);
+
+void msm_isp_axi_destroy_stream(
+ struct msm_vfe_axi_shared_data *axi_data, int stream_idx);
+
+int msm_isp_validate_axi_request(
+ struct msm_vfe_axi_shared_data *axi_data,
+ struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd);
+
+void msm_isp_axi_reserve_wm(
+ struct msm_vfe_axi_shared_data *axi_data,
+ struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd);
+
+void msm_isp_axi_reserve_rdi(
+ struct msm_vfe_axi_shared_data *axi_data,
+ struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd);
+
+void msm_isp_axi_reserve_comp_mask(
+ struct msm_vfe_axi_shared_data *axi_data,
+ struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd);
+
+int msm_isp_axi_check_stream_state(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream_cfg_cmd *stream_cfg_cmd);
+
+int msm_isp_request_axi_stream(struct vfe_device *vfe_dev, void *arg);
+int msm_isp_cfg_axi_stream(struct vfe_device *vfe_dev, void *arg);
+int msm_isp_release_axi_stream(struct vfe_device *vfe_dev, void *arg);
+
+void msm_isp_axi_stream_enable_cfg(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info,
+ uint32_t *wm_reload_mask, uint32_t *reg_update_mask);
+
+void msm_isp_axi_stream_update(struct vfe_device *vfe_dev);
+
+int msm_isp_cfg_ping_pong_address(struct vfe_device *vfe_dev,
+ struct msm_vfe_axi_stream *stream_info, uint32_t pingpong_status,
+ struct timeval *tv);
+
+void msm_isp_update_framedrop_reg(struct vfe_device *vfe_dev);
+void msm_isp_update_framedrop_count(struct vfe_device *vfe_dev);
+void msm_isp_new_frame_notify(struct vfe_device *vfe_dev,
+ enum msm_vfe_input_src frame_src);
+void msm_isp_process_axi_irq(struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1,
+ struct timeval *tv);
+#endif /* __MSM_ISP_AXI_UTIL_H__ */
diff --git a/drivers/media/video/msmb/isp/msm_isp_stats_util.c b/drivers/media/video/msmb/isp/msm_isp_stats_util.c
new file mode 100644
index 0000000..86a4a3d
--- /dev/null
+++ b/drivers/media/video/msmb/isp/msm_isp_stats_util.c
@@ -0,0 +1,263 @@
+/* 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/io.h>
+#include <media/v4l2-subdev.h>
+#include "msm_isp_util.h"
+#include "msm_isp_stats_util.h"
+#define VFE_PING_ACTIVE_FLAG 0xFFFFFFFF
+#define VFE_PONG_ACTIVE_FLAG 0x0
+
+int msm_isp_stats_cfg_ping_pong_address(struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_stream *stream_info, uint32_t pingpong_status,
+ struct msm_isp_buffer *out_buf)
+{
+ int rc = -1;
+ struct msm_isp_buffer *buf;
+ int active_bit = 0;
+ int buf_idx;
+ uint32_t bufq_handle = stream_info->bufq_handle;
+
+ out_buf = NULL;
+ active_bit = vfe_dev->hw_info->vfe_ops.stats_ops.
+ get_active_pingpong_idx(pingpong_status,
+ stream_info->stats_type);
+ buf_idx = active_bit^0x1;
+
+ rc = vfe_dev->buf_mgr->ops->get_buf(
+ vfe_dev->buf_mgr, bufq_handle, &buf);
+ if (rc < 0) {
+ pr_err("%s: No free buffer, stats_type = %d\n",
+ __func__, stream_info->stats_type);
+ return rc;
+ }
+ vfe_dev->hw_info->vfe_ops.stats_ops.update_ping_pong_addr(
+ vfe_dev, stream_info->stats_type,
+ pingpong_status, buf->mapped_info[buf_idx].paddr);
+
+ if (stream_info->buf[buf_idx])
+ out_buf = stream_info->buf[buf_idx];
+ stream_info->buf[buf_idx] = buf;
+ return 0;
+}
+
+void msm_isp_process_stats_irq(struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1,
+ struct timeval *tv)
+{
+ uint32_t frame_id;
+ uint32_t stats_comp_mask = 0, stats_mask = 0;
+ ISP_DBG("%s: status: 0x%x\n", __func__, irq_status0);
+ stats_comp_mask = vfe_dev->hw_info->vfe_ops.stats_ops.
+ get_comp_mask(irq_status0, irq_status1);
+ stats_mask = vfe_dev->hw_info->vfe_ops.stats_ops.
+ get_wm_mask(irq_status0, irq_status1);
+ if (!(stats_comp_mask || stats_mask))
+ return;
+ frame_id = vfe_dev->hw_info->vfe_ops.stats_ops.get_frame_id(vfe_dev);
+ /* TD: process comp/non comp stats */
+}
+
+static int msm_isp_stats_reserve_comp_mask(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_shared_data *stats_data,
+ struct msm_vfe_stats_stream *stream_info)
+{
+ int i;
+ uint8_t num_stats_comp;
+ int8_t comp_idx = -1;
+ if (stream_info->comp_flag == 0)
+ return 0;
+
+ num_stats_comp = vfe_dev->axi_data.hw_info->num_stats_comp_mask;
+ for (i = 0; i < num_stats_comp; i++) {
+ if (!stats_data->composite_info[i].stats_mask == 0 &&
+ comp_idx < 0)
+ comp_idx = i;
+ if (stats_data->composite_info[i].comp_flag ==
+ stream_info->comp_flag) {
+ comp_idx = i;
+ break;
+ }
+ }
+ if (comp_idx < 0) {
+ pr_err("%s: no more stats comp idx\n", __func__);
+ return -EACCES;
+ }
+ stats_data->composite_info[comp_idx].stats_mask |=
+ (1 << stream_info->stats_type);
+ if (stats_data->composite_info[comp_idx].comp_flag == 0)
+ stats_data->composite_info[comp_idx].comp_flag =
+ stream_info->comp_flag;
+ stream_info->comp_idx = comp_idx;
+ return 0;
+}
+
+static int msm_isp_stats_unreserve_comp_mask(
+ struct vfe_device *vfe_dev,
+ struct msm_vfe_stats_shared_data *stats_data,
+ struct msm_vfe_stats_stream *stream_info)
+{
+ uint8_t comp_idx = stream_info->comp_idx;
+
+ if (stream_info->comp_flag == 0)
+ return 0;
+ stats_data->composite_info[comp_idx].stats_mask &=
+ ~(1 << stream_info->stats_type);
+ if (stats_data->composite_info[comp_idx].stats_mask == 0)
+ memset(&stats_data->composite_info[comp_idx], 0,
+ sizeof(struct msm_vfe_stats_composite_info));
+ return 0;
+}
+
+int msm_isp_request_stats_stream(struct vfe_device *vfe_dev, void *arg)
+{
+ int rc = 0;
+ struct msm_vfe_stats_stream_request_cmd *stream_cfg_cmd = arg;
+ struct msm_vfe_stats_stream *stream_info = NULL;
+ struct msm_vfe_stats_shared_data *stats_data = &vfe_dev->stats_data;
+
+ if (stream_cfg_cmd->stats_type < MSM_ISP_STATS_AEC ||
+ stream_cfg_cmd->stats_type >= MSM_ISP_STATS_MAX) {
+ pr_err("%s: invalid stats type %d received\n",
+ __func__, stream_cfg_cmd->stats_type);
+ return -EINVAL;
+ }
+ stream_info = &stats_data->stream_info[stream_cfg_cmd->stats_type];
+ if (stream_info->stream_handle != 0) {
+ pr_err("%s: stats type %d has been used already\n",
+ __func__, stream_cfg_cmd->stats_type);
+ return -EBUSY;
+ }
+
+ stats_data->stream_handle_cnt++;
+ if (stats_data->stream_handle_cnt == 0)
+ stats_data->stream_handle_cnt++;
+ stream_info->stream_handle =
+ stats_data->stream_handle_cnt << 16 |
+ stream_cfg_cmd->stats_type;
+ stream_info->enable = 0;
+ stream_info->stats_type = stream_cfg_cmd->stats_type;
+ stream_info->comp_flag = stream_cfg_cmd->comp_flag;
+ stream_info->session_id = stream_cfg_cmd->session_id;
+ stream_info->stream_id = stream_cfg_cmd->stream_id;
+ stream_info->framedrop_pattern = stream_cfg_cmd->framedrop_pattern;
+ stream_cfg_cmd->stream_handle = stream_info->stream_handle;
+ msm_isp_stats_reserve_comp_mask(vfe_dev, stats_data, stream_info);
+ if (stream_info->comp_flag)
+ vfe_dev->hw_info->vfe_ops.stats_ops.
+ cfg_comp_mask(vfe_dev, stream_info);
+ else
+ vfe_dev->hw_info->vfe_ops.stats_ops.
+ cfg_wm_irq_mask(vfe_dev, stream_info);
+ vfe_dev->hw_info->vfe_ops.stats_ops.
+ cfg_wm_reg(vfe_dev, stream_info);
+ return rc;
+}
+
+int msm_isp_release_stats_stream(struct vfe_device *vfe_dev, void *arg)
+{
+ int rc = 0;
+ struct msm_vfe_stats_stream_release_cmd *stream_release_cmd = arg;
+ struct msm_vfe_stats_shared_data *stats_data = &vfe_dev->stats_data;
+ struct msm_vfe_stats_stream *stream_info =
+ &stats_data->stream_info[
+ (stream_release_cmd->stream_handle & 0xFF)];
+
+ if (stream_info == NULL ||
+ stream_info->stream_handle !=
+ stream_release_cmd->stream_handle) {
+ pr_err("%s: handle mismatch(0x%x, 0x%x\n",
+ __func__, stream_info->stream_handle,
+ stream_release_cmd->stream_handle);
+ rc = -EINVAL;
+ }
+ if (stream_info->enable) {
+ struct msm_vfe_stats_stream_cfg_cmd stop_cmd;
+ memset(&stop_cmd, 0, sizeof(stop_cmd));
+ stop_cmd.num_streams = 1;
+ stop_cmd.stream_handle[0] = stream_release_cmd->stream_handle;
+ stop_cmd.enable = 0;
+ rc = msm_isp_cfg_stats_stream(vfe_dev, (void *)&stop_cmd);
+ if (rc < 0) {
+ pr_err("%s: cannot stop stats type %d\n",
+ __func__, stream_info->stats_type);
+ return -EPERM;
+ }
+ }
+ if (stream_info->bufq_handle) {
+ vfe_dev->buf_mgr->ops->release_buf(vfe_dev->buf_mgr,
+ stream_info->bufq_handle);
+ stream_info->bufq_handle = 0;
+ }
+ vfe_dev->hw_info->vfe_ops.stats_ops.
+ clear_wm_reg(vfe_dev, stream_info);
+ if (stream_info->comp_flag) {
+ msm_isp_stats_unreserve_comp_mask(vfe_dev,
+ stats_data, stream_info);
+ vfe_dev->hw_info->vfe_ops.stats_ops.
+ clear_comp_mask(vfe_dev, stream_info);
+ } else {
+ vfe_dev->hw_info->vfe_ops.stats_ops.
+ clear_wm_irq_mask(vfe_dev, stream_info);
+ }
+ vfe_dev->hw_info->vfe_ops.stats_ops.
+ clear_framedrop(vfe_dev, stream_info);
+ memset(stream_info, 0, sizeof(struct msm_vfe_stats_stream));
+ return rc;
+}
+
+int msm_isp_cfg_stats_stream(struct vfe_device *vfe_dev, void *arg)
+{
+ int i, rc = 0;
+ uint32_t pingpong_status = 0;
+ struct msm_isp_buffer *buf = NULL;
+ struct msm_vfe_stats_stream_cfg_cmd *stream_cfg_cmd = arg;
+ struct msm_vfe_stats_stream *stream_info;
+ struct msm_vfe_stats_shared_data *stats_data = &vfe_dev->stats_data;
+ int idx;
+ uint32_t stats_mask = 0;
+ uint8_t enable = stream_cfg_cmd->enable;
+
+ for (i = 0; i < stream_cfg_cmd->num_streams; i++) {
+ idx = stream_cfg_cmd->stream_handle[i] & 0xF;
+ stream_info = &stats_data->stream_info[idx];
+ if (stream_info->stream_handle !=
+ stream_cfg_cmd->stream_handle[i]) {
+ pr_err("%s: invalid stream handle -0x%x received\n",
+ __func__, stream_cfg_cmd->stream_handle[i]);
+ continue;
+ }
+ if (enable) {
+ stream_info->bufq_handle =
+ vfe_dev->buf_mgr->ops->get_bufq_handle(
+ vfe_dev->buf_mgr, stream_info->session_id,
+ stream_info->stream_id);
+ if (stream_info->bufq_handle == 0) {
+ pr_err("%s: no buf configured for stats type = %d\n",
+ __func__,
+ stream_info->stats_type);
+ return -EINVAL;
+ }
+ }
+ /* config ping address */
+ pingpong_status = VFE_PONG_ACTIVE_FLAG;
+ stats_mask |= (1 << stream_info->stats_type);
+ msm_isp_stats_cfg_ping_pong_address(vfe_dev,
+ stream_info, pingpong_status, buf);
+ pingpong_status = VFE_PING_ACTIVE_FLAG;
+ msm_isp_stats_cfg_ping_pong_address(vfe_dev,
+ stream_info, pingpong_status, buf);
+ }
+ vfe_dev->hw_info->vfe_ops.stats_ops.
+ stats_enable(vfe_dev, stats_mask, enable);
+ return rc;
+}
diff --git a/drivers/media/video/msmb/isp/msm_isp_stats_util.h b/drivers/media/video/msmb/isp/msm_isp_stats_util.h
new file mode 100644
index 0000000..4feb653
--- /dev/null
+++ b/drivers/media/video/msmb/isp/msm_isp_stats_util.h
@@ -0,0 +1,23 @@
+/* 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 __MSM_ISP_STATS_UTIL_H__
+#define __MSM_ISP_STATS_UTIL_H__
+
+#include "msm_isp.h"
+
+void msm_isp_process_stats_irq(struct vfe_device *vfe_dev,
+ uint32_t irq_status0, uint32_t irq_status1,
+ struct timeval *tv);
+int msm_isp_cfg_stats_stream(struct vfe_device *vfe_dev, void *arg);
+int msm_isp_release_stats_stream(struct vfe_device *vfe_dev, void *arg);
+int msm_isp_request_stats_stream(struct vfe_device *vfe_dev, void *arg);
+#endif /* __MSM_ISP_STATS_UTIL_H__ */
diff --git a/drivers/media/video/msmb/isp/msm_isp_util.c b/drivers/media/video/msmb/isp/msm_isp_util.c
new file mode 100644
index 0000000..ee3e50e
--- /dev/null
+++ b/drivers/media/video/msmb/isp/msm_isp_util.c
@@ -0,0 +1,462 @@
+/* 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/io.h>
+#include <media/v4l2-subdev.h>
+
+#include "msm.h"
+#include "msm_isp_util.h"
+#include "msm_isp_axi_util.h"
+#include "msm_isp_stats_util.h"
+#include "msm_camera_io_util.h"
+
+#define MAX_ISP_V4l2_EVENTS 100
+
+void msm_isp_gettimeofday(struct timeval *tv)
+{
+ struct timespec ts;
+
+ ktime_get_ts(&ts);
+ tv->tv_sec = ts.tv_sec;
+ tv->tv_usec = ts.tv_nsec/1000;
+}
+
+int msm_isp_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh,
+ struct v4l2_event_subscription *sub)
+{
+ struct vfe_device *vfe_dev = v4l2_get_subdevdata(sd);
+ int rc = 0;
+ rc = v4l2_event_subscribe(fh, sub, MAX_ISP_V4l2_EVENTS);
+ if (rc == 0) {
+ if (sub->type == V4L2_EVENT_ALL) {
+ int i;
+
+ vfe_dev->axi_data.event_mask = 0;
+ for (i = 0; i < ISP_EVENT_MAX; i++)
+ vfe_dev->axi_data.event_mask |= (1 << i);
+ } else {
+ int event_idx = sub->type - ISP_EVENT_BASE;
+
+ vfe_dev->axi_data.event_mask |= (1 << event_idx);
+ }
+ }
+ return rc;
+}
+
+int msm_isp_unsubscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh,
+ struct v4l2_event_subscription *sub)
+{
+ struct vfe_device *vfe_dev = v4l2_get_subdevdata(sd);
+ int rc = 0;
+
+ rc = v4l2_event_unsubscribe(fh, sub);
+ if (sub->type == V4L2_EVENT_ALL) {
+ vfe_dev->axi_data.event_mask = 0;
+ } else {
+ int event_idx = sub->type - ISP_EVENT_BASE;
+
+ vfe_dev->axi_data.event_mask &= ~(1 << event_idx);
+ }
+ return rc;
+}
+
+int msm_isp_cfg_pix(struct vfe_device *vfe_dev,
+ struct msm_vfe_pix_cfg *pix_cfg)
+{
+ int rc = 0;
+ /*TD Validate config info
+ * should check if all streams are off */
+
+ vfe_dev->axi_data.src_info[VFE_PIX_0].input_mux = pix_cfg->input_mux;
+
+ vfe_dev->hw_info->vfe_ops.core_ops.cfg_camif(vfe_dev, pix_cfg);
+ return rc;
+}
+
+int msm_isp_cfg_input(struct vfe_device *vfe_dev, void *arg)
+{
+ int rc = 0;
+ struct msm_vfe_input_cfg *input_cfg = arg;
+
+ switch (input_cfg->input_src) {
+ case VFE_PIX_0:
+ msm_isp_cfg_pix(vfe_dev, &input_cfg->d.pix_cfg);
+ break;
+ case VFE_RAW_0:
+ case VFE_RAW_1:
+ case VFE_RAW_2:
+ case VFE_SRC_MAX:
+ break;
+ }
+ return rc;
+}
+
+long msm_isp_ioctl(struct v4l2_subdev *sd,
+ unsigned int cmd, void *arg)
+{
+ struct vfe_device *vfe_dev = v4l2_get_subdevdata(sd);
+
+ mutex_lock(&vfe_dev->mutex);
+ ISP_DBG("%s cmd: %d\n", __func__, cmd);
+
+ switch (cmd) {
+ case VIDIOC_MSM_VFE_REG_CFG: {
+ msm_isp_proc_cmd(vfe_dev, arg);
+ break;
+ }
+ case VIDIOC_MSM_ISP_REQUEST_BUF:
+ case VIDIOC_MSM_ISP_ENQUEUE_BUF:
+ case VIDIOC_MSM_ISP_RELEASE_BUF: {
+ msm_isp_proc_buf_cmd(vfe_dev->buf_mgr, cmd, arg);
+ break;
+ }
+ case VIDIOC_MSM_ISP_REQUEST_STREAM:
+ msm_isp_request_axi_stream(vfe_dev, arg);
+ break;
+ case VIDIOC_MSM_ISP_RELEASE_STREAM:
+ msm_isp_release_axi_stream(vfe_dev, arg);
+ break;
+ case VIDIOC_MSM_ISP_CFG_STREAM:
+ msm_isp_cfg_axi_stream(vfe_dev, arg);
+ break;
+ case VIDIOC_MSM_ISP_INPUT_CFG:
+ msm_isp_cfg_input(vfe_dev, arg);
+ break;
+ case VIDIOC_MSM_ISP_SET_SRC_STATE:
+ msm_isp_set_src_state(vfe_dev, arg);
+ break;
+ case VIDIOC_MSM_ISP_REQUEST_STATS_STREAM:
+ msm_isp_request_stats_stream(vfe_dev, arg);
+ break;
+ case VIDIOC_MSM_ISP_RELEASE_STATS_STREAM:
+ msm_isp_release_stats_stream(vfe_dev, arg);
+ break;
+ case VIDIOC_MSM_ISP_CFG_STATS_STREAM:
+ msm_isp_cfg_stats_stream(vfe_dev, arg);
+ break;
+ }
+
+ mutex_unlock(&vfe_dev->mutex);
+ return 0;
+}
+
+static int msm_isp_send_hw_cmd(struct vfe_device *vfe_dev,
+ struct msm_vfe_reg_cfg_cmd *reg_cfg_cmd, uint32_t *cfg_data)
+{
+ switch (reg_cfg_cmd->cmd_type) {
+ case VFE_WRITE: {
+ if (resource_size(vfe_dev->vfe_mem) <
+ (reg_cfg_cmd->reg_offset + reg_cfg_cmd->len)) {
+ pr_err("%s: Invalid length\n", __func__);
+ return -EINVAL;
+ }
+ msm_camera_io_memcpy(vfe_dev->vfe_base +
+ reg_cfg_cmd->reg_offset,
+ cfg_data + reg_cfg_cmd->cmd_data/4, reg_cfg_cmd->len);
+ break;
+ }
+ case VFE_WRITE_MB: {
+ uint32_t *data_ptr = cfg_data + reg_cfg_cmd->cmd_data/4;
+ msm_camera_io_w_mb(*data_ptr, vfe_dev->vfe_base +
+ reg_cfg_cmd->reg_offset);
+ break;
+ }
+ case VFE_WRITE_MASK: {
+ uint32_t temp;
+ temp = msm_camera_io_r(vfe_dev->vfe_base +
+ reg_cfg_cmd->reg_offset);
+ temp |= reg_cfg_cmd->cmd_data;
+ msm_camera_io_w(temp, vfe_dev->vfe_base +
+ reg_cfg_cmd->reg_offset);
+ break;
+ }
+ case VFE_CLEAR_MASK: {
+ uint32_t temp;
+ temp = msm_camera_io_r(vfe_dev->vfe_base +
+ reg_cfg_cmd->reg_offset);
+ temp &= ~reg_cfg_cmd->cmd_data;
+ msm_camera_io_w(temp, vfe_dev->vfe_base +
+ reg_cfg_cmd->reg_offset);
+ break;
+ }
+ case VFE_WRITE_AUTO_INCREMENT: {
+ int i;
+ uint32_t *data_ptr = cfg_data + reg_cfg_cmd->cmd_data/4;
+ for (i = 0; i < reg_cfg_cmd->len/4; i++)
+ msm_camera_io_w(*data_ptr++,
+ vfe_dev->vfe_base + reg_cfg_cmd->reg_offset);
+ break;
+ }
+ case VFE_READ: {
+ int i;
+ uint32_t *data_ptr = cfg_data + reg_cfg_cmd->cmd_data/4;
+ for (i = 0; i < reg_cfg_cmd->len/4; i++)
+ *data_ptr++ = msm_camera_io_r(
+ vfe_dev->vfe_base + reg_cfg_cmd->reg_offset++);
+ break;
+ }
+ }
+ return 0;
+}
+
+int msm_isp_proc_cmd(struct vfe_device *vfe_dev, void *arg)
+{
+ int rc = 0, i;
+ struct msm_vfe_cfg_cmd2 *proc_cmd = arg;
+ struct msm_vfe_reg_cfg_cmd *reg_cfg_cmd;
+ uint32_t *cfg_data;
+
+ reg_cfg_cmd = kzalloc(sizeof(struct msm_vfe_reg_cfg_cmd)*
+ proc_cmd->num_cfg, GFP_KERNEL);
+ if (!reg_cfg_cmd) {
+ pr_err("%s: reg_cfg alloc failed\n", __func__);
+ rc = -ENOMEM;
+ goto reg_cfg_failed;
+ }
+
+ cfg_data = kzalloc(proc_cmd->cmd_len, GFP_KERNEL);
+ if (!cfg_data) {
+ pr_err("%s: cfg_data alloc failed\n", __func__);
+ rc = -ENOMEM;
+ goto cfg_data_failed;
+ }
+
+ if (copy_from_user(reg_cfg_cmd,
+ (void __user *)(proc_cmd->cfg_cmd),
+ sizeof(struct msm_vfe_reg_cfg_cmd) * proc_cmd->num_cfg)) {
+ rc = -EFAULT;
+ goto copy_cmd_failed;
+ }
+
+ if (copy_from_user(cfg_data,
+ (void __user *)(proc_cmd->cfg_data),
+ proc_cmd->cmd_len)) {
+ rc = -EFAULT;
+ goto copy_cmd_failed;
+ }
+
+ for (i = 0; i < proc_cmd->num_cfg; i++)
+ msm_isp_send_hw_cmd(vfe_dev, ®_cfg_cmd[i], cfg_data);
+
+ if (copy_to_user(proc_cmd->cfg_data,
+ cfg_data, proc_cmd->cmd_len)) {
+ rc = -EFAULT;
+ goto copy_cmd_failed;
+ }
+
+copy_cmd_failed:
+ kfree(cfg_data);
+cfg_data_failed:
+ kfree(reg_cfg_cmd);
+reg_cfg_failed:
+ return rc;
+}
+
+int msm_isp_send_event(struct vfe_device *vfe_dev,
+ uint32_t event_type,
+ struct msm_isp_event_data *event_data)
+{
+ struct v4l2_event isp_event;
+ memset(&isp_event, 0, sizeof(struct v4l2_event));
+ isp_event.id = 0;
+ isp_event.type = event_type;
+ memcpy(&isp_event.u.data[0], event_data,
+ sizeof(struct msm_isp_event_data));
+ v4l2_event_queue(vfe_dev->subdev.sd.devnode, &isp_event);
+ return 0;
+}
+
+#define CAL_WORD(width, M, N) ((width * M + N - 1) / N)
+
+int msm_isp_cal_word_per_line(uint32_t output_format,
+ uint32_t pixel_per_line)
+{
+ int val = -1;
+ switch (output_format) {
+ case V4L2_PIX_FMT_SBGGR8:
+ case V4L2_PIX_FMT_SGBRG8:
+ case V4L2_PIX_FMT_SGRBG8:
+ case V4L2_PIX_FMT_SRGGB8:
+ val = CAL_WORD(pixel_per_line, 1, 8);
+ break;
+ case V4L2_PIX_FMT_SBGGR10:
+ case V4L2_PIX_FMT_SGBRG10:
+ case V4L2_PIX_FMT_SGRBG10:
+ case V4L2_PIX_FMT_SRGGB10:
+ val = CAL_WORD(pixel_per_line, 1, 6);
+ break;
+ case V4L2_PIX_FMT_SBGGR12:
+ case V4L2_PIX_FMT_SGBRG12:
+ case V4L2_PIX_FMT_SGRBG12:
+ case V4L2_PIX_FMT_SRGGB12:
+ val = CAL_WORD(pixel_per_line, 1, 5);
+ break;
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV21:
+ val = CAL_WORD(pixel_per_line, 1, 8);
+ break;
+ /*TD: Add more image format*/
+ default:
+ pr_err("%s: Invalid output format\n", __func__);
+ break;
+ }
+ return val;
+}
+
+irqreturn_t msm_isp_process_irq(int irq_num, void *data)
+{
+ unsigned long flags;
+ struct msm_vfe_tasklet_queue_cmd *queue_cmd;
+ struct vfe_device *vfe_dev = (struct vfe_device *) data;
+ uint32_t irq_status0, irq_status1;
+
+ vfe_dev->hw_info->vfe_ops.irq_ops.
+ read_irq_status(vfe_dev, &irq_status0, &irq_status1);
+ if ((irq_status0 == 0) && (irq_status1 == 0)) {
+ ISP_DBG("%s: irq_status0 & 1 are both 0!\n", __func__);
+ return IRQ_HANDLED;
+ }
+
+ spin_lock_irqsave(&vfe_dev->tasklet_lock, flags);
+ queue_cmd = &vfe_dev->tasklet_queue_cmd[vfe_dev->taskletq_idx];
+ if (queue_cmd->cmd_used) {
+ pr_err("%s: Tasklet queue overflow\n", __func__);
+ list_del(&queue_cmd->list);
+ } else {
+ atomic_add(1, &vfe_dev->irq_cnt);
+ }
+ queue_cmd->vfeInterruptStatus0 = irq_status0;
+ queue_cmd->vfeInterruptStatus1 = irq_status1;
+ msm_isp_gettimeofday(&queue_cmd->tv);
+ queue_cmd->cmd_used = 1;
+ vfe_dev->taskletq_idx =
+ (vfe_dev->taskletq_idx + 1) % MSM_VFE_TASKLETQ_SIZE;
+ list_add_tail(&queue_cmd->list, &vfe_dev->tasklet_q);
+ spin_unlock_irqrestore(&vfe_dev->tasklet_lock, flags);
+ tasklet_schedule(&vfe_dev->vfe_tasklet);
+ return IRQ_HANDLED;
+}
+
+void msm_isp_do_tasklet(unsigned long data)
+{
+ unsigned long flags;
+ struct vfe_device *vfe_dev = (struct vfe_device *) data;
+ struct msm_vfe_irq_ops *irq_ops = &vfe_dev->hw_info->vfe_ops.irq_ops;
+ struct msm_vfe_tasklet_queue_cmd *queue_cmd;
+ struct timeval tv;
+ uint32_t irq_status0, irq_status1;
+ while (atomic_read(&vfe_dev->irq_cnt)) {
+ spin_lock_irqsave(&vfe_dev->tasklet_lock, flags);
+ queue_cmd = list_first_entry(&vfe_dev->tasklet_q,
+ struct msm_vfe_tasklet_queue_cmd, list);
+ if (!queue_cmd) {
+ atomic_set(&vfe_dev->irq_cnt, 0);
+ spin_unlock_irqrestore(&vfe_dev->tasklet_lock, flags);
+ return;
+ }
+ atomic_sub(1, &vfe_dev->irq_cnt);
+ list_del(&queue_cmd->list);
+ queue_cmd->cmd_used = 0;
+ irq_status0 = queue_cmd->vfeInterruptStatus0;
+ irq_status1 = queue_cmd->vfeInterruptStatus1;
+ tv = queue_cmd->tv;
+ spin_unlock_irqrestore(&vfe_dev->tasklet_lock, flags);
+ ISP_DBG("%s: status0: 0x%x status1: 0x%x\n",
+ __func__, irq_status0, irq_status1);
+ irq_ops->process_reset_irq(vfe_dev,
+ irq_status0, irq_status1);
+ irq_ops->process_halt_irq(vfe_dev,
+ irq_status0, irq_status1);
+ irq_ops->process_camif_irq(vfe_dev,
+ irq_status0, irq_status1);
+ irq_ops->process_error_irq(vfe_dev,
+ irq_status0, irq_status1);
+ irq_ops->process_axi_irq(vfe_dev,
+ irq_status0, irq_status1, &tv);
+ irq_ops->process_stats_irq(vfe_dev,
+ irq_status0, irq_status1, &tv);
+ irq_ops->process_reg_update(vfe_dev, irq_status0, irq_status1);
+ }
+}
+
+void msm_isp_set_src_state(struct vfe_device *vfe_dev, void *arg)
+{
+ struct msm_vfe_axi_src_state *src_state = arg;
+ vfe_dev->axi_data.src_info[src_state->input_src].active =
+ src_state->src_active;
+}
+
+int msm_isp_open_node(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+ struct vfe_device *vfe_dev = v4l2_get_subdevdata(sd);
+ long rc;
+ ISP_DBG("%s\n", __func__);
+
+ mutex_lock(&vfe_dev->mutex);
+ if (vfe_dev->vfe_open_cnt == 1) {
+ pr_err("VFE already open\n");
+ mutex_unlock(&vfe_dev->mutex);
+ return -ENODEV;
+ }
+
+ if (vfe_dev->hw_info->vfe_ops.core_ops.init_hw(vfe_dev) < 0) {
+ pr_err("%s: init hardware failed\n", __func__);
+ mutex_unlock(&vfe_dev->mutex);
+ return -EBUSY;
+ }
+
+ rc = vfe_dev->hw_info->vfe_ops.core_ops.reset_hw(vfe_dev);
+ if (rc <= 0) {
+ pr_err("%s: reset timeout\n", __func__);
+ mutex_unlock(&vfe_dev->mutex);
+ return -EINVAL;
+ }
+ vfe_dev->hw_info->vfe_ops.core_ops.init_hw_reg(vfe_dev);
+
+ vfe_dev->buf_mgr->ops->attach_ctx(vfe_dev->buf_mgr, vfe_dev->iommu_ctx);
+ vfe_dev->buf_mgr->ops->buf_mgr_init(vfe_dev->buf_mgr, "msm_isp", 14);
+
+ memset(&vfe_dev->axi_data, 0, sizeof(struct msm_vfe_axi_shared_data));
+ vfe_dev->axi_data.hw_info = vfe_dev->hw_info->axi_hw_info;
+
+ ISP_DBG("%s: HW Version: 0x%x\n",
+ __func__, msm_camera_io_r(vfe_dev->vfe_base));
+
+ vfe_dev->vfe_open_cnt++;
+ vfe_dev->taskletq_idx = 0;
+ mutex_unlock(&vfe_dev->mutex);
+ return 0;
+}
+
+int msm_isp_close_node(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+ long rc;
+ struct vfe_device *vfe_dev = v4l2_get_subdevdata(sd);
+ ISP_DBG("%s\n", __func__);
+ mutex_lock(&vfe_dev->mutex);
+ if (vfe_dev->vfe_open_cnt == 0) {
+ pr_err("%s: Invalid close\n", __func__);
+ mutex_unlock(&vfe_dev->mutex);
+ return -ENODEV;
+ }
+
+ rc = vfe_dev->hw_info->vfe_ops.axi_ops.halt(vfe_dev);
+ if (rc <= 0)
+ pr_err("%s: halt timeout\n", __func__);
+
+ vfe_dev->buf_mgr->ops->buf_mgr_deinit(vfe_dev->buf_mgr);
+ vfe_dev->buf_mgr->ops->detach_ctx(vfe_dev->buf_mgr, vfe_dev->iommu_ctx);
+ vfe_dev->hw_info->vfe_ops.core_ops.release_hw(vfe_dev);
+
+ vfe_dev->vfe_open_cnt--;
+ mutex_unlock(&vfe_dev->mutex);
+ return 0;
+}
diff --git a/drivers/media/video/msmb/isp/msm_isp_util.h b/drivers/media/video/msmb/isp/msm_isp_util.h
new file mode 100644
index 0000000..729c8b5
--- /dev/null
+++ b/drivers/media/video/msmb/isp/msm_isp_util.h
@@ -0,0 +1,45 @@
+/* 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 __MSM_ISP_UTIL_H__
+#define __MSM_ISP_UTIL_H__
+
+#include "msm_isp.h"
+
+/* #define CONFIG_MSM_ISP_DBG 1 */
+
+#ifdef CONFIG_MSM_ISP_DBG
+#define ISP_DBG(fmt, args...) printk(fmt, ##args)
+#else
+#define ISP_DBG(fmt, args...) pr_debug(fmt, ##args)
+#endif
+
+void msm_isp_gettimeofday(struct timeval *tv);
+
+int msm_isp_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh,
+ struct v4l2_event_subscription *sub);
+
+int msm_isp_unsubscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh,
+ struct v4l2_event_subscription *sub);
+
+int msm_isp_proc_cmd(struct vfe_device *vfe_dev, void *arg);
+int msm_isp_send_event(struct vfe_device *vfe_dev,
+ uint32_t type, struct msm_isp_event_data *event_data);
+int msm_isp_cal_word_per_line(uint32_t output_format,
+ uint32_t pixel_per_line);
+irqreturn_t msm_isp_process_irq(int irq_num, void *data);
+void msm_isp_set_src_state(struct vfe_device *vfe_dev, void *arg);
+void msm_isp_do_tasklet(unsigned long data);
+
+int msm_isp_open_node(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh);
+int msm_isp_close_node(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh);
+long msm_isp_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg);
+#endif /* __MSM_ISP_UTIL_H__ */
diff --git a/drivers/media/video/msmb/ispif/Makefile b/drivers/media/video/msmb/ispif/Makefile
new file mode 100644
index 0000000..908cc28
--- /dev/null
+++ b/drivers/media/video/msmb/ispif/Makefile
@@ -0,0 +1,3 @@
+ccflags-y += -Idrivers/media/video/msmb
+ccflags-y += -Idrivers/media/video/msmb/sensor/io
+obj-$(CONFIG_MSM_CSID) += msm_ispif.o
diff --git a/drivers/media/video/msmb/ispif/msm_ispif.c b/drivers/media/video/msmb/ispif/msm_ispif.c
new file mode 100644
index 0000000..ffb9263
--- /dev/null
+++ b/drivers/media/video/msmb/ispif/msm_ispif.c
@@ -0,0 +1,954 @@
+/* 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/delay.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/jiffies.h>
+#include <linux/of.h>
+#include <linux/videodev2.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+
+#include "msm_ispif.h"
+#include "msm.h"
+#include "msm_ispif_hwreg.h"
+#include "msm_sd.h"
+#include "msm_camera_io_util.h"
+
+#define V4L2_IDENT_ISPIF 50001
+#define MSM_ISPIF_DRV_NAME "msm_ispif"
+#define DUMP_BUFF_SIZE_128 128
+
+#define ISPIF_INTF_CMD_ENABLE_FRAME_BOUNDARY 0x01
+#define ISPIF_INTF_CMD_DISABLE_FRAME_BOUNDARY 0x00
+#define ISPIF_INTF_CMD_DISABLE_IMMEDIATELY 0x02
+
+#define CONFIG_MSMB_CAMERA_DEBUG
+#undef CDBG
+#ifdef CONFIG_MSMB_CAMERA_DEBUG
+#define CDBG(fmt, args...) pr_debug(fmt, ##args)
+#else
+#define CDBG(fmt, args...) do { } while (0)
+#endif
+
+static void msm_ispif_io_dump(void __iomem *addr, int size)
+{
+ char line_str[DUMP_BUFF_SIZE_128], *p_str;
+ int i;
+ u32 *p = (u32 *) addr;
+ u32 data;
+
+
+ CDBG("%s: %p %d\n", __func__, addr, size);
+ line_str[0] = '\0';
+ p_str = line_str;
+ for (i = 0; i < size/4; i++) {
+ if (i % 4 == 0) {
+ snprintf(p_str, 12, "%08x: ", (u32) p);
+ p_str += 10;
+ }
+ data = readl_relaxed(p++);
+ snprintf(p_str, 12, "%08x ", data);
+ p_str += 9;
+ if ((i + 1) % 4 == 0) {
+ CDBG("%s\n", line_str);
+ line_str[0] = '\0';
+ p_str = line_str;
+ }
+ }
+ if (line_str[0] != '\0')
+ CDBG("%s\n", line_str);
+}
+
+static void msm_ispif_io_dump_reg(struct ispif_device *ispif)
+{
+ int size;
+
+ if (!ispif->enb_dump_reg)
+ return;
+ size = 0x250;
+ msm_ispif_io_dump(ispif->base+0x100, size);
+}
+
+static int msm_ispif_intf_reset(struct ispif_device *ispif,
+ struct msm_ispif_param_data *params)
+{
+
+ int i, rc = 0;
+ enum msm_ispif_intftype intf_type;
+ uint32_t data = (0x1 << STROBED_RST_EN);
+
+ for (i = 0; i < params->num; i++) {
+ intf_type = params->entries[i].intftype;
+ ispif->sof_count[params->vfe_intf].sof_cnt[intf_type] = 0;
+ switch (intf_type) {
+ case PIX0:
+ data |= (0x1 << PIX_0_VFE_RST_STB) |
+ (0x1 << PIX_0_CSID_RST_STB);
+ break;
+ case RDI0:
+ data |= (0x1 << RDI_0_VFE_RST_STB) |
+ (0x1 << RDI_0_CSID_RST_STB);
+ break;
+ case PIX1:
+ data |= (0x1 << PIX_1_VFE_RST_STB) |
+ (0x1 << PIX_1_CSID_RST_STB);
+ break;
+ case RDI1:
+ data |= (0x1 << RDI_1_VFE_RST_STB) |
+ (0x1 << RDI_1_CSID_RST_STB);
+ break;
+ case RDI2:
+ data |= (0x1 << RDI_2_VFE_RST_STB) |
+ (0x1 << RDI_2_CSID_RST_STB);
+ break;
+ default:
+ rc = -EINVAL;
+ break;
+ }
+ }
+ if (data > 0x1) {
+ unsigned long jiffes = msecs_to_jiffies(500);
+ long lrc = 0;
+ if (params->vfe_intf == VFE0)
+ msm_camera_io_w(data, ispif->base + ISPIF_RST_CMD_ADDR);
+ else
+ msm_camera_io_w(data, ispif->base +
+ ISPIF_RST_CMD_1_ADDR);
+ lrc = wait_for_completion_interruptible_timeout(
+ &ispif->reset_complete, jiffes);
+ if (lrc < 0 || !lrc) {
+ pr_err("%s: wait timeout ret = %ld\n", __func__, lrc);
+ rc = -EIO;
+ }
+ }
+ return rc;
+}
+
+static int msm_ispif_reset(struct ispif_device *ispif)
+{
+ int rc = 0;
+ unsigned long jiffes = msecs_to_jiffies(500);
+ long lrc = 0;
+
+ memset(ispif->sof_count, 0, sizeof(ispif->sof_count));
+ msm_camera_io_w(ISPIF_RST_CMD_MASK, ispif->base + ISPIF_RST_CMD_ADDR);
+ if (ispif->csid_version == CSID_VERSION_V3)
+ msm_camera_io_w_mb(ISPIF_RST_CMD_1_MASK, ispif->base +
+ ISPIF_RST_CMD_1_ADDR);
+ CDBG("%s: Sending reset\n", __func__);
+ lrc = wait_for_completion_interruptible_timeout(
+ &ispif->reset_complete, jiffes);
+ if (lrc < 0 || !lrc) {
+ pr_err("%s: wait timeout ret = %ld\n", __func__, lrc);
+ rc = -EIO;
+ }
+ CDBG("%s: reset returned\n", __func__);
+ return rc;
+}
+
+static int msm_ispif_subdev_g_chip_ident(struct v4l2_subdev *sd,
+ struct v4l2_dbg_chip_ident *chip)
+{
+ BUG_ON(!chip);
+ chip->ident = V4L2_IDENT_ISPIF;
+ chip->revision = 0;
+ return 0;
+}
+
+static void msm_ispif_sel_csid_core(struct ispif_device *ispif,
+ uint8_t intftype, uint8_t csid, uint8_t vfe_intf)
+{
+ int rc = 0;
+ uint32_t data = 0;
+
+ if (ispif->csid_version <= CSID_VERSION_V2) {
+ if (ispif->ispif_clk[intftype] == NULL) {
+ CDBG("%s: ispif NULL clk\n", __func__);
+ return;
+ }
+ rc = clk_set_rate(ispif->ispif_clk[intftype], csid);
+ if (rc < 0)
+ pr_err("%s: clk_set_rate failed %d\n", __func__, rc);
+ return;
+ }
+ data = msm_camera_io_r(ispif->base + ISPIF_INPUT_SEL_ADDR +
+ (0x200 * vfe_intf));
+ switch (intftype) {
+ case PIX0:
+ data &= ~(0x3); /* clear old setting */
+ data |= csid; /* add new setting */
+ break;
+ case RDI0:
+ data &= ~(0x3 << 4); /* clear old setting */
+ data |= (csid << 4); /* add new setting */
+ break;
+ case PIX1:
+ data &= ~(0x3 << 8); /* clear old setting */
+ data |= (csid << 8); /* add new setting */
+ break;
+ case RDI1:
+ data &= ~(0x3 << 12); /* clear old setting */
+ data |= (csid << 12); /* add new setting */
+ break;
+ case RDI2:
+ data &= ~(0x3 << 20); /* clear old setting */
+ data |= (csid << 20); /* add new setting */
+ break;
+ }
+ if (data) {
+ msm_camera_io_w_mb(data, ispif->base + ISPIF_INPUT_SEL_ADDR +
+ (0x200 * vfe_intf));
+ }
+}
+
+static void msm_ispif_enable_intf_cids(struct ispif_device *ispif,
+ uint8_t intftype, uint16_t cid_mask,
+ uint8_t vfe_intf, uint8_t enable)
+{
+ uint32_t data = 0;
+
+ switch (intftype) {
+ case PIX0:
+ data = msm_camera_io_r(ispif->base +
+ ISPIF_PIX_0_INTF_CID_MASK_ADDR + (0x200 * vfe_intf));
+ if (enable)
+ data |= cid_mask; /* add new config */
+ else
+ data &= ~cid_mask; /* remove CID bit */
+ msm_camera_io_w_mb(data, ispif->base +
+ ISPIF_PIX_0_INTF_CID_MASK_ADDR + (0x200 * vfe_intf));
+ break;
+ case RDI0:
+ data = msm_camera_io_r(ispif->base +
+ ISPIF_RDI_0_INTF_CID_MASK_ADDR + (0x200 * vfe_intf));
+ if (enable)
+ data |= cid_mask; /* add new config */
+ else
+ data &= ~cid_mask; /* remove CID bit */
+ msm_camera_io_w_mb(data, ispif->base +
+ ISPIF_RDI_0_INTF_CID_MASK_ADDR + (0x200 * vfe_intf));
+ break;
+ case PIX1:
+ data = msm_camera_io_r(ispif->base +
+ ISPIF_PIX_1_INTF_CID_MASK_ADDR + (0x200 * vfe_intf));
+ if (enable)
+ data |= cid_mask; /* add new config */
+ else
+ data &= ~cid_mask; /* remove CID bit */
+ msm_camera_io_w_mb(data, ispif->base +
+ ISPIF_PIX_1_INTF_CID_MASK_ADDR + (0x200 * vfe_intf));
+ break;
+ case RDI1:
+ data = msm_camera_io_r(ispif->base +
+ ISPIF_RDI_1_INTF_CID_MASK_ADDR + (0x200 * vfe_intf));
+ if (enable)
+ data |= cid_mask; /* add new config */
+ else
+ data &= ~cid_mask; /* remove CID bit */
+ msm_camera_io_w_mb(data, ispif->base +
+ ISPIF_RDI_1_INTF_CID_MASK_ADDR + (0x200 * vfe_intf));
+ break;
+ case RDI2:
+ data = msm_camera_io_r(ispif->base +
+ ISPIF_RDI_2_INTF_CID_MASK_ADDR + (0x200 * vfe_intf));
+ if (enable)
+ data |= cid_mask; /* add new config */
+ else
+ data &= ~cid_mask; /* remove CID bit */
+ msm_camera_io_w_mb(data, ispif->base +
+ ISPIF_RDI_2_INTF_CID_MASK_ADDR + (0x200 * vfe_intf));
+ break;
+ }
+}
+
+static int32_t msm_ispif_validate_intf_status(struct ispif_device *ispif,
+ uint8_t intftype, uint8_t vfe_intf)
+{
+ int32_t rc = 0;
+ uint32_t data = 0;
+ switch (intftype) {
+ case PIX0:
+ data = msm_camera_io_r(ispif->base +
+ ISPIF_PIX_0_STATUS_ADDR + (0x200 * vfe_intf));
+ break;
+ case RDI0:
+ data = msm_camera_io_r(ispif->base +
+ ISPIF_RDI_0_STATUS_ADDR + (0x200 * vfe_intf));
+ break;
+ case PIX1:
+ data = msm_camera_io_r(ispif->base +
+ ISPIF_PIX_1_STATUS_ADDR + (0x200 * vfe_intf));
+ break;
+ case RDI1:
+ data = msm_camera_io_r(ispif->base +
+ ISPIF_RDI_1_STATUS_ADDR + (0x200 * vfe_intf));
+ break;
+ case RDI2:
+ data = msm_camera_io_r(ispif->base +
+ ISPIF_RDI_2_STATUS_ADDR + (0x200 * vfe_intf));
+ break;
+ }
+ if ((data & 0xf) != 0xf)
+ rc = -EBUSY;
+ return rc;
+}
+
+static uint16_t msm_ispif_get_cids_mask_from_cfg(
+ struct msm_ispif_params_entry *entry)
+{
+ int i;
+ uint16_t cids_mask = 0;
+
+ for (i = 0; i < entry->num_cids; i++)
+ cids_mask |= (1 << entry->cids[i]);
+ return cids_mask;
+}
+
+static int msm_ispif_config(struct ispif_device *ispif,
+ struct msm_ispif_param_data *params)
+{
+ int rc = 0, i = 0;
+ enum msm_ispif_intftype intftype;
+ enum msm_ispif_vfe_intf vfe_intf = params->vfe_intf;
+ uint16_t cid_mask;
+
+ msm_camera_io_w(0x00000000, ispif->base + ISPIF_IRQ_MASK_ADDR);
+ msm_camera_io_w(0x00000000, ispif->base + ISPIF_IRQ_MASK_1_ADDR);
+ msm_camera_io_w_mb(0x00000000, ispif->base + ISPIF_IRQ_MASK_2_ADDR);
+ for (i = 0; i < params->num; i++) {
+ intftype = params->entries[i].intftype;
+ vfe_intf = params->vfe_intf;
+ CDBG("%s intftype %x, vfe_intf %d, csid %d\n", __func__,
+ intftype, vfe_intf, params->entries[i].csid);
+ if ((intftype >= INTF_MAX) ||
+ (ispif->csid_version <= CSID_VERSION_V2 &&
+ vfe_intf > VFE0) ||
+ (ispif->csid_version == CSID_VERSION_V3 &&
+ vfe_intf >= VFE_MAX)) {
+ pr_err("%s: VFEID %d and CSID version %d mismatch\n",
+ __func__, vfe_intf, ispif->csid_version);
+ return -EINVAL;
+ }
+ rc = msm_ispif_validate_intf_status(ispif, intftype, vfe_intf);
+ if (rc < 0) {
+ pr_err("%s:validate_intf_status failed, rc = %d\n",
+ __func__, rc);
+ return rc;
+ }
+ msm_ispif_sel_csid_core(ispif, intftype,
+ params->entries[i].csid, vfe_intf);
+ cid_mask = msm_ispif_get_cids_mask_from_cfg(
+ ¶ms->entries[i]);
+ msm_ispif_enable_intf_cids(ispif, intftype,
+ cid_mask, vfe_intf, 1);
+ }
+
+ msm_camera_io_w(ISPIF_IRQ_STATUS_MASK, ispif->base +
+ ISPIF_IRQ_MASK_ADDR);
+
+ msm_camera_io_w(ISPIF_IRQ_STATUS_MASK, ispif->base +
+ ISPIF_IRQ_CLEAR_ADDR);
+
+ msm_camera_io_w(ISPIF_IRQ_STATUS_1_MASK, ispif->base +
+ ISPIF_IRQ_MASK_1_ADDR);
+
+ msm_camera_io_w(ISPIF_IRQ_STATUS_1_MASK, ispif->base +
+ ISPIF_IRQ_CLEAR_1_ADDR);
+
+ msm_camera_io_w(ISPIF_IRQ_STATUS_2_MASK, ispif->base +
+ ISPIF_IRQ_MASK_2_ADDR);
+
+ msm_camera_io_w(ISPIF_IRQ_STATUS_2_MASK, ispif->base +
+ ISPIF_IRQ_CLEAR_2_ADDR);
+
+ msm_camera_io_w_mb(ISPIF_IRQ_GLOBAL_CLEAR_CMD, ispif->base +
+ ISPIF_IRQ_GLOBAL_CLEAR_CMD_ADDR);
+ return rc;
+}
+
+static void msm_ispif_intf_cmd(struct ispif_device *ispif,
+ uint32_t cmd_bits,
+ struct msm_ispif_param_data *params)
+{
+ uint8_t vc = 0;
+ int i, k;
+ enum msm_ispif_intftype intf_type;
+ enum msm_ispif_cid cid;
+ enum msm_ispif_vfe_intf vfe_intf = params->vfe_intf;
+
+ for (i = 0; i < params->num; i++) {
+ intf_type = params->entries[i].intftype;
+ for (k = 0; k < params->entries[i].num_cids; k++) {
+ cid = params->entries[i].cids[k];
+ vc = cid % 4;
+ if (intf_type == RDI2) {
+ /* zero out two bits */
+ ispif->applied_intf_cmd[vfe_intf].intf_cmd1 &=
+ ~(0x3 << (vc * 2 + 8));
+ ispif->applied_intf_cmd[vfe_intf].intf_cmd1 |=
+ (cmd_bits << (vc * 2 + 8)); /* set cmd bits */
+ } else {
+ /* zero 2 bits */
+ ispif->applied_intf_cmd[vfe_intf].intf_cmd &=
+ ~(0x3 << (vc * 2 + vfe_intf * 8));
+ /* set cmd bits */
+ ispif->applied_intf_cmd[vfe_intf].intf_cmd |=
+ (cmd_bits << (vc * 2 + vfe_intf * 8));
+ }
+ }
+ }
+ /* cmd for PIX0, PIX1, RDI0, RDI1 */
+ if (ispif->applied_intf_cmd[vfe_intf].intf_cmd != 0xFFFFFFFF) {
+ msm_camera_io_w_mb(ispif->applied_intf_cmd[vfe_intf].intf_cmd,
+ ispif->base + ISPIF_INTF_CMD_ADDR +
+ (0x200 * vfe_intf));
+ }
+ /* cmd for RDI2 */
+ if (ispif->applied_intf_cmd[vfe_intf].intf_cmd1 != 0xFFFFFFFF)
+ msm_camera_io_w_mb(ispif->applied_intf_cmd[vfe_intf].intf_cmd1,
+ ispif->base + ISPIF_INTF_CMD_1_ADDR +
+ (0x200 * vfe_intf));
+}
+
+static int msm_ispif_stop_immediately(struct ispif_device *ispif,
+ struct msm_ispif_param_data *params)
+{
+ int i, rc = 0;
+ uint16_t cid_mask = 0;
+
+ msm_ispif_intf_cmd(ispif, ISPIF_INTF_CMD_DISABLE_IMMEDIATELY, params);
+
+ /* after stop the interface we need to unmask the CID enable bits */
+ for (i = 0; i < params->num; i++) {
+ cid_mask = msm_ispif_get_cids_mask_from_cfg(
+ ¶ms->entries[i]);
+ msm_ispif_enable_intf_cids(ispif, params->entries[i].intftype,
+ cid_mask, params->vfe_intf, 0);
+ }
+ return rc;
+}
+
+static int msm_ispif_start_frame_boundary(struct ispif_device *ispif,
+ struct msm_ispif_param_data *params)
+{
+ int rc = 0;
+
+ rc = msm_ispif_intf_reset(ispif, params);
+ msm_ispif_intf_cmd(ispif, ISPIF_INTF_CMD_ENABLE_FRAME_BOUNDARY, params);
+ return rc;
+}
+
+static int msm_ispif_stop_frame_boundary(struct ispif_device *ispif,
+ struct msm_ispif_param_data *params)
+{
+ int i, rc = 0;
+ uint16_t cid_mask = 0;
+
+ msm_ispif_intf_cmd(ispif,
+ ISPIF_INTF_CMD_DISABLE_FRAME_BOUNDARY, params);
+ for (i = 0; i < params->num; i++) {
+ cid_mask =
+ msm_ispif_get_cids_mask_from_cfg(
+ ¶ms->entries[i]);
+ switch (params->entries[i].intftype) {
+ case PIX0:
+ while ((msm_camera_io_r(ispif->base +
+ ISPIF_PIX_0_STATUS_ADDR +
+ (0x200 * params->vfe_intf)) & 0xf) != 0xf) {
+ CDBG("Wait for pix0 Idle\n");
+ }
+ break;
+ case RDI0:
+ while ((msm_camera_io_r(ispif->base +
+ ISPIF_RDI_0_STATUS_ADDR +
+ (0x200 * params->vfe_intf)) & 0xf) != 0xf) {
+ CDBG("Wait for rdi0 Idle\n");
+ }
+ break;
+ case PIX1:
+ while ((msm_camera_io_r(ispif->base +
+ ISPIF_PIX_1_STATUS_ADDR +
+ (0x200 * params->vfe_intf)) & 0xf) != 0xf) {
+ CDBG("Wait for pix1 Idle\n");
+ }
+ break;
+ case RDI1:
+ while ((msm_camera_io_r(ispif->base +
+ ISPIF_RDI_1_STATUS_ADDR +
+ (0x200 * params->vfe_intf)) & 0xf) != 0xf) {
+ CDBG("Wait for rdi1 Idle\n");
+ }
+ break;
+ case RDI2:
+ while ((msm_camera_io_r(ispif->base +
+ ISPIF_RDI_2_STATUS_ADDR +
+ (0x200 * params->vfe_intf)) & 0xf) != 0xf) {
+ CDBG("Wait for rdi2 Idle\n");
+ }
+ break;
+ default:
+ break;
+ }
+ /* disable CIDs in CID_MASK register */
+ msm_ispif_enable_intf_cids(ispif, params->entries[i].intftype,
+ cid_mask, params->vfe_intf, 0);
+ }
+ return rc;
+}
+
+static void ispif_process_irq(struct ispif_device *ispif,
+ struct ispif_irq_status *out, enum msm_ispif_vfe_intf vfe_id)
+{
+ if (out[vfe_id].ispifIrqStatus0 &
+ ISPIF_IRQ_STATUS_PIX_SOF_MASK) {
+ ispif->sof_count[vfe_id].sof_cnt[PIX0]++;
+ }
+ if (out[vfe_id].ispifIrqStatus0 &
+ ISPIF_IRQ_STATUS_RDI0_SOF_MASK) {
+ ispif->sof_count[vfe_id].sof_cnt[RDI0]++;
+ }
+ if (out[vfe_id].ispifIrqStatus1 &
+ ISPIF_IRQ_STATUS_RDI1_SOF_MASK) {
+ ispif->sof_count[vfe_id].sof_cnt[RDI1]++;
+ }
+ if (out[vfe_id].ispifIrqStatus2 &
+ ISPIF_IRQ_STATUS_RDI2_SOF_MASK) {
+ ispif->sof_count[vfe_id].sof_cnt[RDI2]++;
+ }
+ return;
+}
+
+static inline void msm_ispif_read_irq_status(struct ispif_irq_status *out,
+ void *data)
+{
+ struct ispif_device *ispif = (struct ispif_device *)data;
+
+ out[VFE0].ispifIrqStatus0 = msm_camera_io_r(ispif->base +
+ ISPIF_IRQ_STATUS_ADDR);
+ out[VFE0].ispifIrqStatus1 = msm_camera_io_r(ispif->base +
+ ISPIF_IRQ_STATUS_1_ADDR);
+ out[VFE0].ispifIrqStatus2 = msm_camera_io_r(ispif->base +
+ ISPIF_IRQ_STATUS_2_ADDR);
+ msm_camera_io_w(out[VFE0].ispifIrqStatus0,
+ ispif->base + ISPIF_IRQ_CLEAR_ADDR);
+ msm_camera_io_w(out[VFE0].ispifIrqStatus1,
+ ispif->base + ISPIF_IRQ_CLEAR_1_ADDR);
+ msm_camera_io_w_mb(out[VFE0].ispifIrqStatus2,
+ ispif->base + ISPIF_IRQ_CLEAR_2_ADDR);
+
+ if (out[VFE0].ispifIrqStatus0 & ISPIF_IRQ_STATUS_MASK) {
+ if (out[VFE0].ispifIrqStatus0 & (0x1 <<
+ RESET_DONE_IRQ))
+ complete(&ispif->reset_complete);
+ if (out[VFE0].ispifIrqStatus0 & (0x1 <<
+ PIX_INTF_0_OVERFLOW_IRQ))
+ pr_err("%s: VFE0 pix0 overflow.\n", __func__);
+ if (out[VFE0].ispifIrqStatus0 & (0x1 <<
+ RAW_INTF_0_OVERFLOW_IRQ))
+ pr_err("%s: VFE0 rdi0 overflow.\n", __func__);
+ if (out[VFE0].ispifIrqStatus1 & (0x1 <<
+ RAW_INTF_1_OVERFLOW_IRQ))
+ pr_err("%s: VFE0 rdi1 overflow.\n", __func__);
+ if (out[VFE0].ispifIrqStatus2 & (0x1 <<
+ RAW_INTF_2_OVERFLOW_IRQ))
+ pr_err("%s: VFE0 rdi2 overflow.\n", __func__);
+ if ((out[VFE0].ispifIrqStatus0 &
+ ISPIF_IRQ_STATUS_SOF_MASK) ||
+ (out[VFE0].ispifIrqStatus1 &
+ ISPIF_IRQ_STATUS_SOF_MASK) ||
+ (out[VFE0].ispifIrqStatus2 &
+ ISPIF_IRQ_STATUS_RDI2_SOF_MASK))
+ ispif_process_irq(ispif, out, VFE0);
+ }
+ if (ispif->csid_version == CSID_VERSION_V3) {
+ out[VFE1].ispifIrqStatus0 = msm_camera_io_r(ispif->base +
+ ISPIF_IRQ_STATUS_ADDR + 0x200);
+ msm_camera_io_w(out[VFE1].ispifIrqStatus0,
+ ispif->base + ISPIF_IRQ_CLEAR_ADDR + 0x200);
+ out[VFE1].ispifIrqStatus1 = msm_camera_io_r(ispif->base +
+ ISPIF_IRQ_STATUS_1_ADDR + 0x200);
+ msm_camera_io_w(out[VFE1].ispifIrqStatus1,
+ ispif->base + ISPIF_IRQ_CLEAR_1_ADDR + 0x200);
+ out[VFE1].ispifIrqStatus2 = msm_camera_io_r(ispif->base +
+ ISPIF_IRQ_STATUS_2_ADDR + 0x200);
+ msm_camera_io_w_mb(out[VFE1].ispifIrqStatus2,
+ ispif->base + ISPIF_IRQ_CLEAR_2_ADDR + 0x200);
+ if (out[VFE1].ispifIrqStatus0 & (0x1 <<
+ PIX_INTF_0_OVERFLOW_IRQ))
+ pr_err("%s: VFE1 pix0 overflow.\n", __func__);
+ if (out[VFE1].ispifIrqStatus0 & (0x1 <<
+ RAW_INTF_0_OVERFLOW_IRQ))
+ pr_err("%s: VFE1 rdi0 overflow.\n", __func__);
+ if (out[VFE1].ispifIrqStatus1 & (0x1 <<
+ RAW_INTF_1_OVERFLOW_IRQ))
+ pr_err("%s: VFE1 rdi1 overflow.\n", __func__);
+ if (out[VFE1].ispifIrqStatus2 & (0x1 <<
+ RAW_INTF_2_OVERFLOW_IRQ))
+ pr_err("%s: VFE1 rdi2 overflow.\n", __func__);
+ if ((out[VFE1].ispifIrqStatus0 & ISPIF_IRQ_STATUS_SOF_MASK) ||
+ (out[VFE1].ispifIrqStatus1 &
+ ISPIF_IRQ_STATUS_SOF_MASK) ||
+ (out[VFE1].ispifIrqStatus2 &
+ ISPIF_IRQ_STATUS_RDI2_SOF_MASK))
+ ispif_process_irq(ispif, out, VFE1);
+ }
+ msm_camera_io_w_mb(ISPIF_IRQ_GLOBAL_CLEAR_CMD, ispif->base +
+ ISPIF_IRQ_GLOBAL_CLEAR_CMD_ADDR);
+}
+
+static irqreturn_t msm_io_ispif_irq(int irq_num, void *data)
+{
+ struct ispif_irq_status irq[VFE_MAX];
+
+ msm_ispif_read_irq_status(irq, data);
+ return IRQ_HANDLED;
+}
+
+static struct msm_cam_clk_info ispif_8960_clk_info[] = {
+ {"csi_pix_clk", 0},
+ {"csi_rdi_clk", 0},
+ {"csi_pix1_clk", 0},
+ {"csi_rdi1_clk", 0},
+ {"csi_rdi2_clk", 0},
+};
+static struct msm_cam_clk_info ispif_8974_clk_info[] = {
+ {"camss_vfe_vfe_clk", -1},
+ {"camss_csi_vfe_clk", -1},
+ {"camss_vfe_vfe_clk1", -1},
+ {"camss_csi_vfe_clk1", -1},
+};
+
+static int msm_ispif_init(struct ispif_device *ispif,
+ uint32_t csid_version)
+{
+ int rc = 0;
+
+ if (ispif->ispif_state == ISPIF_POWER_UP) {
+ CDBG("%s: ispif already initted state = %d\n", __func__,
+ ispif->ispif_state);
+ rc = -EAGAIN;
+ return rc;
+ }
+
+ /* can we set to zero? */
+ ispif->applied_intf_cmd[VFE0].intf_cmd = 0xFFFFFFFF;
+ ispif->applied_intf_cmd[VFE0].intf_cmd1 = 0xFFFFFFFF;
+ ispif->applied_intf_cmd[VFE1].intf_cmd = 0xFFFFFFFF;
+ ispif->applied_intf_cmd[VFE1].intf_cmd1 = 0xFFFFFFFF;
+ memset(ispif->sof_count, 0, sizeof(ispif->sof_count));
+
+ ispif->csid_version = csid_version;
+ if (ispif->csid_version < CSID_VERSION_V2) {
+ rc = msm_cam_clk_enable(&ispif->pdev->dev, ispif_8960_clk_info,
+ ispif->ispif_clk, 2, 1);
+ if (rc < 0) {
+ pr_err("%s: cannot enable clock, error = %d\n",
+ __func__, rc);
+ goto end;
+ }
+ } else if (ispif->csid_version == CSID_VERSION_V2) {
+ rc = msm_cam_clk_enable(&ispif->pdev->dev, ispif_8960_clk_info,
+ ispif->ispif_clk, ARRAY_SIZE(ispif_8960_clk_info), 1);
+ if (rc < 0) {
+ pr_err("%s: cannot enable clock, error = %d\n",
+ __func__, rc);
+ goto end;
+ }
+ } else {
+ rc = msm_cam_clk_enable(&ispif->pdev->dev, ispif_8974_clk_info,
+ ispif->ispif_clk, ARRAY_SIZE(ispif_8974_clk_info), 1);
+ if (rc < 0) {
+ pr_err("%s: cannot enable clock, error = %d\n",
+ __func__, rc);
+ goto end;
+ }
+ }
+ ispif->base = ioremap(ispif->mem->start,
+ resource_size(ispif->mem));
+ if (!ispif->base) {
+ rc = -ENOMEM;
+ pr_err("%s: nomem\n", __func__);
+ goto error_clk;
+ }
+ rc = request_irq(ispif->irq->start, msm_io_ispif_irq,
+ IRQF_TRIGGER_RISING, "ispif", ispif);
+ init_completion(&ispif->reset_complete);
+ if (rc < 0) {
+ pr_err("%s: request_irq error = %d\n", __func__, rc);
+ goto error_irq;
+ }
+ rc = msm_ispif_reset(ispif);
+ if (rc == 0) {
+ ispif->ispif_state = ISPIF_POWER_UP;
+ CDBG("%s: power up done\n", __func__);
+ goto end;
+ }
+ free_irq(ispif->irq->start, ispif);
+error_irq:
+ iounmap(ispif->base);
+error_clk:
+ if (ispif->csid_version < CSID_VERSION_V2) {
+ msm_cam_clk_enable(&ispif->pdev->dev, ispif_8960_clk_info,
+ ispif->ispif_clk, 2, 0);
+ } else if (ispif->csid_version == CSID_VERSION_V2) {
+ msm_cam_clk_enable(&ispif->pdev->dev, ispif_8960_clk_info,
+ ispif->ispif_clk, ARRAY_SIZE(ispif_8960_clk_info), 0);
+ }
+end:
+ return rc;
+}
+
+static void msm_ispif_release(struct ispif_device *ispif)
+{
+ if (ispif->ispif_state != ISPIF_POWER_UP) {
+ pr_err("%s: ispif invalid state %d\n", __func__,
+ ispif->ispif_state);
+ return;
+ }
+ /* make sure no streaming going on */
+ msm_ispif_reset(ispif);
+ free_irq(ispif->irq->start, ispif);
+ iounmap(ispif->base);
+ if (ispif->csid_version < CSID_VERSION_V2) {
+ msm_cam_clk_enable(&ispif->pdev->dev, ispif_8960_clk_info,
+ ispif->ispif_clk, 2, 0);
+ } else if (ispif->csid_version == CSID_VERSION_V2) {
+ msm_cam_clk_enable(&ispif->pdev->dev, ispif_8960_clk_info,
+ ispif->ispif_clk, ARRAY_SIZE(ispif_8960_clk_info), 0);
+ }
+ ispif->ispif_state = ISPIF_POWER_DOWN;
+}
+
+static int msm_ispif_clk_enable(struct ispif_device *ispif,
+ uint32_t csid_version, int enable)
+{
+ int rc = 0;
+
+ if (csid_version != CSID_VERSION_V3)
+ goto end;
+ rc = msm_cam_clk_enable(&ispif->pdev->dev, ispif_8974_clk_info,
+ ispif->ispif_clk, ARRAY_SIZE(ispif_8974_clk_info), enable);
+ if (rc < 0)
+ pr_err("%s: cannot enable clock, error = %d\n", __func__, rc);
+end:
+ return rc;
+}
+
+static long msm_ispif_cmd(struct v4l2_subdev *sd, void *arg)
+{
+ long rc = 0;
+ struct ispif_cfg_data *pcdata = (struct ispif_cfg_data *)arg;
+ struct ispif_device *ispif =
+ (struct ispif_device *)v4l2_get_subdevdata(sd);
+ mutex_lock(&ispif->mutex);
+ switch (pcdata->cfg_type) {
+ case ISPIF_CLK_ENABLE:
+ rc = msm_ispif_clk_enable(ispif, pcdata->csid_version, 1);
+ break;
+ case ISPIF_CLK_DISABLE:
+ rc = msm_ispif_clk_enable(ispif, pcdata->csid_version, 0);
+ break;
+ case ISPIF_ENABLE_REG_DUMP:
+ ispif->enb_dump_reg = pcdata->reg_dump; /* save dump config */
+ break;
+ case ISPIF_INIT:
+ /* need to move back to CDBG */
+ rc = msm_ispif_init(ispif, pcdata->csid_version);
+ msm_ispif_io_dump_reg(ispif);
+ break;
+ case ISPIF_CFG:
+ rc = msm_ispif_config(ispif, &pcdata->params);
+ msm_ispif_io_dump_reg(ispif);
+ break;
+ case ISPIF_START_FRAME_BOUNDARY:
+ rc = msm_ispif_start_frame_boundary(ispif, &pcdata->params);
+ msm_ispif_io_dump_reg(ispif);
+ break;
+ case ISPIF_STOP_FRAME_BOUNDARY:
+ rc = msm_ispif_stop_frame_boundary(ispif, &pcdata->params);
+ msm_ispif_io_dump_reg(ispif);
+ break;
+ case ISPIF_STOP_IMMEDIATELY:
+ rc = msm_ispif_stop_immediately(ispif, &pcdata->params);
+ msm_ispif_io_dump_reg(ispif);
+ break;
+ case ISPIF_RELEASE:
+ msm_ispif_release(ispif);
+ break;
+ default:
+ break;
+ }
+ mutex_unlock(&ispif->mutex);
+ return rc;
+}
+
+static long msm_ispif_subdev_ioctl(struct v4l2_subdev *sd,
+ unsigned int cmd, void *arg)
+{
+ switch (cmd) {
+ case VIDIOC_MSM_ISPIF_CFG:
+ return msm_ispif_cmd(sd, arg);
+ default:
+ pr_err("%s: invalid cmd received\n", __func__);
+ return -ENOIOCTLCMD;
+ }
+}
+
+static int ispif_open_node(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+ struct ispif_device *ispif = v4l2_get_subdevdata(sd);
+ int rc = 0;
+
+ mutex_lock(&ispif->mutex);
+ if (ispif->open_cnt > 0) {
+ CDBG("%s: dev already open\n", __func__);
+ goto end;
+ }
+ /* mem remap is done in init when the clock is on */
+ ispif->open_cnt++;
+end:
+ mutex_unlock(&ispif->mutex);
+ return rc;
+}
+
+static int ispif_close_node(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+ struct ispif_device *ispif = v4l2_get_subdevdata(sd);
+ int rc = 0;
+
+ CDBG("%s\n", __func__);
+ mutex_lock(&ispif->mutex);
+ if (ispif->open_cnt == 0) {
+ pr_err("Invalid close\n");
+ rc = -ENODEV;
+ goto end;
+ }
+ ispif->open_cnt--;
+ if (ispif->open_cnt == 0)
+ msm_ispif_release(ispif);
+end:
+ mutex_unlock(&ispif->mutex);
+ return rc;
+}
+
+static struct v4l2_subdev_core_ops msm_ispif_subdev_core_ops = {
+ .g_chip_ident = &msm_ispif_subdev_g_chip_ident,
+ .ioctl = &msm_ispif_subdev_ioctl,
+};
+
+static const struct v4l2_subdev_ops msm_ispif_subdev_ops = {
+ .core = &msm_ispif_subdev_core_ops,
+};
+
+static const struct v4l2_subdev_internal_ops msm_ispif_internal_ops = {
+ .open = ispif_open_node,
+ .close = ispif_close_node,
+};
+static int __devinit ispif_probe(struct platform_device *pdev)
+{
+ int rc = 0;
+ struct ispif_device *ispif;
+
+ CDBG("%s\n", __func__);
+ ispif = kzalloc(sizeof(struct ispif_device), GFP_KERNEL);
+ if (!ispif) {
+ pr_err("%s: no enough memory\n", __func__);
+ return -ENOMEM;
+ }
+
+ v4l2_subdev_init(&ispif->msm_sd.sd, &msm_ispif_subdev_ops);
+ ispif->msm_sd.sd.internal_ops = &msm_ispif_internal_ops;
+ ispif->msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+ snprintf(ispif->msm_sd.sd.name,
+ ARRAY_SIZE(ispif->msm_sd.sd.name), "msm_ispif");
+ v4l2_set_subdevdata(&ispif->msm_sd.sd, ispif);
+ platform_set_drvdata(pdev, &ispif->msm_sd.sd);
+ mutex_init(&ispif->mutex);
+ ispif->pdev = pdev;
+ media_entity_init(&ispif->msm_sd.sd.entity, 0, NULL, 0);
+ ispif->msm_sd.sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV;
+ ispif->msm_sd.sd.entity.group_id = MSM_CAMERA_SUBDEV_ISPIF;
+ ispif->msm_sd.sd.entity.name = pdev->name;
+ rc = msm_sd_register(&ispif->msm_sd);
+ if (rc != 0) {
+ pr_err("%s: msm_sd_register error = %d\n", __func__, rc);
+ goto error;
+ }
+ if (pdev->dev.of_node)
+ of_property_read_u32((&pdev->dev)->of_node,
+ "cell-index", &pdev->id);
+
+ ispif->mem = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM, "ispif");
+ if (!ispif->mem) {
+ pr_err("%s: no mem resource?\n", __func__);
+ rc = -ENODEV;
+ goto error;
+ }
+ ispif->irq = platform_get_resource_byname(pdev,
+ IORESOURCE_IRQ, "ispif");
+ if (!ispif->irq) {
+ pr_err("%s: no irq resource?\n", __func__);
+ rc = -ENODEV;
+ goto error;
+ }
+ ispif->io = request_mem_region(ispif->mem->start,
+ resource_size(ispif->mem), pdev->name);
+ if (!ispif->io) {
+ pr_err("%s: no valid mem region\n", __func__);
+ rc = -EBUSY;
+ goto error;
+ }
+ ispif->pdev = pdev;
+ ispif->ispif_state = ISPIF_POWER_DOWN;
+ ispif->open_cnt = 0;
+ return 0;
+
+error:
+ mutex_destroy(&ispif->mutex);
+ kfree(ispif);
+ return rc;
+}
+
+static const struct of_device_id msm_ispif_dt_match[] = {
+ {.compatible = "qcom,ispif"},
+};
+
+MODULE_DEVICE_TABLE(of, msm_ispif_dt_match);
+
+static struct platform_driver ispif_driver = {
+ .probe = ispif_probe,
+ .driver = {
+ .name = MSM_ISPIF_DRV_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = msm_ispif_dt_match,
+ },
+};
+
+static int __init msm_ispif_init_module(void)
+{
+ return platform_driver_register(&ispif_driver);
+}
+
+static void __exit msm_ispif_exit_module(void)
+{
+ platform_driver_unregister(&ispif_driver);
+}
+
+module_init(msm_ispif_init_module);
+module_exit(msm_ispif_exit_module);
+MODULE_DESCRIPTION("MSM ISP Interface driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/msmb/ispif/msm_ispif.h b/drivers/media/video/msmb/ispif/msm_ispif.h
new file mode 100644
index 0000000..c4418c1
--- /dev/null
+++ b/drivers/media/video/msmb/ispif/msm_ispif.h
@@ -0,0 +1,59 @@
+/* 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 MSM_ISPIF_H
+#define MSM_ISPIF_H
+
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <media/v4l2-subdev.h>
+#include <media/msmb_ispif.h>
+#include "msm_sd.h"
+
+struct ispif_irq_status {
+ uint32_t ispifIrqStatus0;
+ uint32_t ispifIrqStatus1;
+ uint32_t ispifIrqStatus2;
+};
+
+enum msm_ispif_state_t {
+ ISPIF_POWER_UP,
+ ISPIF_POWER_DOWN,
+};
+struct ispif_sof_count {
+ uint32_t sof_cnt[INTF_MAX];
+};
+
+struct ispif_intf_cmd {
+ uint32_t intf_cmd;
+ uint32_t intf_cmd1;
+};
+
+struct ispif_device {
+ struct platform_device *pdev;
+ struct msm_sd_subdev msm_sd;
+ struct resource *mem;
+ struct resource *irq;
+ struct resource *io;
+ void __iomem *base;
+ struct mutex mutex;
+ uint8_t start_ack_pending;
+ struct completion reset_complete;
+ uint32_t csid_version;
+ int enb_dump_reg;
+ uint32_t open_cnt;
+ struct ispif_sof_count sof_count[VFE_MAX];
+ struct ispif_intf_cmd applied_intf_cmd[VFE_MAX];
+ enum msm_ispif_state_t ispif_state;
+ struct clk *ispif_clk[INTF_MAX];
+};
+#endif
diff --git a/drivers/media/video/msmb/ispif/msm_ispif_hwreg.h b/drivers/media/video/msmb/ispif/msm_ispif_hwreg.h
new file mode 100644
index 0000000..16575ae
--- /dev/null
+++ b/drivers/media/video/msmb/ispif/msm_ispif_hwreg.h
@@ -0,0 +1,102 @@
+/* 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 MSM_ISPIF_HWREG_H
+#define MSM_ISPIF_HWREG_H
+
+
+/* ISPIF registers */
+
+#define ISPIF_RST_CMD_ADDR 0x08
+#define ISPIF_RST_CMD_1_ADDR 0x0C
+#define ISPIF_INTF_CMD_ADDR 0x248
+#define ISPIF_INTF_CMD_1_ADDR 0x24C
+#define ISPIF_CTRL_ADDR 0x08
+#define ISPIF_INPUT_SEL_ADDR 0x244
+#define ISPIF_PIX_0_INTF_CID_MASK_ADDR 0x254
+#define ISPIF_RDI_0_INTF_CID_MASK_ADDR 0x264
+#define ISPIF_PIX_1_INTF_CID_MASK_ADDR 0x258
+#define ISPIF_RDI_1_INTF_CID_MASK_ADDR 0x268
+#define ISPIF_RDI_2_INTF_CID_MASK_ADDR 0x26C
+#define ISPIF_PIX_0_STATUS_ADDR 0x2C0
+#define ISPIF_RDI_0_STATUS_ADDR 0x2D0
+#define ISPIF_PIX_1_STATUS_ADDR 0x2C4
+#define ISPIF_RDI_1_STATUS_ADDR 0x2D4
+#define ISPIF_RDI_2_STATUS_ADDR 0x2D8
+#define ISPIF_IRQ_MASK_ADDR 0x208
+#define ISPIF_IRQ_CLEAR_ADDR 0x230
+#define ISPIF_IRQ_STATUS_ADDR 0x21C
+#define ISPIF_IRQ_MASK_1_ADDR 0x20C
+#define ISPIF_IRQ_CLEAR_1_ADDR 0x234
+#define ISPIF_IRQ_STATUS_1_ADDR 0x220
+#define ISPIF_IRQ_MASK_2_ADDR 0x210
+#define ISPIF_IRQ_CLEAR_2_ADDR 0x238
+#define ISPIF_IRQ_STATUS_2_ADDR 0x224
+#define ISPIF_IRQ_GLOBAL_CLEAR_CMD_ADDR 0x1C
+
+/* new */
+#define ISPIF_VFE_m_CTRL_0_ADDR 0x200
+#define ISPIF_VFE_m_IRQ_MASK_0 0x208
+#define ISPIF_VFE_m_IRQ_MASK_1 0x20C
+#define ISPIF_VFE_m_IRQ_MASK_2 0x210
+#define ISPIF_VFE_m_IRQ_STATUS_0 0x21C
+#define ISPIF_VFE_m_IRQ_STATUS_1 0x220
+#define ISPIF_VFE_m_IRQ_STATUS_2 0x224
+#define ISPIF_VFE_m_IRQ_CLEAR_0 0x230
+#define ISPIF_VFE_m_IRQ_CLEAR_1 0x234
+#define ISPIF_VFE_m_IRQ_CLEAR_2 0x238
+
+/*ISPIF RESET BITS*/
+
+#define VFE_CLK_DOMAIN_RST 31
+#define RDI_CLK_DOMAIN_RST 26
+#define RDI_1_CLK_DOMAIN_RST 27
+#define RDI_2_CLK_DOMAIN_RST 28
+#define PIX_CLK_DOMAIN_RST 29
+#define PIX_1_CLK_DOMAIN_RST 30
+#define AHB_CLK_DOMAIN_RST 25
+#define RDI_2_VFE_RST_STB 12
+#define RDI_2_CSID_RST_STB 11
+#define RDI_1_VFE_RST_STB 10
+#define RDI_1_CSID_RST_STB 9
+#define RDI_0_VFE_RST_STB 8
+#define RDI_0_CSID_RST_STB 7
+#define PIX_1_VFE_RST_STB 6
+#define PIX_1_CSID_RST_STB 5
+#define PIX_0_VFE_RST_STB 4
+#define PIX_0_CSID_RST_STB 3
+#define SW_REG_RST_STB 2
+#define MISC_LOGIC_RST_STB 1
+#define STROBED_RST_EN 0
+
+#define ISPIF_RST_CMD_MASK 0xFE0F1FFF
+#define ISPIF_RST_CMD_1_MASK 0xFC0F1FF9
+
+#define PIX_INTF_0_OVERFLOW_IRQ 12
+#define RAW_INTF_0_OVERFLOW_IRQ 25
+#define RAW_INTF_1_OVERFLOW_IRQ 25
+#define RAW_INTF_2_OVERFLOW_IRQ 12
+#define RESET_DONE_IRQ 27
+
+#define ISPIF_IRQ_STATUS_MASK 0x0A493249
+#define ISPIF_IRQ_STATUS_1_MASK 0x02493249
+#define ISPIF_IRQ_STATUS_2_MASK 0x00001249
+
+#define ISPIF_IRQ_STATUS_PIX_SOF_MASK 0x249
+#define ISPIF_IRQ_STATUS_RDI0_SOF_MASK 0x492000
+#define ISPIF_IRQ_STATUS_RDI1_SOF_MASK 0x492000
+#define ISPIF_IRQ_STATUS_RDI2_SOF_MASK 0x249
+
+#define ISPIF_IRQ_STATUS_SOF_MASK 0x492249
+#define ISPIF_IRQ_GLOBAL_CLEAR_CMD 0x1
+
+#endif
diff --git a/drivers/media/video/msmb/msm.c b/drivers/media/video/msmb/msm.c
new file mode 100644
index 0000000..c908333
--- /dev/null
+++ b/drivers/media/video/msmb/msm.c
@@ -0,0 +1,1060 @@
+/* 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
+ * 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/of.h>
+#include <linux/module.h>
+#include <linux/workqueue.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/ioctl.h>
+#include <linux/spinlock.h>
+#include <linux/proc_fs.h>
+#include <linux/atomic.h>
+#include <linux/wait.h>
+#include <linux/videodev2.h>
+#include <linux/msm_ion.h>
+#include <linux/iommu.h>
+#include <linux/platform_device.h>
+#include <media/v4l2-fh.h>
+#include "msm.h"
+#include "msm_vb2.h"
+#include "msm_sd.h"
+
+struct msm_queue_head {
+ struct list_head list;
+ spinlock_t lock;
+ int len;
+ int max;
+};
+
+/** msm_event:
+ *
+ * event sent by imaging server
+ **/
+struct msm_event {
+ struct video_device *vdev;
+ atomic_t on_heap;
+};
+
+struct msm_command {
+ struct list_head list;
+ struct v4l2_event event;
+ atomic_t on_heap;
+};
+
+/** struct msm_command_ack
+ *
+ * Object of command_ack_q, which is
+ * created per open operation
+ *
+ * contains struct msm_command
+ **/
+struct msm_command_ack {
+ struct list_head list;
+ struct msm_queue_head command_q;
+ wait_queue_head_t wait;
+ int stream_id;
+};
+
+struct msm_stream {
+ struct list_head list;
+
+ /* stream index per session, same
+ * as stream_id but set through s_parm */
+ unsigned int stream_id;
+
+ /* vb2 buffer handling */
+ struct vb2_queue *vb2_q;
+};
+
+struct msm_v4l2_subdev {
+ /* FIXME: for session close and error handling such
+ * as daemon shutdown */
+ int close_sequence;
+};
+
+struct msm_session {
+ struct list_head list;
+
+ /* session index */
+ unsigned int session_id;
+
+ /* event queue sent by imaging server */
+ struct msm_event event_q;
+
+ /* ACK by imaging server. Object type of
+ * struct msm_command_ack per open,
+ * assumption is application can send
+ * command on every opened video node */
+ struct msm_queue_head command_ack_q;
+
+ /* real streams(either data or metadate) owned by one
+ * session struct msm_stream */
+ struct msm_queue_head stream_q;
+};
+
+static struct v4l2_device *msm_v4l2_dev;
+
+static struct msm_queue_head *msm_session_q;
+
+/* config node envent queue */
+static struct v4l2_fh *msm_eventq;
+spinlock_t msm_eventq_lock;
+
+static struct pid *msm_pid;
+spinlock_t msm_pid_lock;
+
+#define msm_dequeue(queue, type, member) ({ \
+ unsigned long flags; \
+ struct msm_queue_head *__q = (queue); \
+ type *node = 0; \
+ spin_lock_irqsave(&__q->lock, flags); \
+ if (!list_empty(&__q->list)) { \
+ __q->len--; \
+ node = list_first_entry(&__q->list, \
+ type, member); \
+ if ((node) && (&node->member) && (&node->member.next)) \
+ list_del_init(&node->member); \
+ } \
+ spin_unlock_irqrestore(&__q->lock, flags); \
+ node; \
+})
+
+#define msm_delete_sd_entry(queue, type, member, q_node) ({ \
+ unsigned long flags; \
+ struct msm_queue_head *__q = (queue); \
+ type *node = 0; \
+ spin_lock_irqsave(&__q->lock, flags); \
+ if (!list_empty(&__q->list)) { \
+ list_for_each_entry(node, &__q->list, member) \
+ if (node->sd == q_node) { \
+ __q->len--; \
+ list_del_init(&node->member); \
+ kfree(node); \
+ break; \
+ } \
+ } \
+ spin_unlock_irqrestore(&__q->lock, flags); \
+})
+
+#define msm_delete_entry(queue, type, member, q_node) ({ \
+ unsigned long flags; \
+ struct msm_queue_head *__q = (queue); \
+ type *node = 0; \
+ spin_lock_irqsave(&__q->lock, flags); \
+ if (!list_empty(&__q->list)) { \
+ list_for_each_entry(node, &__q->list, member) \
+ if (node == q_node) { \
+ __q->len--; \
+ list_del_init(&node->member); \
+ kfree(node); \
+ break; \
+ } \
+ } \
+ spin_unlock_irqrestore(&__q->lock, flags); \
+})
+
+#define msm_queue_drain(queue, type, member) do { \
+ unsigned long flags; \
+ struct msm_queue_head *__q = (queue); \
+ type *node; \
+ spin_lock_irqsave(&__q->lock, flags); \
+ while (!list_empty(&__q->list)) { \
+ __q->len--; \
+ node = list_first_entry(&__q->list, \
+ type, member); \
+ if (node) { \
+ if (&node->member) \
+ list_del_init(&node->member); \
+ kfree(node); \
+ } \
+ } \
+ spin_unlock_irqrestore(&__q->lock, flags); \
+} while (0);
+
+typedef int (*msm_queue_func)(void *d1, void *d2);
+#define msm_queue_traverse_action(queue, type, member, func, data) do {\
+ unsigned long flags; \
+ struct msm_queue_head *__q = (queue); \
+ type *node = 0; \
+ msm_queue_func __f = (func); \
+ spin_lock_irqsave(&__q->lock, flags); \
+ if (!list_empty(&__q->list)) { \
+ list_for_each_entry(node, &__q->list, member) \
+ if (node && __f) { \
+ __f(node, data); \
+ } \
+ } \
+ spin_unlock_irqrestore(&__q->lock, flags); \
+} while (0)
+
+typedef int (*msm_queue_find_func)(void *d1, void *d2);
+#define msm_queue_find(queue, type, member, func, data) ({\
+ unsigned long flags; \
+ struct msm_queue_head *__q = (queue); \
+ type *node = 0; \
+ typeof(node) __ret = NULL; \
+ msm_queue_find_func __f = (func); \
+ spin_lock_irqsave(&__q->lock, flags); \
+ if (!list_empty(&__q->list)) { \
+ list_for_each_entry(node, &__q->list, member) \
+ if ((__f) && __f(node, data)) { \
+ __ret = node; \
+ break; \
+ } \
+ } \
+ spin_unlock_irqrestore(&__q->lock, flags); \
+ __ret; \
+})
+
+static void msm_init_queue(struct msm_queue_head *qhead)
+{
+ BUG_ON(!qhead);
+
+ INIT_LIST_HEAD(&qhead->list);
+ spin_lock_init(&qhead->lock);
+ qhead->len = 0;
+ qhead->max = 0;
+}
+
+static void msm_enqueue(struct msm_queue_head *qhead,
+ struct list_head *entry)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&qhead->lock, flags);
+ qhead->len++;
+ if (qhead->len > qhead->max)
+ qhead->max = qhead->len;
+ list_add_tail(entry, &qhead->list);
+ spin_unlock_irqrestore(&qhead->lock, flags);
+}
+
+/* index = session id */
+static inline int __msm_queue_find_session(void *d1, void *d2)
+{
+ struct msm_session *session = d1;
+ return (session->session_id == *(unsigned int *)d2) ? 1 : 0;
+}
+
+static inline int __msm_queue_find_stream(void *d1, void *d2)
+{
+ struct msm_stream *stream = d1;
+ return (stream->stream_id == *(unsigned int *)d2) ? 1 : 0;
+}
+
+static inline int __msm_queue_find_command_ack_q(void *d1, void *d2)
+{
+ struct msm_command_ack *ack = d1;
+ return (ack->stream_id == *(unsigned int *)d2) ? 1 : 0;
+}
+
+int msm_create_stream(unsigned int session_id,
+ unsigned int stream_id, struct vb2_queue *q)
+{
+ struct msm_session *session;
+ struct msm_stream *stream;
+
+ session = msm_queue_find(msm_session_q, struct msm_session,
+ list, __msm_queue_find_session, &session_id);
+ if (!session)
+ return -EINVAL;
+
+ stream = kzalloc(sizeof(*stream), GFP_KERNEL);
+ if (!stream)
+ return -ENOMEM;
+
+ stream->stream_id = stream_id;
+ stream->vb2_q = q;
+
+ msm_enqueue(&session->stream_q, &stream->list);
+ session->stream_q.len++;
+
+ return 0;
+}
+
+void msm_delete_stream(unsigned int session_id, unsigned int stream_id)
+{
+ struct msm_session *session = NULL;
+ struct msm_stream *stream = NULL;
+
+ session = msm_queue_find(msm_session_q, struct msm_session,
+ list, __msm_queue_find_session, &session_id);
+ if (!session)
+ return;
+
+ stream = msm_queue_find(&session->stream_q, struct msm_stream,
+ list, __msm_queue_find_stream, &stream_id);
+ if (!stream)
+ return;
+
+ list_del_init(&stream->list);
+ session->stream_q.len--;
+ kfree(stream);
+}
+
+static void msm_sd_unregister_subdev(struct video_device *vdev)
+{
+ struct v4l2_subdev *sd = video_get_drvdata(vdev);
+ sd->devnode = NULL;
+ kfree(vdev);
+}
+
+static inline int __msm_sd_register_subdev(struct v4l2_subdev *sd)
+{
+ int rc = 0;
+ struct video_device *vdev;
+
+ if (!msm_v4l2_dev || !sd || !sd->name[0])
+ return -EINVAL;
+
+ rc = v4l2_device_register_subdev(msm_v4l2_dev, sd);
+ if (rc < 0)
+ return rc;
+
+ /* Register a device node for every subdev marked with the
+ * V4L2_SUBDEV_FL_HAS_DEVNODE flag.
+ */
+ if (!(sd->flags & V4L2_SUBDEV_FL_HAS_DEVNODE))
+ return rc;
+
+ vdev = kzalloc(sizeof(*vdev), GFP_KERNEL);
+ if (!vdev) {
+ rc = -ENOMEM;
+ goto clean_up;
+ }
+
+ video_set_drvdata(vdev, sd);
+ strlcpy(vdev->name, sd->name, sizeof(vdev->name));
+ vdev->v4l2_dev = msm_v4l2_dev;
+ vdev->fops = &v4l2_subdev_fops;
+ vdev->release = msm_sd_unregister_subdev;
+ rc = __video_register_device(vdev, VFL_TYPE_SUBDEV, -1, 1,
+ sd->owner);
+ if (rc < 0) {
+ kfree(vdev);
+ goto clean_up;
+ }
+
+#if defined(CONFIG_MEDIA_CONTROLLER)
+ sd->entity.info.v4l.major = VIDEO_MAJOR;
+ sd->entity.info.v4l.minor = vdev->minor;
+ sd->entity.name = video_device_node_name(vdev);
+#endif
+ sd->devnode = vdev;
+ return 0;
+
+clean_up:
+ if (sd->devnode)
+ video_unregister_device(sd->devnode);
+ return rc;
+}
+
+int msm_sd_register(struct msm_sd_subdev *msm_subdev)
+{
+ if (WARN_ON(!msm_subdev))
+ return -EINVAL;
+
+ if (WARN_ON(!msm_v4l2_dev) && WARN_ON(!msm_v4l2_dev->dev))
+ return -EIO;
+
+ return __msm_sd_register_subdev(&msm_subdev->sd);
+}
+
+int msm_sd_unregister(struct msm_sd_subdev *msm_subdev)
+{
+ if (WARN_ON(!msm_subdev))
+ return -EINVAL;
+
+ v4l2_device_unregister_subdev(&msm_subdev->sd);
+ return 0;
+}
+
+int msm_create_session(unsigned int session_id, struct video_device *vdev)
+{
+ struct msm_session *session = NULL;
+
+ if (!msm_session_q)
+ return -ENODEV;
+
+ session = msm_queue_find(msm_session_q, struct msm_session,
+ list, __msm_queue_find_session, &session_id);
+ if (session)
+ return -EINVAL;
+
+ session = kzalloc(sizeof(*session), GFP_KERNEL);
+ if (!session)
+ return -ENOMEM;
+
+ session->session_id = session_id;
+ session->event_q.vdev = vdev;
+ msm_init_queue(&session->command_ack_q);
+ msm_init_queue(&session->stream_q);
+ msm_enqueue(msm_session_q, &session->list);
+ return 0;
+}
+
+int msm_create_command_ack_q(unsigned int session_id, unsigned int stream_id)
+{
+ struct msm_session *session;
+ struct msm_command_ack *cmd_ack;
+
+ if (!msm_session_q)
+ return -ENODEV;
+
+ session = msm_queue_find(msm_session_q, struct msm_session,
+ list, __msm_queue_find_session, &session_id);
+ if (!session)
+ return -EINVAL;
+
+ cmd_ack = kzalloc(sizeof(*cmd_ack), GFP_KERNEL);
+ if (!cmd_ack)
+ return -ENOMEM;
+
+ msm_init_queue(&cmd_ack->command_q);
+ INIT_LIST_HEAD(&cmd_ack->list);
+ init_waitqueue_head(&cmd_ack->wait);
+ cmd_ack->stream_id = stream_id;
+
+ msm_enqueue(&session->command_ack_q, &cmd_ack->list);
+ session->command_ack_q.len++;
+
+ return 0;
+}
+
+void msm_delete_command_ack_q(unsigned int session_id, unsigned int stream_id)
+{
+ struct msm_session *session;
+ struct msm_command_ack *cmd_ack;
+
+ session = msm_queue_find(msm_session_q, struct msm_session,
+ list, __msm_queue_find_session, &session_id);
+ if (!session)
+ return;
+
+ cmd_ack = msm_queue_find(&session->command_ack_q,
+ struct msm_command_ack, list, __msm_queue_find_command_ack_q,
+ &stream_id);
+ if (!cmd_ack)
+ return;
+
+ msm_queue_drain(&cmd_ack->command_q, struct msm_command, list);
+}
+
+static inline int __msm_v4l2_subdev_shutdown(struct v4l2_subdev *sd)
+{
+ return 0;
+}
+
+static void msm_sd_try_shutdown(void)
+{
+ unsigned long flags;
+ struct v4l2_subdev *sd;
+
+ /* release all subdev's resource */
+ spin_lock_irqsave(&msm_v4l2_dev->lock, flags);
+ if (!list_empty(&msm_v4l2_dev->subdevs)) {
+ list_for_each_entry(sd, &msm_v4l2_dev->subdevs, list)
+ __msm_v4l2_subdev_shutdown(sd);
+ }
+ spin_unlock_irqrestore(&msm_v4l2_dev->lock, flags);
+}
+
+static inline int __msm_sd_close_session_streams(struct v4l2_subdev *sd,
+ struct msm_sd_close_ioctl *sd_close)
+{
+ v4l2_subdev_call(sd, core, ioctl,
+ MSM_SD_CLOSE_SESSION_AND_STREAM, &sd_close);
+
+ return 0;
+}
+
+static inline int __msm_destroy_session_streams(void *d1, void *d2)
+{
+ struct msm_stream *stream = d1;
+ struct msm_sd_close_ioctl *sd_close = d2;
+ struct v4l2_subdev *sd;
+ unsigned long flags;
+
+ sd_close->stream = stream->stream_id;
+
+ spin_lock_irqsave(&msm_v4l2_dev->lock, flags);
+ if (!list_empty(&msm_v4l2_dev->subdevs))
+ list_for_each_entry(sd, &msm_v4l2_dev->subdevs, list)
+ __msm_sd_close_session_streams(sd, sd_close);
+ spin_unlock_irqrestore(&msm_v4l2_dev->lock, flags);
+
+ return 0;
+}
+
+static void msm_destroy_session_streams(struct msm_session *session)
+{
+ struct msm_sd_close_ioctl sd_close;
+
+ /* to ensure error handling purpose, it needs to detach all subdevs
+ * which are being connected to streams */
+ if (!session)
+ return;
+
+ sd_close.session = session->session_id;
+
+ msm_queue_traverse_action(&session->stream_q, struct msm_stream, list,
+ __msm_destroy_session_streams, &sd_close);
+
+ msm_queue_drain(&session->stream_q, struct msm_stream, list);
+}
+
+static inline int __msm_remove_session_cmd_ack_q(void *d1, void *d2)
+{
+ struct msm_command_ack *cmd_ack = d1;
+
+ msm_queue_drain(&cmd_ack->command_q, struct msm_command, list);
+
+ return 0;
+}
+
+static void msm_remove_session_cmd_ack_q(struct msm_session *session)
+{
+ if (!session)
+ return;
+
+ /* to ensure error handling purpose, it needs to detach all subdevs
+ * which are being connected to streams */
+ msm_queue_traverse_action(&session->command_ack_q,
+ struct msm_command_ack, list,
+ __msm_remove_session_cmd_ack_q, NULL);
+
+ msm_queue_drain(&session->command_ack_q, struct msm_command_ack, list);
+}
+
+int msm_destroy_session(unsigned int session_id)
+{
+ struct msm_session *session;
+
+ session = msm_queue_find(msm_session_q, struct msm_session,
+ list, __msm_queue_find_session, &session_id);
+ if (!session)
+ return -EINVAL;
+
+ msm_destroy_session_streams(session);
+ msm_remove_session_cmd_ack_q(session);
+
+ msm_delete_entry(msm_session_q, struct msm_session,
+ list, session);
+
+ return 0;
+}
+
+static long msm_private_ioctl(struct file *file, void *fh,
+ bool valid_prio, int cmd, void *arg)
+{
+ int rc = 0;
+ struct msm_v4l2_event_data *event_data;
+ struct msm_session *session;
+ unsigned int session_id;
+ unsigned int stream_id;
+
+ event_data = (struct msm_v4l2_event_data *)
+ ((struct v4l2_event *)arg)->u.data;
+
+ session_id = event_data->session_id;
+ stream_id = event_data->stream_id;
+
+ session = msm_queue_find(msm_session_q, struct msm_session,
+ list, __msm_queue_find_session, &session_id);
+
+ if (!session)
+ return -EINVAL;
+
+ switch (cmd) {
+ case MSM_CAM_V4L2_IOCTL_NOTIFY: {
+ if (WARN_ON(!session->event_q.vdev)) {
+ rc = -EFAULT;
+ break;
+ }
+
+ v4l2_event_queue(session->event_q.vdev,
+ (struct v4l2_event *)arg);
+ }
+ break;
+
+ case MSM_CAM_V4L2_IOCTL_CMD_ACK: {
+ struct msm_command_ack *cmd_ack;
+ struct msm_command *ret_cmd;
+
+ ret_cmd = kzalloc(sizeof(*ret_cmd), GFP_KERNEL);
+ if (!ret_cmd) {
+ rc = -ENOMEM;
+ break;
+ }
+
+ cmd_ack = msm_queue_find(&session->command_ack_q,
+ struct msm_command_ack, list,
+ __msm_queue_find_command_ack_q,
+ &stream_id);
+ if (WARN_ON(!cmd_ack)) {
+ kfree(ret_cmd);
+ rc = -EFAULT;
+ break;
+ }
+
+ ret_cmd->event = *(struct v4l2_event *)arg;
+ msm_enqueue(&cmd_ack->command_q, &ret_cmd->list);
+ wake_up(&cmd_ack->wait);
+ }
+ break;
+
+ default:
+ rc = -ENOTTY;
+ break;
+ }
+
+ return rc;
+}
+
+static int msm_unsubscribe_event(struct v4l2_fh *fh,
+ struct v4l2_event_subscription *sub)
+{
+ return v4l2_event_unsubscribe(fh, sub);
+}
+
+static int msm_subscribe_event(struct v4l2_fh *fh,
+ struct v4l2_event_subscription *sub)
+{
+ return v4l2_event_subscribe(fh, sub, 5);
+}
+
+static const struct v4l2_ioctl_ops g_msm_ioctl_ops = {
+ .vidioc_subscribe_event = msm_subscribe_event,
+ .vidioc_unsubscribe_event = msm_unsubscribe_event,
+ .vidioc_default = msm_private_ioctl,
+};
+
+static unsigned int msm_poll(struct file *f,
+ struct poll_table_struct *pll_table)
+{
+ int rc = 0;
+ struct v4l2_fh *eventq = f->private_data;
+
+ BUG_ON(!eventq);
+
+ poll_wait(f, &eventq->wait, pll_table);
+
+ if (v4l2_event_pending(eventq))
+ rc = POLLIN | POLLRDNORM;
+
+ return rc;
+}
+
+/* something seriously wrong if msm_close is triggered
+ * !!! user space imaging server is shutdown !!!
+ */
+int msm_post_event(struct v4l2_event *event, int timeout)
+{
+ int rc = 0;
+ struct video_device *vdev;
+ struct msm_session *session;
+ struct msm_v4l2_event_data *event_data =
+ (struct msm_v4l2_event_data *)&event->u.data[0];
+ struct msm_command_ack *cmd_ack;
+ struct msm_command *cmd;
+ int session_id, stream_id;
+ unsigned long flags = 0;
+
+ session_id = event_data->session_id;
+ stream_id = event_data->stream_id;
+
+ spin_lock_irqsave(&msm_eventq_lock, flags);
+ if (!msm_eventq) {
+ spin_unlock_irqrestore(&msm_eventq_lock, flags);
+ return -ENODEV;
+ }
+ spin_unlock_irqrestore(&msm_eventq_lock, flags);
+
+ vdev = msm_eventq->vdev;
+
+ /* send to imaging server and wait for ACK */
+ session = msm_queue_find(msm_session_q, struct msm_session,
+ list, __msm_queue_find_session, &session_id);
+ if (WARN_ON(!session))
+ return -EIO;
+
+ cmd_ack = msm_queue_find(&session->command_ack_q,
+ struct msm_command_ack, list,
+ __msm_queue_find_command_ack_q, &stream_id);
+ if (WARN_ON(!cmd_ack))
+ return -EIO;
+
+ v4l2_event_queue(vdev, event);
+
+ if (timeout < 0)
+ return rc;
+
+ /* should wait on session based condition */
+ rc = wait_event_interruptible_timeout(cmd_ack->wait,
+ !list_empty_careful(&cmd_ack->command_q.list),
+ msecs_to_jiffies(timeout));
+ if (list_empty_careful(&cmd_ack->command_q.list)) {
+ if (!rc)
+ rc = -ETIMEDOUT;
+ if (rc < 0)
+ return rc;
+ }
+
+ cmd = msm_dequeue(&cmd_ack->command_q,
+ struct msm_command, list);
+ if (!cmd)
+ return -EINVAL;
+
+ event_data = (struct msm_v4l2_event_data *)cmd->event.u.data;
+
+ /* compare cmd_ret and event */
+ if (WARN_ON(event->type != cmd->event.type) ||
+ WARN_ON(event->id != cmd->event.id))
+ rc = -EINVAL;
+
+ *event = cmd->event;
+
+ kfree(cmd);
+ return rc;
+}
+
+static int __msm_close_destry_session_notify_apps(void *d1, void *d2)
+{
+ struct v4l2_event event;
+ struct msm_v4l2_event_data *event_data =
+ (struct msm_v4l2_event_data *)&event.u.data[0];
+ struct msm_session *session = d1;
+
+ event.type = MSM_CAMERA_V4L2_EVENT_TYPE;
+ event.id = MSM_CAMERA_MSM_NOTIFY;
+ event_data->command = MSM_CAMERA_PRIV_SHUTDOWN;
+
+ v4l2_event_queue(session->event_q.vdev, &event);
+
+ msm_destroy_session_streams(session);
+ msm_remove_session_cmd_ack_q(session);
+
+ return 0;
+}
+
+static int msm_close(struct file *filep)
+{
+ int rc = 0;
+ unsigned long flags;
+ struct msm_video_device *pvdev = video_drvdata(filep);
+
+ /* 1st thing 1st, send v4l2_event to HAL immediately,
+ * to ensure error handling purpose, it needs to detach all subdevs
+ * which are being connected to streams */
+ msm_queue_traverse_action(msm_session_q, struct msm_session, list,
+ __msm_close_destry_session_notify_apps, NULL);
+
+ msm_queue_drain(msm_session_q, struct msm_session, list);
+
+ spin_lock_irqsave(&msm_eventq_lock, flags);
+ msm_eventq = NULL;
+ spin_unlock_irqrestore(&msm_eventq_lock, flags);
+ v4l2_fh_release(filep);
+
+ msm_sd_try_shutdown();
+
+ spin_lock_irqsave(&msm_pid_lock, flags);
+ put_pid(msm_pid);
+ msm_pid = NULL;
+ spin_unlock_irqrestore(&msm_pid_lock, flags);
+
+ atomic_set(&pvdev->opened, 0);
+
+ return rc;
+}
+
+static inline void msm_list_switch(struct list_head *l1,
+ struct list_head *l2)
+{
+ l1->next = l2->next;
+ l2->prev = l1->prev;
+ l1->prev->next = l2;
+ l2->next->prev = l1;
+ l1->prev = l2;
+ l2->next = l1;
+}
+
+static int msm_open(struct file *filep)
+{
+ int rc;
+ unsigned long flags;
+ struct msm_video_device *pvdev = video_drvdata(filep);
+
+ BUG_ON(!pvdev);
+
+ /* !!! only ONE open is allowed !!! */
+ if (atomic_read(&pvdev->opened))
+ return -EBUSY;
+
+ atomic_set(&pvdev->opened, 1);
+
+ spin_lock_irqsave(&msm_pid_lock, flags);
+ msm_pid = get_pid(task_pid(current));
+ spin_unlock_irqrestore(&msm_pid_lock, flags);
+
+ /* create event queue */
+ rc = v4l2_fh_open(filep);
+ if (rc < 0)
+ return rc;
+
+ spin_lock_irqsave(&msm_eventq_lock, flags);
+ msm_eventq = filep->private_data;
+ spin_unlock_irqrestore(&msm_eventq_lock, flags);
+
+ return rc;
+}
+
+static struct v4l2_file_operations msm_fops = {
+ .owner = THIS_MODULE,
+ .open = msm_open,
+ .poll = msm_poll,
+ .release = msm_close,
+ .ioctl = video_ioctl2,
+};
+
+struct msm_stream *msm_get_stream(unsigned int session_id,
+ unsigned int stream_id)
+{
+ struct msm_session *session;
+ struct msm_stream *stream;
+
+ session = msm_queue_find(msm_session_q, struct msm_session,
+ list, __msm_queue_find_session, &session_id);
+ if (!session)
+ return ERR_PTR(-EINVAL);
+
+ stream = msm_queue_find(&session->stream_q, struct msm_stream,
+ list, __msm_queue_find_stream, &stream_id);
+
+ if (!stream)
+ return ERR_PTR(-EINVAL);
+
+ return stream;
+}
+
+struct vb2_queue *msm_get_stream_vb2q(unsigned int session_id,
+ unsigned int stream_id)
+{
+ struct msm_session *session;
+ struct msm_stream *stream;
+
+ session = msm_queue_find(msm_session_q, struct msm_session,
+ list, __msm_queue_find_session, &session_id);
+ if (!session)
+ return NULL;
+
+ stream = msm_queue_find(&session->stream_q, struct msm_stream,
+ list, __msm_queue_find_stream, &stream_id);
+ if (!stream)
+ return NULL;
+
+ return stream->vb2_q;
+}
+
+static struct v4l2_subdev *msm_sd_find(const char *name)
+{
+ unsigned long flags;
+ struct v4l2_subdev *subdev = NULL;
+
+ spin_lock_irqsave(&msm_v4l2_dev->lock, flags);
+ if (!list_empty(&msm_v4l2_dev->subdevs)) {
+ list_for_each_entry(subdev, &msm_v4l2_dev->subdevs, list)
+ if (!strcmp(name, subdev->name))
+ break;
+ }
+ spin_unlock_irqrestore(&msm_v4l2_dev->lock, flags);
+
+ return subdev;
+}
+
+static void msm_sd_notify(struct v4l2_subdev *sd,
+ unsigned int notification, void *arg)
+{
+ int rc = 0;
+ struct v4l2_subdev *subdev = NULL;
+
+ BUG_ON(!sd);
+ BUG_ON(!arg);
+
+ /* Check if subdev exists before processing*/
+ if (!msm_sd_find(sd->name))
+ return;
+
+ switch (notification) {
+ case MSM_SD_NOTIFY_GET_SD: {
+ struct msm_sd_req_sd *get_sd = arg;
+
+ get_sd->subdev = msm_sd_find(get_sd->name);
+ /* TODO: might need to add ref count on ret_sd */
+ }
+ break;
+
+ case MSM_SD_NOTIFY_PUT_SD: {
+ struct msm_sd_req_sd *put_sd = arg;
+ subdev = msm_sd_find(put_sd->name);
+ }
+ break;
+
+ case MSM_SD_NOTIFY_REQ_CB: {
+ struct msm_sd_req_vb2_q *req_sd = arg;
+ rc = msm_vb2_request_cb(req_sd);
+ if (rc < 0)
+ return;
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+static int __devinit msm_probe(struct platform_device *pdev)
+{
+ struct msm_video_device *pvdev;
+ int rc = 0;
+
+ msm_v4l2_dev = kzalloc(sizeof(*msm_v4l2_dev),
+ GFP_KERNEL);
+ if (WARN_ON(!msm_v4l2_dev)) {
+ rc = -ENOMEM;
+ goto probe_end;
+ }
+
+ pvdev = kzalloc(sizeof(struct msm_video_device),
+ GFP_KERNEL);
+ if (WARN_ON(!pvdev)) {
+ rc = -ENOMEM;
+ goto pvdev_fail;
+ }
+
+ pvdev->vdev = video_device_alloc();
+ if (WARN_ON(!pvdev->vdev)) {
+ rc = -ENOMEM;
+ goto video_fail;
+ }
+
+#if defined(CONFIG_MEDIA_CONTROLLER)
+ msm_v4l2_dev->mdev = kzalloc(sizeof(struct media_device),
+ GFP_KERNEL);
+ if (!msm_v4l2_dev->mdev) {
+ rc = -ENOMEM;
+ goto mdev_fail;
+ }
+ strlcpy(msm_v4l2_dev->mdev->model, MSM_CONFIGURATION_NAME,
+ sizeof(msm_v4l2_dev->mdev->model));
+ msm_v4l2_dev->mdev->dev = &(pdev->dev);
+
+ rc = media_device_register(msm_v4l2_dev->mdev);
+ if (WARN_ON(rc < 0))
+ goto media_fail;
+
+ if (WARN_ON((rc == media_entity_init(&pvdev->vdev->entity,
+ 0, NULL, 0)) < 0))
+ goto entity_fail;
+
+ pvdev->vdev->entity.type = MEDIA_ENT_T_DEVNODE_V4L;
+ pvdev->vdev->entity.group_id = QCAMERA_VNODE_GROUP_ID;
+#endif
+
+ msm_v4l2_dev->notify = msm_sd_notify;
+
+ pvdev->vdev->v4l2_dev = msm_v4l2_dev;
+
+ rc = v4l2_device_register(&(pdev->dev), pvdev->vdev->v4l2_dev);
+ if (WARN_ON(rc < 0))
+ goto register_fail;
+
+ strlcpy(pvdev->vdev->name, "msm-config", sizeof(pvdev->vdev->name));
+ pvdev->vdev->release = video_device_release;
+ pvdev->vdev->fops = &msm_fops;
+ pvdev->vdev->ioctl_ops = &g_msm_ioctl_ops;
+ pvdev->vdev->minor = -1;
+ pvdev->vdev->vfl_type = VFL_TYPE_GRABBER;
+ rc = video_register_device(pvdev->vdev,
+ VFL_TYPE_GRABBER, -1);
+ if (WARN_ON(rc < 0))
+ goto v4l2_fail;
+
+#if defined(CONFIG_MEDIA_CONTROLLER)
+ /* FIXME: How to get rid of this messy? */
+ pvdev->vdev->entity.name = video_device_node_name(pvdev->vdev);
+#endif
+
+ atomic_set(&pvdev->opened, 0);
+ video_set_drvdata(pvdev->vdev, pvdev);
+
+ msm_session_q = kzalloc(sizeof(*msm_session_q), GFP_KERNEL);
+ if (WARN_ON(!msm_session_q))
+ goto v4l2_fail;
+
+ msm_init_queue(msm_session_q);
+ spin_lock_init(&msm_eventq_lock);
+ spin_lock_init(&msm_pid_lock);
+
+ goto probe_end;
+
+v4l2_fail:
+ v4l2_device_unregister(pvdev->vdev->v4l2_dev);
+register_fail:
+#if defined(CONFIG_MEDIA_CONTROLLER)
+ media_entity_cleanup(&pvdev->vdev->entity);
+entity_fail:
+ media_device_unregister(msm_v4l2_dev->mdev);
+media_fail:
+ kfree(msm_v4l2_dev->mdev);
+mdev_fail:
+#endif
+ video_device_release(pvdev->vdev);
+video_fail:
+ kfree(pvdev);
+pvdev_fail:
+ kfree(msm_v4l2_dev);
+probe_end:
+ return rc;
+}
+
+static const struct of_device_id msm_dt_match[] = {
+ {.compatible = "qcom,msm-cam"},
+}
+
+MODULE_DEVICE_TABLE(of, msm_dt_match);
+
+static struct platform_driver msm_driver = {
+ .probe = msm_probe,
+ .driver = {
+ .name = "msm",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_dt_match,
+ },
+};
+
+static int __init msm_init(void)
+{
+ return platform_driver_register(&msm_driver);
+}
+
+static void __exit msm_exit(void)
+{
+ platform_driver_unregister(&msm_driver);
+}
+
+
+module_init(msm_init);
+module_exit(msm_exit);
+MODULE_DESCRIPTION("MSM V4L2 Camera");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/msmb/msm.h b/drivers/media/video/msmb/msm.h
new file mode 100644
index 0000000..eb15cab
--- /dev/null
+++ b/drivers/media/video/msmb/msm.h
@@ -0,0 +1,55 @@
+/* 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
+ * 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 _MSM_H
+#define _MSM_H
+
+#include <linux/version.h>
+#include <linux/i2c.h>
+#include <linux/videodev2.h>
+#include <linux/pm_qos.h>
+#include <linux/wakelock.h>
+#include <linux/msm_ion.h>
+#include <linux/iommu.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-mediabus.h>
+#include <media/videobuf2-dma-contig.h>
+#include <media/videobuf2-msm-mem.h>
+#include <media/msmb_camera.h>
+
+#define MSM_POST_EVT_TIMEOUT 5000
+#define MSM_POST_EVT_NOTIMEOUT 0xFFFFFFFF
+
+struct msm_video_device {
+ struct video_device *vdev;
+ atomic_t opened;
+};
+
+int msm_post_event(struct v4l2_event *event, int timeout);
+int msm_create_session(unsigned int session, struct video_device *vdev);
+int msm_destroy_session(unsigned int session_id);
+
+int msm_create_stream(unsigned int session_id,
+ unsigned int stream_id, struct vb2_queue *q);
+void msm_delete_stream(unsigned int session_id, unsigned int stream_id);
+int msm_create_command_ack_q(unsigned int session_id, unsigned int stream_id);
+void msm_delete_command_ack_q(unsigned int session_id, unsigned int stream_id);
+struct msm_stream *msm_get_stream(unsigned int session_id,
+ unsigned int stream_id);
+struct vb2_queue *msm_get_stream_vb2q(unsigned int session_id,
+ unsigned int stream_id);
+
+#endif /*_MSM_H */
diff --git a/drivers/media/video/msmb/msm_sd.h b/drivers/media/video/msmb/msm_sd.h
new file mode 100644
index 0000000..aaf3548
--- /dev/null
+++ b/drivers/media/video/msmb/msm_sd.h
@@ -0,0 +1,82 @@
+/* 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
+ * 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 _MSM_SD_H
+#define _MSM_SD_H
+
+#include <media/v4l2-subdev.h>
+#include <media/msmb_camera.h>
+
+/* NOTE: this header file should ONLY be included by subdev drivers */
+
+struct msm_sd_close_ioctl {
+ unsigned int session;
+ unsigned int stream;
+};
+
+#define MSM_SD_CLOSE_STREAM \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 26, struct msm_sd_close_ioctl)
+
+#define MSM_SD_CLOSE_SESSION \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 27, struct msm_sd_close_ioctl)
+
+#define MSM_SD_CLOSE_SESSION_AND_STREAM \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 28, struct msm_sd_close_ioctl)
+
+#define MSM_SD_SHUTDOWN \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 29, struct msm_sd_close_ioctl)
+
+/*
+ * This is used to install Sequence in msm_sd_register.
+ * During msm_close, proper close sequence will be triggered.
+ * For example:
+ *
+ * close_sequence = 0x00100001 (ISP)
+ * close_sequence = 0x00100002 (ISP)
+ * close_sequence = 0x00100003 (ISP)
+ * close_sequence = 0x00200001 (sensor)
+ * close_sequence = 0x00200002 (sensor)
+ * close_sequence = 0x00200003 (sensor)
+ */
+#define MSM_SD_CLOSE_1ST_CATEGORY 0x00010000
+#define MSM_SD_CLOSE_2ND_CATEGORY 0x00020000
+#define MSM_SD_CLOSE_3RD_CATEGORY 0x00030000
+
+struct msm_sd_subdev {
+ struct v4l2_subdev sd;
+ int close_seq;
+};
+
+struct msm_sd_req_sd {
+ char *name;
+ struct v4l2_subdev *subdev;
+};
+
+struct msm_sd_req_vb2_q {
+ struct vb2_buffer *(*get_buf)(int session_id, unsigned int stream_id);
+ struct vb2_queue *(*get_vb2_queue)(int session_id,
+ unsigned int stream_id);
+ int (*put_buf)(struct vb2_buffer *vb2_buf);
+ int (*buf_done)(struct vb2_buffer *vb2_buf);
+};
+
+#define MSM_SD_NOTIFY_GET_SD 0x00000001
+#define MSM_SD_NOTIFY_PUT_SD 0x00000002
+#define MSM_SD_NOTIFY_REQ_CB 0x00000003
+
+int msm_sd_register(struct msm_sd_subdev *msm_subdev);
+int msm_sd_unregister(struct msm_sd_subdev *sd);
+struct v4l2_subdev *msm_sd_get_subdev(struct v4l2_subdev *sd,
+ const char *get_name);
+void msm_sd_put_subdev(struct v4l2_subdev *sd, struct v4l2_subdev *put);
+
+#endif /*_MSM_SD_H */
diff --git a/drivers/media/video/msmb/msm_vb2/Makefile b/drivers/media/video/msmb/msm_vb2/Makefile
new file mode 100644
index 0000000..9f61289
--- /dev/null
+++ b/drivers/media/video/msmb/msm_vb2/Makefile
@@ -0,0 +1,3 @@
+ccflags-y += -Idrivers/media/video/msmb
+ccflags-y += -Idrivers/media/video/msmb/msm_vb2
+obj-$(CONFIG_MSMB_CAMERA) += msm_vb2.o
diff --git a/drivers/media/video/msmb/msm_vb2/msm_vb2.c b/drivers/media/video/msmb/msm_vb2/msm_vb2.c
new file mode 100644
index 0000000..8a572a6
--- /dev/null
+++ b/drivers/media/video/msmb/msm_vb2/msm_vb2.c
@@ -0,0 +1,197 @@
+/* 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
+ * 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 "msm_vb2.h"
+
+static spinlock_t vb2_buf_lock;
+
+static int msm_vb2_queue_setup(struct vb2_queue *q,
+ const struct v4l2_format *fmt,
+ unsigned int *num_buffers, unsigned int *num_planes,
+ unsigned int sizes[], void *alloc_ctxs[])
+{
+ int i;
+ struct msm_v4l2_format_data *data = q->drv_priv;
+
+ if (data->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ if (WARN_ON(data->num_planes > VIDEO_MAX_PLANES))
+ return -EINVAL;
+
+ *num_planes = data->num_planes;
+
+ for (i = 0; i < data->num_planes; i++)
+ sizes[i] = data->plane_sizes[i];
+ } else {
+ pr_err("%s: Unsupported buf type :%d\n", __func__,
+ data->type);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+int msm_vb2_buf_init(struct vb2_buffer *vb)
+{
+ struct msm_vb2_buffer *msm_vb2_buf;
+
+ msm_vb2_buf = container_of(vb, struct msm_vb2_buffer, vb2_buf);
+ msm_vb2_buf->in_freeq = 0;
+ spin_lock_init(&vb2_buf_lock);
+
+ return 0;
+}
+
+static void msm_vb2_buf_queue(struct vb2_buffer *vb)
+{
+}
+
+static struct vb2_ops msm_vb2_get_q_op = {
+ .queue_setup = msm_vb2_queue_setup,
+ .buf_init = msm_vb2_buf_init,
+ .buf_queue = msm_vb2_buf_queue,
+};
+
+
+struct vb2_ops *msm_vb2_get_q_ops(void)
+{
+ return &msm_vb2_get_q_op;
+}
+
+static void *msm_vb2_dma_contig_get_userptr(void *alloc_ctx,
+ unsigned long vaddr, unsigned long size, int write)
+{
+ struct msm_vb2_private_data *priv;
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return ERR_PTR(-ENOMEM);
+ priv->vaddr = (void *)vaddr;
+ priv->size = size;
+ priv->alloc_ctx = alloc_ctx;
+ return priv;
+}
+
+static void msm_vb2_dma_contig_put_userptr(void *buf_priv)
+{
+ kfree(buf_priv);
+}
+
+static struct vb2_mem_ops msm_vb2_get_q_mem_op = {
+ .get_userptr = msm_vb2_dma_contig_get_userptr,
+ .put_userptr = msm_vb2_dma_contig_put_userptr,
+};
+
+struct vb2_mem_ops *msm_vb2_get_q_mem_ops(void)
+{
+ return &msm_vb2_get_q_mem_op;
+}
+
+static struct vb2_queue *msm_vb2_get_queue(int session_id,
+ unsigned int stream_id)
+{
+ return msm_get_stream_vb2q(session_id, stream_id);
+}
+
+static struct vb2_buffer *msm_vb2_get_buf(int session_id,
+ unsigned int stream_id)
+{
+ struct vb2_queue *q;
+ struct vb2_buffer *vb2_buf = NULL;
+ struct msm_vb2_buffer *msm_vb2;
+ unsigned long flags;
+
+ spin_lock_irqsave(&vb2_buf_lock, flags);
+
+ q = msm_get_stream_vb2q(session_id, stream_id);
+
+ /*FIXME: need a check if stream on issue*/
+ if (!q) {
+ pr_err("%s: stream q not available\n", __func__);
+ goto end;
+ }
+
+ list_for_each_entry(vb2_buf, &(q->queued_list),
+ queued_entry) {
+ if (vb2_buf->state != VB2_BUF_STATE_ACTIVE)
+ continue;
+
+ msm_vb2 = container_of(vb2_buf, struct msm_vb2_buffer, vb2_buf);
+ if (msm_vb2->in_freeq)
+ continue;
+
+ msm_vb2->in_freeq = 1;
+ goto end;
+ }
+ vb2_buf = NULL;
+end:
+ spin_unlock_irqrestore(&vb2_buf_lock, flags);
+ return vb2_buf;
+}
+
+static int msm_vb2_put_buf(struct vb2_buffer *vb)
+{
+ struct msm_vb2_buffer *msm_vb2;
+ int rc = 0;
+
+ if (vb) {
+ msm_vb2 =
+ container_of(vb, struct msm_vb2_buffer, vb2_buf);
+ if (msm_vb2->in_freeq) {
+ msm_vb2->in_freeq = 0;
+ rc = 0;
+ } else
+ rc = -EINVAL;
+ } else {
+ pr_err("%s: VB buffer is null\n", __func__);
+ rc = -EINVAL;
+ }
+ return rc;
+}
+
+static int msm_vb2_buf_done(struct vb2_buffer *vb)
+{
+ unsigned long flags;
+ struct msm_vb2_buffer *msm_vb2;
+ int rc = 0;
+
+ spin_lock_irqsave(&vb2_buf_lock, flags);
+ if (vb) {
+ msm_vb2 =
+ container_of(vb, struct msm_vb2_buffer, vb2_buf);
+ /* put buf before buf done */
+ if (msm_vb2->in_freeq) {
+ vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
+ rc = 0;
+ } else
+ rc = -EINVAL;
+ } else {
+ pr_err("%s: VB buffer is null\n", __func__);
+ rc = -EINVAL;
+ }
+
+ spin_unlock_irqrestore(&vb2_buf_lock, flags);
+ return rc;
+}
+
+int msm_vb2_request_cb(struct msm_sd_req_vb2_q *req)
+{
+ if (!req) {
+ pr_err("%s: suddev is null\n", __func__);
+ return -EINVAL;
+ }
+
+ req->get_buf = msm_vb2_get_buf;
+ req->get_vb2_queue = msm_vb2_get_queue;
+ req->put_buf = msm_vb2_put_buf;
+ req->buf_done = msm_vb2_buf_done;
+
+ return 0;
+}
+
diff --git a/drivers/media/video/msmb/msm_vb2/msm_vb2.h b/drivers/media/video/msmb/msm_vb2/msm_vb2.h
new file mode 100644
index 0000000..148d577
--- /dev/null
+++ b/drivers/media/video/msmb/msm_vb2/msm_vb2.h
@@ -0,0 +1,58 @@
+/* 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
+ * 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 _MSM_VB_H
+#define _MSM_VB_H
+
+#include <linux/version.h>
+#include <linux/i2c.h>
+#include <linux/videodev2.h>
+#include <linux/pm_qos.h>
+#include <linux/wakelock.h>
+#include <linux/msm_ion.h>
+#include <linux/iommu.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-mediabus.h>
+#include <media/videobuf2-dma-contig.h>
+#include <media/videobuf2-msm-mem.h>
+#include <media/msmb_camera.h>
+#include <media/videobuf2-core.h>
+#include "msm.h"
+#include "msm_sd.h"
+
+struct msm_vb2_buffer {
+ /*
+ * vb2 buffer has to be first in the structure
+ * because both v4l2 frameworks and driver directly
+ * cast msm_vb2_buffer to a vb2_buf.
+ */
+ struct vb2_buffer vb2_buf;
+ struct list_head list;
+ int in_freeq;
+};
+
+struct msm_vb2_private_data {
+ void *vaddr;
+ unsigned long size;
+ /* Offset of the plane inside the buffer */
+ void *alloc_ctx;
+};
+
+struct vb2_ops *msm_vb2_get_q_ops(void);
+struct vb2_mem_ops *msm_vb2_get_q_mem_ops(void);
+int msm_vb2_request_cb(struct msm_sd_req_vb2_q *req_sd);
+
+#endif /*_MSM_VB_H */
diff --git a/drivers/media/video/msmb/sensor/Makefile b/drivers/media/video/msmb/sensor/Makefile
new file mode 100644
index 0000000..5fa3f6e
--- /dev/null
+++ b/drivers/media/video/msmb/sensor/Makefile
@@ -0,0 +1,8 @@
+ccflags-y += -Idrivers/media/video/msmb
+ccflags-y += -Idrivers/media/video/msmb/msm_vb2
+ccflags-y += -Idrivers/media/video/msmb/camera
+ccflags-y += -Idrivers/media/video/msmb/sensor/io
+ccflags-y += -Idrivers/media/video/msmb/sensor/cci
+obj-$(CONFIG_MSMB_CAMERA) += cci/ io/ csiphy/ csid/
+obj-$(CONFIG_MSM_CAMERA_SENSOR) += msm_sensor.o
+obj-$(CONFIG_S5K3L1YX) += s5k3l1yx.o
diff --git a/drivers/media/video/msmb/sensor/cci/Makefile b/drivers/media/video/msmb/sensor/cci/Makefile
new file mode 100644
index 0000000..8eef3fc
--- /dev/null
+++ b/drivers/media/video/msmb/sensor/cci/Makefile
@@ -0,0 +1,3 @@
+ccflags-y += -Idrivers/media/video/msmb/
+ccflags-y += -Idrivers/media/video/msmb/sensor/io
+obj-$(CONFIG_MSM_CCI) += msm_cci.o
diff --git a/drivers/media/video/msmb/sensor/cci/msm_cam_cci_hwreg.h b/drivers/media/video/msmb/sensor/cci/msm_cam_cci_hwreg.h
new file mode 100644
index 0000000..19cff3b
--- /dev/null
+++ b/drivers/media/video/msmb/sensor/cci/msm_cam_cci_hwreg.h
@@ -0,0 +1,61 @@
+/* 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
+ * 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 __MSM_CAM_CCI_HWREG__
+#define __MSM_CAM_CCI_HWREG__
+
+#define CCI_HW_VERSION_ADDR 0x00000000
+#define CCI_RESET_CMD_ADDR 0x00000004
+#define CCI_RESET_CMD_RMSK 0xcf73f3f7
+#define CCI_M0_RESET_RMSK 0x3F1
+#define CCI_M1_RESET_RMSK 0x3F001
+#define CCI_QUEUE_START_ADDR 0x00000008
+#define CCI_SET_CID_SYNC_TIMER_0_ADDR 0x00000010
+#define CCI_I2C_M0_SCL_CTL_ADDR 0x00000100
+#define CCI_I2C_M0_SDA_CTL_0_ADDR 0x00000104
+#define CCI_I2C_M0_SDA_CTL_1_ADDR 0x00000108
+#define CCI_I2C_M0_SDA_CTL_2_ADDR 0x0000010c
+#define CCI_I2C_M0_READ_DATA_ADDR 0x00000118
+#define CCI_I2C_M0_MISC_CTL_ADDR 0x00000110
+#define CCI_I2C_M0_READ_BUF_LEVEL_ADDR 0x0000011C
+#define CCI_HALT_REQ_ADDR 0x00000034
+#define CCI_M0_HALT_REQ_RMSK 0x1
+#define CCI_M1_HALT_REQ_RMSK 0x01
+#define CCI_HALT_REQ_ADDR 0x00000034
+#define CCI_I2C_M1_SCL_CTL_ADDR 0x00000200
+#define CCI_I2C_M1_SDA_CTL_0_ADDR 0x00000204
+#define CCI_I2C_M1_SDA_CTL_1_ADDR 0x00000208
+#define CCI_I2C_M1_SDA_CTL_2_ADDR 0x0000020c
+#define CCI_I2C_M1_MISC_CTL_ADDR 0x00000210
+#define CCI_I2C_M0_Q0_CUR_WORD_CNT_ADDR 0x00000304
+#define CCI_I2C_M0_Q0_CUR_CMD_ADDR 0x00000308
+#define CCI_I2C_M0_Q0_EXEC_WORD_CNT_ADDR 0x00000300
+#define CCI_I2C_M0_Q0_LOAD_DATA_ADDR 0x00000310
+#define CCI_IRQ_MASK_0_ADDR 0x00000c04
+#define CCI_IRQ_MASK_0_RMSK 0x7fff7ff7
+#define CCI_IRQ_CLEAR_0_ADDR 0x00000c08
+#define CCI_IRQ_STATUS_0_ADDR 0x00000c0c
+#define CCI_IRQ_STATUS_0_I2C_M1_Q1_NACK_ERR_BMSK 0x40000000
+#define CCI_IRQ_STATUS_0_I2C_M1_Q0_NACK_ERR_BMSK 0x20000000
+#define CCI_IRQ_STATUS_0_I2C_M0_Q1_NACK_ERR_BMSK 0x10000000
+#define CCI_IRQ_STATUS_0_I2C_M0_Q0_NACK_ERR_BMSK 0x8000000
+#define CCI_IRQ_STATUS_0_I2C_M1_Q0Q1_HALT_ACK_BMSK 0x4000000
+#define CCI_IRQ_STATUS_0_I2C_M0_Q0Q1_HALT_ACK_BMSK 0x2000000
+#define CCI_IRQ_STATUS_0_RST_DONE_ACK_BMSK 0x1000000
+#define CCI_IRQ_STATUS_0_I2C_M1_Q1_REPORT_BMSK 0x100000
+#define CCI_IRQ_STATUS_0_I2C_M1_Q0_REPORT_BMSK 0x10000
+#define CCI_IRQ_STATUS_0_I2C_M1_RD_DONE_BMSK 0x1000
+#define CCI_IRQ_STATUS_0_I2C_M0_Q1_REPORT_BMSK 0x100
+#define CCI_IRQ_STATUS_0_I2C_M0_Q0_REPORT_BMSK 0x10
+#define CCI_IRQ_STATUS_0_I2C_M0_RD_DONE_BMSK 0x1
+#define CCI_IRQ_GLOBAL_CLEAR_CMD_ADDR 0x00000c00
+#endif /* __MSM_CAM_CCI_HWREG__ */
diff --git a/drivers/media/video/msmb/sensor/cci/msm_cci.c b/drivers/media/video/msmb/sensor/cci/msm_cci.c
new file mode 100644
index 0000000..b1c9a40
--- /dev/null
+++ b/drivers/media/video/msmb/sensor/cci/msm_cci.c
@@ -0,0 +1,969 @@
+/* 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
+ * 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/delay.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/of_platform.h>
+#include <media/msm_isp.h>
+#include "msm_sd.h"
+#include "msm_cci.h"
+#include "msm_cam_cci_hwreg.h"
+#include "msm_camera_io_util.h"
+
+#define V4L2_IDENT_CCI 50005
+#define CCI_I2C_QUEUE_0_SIZE 64
+#define CCI_I2C_QUEUE_1_SIZE 16
+
+#define CCI_TIMEOUT msecs_to_jiffies(100)
+
+/* TODO move this somewhere else */
+#define MSM_CCI_DRV_NAME "msm_cci"
+
+#define CONFIG_MSMB_CAMERA_DEBUG
+#undef CDBG
+#ifdef CONFIG_MSMB_CAMERA_DEBUG
+#define CDBG(fmt, args...) pr_debug(fmt, ##args)
+#else
+#define CDBG(fmt, args...) do { } while (0)
+#endif
+
+static struct v4l2_subdev *g_cci_subdev;
+
+static void msm_cci_set_clk_param(struct cci_device *cci_dev)
+{
+ struct msm_cci_clk_params_t *clk_params = &cci_dev->cci_clk_params;
+
+ msm_camera_io_w(clk_params->hw_thigh << 16 | clk_params->hw_tlow,
+ cci_dev->base + CCI_I2C_M0_SCL_CTL_ADDR);
+ msm_camera_io_w(clk_params->hw_tsu_sto << 16 | clk_params->hw_tsu_sta,
+ cci_dev->base + CCI_I2C_M0_SDA_CTL_0_ADDR);
+ msm_camera_io_w(clk_params->hw_thd_dat << 16 | clk_params->hw_thd_sta,
+ cci_dev->base + CCI_I2C_M0_SDA_CTL_1_ADDR);
+ msm_camera_io_w(clk_params->hw_tbuf,
+ cci_dev->base + CCI_I2C_M0_SDA_CTL_2_ADDR);
+ msm_camera_io_w(clk_params->hw_scl_stretch_en << 8 |
+ clk_params->hw_trdhld << 4 | clk_params->hw_tsp,
+ cci_dev->base + CCI_I2C_M0_MISC_CTL_ADDR);
+ msm_camera_io_w(clk_params->hw_thigh << 16 | clk_params->hw_tlow,
+ cci_dev->base + CCI_I2C_M1_SCL_CTL_ADDR);
+ msm_camera_io_w(clk_params->hw_tsu_sto << 16 | clk_params->hw_tsu_sta,
+ cci_dev->base + CCI_I2C_M1_SDA_CTL_0_ADDR);
+ msm_camera_io_w(clk_params->hw_thd_dat << 16 | clk_params->hw_thd_sta,
+ cci_dev->base + CCI_I2C_M1_SDA_CTL_1_ADDR);
+ msm_camera_io_w(clk_params->hw_tbuf,
+ cci_dev->base + CCI_I2C_M1_SDA_CTL_2_ADDR);
+ msm_camera_io_w(clk_params->hw_scl_stretch_en << 8 |
+ clk_params->hw_trdhld << 4 | clk_params->hw_tsp,
+ cci_dev->base + CCI_I2C_M1_MISC_CTL_ADDR);
+ return;
+}
+
+static int32_t msm_cci_i2c_config_sync_timer(struct v4l2_subdev *sd,
+ struct msm_camera_cci_ctrl *c_ctrl)
+{
+ struct cci_device *cci_dev;
+ cci_dev = v4l2_get_subdevdata(sd);
+ msm_camera_io_w(c_ctrl->cci_info->cid, cci_dev->base +
+ CCI_SET_CID_SYNC_TIMER_0_ADDR + (c_ctrl->cci_info->cid * 0x4));
+ return 0;
+}
+
+static int32_t msm_cci_i2c_set_freq(struct v4l2_subdev *sd,
+ struct msm_camera_cci_ctrl *c_ctrl)
+{
+ struct cci_device *cci_dev;
+ uint32_t val;
+ cci_dev = v4l2_get_subdevdata(sd);
+ val = c_ctrl->cci_info->freq;
+ msm_camera_io_w(val, cci_dev->base + CCI_I2C_M0_SCL_CTL_ADDR +
+ c_ctrl->cci_info->cci_i2c_master*0x100);
+ msm_camera_io_w(val, cci_dev->base + CCI_I2C_M0_SDA_CTL_0_ADDR +
+ c_ctrl->cci_info->cci_i2c_master*0x100);
+ msm_camera_io_w(val, cci_dev->base + CCI_I2C_M0_SDA_CTL_1_ADDR +
+ c_ctrl->cci_info->cci_i2c_master*0x100);
+ msm_camera_io_w(val, cci_dev->base + CCI_I2C_M0_SDA_CTL_2_ADDR +
+ c_ctrl->cci_info->cci_i2c_master*0x100);
+ msm_camera_io_w(val, cci_dev->base + CCI_I2C_M0_MISC_CTL_ADDR +
+ c_ctrl->cci_info->cci_i2c_master*0x100);
+ return 0;
+}
+
+static int32_t msm_cci_validate_queue(struct cci_device *cci_dev,
+ uint32_t len,
+ enum cci_i2c_master_t master,
+ enum cci_i2c_queue_t queue)
+{
+ int32_t rc = 0;
+ uint32_t read_val = 0;
+ uint32_t reg_offset = master * 0x200 + queue * 0x100;
+ read_val = msm_camera_io_r(cci_dev->base +
+ CCI_I2C_M0_Q0_CUR_WORD_CNT_ADDR + reg_offset);
+ CDBG("%s line %d CCI_I2C_M0_Q0_CUR_WORD_CNT_ADDR %d len %d max %d\n",
+ __func__, __LINE__, read_val, len,
+ cci_dev->cci_i2c_queue_info[master][queue].max_queue_size);
+ if ((read_val + len + 1) > cci_dev->
+ cci_i2c_queue_info[master][queue].max_queue_size) {
+ uint32_t reg_val = 0;
+ uint32_t report_val = CCI_I2C_REPORT_CMD | (1 << 8);
+ CDBG("%s:%d CCI_I2C_REPORT_CMD\n", __func__, __LINE__);
+ msm_camera_io_w(report_val,
+ cci_dev->base + CCI_I2C_M0_Q0_LOAD_DATA_ADDR +
+ reg_offset);
+ read_val++;
+ CDBG("%s:%d CCI_I2C_M0_Q0_EXEC_WORD_CNT_ADDR %d\n",
+ __func__, __LINE__, read_val);
+ msm_camera_io_w(read_val, cci_dev->base +
+ CCI_I2C_M0_Q0_EXEC_WORD_CNT_ADDR + reg_offset);
+ reg_val = 1 << ((master * 2) + queue);
+ CDBG("%s:%d CCI_QUEUE_START_ADDR\n", __func__, __LINE__);
+ msm_camera_io_w(reg_val, cci_dev->base + CCI_QUEUE_START_ADDR);
+ CDBG("%s line %d wait_for_completion_interruptible\n",
+ __func__, __LINE__);
+ rc = wait_for_completion_interruptible_timeout(&cci_dev->
+ cci_master_info[master].reset_complete, CCI_TIMEOUT);
+ if (rc <= 0) {
+ pr_err("%s: wait_for_completion_interruptible_timeout %d\n",
+ __func__, __LINE__);
+ if (rc == 0)
+ rc = -ETIMEDOUT;
+ return rc;
+ }
+ rc = cci_dev->cci_master_info[master].status;
+ if (rc < 0)
+ pr_err("%s failed rc %d\n", __func__, rc);
+ }
+ return rc;
+}
+
+static int32_t msm_cci_data_queue(struct cci_device *cci_dev,
+ struct msm_camera_cci_ctrl *c_ctrl, enum cci_i2c_queue_t queue)
+{
+ uint16_t i = 0, j = 0, k = 0, h = 0, len = 0;
+ int32_t rc = 0;
+ uint32_t cmd = 0;
+ uint8_t data[10];
+ uint16_t reg_addr = 0;
+ struct msm_camera_cci_i2c_write_cfg *i2c_msg =
+ &c_ctrl->cfg.cci_i2c_write_cfg;
+ uint16_t cmd_size = i2c_msg->size;
+ struct msm_camera_i2c_reg_conf *i2c_cmd = i2c_msg->reg_conf_tbl;
+ enum cci_i2c_master_t master = c_ctrl->cci_info->cci_i2c_master;
+ CDBG("%s addr type %d data type %d\n", __func__,
+ i2c_msg->addr_type, i2c_msg->data_type);
+ /* assume total size within the max queue */
+ while (cmd_size) {
+ CDBG("%s cmd_size %d addr 0x%x data 0x%x", __func__,
+ cmd_size, i2c_cmd->reg_addr, i2c_cmd->reg_data);
+ data[i++] = CCI_I2C_WRITE_CMD;
+ if (i2c_cmd->reg_addr)
+ reg_addr = i2c_cmd->reg_addr;
+ /* either byte or word addr */
+ if (i2c_msg->addr_type == MSM_CAMERA_I2C_BYTE_ADDR)
+ data[i++] = reg_addr;
+ else {
+ data[i++] = (reg_addr & 0xFF00) >> 8;
+ data[i++] = reg_addr & 0x00FF;
+ }
+ /* max of 10 data bytes */
+ do {
+ if (i2c_msg->data_type == MSM_CAMERA_I2C_BYTE_DATA) {
+ data[i++] = i2c_cmd->reg_data;
+ reg_addr++;
+ } else {
+ if ((i + 1) <= 10) {
+ data[i++] = (i2c_cmd->reg_data &
+ 0xFF00) >> 8; /* MSB */
+ data[i++] = i2c_cmd->reg_data &
+ 0x00FF; /* LSB */
+ reg_addr += 2;
+ } else
+ break;
+ }
+ i2c_cmd++;
+ } while (--cmd_size && !i2c_cmd->reg_addr && (i <= 10));
+ data[0] |= ((i-1) << 4);
+ len = ((i-1)/4) + 1;
+ rc = msm_cci_validate_queue(cci_dev, len, master, queue);
+ if (rc < 0) {
+ pr_err("%s: failed %d", __func__, __LINE__);
+ return rc;
+ }
+ for (h = 0, k = 0; h < len; h++) {
+ cmd = 0;
+ for (j = 0; (j < 4 && k < i); j++)
+ cmd |= (data[k++] << (j * 8));
+ CDBG("%s CCI_I2C_M0_Q0_LOAD_DATA_ADDR 0x%x\n",
+ __func__, cmd);
+ msm_camera_io_w(cmd, cci_dev->base +
+ CCI_I2C_M0_Q0_LOAD_DATA_ADDR +
+ master * 0x200 + queue * 0x100);
+ }
+ i = 0;
+ }
+ return rc;
+}
+
+static int32_t msm_cci_write_i2c_queue(struct cci_device *cci_dev,
+ uint32_t val,
+ enum cci_i2c_master_t master,
+ enum cci_i2c_queue_t queue)
+{
+ int32_t rc = 0;
+ uint32_t reg_offset = master * 0x200 + queue * 0x100;
+ CDBG("%s:%d called\n", __func__, __LINE__);
+ rc = msm_cci_validate_queue(cci_dev, 1, master, queue);
+ if (rc < 0) {
+ pr_err("%s: failed %d", __func__, __LINE__);
+ return rc;
+ }
+ CDBG("%s CCI_I2C_M0_Q0_LOAD_DATA_ADDR:val %x:%x\n",
+ __func__, CCI_I2C_M0_Q0_LOAD_DATA_ADDR +
+ reg_offset, val);
+ msm_camera_io_w(val, cci_dev->base + CCI_I2C_M0_Q0_LOAD_DATA_ADDR +
+ reg_offset);
+ return rc;
+}
+
+static int32_t msm_cci_i2c_read(struct v4l2_subdev *sd,
+ struct msm_camera_cci_ctrl *c_ctrl)
+{
+ uint32_t rc = 0;
+ uint32_t val = 0;
+ int32_t read_words = 0, exp_words = 0;
+ int32_t index = 0, first_byte = 0;
+ uint32_t i = 0;
+ enum cci_i2c_master_t master;
+ enum cci_i2c_queue_t queue = QUEUE_1;
+ struct cci_device *cci_dev = NULL;
+ struct msm_camera_cci_i2c_read_cfg *read_cfg = NULL;
+ CDBG("%s line %d\n", __func__, __LINE__);
+ cci_dev = v4l2_get_subdevdata(sd);
+ master = c_ctrl->cci_info->cci_i2c_master;
+ read_cfg = &c_ctrl->cfg.cci_i2c_read_cfg;
+ mutex_lock(&cci_dev->cci_master_info[master].mutex);
+ CDBG("%s master %d, queue %d\n", __func__, master, queue);
+ CDBG("%s set param sid 0x%x retries %d id_map %d\n", __func__,
+ c_ctrl->cci_info->sid, c_ctrl->cci_info->retries,
+ c_ctrl->cci_info->id_map);
+ val = CCI_I2C_SET_PARAM_CMD | c_ctrl->cci_info->sid << 4 |
+ c_ctrl->cci_info->retries << 16 |
+ c_ctrl->cci_info->id_map << 18;
+ rc = msm_cci_write_i2c_queue(cci_dev, val, master, queue);
+ if (rc < 0) {
+ CDBG("%s failed line %d\n", __func__, __LINE__);
+ goto ERROR;
+ }
+
+ val = CCI_I2C_LOCK_CMD;
+ rc = msm_cci_write_i2c_queue(cci_dev, val, master, queue);
+ if (rc < 0) {
+ CDBG("%s failed line %d\n", __func__, __LINE__);
+ goto ERROR;
+ }
+
+ if (read_cfg->addr_type == MSM_CAMERA_I2C_BYTE_ADDR)
+ val = CCI_I2C_WRITE_CMD | (read_cfg->addr_type << 4) |
+ ((read_cfg->addr & 0xFF) << 8);
+ if (read_cfg->addr_type == MSM_CAMERA_I2C_WORD_ADDR)
+ val = CCI_I2C_WRITE_CMD | (read_cfg->addr_type << 4) |
+ (((read_cfg->addr & 0xFF00) >> 8) << 8) |
+ ((read_cfg->addr & 0xFF) << 16);
+ rc = msm_cci_write_i2c_queue(cci_dev, val, master, queue);
+ if (rc < 0) {
+ CDBG("%s failed line %d\n", __func__, __LINE__);
+ goto ERROR;
+ }
+
+ val = CCI_I2C_READ_CMD | (read_cfg->num_byte << 4);
+ rc = msm_cci_write_i2c_queue(cci_dev, val, master, queue);
+ if (rc < 0) {
+ CDBG("%s failed line %d\n", __func__, __LINE__);
+ goto ERROR;
+ }
+
+ val = CCI_I2C_UNLOCK_CMD;
+ rc = msm_cci_write_i2c_queue(cci_dev, val, master, queue);
+ if (rc < 0) {
+ CDBG("%s failed line %d\n", __func__, __LINE__);
+ goto ERROR;
+ }
+
+ val = msm_camera_io_r(cci_dev->base + CCI_I2C_M0_Q0_CUR_WORD_CNT_ADDR +
+ master * 0x200 + queue * 0x100);
+ CDBG("%s cur word cnt %x\n", __func__, val);
+ msm_camera_io_w(val, cci_dev->base + CCI_I2C_M0_Q0_EXEC_WORD_CNT_ADDR +
+ master * 0x200 + queue * 0x100);
+
+ val = 1 << ((master * 2) + queue);
+ msm_camera_io_w(val, cci_dev->base + CCI_QUEUE_START_ADDR);
+ CDBG("%s:%d E wait_for_completion_interruptible_timeout\n", __func__,
+ __LINE__);
+ rc = wait_for_completion_interruptible_timeout(&cci_dev->
+ cci_master_info[master].reset_complete, CCI_TIMEOUT);
+ if (rc <= 0) {
+ pr_err("%s: wait_for_completion_interruptible_timeout %d\n",
+ __func__, __LINE__);
+ if (rc == 0)
+ rc = -ETIMEDOUT;
+ return rc;
+ } else {
+ rc = 0;
+ }
+ CDBG("%s:%d E wait_for_completion_interruptible_timeout\n", __func__,
+ __LINE__);
+
+ read_words = msm_camera_io_r(cci_dev->base +
+ CCI_I2C_M0_READ_BUF_LEVEL_ADDR + master * 0x100);
+ exp_words = ((read_cfg->num_byte / 4) + 1);
+ if (read_words != exp_words) {
+ pr_err("%s:%d read_words = %d, exp words = %d\n", __func__,
+ __LINE__, read_words, exp_words);
+ memset(read_cfg->data, 0, read_cfg->num_byte);
+ rc = -EINVAL;
+ goto ERROR;
+ }
+ index = 0;
+ CDBG("%s index %d num_type %d\n", __func__, index,
+ read_cfg->num_byte);
+ first_byte = 0;
+ do {
+ val = msm_camera_io_r(cci_dev->base +
+ CCI_I2C_M0_READ_DATA_ADDR + master * 0x100);
+ CDBG("%s read val %x\n", __func__, val);
+ for (i = 0; (i < 4) && (index < read_cfg->num_byte); i++) {
+ CDBG("%s i %d index %d\n", __func__, i, index);
+ if (!first_byte) {
+ CDBG("%s sid %x\n", __func__, val & 0xFF);
+ first_byte++;
+ } else {
+ read_cfg->data[index] =
+ (val >> (i * 8)) & 0xFF;
+ CDBG("%s data[%d] %x\n", __func__, index,
+ read_cfg->data[index]);
+ index++;
+ }
+ }
+ } while (--read_words > 0);
+ERROR:
+ mutex_unlock(&cci_dev->cci_master_info[master].mutex);
+ return rc;
+}
+
+static int32_t msm_cci_i2c_write(struct v4l2_subdev *sd,
+ struct msm_camera_cci_ctrl *c_ctrl)
+{
+ int32_t rc = 0;
+ struct cci_device *cci_dev;
+ uint32_t val;
+ enum cci_i2c_master_t master;
+ enum cci_i2c_queue_t queue = QUEUE_0;
+ cci_dev = v4l2_get_subdevdata(sd);
+ master = c_ctrl->cci_info->cci_i2c_master;
+ CDBG("%s master %d, queue %d\n", __func__, master, queue);
+ CDBG("%s set param sid 0x%x retries %d id_map %d\n", __func__,
+ c_ctrl->cci_info->sid, c_ctrl->cci_info->retries,
+ c_ctrl->cci_info->id_map);
+ mutex_lock(&cci_dev->cci_master_info[master].mutex);
+ val = CCI_I2C_SET_PARAM_CMD | c_ctrl->cci_info->sid << 4 |
+ c_ctrl->cci_info->retries << 16 |
+ c_ctrl->cci_info->id_map << 18;
+ CDBG("%s:%d CCI_I2C_SET_PARAM_CMD\n", __func__, __LINE__);
+ rc = msm_cci_write_i2c_queue(cci_dev, val, master, queue);
+ if (rc < 0) {
+ CDBG("%s failed line %d\n", __func__, __LINE__);
+ goto ERROR;
+ }
+
+ val = CCI_I2C_LOCK_CMD;
+ CDBG("%s:%d CCI_I2C_LOCK_CMD\n", __func__, __LINE__);
+ rc = msm_cci_write_i2c_queue(cci_dev, val, master, queue);
+ if (rc < 0) {
+ CDBG("%s failed line %d\n", __func__, __LINE__);
+ goto ERROR;
+ }
+
+ msm_cci_data_queue(cci_dev, c_ctrl, queue);
+ val = CCI_I2C_UNLOCK_CMD;
+ CDBG("%s:%d CCI_I2C_UNLOCK_CMD\n", __func__, __LINE__);
+ rc = msm_cci_write_i2c_queue(cci_dev, val, master, queue);
+ if (rc < 0) {
+ CDBG("%s failed line %d\n", __func__, __LINE__);
+ goto ERROR;
+ }
+
+ val = CCI_I2C_REPORT_CMD | (1 << 8);
+ CDBG("%s:%d CCI_I2C_REPORT_CMD\n", __func__, __LINE__);
+ rc = msm_cci_write_i2c_queue(cci_dev, val, master, queue);
+ if (rc < 0) {
+ CDBG("%s failed line %d\n", __func__, __LINE__);
+ goto ERROR;
+ }
+
+ val = msm_camera_io_r(cci_dev->base + CCI_I2C_M0_Q0_CUR_WORD_CNT_ADDR +
+ master * 0x200 + queue * 0x100);
+ CDBG("%s:%d cur word count %d\n", __func__, __LINE__, val);
+ CDBG("%s:%d CCI_I2C_M0_Q0_EXEC_WORD_CNT_ADDR\n", __func__, __LINE__);
+ msm_camera_io_w(val, cci_dev->base + CCI_I2C_M0_Q0_EXEC_WORD_CNT_ADDR +
+ master * 0x200 + queue * 0x100);
+
+ val = 1 << ((master * 2) + queue);
+ CDBG("%s:%d CCI_QUEUE_START_ADDR\n", __func__, __LINE__);
+ msm_camera_io_w(val, cci_dev->base + CCI_QUEUE_START_ADDR +
+ master*0x200 + queue * 0x100);
+
+ CDBG("%s:%d E wait_for_completion_interruptible\n",
+ __func__, __LINE__);
+ rc = wait_for_completion_interruptible_timeout(&cci_dev->
+ cci_master_info[master].reset_complete, CCI_TIMEOUT);
+ if (rc <= 0) {
+ pr_err("%s: wait_for_completion_interruptible_timeout %d\n",
+ __func__, __LINE__);
+ if (rc == 0)
+ rc = -ETIMEDOUT;
+ return rc;
+ } else {
+ rc = 0;
+ }
+ CDBG("%s:%d X wait_for_completion_interruptible\n", __func__,
+ __LINE__);
+
+ERROR:
+ mutex_unlock(&cci_dev->cci_master_info[master].mutex);
+ return rc;
+}
+
+static int msm_cci_subdev_g_chip_ident(struct v4l2_subdev *sd,
+ struct v4l2_dbg_chip_ident *chip)
+{
+ BUG_ON(!chip);
+ chip->ident = V4L2_IDENT_CCI;
+ chip->revision = 0;
+ return 0;
+}
+
+static struct msm_cam_clk_info cci_clk_info[] = {
+ {"camss_top_ahb_clk", -1},
+ {"cci_src_clk", 19200000},
+ {"cci_ahb_clk", -1},
+ {"cci_clk", -1},
+};
+
+static int32_t msm_cci_init(struct v4l2_subdev *sd)
+{
+ int rc = 0;
+ struct cci_device *cci_dev;
+ cci_dev = v4l2_get_subdevdata(sd);
+ CDBG("%s line %d\n", __func__, __LINE__);
+
+ if (!cci_dev) {
+ pr_err("%s cci device NULL\n", __func__);
+ rc = -ENOMEM;
+ return rc;
+ }
+
+ if (cci_dev->ref_count++) {
+ CDBG("%s ref_count %d\n", __func__, cci_dev->ref_count);
+ return 0;
+ }
+
+ rc = msm_camera_request_gpio_table(cci_dev->cci_gpio_tbl,
+ cci_dev->cci_gpio_tbl_size, 1);
+ if (rc < 0) {
+ cci_dev->ref_count--;
+ CDBG("%s: request gpio failed\n", __func__);
+ goto clk_enable_failed;
+ }
+
+ rc = msm_cam_clk_enable(&cci_dev->pdev->dev, cci_clk_info,
+ cci_dev->cci_clk, ARRAY_SIZE(cci_clk_info), 1);
+ if (rc < 0) {
+ cci_dev->ref_count--;
+ CDBG("%s: clk enable failed\n", __func__);
+ goto clk_enable_failed;
+ }
+
+ enable_irq(cci_dev->irq->start);
+ cci_dev->hw_version = msm_camera_io_r(cci_dev->base +
+ CCI_HW_VERSION_ADDR);
+ cci_dev->cci_master_info[MASTER_0].reset_pending = TRUE;
+ msm_camera_io_w(CCI_RESET_CMD_RMSK, cci_dev->base + CCI_RESET_CMD_ADDR);
+ msm_camera_io_w(0x1, cci_dev->base + CCI_RESET_CMD_ADDR);
+ rc = wait_for_completion_interruptible_timeout(
+ &cci_dev->cci_master_info[MASTER_0].reset_complete,
+ CCI_TIMEOUT);
+ if (rc <= 0) {
+ pr_err("%s: wait_for_completion_interruptible_timeout %d\n",
+ __func__, __LINE__);
+ if (rc == 0)
+ rc = -ETIMEDOUT;
+ return rc;
+ }
+ msm_cci_set_clk_param(cci_dev);
+ msm_camera_io_w(CCI_IRQ_MASK_0_RMSK,
+ cci_dev->base + CCI_IRQ_MASK_0_ADDR);
+ msm_camera_io_w(CCI_IRQ_MASK_0_RMSK,
+ cci_dev->base + CCI_IRQ_CLEAR_0_ADDR);
+ msm_camera_io_w(0x1, cci_dev->base + CCI_IRQ_GLOBAL_CLEAR_CMD_ADDR);
+ cci_dev->cci_state = CCI_STATE_ENABLED;
+ return 0;
+
+clk_enable_failed:
+ return rc;
+}
+
+static int32_t msm_cci_release(struct v4l2_subdev *sd)
+{
+ struct cci_device *cci_dev;
+ cci_dev = v4l2_get_subdevdata(sd);
+
+ if (!cci_dev->ref_count || cci_dev->cci_state != CCI_STATE_ENABLED) {
+ pr_err("%s invalid ref count %d / cci state %d\n",
+ __func__, cci_dev->ref_count, cci_dev->cci_state);
+ return -EINVAL;
+ }
+
+ if (--cci_dev->ref_count) {
+ CDBG("%s ref_count %d\n", __func__, cci_dev->ref_count);
+ return 0;
+ }
+
+ disable_irq(cci_dev->irq->start);
+
+ msm_cam_clk_enable(&cci_dev->pdev->dev, cci_clk_info,
+ cci_dev->cci_clk, ARRAY_SIZE(cci_clk_info), 0);
+
+ msm_camera_request_gpio_table(cci_dev->cci_gpio_tbl,
+ cci_dev->cci_gpio_tbl_size, 0);
+
+ cci_dev->cci_state = CCI_STATE_DISABLED;
+ return 0;
+}
+
+static int32_t msm_cci_config(struct v4l2_subdev *sd,
+ struct msm_camera_cci_ctrl *cci_ctrl)
+{
+ int32_t rc = 0;
+ CDBG("%s line %d cmd %d\n", __func__, __LINE__,
+ cci_ctrl->cmd);
+ switch (cci_ctrl->cmd) {
+ case MSM_CCI_INIT:
+ rc = msm_cci_init(sd);
+ break;
+ case MSM_CCI_RELEASE:
+ rc = msm_cci_release(sd);
+ break;
+ case MSM_CCI_SET_SID:
+ break;
+ case MSM_CCI_SET_FREQ:
+ rc = msm_cci_i2c_set_freq(sd, cci_ctrl);
+ break;
+ case MSM_CCI_SET_SYNC_CID:
+ rc = msm_cci_i2c_config_sync_timer(sd, cci_ctrl);
+ break;
+ case MSM_CCI_I2C_READ:
+ rc = msm_cci_i2c_read(sd, cci_ctrl);
+ break;
+ case MSM_CCI_I2C_WRITE:
+ rc = msm_cci_i2c_write(sd, cci_ctrl);
+ break;
+ case MSM_CCI_GPIO_WRITE:
+ break;
+ default:
+ rc = -ENOIOCTLCMD;
+ }
+ CDBG("%s line %d rc %d\n", __func__, __LINE__, rc);
+ cci_ctrl->status = rc;
+ return rc;
+}
+
+static irqreturn_t msm_cci_irq(int irq_num, void *data)
+{
+ uint32_t irq;
+ struct cci_device *cci_dev = data;
+ irq = msm_camera_io_r(cci_dev->base + CCI_IRQ_STATUS_0_ADDR);
+ msm_camera_io_w(irq, cci_dev->base + CCI_IRQ_CLEAR_0_ADDR);
+ msm_camera_io_w(0x1, cci_dev->base + CCI_IRQ_GLOBAL_CLEAR_CMD_ADDR);
+ msm_camera_io_w(0x0, cci_dev->base + CCI_IRQ_GLOBAL_CLEAR_CMD_ADDR);
+ CDBG("%s CCI_I2C_M0_STATUS_ADDR = 0x%x\n", __func__, irq);
+ if (irq & CCI_IRQ_STATUS_0_RST_DONE_ACK_BMSK) {
+ if (cci_dev->cci_master_info[MASTER_0].reset_pending == TRUE) {
+ cci_dev->cci_master_info[MASTER_0].reset_pending =
+ FALSE;
+ complete(&cci_dev->cci_master_info[MASTER_0].
+ reset_complete);
+ }
+ if (cci_dev->cci_master_info[MASTER_1].reset_pending == TRUE) {
+ cci_dev->cci_master_info[MASTER_1].reset_pending =
+ FALSE;
+ complete(&cci_dev->cci_master_info[MASTER_1].
+ reset_complete);
+ }
+ } else if ((irq & CCI_IRQ_STATUS_0_I2C_M0_RD_DONE_BMSK) ||
+ (irq & CCI_IRQ_STATUS_0_I2C_M0_Q0_REPORT_BMSK) ||
+ (irq & CCI_IRQ_STATUS_0_I2C_M0_Q1_REPORT_BMSK)) {
+ cci_dev->cci_master_info[MASTER_0].status = 0;
+ complete(&cci_dev->cci_master_info[MASTER_0].reset_complete);
+ } else if ((irq & CCI_IRQ_STATUS_0_I2C_M1_RD_DONE_BMSK) ||
+ (irq & CCI_IRQ_STATUS_0_I2C_M1_Q0_REPORT_BMSK) ||
+ (irq & CCI_IRQ_STATUS_0_I2C_M1_Q1_REPORT_BMSK)) {
+ cci_dev->cci_master_info[MASTER_1].status = 0;
+ complete(&cci_dev->cci_master_info[MASTER_1].reset_complete);
+ } else if ((irq & CCI_IRQ_STATUS_0_I2C_M0_Q0_NACK_ERR_BMSK) ||
+ (irq & CCI_IRQ_STATUS_0_I2C_M0_Q1_NACK_ERR_BMSK)) {
+ cci_dev->cci_master_info[MASTER_0].status = -EINVAL;
+ msm_camera_io_w(CCI_M0_HALT_REQ_RMSK,
+ cci_dev->base + CCI_HALT_REQ_ADDR);
+ } else if ((irq & CCI_IRQ_STATUS_0_I2C_M1_Q0_NACK_ERR_BMSK) ||
+ (irq & CCI_IRQ_STATUS_0_I2C_M1_Q1_NACK_ERR_BMSK)) {
+ cci_dev->cci_master_info[MASTER_1].status = -EINVAL;
+ msm_camera_io_w(CCI_M1_HALT_REQ_RMSK,
+ cci_dev->base + CCI_HALT_REQ_ADDR);
+ } else if (irq & CCI_IRQ_STATUS_0_I2C_M0_Q0Q1_HALT_ACK_BMSK) {
+ cci_dev->cci_master_info[MASTER_0].reset_pending = TRUE;
+ msm_camera_io_w(CCI_M0_RESET_RMSK,
+ cci_dev->base + CCI_RESET_CMD_ADDR);
+ } else if (irq & CCI_IRQ_STATUS_0_I2C_M1_Q0Q1_HALT_ACK_BMSK) {
+ cci_dev->cci_master_info[MASTER_1].reset_pending = TRUE;
+ msm_camera_io_w(CCI_M1_RESET_RMSK,
+ cci_dev->base + CCI_RESET_CMD_ADDR);
+ } else {
+ pr_err("%s unhandled irq 0x%x\n", __func__, irq);
+ cci_dev->cci_master_info[MASTER_0].status = 0;
+ complete(&cci_dev->cci_master_info[MASTER_0].reset_complete);
+ cci_dev->cci_master_info[MASTER_1].status = 0;
+ complete(&cci_dev->cci_master_info[MASTER_1].reset_complete);
+ }
+ return IRQ_HANDLED;
+}
+
+static int msm_cci_irq_routine(struct v4l2_subdev *sd, u32 status,
+ bool *handled)
+{
+ struct cci_device *cci_dev = v4l2_get_subdevdata(sd);
+ irqreturn_t ret;
+ CDBG("%s line %d\n", __func__, __LINE__);
+ ret = msm_cci_irq(cci_dev->irq->start, cci_dev);
+ CDBG("%s: msm_cci_irq return %d\n", __func__, ret);
+ *handled = TRUE;
+ return 0;
+}
+
+static long msm_cci_subdev_ioctl(struct v4l2_subdev *sd,
+ unsigned int cmd, void *arg)
+{
+ int32_t rc = 0;
+ CDBG("%s line %d\n", __func__, __LINE__);
+ switch (cmd) {
+ case VIDIOC_MSM_CCI_CFG:
+ rc = msm_cci_config(sd, arg);
+ break;
+ default:
+ rc = -ENOIOCTLCMD;
+ }
+ CDBG("%s line %d rc %d\n", __func__, __LINE__, rc);
+ return rc;
+}
+
+static struct v4l2_subdev_core_ops msm_cci_subdev_core_ops = {
+ .g_chip_ident = &msm_cci_subdev_g_chip_ident,
+ .ioctl = &msm_cci_subdev_ioctl,
+ .interrupt_service_routine = msm_cci_irq_routine,
+};
+
+static const struct v4l2_subdev_ops msm_cci_subdev_ops = {
+ .core = &msm_cci_subdev_core_ops,
+};
+
+static const struct v4l2_subdev_internal_ops msm_cci_internal_ops;
+
+static void msm_cci_init_cci_params(struct cci_device *new_cci_dev)
+{
+ uint8_t i = 0, j = 0;
+ for (i = 0; i < NUM_MASTERS; i++) {
+ new_cci_dev->cci_master_info[i].status = 0;
+ mutex_init(&new_cci_dev->cci_master_info[i].mutex);
+ init_completion(&new_cci_dev->
+ cci_master_info[i].reset_complete);
+ for (j = 0; j < NUM_QUEUES; j++) {
+ if (j == QUEUE_0)
+ new_cci_dev->cci_i2c_queue_info[i][j].
+ max_queue_size = CCI_I2C_QUEUE_0_SIZE;
+ else
+ new_cci_dev->cci_i2c_queue_info[i][j].
+ max_queue_size = CCI_I2C_QUEUE_1_SIZE;
+ }
+ }
+ return;
+}
+
+static int32_t msm_cci_init_gpio_params(struct cci_device *cci_dev)
+{
+ int32_t rc = 0, i = 0;
+ uint32_t *val_array = NULL;
+ uint8_t tbl_size = 0;
+ struct device_node *of_node = cci_dev->pdev->dev.of_node;
+ struct gpio *gpio_tbl = NULL;
+
+ cci_dev->cci_gpio_tbl_size = tbl_size = of_gpio_count(of_node);
+ CDBG("%s gpio count %d\n", __func__, tbl_size);
+ if (!tbl_size) {
+ pr_err("%s:%d gpio count 0\n", __func__, __LINE__);
+ return 0;
+ }
+
+ gpio_tbl = cci_dev->cci_gpio_tbl =
+ kzalloc(sizeof(struct gpio) * tbl_size, GFP_KERNEL);
+ if (!gpio_tbl) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ return 0;
+ }
+
+ for (i = 0; i < tbl_size; i++) {
+ gpio_tbl[i].gpio = of_get_gpio(of_node, i);
+ CDBG("%s gpio_tbl[%d].gpio = %d\n", __func__, i,
+ gpio_tbl[i].gpio);
+ }
+
+ val_array = kzalloc(sizeof(uint32_t) * tbl_size, GFP_KERNEL);
+ if (!val_array) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ rc = -ENOMEM;
+ goto ERROR1;
+ }
+
+ rc = of_property_read_u32_array(of_node, "qcom,gpio-tbl-flags",
+ val_array, tbl_size);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto ERROR2;
+ }
+ for (i = 0; i < tbl_size; i++) {
+ gpio_tbl[i].flags = val_array[i];
+ CDBG("%s gpio_tbl[%d].flags = %ld\n", __func__, i,
+ gpio_tbl[i].flags);
+ }
+
+ for (i = 0; i < tbl_size; i++) {
+ rc = of_property_read_string_index(of_node,
+ "qcom,gpio-tbl-label", i, &gpio_tbl[i].label);
+ CDBG("%s gpio_tbl[%d].label = %s\n", __func__, i,
+ gpio_tbl[i].label);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto ERROR2;
+ }
+ }
+
+ kfree(val_array);
+ return rc;
+
+ERROR2:
+ kfree(val_array);
+ERROR1:
+ kfree(cci_dev->cci_gpio_tbl);
+ cci_dev->cci_gpio_tbl = NULL;
+ cci_dev->cci_gpio_tbl_size = 0;
+ return rc;
+}
+
+static void msm_cci_init_clk_params(struct cci_device *cci_dev)
+{
+ int32_t rc = 0;
+ uint32_t val = 0;
+ struct device_node *of_node = cci_dev->pdev->dev.of_node;
+
+ rc = of_property_read_u32(of_node, "qcom,hw-thigh", &val);
+ CDBG("%s qcom,hw-thigh %d, rc %d\n", __func__, val, rc);
+ if (!rc)
+ cci_dev->cci_clk_params.hw_thigh = val;
+
+ rc = of_property_read_u32(of_node, "qcom,hw-tlow", &val);
+ CDBG("%s qcom,hw-tlow %d, rc %d\n", __func__, val, rc);
+ if (!rc)
+ cci_dev->cci_clk_params.hw_tlow = val;
+
+ rc = of_property_read_u32(of_node, "qcom,hw-tsu-sto", &val);
+ CDBG("%s qcom,hw-tsu-sto %d, rc %d\n", __func__, val, rc);
+ if (!rc)
+ cci_dev->cci_clk_params.hw_tsu_sto = val;
+
+ rc = of_property_read_u32(of_node, "qcom,hw-tsu-sta", &val);
+ CDBG("%s qcom,hw-tsu-sta %d, rc %d\n", __func__, val, rc);
+ if (!rc)
+ cci_dev->cci_clk_params.hw_tsu_sta = val;
+
+ rc = of_property_read_u32(of_node, "qcom,hw-thd-dat", &val);
+ CDBG("%s qcom,hw-thd-dat %d, rc %d\n", __func__, val, rc);
+ if (!rc)
+ cci_dev->cci_clk_params.hw_thd_dat = val;
+
+ rc = of_property_read_u32(of_node, "qcom,hw-thd-sta", &val);
+ CDBG("%s qcom,hwthd-sta %d, rc %d\n", __func__, val, rc);
+ if (!rc)
+ cci_dev->cci_clk_params.hw_thd_sta = val;
+
+ rc = of_property_read_u32(of_node, "qcom,hw-tbuf", &val);
+ CDBG("%s qcom,hw-tbuf %d, rc %d\n", __func__, val, rc);
+ if (!rc)
+ cci_dev->cci_clk_params.hw_tbuf = val;
+
+ rc = of_property_read_u32(of_node, "qcom,hw-scl-stretch-en", &val);
+ CDBG("%s qcom,hw-scl-stretch-en %d, rc %d\n", __func__, val, rc);
+ if (!rc)
+ cci_dev->cci_clk_params.hw_scl_stretch_en = val;
+
+ rc = of_property_read_u32(of_node, "qcom,hw-trdhld", &val);
+ CDBG("%s qcom,hw-trdhld %d, rc %d\n", __func__, val, rc);
+ if (!rc)
+ cci_dev->cci_clk_params.hw_trdhld = val;
+
+ rc = of_property_read_u32(of_node, "qcom,hw-tsp", &val);
+ CDBG("%s qcom,hw-tsp %d, rc %d\n", __func__, val, rc);
+ if (!rc)
+ cci_dev->cci_clk_params.hw_tsp = val;
+
+ return;
+}
+
+struct v4l2_subdev *msm_cci_get_subdev(void)
+{
+ return g_cci_subdev;
+}
+
+static int __devinit msm_cci_probe(struct platform_device *pdev)
+{
+ struct cci_device *new_cci_dev;
+ int rc = 0;
+ pr_err("%s: pdev %p device id = %d\n", __func__, pdev, pdev->id);
+ new_cci_dev = kzalloc(sizeof(struct cci_device), GFP_KERNEL);
+ if (!new_cci_dev) {
+ CDBG("%s: no enough memory\n", __func__);
+ return -ENOMEM;
+ }
+ v4l2_subdev_init(&new_cci_dev->msm_sd.sd, &msm_cci_subdev_ops);
+ new_cci_dev->msm_sd.sd.internal_ops = &msm_cci_internal_ops;
+ snprintf(new_cci_dev->msm_sd.sd.name,
+ ARRAY_SIZE(new_cci_dev->msm_sd.sd.name), "msm_cci");
+ v4l2_set_subdevdata(&new_cci_dev->msm_sd.sd, new_cci_dev);
+ platform_set_drvdata(pdev, &new_cci_dev->msm_sd.sd);
+ CDBG("%s sd %p\n", __func__, &new_cci_dev->msm_sd.sd);
+ if (pdev->dev.of_node)
+ of_property_read_u32((&pdev->dev)->of_node,
+ "cell-index", &pdev->id);
+
+ new_cci_dev->mem = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM, "cci");
+ if (!new_cci_dev->mem) {
+ CDBG("%s: no mem resource?\n", __func__);
+ rc = -ENODEV;
+ goto cci_no_resource;
+ }
+ new_cci_dev->irq = platform_get_resource_byname(pdev,
+ IORESOURCE_IRQ, "cci");
+ CDBG("%s line %d cci irq start %d end %d\n", __func__,
+ __LINE__,
+ new_cci_dev->irq->start,
+ new_cci_dev->irq->end);
+ if (!new_cci_dev->irq) {
+ CDBG("%s: no irq resource?\n", __func__);
+ rc = -ENODEV;
+ goto cci_no_resource;
+ }
+ new_cci_dev->io = request_mem_region(new_cci_dev->mem->start,
+ resource_size(new_cci_dev->mem), pdev->name);
+ if (!new_cci_dev->io) {
+ CDBG("%s: no valid mem region\n", __func__);
+ rc = -EBUSY;
+ goto cci_no_resource;
+ }
+
+ new_cci_dev->base = ioremap(new_cci_dev->mem->start,
+ resource_size(new_cci_dev->mem));
+ if (!new_cci_dev->base) {
+ rc = -ENOMEM;
+ goto cci_release_mem;
+ }
+ rc = request_irq(new_cci_dev->irq->start, msm_cci_irq,
+ IRQF_TRIGGER_RISING, "cci", new_cci_dev);
+ if (rc < 0) {
+ CDBG("%s: irq request fail\n", __func__);
+ rc = -EBUSY;
+ goto cci_release_mem;
+ }
+ disable_irq(new_cci_dev->irq->start);
+ msm_sd_register(&new_cci_dev->msm_sd);
+ new_cci_dev->pdev = pdev;
+ msm_cci_init_cci_params(new_cci_dev);
+ msm_cci_init_clk_params(new_cci_dev);
+ msm_cci_init_gpio_params(new_cci_dev);
+ rc = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
+ if (rc)
+ pr_err("%s: failed to add child nodes, rc=%d\n", __func__, rc);
+ new_cci_dev->cci_state = CCI_STATE_DISABLED;
+ g_cci_subdev = &new_cci_dev->msm_sd.sd;
+ CDBG("%s cci subdev %p\n", __func__, &new_cci_dev->msm_sd.sd);
+ CDBG("%s line %d\n", __func__, __LINE__);
+ return 0;
+
+cci_release_mem:
+ release_mem_region(new_cci_dev->mem->start,
+ resource_size(new_cci_dev->mem));
+cci_no_resource:
+ kfree(new_cci_dev);
+ return 0;
+}
+
+static int __exit msm_cci_exit(struct platform_device *pdev)
+{
+ struct v4l2_subdev *subdev = platform_get_drvdata(pdev);
+ struct cci_device *cci_dev =
+ v4l2_get_subdevdata(subdev);
+ release_mem_region(cci_dev->mem->start, resource_size(cci_dev->mem));
+ kfree(cci_dev);
+ return 0;
+}
+
+static const struct of_device_id msm_cci_dt_match[] = {
+ {.compatible = "qcom,cci"},
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, msm_cci_dt_match);
+
+static struct platform_driver cci_driver = {
+ .probe = msm_cci_probe,
+ .remove = msm_cci_exit,
+ .driver = {
+ .name = MSM_CCI_DRV_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = msm_cci_dt_match,
+ },
+};
+
+static int __init msm_cci_init_module(void)
+{
+ return platform_driver_register(&cci_driver);
+}
+
+static void __exit msm_cci_exit_module(void)
+{
+ platform_driver_unregister(&cci_driver);
+}
+
+module_init(msm_cci_init_module);
+module_exit(msm_cci_exit_module);
+MODULE_DESCRIPTION("MSM CCI driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/msmb/sensor/cci/msm_cci.h b/drivers/media/video/msmb/sensor/cci/msm_cci.h
new file mode 100644
index 0000000..527a8db
--- /dev/null
+++ b/drivers/media/video/msmb/sensor/cci/msm_cci.h
@@ -0,0 +1,189 @@
+/* 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
+ * 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 MSM_CCI_H
+#define MSM_CCI_H
+
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <media/v4l2-subdev.h>
+#include <media/msm_cam_sensor.h>
+#include "msm_sd.h"
+
+#define NUM_MASTERS 2
+#define NUM_QUEUES 2
+
+#define TRUE 1
+#define FALSE 0
+
+enum cci_i2c_master_t {
+ MASTER_0,
+ MASTER_1,
+};
+
+enum cci_i2c_queue_t {
+ QUEUE_0,
+ QUEUE_1,
+};
+
+struct msm_camera_cci_client {
+ struct v4l2_subdev *cci_subdev;
+ uint32_t freq;
+ enum cci_i2c_master_t cci_i2c_master;
+ uint16_t sid;
+ uint16_t cid;
+ uint32_t timeout;
+ uint16_t retries;
+ uint16_t id_map;
+};
+
+enum msm_cci_cmd_type {
+ MSM_CCI_INIT,
+ MSM_CCI_RELEASE,
+ MSM_CCI_SET_SID,
+ MSM_CCI_SET_FREQ,
+ MSM_CCI_SET_SYNC_CID,
+ MSM_CCI_I2C_READ,
+ MSM_CCI_I2C_WRITE,
+ MSM_CCI_GPIO_WRITE,
+};
+
+struct msm_camera_cci_wait_sync_cfg {
+ uint16_t line;
+ uint16_t delay;
+};
+
+struct msm_camera_cci_gpio_cfg {
+ uint16_t gpio_queue;
+ uint16_t i2c_queue;
+};
+
+struct msm_camera_cci_i2c_write_cfg {
+ struct msm_camera_i2c_reg_conf *reg_conf_tbl;
+ enum msm_camera_i2c_reg_addr_type addr_type;
+ enum msm_camera_i2c_data_type data_type;
+ uint16_t size;
+};
+
+struct msm_camera_cci_i2c_read_cfg {
+ uint16_t addr;
+ enum msm_camera_i2c_reg_addr_type addr_type;
+ uint8_t *data;
+ uint16_t num_byte;
+};
+
+struct msm_camera_cci_i2c_queue_info {
+ uint32_t max_queue_size;
+ uint32_t report_id;
+ uint32_t irq_en;
+ uint32_t capture_rep_data;
+};
+
+struct msm_camera_cci_ctrl {
+ int32_t status;
+ struct msm_camera_cci_client *cci_info;
+ enum msm_cci_cmd_type cmd;
+ union {
+ struct msm_camera_cci_i2c_write_cfg cci_i2c_write_cfg;
+ struct msm_camera_cci_i2c_read_cfg cci_i2c_read_cfg;
+ struct msm_camera_cci_wait_sync_cfg cci_wait_sync_cfg;
+ struct msm_camera_cci_gpio_cfg gpio_cfg;
+ } cfg;
+};
+
+struct msm_camera_cci_master_info {
+ uint32_t status;
+ uint8_t reset_pending;
+ struct mutex mutex;
+ struct completion reset_complete;
+};
+
+struct msm_cci_clk_params_t {
+ uint16_t hw_thigh;
+ uint16_t hw_tlow;
+ uint16_t hw_tsu_sto;
+ uint16_t hw_tsu_sta;
+ uint16_t hw_thd_dat;
+ uint16_t hw_thd_sta;
+ uint16_t hw_tbuf;
+ uint8_t hw_scl_stretch_en;
+ uint8_t hw_trdhld;
+ uint8_t hw_tsp;
+};
+
+enum msm_cci_state_t {
+ CCI_STATE_ENABLED,
+ CCI_STATE_DISABLED,
+};
+
+struct cci_device {
+ struct platform_device *pdev;
+ struct msm_sd_subdev msm_sd;
+ struct v4l2_subdev subdev;
+ struct resource *mem;
+ struct resource *irq;
+ struct resource *io;
+ void __iomem *base;
+
+ uint32_t hw_version;
+ uint8_t ref_count;
+ enum msm_cci_state_t cci_state;
+
+ struct clk *cci_clk[5];
+ struct msm_camera_cci_i2c_queue_info
+ cci_i2c_queue_info[NUM_MASTERS][NUM_QUEUES];
+ struct msm_camera_cci_master_info cci_master_info[NUM_MASTERS];
+ struct msm_cci_clk_params_t cci_clk_params;
+ struct gpio *cci_gpio_tbl;
+ uint8_t cci_gpio_tbl_size;
+};
+
+enum msm_cci_i2c_cmd_type {
+ CCI_I2C_SET_PARAM_CMD = 1,
+ CCI_I2C_WAIT_CMD,
+ CCI_I2C_WAIT_SYNC_CMD,
+ CCI_I2C_WAIT_GPIO_EVENT_CMD,
+ CCI_I2C_TRIG_I2C_EVENT_CMD,
+ CCI_I2C_LOCK_CMD,
+ CCI_I2C_UNLOCK_CMD,
+ CCI_I2C_REPORT_CMD,
+ CCI_I2C_WRITE_CMD,
+ CCI_I2C_READ_CMD,
+ CCI_I2C_WRITE_DISABLE_P_CMD,
+ CCI_I2C_READ_DISABLE_P_CMD,
+ CCI_I2C_WRITE_CMD2,
+ CCI_I2C_WRITE_CMD3,
+ CCI_I2C_REPEAT_CMD,
+ CCI_I2C_INVALID_CMD,
+};
+
+enum msm_cci_gpio_cmd_type {
+ CCI_GPIO_SET_PARAM_CMD = 1,
+ CCI_GPIO_WAIT_CMD,
+ CCI_GPIO_WAIT_SYNC_CMD,
+ CCI_GPIO_WAIT_GPIO_IN_EVENT_CMD,
+ CCI_GPIO_WAIT_I2C_Q_TRIG_EVENT_CMD,
+ CCI_GPIO_OUT_CMD,
+ CCI_GPIO_TRIG_EVENT_CMD,
+ CCI_GPIO_REPORT_CMD,
+ CCI_GPIO_REPEAT_CMD,
+ CCI_GPIO_CONTINUE_CMD,
+ CCI_GPIO_INVALID_CMD,
+};
+
+struct v4l2_subdev *msm_cci_get_subdev(void);
+
+#define VIDIOC_MSM_CCI_CFG \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 23, struct msm_camera_cci_ctrl *)
+
+#endif
diff --git a/drivers/media/video/msmb/sensor/csid/Makefile b/drivers/media/video/msmb/sensor/csid/Makefile
new file mode 100644
index 0000000..44d7726
--- /dev/null
+++ b/drivers/media/video/msmb/sensor/csid/Makefile
@@ -0,0 +1,8 @@
+ccflags-y += -Idrivers/media/video/msmb
+ccflags-y += -Idrivers/media/video/msmb/sensor/io
+ifeq ($(CONFIG_MSM_CSI20_HEADER),y)
+ ccflags-y += -Idrivers/media/video/msmb/sensor/csid/include/csi2.0
+else ifeq ($(CONFIG_MSM_CSI30_HEADER),y)
+ ccflags-y += -Idrivers/media/video/msmb/sensor/csid/include/csi3.0
+endif
+obj-$(CONFIG_MSM_CSID) += msm_csid.o
diff --git a/drivers/media/video/msmb/sensor/csid/include/csi2.0/msm_csid_hwreg.h b/drivers/media/video/msmb/sensor/csid/include/csi2.0/msm_csid_hwreg.h
new file mode 100644
index 0000000..87a114e
--- /dev/null
+++ b/drivers/media/video/msmb/sensor/csid/include/csi2.0/msm_csid_hwreg.h
@@ -0,0 +1,52 @@
+/* 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
+ * 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 MSM_CSID_HWREG_H
+#define MSM_CSID_HWREG_H
+
+/* MIPI CSID registers */
+#define CSID_HW_VERSION_ADDR 0x0
+#define CSID_CORE_CTRL_0_ADDR 0x4
+#define CSID_CORE_CTRL_1_ADDR 0x4
+#define CSID_RST_CMD_ADDR 0x8
+#define CSID_CID_LUT_VC_0_ADDR 0xc
+#define CSID_CID_LUT_VC_1_ADDR 0x10
+#define CSID_CID_LUT_VC_2_ADDR 0x14
+#define CSID_CID_LUT_VC_3_ADDR 0x18
+#define CSID_CID_n_CFG_ADDR 0x1C
+#define CSID_IRQ_CLEAR_CMD_ADDR 0x5c
+#define CSID_IRQ_MASK_ADDR 0x60
+#define CSID_IRQ_STATUS_ADDR 0x64
+#define CSID_CAPTURED_UNMAPPED_LONG_PKT_HDR_ADDR 0x68
+#define CSID_CAPTURED_MMAPPED_LONG_PKT_HDR_ADDR 0x6c
+#define CSID_CAPTURED_SHORT_PKT_ADDR 0x70
+#define CSID_CAPTURED_LONG_PKT_HDR_ADDR 0x74
+#define CSID_CAPTURED_LONG_PKT_FTR_ADDR 0x78
+#define CSID_PIF_MISR_DL0_ADDR 0x7C
+#define CSID_PIF_MISR_DL1_ADDR 0x80
+#define CSID_PIF_MISR_DL2_ADDR 0x84
+#define CSID_PIF_MISR_DL3_ADDR 0x88
+#define CSID_STATS_TOTAL_PKTS_RCVD_ADDR 0x8C
+#define CSID_STATS_ECC_ADDR 0x90
+#define CSID_STATS_CRC_ADDR 0x94
+#define CSID_TG_CTRL_ADDR 0x9C
+#define CSID_TG_VC_CFG_ADDR 0xA0
+#define CSID_TG_DT_n_CFG_0_ADDR 0xA8
+#define CSID_TG_DT_n_CFG_1_ADDR 0xAC
+#define CSID_TG_DT_n_CFG_2_ADDR 0xB0
+#define CSID_RST_DONE_IRQ_BITSHIFT 11
+#define CSID_RST_STB_ALL 0x7FFF
+#define CSID_DL_INPUT_SEL_SHIFT 0x2
+#define CSID_PHY_SEL_SHIFT 17
+#define CSID_VERSION 0x02000011
+
+#endif
diff --git a/drivers/media/video/msmb/sensor/csid/include/csi3.0/msm_csid_hwreg.h b/drivers/media/video/msmb/sensor/csid/include/csi3.0/msm_csid_hwreg.h
new file mode 100644
index 0000000..8cea521
--- /dev/null
+++ b/drivers/media/video/msmb/sensor/csid/include/csi3.0/msm_csid_hwreg.h
@@ -0,0 +1,52 @@
+/* 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
+ * 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 MSM_CSID_HWREG_H
+#define MSM_CSID_HWREG_H
+
+/* MIPI CSID registers */
+#define CSID_HW_VERSION_ADDR 0x0
+#define CSID_CORE_CTRL_0_ADDR 0x4
+#define CSID_CORE_CTRL_1_ADDR 0x8
+#define CSID_RST_CMD_ADDR 0xC
+#define CSID_CID_LUT_VC_0_ADDR 0x10
+#define CSID_CID_LUT_VC_1_ADDR 0x14
+#define CSID_CID_LUT_VC_2_ADDR 0x18
+#define CSID_CID_LUT_VC_3_ADDR 0x1C
+#define CSID_CID_n_CFG_ADDR 0x20
+#define CSID_IRQ_CLEAR_CMD_ADDR 0x60
+#define CSID_IRQ_MASK_ADDR 0x64
+#define CSID_IRQ_STATUS_ADDR 0x68
+#define CSID_CAPTURED_UNMAPPED_LONG_PKT_HDR_ADDR 0x6C
+#define CSID_CAPTURED_MMAPPED_LONG_PKT_HDR_ADDR 0x70
+#define CSID_CAPTURED_SHORT_PKT_ADDR 0x74
+#define CSID_CAPTURED_LONG_PKT_HDR_ADDR 0x78
+#define CSID_CAPTURED_LONG_PKT_FTR_ADDR 0x7C
+#define CSID_PIF_MISR_DL0_ADDR 0x80
+#define CSID_PIF_MISR_DL1_ADDR 0x84
+#define CSID_PIF_MISR_DL2_ADDR 0x88
+#define CSID_PIF_MISR_DL3_ADDR 0x8C
+#define CSID_STATS_TOTAL_PKTS_RCVD_ADDR 0x90
+#define CSID_STATS_ECC_ADDR 0x94
+#define CSID_STATS_CRC_ADDR 0x98
+#define CSID_TG_CTRL_ADDR 0xA0
+#define CSID_TG_VC_CFG_ADDR 0xA4
+#define CSID_TG_DT_n_CFG_0_ADDR 0xAC
+#define CSID_TG_DT_n_CFG_1_ADDR 0xB0
+#define CSID_TG_DT_n_CFG_2_ADDR 0xB4
+#define CSID_RST_DONE_IRQ_BITSHIFT 11
+#define CSID_RST_STB_ALL 0x7FFF
+#define CSID_DL_INPUT_SEL_SHIFT 0x4
+#define CSID_PHY_SEL_SHIFT 17
+#define CSID_VERSION 0x30000000
+
+#endif
diff --git a/drivers/media/video/msmb/sensor/csid/msm_csid.c b/drivers/media/video/msmb/sensor/csid/msm_csid.c
new file mode 100644
index 0000000..5889f20
--- /dev/null
+++ b/drivers/media/video/msmb/sensor/csid/msm_csid.c
@@ -0,0 +1,693 @@
+/* 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
+ * 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/delay.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/irqreturn.h>
+#include "msm_csid.h"
+#include "msm_csid_hwreg.h"
+#include "msm_sd.h"
+#include "msm_camera_io_util.h"
+
+#define V4L2_IDENT_CSID 50002
+#define CSID_VERSION_V2 0x02000011
+#define CSID_VERSION_V3 0x30000000
+#define MSM_CSID_DRV_NAME "msm_csid"
+
+#define DBG_CSID 1
+
+#define TRUE 1
+#define FALSE 0
+
+#define CONFIG_MSMB_CAMERA_DEBUG
+#undef CDBG
+#ifdef CONFIG_MSMB_CAMERA_DEBUG
+#define CDBG(fmt, args...) pr_err(fmt, ##args)
+#else
+#define CDBG(fmt, args...) do { } while (0)
+#endif
+
+static uint32_t irq_count;
+
+static int msm_csid_cid_lut(
+ struct msm_camera_csid_lut_params *csid_lut_params,
+ void __iomem *csidbase)
+{
+ int rc = 0, i = 0;
+ uint32_t val = 0;
+
+ if (!csid_lut_params) {
+ pr_err("%s:%d csid_lut_params NULL\n", __func__, __LINE__);
+ return -EINVAL;
+ }
+ for (i = 0; i < csid_lut_params->num_cid && i < 16; i++) {
+ CDBG("%s lut params num_cid = %d, cid = %d, dt = %x, df = %d\n",
+ __func__,
+ csid_lut_params->num_cid,
+ csid_lut_params->vc_cfg[i]->cid,
+ csid_lut_params->vc_cfg[i]->dt,
+ csid_lut_params->vc_cfg[i]->decode_format);
+ if (csid_lut_params->vc_cfg[i]->dt < 0x12 ||
+ csid_lut_params->vc_cfg[i]->dt > 0x37) {
+ pr_err("%s: unsupported data type 0x%x\n",
+ __func__, csid_lut_params->vc_cfg[i]->dt);
+ return rc;
+ }
+ val = msm_camera_io_r(csidbase + CSID_CID_LUT_VC_0_ADDR +
+ (csid_lut_params->vc_cfg[i]->cid >> 2) * 4)
+ & ~(0xFF << ((csid_lut_params->vc_cfg[i]->cid % 4) *
+ 8));
+ val |= (csid_lut_params->vc_cfg[i]->dt <<
+ ((csid_lut_params->vc_cfg[i]->cid % 4) * 8));
+ msm_camera_io_w(val, csidbase + CSID_CID_LUT_VC_0_ADDR +
+ (csid_lut_params->vc_cfg[i]->cid >> 2) * 4);
+
+ val = (csid_lut_params->vc_cfg[i]->decode_format << 4) | 0x3;
+ msm_camera_io_w(val, csidbase + CSID_CID_n_CFG_ADDR +
+ (csid_lut_params->vc_cfg[i]->cid * 4));
+ }
+ return rc;
+}
+
+#if DBG_CSID
+static void msm_csid_set_debug_reg(void __iomem *csidbase,
+ struct msm_camera_csid_params *csid_params)
+{
+ uint32_t val = 0;
+ val = ((1 << csid_params->lane_cnt) - 1) << 20;
+ msm_camera_io_w(0x7f010801 | val, csidbase + CSID_IRQ_MASK_ADDR);
+ msm_camera_io_w(0x7f010801 | val, csidbase + CSID_IRQ_CLEAR_CMD_ADDR);
+}
+#else
+static void msm_csid_set_debug_reg(void __iomem *csidbase,
+ struct msm_camera_csid_params *csid_params) {}
+#endif
+
+static void msm_csid_reset(struct csid_device *csid_dev)
+{
+ CDBG("%s:%d called\n", __func__, __LINE__);
+ msm_camera_io_w(CSID_RST_STB_ALL, csid_dev->base + CSID_RST_CMD_ADDR);
+ CDBG("%s:%d called\n", __func__, __LINE__);
+ wait_for_completion_interruptible(&csid_dev->reset_complete);
+ CDBG("%s:%d called\n", __func__, __LINE__);
+ return;
+}
+
+static int msm_csid_config(struct csid_device *csid_dev,
+ struct msm_camera_csid_params *csid_params)
+{
+ int rc = 0;
+ uint32_t val = 0;
+ void __iomem *csidbase;
+ csidbase = csid_dev->base;
+ if (!csidbase || !csid_params) {
+ pr_err("%s:%d csidbase %p, csid params %p\n", __func__,
+ __LINE__, csidbase, csid_params);
+ return -EINVAL;
+ }
+
+ CDBG("%s csid_params, lane_cnt = %d, lane_assign = %x, phy sel = %d\n",
+ __func__,
+ csid_params->lane_cnt,
+ csid_params->lane_assign,
+ csid_params->phy_sel);
+
+ msm_csid_reset(csid_dev);
+
+ val = csid_params->lane_cnt - 1;
+ val |= csid_params->lane_assign << CSID_DL_INPUT_SEL_SHIFT;
+ if (csid_dev->hw_version < 0x30000000) {
+ val |= (0xF << 10);
+ msm_camera_io_w(val, csidbase + CSID_CORE_CTRL_0_ADDR);
+ } else {
+ msm_camera_io_w(val, csidbase + CSID_CORE_CTRL_0_ADDR);
+ val = csid_params->phy_sel << CSID_PHY_SEL_SHIFT;
+ val |= 0xF;
+ msm_camera_io_w(val, csidbase + CSID_CORE_CTRL_1_ADDR);
+ }
+
+ rc = msm_csid_cid_lut(&csid_params->lut_params, csidbase);
+ if (rc < 0)
+ return rc;
+
+ msm_csid_set_debug_reg(csidbase, csid_params);
+ return rc;
+}
+
+static irqreturn_t msm_csid_irq(int irq_num, void *data)
+{
+ uint32_t irq;
+ struct csid_device *csid_dev = data;
+ uint32_t val = 0;
+ void __iomem *csidbase;
+ csidbase = csid_dev->base;
+
+ if (!csid_dev) {
+ pr_err("%s:%d csid_dev NULL\n", __func__, __LINE__);
+ return IRQ_HANDLED;
+ }
+ irq = msm_camera_io_r(csid_dev->base + CSID_IRQ_STATUS_ADDR);
+ CDBG("%s CSID%d_IRQ_STATUS_ADDR = 0x%x\n",
+ __func__, csid_dev->pdev->id, irq);
+ if (irq & (0x1 << CSID_RST_DONE_IRQ_BITSHIFT))
+ complete(&csid_dev->reset_complete);
+ if (irq & 0x1) {
+ pr_debug("%s CSID%d_IRQ_STATUS_ADDR = 0x%x\n",
+ __func__, csid_dev->pdev->id, irq);
+ irq_count++;
+ if (irq_count >= 5) {
+ msm_camera_io_w(0x7f010800 | val,
+ csidbase + CSID_IRQ_MASK_ADDR);
+ msm_camera_io_w(0x7f010800 | val,
+ csidbase + CSID_IRQ_CLEAR_CMD_ADDR);
+ }
+ }
+ msm_camera_io_w(irq, csid_dev->base + CSID_IRQ_CLEAR_CMD_ADDR);
+ return IRQ_HANDLED;
+}
+
+static int msm_csid_irq_routine(struct v4l2_subdev *sd, u32 status,
+ bool *handled)
+{
+ struct csid_device *csid_dev = v4l2_get_subdevdata(sd);
+ irqreturn_t ret;
+ CDBG("%s E\n", __func__);
+ ret = msm_csid_irq(csid_dev->irq->start, csid_dev);
+ *handled = TRUE;
+ return 0;
+}
+
+static int msm_csid_subdev_g_chip_ident(struct v4l2_subdev *sd,
+ struct v4l2_dbg_chip_ident *chip)
+{
+ BUG_ON(!chip);
+ chip->ident = V4L2_IDENT_CSID;
+ chip->revision = 0;
+ return 0;
+}
+
+static struct msm_cam_clk_info csid_8960_clk_info[] = {
+ {"csi_src_clk", 177780000},
+ {"csi_clk", -1},
+ {"csi_phy_clk", -1},
+ {"csi_pclk", -1},
+};
+
+static struct msm_cam_clk_info csid0_8974_clk_info[] = {
+ {"camss_top_ahb_clk", -1},
+ {"ispif_ahb_clk", -1},
+ {"csi0_ahb_clk", -1},
+ {"csi0_src_clk", 200000000},
+ {"csi0_clk", -1},
+ {"csi0_phy_clk", -1},
+ {"csi0_pix_clk", -1},
+ {"csi0_rdi_clk", -1},
+};
+
+static struct msm_cam_clk_info csid1_8974_clk_info[] = {
+ {"csi1_ahb_clk", -1},
+ {"csi1_src_clk", 200000000},
+ {"csi1_clk", -1},
+ {"csi1_phy_clk", -1},
+ {"csi1_pix_clk", -1},
+ {"csi1_rdi_clk", -1},
+};
+
+static struct msm_cam_clk_info csid2_8974_clk_info[] = {
+ {"csi2_ahb_clk", -1},
+ {"csi2_src_clk", 200000000},
+ {"csi2_clk", -1},
+ {"csi2_phy_clk", -1},
+ {"csi2_pix_clk", -1},
+ {"csi2_rdi_clk", -1},
+};
+
+static struct msm_cam_clk_info csid3_8974_clk_info[] = {
+ {"csi3_ahb_clk", -1},
+ {"csi3_src_clk", 200000000},
+ {"csi3_clk", -1},
+ {"csi3_phy_clk", -1},
+ {"csi3_pix_clk", -1},
+ {"csi3_rdi_clk", -1},
+};
+
+static struct msm_cam_clk_setting csid_8974_clk_info[] = {
+ {&csid0_8974_clk_info[0], ARRAY_SIZE(csid0_8974_clk_info)},
+ {&csid1_8974_clk_info[0], ARRAY_SIZE(csid1_8974_clk_info)},
+ {&csid2_8974_clk_info[0], ARRAY_SIZE(csid2_8974_clk_info)},
+ {&csid3_8974_clk_info[0], ARRAY_SIZE(csid3_8974_clk_info)},
+};
+
+static struct camera_vreg_t csid_8960_vreg_info[] = {
+ {"mipi_csi_vdd", REG_LDO, 1200000, 1200000, 20000},
+};
+
+static struct camera_vreg_t csid_8974_vreg_info[] = {
+ {"mipi_csi_vdd", REG_LDO, 1800000, 1800000, 12000},
+};
+
+static int msm_csid_init(struct csid_device *csid_dev, uint32_t *csid_version)
+{
+ int rc = 0;
+ uint8_t core_id = 0;
+
+ CDBG("%s:%d called\n", __func__, __LINE__);
+ if (!csid_version) {
+ pr_err("%s:%d csid_version NULL\n", __func__, __LINE__);
+ rc = -EINVAL;
+ return rc;
+ }
+ CDBG("%s:%d called\n", __func__, __LINE__);
+
+ if (csid_dev->csid_state == CSID_POWER_UP) {
+ pr_err("%s: csid invalid state %d\n", __func__,
+ csid_dev->csid_state);
+ rc = -EINVAL;
+ return rc;
+ }
+ CDBG("%s:%d called\n", __func__, __LINE__);
+
+ csid_dev->base = ioremap(csid_dev->mem->start,
+ resource_size(csid_dev->mem));
+ if (!csid_dev->base) {
+ pr_err("%s csid_dev->base NULL\n", __func__);
+ rc = -ENOMEM;
+ return rc;
+ }
+ CDBG("%s:%d called\n", __func__, __LINE__);
+
+ if (CSID_VERSION <= CSID_VERSION_V2) {
+ CDBG("%s:%d called\n", __func__, __LINE__);
+ rc = msm_camera_config_vreg(&csid_dev->pdev->dev,
+ csid_8960_vreg_info, ARRAY_SIZE(csid_8960_vreg_info),
+ NULL, 0, &csid_dev->csi_vdd, 1);
+ if (rc < 0) {
+ pr_err("%s: regulator on failed\n", __func__);
+ goto vreg_config_failed;
+ }
+ CDBG("%s:%d called\n", __func__, __LINE__);
+
+ rc = msm_camera_enable_vreg(&csid_dev->pdev->dev,
+ csid_8960_vreg_info, ARRAY_SIZE(csid_8960_vreg_info),
+ NULL, 0, &csid_dev->csi_vdd, 1);
+ if (rc < 0) {
+ pr_err("%s: regulator enable failed\n", __func__);
+ goto vreg_enable_failed;
+ }
+ CDBG("%s:%d called\n", __func__, __LINE__);
+
+ rc = msm_cam_clk_enable(&csid_dev->pdev->dev,
+ csid_8960_clk_info, csid_dev->csid_clk,
+ ARRAY_SIZE(csid_8960_clk_info), 1);
+ if (rc < 0) {
+ pr_err("%s: clock enable failed\n", __func__);
+ goto clk_enable_failed;
+ }
+ CDBG("%s:%d called\n", __func__, __LINE__);
+ } else if (CSID_VERSION == CSID_VERSION_V3) {
+ CDBG("%s:%d called\n", __func__, __LINE__);
+ rc = msm_camera_config_vreg(&csid_dev->pdev->dev,
+ csid_8974_vreg_info, ARRAY_SIZE(csid_8974_vreg_info),
+ NULL, 0, &csid_dev->csi_vdd, 1);
+ if (rc < 0) {
+ pr_err("%s: regulator on failed\n", __func__);
+ goto vreg_config_failed;
+ }
+ CDBG("%s:%d called\n", __func__, __LINE__);
+
+ rc = msm_camera_enable_vreg(&csid_dev->pdev->dev,
+ csid_8974_vreg_info, ARRAY_SIZE(csid_8974_vreg_info),
+ NULL, 0, &csid_dev->csi_vdd, 1);
+ if (rc < 0) {
+ pr_err("%s: regulator enable failed\n", __func__);
+ goto vreg_enable_failed;
+ }
+ CDBG("%s:%d called\n", __func__, __LINE__);
+
+ rc = msm_cam_clk_enable(&csid_dev->pdev->dev,
+ csid_8974_clk_info[0].clk_info, csid_dev->csid0_clk,
+ csid_8974_clk_info[0].num_clk_info, 1);
+ if (rc < 0) {
+ pr_err("%s: clock enable failed\n", __func__);
+ goto csid0_clk_enable_failed;
+ }
+ CDBG("%s:%d called\n", __func__, __LINE__);
+ core_id = csid_dev->pdev->id;
+ if (core_id) {
+ CDBG("%s:%d called\n", __func__, __LINE__);
+ rc = msm_cam_clk_enable(&csid_dev->pdev->dev,
+ csid_8974_clk_info[core_id].clk_info,
+ csid_dev->csid_clk,
+ csid_8974_clk_info[core_id].num_clk_info, 1);
+ if (rc < 0) {
+ pr_err("%s: clock enable failed\n",
+ __func__);
+ goto clk_enable_failed;
+ }
+ }
+ }
+ CDBG("%s:%d called\n", __func__, __LINE__);
+
+ csid_dev->hw_version =
+ msm_camera_io_r(csid_dev->base + CSID_HW_VERSION_ADDR);
+ CDBG("%s:%d called csid_dev->hw_version %x\n", __func__, __LINE__,
+ csid_dev->hw_version);
+ *csid_version = csid_dev->hw_version;
+
+ CDBG("%s:%d called\n", __func__, __LINE__);
+ init_completion(&csid_dev->reset_complete);
+ CDBG("%s:%d called\n", __func__, __LINE__);
+
+ enable_irq(csid_dev->irq->start);
+ CDBG("%s:%d called\n", __func__, __LINE__);
+
+ msm_csid_reset(csid_dev);
+ CDBG("%s:%d called\n", __func__, __LINE__);
+ csid_dev->csid_state = CSID_POWER_UP;
+ irq_count = 0;
+ return rc;
+
+clk_enable_failed:
+ if (CSID_VERSION == CSID_VERSION_V3) {
+ msm_cam_clk_enable(&csid_dev->pdev->dev,
+ csid_8974_clk_info[0].clk_info, csid_dev->csid0_clk,
+ csid_8974_clk_info[0].num_clk_info, 0);
+ }
+csid0_clk_enable_failed:
+ if (CSID_VERSION <= CSID_VERSION_V2) {
+ msm_camera_enable_vreg(&csid_dev->pdev->dev,
+ csid_8960_vreg_info, ARRAY_SIZE(csid_8960_vreg_info),
+ NULL, 0, &csid_dev->csi_vdd, 0);
+ } else if (CSID_VERSION == CSID_VERSION_V3) {
+ msm_camera_enable_vreg(&csid_dev->pdev->dev,
+ csid_8974_vreg_info, ARRAY_SIZE(csid_8974_vreg_info),
+ NULL, 0, &csid_dev->csi_vdd, 0);
+ }
+vreg_enable_failed:
+ if (CSID_VERSION <= CSID_VERSION_V2) {
+ msm_camera_config_vreg(&csid_dev->pdev->dev,
+ csid_8960_vreg_info, ARRAY_SIZE(csid_8960_vreg_info),
+ NULL, 0, &csid_dev->csi_vdd, 0);
+ } else if (CSID_VERSION == CSID_VERSION_V3) {
+ msm_camera_config_vreg(&csid_dev->pdev->dev,
+ csid_8974_vreg_info, ARRAY_SIZE(csid_8974_vreg_info),
+ NULL, 0, &csid_dev->csi_vdd, 0);
+ }
+vreg_config_failed:
+ iounmap(csid_dev->base);
+ csid_dev->base = NULL;
+ return rc;
+}
+
+static int msm_csid_release(struct csid_device *csid_dev)
+{
+ uint32_t irq;
+ uint8_t core_id = 0;
+
+ if (csid_dev->csid_state != CSID_POWER_UP) {
+ pr_err("%s: csid invalid state %d\n", __func__,
+ csid_dev->csid_state);
+ return -EINVAL;
+ }
+
+ irq = msm_camera_io_r(csid_dev->base + CSID_IRQ_STATUS_ADDR);
+ msm_camera_io_w(irq, csid_dev->base + CSID_IRQ_CLEAR_CMD_ADDR);
+ msm_camera_io_w(0, csid_dev->base + CSID_IRQ_MASK_ADDR);
+
+ disable_irq(csid_dev->irq->start);
+
+ if (csid_dev->hw_version <= CSID_VERSION_V2) {
+ msm_cam_clk_enable(&csid_dev->pdev->dev, csid_8960_clk_info,
+ csid_dev->csid_clk, ARRAY_SIZE(csid_8960_clk_info), 0);
+
+ msm_camera_enable_vreg(&csid_dev->pdev->dev,
+ csid_8960_vreg_info, ARRAY_SIZE(csid_8960_vreg_info),
+ NULL, 0, &csid_dev->csi_vdd, 0);
+
+ msm_camera_config_vreg(&csid_dev->pdev->dev,
+ csid_8960_vreg_info, ARRAY_SIZE(csid_8960_vreg_info),
+ NULL, 0, &csid_dev->csi_vdd, 0);
+ } else if (csid_dev->hw_version == CSID_VERSION_V3) {
+ core_id = csid_dev->pdev->id;
+ if (core_id)
+ msm_cam_clk_enable(&csid_dev->pdev->dev,
+ csid_8974_clk_info[core_id].clk_info,
+ csid_dev->csid_clk,
+ csid_8974_clk_info[core_id].num_clk_info, 0);
+
+ msm_cam_clk_enable(&csid_dev->pdev->dev,
+ csid_8974_clk_info[0].clk_info, csid_dev->csid0_clk,
+ csid_8974_clk_info[0].num_clk_info, 0);
+
+ msm_camera_enable_vreg(&csid_dev->pdev->dev,
+ csid_8974_vreg_info, ARRAY_SIZE(csid_8974_vreg_info),
+ NULL, 0, &csid_dev->csi_vdd, 0);
+
+ msm_camera_config_vreg(&csid_dev->pdev->dev,
+ csid_8974_vreg_info, ARRAY_SIZE(csid_8974_vreg_info),
+ NULL, 0, &csid_dev->csi_vdd, 0);
+ }
+
+ iounmap(csid_dev->base);
+ csid_dev->base = NULL;
+ csid_dev->csid_state = CSID_POWER_DOWN;
+ return 0;
+}
+
+static long msm_csid_cmd(struct csid_device *csid_dev, void *arg)
+{
+ int rc = 0;
+ struct csid_cfg_data *cdata = (struct csid_cfg_data *)arg;
+
+ if (!csid_dev || !cdata) {
+ pr_err("%s:%d csid_dev %p, cdata %p\n", __func__, __LINE__,
+ csid_dev, cdata);
+ return -EINVAL;
+ }
+ CDBG("%s cfgtype = %d\n", __func__, cdata->cfgtype);
+ switch (cdata->cfgtype) {
+ case CSID_INIT:
+ rc = msm_csid_init(csid_dev, &cdata->cfg.csid_version);
+ CDBG("%s csid version %x\n", __func__,
+ cdata->cfg.csid_version);
+ break;
+ case CSID_CFG: {
+ struct msm_camera_csid_params csid_params;
+ struct msm_camera_csid_vc_cfg *vc_cfg = NULL;
+ int32_t i = 0;
+ if (copy_from_user(&csid_params,
+ (void *)cdata->cfg.csid_params,
+ sizeof(struct msm_camera_csid_params))) {
+ pr_err("%s: %d failed\n", __func__, __LINE__);
+ rc = -EFAULT;
+ break;
+ }
+ for (i = 0; i < csid_params.lut_params.num_cid; i++) {
+ vc_cfg = kzalloc(csid_params.lut_params.num_cid *
+ sizeof(struct msm_camera_csid_vc_cfg),
+ GFP_KERNEL);
+ if (!vc_cfg) {
+ pr_err("%s: %d failed\n", __func__, __LINE__);
+ for (i--; i >= 0; i--)
+ kfree(csid_params.lut_params.vc_cfg[i]);
+ rc = -ENOMEM;
+ break;
+ }
+ if (copy_from_user(vc_cfg,
+ (void *)csid_params.lut_params.vc_cfg[i],
+ (csid_params.lut_params.num_cid *
+ sizeof(struct msm_camera_csid_vc_cfg)))) {
+ pr_err("%s: %d failed\n", __func__, __LINE__);
+ kfree(vc_cfg);
+ for (i--; i >= 0; i--)
+ kfree(csid_params.lut_params.vc_cfg[i]);
+ rc = -EFAULT;
+ break;
+ }
+ csid_params.lut_params.vc_cfg[i] = vc_cfg;
+ }
+ rc = msm_csid_config(csid_dev, &csid_params);
+ for (i--; i >= 0; i--)
+ kfree(csid_params.lut_params.vc_cfg[i]);
+ break;
+ }
+ case CSID_RELEASE:
+ rc = msm_csid_release(csid_dev);
+ break;
+ default:
+ pr_err("%s: %d failed\n", __func__, __LINE__);
+ rc = -ENOIOCTLCMD;
+ break;
+ }
+ return rc;
+}
+
+static int32_t msm_csid_get_subdev_id(struct csid_device *csid_dev, void *arg)
+{
+ uint32_t *subdev_id = (uint32_t *)arg;
+ if (!subdev_id) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ return -EINVAL;
+ }
+ *subdev_id = csid_dev->pdev->id;
+ pr_debug("%s:%d subdev_id %d\n", __func__, __LINE__, *subdev_id);
+ return 0;
+}
+
+static long msm_csid_subdev_ioctl(struct v4l2_subdev *sd,
+ unsigned int cmd, void *arg)
+{
+ int rc = -ENOIOCTLCMD;
+ struct csid_device *csid_dev = v4l2_get_subdevdata(sd);
+ mutex_lock(&csid_dev->mutex);
+ CDBG("%s:%d id %d\n", __func__, __LINE__, csid_dev->pdev->id);
+ switch (cmd) {
+ case VIDIOC_MSM_SENSOR_GET_SUBDEV_ID:
+ rc = msm_csid_get_subdev_id(csid_dev, arg);
+ break;
+ case VIDIOC_MSM_CSID_IO_CFG:
+ rc = msm_csid_cmd(csid_dev, arg);
+ break;
+ case VIDIOC_MSM_CSID_RELEASE:
+ rc = msm_csid_release(csid_dev);
+ break;
+ default:
+ pr_err("%s: command not found\n", __func__);
+ }
+ CDBG("%s:%d\n", __func__, __LINE__);
+ mutex_unlock(&csid_dev->mutex);
+ return rc;
+}
+
+static const struct v4l2_subdev_internal_ops msm_csid_internal_ops;
+
+static struct v4l2_subdev_core_ops msm_csid_subdev_core_ops = {
+ .g_chip_ident = &msm_csid_subdev_g_chip_ident,
+ .ioctl = &msm_csid_subdev_ioctl,
+ .interrupt_service_routine = msm_csid_irq_routine,
+};
+
+static const struct v4l2_subdev_ops msm_csid_subdev_ops = {
+ .core = &msm_csid_subdev_core_ops,
+};
+
+static int __devinit csid_probe(struct platform_device *pdev)
+{
+ struct csid_device *new_csid_dev;
+
+ int rc = 0;
+ CDBG("%s:%d called\n", __func__, __LINE__);
+ new_csid_dev = kzalloc(sizeof(struct csid_device), GFP_KERNEL);
+ if (!new_csid_dev) {
+ pr_err("%s: no enough memory\n", __func__);
+ return -ENOMEM;
+ }
+
+ v4l2_subdev_init(&new_csid_dev->msm_sd.sd, &msm_csid_subdev_ops);
+ v4l2_set_subdevdata(&new_csid_dev->msm_sd.sd, new_csid_dev);
+ platform_set_drvdata(pdev, &new_csid_dev->msm_sd.sd);
+ mutex_init(&new_csid_dev->mutex);
+
+ if (pdev->dev.of_node)
+ of_property_read_u32((&pdev->dev)->of_node,
+ "cell-index", &pdev->id);
+
+ CDBG("%s device id %d\n", __func__, pdev->id);
+ new_csid_dev->mem = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM, "csid");
+ if (!new_csid_dev->mem) {
+ pr_err("%s: no mem resource?\n", __func__);
+ rc = -ENODEV;
+ goto csid_no_resource;
+ }
+ new_csid_dev->irq = platform_get_resource_byname(pdev,
+ IORESOURCE_IRQ, "csid");
+ if (!new_csid_dev->irq) {
+ pr_err("%s: no irq resource?\n", __func__);
+ rc = -ENODEV;
+ goto csid_no_resource;
+ }
+ new_csid_dev->io = request_mem_region(new_csid_dev->mem->start,
+ resource_size(new_csid_dev->mem), pdev->name);
+ if (!new_csid_dev->io) {
+ pr_err("%s: no valid mem region\n", __func__);
+ rc = -EBUSY;
+ goto csid_no_resource;
+ }
+
+ new_csid_dev->pdev = pdev;
+ new_csid_dev->msm_sd.sd.internal_ops = &msm_csid_internal_ops;
+ new_csid_dev->msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ snprintf(new_csid_dev->msm_sd.sd.name,
+ ARRAY_SIZE(new_csid_dev->msm_sd.sd.name), "msm_csid");
+ media_entity_init(&new_csid_dev->msm_sd.sd.entity, 0, NULL, 0);
+ new_csid_dev->msm_sd.sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV;
+ new_csid_dev->msm_sd.sd.entity.group_id = MSM_CAMERA_SUBDEV_CSID;
+ msm_sd_register(&new_csid_dev->msm_sd);
+
+ rc = request_irq(new_csid_dev->irq->start, msm_csid_irq,
+ IRQF_TRIGGER_RISING, "csid", new_csid_dev);
+ if (rc < 0) {
+ release_mem_region(new_csid_dev->mem->start,
+ resource_size(new_csid_dev->mem));
+ pr_err("%s: irq request fail\n", __func__);
+ rc = -EBUSY;
+ goto csid_no_resource;
+ }
+ disable_irq(new_csid_dev->irq->start);
+ if (rc < 0) {
+ release_mem_region(new_csid_dev->mem->start,
+ resource_size(new_csid_dev->mem));
+ pr_err("%s Error registering irq ", __func__);
+ goto csid_no_resource;
+ }
+
+ new_csid_dev->csid_state = CSID_POWER_DOWN;
+ return 0;
+
+csid_no_resource:
+ mutex_destroy(&new_csid_dev->mutex);
+ kfree(new_csid_dev);
+ return 0;
+}
+
+static const struct of_device_id msm_csid_dt_match[] = {
+ {.compatible = "qcom,csid"},
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, msm_csid_dt_match);
+
+static struct platform_driver csid_driver = {
+ .probe = csid_probe,
+ .driver = {
+ .name = MSM_CSID_DRV_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = msm_csid_dt_match,
+ },
+};
+
+static int __init msm_csid_init_module(void)
+{
+ return platform_driver_register(&csid_driver);
+}
+
+static void __exit msm_csid_exit_module(void)
+{
+ platform_driver_unregister(&csid_driver);
+}
+
+module_init(msm_csid_init_module);
+module_exit(msm_csid_exit_module);
+MODULE_DESCRIPTION("MSM CSID driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/msmb/sensor/csid/msm_csid.h b/drivers/media/video/msmb/sensor/csid/msm_csid.h
new file mode 100644
index 0000000..7ae1392
--- /dev/null
+++ b/drivers/media/video/msmb/sensor/csid/msm_csid.h
@@ -0,0 +1,47 @@
+/* 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
+ * 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 MSM_CSID_H
+#define MSM_CSID_H
+
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <media/v4l2-subdev.h>
+#include <media/msm_cam_sensor.h>
+#include "msm_sd.h"
+
+enum msm_csid_state_t {
+ CSID_POWER_UP,
+ CSID_POWER_DOWN,
+};
+
+struct csid_device {
+ struct platform_device *pdev;
+ struct msm_sd_subdev msm_sd;
+ struct resource *mem;
+ struct resource *irq;
+ struct resource *io;
+ struct regulator *csi_vdd;
+ void __iomem *base;
+ struct mutex mutex;
+ struct completion reset_complete;
+ uint32_t hw_version;
+ enum msm_csid_state_t csid_state;
+
+ struct clk *csid0_clk[11];
+ struct clk *csid_clk[11];
+};
+
+#define VIDIOC_MSM_CSID_RELEASE \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 5, struct v4l2_subdev*)
+#endif
diff --git a/drivers/media/video/msmb/sensor/csiphy/Makefile b/drivers/media/video/msmb/sensor/csiphy/Makefile
new file mode 100644
index 0000000..11e352c
--- /dev/null
+++ b/drivers/media/video/msmb/sensor/csiphy/Makefile
@@ -0,0 +1,8 @@
+ccflags-y += -Idrivers/media/video/msmb
+ccflags-y += -Idrivers/media/video/msmb/sensor/io
+ifeq ($(CONFIG_MSM_CSI20_HEADER),y)
+ ccflags-y += -Idrivers/media/video/msmb/sensor/csiphy/include/csi2.0
+else ifeq ($(CONFIG_MSM_CSI30_HEADER),y)
+ ccflags-y += -Idrivers/media/video/msmb/sensor/csiphy/include/csi3.0
+endif
+obj-$(CONFIG_MSM_CSIPHY) += msm_csiphy.o
diff --git a/drivers/media/video/msmb/sensor/csiphy/include/csi2.0/msm_csiphy_hwreg.h b/drivers/media/video/msmb/sensor/csiphy/include/csi2.0/msm_csiphy_hwreg.h
new file mode 100644
index 0000000..e5093f8
--- /dev/null
+++ b/drivers/media/video/msmb/sensor/csiphy/include/csi2.0/msm_csiphy_hwreg.h
@@ -0,0 +1,43 @@
+/* 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
+ * 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 MSM_CSIPHY_HWREG_H
+#define MSM_CSIPHY_HWREG_H
+
+/*MIPI CSI PHY registers*/
+#define MIPI_CSIPHY_HW_VERSION_ADDR 0x180
+#define MIPI_CSIPHY_LNn_CFG1_ADDR 0x0
+#define MIPI_CSIPHY_LNn_CFG2_ADDR 0x4
+#define MIPI_CSIPHY_LNn_CFG3_ADDR 0x8
+#define MIPI_CSIPHY_LNn_CFG4_ADDR 0xC
+#define MIPI_CSIPHY_LNn_CFG5_ADDR 0x10
+#define MIPI_CSIPHY_LNCK_CFG1_ADDR 0x100
+#define MIPI_CSIPHY_LNCK_CFG2_ADDR 0x104
+#define MIPI_CSIPHY_LNCK_CFG3_ADDR 0x108
+#define MIPI_CSIPHY_LNCK_CFG4_ADDR 0x10C
+#define MIPI_CSIPHY_LNCK_CFG5_ADDR 0x110
+#define MIPI_CSIPHY_LNCK_MISC1_ADDR 0x128
+#define MIPI_CSIPHY_GLBL_RESET_ADDR 0x140
+#define MIPI_CSIPHY_GLBL_PWR_CFG_ADDR 0x144
+#define MIPI_CSIPHY_GLBL_IRQ_CMD_ADDR 0x164
+#define MIPI_CSIPHY_INTERRUPT_STATUS0_ADDR 0x180
+#define MIPI_CSIPHY_INTERRUPT_MASK0_ADDR 0x1A0
+#define MIPI_CSIPHY_INTERRUPT_MASK_VAL 0x6F
+#define MIPI_CSIPHY_INTERRUPT_MASK_ADDR 0x1A4
+#define MIPI_CSIPHY_INTERRUPT_CLEAR0_ADDR 0x1C0
+#define MIPI_CSIPHY_INTERRUPT_CLEAR_ADDR 0x1C4
+#define MIPI_CSIPHY_MODE_CONFIG_SHIFT 0x4
+#define MIPI_CSIPHY_GLBL_T_INIT_CFG0_ADDR 0x1E0
+#define MIPI_CSIPHY_T_WAKEUP_CFG0_ADDR 0x1E8
+#define CSIPHY_VERSION 0x0
+
+#endif
diff --git a/drivers/media/video/msmb/sensor/csiphy/include/csi3.0/msm_csiphy_hwreg.h b/drivers/media/video/msmb/sensor/csiphy/include/csi3.0/msm_csiphy_hwreg.h
new file mode 100644
index 0000000..b90fbc5
--- /dev/null
+++ b/drivers/media/video/msmb/sensor/csiphy/include/csi3.0/msm_csiphy_hwreg.h
@@ -0,0 +1,43 @@
+/* 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
+ * 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 MSM_CSIPHY_HWREG_H
+#define MSM_CSIPHY_HWREG_H
+
+/*MIPI CSI PHY registers*/
+#define MIPI_CSIPHY_LNn_CFG1_ADDR 0x0
+#define MIPI_CSIPHY_LNn_CFG2_ADDR 0x4
+#define MIPI_CSIPHY_LNn_CFG3_ADDR 0x8
+#define MIPI_CSIPHY_LNn_CFG4_ADDR 0xC
+#define MIPI_CSIPHY_LNn_CFG5_ADDR 0x10
+#define MIPI_CSIPHY_LNCK_CFG1_ADDR 0x100
+#define MIPI_CSIPHY_LNCK_CFG2_ADDR 0x104
+#define MIPI_CSIPHY_LNCK_CFG3_ADDR 0x108
+#define MIPI_CSIPHY_LNCK_CFG4_ADDR 0x10C
+#define MIPI_CSIPHY_LNCK_CFG5_ADDR 0x110
+#define MIPI_CSIPHY_LNCK_MISC1_ADDR 0x128
+#define MIPI_CSIPHY_GLBL_RESET_ADDR 0x140
+#define MIPI_CSIPHY_GLBL_PWR_CFG_ADDR 0x144
+#define MIPI_CSIPHY_GLBL_IRQ_CMD_ADDR 0x164
+#define MIPI_CSIPHY_HW_VERSION_ADDR 0x188
+#define MIPI_CSIPHY_INTERRUPT_STATUS0_ADDR 0x18C
+#define MIPI_CSIPHY_INTERRUPT_MASK0_ADDR 0x1AC
+#define MIPI_CSIPHY_INTERRUPT_MASK_VAL 0x3F
+#define MIPI_CSIPHY_INTERRUPT_MASK_ADDR 0x1AC
+#define MIPI_CSIPHY_INTERRUPT_CLEAR0_ADDR 0x1CC
+#define MIPI_CSIPHY_INTERRUPT_CLEAR_ADDR 0x1CC
+#define MIPI_CSIPHY_MODE_CONFIG_SHIFT 0x4
+#define MIPI_CSIPHY_GLBL_T_INIT_CFG0_ADDR 0x1EC
+#define MIPI_CSIPHY_T_WAKEUP_CFG0_ADDR 0x1F4
+#define CSIPHY_VERSION 0x10
+
+#endif
diff --git a/drivers/media/video/msmb/sensor/csiphy/msm_csiphy.c b/drivers/media/video/msmb/sensor/csiphy/msm_csiphy.c
new file mode 100644
index 0000000..cc29ed4
--- /dev/null
+++ b/drivers/media/video/msmb/sensor/csiphy/msm_csiphy.c
@@ -0,0 +1,640 @@
+/* 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
+ * 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/delay.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/module.h>
+#include <linux/irqreturn.h>
+#include <mach/vreg.h>
+#include "msm_csiphy.h"
+#include "msm_sd.h"
+#include "msm_csiphy_hwreg.h"
+#include "msm_camera_io_util.h"
+#define DBG_CSIPHY 0
+
+#define V4L2_IDENT_CSIPHY 50003
+#define CSIPHY_VERSION_V3 0x10
+#define MSM_CSIPHY_DRV_NAME "msm_csiphy"
+
+#define CONFIG_MSMB_CAMERA_DEBUG
+#undef CDBG
+#ifdef CONFIG_MSMB_CAMERA_DEBUG
+#define CDBG(fmt, args...) pr_err(fmt, ##args)
+#else
+#define CDBG(fmt, args...) do { } while (0)
+#endif
+
+static int msm_csiphy_lane_config(struct csiphy_device *csiphy_dev,
+ struct msm_camera_csiphy_params *csiphy_params)
+{
+ int rc = 0;
+ int j = 0;
+ uint32_t val = 0;
+ uint8_t lane_cnt = 0;
+ uint16_t lane_mask = 0;
+ void __iomem *csiphybase;
+ csiphybase = csiphy_dev->base;
+ if (!csiphybase) {
+ pr_err("%s: csiphybase NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ csiphy_dev->lane_mask[csiphy_dev->pdev->id] |= csiphy_params->lane_mask;
+ lane_mask = csiphy_dev->lane_mask[csiphy_dev->pdev->id];
+ lane_cnt = csiphy_params->lane_cnt;
+ if (csiphy_params->lane_cnt < 1 || csiphy_params->lane_cnt > 4) {
+ pr_err("%s: unsupported lane cnt %d\n",
+ __func__, csiphy_params->lane_cnt);
+ return rc;
+ }
+
+ CDBG("%s csiphy_params, mask = %x, cnt = %d, settle cnt = %x\n",
+ __func__,
+ csiphy_params->lane_mask,
+ csiphy_params->lane_cnt,
+ csiphy_params->settle_cnt);
+ msm_camera_io_w(0x1, csiphybase + MIPI_CSIPHY_GLBL_T_INIT_CFG0_ADDR);
+ msm_camera_io_w(0x1, csiphybase + MIPI_CSIPHY_T_WAKEUP_CFG0_ADDR);
+
+ if (csiphy_dev->hw_version != CSIPHY_VERSION_V3) {
+ val = 0x3;
+ msm_camera_io_w((lane_mask << 2) | val,
+ csiphybase + MIPI_CSIPHY_GLBL_PWR_CFG_ADDR);
+ msm_camera_io_w(0x10, csiphybase + MIPI_CSIPHY_LNCK_CFG2_ADDR);
+ msm_camera_io_w(csiphy_params->settle_cnt,
+ csiphybase + MIPI_CSIPHY_LNCK_CFG3_ADDR);
+ msm_camera_io_w(0x24,
+ csiphybase + MIPI_CSIPHY_INTERRUPT_MASK0_ADDR);
+ msm_camera_io_w(0x24,
+ csiphybase + MIPI_CSIPHY_INTERRUPT_CLEAR0_ADDR);
+ } else {
+ val = 0x1;
+ msm_camera_io_w((lane_mask << 1) | val,
+ csiphybase + MIPI_CSIPHY_GLBL_PWR_CFG_ADDR);
+ msm_camera_io_w(csiphy_params->combo_mode <<
+ MIPI_CSIPHY_MODE_CONFIG_SHIFT,
+ csiphybase + MIPI_CSIPHY_GLBL_RESET_ADDR);
+ }
+
+ lane_mask &= 0x1f;
+ while (lane_mask & 0x1f) {
+ if (!(lane_mask & 0x1)) {
+ j++;
+ lane_mask >>= 1;
+ continue;
+ }
+ msm_camera_io_w(0x10,
+ csiphybase + MIPI_CSIPHY_LNn_CFG2_ADDR + 0x40*j);
+ msm_camera_io_w(csiphy_params->settle_cnt,
+ csiphybase + MIPI_CSIPHY_LNn_CFG3_ADDR + 0x40*j);
+ msm_camera_io_w(MIPI_CSIPHY_INTERRUPT_MASK_VAL, csiphybase +
+ MIPI_CSIPHY_INTERRUPT_MASK_ADDR + 0x4*j);
+ msm_camera_io_w(MIPI_CSIPHY_INTERRUPT_MASK_VAL, csiphybase +
+ MIPI_CSIPHY_INTERRUPT_CLEAR_ADDR + 0x4*j);
+ j++;
+ lane_mask >>= 1;
+ }
+ msleep(20);
+ return rc;
+}
+
+static irqreturn_t msm_csiphy_irq(int irq_num, void *data)
+{
+ uint32_t irq;
+ int i;
+ struct csiphy_device *csiphy_dev = data;
+
+ for (i = 0; i < 8; i++) {
+ irq = msm_camera_io_r(
+ csiphy_dev->base +
+ MIPI_CSIPHY_INTERRUPT_STATUS0_ADDR + 0x4*i);
+ msm_camera_io_w(irq,
+ csiphy_dev->base +
+ MIPI_CSIPHY_INTERRUPT_CLEAR0_ADDR + 0x4*i);
+ pr_err("%s MIPI_CSIPHY%d_INTERRUPT_STATUS%d = 0x%x\n",
+ __func__, csiphy_dev->pdev->id, i, irq);
+ msm_camera_io_w(0x1, csiphy_dev->base +
+ MIPI_CSIPHY_GLBL_IRQ_CMD_ADDR);
+ msm_camera_io_w(0x0, csiphy_dev->base +
+ MIPI_CSIPHY_GLBL_IRQ_CMD_ADDR);
+ msm_camera_io_w(0x0,
+ csiphy_dev->base +
+ MIPI_CSIPHY_INTERRUPT_CLEAR0_ADDR + 0x4*i);
+ }
+ return IRQ_HANDLED;
+}
+
+static void msm_csiphy_reset(struct csiphy_device *csiphy_dev)
+{
+ msm_camera_io_w(0x1, csiphy_dev->base + MIPI_CSIPHY_GLBL_RESET_ADDR);
+ usleep_range(5000, 8000);
+ msm_camera_io_w(0x0, csiphy_dev->base + MIPI_CSIPHY_GLBL_RESET_ADDR);
+}
+
+static int msm_csiphy_subdev_g_chip_ident(struct v4l2_subdev *sd,
+ struct v4l2_dbg_chip_ident *chip)
+{
+ BUG_ON(!chip);
+ chip->ident = V4L2_IDENT_CSIPHY;
+ chip->revision = 0;
+ return 0;
+}
+
+static struct msm_cam_clk_info csiphy_8960_clk_info[] = {
+ {"csiphy_timer_src_clk", 177780000},
+ {"csiphy_timer_clk", -1},
+};
+
+static struct msm_cam_clk_info csiphy_8974_clk_info[] = {
+ {"camss_top_ahb_clk", -1},
+ {"ispif_ahb_clk", -1},
+ {"csiphy_timer_src_clk", 200000000},
+ {"csiphy_timer_clk", -1},
+};
+
+#if DBG_CSIPHY
+static int msm_csiphy_init(struct csiphy_device *csiphy_dev)
+{
+ int rc = 0;
+ if (csiphy_dev == NULL) {
+ pr_err("%s: csiphy_dev NULL\n", __func__);
+ rc = -ENOMEM;
+ return rc;
+ }
+
+ CDBG("%s:%d called\n", __func__, __LINE__);
+ if (csiphy_dev->csiphy_state == CSIPHY_POWER_UP) {
+ pr_err("%s: csiphy invalid state %d\n", __func__,
+ csiphy_dev->csiphy_state);
+ rc = -EINVAL;
+ return rc;
+ }
+ CDBG("%s:%d called\n", __func__, __LINE__);
+
+ if (csiphy_dev->ref_count++) {
+ CDBG("%s csiphy refcount = %d\n", __func__,
+ csiphy_dev->ref_count);
+ return rc;
+ }
+ CDBG("%s:%d called\n", __func__, __LINE__);
+
+ csiphy_dev->base = ioremap(csiphy_dev->mem->start,
+ resource_size(csiphy_dev->mem));
+ if (!csiphy_dev->base) {
+ pr_err("%s: csiphy_dev->base NULL\n", __func__);
+ csiphy_dev->ref_count--;
+ rc = -ENOMEM;
+ return rc;
+ }
+ CDBG("%s:%d called\n", __func__, __LINE__);
+
+ if (CSIPHY_VERSION != CSIPHY_VERSION_V3) {
+ CDBG("%s:%d called\n", __func__, __LINE__);
+ rc = msm_cam_clk_enable(&csiphy_dev->pdev->dev,
+ csiphy_8960_clk_info, csiphy_dev->csiphy_clk,
+ ARRAY_SIZE(csiphy_8960_clk_info), 1);
+ } else {
+ CDBG("%s:%d called\n", __func__, __LINE__);
+ rc = msm_cam_clk_enable(&csiphy_dev->pdev->dev,
+ csiphy_8974_clk_info, csiphy_dev->csiphy_clk,
+ ARRAY_SIZE(csiphy_8974_clk_info), 1);
+ }
+
+ CDBG("%s:%d called\n", __func__, __LINE__);
+ if (rc < 0) {
+ pr_err("%s: csiphy clk enable failed\n", __func__);
+ csiphy_dev->ref_count--;
+ iounmap(csiphy_dev->base);
+ csiphy_dev->base = NULL;
+ return rc;
+ }
+ CDBG("%s:%d called\n", __func__, __LINE__);
+
+ enable_irq(csiphy_dev->irq->start);
+
+ msm_csiphy_reset(csiphy_dev);
+
+ CDBG("%s:%d called\n", __func__, __LINE__);
+ csiphy_dev->hw_version =
+ msm_camera_io_r(csiphy_dev->base + MIPI_CSIPHY_HW_VERSION_ADDR);
+
+ CDBG("%s:%d called csiphy_dev->hw_version %x\n", __func__, __LINE__,
+ csiphy_dev->hw_version);
+ csiphy_dev->csiphy_state = CSIPHY_POWER_UP;
+ return 0;
+}
+#else
+static int msm_csiphy_init(struct csiphy_device *csiphy_dev)
+{
+ int rc = 0;
+ if (csiphy_dev == NULL) {
+ pr_err("%s: csiphy_dev NULL\n", __func__);
+ rc = -ENOMEM;
+ return rc;
+ }
+
+ CDBG("%s:%d called\n", __func__, __LINE__);
+ if (csiphy_dev->csiphy_state == CSIPHY_POWER_UP) {
+ pr_err("%s: csiphy invalid state %d\n", __func__,
+ csiphy_dev->csiphy_state);
+ rc = -EINVAL;
+ return rc;
+ }
+ CDBG("%s:%d called\n", __func__, __LINE__);
+
+ if (csiphy_dev->ref_count++) {
+ CDBG("%s csiphy refcount = %d\n", __func__,
+ csiphy_dev->ref_count);
+ return rc;
+ }
+ CDBG("%s:%d called\n", __func__, __LINE__);
+
+ csiphy_dev->base = ioremap(csiphy_dev->mem->start,
+ resource_size(csiphy_dev->mem));
+ if (!csiphy_dev->base) {
+ pr_err("%s: csiphy_dev->base NULL\n", __func__);
+ csiphy_dev->ref_count--;
+ rc = -ENOMEM;
+ return rc;
+ }
+ CDBG("%s:%d called\n", __func__, __LINE__);
+
+ if (CSIPHY_VERSION != CSIPHY_VERSION_V3) {
+ CDBG("%s:%d called\n", __func__, __LINE__);
+ rc = msm_cam_clk_enable(&csiphy_dev->pdev->dev,
+ csiphy_8960_clk_info, csiphy_dev->csiphy_clk,
+ ARRAY_SIZE(csiphy_8960_clk_info), 1);
+ } else {
+ CDBG("%s:%d called\n", __func__, __LINE__);
+ rc = msm_cam_clk_enable(&csiphy_dev->pdev->dev,
+ csiphy_8974_clk_info, csiphy_dev->csiphy_clk,
+ ARRAY_SIZE(csiphy_8974_clk_info), 1);
+ }
+
+ CDBG("%s:%d called\n", __func__, __LINE__);
+ if (rc < 0) {
+ pr_err("%s: csiphy clk enable failed\n", __func__);
+ csiphy_dev->ref_count--;
+ iounmap(csiphy_dev->base);
+ csiphy_dev->base = NULL;
+ return rc;
+ }
+ CDBG("%s:%d called\n", __func__, __LINE__);
+
+ msm_csiphy_reset(csiphy_dev);
+
+ CDBG("%s:%d called\n", __func__, __LINE__);
+ csiphy_dev->hw_version =
+ msm_camera_io_r(csiphy_dev->base + MIPI_CSIPHY_HW_VERSION_ADDR);
+
+ CDBG("%s:%d called csiphy_dev->hw_version %x\n", __func__, __LINE__,
+ csiphy_dev->hw_version);
+ csiphy_dev->csiphy_state = CSIPHY_POWER_UP;
+ return 0;
+}
+#endif
+
+#if DBG_CSIPHY
+static int msm_csiphy_release(struct csiphy_device *csiphy_dev, void *arg)
+{
+ int i = 0;
+ struct msm_camera_csi_lane_params *csi_lane_params;
+ uint16_t csi_lane_mask;
+ csi_lane_params = (struct msm_camera_csi_lane_params *)arg;
+ csi_lane_mask = csi_lane_params->csi_lane_mask;
+
+ if (!csiphy_dev || !csiphy_dev->ref_count) {
+ pr_err("%s csiphy dev NULL / ref_count ZERO\n", __func__);
+ return 0;
+ }
+
+ if (csiphy_dev->csiphy_state != CSIPHY_POWER_UP) {
+ pr_err("%s: csiphy invalid state %d\n", __func__,
+ csiphy_dev->csiphy_state);
+ return -EINVAL;
+ }
+
+ CDBG("%s csiphy_params, lane assign %x mask = %x\n",
+ __func__,
+ csi_lane_params->csi_lane_assign,
+ csi_lane_params->csi_lane_mask);
+
+ if (csiphy_dev->hw_version != CSIPHY_VERSION_V3) {
+ csiphy_dev->lane_mask[csiphy_dev->pdev->id] = 0;
+ for (i = 0; i < 4; i++)
+ msm_camera_io_w(0x0, csiphy_dev->base +
+ MIPI_CSIPHY_LNn_CFG2_ADDR + 0x40*i);
+ } else {
+ csiphy_dev->lane_mask[csiphy_dev->pdev->id] &=
+ ~(csi_lane_params->csi_lane_mask);
+ i = 0;
+ while (csi_lane_mask & 0x1F) {
+ if (csi_lane_mask & 0x1) {
+ msm_camera_io_w(0x0, csiphy_dev->base +
+ MIPI_CSIPHY_LNn_CFG2_ADDR + 0x40*i);
+ }
+ csi_lane_mask >>= 1;
+ i++;
+ }
+ }
+
+ if (--csiphy_dev->ref_count) {
+ CDBG("%s csiphy refcount = %d\n", __func__,
+ csiphy_dev->ref_count);
+ return 0;
+ }
+
+ msm_camera_io_w(0x0, csiphy_dev->base + MIPI_CSIPHY_LNCK_CFG2_ADDR);
+ msm_camera_io_w(0x0, csiphy_dev->base + MIPI_CSIPHY_GLBL_PWR_CFG_ADDR);
+
+ disable_irq(csiphy_dev->irq->start);
+
+ if (CSIPHY_VERSION != CSIPHY_VERSION_V3)
+ msm_cam_clk_enable(&csiphy_dev->pdev->dev,
+ csiphy_8960_clk_info, csiphy_dev->csiphy_clk,
+ ARRAY_SIZE(csiphy_8960_clk_info), 0);
+ else
+ msm_cam_clk_enable(&csiphy_dev->pdev->dev,
+ csiphy_8974_clk_info, csiphy_dev->csiphy_clk,
+ ARRAY_SIZE(csiphy_8974_clk_info), 0);
+
+ iounmap(csiphy_dev->base);
+ csiphy_dev->base = NULL;
+ csiphy_dev->csiphy_state = CSIPHY_POWER_DOWN;
+ return 0;
+}
+#else
+static int msm_csiphy_release(struct csiphy_device *csiphy_dev, void *arg)
+{
+ int i = 0;
+ struct msm_camera_csi_lane_params *csi_lane_params;
+ uint16_t csi_lane_mask;
+ csi_lane_params = (struct msm_camera_csi_lane_params *)arg;
+ csi_lane_mask = csi_lane_params->csi_lane_mask;
+
+ if (!csiphy_dev || !csiphy_dev->ref_count) {
+ pr_err("%s csiphy dev NULL / ref_count ZERO\n", __func__);
+ return 0;
+ }
+
+ if (csiphy_dev->csiphy_state != CSIPHY_POWER_UP) {
+ pr_err("%s: csiphy invalid state %d\n", __func__,
+ csiphy_dev->csiphy_state);
+ return -EINVAL;
+ }
+
+ CDBG("%s csiphy_params, lane assign %x mask = %x\n",
+ __func__,
+ csi_lane_params->csi_lane_assign,
+ csi_lane_params->csi_lane_mask);
+
+ if (csiphy_dev->hw_version != CSIPHY_VERSION_V3) {
+ csiphy_dev->lane_mask[csiphy_dev->pdev->id] = 0;
+ for (i = 0; i < 4; i++)
+ msm_camera_io_w(0x0, csiphy_dev->base +
+ MIPI_CSIPHY_LNn_CFG2_ADDR + 0x40*i);
+ } else {
+ csiphy_dev->lane_mask[csiphy_dev->pdev->id] &=
+ ~(csi_lane_params->csi_lane_mask);
+ i = 0;
+ while (csi_lane_mask & 0x1F) {
+ if (csi_lane_mask & 0x1) {
+ msm_camera_io_w(0x0, csiphy_dev->base +
+ MIPI_CSIPHY_LNn_CFG2_ADDR + 0x40*i);
+ }
+ csi_lane_mask >>= 1;
+ i++;
+ }
+ }
+
+ if (--csiphy_dev->ref_count) {
+ CDBG("%s csiphy refcount = %d\n", __func__,
+ csiphy_dev->ref_count);
+ return 0;
+ }
+
+ msm_camera_io_w(0x0, csiphy_dev->base + MIPI_CSIPHY_LNCK_CFG2_ADDR);
+ msm_camera_io_w(0x0, csiphy_dev->base + MIPI_CSIPHY_GLBL_PWR_CFG_ADDR);
+
+ if (CSIPHY_VERSION != CSIPHY_VERSION_V3)
+ msm_cam_clk_enable(&csiphy_dev->pdev->dev,
+ csiphy_8960_clk_info, csiphy_dev->csiphy_clk,
+ ARRAY_SIZE(csiphy_8960_clk_info), 0);
+ else
+ msm_cam_clk_enable(&csiphy_dev->pdev->dev,
+ csiphy_8974_clk_info, csiphy_dev->csiphy_clk,
+ ARRAY_SIZE(csiphy_8974_clk_info), 0);
+
+ iounmap(csiphy_dev->base);
+ csiphy_dev->base = NULL;
+ csiphy_dev->csiphy_state = CSIPHY_POWER_DOWN;
+ return 0;
+}
+#endif
+
+static long msm_csiphy_cmd(struct csiphy_device *csiphy_dev, void *arg)
+{
+ int rc = 0;
+ struct csiphy_cfg_data *cdata = (struct csiphy_cfg_data *)arg;
+ struct msm_camera_csiphy_params csiphy_params;
+ struct msm_camera_csi_lane_params csi_lane_params;
+ if (!csiphy_dev || !cdata) {
+ pr_err("%s: csiphy_dev NULL\n", __func__);
+ return -EINVAL;
+ }
+ switch (cdata->cfgtype) {
+ case CSIPHY_INIT:
+ rc = msm_csiphy_init(csiphy_dev);
+ break;
+ case CSIPHY_CFG:
+ if (copy_from_user(&csiphy_params,
+ (void *)cdata->cfg.csiphy_params,
+ sizeof(struct msm_camera_csiphy_params))) {
+ pr_err("%s: %d failed\n", __func__, __LINE__);
+ rc = -EFAULT;
+ break;
+ }
+ rc = msm_csiphy_lane_config(csiphy_dev, &csiphy_params);
+ break;
+ case CSIPHY_RELEASE:
+ if (copy_from_user(&csi_lane_params,
+ (void *)cdata->cfg.csi_lane_params,
+ sizeof(struct msm_camera_csi_lane_params))) {
+ pr_err("%s: %d failed\n", __func__, __LINE__);
+ rc = -EFAULT;
+ break;
+ }
+ rc = msm_csiphy_release(csiphy_dev, &csi_lane_params);
+ break;
+ default:
+ pr_err("%s: %d failed\n", __func__, __LINE__);
+ rc = -ENOIOCTLCMD;
+ break;
+ }
+ return rc;
+}
+
+static int32_t msm_csiphy_get_subdev_id(struct csiphy_device *csiphy_dev,
+ void *arg)
+{
+ uint32_t *subdev_id = (uint32_t *)arg;
+ if (!subdev_id) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ return -EINVAL;
+ }
+ *subdev_id = csiphy_dev->pdev->id;
+ pr_debug("%s:%d subdev_id %d\n", __func__, __LINE__, *subdev_id);
+ return 0;
+}
+
+static long msm_csiphy_subdev_ioctl(struct v4l2_subdev *sd,
+ unsigned int cmd, void *arg)
+{
+ int rc = -ENOIOCTLCMD;
+ struct csiphy_device *csiphy_dev = v4l2_get_subdevdata(sd);
+ CDBG("%s:%d id %d\n", __func__, __LINE__, csiphy_dev->pdev->id);
+ mutex_lock(&csiphy_dev->mutex);
+ switch (cmd) {
+ case VIDIOC_MSM_SENSOR_GET_SUBDEV_ID:
+ rc = msm_csiphy_get_subdev_id(csiphy_dev, arg);
+ break;
+ case VIDIOC_MSM_CSIPHY_IO_CFG:
+ rc = msm_csiphy_cmd(csiphy_dev, arg);
+ break;
+ case VIDIOC_MSM_CSIPHY_RELEASE:
+ rc = msm_csiphy_release(csiphy_dev, arg);
+ break;
+ default:
+ pr_err("%s: command not found\n", __func__);
+ }
+ mutex_unlock(&csiphy_dev->mutex);
+ CDBG("%s:%d\n", __func__, __LINE__);
+ return rc;
+}
+
+static const struct v4l2_subdev_internal_ops msm_csiphy_internal_ops;
+
+static struct v4l2_subdev_core_ops msm_csiphy_subdev_core_ops = {
+ .g_chip_ident = &msm_csiphy_subdev_g_chip_ident,
+ .ioctl = &msm_csiphy_subdev_ioctl,
+};
+
+static const struct v4l2_subdev_ops msm_csiphy_subdev_ops = {
+ .core = &msm_csiphy_subdev_core_ops,
+};
+
+static int __devinit csiphy_probe(struct platform_device *pdev)
+{
+ struct csiphy_device *new_csiphy_dev;
+ int rc = 0;
+
+ new_csiphy_dev = kzalloc(sizeof(struct csiphy_device), GFP_KERNEL);
+ if (!new_csiphy_dev) {
+ pr_err("%s: no enough memory\n", __func__);
+ return -ENOMEM;
+ }
+
+ v4l2_subdev_init(&new_csiphy_dev->msm_sd.sd, &msm_csiphy_subdev_ops);
+ v4l2_set_subdevdata(&new_csiphy_dev->msm_sd.sd, new_csiphy_dev);
+ platform_set_drvdata(pdev, &new_csiphy_dev->msm_sd.sd);
+
+ mutex_init(&new_csiphy_dev->mutex);
+
+ if (pdev->dev.of_node)
+ of_property_read_u32((&pdev->dev)->of_node,
+ "cell-index", &pdev->id);
+ CDBG("%s: device id = %d\n", __func__, pdev->id);
+
+ new_csiphy_dev->mem = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM, "csiphy");
+ if (!new_csiphy_dev->mem) {
+ pr_err("%s: no mem resource?\n", __func__);
+ rc = -ENODEV;
+ goto csiphy_no_resource;
+ }
+ new_csiphy_dev->irq = platform_get_resource_byname(pdev,
+ IORESOURCE_IRQ, "csiphy");
+ if (!new_csiphy_dev->irq) {
+ pr_err("%s: no irq resource?\n", __func__);
+ rc = -ENODEV;
+ goto csiphy_no_resource;
+ }
+ new_csiphy_dev->io = request_mem_region(new_csiphy_dev->mem->start,
+ resource_size(new_csiphy_dev->mem), pdev->name);
+ if (!new_csiphy_dev->io) {
+ pr_err("%s: no valid mem region\n", __func__);
+ rc = -EBUSY;
+ goto csiphy_no_resource;
+ }
+
+ rc = request_irq(new_csiphy_dev->irq->start, msm_csiphy_irq,
+ IRQF_TRIGGER_RISING, "csiphy", new_csiphy_dev);
+ if (rc < 0) {
+ release_mem_region(new_csiphy_dev->mem->start,
+ resource_size(new_csiphy_dev->mem));
+ pr_err("%s: irq request fail\n", __func__);
+ rc = -EBUSY;
+ goto csiphy_no_resource;
+ }
+ disable_irq(new_csiphy_dev->irq->start);
+
+ new_csiphy_dev->pdev = pdev;
+ new_csiphy_dev->msm_sd.sd.internal_ops = &msm_csiphy_internal_ops;
+ new_csiphy_dev->msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ snprintf(new_csiphy_dev->msm_sd.sd.name,
+ ARRAY_SIZE(new_csiphy_dev->msm_sd.sd.name), "msm_csiphy");
+ media_entity_init(&new_csiphy_dev->msm_sd.sd.entity, 0, NULL, 0);
+ new_csiphy_dev->msm_sd.sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV;
+ new_csiphy_dev->msm_sd.sd.entity.group_id = MSM_CAMERA_SUBDEV_CSIPHY;
+
+ msm_sd_register(&new_csiphy_dev->msm_sd);
+ new_csiphy_dev->csiphy_state = CSIPHY_POWER_DOWN;
+ return 0;
+
+csiphy_no_resource:
+ mutex_destroy(&new_csiphy_dev->mutex);
+ kfree(new_csiphy_dev);
+ return 0;
+}
+
+static const struct of_device_id msm_csiphy_dt_match[] = {
+ {.compatible = "qcom,csiphy"},
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, msm_csiphy_dt_match);
+
+static struct platform_driver csiphy_driver = {
+ .probe = csiphy_probe,
+ .driver = {
+ .name = MSM_CSIPHY_DRV_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = msm_csiphy_dt_match,
+ },
+};
+
+static int __init msm_csiphy_init_module(void)
+{
+ return platform_driver_register(&csiphy_driver);
+}
+
+static void __exit msm_csiphy_exit_module(void)
+{
+ platform_driver_unregister(&csiphy_driver);
+}
+
+module_init(msm_csiphy_init_module);
+module_exit(msm_csiphy_exit_module);
+MODULE_DESCRIPTION("MSM CSIPHY driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/msmb/sensor/csiphy/msm_csiphy.h b/drivers/media/video/msmb/sensor/csiphy/msm_csiphy.h
new file mode 100644
index 0000000..e19be34
--- /dev/null
+++ b/drivers/media/video/msmb/sensor/csiphy/msm_csiphy.h
@@ -0,0 +1,49 @@
+/* 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
+ * 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 MSM_CSIPHY_H
+#define MSM_CSIPHY_H
+
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <media/v4l2-subdev.h>
+#include <media/msm_cam_sensor.h>
+#include "msm_sd.h"
+
+#define MAX_CSIPHY 3
+
+enum msm_csiphy_state_t {
+ CSIPHY_POWER_UP,
+ CSIPHY_POWER_DOWN,
+};
+
+struct csiphy_device {
+ struct platform_device *pdev;
+ struct msm_sd_subdev msm_sd;
+ struct v4l2_subdev subdev;
+ struct resource *mem;
+ struct resource *irq;
+ struct resource *io;
+ void __iomem *base;
+ struct mutex mutex;
+ uint32_t hw_version;
+ enum msm_csiphy_state_t csiphy_state;
+
+ struct clk *csiphy_clk[4];
+ uint8_t ref_count;
+ uint16_t lane_mask[MAX_CSIPHY];
+};
+
+#define VIDIOC_MSM_CSIPHY_RELEASE \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 9, void *)
+#endif
diff --git a/drivers/media/video/msmb/sensor/io/Makefile b/drivers/media/video/msmb/sensor/io/Makefile
new file mode 100644
index 0000000..ec1faa5
--- /dev/null
+++ b/drivers/media/video/msmb/sensor/io/Makefile
@@ -0,0 +1,3 @@
+ccflags-y += -Idrivers/media/video/msmb/
+ccflags-y += -Idrivers/media/video/msmb/sensor/cci
+obj-$(CONFIG_MSMB_CAMERA) += msm_camera_io_util.o msm_camera_cci_i2c.o msm_camera_qup_i2c.o msm_camera_i2c_mux.o
diff --git a/drivers/media/video/msmb/sensor/io/msm_camera_cci_i2c.c b/drivers/media/video/msmb/sensor/io/msm_camera_cci_i2c.c
new file mode 100644
index 0000000..b07bb36
--- /dev/null
+++ b/drivers/media/video/msmb/sensor/io/msm_camera_cci_i2c.c
@@ -0,0 +1,284 @@
+/* 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
+ * 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 <mach/camera2.h>
+#include "msm_camera_i2c.h"
+#include "msm_cci.h"
+
+#define CONFIG_MSMB_CAMERA_DEBUG
+#undef CDBG
+#ifdef CONFIG_MSMB_CAMERA_DEBUG
+#define CDBG(fmt, args...) pr_debug(fmt, ##args)
+#define S_I2C_DBG(fmt, args...) pr_debug(fmt, ##args)
+#else
+#define CDBG(fmt, args...) do { } while (0)
+#define S_I2C_DBG(fmt, args...) do { } while (0)
+#endif
+
+
+int32_t msm_camera_cci_i2c_read(struct msm_camera_i2c_client *client,
+ uint16_t addr, uint16_t *data,
+ enum msm_camera_i2c_data_type data_type)
+{
+ int32_t rc = -EFAULT;
+ unsigned char buf[client->addr_type+data_type];
+ struct msm_camera_cci_ctrl cci_ctrl;
+
+ if ((client->addr_type != MSM_CAMERA_I2C_BYTE_ADDR
+ && client->addr_type != MSM_CAMERA_I2C_WORD_ADDR)
+ || (data_type != MSM_CAMERA_I2C_BYTE_DATA
+ && data_type != MSM_CAMERA_I2C_WORD_DATA))
+ return rc;
+
+ cci_ctrl.cmd = MSM_CCI_I2C_READ;
+ cci_ctrl.cci_info = client->cci_client;
+ cci_ctrl.cfg.cci_i2c_read_cfg.addr = addr;
+ cci_ctrl.cfg.cci_i2c_read_cfg.addr_type = client->addr_type;
+ cci_ctrl.cfg.cci_i2c_read_cfg.data = buf;
+ cci_ctrl.cfg.cci_i2c_read_cfg.num_byte = data_type;
+ rc = v4l2_subdev_call(client->cci_client->cci_subdev,
+ core, ioctl, VIDIOC_MSM_CCI_CFG, &cci_ctrl);
+ if (rc < 0) {
+ pr_err("%s: line %d rc = %d\n", __func__, __LINE__, rc);
+ return rc;
+ }
+ rc = cci_ctrl.status;
+ if (data_type == MSM_CAMERA_I2C_BYTE_DATA)
+ *data = buf[0];
+ else
+ *data = buf[0] << 8 | buf[1];
+
+ S_I2C_DBG("%s addr = 0x%x data: 0x%x\n", __func__, addr, *data);
+ return rc;
+}
+
+int32_t msm_camera_cci_i2c_read_seq(struct msm_camera_i2c_client *client,
+ uint16_t addr, uint8_t *data, uint16_t num_byte)
+{
+ int32_t rc = -EFAULT;
+ unsigned char buf[client->addr_type+num_byte];
+ int i;
+ struct msm_camera_cci_ctrl cci_ctrl;
+
+ if ((client->addr_type != MSM_CAMERA_I2C_BYTE_ADDR
+ && client->addr_type != MSM_CAMERA_I2C_WORD_ADDR)
+ || num_byte == 0)
+ return rc;
+
+ cci_ctrl.cmd = MSM_CCI_I2C_READ;
+ cci_ctrl.cci_info = client->cci_client;
+ cci_ctrl.cfg.cci_i2c_read_cfg.addr = addr;
+ cci_ctrl.cfg.cci_i2c_read_cfg.addr_type = client->addr_type;
+ cci_ctrl.cfg.cci_i2c_read_cfg.data = buf;
+ cci_ctrl.cfg.cci_i2c_read_cfg.num_byte = num_byte;
+ rc = v4l2_subdev_call(client->cci_client->cci_subdev,
+ core, ioctl, VIDIOC_MSM_CCI_CFG, &cci_ctrl);
+ CDBG("%s line %d rc = %d\n", __func__, __LINE__, rc);
+ rc = cci_ctrl.status;
+
+ S_I2C_DBG("%s addr = 0x%x", __func__, addr);
+ for (i = 0; i < num_byte; i++) {
+ data[i] = buf[i];
+ S_I2C_DBG("Byte %d: 0x%x\n", i, buf[i]);
+ S_I2C_DBG("Data: 0x%x\n", data[i]);
+ }
+ return rc;
+}
+
+int32_t msm_camera_cci_i2c_write(struct msm_camera_i2c_client *client,
+ uint16_t addr, uint16_t data,
+ enum msm_camera_i2c_data_type data_type)
+{
+ int32_t rc = -EFAULT;
+ struct msm_camera_cci_ctrl cci_ctrl;
+ struct msm_camera_i2c_reg_conf reg_conf_tbl;
+
+ if ((client->addr_type != MSM_CAMERA_I2C_BYTE_ADDR
+ && client->addr_type != MSM_CAMERA_I2C_WORD_ADDR)
+ || (data_type != MSM_CAMERA_I2C_BYTE_DATA
+ && data_type != MSM_CAMERA_I2C_WORD_DATA))
+ return rc;
+
+ CDBG("%s:%d reg addr = 0x%x data type: %d\n",
+ __func__, __LINE__, addr, data_type);
+ reg_conf_tbl.reg_addr = addr;
+ reg_conf_tbl.reg_data = data;
+ cci_ctrl.cmd = MSM_CCI_I2C_WRITE;
+ cci_ctrl.cci_info = client->cci_client;
+ cci_ctrl.cfg.cci_i2c_write_cfg.reg_conf_tbl = ®_conf_tbl;
+ cci_ctrl.cfg.cci_i2c_write_cfg.data_type = data_type;
+ cci_ctrl.cfg.cci_i2c_write_cfg.addr_type = client->addr_type;
+ cci_ctrl.cfg.cci_i2c_write_cfg.size = 1;
+ rc = v4l2_subdev_call(client->cci_client->cci_subdev,
+ core, ioctl, VIDIOC_MSM_CCI_CFG, &cci_ctrl);
+ if (rc < 0) {
+ pr_err("%s: line %d rc = %d\n", __func__, __LINE__, rc);
+ return rc;
+ }
+ rc = cci_ctrl.status;
+ return rc;
+}
+
+int32_t msm_camera_cci_i2c_write_seq(struct msm_camera_i2c_client *client,
+ uint16_t addr, uint8_t *data, uint16_t num_byte)
+{
+ int32_t rc = -EFAULT;
+ uint8_t i = 0;
+ struct msm_camera_cci_ctrl cci_ctrl;
+ struct msm_camera_i2c_reg_conf reg_conf_tbl[num_byte];
+
+ if ((client->addr_type != MSM_CAMERA_I2C_BYTE_ADDR
+ && client->addr_type != MSM_CAMERA_I2C_WORD_ADDR)
+ || num_byte == 0)
+ return rc;
+
+ S_I2C_DBG("%s reg addr = 0x%x num bytes: %d\n",
+ __func__, addr, num_byte);
+ reg_conf_tbl[0].reg_addr = addr;
+ for (i = 0; i < num_byte; i++)
+ reg_conf_tbl[i].reg_data = data[i];
+ cci_ctrl.cmd = MSM_CCI_I2C_WRITE;
+ cci_ctrl.cci_info = client->cci_client;
+ cci_ctrl.cfg.cci_i2c_write_cfg.reg_conf_tbl = reg_conf_tbl;
+ cci_ctrl.cfg.cci_i2c_write_cfg.size = num_byte;
+ rc = v4l2_subdev_call(client->cci_client->cci_subdev,
+ core, ioctl, VIDIOC_MSM_CCI_CFG, &cci_ctrl);
+ CDBG("%s line %d rc = %d\n", __func__, __LINE__, rc);
+ rc = cci_ctrl.status;
+ return rc;
+}
+
+int32_t msm_camera_cci_i2c_write_table(
+ struct msm_camera_i2c_client *client,
+ struct msm_camera_i2c_reg_setting *write_setting)
+{
+ int i;
+ int32_t rc = -EFAULT;
+ struct msm_camera_i2c_reg_array *reg_setting;
+ uint16_t client_addr_type;
+
+ if (!client || !write_setting)
+ return rc;
+
+ if ((write_setting->addr_type != MSM_CAMERA_I2C_BYTE_ADDR
+ && write_setting->addr_type != MSM_CAMERA_I2C_WORD_ADDR)
+ || (write_setting->data_type != MSM_CAMERA_I2C_BYTE_DATA
+ && write_setting->data_type != MSM_CAMERA_I2C_WORD_DATA))
+ return rc;
+
+ reg_setting = write_setting->reg_setting;
+ client_addr_type = client->addr_type;
+ client->addr_type = write_setting->addr_type;
+
+ for (i = 0; i < write_setting->size; i++) {
+ rc = msm_camera_cci_i2c_write(client, reg_setting->reg_addr,
+ reg_setting->reg_data, write_setting->data_type);
+ if (rc < 0)
+ return rc;
+ reg_setting++;
+ }
+ if (write_setting->delay > 20)
+ msleep(write_setting->delay);
+ else if (write_setting->delay)
+ usleep_range(write_setting->delay * 1000, (write_setting->delay
+ * 1000) + 1000);
+
+ client->addr_type = client_addr_type;
+ return rc;
+}
+
+int32_t msm_camera_cci_i2c_write_seq_table(
+ struct msm_camera_i2c_client *client,
+ struct msm_camera_i2c_seq_reg_setting *write_setting)
+{
+ int i;
+ int32_t rc = -EFAULT;
+ struct msm_camera_i2c_seq_reg_array *reg_setting;
+ uint16_t client_addr_type;
+
+ if (!client || !write_setting)
+ return rc;
+
+ if ((write_setting->addr_type != MSM_CAMERA_I2C_BYTE_ADDR
+ && write_setting->addr_type != MSM_CAMERA_I2C_WORD_ADDR)) {
+ pr_err("%s Invalide addr type %d\n", __func__,
+ write_setting->addr_type);
+ return rc;
+ }
+
+ reg_setting = write_setting->reg_setting;
+ client_addr_type = client->addr_type;
+ client->addr_type = write_setting->addr_type;
+
+ for (i = 0; i < write_setting->size; i++) {
+ rc = msm_camera_cci_i2c_write_seq(client, reg_setting->reg_addr,
+ reg_setting->reg_data, reg_setting->reg_data_size);
+ if (rc < 0)
+ return rc;
+ reg_setting++;
+ }
+ if (write_setting->delay > 20)
+ msleep(write_setting->delay);
+ else if (write_setting->delay)
+ usleep_range(write_setting->delay * 1000, (write_setting->delay
+ * 1000) + 1000);
+
+ client->addr_type = client_addr_type;
+ return rc;
+}
+
+int32_t msm_camera_cci_i2c_write_table_w_microdelay(
+ struct msm_camera_i2c_client *client,
+ struct msm_camera_i2c_reg_tbl *reg_tbl, uint16_t size,
+ enum msm_camera_i2c_data_type data_type)
+{
+ int i;
+ int32_t rc = -EFAULT;
+
+ if (!client || !reg_tbl)
+ return rc;
+
+ if ((client->addr_type != MSM_CAMERA_I2C_BYTE_ADDR
+ && client->addr_type != MSM_CAMERA_I2C_WORD_ADDR)
+ || (data_type != MSM_CAMERA_I2C_BYTE_DATA
+ && data_type != MSM_CAMERA_I2C_WORD_DATA))
+ return rc;
+
+ for (i = 0; i < size; i++) {
+ rc = msm_camera_cci_i2c_write(client, reg_tbl->reg_addr,
+ reg_tbl->reg_data, data_type);
+ if (rc < 0)
+ return rc;
+ if (reg_tbl->delay)
+ usleep_range(reg_tbl->delay, reg_tbl->delay + 1000);
+ reg_tbl++;
+ }
+ return rc;
+}
+
+int32_t msm_sensor_cci_i2c_util(struct msm_camera_i2c_client *client,
+ uint16_t cci_cmd)
+{
+ int32_t rc = 0;
+ struct msm_camera_cci_ctrl cci_ctrl;
+
+ CDBG("%s line %d\n", __func__, __LINE__);
+ cci_ctrl.cmd = cci_cmd;
+ cci_ctrl.cci_info = client->cci_client;
+ rc = v4l2_subdev_call(client->cci_client->cci_subdev,
+ core, ioctl, VIDIOC_MSM_CCI_CFG, &cci_ctrl);
+ if (rc < 0) {
+ pr_err("%s line %d rc = %d\n", __func__, __LINE__, rc);
+ return rc;
+ }
+ return cci_ctrl.status;
+}
diff --git a/drivers/media/video/msmb/sensor/io/msm_camera_i2c.h b/drivers/media/video/msmb/sensor/io/msm_camera_i2c.h
new file mode 100644
index 0000000..aa38e62
--- /dev/null
+++ b/drivers/media/video/msmb/sensor/io/msm_camera_i2c.h
@@ -0,0 +1,108 @@
+/* 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
+ * 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 MSM_CAMERA_CCI_I2C_H
+#define MSM_CAMERA_CCI_I2C_H
+
+#include <linux/delay.h>
+#include <media/v4l2-subdev.h>
+#include <media/msm_cam_sensor.h>
+
+struct msm_camera_i2c_client {
+ struct msm_camera_i2c_fn_t *i2c_func_tbl;
+ struct i2c_client *client;
+ struct msm_camera_cci_client *cci_client;
+ enum msm_camera_i2c_reg_addr_type addr_type;
+};
+
+struct msm_camera_i2c_reg_tbl {
+ uint16_t reg_addr;
+ uint16_t reg_data;
+ uint16_t delay;
+};
+
+struct msm_camera_i2c_fn_t {
+ int (*i2c_read) (struct msm_camera_i2c_client *, uint16_t, uint16_t *,
+ enum msm_camera_i2c_data_type);
+ int32_t (*i2c_read_seq)(struct msm_camera_i2c_client *, uint16_t,
+ uint8_t *, uint16_t);
+ int (*i2c_write) (struct msm_camera_i2c_client *, uint16_t, uint16_t,
+ enum msm_camera_i2c_data_type);
+ int (*i2c_write_seq) (struct msm_camera_i2c_client *, uint16_t ,
+ uint8_t *, uint16_t);
+ int32_t (*i2c_write_table)(struct msm_camera_i2c_client *,
+ struct msm_camera_i2c_reg_setting *);
+ int32_t (*i2c_write_seq_table)(struct msm_camera_i2c_client *,
+ struct msm_camera_i2c_seq_reg_setting *);
+ int32_t (*i2c_write_table_w_microdelay)
+ (struct msm_camera_i2c_client *,
+ struct msm_camera_i2c_reg_tbl *, uint16_t,
+ enum msm_camera_i2c_data_type);
+ int32_t (*i2c_util)(struct msm_camera_i2c_client *, uint16_t);
+};
+
+int32_t msm_camera_cci_i2c_read(struct msm_camera_i2c_client *client,
+ uint16_t addr, uint16_t *data,
+ enum msm_camera_i2c_data_type data_type);
+
+int32_t msm_camera_cci_i2c_read_seq(struct msm_camera_i2c_client *client,
+ uint16_t addr, uint8_t *data, uint16_t num_byte);
+
+int32_t msm_camera_cci_i2c_write(struct msm_camera_i2c_client *client,
+ uint16_t addr, uint16_t data,
+ enum msm_camera_i2c_data_type data_type);
+
+int32_t msm_camera_cci_i2c_write_seq(struct msm_camera_i2c_client *client,
+ uint16_t addr, uint8_t *data, uint16_t num_byte);
+
+int32_t msm_camera_cci_i2c_write_table(
+ struct msm_camera_i2c_client *client,
+ struct msm_camera_i2c_reg_setting *write_setting);
+
+int32_t msm_camera_cci_i2c_write_seq_table(
+ struct msm_camera_i2c_client *client,
+ struct msm_camera_i2c_seq_reg_setting *write_setting);
+
+int32_t msm_camera_cci_i2c_write_table_w_microdelay(
+ struct msm_camera_i2c_client *client,
+ struct msm_camera_i2c_reg_tbl *reg_tbl, uint16_t size,
+ enum msm_camera_i2c_data_type data_type);
+
+int32_t msm_sensor_cci_i2c_util(struct msm_camera_i2c_client *client,
+ uint16_t cci_cmd);
+
+int32_t msm_camera_qup_i2c_read(struct msm_camera_i2c_client *client,
+ uint16_t addr, uint16_t *data,
+ enum msm_camera_i2c_data_type data_type);
+
+int32_t msm_camera_qup_i2c_read_seq(struct msm_camera_i2c_client *client,
+ uint16_t addr, uint8_t *data, uint16_t num_byte);
+
+int32_t msm_camera_qup_i2c_write(struct msm_camera_i2c_client *client,
+ uint16_t addr, uint16_t data,
+ enum msm_camera_i2c_data_type data_type);
+
+int32_t msm_camera_qup_i2c_write_seq(struct msm_camera_i2c_client *client,
+ uint16_t addr, uint8_t *data, uint16_t num_byte);
+
+int32_t msm_camera_qup_i2c_write_table(struct msm_camera_i2c_client *client,
+ struct msm_camera_i2c_reg_setting *write_setting);
+
+int32_t msm_camera_qup_i2c_write_seq_table(struct msm_camera_i2c_client *client,
+ struct msm_camera_i2c_seq_reg_setting *write_setting);
+
+int32_t msm_camera_qup_i2c_write_table_w_microdelay(
+ struct msm_camera_i2c_client *client,
+ struct msm_camera_i2c_reg_tbl *reg_tbl, uint16_t size,
+ enum msm_camera_i2c_data_type data_type);
+
+#endif
diff --git a/drivers/media/video/msmb/sensor/io/msm_camera_i2c_mux.c b/drivers/media/video/msmb/sensor/io/msm_camera_i2c_mux.c
new file mode 100644
index 0000000..49759e6
--- /dev/null
+++ b/drivers/media/video/msmb/sensor/io/msm_camera_i2c_mux.c
@@ -0,0 +1,188 @@
+/* Copyright (c) 2011-2013, The Linux Foundatation. 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/delay.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <mach/board.h>
+#include <mach/camera.h>
+#include "msm_camera_i2c_mux.h"
+
+/* TODO move this somewhere else */
+#define MSM_I2C_MUX_DRV_NAME "msm_cam_i2c_mux"
+static int msm_i2c_mux_config(struct i2c_mux_device *mux_device, uint8_t *mode)
+{
+ uint32_t val;
+ val = msm_camera_io_r(mux_device->ctl_base);
+ if (*mode == MODE_DUAL) {
+ msm_camera_io_w(val | 0x3, mux_device->ctl_base);
+ } else if (*mode == MODE_L) {
+ msm_camera_io_w(((val | 0x2) & ~(0x1)), mux_device->ctl_base);
+ val = msm_camera_io_r(mux_device->ctl_base);
+ CDBG("the camio mode config left value is %d\n", val);
+ } else {
+ msm_camera_io_w(((val | 0x1) & ~(0x2)), mux_device->ctl_base);
+ val = msm_camera_io_r(mux_device->ctl_base);
+ CDBG("the camio mode config right value is %d\n", val);
+ }
+ return 0;
+}
+
+static int msm_i2c_mux_init(struct i2c_mux_device *mux_device)
+{
+ int rc = 0, val = 0;
+ if (mux_device->use_count == 0) {
+ mux_device->ctl_base = ioremap(mux_device->ctl_mem->start,
+ resource_size(mux_device->ctl_mem));
+ if (!mux_device->ctl_base) {
+ rc = -ENOMEM;
+ return rc;
+ }
+ mux_device->rw_base = ioremap(mux_device->rw_mem->start,
+ resource_size(mux_device->rw_mem));
+ if (!mux_device->rw_base) {
+ rc = -ENOMEM;
+ iounmap(mux_device->ctl_base);
+ return rc;
+ }
+ val = msm_camera_io_r(mux_device->rw_base);
+ msm_camera_io_w((val | 0x200), mux_device->rw_base);
+ }
+ mux_device->use_count++;
+ return 0;
+};
+
+static int msm_i2c_mux_release(struct i2c_mux_device *mux_device)
+{
+ int val = 0;
+ mux_device->use_count--;
+ if (mux_device->use_count == 0) {
+ val = msm_camera_io_r(mux_device->rw_base);
+ msm_camera_io_w((val & ~0x200), mux_device->rw_base);
+ iounmap(mux_device->rw_base);
+ iounmap(mux_device->ctl_base);
+ }
+ return 0;
+}
+
+static long msm_i2c_mux_subdev_ioctl(struct v4l2_subdev *sd,
+ unsigned int cmd, void *arg)
+{
+ struct i2c_mux_device *mux_device;
+ int rc = 0;
+ mux_device = v4l2_get_subdevdata(sd);
+ if (mux_device == NULL) {
+ rc = -ENOMEM;
+ return rc;
+ }
+ mutex_lock(&mux_device->mutex);
+ switch (cmd) {
+ case VIDIOC_MSM_I2C_MUX_CFG:
+ rc = msm_i2c_mux_config(mux_device, (uint8_t *) arg);
+ break;
+ case VIDIOC_MSM_I2C_MUX_INIT:
+ rc = msm_i2c_mux_init(mux_device);
+ break;
+ case VIDIOC_MSM_I2C_MUX_RELEASE:
+ rc = msm_i2c_mux_release(mux_device);
+ break;
+ default:
+ rc = -ENOIOCTLCMD;
+ }
+ mutex_unlock(&mux_device->mutex);
+ return rc;
+}
+
+static struct v4l2_subdev_core_ops msm_i2c_mux_subdev_core_ops = {
+ .ioctl = &msm_i2c_mux_subdev_ioctl,
+};
+
+static const struct v4l2_subdev_ops msm_i2c_mux_subdev_ops = {
+ .core = &msm_i2c_mux_subdev_core_ops,
+};
+
+static int __devinit i2c_mux_probe(struct platform_device *pdev)
+{
+ struct i2c_mux_device *mux_device;
+ int rc = 0;
+ CDBG("%s: device id = %d\n", __func__, pdev->id);
+ mux_device = kzalloc(sizeof(struct i2c_mux_device), GFP_KERNEL);
+ if (!mux_device) {
+ pr_err("%s: no enough memory\n", __func__);
+ return -ENOMEM;
+ }
+
+ v4l2_subdev_init(&mux_device->subdev, &msm_i2c_mux_subdev_ops);
+ v4l2_set_subdevdata(&mux_device->subdev, mux_device);
+ platform_set_drvdata(pdev, &mux_device->subdev);
+ mutex_init(&mux_device->mutex);
+
+ mux_device->ctl_mem = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM, "i2c_mux_ctl");
+ if (!mux_device->ctl_mem) {
+ pr_err("%s: no mem resource?\n", __func__);
+ rc = -ENODEV;
+ goto i2c_mux_no_resource;
+ }
+ mux_device->ctl_io = request_mem_region(mux_device->ctl_mem->start,
+ resource_size(mux_device->ctl_mem), pdev->name);
+ if (!mux_device->ctl_io) {
+ pr_err("%s: no valid mem region\n", __func__);
+ rc = -EBUSY;
+ goto i2c_mux_no_resource;
+ }
+ mux_device->rw_mem = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM, "i2c_mux_rw");
+ if (!mux_device->rw_mem) {
+ pr_err("%s: no mem resource?\n", __func__);
+ rc = -ENODEV;
+ goto i2c_mux_no_resource;
+ }
+ mux_device->rw_io = request_mem_region(mux_device->rw_mem->start,
+ resource_size(mux_device->rw_mem), pdev->name);
+ if (!mux_device->rw_io) {
+ pr_err("%s: no valid mem region\n", __func__);
+ rc = -EBUSY;
+ goto i2c_mux_no_resource;
+ }
+ mux_device->pdev = pdev;
+ return 0;
+
+i2c_mux_no_resource:
+ mutex_destroy(&mux_device->mutex);
+ kfree(mux_device);
+ return 0;
+}
+
+static struct platform_driver i2c_mux_driver = {
+ .probe = i2c_mux_probe,
+ .driver = {
+ .name = MSM_I2C_MUX_DRV_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init msm_camera_i2c_mux_init_module(void)
+{
+ return platform_driver_register(&i2c_mux_driver);
+}
+
+static void __exit msm_camera_i2c_mux_exit_module(void)
+{
+ platform_driver_unregister(&i2c_mux_driver);
+}
+
+module_init(msm_camera_i2c_mux_init_module);
+module_exit(msm_camera_i2c_mux_exit_module);
+MODULE_DESCRIPTION("MSM Camera I2C mux driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/msmb/sensor/io/msm_camera_i2c_mux.h b/drivers/media/video/msmb/sensor/io/msm_camera_i2c_mux.h
new file mode 100644
index 0000000..30f908b
--- /dev/null
+++ b/drivers/media/video/msmb/sensor/io/msm_camera_i2c_mux.h
@@ -0,0 +1,46 @@
+/* Copyright (c) 2011-2012, 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 MSM_I2C_MUX_H
+#define MSM_I2C_MUX_H
+
+#include <linux/io.h>
+#include <media/v4l2-subdev.h>
+
+struct i2c_mux_device {
+ struct platform_device *pdev;
+ struct v4l2_subdev subdev;
+ struct resource *ctl_mem;
+ struct resource *ctl_io;
+ void __iomem *ctl_base;
+ struct resource *rw_mem;
+ struct resource *rw_io;
+ void __iomem *rw_base;
+ struct mutex mutex;
+ unsigned use_count;
+};
+
+struct i2c_mux_cfg_params {
+ struct v4l2_subdev *subdev;
+ void *parms;
+};
+
+#define VIDIOC_MSM_I2C_MUX_CFG \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 13, struct i2c_mux_cfg_params)
+
+#define VIDIOC_MSM_I2C_MUX_INIT \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 14, struct v4l2_subdev*)
+
+#define VIDIOC_MSM_I2C_MUX_RELEASE \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 15, struct v4l2_subdev*)
+
+#endif
diff --git a/drivers/media/video/msmb/sensor/io/msm_camera_io_util.c b/drivers/media/video/msmb/sensor/io/msm_camera_io_util.c
new file mode 100644
index 0000000..0f41a68
--- /dev/null
+++ b/drivers/media/video/msmb/sensor/io/msm_camera_io_util.c
@@ -0,0 +1,523 @@
+/* Copyright (c) 2011-2013, The Linux Foundataion. 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/delay.h>
+#include <linux/clk.h>
+#include <linux/gpio.h>
+#include <linux/regulator/consumer.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <mach/camera2.h>
+#include <mach/gpiomux.h>
+#include <mach/msm_bus.h>
+#include "msm_camera_io_util.h"
+
+#define BUFF_SIZE_128 128
+
+#define CONFIG_MSMB_CAMERA_DEBUG
+#undef CDBG
+#ifdef CONFIG_MSMB_CAMERA_DEBUG
+#define CDBG(fmt, args...) pr_debug(fmt, ##args)
+#else
+#define CDBG(fmt, args...) do { } while (0)
+#endif
+
+void msm_camera_io_w(u32 data, void __iomem *addr)
+{
+ CDBG("%s: %08x %08x\n", __func__, (int) (addr), (data));
+ writel_relaxed((data), (addr));
+}
+
+void msm_camera_io_w_mb(u32 data, void __iomem *addr)
+{
+ CDBG("%s: %08x %08x\n", __func__, (int) (addr), (data));
+ wmb();
+ writel_relaxed((data), (addr));
+ wmb();
+}
+
+u32 msm_camera_io_r(void __iomem *addr)
+{
+ uint32_t data = readl_relaxed(addr);
+ CDBG("%s: %08x %08x\n", __func__, (int) (addr), (data));
+ return data;
+}
+
+u32 msm_camera_io_r_mb(void __iomem *addr)
+{
+ uint32_t data;
+ rmb();
+ data = readl_relaxed(addr);
+ rmb();
+ CDBG("%s: %08x %08x\n", __func__, (int) (addr), (data));
+ return data;
+}
+
+void msm_camera_io_memcpy_toio(void __iomem *dest_addr,
+ void __iomem *src_addr, u32 len)
+{
+ int i;
+ u32 *d = (u32 *) dest_addr;
+ u32 *s = (u32 *) src_addr;
+
+ for (i = 0; i < len; i++)
+ writel_relaxed(*s++, d++);
+}
+
+void msm_camera_io_dump(void __iomem *addr, int size)
+{
+ char line_str[BUFF_SIZE_128], *p_str;
+ int i;
+ u32 *p = (u32 *) addr;
+ u32 data;
+ CDBG("%s: %p %d\n", __func__, addr, size);
+ line_str[0] = '\0';
+ p_str = line_str;
+ for (i = 0; i < size/4; i++) {
+ if (i % 4 == 0) {
+ snprintf(p_str, 12, "%08x: ", (u32) p);
+ p_str += 10;
+ }
+ data = readl_relaxed(p++);
+ snprintf(p_str, 12, "%08x ", data);
+ p_str += 9;
+ if ((i + 1) % 4 == 0) {
+ CDBG("%s\n", line_str);
+ line_str[0] = '\0';
+ p_str = line_str;
+ }
+ }
+ if (line_str[0] != '\0')
+ CDBG("%s\n", line_str);
+}
+
+void msm_camera_io_memcpy(void __iomem *dest_addr,
+ void __iomem *src_addr, u32 len)
+{
+ CDBG("%s: %p %p %d\n", __func__, dest_addr, src_addr, len);
+ msm_camera_io_memcpy_toio(dest_addr, src_addr, len / 4);
+ msm_camera_io_dump(dest_addr, len);
+}
+
+int msm_cam_clk_enable(struct device *dev, struct msm_cam_clk_info *clk_info,
+ struct clk **clk_ptr, int num_clk, int enable)
+{
+ int i;
+ int rc = 0;
+ if (enable) {
+ for (i = 0; i < num_clk; i++) {
+ CDBG("%s enable %s\n", __func__,
+ clk_info[i].clk_name);
+ clk_ptr[i] = clk_get(dev, clk_info[i].clk_name);
+ if (IS_ERR(clk_ptr[i])) {
+ pr_err("%s get failed\n", clk_info[i].clk_name);
+ rc = PTR_ERR(clk_ptr[i]);
+ goto cam_clk_get_err;
+ }
+ if (clk_info[i].clk_rate >= 0) {
+ rc = clk_set_rate(clk_ptr[i],
+ clk_info[i].clk_rate);
+ if (rc < 0) {
+ pr_err("%s set failed\n",
+ clk_info[i].clk_name);
+ goto cam_clk_set_err;
+ }
+ }
+ rc = clk_prepare(clk_ptr[i]);
+ if (rc < 0) {
+ pr_err("%s prepare failed\n",
+ clk_info[i].clk_name);
+ goto cam_clk_prepare_err;
+ }
+
+ rc = clk_enable(clk_ptr[i]);
+ if (rc < 0) {
+ pr_err("%s enable failed\n",
+ clk_info[i].clk_name);
+ goto cam_clk_enable_err;
+ }
+ if (clk_info[i].delay > 20) {
+ msleep(clk_info[i].delay);
+ } else if (clk_info[i].delay) {
+ usleep_range(clk_info[i].delay * 1000,
+ (clk_info[i].delay * 1000) + 1000);
+ }
+ }
+ } else {
+ for (i = num_clk - 1; i >= 0; i--) {
+ if (clk_ptr[i] != NULL) {
+ CDBG("%s disable %s\n", __func__,
+ clk_info[i].clk_name);
+ clk_disable(clk_ptr[i]);
+ clk_unprepare(clk_ptr[i]);
+ clk_put(clk_ptr[i]);
+ }
+ }
+ }
+ return rc;
+
+
+cam_clk_enable_err:
+ clk_unprepare(clk_ptr[i]);
+cam_clk_prepare_err:
+cam_clk_set_err:
+ clk_put(clk_ptr[i]);
+cam_clk_get_err:
+ for (i--; i >= 0; i--) {
+ if (clk_ptr[i] != NULL) {
+ clk_disable(clk_ptr[i]);
+ clk_unprepare(clk_ptr[i]);
+ clk_put(clk_ptr[i]);
+ }
+ }
+ return rc;
+}
+
+int msm_camera_config_vreg(struct device *dev, struct camera_vreg_t *cam_vreg,
+ int num_vreg, enum msm_camera_vreg_name_t *vreg_seq,
+ int num_vreg_seq, struct regulator **reg_ptr, int config)
+{
+ int i = 0, j = 0;
+ int rc = 0;
+ struct camera_vreg_t *curr_vreg;
+
+ if (num_vreg_seq > num_vreg) {
+ pr_err("%s:%d vreg sequence invalid\n", __func__, __LINE__);
+ return -EINVAL;
+ }
+ if (!num_vreg_seq)
+ num_vreg_seq = num_vreg;
+
+ if (config) {
+ for (i = 0; i < num_vreg_seq; i++) {
+ if (vreg_seq) {
+ j = vreg_seq[i];
+ if (j >= num_vreg)
+ continue;
+ } else
+ j = i;
+ curr_vreg = &cam_vreg[j];
+ reg_ptr[j] = regulator_get(dev,
+ curr_vreg->reg_name);
+ if (IS_ERR(reg_ptr[j])) {
+ pr_err("%s: %s get failed\n",
+ __func__,
+ curr_vreg->reg_name);
+ reg_ptr[j] = NULL;
+ goto vreg_get_fail;
+ }
+ if (curr_vreg->type == REG_LDO) {
+ rc = regulator_set_voltage(
+ reg_ptr[j],
+ curr_vreg->min_voltage,
+ curr_vreg->max_voltage);
+ if (rc < 0) {
+ pr_err("%s: %s set voltage failed\n",
+ __func__,
+ curr_vreg->reg_name);
+ goto vreg_set_voltage_fail;
+ }
+ if (curr_vreg->op_mode >= 0) {
+ rc = regulator_set_optimum_mode(
+ reg_ptr[j],
+ curr_vreg->op_mode);
+ if (rc < 0) {
+ pr_err(
+ "%s:%s set optimum mode fail\n",
+ __func__,
+ curr_vreg->reg_name);
+ goto vreg_set_opt_mode_fail;
+ }
+ }
+ }
+ }
+ } else {
+ for (i = num_vreg_seq-1; i >= 0; i--) {
+ if (vreg_seq) {
+ j = vreg_seq[i];
+ if (j >= num_vreg)
+ continue;
+ } else
+ j = i;
+ curr_vreg = &cam_vreg[j];
+ if (reg_ptr[j]) {
+ if (curr_vreg->type == REG_LDO) {
+ if (curr_vreg->op_mode >= 0) {
+ regulator_set_optimum_mode(
+ reg_ptr[j], 0);
+ }
+ regulator_set_voltage(
+ reg_ptr[j], 0, curr_vreg->
+ max_voltage);
+ }
+ regulator_put(reg_ptr[j]);
+ reg_ptr[j] = NULL;
+ }
+ }
+ }
+ return 0;
+
+vreg_unconfig:
+if (curr_vreg->type == REG_LDO)
+ regulator_set_optimum_mode(reg_ptr[j], 0);
+
+vreg_set_opt_mode_fail:
+if (curr_vreg->type == REG_LDO)
+ regulator_set_voltage(reg_ptr[j], 0,
+ curr_vreg->max_voltage);
+
+vreg_set_voltage_fail:
+ regulator_put(reg_ptr[j]);
+ reg_ptr[j] = NULL;
+
+vreg_get_fail:
+ for (i--; i >= 0; i--) {
+ if (vreg_seq) {
+ j = vreg_seq[i];
+ if (j >= num_vreg)
+ continue;
+ } else
+ j = i;
+ curr_vreg = &cam_vreg[j];
+ goto vreg_unconfig;
+ }
+ return -ENODEV;
+}
+
+int msm_camera_enable_vreg(struct device *dev, struct camera_vreg_t *cam_vreg,
+ int num_vreg, enum msm_camera_vreg_name_t *vreg_seq,
+ int num_vreg_seq, struct regulator **reg_ptr, int enable)
+{
+ int i = 0, j = 0, rc = 0;
+
+ if (num_vreg_seq > num_vreg) {
+ pr_err("%s:%d vreg sequence invalid\n", __func__, __LINE__);
+ return -EINVAL;
+ }
+ if (!num_vreg_seq)
+ num_vreg_seq = num_vreg;
+
+ if (enable) {
+ for (i = 0; i < num_vreg_seq; i++) {
+ if (vreg_seq) {
+ j = vreg_seq[i];
+ if (j >= num_vreg)
+ continue;
+ } else
+ j = i;
+ if (IS_ERR(reg_ptr[j])) {
+ pr_err("%s: %s null regulator\n",
+ __func__, cam_vreg[j].reg_name);
+ goto disable_vreg;
+ }
+ rc = regulator_enable(reg_ptr[j]);
+ if (rc < 0) {
+ pr_err("%s: %s enable failed\n",
+ __func__, cam_vreg[j].reg_name);
+ goto disable_vreg;
+ }
+ if (cam_vreg[j].delay > 20)
+ msleep(cam_vreg[j].delay);
+ else if (cam_vreg[j].delay)
+ usleep_range(cam_vreg[j].delay * 1000,
+ (cam_vreg[j].delay * 1000) + 1000);
+ }
+ } else {
+ for (i = num_vreg_seq-1; i >= 0; i--) {
+ if (vreg_seq) {
+ j = vreg_seq[i];
+ if (j >= num_vreg)
+ continue;
+ } else
+ j = i;
+ regulator_disable(reg_ptr[j]);
+ if (cam_vreg[j].delay > 20)
+ msleep(cam_vreg[j].delay);
+ else if (cam_vreg[j].delay)
+ usleep_range(cam_vreg[j].delay * 1000,
+ (cam_vreg[j].delay * 1000) + 1000);
+ }
+ }
+ return rc;
+disable_vreg:
+ for (i--; i >= 0; i--) {
+ if (vreg_seq) {
+ j = vreg_seq[i];
+ if (j >= num_vreg)
+ continue;
+ } else
+ j = i;
+ regulator_disable(reg_ptr[j]);
+ if (cam_vreg[j].delay > 20)
+ msleep(cam_vreg[j].delay);
+ else if (cam_vreg[j].delay)
+ usleep_range(cam_vreg[j].delay * 1000,
+ (cam_vreg[j].delay * 1000) + 1000);
+ }
+ return rc;
+}
+
+void msm_camera_bus_scale_cfg(uint32_t bus_perf_client,
+ enum msm_bus_perf_setting perf_setting)
+{
+ int rc = 0;
+ if (!bus_perf_client) {
+ pr_err("%s: Bus Client NOT Registered!!!\n", __func__);
+ return;
+ }
+
+ switch (perf_setting) {
+ case S_EXIT:
+ rc = msm_bus_scale_client_update_request(bus_perf_client, 1);
+ msm_bus_scale_unregister_client(bus_perf_client);
+ break;
+ case S_PREVIEW:
+ rc = msm_bus_scale_client_update_request(bus_perf_client, 1);
+ break;
+ case S_VIDEO:
+ rc = msm_bus_scale_client_update_request(bus_perf_client, 2);
+ break;
+ case S_CAPTURE:
+ rc = msm_bus_scale_client_update_request(bus_perf_client, 3);
+ break;
+ case S_ZSL:
+ rc = msm_bus_scale_client_update_request(bus_perf_client, 4);
+ break;
+ case S_LIVESHOT:
+ rc = msm_bus_scale_client_update_request(bus_perf_client, 5);
+ break;
+ case S_DEFAULT:
+ break;
+ default:
+ pr_warning("%s: INVALID CASE\n", __func__);
+ }
+}
+
+int msm_camera_set_gpio_table(struct msm_gpio_set_tbl *gpio_tbl,
+ uint8_t gpio_tbl_size, int gpio_en)
+{
+ int rc = 0, i;
+
+ if (gpio_en) {
+ for (i = 0; i < gpio_tbl_size; i++) {
+ gpio_set_value_cansleep(gpio_tbl[i].gpio,
+ gpio_tbl[i].flags);
+ usleep_range(gpio_tbl[i].delay,
+ gpio_tbl[i].delay + 1000);
+ }
+ } else {
+ for (i = gpio_tbl_size - 1; i >= 0; i--) {
+ if (gpio_tbl[i].flags)
+ gpio_set_value_cansleep(gpio_tbl[i].gpio,
+ GPIOF_OUT_INIT_LOW);
+ }
+ }
+ return rc;
+}
+
+int msm_camera_config_single_vreg(struct device *dev,
+ struct camera_vreg_t *cam_vreg, struct regulator **reg_ptr, int config)
+{
+ int rc = 0;
+ if (config) {
+ CDBG("%s enable %s\n", __func__, cam_vreg->reg_name);
+ *reg_ptr = regulator_get(dev, cam_vreg->reg_name);
+ if (IS_ERR(*reg_ptr)) {
+ pr_err("%s: %s get failed\n", __func__,
+ cam_vreg->reg_name);
+ *reg_ptr = NULL;
+ goto vreg_get_fail;
+ }
+ if (cam_vreg->type == REG_LDO) {
+ rc = regulator_set_voltage(
+ *reg_ptr, cam_vreg->min_voltage,
+ cam_vreg->max_voltage);
+ if (rc < 0) {
+ pr_err("%s: %s set voltage failed\n",
+ __func__, cam_vreg->reg_name);
+ goto vreg_set_voltage_fail;
+ }
+ if (cam_vreg->op_mode >= 0) {
+ rc = regulator_set_optimum_mode(*reg_ptr,
+ cam_vreg->op_mode);
+ if (rc < 0) {
+ pr_err(
+ "%s: %s set optimum mode failed\n",
+ __func__, cam_vreg->reg_name);
+ goto vreg_set_opt_mode_fail;
+ }
+ }
+ }
+ rc = regulator_enable(*reg_ptr);
+ if (rc < 0) {
+ pr_err("%s: %s enable failed\n",
+ __func__, cam_vreg->reg_name);
+ goto vreg_unconfig;
+ }
+ } else {
+ if (*reg_ptr) {
+ CDBG("%s disable %s\n", __func__, cam_vreg->reg_name);
+ regulator_disable(*reg_ptr);
+ if (cam_vreg->type == REG_LDO) {
+ if (cam_vreg->op_mode >= 0)
+ regulator_set_optimum_mode(*reg_ptr, 0);
+ regulator_set_voltage(
+ *reg_ptr, 0, cam_vreg->max_voltage);
+ }
+ regulator_put(*reg_ptr);
+ *reg_ptr = NULL;
+ }
+ }
+ return 0;
+
+vreg_unconfig:
+if (cam_vreg->type == REG_LDO)
+ regulator_set_optimum_mode(*reg_ptr, 0);
+
+vreg_set_opt_mode_fail:
+if (cam_vreg->type == REG_LDO)
+ regulator_set_voltage(*reg_ptr, 0, cam_vreg->max_voltage);
+
+vreg_set_voltage_fail:
+ regulator_put(*reg_ptr);
+ *reg_ptr = NULL;
+
+vreg_get_fail:
+ return -ENODEV;
+}
+
+int msm_camera_request_gpio_table(struct gpio *gpio_tbl, uint8_t size,
+ int gpio_en)
+{
+ int rc = 0, i = 0;
+
+ if (!gpio_tbl || !size) {
+ pr_err("%s:%d invalid gpio_tbl %p / size %d\n", __func__,
+ __LINE__, gpio_tbl, size);
+ return -EINVAL;
+ }
+ for (i = 0; i < size; i++) {
+ CDBG("%s:%d i %d, gpio %d dir %ld\n", __func__, __LINE__, i,
+ gpio_tbl[i].gpio, gpio_tbl[i].flags);
+ }
+ if (gpio_en) {
+ rc = gpio_request_array(gpio_tbl, size);
+ if (rc < 0) {
+ pr_err("%s:%d camera gpio request failed\n", __func__,
+ __LINE__);
+ return rc;
+ }
+ } else {
+ gpio_free_array(gpio_tbl, size);
+ }
+ return rc;
+}
diff --git a/drivers/media/video/msmb/sensor/io/msm_camera_io_util.h b/drivers/media/video/msmb/sensor/io/msm_camera_io_util.h
new file mode 100644
index 0000000..499a045
--- /dev/null
+++ b/drivers/media/video/msmb/sensor/io/msm_camera_io_util.h
@@ -0,0 +1,54 @@
+/* Copyright (c) 2011-2013, The Linux Foundataion. 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 __MSM_CAMERA_IO_UTIL_H
+#define __MSM_CAMERA_IO_UTIL_H
+
+#include <linux/regulator/consumer.h>
+#include <linux/gpio.h>
+#include <mach/camera2.h>
+#include <media/msm_cam_sensor.h>
+
+void msm_camera_io_w(u32 data, void __iomem *addr);
+void msm_camera_io_w_mb(u32 data, void __iomem *addr);
+u32 msm_camera_io_r(void __iomem *addr);
+u32 msm_camera_io_r_mb(void __iomem *addr);
+void msm_camera_io_dump(void __iomem *addr, int size);
+void msm_camera_io_memcpy(void __iomem *dest_addr,
+ void __iomem *src_addr, u32 len);
+
+int msm_cam_clk_enable(struct device *dev, struct msm_cam_clk_info *clk_info,
+ struct clk **clk_ptr, int num_clk, int enable);
+
+int msm_camera_config_vreg(struct device *dev, struct camera_vreg_t *cam_vreg,
+ int num_vreg, enum msm_camera_vreg_name_t *vreg_seq,
+ int num_vreg_seq, struct regulator **reg_ptr, int config);
+int msm_camera_enable_vreg(struct device *dev, struct camera_vreg_t *cam_vreg,
+ int num_vreg, enum msm_camera_vreg_name_t *vreg_seq,
+ int num_vreg_seq, struct regulator **reg_ptr, int enable);
+
+void msm_camera_bus_scale_cfg(uint32_t bus_perf_client,
+ enum msm_bus_perf_setting perf_setting);
+
+int msm_camera_set_gpio_table(struct msm_gpio_set_tbl *gpio_tbl,
+ uint8_t gpio_tbl_size, int gpio_en);
+
+void msm_camera_config_single_gpio(uint16_t gpio, unsigned long flags,
+ int gpio_en);
+
+int msm_camera_config_single_vreg(struct device *dev,
+ struct camera_vreg_t *cam_vreg, struct regulator **reg_ptr, int config);
+
+int msm_camera_request_gpio_table(struct gpio *gpio_tbl, uint8_t size,
+ int gpio_en);
+
+#endif
diff --git a/drivers/media/video/msmb/sensor/io/msm_camera_qup_i2c.c b/drivers/media/video/msmb/sensor/io/msm_camera_qup_i2c.c
new file mode 100644
index 0000000..d2cfbff
--- /dev/null
+++ b/drivers/media/video/msmb/sensor/io/msm_camera_qup_i2c.c
@@ -0,0 +1,333 @@
+/* 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
+ * 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 <mach/camera2.h>
+#include "msm_camera_i2c.h"
+
+#define CONFIG_MSMB_CAMERA_DEBUG
+#undef CDBG
+#ifdef CONFIG_MSMB_CAMERA_DEBUG
+#define CDBG(fmt, args...) pr_debug(fmt, ##args)
+#define S_I2C_DBG(fmt, args...) pr_debug(fmt, ##args)
+#else
+#define CDBG(fmt, args...) do { } while (0)
+#define S_I2C_DBG(fmt, args...) do { } while (0)
+#endif
+
+static int32_t msm_camera_qup_i2c_rxdata(
+ struct msm_camera_i2c_client *dev_client, unsigned char *rxdata,
+ int data_length)
+{
+ int32_t rc = 0;
+ uint16_t saddr = dev_client->client->addr >> 1;
+ struct i2c_msg msgs[] = {
+ {
+ .addr = saddr,
+ .flags = 0,
+ .len = dev_client->addr_type,
+ .buf = rxdata,
+ },
+ {
+ .addr = saddr,
+ .flags = I2C_M_RD,
+ .len = data_length,
+ .buf = rxdata,
+ },
+ };
+ rc = i2c_transfer(dev_client->client->adapter, msgs, 2);
+ if (rc < 0)
+ S_I2C_DBG("msm_camera_qup_i2c_rxdata failed 0x%x\n", saddr);
+ return rc;
+}
+
+static int32_t msm_camera_qup_i2c_txdata(
+ struct msm_camera_i2c_client *dev_client, unsigned char *txdata,
+ int length)
+{
+ int32_t rc = 0;
+ uint16_t saddr = dev_client->client->addr >> 1;
+ struct i2c_msg msg[] = {
+ {
+ .addr = saddr,
+ .flags = 0,
+ .len = length,
+ .buf = txdata,
+ },
+ };
+ rc = i2c_transfer(dev_client->client->adapter, msg, 1);
+ if (rc < 0)
+ S_I2C_DBG("msm_camera_qup_i2c_txdata faild 0x%x\n", saddr);
+ return 0;
+}
+
+int32_t msm_camera_qup_i2c_read(struct msm_camera_i2c_client *client,
+ uint16_t addr, uint16_t *data,
+ enum msm_camera_i2c_data_type data_type)
+{
+ int32_t rc = -EFAULT;
+ unsigned char buf[client->addr_type+data_type];
+
+ if ((client->addr_type != MSM_CAMERA_I2C_BYTE_ADDR
+ && client->addr_type != MSM_CAMERA_I2C_WORD_ADDR)
+ || (data_type != MSM_CAMERA_I2C_BYTE_DATA
+ && data_type != MSM_CAMERA_I2C_WORD_DATA))
+ return rc;
+
+ if (client->addr_type == MSM_CAMERA_I2C_BYTE_ADDR) {
+ buf[0] = addr;
+ } else if (client->addr_type == MSM_CAMERA_I2C_WORD_ADDR) {
+ buf[0] = addr >> BITS_PER_BYTE;
+ buf[1] = addr;
+ }
+ rc = msm_camera_qup_i2c_rxdata(client, buf, data_type);
+ if (rc < 0) {
+ S_I2C_DBG("%s fail\n", __func__);
+ return rc;
+ }
+
+ if (data_type == MSM_CAMERA_I2C_BYTE_DATA)
+ *data = buf[0];
+ else
+ *data = buf[0] << 8 | buf[1];
+
+ S_I2C_DBG("%s addr = 0x%x data: 0x%x\n", __func__, addr, *data);
+ return rc;
+}
+
+int32_t msm_camera_qup_i2c_read_seq(struct msm_camera_i2c_client *client,
+ uint16_t addr, uint8_t *data, uint16_t num_byte)
+{
+ int32_t rc = -EFAULT;
+ unsigned char buf[client->addr_type+num_byte];
+ int i;
+
+ if ((client->addr_type != MSM_CAMERA_I2C_BYTE_ADDR
+ && client->addr_type != MSM_CAMERA_I2C_WORD_ADDR)
+ || num_byte == 0)
+ return rc;
+
+ if (client->addr_type == MSM_CAMERA_I2C_BYTE_ADDR) {
+ buf[0] = addr;
+ } else if (client->addr_type == MSM_CAMERA_I2C_WORD_ADDR) {
+ buf[0] = addr >> BITS_PER_BYTE;
+ buf[1] = addr;
+ }
+ rc = msm_camera_qup_i2c_rxdata(client, buf, num_byte);
+ if (rc < 0) {
+ S_I2C_DBG("%s fail\n", __func__);
+ return rc;
+ }
+
+ S_I2C_DBG("%s addr = 0x%x", __func__, addr);
+ for (i = 0; i < num_byte; i++) {
+ data[i] = buf[i];
+ S_I2C_DBG("Byte %d: 0x%x\n", i, buf[i]);
+ S_I2C_DBG("Data: 0x%x\n", data[i]);
+ }
+ return rc;
+}
+
+int32_t msm_camera_qup_i2c_write(struct msm_camera_i2c_client *client,
+ uint16_t addr, uint16_t data,
+ enum msm_camera_i2c_data_type data_type)
+{
+ int32_t rc = -EFAULT;
+ unsigned char buf[client->addr_type+data_type];
+ uint8_t len = 0;
+
+ if ((client->addr_type != MSM_CAMERA_I2C_BYTE_ADDR
+ && client->addr_type != MSM_CAMERA_I2C_WORD_ADDR)
+ || (data_type != MSM_CAMERA_I2C_BYTE_DATA
+ && data_type != MSM_CAMERA_I2C_WORD_DATA))
+ return rc;
+
+ S_I2C_DBG("%s reg addr = 0x%x data type: %d\n",
+ __func__, addr, data_type);
+ if (client->addr_type == MSM_CAMERA_I2C_BYTE_ADDR) {
+ buf[0] = addr;
+ S_I2C_DBG("%s byte %d: 0x%x\n", __func__,
+ len, buf[len]);
+ len = 1;
+ } else if (client->addr_type == MSM_CAMERA_I2C_WORD_ADDR) {
+ buf[0] = addr >> BITS_PER_BYTE;
+ buf[1] = addr;
+ S_I2C_DBG("%s byte %d: 0x%x\n", __func__,
+ len, buf[len]);
+ S_I2C_DBG("%s byte %d: 0x%x\n", __func__,
+ len+1, buf[len+1]);
+ len = 2;
+ }
+ S_I2C_DBG("Data: 0x%x\n", data);
+ if (data_type == MSM_CAMERA_I2C_BYTE_DATA) {
+ buf[len] = data;
+ S_I2C_DBG("Byte %d: 0x%x\n", len, buf[len]);
+ len += 1;
+ } else if (data_type == MSM_CAMERA_I2C_WORD_DATA) {
+ buf[len] = data >> BITS_PER_BYTE;
+ buf[len+1] = data;
+ S_I2C_DBG("Byte %d: 0x%x\n", len, buf[len]);
+ S_I2C_DBG("Byte %d: 0x%x\n", len+1, buf[len+1]);
+ len += 2;
+ }
+ rc = msm_camera_qup_i2c_txdata(client, buf, len);
+ if (rc < 0)
+ S_I2C_DBG("%s fail\n", __func__);
+ return rc;
+}
+
+int32_t msm_camera_qup_i2c_write_seq(struct msm_camera_i2c_client *client,
+ uint16_t addr, uint8_t *data, uint16_t num_byte)
+{
+ int32_t rc = -EFAULT;
+ unsigned char buf[client->addr_type+num_byte];
+ uint8_t len = 0, i = 0;
+
+ if ((client->addr_type != MSM_CAMERA_I2C_BYTE_ADDR
+ && client->addr_type != MSM_CAMERA_I2C_WORD_ADDR)
+ || num_byte == 0)
+ return rc;
+
+ S_I2C_DBG("%s reg addr = 0x%x num bytes: %d\n",
+ __func__, addr, num_byte);
+ if (client->addr_type == MSM_CAMERA_I2C_BYTE_ADDR) {
+ buf[0] = addr;
+ S_I2C_DBG("%s byte %d: 0x%x\n", __func__,
+ len, buf[len]);
+ len = 1;
+ } else if (client->addr_type == MSM_CAMERA_I2C_WORD_ADDR) {
+ buf[0] = addr >> BITS_PER_BYTE;
+ buf[1] = addr;
+ S_I2C_DBG("%s byte %d: 0x%x\n", __func__,
+ len, buf[len]);
+ S_I2C_DBG("%s byte %d: 0x%x\n", __func__,
+ len+1, buf[len+1]);
+ len = 2;
+ }
+ for (i = 0; i < num_byte; i++) {
+ buf[i+len] = data[i];
+ S_I2C_DBG("Byte %d: 0x%x\n", i+len, buf[i+len]);
+ S_I2C_DBG("Data: 0x%x\n", data[i]);
+ }
+ rc = msm_camera_qup_i2c_txdata(client, buf, len+num_byte);
+ if (rc < 0)
+ S_I2C_DBG("%s fail\n", __func__);
+ return rc;
+}
+
+int32_t msm_camera_qup_i2c_write_table(struct msm_camera_i2c_client *client,
+ struct msm_camera_i2c_reg_setting *write_setting)
+{
+ int i;
+ int32_t rc = -EFAULT;
+ struct msm_camera_i2c_reg_array *reg_setting;
+ uint16_t client_addr_type;
+
+ if (!client || !write_setting)
+ return rc;
+
+ if ((write_setting->addr_type != MSM_CAMERA_I2C_BYTE_ADDR
+ && write_setting->addr_type != MSM_CAMERA_I2C_WORD_ADDR)
+ || (write_setting->data_type != MSM_CAMERA_I2C_BYTE_DATA
+ && write_setting->data_type != MSM_CAMERA_I2C_WORD_DATA))
+ return rc;
+
+ reg_setting = write_setting->reg_setting;
+ client_addr_type = client->addr_type;
+ client->addr_type = write_setting->addr_type;
+
+ for (i = 0; i < write_setting->size; i++) {
+ CDBG("%s addr %x data %x\n", __func__,
+ reg_setting->reg_addr, reg_setting->reg_data);
+
+ rc = msm_camera_qup_i2c_write(client, reg_setting->reg_addr,
+ reg_setting->reg_data, write_setting->data_type);
+ if (rc < 0)
+ break;
+ reg_setting++;
+ }
+ if (write_setting->delay > 20)
+ msleep(write_setting->delay);
+ else if (write_setting->delay)
+ usleep_range(write_setting->delay * 1000, (write_setting->delay
+ * 1000) + 1000);
+
+ client->addr_type = client_addr_type;
+ return rc;
+}
+
+int32_t msm_camera_qup_i2c_write_seq_table(struct msm_camera_i2c_client *client,
+ struct msm_camera_i2c_seq_reg_setting *write_setting)
+{
+ int i;
+ int32_t rc = -EFAULT;
+ struct msm_camera_i2c_seq_reg_array *reg_setting;
+ uint16_t client_addr_type;
+
+ if (!client || !write_setting)
+ return rc;
+
+ if ((write_setting->addr_type != MSM_CAMERA_I2C_BYTE_ADDR
+ && write_setting->addr_type != MSM_CAMERA_I2C_WORD_ADDR)) {
+ pr_err("%s Invalide addr type %d\n", __func__,
+ write_setting->addr_type);
+ return rc;
+ }
+
+ reg_setting = write_setting->reg_setting;
+ client_addr_type = client->addr_type;
+ client->addr_type = write_setting->addr_type;
+
+ for (i = 0; i < write_setting->size; i++) {
+ rc = msm_camera_qup_i2c_write_seq(client, reg_setting->reg_addr,
+ reg_setting->reg_data, reg_setting->reg_data_size);
+ if (rc < 0)
+ break;
+ reg_setting++;
+ }
+ if (write_setting->delay > 20)
+ msleep(write_setting->delay);
+ else if (write_setting->delay)
+ usleep_range(write_setting->delay * 1000, (write_setting->delay
+ * 1000) + 1000);
+
+ client->addr_type = client_addr_type;
+ return rc;
+}
+
+int32_t msm_camera_qup_i2c_write_table_w_microdelay(
+ struct msm_camera_i2c_client *client,
+ struct msm_camera_i2c_reg_tbl *reg_tbl, uint16_t size,
+ enum msm_camera_i2c_data_type data_type)
+{
+ int i;
+ int32_t rc = -EFAULT;
+
+ if (!client || !reg_tbl)
+ return rc;
+
+ if ((client->addr_type != MSM_CAMERA_I2C_BYTE_ADDR
+ && client->addr_type != MSM_CAMERA_I2C_WORD_ADDR)
+ || (data_type != MSM_CAMERA_I2C_BYTE_DATA
+ && data_type != MSM_CAMERA_I2C_WORD_DATA))
+ return rc;
+
+ for (i = 0; i < size; i++) {
+ rc = msm_camera_qup_i2c_write(client, reg_tbl->reg_addr,
+ reg_tbl->reg_data, data_type);
+ if (rc < 0)
+ break;
+ if (reg_tbl->delay)
+ usleep_range(reg_tbl->delay, reg_tbl->delay + 1000);
+ reg_tbl++;
+ }
+ return rc;
+}
diff --git a/drivers/media/video/msmb/sensor/msm_sensor.c b/drivers/media/video/msmb/sensor/msm_sensor.c
new file mode 100644
index 0000000..78c3207
--- /dev/null
+++ b/drivers/media/video/msmb/sensor/msm_sensor.c
@@ -0,0 +1,1542 @@
+/* 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
+ * 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 <mach/gpiomux.h>
+#include "msm_sensor.h"
+#include "msm_sd.h"
+#include "camera.h"
+#include "msm_cci.h"
+#include "msm_camera_io_util.h"
+#include "msm_camera_i2c_mux.h"
+
+#undef CDBG
+#ifdef CONFIG_MSMB_CAMERA_DEBUG
+#define CDBG(fmt, args...) pr_err(fmt, ##args)
+#else
+#define CDBG(fmt, args...) do { } while (0)
+#endif
+
+static int32_t msm_sensor_enable_i2c_mux(struct msm_camera_i2c_conf *i2c_conf)
+{
+ struct v4l2_subdev *i2c_mux_sd =
+ dev_get_drvdata(&i2c_conf->mux_dev->dev);
+ v4l2_subdev_call(i2c_mux_sd, core, ioctl,
+ VIDIOC_MSM_I2C_MUX_INIT, NULL);
+ v4l2_subdev_call(i2c_mux_sd, core, ioctl,
+ VIDIOC_MSM_I2C_MUX_CFG, (void *)&i2c_conf->i2c_mux_mode);
+ return 0;
+}
+
+static int32_t msm_sensor_disable_i2c_mux(struct msm_camera_i2c_conf *i2c_conf)
+{
+ struct v4l2_subdev *i2c_mux_sd =
+ dev_get_drvdata(&i2c_conf->mux_dev->dev);
+ v4l2_subdev_call(i2c_mux_sd, core, ioctl,
+ VIDIOC_MSM_I2C_MUX_RELEASE, NULL);
+ return 0;
+}
+
+static int32_t msm_sensor_get_sub_module_index(struct device_node *of_node,
+ struct msm_camera_sensor_board_info *sensordata)
+{
+ int32_t rc = 0, i = 0;
+ uint32_t val = 0, count = 0;
+ uint32_t *val_array = NULL;
+
+ sensordata->sensor_info = kzalloc(sizeof(struct msm_sensor_info_t),
+ GFP_KERNEL);
+ if (!sensordata->sensor_info) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ return -ENOMEM;
+ }
+ for (i = 0; i < SUB_MODULE_MAX; i++)
+ sensordata->sensor_info->subdev_id[i] = -1;
+
+ if (of_property_read_bool(of_node, "qcom,actuator-sd-index") ==
+ true) {
+ rc = of_property_read_u32(of_node, "qcom,actuator-sd-index",
+ &val);
+ CDBG("%s qcom,actuator-sd-index %d, rc %d\n", __func__, val,
+ rc);
+ if (rc < 0) {
+ pr_err("%s:%d failed rc %d\n", __func__, __LINE__, rc);
+ return rc;
+ }
+ sensordata->sensor_info->subdev_id[SUB_MODULE_ACTUATOR] = val;
+ }
+
+ if (of_property_read_bool(of_node, "qcom,eeprom-sd-index") ==
+ true) {
+ rc = of_property_read_u32(of_node, "qcom,eeprom-sd-index",
+ &val);
+ CDBG("%s qcom,eeprom-sd-index %d, rc %d\n", __func__, val, rc);
+ if (rc < 0) {
+ pr_err("%s:%d failed rc %d\n", __func__, __LINE__, rc);
+ return rc;
+ }
+ sensordata->sensor_info->subdev_id[SUB_MODULE_EEPROM] = val;
+ }
+
+ if (of_property_read_bool(of_node, "qcom,led-flash-sd-index") ==
+ true) {
+ rc = of_property_read_u32(of_node, "qcom,led-flash-sd-index",
+ &val);
+ CDBG("%s qcom,led-flash-sd-index %d, rc %d\n", __func__, val,
+ rc);
+ if (rc < 0) {
+ pr_err("%s:%d failed rc %d\n", __func__, __LINE__, rc);
+ return rc;
+ }
+ sensordata->sensor_info->subdev_id[SUB_MODULE_LED_FLASH] = val;
+ }
+
+ if (of_property_read_bool(of_node, "qcom,strobe-flash-sd-index") ==
+ true) {
+ rc = of_property_read_u32(of_node, "qcom,strobe-flash-sd-index",
+ &val);
+ CDBG("%s qcom,strobe-flash-sd-index %d, rc %d\n", __func__,
+ val, rc);
+ if (rc < 0) {
+ pr_err("%s:%d failed rc %d\n", __func__, __LINE__, rc);
+ return rc;
+ }
+ sensordata->sensor_info->subdev_id[SUB_MODULE_STROBE_FLASH] =
+ val;
+ }
+
+ if (of_get_property(of_node, "qcom,csiphy-sd-index", &count)) {
+ count /= sizeof(uint32_t);
+ if (count > 2) {
+ pr_err("%s qcom,csiphy-sd-index count %d > 2\n",
+ __func__, count);
+ return -EINVAL;
+ }
+ val_array = kzalloc(sizeof(uint32_t) * count, GFP_KERNEL);
+ if (!val_array) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ return -ENOMEM;
+ }
+
+ rc = of_property_read_u32_array(of_node, "qcom,csiphy-sd-index",
+ val_array, count);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ kfree(val_array);
+ return rc;
+ }
+ for (i = 0; i < count; i++) {
+ sensordata->sensor_info->subdev_id
+ [SUB_MODULE_CSIPHY + i] = val_array[i];
+ CDBG("%s csiphy_core[%d] = %d\n",
+ __func__, i, val_array[i]);
+ }
+ kfree(val_array);
+ } else {
+ pr_err("%s:%d qcom,csiphy-sd-index not present\n", __func__,
+ __LINE__);
+ return -EINVAL;
+ }
+
+ if (of_get_property(of_node, "qcom,csid-sd-index", &count)) {
+ count /= sizeof(uint32_t);
+ if (count > 2) {
+ pr_err("%s qcom,csid-sd-index count %d > 2\n",
+ __func__, count);
+ return -EINVAL;
+ }
+ val_array = kzalloc(sizeof(uint32_t) * count, GFP_KERNEL);
+ if (!val_array) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ return -ENOMEM;
+ }
+
+ rc = of_property_read_u32_array(of_node, "qcom,csid-sd-index",
+ val_array, count);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ kfree(val_array);
+ return rc;
+ }
+ for (i = 0; i < count; i++) {
+ sensordata->sensor_info->subdev_id
+ [SUB_MODULE_CSID + i] = val_array[i];
+ CDBG("%s csid_core[%d] = %d\n",
+ __func__, i, val_array[i]);
+ }
+ kfree(val_array);
+ } else {
+ pr_err("%s:%d qcom,csid-sd-index not present\n", __func__,
+ __LINE__);
+ return -EINVAL;
+ }
+ return rc;
+}
+
+static int32_t msm_sensor_get_dt_csi_data(struct device_node *of_node,
+ struct msm_camera_sensor_board_info *sensordata)
+{
+ int32_t rc = 0;
+ uint32_t val = 0;
+
+ sensordata->csi_lane_params = kzalloc(
+ sizeof(struct msm_camera_csi_lane_params), GFP_KERNEL);
+ if (!sensordata->csi_lane_params) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ rc = -ENOMEM;
+ goto ERROR1;
+ }
+
+ rc = of_property_read_u32(of_node, "qcom,csi-lane-assign", &val);
+ CDBG("%s qcom,csi-lane-assign %x, rc %d\n", __func__, val, rc);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto ERROR2;
+ }
+ sensordata->csi_lane_params->csi_lane_assign = val;
+
+ rc = of_property_read_u32(of_node, "qcom,csi-lane-mask", &val);
+ CDBG("%s qcom,csi-lane-mask %x, rc %d\n", __func__, val, rc);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto ERROR2;
+ }
+ sensordata->csi_lane_params->csi_lane_mask = val;
+
+ return rc;
+ERROR2:
+ kfree(sensordata->csi_lane_params);
+ERROR1:
+ return rc;
+}
+
+static int32_t msm_sensor_get_dt_vreg_data(struct device_node *of_node,
+ struct msm_camera_sensor_board_info *sensordata)
+{
+ int32_t rc = 0, i = 0;
+ uint32_t count = 0;
+ uint32_t *vreg_array = NULL;
+
+ count = of_property_count_strings(of_node, "qcom,cam-vreg-name");
+ CDBG("%s qcom,cam-vreg-name count %d\n", __func__, count);
+
+ if (!count)
+ return 0;
+
+ sensordata->cam_vreg = kzalloc(sizeof(struct camera_vreg_t) * count,
+ GFP_KERNEL);
+ if (!sensordata->cam_vreg) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ return -ENOMEM;
+ }
+
+ sensordata->num_vreg = count;
+ for (i = 0; i < count; i++) {
+ rc = of_property_read_string_index(of_node,
+ "qcom,cam-vreg-name", i,
+ &sensordata->cam_vreg[i].reg_name);
+ CDBG("%s reg_name[%d] = %s\n", __func__, i,
+ sensordata->cam_vreg[i].reg_name);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto ERROR1;
+ }
+ }
+
+ vreg_array = kzalloc(sizeof(uint32_t) * count, GFP_KERNEL);
+ if (!vreg_array) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ rc = -ENOMEM;
+ goto ERROR1;
+ }
+
+ rc = of_property_read_u32_array(of_node, "qcom,cam-vreg-type",
+ vreg_array, count);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto ERROR2;
+ }
+ for (i = 0; i < count; i++) {
+ sensordata->cam_vreg[i].type = vreg_array[i];
+ CDBG("%s cam_vreg[%d].type = %d\n", __func__, i,
+ sensordata->cam_vreg[i].type);
+ }
+
+ rc = of_property_read_u32_array(of_node, "qcom,cam-vreg-min-voltage",
+ vreg_array, count);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto ERROR2;
+ }
+ for (i = 0; i < count; i++) {
+ sensordata->cam_vreg[i].min_voltage = vreg_array[i];
+ CDBG("%s cam_vreg[%d].min_voltage = %d\n", __func__,
+ i, sensordata->cam_vreg[i].min_voltage);
+ }
+
+ rc = of_property_read_u32_array(of_node, "qcom,cam-vreg-max-voltage",
+ vreg_array, count);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto ERROR2;
+ }
+ for (i = 0; i < count; i++) {
+ sensordata->cam_vreg[i].max_voltage = vreg_array[i];
+ CDBG("%s cam_vreg[%d].max_voltage = %d\n", __func__,
+ i, sensordata->cam_vreg[i].max_voltage);
+ }
+
+ rc = of_property_read_u32_array(of_node, "qcom,cam-vreg-op-mode",
+ vreg_array, count);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto ERROR2;
+ }
+ for (i = 0; i < count; i++) {
+ sensordata->cam_vreg[i].op_mode = vreg_array[i];
+ CDBG("%s cam_vreg[%d].op_mode = %d\n", __func__, i,
+ sensordata->cam_vreg[i].op_mode);
+ }
+
+ kfree(vreg_array);
+ return rc;
+ERROR2:
+ kfree(vreg_array);
+ERROR1:
+ kfree(sensordata->cam_vreg);
+ sensordata->num_vreg = 0;
+ return rc;
+}
+
+static int32_t msm_sensor_get_dt_gpio_req_tbl(struct device_node *of_node,
+ struct msm_camera_gpio_conf *gconf, uint16_t *gpio_array,
+ uint16_t gpio_array_size)
+{
+ int32_t rc = 0, i = 0;
+ uint32_t count = 0;
+ uint32_t *val_array = NULL;
+
+ if (!of_get_property(of_node, "qcom,gpio-req-tbl-num", &count))
+ return 0;
+
+ count /= sizeof(uint32_t);
+ if (!count) {
+ pr_err("%s qcom,gpio-req-tbl-num 0\n", __func__);
+ return 0;
+ }
+
+ val_array = kzalloc(sizeof(uint32_t) * count, GFP_KERNEL);
+ if (!val_array) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ return -ENOMEM;
+ }
+
+ gconf->cam_gpio_req_tbl = kzalloc(sizeof(struct gpio) * count,
+ GFP_KERNEL);
+ if (!gconf->cam_gpio_req_tbl) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ rc = -ENOMEM;
+ goto ERROR1;
+ }
+ gconf->cam_gpio_req_tbl_size = count;
+
+ rc = of_property_read_u32_array(of_node, "qcom,gpio-req-tbl-num",
+ val_array, count);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto ERROR2;
+ }
+ for (i = 0; i < count; i++) {
+ if (val_array[i] >= gpio_array_size) {
+ pr_err("%s gpio req tbl index %d invalid\n",
+ __func__, val_array[i]);
+ return -EINVAL;
+ }
+ gconf->cam_gpio_req_tbl[i].gpio = gpio_array[val_array[i]];
+ CDBG("%s cam_gpio_req_tbl[%d].gpio = %d\n", __func__, i,
+ gconf->cam_gpio_req_tbl[i].gpio);
+ }
+
+ rc = of_property_read_u32_array(of_node, "qcom,gpio-req-tbl-flags",
+ val_array, count);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto ERROR2;
+ }
+ for (i = 0; i < count; i++) {
+ gconf->cam_gpio_req_tbl[i].flags = val_array[i];
+ CDBG("%s cam_gpio_req_tbl[%d].flags = %ld\n", __func__, i,
+ gconf->cam_gpio_req_tbl[i].flags);
+ }
+
+ for (i = 0; i < count; i++) {
+ rc = of_property_read_string_index(of_node,
+ "qcom,gpio-req-tbl-label", i,
+ &gconf->cam_gpio_req_tbl[i].label);
+ CDBG("%s cam_gpio_req_tbl[%d].label = %s\n", __func__, i,
+ gconf->cam_gpio_req_tbl[i].label);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto ERROR2;
+ }
+ }
+
+ kfree(val_array);
+ return rc;
+
+ERROR2:
+ kfree(gconf->cam_gpio_req_tbl);
+ERROR1:
+ kfree(val_array);
+ gconf->cam_gpio_req_tbl_size = 0;
+ return rc;
+}
+
+static int32_t msm_sensor_get_dt_gpio_set_tbl(struct device_node *of_node,
+ struct msm_camera_gpio_conf *gconf, uint16_t *gpio_array,
+ uint16_t gpio_array_size)
+{
+ int32_t rc = 0, i = 0;
+ uint32_t count = 0;
+ uint32_t *val_array = NULL;
+
+ if (!of_get_property(of_node, "qcom,gpio-set-tbl-num", &count))
+ return 0;
+
+ count /= sizeof(uint32_t);
+ if (!count) {
+ pr_err("%s qcom,gpio-set-tbl-num 0\n", __func__);
+ return 0;
+ }
+
+ val_array = kzalloc(sizeof(uint32_t) * count, GFP_KERNEL);
+ if (!val_array) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ return -ENOMEM;
+ }
+
+ gconf->cam_gpio_set_tbl = kzalloc(sizeof(struct msm_gpio_set_tbl) *
+ count, GFP_KERNEL);
+ if (!gconf->cam_gpio_set_tbl) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ rc = -ENOMEM;
+ goto ERROR1;
+ }
+ gconf->cam_gpio_set_tbl_size = count;
+
+ rc = of_property_read_u32_array(of_node, "qcom,gpio-set-tbl-num",
+ val_array, count);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto ERROR2;
+ }
+ for (i = 0; i < count; i++) {
+ if (val_array[i] >= gpio_array_size) {
+ pr_err("%s gpio set tbl index %d invalid\n",
+ __func__, val_array[i]);
+ return -EINVAL;
+ }
+ gconf->cam_gpio_set_tbl[i].gpio = gpio_array[val_array[i]];
+ CDBG("%s cam_gpio_set_tbl[%d].gpio = %d\n", __func__, i,
+ gconf->cam_gpio_set_tbl[i].gpio);
+ }
+
+ rc = of_property_read_u32_array(of_node, "qcom,gpio-set-tbl-flags",
+ val_array, count);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto ERROR2;
+ }
+ for (i = 0; i < count; i++) {
+ gconf->cam_gpio_set_tbl[i].flags = val_array[i];
+ CDBG("%s cam_gpio_set_tbl[%d].flags = %ld\n", __func__, i,
+ gconf->cam_gpio_set_tbl[i].flags);
+ }
+
+ rc = of_property_read_u32_array(of_node, "qcom,gpio-set-tbl-delay",
+ val_array, count);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto ERROR2;
+ }
+ for (i = 0; i < count; i++) {
+ gconf->cam_gpio_set_tbl[i].delay = val_array[i];
+ CDBG("%s cam_gpio_set_tbl[%d].delay = %d\n", __func__, i,
+ gconf->cam_gpio_set_tbl[i].delay);
+ }
+
+ kfree(val_array);
+ return rc;
+
+ERROR2:
+ kfree(gconf->cam_gpio_set_tbl);
+ERROR1:
+ kfree(val_array);
+ gconf->cam_gpio_set_tbl_size = 0;
+ return rc;
+}
+
+static int32_t msm_sensor_init_gpio_pin_tbl(struct device_node *of_node,
+ struct msm_camera_gpio_conf *gconf, uint16_t *gpio_array,
+ uint16_t gpio_array_size)
+{
+ int32_t rc = 0;
+ int32_t val = 0;
+
+ gconf->gpio_num_info = kzalloc(sizeof(struct msm_camera_gpio_num_info),
+ GFP_KERNEL);
+ if (!gconf->gpio_num_info) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ rc = -ENOMEM;
+ return rc;
+ }
+
+ if (of_property_read_bool(of_node, "qcom,gpio-reset") == true) {
+ rc = of_property_read_u32(of_node, "qcom,gpio-reset", &val);
+ if (rc < 0) {
+ pr_err("%s:%d read qcom,gpio-reset failed rc %d\n",
+ __func__, __LINE__, rc);
+ goto ERROR;
+ } else if (val >= gpio_array_size) {
+ pr_err("%s:%d qcom,gpio-reset invalid %d\n",
+ __func__, __LINE__, val);
+ goto ERROR;
+ }
+ gconf->gpio_num_info->gpio_num[SENSOR_GPIO_RESET] =
+ gpio_array[val];
+ CDBG("%s qcom,gpio-reset %d\n", __func__,
+ gconf->gpio_num_info->gpio_num[SENSOR_GPIO_RESET]);
+ }
+
+ if (of_property_read_bool(of_node, "qcom,gpio-standby") == true) {
+ rc = of_property_read_u32(of_node, "qcom,gpio-standby", &val);
+ if (rc < 0) {
+ pr_err("%s:%d read qcom,gpio-standby failed rc %d\n",
+ __func__, __LINE__, rc);
+ goto ERROR;
+ } else if (val >= gpio_array_size) {
+ pr_err("%s:%d qcom,gpio-standby invalid %d\n",
+ __func__, __LINE__, val);
+ goto ERROR;
+ }
+ gconf->gpio_num_info->gpio_num[SENSOR_GPIO_STANDBY] =
+ gpio_array[val];
+ CDBG("%s qcom,gpio-reset %d\n", __func__,
+ gconf->gpio_num_info->gpio_num[SENSOR_GPIO_STANDBY]);
+ }
+ return rc;
+
+ERROR:
+ kfree(gconf->gpio_num_info);
+ gconf->gpio_num_info = NULL;
+ return rc;
+}
+
+static int32_t msm_sensor_get_dt_actuator_data(struct device_node *of_node,
+ struct msm_camera_sensor_board_info *sensordata)
+{
+ int32_t rc = 0;
+ uint32_t val = 0;
+
+ rc = of_property_read_u32(of_node, "qcom,actuator-cam-name", &val);
+ CDBG("%s qcom,actuator-cam-name %d, rc %d\n", __func__, val, rc);
+ if (rc < 0)
+ return 0;
+
+ sensordata->actuator_info = kzalloc(sizeof(struct msm_actuator_info),
+ GFP_KERNEL);
+ if (!sensordata->actuator_info) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ rc = -ENOMEM;
+ goto ERROR;
+ }
+
+ sensordata->actuator_info->cam_name = val;
+
+ rc = of_property_read_u32(of_node, "qcom,actuator-vcm-pwd", &val);
+ CDBG("%s qcom,actuator-vcm-pwd %d, rc %d\n", __func__, val, rc);
+ if (!rc)
+ sensordata->actuator_info->vcm_pwd = val;
+
+ rc = of_property_read_u32(of_node, "qcom,actuator-vcm-enable", &val);
+ CDBG("%s qcom,actuator-vcm-enable %d, rc %d\n", __func__, val, rc);
+ if (!rc)
+ sensordata->actuator_info->vcm_enable = val;
+
+ return 0;
+ERROR:
+ return rc;
+}
+
+static int32_t msm_sensor_get_dt_data(struct platform_device *pdev,
+ struct msm_sensor_ctrl_t *s_ctrl)
+{
+ int32_t rc = 0, i = 0;
+ struct device_node *of_node = pdev->dev.of_node;
+ struct msm_camera_gpio_conf *gconf = NULL;
+ struct msm_camera_sensor_board_info *sensordata = NULL;
+ uint16_t *gpio_array = NULL;
+ uint16_t gpio_array_size = 0;
+ uint32_t id_info[3];
+
+ s_ctrl->sensordata = kzalloc(sizeof(
+ struct msm_camera_sensor_board_info),
+ GFP_KERNEL);
+ if (!s_ctrl->sensordata) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ return -ENOMEM;
+ }
+
+ sensordata = s_ctrl->sensordata;
+
+ sensordata->sensor_init_params = kzalloc(sizeof(
+ struct msm_sensor_init_params), GFP_KERNEL);
+ if (!sensordata->sensor_init_params) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ return -ENOMEM;
+ }
+
+ rc = of_property_read_string(of_node, "qcom,sensor-name",
+ &sensordata->sensor_name);
+ CDBG("%s qcom,sensor-name %s, rc %d\n", __func__,
+ sensordata->sensor_name, rc);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto ERROR1;
+ }
+
+ rc = of_property_read_u32(of_node, "qcom,sensor-mode",
+ &sensordata->sensor_init_params->modes_supported);
+ CDBG("%s qcom,sensor-mode %d, rc %d\n", __func__,
+ sensordata->sensor_init_params->modes_supported, rc);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto ERROR1;
+ }
+
+ rc = of_property_read_u32(of_node, "qcom,sensor-position",
+ &sensordata->sensor_init_params->position);
+ CDBG("%s qcom,sensor-position %d, rc %d\n", __func__,
+ sensordata->sensor_init_params->position, rc);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto ERROR1;
+ }
+
+ rc = of_property_read_u32(of_node, "qcom,mount-angle",
+ &sensordata->sensor_init_params->sensor_mount_angle);
+ CDBG("%s qcom,mount-angle %d, rc %d\n", __func__,
+ sensordata->sensor_init_params->sensor_mount_angle, rc);
+ if (rc < 0) {
+ /* Set default mount angle */
+ sensordata->sensor_init_params->sensor_mount_angle = 0;
+ rc = 0;
+ }
+
+ rc = msm_sensor_get_sub_module_index(of_node, sensordata);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto ERROR1;
+ }
+
+ rc = msm_sensor_get_dt_csi_data(of_node, sensordata);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto ERROR1;
+ }
+
+ rc = msm_sensor_get_dt_vreg_data(of_node, sensordata);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto ERROR2;
+ }
+
+ sensordata->gpio_conf = kzalloc(sizeof(struct msm_camera_gpio_conf),
+ GFP_KERNEL);
+ if (!sensordata->gpio_conf) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ rc = -ENOMEM;
+ goto ERROR3;
+ }
+ gconf = sensordata->gpio_conf;
+
+ gpio_array_size = of_gpio_count(of_node);
+ CDBG("%s gpio count %d\n", __func__, gpio_array_size);
+
+ if (gpio_array_size) {
+ gpio_array = kzalloc(sizeof(uint16_t) * gpio_array_size,
+ GFP_KERNEL);
+ if (!gpio_array) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto ERROR4;
+ }
+ for (i = 0; i < gpio_array_size; i++) {
+ gpio_array[i] = of_get_gpio(of_node, i);
+ CDBG("%s gpio_array[%d] = %d\n", __func__, i,
+ gpio_array[i]);
+ }
+
+ rc = msm_sensor_get_dt_gpio_req_tbl(of_node, gconf,
+ gpio_array, gpio_array_size);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto ERROR4;
+ }
+
+ rc = msm_sensor_get_dt_gpio_set_tbl(of_node, gconf,
+ gpio_array, gpio_array_size);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto ERROR5;
+ }
+
+ rc = msm_sensor_init_gpio_pin_tbl(of_node, gconf,
+ gpio_array, gpio_array_size);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto ERROR6;
+ }
+ }
+ rc = msm_sensor_get_dt_actuator_data(of_node, sensordata);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto ERROR7;
+ }
+
+ sensordata->slave_info = kzalloc(sizeof(struct msm_camera_slave_info),
+ GFP_KERNEL);
+ if (!sensordata->slave_info) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ rc = -ENOMEM;
+ goto ERROR8;
+ }
+
+ rc = of_property_read_u32_array(of_node, "qcom,slave-id",
+ id_info, 3);
+ if (rc < 0) {
+ pr_err("%s failed %d\n", __func__, __LINE__);
+ goto ERROR9;
+ }
+
+ sensordata->slave_info->sensor_slave_addr = id_info[0];
+ sensordata->slave_info->sensor_id_reg_addr = id_info[1];
+ sensordata->slave_info->sensor_id = id_info[2];
+
+ kfree(gpio_array);
+ return rc;
+
+ERROR9:
+ kfree(s_ctrl->sensordata->slave_info);
+ERROR8:
+ kfree(s_ctrl->sensordata->actuator_info);
+ERROR7:
+ kfree(s_ctrl->sensordata->gpio_conf->gpio_num_info);
+ERROR6:
+ kfree(s_ctrl->sensordata->gpio_conf->cam_gpio_set_tbl);
+ERROR5:
+ kfree(s_ctrl->sensordata->gpio_conf->cam_gpio_req_tbl);
+ERROR4:
+ kfree(s_ctrl->sensordata->gpio_conf);
+ERROR3:
+ kfree(s_ctrl->sensordata->cam_vreg);
+ERROR2:
+ kfree(s_ctrl->sensordata->csi_lane_params);
+ERROR1:
+ kfree(s_ctrl->sensordata);
+ kfree(gpio_array);
+ return rc;
+}
+
+int32_t msm_sensor_free_sensor_data(struct msm_sensor_ctrl_t *s_ctrl)
+{
+ if (!s_ctrl->pdev)
+ return 0;
+ kfree(s_ctrl->sensordata->slave_info);
+ kfree(s_ctrl->sensordata->actuator_info);
+ kfree(s_ctrl->sensordata->gpio_conf->gpio_num_info);
+ kfree(s_ctrl->sensordata->gpio_conf->cam_gpio_set_tbl);
+ kfree(s_ctrl->sensordata->gpio_conf->cam_gpio_req_tbl);
+ kfree(s_ctrl->sensordata->gpio_conf);
+ kfree(s_ctrl->sensordata->cam_vreg);
+ kfree(s_ctrl->sensordata->csi_lane_params);
+ kfree(s_ctrl->sensordata->sensor_info);
+ kfree(s_ctrl->sensordata->sensor_init_params);
+ kfree(s_ctrl->sensordata);
+ return 0;
+}
+
+static struct msm_cam_clk_info cam_8960_clk_info[] = {
+ [SENSOR_CAM_MCLK] = {"cam_clk", 24000000},
+};
+
+static struct msm_cam_clk_info cam_8974_clk_info[] = {
+ [SENSOR_CAM_MCLK] = {"cam_src_clk", 19200000},
+ [SENSOR_CAM_CLK] = {"cam_clk", 0},
+};
+
+int32_t msm_sensor_power_up(struct msm_sensor_ctrl_t *s_ctrl)
+{
+ int32_t rc = 0, index = 0;
+ struct msm_sensor_power_setting_array *power_setting_array = NULL;
+ struct msm_sensor_power_setting *power_setting = NULL;
+ struct msm_camera_sensor_board_info *data = s_ctrl->sensordata;
+
+ CDBG("%s:%d\n", __func__, __LINE__);
+ power_setting_array = &s_ctrl->power_setting_array;
+
+ if (data->gpio_conf->cam_gpiomux_conf_tbl != NULL) {
+ pr_err("%s:%d mux install\n", __func__, __LINE__);
+ msm_gpiomux_install(
+ (struct msm_gpiomux_config *)
+ data->gpio_conf->cam_gpiomux_conf_tbl,
+ data->gpio_conf->cam_gpiomux_conf_tbl_size);
+ }
+
+ rc = msm_camera_request_gpio_table(
+ data->gpio_conf->cam_gpio_req_tbl,
+ data->gpio_conf->cam_gpio_req_tbl_size, 1);
+ if (rc < 0) {
+ pr_err("%s: request gpio failed\n", __func__);
+ return rc;
+ }
+ for (index = 0; index < power_setting_array->size; index++) {
+ CDBG("%s index %d\n", __func__, index);
+ power_setting = &power_setting_array->power_setting[index];
+ CDBG("%s type %d\n", __func__, power_setting->seq_type);
+ switch (power_setting->seq_type) {
+ case SENSOR_CLK:
+ if (power_setting->seq_val >= s_ctrl->clk_info_size) {
+ pr_err("%s clk index %d >= max %d\n", __func__,
+ power_setting->seq_val,
+ s_ctrl->clk_info_size);
+ goto power_up_failed;
+ }
+ if (power_setting->config_val)
+ s_ctrl->clk_info[power_setting->seq_val].
+ clk_rate = power_setting->config_val;
+
+ rc = msm_cam_clk_enable(s_ctrl->dev,
+ &s_ctrl->clk_info[0],
+ (struct clk **)&power_setting->data[0],
+ s_ctrl->clk_info_size,
+ 1);
+ if (rc < 0) {
+ pr_err("%s: clk enable failed\n",
+ __func__);
+ goto power_up_failed;
+ }
+ break;
+ case SENSOR_GPIO:
+ if (power_setting->seq_val >= SENSOR_GPIO_MAX ||
+ !data->gpio_conf->gpio_num_info) {
+ pr_err("%s gpio index %d >= max %d\n", __func__,
+ power_setting->seq_val,
+ SENSOR_GPIO_MAX);
+ goto power_up_failed;
+ }
+ pr_debug("%s:%d gpio set val %d\n", __func__, __LINE__,
+ data->gpio_conf->gpio_num_info->gpio_num
+ [power_setting->seq_val]);
+ gpio_set_value_cansleep(
+ data->gpio_conf->gpio_num_info->gpio_num
+ [power_setting->seq_val],
+ power_setting->config_val);
+ break;
+ case SENSOR_VREG:
+ if (power_setting->seq_val >= CAM_VREG_MAX) {
+ pr_err("%s vreg index %d >= max %d\n", __func__,
+ power_setting->seq_val,
+ SENSOR_GPIO_MAX);
+ goto power_up_failed;
+ }
+ msm_camera_config_single_vreg(s_ctrl->dev,
+ &data->cam_vreg[power_setting->seq_val],
+ (struct regulator **)&power_setting->data[0],
+ 1);
+ break;
+ case SENSOR_I2C_MUX:
+ if (data->i2c_conf && data->i2c_conf->use_i2c_mux)
+ msm_sensor_enable_i2c_mux(data->i2c_conf);
+ break;
+ default:
+ pr_err("%s error power seq type %d\n", __func__,
+ power_setting->seq_type);
+ break;
+ }
+ if (power_setting->delay > 20) {
+ msleep(power_setting->delay);
+ } else if (power_setting->delay) {
+ usleep_range(power_setting->delay * 1000,
+ (power_setting->delay * 1000) + 1000);
+ }
+ }
+
+ if (s_ctrl->sensor_device_type == MSM_SENSOR_PLATFORM_DEVICE) {
+ rc = s_ctrl->sensor_i2c_client->i2c_func_tbl->i2c_util(
+ s_ctrl->sensor_i2c_client, MSM_CCI_INIT);
+ if (rc < 0) {
+ pr_err("%s cci_init failed\n", __func__);
+ goto power_up_failed;
+ }
+ }
+
+ if (s_ctrl->func_tbl->sensor_match_id)
+ rc = s_ctrl->func_tbl->sensor_match_id(s_ctrl);
+ else
+ rc = msm_sensor_match_id(s_ctrl);
+ if (rc < 0) {
+ pr_err("%s:%d match id failed rc %d\n", __func__, __LINE__, rc);
+ goto power_up_failed;
+ }
+
+ CDBG("%s exit\n", __func__);
+ return 0;
+power_up_failed:
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ if (s_ctrl->sensor_device_type == MSM_SENSOR_PLATFORM_DEVICE) {
+ s_ctrl->sensor_i2c_client->i2c_func_tbl->i2c_util(
+ s_ctrl->sensor_i2c_client, MSM_CCI_RELEASE);
+ }
+
+ for (index--; index >= 0; index--) {
+ CDBG("%s index %d\n", __func__, index);
+ power_setting = &power_setting_array->power_setting[index];
+ CDBG("%s type %d\n", __func__, power_setting->seq_type);
+ switch (power_setting->seq_type) {
+ case SENSOR_CLK:
+ msm_cam_clk_enable(s_ctrl->dev,
+ &s_ctrl->clk_info[0],
+ (struct clk **)&power_setting->data[0],
+ s_ctrl->clk_info_size,
+ 0);
+ break;
+ case SENSOR_GPIO:
+ gpio_set_value_cansleep(
+ data->gpio_conf->gpio_num_info->gpio_num
+ [power_setting->seq_val], GPIOF_OUT_INIT_LOW);
+ break;
+ case SENSOR_VREG:
+ msm_camera_config_single_vreg(s_ctrl->dev,
+ &data->cam_vreg[power_setting->seq_val],
+ (struct regulator **)&power_setting->data[0],
+ 0);
+ break;
+ case SENSOR_I2C_MUX:
+ if (data->i2c_conf && data->i2c_conf->use_i2c_mux)
+ msm_sensor_disable_i2c_mux(data->i2c_conf);
+ break;
+ default:
+ pr_err("%s error power seq type %d\n", __func__,
+ power_setting->seq_type);
+ break;
+ }
+ if (power_setting->delay > 20) {
+ msleep(power_setting->delay);
+ } else if (power_setting->delay) {
+ usleep_range(power_setting->delay * 1000,
+ (power_setting->delay * 1000) + 1000);
+ }
+ }
+ msm_camera_request_gpio_table(
+ data->gpio_conf->cam_gpio_req_tbl,
+ data->gpio_conf->cam_gpio_req_tbl_size, 0);
+ return rc;
+}
+
+int32_t msm_sensor_power_down(struct msm_sensor_ctrl_t *s_ctrl)
+{
+ int32_t index = 0;
+ struct msm_sensor_power_setting_array *power_setting_array = NULL;
+ struct msm_sensor_power_setting *power_setting = NULL;
+ struct msm_camera_sensor_board_info *data = s_ctrl->sensordata;
+
+ CDBG("%s:%d\n", __func__, __LINE__);
+ power_setting_array = &s_ctrl->power_setting_array;
+
+ if (s_ctrl->sensor_device_type == MSM_SENSOR_PLATFORM_DEVICE) {
+ s_ctrl->sensor_i2c_client->i2c_func_tbl->i2c_util(
+ s_ctrl->sensor_i2c_client, MSM_CCI_RELEASE);
+ }
+
+ for (index = (power_setting_array->size - 1); index >= 0; index--) {
+ CDBG("%s index %d\n", __func__, index);
+ power_setting = &power_setting_array->power_setting[index];
+ CDBG("%s type %d\n", __func__, power_setting->seq_type);
+ switch (power_setting->seq_type) {
+ case SENSOR_CLK:
+ msm_cam_clk_enable(s_ctrl->dev,
+ &s_ctrl->clk_info[0],
+ (struct clk **)&power_setting->data[0],
+ s_ctrl->clk_info_size,
+ 0);
+ break;
+ case SENSOR_GPIO:
+ if (power_setting->seq_val >= SENSOR_GPIO_MAX ||
+ !data->gpio_conf->gpio_num_info) {
+ pr_err("%s gpio index %d >= max %d\n", __func__,
+ power_setting->seq_val,
+ SENSOR_GPIO_MAX);
+ continue;
+ }
+ gpio_set_value_cansleep(
+ data->gpio_conf->gpio_num_info->gpio_num
+ [power_setting->seq_val], GPIOF_OUT_INIT_LOW);
+ break;
+ case SENSOR_VREG:
+ if (power_setting->seq_val >= CAM_VREG_MAX) {
+ pr_err("%s vreg index %d >= max %d\n", __func__,
+ power_setting->seq_val,
+ SENSOR_GPIO_MAX);
+ continue;
+ }
+ msm_camera_config_single_vreg(s_ctrl->dev,
+ &data->cam_vreg[power_setting->seq_val],
+ (struct regulator **)&power_setting->data[0],
+ 0);
+ break;
+ case SENSOR_I2C_MUX:
+ if (data->i2c_conf && data->i2c_conf->use_i2c_mux)
+ msm_sensor_disable_i2c_mux(data->i2c_conf);
+ break;
+ default:
+ pr_err("%s error power seq type %d\n", __func__,
+ power_setting->seq_type);
+ break;
+ }
+ if (power_setting->delay > 20) {
+ msleep(power_setting->delay);
+ } else if (power_setting->delay) {
+ usleep_range(power_setting->delay * 1000,
+ (power_setting->delay * 1000) + 1000);
+ }
+ }
+ msm_camera_request_gpio_table(
+ data->gpio_conf->cam_gpio_req_tbl,
+ data->gpio_conf->cam_gpio_req_tbl_size, 0);
+ CDBG("%s exit\n", __func__);
+ return 0;
+}
+
+int32_t msm_sensor_match_id(struct msm_sensor_ctrl_t *s_ctrl)
+{
+ int32_t rc = 0;
+ uint16_t chipid = 0;
+ rc = s_ctrl->sensor_i2c_client->i2c_func_tbl->i2c_read(
+ s_ctrl->sensor_i2c_client,
+ s_ctrl->sensordata->slave_info->sensor_id_reg_addr,
+ &chipid, MSM_CAMERA_I2C_WORD_DATA);
+ if (rc < 0) {
+ pr_err("%s: %s: read id failed\n", __func__,
+ s_ctrl->sensordata->sensor_name);
+ return rc;
+ }
+
+ CDBG("%s: read id: %x expected id %x:\n", __func__, chipid,
+ s_ctrl->sensordata->slave_info->sensor_id);
+ if (chipid != s_ctrl->sensordata->slave_info->sensor_id) {
+ pr_err("msm_sensor_match_id chip id doesnot match\n");
+ return -ENODEV;
+ }
+ return rc;
+}
+
+static struct msm_sensor_ctrl_t *get_sctrl(struct v4l2_subdev *sd)
+{
+ return container_of(container_of(sd, struct msm_sd_subdev, sd),
+ struct msm_sensor_ctrl_t, msm_sd);
+}
+
+static void msm_sensor_stop_stream(struct msm_sensor_ctrl_t *s_ctrl)
+{
+ s_ctrl->sensor_i2c_client->i2c_func_tbl->i2c_write_table(
+ s_ctrl->sensor_i2c_client, &s_ctrl->stop_setting);
+ kfree(s_ctrl->stop_setting.reg_setting);
+ return;
+}
+
+static long msm_sensor_subdev_ioctl(struct v4l2_subdev *sd,
+ unsigned int cmd, void *arg)
+{
+ struct msm_sensor_ctrl_t *s_ctrl = get_sctrl(sd);
+ void __user *argp = (void __user *)arg;
+ if (!s_ctrl) {
+ pr_err("%s s_ctrl NULL\n", __func__);
+ return -EBADF;
+ }
+ switch (cmd) {
+ case VIDIOC_MSM_SENSOR_CFG:
+ return s_ctrl->func_tbl->sensor_config(s_ctrl, argp);
+ case VIDIOC_MSM_SENSOR_RELEASE:
+ msm_sensor_stop_stream(s_ctrl);
+ return 0;
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+int32_t msm_sensor_config(struct msm_sensor_ctrl_t *s_ctrl,
+ void __user *argp)
+{
+ struct sensorb_cfg_data *cdata = (struct sensorb_cfg_data *)argp;
+ long rc = 0;
+ int32_t i = 0;
+ mutex_lock(s_ctrl->msm_sensor_mutex);
+ CDBG("%s:%d %s cfgtype = %d\n", __func__, __LINE__,
+ s_ctrl->sensordata->sensor_name, cdata->cfgtype);
+ switch (cdata->cfgtype) {
+ case CFG_GET_SENSOR_INFO:
+ memcpy(cdata->cfg.sensor_info.sensor_name,
+ s_ctrl->sensordata->sensor_name,
+ sizeof(cdata->cfg.sensor_info.sensor_name));
+ cdata->cfg.sensor_info.session_id =
+ s_ctrl->sensordata->sensor_info->session_id;
+ for (i = 0; i < SUB_MODULE_MAX; i++)
+ cdata->cfg.sensor_info.subdev_id[i] =
+ s_ctrl->sensordata->sensor_info->subdev_id[i];
+ CDBG("%s:%d sensor name %s\n", __func__, __LINE__,
+ cdata->cfg.sensor_info.sensor_name);
+ CDBG("%s:%d session id %d\n", __func__, __LINE__,
+ cdata->cfg.sensor_info.session_id);
+ for (i = 0; i < SUB_MODULE_MAX; i++)
+ CDBG("%s:%d subdev_id[%d] %d\n", __func__, __LINE__, i,
+ cdata->cfg.sensor_info.subdev_id[i]);
+
+ break;
+ case CFG_GET_SENSOR_INIT_PARAMS:
+ cdata->cfg.sensor_init_params =
+ *s_ctrl->sensordata->sensor_init_params;
+ CDBG("%s:%d init params mode %d pos %d mount %d\n", __func__,
+ __LINE__,
+ cdata->cfg.sensor_init_params.modes_supported,
+ cdata->cfg.sensor_init_params.position,
+ cdata->cfg.sensor_init_params.sensor_mount_angle);
+ break;
+ case CFG_SET_SLAVE_INFO: {
+ struct msm_camera_sensor_slave_info sensor_slave_info;
+ struct msm_sensor_power_setting_array *power_setting_array;
+ int slave_index = 0;
+ if (copy_from_user(&sensor_slave_info,
+ (void *)cdata->cfg.setting,
+ sizeof(struct msm_camera_sensor_slave_info))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -EFAULT;
+ break;
+ }
+ /* Update sensor slave address */
+ if (sensor_slave_info.slave_addr) {
+ s_ctrl->sensor_i2c_client->cci_client->sid =
+ sensor_slave_info.slave_addr >> 1;
+ }
+
+ /* Update sensor address type */
+ s_ctrl->sensor_i2c_client->addr_type =
+ sensor_slave_info.addr_type;
+
+ /* Update power up / down sequence */
+ s_ctrl->power_setting_array =
+ sensor_slave_info.power_setting_array;
+ power_setting_array = &s_ctrl->power_setting_array;
+ power_setting_array->power_setting = kzalloc(
+ power_setting_array->size *
+ sizeof(struct msm_sensor_power_setting), GFP_KERNEL);
+ if (!power_setting_array->power_setting) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -ENOMEM;
+ break;
+ }
+ if (copy_from_user(power_setting_array->power_setting,
+ (void *)
+ sensor_slave_info.power_setting_array.power_setting,
+ power_setting_array->size *
+ sizeof(struct msm_sensor_power_setting))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -EFAULT;
+ break;
+ }
+ s_ctrl->free_power_setting = true;
+ CDBG("%s sensor id %x\n", __func__,
+ sensor_slave_info.slave_addr);
+ CDBG("%s sensor addr type %d\n", __func__,
+ sensor_slave_info.addr_type);
+ CDBG("%s sensor reg %x\n", __func__,
+ sensor_slave_info.sensor_id_info.sensor_id_reg_addr);
+ CDBG("%s sensor id %x\n", __func__,
+ sensor_slave_info.sensor_id_info.sensor_id);
+ for (slave_index = 0; slave_index <
+ power_setting_array->size; slave_index++) {
+ CDBG("%s i %d power setting %d %d %ld %d\n", __func__,
+ slave_index,
+ power_setting_array->power_setting[slave_index].
+ seq_type,
+ power_setting_array->power_setting[slave_index].
+ seq_val,
+ power_setting_array->power_setting[slave_index].
+ config_val,
+ power_setting_array->power_setting[slave_index].
+ delay);
+ }
+ break;
+ }
+ case CFG_WRITE_I2C_ARRAY: {
+ struct msm_camera_i2c_reg_setting conf_array;
+ struct msm_camera_i2c_reg_array *reg_setting = NULL;
+
+ if (copy_from_user(&conf_array,
+ (void *)cdata->cfg.setting,
+ sizeof(struct msm_camera_i2c_reg_setting))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -EFAULT;
+ break;
+ }
+
+ reg_setting = kzalloc(conf_array.size *
+ (sizeof(struct msm_camera_i2c_reg_array)), GFP_KERNEL);
+ if (!reg_setting) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -ENOMEM;
+ break;
+ }
+ if (copy_from_user(reg_setting, (void *)conf_array.reg_setting,
+ conf_array.size *
+ sizeof(struct msm_camera_i2c_reg_array))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ kfree(reg_setting);
+ rc = -EFAULT;
+ break;
+ }
+
+ conf_array.reg_setting = reg_setting;
+ rc = s_ctrl->sensor_i2c_client->i2c_func_tbl->i2c_write_table(
+ s_ctrl->sensor_i2c_client, &conf_array);
+ kfree(reg_setting);
+ break;
+ }
+ case CFG_WRITE_I2C_SEQ_ARRAY: {
+ struct msm_camera_i2c_seq_reg_setting conf_array;
+ struct msm_camera_i2c_seq_reg_array *reg_setting = NULL;
+
+ if (copy_from_user(&conf_array,
+ (void *)cdata->cfg.setting,
+ sizeof(struct msm_camera_i2c_seq_reg_setting))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -EFAULT;
+ break;
+ }
+
+ reg_setting = kzalloc(conf_array.size *
+ (sizeof(struct msm_camera_i2c_seq_reg_array)),
+ GFP_KERNEL);
+ if (!reg_setting) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -ENOMEM;
+ break;
+ }
+ if (copy_from_user(reg_setting, (void *)conf_array.reg_setting,
+ conf_array.size *
+ sizeof(struct msm_camera_i2c_seq_reg_array))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ kfree(reg_setting);
+ rc = -EFAULT;
+ break;
+ }
+
+ conf_array.reg_setting = reg_setting;
+ rc = s_ctrl->sensor_i2c_client->i2c_func_tbl->
+ i2c_write_seq_table(s_ctrl->sensor_i2c_client,
+ &conf_array);
+ kfree(reg_setting);
+ break;
+ }
+
+ case CFG_POWER_UP:
+ if (s_ctrl->func_tbl->sensor_power_up)
+ rc = s_ctrl->func_tbl->sensor_power_up(s_ctrl);
+ else
+ rc = -EFAULT;
+ break;
+
+ case CFG_POWER_DOWN:
+ if (s_ctrl->func_tbl->sensor_power_down)
+ rc = s_ctrl->func_tbl->sensor_power_down(
+ s_ctrl);
+ else
+ rc = -EFAULT;
+ break;
+
+ case CFG_SET_STOP_STREAM_SETTING: {
+ struct msm_camera_i2c_reg_setting *stop_setting =
+ &s_ctrl->stop_setting;
+ struct msm_camera_i2c_reg_array *reg_setting = NULL;
+ if (copy_from_user(stop_setting,
+ (void *)cdata->cfg.setting,
+ sizeof(struct msm_camera_i2c_reg_setting))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -EFAULT;
+ break;
+ }
+
+ reg_setting = stop_setting->reg_setting;
+ stop_setting->reg_setting = kzalloc(stop_setting->size *
+ (sizeof(struct msm_camera_i2c_reg_array)), GFP_KERNEL);
+ if (!stop_setting->reg_setting) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -ENOMEM;
+ break;
+ }
+ if (copy_from_user(stop_setting->reg_setting,
+ (void *)reg_setting,
+ stop_setting->size *
+ sizeof(struct msm_camera_i2c_reg_array))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ kfree(stop_setting->reg_setting);
+ stop_setting->reg_setting = NULL;
+ stop_setting->size = 0;
+ rc = -EFAULT;
+ break;
+ }
+ break;
+ }
+ default:
+ rc = -EFAULT;
+ break;
+ }
+
+ mutex_unlock(s_ctrl->msm_sensor_mutex);
+
+ return rc;
+}
+
+static int32_t msm_sensor_power(struct v4l2_subdev *sd, int on)
+{
+ int rc = 0;
+ struct msm_sensor_ctrl_t *s_ctrl = get_sctrl(sd);
+ mutex_lock(s_ctrl->msm_sensor_mutex);
+ if (!on)
+ rc = s_ctrl->func_tbl->sensor_power_down(s_ctrl);
+ if (s_ctrl->free_power_setting == true) {
+ kfree(s_ctrl->power_setting_array.power_setting);
+ s_ctrl->free_power_setting = false;
+ }
+ mutex_unlock(s_ctrl->msm_sensor_mutex);
+ return rc;
+}
+
+static int32_t msm_sensor_v4l2_enum_fmt(struct v4l2_subdev *sd,
+ unsigned int index, enum v4l2_mbus_pixelcode *code)
+{
+ struct msm_sensor_ctrl_t *s_ctrl = get_sctrl(sd);
+
+ if ((unsigned int)index >= s_ctrl->sensor_v4l2_subdev_info_size)
+ return -EINVAL;
+
+ *code = s_ctrl->sensor_v4l2_subdev_info[index].code;
+ return 0;
+}
+
+static struct v4l2_subdev_core_ops msm_sensor_subdev_core_ops = {
+ .ioctl = msm_sensor_subdev_ioctl,
+ .s_power = msm_sensor_power,
+};
+
+static struct v4l2_subdev_video_ops msm_sensor_subdev_video_ops = {
+ .enum_mbus_fmt = msm_sensor_v4l2_enum_fmt,
+};
+
+static struct v4l2_subdev_ops msm_sensor_subdev_ops = {
+ .core = &msm_sensor_subdev_core_ops,
+ .video = &msm_sensor_subdev_video_ops,
+};
+
+static struct msm_sensor_fn_t msm_sensor_func_tbl = {
+ .sensor_config = msm_sensor_config,
+ .sensor_power_up = msm_sensor_power_up,
+ .sensor_power_down = msm_sensor_power_down,
+ .sensor_match_id = msm_sensor_match_id,
+};
+
+static struct msm_camera_i2c_fn_t msm_sensor_cci_func_tbl = {
+ .i2c_read = msm_camera_cci_i2c_read,
+ .i2c_read_seq = msm_camera_cci_i2c_read_seq,
+ .i2c_write = msm_camera_cci_i2c_write,
+ .i2c_write_table = msm_camera_cci_i2c_write_table,
+ .i2c_write_seq_table = msm_camera_cci_i2c_write_seq_table,
+ .i2c_write_table_w_microdelay =
+ msm_camera_cci_i2c_write_table_w_microdelay,
+ .i2c_util = msm_sensor_cci_i2c_util,
+};
+
+static struct msm_camera_i2c_fn_t msm_sensor_qup_func_tbl = {
+ .i2c_read = msm_camera_qup_i2c_read,
+ .i2c_read_seq = msm_camera_qup_i2c_read_seq,
+ .i2c_write = msm_camera_qup_i2c_write,
+ .i2c_write_table = msm_camera_qup_i2c_write_table,
+ .i2c_write_seq_table = msm_camera_qup_i2c_write_seq_table,
+ .i2c_write_table_w_microdelay =
+ msm_camera_qup_i2c_write_table_w_microdelay,
+};
+
+int32_t msm_sensor_platform_probe(struct platform_device *pdev, void *data)
+{
+ int32_t rc = 0;
+ struct msm_sensor_ctrl_t *s_ctrl =
+ (struct msm_sensor_ctrl_t *)data;
+ struct msm_camera_cci_client *cci_client = NULL;
+ uint32_t session_id;
+ s_ctrl->pdev = pdev;
+ s_ctrl->dev = &pdev->dev;
+ CDBG("%s called data %p\n", __func__, data);
+ CDBG("%s pdev name %s\n", __func__, pdev->id_entry->name);
+ if (pdev->dev.of_node) {
+ rc = msm_sensor_get_dt_data(pdev, s_ctrl);
+ if (rc < 0) {
+ pr_err("%s failed line %d\n", __func__, __LINE__);
+ return rc;
+ }
+ }
+ s_ctrl->sensor_device_type = MSM_SENSOR_PLATFORM_DEVICE;
+ s_ctrl->sensor_i2c_client->cci_client = kzalloc(sizeof(
+ struct msm_camera_cci_client), GFP_KERNEL);
+ if (!s_ctrl->sensor_i2c_client->cci_client) {
+ pr_err("%s failed line %d\n", __func__, __LINE__);
+ return rc;
+ }
+ /* TODO: get CCI subdev */
+ cci_client = s_ctrl->sensor_i2c_client->cci_client;
+ cci_client->cci_subdev = msm_cci_get_subdev();
+ cci_client->cci_i2c_master = MASTER_0;
+ cci_client->sid =
+ s_ctrl->sensordata->slave_info->sensor_slave_addr >> 1;
+ cci_client->retries = 3;
+ cci_client->id_map = 0;
+ if (!s_ctrl->func_tbl)
+ s_ctrl->func_tbl = &msm_sensor_func_tbl;
+ if (!s_ctrl->sensor_i2c_client->i2c_func_tbl)
+ s_ctrl->sensor_i2c_client->i2c_func_tbl =
+ &msm_sensor_cci_func_tbl;
+ if (!s_ctrl->sensor_v4l2_subdev_ops)
+ s_ctrl->sensor_v4l2_subdev_ops = &msm_sensor_subdev_ops;
+ s_ctrl->clk_info = cam_8974_clk_info;
+ s_ctrl->clk_info_size = ARRAY_SIZE(cam_8974_clk_info);
+ rc = s_ctrl->func_tbl->sensor_power_up(s_ctrl);
+ if (rc < 0) {
+ pr_err("%s %s power up failed\n", __func__,
+ s_ctrl->sensordata->sensor_name);
+ kfree(cci_client);
+ return rc;
+ }
+
+ CDBG("%s %s probe succeeded\n", __func__,
+ s_ctrl->sensordata->sensor_name);
+ v4l2_subdev_init(&s_ctrl->msm_sd.sd,
+ s_ctrl->sensor_v4l2_subdev_ops);
+ snprintf(s_ctrl->msm_sd.sd.name,
+ sizeof(s_ctrl->msm_sd.sd.name), "%s",
+ s_ctrl->sensordata->sensor_name);
+ v4l2_set_subdevdata(&s_ctrl->msm_sd.sd, pdev);
+ s_ctrl->msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ media_entity_init(&s_ctrl->msm_sd.sd.entity, 0, NULL, 0);
+ s_ctrl->msm_sd.sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV;
+ s_ctrl->msm_sd.sd.entity.group_id = MSM_CAMERA_SUBDEV_SENSOR;
+ s_ctrl->msm_sd.sd.entity.name =
+ s_ctrl->msm_sd.sd.name;
+
+ rc = camera_init_v4l2(&s_ctrl->pdev->dev, &session_id);
+ CDBG("%s rc %d session_id %d\n", __func__, rc, session_id);
+ s_ctrl->sensordata->sensor_info->session_id = session_id;
+ msm_sd_register(&s_ctrl->msm_sd);
+ CDBG("%s:%d\n", __func__, __LINE__);
+
+ s_ctrl->func_tbl->sensor_power_down(s_ctrl);
+ CDBG("%s:%d\n", __func__, __LINE__);
+ return rc;
+}
+
+int32_t msm_sensor_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int rc = 0;
+ struct msm_sensor_ctrl_t *s_ctrl;
+ uint32_t session_id;
+ CDBG("%s %s_i2c_probe called\n", __func__, client->name);
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ pr_err("%s %s i2c_check_functionality failed\n",
+ __func__, client->name);
+ rc = -EFAULT;
+ return rc;
+ }
+
+ s_ctrl = (struct msm_sensor_ctrl_t *)(id->driver_data);
+ if (!s_ctrl) {
+ pr_err("%s:%d sensor ctrl structure NULL\n", __func__,
+ __LINE__);
+ return -EINVAL;
+ }
+
+ s_ctrl->sensor_device_type = MSM_SENSOR_I2C_DEVICE;
+ s_ctrl->sensordata = client->dev.platform_data;
+ if (s_ctrl->sensordata == NULL) {
+ pr_err("%s %s NULL sensor data\n", __func__, client->name);
+ return -EFAULT;
+ }
+
+ if (s_ctrl->sensor_i2c_client != NULL) {
+ s_ctrl->sensor_i2c_client->client = client;
+ s_ctrl->dev = &client->dev;
+ if (s_ctrl->sensordata->slave_info->sensor_slave_addr)
+ s_ctrl->sensor_i2c_client->client->addr =
+ s_ctrl->sensordata->slave_info->
+ sensor_slave_addr;
+ } else {
+ pr_err("%s %s sensor_i2c_client NULL\n",
+ __func__, client->name);
+ rc = -EFAULT;
+ return rc;
+ }
+
+ if (!s_ctrl->func_tbl)
+ s_ctrl->func_tbl = &msm_sensor_func_tbl;
+ if (!s_ctrl->sensor_i2c_client->i2c_func_tbl)
+ s_ctrl->sensor_i2c_client->i2c_func_tbl =
+ &msm_sensor_qup_func_tbl;
+ if (!s_ctrl->sensor_v4l2_subdev_ops)
+ s_ctrl->sensor_v4l2_subdev_ops = &msm_sensor_subdev_ops;
+
+ s_ctrl->clk_info = cam_8960_clk_info;
+ s_ctrl->clk_info_size = ARRAY_SIZE(cam_8960_clk_info);
+
+ rc = s_ctrl->func_tbl->sensor_power_up(s_ctrl);
+ if (rc < 0) {
+ pr_err("%s %s power up failed\n", __func__, client->name);
+ return rc;
+ }
+
+ CDBG("%s %s probe succeeded\n", __func__, client->name);
+ snprintf(s_ctrl->msm_sd.sd.name,
+ sizeof(s_ctrl->msm_sd.sd.name), "%s", id->name);
+ v4l2_i2c_subdev_init(&s_ctrl->msm_sd.sd, client,
+ s_ctrl->sensor_v4l2_subdev_ops);
+ v4l2_set_subdevdata(&s_ctrl->msm_sd.sd, client);
+ s_ctrl->msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ media_entity_init(&s_ctrl->msm_sd.sd.entity, 0, NULL, 0);
+ s_ctrl->msm_sd.sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV;
+ s_ctrl->msm_sd.sd.entity.group_id = MSM_CAMERA_SUBDEV_SENSOR;
+ s_ctrl->msm_sd.sd.entity.name =
+ s_ctrl->msm_sd.sd.name;
+
+ rc = camera_init_v4l2(&s_ctrl->sensor_i2c_client->client->dev,
+ &session_id);
+ CDBG("%s rc %d session_id %d\n", __func__, rc, session_id);
+ s_ctrl->sensordata->sensor_info->session_id = session_id;
+ msm_sd_register(&s_ctrl->msm_sd);
+ CDBG("%s:%d\n", __func__, __LINE__);
+
+ s_ctrl->func_tbl->sensor_power_down(s_ctrl);
+ return rc;
+}
diff --git a/drivers/media/video/msmb/sensor/msm_sensor.h b/drivers/media/video/msmb/sensor/msm_sensor.h
new file mode 100644
index 0000000..20a2b14
--- /dev/null
+++ b/drivers/media/video/msmb/sensor/msm_sensor.h
@@ -0,0 +1,86 @@
+/* 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
+ * 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 MSM_SENSOR_H
+#define MSM_SENSOR_H
+
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/uaccess.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/gpio.h>
+#include <mach/camera2.h>
+#include <media/msm_cam_sensor.h>
+#include <media/v4l2-subdev.h>
+#include "msm_camera_i2c.h"
+#include "msm_sd.h"
+
+#define DEFINE_MSM_MUTEX(mutexname) \
+ static struct mutex mutexname = __MUTEX_INITIALIZER(mutexname)
+
+struct msm_sensor_ctrl_t;
+
+struct msm_sensor_fn_t {
+ int (*sensor_config) (struct msm_sensor_ctrl_t *, void __user *);
+ int (*sensor_power_down)
+ (struct msm_sensor_ctrl_t *);
+ int (*sensor_power_up) (struct msm_sensor_ctrl_t *);
+ int32_t (*sensor_match_id)(struct msm_sensor_ctrl_t *s_ctrl);
+};
+
+struct msm_sensor_ctrl_t {
+ struct platform_device *pdev;
+ enum msm_sensor_device_type_t sensor_device_type;
+ struct msm_camera_sensor_board_info *sensordata;
+ struct msm_sensor_power_setting_array power_setting_array;
+ struct mutex *msm_sensor_mutex;
+
+ struct msm_camera_i2c_client *sensor_i2c_client;
+ struct device *dev;
+
+ struct msm_sd_subdev msm_sd;
+ struct v4l2_subdev_info *sensor_v4l2_subdev_info;
+ uint8_t sensor_v4l2_subdev_info_size;
+ struct v4l2_subdev_ops *sensor_v4l2_subdev_ops;
+ struct msm_sensor_fn_t *func_tbl;
+ struct msm_camera_i2c_reg_setting stop_setting;
+ bool free_power_setting;
+ struct msm_cam_clk_info *clk_info;
+ uint16_t clk_info_size;
+};
+
+int32_t msm_sensor_config(struct msm_sensor_ctrl_t *s_ctrl,
+ void __user *argp);
+
+int32_t msm_sensor_power_up(struct msm_sensor_ctrl_t *s_ctrl);
+
+int32_t msm_sensor_power_down(struct msm_sensor_ctrl_t *s_ctrl);
+
+int32_t msm_sensor_match_id(struct msm_sensor_ctrl_t *s_ctrl);
+
+int32_t msm_sensor_platform_probe(struct platform_device *pdev,
+ void *data);
+
+int32_t msm_sensor_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id);
+
+int32_t msm_sensor_free_sensor_data(struct msm_sensor_ctrl_t *s_ctrl);
+
+#endif
diff --git a/drivers/media/video/msmb/sensor/s5k3l1yx.c b/drivers/media/video/msmb/sensor/s5k3l1yx.c
new file mode 100644
index 0000000..9c36b5b
--- /dev/null
+++ b/drivers/media/video/msmb/sensor/s5k3l1yx.c
@@ -0,0 +1,155 @@
+/* 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
+ * 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 "msm_sensor.h"
+#define S5K3L1YX_SENSOR_NAME "s5k3l1yx"
+DEFINE_MSM_MUTEX(s5k3l1yx_mut);
+
+static struct msm_sensor_ctrl_t s5k3l1yx_s_ctrl;
+
+static struct msm_sensor_power_setting s5k3l1yx_power_setting[] = {
+ {
+ .seq_type = SENSOR_VREG,
+ .seq_val = CAM_VDIG,
+ .config_val = 0,
+ .delay = 0,
+ },
+ {
+ .seq_type = SENSOR_VREG,
+ .seq_val = CAM_VANA,
+ .config_val = 0,
+ .delay = 0,
+ },
+ {
+ .seq_type = SENSOR_VREG,
+ .seq_val = CAM_VIO,
+ .config_val = 0,
+ .delay = 0,
+ },
+ {
+ .seq_type = SENSOR_VREG,
+ .seq_val = CAM_VAF,
+ .config_val = 0,
+ .delay = 0,
+ },
+ {
+ .seq_type = SENSOR_GPIO,
+ .seq_val = SENSOR_GPIO_RESET,
+ .config_val = GPIO_OUT_LOW,
+ .delay = 1,
+ },
+ {
+ .seq_type = SENSOR_GPIO,
+ .seq_val = SENSOR_GPIO_RESET,
+ .config_val = GPIO_OUT_HIGH,
+ .delay = 30,
+ },
+ {
+ .seq_type = SENSOR_CLK,
+ .seq_val = SENSOR_CAM_MCLK,
+ .config_val = 0,
+ .delay = 1,
+ },
+ {
+ .seq_type = SENSOR_I2C_MUX,
+ .seq_val = 0,
+ .config_val = 0,
+ .delay = 0,
+ },
+};
+
+static struct v4l2_subdev_info s5k3l1yx_subdev_info[] = {
+ {
+ .code = V4L2_MBUS_FMT_SBGGR10_1X10,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .fmt = 1,
+ .order = 0,
+ },
+};
+
+static const struct i2c_device_id s5k3l1yx_i2c_id[] = {
+ {S5K3L1YX_SENSOR_NAME, (kernel_ulong_t)&s5k3l1yx_s_ctrl},
+ { }
+};
+
+static struct i2c_driver s5k3l1yx_i2c_driver = {
+ .id_table = s5k3l1yx_i2c_id,
+ .probe = msm_sensor_i2c_probe,
+ .driver = {
+ .name = S5K3L1YX_SENSOR_NAME,
+ },
+};
+
+static struct msm_camera_i2c_client s5k3l1yx_sensor_i2c_client = {
+ .addr_type = MSM_CAMERA_I2C_WORD_ADDR,
+};
+
+static const struct of_device_id s5k3l1yx_dt_match[] = {
+ {.compatible = "qcom,s5k3l1yx", .data = &s5k3l1yx_s_ctrl},
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, s5k3l1yx_dt_match);
+
+static struct platform_driver s5k3l1yx_platform_driver = {
+ .driver = {
+ .name = "qcom,s5k3l1yx",
+ .owner = THIS_MODULE,
+ .of_match_table = s5k3l1yx_dt_match,
+ },
+};
+
+static int32_t s5k3l1yx_platform_probe(struct platform_device *pdev)
+{
+ int32_t rc = 0;
+ const struct of_device_id *match;
+ match = of_match_device(s5k3l1yx_dt_match, &pdev->dev);
+ rc = msm_sensor_platform_probe(pdev, match->data);
+ return rc;
+}
+
+static int __init s5k3l1yx_init_module(void)
+{
+ int32_t rc = 0;
+ pr_info("%s:%d\n", __func__, __LINE__);
+ rc = platform_driver_probe(&s5k3l1yx_platform_driver,
+ s5k3l1yx_platform_probe);
+ if (!rc)
+ return rc;
+ pr_err("%s:%d rc %d\n", __func__, __LINE__, rc);
+ return i2c_add_driver(&s5k3l1yx_i2c_driver);
+}
+
+static void __exit s5k3l1yx_exit_module(void)
+{
+ pr_info("%s:%d\n", __func__, __LINE__);
+ if (s5k3l1yx_s_ctrl.pdev) {
+ msm_sensor_free_sensor_data(&s5k3l1yx_s_ctrl);
+ platform_driver_unregister(&s5k3l1yx_platform_driver);
+ } else
+ i2c_del_driver(&s5k3l1yx_i2c_driver);
+ return;
+}
+
+static struct msm_sensor_ctrl_t s5k3l1yx_s_ctrl = {
+ .sensor_i2c_client = &s5k3l1yx_sensor_i2c_client,
+ .power_setting_array.power_setting = s5k3l1yx_power_setting,
+ .power_setting_array.size = ARRAY_SIZE(s5k3l1yx_power_setting),
+ .msm_sensor_mutex = &s5k3l1yx_mut,
+ .sensor_v4l2_subdev_info = s5k3l1yx_subdev_info,
+ .sensor_v4l2_subdev_info_size = ARRAY_SIZE(s5k3l1yx_subdev_info),
+};
+
+module_init(s5k3l1yx_init_module);
+module_exit(s5k3l1yx_exit_module);
+MODULE_DESCRIPTION("s5k3l1yx");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/v4l2-ctrls.c b/drivers/media/video/v4l2-ctrls.c
index 4012fec..3222ea0 100644
--- a/drivers/media/video/v4l2-ctrls.c
+++ b/drivers/media/video/v4l2-ctrls.c
@@ -259,6 +259,7 @@
"Single",
"Max Macroblocks",
"Max Bytes",
+ "GOB",
NULL,
};
static const char * const entropy_mode[] = {
diff --git a/drivers/mfd/wcd9xxx-core.c b/drivers/mfd/wcd9xxx-core.c
index bc129da..8953475 100644
--- a/drivers/mfd/wcd9xxx-core.c
+++ b/drivers/mfd/wcd9xxx-core.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, Code Aurora Forum. 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
@@ -334,7 +334,7 @@
{
int ret;
- if (wcd9xxx->reset_gpio) {
+ if (wcd9xxx->reset_gpio && wcd9xxx->slim_device_bootup) {
ret = gpio_request(wcd9xxx->reset_gpio, "CDC_RESET");
if (ret) {
pr_err("%s: Failed to request gpio %d\n", __func__,
@@ -342,7 +342,8 @@
wcd9xxx->reset_gpio = 0;
return ret;
}
-
+ }
+ if (wcd9xxx->reset_gpio) {
gpio_direction_output(wcd9xxx->reset_gpio, 0);
msleep(20);
gpio_direction_output(wcd9xxx->reset_gpio, 1);
@@ -880,6 +881,7 @@
dev_set_drvdata(&client->dev, wcd9xxx);
wcd9xxx->dev = &client->dev;
wcd9xxx->reset_gpio = pdata->reset_gpio;
+ wcd9xxx->slim_device_bootup = true;
if (client->dev.of_node)
wcd9xxx->mclk_rate = pdata->mclk_rate;
ret = wcd9xxx_enable_supplies(wcd9xxx, pdata);
@@ -1259,6 +1261,7 @@
wcd9xxx->reset_gpio = pdata->reset_gpio;
wcd9xxx->dev = &slim->dev;
wcd9xxx->mclk_rate = pdata->mclk_rate;
+ wcd9xxx->slim_device_bootup = true;
ret = wcd9xxx_enable_supplies(wcd9xxx, pdata);
if (ret)
@@ -1378,6 +1381,29 @@
return ret;
}
+static int wcd9xxx_device_up(struct wcd9xxx *wcd9xxx)
+{
+ int ret = 0;
+
+ if (wcd9xxx->slim_device_bootup) {
+ wcd9xxx->slim_device_bootup = false;
+ return 0;
+ }
+ ret = wcd9xxx_reset(wcd9xxx);
+ if (ret)
+ pr_err("%s: Resetting Codec failed\n", __func__);
+
+ wcd9xxx_bring_up(wcd9xxx);
+ wcd9xxx->post_reset(wcd9xxx);
+ return ret;
+}
+
+static int wcd9xxx_slim_device_up(struct slim_device *sldev)
+{
+ struct wcd9xxx *wcd9xxx = slim_get_devicedata(sldev);
+ return wcd9xxx_device_up(wcd9xxx);
+}
+
static int wcd9xxx_slim_resume(struct slim_device *sldev)
{
struct wcd9xxx *wcd9xxx = slim_get_devicedata(sldev);
@@ -1533,6 +1559,7 @@
.id_table = taiko_slimtest_id,
.resume = wcd9xxx_slim_resume,
.suspend = wcd9xxx_slim_suspend,
+ .device_up = wcd9xxx_slim_device_up,
};
static const struct slim_device_id tapan_slimtest_id[] = {
diff --git a/drivers/mfd/wcd9xxx-irq.c b/drivers/mfd/wcd9xxx-irq.c
index 23e0fcc..962558c 100644
--- a/drivers/mfd/wcd9xxx-irq.c
+++ b/drivers/mfd/wcd9xxx-irq.c
@@ -182,23 +182,37 @@
}
EXPORT_SYMBOL_GPL(wcd9xxx_unlock_sleep);
+void wcd9xxx_nested_irq_lock(struct wcd9xxx *wcd9xxx)
+{
+ mutex_lock(&wcd9xxx->nested_irq_lock);
+}
+
+void wcd9xxx_nested_irq_unlock(struct wcd9xxx *wcd9xxx)
+{
+ mutex_unlock(&wcd9xxx->nested_irq_lock);
+}
+
static void wcd9xxx_irq_dispatch(struct wcd9xxx *wcd9xxx, int irqbit)
{
if ((irqbit <= WCD9XXX_IRQ_MBHC_INSERTION) &&
(irqbit >= WCD9XXX_IRQ_MBHC_REMOVAL)) {
+ wcd9xxx_nested_irq_lock(wcd9xxx);
wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_INTR_CLEAR0 +
BIT_BYTE(irqbit),
BYTE_BIT_MASK(irqbit));
if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_I2C)
wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_INTR_MODE, 0x02);
handle_nested_irq(phyirq_to_virq(wcd9xxx, irqbit));
+ wcd9xxx_nested_irq_unlock(wcd9xxx);
} else {
+ wcd9xxx_nested_irq_lock(wcd9xxx);
handle_nested_irq(phyirq_to_virq(wcd9xxx, irqbit));
wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_INTR_CLEAR0 +
BIT_BYTE(irqbit),
BYTE_BIT_MASK(irqbit));
if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_I2C)
wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_INTR_MODE, 0x02);
+ wcd9xxx_nested_irq_unlock(wcd9xxx);
}
}
@@ -325,11 +339,13 @@
u8 irq_level[wcd9xxx_num_irq_regs(wcd9xxx)];
mutex_init(&wcd9xxx->irq_lock);
+ mutex_init(&wcd9xxx->nested_irq_lock);
wcd9xxx->irq = wcd9xxx_irq_get_upstream_irq(wcd9xxx);
if (!wcd9xxx->irq) {
pr_warn("%s: irq driver is not yet initialized\n", __func__);
mutex_destroy(&wcd9xxx->irq_lock);
+ mutex_destroy(&wcd9xxx->nested_irq_lock);
return -EPROBE_DEFER;
}
pr_debug("%s: probed irq %d\n", __func__, wcd9xxx->irq);
@@ -339,6 +355,8 @@
if (ret) {
pr_err("%s: Failed to setup downstream IRQ\n", __func__);
wcd9xxx_irq_put_upstream_irq(wcd9xxx);
+ mutex_destroy(&wcd9xxx->irq_lock);
+ mutex_destroy(&wcd9xxx->nested_irq_lock);
return ret;
}
@@ -388,6 +406,7 @@
pr_err("%s: Failed to init wcd9xxx irq\n", __func__);
wcd9xxx_irq_put_upstream_irq(wcd9xxx);
mutex_destroy(&wcd9xxx->irq_lock);
+ mutex_destroy(&wcd9xxx->nested_irq_lock);
}
return ret;
@@ -424,6 +443,7 @@
device_init_wakeup(wcd9xxx->dev, 0);
}
mutex_destroy(&wcd9xxx->irq_lock);
+ mutex_destroy(&wcd9xxx->nested_irq_lock);
}
#ifndef CONFIG_OF
diff --git a/drivers/misc/qseecom.c b/drivers/misc/qseecom.c
index c415952..b11e30e 100644
--- a/drivers/misc/qseecom.c
+++ b/drivers/misc/qseecom.c
@@ -2,7 +2,7 @@
/*Qualcomm Secure Execution Environment Communicator (QSEECOM) driver
*
- * Copyright (c) 2012, Code Aurora Forum. 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
@@ -58,7 +58,7 @@
#define QSEE_CE_CLK_100MHZ 100000000
#define QSEE_CE_CLK_50MHZ 50000000
-#define QSEECOM_MAX_SG_ENTRY 10
+#define QSEECOM_MAX_SG_ENTRY 512
enum qseecom_command_scm_resp_type {
QSEOS_APP_ID = 0xEE01,
@@ -1463,7 +1463,7 @@
struct qseecom_command_scm_resp resp;
u8 *img_data = NULL;
- if (__qseecom_get_fw_size("commonlib", &fw_size))
+ if (__qseecom_get_fw_size("cmnlib", &fw_size))
return -EIO;
img_data = kzalloc(fw_size, GFP_KERNEL);
@@ -1471,7 +1471,7 @@
pr_err("Mem allocation for lib image data failed\n");
return -ENOMEM;
}
- ret = __qseecom_get_fw_data("commonlib", img_data, &load_req);
+ ret = __qseecom_get_fw_data("cmnlib", img_data, &load_req);
if (ret) {
kzfree(img_data);
return -EIO;
@@ -1662,6 +1662,7 @@
/* Populate the structure for sending scm call to load image */
data->client.sb_virt = (char *) ion_map_kernel(qseecom.ion_clnt,
data->client.ihandle);
+ data->client.user_virt_sb_base = (uint32_t)data->client.sb_virt;
data->client.sb_phys = pa;
(*handle)->dev = (void *)data;
(*handle)->sbuf = (unsigned char *)data->client.sb_virt;
@@ -1771,16 +1772,25 @@
int qseecom_set_bandwidth(struct qseecom_handle *handle, bool high)
{
+ int ret = 0;
if ((handle == NULL) || (handle->dev == NULL)) {
pr_err("No valid kernel client\n");
return -EINVAL;
}
- if (high)
- return qsee_vote_for_clock(handle->dev, CLK_DFAB);
- else {
+ if (high) {
+ ret = qsee_vote_for_clock(handle->dev, CLK_DFAB);
+ if (ret)
+ pr_err("Failed to vote for DFAB clock%d\n", ret);
+ ret = qsee_vote_for_clock(handle->dev, CLK_SFPB);
+ if (ret) {
+ pr_err("Failed to vote for SFPB clock%d\n", ret);
+ qsee_disable_clock_vote(handle->dev, CLK_DFAB);
+ }
+ } else {
qsee_disable_clock_vote(handle->dev, CLK_DFAB);
- return 0;
+ qsee_disable_clock_vote(handle->dev, CLK_SFPB);
}
+ return ret;
}
EXPORT_SYMBOL(qseecom_set_bandwidth);
@@ -2277,12 +2287,16 @@
ret = qsee_vote_for_clock(data, CLK_DFAB);
if (ret)
pr_err("Failed to vote for DFAB clock%d\n", ret);
+ ret = qsee_vote_for_clock(data, CLK_SFPB);
+ if (ret)
+ pr_err("Failed to vote for SFPB clock%d\n", ret);
atomic_dec(&data->ioctl_count);
break;
}
case QSEECOM_IOCTL_PERF_DISABLE_REQ:{
atomic_inc(&data->ioctl_count);
qsee_disable_clock_vote(data, CLK_DFAB);
+ qsee_disable_clock_vote(data, CLK_SFPB);
atomic_dec(&data->ioctl_count);
break;
}
diff --git a/drivers/misc/tspp.c b/drivers/misc/tspp.c
index 563a013..4e504da5 100644
--- a/drivers/misc/tspp.c
+++ b/drivers/misc/tspp.c
@@ -41,6 +41,8 @@
#include <mach/dma.h>
#include <mach/msm_tspp.h>
#include <linux/debugfs.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
/*
* General defines
@@ -647,45 +649,19 @@
}
/*** GPIO functions ***/
-static void tspp_gpios_free(const struct msm_gpio *table, int size)
-{
- int i;
- const struct msm_gpio *g;
- for (i = size-1; i >= 0; i--) {
- g = table + i;
- gpio_free(GPIO_PIN(g->gpio_cfg));
- }
-}
-
-static int tspp_gpios_request(const struct msm_gpio *table, int size)
-{
- int rc;
- int i;
- const struct msm_gpio *g;
- for (i = 0; i < size; i++) {
- g = table + i;
- rc = gpio_request(GPIO_PIN(g->gpio_cfg), g->label);
- if (rc) {
- pr_err("tspp: gpio_request(%d) <%s> failed: %d\n",
- GPIO_PIN(g->gpio_cfg), g->label ?: "?", rc);
- goto err;
- }
- }
- return 0;
-err:
- tspp_gpios_free(table, i);
- return rc;
-}
-
static int tspp_gpios_disable(const struct msm_gpio *table, int size)
{
int rc = 0;
int i;
const struct msm_gpio *g;
+
for (i = size-1; i >= 0; i--) {
int tmp;
g = table + i;
- tmp = gpio_tlmm_config(g->gpio_cfg, GPIO_CFG_DISABLE);
+
+ tmp = gpio_tlmm_config(GPIO_CFG(GPIO_PIN(g->gpio_cfg),
+ 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA),
+ GPIO_CFG_DISABLE);
if (tmp) {
pr_err("tspp_gpios_disable(0x%08x, GPIO_CFG_DISABLE) <%s> failed: %d\n",
g->gpio_cfg, g->label ?: "?", rc);
@@ -704,8 +680,9 @@
static int tspp_gpios_enable(const struct msm_gpio *table, int size)
{
int rc;
- int i;
+ int i, j;
const struct msm_gpio *g;
+
for (i = 0; i < size; i++) {
g = table + i;
rc = gpio_tlmm_config(g->gpio_cfg, GPIO_CFG_ENABLE);
@@ -721,39 +698,26 @@
}
return 0;
err:
- tspp_gpios_disable(table, i);
- return rc;
-}
+ for (j = 0; j < i; j++)
+ tspp_gpios_disable(table, j);
-static int tspp_gpios_request_enable(const struct msm_gpio *table, int size)
-{
- int rc = tspp_gpios_request(table, size);
- if (rc)
- return rc;
- rc = tspp_gpios_enable(table, size);
- if (rc)
- tspp_gpios_free(table, size);
return rc;
}
-static void tspp_gpios_disable_free(const struct msm_gpio *table, int size)
-{
- tspp_gpios_disable(table, size);
- tspp_gpios_free(table, size);
-}
-
static int tspp_start_gpios(struct tspp_device *device)
{
struct msm_tspp_platform_data *pdata =
device->pdev->dev.platform_data;
- return tspp_gpios_request_enable(pdata->gpios, pdata->num_gpios);
+
+ return tspp_gpios_enable(pdata->gpios, pdata->num_gpios);
}
static void tspp_stop_gpios(struct tspp_device *device)
{
struct msm_tspp_platform_data *pdata =
device->pdev->dev.platform_data;
- tspp_gpios_disable_free(pdata->gpios, pdata->num_gpios);
+
+ tspp_gpios_disable(pdata->gpios, pdata->num_gpios);
}
/*** Clock functions ***/
@@ -1427,9 +1391,10 @@
event = &channel->event;
/* start the clocks if needed */
- tspp_clock_start(pdev);
- if (tspp_channels_in_use(pdev) == 0)
+ if (tspp_channels_in_use(pdev) == 0) {
+ tspp_clock_start(pdev);
wake_lock(&pdev->wake_lock);
+ }
/* mark it as used */
channel->used = 1;
@@ -1604,9 +1569,10 @@
channel->locked = NULL;
channel->used = 0;
- if (tspp_channels_in_use(pdev) == 0)
+ if (tspp_channels_in_use(pdev) == 0) {
wake_unlock(&pdev->wake_lock);
- tspp_clock_stop(pdev);
+ tspp_clock_stop(pdev);
+ }
return 0;
}
@@ -2554,6 +2520,137 @@
}
}
+/* copy device-tree data to platfrom data struct */
+static __devinit struct msm_tspp_platform_data *
+msm_tspp_dt_to_pdata(struct platform_device *pdev)
+{
+ struct device_node *node = pdev->dev.of_node;
+ struct msm_tspp_platform_data *data;
+ struct msm_gpio *gpios;
+ int i, rc;
+ int gpio;
+ u32 gpio_func;
+
+ /* Note: memory allocated by devm_kzalloc is freed automatically */
+ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+ if (!data) {
+ pr_err("tspp: Unable to allocate platform data\n");
+ return NULL;
+ }
+ rc = of_property_read_string(node, "qcom,tsif-pclk", &data->tsif_pclk);
+ if (rc) {
+ pr_err("tspp: Could not find tsif-pclk property, err = %d\n",
+ rc);
+ return NULL;
+ }
+ rc = of_property_read_string(node, "qcom,tsif-ref-clk",
+ &data->tsif_ref_clk);
+ if (rc) {
+ pr_err("tspp: Could not find tsif-ref-clk property, err = %d\n",
+ rc);
+ return NULL;
+ }
+
+ data->num_gpios = of_gpio_count(node);
+ if (data->num_gpios == 0) {
+ pr_err("tspp: Could not find GPIO definitions\n");
+ return NULL;
+ }
+ gpios = devm_kzalloc(&pdev->dev,
+ (data->num_gpios * sizeof(struct msm_gpio)),
+ GFP_KERNEL);
+ if (!gpios) {
+ pr_err("tspp: Unable to allocate memory for GPIOs table\n");
+ return NULL;
+ }
+ /* Assuming GPIO FUNC property is the same for all GPIOs */
+ if (of_property_read_u32(node, "qcom,gpios-func", &gpio_func)) {
+ pr_err("tspp: Could not find gpios-func property\n");
+ return NULL;
+ }
+ for (i = 0; i < data->num_gpios; i++) {
+ gpio = of_get_gpio(node, i);
+ gpios[i].gpio_cfg = GPIO_CFG(gpio, gpio_func,
+ GPIO_CFG_INPUT,
+ GPIO_CFG_PULL_DOWN,
+ GPIO_CFG_2MA);
+ rc = of_property_read_string_index(node, "qcom,gpio-names",
+ i, &gpios[i].label);
+ if (rc)
+ pr_warn("tspp: Could not find gpio-names property\n");
+ }
+
+ data->gpios = gpios;
+
+ return data;
+}
+
+static int msm_tspp_map_irqs(struct platform_device *pdev,
+ struct tspp_device *device)
+{
+ int rc;
+ int i;
+
+ /* get IRQ numbers from platform information */
+
+ /* map TSPP IRQ */
+ rc = platform_get_irq_byname(pdev, "TSIF_TSPP_IRQ");
+ if (rc > 0) {
+ device->tspp_irq = rc;
+ rc = request_irq(device->tspp_irq, tspp_isr, IRQF_SHARED,
+ dev_name(&pdev->dev), device);
+ if (rc) {
+ dev_err(&pdev->dev,
+ "failed to request TSPP IRQ %d : %d",
+ device->tspp_irq, rc);
+ device->tspp_irq = 0;
+ return -EINVAL;
+ }
+ } else {
+ dev_err(&pdev->dev, "failed to get TSPP IRQ");
+ return -EINVAL;
+ }
+
+ /* map TSIF IRQs */
+ rc = platform_get_irq_byname(pdev, "TSIF0_IRQ");
+ if (rc > 0) {
+ device->tsif[0].tsif_irq = rc;
+ } else {
+ dev_err(&pdev->dev, "failed to get TSIF0 IRQ");
+ return -EINVAL;
+ }
+
+ rc = platform_get_irq_byname(pdev, "TSIF1_IRQ");
+ if (rc > 0) {
+ device->tsif[1].tsif_irq = rc;
+ } else {
+ dev_err(&pdev->dev, "failed to get TSIF1 IRQ");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < TSPP_TSIF_INSTANCES; i++) {
+ rc = request_irq(device->tsif[i].tsif_irq,
+ tsif_isr, IRQF_SHARED,
+ dev_name(&pdev->dev), &device->tsif[i]);
+ if (rc) {
+ dev_warn(&pdev->dev, "failed to request TSIF%d IRQ: %d",
+ i, rc);
+ device->tsif[i].tsif_irq = 0;
+ }
+ }
+
+ /* map BAM IRQ */
+ rc = platform_get_irq_byname(pdev, "TSIF_BAM_IRQ");
+ if (rc > 0) {
+ device->bam_irq = rc;
+ } else {
+ dev_err(&pdev->dev, "failed to get TSPP BAM IRQ");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static int __devinit msm_tspp_probe(struct platform_device *pdev)
{
int rc = -ENODEV;
@@ -2567,8 +2664,20 @@
struct resource *mem_bam;
struct tspp_channel *channel;
- /* must have platform data */
- data = pdev->dev.platform_data;
+ if (pdev->dev.of_node) {
+ /* get information from device tree */
+ data = msm_tspp_dt_to_pdata(pdev);
+ /* get device ID */
+ rc = of_property_read_u32(pdev->dev.of_node,
+ "cell-index", &pdev->id);
+ if (rc)
+ pdev->id = -1;
+
+ pdev->dev.platform_data = data;
+ } else {
+ /* must have platform data */
+ data = pdev->dev.platform_data;
+ }
if (!data) {
pr_err("tspp: Platform data not available");
rc = -EINVAL;
@@ -2617,7 +2726,8 @@
}
/* map I/O memory */
- mem_tsif0 = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ mem_tsif0 = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM, "MSM_TSIF0_PHYS");
if (!mem_tsif0) {
pr_err("tspp: Missing tsif0 MEM resource");
rc = -ENXIO;
@@ -2630,7 +2740,8 @@
goto err_map_tsif0;
}
- mem_tsif1 = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ mem_tsif1 = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM, "MSM_TSIF1_PHYS");
if (!mem_tsif1) {
dev_err(&pdev->dev, "Missing tsif1 MEM resource");
rc = -ENXIO;
@@ -2643,7 +2754,8 @@
goto err_map_tsif1;
}
- mem_tspp = platform_get_resource(pdev, IORESOURCE_MEM, 2);
+ mem_tspp = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM, "MSM_TSPP_PHYS");
if (!mem_tspp) {
dev_err(&pdev->dev, "Missing MEM resource");
rc = -ENXIO;
@@ -2655,7 +2767,8 @@
goto err_map_dev;
}
- mem_bam = platform_get_resource(pdev, IORESOURCE_MEM, 3);
+ mem_bam = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM, "MSM_TSPP_BAM_PHYS");
if (!mem_bam) {
pr_err("tspp: Missing bam MEM resource");
rc = -ENXIO;
@@ -2670,39 +2783,8 @@
goto err_map_bam;
}
- /* map TSPP IRQ */
- rc = platform_get_irq(pdev, 0);
- if (rc > 0) {
- device->tspp_irq = rc;
- rc = request_irq(device->tspp_irq, tspp_isr, IRQF_SHARED,
- dev_name(&pdev->dev), device);
- if (rc) {
- dev_err(&pdev->dev, "failed to request IRQ %d : %d",
- device->tspp_irq, rc);
- goto err_irq;
- }
- } else {
- dev_err(&pdev->dev, "failed to get tspp IRQ");
+ if (msm_tspp_map_irqs(pdev, device))
goto err_irq;
- }
-
- /* map TSIF IRQs */
- device->tsif[0].tsif_irq = TSIF1_IRQ;
- device->tsif[1].tsif_irq = TSIF2_IRQ;
-
- for (i = 0; i < TSPP_TSIF_INSTANCES; i++) {
- rc = request_irq(device->tsif[i].tsif_irq,
- tsif_isr, IRQF_SHARED,
- dev_name(&pdev->dev), &device->tsif[i]);
- if (rc) {
- dev_warn(&pdev->dev, "failed to request TSIF%d IRQ: %d",
- i, rc);
- device->tsif[i].tsif_irq = 0;
- }
- }
-
- /* BAM IRQ */
- device->bam_irq = TSIF_BAM_IRQ;
/* GPIOs */
rc = tspp_start_gpios(device);
@@ -2737,17 +2819,17 @@
device->bam_props.irq = device->bam_irq;
device->bam_props.manage = SPS_BAM_MGR_LOCAL;
+ if (tspp_clock_start(device) != 0) {
+ dev_err(&pdev->dev, "Can't start clocks");
+ goto err_clock;
+ }
+
if (sps_register_bam_device(&device->bam_props,
&device->bam_handle) != 0) {
pr_err("tspp: failed to register bam");
goto err_bam;
}
- if (tspp_clock_start(device) != 0) {
- dev_err(&pdev->dev, "Can't start clocks");
- goto err_clock;
- }
-
spin_lock_init(&device->spinlock);
tasklet_init(&device->tlet, tspp_sps_complete_tlet,
(unsigned long)device);
@@ -2756,7 +2838,11 @@
tspp_global_reset(device);
version = readl_relaxed(device->base + TSPP_VERSION);
- if (version != 1)
+ /*
+ * TSPP version can be bits [7:0] or alternatively,
+ * TSPP major version is bits [31:28].
+ */
+ if ((version != 0x1) && (((version >> 28) & 0xF) != 0x1))
pr_warn("tspp: unrecognized hw version=%i", version);
/* initialize the channels */
@@ -2776,21 +2862,30 @@
return 0;
err_channel:
- /* uninitialize channels */
+ /* un-initialize channels */
for (j = 0; j < i; j++) {
channel = &(device->channels[i]);
device_destroy(tspp_class, channel->cdev.dev);
cdev_del(&channel->cdev);
}
-err_clock:
+
sps_deregister_bam_device(device->bam_handle);
+err_clock:
err_bam:
tspp_debugfs_exit(device);
for (i = 0; i < TSPP_TSIF_INSTANCES; i++)
tsif_debugfs_exit(&device->tsif[i]);
+
+ tspp_stop_gpios(device);
err_gpio:
err_irq:
- tspp_stop_gpios(device);
+ for (i = 0; i < TSPP_TSIF_INSTANCES; i++) {
+ if (device->tsif[i].tsif_irq)
+ free_irq(device->tsif[i].tsif_irq, &device->tsif[i]);
+ }
+ if (device->tspp_irq)
+ free_irq(device->tspp_irq, device);
+
iounmap(device->bam_props.virt_addr);
err_map_bam:
err_res_bam:
@@ -2830,7 +2925,10 @@
cdev_del(&channel->cdev);
}
+ /* de-registering BAM device requires clocks */
+ tspp_clock_start(device);
sps_deregister_bam_device(device->bam_handle);
+ tspp_clock_stop(device);
for (i = 0; i < TSPP_TSIF_INSTANCES; i++) {
tsif_debugfs_exit(&device->tsif[i]);
@@ -2879,12 +2977,18 @@
.runtime_resume = tspp_runtime_resume,
};
+static struct of_device_id msm_match_table[] = {
+ {.compatible = "qcom,msm_tspp"},
+ {}
+};
+
static struct platform_driver msm_tspp_driver = {
.probe = msm_tspp_probe,
.remove = __exit_p(msm_tspp_remove),
.driver = {
.name = "msm_tspp",
.pm = &tspp_dev_pm_ops,
+ .of_match_table = msm_match_table,
},
};
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index 0f9ee46d..4708096 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -57,6 +57,7 @@
#define INAND_CMD38_ARG_SECERASE 0x80
#define INAND_CMD38_ARG_SECTRIM1 0x81
#define INAND_CMD38_ARG_SECTRIM2 0x88
+#define MMC_BLK_TIMEOUT_MS (30 * 1000) /* 30 sec timeout */
#define MMC_SANITIZE_REQ_TIMEOUT 240000 /* msec */
@@ -577,8 +578,6 @@
struct mmc_request mrq = {NULL};
struct scatterlist sg;
int err;
- int is_rpmb = false;
- u32 status = 0;
/*
* The caller must have CAP_SYS_RAWIO, and must be calling this on the
@@ -598,9 +597,6 @@
goto cmd_done;
}
- if (md->area_type & MMC_BLK_DATA_AREA_RPMB)
- is_rpmb = true;
-
card = md->queue.card;
if (IS_ERR(card)) {
err = PTR_ERR(card);
@@ -661,13 +657,6 @@
goto cmd_rel_host;
}
- if (is_rpmb) {
- err = mmc_set_blockcount(card, data.blocks,
- idata->ic.write_flag & (1 << 31));
- if (err)
- goto cmd_rel_host;
- }
-
mmc_wait_for_req(card->host, &mrq);
if (cmd.error) {
@@ -703,7 +692,171 @@
}
}
- if (is_rpmb) {
+cmd_rel_host:
+ mmc_release_host(card->host);
+
+cmd_done:
+ mmc_blk_put(md);
+ kfree(idata->buf);
+ kfree(idata);
+ return err;
+}
+
+struct mmc_blk_ioc_rpmb_data {
+ struct mmc_blk_ioc_data *data[MMC_IOC_MAX_RPMB_CMD];
+};
+
+static struct mmc_blk_ioc_rpmb_data *mmc_blk_ioctl_rpmb_copy_from_user(
+ struct mmc_ioc_rpmb __user *user)
+{
+ struct mmc_blk_ioc_rpmb_data *idata;
+ int err, i;
+
+ idata = kzalloc(sizeof(*idata), GFP_KERNEL);
+ if (!idata) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ for (i = 0; i < MMC_IOC_MAX_RPMB_CMD; i++) {
+ idata->data[i] = mmc_blk_ioctl_copy_from_user(&(user->cmds[i]));
+ if (IS_ERR(idata->data[i])) {
+ err = PTR_ERR(idata->data[i]);
+ goto copy_err;
+ }
+ }
+
+ return idata;
+
+copy_err:
+ while (--i >= 0) {
+ kfree(idata->data[i]->buf);
+ kfree(idata->data[i]);
+ }
+ kfree(idata);
+out:
+ return ERR_PTR(err);
+}
+
+static int mmc_blk_ioctl_rpmb_cmd(struct block_device *bdev,
+ struct mmc_ioc_rpmb __user *ic_ptr)
+{
+ struct mmc_blk_ioc_rpmb_data *idata;
+ struct mmc_blk_data *md;
+ struct mmc_card *card;
+ struct mmc_command cmd = {0};
+ struct mmc_data data = {0};
+ struct mmc_request mrq = {NULL};
+ struct scatterlist sg;
+ int err = 0, i = 0;
+ u32 status = 0;
+
+ /* The caller must have CAP_SYS_RAWIO */
+ if (!capable(CAP_SYS_RAWIO))
+ return -EPERM;
+
+ md = mmc_blk_get(bdev->bd_disk);
+ /* make sure this is a rpmb partition */
+ if ((!md) || (!(md->area_type & MMC_BLK_DATA_AREA_RPMB))) {
+ err = -EINVAL;
+ goto cmd_done;
+ }
+
+ idata = mmc_blk_ioctl_rpmb_copy_from_user(ic_ptr);
+ if (IS_ERR(idata)) {
+ err = PTR_ERR(idata);
+ goto cmd_done;
+ }
+
+ card = md->queue.card;
+ if (IS_ERR(card)) {
+ err = PTR_ERR(card);
+ goto idata_free;
+ }
+
+ mmc_claim_host(card->host);
+
+ err = mmc_blk_part_switch(card, md);
+ if (err)
+ goto cmd_rel_host;
+
+ for (i = 0; i < MMC_IOC_MAX_RPMB_CMD; i++) {
+ struct mmc_blk_ioc_data *curr_data;
+ struct mmc_ioc_cmd *curr_cmd;
+
+ curr_data = idata->data[i];
+ curr_cmd = &curr_data->ic;
+ if (!curr_cmd->opcode)
+ break;
+
+ cmd.opcode = curr_cmd->opcode;
+ cmd.arg = curr_cmd->arg;
+ cmd.flags = curr_cmd->flags;
+
+ if (curr_data->buf_bytes) {
+ data.sg = &sg;
+ data.sg_len = 1;
+ data.blksz = curr_cmd->blksz;
+ data.blocks = curr_cmd->blocks;
+
+ sg_init_one(data.sg, curr_data->buf,
+ curr_data->buf_bytes);
+
+ if (curr_cmd->write_flag)
+ data.flags = MMC_DATA_WRITE;
+ else
+ data.flags = MMC_DATA_READ;
+
+ /* data.flags must already be set before doing this. */
+ mmc_set_data_timeout(&data, card);
+
+ /*
+ * Allow overriding the timeout_ns for empirical tuning.
+ */
+ if (curr_cmd->data_timeout_ns)
+ data.timeout_ns = curr_cmd->data_timeout_ns;
+
+ mrq.data = &data;
+ }
+
+ mrq.cmd = &cmd;
+
+ err = mmc_set_blockcount(card, data.blocks,
+ curr_cmd->write_flag & (1 << 31));
+ if (err)
+ goto cmd_rel_host;
+
+ mmc_wait_for_req(card->host, &mrq);
+
+ if (cmd.error) {
+ dev_err(mmc_dev(card->host), "%s: cmd error %d\n",
+ __func__, cmd.error);
+ err = cmd.error;
+ goto cmd_rel_host;
+ }
+ if (data.error) {
+ dev_err(mmc_dev(card->host), "%s: data error %d\n",
+ __func__, data.error);
+ err = data.error;
+ goto cmd_rel_host;
+ }
+
+ if (copy_to_user(&(ic_ptr->cmds[i].response), cmd.resp,
+ sizeof(cmd.resp))) {
+ err = -EFAULT;
+ goto cmd_rel_host;
+ }
+
+ if (!curr_cmd->write_flag) {
+ if (copy_to_user((void __user *)(unsigned long)
+ curr_cmd->data_ptr,
+ curr_data->buf,
+ curr_data->buf_bytes)) {
+ err = -EFAULT;
+ goto cmd_rel_host;
+ }
+ }
+
/*
* Ensure RPMB command has completed by polling CMD13
* "Send Status".
@@ -718,10 +871,15 @@
cmd_rel_host:
mmc_release_host(card->host);
+idata_free:
+ for (i = 0; i < MMC_IOC_MAX_RPMB_CMD; i++) {
+ kfree(idata->data[i]->buf);
+ kfree(idata->data[i]);
+ }
+ kfree(idata);
+
cmd_done:
mmc_blk_put(md);
- kfree(idata->buf);
- kfree(idata);
return err;
}
@@ -731,6 +889,9 @@
int ret = -EINVAL;
if (cmd == MMC_IOC_CMD)
ret = mmc_blk_ioctl_cmd(bdev, (struct mmc_ioc_cmd __user *)arg);
+ if (cmd == MMC_IOC_RPMB_CMD)
+ ret = mmc_blk_ioctl_rpmb_cmd(bdev,
+ (struct mmc_ioc_rpmb __user *)arg);
return ret;
}
@@ -759,7 +920,8 @@
int ret;
struct mmc_blk_data *main_md = mmc_get_drvdata(card);
- if (main_md->part_curr == md->part_type)
+ if ((main_md->part_curr == md->part_type) &&
+ (card->part_curr == md->part_type))
return 0;
if (mmc_card_mmc(card)) {
@@ -775,6 +937,7 @@
return ret;
card->ext_csd.part_config = part_config;
+ card->part_curr = md->part_type;
}
main_md->part_curr = md->part_type;
@@ -1300,6 +1463,9 @@
*/
if (!mmc_host_is_spi(card->host) && rq_data_dir(req) != READ) {
u32 status;
+ unsigned long timeout;
+
+ timeout = jiffies + msecs_to_jiffies(MMC_BLK_TIMEOUT_MS);
do {
int err = get_card_status(card, &status, 5);
if (err) {
@@ -1307,6 +1473,17 @@
req->rq_disk->disk_name, err);
return MMC_BLK_CMD_ERR;
}
+
+ /* Timeout if the device never becomes ready for data
+ * and never leaves the program state.
+ */
+ if (time_after(jiffies, timeout)) {
+ pr_err("%s: Card stuck in programming state!"\
+ " %s %s\n", mmc_hostname(card->host),
+ req->rq_disk->disk_name, __func__);
+
+ return MMC_BLK_CMD_ERR;
+ }
/*
* Some cards mishandle the status bits,
* so make sure to check both the busy
@@ -1624,39 +1801,6 @@
}
EXPORT_SYMBOL(mmc_blk_init_packed_statistics);
-/**
- * mmc_blk_init_async_event_statistics() - Init async event
- * statistics data
- * @card: The mmc_card in which the async_event_stats
- * struct is a member
- *
- * Initiate counters for the new request feature, and mark the
- * statistics as enabled.
- */
-void mmc_blk_init_async_event_statistics(struct mmc_card *card)
-{
- if (!card)
- return;
-
- /* init async events tests stats */
- memset(&card->async_event_stats,
- sizeof(struct mmc_async_event_stats), 0);
- card->async_event_stats.null_fetched = 0;
- card->async_event_stats.wakeup_new = 0;
- card->async_event_stats.new_request_flag = 0;
- card->async_event_stats.q_no_waiting = 0;
- card->async_event_stats.enabled = true;
- card->async_event_stats.no_mmc_request_action = 0;
- card->async_event_stats.wakeup_mq_thread = 0;
- card->async_event_stats.fetch_due_to_new_req = 0;
- card->async_event_stats.returned_new_req = 0;
- card->async_event_stats.done_flag = 0;
- card->async_event_stats.cmd_retry = 0;
- card->async_event_stats.done_when_new_req_event_on = 0;
- card->async_event_stats.new_req_when_new_marked = 0;
-}
-EXPORT_SYMBOL(mmc_blk_init_async_event_statistics);
-
static u8 mmc_blk_prep_packed_list(struct mmc_queue *mq, struct request *req)
{
struct request_queue *q = mq->queue;
@@ -1669,12 +1813,7 @@
u8 put_back = 0;
u8 max_packed_rw = 0;
u8 reqs = 0;
- struct mmc_wr_pack_stats *stats;
-
- if (!card)
- goto no_packed;
-
- stats = &card->wr_pack_stats;
+ struct mmc_wr_pack_stats *stats = &card->wr_pack_stats;
mmc_blk_clear_packed(mq->mqrq_cur);
@@ -2010,7 +2149,6 @@
struct mmc_async_req *areq;
const u8 packed_num = 2;
u8 reqs = 0;
- struct mmc_async_event_stats *stats = &card->async_event_stats;
if (!rqc && !mq->mqrq_prev->req)
return 0;
@@ -2033,12 +2171,8 @@
areq = NULL;
areq = mmc_start_req(card->host, areq, (int *) &status);
if (!areq) {
- if (status == MMC_BLK_NEW_REQUEST && stats) {
- if (stats->enabled)
- stats->returned_new_req++;
-
+ if (status == MMC_BLK_NEW_REQUEST)
mq->flags |= MMC_QUEUE_NEW_REQUEST;
- }
return 0;
}
diff --git a/drivers/mmc/card/mmc_block_test.c b/drivers/mmc/card/mmc_block_test.c
index 4a21fd4..7a4d19e 100644
--- a/drivers/mmc/card/mmc_block_test.c
+++ b/drivers/mmc/card/mmc_block_test.c
@@ -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
@@ -26,7 +26,7 @@
#define MODULE_NAME "mmc_block_test"
#define TEST_MAX_SECTOR_RANGE (600*1024*1024) /* 600 MB */
-#define TEST_MAX_BIOS_PER_REQ 120
+#define TEST_MAX_BIOS_PER_REQ 128
#define CMD23_PACKED_BIT (1 << 30)
#define LARGE_PRIME_1 1103515367
#define LARGE_PRIME_2 35757
@@ -37,28 +37,30 @@
#define SECTOR_SIZE 512
#define NUM_OF_SECTORS_PER_BIO ((BIO_U32_SIZE * 4) / SECTOR_SIZE)
#define BIO_TO_SECTOR(x) (x * NUM_OF_SECTORS_PER_BIO)
-/* the desired long test size to be written or read */
-#define LONG_TEST_MAX_NUM_BYTES (50*1024*1024) /* 50MB */
+/* the desired long test size to be read */
+#define LONG_READ_TEST_MAX_NUM_BYTES (50*1024*1024) /* 50MB */
+/* the minimum amount of requests that will be created */
+#define LONG_WRITE_TEST_MIN_NUM_REQS 200 /* 100MB */
/* request queue limitation is 128 requests, and we leave 10 spare requests */
#define TEST_MAX_REQUESTS 118
-#define LONG_TEST_MAX_NUM_REQS (LONG_TEST_MAX_NUM_BYTES / \
+#define LONG_READ_TEST_MAX_NUM_REQS (LONG_READ_TEST_MAX_NUM_BYTES / \
(TEST_MAX_BIOS_PER_REQ * sizeof(int) * BIO_U32_SIZE))
/* this doesn't allow the test requests num to be greater than the maximum */
-#define LONG_TEST_ACTUAL_NUM_REQS \
- ((TEST_MAX_REQUESTS < LONG_TEST_MAX_NUM_REQS) ? \
- TEST_MAX_REQUESTS : LONG_TEST_MAX_NUM_REQS)
+#define LONG_READ_TEST_ACTUAL_NUM_REQS \
+ ((TEST_MAX_REQUESTS < LONG_READ_TEST_MAX_NUM_REQS) ? \
+ TEST_MAX_REQUESTS : LONG_READ_TEST_MAX_NUM_REQS)
#define MB_MSEC_RATIO_APPROXIMATION ((1024 * 1024) / 1000)
/* actual number of bytes in test */
-#define LONG_TEST_ACTUAL_BYTE_NUM (LONG_TEST_ACTUAL_NUM_REQS * \
+#define LONG_READ_NUM_BYTES (LONG_READ_TEST_ACTUAL_NUM_REQS * \
(TEST_MAX_BIOS_PER_REQ * sizeof(int) * BIO_U32_SIZE))
/* actual number of MiB in test multiplied by 10, for single digit precision*/
-#define LONG_TEST_ACTUAL_MB_NUM_X_10 ((LONG_TEST_ACTUAL_BYTE_NUM * 10) / \
- (1024 * 1024))
+#define BYTE_TO_MB_x_10(x) ((x * 10) / (1024 * 1024))
/* extract integer value */
-#define LONG_TEST_SIZE_INTEGER (LONG_TEST_ACTUAL_MB_NUM_X_10 / 10)
+#define LONG_TEST_SIZE_INTEGER(x) (BYTE_TO_MB_x_10(x) / 10)
/* and calculate the MiB value fraction */
-#define LONG_TEST_SIZE_FRACTION (LONG_TEST_ACTUAL_MB_NUM_X_10 - \
- (LONG_TEST_SIZE_INTEGER * 10))
+#define LONG_TEST_SIZE_FRACTION(x) (BYTE_TO_MB_x_10(x) - \
+ (LONG_TEST_SIZE_INTEGER(x) * 10))
+#define LONG_WRITE_TEST_SLEEP_TIME_MS 5
#define test_pr_debug(fmt, args...) pr_debug("%s: "fmt"\n", MODULE_NAME, args)
#define test_pr_info(fmt, args...) pr_info("%s: "fmt"\n", MODULE_NAME, args)
@@ -222,8 +224,8 @@
enum bkops_test_stages bkops_stage;
/* A wait queue for BKOPs tests */
wait_queue_head_t bkops_wait_q;
-
- unsigned int completed_req_count;
+ /* A counter for the number of test requests completed */
+ unsigned int completed_req_count;
};
static struct mmc_block_test_data *mbtd;
@@ -285,50 +287,6 @@
spin_unlock(&card->wr_pack_stats.lock);
}
-/**
- * mmc_print_async_event_stats() - Print async event statistics
- * @card: The mmc_card in which the async_event_stats
- * struct is a member
- */
-void mmc_print_async_event_stats(struct mmc_card *card)
-{
- struct mmc_async_event_stats *s;
-
- if (!card)
- return;
-
- s = &card->async_event_stats;
- if (!s)
- return;
-
- pr_info("%s: new notification & req statistics:\n",
- mmc_hostname(card->host));
- pr_info("%s: done_flag:%d", mmc_hostname(card->host),
- s->done_flag);
- pr_info("%s: cmd_retry:%d", mmc_hostname(card->host),
- s->cmd_retry);
- pr_info("%s: NULL fetched:%d", mmc_hostname(card->host),
- s->null_fetched);
- pr_info("%s: wake up new:%d", mmc_hostname(card->host),
- s->wakeup_new);
- pr_info("%s: new_request_flag:%d", mmc_hostname(card->host),
- s->new_request_flag);
- pr_info("%s: no waiting:%d\n", mmc_hostname(card->host),
- s->q_no_waiting);
- pr_info("%s: no_mmc_request_action:%d", mmc_hostname(card->host),
- s->no_mmc_request_action);
- pr_info("%s: wakeup_mq_thread:%d", mmc_hostname(card->host),
- s->wakeup_mq_thread);
- pr_info("%s: fetch_due_to_new_req:%d", mmc_hostname(card->host),
- s->fetch_due_to_new_req);
- pr_info("%s: returned_new_req:%d", mmc_hostname(card->host),
- s->returned_new_req);
- pr_info("%s: done_when_new_req_event_on:%d", mmc_hostname(card->host),
- s->done_when_new_req_event_on);
- pr_info("%s: new_req_when_new_marked:%d", mmc_hostname(card->host),
- s->new_req_when_new_marked);
-}
-
/*
* A callback assigned to the packed_test_fn field.
* Called from block layer in mmc_blk_packed_hdr_wrq_prep.
@@ -1024,8 +982,6 @@
__func__, mbtd->random_test_seed);
}
- mmc_blk_init_packed_statistics(mq->card);
-
ret = prepare_request_add_write_reqs(td, num_requests, is_err_expected,
is_random);
if (ret)
@@ -1121,8 +1077,6 @@
__func__, mbtd->random_test_seed);
}
- mmc_blk_init_packed_statistics(mq->card);
-
if (td->test_info.testcase ==
TEST_PACK_MIX_NO_PACKED_PACKED_NO_PACKED) {
temp_num_req = num_requests;
@@ -1277,8 +1231,6 @@
max_packed_reqs = mq->card->ext_csd.max_packed_writes;
- mmc_blk_init_packed_statistics(mq->card);
-
for (i = 1; i <= num_requests; i++) {
if (i > (num_requests / 2))
is_err_expected = 1;
@@ -1388,13 +1340,12 @@
return num_requests;
}
-static int prepare_long_test_requests(struct test_data *td)
+static int prepare_long_read_test_requests(struct test_data *td)
{
int ret;
int start_sec;
int j;
- int test_direction;
if (td)
start_sec = td->start_sector;
@@ -1403,23 +1354,18 @@
return -EINVAL;
}
- if (td->test_info.testcase == TEST_LONG_SEQUENTIAL_WRITE)
- test_direction = WRITE;
- else
- test_direction = READ;
+ test_pr_info("%s: Adding %d read requests, first req_id=%d", __func__,
+ LONG_READ_TEST_ACTUAL_NUM_REQS, td->wr_rd_next_req_id);
- test_pr_info("%s: Adding %d write requests, first req_id=%d", __func__,
- LONG_TEST_ACTUAL_NUM_REQS, td->wr_rd_next_req_id);
+ for (j = 0; j < LONG_READ_TEST_ACTUAL_NUM_REQS; j++) {
- for (j = 0; j < LONG_TEST_ACTUAL_NUM_REQS; j++) {
-
- ret = test_iosched_add_wr_rd_test_req(0, test_direction,
+ ret = test_iosched_add_wr_rd_test_req(0, READ,
start_sec,
TEST_MAX_BIOS_PER_REQ,
TEST_NO_PATTERN, NULL);
if (ret) {
- test_pr_err("%s: failed to add a bio request",
- __func__);
+ test_pr_err("%s: failed to add a read request, err = %d"
+ , __func__, ret);
return ret;
}
@@ -1539,10 +1485,8 @@
test_packed_trigger, is_random);
break;
case TEST_LONG_SEQUENTIAL_WRITE:
- ret = prepare_long_test_requests(td);
- break;
case TEST_LONG_SEQUENTIAL_READ:
- ret = prepare_long_test_requests(td);
+ ret = prepare_long_read_test_requests(td);
break;
default:
test_pr_info("%s: Invalid test case...", __func__);
@@ -1552,6 +1496,44 @@
return ret;
}
+static int run_packed_test(struct test_data *td)
+{
+ struct mmc_queue *mq;
+ struct request_queue *req_q;
+
+ if (!td) {
+ pr_err("%s: NULL td", __func__);
+ return -EINVAL;
+ }
+
+ req_q = td->req_q;
+
+ if (!req_q) {
+ pr_err("%s: NULL request queue", __func__);
+ return -EINVAL;
+ }
+
+ mq = req_q->queuedata;
+ if (!mq) {
+ test_pr_err("%s: NULL mq", __func__);
+ return -EINVAL;
+ }
+ mmc_blk_init_packed_statistics(mq->card);
+
+ if (td->test_info.testcase != TEST_PACK_MIX_PACKED_NO_PACKED_PACKED) {
+ /*
+ * Verify that the packing is disabled before starting the
+ * test
+ */
+ mq->wr_packing_enabled = false;
+ mq->num_of_potential_packed_wr_reqs = 0;
+ }
+
+ __blk_run_queue(td->req_q);
+
+ return 0;
+}
+
/*
* An implementation for the post_test_fn in the test_info data structure.
* In our case we just reset the function pointers in the mmc_queue in order for
@@ -2097,9 +2079,6 @@
if (!mq || !mq->card)
goto exit;
- /* disable async_event test stats */
- mq->card->async_event_stats.enabled = false;
- mmc_print_async_event_stats(mq->card);
test_pr_info("Completed %d requests",
mbtd->completed_req_count);
@@ -2153,14 +2132,12 @@
struct mmc_queue *mq = (struct mmc_queue *)q->queuedata;
mmc_blk_init_packed_statistics(mq->card);
- mmc_blk_init_async_event_statistics(mq->card);
-
mbtd->completed_req_count = 0;
return 0;
}
-static int test_new_req_notification(struct test_data *ptd)
+static int run_new_req(struct test_data *ptd)
{
int ret = 0;
int i;
@@ -2230,18 +2207,6 @@
return ret;
}
-static int run_new_req(struct test_data *td)
-{
- int ret = 0;
- struct request_queue *q = td->req_q;
- struct mmc_queue *mq = (struct mmc_queue *)q->queuedata;
-
- mmc_blk_init_async_event_statistics(mq->card);
- ret = test_new_req_notification(td);
-
- return ret;
-}
-
static bool message_repeat;
static int test_open(struct inode *inode, struct file *file)
{
@@ -2282,6 +2247,7 @@
mbtd->test_info.data = mbtd;
mbtd->test_info.prepare_test_fn = prepare_test;
+ mbtd->test_info.run_test_fn = run_packed_test;
mbtd->test_info.check_test_result_fn = check_wr_packing_statistics;
mbtd->test_info.get_test_case_str_fn = get_test_case_str;
mbtd->test_info.post_test_fn = post_test;
@@ -2380,6 +2346,7 @@
mbtd->test_info.data = mbtd;
mbtd->test_info.prepare_test_fn = prepare_test;
+ mbtd->test_info.run_test_fn = run_packed_test;
mbtd->test_info.check_test_result_fn = check_wr_packing_statistics;
mbtd->test_info.get_test_case_str_fn = get_test_case_str;
mbtd->test_info.post_test_fn = post_test;
@@ -2479,6 +2446,7 @@
mbtd->test_info.data = mbtd;
mbtd->test_info.prepare_test_fn = prepare_test;
+ mbtd->test_info.run_test_fn = run_packed_test;
mbtd->test_info.check_test_result_fn = check_wr_packing_statistics;
mbtd->test_info.get_test_case_str_fn = get_test_case_str;
mbtd->test_info.post_test_fn = post_test;
@@ -2591,6 +2559,7 @@
mbtd->test_info.data = mbtd;
mbtd->test_info.prepare_test_fn = prepare_test;
+ mbtd->test_info.run_test_fn = run_packed_test;
mbtd->test_info.check_test_result_fn = check_wr_packing_statistics;
mbtd->test_info.get_test_case_str_fn = get_test_case_str;
@@ -2792,7 +2761,7 @@
int ret = 0;
int i = 0;
int number = -1;
- unsigned int mtime, integer, fraction;
+ unsigned long mtime, integer, fraction;
test_pr_info("%s: -- Long Sequential Read TEST --", __func__);
@@ -2820,21 +2789,22 @@
mtime = jiffies_to_msecs(mbtd->test_info.test_duration);
- test_pr_info("%s: time is %u msec, size is %u.%u MiB",
- __func__, mtime, LONG_TEST_SIZE_INTEGER,
- LONG_TEST_SIZE_FRACTION);
+ test_pr_info("%s: time is %lu msec, size is %u.%u MiB",
+ __func__, mtime,
+ LONG_TEST_SIZE_INTEGER(LONG_READ_NUM_BYTES),
+ LONG_TEST_SIZE_FRACTION(LONG_READ_NUM_BYTES));
/* we first multiply in order not to lose precision */
mtime *= MB_MSEC_RATIO_APPROXIMATION;
/* divide values to get a MiB/sec integer value with one
digit of precision. Multiply by 10 for one digit precision
*/
- fraction = integer = (LONG_TEST_ACTUAL_BYTE_NUM * 10) / mtime;
+ fraction = integer = (LONG_READ_NUM_BYTES * 10) / mtime;
integer /= 10;
/* and calculate the MiB value fraction */
fraction -= integer * 10;
- test_pr_info("%s: Throughput: %u.%u MiB/sec\n"
+ test_pr_info("%s: Throughput: %lu.%lu MiB/sec\n"
, __func__, integer, fraction);
/* Allow FS requests to be dispatched */
@@ -2873,6 +2843,72 @@
.read = long_sequential_read_test_read,
};
+static void long_seq_write_free_end_io_fn(struct request *rq, int err)
+{
+ struct test_request *test_rq =
+ (struct test_request *)rq->elv.priv[0];
+ struct test_data *ptd = test_get_test_data();
+
+ BUG_ON(!test_rq);
+
+ spin_lock_irq(&ptd->lock);
+ list_del_init(&test_rq->queuelist);
+ ptd->dispatched_count--;
+ __blk_put_request(ptd->req_q, test_rq->rq);
+ spin_unlock_irq(&ptd->lock);
+
+ kfree(test_rq->bios_buffer);
+ kfree(test_rq);
+ mbtd->completed_req_count++;
+
+ check_test_completion();
+}
+
+static int run_long_seq_write(struct test_data *td)
+{
+ int ret = 0;
+ int i;
+
+ td->test_count = 0;
+ mbtd->completed_req_count = 0;
+
+ test_pr_info("%s: Adding at least %d write requests, first req_id=%d",
+ __func__, LONG_WRITE_TEST_MIN_NUM_REQS,
+ td->wr_rd_next_req_id);
+
+ do {
+ for (i = 0; i < TEST_MAX_REQUESTS; i++) {
+ /*
+ * since our requests come from a pool containing 128
+ * requests, we don't want to exhaust this quantity,
+ * therefore we add up to TEST_MAX_REQUESTS (which
+ * includes a safety margin) and then call the mmc layer
+ * to fetch them
+ */
+ if (td->test_count > TEST_MAX_REQUESTS)
+ break;
+
+ ret = test_iosched_add_wr_rd_test_req(0, WRITE,
+ td->start_sector, TEST_MAX_BIOS_PER_REQ,
+ TEST_PATTERN_5A,
+ long_seq_write_free_end_io_fn);
+ if (ret) {
+ test_pr_err("%s: failed to create write request"
+ , __func__);
+ break;
+ }
+ }
+
+ __blk_run_queue(td->req_q);
+
+ } while (mbtd->completed_req_count < LONG_WRITE_TEST_MIN_NUM_REQS);
+
+ test_pr_info("%s: completed %d requests", __func__,
+ mbtd->completed_req_count);
+
+ return ret;
+}
+
static ssize_t long_sequential_write_test_write(struct file *file,
const char __user *buf,
size_t count,
@@ -2881,7 +2917,7 @@
int ret = 0;
int i = 0;
int number = -1;
- unsigned int mtime, integer, fraction;
+ unsigned long mtime, integer, fraction, byte_count;
test_pr_info("%s: -- Long Sequential Write TEST --", __func__);
@@ -2894,13 +2930,16 @@
mbtd->test_group = TEST_GENERAL_GROUP;
mbtd->test_info.data = mbtd;
- mbtd->test_info.prepare_test_fn = prepare_test;
mbtd->test_info.get_test_case_str_fn = get_test_case_str;
+ mbtd->test_info.run_test_fn = run_long_seq_write;
for (i = 0 ; i < number ; ++i) {
test_pr_info("%s: Cycle # %d / %d", __func__, i+1, number);
test_pr_info("%s: ====================", __func__);
+ integer = 0;
+ fraction = 0;
+ mbtd->test_info.test_byte_count = 0;
mbtd->test_info.testcase = TEST_LONG_SEQUENTIAL_WRITE;
mbtd->is_random = NON_RANDOM_TEST;
ret = test_iosched_start_test(&mbtd->test_info);
@@ -2908,22 +2947,23 @@
break;
mtime = jiffies_to_msecs(mbtd->test_info.test_duration);
+ byte_count = mbtd->test_info.test_byte_count;
- test_pr_info("%s: time is %u msec, size is %u.%u MiB",
- __func__, mtime, LONG_TEST_SIZE_INTEGER,
- LONG_TEST_SIZE_FRACTION);
+ test_pr_info("%s: time is %lu msec, size is %lu.%lu MiB",
+ __func__, mtime, LONG_TEST_SIZE_INTEGER(byte_count),
+ LONG_TEST_SIZE_FRACTION(byte_count));
/* we first multiply in order not to lose precision */
mtime *= MB_MSEC_RATIO_APPROXIMATION;
/* divide values to get a MiB/sec integer value with one
digit of precision
*/
- fraction = integer = (LONG_TEST_ACTUAL_BYTE_NUM * 10) / mtime;
+ fraction = integer = (byte_count * 10) / mtime;
integer /= 10;
/* and calculate the MiB value fraction */
fraction -= integer * 10;
- test_pr_info("%s: Throughput: %u.%u MiB/sec\n",
+ test_pr_info("%s: Throughput: %lu.%lu MiB/sec\n",
__func__, integer, fraction);
/* Allow FS requests to be dispatched */
diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
index 9babeab..c4b2d16 100644
--- a/drivers/mmc/card/queue.c
+++ b/drivers/mmc/card/queue.c
@@ -59,19 +59,12 @@
struct request_queue *q = mq->queue;
struct request *req;
struct mmc_card *card = mq->card;
- struct mmc_async_event_stats *stats;
- struct mmc_queue_req *tmp;
-
- if (!card)
- return 0;
-
- stats = &mq->card->async_event_stats;
current->flags |= PF_MEMALLOC;
down(&mq->thread_sem);
do {
-
+ struct mmc_queue_req *tmp;
req = NULL; /* Must be set to NULL at each iteration */
spin_lock_irq(q->queue_lock);
@@ -85,8 +78,6 @@
mq->issue_fn(mq, req);
if (mq->flags & MMC_QUEUE_NEW_REQUEST) {
mq->flags &= ~MMC_QUEUE_NEW_REQUEST;
- if (stats && stats->enabled)
- stats->fetch_due_to_new_req++;
continue; /* fetch again */
}
@@ -124,7 +115,6 @@
static void mmc_request(struct request_queue *q)
{
struct mmc_queue *mq = q->queuedata;
- struct mmc_async_event_stats *stats;
struct request *req;
unsigned long flags;
struct mmc_context_info *cntx;
@@ -136,39 +126,22 @@
}
return;
}
- if (mq->card) {
- cntx = &mq->card->host->context_info;
- stats = &mq->card->async_event_stats;
- } else
- return;
cntx = &mq->card->host->context_info;
- stats = &mq->card->async_event_stats;
if (!mq->mqrq_cur->req && mq->mqrq_prev->req) {
/*
* New MMC request arrived when MMC thread may be
* blocked on the previous request to be complete
* with no current request fetched
*/
-
spin_lock_irqsave(&cntx->lock, flags);
if (cntx->is_waiting_last_req) {
- if (stats && stats->enabled)
- stats->wakeup_new++;
- if (cntx->is_new_req)
- if (stats->enabled)
- stats->new_req_when_new_marked++;
cntx->is_new_req = true;
wake_up_interruptible(&cntx->wait);
- } else if (stats->enabled)
- stats->q_no_waiting++;
+ }
spin_unlock_irqrestore(&cntx->lock, flags);
- } else if (!mq->mqrq_cur->req && !mq->mqrq_prev->req) {
+ } else if (!mq->mqrq_cur->req && !mq->mqrq_prev->req)
wake_up_process(mq->thread);
- if (stats->enabled)
- stats->wakeup_mq_thread++;
- } else if (stats->enabled)
- stats->no_mmc_request_action++;
}
static struct scatterlist *mmc_alloc_sg(int sg_len, int *err)
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 65f748f..c1de8e8 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -47,6 +47,9 @@
static void mmc_clk_scaling(struct mmc_host *host, bool from_wq);
+/* If the device is not responding */
+#define MMC_CORE_TIMEOUT_MS (10 * 60 * 1000) /* 10 minute timeout */
+
/*
* Background operations can take a long time, depending on the housekeeping
* operations the card has to perform.
@@ -560,6 +563,7 @@
mmc_start_bkops(card, false);
}
EXPORT_SYMBOL(mmc_start_idle_time_bkops);
+
/*
* mmc_wait_data_done() - done callback for data request
* @mrq: done data request
@@ -630,7 +634,6 @@
struct mmc_context_info *context_info = &host->context_info;
int err;
unsigned long flags;
- struct mmc_async_event_stats *stats = &host->card->async_event_stats;
while (1) {
wait_io_event_interruptible(context_info->wait,
@@ -643,18 +646,13 @@
context_info->is_done_rcv = false;
context_info->is_new_req = false;
cmd = mrq->cmd;
- if (stats->enabled) {
- stats->done_flag++;
- if (context_info->is_new_req)
- stats->done_when_new_req_event_on++;
- }
if (!cmd->error || !cmd->retries ||
mmc_card_removed(host->card)) {
err = host->areq->err_check(host->card,
host->areq);
break; /* return err */
} else {
- pr_info("%s: req failed (CMD%u):%d, retrying\n",
+ pr_info("%s: req failed (CMD%u): %d, retrying...\n",
mmc_hostname(host),
cmd->opcode, cmd->error);
cmd->retries--;
@@ -664,8 +662,6 @@
}
} else if (context_info->is_new_req) {
context_info->is_new_req = false;
- if (stats->enabled)
- stats->new_request_flag++;
if (!next_req) {
err = MMC_BLK_NEW_REQUEST;
break; /* return err */
@@ -854,6 +850,7 @@
{
int err;
u32 status;
+ unsigned long prg_wait;
BUG_ON(!card);
@@ -869,30 +866,39 @@
goto out;
}
- /*
- * If the card status is in PRG-state, we can send the HPI command.
- */
- if (R1_CURRENT_STATE(status) == R1_STATE_PRG) {
- do {
- /*
- * We don't know when the HPI command will finish
- * processing, so we need to resend HPI until out
- * of prg-state, and keep checking the card status
- * with SEND_STATUS. If a timeout error occurs when
- * sending the HPI command, we are already out of
- * prg-state.
- */
- err = mmc_send_hpi_cmd(card, &status);
- if (err)
- pr_debug("%s: abort HPI (%d error)\n",
- mmc_hostname(card->host), err);
+ switch (R1_CURRENT_STATE(status)) {
+ case R1_STATE_IDLE:
+ case R1_STATE_READY:
+ case R1_STATE_STBY:
+ case R1_STATE_TRAN:
+ /*
+ * In idle and transfer states, HPI is not needed and the caller
+ * can issue the next intended command immediately
+ */
+ goto out;
+ case R1_STATE_PRG:
+ break;
+ default:
+ /* In all other states, it's illegal to issue HPI */
+ pr_debug("%s: HPI cannot be sent. Card state=%d\n",
+ mmc_hostname(card->host), R1_CURRENT_STATE(status));
+ err = -EINVAL;
+ goto out;
+ }
- err = mmc_send_status(card, &status);
- if (err)
- break;
- } while (R1_CURRENT_STATE(status) == R1_STATE_PRG);
- } else
- pr_debug("%s: Left prg-state\n", mmc_hostname(card->host));
+ err = mmc_send_hpi_cmd(card, &status);
+ if (err)
+ goto out;
+
+ prg_wait = jiffies + msecs_to_jiffies(card->ext_csd.out_of_int_time);
+ do {
+ err = mmc_send_status(card, &status);
+
+ if (!err && R1_CURRENT_STATE(status) == R1_STATE_TRAN)
+ break;
+ if (time_after(jiffies, prg_wait))
+ err = -ETIMEDOUT;
+ } while (!err);
out:
mmc_release_host(card->host);
@@ -1862,7 +1868,6 @@
#endif
host->detect_change = 1;
- wake_lock(&host->detect_wake_lock);
mmc_schedule_delayed_work(&host->detect, delay);
}
@@ -2021,6 +2026,7 @@
{
struct mmc_command cmd = {0};
unsigned int qty = 0;
+ unsigned long timeout;
int err;
/*
@@ -2098,6 +2104,7 @@
if (mmc_host_is_spi(card->host))
goto out;
+ timeout = jiffies + msecs_to_jiffies(MMC_CORE_TIMEOUT_MS);
do {
memset(&cmd, 0, sizeof(struct mmc_command));
cmd.opcode = MMC_SEND_STATUS;
@@ -2111,8 +2118,19 @@
err = -EIO;
goto out;
}
+
+ /* Timeout if the device never becomes ready for data and
+ * never leaves the program state.
+ */
+ if (time_after(jiffies, timeout)) {
+ pr_err("%s: Card stuck in programming state! %s\n",
+ mmc_hostname(card->host), __func__);
+ err = -EIO;
+ goto out;
+ }
+
} while (!(cmd.resp[0] & R1_READY_FOR_DATA) ||
- R1_CURRENT_STATE(cmd.resp[0]) == R1_STATE_PRG);
+ (R1_CURRENT_STATE(cmd.resp[0]) == R1_STATE_PRG));
out:
return err;
}
@@ -2888,12 +2906,9 @@
out:
if (extend_wakelock)
wake_lock_timeout(&host->detect_wake_lock, HZ / 2);
- else
- wake_unlock(&host->detect_wake_lock);
- if (host->caps & MMC_CAP_NEEDS_POLL) {
- wake_lock(&host->detect_wake_lock);
+
+ if (host->caps & MMC_CAP_NEEDS_POLL)
mmc_schedule_delayed_work(&host->detect, HZ);
- }
}
void mmc_start_host(struct mmc_host *host)
@@ -2911,8 +2926,8 @@
spin_unlock_irqrestore(&host->lock, flags);
#endif
- if (cancel_delayed_work_sync(&host->detect))
- wake_unlock(&host->detect_wake_lock);
+ cancel_delayed_work_sync(&host->detect);
+
mmc_flush_scheduled_work();
/* clear pm flags now and let card drivers set them as needed */
@@ -3110,8 +3125,7 @@
if (mmc_bus_needs_resume(host))
return 0;
- if (cancel_delayed_work(&host->detect))
- wake_unlock(&host->detect_wake_lock);
+ cancel_delayed_work(&host->detect);
mmc_flush_scheduled_work();
mmc_bus_get(host);
@@ -3258,10 +3272,22 @@
spin_unlock_irqrestore(&host->lock, flags);
break;
}
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ /* Wait for pending detect work to be completed */
+ if (!(host->caps & MMC_CAP_NEEDS_POLL))
+ flush_work(&host->detect.work);
+
+ spin_lock_irqsave(&host->lock, flags);
host->rescan_disable = 1;
spin_unlock_irqrestore(&host->lock, flags);
- if (cancel_delayed_work_sync(&host->detect))
- wake_unlock(&host->detect_wake_lock);
+
+ /*
+ * In some cases, the detect work might be scheduled
+ * just before rescan_disable is set to true.
+ * Cancel such the scheduled works.
+ */
+ cancel_delayed_work_sync(&host->detect);
if (!host->bus_ops || host->bus_ops->suspend)
break;
@@ -3289,7 +3315,10 @@
host->rescan_disable = 0;
spin_unlock_irqrestore(&host->lock, flags);
mmc_detect_change(host, 0);
+ break;
+ default:
+ return -EINVAL;
}
return 0;
@@ -3312,6 +3341,14 @@
EXPORT_SYMBOL(mmc_set_embedded_sdio_data);
#endif
+/**
+ * mmc_init_context_info() - init synchronization context
+ * @host: mmc host
+ *
+ * Init struct context_info needed to implement asynchronous
+ * request mechanism, used by mmc core, host driver and mmc requests
+ * supplier.
+ */
void mmc_init_context_info(struct mmc_host *host)
{
spin_lock_init(&host->context_info.lock);
diff --git a/drivers/mmc/core/debugfs.c b/drivers/mmc/core/debugfs.c
index d91bc31..931ddb0 100644
--- a/drivers/mmc/core/debugfs.c
+++ b/drivers/mmc/core/debugfs.c
@@ -535,141 +535,6 @@
.write = mmc_wr_pack_stats_write,
};
-static int mmc_new_req_stats_open(struct inode *inode, struct file *filp)
-{
- struct mmc_card *card = inode->i_private;
-
- filp->private_data = card;
- card->async_event_stats.print_in_read = 1;
- return 0;
-}
-
-static ssize_t mmc_new_req_stats_read(struct file *filp, char __user *ubuf,
- size_t cnt, loff_t *ppos)
-{
- struct mmc_card *card = filp->private_data;
- struct mmc_async_event_stats *s;
- char *temp_buf;
-
- if (!card)
- return cnt;
-
- s = &card->async_event_stats;
-
- if (!card->async_event_stats.enabled) {
- pr_info("%s: New Request statistics are disabled\n",
- mmc_hostname(card->host));
- goto exit;
- }
-
- temp_buf = kmalloc(2 * TEMP_BUF_SIZE, GFP_KERNEL);
- if (!temp_buf)
- goto exit;
-
- memset(ubuf, 0, cnt);
- memset(temp_buf, 0, 2 * TEMP_BUF_SIZE);
-
- snprintf(temp_buf, TEMP_BUF_SIZE,
- "%s: new notification & req statistics:\n",
- mmc_hostname(card->host));
- strlcat(ubuf, temp_buf, cnt);
-
- snprintf(temp_buf, TEMP_BUF_SIZE,
- "%s: done_flag:%d\n", mmc_hostname(card->host), s->done_flag);
- strlcat(ubuf, temp_buf, cnt);
-
- snprintf(temp_buf, TEMP_BUF_SIZE,
- "%s: cmd_retry:%d\n", mmc_hostname(card->host), s->cmd_retry);
- strlcat(ubuf, temp_buf, cnt);
-
- snprintf(temp_buf, TEMP_BUF_SIZE,
- "%s: NULL fetched:%d\n", mmc_hostname(card->host),
- s->null_fetched);
- strlcat(ubuf, temp_buf, cnt);
-
- snprintf(temp_buf, TEMP_BUF_SIZE,
- "%s: wake up new:%d\n",
- mmc_hostname(card->host), s->wakeup_new);
- strlcat(ubuf, temp_buf, cnt);
-
- snprintf(temp_buf, TEMP_BUF_SIZE,
- "%s: new_request_flag:%d\n", mmc_hostname(card->host),
- s->new_request_flag);
- strlcat(ubuf, temp_buf, cnt);
-
- snprintf(temp_buf, TEMP_BUF_SIZE,
- "%s: no waiting:%d\n", mmc_hostname(card->host),
- s->q_no_waiting);
- strlcat(ubuf, temp_buf, cnt);
-
- snprintf(temp_buf, TEMP_BUF_SIZE,
- "%s: no_mmc_request_action:%d\n", mmc_hostname(card->host),
- s->no_mmc_request_action);
- strlcat(ubuf, temp_buf, cnt);
-
- snprintf(temp_buf, TEMP_BUF_SIZE,
- "%s: wakeup_mq_thread:%d\n", mmc_hostname(card->host),
- s->wakeup_mq_thread);
- strlcat(ubuf, temp_buf, cnt);
-
- snprintf(temp_buf, TEMP_BUF_SIZE,
- "%s: fetch_due_to_new_req:%d\n", mmc_hostname(card->host),
- s->fetch_due_to_new_req);
- strlcat(ubuf, temp_buf, cnt);
-
- snprintf(temp_buf, TEMP_BUF_SIZE,
- "%s: returned_new_req:%d\n", mmc_hostname(card->host),
- s->returned_new_req);
- strlcat(ubuf, temp_buf, cnt);
-
- snprintf(temp_buf, TEMP_BUF_SIZE,
- "%s: done_when_new_req_event_on:%d\n",
- mmc_hostname(card->host), s->done_when_new_req_event_on);
- strlcat(ubuf, temp_buf, cnt);
-
- kfree(temp_buf);
-
- pr_info("%s", ubuf);
-
-exit:
- if (card->async_event_stats.print_in_read == 1) {
- card->async_event_stats.print_in_read = 0;
- return strnlen(ubuf, cnt);
- }
-
- return 0;
-}
-
-static ssize_t mmc_new_req_stats_write(struct file *filp,
- const char __user *ubuf, size_t cnt,
- loff_t *ppos)
-{
- struct mmc_card *card = filp->private_data;
- int value;
-
- if (!card)
- return cnt;
-
- sscanf(ubuf, "%d", &value);
- if (value) {
- mmc_blk_init_async_event_statistics(card);
- pr_info("%s: %s: New request statistics are enabled",
- mmc_hostname(card->host), __func__);
- } else {
- card->async_event_stats.enabled = false;
- pr_info("%s: %s: New request statistics are disabled",
- mmc_hostname(card->host), __func__);
- }
-
- return cnt;
-}
-
-static const struct file_operations mmc_dbg_new_req_stats_fops = {
- .open = mmc_new_req_stats_open,
- .read = mmc_new_req_stats_read,
- .write = mmc_new_req_stats_write,
-};
-
static int mmc_bkops_stats_open(struct inode *inode, struct file *filp)
{
struct mmc_card *card = inode->i_private;
@@ -815,10 +680,6 @@
&mmc_dbg_wr_pack_stats_fops))
goto err;
- if (!debugfs_create_file("new_req_stats", S_IRUSR, root, card,
- &mmc_dbg_new_req_stats_fops))
- goto err;
-
if (mmc_card_mmc(card) && (card->ext_csd.rev >= 5) &&
card->ext_csd.bkops_en)
if (!debugfs_create_file("bkops_stats", S_IRUSR, root, card,
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index ec30cad..a9f7819 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -1086,6 +1086,8 @@
card->ext_csd.part_time);
if (err && err != -EBADMSG)
goto free_card;
+ card->part_curr = card->ext_csd.part_config &
+ EXT_CSD_PART_CONFIG_ACC_MASK;
}
/*
diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c
index f8c9720..8087ea6 100644
--- a/drivers/mmc/core/mmc_ops.c
+++ b/drivers/mmc/core/mmc_ops.c
@@ -21,6 +21,8 @@
#include "core.h"
#include "mmc_ops.h"
+#define MMC_OPS_TIMEOUT_MS (10 * 60 * 1000) /* 10 minute timeout */
+
static int _mmc_select_card(struct mmc_host *host, struct mmc_card *card)
{
int err;
@@ -386,6 +388,7 @@
{
int err;
struct mmc_command cmd = {0};
+ unsigned long timeout;
u32 status;
BUG_ON(!card);
@@ -415,6 +418,7 @@
return 0;
/* Must check status to be sure of no errors */
+ timeout = jiffies + msecs_to_jiffies(MMC_OPS_TIMEOUT_MS);
do {
err = mmc_send_status(card, &status);
if (err)
@@ -423,6 +427,13 @@
break;
if (mmc_host_is_spi(card->host))
break;
+
+ /* Timeout if the device never leaves the program state. */
+ if (time_after(jiffies, timeout)) {
+ pr_err("%s: Card stuck in programming state! %s\n",
+ mmc_hostname(card->host), __func__);
+ return -ETIMEDOUT;
+ }
} while (R1_CURRENT_STATE(status) == R1_STATE_PRG);
if (mmc_host_is_spi(card->host)) {
@@ -594,7 +605,6 @@
cmd.opcode = opcode;
cmd.arg = card->rca << 16 | 1;
- cmd.cmd_timeout_ms = card->ext_csd.out_of_int_time;
err = mmc_wait_for_cmd(card->host, &cmd, 0);
if (err) {
diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c
index 91720ee..2fedc97 100644
--- a/drivers/mmc/host/msm_sdcc.c
+++ b/drivers/mmc/host/msm_sdcc.c
@@ -3,7 +3,7 @@
*
* Copyright (C) 2007 Google Inc,
* Copyright (C) 2003 Deep Blue Solutions, Ltd, All Rights Reserved.
- * Copyright (c) 2009-2012, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2009-2013, The Linux Foundation. All rights reserved.
*
*
* This program is free software; you can redistribute it and/or modify
@@ -1965,7 +1965,7 @@
msmsdcc_do_cmdirq(host, status);
}
- if (host->curr.data) {
+ if (data) {
/* Check for data errors */
if (status & (MCI_DATACRCFAIL|MCI_DATATIMEOUT|
MCI_TXUNDERRUN|MCI_RXOVERRUN)) {
@@ -4969,9 +4969,6 @@
} else {
mmc->caps &= ~MMC_CAP_NEEDS_POLL;
}
-#ifdef CONFIG_HAS_EARLYSUSPEND
- host->polling_enabled = mmc->caps & MMC_CAP_NEEDS_POLL;
-#endif
spin_unlock_irqrestore(&host->lock, flags);
return count;
}
@@ -5091,33 +5088,6 @@
return count;
}
-#ifdef CONFIG_HAS_EARLYSUSPEND
-static void msmsdcc_early_suspend(struct early_suspend *h)
-{
- struct msmsdcc_host *host =
- container_of(h, struct msmsdcc_host, early_suspend);
- unsigned long flags;
-
- spin_lock_irqsave(&host->lock, flags);
- host->polling_enabled = host->mmc->caps & MMC_CAP_NEEDS_POLL;
- host->mmc->caps &= ~MMC_CAP_NEEDS_POLL;
- spin_unlock_irqrestore(&host->lock, flags);
-};
-static void msmsdcc_late_resume(struct early_suspend *h)
-{
- struct msmsdcc_host *host =
- container_of(h, struct msmsdcc_host, early_suspend);
- unsigned long flags;
-
- if (host->polling_enabled) {
- spin_lock_irqsave(&host->lock, flags);
- host->mmc->caps |= MMC_CAP_NEEDS_POLL;
- mmc_detect_change(host->mmc, 0);
- spin_unlock_irqrestore(&host->lock, flags);
- }
-};
-#endif
-
static void msmsdcc_print_regs(const char *name, void __iomem *base,
u32 phys_base, unsigned int no_of_regs)
{
@@ -6211,13 +6181,6 @@
mmc->clk_scaling.polling_delay_ms = 100;
mmc->caps2 |= MMC_CAP2_CLK_SCALE;
-#ifdef CONFIG_HAS_EARLYSUSPEND
- host->early_suspend.suspend = msmsdcc_early_suspend;
- host->early_suspend.resume = msmsdcc_late_resume;
- host->early_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB;
- register_early_suspend(&host->early_suspend);
-#endif
-
pr_info("%s: Qualcomm MSM SDCC-core at 0x%016llx irq %d,%d dma %d"
" dmacrcri %d\n", mmc_hostname(mmc),
(unsigned long long)core_memres->start,
@@ -6473,9 +6436,6 @@
iounmap(host->base);
mmc_free_host(mmc);
-#ifdef CONFIG_HAS_EARLYSUSPEND
- unregister_early_suspend(&host->early_suspend);
-#endif
pm_runtime_disable(&(pdev)->dev);
pm_runtime_set_suspended(&(pdev)->dev);
@@ -6764,9 +6724,21 @@
msmsdcc_disable_status_gpio(host);
}
- if (!pm_runtime_suspended(dev))
+ /*
+ * If system comes out of suspend, msmsdcc_pm_resume() sets the
+ * host->pending_resume flag if the SDCC wasn't runtime suspended.
+ * Now if the system again goes to suspend without any SDCC activity
+ * then host->pending_resume flag will remain set which may cause
+ * the SDCC resume to happen first and then suspend.
+ * To avoid this unnecessary resume/suspend, make sure that
+ * pending_resume flag is cleared before calling the
+ * msmsdcc_runtime_suspend().
+ */
+ if (!pm_runtime_suspended(dev) && !host->pending_resume)
rc = msmsdcc_runtime_suspend(dev);
out:
+ /* This flag must not be set if system is entering into suspend */
+ host->pending_resume = false;
msmsdcc_print_pm_stats(host, start, __func__, rc);
return rc;
}
diff --git a/drivers/mmc/host/msm_sdcc.h b/drivers/mmc/host/msm_sdcc.h
index 8ae5b86..877120c 100644
--- a/drivers/mmc/host/msm_sdcc.h
+++ b/drivers/mmc/host/msm_sdcc.h
@@ -2,7 +2,7 @@
* linux/drivers/mmc/host/msmsdcc.h - QCT MSM7K SDC Controller
*
* Copyright (C) 2008 Google, All Rights Reserved.
- * Copyright (c) 2009-2012, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2009-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 as
@@ -25,7 +25,6 @@
#include <linux/scatterlist.h>
#include <linux/dma-mapping.h>
#include <linux/wakelock.h>
-#include <linux/earlysuspend.h>
#include <linux/pm_qos.h>
#include <mach/sps.h>
@@ -381,11 +380,6 @@
struct msmsdcc_sps_data sps;
struct msmsdcc_pio_data pio;
-#ifdef CONFIG_HAS_EARLYSUSPEND
- struct early_suspend early_suspend;
- int polling_enabled;
-#endif
-
struct tasklet_struct dma_tlet;
unsigned int prog_enable;
diff --git a/drivers/net/ethernet/msm/msm_rmnet_bam.c b/drivers/net/ethernet/msm/msm_rmnet_bam.c
index 295c55c..83f486c 100644
--- a/drivers/net/ethernet/msm/msm_rmnet_bam.c
+++ b/drivers/net/ethernet/msm/msm_rmnet_bam.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, Code Aurora Forum. 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
@@ -746,6 +746,112 @@
return 0;
}
+/* support for 9 new rmnet ports */
+#define RMNET_REV_DEVICE_COUNT (9)
+static struct net_device *netdevs_rev[RMNET_REV_DEVICE_COUNT];
+static struct platform_driver bam_rmnet_rev_drivers[RMNET_REV_DEVICE_COUNT];
+
+static int bam_rmnet_rev_probe(struct platform_device *pdev)
+{
+ int i;
+ char name[BAM_DMUX_CH_NAME_MAX_LEN];
+ struct rmnet_private *p;
+
+ for (i = 0; i < RMNET_REV_DEVICE_COUNT; ++i) {
+ scnprintf(name, BAM_DMUX_CH_NAME_MAX_LEN, "bam_dmux_ch_%d",
+ (i+BAM_DMUX_DATA_REV_RMNET_0));
+ if (!strncmp(pdev->name, name, BAM_DMUX_CH_NAME_MAX_LEN))
+ break;
+ }
+
+ if (i >= RMNET_REV_DEVICE_COUNT) {
+ pr_err("%s: wrong netdev %s\n", __func__, pdev->name);
+ return 0;
+ }
+
+ p = netdev_priv(netdevs_rev[i]);
+ if (p->in_reset) {
+ p->in_reset = 0;
+ msm_bam_dmux_open(p->ch_id, netdevs_rev[i], bam_notify);
+ netif_carrier_on(netdevs_rev[i]);
+ netif_start_queue(netdevs_rev[i]);
+ }
+
+ return 0;
+}
+
+static int bam_rmnet_rev_remove(struct platform_device *pdev)
+{
+ int i;
+ char name[BAM_DMUX_CH_NAME_MAX_LEN];
+ struct rmnet_private *p;
+
+ for (i = 0; i < RMNET_REV_DEVICE_COUNT; ++i) {
+ scnprintf(name, BAM_DMUX_CH_NAME_MAX_LEN, "bam_dmux_ch_%d",
+ (i+BAM_DMUX_DATA_REV_RMNET_0));
+ if (!strncmp(pdev->name, name, BAM_DMUX_CH_NAME_MAX_LEN))
+ break;
+ }
+
+ if (i >= RMNET_REV_DEVICE_COUNT) {
+ pr_err("%s: wrong netdev %s\n", __func__, pdev->name);
+ return 0;
+ }
+
+ p = netdev_priv(netdevs_rev[i]);
+ p->in_reset = 1;
+ if (p->waiting_for_ul_skb != NULL) {
+ dev_kfree_skb_any(p->waiting_for_ul_skb);
+ p->waiting_for_ul_skb = NULL;
+ }
+ msm_bam_dmux_close(p->ch_id);
+ netif_carrier_off(netdevs_rev[i]);
+ netif_stop_queue(netdevs_rev[i]);
+ return 0;
+}
+
+#ifdef CONFIG_MSM_RMNET_DEBUG
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static int rmnet_debug_init_timeout_suspend(struct net_device *dev)
+{
+ struct device *d;
+ d = &(dev->dev);
+ return device_create_file(d, &dev_attr_timeout_suspend);
+}
+#else
+static int rmnet_debug_init_timeout_suspend(struct net_device *dev)
+{
+ return 0;
+}
+#endif
+static int rmnet_debug_init(struct net_device *dev)
+{
+
+ struct device *d;
+ struct rmnet_private *p;
+ int err = 0;
+ d = &(dev->dev);
+ p = netdev_priv(dev);
+ p->timeout_us = 0;
+ p->wakeups_xmit = p->wakeups_rcv = 0;
+ err = device_create_file(d, &dev_attr_timeout);
+ if (err)
+ return err;
+ err = device_create_file(d, &dev_attr_wakeups_xmit);
+ if (err)
+ return err;
+ err = device_create_file(d, &dev_attr_wakeups_rcv);
+ if (err)
+ return err;
+ err = rmnet_debug_init_timeout_suspend(dev);
+ return err;
+}
+#else
+static int rmnet_debug_init(struct net_device *dev)
+{
+ return 0;
+}
+#endif
static int __init rmnet_init(void)
{
int ret;
@@ -828,6 +934,53 @@
return ret;
}
}
+ /*Support for new rmnet ports */
+ for (n = 0; n < RMNET_REV_DEVICE_COUNT; n++) {
+ dev = alloc_netdev(sizeof(struct rmnet_private),
+ "rev_rmnet%d", rmnet_setup);
+
+ if (!dev) {
+ pr_err("%s: no memory for rev netdev %d\n",
+ __func__, n);
+ return -ENOMEM;
+ }
+
+ netdevs_rev[n] = dev;
+ d = &(dev->dev);
+ p = netdev_priv(dev);
+ /* Initial config uses Ethernet */
+ p->operation_mode = RMNET_MODE_LLP_ETH;
+ p->ch_id = n+BAM_DMUX_DATA_REV_RMNET_0;
+ p->waiting_for_ul_skb = NULL;
+ p->in_reset = 0;
+ spin_lock_init(&p->lock);
+ spin_lock_init(&p->tx_queue_lock);
+
+ ret = register_netdev(dev);
+ if (ret) {
+ pr_err("%s: unable to register rev netdev %d rc=%d\n",
+ __func__, n, ret);
+ free_netdev(dev);
+ return ret;
+ }
+ if (rmnet_debug_init(dev))
+ continue;
+ bam_rmnet_rev_drivers[n].probe = bam_rmnet_rev_probe;
+ bam_rmnet_rev_drivers[n].remove = bam_rmnet_rev_remove;
+ tempname = kmalloc(BAM_DMUX_CH_NAME_MAX_LEN, GFP_KERNEL);
+ if (tempname == NULL)
+ return -ENOMEM;
+ scnprintf(tempname, BAM_DMUX_CH_NAME_MAX_LEN, "bam_dmux_ch_%d",
+ (n+BAM_DMUX_DATA_REV_RMNET_0));
+ bam_rmnet_rev_drivers[n].driver.name = tempname;
+ bam_rmnet_rev_drivers[n].driver.owner = THIS_MODULE;
+ ret = platform_driver_register(&bam_rmnet_rev_drivers[n]);
+ if (ret) {
+ pr_err("%s: new rev driver registration failed n=%d rc=%d\n",
+ __func__, n, ret);
+ return ret;
+ }
+ }
return 0;
}
diff --git a/drivers/platform/msm/sps/sps_bam.c b/drivers/platform/msm/sps/sps_bam.c
index a1ff7cb..7716ccb 100644
--- a/drivers/platform/msm/sps/sps_bam.c
+++ b/drivers/platform/msm/sps/sps_bam.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, Code Aurora Forum. 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
@@ -1049,6 +1049,13 @@
else {
pipe->sys.desc_cache =
vmalloc(pipe->desc_size + size);
+
+ if (pipe->sys.desc_cache == NULL) {
+ SPS_ERR("sps:No memory for pipe %d of BAM 0x%x",
+ pipe_index, BAM_ID(dev));
+ return -ENOMEM;
+ }
+
memset(pipe->sys.desc_cache, 0, pipe->desc_size + size);
}
diff --git a/drivers/platform/msm/usb_bam.c b/drivers/platform/msm/usb_bam.c
index a808e0b..233ea99 100644
--- a/drivers/platform/msm/usb_bam.c
+++ b/drivers/platform/msm/usb_bam.c
@@ -943,6 +943,9 @@
pdata->ignore_core_reset_ack = of_property_read_bool(node,
"qcom,ignore-core-reset-ack");
+ pdata->disable_clk_gating = of_property_read_bool(node,
+ "qcom,disable-clk-gating");
+
for_each_child_of_node(pdev->dev.of_node, node)
pipe_entry++;
@@ -1103,6 +1106,8 @@
*/
if (pdata->ignore_core_reset_ack && pdata->usb_active_bam != SSUSB_BAM)
usb_props.options = SPS_BAM_NO_EXT_P_RST;
+ if (pdata->disable_clk_gating)
+ usb_props.options |= SPS_BAM_NO_LOCAL_CLK_GATING;
ret = sps_register_bam_device(&usb_props, &h_bam);
if (ret < 0) {
diff --git a/drivers/power/bq28400_battery.c b/drivers/power/bq28400_battery.c
index 1852687..beab4e2 100644
--- a/drivers/power/bq28400_battery.c
+++ b/drivers/power/bq28400_battery.c
@@ -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
@@ -136,6 +136,8 @@
struct power_supply batt_psy;
struct power_supply *dc_psy;
bool is_charging_enabled;
+ u32 temp_cold; /* in degree celsius */
+ u32 temp_hot; /* in degree celsius */
};
static struct bq28400_device *bq28400_dev;
@@ -486,10 +488,14 @@
int rsoc;
s16 current_ma = 0;
u16 battery_status;
+ int temperature;
+ struct bq28400_device *dev = i2c_get_clientdata(client);
battery_status = bq28400_read_reg(client, SBS_BATTERY_STATUS);
rsoc = bq28400_read_rsoc(client);
current_ma = bq28400_read_current(client);
+ temperature = bq28400_read_temperature(client);
+ temperature = temperature / 10; /* in degree celsius */
if (battery_status & BAT_STATUS_EMPTY)
pr_debug("Battery report Empty.\n");
@@ -513,8 +519,11 @@
return POWER_SUPPLY_STATUS_FULL;
}
- /* Enable charging when battery is not full */
- bq28400_enable_charging(bq28400_dev, true);
+ /* Enable charging when battery is not full and temperature is ok */
+ if ((temperature > dev->temp_cold) && (temperature < dev->temp_hot))
+ bq28400_enable_charging(bq28400_dev, true);
+ else
+ bq28400_enable_charging(bq28400_dev, false);
/*
* Positive current indicates charging
@@ -825,6 +834,12 @@
const struct i2c_device_id *id)
{
int ret = 0;
+ struct device_node *dev_node = client->dev.of_node;
+
+ if (dev_node == NULL) {
+ pr_err("Device Tree node doesn't exist.\n");
+ return -ENODEV;
+ }
if (!i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_BYTE_DATA)) {
@@ -843,6 +858,23 @@
return -ENOMEM;
}
+ /* Note: Lithium-ion battery normal temperature range 0..40 C */
+ ret = of_property_read_u32(dev_node, "ti,temp-cold",
+ &(bq28400_dev->temp_cold));
+ if (ret) {
+ pr_err("Unable to read cold temperature. ret=%d.\n", ret);
+ goto err_dev_node;
+ }
+ pr_debug("cold temperature limit = %d C.\n", bq28400_dev->temp_cold);
+
+ ret = of_property_read_u32(dev_node, "ti,temp-hot",
+ &(bq28400_dev->temp_hot));
+ if (ret) {
+ pr_err("Unable to read hot temperature. ret=%d.\n", ret);
+ goto err_dev_node;
+ }
+ pr_debug("hot temperature limit = %d C.\n", bq28400_dev->temp_hot);
+
bq28400_dev->client = client;
i2c_set_clientdata(client, bq28400_dev);
@@ -864,7 +896,7 @@
schedule_delayed_work(&bq28400_dev->periodic_user_space_update_work,
msecs_to_jiffies(1000));
- pr_info("Device is ready.\n");
+ pr_debug("Device is ready.\n");
return 0;
@@ -873,6 +905,7 @@
debugfs_remove_recursive(bq28400_dev->dent);
power_supply_unregister(&bq28400_dev->batt_psy);
err_register_psy:
+err_dev_node:
kfree(bq28400_dev);
bq28400_dev = NULL;
diff --git a/drivers/power/pm8921-charger.c b/drivers/power/pm8921-charger.c
index a09e5ee..061be69 100644
--- a/drivers/power/pm8921-charger.c
+++ b/drivers/power/pm8921-charger.c
@@ -928,7 +928,7 @@
if (trim < 0)
trim = 0;
- pr_err("trim_orig %d write 0x%x index=%d value 0x%x to USB_OVP_TRIM\n",
+ pr_debug("trim_orig %d write 0x%x index=%d value 0x%x to USB_OVP_TRIM\n",
usb_trim_reg_orig, trim, index, chip->usb_trim_table[index]);
rc = pm8xxx_readb(chip->dev->parent, REG_SBI_CONFIG, &sbi_config);
@@ -1404,6 +1404,8 @@
enum power_supply_property psp,
union power_supply_propval *val)
{
+ int type;
+
/* Check if called before init */
if (!the_chip)
return -EINVAL;
@@ -1423,10 +1425,11 @@
return 0;
}
- /* USB with max current greater than 500 mA connected */
- if (usb_target_ma > USB_WALL_THRESHOLD_MA)
+ type = the_chip->usb_psy.type;
+ if (type == POWER_SUPPLY_TYPE_USB_DCP ||
+ type == POWER_SUPPLY_TYPE_USB_ACA ||
+ type == POWER_SUPPLY_TYPE_USB_CDP)
val->intval = is_usb_chg_plugged_in(the_chip);
- return 0;
break;
default:
@@ -1521,11 +1524,9 @@
case POWER_SUPPLY_PROP_ONLINE:
val->intval = 0;
- /* USB charging */
- if (usb_target_ma < USB_WALL_THRESHOLD_MA)
+ if (the_chip->usb_psy.type == POWER_SUPPLY_TYPE_USB)
val->intval = is_usb_chg_plugged_in(the_chip);
- else
- return 0;
+
break;
case POWER_SUPPLY_PROP_SCOPE:
@@ -2186,7 +2187,7 @@
return -EINVAL;
}
- if (type < POWER_SUPPLY_TYPE_USB)
+ if (type < POWER_SUPPLY_TYPE_USB && type > POWER_SUPPLY_TYPE_BATTERY)
return -EINVAL;
the_chip->usb_psy.type = type;
@@ -3993,8 +3994,6 @@
#define ENUM_TIMER_STOP_BIT BIT(1)
#define BOOT_DONE_BIT BIT(6)
-#define BOOT_TIMER_EN_BIT BIT(1)
-#define BOOT_DONE_MASK (BOOT_DONE_BIT | BOOT_TIMER_EN_BIT)
#define CHG_BATFET_ON_BIT BIT(3)
#define CHG_VCP_EN BIT(0)
#define CHG_BAT_TEMP_DIS_BIT BIT(2)
@@ -4017,7 +4016,7 @@
detect_battery_removal(chip);
rc = pm_chg_masked_write(chip, SYS_CONFIG_2,
- BOOT_DONE_MASK, BOOT_DONE_MASK);
+ BOOT_DONE_BIT, BOOT_DONE_BIT);
if (rc) {
pr_err("Failed to set BOOT_DONE_BIT rc=%d\n", rc);
return rc;
@@ -4210,7 +4209,7 @@
return rc;
}
/* Check if die 3.0.1 is present */
- if (subrev == 0x1)
+ if (subrev & 0x1)
pm_chg_write(chip, CHG_BUCK_CTRL_TEST3, 0xA4);
else
pm_chg_write(chip, CHG_BUCK_CTRL_TEST3, 0xAC);
@@ -4757,6 +4756,9 @@
platform_set_drvdata(pdev, chip);
the_chip = chip;
+ /* set initial state of the USB charger type to UNKNOWN */
+ power_supply_set_supply_type(&chip->usb_psy, POWER_SUPPLY_TYPE_UNKNOWN);
+
wake_lock_init(&chip->eoc_wake_lock, WAKE_LOCK_SUSPEND, "pm8921_eoc");
INIT_DELAYED_WORK(&chip->eoc_work, eoc_worker);
INIT_DELAYED_WORK(&chip->vin_collapse_check_work,
diff --git a/drivers/power/qpnp-bms.c b/drivers/power/qpnp-bms.c
index 1bc027c..63cab43 100644
--- a/drivers/power/qpnp-bms.c
+++ b/drivers/power/qpnp-bms.c
@@ -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
@@ -34,6 +34,8 @@
/* Coulomb counter clear registers */
#define BMS1_CC_DATA_CTL 0x42
#define BMS1_CC_CLEAR_CTL 0x43
+/* BMS Tolerances */
+#define BMS1_TOL_CTL 0X44
/* OCV limit registers */
#define BMS1_OCV_USE_LOW_LIMIT_THR0 0x48
#define BMS1_OCV_USE_LOW_LIMIT_THR1 0x49
@@ -93,6 +95,7 @@
int iavg_ua;
int uuc_uah;
int ocv_charge_uah;
+ int delta_time_s;
};
struct raw_soc_params {
@@ -139,8 +142,6 @@
bool use_external_rsense;
- unsigned int start_percent;
- unsigned int end_percent;
bool ignore_shutdown_soc;
int shutdown_soc_invalid;
int shutdown_soc;
@@ -204,6 +205,7 @@
POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
};
+static bool bms_reset;
static int qpnp_read_wrapper(struct qpnp_bms_chip *chip, u8 *val,
u16 base, int count)
@@ -569,7 +571,7 @@
} else if (chip->prev_last_good_ocv_raw != raw->last_good_ocv_raw) {
convert_and_store_ocv(chip, raw);
/* forget the old cc value upon ocv */
- chip->last_cc_uah = 0;
+ chip->last_cc_uah = INT_MIN;
} else {
raw->last_good_ocv_uv = chip->last_ocv_uv;
}
@@ -713,46 +715,18 @@
}
static void calculate_iavg(struct qpnp_bms_chip *chip, int cc_uah,
- int *iavg_ua)
+ int *iavg_ua, int delta_time_s)
{
- int delta_cc_uah, delta_time_s, rc;
- struct rtc_time tm;
- struct rtc_device *rtc;
- unsigned long now_tm_sec = 0;
+ int delta_cc_uah = 0;
- rc = 0;
/* if anything fails report the previous iavg_ua */
*iavg_ua = chip->prev_iavg_ua;
- rtc = rtc_class_open(CONFIG_RTC_HCTOSYS_DEVICE);
- if (rtc == NULL) {
- pr_err("%s: unable to open rtc device (%s)\n",
- __FILE__, CONFIG_RTC_HCTOSYS_DEVICE);
- goto out;
- }
-
- rc = rtc_read_time(rtc, &tm);
- if (rc) {
- pr_err("Error reading rtc device (%s) : %d\n",
- CONFIG_RTC_HCTOSYS_DEVICE, rc);
- goto out;
- }
-
- rc = rtc_valid_tm(&tm);
- if (rc) {
- pr_err("Invalid RTC time (%s): %d\n",
- CONFIG_RTC_HCTOSYS_DEVICE, rc);
- goto out;
- }
- rtc_tm_to_time(&tm, &now_tm_sec);
-
- if (chip->tm_sec == 0) {
+ if (chip->last_cc_uah == INT_MIN) {
get_battery_current(chip, iavg_ua);
goto out;
}
- delta_time_s = (now_tm_sec - chip->tm_sec);
-
/* use the previous iavg if called within 15 seconds */
if (delta_time_s < 15) {
*iavg_ua = chip->prev_iavg_ua;
@@ -763,19 +737,13 @@
*iavg_ua = div_s64((s64)delta_cc_uah * 3600, delta_time_s);
- pr_debug("tm_sec = %ld, now_tm_sec = %ld delta_s = %d delta_cc = %d iavg_ua = %d\n",
- chip->tm_sec, now_tm_sec,
- delta_time_s, delta_cc_uah, (int)*iavg_ua);
-
out:
+ pr_debug("delta_cc = %d iavg_ua = %d\n", delta_cc_uah, (int)*iavg_ua);
/* remember the iavg */
chip->prev_iavg_ua = *iavg_ua;
/* remember cc_uah */
chip->last_cc_uah = cc_uah;
-
- /* remember this time */
- chip->tm_sec = now_tm_sec;
}
static int calculate_termination_uuc(struct qpnp_bms_chip *chip,
@@ -827,6 +795,7 @@
return uuc_uah;
}
+#define TIME_PER_PERCENT_UUC 60
static int adjust_uuc(struct qpnp_bms_chip *chip,
struct soc_params *params,
int new_pc_unusable,
@@ -835,18 +804,23 @@
{
int new_unusable_mv, new_iavg_ma;
int batt_temp_degc = batt_temp / 10;
+ int max_percent_change;
+
+ max_percent_change = max(params->delta_time_s
+ / TIME_PER_PERCENT_UUC, 1);
if (chip->prev_pc_unusable == -EINVAL
- || abs(chip->prev_pc_unusable - new_pc_unusable) <= 1) {
+ || abs(chip->prev_pc_unusable - new_pc_unusable)
+ <= max_percent_change) {
chip->prev_pc_unusable = new_pc_unusable;
return new_uuc_uah;
}
/* the uuc is trying to change more than 1% restrict it */
if (new_pc_unusable > chip->prev_pc_unusable)
- chip->prev_pc_unusable++;
+ chip->prev_pc_unusable += max_percent_change;
else
- chip->prev_pc_unusable--;
+ chip->prev_pc_unusable -= max_percent_change;
new_uuc_uah = (params->fcc_uah * chip->prev_pc_unusable) / 100;
@@ -918,6 +892,12 @@
chip->iavg_num_samples);
}
+ /*
+ * if we're in bms reset mode, force uuc to be 3% of fcc
+ */
+ if (bms_reset)
+ return (params->fcc_uah * 3) / 100;
+
uuc_uah_iavg = calculate_termination_uuc(chip, params, uuc_iavg_ma,
batt_temp, &pc_unusable);
pr_debug("uuc_iavg_ma = %d uuc with iavg = %d\n",
@@ -976,6 +956,58 @@
params->ocv_charge_uah = (int)ocv_charge_uah;
}
+static int get_current_time(unsigned long *now_tm_sec)
+{
+ struct rtc_time tm;
+ struct rtc_device *rtc;
+ int rc;
+
+ rtc = rtc_class_open(CONFIG_RTC_HCTOSYS_DEVICE);
+ if (rtc == NULL) {
+ pr_err("%s: unable to open rtc device (%s)\n",
+ __FILE__, CONFIG_RTC_HCTOSYS_DEVICE);
+ rc = -EINVAL;
+ goto close_time;
+ }
+
+ rc = rtc_read_time(rtc, &tm);
+ if (rc) {
+ pr_err("Error reading rtc device (%s) : %d\n",
+ CONFIG_RTC_HCTOSYS_DEVICE, rc);
+ goto close_time;
+ }
+
+ rc = rtc_valid_tm(&tm);
+ if (rc) {
+ pr_err("Invalid RTC time (%s): %d\n",
+ CONFIG_RTC_HCTOSYS_DEVICE, rc);
+ goto close_time;
+ }
+ rtc_tm_to_time(&tm, now_tm_sec);
+
+close_time:
+ rtc_class_close(rtc);
+ return rc;
+}
+
+static int calculate_delta_time(struct qpnp_bms_chip *chip, int *delta_time_s)
+{
+ unsigned long now_tm_sec = 0;
+
+ /* default to delta time = 0 if anything fails */
+ *delta_time_s = 0;
+
+ get_current_time(&now_tm_sec);
+
+ *delta_time_s = (now_tm_sec - chip->tm_sec);
+ pr_debug("tm_sec = %ld, now_tm_sec = %ld delta_s = %d\n",
+ chip->tm_sec, now_tm_sec, *delta_time_s);
+
+ /* remember this time */
+ chip->tm_sec = now_tm_sec;
+ return 0;
+}
+
static void calculate_soc_params(struct qpnp_bms_chip *chip,
struct raw_soc_params *raw,
struct soc_params *params,
@@ -983,6 +1015,7 @@
{
int soc_rbatt;
+ calculate_delta_time(chip, ¶ms->delta_time_s);
params->fcc_uah = calculate_fcc(chip, batt_temp);
pr_debug("FCC = %uuAh batt_temp = %d\n", params->fcc_uah, batt_temp);
@@ -1006,7 +1039,8 @@
soc_rbatt = 0;
params->rbatt_mohm = get_rbatt(chip, soc_rbatt, batt_temp);
- calculate_iavg(chip, params->cc_uah, ¶ms->iavg_ua);
+ calculate_iavg(chip, params->cc_uah, ¶ms->iavg_ua,
+ params->delta_time_s);
params->uuc_uah = calculate_unusable_charge_uah(chip, params,
batt_temp);
@@ -1085,6 +1119,24 @@
return 0;
}
+static bool is_battery_charging(struct qpnp_bms_chip *chip)
+{
+ union power_supply_propval ret = {0,};
+
+ if (chip->batt_psy == NULL)
+ chip->batt_psy = power_supply_get_by_name("battery");
+ if (chip->batt_psy) {
+ /* if battery has been registered, use the status property */
+ chip->batt_psy->get_property(chip->batt_psy,
+ POWER_SUPPLY_PROP_STATUS, &ret);
+ return ret.intval == POWER_SUPPLY_STATUS_CHARGING;
+ }
+
+ /* Default to false if the battery power supply is not registered. */
+ pr_debug("battery power supply is not registered\n");
+ return false;
+}
+
static bool is_batfet_open(struct qpnp_bms_chip *chip)
{
union power_supply_propval ret = {0,};
@@ -1134,6 +1186,76 @@
return soc;
}
+#define IBAT_TOL_MASK 0x0F
+#define OCV_TOL_MASK 0xF0
+#define IBAT_TOL_DEFAULT 0x03
+#define IBAT_TOL_NOCHG 0x0F
+#define OCV_TOL_DEFAULT 0x20
+#define OCV_TOL_NO_OCV 0x00
+static int stop_ocv_updates(struct qpnp_bms_chip *chip)
+{
+ pr_debug("stopping ocv updates\n");
+ return qpnp_masked_write(chip, BMS1_TOL_CTL,
+ OCV_TOL_MASK, OCV_TOL_NO_OCV);
+}
+
+static int reset_bms_for_test(struct qpnp_bms_chip *chip)
+{
+ int ibat_ua, vbat_uv, rc;
+ int ocv_est_uv;
+
+ if (!chip) {
+ pr_err("BMS driver has not been initialized yet!\n");
+ return -EINVAL;
+ }
+
+ rc = get_simultaneous_batt_v_and_i(chip, &ibat_ua, &vbat_uv);
+
+ ocv_est_uv = vbat_uv + (ibat_ua * chip->r_conn_mohm) / 1000;
+ pr_debug("forcing ocv to be %d due to bms reset mode\n", ocv_est_uv);
+ chip->last_ocv_uv = ocv_est_uv;
+ chip->last_soc = -EINVAL;
+ reset_cc(chip);
+ chip->last_cc_uah = INT_MIN;
+ stop_ocv_updates(chip);
+
+ pr_debug("bms reset to ocv = %duv vbat_ua = %d ibat_ua = %d\n",
+ chip->last_ocv_uv, vbat_uv, ibat_ua);
+
+ return rc;
+}
+
+static int bms_reset_set(const char *val, const struct kernel_param *kp)
+{
+ int rc;
+
+ rc = param_set_bool(val, kp);
+ if (rc) {
+ pr_err("Unable to set bms_reset: %d\n", rc);
+ return rc;
+ }
+
+ if (*(bool *)kp->arg) {
+ struct power_supply *bms_psy = power_supply_get_by_name("bms");
+ struct qpnp_bms_chip *chip = container_of(bms_psy,
+ struct qpnp_bms_chip, bms_psy);
+
+ rc = reset_bms_for_test(chip);
+ if (rc) {
+ pr_err("Unable to modify bms_reset: %d\n", rc);
+ return rc;
+ }
+ }
+ return 0;
+}
+
+static struct kernel_param_ops bms_reset_ops = {
+ .set = bms_reset_set,
+ .get = param_get_bool,
+};
+
+module_param_cb(bms_reset, &bms_reset_ops, &bms_reset, 0644);
+
static int charging_adjustments(struct qpnp_bms_chip *chip,
struct soc_params *params, int soc,
int vbat_uv, int ibat_ua, int batt_temp)
@@ -1225,6 +1347,12 @@
(s64)params->fcc_uah - params->uuc_uah);
soc_est = bound_soc(soc_est);
+ /* never adjust during bms reset mode */
+ if (bms_reset) {
+ pr_debug("bms reset mode, SOC adjustment skipped\n");
+ goto out;
+ }
+
if (ibat_ua < 0 && !is_batfet_open(chip)) {
soc = charging_adjustments(chip, params, soc, vbat_uv, ibat_ua,
batt_temp);
@@ -1583,11 +1711,13 @@
*/
/* if not charging, return last soc */
- if (chip->start_percent == -EINVAL)
+ if (!is_battery_charging(chip))
return prev_soc;
chg_time_sec = DIV_ROUND_UP(chip->charge_time_us, USEC_PER_SEC);
catch_up_sec = DIV_ROUND_UP(chip->catch_up_time_us, USEC_PER_SEC);
+ if (catch_up_sec == 0)
+ return new_soc;
pr_debug("cts= %d catch_up_sec = %d\n", chg_time_sec, catch_up_sec);
/*
@@ -1658,7 +1788,7 @@
* account for charge time - limit it to SOC_CATCHUP_SEC to
* avoid overflows when charging continues for extended periods
*/
- if (chip->start_percent != -EINVAL) {
+ if (is_battery_charging(chip)) {
if (chip->charge_time_us == 0) {
/*
* calculating soc for the first time
@@ -1687,8 +1817,7 @@
}
/* last_soc < soc ... scale and catch up */
- if (chip->last_soc != -EINVAL && chip->last_soc < soc
- && soc != 100 && chip->catch_up_time_us != 0)
+ if (chip->last_soc != -EINVAL && chip->last_soc < soc && soc != 100)
soc = scale_soc_while_chg(chip, delta_time_us,
soc, chip->last_soc);
@@ -2005,13 +2134,12 @@
static inline void bms_initialize_constants(struct qpnp_bms_chip *chip)
{
- chip->start_percent = -EINVAL;
- chip->end_percent = -EINVAL;
chip->prev_pc_unusable = -EINVAL;
chip->soc_at_cv = -EINVAL;
chip->calculated_soc = -EINVAL;
chip->last_soc = -EINVAL;
chip->last_soc_est = -EINVAL;
+ chip->last_cc_uah = INT_MIN;
chip->first_time_calc_soc = 1;
chip->first_time_calc_uuc = 1;
}
@@ -2087,6 +2215,7 @@
static int read_iadc_channel_select(struct qpnp_bms_chip *chip)
{
u8 iadc_channel_select;
+ int32_t rds_rsense_nohm;
int rc;
rc = qpnp_read_wrapper(chip, &iadc_channel_select,
@@ -2097,10 +2226,17 @@
}
iadc_channel_select &= ADC_CH_SEL_MASK;
- if (iadc_channel_select == INTERNAL_RSENSE) {
- pr_debug("Internal rsense used\n");
- if (chip->use_external_rsense) {
- pr_debug("Changing rsense to external\n");
+ if (iadc_channel_select != EXTERNAL_RSENSE
+ && iadc_channel_select != INTERNAL_RSENSE) {
+ pr_err("IADC1_BMS_IADC configured incorrectly. Selected channel = %d\n",
+ iadc_channel_select);
+ return -EINVAL;
+ }
+
+ if (chip->use_external_rsense) {
+ pr_debug("External rsense selected\n");
+ if (iadc_channel_select == INTERNAL_RSENSE) {
+ pr_debug("Internal rsense detected; Changing rsense to external\n");
rc = qpnp_masked_write_iadc(chip,
IADC1_BMS_ADC_CH_SEL_CTL,
ADC_CH_SEL_MASK,
@@ -2113,10 +2249,10 @@
}
reset_cc(chip);
}
- } else if (iadc_channel_select == EXTERNAL_RSENSE) {
- pr_debug("External rsense used\n");
- if (!chip->use_external_rsense) {
- pr_debug("Changing rsense to internal\n");
+ } else {
+ pr_debug("Internal rsense selected\n");
+ if (iadc_channel_select == EXTERNAL_RSENSE) {
+ pr_debug("External rsense detected; Changing rsense to internal\n");
rc = qpnp_masked_write_iadc(chip,
IADC1_BMS_ADC_CH_SEL_CTL,
ADC_CH_SEL_MASK,
@@ -2129,10 +2265,16 @@
}
reset_cc(chip);
}
- } else {
- pr_err("IADC1_BMS_IADC configured incorrectly. Selected channel = %d\n",
- iadc_channel_select);
- return -EINVAL;
+
+ rc = qpnp_iadc_get_rsense(&rds_rsense_nohm);
+ if (rc) {
+ pr_err("Unable to read RDS resistance value from IADC; rc = %d\n",
+ rc);
+ return rc;
+ }
+ chip->r_sense_mohm = rds_rsense_nohm/1000000;
+ pr_debug("rds_rsense = %d nOhm, saved as %d mOhm\n",
+ rds_rsense_nohm, chip->r_sense_mohm);
}
return 0;
}
@@ -2238,10 +2380,9 @@
vbatt = 0;
get_battery_voltage(&vbatt);
- pr_debug("OK battery_capacity_at_boot=%d vbatt = %d\n",
+ pr_info("probe success: soc =%d vbatt = %d ocv = %d r_sense_mohm = %u\n",
get_prop_bms_capacity(chip),
- vbatt);
- pr_info("probe success\n");
+ vbatt, chip->last_ocv_uv, chip->r_sense_mohm);
return 0;
unregister_dc:
diff --git a/drivers/power/qpnp-charger.c b/drivers/power/qpnp-charger.c
index 510071a..32359e5 100644
--- a/drivers/power/qpnp-charger.c
+++ b/drivers/power/qpnp-charger.c
@@ -81,6 +81,7 @@
#define CHGR_MISC_BOOT_DONE 0x42
#define CHGR_BUCK_COMPARATOR_OVRIDE_3 0xED
#define MISC_REVISION2 0x01
+#define USB_OVP_CTL 0x42
#define SEC_ACCESS 0xD0
/* SMBB peripheral subtype values */
@@ -99,6 +100,7 @@
#define CHGR_BOOT_DONE BIT(7)
#define CHGR_CHG_EN BIT(7)
#define CHGR_ON_BAT_FORCE_BIT BIT(0)
+#define USB_VALID_DEB_20MS 0x03
/* Interrupt definitions */
/* smbb_chg_interrupts */
@@ -481,6 +483,10 @@
static int
qpnp_chg_force_run_on_batt(struct qpnp_chg_chip *chip, int disable)
{
+ /* Don't run on battery for batteryless hardware */
+ if (chip->use_default_batt_values)
+ return 0;
+
/* This bit forces the charger to run off of the battery rather
* than a connected charger */
return qpnp_chg_masked_write(chip, chip->chgr_base + CHGR_CHG_CTRL,
@@ -1189,6 +1195,11 @@
}
rc = qpnp_chg_masked_write(chip,
+ chip->usb_chgpth_base + USB_OVP_CTL,
+ USB_VALID_DEB_20MS,
+ USB_VALID_DEB_20MS, 1);
+
+ rc = qpnp_chg_masked_write(chip,
chip->usb_chgpth_base + CHGR_USB_ENUM_T_STOP,
ENUM_T_STOP_BIT,
ENUM_T_STOP_BIT, 1);
diff --git a/drivers/regulator/fixed.c b/drivers/regulator/fixed.c
index 7e74eca..d0410a4 100644
--- a/drivers/regulator/fixed.c
+++ b/drivers/regulator/fixed.c
@@ -104,6 +104,9 @@
if (of_find_property(np, "enable-active-high", NULL))
config->enable_high = true;
+ if (of_find_property(np, "parent-supply", NULL))
+ init_data->supply_regulator = "parent";
+
return config;
}
diff --git a/drivers/slimbus/slim-msm-ctrl.c b/drivers/slimbus/slim-msm-ctrl.c
index 9c69f47..9b0b8b4 100644
--- a/drivers/slimbus/slim-msm-ctrl.c
+++ b/drivers/slimbus/slim-msm-ctrl.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, Code Aurora Forum. 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
@@ -1061,7 +1061,7 @@
dev_err(dev->dev, "rx thread wait error:%d", ret);
/* 1 irq notification per message */
- if (!dev->use_rx_msgqs) {
+ if (dev->use_rx_msgqs != MSM_MSGQ_ENABLED) {
msm_slim_rxwq(dev);
continue;
}
@@ -1255,9 +1255,9 @@
spin_lock_init(&dev->rx_lock);
dev->ee = 1;
if (rxreg_access)
- dev->use_rx_msgqs = 0;
+ dev->use_rx_msgqs = MSM_MSGQ_DISABLED;
else
- dev->use_rx_msgqs = 1;
+ dev->use_rx_msgqs = MSM_MSGQ_RESET;
dev->irq = irq->start;
dev->bam.irq = bam_irq->start;
@@ -1328,7 +1328,7 @@
* Manager register initialization
* If RX msg Q is used, disable RX_MSG_RCVD interrupt
*/
- if (dev->use_rx_msgqs)
+ if (dev->use_rx_msgqs == MSM_MSGQ_ENABLED)
writel_relaxed((MGR_INT_RECFG_DONE | MGR_INT_TX_NACKED_2 |
MGR_INT_MSG_BUF_CONTE | /* MGR_INT_RX_MSG_RCVD | */
MGR_INT_TX_MSG_SENT), dev->base + MGR_INT_EN);
@@ -1357,7 +1357,7 @@
mb();
/* Enable RX msg Q */
- if (dev->use_rx_msgqs)
+ if (dev->use_rx_msgqs == MSM_MSGQ_ENABLED)
writel_relaxed(MGR_CFG_ENABLE | MGR_CFG_RX_MSGQ_EN,
dev->base + MGR_CFG);
else
@@ -1409,7 +1409,7 @@
err_request_irq_failed:
kthread_stop(dev->rx_msgq_thread);
err_thread_create_failed:
- msm_slim_sps_exit(dev);
+ msm_slim_sps_exit(dev, true);
err_sps_init_failed:
if (dev->hclk) {
clk_disable_unprepare(dev->hclk);
@@ -1453,7 +1453,7 @@
clk_put(dev->rclk);
if (dev->hclk)
clk_put(dev->hclk);
- msm_slim_sps_exit(dev);
+ msm_slim_sps_exit(dev, true);
kthread_stop(dev->rx_msgq_thread);
iounmap(dev->bam.base);
iounmap(dev->base);
diff --git a/drivers/slimbus/slim-msm-ngd.c b/drivers/slimbus/slim-msm-ngd.c
index 78e8a6f..eb741ef 100644
--- a/drivers/slimbus/slim-msm-ngd.c
+++ b/drivers/slimbus/slim-msm-ngd.c
@@ -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
@@ -33,6 +33,7 @@
#define NGD_SLIM_NAME "ngd_msm_ctrl"
#define SLIM_LA_MGR 0xFF
#define SLIM_ROOT_FREQ 24576000
+#define LADDR_RETRY 5
#define NGD_BASE_V1(r) (((r) % 2) ? 0x800 : 0xA00)
#define NGD_BASE_V2(r) (((r) % 2) ? 0x1000 : 0x2000)
@@ -78,6 +79,12 @@
NGD_TX_BUSY = 0x0,
};
+enum ngd_status {
+ NGD_LADDR = 1 << 1,
+};
+
+static int ngd_slim_runtime_resume(struct device *device);
+
static irqreturn_t ngd_slim_interrupt(int irq, void *d)
{
struct msm_slim_ctrl *dev = (struct msm_slim_ctrl *)d;
@@ -122,7 +129,7 @@
* queuing work
*/
mb();
- if (dev->use_rx_msgqs)
+ if (dev->use_rx_msgqs == MSM_MSGQ_ENABLED)
dev_err(dev->dev,
"direct message received even with RX MSGQs");
else
@@ -144,23 +151,24 @@
return IRQ_HANDLED;
}
-static int ngd_clk_pause_wakeup(struct slim_controller *ctrl)
-{
- struct msm_slim_ctrl *dev = slim_get_ctrldata(ctrl);
- return msm_slim_qmi_power_request(dev, true);
-}
-
static int ngd_qmi_available(struct notifier_block *n, unsigned long code,
void *_cmd)
{
struct msm_slim_qmi *qmi = container_of(n, struct msm_slim_qmi, nb);
+ struct msm_slim_ctrl *dev =
+ container_of(qmi, struct msm_slim_ctrl, qmi);
pr_info("Slimbus QMI NGD CB received event:%ld", code);
switch (code) {
case QMI_SERVER_ARRIVE:
- complete(&qmi->qmi_comp);
+ schedule_work(&qmi->ssr_up);
break;
case QMI_SERVER_EXIT:
- /* SSR implementation */
+ dev->state = MSM_CTRL_DOWN;
+ /* make sure autosuspend is not called until ADSP comes up*/
+ pm_runtime_get_noresume(dev->dev);
+ /* Reset ctrl_up completion */
+ init_completion(&dev->ctrl_up);
+ schedule_work(&qmi->ssr_down);
break;
default:
break;
@@ -217,8 +225,17 @@
u8 wbuf[SLIM_RX_MSGQ_BUF_LEN];
if (txn->mc == (SLIM_MSG_CLK_PAUSE_SEQ_FLG |
- SLIM_MSG_MC_RECONFIGURE_NOW))
- return msm_slim_qmi_power_request(dev, false);
+ SLIM_MSG_MC_RECONFIGURE_NOW)) {
+ if (dev->use_rx_msgqs == MSM_MSGQ_ENABLED) {
+ ret = sps_disconnect(dev->rx_msgq.sps);
+ dev->use_rx_msgqs = MSM_MSGQ_RESET;
+ }
+ if (!ret)
+ ret = msm_slim_qmi_power_request(dev, false);
+ else
+ pr_err("SPS pipe disconnect error:%d", ret);
+ return ret;
+ }
else if (txn->mc & SLIM_MSG_CLK_PAUSE_SEQ_FLG)
return 0;
@@ -227,22 +244,43 @@
txn->mc <= SLIM_MSG_MC_RECONFIGURE_NOW)) {
return 0;
}
- msm_slim_get_ctrl(dev);
+ /* If txn is tried when controller is down, wait for ADSP to boot */
+ if (txn->mc != SLIM_USR_MC_REPORT_SATELLITE) {
+ if (dev->state == MSM_CTRL_DOWN) {
+ u8 mc = (u8)txn->mc;
+ int timeout;
+ dev_err(dev->dev, "ADSP slimbus not up yet");
+ /*
+ * Messages related to data channel management can't
+ * wait since they are holding reconfiguration lock.
+ * clk_pause in resume (which can change state back to
+ * MSM_CTRL_AWAKE), will need that lock
+ */
+ if ((txn->mt == SLIM_MSG_MT_CORE) &&
+ ((mc >= SLIM_MSG_MC_CONNECT_SOURCE &&
+ mc <= SLIM_MSG_MC_CHANGE_CONTENT) ||
+ (mc >= SLIM_MSG_MC_BEGIN_RECONFIGURATION &&
+ mc <= SLIM_MSG_MC_RECONFIGURE_NOW)))
+ return -EREMOTEIO;
+ if ((txn->mt == SLIM_MSG_MT_DEST_REFERRED_USER) &&
+ ((mc >= SLIM_USR_MC_DEFINE_CHAN &&
+ mc <= SLIM_USR_MC_DISCONNECT_PORT)))
+ return -EREMOTEIO;
+ timeout = wait_for_completion_timeout(&dev->ctrl_up,
+ HZ);
+ if (!timeout)
+ return -ETIMEDOUT;
+ }
+ msm_slim_get_ctrl(dev);
+ }
mutex_lock(&dev->tx_lock);
+
if (txn->mc != SLIM_USR_MC_REPORT_SATELLITE &&
- (dev->state == MSM_CTRL_ASLEEP ||
- dev->state == MSM_CTRL_SLEEPING)) {
- int timeout;
+ (dev->state != MSM_CTRL_AWAKE)) {
dev_err(dev->dev, "controller not ready");
mutex_unlock(&dev->tx_lock);
- /* Reconf is signalled when master responds */
- timeout = wait_for_completion_timeout(&dev->reconf, HZ);
- if (timeout) {
- mutex_lock(&dev->tx_lock);
- } else {
- msm_slim_put_ctrl(dev);
- return -EBUSY;
- }
+ msm_slim_put_ctrl(dev);
+ return -EREMOTEIO;
}
if (txn->mt == SLIM_MSG_MT_CORE &&
(txn->mc == SLIM_MSG_MC_CONNECT_SOURCE ||
@@ -370,24 +408,23 @@
mutex_unlock(&dev->tx_lock);
msm_slim_put_ctrl(dev);
timeout = wait_for_completion_timeout(txn->comp, HZ);
- if (!timeout) {
- pr_err("connect/disc :0x%x, tid:%d timed out", txn->mc,
- txn->tid);
+ if (!timeout)
ret = -ETIMEDOUT;
+ else
+ ret = txn->ec;
+ if (ret) {
+ pr_err("connect/disconnect:0x%x,tid:%d err:%d", txn->mc,
+ txn->tid, ret);
mutex_lock(&ctrl->m_ctrl);
ctrl->txnt[txn->tid] = NULL;
mutex_unlock(&ctrl->m_ctrl);
- } else {
- ret = txn->ec;
}
- if (ret)
- pr_err("connect/disconnect:0x%x,tid:%d err:%d", txn->mc,
- txn->tid, ret);
return ret ? ret : dev->err;
}
ngd_xfer_err:
mutex_unlock(&dev->tx_lock);
- msm_slim_put_ctrl(dev);
+ if (txn->mc != SLIM_USR_MC_REPORT_SATELLITE)
+ msm_slim_put_ctrl(dev);
return ret ? ret : dev->err;
}
@@ -398,20 +435,18 @@
if (!ret) {
int timeout;
timeout = wait_for_completion_timeout(txn->comp, HZ);
- if (!timeout) {
- pr_err("master req:0x%x, tid:%d timed out", txn->mc,
- txn->tid);
+ if (!timeout)
ret = -ETIMEDOUT;
- mutex_lock(&ctrl->m_ctrl);
- ctrl->txnt[txn->tid] = NULL;
- mutex_unlock(&ctrl->m_ctrl);
- } else {
+ else
ret = txn->ec;
- }
}
- if (ret)
+ if (ret) {
pr_err("master msg:0x%x,tid:%d ret:%d", txn->mc,
txn->tid, ret);
+ mutex_lock(&ctrl->m_ctrl);
+ ctrl->txnt[txn->tid] = NULL;
+ mutex_unlock(&ctrl->m_ctrl);
+ }
return ret;
}
@@ -517,7 +552,7 @@
return ret;
txn.len = 0;
}
- return ret;
+ return 0;
}
static int ngd_set_laddr(struct slim_controller *ctrl, const u8 *ea,
@@ -555,6 +590,24 @@
return ret;
}
+static void ngd_slim_setup_rx_path(struct msm_slim_ctrl *dev)
+{
+ int ret;
+ if (dev->state == MSM_CTRL_DOWN) {
+ msm_slim_sps_init(dev, dev->bam_mem,
+ NGD_BASE(dev->ctrl.nr,
+ dev->ver) + NGD_STATUS, true);
+ } else {
+ if (dev->use_rx_msgqs == MSM_MSGQ_DISABLED)
+ return;
+ ret = msm_slim_connect_endp(dev, &dev->rx_msgq,
+ &dev->rx_msgq_notify);
+ if (!ret)
+ dev->use_rx_msgqs = MSM_MSGQ_ENABLED;
+ else
+ pr_err("RX msgq not being used:%d", ret);
+ }
+}
static void ngd_slim_rx(struct msm_slim_ctrl *dev, u8 *buf)
{
u8 mc, mt, len;
@@ -582,26 +635,28 @@
txn.wbuf = wbuf;
txn.len = 4;
pr_info("SLIM SAT: Received master capability");
- dev->use_rx_msgqs = 1;
- msm_slim_sps_init(dev, dev->bam_mem,
- NGD_BASE(dev->ctrl.nr, dev->ver) + NGD_STATUS, true);
- if (dev->use_rx_msgqs)
- msgq_en |= NGD_CFG_RX_MSGQ_EN;
- writel_relaxed(msgq_en, dev->base +
- NGD_BASE(dev->ctrl.nr, dev->ver));
- /* make sure NGD MSG-Q config goes through */
- mb();
+ if (dev->state >= MSM_CTRL_ASLEEP) {
+ ngd_slim_setup_rx_path(dev);
+ if (dev->use_rx_msgqs == MSM_MSGQ_ENABLED)
+ msgq_en |= NGD_CFG_RX_MSGQ_EN;
+ writel_relaxed(msgq_en, dev->base +
+ NGD_BASE(dev->ctrl.nr, dev->ver));
+ /* make sure NGD MSG-Q config goes through */
+ mb();
+ }
ret = ngd_xfer_msg(&dev->ctrl, &txn);
if (!ret) {
+ enum msm_ctrl_state prev_state = dev->state;
dev->state = MSM_CTRL_AWAKE;
-
- pm_runtime_use_autosuspend(dev->dev);
- pm_runtime_set_autosuspend_delay(dev->dev,
- MSM_SLIM_AUTOSUSPEND);
- pm_runtime_set_active(dev->dev);
- pm_runtime_enable(dev->dev);
- complete(&dev->reconf);
+ if (prev_state >= MSM_CTRL_ASLEEP)
+ complete(&dev->reconf);
+ else
+ pr_err("SLIM: unexpected capability, state:%d",
+ prev_state);
+ /* ADSP SSR, send device_up notifications */
+ if (prev_state == MSM_CTRL_DOWN)
+ schedule_work(&dev->slave_notify);
}
}
if (mc == SLIM_MSG_MC_REPLY_INFORMATION ||
@@ -654,38 +709,114 @@
}
}
-static int ngd_slim_enable(struct msm_slim_ctrl *dev, bool enable)
+static int ngd_slim_power_up(struct msm_slim_ctrl *dev)
{
- u32 ngd_int = (NGD_INT_RECFG_DONE | NGD_INT_TX_NACKED_2 |
+ void __iomem *ngd;
+ int timeout, ret;
+ enum msm_ctrl_state cur_state = dev->state;
+ u32 laddr;
+ u32 ngd_int = (NGD_INT_TX_NACKED_2 |
NGD_INT_MSG_BUF_CONTE | NGD_INT_MSG_TX_INVAL |
NGD_INT_IE_VE_CHG | NGD_INT_DEV_ERR |
NGD_INT_TX_MSG_SENT | NGD_INT_RX_MSG_RCVD);
- if (enable) {
- int ret = msm_slim_qmi_init(dev, false);
- if (ret)
- return ret;
- ret = msm_slim_qmi_power_request(dev, true);
- if (ret)
- return ret;
- writel_relaxed(ngd_int, dev->base + NGD_INT_EN +
- NGD_BASE(dev->ctrl.nr, dev->ver));
+
+ if (cur_state == MSM_CTRL_DOWN) {
+ int timeout = wait_for_completion_timeout(&dev->qmi.qmi_comp,
+ HZ);
+ if (!timeout)
+ pr_err("slimbus QMI init timed out");
+ }
+
+ ret = msm_slim_qmi_power_request(dev, true);
+ if (ret) {
+ pr_err("SLIM QMI power request failed:%d", ret);
+ return ret;
+ }
+ if (!dev->ver) {
+ dev->ver = readl_relaxed(dev->base);
+ /* Version info in 16 MSbits */
+ dev->ver >>= 16;
+ }
+ ngd = dev->base + NGD_BASE(dev->ctrl.nr, dev->ver);
+ laddr = readl_relaxed(ngd + NGD_STATUS);
+ if (laddr & NGD_LADDR) {
/*
- * Enable NGD. Configure NGD in register acc. mode until master
- * announcement is received
+ * ADSP power collapse case, where HW wasn't reset.
+ * Reconnect BAM pipes if disconnected
*/
- writel_relaxed(1, dev->base + NGD_BASE(dev->ctrl.nr, dev->ver));
- /* make sure NGD enabling goes through */
- mb();
- } else {
- writel_relaxed(0, dev->base + NGD_BASE(dev->ctrl.nr, dev->ver));
- writel_relaxed(0, dev->base + NGD_INT_EN +
+ ngd_slim_setup_rx_path(dev);
+ return 0;
+ } else if (cur_state != MSM_CTRL_DOWN) {
+ pr_info("ADSP P.C. CTRL state:%d NGD not enumerated:0x%x",
+ dev->state, laddr);
+ }
+
+ /*
+ * ADSP power collapse case (OR SSR), where HW was reset
+ * BAM programming will happen when capability message is received
+ */
+ writel_relaxed(ngd_int, dev->base + NGD_INT_EN +
NGD_BASE(dev->ctrl.nr, dev->ver));
- /* make sure NGD disabling goes through */
- mb();
+ /*
+ * Enable NGD. Configure NGD in register acc. mode until master
+ * announcement is received
+ */
+ writel_relaxed(1, dev->base + NGD_BASE(dev->ctrl.nr, dev->ver));
+ /* make sure NGD enabling goes through */
+ mb();
+
+ timeout = wait_for_completion_timeout(&dev->reconf, HZ);
+ if (!timeout) {
+ pr_err("failed to received master capability");
+ return -ETIMEDOUT;
+ }
+ if (cur_state == MSM_CTRL_DOWN)
+ complete(&dev->ctrl_up);
+ return 0;
+}
+
+static int ngd_slim_enable(struct msm_slim_ctrl *dev, bool enable)
+{
+ int ret = 0;
+ if (enable) {
+ ret = msm_slim_qmi_init(dev, false);
+ /* controller state should be in sync with framework state */
+ if (!ret) {
+ ret = slim_ctrl_clk_pause(&dev->ctrl, false,
+ SLIM_CLK_UNSPECIFIED);
+ complete(&dev->qmi.qmi_comp);
+ /*
+ * Power-up won't be called if clock pause failed.
+ * This can happen if ADSP SSR happened when audio
+ * session is in progress. Framework will think that
+ * clock pause failed so no need to wakeup controller.
+ * Call power-up explicitly in that case, since slimbus
+ * HW needs to be powered-on to be in sync with
+ * framework state
+ */
+ if (ret)
+ ngd_slim_power_up(dev);
+ if (!pm_runtime_enabled(dev->dev) ||
+ !pm_runtime_suspended(dev->dev))
+ ngd_slim_runtime_resume(dev->dev);
+ else
+ pm_runtime_resume(dev->dev);
+ pm_runtime_mark_last_busy(dev->dev);
+ pm_runtime_put(dev->dev);
+ } else
+ dev_err(dev->dev, "qmi init fail, ret:%d, state:%d",
+ ret, dev->state);
+ } else {
msm_slim_qmi_exit(dev);
}
- return 0;
+ return ret;
+}
+
+static int ngd_clk_pause_wakeup(struct slim_controller *ctrl)
+{
+ struct msm_slim_ctrl *dev = slim_get_ctrldata(ctrl);
+ return ngd_slim_power_up(dev);
}
static int ngd_slim_rx_msgq_thread(void *data)
@@ -698,14 +829,6 @@
u32 buffer[10];
u8 msg_len = 0;
- wait_for_completion_interruptible(&dev->qmi.qmi_comp);
- ret = ngd_slim_enable(dev, true);
- /* Exit the thread if component can't be enabled */
- if (ret) {
- pr_err("Enabling NGD failed:%d", ret);
- return 0;
- }
-
while (!kthread_should_stop()) {
set_current_state(TASK_INTERRUPTIBLE);
ret = wait_for_completion_interruptible(notify);
@@ -714,7 +837,7 @@
continue;
}
/* 1 irq notification per message */
- if (!dev->use_rx_msgqs) {
+ if (dev->use_rx_msgqs != MSM_MSGQ_ENABLED) {
msm_slim_rx_dequeue(dev, (u8 *)buffer);
ngd_slim_rx(dev, (u8 *)buffer);
continue;
@@ -741,6 +864,63 @@
return 0;
}
+static void ngd_laddr_lookup(struct work_struct *work)
+{
+ struct msm_slim_ctrl *dev =
+ container_of(work, struct msm_slim_ctrl, slave_notify);
+ struct slim_controller *ctrl = &dev->ctrl;
+ struct slim_device *sbdev;
+ int i;
+ mutex_lock(&ctrl->m_ctrl);
+ list_for_each_entry(sbdev, &ctrl->devs, dev_list) {
+ int ret = 0;
+ mutex_unlock(&ctrl->m_ctrl);
+ for (i = 0; i < LADDR_RETRY; i++) {
+ ret = slim_get_logical_addr(sbdev, sbdev->e_addr,
+ 6, &sbdev->laddr);
+ if (!ret)
+ break;
+ else /* time for ADSP to assign LA */
+ msleep(20);
+ }
+ mutex_lock(&ctrl->m_ctrl);
+ }
+ mutex_unlock(&ctrl->m_ctrl);
+}
+
+static void ngd_adsp_down(struct work_struct *work)
+{
+ struct msm_slim_qmi *qmi =
+ container_of(work, struct msm_slim_qmi, ssr_down);
+ struct msm_slim_ctrl *dev =
+ container_of(qmi, struct msm_slim_ctrl, qmi);
+ struct slim_controller *ctrl = &dev->ctrl;
+ struct slim_device *sbdev;
+ int i;
+
+ ngd_slim_enable(dev, false);
+ /* disconnect BAM pipes */
+ msm_slim_sps_exit(dev, false);
+ mutex_lock(&ctrl->m_ctrl);
+ /* device up should be called again after SSR */
+ list_for_each_entry(sbdev, &ctrl->devs, dev_list)
+ sbdev->notified = false;
+ /* invalidate logical addresses */
+ for (i = 0; i < ctrl->num_dev; i++)
+ ctrl->addrt[i].valid = false;
+ mutex_unlock(&ctrl->m_ctrl);
+ pr_info("SLIM ADSP SSR (DOWN) done");
+}
+
+static void ngd_adsp_up(struct work_struct *work)
+{
+ struct msm_slim_qmi *qmi =
+ container_of(work, struct msm_slim_qmi, ssr_up);
+ struct msm_slim_ctrl *dev =
+ container_of(qmi, struct msm_slim_ctrl, qmi);
+ ngd_slim_enable(dev, true);
+}
+
static int __devinit ngd_slim_probe(struct platform_device *pdev)
{
struct msm_slim_ctrl *dev;
@@ -749,6 +929,7 @@
struct resource *slim_mem;
struct resource *irq, *bam_irq;
enum apr_subsys_state q6_state;
+ bool rxreg_access = false;
q6_state = apr_get_q6_state();
if (q6_state == APR_SUBSYS_DOWN) {
@@ -811,6 +992,8 @@
dev_err(&pdev->dev, "Cell index not specified:%d", ret);
goto err_ctrl_failed;
}
+ rxreg_access = of_property_read_bool(pdev->dev.of_node,
+ "qcom,rxreg-access");
} else {
dev->ctrl.nr = pdev->id;
}
@@ -834,15 +1017,17 @@
dev->bam_mem = bam_mem;
init_completion(&dev->reconf);
+ init_completion(&dev->ctrl_up);
mutex_init(&dev->tx_lock);
spin_lock_init(&dev->rx_lock);
dev->ee = 1;
dev->irq = irq->start;
dev->bam.irq = bam_irq->start;
- dev->ver = readl_relaxed(dev->base);
- /* Version info in 16 MSbits */
- dev->ver >>= 16;
+ if (rxreg_access)
+ dev->use_rx_msgqs = MSM_MSGQ_DISABLED;
+ else
+ dev->use_rx_msgqs = MSM_MSGQ_RESET;
init_completion(&dev->rx_msgq_notify);
/* Register with framework */
@@ -854,7 +1039,7 @@
dev->ctrl.dev.parent = &pdev->dev;
dev->ctrl.dev.of_node = pdev->dev.of_node;
- dev->state = MSM_CTRL_ASLEEP;
+ dev->state = MSM_CTRL_DOWN;
ret = request_irq(dev->irq, ngd_slim_interrupt,
IRQF_TRIGGER_HIGH, "ngd_slim_irq", dev);
@@ -865,7 +1050,16 @@
}
init_completion(&dev->qmi.qmi_comp);
+ pm_runtime_use_autosuspend(dev->dev);
+ pm_runtime_set_autosuspend_delay(dev->dev, MSM_SLIM_AUTOSUSPEND);
+ pm_runtime_set_suspended(dev->dev);
+ pm_runtime_enable(dev->dev);
+
+ INIT_WORK(&dev->slave_notify, ngd_laddr_lookup);
+ INIT_WORK(&dev->qmi.ssr_down, ngd_adsp_down);
+ INIT_WORK(&dev->qmi.ssr_up, ngd_adsp_up);
dev->qmi.nb.notifier_call = ngd_qmi_available;
+ pm_runtime_get_noresume(dev->dev);
ret = qmi_svc_event_notifier_register(SLIMBUS_QMI_SVC_ID,
SLIMBUS_QMI_INS_ID, &dev->qmi.nb);
if (ret) {
@@ -873,6 +1067,7 @@
goto qmi_register_failed;
}
+
/* Fire up the Rx message queue thread */
dev->rx_msgq_thread = kthread_run(ngd_slim_rx_msgq_thread, dev,
NGD_SLIM_NAME "_ngd_msgq_thread");
@@ -914,7 +1109,6 @@
qmi_svc_event_notifier_unregister(SLIMBUS_QMI_SVC_ID,
SLIMBUS_QMI_INS_ID, &dev->qmi.nb);
pm_runtime_disable(&pdev->dev);
- pm_runtime_set_suspended(&pdev->dev);
free_irq(dev->irq, dev);
slim_del_controller(&dev->ctrl);
kthread_stop(dev->rx_msgq_thread);
@@ -938,31 +1132,12 @@
* functions to be called from system suspend/resume. So they are not
* inside ifdef CONFIG_PM_RUNTIME
*/
-#ifdef CONFIG_PM_SLEEP
-static int ngd_slim_runtime_suspend(struct device *device)
-{
- struct platform_device *pdev = to_platform_device(device);
- struct msm_slim_ctrl *dev = platform_get_drvdata(pdev);
- int ret;
- dev_dbg(device, "pm_runtime: suspending...\n");
- dev->state = MSM_CTRL_SLEEPING;
- ret = slim_ctrl_clk_pause(&dev->ctrl, false, SLIM_CLK_UNSPECIFIED);
- if (ret) {
- dev_err(device, "clk pause not entered:%d", ret);
- dev->state = MSM_CTRL_AWAKE;
- } else {
- dev->state = MSM_CTRL_ASLEEP;
- }
- return ret;
-}
-
static int ngd_slim_runtime_resume(struct device *device)
{
struct platform_device *pdev = to_platform_device(device);
struct msm_slim_ctrl *dev = platform_get_drvdata(pdev);
int ret = 0;
- dev_dbg(device, "pm_runtime: resuming...\n");
- if (dev->state == MSM_CTRL_ASLEEP)
+ if (dev->state >= MSM_CTRL_ASLEEP)
ret = slim_ctrl_clk_pause(&dev->ctrl, true, 0);
if (ret) {
dev_err(device, "clk pause not exited:%d", ret);
@@ -973,6 +1148,24 @@
return ret;
}
+#ifdef CONFIG_PM_SLEEP
+static int ngd_slim_runtime_suspend(struct device *device)
+{
+ struct platform_device *pdev = to_platform_device(device);
+ struct msm_slim_ctrl *dev = platform_get_drvdata(pdev);
+ int ret = 0;
+ dev->state = MSM_CTRL_SLEEPING;
+ ret = slim_ctrl_clk_pause(&dev->ctrl, false, SLIM_CLK_UNSPECIFIED);
+ if (ret) {
+ if (ret != -EBUSY)
+ dev_err(device, "clk pause not entered:%d", ret);
+ dev->state = MSM_CTRL_AWAKE;
+ } else {
+ dev->state = MSM_CTRL_ASLEEP;
+ }
+ return ret;
+}
+
static int ngd_slim_suspend(struct device *dev)
{
int ret = -EBUSY;
diff --git a/drivers/slimbus/slim-msm.c b/drivers/slimbus/slim-msm.c
index c62ac27..3e19f9b 100644
--- a/drivers/slimbus/slim-msm.c
+++ b/drivers/slimbus/slim-msm.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, Code Aurora Forum. 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
@@ -9,7 +9,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
-
#include <linux/pm_runtime.h>
#include <linux/dma-mapping.h>
#include <linux/slimbus/slimbus.h>
@@ -378,53 +377,19 @@
return ret;
}
-static int msm_slim_init_rx_msgq(struct msm_slim_ctrl *dev, u32 pipe_reg)
+int msm_slim_connect_endp(struct msm_slim_ctrl *dev,
+ struct msm_slim_endp *endpoint,
+ struct completion *notify)
{
int i, ret;
- u32 pipe_offset;
- struct msm_slim_endp *endpoint = &dev->rx_msgq;
- struct sps_connect *config = &endpoint->config;
- struct sps_mem_buffer *descr = &config->desc;
- struct sps_mem_buffer *mem = &endpoint->buf;
- struct completion *notify = &dev->rx_msgq_notify;
-
struct sps_register_event sps_error_event; /* SPS_ERROR */
struct sps_register_event sps_descr_event; /* DESCR_DONE */
-
- init_completion(notify);
- if (!dev->use_rx_msgqs)
- return 0;
-
- /* Allocate the endpoint */
- ret = msm_slim_init_endpoint(dev, endpoint);
- if (ret) {
- dev_err(dev->dev, "init_endpoint failed 0x%x\n", ret);
- goto sps_init_endpoint_failed;
- }
-
- /* Get the pipe indices for the message queues */
- pipe_offset = (readl_relaxed(dev->base + pipe_reg) & 0xfc) >> 2;
- dev_dbg(dev->dev, "Message queue pipe offset %d\n", pipe_offset);
-
- config->mode = SPS_MODE_SRC;
- config->source = dev->bam.hdl;
- config->destination = SPS_DEV_HANDLE_MEM;
- config->src_pipe_index = pipe_offset;
- config->options = SPS_O_DESC_DONE | SPS_O_ERROR |
- SPS_O_ACK_TRANSFERS | SPS_O_AUTO_ENABLE;
-
- /* Allocate memory for the FIFO descriptors */
- ret = msm_slim_sps_mem_alloc(dev, descr,
- MSM_SLIM_DESC_NUM * sizeof(struct sps_iovec));
- if (ret) {
- dev_err(dev->dev, "unable to allocate SPS descriptors\n");
- goto alloc_descr_failed;
- }
+ struct sps_connect *config = &endpoint->config;
ret = sps_connect(endpoint->sps, config);
if (ret) {
dev_err(dev->dev, "sps_connect failed 0x%x\n", ret);
- goto sps_connect_failed;
+ return ret;
}
memset(&sps_descr_event, 0x00, sizeof(sps_descr_event));
@@ -453,13 +418,6 @@
goto sps_reg_event_failed;
}
- /* Allocate memory for the message buffer(s), N descrs, 4-byte mesg */
- ret = msm_slim_sps_mem_alloc(dev, mem, MSM_SLIM_DESC_NUM * 4);
- if (ret) {
- dev_err(dev->dev, "dma_alloc_coherent failed\n");
- goto alloc_buffer_failed;
- }
-
/*
* Call transfer_one for each 4-byte buffer
* Use (buf->size/4) - 1 for the number of buffer to post
@@ -475,20 +433,74 @@
}
return 0;
-
sps_transfer_failed:
- msm_slim_sps_mem_free(dev, mem);
-alloc_buffer_failed:
memset(&sps_error_event, 0x00, sizeof(sps_error_event));
sps_register_event(endpoint->sps, &sps_error_event);
sps_reg_event_failed:
sps_disconnect(endpoint->sps);
-sps_connect_failed:
+ return ret;
+}
+static int msm_slim_init_rx_msgq(struct msm_slim_ctrl *dev, u32 pipe_reg)
+{
+ int ret;
+ u32 pipe_offset;
+ struct msm_slim_endp *endpoint = &dev->rx_msgq;
+ struct sps_connect *config = &endpoint->config;
+ struct sps_mem_buffer *descr = &config->desc;
+ struct sps_mem_buffer *mem = &endpoint->buf;
+ struct completion *notify = &dev->rx_msgq_notify;
+
+ init_completion(notify);
+ if (dev->use_rx_msgqs == MSM_MSGQ_DISABLED)
+ return 0;
+
+ /* Allocate the endpoint */
+ ret = msm_slim_init_endpoint(dev, endpoint);
+ if (ret) {
+ dev_err(dev->dev, "init_endpoint failed 0x%x\n", ret);
+ goto sps_init_endpoint_failed;
+ }
+
+ /* Get the pipe indices for the message queues */
+ pipe_offset = (readl_relaxed(dev->base + pipe_reg) & 0xfc) >> 2;
+ dev_dbg(dev->dev, "Message queue pipe offset %d\n", pipe_offset);
+
+ config->mode = SPS_MODE_SRC;
+ config->source = dev->bam.hdl;
+ config->destination = SPS_DEV_HANDLE_MEM;
+ config->src_pipe_index = pipe_offset;
+ config->options = SPS_O_DESC_DONE | SPS_O_ERROR |
+ SPS_O_ACK_TRANSFERS | SPS_O_AUTO_ENABLE;
+
+ /* Allocate memory for the FIFO descriptors */
+ ret = msm_slim_sps_mem_alloc(dev, descr,
+ MSM_SLIM_DESC_NUM * sizeof(struct sps_iovec));
+ if (ret) {
+ dev_err(dev->dev, "unable to allocate SPS descriptors\n");
+ goto alloc_descr_failed;
+ }
+
+ /* Allocate memory for the message buffer(s), N descrs, 4-byte mesg */
+ ret = msm_slim_sps_mem_alloc(dev, mem, MSM_SLIM_DESC_NUM * 4);
+ if (ret) {
+ dev_err(dev->dev, "dma_alloc_coherent failed\n");
+ goto alloc_buffer_failed;
+ }
+
+ ret = msm_slim_connect_endp(dev, endpoint, notify);
+
+ if (!ret) {
+ dev->use_rx_msgqs = MSM_MSGQ_ENABLED;
+ return 0;
+ }
+
+ msm_slim_sps_mem_free(dev, mem);
+alloc_buffer_failed:
msm_slim_sps_mem_free(dev, descr);
alloc_descr_failed:
msm_slim_free_endpoint(endpoint);
sps_init_endpoint_failed:
- dev->use_rx_msgqs = 0;
+ dev->use_rx_msgqs = MSM_MSGQ_DISABLED;
return ret;
}
@@ -517,6 +529,8 @@
},
};
+ if (dev->bam.hdl)
+ goto init_rx_msgq;
bam_props.ee = dev->ee;
bam_props.virt_addr = dev->bam.base;
bam_props.phys_addr = bam_mem->start;
@@ -548,7 +562,7 @@
ret = sps_register_bam_device(&bam_props, &bam_handle);
if (ret) {
dev_err(dev->dev, "disabling BAM: reg-bam failed 0x%x\n", ret);
- dev->use_rx_msgqs = 0;
+ dev->use_rx_msgqs = MSM_MSGQ_DISABLED;
goto init_rx_msgq;
}
dev->bam.hdl = bam_handle;
@@ -565,9 +579,9 @@
return ret;
}
-void msm_slim_sps_exit(struct msm_slim_ctrl *dev)
+void msm_slim_sps_exit(struct msm_slim_ctrl *dev, bool dereg)
{
- if (dev->use_rx_msgqs) {
+ if (dev->use_rx_msgqs == MSM_MSGQ_ENABLED) {
struct msm_slim_endp *endpoint = &dev->rx_msgq;
struct sps_connect *config = &endpoint->config;
struct sps_mem_buffer *descr = &config->desc;
@@ -579,7 +593,11 @@
sps_disconnect(endpoint->sps);
msm_slim_sps_mem_free(dev, descr);
msm_slim_free_endpoint(endpoint);
+ dev->use_rx_msgqs = MSM_MSGQ_RESET;
+ }
+ if (dereg) {
sps_deregister_bam_device(dev->bam.hdl);
+ dev->bam.hdl = 0L;
}
}
@@ -589,6 +607,11 @@
#define SLIMBUS_QMI_POWER_REQ_V01 0x0021
#define SLIMBUS_QMI_POWER_RESP_V01 0x0021
+#define SLIMBUS_QMI_POWER_REQ_MAX_MSG_LEN 7
+#define SLIMBUS_QMI_POWER_RESP_MAX_MSG_LEN 7
+#define SLIMBUS_QMI_SELECT_INSTANCE_REQ_MAX_MSG_LEN 14
+#define SLIMBUS_QMI_SELECT_INSTANCE_RESP_MAX_MSG_LEN 7
+
enum slimbus_mode_enum_type_v01 {
/* To force a 32 bit signed enum. Do not change or use*/
SLIMBUS_MODE_ENUM_TYPE_MIN_ENUM_VAL_V01 = INT_MIN,
@@ -791,11 +814,11 @@
int rc;
req_desc.msg_id = SLIMBUS_QMI_SELECT_INSTANCE_REQ_V01;
- req_desc.max_msg_len = sizeof(*req);
+ req_desc.max_msg_len = SLIMBUS_QMI_SELECT_INSTANCE_REQ_MAX_MSG_LEN;
req_desc.ei_array = slimbus_select_inst_req_msg_v01_ei;
resp_desc.msg_id = SLIMBUS_QMI_SELECT_INSTANCE_RESP_V01;
- resp_desc.max_msg_len = sizeof(resp);
+ resp_desc.max_msg_len = SLIMBUS_QMI_SELECT_INSTANCE_RESP_MAX_MSG_LEN;
resp_desc.ei_array = slimbus_select_inst_resp_msg_v01_ei;
rc = qmi_send_req_wait(dev->qmi.handle, &req_desc, req, sizeof(*req),
@@ -823,11 +846,11 @@
int rc;
req_desc.msg_id = SLIMBUS_QMI_POWER_REQ_V01;
- req_desc.max_msg_len = sizeof(*req);
+ req_desc.max_msg_len = SLIMBUS_QMI_POWER_REQ_MAX_MSG_LEN;
req_desc.ei_array = slimbus_power_req_msg_v01_ei;
resp_desc.msg_id = SLIMBUS_QMI_POWER_RESP_V01;
- resp_desc.max_msg_len = sizeof(resp);
+ resp_desc.max_msg_len = SLIMBUS_QMI_POWER_RESP_MAX_MSG_LEN;
resp_desc.ei_array = slimbus_power_resp_msg_v01_ei;
rc = qmi_send_req_wait(dev->qmi.handle, &req_desc, req, sizeof(*req),
diff --git a/drivers/slimbus/slim-msm.h b/drivers/slimbus/slim-msm.h
index 3daf7ee..6e329b3 100644
--- a/drivers/slimbus/slim-msm.h
+++ b/drivers/slimbus/slim-msm.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, Code Aurora Forum. 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
@@ -152,6 +152,13 @@
MSM_CTRL_AWAKE,
MSM_CTRL_SLEEPING,
MSM_CTRL_ASLEEP,
+ MSM_CTRL_DOWN,
+};
+
+enum msm_slim_msgq {
+ MSM_MSGQ_DISABLED,
+ MSM_MSGQ_RESET,
+ MSM_MSGQ_ENABLED,
};
struct msm_slim_sps_bam {
@@ -176,6 +183,8 @@
struct kthread_worker kworker;
struct completion qmi_comp;
struct notifier_block nb;
+ struct work_struct ssr_down;
+ struct work_struct ssr_up;
};
struct msm_slim_ctrl {
@@ -206,14 +215,16 @@
struct clk *hclk;
struct mutex tx_lock;
u8 pgdla;
- bool use_rx_msgqs;
+ enum msm_slim_msgq use_rx_msgqs;
int pipe_b;
struct completion reconf;
bool reconf_busy;
bool chan_active;
enum msm_ctrl_state state;
+ struct completion ctrl_up;
int nsats;
u32 ver;
+ struct work_struct slave_notify;
struct msm_slim_qmi qmi;
};
@@ -266,8 +277,11 @@
int msm_slim_rx_msgq_get(struct msm_slim_ctrl *dev, u32 *data, int offset);
int msm_slim_sps_init(struct msm_slim_ctrl *dev, struct resource *bam_mem,
u32 pipe_reg, bool remote);
-void msm_slim_sps_exit(struct msm_slim_ctrl *dev);
+void msm_slim_sps_exit(struct msm_slim_ctrl *dev, bool dereg);
+int msm_slim_connect_endp(struct msm_slim_ctrl *dev,
+ struct msm_slim_endp *endpoint,
+ struct completion *notify);
void msm_slim_qmi_exit(struct msm_slim_ctrl *dev);
int msm_slim_qmi_init(struct msm_slim_ctrl *dev, bool apps_is_master);
int msm_slim_qmi_power_request(struct msm_slim_ctrl *dev, bool active);
diff --git a/drivers/slimbus/slimbus.c b/drivers/slimbus/slimbus.c
index c320e46..e5b3158 100644
--- a/drivers/slimbus/slimbus.c
+++ b/drivers/slimbus/slimbus.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, Code Aurora Forum. 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
@@ -3047,6 +3047,7 @@
for (i = 0; i < ctrl->last_tid; i++) {
if (ctrl->txnt[i]) {
ret = -EBUSY;
+ pr_info("slim_clk_pause: txn-rsp for %d pending", i);
mutex_unlock(&ctrl->m_ctrl);
return -EBUSY;
}
@@ -3057,6 +3058,7 @@
mutex_lock(&ctrl->sched.m_reconf);
/* Data channels active */
if (ctrl->sched.usedslots) {
+ pr_info("slim_clk_pause: data channel active");
ret = -EBUSY;
goto clk_pause_ret;
}
diff --git a/drivers/tty/serial/msm_serial_hs.c b/drivers/tty/serial/msm_serial_hs.c
index 2d73af6..7645df4 100644
--- a/drivers/tty/serial/msm_serial_hs.c
+++ b/drivers/tty/serial/msm_serial_hs.c
@@ -54,18 +54,35 @@
#include <linux/device.h>
#include <linux/wakelock.h>
#include <linux/debugfs.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
#include <asm/atomic.h>
#include <asm/irq.h>
#include <mach/hardware.h>
#include <mach/dma.h>
+#include <mach/sps.h>
#include <mach/msm_serial_hs.h>
+#include <mach/msm_bus.h>
#include "msm_serial_hs_hwreg.h"
+#define UART_SPS_CONS_PERIPHERAL 0
+#define UART_SPS_PROD_PERIPHERAL 1
static int hs_serial_debug_mask = 1;
module_param_named(debug_mask, hs_serial_debug_mask,
int, S_IRUGO | S_IWUSR | S_IWGRP);
+/*
+ * There are 3 different kind of UART Core available on MSM.
+ * High Speed UART (i.e. Legacy HSUART), GSBI based HSUART
+ * and BSLP based HSUART.
+ */
+enum uart_core_type {
+ LEGACY_HSUART,
+ GSBI_HSUART,
+ BLSP_HSUART,
+};
enum flush_reason {
FLUSH_NONE,
@@ -92,6 +109,17 @@
CLK_REQ_OFF_RXSTALE_FLUSHED,
};
+/* SPS data structures to support HSUART with BAM
+ * @sps_pipe - This struct defines BAM pipe descriptor
+ * @sps_connect - This struct defines a connection's end point
+ * @sps_register - This struct defines a event registration parameters
+ */
+struct msm_hs_sps_ep_conn_data {
+ struct sps_pipe *pipe_handle;
+ struct sps_connect config;
+ struct sps_register_event event;
+};
+
struct msm_hs_tx {
unsigned int tx_ready_int_en; /* ok to dma more tx */
unsigned int dma_in_flight; /* tx dma in progress */
@@ -105,6 +133,7 @@
int tx_count;
dma_addr_t dma_base;
struct tasklet_struct tlet;
+ struct msm_hs_sps_ep_conn_data cons;
};
struct msm_hs_rx {
@@ -122,6 +151,7 @@
struct wake_lock wake_lock;
struct delayed_work flip_insert_work;
struct tasklet_struct tlet;
+ struct msm_hs_sps_ep_conn_data prod;
};
enum buffer_states {
@@ -168,7 +198,24 @@
struct work_struct clock_off_w; /* work for actual clock off */
struct workqueue_struct *hsuart_wq; /* hsuart workqueue */
struct mutex clk_mutex; /* mutex to guard against clock off/clock on */
+ struct work_struct reset_bam_rx; /* work for reset bam rx endpoint */
+ struct work_struct disconnect_rx_endpoint; /* disconnect rx_endpoint */
bool tty_flush_receive;
+ enum uart_core_type uart_type;
+ u32 bam_handle;
+ resource_size_t bam_mem;
+ int bam_irq;
+ unsigned char __iomem *bam_base;
+ unsigned int bam_tx_ep_pipe_index;
+ unsigned int bam_rx_ep_pipe_index;
+ /* struct sps_event_notify is an argument passed when triggering a
+ * callback event object registered for an SPS connection end point.
+ */
+ struct sps_event_notify notify;
+ /* bus client handler */
+ u32 bus_perf_client;
+ /* BLSP UART required BUS Scaling data */
+ struct msm_bus_scale_pdata *bus_scale_table;
};
#define MSM_UARTDM_BURST_SIZE 16 /* DM burst size (in bytes) */
@@ -176,12 +223,17 @@
#define UARTDM_RX_BUF_SIZE 512
#define RETRY_TIMEOUT 5
#define UARTDM_NR 256
+#define BAM_PIPE_MIN 0
+#define BAM_PIPE_MAX 11
static struct dentry *debug_base;
static struct msm_hs_port q_uart_port[UARTDM_NR];
static struct platform_driver msm_serial_hs_platform_driver;
static struct uart_driver msm_hs_driver;
static struct uart_ops msm_hs_ops;
+static void msm_hs_start_rx_locked(struct uart_port *uport);
+static void msm_serial_hs_rx_tlet(unsigned long tlet_ptr);
+static void flip_insert_work(struct work_struct *work);
#define UARTDM_TO_MSM(uart_port) \
container_of((uart_port), struct msm_hs_port, uport)
@@ -244,7 +296,10 @@
/* assume gsbi uart if gsbi resource found in pdata */
return ((msm_uport->mapped_gsbi != NULL));
}
-
+static unsigned int is_blsp_uart(struct msm_hs_port *msm_uport)
+{
+ return (msm_uport->uart_type == BLSP_HSUART);
+}
static inline unsigned int msm_hs_read(struct uart_port *uport,
unsigned int offset)
{
@@ -320,13 +375,21 @@
if (val) {
spin_lock_irqsave(&uport->lock, flags);
ret = msm_hs_read(uport, UARTDM_MR2_ADDR);
- ret |= UARTDM_MR2_LOOP_MODE_BMSK;
+ if (is_blsp_uart(msm_uport))
+ ret |= (UARTDM_MR2_LOOP_MODE_BMSK |
+ UARTDM_MR2_RFR_CTS_LOOP_MODE_BMSK);
+ else
+ ret |= UARTDM_MR2_LOOP_MODE_BMSK;
msm_hs_write(uport, UARTDM_MR2_ADDR, ret);
spin_unlock_irqrestore(&uport->lock, flags);
} else {
spin_lock_irqsave(&uport->lock, flags);
ret = msm_hs_read(uport, UARTDM_MR2_ADDR);
- ret &= ~UARTDM_MR2_LOOP_MODE_BMSK;
+ if (is_blsp_uart(msm_uport))
+ ret &= ~(UARTDM_MR2_LOOP_MODE_BMSK |
+ UARTDM_MR2_RFR_CTS_LOOP_MODE_BMSK);
+ else
+ ret &= ~UARTDM_MR2_LOOP_MODE_BMSK;
msm_hs_write(uport, UARTDM_MR2_ADDR, ret);
spin_unlock_irqrestore(&uport->lock, flags);
}
@@ -470,6 +533,89 @@
return 0;
}
+
+/* Connect a UART peripheral's SPS endpoint(consumer endpoint)
+ *
+ * Also registers a SPS callback function for the consumer
+ * process with the SPS driver
+ *
+ * @uport - Pointer to uart uport structure
+ *
+ * @return - 0 if successful else negative value.
+ *
+ */
+
+static int msm_hs_spsconnect_tx(struct uart_port *uport)
+{
+ int ret;
+ struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+ struct msm_hs_tx *tx = &msm_uport->tx;
+ struct sps_pipe *sps_pipe_handle = tx->cons.pipe_handle;
+ struct sps_connect *sps_config = &tx->cons.config;
+ struct sps_register_event *sps_event = &tx->cons.event;
+
+ /* Establish connection between peripheral and memory endpoint */
+ ret = sps_connect(sps_pipe_handle, sps_config);
+ if (ret) {
+ pr_err("msm_serial_hs: sps_connect() failed for tx!!\n"
+ "pipe_handle=0x%x ret=%d", (u32)sps_pipe_handle, ret);
+ return ret;
+ }
+ /* Register callback event for EOT (End of transfer) event. */
+ ret = sps_register_event(sps_pipe_handle, sps_event);
+ if (ret) {
+ pr_err("msm_serial_hs: sps_connect() failed for tx!!\n"
+ "pipe_handle=0x%x ret=%d", (u32)sps_pipe_handle, ret);
+ goto reg_event_err;
+ }
+ return 0;
+
+reg_event_err:
+ sps_disconnect(sps_pipe_handle);
+ return ret;
+}
+
+/* Connect a UART peripheral's SPS endpoint(producer endpoint)
+ *
+ * Also registers a SPS callback function for the producer
+ * process with the SPS driver
+ *
+ * @uport - Pointer to uart uport structure
+ *
+ * @return - 0 if successful else negative value.
+ *
+ */
+
+static int msm_hs_spsconnect_rx(struct uart_port *uport)
+{
+ int ret;
+ struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+ struct msm_hs_rx *rx = &msm_uport->rx;
+ struct sps_pipe *sps_pipe_handle = rx->prod.pipe_handle;
+ struct sps_connect *sps_config = &rx->prod.config;
+ struct sps_register_event *sps_event = &rx->prod.event;
+
+ /* Establish connection between peripheral and memory endpoint */
+ ret = sps_connect(sps_pipe_handle, sps_config);
+ if (ret) {
+ pr_err("msm_serial_hs: sps_connect() failed for rx!!\n"
+ "pipe_handle=0x%x ret=%d", (u32)sps_pipe_handle, ret);
+ return ret;
+ }
+ /* Register callback event for EOT (End of transfer) event. */
+ ret = sps_register_event(sps_pipe_handle, sps_event);
+ if (ret) {
+ pr_err("msm_serial_hs: sps_connect() failed for rx!!\n"
+ "pipe_handle=0x%x ret=%d", (u32)sps_pipe_handle, ret);
+ goto reg_event_err;
+ }
+ return 0;
+
+reg_event_err:
+ sps_disconnect(sps_pipe_handle);
+ return ret;
+}
+
/*
* programs the UARTDM_CSR register with correct bit rates
*
@@ -479,9 +625,8 @@
* Goal is to have around 8 ms before indicate stale.
* roundup (((Bit Rate * .008) / 10) + 1
*/
-static unsigned long msm_hs_set_bps_locked(struct uart_port *uport,
- unsigned int bps,
- unsigned long flags)
+static void msm_hs_set_bps_locked(struct uart_port *uport,
+ unsigned int bps)
{
unsigned long rxstale;
unsigned long data;
@@ -581,15 +726,11 @@
uport->uartclk = 7372800;
}
- spin_unlock_irqrestore(&uport->lock, flags);
if (clk_set_rate(msm_uport->clk, uport->uartclk)) {
printk(KERN_WARNING "Error setting clock rate on UART\n");
WARN_ON(1);
- spin_lock_irqsave(&uport->lock, flags);
- return flags;
}
- spin_lock_irqsave(&uport->lock, flags);
data = rxstale & UARTDM_IPR_STALE_LSB_BMSK;
data |= UARTDM_IPR_STALE_TIMEOUT_MSB_BMSK & (rxstale << 2);
@@ -601,7 +742,6 @@
*/
msm_hs_write(uport, UARTDM_CR_ADDR, RESET_TX);
msm_hs_write(uport, UARTDM_CR_ADDR, RESET_RX);
- return flags;
}
@@ -654,6 +794,23 @@
msm_hs_write(uport, UARTDM_IPR_ADDR, data);
}
+
+/* Reset BAM RX Endpoint Pipe Index from workqueue context*/
+
+static void hsuart_reset_bam_rx_work(struct work_struct *w)
+{
+ struct msm_hs_port *msm_uport = container_of(w, struct msm_hs_port,
+ reset_bam_rx);
+ struct uart_port *uport = &msm_uport->uport;
+ struct msm_hs_rx *rx = &msm_uport->rx;
+ struct sps_pipe *sps_pipe_handle = rx->prod.pipe_handle;
+
+ sps_disconnect(sps_pipe_handle);
+ msm_hs_spsconnect_rx(uport);
+
+ msm_serial_hs_rx_tlet((unsigned long) &rx->tlet);
+}
+
/*
* termios : new ktermios
* oldtermios: old ktermios previous setting
@@ -666,12 +823,12 @@
{
unsigned int bps;
unsigned long data;
- unsigned long flags;
unsigned int c_cflag = termios->c_cflag;
struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+ struct msm_hs_rx *rx = &msm_uport->rx;
+ struct sps_pipe *sps_pipe_handle = rx->prod.pipe_handle;
mutex_lock(&msm_uport->clk_mutex);
- spin_lock_irqsave(&uport->lock, flags);
/*
* Disable Rx channel of UARTDM
@@ -683,7 +840,13 @@
* suggested to do disable/reset or reset/disable at the same time.
*/
data = msm_hs_read(uport, UARTDM_DMEN_ADDR);
- data &= ~UARTDM_RX_DM_EN_BMSK;
+ if (is_blsp_uart(msm_uport)) {
+ /* Disable UARTDM RX BAM Interface */
+ data &= ~UARTDM_RX_BAM_ENABLE_BMSK;
+ } else {
+ data &= ~UARTDM_RX_DM_EN_BMSK;
+ }
+
msm_hs_write(uport, UARTDM_DMEN_ADDR, data);
/* 300 is the minimum baud support by the driver */
@@ -697,7 +860,7 @@
if (!uport->uartclk)
msm_hs_set_std_bps_locked(uport, bps);
else
- flags = msm_hs_set_bps_locked(uport, bps, flags);
+ msm_hs_set_bps_locked(uport, bps);
data = msm_hs_read(uport, UARTDM_MR2_ADDR);
data &= ~UARTDM_MR2_PARITY_MODE_BMSK;
@@ -775,13 +938,18 @@
* dsb requires here.
*/
mb();
- /* do discard flush */
- msm_dmov_flush(msm_uport->dma_rx_channel, 0);
+ if (is_blsp_uart(msm_uport)) {
+ sps_disconnect(sps_pipe_handle);
+ msm_hs_spsconnect_rx(uport);
+ msm_serial_hs_rx_tlet((unsigned long) &rx->tlet);
+ } else {
+ /* do discard flush */
+ msm_dmov_flush(msm_uport->dma_rx_channel, 0);
+ }
}
msm_hs_write(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg);
mb();
- spin_unlock_irqrestore(&uport->lock, flags);
mutex_unlock(&msm_uport->clk_mutex);
}
@@ -814,6 +982,20 @@
msm_uport->tx.tx_ready_int_en = 0;
}
+/* Disconnect BAM RX Endpoint Pipe Index from workqueue context*/
+static void hsuart_disconnect_rx_endpoint_work(struct work_struct *w)
+{
+ struct msm_hs_port *msm_uport = container_of(w, struct msm_hs_port,
+ disconnect_rx_endpoint);
+ struct msm_hs_rx *rx = &msm_uport->rx;
+ struct sps_pipe *sps_pipe_handle = rx->prod.pipe_handle;
+
+ sps_disconnect(sps_pipe_handle);
+ wake_lock_timeout(&msm_uport->rx.wake_lock, HZ / 2);
+ msm_uport->rx.flush = FLUSH_SHUTDOWN;
+ wake_up(&msm_uport->rx.wait);
+}
+
/*
* Standard API, Stop receiver as soon as possible.
*
@@ -829,7 +1011,10 @@
/* disable dlink */
data = msm_hs_read(uport, UARTDM_DMEN_ADDR);
- data &= ~UARTDM_RX_DM_EN_BMSK;
+ if (is_blsp_uart(msm_uport))
+ data &= ~UARTDM_RX_BAM_ENABLE_BMSK;
+ else
+ data &= ~UARTDM_RX_DM_EN_BMSK;
msm_hs_write(uport, UARTDM_DMEN_ADDR, data);
/* calling DMOV or CLOCK API. Hence mb() */
@@ -837,10 +1022,17 @@
/* Disable the receiver */
if (msm_uport->rx.flush == FLUSH_NONE) {
wake_lock(&msm_uport->rx.wake_lock);
- /* do discard flush */
- msm_dmov_flush(msm_uport->dma_rx_channel, 0);
+ if (is_blsp_uart(msm_uport)) {
+ msm_uport->rx.flush = FLUSH_STOP;
+ /* workqueue for BAM rx endpoint disconnect */
+ queue_work(msm_uport->hsuart_wq,
+ &msm_uport->disconnect_rx_endpoint);
+ } else {
+ /* do discard flush */
+ msm_dmov_flush(msm_uport->dma_rx_channel, 0);
+ }
}
- if (msm_uport->rx.flush != FLUSH_SHUTDOWN)
+ if (!is_blsp_uart(msm_uport) && msm_uport->rx.flush != FLUSH_SHUTDOWN)
msm_uport->rx.flush = FLUSH_STOP;
}
@@ -852,9 +1044,11 @@
int aligned_tx_count;
dma_addr_t src_addr;
dma_addr_t aligned_src_addr;
+ u32 flags = SPS_IOVEC_FLAG_EOT;
struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
struct msm_hs_tx *tx = &msm_uport->tx;
struct circ_buf *tx_buf = &msm_uport->uport.state->xmit;
+ struct sps_pipe *sps_pipe_handle;
if (uart_circ_empty(tx_buf) || uport->state->port.tty->stopped) {
msm_hs_stop_tx_locked(uport);
@@ -882,14 +1076,21 @@
dma_sync_single_for_device(uport->dev, aligned_src_addr,
aligned_tx_count, DMA_TO_DEVICE);
- tx->command_ptr->num_rows = (((tx_count + 15) >> 4) << 16) |
- ((tx_count + 15) >> 4);
- tx->command_ptr->src_row_addr = src_addr;
+ if (is_blsp_uart(msm_uport)) {
+ /* Issue TX BAM Start IFC command */
+ msm_hs_write(uport, UARTDM_CR_ADDR, START_TX_BAM_IFC);
+ } else {
+ tx->command_ptr->num_rows =
+ (((tx_count + 15) >> 4) << 16) |
+ ((tx_count + 15) >> 4);
+ tx->command_ptr->src_row_addr = src_addr;
- dma_sync_single_for_device(uport->dev, tx->mapped_cmd_ptr,
- sizeof(dmov_box), DMA_TO_DEVICE);
+ dma_sync_single_for_device(uport->dev, tx->mapped_cmd_ptr,
+ sizeof(dmov_box), DMA_TO_DEVICE);
- *tx->command_ptr_ptr = CMD_PTR_LP | DMOV_CMD_ADDR(tx->mapped_cmd_ptr);
+ *tx->command_ptr_ptr = CMD_PTR_LP |
+ DMOV_CMD_ADDR(tx->mapped_cmd_ptr);
+ }
/* Save tx_count to use in Callback */
tx->tx_count = tx_count;
@@ -901,16 +1102,28 @@
/* Calling next DMOV API. Hence mb() here. */
mb();
- dma_sync_single_for_device(uport->dev, tx->mapped_cmd_ptr_ptr,
- sizeof(u32), DMA_TO_DEVICE);
msm_uport->tx.flush = FLUSH_NONE;
- msm_dmov_enqueue_cmd(msm_uport->dma_tx_channel, &tx->xfer);
+
+ if (is_blsp_uart(msm_uport)) {
+ sps_pipe_handle = tx->cons.pipe_handle;
+ /* Queue transfer request to SPS */
+ sps_transfer_one(sps_pipe_handle, src_addr, tx_count,
+ msm_uport, flags);
+ } else {
+ dma_sync_single_for_device(uport->dev, tx->mapped_cmd_ptr_ptr,
+ sizeof(u32), DMA_TO_DEVICE);
+
+ msm_dmov_enqueue_cmd(msm_uport->dma_tx_channel, &tx->xfer);
+ }
}
/* Start to receive the next chunk of data */
static void msm_hs_start_rx_locked(struct uart_port *uport)
{
struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+ struct msm_hs_rx *rx = &msm_uport->rx;
+ struct sps_pipe *sps_pipe_handle;
+ u32 flags = SPS_IOVEC_FLAG_EOT;
unsigned int buffer_pending = msm_uport->rx.buffer_pending;
unsigned int data;
@@ -919,6 +1132,10 @@
printk(KERN_ERR "Error: rx started in buffer state = %x",
buffer_pending);
+ if (is_blsp_uart(msm_uport)) {
+ /* Issue RX BAM Start IFC command */
+ msm_hs_write(uport, UARTDM_CR_ADDR, START_RX_BAM_IFC);
+ }
msm_hs_write(uport, UARTDM_CR_ADDR, RESET_STALE_INT);
msm_hs_write(uport, UARTDM_DMRX_ADDR, UARTDM_RX_BUF_SIZE);
msm_hs_write(uport, UARTDM_CR_ADDR, STALE_EVENT_ENABLE);
@@ -929,15 +1146,29 @@
* disable in set_termios before configuring baud rate.
*/
data = msm_hs_read(uport, UARTDM_DMEN_ADDR);
- data |= UARTDM_RX_DM_EN_BMSK;
+ if (is_blsp_uart(msm_uport)) {
+ /* Enable UARTDM Rx BAM Interface */
+ data |= UARTDM_RX_BAM_ENABLE_BMSK;
+ } else {
+ data |= UARTDM_RX_DM_EN_BMSK;
+ }
+
msm_hs_write(uport, UARTDM_DMEN_ADDR, data);
msm_hs_write(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg);
/* Calling next DMOV API. Hence mb() here. */
mb();
msm_uport->rx.flush = FLUSH_NONE;
- msm_dmov_enqueue_cmd(msm_uport->dma_rx_channel, &msm_uport->rx.xfer);
+ if (is_blsp_uart(msm_uport)) {
+ sps_pipe_handle = rx->prod.pipe_handle;
+ /* Queue transfer request to SPS */
+ sps_transfer_one(sps_pipe_handle, rx->rbuffer,
+ UARTDM_RX_BUF_SIZE, msm_uport, flags);
+ } else {
+ msm_dmov_enqueue_cmd(msm_uport->dma_rx_channel,
+ &msm_uport->rx.xfer);
+ }
}
static void flip_insert_work(struct work_struct *work)
@@ -999,6 +1230,7 @@
{
int retval;
int rx_count;
+ static int remaining_rx_count, bytes_pending;
unsigned long status;
unsigned long flags;
unsigned int error_f = 0;
@@ -1075,6 +1307,20 @@
rx_count = msm_hs_read(uport, UARTDM_RX_TOTAL_SNAP_ADDR);
+ if (is_blsp_uart(msm_uport)) {
+ if (rx_count > UARTDM_RX_BUF_SIZE) {
+ if (bytes_pending) {
+ rx_count = remaining_rx_count;
+ bytes_pending = 0;
+ } else {
+ remaining_rx_count = rx_count -
+ UARTDM_RX_BUF_SIZE;
+ if (remaining_rx_count)
+ bytes_pending = 1;
+ rx_count = UARTDM_RX_BUF_SIZE;
+ }
+ }
+ }
/* order the read of rx.buffer */
rmb();
@@ -1124,6 +1370,31 @@
}
}
+/**
+ * Callback notification from SPS driver
+ *
+ * This callback function gets triggered called from
+ * SPS driver when requested SPS data transfer is
+ * completed.
+ *
+ */
+
+static void msm_hs_sps_tx_callback(struct sps_event_notify *notify)
+{
+ struct msm_hs_port *msm_uport =
+ (struct msm_hs_port *)
+ ((struct sps_event_notify *)notify)->user;
+
+ msm_uport->notify = *notify;
+ pr_debug("%s: sps ev_id=%d, addr=0x%x, size=0x%x, flags=0x%x\n",
+ __func__, notify->event_id,
+ notify->data.transfer.iovec.addr,
+ notify->data.transfer.iovec.size,
+ notify->data.transfer.iovec.flags);
+
+ tasklet_schedule(&msm_uport->tx.tlet);
+}
+
/*
* This routine is called when we are done with a DMA transfer
*
@@ -1170,6 +1441,33 @@
spin_unlock_irqrestore(&(msm_uport->uport.lock), flags);
}
+/**
+ * Callback notification from SPS driver
+ *
+ * This callback function gets triggered called from
+ * SPS driver when requested SPS data transfer is
+ * completed.
+ *
+ */
+
+static void msm_hs_sps_rx_callback(struct sps_event_notify *notify)
+{
+
+ struct msm_hs_port *msm_uport =
+ (struct msm_hs_port *)
+ ((struct sps_event_notify *)notify)->user;
+
+ msm_uport->notify = *notify;
+ pr_debug("%s: sps ev_id=%d, addr=0x%x, size=0x%x, flags=0x%x\n",
+ __func__, notify->event_id,
+ notify->data.transfer.iovec.addr,
+ notify->data.transfer.iovec.size,
+ notify->data.transfer.iovec.flags);
+
+ if (msm_uport->rx.flush == FLUSH_NONE)
+ tasklet_schedule(&msm_uport->rx.tlet);
+}
+
/*
* This routine is called when we are done with a DMA transfer or the
* a flush has been sent to the data mover driver.
@@ -1265,7 +1563,8 @@
{
struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
- msm_uport->tty_flush_receive = true;
+ if (msm_uport->tx.dma_in_flight)
+ msm_uport->tty_flush_receive = true;
}
/*
@@ -1328,6 +1627,7 @@
{
unsigned long sr_status;
unsigned long flags;
+ int ret;
struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
struct circ_buf *tx_buf = &uport->state->xmit;
@@ -1356,6 +1656,14 @@
switch (msm_uport->clk_req_off_state) {
case CLK_REQ_OFF_START:
msm_uport->clk_req_off_state = CLK_REQ_OFF_RXSTALE_ISSUED;
+ if (is_blsp_uart(msm_uport)) {
+ /* Stale interrupt when RX-FIFO is empty
+ * will fire if STALE_IRQ_EMPTY bit is set
+ * for UART Core v1.4
+ */
+ msm_hs_write(uport, UARTDM_BCR_ADDR,
+ UARTDM_BCR_STALE_IRQ_EMPTY);
+ }
msm_hs_write(uport, UARTDM_CR_ADDR, FORCE_STALE_EVENT);
/*
* Before returning make sure that device writel completed.
@@ -1400,6 +1708,15 @@
wake_unlock(&msm_uport->dma_wake_lock);
spin_unlock_irqrestore(&uport->lock, flags);
+
+ /* Reset PNOC Bus Scaling */
+ if (is_blsp_uart(msm_uport)) {
+ ret = msm_bus_scale_client_update_request(
+ msm_uport->bus_perf_client, 0);
+ if (ret)
+ pr_err("%s(): Failed to reset bus bw vote\n", __func__);
+ }
+
mutex_unlock(&msm_uport->clk_mutex);
return 1;
}
@@ -1458,13 +1775,25 @@
*/
mb();
- if (msm_uport->clk_req_off_state == CLK_REQ_OFF_RXSTALE_ISSUED)
+ if (msm_uport->clk_req_off_state ==
+ CLK_REQ_OFF_RXSTALE_ISSUED) {
msm_uport->clk_req_off_state =
CLK_REQ_OFF_FLUSH_ISSUED;
+ if (is_blsp_uart(msm_uport)) {
+ /* Reset BCR Register for UARTDM Core v14*/
+ msm_hs_write(uport, UARTDM_BCR_ADDR, 0x0);
+ }
+ }
+
if (rx->flush == FLUSH_NONE) {
rx->flush = FLUSH_DATA_READY;
- msm_dmov_flush(msm_uport->dma_rx_channel, 1);
+ if (is_blsp_uart(msm_uport)) {
+ queue_work(msm_uport->hsuart_wq,
+ &msm_uport->reset_bam_rx);
+ } else {
+ msm_dmov_flush(msm_uport->dma_rx_channel, 1);
+ }
}
}
/* tx ready interrupt */
@@ -1558,6 +1887,16 @@
wake_lock(&msm_uport->dma_wake_lock);
disable_irq_nosync(msm_uport->wakeup.irq);
spin_unlock_irqrestore(&uport->lock, flags);
+
+ /* Vote for PNOC BUS Scaling */
+ if (is_blsp_uart(msm_uport)) {
+ ret = msm_bus_scale_client_update_request(
+ msm_uport->bus_perf_client, 1);
+ if (ret)
+ pr_err("%s():Failed to vote for bus scaling.\n",
+ __func__);
+ }
+
ret = clk_prepare_enable(msm_uport->clk);
if (ret) {
dev_err(uport->dev, "Clock ON Failure"
@@ -1581,7 +1920,10 @@
msm_uport->rx.flush == FLUSH_SHUTDOWN) {
msm_hs_write(uport, UARTDM_CR_ADDR, RESET_RX);
data = msm_hs_read(uport, UARTDM_DMEN_ADDR);
- data |= UARTDM_RX_DM_EN_BMSK;
+ if (is_blsp_uart(msm_uport))
+ data |= UARTDM_RX_BAM_ENABLE_BMSK;
+ else
+ data |= UARTDM_RX_DM_EN_BMSK;
msm_hs_write(uport, UARTDM_DMEN_ADDR, data);
/* Complete above device write. Hence mb() here. */
mb();
@@ -1661,6 +2003,9 @@
pdev->dev.platform_data;
struct circ_buf *tx_buf = &uport->state->xmit;
struct msm_hs_tx *tx = &msm_uport->tx;
+ struct msm_hs_rx *rx = &msm_uport->rx;
+ struct sps_pipe *sps_pipe_handle_tx = tx->cons.pipe_handle;
+ struct sps_pipe *sps_pipe_handle_rx = rx->prod.pipe_handle;
rfr_level = uport->fifosize;
if (rfr_level > 16)
@@ -1682,6 +2027,24 @@
if (unlikely(pdata->gpio_config(1)))
dev_err(uport->dev, "Cannot configure gpios\n");
+
+ /* SPS Connect for BAM endpoints */
+ if (is_blsp_uart(msm_uport)) {
+ /* SPS connect for TX */
+ ret = msm_hs_spsconnect_tx(uport);
+ if (ret) {
+ pr_err("msm_serial_hs: SPS connect failed for TX");
+ goto deinit_uart_clk;
+ }
+
+ /* SPS connect for RX */
+ ret = msm_hs_spsconnect_rx(uport);
+ if (ret) {
+ pr_err("msm_serial_hs: SPS connect failed for RX");
+ goto sps_disconnect_tx;
+ }
+ }
+
/* Set auto RFR Level */
data = msm_hs_read(uport, UARTDM_MR1_ADDR);
data &= ~UARTDM_MR1_AUTO_RFR_LEVEL1_BMSK;
@@ -1697,8 +2060,13 @@
msm_hs_write(uport, UARTDM_IPR_ADDR, data);
}
- /* Enable Data Mover Mode */
- data = UARTDM_TX_DM_EN_BMSK | UARTDM_RX_DM_EN_BMSK;
+ if (is_blsp_uart(msm_uport)) {
+ /* Enable BAM mode */
+ data = UARTDM_TX_BAM_ENABLE_BMSK | UARTDM_RX_BAM_ENABLE_BMSK;
+ } else {
+ /* Enable Data Mover Mode */
+ data = UARTDM_TX_DM_EN_BMSK | UARTDM_RX_DM_EN_BMSK;
+ }
msm_hs_write(uport, UARTDM_DMEN_ADDR, data);
/* Reset TX */
@@ -1719,18 +2087,20 @@
tx->tx_ready_int_en = 0;
tx->dma_in_flight = 0;
- tx->xfer.complete_func = msm_hs_dmov_tx_callback;
+ if (!is_blsp_uart(msm_uport)) {
+ tx->xfer.complete_func = msm_hs_dmov_tx_callback;
- tx->command_ptr->cmd = CMD_LC |
- CMD_DST_CRCI(msm_uport->dma_tx_crci) | CMD_MODE_BOX;
+ tx->command_ptr->cmd = CMD_LC |
+ CMD_DST_CRCI(msm_uport->dma_tx_crci) | CMD_MODE_BOX;
- tx->command_ptr->src_dst_len = (MSM_UARTDM_BURST_SIZE << 16)
+ tx->command_ptr->src_dst_len = (MSM_UARTDM_BURST_SIZE << 16)
| (MSM_UARTDM_BURST_SIZE);
- tx->command_ptr->row_offset = (MSM_UARTDM_BURST_SIZE << 16);
+ tx->command_ptr->row_offset = (MSM_UARTDM_BURST_SIZE << 16);
- tx->command_ptr->dst_row_addr =
- msm_uport->uport.mapbase + UARTDM_TF_ADDR;
+ tx->command_ptr->dst_row_addr =
+ msm_uport->uport.mapbase + UARTDM_TF_ADDR;
+ }
msm_uport->imr_reg |= UARTDM_ISR_RXSTALE_BMSK;
/* Enable reading the current CTS, no harm even if CTS is ignored */
@@ -1747,7 +2117,7 @@
ret = irq_set_irq_wake(msm_uport->wakeup.irq, 1);
if (unlikely(ret)) {
pr_err("%s():Err setting wakeup irq\n", __func__);
- goto deinit_uart_clk;
+ goto sps_disconnect_rx;
}
}
@@ -1771,6 +2141,15 @@
disable_irq(msm_uport->wakeup.irq);
}
+ /* Vote for PNOC BUS Scaling */
+ if (is_blsp_uart(msm_uport)) {
+ ret = msm_bus_scale_client_update_request(
+ msm_uport->bus_perf_client, 1);
+ if (ret)
+ pr_err("%s(): Failed to vote for bus scaling\n",
+ __func__);
+ }
+
spin_lock_irqsave(&uport->lock, flags);
msm_hs_start_rx_locked(uport);
@@ -1787,6 +2166,12 @@
free_irq(uport->irq, msm_uport);
free_wake_irq:
irq_set_irq_wake(msm_uport->wakeup.irq, 0);
+sps_disconnect_rx:
+ if (is_blsp_uart(msm_uport))
+ sps_disconnect(sps_pipe_handle_rx);
+sps_disconnect_tx:
+ if (is_blsp_uart(msm_uport))
+ sps_disconnect(sps_pipe_handle_tx);
deinit_uart_clk:
clk_disable_unprepare(msm_uport->clk);
if (msm_uport->pclk)
@@ -1804,24 +2189,6 @@
struct msm_hs_tx *tx = &msm_uport->tx;
struct msm_hs_rx *rx = &msm_uport->rx;
- /* Allocate the command pointer. Needs to be 64 bit aligned */
- tx->command_ptr = kmalloc(sizeof(dmov_box), GFP_KERNEL | __GFP_DMA);
- if (!tx->command_ptr)
- return -ENOMEM;
-
- tx->command_ptr_ptr = kmalloc(sizeof(u32), GFP_KERNEL | __GFP_DMA);
- if (!tx->command_ptr_ptr) {
- ret = -ENOMEM;
- goto free_tx_command_ptr;
- }
-
- tx->mapped_cmd_ptr = dma_map_single(uport->dev, tx->command_ptr,
- sizeof(dmov_box), DMA_TO_DEVICE);
- tx->mapped_cmd_ptr_ptr = dma_map_single(uport->dev,
- tx->command_ptr_ptr,
- sizeof(u32), DMA_TO_DEVICE);
- tx->xfer.cmdptr = DMOV_CMD_ADDR(tx->mapped_cmd_ptr_ptr);
-
init_waitqueue_head(&rx->wait);
init_waitqueue_head(&tx->wait);
wake_lock_init(&rx->wake_lock, WAKE_LOCK_SUSPEND, "msm_serial_hs_rx");
@@ -1848,12 +2215,40 @@
goto free_pool;
}
+ /* Set up Uart Receive */
+ msm_hs_write(uport, UARTDM_RFWR_ADDR, 0);
+
+ INIT_DELAYED_WORK(&rx->flip_insert_work, flip_insert_work);
+
+ if (is_blsp_uart(msm_uport))
+ return ret;
+
+ /* Allocate the command pointer. Needs to be 64 bit aligned */
+ tx->command_ptr = kmalloc(sizeof(dmov_box), GFP_KERNEL | __GFP_DMA);
+ if (!tx->command_ptr) {
+ return -ENOMEM;
+ goto free_rx_buffer;
+ }
+
+ tx->command_ptr_ptr = kmalloc(sizeof(u32), GFP_KERNEL | __GFP_DMA);
+ if (!tx->command_ptr_ptr) {
+ ret = -ENOMEM;
+ goto free_tx_command_ptr;
+ }
+
+ tx->mapped_cmd_ptr = dma_map_single(uport->dev, tx->command_ptr,
+ sizeof(dmov_box), DMA_TO_DEVICE);
+ tx->mapped_cmd_ptr_ptr = dma_map_single(uport->dev,
+ tx->command_ptr_ptr,
+ sizeof(u32), DMA_TO_DEVICE);
+ tx->xfer.cmdptr = DMOV_CMD_ADDR(tx->mapped_cmd_ptr_ptr);
+
/* Allocate the command pointer. Needs to be 64 bit aligned */
rx->command_ptr = kmalloc(sizeof(dmov_box), GFP_KERNEL | __GFP_DMA);
if (!rx->command_ptr) {
pr_err("%s(): cannot allocate rx->command_ptr", __func__);
ret = -ENOMEM;
- goto free_rx_buffer;
+ goto free_tx_command_ptr_ptr;
}
rx->command_ptr_ptr = kmalloc(sizeof(u32), GFP_KERNEL | __GFP_DMA);
@@ -1868,9 +2263,6 @@
rx->command_ptr->dst_row_addr = rx->rbuffer;
- /* Set up Uart Receive */
- msm_hs_write(uport, UARTDM_RFWR_ADDR, 0);
-
rx->xfer.complete_func = msm_hs_dmov_rx_callback;
rx->command_ptr->cmd = CMD_LC |
@@ -1890,13 +2282,21 @@
sizeof(u32), DMA_TO_DEVICE);
rx->xfer.cmdptr = DMOV_CMD_ADDR(rx->cmdptr_dmaaddr);
- INIT_DELAYED_WORK(&rx->flip_insert_work, flip_insert_work);
-
return ret;
free_rx_command_ptr:
kfree(rx->command_ptr);
+free_tx_command_ptr_ptr:
+ kfree(msm_uport->tx.command_ptr_ptr);
+ dma_unmap_single(uport->dev, msm_uport->tx.mapped_cmd_ptr_ptr,
+ sizeof(u32), DMA_TO_DEVICE);
+ dma_unmap_single(uport->dev, msm_uport->tx.mapped_cmd_ptr,
+ sizeof(dmov_box), DMA_TO_DEVICE);
+
+free_tx_command_ptr:
+ kfree(msm_uport->tx.command_ptr);
+
free_rx_buffer:
dma_pool_free(msm_uport->rx.pool, msm_uport->rx.buffer,
msm_uport->rx.rbuffer);
@@ -1909,47 +2309,400 @@
wake_lock_destroy(&msm_uport->dma_wake_lock);
tasklet_kill(&msm_uport->tx.tlet);
tasklet_kill(&msm_uport->rx.tlet);
- dma_unmap_single(uport->dev, msm_uport->tx.mapped_cmd_ptr_ptr,
- sizeof(u32), DMA_TO_DEVICE);
- dma_unmap_single(uport->dev, msm_uport->tx.mapped_cmd_ptr,
- sizeof(dmov_box), DMA_TO_DEVICE);
- kfree(msm_uport->tx.command_ptr_ptr);
-
-free_tx_command_ptr:
- kfree(msm_uport->tx.command_ptr);
return ret;
}
+struct msm_serial_hs_platform_data
+ *msm_hs_dt_to_pdata(struct platform_device *pdev)
+{
+ struct device_node *node = pdev->dev.of_node;
+ struct msm_serial_hs_platform_data *pdata;
+ int rx_to_inject, ret;
+
+ pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata) {
+ pr_err("unable to allocate memory for platform data\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ /* UART TX GPIO */
+ pdata->uart_tx_gpio = of_get_named_gpio(node,
+ "qcom,tx-gpio", 0);
+ if (pdata->uart_tx_gpio < 0)
+ pr_debug("uart_tx_gpio is not available\n");
+
+ /* UART RX GPIO */
+ pdata->uart_rx_gpio = of_get_named_gpio(node,
+ "qcom,rx-gpio", 0);
+ if (pdata->uart_rx_gpio < 0)
+ pr_debug("uart_rx_gpio is not available\n");
+
+ /* UART CTS GPIO */
+ pdata->uart_cts_gpio = of_get_named_gpio(node,
+ "qcom,cts-gpio", 0);
+ if (pdata->uart_cts_gpio < 0)
+ pr_debug("uart_cts_gpio is not available\n");
+
+ /* UART RFR GPIO */
+ pdata->uart_rfr_gpio = of_get_named_gpio(node,
+ "qcom,rfr-gpio", 0);
+ if (pdata->uart_rfr_gpio < 0)
+ pr_debug("uart_rfr_gpio is not available\n");
+
+ pdata->inject_rx_on_wakeup = of_property_read_bool(node,
+ "qcom,inject-rx-on-wakeup");
+
+ if (pdata->inject_rx_on_wakeup) {
+ ret = of_property_read_u32(node, "qcom,rx-char-to-inject",
+ &rx_to_inject);
+ if (ret < 0) {
+ pr_err("Error: Rx_char_to_inject not specified.\n");
+ return ERR_PTR(ret);
+ }
+ pdata->rx_to_inject = (char)rx_to_inject;
+ }
+
+ ret = of_property_read_u32(node, "qcom,bam-tx-ep-pipe-index",
+ &pdata->bam_tx_ep_pipe_index);
+ if (ret < 0) {
+ pr_err("Error: Getting UART BAM TX EP Pipe Index.\n");
+ return ERR_PTR(ret);
+ }
+
+ if (!(pdata->bam_tx_ep_pipe_index >= BAM_PIPE_MIN &&
+ pdata->bam_tx_ep_pipe_index <= BAM_PIPE_MAX)) {
+ pr_err("Error: Invalid UART BAM TX EP Pipe Index.\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ ret = of_property_read_u32(node, "qcom,bam-rx-ep-pipe-index",
+ &pdata->bam_rx_ep_pipe_index);
+ if (ret < 0) {
+ pr_err("Error: Getting UART BAM RX EP Pipe Index.\n");
+ return ERR_PTR(ret);
+ }
+
+ if (!(pdata->bam_rx_ep_pipe_index >= BAM_PIPE_MIN &&
+ pdata->bam_rx_ep_pipe_index <= BAM_PIPE_MAX)) {
+ pr_err("Error: Invalid UART BAM RX EP Pipe Index.\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ pr_debug("tx_ep_pipe_index:%d rx_ep_pipe_index:%d\n"
+ "tx_gpio:%d rx_gpio:%d rfr_gpio:%d cts_gpio:%d",
+ pdata->bam_tx_ep_pipe_index, pdata->bam_rx_ep_pipe_index,
+ pdata->uart_tx_gpio, pdata->uart_rx_gpio, pdata->uart_cts_gpio,
+ pdata->uart_rfr_gpio);
+
+ return pdata;
+}
+
+
+/**
+ * Deallocate UART peripheral's SPS endpoint
+ * @msm_uport - Pointer to msm_hs_port structure
+ * @ep - Pointer to sps endpoint data structure
+ */
+
+static void msm_hs_exit_ep_conn(struct msm_hs_port *msm_uport,
+ struct msm_hs_sps_ep_conn_data *ep)
+{
+ struct sps_pipe *sps_pipe_handle = ep->pipe_handle;
+ struct sps_connect *sps_config = &ep->config;
+
+ dma_free_coherent(msm_uport->uport.dev,
+ sps_config->desc.size,
+ &sps_config->desc.phys_base,
+ GFP_KERNEL);
+ sps_free_endpoint(sps_pipe_handle);
+}
+
+
+/**
+ * Allocate UART peripheral's SPS endpoint
+ *
+ * This function allocates endpoint context
+ * by calling appropriate SPS driver APIs.
+ *
+ * @msm_uport - Pointer to msm_hs_port structure
+ * @ep - Pointer to sps endpoint data structure
+ * @is_produce - 1 means Producer endpoint
+ * - 0 means Consumer endpoint
+ *
+ * @return - 0 if successful else negative value
+ */
+
+static int msm_hs_sps_init_ep_conn(struct msm_hs_port *msm_uport,
+ struct msm_hs_sps_ep_conn_data *ep,
+ bool is_producer)
+{
+ int rc = 0;
+ struct sps_pipe *sps_pipe_handle;
+ struct sps_connect *sps_config = &ep->config;
+ struct sps_register_event *sps_event = &ep->event;
+
+ /* Allocate endpoint context */
+ sps_pipe_handle = sps_alloc_endpoint();
+ if (!sps_pipe_handle) {
+ pr_err("msm_serial_hs: sps_alloc_endpoint() failed!!\n"
+ "is_producer=%d", is_producer);
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ /* Get default connection configuration for an endpoint */
+ rc = sps_get_config(sps_pipe_handle, sps_config);
+ if (rc) {
+ pr_err("msm_serial_hs: sps_get_config() failed!!\n"
+ "pipe_handle=0x%x rc=%d", (u32)sps_pipe_handle, rc);
+ goto get_config_err;
+ }
+
+ /* Modify the default connection configuration */
+ if (is_producer) {
+ /* For UART producer transfer, source is UART peripheral
+ where as destination is system memory */
+ sps_config->source = msm_uport->bam_handle;
+ sps_config->destination = SPS_DEV_HANDLE_MEM;
+ sps_config->mode = SPS_MODE_SRC;
+ sps_config->src_pipe_index = msm_uport->bam_rx_ep_pipe_index;
+ sps_config->dest_pipe_index = 0;
+ sps_config->options = SPS_O_EOT;
+ } else {
+ /* For UART consumer transfer, source is system memory
+ where as destination is UART peripheral */
+ sps_config->source = SPS_DEV_HANDLE_MEM;
+ sps_config->destination = msm_uport->bam_handle;
+ sps_config->mode = SPS_MODE_DEST;
+ sps_config->src_pipe_index = 0;
+ sps_config->dest_pipe_index = msm_uport->bam_tx_ep_pipe_index;
+ sps_config->options = SPS_O_EOT;
+ }
+
+ sps_config->event_thresh = 0x10;
+
+ /* Allocate maximum descriptor fifo size */
+ sps_config->desc.size = 65532;
+ sps_config->desc.base = dma_alloc_coherent(msm_uport->uport.dev,
+ sps_config->desc.size,
+ &sps_config->desc.phys_base,
+ GFP_KERNEL);
+ if (!sps_config->desc.base) {
+ rc = -ENOMEM;
+ pr_err("msm_serial_hs: dma_alloc_coherent() failed!!\n");
+ goto get_config_err;
+ }
+ memset(sps_config->desc.base, 0x00, sps_config->desc.size);
+
+ sps_event->mode = SPS_TRIGGER_CALLBACK;
+ sps_event->options = SPS_O_EOT;
+ if (is_producer)
+ sps_event->callback = msm_hs_sps_rx_callback;
+ else
+ sps_event->callback = msm_hs_sps_tx_callback;
+
+ sps_event->user = (void *)msm_uport;
+
+ /* Now save the sps pipe handle */
+ ep->pipe_handle = sps_pipe_handle;
+ pr_debug("msm_serial_hs: success !! %s: pipe_handle=0x%x\n"
+ "desc_fifo.phys_base=0x%x\n",
+ is_producer ? "READ" : "WRITE",
+ (u32)sps_pipe_handle, sps_config->desc.phys_base);
+ return 0;
+
+get_config_err:
+ sps_free_endpoint(sps_pipe_handle);
+out:
+ return rc;
+}
+
+/**
+ * Initialize SPS HW connected with UART core
+ *
+ * This function register BAM HW resources with
+ * SPS driver and then initialize 2 SPS endpoints
+ *
+ * msm_uport - Pointer to msm_hs_port structure
+ *
+ * @return - 0 if successful else negative value
+ */
+
+static int msm_hs_sps_init(struct msm_hs_port *msm_uport)
+{
+ int rc = 0;
+ struct sps_bam_props bam = {0};
+ u32 bam_handle;
+
+ rc = sps_phy2h(msm_uport->bam_mem, &bam_handle);
+ if (rc || !bam_handle) {
+ bam.phys_addr = msm_uport->bam_mem;
+ bam.virt_addr = msm_uport->bam_base;
+ /*
+ * This event thresold value is only significant for BAM-to-BAM
+ * transfer. It's ignored for BAM-to-System mode transfer.
+ */
+ bam.event_threshold = 0x10; /* Pipe event threshold */
+ bam.summing_threshold = 1; /* BAM event threshold */
+
+ /* SPS driver wll handle the UART BAM IRQ */
+ bam.irq = (u32)msm_uport->bam_irq;
+ bam.manage = SPS_BAM_MGR_LOCAL;
+
+ pr_debug("msm_serial_hs: bam physical base=0x%x\n",
+ (u32)bam.phys_addr);
+ pr_debug("msm_serial_hs: bam virtual base=0x%x\n",
+ (u32)bam.virt_addr);
+
+ /* Register UART Peripheral BAM device to SPS driver */
+ rc = sps_register_bam_device(&bam, &bam_handle);
+ if (rc) {
+ pr_err("msm_serial_hs: BAM device register failed\n");
+ return rc;
+ }
+ pr_info("msm_serial_hs: BAM device registered. bam_handle=0x%x",
+ msm_uport->bam_handle);
+ }
+ msm_uport->bam_handle = bam_handle;
+
+ rc = msm_hs_sps_init_ep_conn(msm_uport, &msm_uport->rx.prod,
+ UART_SPS_PROD_PERIPHERAL);
+ if (rc) {
+ pr_err("%s: Failed to Init Producer BAM-pipe", __func__);
+ goto deregister_bam;
+ }
+
+ rc = msm_hs_sps_init_ep_conn(msm_uport, &msm_uport->tx.cons,
+ UART_SPS_CONS_PERIPHERAL);
+ if (rc) {
+ pr_err("%s: Failed to Init Consumer BAM-pipe", __func__);
+ goto deinit_ep_conn_prod;
+ }
+ return 0;
+
+deinit_ep_conn_prod:
+ msm_hs_exit_ep_conn(msm_uport, &msm_uport->rx.prod);
+deregister_bam:
+ sps_deregister_bam_device(msm_uport->bam_handle);
+ return rc;
+}
+
static int __devinit msm_hs_probe(struct platform_device *pdev)
{
- int ret;
+ int ret = 0;
struct uart_port *uport;
struct msm_hs_port *msm_uport;
+ struct resource *core_resource;
+ struct resource *bam_resource;
struct resource *resource;
+ int core_irqres, bam_irqres;
struct msm_serial_hs_platform_data *pdata = pdev->dev.platform_data;
+ struct device_node *node = pdev->dev.of_node;
+
+ if (pdev->dev.of_node) {
+ dev_dbg(&pdev->dev, "device tree enabled\n");
+ pdata = msm_hs_dt_to_pdata(pdev);
+ if (IS_ERR(pdata))
+ return PTR_ERR(pdata);
+
+ of_property_read_u32(node, "cell-index",
+ &pdev->id);
+
+ pdev->dev.platform_data = pdata;
+ }
if (pdev->id < 0 || pdev->id >= UARTDM_NR) {
- printk(KERN_ERR "Invalid plaform device ID = %d\n", pdev->id);
+ pr_err("Invalid plaform device ID = %d\n", pdev->id);
return -EINVAL;
}
msm_uport = &q_uart_port[pdev->id];
uport = &msm_uport->uport;
-
uport->dev = &pdev->dev;
- resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (unlikely(!resource))
- return -ENXIO;
- uport->mapbase = resource->start; /* virtual address */
+ if (pdev->dev.of_node)
+ msm_uport->uart_type = BLSP_HSUART;
- uport->membase = ioremap(uport->mapbase, PAGE_SIZE);
- if (unlikely(!uport->membase))
- return -ENOMEM;
+ /* Get required resources for BAM HSUART */
+ if (is_blsp_uart(msm_uport)) {
+ core_resource = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM, "core_mem");
+ bam_resource = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM, "bam_mem");
+ core_irqres = platform_get_irq_byname(pdev, "core_irq");
+ bam_irqres = platform_get_irq_byname(pdev, "bam_irq");
- uport->irq = platform_get_irq(pdev, 0);
- if (unlikely((int)uport->irq < 0))
- return -ENXIO;
+ if (!core_resource) {
+ pr_err("Invalid core HSUART Resources.\n");
+ return -ENXIO;
+ }
+
+ if (!bam_resource) {
+ pr_err("Invalid BAM HSUART Resources.\n");
+ return -ENXIO;
+ }
+
+ if (!core_irqres) {
+ pr_err("Invalid core irqres Resources.\n");
+ return -ENXIO;
+ }
+ if (!bam_irqres) {
+ pr_err("Invalid bam irqres Resources.\n");
+ return -ENXIO;
+ }
+
+ uport->mapbase = core_resource->start;
+
+ uport->membase = ioremap(uport->mapbase,
+ resource_size(core_resource));
+ if (unlikely(!uport->membase)) {
+ pr_err("UART Resource ioremap Failed.\n");
+ return -ENOMEM;
+ }
+ msm_uport->bam_mem = bam_resource->start;
+ msm_uport->bam_base = ioremap(msm_uport->bam_mem,
+ resource_size(bam_resource));
+ if (unlikely(!msm_uport->bam_base)) {
+ pr_err("UART BAM Resource ioremap Failed.\n");
+ iounmap(uport->membase);
+ return -ENOMEM;
+ }
+
+ uport->irq = core_irqres;
+ msm_uport->bam_irq = bam_irqres;
+
+ msm_uport->bus_scale_table = msm_bus_cl_get_pdata(pdev);
+ if (!msm_uport->bus_scale_table) {
+ pr_err("BLSP UART: Bus scaling is disabled\n");
+ goto unmap_memory;
+ } else {
+ msm_uport->bus_perf_client =
+ msm_bus_scale_register_client
+ (msm_uport->bus_scale_table);
+ if (IS_ERR(&msm_uport->bus_perf_client)) {
+ pr_err("%s(): Bus client register failed.\n",
+ __func__);
+ goto unmap_memory;
+ }
+ }
+ } else {
+
+ resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (unlikely(!resource))
+ return -ENXIO;
+ uport->mapbase = resource->start;
+ uport->membase = ioremap(uport->mapbase,
+ resource_size(resource));
+ if (unlikely(!uport->membase))
+ return -ENOMEM;
+
+ uport->irq = platform_get_irq(pdev, 0);
+ if (unlikely((int)uport->irq < 0)) {
+ pr_err("UART IRQ Failed.\n");
+ iounmap(uport->membase);
+ return -ENXIO;
+ }
+ }
if (pdata == NULL)
msm_uport->wakeup.irq = -1;
@@ -1959,24 +2712,41 @@
msm_uport->wakeup.inject_rx = pdata->inject_rx_on_wakeup;
msm_uport->wakeup.rx_to_inject = pdata->rx_to_inject;
- if (unlikely(msm_uport->wakeup.irq < 0))
- return -ENXIO;
+ if (unlikely(msm_uport->wakeup.irq < 0)) {
+ ret = -ENXIO;
+ goto unmap_memory;
+ }
+ if (is_blsp_uart(msm_uport)) {
+ msm_uport->bam_tx_ep_pipe_index =
+ pdata->bam_tx_ep_pipe_index;
+ msm_uport->bam_rx_ep_pipe_index =
+ pdata->bam_rx_ep_pipe_index;
+ }
}
- resource = platform_get_resource_byname(pdev, IORESOURCE_DMA,
- "uartdm_channels");
- if (unlikely(!resource))
- return -ENXIO;
- msm_uport->dma_tx_channel = resource->start;
- msm_uport->dma_rx_channel = resource->end;
+ if (!is_blsp_uart(msm_uport)) {
- resource = platform_get_resource_byname(pdev, IORESOURCE_DMA,
- "uartdm_crci");
- if (unlikely(!resource))
- return -ENXIO;
- msm_uport->dma_tx_crci = resource->start;
- msm_uport->dma_rx_crci = resource->end;
+ resource = platform_get_resource_byname(pdev,
+ IORESOURCE_DMA, "uartdm_channels");
+ if (unlikely(!resource)) {
+ ret = -ENXIO;
+ goto unmap_memory;
+ }
+
+ msm_uport->dma_tx_channel = resource->start;
+ msm_uport->dma_rx_channel = resource->end;
+
+ resource = platform_get_resource_byname(pdev,
+ IORESOURCE_DMA, "uartdm_crci");
+ if (unlikely(!resource)) {
+ ret = -ENXIO;
+ goto unmap_memory;
+ }
+
+ msm_uport->dma_tx_crci = resource->start;
+ msm_uport->dma_rx_crci = resource->end;
+ }
uport->iotype = UPIO_MEM;
uport->fifosize = 64;
@@ -1986,8 +2756,10 @@
msm_uport->imr_reg = 0x0;
msm_uport->clk = clk_get(&pdev->dev, "core_clk");
- if (IS_ERR(msm_uport->clk))
- return PTR_ERR(msm_uport->clk);
+ if (IS_ERR(msm_uport->clk)) {
+ ret = PTR_ERR(msm_uport->clk);
+ goto unmap_memory;
+ }
msm_uport->pclk = clk_get(&pdev->dev, "iface_clk");
/*
@@ -2000,7 +2772,7 @@
ret = clk_set_rate(msm_uport->clk, uport->uartclk);
if (ret) {
printk(KERN_WARNING "Error setting clock rate on UART\n");
- return ret;
+ goto unmap_memory;
}
msm_uport->hsuart_wq = alloc_workqueue("k_hsuart",
@@ -2008,30 +2780,41 @@
if (!msm_uport->hsuart_wq) {
pr_err("%s(): Unable to create workqueue hsuart_wq\n",
__func__);
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto unmap_memory;
}
INIT_WORK(&msm_uport->clock_off_w, hsuart_clock_off_work);
+
+ /* Init work for Reset Rx bam endpoints */
+ INIT_WORK(&msm_uport->reset_bam_rx, hsuart_reset_bam_rx_work);
+
+ /* Init work for sps_disconnect in stop_rx_locked */
+ INIT_WORK(&msm_uport->disconnect_rx_endpoint,
+ hsuart_disconnect_rx_endpoint_work);
mutex_init(&msm_uport->clk_mutex);
+ /* Initialize SPS HW connected with UART core */
+ if (is_blsp_uart(msm_uport)) {
+ ret = msm_hs_sps_init(msm_uport);
+ if (unlikely(ret)) {
+ pr_err("SPS Initialization failed ! err=%d", ret);
+ goto workqueue_destroy;
+ }
+ }
+
clk_prepare_enable(msm_uport->clk);
if (msm_uport->pclk)
clk_prepare_enable(msm_uport->pclk);
ret = uartdm_init_port(uport);
if (unlikely(ret)) {
- clk_disable_unprepare(msm_uport->clk);
- if (msm_uport->pclk)
- clk_disable_unprepare(msm_uport->pclk);
- return ret;
+ goto err_clock;
}
/* configure the CR Protection to Enable */
msm_hs_write(uport, UARTDM_CR_ADDR, CR_PROTECTION_EN);
- clk_disable_unprepare(msm_uport->clk);
- if (msm_uport->pclk)
- clk_disable_unprepare(msm_uport->pclk);
/*
* Enable Command register protection before going ahead as this hw
@@ -2048,14 +2831,33 @@
ret = sysfs_create_file(&pdev->dev.kobj, &dev_attr_clock.attr);
if (unlikely(ret))
- return ret;
+ goto err_clock;
msm_serial_debugfs_init(msm_uport, pdev->id);
uport->line = pdev->id;
if (pdata != NULL && pdata->userid && pdata->userid <= UARTDM_NR)
uport->line = pdata->userid;
- return uart_add_one_port(&msm_hs_driver, uport);
+ ret = uart_add_one_port(&msm_hs_driver, uport);
+ if (!ret) {
+ clk_disable_unprepare(msm_uport->clk);
+ if (msm_uport->pclk)
+ clk_disable_unprepare(msm_uport->pclk);
+ return ret;
+ }
+
+err_clock:
+ clk_disable_unprepare(msm_uport->clk);
+ if (msm_uport->pclk)
+ clk_disable_unprepare(msm_uport->pclk);
+workqueue_destroy:
+ destroy_workqueue(msm_uport->hsuart_wq);
+unmap_memory:
+ iounmap(uport->membase);
+ if (is_blsp_uart(msm_uport))
+ iounmap(msm_uport->bam_base);
+
+ return ret;
}
static int __init msm_serial_hs_init(void)
@@ -2102,27 +2904,35 @@
struct platform_device *pdev = to_platform_device(uport->dev);
const struct msm_serial_hs_platform_data *pdata =
pdev->dev.platform_data;
+ struct msm_hs_tx *tx = &msm_uport->tx;
+ struct sps_pipe *sps_pipe_handle = tx->cons.pipe_handle;
if (msm_uport->tx.dma_in_flight) {
- spin_lock_irqsave(&uport->lock, flags);
- /* disable UART TX interface to DM */
- data = msm_hs_read(uport, UARTDM_DMEN_ADDR);
- data &= ~UARTDM_TX_DM_EN_BMSK;
- msm_hs_write(uport, UARTDM_DMEN_ADDR, data);
- /* turn OFF UART Transmitter */
- msm_hs_write(uport, UARTDM_CR_ADDR, UARTDM_CR_TX_DISABLE_BMSK);
- /* reset UART TX */
- msm_hs_write(uport, UARTDM_CR_ADDR, RESET_TX);
- /* reset UART TX Error */
- msm_hs_write(uport, UARTDM_CR_ADDR, RESET_TX_ERROR);
- msm_uport->tx.flush = FLUSH_STOP;
- spin_unlock_irqrestore(&uport->lock, flags);
- /* discard flush */
- msm_dmov_flush(msm_uport->dma_tx_channel, 0);
- ret = wait_event_timeout(msm_uport->tx.wait,
- msm_uport->tx.flush == FLUSH_SHUTDOWN, 100);
- if (!ret)
- pr_err("%s():HSUART TX Stalls.\n", __func__);
+ if (!is_blsp_uart(msm_uport)) {
+ spin_lock_irqsave(&uport->lock, flags);
+ /* disable UART TX interface to DM */
+ data = msm_hs_read(uport, UARTDM_DMEN_ADDR);
+ data &= ~UARTDM_TX_DM_EN_BMSK;
+ msm_hs_write(uport, UARTDM_DMEN_ADDR, data);
+ /* turn OFF UART Transmitter */
+ msm_hs_write(uport, UARTDM_CR_ADDR,
+ UARTDM_CR_TX_DISABLE_BMSK);
+ /* reset UART TX */
+ msm_hs_write(uport, UARTDM_CR_ADDR, RESET_TX);
+ /* reset UART TX Error */
+ msm_hs_write(uport, UARTDM_CR_ADDR, RESET_TX_ERROR);
+ msm_uport->tx.flush = FLUSH_STOP;
+ spin_unlock_irqrestore(&uport->lock, flags);
+ /* discard flush */
+ msm_dmov_flush(msm_uport->dma_tx_channel, 0);
+ ret = wait_event_timeout(msm_uport->tx.wait,
+ msm_uport->tx.flush == FLUSH_SHUTDOWN, 100);
+ if (!ret)
+ pr_err("%s():HSUART TX Stalls.\n", __func__);
+ } else {
+ /* BAM Disconnect for TX */
+ sps_disconnect(sps_pipe_handle);
+ }
}
tasklet_kill(&msm_uport->tx.tlet);
BUG_ON(msm_uport->rx.flush < FLUSH_STOP);
@@ -2145,6 +2955,15 @@
* Hence mb() requires here.
*/
mb();
+
+ /* Reset PNOC Bus Scaling */
+ if (is_blsp_uart(msm_uport)) {
+ ret = msm_bus_scale_client_update_request(
+ msm_uport->bus_perf_client, 0);
+ if (ret)
+ pr_err("%s(): Failed to reset bus bw vote\n", __func__);
+ }
+
if (msm_uport->clk_state != MSM_HS_CLK_OFF) {
/* to balance clk_state */
clk_disable_unprepare(msm_uport->clk);
@@ -2211,12 +3030,18 @@
.runtime_idle = msm_hs_runtime_idle,
};
+static struct of_device_id msm_hs_match_table[] = {
+ { .compatible = "qcom,msm-hsuart-v14" },
+ {}
+};
+
static struct platform_driver msm_serial_hs_platform_driver = {
.probe = msm_hs_probe,
.remove = __devexit_p(msm_hs_remove),
.driver = {
.name = "msm_serial_hs",
.pm = &msm_hs_dev_pm_ops,
+ .of_match_table = msm_hs_match_table,
},
};
diff --git a/drivers/tty/serial/msm_serial_hs_hwreg.h b/drivers/tty/serial/msm_serial_hs_hwreg.h
index 20d6781..9fa4f55 100644
--- a/drivers/tty/serial/msm_serial_hs_hwreg.h
+++ b/drivers/tty/serial/msm_serial_hs_hwreg.h
@@ -215,7 +215,7 @@
#ifdef CONFIG_MSM_UARTDM_Core_v14
/* write only register */
-#define UARTDM_CSR_ADDR 0x0a
+#define UARTDM_CSR_ADDR 0xa0
/* write only register */
#define UARTDM_TF_ADDR 0x100
diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile
index 0b46082..6875b74 100644
--- a/drivers/usb/dwc3/Makefile
+++ b/drivers/usb/dwc3/Makefile
@@ -1,6 +1,7 @@
ccflags-$(CONFIG_USB_DWC3_DEBUG) := -DDEBUG
ccflags-$(CONFIG_USB_DWC3_VERBOSE) += -DVERBOSE_DEBUG
ccflags-y += -Idrivers/usb/host
+ccflags-y += -Idrivers/base/power
obj-$(CONFIG_USB_DWC3) += dwc3.o
diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c
index 1ee0828..3ae6f22 100644
--- a/drivers/usb/dwc3/dwc3-msm.c
+++ b/drivers/usb/dwc3/dwc3-msm.c
@@ -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
@@ -1459,6 +1459,7 @@
{
int ret;
bool dcp;
+ bool host_bus_suspend;
dev_dbg(mdwc->dev, "%s: entering lpm\n", __func__);
@@ -1481,17 +1482,7 @@
}
dcp = mdwc->charger.chg_type == DWC3_DCP_CHARGER;
-
- /* Sequence to put hardware in low power state:
- * 1. Set OTGDISABLE to disable OTG block in HSPHY (saves power)
- * 2. Clear charger detection control fields (performed above)
- * 3. SUSPEND PHY and turn OFF core clock after some delay
- * 4. Clear interrupt latch register and enable BSV, ID HV interrupts
- * 5. Enable PHY retention
- */
- dwc3_msm_write_readback(mdwc->base, HS_PHY_CTRL_REG, 0x1000, 0x1000);
- dwc3_msm_write_readback(mdwc->base, HS_PHY_CTRL_REG,
- 0xC00000, 0x800000);
+ host_bus_suspend = mdwc->host_mode == 1;
/* Sequence to put SSPHY in low power state:
* 1. Clear REF_SS_PHY_EN in SS_PHY_CTRL_REG
@@ -1507,10 +1498,43 @@
usleep_range(1000, 1200);
clk_disable_unprepare(mdwc->ref_clk);
- dwc3_msm_write_reg(mdwc->base, HS_PHY_IRQ_STAT_REG, 0xFFF);
- dwc3_msm_write_readback(mdwc->base, HS_PHY_CTRL_REG, 0x18000, 0x18000);
- if (!dcp)
+ if (host_bus_suspend) {
+ /* Sequence for host bus suspend case:
+ * 1. Set suspend and sleep bits in GUSB2PHYCONFIG reg
+ * 2. Clear interrupt latch register and enable BSV, ID HV intr
+ * 3. Enable DP and DM HV interrupts in ALT_INTERRUPT_EN_REG
+ * 4. Enable PHY retention
+ */
+ dwc3_msm_write_reg(mdwc->base, DWC3_GUSB2PHYCFG(0),
+ dwc3_msm_read_reg(mdwc->base, DWC3_GUSB2PHYCFG(0)) |
+ 0x00000140);
+ dwc3_msm_write_reg(mdwc->base, HS_PHY_IRQ_STAT_REG, 0xFFF);
+ if (mdwc->otg_xceiv && (!mdwc->ext_xceiv.otg_capability))
+ dwc3_msm_write_readback(mdwc->base, HS_PHY_CTRL_REG,
+ 0x18000, 0x18000);
+ dwc3_msm_write_reg(mdwc->base, ALT_INTERRUPT_EN_REG, 0x00A);
dwc3_msm_write_readback(mdwc->base, HS_PHY_CTRL_REG, 0x2, 0x0);
+ udelay(5);
+ } else {
+ /* Sequence to put hardware in low power state:
+ * 1. Set OTGDISABLE to disable OTG block in HSPHY (saves power)
+ * 2. Clear charger detection control fields (performed above)
+ * 3. SUSPEND PHY and turn OFF core clock after some delay
+ * 4. Clear interrupt latch register and enable BSV, ID HV intr
+ * 5. Enable PHY retention
+ */
+ dwc3_msm_write_readback(mdwc->base, HS_PHY_CTRL_REG, 0x1000,
+ 0x1000);
+ dwc3_msm_write_readback(mdwc->base, HS_PHY_CTRL_REG,
+ 0xC00000, 0x800000);
+ dwc3_msm_write_reg(mdwc->base, HS_PHY_IRQ_STAT_REG, 0xFFF);
+ if (mdwc->otg_xceiv && (!mdwc->ext_xceiv.otg_capability))
+ dwc3_msm_write_readback(mdwc->base, HS_PHY_CTRL_REG,
+ 0x18000, 0x18000);
+ if (!dcp)
+ dwc3_msm_write_readback(mdwc->base, HS_PHY_CTRL_REG,
+ 0x2, 0x0);
+ }
/* make sure above writes are completed before turning off clocks */
wmb();
@@ -1530,7 +1554,8 @@
dev_err(mdwc->dev, "Failed to reset bus bw vote\n");
}
- if (mdwc->otg_xceiv && mdwc->ext_xceiv.otg_capability && !dcp)
+ if (mdwc->otg_xceiv && mdwc->ext_xceiv.otg_capability && !dcp &&
+ !host_bus_suspend)
dwc3_hsusb_ldo_enable(0);
dwc3_ssusb_ldo_enable(0);
@@ -1551,6 +1576,7 @@
{
int ret;
bool dcp;
+ bool host_bus_suspend;
dev_dbg(mdwc->dev, "%s: exiting lpm\n", __func__);
@@ -1575,7 +1601,9 @@
__func__, ret);
dcp = mdwc->charger.chg_type == DWC3_DCP_CHARGER;
- if (mdwc->otg_xceiv && mdwc->ext_xceiv.otg_capability && !dcp)
+ host_bus_suspend = mdwc->host_mode == 1;
+ if (mdwc->otg_xceiv && mdwc->ext_xceiv.otg_capability && !dcp &&
+ !host_bus_suspend)
dwc3_hsusb_ldo_enable(1);
dwc3_ssusb_ldo_enable(1);
@@ -1587,20 +1615,41 @@
clk_prepare_enable(mdwc->iface_clk);
clk_prepare_enable(mdwc->core_clk);
- /* Disable HV interrupt */
- dwc3_msm_write_readback(mdwc->base, HS_PHY_CTRL_REG, 0x18000, 0x0);
- /* Disable Retention */
- dwc3_msm_write_readback(mdwc->base, HS_PHY_CTRL_REG, 0x2, 0x2);
+ if (host_bus_suspend) {
+ /* Disable HV interrupt */
+ if (mdwc->otg_xceiv && (!mdwc->ext_xceiv.otg_capability))
+ dwc3_msm_write_readback(mdwc->base, HS_PHY_CTRL_REG,
+ 0x18000, 0x0);
+ /* Clear interrupt latch register */
+ dwc3_msm_write_reg(mdwc->base, HS_PHY_IRQ_STAT_REG, 0x000);
- dwc3_msm_write_reg(mdwc->base, DWC3_GUSB2PHYCFG(0),
- dwc3_msm_read_reg(mdwc->base, DWC3_GUSB2PHYCFG(0)) | 0xF0000000);
- /* 10usec delay required before de-asserting PHY RESET */
- udelay(10);
- dwc3_msm_write_reg(mdwc->base, DWC3_GUSB2PHYCFG(0),
- dwc3_msm_read_reg(mdwc->base, DWC3_GUSB2PHYCFG(0)) & 0x7FFFFFFF);
+ /* Disable DP and DM HV interrupt */
+ dwc3_msm_write_reg(mdwc->base, ALT_INTERRUPT_EN_REG, 0x000);
- /* Bring PHY out of suspend */
- dwc3_msm_write_readback(mdwc->base, HS_PHY_CTRL_REG, 0xC00000, 0x0);
+ /* Disable Retention */
+ dwc3_msm_write_readback(mdwc->base, HS_PHY_CTRL_REG, 0x2, 0x2);
+ } else {
+ /* Disable HV interrupt */
+ if (mdwc->otg_xceiv && (!mdwc->ext_xceiv.otg_capability))
+ dwc3_msm_write_readback(mdwc->base, HS_PHY_CTRL_REG,
+ 0x18000, 0x0);
+ /* Disable Retention */
+ dwc3_msm_write_readback(mdwc->base, HS_PHY_CTRL_REG, 0x2, 0x2);
+
+ dwc3_msm_write_reg(mdwc->base, DWC3_GUSB2PHYCFG(0),
+ dwc3_msm_read_reg(mdwc->base, DWC3_GUSB2PHYCFG(0)) |
+ 0xF0000000);
+ /* 10usec delay required before de-asserting PHY RESET */
+ udelay(10);
+ dwc3_msm_write_reg(mdwc->base, DWC3_GUSB2PHYCFG(0),
+ dwc3_msm_read_reg(mdwc->base, DWC3_GUSB2PHYCFG(0)) &
+ 0x7FFFFFFF);
+
+ /* Bring PHY out of suspend */
+ dwc3_msm_write_readback(mdwc->base, HS_PHY_CTRL_REG, 0xC00000,
+ 0x0);
+
+ }
/* Assert SS PHY RESET */
dwc3_msm_write_readback(mdwc->base, SS_PHY_CTRL_REG, (1 << 7),
@@ -2186,22 +2235,24 @@
msm->charger.charging_disabled = of_property_read_bool(node,
"qcom,charging-disabled");
- if (!msm->ext_xceiv.otg_capability) {
- /* DWC3 has separate IRQ line for OTG events (ID/BSV etc.) */
- msm->hs_phy_irq = platform_get_irq_byname(pdev, "hs_phy_irq");
- if (msm->hs_phy_irq < 0) {
- dev_dbg(&pdev->dev, "pget_irq for hs_phy_irq failed\n");
- msm->hs_phy_irq = 0;
- } else {
- ret = request_irq(msm->hs_phy_irq, msm_dwc3_irq,
- IRQF_TRIGGER_RISING, "msm_dwc3", msm);
- if (ret) {
- dev_err(&pdev->dev, "irqreq HSPHYINT failed\n");
- goto disable_hs_ldo;
- }
- enable_irq_wake(msm->hs_phy_irq);
- }
+ /*
+ * DWC3 has separate IRQ line for OTG events (ID/BSV) and for
+ * DP and DM linestate transitions during low power mode.
+ */
+ msm->hs_phy_irq = platform_get_irq_byname(pdev, "hs_phy_irq");
+ if (msm->hs_phy_irq < 0) {
+ dev_dbg(&pdev->dev, "pget_irq for hs_phy_irq failed\n");
+ msm->hs_phy_irq = 0;
} else {
+ ret = request_irq(msm->hs_phy_irq, msm_dwc3_irq,
+ IRQF_TRIGGER_RISING, "msm_dwc3", msm);
+ if (ret) {
+ dev_err(&pdev->dev, "irqreq HSPHYINT failed\n");
+ goto disable_hs_ldo;
+ }
+ enable_irq_wake(msm->hs_phy_irq);
+ }
+ if (msm->ext_xceiv.otg_capability) {
/* Use ADC for ID pin detection */
queue_delayed_work(system_nrt_wq, &msm->init_adc_work, 0);
device_create_file(&pdev->dev, &dev_attr_adc_enable);
diff --git a/drivers/usb/dwc3/dwc3_otg.c b/drivers/usb/dwc3/dwc3_otg.c
index eb879e3..4980337 100644
--- a/drivers/usb/dwc3/dwc3_otg.c
+++ b/drivers/usb/dwc3/dwc3_otg.c
@@ -1,7 +1,7 @@
/**
* dwc3_otg.c - DesignWare USB3 DRD Controller OTG
*
- * 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
@@ -55,6 +55,25 @@
}
}
+static int dwc3_otg_set_suspend(struct usb_phy *phy, int suspend)
+{
+ struct usb_otg *otg = phy->otg;
+ struct dwc3_otg *dotg = container_of(otg, struct dwc3_otg, otg);
+
+ if (dotg->host_bus_suspend == suspend)
+ return 0;
+
+ dotg->host_bus_suspend = suspend;
+ if (suspend) {
+ pm_runtime_put_sync(phy->dev);
+ } else {
+ pm_runtime_get_noresume(phy->dev);
+ pm_runtime_resume(phy->dev);
+ }
+
+ return 0;
+}
+
/**
* dwc3_otg_set_host_power - Enable port power control for host operation
*
@@ -149,6 +168,14 @@
* anymore.
*/
dwc3_otg_set_host_regs(dotg);
+ /*
+ * FIXME If micro A cable is disconnected during system suspend,
+ * xhci platform device will be removed before runtime pm is
+ * enabled for xhci device. Due to this, disable_depth becomes
+ * greater than one and runtimepm is not enabled for next microA
+ * connect. Fix this by calling pm_runtime_init for xhci device.
+ */
+ pm_runtime_init(&dwc->xhci->dev);
ret = platform_device_add(dwc->xhci);
if (ret) {
dev_err(otg->phy->dev,
@@ -353,6 +380,9 @@
dev_dbg(phy->dev, "ext PHY_RESUME event received\n");
/* ext_xceiver would have taken h/w out of LPM by now */
ret = pm_runtime_get(phy->dev);
+ if ((phy->state == OTG_STATE_A_HOST) &&
+ dotg->host_bus_suspend)
+ dotg->host_bus_suspend = 0;
if (ret == -EACCES) {
/* pm_runtime_get may fail during system
resume with -EACCES error */
@@ -852,6 +882,7 @@
dotg->otg.phy->otg = &dotg->otg;
dotg->otg.phy->dev = dwc->dev;
dotg->otg.phy->set_power = dwc3_otg_set_power;
+ dotg->otg.phy->set_suspend = dwc3_otg_set_suspend;
ret = usb_set_transceiver(dotg->otg.phy);
if (ret) {
diff --git a/drivers/usb/dwc3/dwc3_otg.h b/drivers/usb/dwc3/dwc3_otg.h
index 5a36a4f..07d6411 100644
--- a/drivers/usb/dwc3/dwc3_otg.h
+++ b/drivers/usb/dwc3/dwc3_otg.h
@@ -1,7 +1,7 @@
/**
* dwc3_otg.h - DesignWare USB3 DRD Controller OTG
*
- * Copyright (c) 2012, Code Aurora Forum. 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
@@ -20,6 +20,7 @@
#include <linux/power_supply.h>
#include <linux/usb/otg.h>
+#include "power.h"
#define DWC3_IDEV_CHG_MAX 1500
@@ -48,6 +49,7 @@
unsigned long inputs;
struct power_supply *psy;
struct completion dwc3_xcvr_vbus_init;
+ int host_bus_suspend;
};
/**
diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c
index 644a779..d6d8a76 100644
--- a/drivers/usb/dwc3/host.c
+++ b/drivers/usb/dwc3/host.c
@@ -55,7 +55,6 @@
dma_set_coherent_mask(&xhci->dev, dwc->dev->coherent_dma_mask);
- xhci->dev.parent = dwc->dev;
xhci->dev.dma_mask = dwc->dev->dma_mask;
xhci->dev.dma_parms = dwc->dev->dma_parms;
diff --git a/drivers/usb/gadget/ci13xxx_udc.c b/drivers/usb/gadget/ci13xxx_udc.c
index 9c7b1ec..0723416 100644
--- a/drivers/usb/gadget/ci13xxx_udc.c
+++ b/drivers/usb/gadget/ci13xxx_udc.c
@@ -69,6 +69,9 @@
#include "ci13xxx_udc.h"
+/* Turns on streaming. overrides CI13XXX_DISABLE_STREAMING */
+static unsigned int streaming;
+module_param(streaming, uint, S_IRUGO | S_IWUSR);
/******************************************************************************
* DEFINE
@@ -332,9 +335,6 @@
udc->udc_driver->notify_event(udc,
CI13XXX_CONTROLLER_RESET_EVENT);
- if (udc->udc_driver->flags & CI13XXX_DISABLE_STREAMING)
- hw_cwrite(CAP_USBMODE, USBMODE_SDIS, USBMODE_SDIS);
-
/* USBMODE should be configured step by step */
hw_cwrite(CAP_USBMODE, USBMODE_CM, USBMODE_CM_IDLE);
hw_cwrite(CAP_USBMODE, USBMODE_CM, USBMODE_CM_DEVICE);
@@ -369,7 +369,15 @@
*/
static int hw_device_state(u32 dma)
{
+ struct ci13xxx *udc = _udc;
+
if (dma) {
+ if (streaming || !(udc->udc_driver->flags &
+ CI13XXX_DISABLE_STREAMING))
+ hw_cwrite(CAP_USBMODE, USBMODE_SDIS, 0);
+ else
+ hw_cwrite(CAP_USBMODE, USBMODE_SDIS, USBMODE_SDIS);
+
hw_cwrite(CAP_ENDPTLISTADDR, ~0, dma);
/* interrupt, error, port change, reset, sleep/suspend */
hw_cwrite(CAP_USBINTR, ~0,
@@ -980,16 +988,16 @@
* dbg_done: prints a DONE event
* @addr: endpoint address
* @td: transfer descriptor
- * @status: status
*/
-static void dbg_done(u8 addr, const u32 token, int status)
+static void dbg_done(u8 addr, const struct usb_request *req)
{
char msg[DBG_DATA_MSG];
- scnprintf(msg, sizeof(msg), "%d %02X",
- (int)(token & TD_TOTAL_BYTES) >> ffs_nr(TD_TOTAL_BYTES),
- (int)(token & TD_STATUS) >> ffs_nr(TD_STATUS));
- dbg_print(addr, "DONE", status, msg);
+ if (req != NULL) {
+ scnprintf(msg, sizeof(msg),
+ "%p %d %d", req, req->actual, req->length);
+ dbg_print(addr, "DONE", req->status, msg);
+ }
}
/**
@@ -1016,7 +1024,7 @@
if (req != NULL) {
scnprintf(msg, sizeof(msg),
- "%d %d", !req->no_interrupt, req->length);
+ "%p %d %d", req, !req->no_interrupt, req->length);
dbg_print(addr, "QUEUE", status, msg);
}
}
@@ -1049,6 +1057,7 @@
{
char msg[DBG_DATA_MSG];
struct ci13xxx_req *req;
+ struct ci13xxx_td_wrapper *td;
struct list_head *ptr = NULL;
if (mep != NULL) {
@@ -1065,15 +1074,14 @@
list_for_each(ptr, &mep->qh.queue) {
req = list_entry(ptr, struct ci13xxx_req, queue);
- scnprintf(msg, sizeof(msg),
- "%08X:%08X:%08X\n",
- req->dma, req->ptr->next,
- req->ptr->token);
- dbg_print(addr, "REQ", 0, msg);
- scnprintf(msg, sizeof(msg), "%08X:%d\n",
- req->ptr->page[0],
- req->req.status);
- dbg_print(addr, "REQPAGE", 0, msg);
+ list_for_each_entry(td, &req->td_list, list) {
+ scnprintf(msg, sizeof(msg),
+ "%08X:%08X:%08X:%08X\n",
+ td->dma, td->ptr->next,
+ td->ptr->token,
+ td->ptr->page[0]);
+ dbg_print(addr, "TD", 0, msg);
+ }
}
}
}
@@ -1444,6 +1452,7 @@
unsigned long flags;
struct list_head *ptr = NULL;
struct ci13xxx_req *req = NULL;
+ struct ci13xxx_td_wrapper *td;
unsigned i, j, n = 0, qSize = sizeof(struct ci13xxx_td)/sizeof(u32);
dbg_trace("[%s] %p\n", __func__, buf);
@@ -1458,15 +1467,18 @@
{
req = list_entry(ptr, struct ci13xxx_req, queue);
- n += scnprintf(buf + n, PAGE_SIZE - n,
- "EP=%02i: TD=%08X %s\n",
- i % hw_ep_max/2, (u32)req->dma,
- ((i < hw_ep_max/2) ? "RX" : "TX"));
-
- for (j = 0; j < qSize; j++)
+ list_for_each_entry(td, &req->td_list, list) {
n += scnprintf(buf + n, PAGE_SIZE - n,
- " %04X: %08X\n", j,
- *((u32 *)req->ptr + j));
+ "EP=%02i: TD=%08X %s\n",
+ i % hw_ep_max/2, (u32)td->dma,
+ ((i < hw_ep_max/2) ?
+ "RX" : "TX"));
+
+ for (j = 0; j < qSize; j++)
+ n += scnprintf(buf + n, PAGE_SIZE - n,
+ " %04X: %08X\n", j,
+ *((u32 *)td->ptr + j));
+ }
}
spin_unlock_irqrestore(udc->lock, flags);
@@ -1484,6 +1496,7 @@
unsigned int ep_num, dir;
int n;
struct ci13xxx_req *mReq = NULL;
+ struct ci13xxx_td_wrapper *td;
if (sscanf(buf, "%u %u", &ep_num, &dir) != 2) {
dev_err(dev, "<ep_num> <dir>: prime the ep");
@@ -1497,7 +1510,8 @@
n = hw_ep_bit(mEp->num, mEp->dir);
mReq = list_entry(mEp->qh.queue.next, struct ci13xxx_req, queue);
- mEp->qh.ptr->td.next = mReq->dma;
+ td = list_first_entry(&mReq->td_list, struct ci13xxx_td_wrapper, list);
+ mEp->qh.ptr->td.next = td->dma;
mEp->qh.ptr->td.token &= ~TD_STATUS;
wmb();
@@ -1527,12 +1541,15 @@
int n;
struct list_head *ptr = NULL;
struct ci13xxx_req *req = NULL;
+ struct ci13xxx_td_wrapper *td;
+ unsigned long flags;
if (sscanf(buf, "%u %u", &ep_num, &dir) != 2) {
dev_err(dev, "<ep_num> <dir>: to print dtds");
goto done;
}
+ spin_lock_irqsave(udc->lock, flags);
if (dir)
mEp = &udc->ci13xxx_ep[ep_num + hw_ep_max/2];
else
@@ -1556,11 +1573,12 @@
list_for_each(ptr, &mEp->qh.queue) {
req = list_entry(ptr, struct ci13xxx_req, queue);
-
- pr_info("\treq:%08x next:%08x token:%08x page0:%08x status:%d\n",
- req->dma, req->ptr->next, req->ptr->token,
- req->ptr->page[0], req->req.status);
+ list_for_each_entry(td, &req->td_list, list)
+ pr_info("\treq:%08x next:%08x token:%08x page0:%08x\n",
+ td->dma, td->ptr->next, td->ptr->token,
+ td->ptr->page[0]);
}
+ spin_unlock_irqrestore(udc->lock, flags);
done:
return count;
@@ -1740,6 +1758,7 @@
{
struct ci13xxx_ep *mep = (struct ci13xxx_ep *)data;
struct ci13xxx_req *req;
+ struct ci13xxx_td_wrapper *td;
struct list_head *ptr = NULL;
int n = hw_ep_bit(mep->num, mep->dir);
unsigned long flags;
@@ -1753,9 +1772,10 @@
goto out;
req = list_entry(mep->qh.queue.next, struct ci13xxx_req, queue);
+ td = list_first_entry(&req->td_list, struct ci13xxx_td_wrapper, list);
mb();
- if (!(TD_STATUS_ACTIVE & req->ptr->token))
+ if (!(TD_STATUS_ACTIVE & td->ptr->token))
goto out;
mep->prime_timer_count++;
@@ -1767,10 +1787,10 @@
mep->qh.ptr->td.next, mep->qh.ptr->td.token);
list_for_each(ptr, &mep->qh.queue) {
req = list_entry(ptr, struct ci13xxx_req, queue);
- pr_info("\treq:%08xnext:%08xtkn:%08xpage0:%08xsts:%d\n",
- req->dma, req->ptr->next,
- req->ptr->token, req->ptr->page[0],
- req->req.status);
+ list_for_each_entry(td, &req->td_list, list)
+ pr_info("\treq:%08xnext:%08xtkn:%08xpage0:%08x\n",
+ td->dma, td->ptr->next,
+ td->ptr->token, td->ptr->page[0]);
}
dbg_usb_op_fail(0xFF, "PRIMEF", mep);
mep->prime_fail_count++;
@@ -1784,7 +1804,69 @@
out:
mep->prime_timer_count = 0;
spin_unlock_irqrestore(mep->lock, flags);
+}
+static int prepare_dtds(struct ci13xxx_ep *mEp, struct ci13xxx_req *mReq)
+{
+ int i;
+ unsigned len;
+ unsigned remaining_len = mReq->req.length;
+ dma_addr_t dma = mReq->req.dma;
+ struct ci13xxx_td_wrapper *td = list_first_entry(&mReq->td_list,
+ struct ci13xxx_td_wrapper, list);
+ struct ci13xxx_td_wrapper *prev_td = NULL;
+ bool zlp = mReq->req.zero && remaining_len &&
+ (remaining_len % mEp->ep.maxpacket == 0);
+
+ do {
+ td = kmalloc(sizeof(*td), GFP_ATOMIC);
+ if (td == NULL) {
+ pr_err("td wrapper allocation failed\n");
+ goto free;
+ }
+ td->ptr = dma_pool_alloc(mEp->td_pool, GFP_ATOMIC,
+ &td->dma);
+ if (td->ptr == NULL) {
+ kfree(td);
+ pr_err("td allocation failed\n");
+ goto free;
+ }
+
+ list_add_tail(&td->list, &mReq->td_list);
+ if (zlp && !remaining_len)
+ zlp = false;
+
+ len = min_t(unsigned, remaining_len, CI13XXX_MAX_REQ_SIZE);
+ remaining_len -= len;
+ memset(td->ptr, 0, sizeof(*td->ptr));
+ td->ptr->token = len << ffs_nr(TD_TOTAL_BYTES);
+ td->ptr->token &= TD_TOTAL_BYTES;
+ td->ptr->token |= TD_STATUS_ACTIVE;
+
+ td->ptr->page[0] = dma;
+ for (i = 1; i < 5; i++)
+ td->ptr->page[i] = (dma + i * CI13XXX_PAGE_SIZE) &
+ ~TD_RESERVED_MASK;
+ if (prev_td)
+ prev_td->ptr->next = td->dma;
+
+ dma += len;
+ prev_td = td;
+
+ } while (remaining_len || zlp);
+
+ td->ptr->next = TD_TERMINATE;
+ if (!mReq->req.no_interrupt)
+ td->ptr->token |= TD_IOC;
+
+ return 0;
+free:
+ list_for_each_entry_safe(td, prev_td, &mReq->td_list, list) {
+ list_del(&td->list);
+ dma_pool_free(mEp->td_pool, td->ptr, td->dma);
+ kfree(td);
+ }
+ return -ENOMEM;
}
/**
@@ -1799,7 +1881,7 @@
unsigned i;
int ret = 0;
unsigned length = mReq->req.length;
- struct ci13xxx *udc = _udc;
+ struct ci13xxx_td_wrapper *td;
trace("%p, %p", mEp, mReq);
@@ -1819,40 +1901,23 @@
mReq->map = 1;
}
- if (mReq->req.zero && length && (length % mEp->ep.maxpacket == 0)) {
- mReq->zptr = dma_pool_alloc(mEp->td_pool, GFP_ATOMIC,
- &mReq->zdma);
- if (mReq->zptr == NULL) {
- if (mReq->map) {
- dma_unmap_single(mEp->device, mReq->req.dma,
- length, mEp->dir ? DMA_TO_DEVICE :
+ if (CI13XX_REQ_VENDOR_ID(mReq->req.udc_priv) == MSM_VENDOR_ID)
+ mReq->req.dma = 0;
+
+ ret = prepare_dtds(mEp, mReq);
+ if (ret) {
+ pr_err("dTD preparation failed\n");
+ if (mReq->map) {
+ dma_unmap_single(mEp->device, mReq->req.dma,
+ mReq->req.length,
+ mEp->dir ? DMA_TO_DEVICE :
DMA_FROM_DEVICE);
- mReq->req.dma = DMA_ADDR_INVALID;
- mReq->map = 0;
- }
- return -ENOMEM;
+ mReq->req.dma = DMA_ADDR_INVALID;
+ mReq->map = 0;
}
- memset(mReq->zptr, 0, sizeof(*mReq->zptr));
- mReq->zptr->next = TD_TERMINATE;
- mReq->zptr->token = TD_STATUS_ACTIVE;
- if (!mReq->req.no_interrupt)
- mReq->zptr->token |= TD_IOC;
+ return ret;
}
- /*
- * TD configuration
- * TODO - handle requests which spawns into several TDs
- */
- memset(mReq->ptr, 0, sizeof(*mReq->ptr));
- mReq->ptr->token = length << ffs_nr(TD_TOTAL_BYTES);
- mReq->ptr->token &= TD_TOTAL_BYTES;
- mReq->ptr->token |= TD_STATUS_ACTIVE;
- if (mReq->zptr) {
- mReq->ptr->next = mReq->zdma;
- } else {
- mReq->ptr->next = TD_TERMINATE;
- if (!mReq->req.no_interrupt)
- mReq->ptr->token |= TD_IOC;
- }
+ td = list_first_entry(&mReq->td_list, struct ci13xxx_td_wrapper, list);
/* MSM Specific: updating the request as required for
* SPS mode. Enable MSM proprietary DMA engine acording
@@ -1860,46 +1925,28 @@
*/
if (CI13XX_REQ_VENDOR_ID(mReq->req.udc_priv) == MSM_VENDOR_ID) {
if (mReq->req.udc_priv & MSM_SPS_MODE) {
- mReq->ptr->token = TD_STATUS_ACTIVE;
+ td->ptr->token = TD_STATUS_ACTIVE;
if (mReq->req.udc_priv & MSM_IS_FINITE_TRANSFER)
- mReq->ptr->next = TD_TERMINATE;
+ td->ptr->next = TD_TERMINATE;
else
- mReq->ptr->next = MSM_ETD_TYPE | mReq->dma;
+ td->ptr->next = MSM_ETD_TYPE | td->dma;
if (!mReq->req.no_interrupt)
- mReq->ptr->token |= MSM_ETD_IOC;
+ td->ptr->token |= MSM_ETD_IOC;
}
- mReq->req.dma = 0;
- }
-
- mReq->ptr->page[0] = mReq->req.dma;
- for (i = 1; i < 5; i++)
- mReq->ptr->page[i] =
- (mReq->req.dma + i * CI13XXX_PAGE_SIZE) & ~TD_RESERVED_MASK;
-
- /* Remote Wakeup */
- if (udc->suspended) {
- if (!udc->remote_wakeup) {
- mReq->req.status = -EAGAIN;
- dev_dbg(mEp->device, "%s: queue failed (suspend) ept #%d\n",
- __func__, mEp->num);
- return -EAGAIN;
- }
- usb_phy_set_suspend(udc->transceiver, 0);
- schedule_delayed_work(&udc->rw_work, REMOTE_WAKEUP_DELAY);
}
if (!list_empty(&mEp->qh.queue)) {
struct ci13xxx_req *mReqPrev;
+ struct ci13xxx_td_wrapper *prev_td;
int n = hw_ep_bit(mEp->num, mEp->dir);
int tmp_stat;
ktime_t start, diff;
mReqPrev = list_entry(mEp->qh.queue.prev,
struct ci13xxx_req, queue);
- if (mReqPrev->zptr)
- mReqPrev->zptr->next = mReq->dma & TD_ADDR_MASK;
- else
- mReqPrev->ptr->next = mReq->dma & TD_ADDR_MASK;
+ prev_td = list_entry(mReqPrev->td_list.prev,
+ struct ci13xxx_td_wrapper, list);
+ prev_td->ptr->next = td->dma & TD_ADDR_MASK;
wmb();
if (hw_cread(CAP_ENDPTPRIME, BIT(n)))
goto done;
@@ -1928,15 +1975,17 @@
struct ci13xxx_req *mReq = \
list_entry(mEp->qh.queue.next,
struct ci13xxx_req, queue);
+ struct ci13xxx_td_wrapper *td = list_first_entry(&mReq->td_list,
+ struct ci13xxx_td_wrapper, list);
- if (TD_STATUS_ACTIVE & mReq->ptr->token) {
- mEp->qh.ptr->td.next = mReq->dma;
+ if (TD_STATUS_ACTIVE & td->ptr->token) {
+ mEp->qh.ptr->td.next = td->dma;
mEp->qh.ptr->td.token &= ~TD_STATUS;
goto prime;
}
}
- mEp->qh.ptr->td.next = mReq->dma; /* TERMINATE = 0 */
+ mEp->qh.ptr->td.next = td->dma; /* TERMINATE = 0 */
if (CI13XX_REQ_VENDOR_ID(mReq->req.udc_priv) == MSM_VENDOR_ID) {
if (mReq->req.udc_priv & MSM_SPS_MODE) {
@@ -1993,27 +2042,27 @@
*/
static int _hardware_dequeue(struct ci13xxx_ep *mEp, struct ci13xxx_req *mReq)
{
+ struct ci13xxx_td_wrapper *td, *temp_td;
+ unsigned rem, total_rem = 0;
+ int status;
+
trace("%p, %p", mEp, mReq);
if (mReq->req.status != -EALREADY)
return -EINVAL;
- /* clean speculative fetches on req->ptr->token */
+ /* clean speculative fetches on td->ptr->token */
mb();
- if ((TD_STATUS_ACTIVE & mReq->ptr->token) != 0)
+ td = list_entry(mReq->td_list.prev, struct ci13xxx_td_wrapper, list);
+
+ if ((TD_STATUS_ACTIVE & td->ptr->token) != 0)
return -EBUSY;
if (CI13XX_REQ_VENDOR_ID(mReq->req.udc_priv) == MSM_VENDOR_ID)
if ((mReq->req.udc_priv & MSM_SPS_MODE) &&
(mReq->req.udc_priv & MSM_IS_FINITE_TRANSFER))
return -EBUSY;
- if (mReq->zptr) {
- if ((TD_STATUS_ACTIVE & mReq->zptr->token) != 0)
- return -EBUSY;
- dma_pool_free(mEp->td_pool, mReq->zptr, mReq->zdma);
- mReq->zptr = NULL;
- }
mReq->req.status = 0;
@@ -2024,18 +2073,27 @@
mReq->map = 0;
}
- mReq->req.status = mReq->ptr->token & TD_STATUS;
- if ((TD_STATUS_HALTED & mReq->req.status) != 0)
- mReq->req.status = -1;
- else if ((TD_STATUS_DT_ERR & mReq->req.status) != 0)
- mReq->req.status = -1;
- else if ((TD_STATUS_TR_ERR & mReq->req.status) != 0)
- mReq->req.status = -1;
+ list_for_each_entry_safe(td, temp_td, &mReq->td_list, list) {
- mReq->req.actual = mReq->ptr->token & TD_TOTAL_BYTES;
- mReq->req.actual >>= ffs_nr(TD_TOTAL_BYTES);
- mReq->req.actual = mReq->req.length - mReq->req.actual;
- mReq->req.actual = mReq->req.status ? 0 : mReq->req.actual;
+ status = td->ptr->token & TD_STATUS;
+ if ((status & TD_STATUS_HALTED) != 0)
+ mReq->req.status = -1;
+ else if ((status & TD_STATUS_DT_ERR) != 0)
+ mReq->req.status = -1;
+ else if ((status & TD_STATUS_TR_ERR) != 0)
+ mReq->req.status = -1;
+
+ rem = td->ptr->token & TD_TOTAL_BYTES;
+ rem >>= ffs_nr(TD_TOTAL_BYTES);
+ total_rem += rem;
+
+ list_del(&td->list);
+ dma_pool_free(mEp->td_pool, td->ptr, td->dma);
+ kfree(td);
+ }
+
+ mReq->req.actual = mReq->req.status ? 0 :
+ (mReq->req.length - total_rem);
return mReq->req.actual;
}
@@ -2052,6 +2110,7 @@
__acquires(mEp->lock)
{
struct ci13xxx_ep *mEpTemp = mEp;
+ struct ci13xxx_td_wrapper *td, *temp_td;
unsigned val;
trace("%p", mEp);
@@ -2093,10 +2152,16 @@
mReq->map = 0;
}
+ list_for_each_entry_safe(td, temp_td, &mReq->td_list, list) {
+ list_del(&td->list);
+ dma_pool_free(mEp->td_pool, td->ptr, td->dma);
+ kfree(td);
+ }
+
if (mReq->req.complete != NULL) {
spin_unlock(mEp->lock);
if ((mEp->type == USB_ENDPOINT_XFER_CONTROL) &&
- mReq->req.length)
+ mReq->req.length)
mEpTemp = &_udc->ep0in;
mReq->req.complete(&mEpTemp->ep, &mReq->req);
if (mEp->type == USB_ENDPOINT_XFER_CONTROL)
@@ -2115,7 +2180,6 @@
*/
static int _gadget_stop_activity(struct usb_gadget *gadget)
{
- struct usb_ep *ep;
struct ci13xxx *udc = container_of(gadget, struct ci13xxx, gadget);
unsigned long flags;
@@ -2136,20 +2200,10 @@
gadget->host_request = 0;
gadget->otg_srp_reqd = 0;
- /* flush all endpoints */
- gadget_for_each_ep(ep, gadget) {
- usb_ep_fifo_flush(ep);
- }
+ udc->driver->disconnect(gadget);
usb_ep_fifo_flush(&udc->ep0out.ep);
usb_ep_fifo_flush(&udc->ep0in.ep);
- udc->driver->disconnect(gadget);
-
- /* make sure to disable all endpoints */
- gadget_for_each_ep(ep, gadget) {
- usb_ep_disable(ep);
- }
-
if (udc->status != NULL) {
usb_ep_free_request(&udc->ep0in.ep, udc->status);
udc->status = NULL;
@@ -2453,7 +2507,7 @@
}
req_dequeue = 0;
list_del_init(&mReq->queue);
- dbg_done(_usb_addr(mEp), mReq->ptr->token, retval);
+ dbg_done(_usb_addr(mEp), &mReq->req);
if (mReq->req.complete != NULL) {
spin_unlock(mEp->lock);
if ((mEp->type == USB_ENDPOINT_XFER_CONTROL) &&
@@ -2821,18 +2875,14 @@
}
mReq = kzalloc(sizeof(struct ci13xxx_req), gfp_flags);
- if (mReq != NULL) {
- INIT_LIST_HEAD(&mReq->queue);
- mReq->req.dma = DMA_ADDR_INVALID;
+ if (mReq == NULL)
+ goto out;
- mReq->ptr = dma_pool_alloc(mEp->td_pool, gfp_flags,
- &mReq->dma);
- if (mReq->ptr == NULL) {
- kfree(mReq);
- mReq = NULL;
- }
- }
+ INIT_LIST_HEAD(&mReq->queue);
+ mReq->req.dma = DMA_ADDR_INVALID;
+ INIT_LIST_HEAD(&mReq->td_list);
+out:
dbg_event(_usb_addr(mEp), "ALLOC", mReq == NULL);
return (mReq == NULL) ? NULL : &mReq->req;
@@ -2861,8 +2911,6 @@
spin_lock_irqsave(mEp->lock, flags);
- if (mReq->ptr)
- dma_pool_free(mEp->td_pool, mReq->ptr, mReq->dma);
kfree(mReq);
dbg_event(_usb_addr(mEp), "FREE", 0);
@@ -2900,6 +2948,17 @@
return -ESHUTDOWN;
}
+ if (udc->suspended) {
+ if (!udc->remote_wakeup) {
+ mReq->req.status = -EAGAIN;
+ dev_dbg(mEp->device, "%s: queue failed (suspend) ept #%d\n",
+ __func__, mEp->num);
+ return -EAGAIN;
+ }
+ usb_phy_set_suspend(udc->transceiver, 0);
+ schedule_delayed_work(&udc->rw_work, REMOTE_WAKEUP_DELAY);
+ }
+
if (mEp->type == USB_ENDPOINT_XFER_CONTROL) {
if (req->length)
mEp = (_udc->ep0_dir == RX) ?
@@ -2918,11 +2977,9 @@
goto done;
}
- if (req->length > (4 * CI13XXX_PAGE_SIZE)) {
- req->length = (4 * CI13XXX_PAGE_SIZE);
- retval = -EMSGSIZE;
- warn("request length truncated");
- }
+ /* REMOVE ME */
+ if (req->length > (CI13XXX_MAX_REQ_SIZE))
+ pr_warn_once("bigger request length\n");
dbg_queue(_usb_addr(mEp), req, retval);
@@ -2954,6 +3011,7 @@
struct ci13xxx_ep *mEp = container_of(ep, struct ci13xxx_ep, ep);
struct ci13xxx_ep *mEpTemp = mEp;
struct ci13xxx_req *mReq = container_of(req, struct ci13xxx_req, req);
+ struct ci13xxx_td_wrapper *td, *temp_td;
unsigned long flags;
trace("%p, %p", ep, req);
@@ -2984,6 +3042,12 @@
}
req->status = -ECONNRESET;
+ list_for_each_entry_safe(td, temp_td, &mReq->td_list, list) {
+ list_del(&td->list);
+ dma_pool_free(mEp->td_pool, td->ptr, td->dma);
+ kfree(td);
+ }
+
if (mReq->req.complete != NULL) {
spin_unlock(mEp->lock);
if ((mEp->type == USB_ENDPOINT_XFER_CONTROL) &&
@@ -3513,8 +3577,7 @@
void __iomem *regs)
{
struct ci13xxx *udc;
- struct ci13xxx_platform_data *pdata =
- (struct ci13xxx_platform_data *)(dev->platform_data);
+ struct ci13xxx_platform_data *pdata;
int retval = 0, i;
trace("%p, %p, %p", dev, regs, driver->name);
@@ -3543,6 +3606,7 @@
INIT_LIST_HEAD(&udc->gadget.ep_list);
udc->gadget.ep0 = NULL;
+ pdata = dev->platform_data;
if (pdata)
udc->gadget.usb_core_id = pdata->usb_core_id;
diff --git a/drivers/usb/gadget/ci13xxx_udc.h b/drivers/usb/gadget/ci13xxx_udc.h
index 6b3cad8..4f84d21 100644
--- a/drivers/usb/gadget/ci13xxx_udc.h
+++ b/drivers/usb/gadget/ci13xxx_udc.h
@@ -20,6 +20,7 @@
* DEFINE
*****************************************************************************/
#define CI13XXX_PAGE_SIZE 4096ul /* page size for TD's */
+#define CI13XXX_MAX_REQ_SIZE (4 * CI13XXX_PAGE_SIZE)
#define ENDPT_MAX (32)
#define CTRL_PAYLOAD_MAX (64)
#define RX (0) /* similar to USB_DIR_OUT but can be used as an index */
@@ -77,15 +78,18 @@
struct usb_ctrlrequest setup;
} __attribute__ ((packed));
+struct ci13xxx_td_wrapper {
+ struct ci13xxx_td *ptr;
+ dma_addr_t dma;
+ struct list_head list;
+};
+
/* Extension of usb_request */
struct ci13xxx_req {
struct usb_request req;
unsigned map;
struct list_head queue;
- struct ci13xxx_td *ptr;
- dma_addr_t dma;
- struct ci13xxx_td *zptr;
- dma_addr_t zdma;
+ struct list_head td_list;
};
/* Extension of usb_ep */
diff --git a/drivers/usb/gadget/f_mtp.c b/drivers/usb/gadget/f_mtp.c
index 82ffbba..0c0d58a 100644
--- a/drivers/usb/gadget/f_mtp.c
+++ b/drivers/usb/gadget/f_mtp.c
@@ -67,6 +67,9 @@
#define MTP_RESPONSE_OK 0x2001
#define MTP_RESPONSE_DEVICE_BUSY 0x2019
+unsigned int mtp_rx_req_len = MTP_BULK_BUFFER_SIZE;
+module_param(mtp_rx_req_len, uint, S_IRUGO | S_IWUSR);
+
static const char mtp_shortname[] = "mtp_usb";
struct mtp_dev {
@@ -493,10 +496,27 @@
req->complete = mtp_complete_in;
mtp_req_put(dev, &dev->tx_idle, req);
}
+
+ /*
+ * The RX buffer should be aligned to EP max packet for
+ * some controllers. At bind time, we don't know the
+ * operational speed. Hence assuming super speed max
+ * packet size.
+ */
+ if (mtp_rx_req_len % 1024)
+ mtp_rx_req_len = MTP_BULK_BUFFER_SIZE;
+
+retry_rx_alloc:
for (i = 0; i < RX_REQ_MAX; i++) {
- req = mtp_request_new(dev->ep_out, MTP_BULK_BUFFER_SIZE);
- if (!req)
- goto fail;
+ req = mtp_request_new(dev->ep_out, mtp_rx_req_len);
+ if (!req) {
+ if (mtp_rx_req_len <= MTP_BULK_BUFFER_SIZE)
+ goto fail;
+ for (; i > 0; i--)
+ mtp_request_free(dev->rx_req[i], dev->ep_out);
+ mtp_rx_req_len = MTP_BULK_BUFFER_SIZE;
+ goto retry_rx_alloc;
+ }
req->complete = mtp_complete_out;
dev->rx_req[i] = req;
}
@@ -526,7 +546,7 @@
DBG(cdev, "mtp_read(%d)\n", count);
- if (count > MTP_BULK_BUFFER_SIZE)
+ if (count > mtp_rx_req_len)
return -EINVAL;
if (!IS_ALIGNED(count, dev->ep_out->maxpacket))
@@ -554,7 +574,7 @@
requeue_req:
/* queue a request */
req = dev->rx_req[0];
- req->length = MTP_BULK_BUFFER_SIZE;
+ req->length = mtp_rx_req_len;
dev->rx_done = 0;
ret = usb_ep_queue(dev->ep_out, req, GFP_KERNEL);
if (ret < 0) {
@@ -832,7 +852,7 @@
cur_buf = (cur_buf + 1) % RX_REQ_MAX;
/* some h/w expects size to be aligned to ep's MTU */
- read_req->length = MTP_BULK_BUFFER_SIZE;
+ read_req->length = mtp_rx_req_len;
dev->rx_done = 0;
ret = usb_ep_queue(dev->ep_out, read_req, GFP_KERNEL);
diff --git a/drivers/usb/gadget/f_qdss.c b/drivers/usb/gadget/f_qdss.c
index 085d0bd..70192de 100644
--- a/drivers/usb/gadget/f_qdss.c
+++ b/drivers/usb/gadget/f_qdss.c
@@ -408,6 +408,7 @@
{
pr_debug("qdss_unbind\n");
+ clear_eps(f);
clear_desc(c->cdev->gadget, f);
}
diff --git a/drivers/usb/gadget/msm72k_udc.c b/drivers/usb/gadget/msm72k_udc.c
index b408bfd..887a10c 100644
--- a/drivers/usb/gadget/msm72k_udc.c
+++ b/drivers/usb/gadget/msm72k_udc.c
@@ -2,7 +2,7 @@
* Driver for HighSpeed USB Client Controller in MSM7K
*
* Copyright (C) 2008 Google, Inc.
- * Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2009-2013, The Linux Foundation. All rights reserved.
* Author: Mike Lockwood <lockwood@android.com>
* Brian Swetland <swetland@google.com>
*
@@ -1649,6 +1649,20 @@
usb_phy_set_power(ui->xceiv, 0);
if (ui->irq) {
+ /* Disable and acknowledge all
+ * USB interrupts before freeing
+ * irq, so that no USB spurious
+ * interrupt occurs during USB cable
+ * disconnect which may lead to
+ * IRQ nobody cared error.
+ */
+ writel_relaxed(0, USB_USBINTR);
+ writel_relaxed(readl_relaxed(USB_USBSTS)
+ , USB_USBSTS);
+ /* Ensure that above STOREs are
+ * completed before enabling
+ * interrupts */
+ wmb();
free_irq(ui->irq, ui);
ui->irq = 0;
}
diff --git a/drivers/usb/gadget/u_bam.c b/drivers/usb/gadget/u_bam.c
index 00744fb..fe39700 100644
--- a/drivers/usb/gadget/u_bam.c
+++ b/drivers/usb/gadget/u_bam.c
@@ -652,7 +652,8 @@
static void gbam2bam_disconnect_work(struct work_struct *w)
{
- struct gbam_port *port = container_of(w, struct gbam_port, connect_w);
+ struct gbam_port *port =
+ container_of(w, struct gbam_port, disconnect_w);
struct bam_ch_info *d = &port->data_ch;
int ret;
diff --git a/drivers/usb/gadget/u_data_hsic.c b/drivers/usb/gadget/u_data_hsic.c
index 746c041..575d85b 100644
--- a/drivers/usb/gadget/u_data_hsic.c
+++ b/drivers/usb/gadget/u_data_hsic.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-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
@@ -634,10 +634,10 @@
ghsic_data_free_buffers(port);
- data_bridge_close(port->brdg.ch_id);
-
+ cancel_work_sync(&port->connect_w);
+ if (test_and_clear_bit(CH_OPENED, &port->bridge_sts))
+ data_bridge_close(port->brdg.ch_id);
clear_bit(CH_READY, &port->bridge_sts);
- clear_bit(CH_OPENED, &port->bridge_sts);
return 0;
}
diff --git a/drivers/usb/gadget/u_rmnet_ctrl_smd.c b/drivers/usb/gadget/u_rmnet_ctrl_smd.c
index 169008b..5817779 100644
--- a/drivers/usb/gadget/u_rmnet_ctrl_smd.c
+++ b/drivers/usb/gadget/u_rmnet_ctrl_smd.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011-2012, Code Aurora Forum. 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
@@ -32,6 +32,8 @@
#define SMD_CH_MAX_LEN 20
#define CH_OPENED 0
#define CH_READY 1
+#define CH_PREPARE_READY 2
+
struct smd_ch_info {
struct smd_channel *ch;
char *name;
@@ -60,6 +62,7 @@
spinlock_t port_lock;
struct delayed_work connect_w;
+ struct delayed_work disconnect_w;
};
static struct rmnet_ctrl_ports {
@@ -324,6 +327,7 @@
{
struct rmnet_ctrl_port *port =
container_of(w, struct rmnet_ctrl_port, connect_w.work);
+ struct rmnet_ctrl_ports *port_entry = &ctrl_smd_ports[port->port_num];
struct smd_ch_info *c = &port->ctrl_ch;
unsigned long flags;
int set_bits = 0;
@@ -332,8 +336,13 @@
pr_debug("%s:\n", __func__);
- if (!test_bit(CH_READY, &c->flags))
+ if (!test_bit(CH_READY, &c->flags)) {
+ if (!test_bit(CH_PREPARE_READY, &c->flags)) {
+ set_bit(CH_PREPARE_READY, &c->flags);
+ platform_driver_register(&(port_entry->pdrv));
+ }
return;
+ }
ret = smd_open(c->name, &c->ch, port, grmnet_ctrl_smd_notify);
if (ret) {
@@ -390,6 +399,23 @@
return 0;
}
+static void grmnet_ctrl_smd_disconnect_w(struct work_struct *w)
+{
+ struct rmnet_ctrl_port *port =
+ container_of(w, struct rmnet_ctrl_port,
+ disconnect_w.work);
+ struct smd_ch_info *c;
+ struct platform_driver *pdrv;
+
+ c = &port->ctrl_ch;
+ if (test_bit(CH_READY, &c->flags) ||
+ test_bit(CH_PREPARE_READY, &c->flags)) {
+ clear_bit(CH_PREPARE_READY, &c->flags);
+ pdrv = &ctrl_smd_ports[port->port_num].pdrv;
+ platform_driver_unregister(pdrv);
+ }
+}
+
void gsmd_ctrl_disconnect(struct grmnet *gr, u8 port_num)
{
struct rmnet_ctrl_port *port;
@@ -435,6 +461,8 @@
smd_close(c->ch);
c->ch = NULL;
}
+
+ queue_delayed_work(grmnet_ctrl_wq, &port->disconnect_w, 0);
}
#define SMD_CH_MAX_LEN 20
@@ -452,6 +480,7 @@
c = &port->ctrl_ch;
if (!strncmp(c->name, pdev->name, SMD_CH_MAX_LEN)) {
+ clear_bit(CH_PREPARE_READY, &c->flags);
set_bit(CH_READY, &c->flags);
/* if usb is online, try opening smd_ch */
@@ -520,6 +549,7 @@
spin_lock_init(&port->port_lock);
INIT_DELAYED_WORK(&port->connect_w, grmnet_ctrl_smd_connect_w);
+ INIT_DELAYED_WORK(&port->disconnect_w, grmnet_ctrl_smd_disconnect_w);
c = &port->ctrl_ch;
c->name = rmnet_ctrl_names[portno];
@@ -537,8 +567,6 @@
pdrv->driver.name = c->name;
pdrv->driver.owner = THIS_MODULE;
- platform_driver_register(pdrv);
-
pr_debug("%s: port:%p portno:%d\n", __func__, port, portno);
return 0;
diff --git a/drivers/usb/host/ehci-msm-hsic.c b/drivers/usb/host/ehci-msm-hsic.c
index 1141a24..8ab4a6a 100644
--- a/drivers/usb/host/ehci-msm-hsic.c
+++ b/drivers/usb/host/ehci-msm-hsic.c
@@ -1625,19 +1625,23 @@
{
struct device_node *node = pdev->dev.of_node;
struct msm_hsic_host_platform_data *pdata;
+ int res_gpio;
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata) {
dev_err(&pdev->dev, "unable to allocate platform data\n");
return NULL;
}
- pdata->strobe = of_get_named_gpio(node, "hsic,strobe-gpio", 0);
- if (pdata->strobe < 0)
- pdata->strobe = 0;
- pdata->data = of_get_named_gpio(node, "hsic,data-gpio", 0);
- if (pdata->data < 0)
- pdata->data = 0;
+ res_gpio = of_get_named_gpio(node, "hsic,strobe-gpio", 0);
+ if (res_gpio < 0)
+ res_gpio = 0;
+ pdata->strobe = res_gpio;
+
+ res_gpio = of_get_named_gpio(node, "hsic,data-gpio", 0);
+ if (res_gpio < 0)
+ res_gpio = 0;
+ pdata->data = res_gpio;
pdata->ignore_cal_pad_config = of_property_read_bool(node,
"hsic,ignore-cal-pad-config");
diff --git a/drivers/usb/host/ehci-msm2.c b/drivers/usb/host/ehci-msm2.c
index 34d90fb..5c87691 100644
--- a/drivers/usb/host/ehci-msm2.c
+++ b/drivers/usb/host/ehci-msm2.c
@@ -1,6 +1,6 @@
/* ehci-msm2.c - HSUSB Host Controller Driver Implementation
*
- * Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2008-2013, The Linux Foundation. All rights reserved.
*
* Partly derived from ehci-fsl.c and ehci-hcd.c
* Copyright (c) 2000-2004 by David Brownell
@@ -32,6 +32,7 @@
#include <linux/usb/ulpi.h>
#include <linux/usb/msm_hsusb_hw.h>
#include <linux/usb/msm_hsusb.h>
+#include <linux/of.h>
#include <mach/clk.h>
#include <mach/msm_xo.h>
#include <mach/msm_iomap.h>
@@ -42,10 +43,12 @@
struct msm_hcd {
struct ehci_hcd ehci;
+ spinlock_t wakeup_lock;
struct device *dev;
struct clk *iface_clk;
struct clk *core_clk;
struct clk *alt_core_clk;
+ struct clk *phy_sleep_clk;
struct regulator *hsusb_vddcx;
struct regulator *hsusb_3p3;
struct regulator *hsusb_1p8;
@@ -60,6 +63,9 @@
atomic_t pm_usage_cnt;
struct wake_lock wlock;
struct work_struct phy_susp_fail_work;
+ int async_irq;
+ bool async_irq_enabled;
+ uint32_t async_int_cnt;
};
static inline struct msm_hcd *hcd_to_mhcd(struct usb_hcd *hcd)
@@ -272,6 +278,7 @@
int rc = 0;
struct usb_hcd *hcd = mhcd_to_hcd(mhcd);
const struct msm_usb_host_platform_data *pdata;
+ int ret = 0;
pdata = mhcd->dev->platform_data;
@@ -282,7 +289,11 @@
}
mhcd->vbus = devm_regulator_get(mhcd->dev, "vbus");
- if (IS_ERR(mhcd->vbus)) {
+ ret = PTR_ERR(mhcd->vbus);
+ if (ret == -EPROBE_DEFER) {
+ pr_debug("failed to get vbus handle, defer probe\n");
+ return ret;
+ } else if (IS_ERR(mhcd->vbus)) {
pr_err("Unable to get vbus\n");
return -ENODEV;
}
@@ -435,18 +446,38 @@
return 0;
}
+/**
+ * Do hard reset to USB hardware block using one of reset methodology based
+ * on availablity of alt_core_clk. There are two kinds of hardware resets.
+ * 1. Conventional synchronous reset where clocks to blocks to be ON while
+ * issuing the reset. 2. Asynchronous reset which requires clocks to be OFF.
+ */
static int msm_ehci_link_clk_reset(struct msm_hcd *mhcd, bool assert)
{
int ret;
if (assert) {
- ret = clk_reset(mhcd->alt_core_clk, CLK_RESET_ASSERT);
+ if (!IS_ERR(mhcd->alt_core_clk)) {
+ ret = clk_reset(mhcd->alt_core_clk, CLK_RESET_ASSERT);
+ } else {
+ /* Using asynchronous block reset to the hardware */
+ clk_disable(mhcd->iface_clk);
+ clk_disable(mhcd->core_clk);
+ ret = clk_reset(mhcd->core_clk, CLK_RESET_ASSERT);
+ }
if (ret)
- dev_err(mhcd->dev, "usb alt_core_clk assert failed\n");
+ dev_err(mhcd->dev, "usb clk assert failed\n");
} else {
- ret = clk_reset(mhcd->alt_core_clk, CLK_RESET_DEASSERT);
+ if (!IS_ERR(mhcd->alt_core_clk)) {
+ ret = clk_reset(mhcd->alt_core_clk, CLK_RESET_DEASSERT);
+ } else {
+ ret = clk_reset(mhcd->core_clk, CLK_RESET_DEASSERT);
+ ndelay(200);
+ clk_enable(mhcd->core_clk);
+ clk_enable(mhcd->iface_clk);
+ }
if (ret)
- dev_err(mhcd->dev, "usb alt_core_clk deassert failed\n");
+ dev_err(mhcd->dev, "usb clk deassert failed\n");
}
return ret;
@@ -455,6 +486,7 @@
static int msm_ehci_phy_reset(struct msm_hcd *mhcd)
{
struct usb_hcd *hcd = mhcd_to_hcd(mhcd);
+ struct msm_usb_host_platform_data *pdata;
u32 val;
int ret;
int retries;
@@ -463,12 +495,17 @@
if (ret)
return ret;
- udelay(1);
+ usleep_range(10, 12);
ret = msm_ehci_link_clk_reset(mhcd, 0);
if (ret)
return ret;
+ pdata = mhcd->dev->platform_data;
+ if (pdata && pdata->use_sec_phy)
+ /* select secondary phy if offset is set for USB operation */
+ writel_relaxed(readl_relaxed(USB_PHY_CTRL2) | (1<<16),
+ USB_PHY_CTRL2);
val = readl_relaxed(USB_PORTSC) & ~PORTSC_PTS_MASK;
writel_relaxed(val | PORTSC_PTS_ULPI, USB_PORTSC);
@@ -499,10 +536,13 @@
static int msm_hsusb_reset(struct msm_hcd *mhcd)
{
struct usb_hcd *hcd = mhcd_to_hcd(mhcd);
+ struct msm_usb_host_platform_data *pdata;
unsigned long timeout;
int ret;
- clk_prepare_enable(mhcd->alt_core_clk);
+ if (!IS_ERR(mhcd->alt_core_clk))
+ clk_prepare_enable(mhcd->alt_core_clk);
+
ret = msm_ehci_phy_reset(mhcd);
if (ret) {
dev_err(mhcd->dev, "phy_reset failed\n");
@@ -521,6 +561,11 @@
/* select ULPI phy */
writel_relaxed(0x80000000, USB_PORTSC);
+ pdata = mhcd->dev->platform_data;
+ if (pdata && pdata->use_sec_phy)
+ writel_relaxed(readl_relaxed(USB_PHY_CTRL2) | (1<<16),
+ USB_PHY_CTRL2);
+
msleep(100);
writel_relaxed(0x0, USB_AHBBURST);
@@ -528,7 +573,9 @@
/* Ensure that RESET operation is completed before turning off clock */
mb();
- clk_disable_unprepare(mhcd->alt_core_clk);
+
+ if (!IS_ERR(mhcd->alt_core_clk))
+ clk_disable_unprepare(mhcd->alt_core_clk);
/*rising edge interrupts with Dp rise and fall enabled*/
msm_ulpi_write(mhcd, ULPI_INT_DP, ULPI_USB_INT_EN_RISE);
@@ -626,6 +673,11 @@
enable_irq_wake(mhcd->pmic_gpio_dp_irq);
enable_irq(mhcd->pmic_gpio_dp_irq);
}
+ if (mhcd->async_irq) {
+ mhcd->async_irq_enabled = 1;
+ enable_irq_wake(mhcd->async_irq);
+ enable_irq(mhcd->async_irq);
+ }
wake_unlock(&mhcd->wlock);
dev_info(mhcd->dev, "EHCI USB in low power mode\n");
@@ -639,6 +691,7 @@
unsigned long timeout;
unsigned temp;
int ret;
+ unsigned long flags;
if (!atomic_read(&mhcd->in_lpm)) {
dev_dbg(mhcd->dev, "%s called in !in_lpm\n", __func__);
@@ -650,6 +703,14 @@
disable_irq_nosync(mhcd->pmic_gpio_dp_irq);
mhcd->pmic_gpio_dp_irq_enabled = 0;
}
+ spin_lock_irqsave(&mhcd->wakeup_lock, flags);
+ if (mhcd->async_irq_enabled) {
+ disable_irq_wake(mhcd->async_irq);
+ disable_irq_nosync(mhcd->async_irq);
+ mhcd->async_irq_enabled = 0;
+ }
+ spin_unlock_irqrestore(&mhcd->wakeup_lock, flags);
+
wake_lock(&mhcd->wlock);
/* Vote for TCXO when waking up the phy */
@@ -722,6 +783,36 @@
return ehci_irq(hcd);
}
+static irqreturn_t msm_async_irq(int irq, void *data)
+{
+ struct msm_hcd *mhcd = data;
+ int ret;
+
+ mhcd->async_int_cnt++;
+ dev_dbg(mhcd->dev, "%s: hsusb host remote wakeup interrupt cnt: %u\n",
+ __func__, mhcd->async_int_cnt);
+
+ wake_lock(&mhcd->wlock);
+
+ spin_lock(&mhcd->wakeup_lock);
+ if (mhcd->async_irq_enabled) {
+ mhcd->async_irq_enabled = 0;
+ disable_irq_wake(irq);
+ disable_irq_nosync(irq);
+ }
+ spin_unlock(&mhcd->wakeup_lock);
+
+ if (!atomic_read(&mhcd->pm_usage_cnt)) {
+ ret = pm_runtime_get(mhcd->dev);
+ if ((ret == 1) || (ret == -EINPROGRESS))
+ pm_runtime_put_noidle(mhcd->dev);
+ else
+ atomic_set(&mhcd->pm_usage_cnt, 1);
+ }
+
+ return IRQ_HANDLED;
+}
+
static irqreturn_t msm_ehci_host_wakeup_irq(int irq, void *data)
{
@@ -751,6 +842,8 @@
static int msm_ehci_reset(struct usb_hcd *hcd)
{
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+ struct msm_hcd *mhcd = hcd_to_mhcd(hcd);
+ struct msm_usb_host_platform_data *pdata;
int retval;
ehci->caps = USB_CAPLENGTH;
@@ -785,6 +878,11 @@
/* Disable streaming mode and select host mode */
writel_relaxed(0x13, USB_USBMODE);
+ pdata = mhcd->dev->platform_data;
+ if (pdata && pdata->use_sec_phy)
+ writel_relaxed(readl_relaxed(USB_PHY_CTRL2) | (1<<16),
+ USB_PHY_CTRL2);
+
ehci_port_power(ehci, 1);
return 0;
}
@@ -844,12 +942,10 @@
/* 60MHz alt_core_clk is for LINK to be used during PHY RESET */
mhcd->alt_core_clk = clk_get(mhcd->dev, "alt_core_clk");
- if (IS_ERR(mhcd->alt_core_clk)) {
- dev_err(mhcd->dev, "failed to get alt_core_clk\n");
- ret = PTR_ERR(mhcd->alt_core_clk);
- return ret;
- }
- clk_set_rate(mhcd->alt_core_clk, 60000000);
+ if (IS_ERR(mhcd->alt_core_clk))
+ dev_dbg(mhcd->dev, "failed to get alt_core_clk\n");
+ else
+ clk_set_rate(mhcd->alt_core_clk, 60000000);
/* iface_clk is required for data transfers */
mhcd->iface_clk = clk_get(mhcd->dev, "iface_clk");
@@ -871,6 +967,12 @@
}
clk_set_rate(mhcd->core_clk, INT_MAX);
+ mhcd->phy_sleep_clk = clk_get(mhcd->dev, "sleep_clk");
+ if (IS_ERR(mhcd->phy_sleep_clk))
+ dev_dbg(mhcd->dev, "failed to get sleep_clk\n");
+ else
+ clk_prepare_enable(mhcd->phy_sleep_clk);
+
clk_prepare_enable(mhcd->core_clk);
clk_prepare_enable(mhcd->iface_clk);
@@ -880,14 +982,39 @@
clk_disable_unprepare(mhcd->iface_clk);
clk_disable_unprepare(mhcd->core_clk);
clk_put(mhcd->core_clk);
+ if (!IS_ERR(mhcd->phy_sleep_clk)) {
+ clk_disable_unprepare(mhcd->phy_sleep_clk);
+ clk_put(mhcd->phy_sleep_clk);
+ }
put_iface_clk:
clk_put(mhcd->iface_clk);
put_alt_core_clk:
- clk_put(mhcd->alt_core_clk);
+ if (!IS_ERR(mhcd->alt_core_clk))
+ clk_put(mhcd->alt_core_clk);
return ret;
}
+struct msm_usb_host_platform_data *ehci_msm2_dt_to_pdata(
+ struct platform_device *pdev)
+{
+ struct device_node *node = pdev->dev.of_node;
+ struct msm_usb_host_platform_data *pdata;
+
+ pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata) {
+ dev_err(&pdev->dev, "unable to allocate platform data\n");
+ return NULL;
+ }
+
+ pdata->use_sec_phy = of_property_read_bool(node,
+ "qcom,usb2-enable-hsphy2");
+ of_property_read_u32(node, "qcom,usb2-power-budget",
+ &pdata->power_budget);
+ return pdata;
+}
+
+static u64 ehci_msm_dma_mask = DMA_BIT_MASK(64);
static int __devinit ehci_msm2_probe(struct platform_device *pdev)
{
struct usb_hcd *hcd;
@@ -899,6 +1026,19 @@
dev_dbg(&pdev->dev, "ehci_msm2 probe\n");
+ if (pdev->dev.of_node) {
+ dev_dbg(&pdev->dev, "device tree enabled\n");
+ pdev->dev.platform_data = ehci_msm2_dt_to_pdata(pdev);
+ }
+
+ if (!pdev->dev.platform_data)
+ dev_dbg(&pdev->dev, "No platform data given\n");
+
+ if (!pdev->dev.dma_mask)
+ pdev->dev.dma_mask = &ehci_msm_dma_mask;
+ if (!pdev->dev.coherent_dma_mask)
+ pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
+
hcd = usb_create_hcd(&msm_hc2_driver, &pdev->dev,
dev_name(&pdev->dev));
if (!hcd) {
@@ -932,13 +1072,29 @@
mhcd = hcd_to_mhcd(hcd);
mhcd->dev = &pdev->dev;
+ spin_lock_init(&mhcd->wakeup_lock);
+
+ mhcd->async_irq = platform_get_irq_byname(pdev, "async_irq");
+ if (mhcd->async_irq < 0) {
+ dev_dbg(&pdev->dev, "platform_get_irq for async_int failed\n");
+ mhcd->async_irq = 0;
+ } else {
+ ret = request_irq(mhcd->async_irq, msm_async_irq,
+ IRQF_TRIGGER_RISING, "msm_ehci_host", mhcd);
+ if (ret) {
+ dev_err(&pdev->dev, "request irq failed (ASYNC INT)\n");
+ goto unmap;
+ }
+ disable_irq(mhcd->async_irq);
+ }
+
snprintf(pdev_name, PDEV_NAME_LEN, "%s.%d", pdev->name, pdev->id);
mhcd->xo_handle = msm_xo_get(MSM_XO_TCXO_D0, pdev_name);
if (IS_ERR(mhcd->xo_handle)) {
dev_err(&pdev->dev, "%s not able to get the handle "
"to vote for TCXO D0 buffer\n", __func__);
ret = PTR_ERR(mhcd->xo_handle);
- goto unmap;
+ goto free_async_irq;
}
ret = msm_xo_mode_vote(mhcd->xo_handle, MSM_XO_MODE_ON);
@@ -1047,6 +1203,9 @@
msm_xo_mode_vote(mhcd->xo_handle, MSM_XO_MODE_OFF);
free_xo_handle:
msm_xo_put(mhcd->xo_handle);
+free_async_irq:
+ if (mhcd->async_irq)
+ free_irq(mhcd->async_irq, mhcd);
unmap:
iounmap(hcd->regs);
put_hcd:
@@ -1065,6 +1224,11 @@
disable_irq_wake(mhcd->pmic_gpio_dp_irq);
free_irq(mhcd->pmic_gpio_dp_irq, mhcd);
}
+ if (mhcd->async_irq) {
+ if (mhcd->async_irq_enabled)
+ disable_irq_wake(mhcd->async_irq);
+ free_irq(mhcd->async_irq, mhcd);
+ }
device_init_wakeup(&pdev->dev, 0);
pm_runtime_disable(&pdev->dev);
pm_runtime_set_suspended(&pdev->dev);
@@ -1160,6 +1324,12 @@
};
#endif
+static const struct of_device_id ehci_msm2_dt_match[] = {
+ { .compatible = "qcom,ehci-host",
+ },
+ {}
+};
+
static struct platform_driver ehci_msm2_driver = {
.probe = ehci_msm2_probe,
.remove = __devexit_p(ehci_msm2_remove),
@@ -1168,5 +1338,6 @@
#ifdef CONFIG_PM
.pm = &ehci_msm2_dev_pm_ops,
#endif
+ .of_match_table = ehci_msm2_dt_match,
},
};
diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c
index e55fed7..79dcf2f 100644
--- a/drivers/usb/host/xhci-plat.c
+++ b/drivers/usb/host/xhci-plat.c
@@ -122,6 +122,7 @@
if (!hcd)
return -ENOMEM;
+ hcd_to_bus(hcd)->skip_resume = true;
hcd->rsrc_start = res->start;
hcd->rsrc_len = resource_size(res);
@@ -153,6 +154,7 @@
goto dealloc_usb2_hcd;
}
+ hcd_to_bus(xhci->shared_hcd)->skip_resume = true;
/*
* Set the xHCI pointer before xhci_plat_setup() (aka hcd_driver.reset)
* is called by usb_add_hcd().
@@ -173,6 +175,8 @@
usb_put_transceiver(phy);
goto put_usb3_hcd;
}
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
} else {
pm_runtime_no_callbacks(&pdev->dev);
pm_runtime_set_active(&pdev->dev);
@@ -205,6 +209,7 @@
struct usb_hcd *hcd = platform_get_drvdata(dev);
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+
usb_remove_hcd(xhci->shared_hcd);
usb_put_hcd(xhci->shared_hcd);
@@ -225,11 +230,74 @@
return 0;
}
+#ifdef CONFIG_PM_RUNTIME
+static int xhci_msm_runtime_idle(struct device *dev)
+{
+ dev_dbg(dev, "xhci msm runtime idle\n");
+ return 0;
+}
+
+static int xhci_msm_runtime_suspend(struct device *dev)
+{
+ dev_dbg(dev, "xhci msm runtime suspend\n");
+ /*
+ * Notify OTG about suspend. It takes care of
+ * putting the hardware in LPM.
+ */
+ if (phy)
+ return usb_phy_set_suspend(phy, 1);
+
+ return 0;
+}
+
+static int xhci_msm_runtime_resume(struct device *dev)
+{
+ dev_dbg(dev, "xhci msm runtime resume\n");
+
+ if (phy)
+ return usb_phy_set_suspend(phy, 0);
+
+ return 0;
+}
+#endif
+
+#ifdef CONFIG_PM_SLEEP
+static int xhci_msm_pm_suspend(struct device *dev)
+{
+ dev_dbg(dev, "xhci-msm PM suspend\n");
+
+ if (phy)
+ return usb_phy_set_suspend(phy, 1);
+
+ return 0;
+}
+
+static int xhci_msm_pm_resume(struct device *dev)
+{
+ dev_dbg(dev, "xhci-msm PM resume\n");
+
+ if (pm_runtime_suspended(dev))
+ return 0;
+
+ if (phy)
+ return usb_phy_set_suspend(phy, 0);
+
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops xhci_msm_dev_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(xhci_msm_pm_suspend, xhci_msm_pm_resume)
+ SET_RUNTIME_PM_OPS(xhci_msm_runtime_suspend, xhci_msm_runtime_resume,
+ xhci_msm_runtime_idle)
+};
+
static struct platform_driver usb_xhci_driver = {
.probe = xhci_plat_probe,
.remove = xhci_plat_remove,
.driver = {
.name = "xhci-hcd",
+ .pm = &xhci_msm_dev_pm_ops,
},
};
MODULE_ALIAS("platform:xhci-hcd");
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 2c26998..df41b4f 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -2464,7 +2464,7 @@
/* Wait for the configure endpoint command to complete */
timeleft = wait_for_completion_interruptible_timeout(
cmd_completion,
- USB_CTRL_SET_TIMEOUT);
+ XHCI_CMD_DEFAULT_TIMEOUT);
if (timeleft <= 0) {
xhci_warn(xhci, "%s while waiting for %s command\n",
timeleft == 0 ? "Timeout" : "Signal",
@@ -3433,7 +3433,7 @@
/* XXX: how much time for xHC slot assignment? */
timeleft = wait_for_completion_interruptible_timeout(&xhci->addr_dev,
- USB_CTRL_SET_TIMEOUT);
+ XHCI_CMD_DEFAULT_TIMEOUT);
if (timeleft <= 0) {
xhci_warn(xhci, "%s while waiting for a slot\n",
timeleft == 0 ? "Timeout" : "Signal");
@@ -3549,7 +3549,7 @@
/* ctrl tx can take up to 5 sec; XXX: need more time for xHC? */
timeleft = wait_for_completion_interruptible_timeout(&xhci->addr_dev,
- USB_CTRL_SET_TIMEOUT);
+ XHCI_CMD_DEFAULT_TIMEOUT);
/* FIXME: From section 4.3.4: "Software shall be responsible for timing
* the SetAddress() "recovery interval" required by USB and aborting the
* command on a timeout.
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 127b0e9..8f3651b 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -1248,6 +1248,9 @@
union xhci_trb *last_trb;
};
+/* xHCI command default timeout value */
+#define XHCI_CMD_DEFAULT_TIMEOUT (5 * HZ)
+
struct xhci_dequeue_state {
struct xhci_segment *new_deq_seg;
union xhci_trb *new_deq_ptr;
diff --git a/drivers/usb/misc/ks_bridge.c b/drivers/usb/misc/ks_bridge.c
index dab6e7f..e76a9a8 100644
--- a/drivers/usb/misc/ks_bridge.c
+++ b/drivers/usb/misc/ks_bridge.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2012-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
@@ -297,6 +297,9 @@
if (!test_bit(USB_DEV_CONNECTED, &ksb->flags))
return -ENODEV;
+ if (count > MAX_DATA_PKT_SIZE)
+ count = MAX_DATA_PKT_SIZE;
+
pkt = ksb_alloc_data_pkt(count, GFP_KERNEL, ksb);
if (IS_ERR(pkt)) {
pr_err("unable to allocate data packet");
diff --git a/drivers/usb/misc/mdm_data_bridge.c b/drivers/usb/misc/mdm_data_bridge.c
index 655e2f6..fcbf0e1 100644
--- a/drivers/usb/misc/mdm_data_bridge.c
+++ b/drivers/usb/misc/mdm_data_bridge.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-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
@@ -343,6 +343,9 @@
dev_dbg(&dev->intf->dev, "%s:\n", __func__);
+ cancel_work_sync(&dev->kevent);
+ cancel_work_sync(&dev->process_rx_w);
+
usb_unlink_anchored_urbs(&dev->tx_active);
usb_unlink_anchored_urbs(&dev->rx_active);
usb_unlink_anchored_urbs(&dev->delayed);
@@ -995,9 +998,6 @@
usb_set_intfdata(intf, NULL);
__dev[dev->id] = NULL;
- cancel_work_sync(&dev->process_rx_w);
- cancel_work_sync(&dev->kevent);
-
/*free rx urbs*/
head = &dev->rx_idle;
spin_lock_irqsave(&dev->rx_done.lock, flags);
diff --git a/drivers/usb/otg/msm_otg.c b/drivers/usb/otg/msm_otg.c
index a4bb7b2..2439af0 100644
--- a/drivers/usb/otg/msm_otg.c
+++ b/drivers/usb/otg/msm_otg.c
@@ -1197,7 +1197,7 @@
motg->chg_type == USB_ACA_C_CHARGER))
charger_type = POWER_SUPPLY_TYPE_USB_ACA;
else
- charger_type = POWER_SUPPLY_TYPE_BATTERY;
+ charger_type = POWER_SUPPLY_TYPE_UNKNOWN;
if (!psy) {
pr_err("No USB power supply registered!\n");
@@ -2276,6 +2276,11 @@
case USB_CHG_STATE_SECONDARY_DONE:
motg->chg_state = USB_CHG_STATE_DETECTED;
case USB_CHG_STATE_DETECTED:
+ /*
+ * Notify the charger type to power supply
+ * owner as soon as we determine the charger.
+ */
+ msm_otg_notify_chg_type(motg);
msm_chg_block_off(motg);
msm_chg_enable_aca_det(motg);
/*
diff --git a/drivers/video/msm/external_common.c b/drivers/video/msm/external_common.c
index 0411baa..a3b92ed 100644
--- a/drivers/video/msm/external_common.c
+++ b/drivers/video/msm/external_common.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2012, Code Aurora Forum. 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
@@ -829,6 +829,74 @@
return ret;
}
+static ssize_t hdmi_common_rda_audio_data_block(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int adb_size = 0;
+ int adb_count = 0;
+ ssize_t ret = 0;
+ char *data = buf;
+
+ if (!external_common_state)
+ return 0;
+
+ adb_count = 1;
+ adb_size = external_common_state->adb_size;
+ ret = sizeof(adb_count) + sizeof(adb_size) + adb_size;
+
+ if (ret > PAGE_SIZE) {
+ DEV_DBG("%s: Insufficient buffer size\n", __func__);
+ return 0;
+ }
+
+ /* Currently only extracting one audio data block */
+ memcpy(data, &adb_count, sizeof(adb_count));
+ data += sizeof(adb_count);
+ memcpy(data, &adb_size, sizeof(adb_size));
+ data += sizeof(adb_size);
+ memcpy(data, external_common_state->audio_data_block,
+ external_common_state->adb_size);
+
+ print_hex_dump(KERN_DEBUG, "AUDIO DATA BLOCK: ", DUMP_PREFIX_NONE,
+ 32, 8, buf, ret, false);
+
+ return ret;
+}
+
+static ssize_t hdmi_common_rda_spkr_alloc_data_block(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int sadb_size = 0;
+ int sadb_count = 0;
+ ssize_t ret = 0;
+ char *data = buf;
+
+ if (!external_common_state)
+ return 0;
+
+ sadb_count = 1;
+ sadb_size = external_common_state->sadb_size;
+ ret = sizeof(sadb_count) + sizeof(sadb_size) + sadb_size;
+
+ if (ret > PAGE_SIZE) {
+ DEV_DBG("%s: Insufficient buffer size\n", __func__);
+ return 0;
+ }
+
+ /* Currently only extracting one speaker allocation data block */
+ memcpy(data, &sadb_count, sizeof(sadb_count));
+ data += sizeof(sadb_count);
+ memcpy(data, &sadb_size, sizeof(sadb_size));
+ data += sizeof(sadb_size);
+ memcpy(data, external_common_state->spkr_alloc_data_block,
+ external_common_state->sadb_size);
+
+ print_hex_dump(KERN_DEBUG, "SPKR ALLOC DATA BLOCK: ", DUMP_PREFIX_NONE,
+ 32, 8, buf, ret, false);
+
+ return ret;
+}
+
static DEVICE_ATTR(video_mode, S_IRUGO | S_IWUGO,
external_common_rda_video_mode, external_common_wta_video_mode);
static DEVICE_ATTR(video_mode_str, S_IRUGO, external_common_rda_video_mode_str,
@@ -859,6 +927,10 @@
hdmi_3d_wta_format_3d);
#endif
static DEVICE_ATTR(hdmi_primary, S_IRUGO, hdmi_common_rda_hdmi_primary, NULL);
+static DEVICE_ATTR(audio_data_block, S_IRUGO, hdmi_common_rda_audio_data_block,
+ NULL);
+static DEVICE_ATTR(spkr_alloc_data_block, S_IRUGO,
+ hdmi_common_rda_spkr_alloc_data_block, NULL);
static struct attribute *external_common_fs_attrs[] = {
&dev_attr_video_mode.attr,
@@ -887,6 +959,8 @@
&dev_attr_cec_wr_frame.attr,
#endif /* CONFIG_FB_MSM_HDMI_MSM_PANEL_CEC_SUPPORT */
&dev_attr_hdmi_primary.attr,
+ &dev_attr_audio_data_block.attr,
+ &dev_attr_spkr_alloc_data_block.attr,
NULL,
};
static struct attribute_group external_common_fs_attr_group = {
@@ -1205,45 +1279,33 @@
static void hdmi_edid_extract_speaker_allocation_data(const uint8 *in_buf)
{
uint8 len;
- const uint8 *sad = hdmi_edid_find_block(in_buf, DBC_START_OFFSET, 4,
+ const uint8 *sadb = hdmi_edid_find_block(in_buf, DBC_START_OFFSET, 4,
&len);
- if (sad == NULL)
+ if (sadb == NULL)
return;
- external_common_state->speaker_allocation_block = sad[1];
- DEV_DBG("EDID: speaker allocation data SP byte = %08x %s%s%s%s%s%s%s\n",
- sad[1],
- (sad[1] & BIT(0)) ? "FL/FR," : "",
- (sad[1] & BIT(1)) ? "LFE," : "",
- (sad[1] & BIT(2)) ? "FC," : "",
- (sad[1] & BIT(3)) ? "RL/RR," : "",
- (sad[1] & BIT(4)) ? "RC," : "",
- (sad[1] & BIT(5)) ? "FLC/FRC," : "",
- (sad[1] & BIT(6)) ? "RLC/RRC," : "");
+ if (len != MAX_SPKR_ALLOC_DATA_BLOCK_SIZE)
+ return;
+
+ memcpy(external_common_state->spkr_alloc_data_block, sadb + 1, len);
+ external_common_state->sadb_size = len;
}
static void hdmi_edid_extract_audio_data_blocks(const uint8 *in_buf)
{
uint8 len;
- const uint8 *sad = hdmi_edid_find_block(in_buf, DBC_START_OFFSET, 1,
+ const uint8 *adb = hdmi_edid_find_block(in_buf, DBC_START_OFFSET, 1,
&len);
- uint32 *adb = external_common_state->audio_data_blocks;
- if (sad == NULL)
+ if (external_common_state->audio_data_block == NULL)
return;
- external_common_state->audio_data_block_cnt = 0;
- while (len >= 3 && external_common_state->audio_data_block_cnt < 16) {
- DEV_DBG("EDID: Audio Data Block=<ch=%d, format=%d "
- "sampling=0x%02x bit-depth=0x%02x>\n",
- (sad[1] & 0x7)+1, sad[1] >> 3, sad[2], sad[3]);
- *adb++ = (uint32)sad[1] + ((uint32)sad[2] << 8)
- + ((uint32)sad[2] << 16);
- ++external_common_state->audio_data_block_cnt;
- len -= 3;
- sad += 3;
- }
+ if (len > MAX_AUDIO_DATA_BLOCK_SIZE)
+ return;
+
+ memcpy(external_common_state->audio_data_block, adb + 1, len);
+ external_common_state->adb_size = len;
}
static void hdmi_edid_extract_extended_data_blocks(const uint8 *in_buf)
@@ -1874,6 +1936,12 @@
sizeof(external_common_state->disp_mode_list));
memset(edid_buf, 0, sizeof(edid_buf));
external_common_state->default_res_supported = false;
+ memset(external_common_state->audio_data_block, 0,
+ sizeof(external_common_state->audio_data_block));
+ memset(external_common_state->spkr_alloc_data_block, 0,
+ sizeof(external_common_state->spkr_alloc_data_block));
+ external_common_state->adb_size = 0;
+ external_common_state->sadb_size = 0;
status = hdmi_common_read_edid_block(0, edid_buf);
if (status || !check_edid_header(edid_buf)) {
diff --git a/drivers/video/msm/external_common.h b/drivers/video/msm/external_common.h
index 70a99ee..b27e4c8 100644
--- a/drivers/video/msm/external_common.h
+++ b/drivers/video/msm/external_common.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2012, Code Aurora Forum. 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
@@ -206,6 +206,14 @@
};
#endif
+/*
+ * As per the CEA-861E spec, there can be a total of 10 short audio
+ * descriptors with each SAD being 3 bytes long.
+ * Thus, the maximum length of the audio data block would be 30 bytes
+ */
+#define MAX_AUDIO_DATA_BLOCK_SIZE 30
+#define MAX_SPKR_ALLOC_DATA_BLOCK_SIZE 3
+
struct external_common_state_type {
boolean hpd_state;
struct kobject *uevent_kobj;
@@ -223,9 +231,7 @@
boolean hpd_feature_on;
boolean hdmi_sink;
struct hdmi_disp_mode_list_type disp_mode_list;
- uint8 speaker_allocation_block;
uint16 video_latency, audio_latency;
- uint8 audio_data_block_cnt;
uint16 physical_address;
uint32 preferred_video_format;
uint8 pt_scan_info;
@@ -235,7 +241,10 @@
uint8 spd_product_description[16];
boolean present_3d;
boolean present_hdcp;
- uint32 audio_data_blocks[16];
+ uint8 audio_data_block[MAX_AUDIO_DATA_BLOCK_SIZE];
+ int adb_size;
+ uint8 spkr_alloc_data_block[MAX_SPKR_ALLOC_DATA_BLOCK_SIZE];
+ int sadb_size;
int (*read_edid_block)(int block, uint8 *edid_buf);
int (*hpd_feature)(int on);
#endif
diff --git a/drivers/video/msm/mdp4.h b/drivers/video/msm/mdp4.h
index 02cdd71..ed0a385 100644
--- a/drivers/video/msm/mdp4.h
+++ b/drivers/video/msm/mdp4.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2009-2012, 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
@@ -955,6 +955,7 @@
int mdp4_pcc_cfg(struct mdp_pcc_cfg_data *cfg_ptr);
int mdp4_argc_cfg(struct mdp_pgc_lut_data *pgc_ptr);
int mdp4_qseed_cfg(struct mdp_qseed_cfg_data *cfg);
+int mdp4_calib_config(struct mdp_calib_config_data *cfg);
int mdp4_qseed_access_cfg(struct mdp_qseed_cfg *cfg, uint32_t base);
u32 mdp4_allocate_writeback_buf(struct msm_fb_data_type *mfd, u32 mix_num);
void mdp4_init_writeback_buf(struct msm_fb_data_type *mfd, u32 mix_num);
diff --git a/drivers/video/msm/mdp4_overlay_dsi_cmd.c b/drivers/video/msm/mdp4_overlay_dsi_cmd.c
index 1d38fb0..5554d88 100644
--- a/drivers/video/msm/mdp4_overlay_dsi_cmd.c
+++ b/drivers/video/msm/mdp4_overlay_dsi_cmd.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, Code Aurora Forum. 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
@@ -870,6 +870,9 @@
pipe = vctrl->base_pipe;
}
+ /* TE enabled */
+ mdp4_mipi_vsync_enable(mfd, pipe, 0);
+
MDP_OUTP(MDP_BASE + 0x021c, 10); /* read pointer */
/*
diff --git a/drivers/video/msm/mdp4_util.c b/drivers/video/msm/mdp4_util.c
index 01ec10e..2423de5 100644
--- a/drivers/video/msm/mdp4_util.c
+++ b/drivers/video/msm/mdp4_util.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2009-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
@@ -634,6 +634,7 @@
#if defined(CONFIG_FB_MSM_WRITEBACK_MSM_PANEL)
if (isr & INTR_OVERLAY2_DONE) {
mdp4_stat.intr_overlay2++;
+ mdp_pipe_ctrl(MDP_OVERLAY2_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
/* disable DTV interrupt */
if (panel & MDP4_PANEL_WRITEBACK)
mdp4_overlay2_done_wfd(&dma_wb_data);
@@ -3080,6 +3081,106 @@
error:
return ret;
}
+
+static int is_valid_calib_addr(void *addr)
+{
+ int ret = 0;
+ unsigned int ptr;
+
+ ptr = (unsigned int) addr;
+
+ if (mdp_rev >= MDP_REV_30 && mdp_rev < MDP_REV_40) {
+ /* if request is outside the MDP reg-map or is not aligned 4 */
+ if (ptr == 0x0 || ptr > 0xF0600 || ptr % 0x4)
+ goto end;
+
+ if (ptr >= 0x90000 && ptr < 0x94000) {
+ if (ptr == 0x90000 || ptr == 0x90070)
+ ret = 1;
+ else if (ptr >= 0x93400 && ptr <= 0x93420)
+ ret = 1;
+ else if (ptr >= 0x93500 && ptr <= 0x93508)
+ ret = 1;
+ else if (ptr >= 0x93580 && ptr <= 0x93588)
+ ret = 1;
+ else if (ptr >= 0x93600 && ptr <= 0x93614)
+ ret = 1;
+ else if (ptr >= 0x93680 && ptr <= 0x93694)
+ ret = 1;
+ else if (ptr >= 0x93800 && ptr <= 0x93BFC)
+ ret = 1;
+ }
+ } else if (mdp_rev >= MDP_REV_40 && mdp_rev <= MDP_REV_44) {
+ /* if request is outside the MDP reg-map or is not aligned 4 */
+ if (ptr > 0xF0600 || ptr % 0x4)
+ goto end;
+
+ if (ptr < 0x90000) {
+ if (ptr == 0x0 || ptr == 0x4 || ptr == 0x28200 ||
+ ptr == 0x28204)
+ ret = 1;
+ } else if (ptr < 0x95000) {
+ if (ptr == 0x90000 || ptr == 0x90070)
+ ret = 1;
+ else if (ptr >= 0x93400 && ptr <= 0x93420)
+ ret = 1;
+ else if (ptr >= 0x93500 && ptr <= 0x93508)
+ ret = 1;
+ else if (ptr >= 0x93580 && ptr <= 0x93588)
+ ret = 1;
+ else if (ptr >= 0x93600 && ptr <= 0x93614)
+ ret = 1;
+ else if (ptr >= 0x93680 && ptr <= 0x93694)
+ ret = 1;
+ else if (ptr >= 0x94800 && ptr <= 0x94BFC)
+ ret = 1;
+ } else if (ptr < 0x9A000) {
+ if (ptr >= 0x98800 && ptr <= 0x9883C)
+ ret = 1;
+ else if (ptr >= 0x98880 && ptr <= 0x988AC)
+ ret = 1;
+ else if (ptr >= 0x98900 && ptr <= 0x9893C)
+ ret = 1;
+ else if (ptr >= 0x98980 && ptr <= 0x989BC)
+ ret = 1;
+ else if (ptr >= 0x98A00 && ptr <= 0x98A3C)
+ ret = 1;
+ else if (ptr >= 0x98A80 && ptr <= 0x98ABC)
+ ret = 1;
+ else if (ptr >= 0x99000 && ptr <= 0x993FC)
+ ret = 1;
+ else if (ptr >= 0x99800 && ptr <= 0x99BFC)
+ ret = 1;
+ } else if (ptr >= 0x9A000 && ptr <= 0x9a08c) {
+ ret = 1;
+ }
+ }
+end:
+ return ret;
+}
+
+int mdp4_calib_config(struct mdp_calib_config_data *cfg)
+{
+ int ret = -1;
+ void *ptr = (void *) cfg->addr;
+
+ if (is_valid_calib_addr(ptr))
+ ret = 0;
+ else
+ return ret;
+
+ ptr = (void *)(((unsigned int) ptr) + MDP_BASE);
+ mdp_clk_ctrl(1);
+ if (cfg->ops & MDP_PP_OPS_READ) {
+ cfg->data = inpdw(ptr);
+ ret = 1;
+ } else if (cfg->ops & MDP_PP_OPS_WRITE) {
+ outpdw(ptr, cfg->data);
+ }
+ mdp_clk_ctrl(0);
+ return ret;
+}
+
u32 mdp4_get_mixer_num(u32 panel_type)
{
u32 mixer_num;
diff --git a/drivers/video/msm/mdss/Kconfig b/drivers/video/msm/mdss/Kconfig
index 56eb90c..7682a49 100644
--- a/drivers/video/msm/mdss/Kconfig
+++ b/drivers/video/msm/mdss/Kconfig
@@ -12,7 +12,7 @@
The MDSS HDMI Panel provides support for transmitting TMDS signals of
MDSS frame buffer data to connected hdmi compliant TVs, monitors etc.
-config FB_MSM_MDSS_HDMI_MHL_8334
+config FB_MSM_MDSS_HDMI_MHL_SII8334
depends on FB_MSM_MDSS_HDMI_PANEL
bool 'MHL SII8334 support '
default n
diff --git a/drivers/video/msm/mdss/Makefile b/drivers/video/msm/mdss/Makefile
index 4deaa8c..17987d4 100644
--- a/drivers/video/msm/mdss/Makefile
+++ b/drivers/video/msm/mdss/Makefile
@@ -19,7 +19,7 @@
obj-$(CONFIG_FB_MSM_MDSS_HDMI_PANEL) += mdss_hdmi_tx.o
obj-$(CONFIG_FB_MSM_MDSS_HDMI_PANEL) += mdss_hdmi_util.o
obj-$(CONFIG_FB_MSM_MDSS_HDMI_PANEL) += mdss_hdmi_edid.o
-obj-$(CONFIG_FB_MSM_MDSS_HDMI_MHL_8334) += mhl_sii8334.o
+obj-$(CONFIG_FB_MSM_MDSS_HDMI_MHL_SII8334) += mhl_sii8334.o mhl_msc.o
obj-$(CONFIG_FB_MSM_MDSS_HDMI_PANEL) += mdss_hdmi_hdcp.o
obj-$(CONFIG_FB_MSM_MDSS_WRITEBACK) += mdss_wb.o
diff --git a/drivers/video/msm/mdss/mdss_fb.c b/drivers/video/msm/mdss/mdss_fb.c
index 50723e7..a283e0a 100644
--- a/drivers/video/msm/mdss/mdss_fb.c
+++ b/drivers/video/msm/mdss/mdss_fb.c
@@ -2,7 +2,7 @@
* Core MDSS framebuffer driver.
*
* Copyright (C) 2007 Google Incorporated
- * Copyright (c) 2008-2012, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2008-2013, The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@@ -370,9 +370,6 @@
mfd->suspend.op_enable = mfd->op_enable;
mfd->suspend.panel_power_on = mfd->panel_power_on;
- del_timer(&mfd->no_update.timer);
- complete(&mfd->no_update.comp);
-
if (mfd->op_enable) {
ret = mdss_fb_blank_sub(FB_BLANK_POWERDOWN, mfd->fbi,
mfd->suspend.op_enable);
@@ -581,6 +578,9 @@
if (mfd->panel_power_on) {
int curr_pwr_state;
+ del_timer(&mfd->no_update.timer);
+ complete(&mfd->no_update.comp);
+
mfd->op_enable = false;
curr_pwr_state = mfd->panel_power_on;
mfd->panel_power_on = false;
@@ -588,10 +588,10 @@
msleep(20);
ret = mfd->off_fnc(mfd);
- if (ret) {
+ if (ret)
mfd->panel_power_on = curr_pwr_state;
+ else
mdss_fb_release_fences(mfd);
- }
mfd->op_enable = true;
}
break;
@@ -889,20 +889,6 @@
var->hsync_len = panel_info->lcdc.h_pulse_width;
var->pixclock = panel_info->clk_rate / 1000;
- if (panel_info->type == MIPI_VIDEO_PANEL) {
- var->reserved[4] = panel_info->mipi.frame_rate;
- } else {
- var->reserved[4] = panel_info->clk_rate /
- ((panel_info->lcdc.h_back_porch +
- panel_info->lcdc.h_front_porch +
- panel_info->lcdc.h_pulse_width +
- panel_info->xres) *
- (panel_info->lcdc.v_back_porch +
- panel_info->lcdc.v_front_porch +
- panel_info->lcdc.v_pulse_width +
- panel_info->yres));
- }
-
/* id field for fb app */
id = (int *)&mfd->panel;
@@ -1589,6 +1575,23 @@
return ret;
}
+static int mdss_fb_get_metadata(struct msm_fb_data_type *mfd,
+ struct msmfb_metadata *metadata)
+{
+ int ret = 0;
+ switch (metadata->op) {
+ case metadata_op_frame_rate:
+ metadata->data.panel_frame_rate =
+ mdss_get_panel_framerate(mfd);
+ break;
+ default:
+ pr_warn("Unsupported request to MDP META IOCTL.\n");
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
static int mdss_fb_ioctl(struct fb_info *info, unsigned int cmd,
unsigned long arg)
{
@@ -1600,6 +1603,7 @@
struct mdp_page_protection fb_page_protection;
int ret = -ENOSYS;
struct mdp_buf_sync buf_sync;
+ struct msmfb_metadata metadata;
mdss_fb_power_setting_idle(mfd);
@@ -1683,6 +1687,15 @@
ret = mdss_fb_display_commit(info, argp);
break;
+ case MSMFB_METADATA_GET:
+ ret = copy_from_user(&metadata, argp, sizeof(metadata));
+ if (ret)
+ return ret;
+ ret = mdss_fb_get_metadata(mfd, &metadata);
+ if (!ret)
+ ret = copy_to_user(argp, &metadata, sizeof(metadata));
+ break;
+
default:
if (mfd->ioctl_handler)
ret = mfd->ioctl_handler(mfd, cmd, argp);
diff --git a/drivers/video/msm/mdss/mdss_mdp.c b/drivers/video/msm/mdss/mdss_mdp.c
index 7ca06ff..cd836a0 100644
--- a/drivers/video/msm/mdss/mdss_mdp.c
+++ b/drivers/video/msm/mdss/mdss_mdp.c
@@ -1,7 +1,7 @@
/*
* MDSS MDP Interface (used by framebuffer core)
*
- * Copyright (c) 2007-2012, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2007-2013, The Linux Foundation. All rights reserved.
* Copyright (C) 2007 Google Incorporated
*
* This software is licensed under the terms of the GNU General Public
@@ -1087,7 +1087,7 @@
{
mdata->early_suspend.suspend = mdss_mdp_early_suspend;
mdata->early_suspend.resume = mdss_mdp_late_resume;
- mdata->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN;
+ mdata->early_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB;
register_early_suspend(&mdata->early_suspend);
return 0;
diff --git a/drivers/video/msm/mdss/mdss_mdp.h b/drivers/video/msm/mdss/mdss_mdp.h
index 9503489..40cca22 100644
--- a/drivers/video/msm/mdss/mdss_mdp.h
+++ b/drivers/video/msm/mdss/mdss_mdp.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. 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
@@ -235,7 +235,6 @@
struct msm_fb_data_type *mfd;
struct mdss_mdp_mixer *mixer;
- struct mutex lock;
struct mdp_overlay req_data;
u32 params_changed;
@@ -309,6 +308,7 @@
int mdss_mdp_csc_setup_data(u32 block, u32 blk_idx, u32 tbl_idx,
struct mdp_csc_cfg *data);
+int mdss_mdp_pipe_pp_setup(struct mdss_mdp_pipe *pipe, u32 *op);
int mdss_mdp_pp_setup(struct mdss_mdp_ctl *ctl);
int mdss_mdp_pcc_config(struct mdp_pcc_cfg_data *cfg_ptr, u32 *copyback);
@@ -326,10 +326,10 @@
struct mdss_mdp_pipe *mdss_mdp_pipe_alloc_pnum(u32 pnum);
-struct mdss_mdp_pipe *mdss_mdp_pipe_alloc_locked(u32 type);
-struct mdss_mdp_pipe *mdss_mdp_pipe_get_locked(u32 ndx);
-int mdss_mdp_pipe_lock(struct mdss_mdp_pipe *pipe);
-void mdss_mdp_pipe_unlock(struct mdss_mdp_pipe *pipe);
+struct mdss_mdp_pipe *mdss_mdp_pipe_alloc(u32 type);
+struct mdss_mdp_pipe *mdss_mdp_pipe_get(u32 ndx);
+int mdss_mdp_pipe_map(struct mdss_mdp_pipe *pipe);
+void mdss_mdp_pipe_unmap(struct mdss_mdp_pipe *pipe);
int mdss_mdp_pipe_destroy(struct mdss_mdp_pipe *pipe);
int mdss_mdp_pipe_queue_data(struct mdss_mdp_pipe *pipe,
@@ -342,6 +342,7 @@
struct mdss_mdp_format_params *mdss_mdp_get_format_params(u32 format);
int mdss_mdp_put_img(struct mdss_mdp_img_data *data);
int mdss_mdp_get_img(struct msmfb_data *img, struct mdss_mdp_img_data *data);
+u32 mdss_get_panel_framerate(struct msm_fb_data_type *mfd);
int mdss_mdp_wb_kickoff(struct mdss_mdp_ctl *ctl);
int mdss_mdp_wb_ioctl_handler(struct msm_fb_data_type *mfd, u32 cmd, void *arg);
diff --git a/drivers/video/msm/mdss/mdss_mdp_ctl.c b/drivers/video/msm/mdss/mdss_mdp_ctl.c
index f62dd24..6030cbc 100644
--- a/drivers/video/msm/mdss/mdss_mdp_ctl.c
+++ b/drivers/video/msm/mdss/mdss_mdp_ctl.c
@@ -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
@@ -698,6 +698,14 @@
ctl->power_on = false;
ctl->play_cnt = 0;
ctl->clk_rate = 0;
+ if (ctl->mixer_left) {
+ mdss_mdp_ctl_write(ctl, MDSS_MDP_REG_CTL_LAYER(
+ ctl->mixer_left->num), 0);
+ }
+ if (ctl->mixer_right) {
+ mdss_mdp_ctl_write(ctl, MDSS_MDP_REG_CTL_LAYER(
+ ctl->mixer_right->num), 0);
+ }
mdss_mdp_ctl_perf_commit(MDSS_MDP_PERF_UPDATE_ALL);
}
@@ -784,6 +792,14 @@
stage);
}
+ if (mixercfg == MDSS_MDP_LM_BORDER_COLOR &&
+ pipe->src_fmt->alpha_enable &&
+ pipe->dst.w == mixer->width &&
+ pipe->dst.h == mixer->height) {
+ pr_debug("setting pipe=%d as BG_PIPE\n", pipe->num);
+ bgalpha = 1;
+ }
+
mixercfg |= stage << (3 * pipe->num);
MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_LM_OP_MODE, blend_op);
diff --git a/drivers/video/msm/mdss/mdss_mdp_hwio.h b/drivers/video/msm/mdss/mdss_mdp_hwio.h
index b6ac126..d2b2eab 100644
--- a/drivers/video/msm/mdss/mdss_mdp_hwio.h
+++ b/drivers/video/msm/mdss/mdss_mdp_hwio.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. 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
@@ -185,6 +185,7 @@
#define MDSS_MDP_REG_VIG_QSEED2_C03_INIT_PHASEY 0x224
#define MDSS_MDP_REG_VIG_QSEED2_C12_INIT_PHASEX 0x228
#define MDSS_MDP_REG_VIG_QSEED2_C12_INIT_PHASEY 0x22C
+#define MDSS_MDP_REG_VIG_PA_BASE 0x310
#define MDSS_MDP_REG_SCALE_CONFIG 0x204
#define MDSS_MDP_REG_SCALE_PHASE_STEP_X 0x210
@@ -340,6 +341,7 @@
#define MDSS_MDP_REG_DSPP_HIST_LUT_BASE 0x230
#define MDSS_MDP_REG_DSPP_PA_BASE 0x238
#define MDSS_MDP_REG_DSPP_GAMUT_BASE 0x2DC
+#define MDSS_MDP_REG_DSPP_GC_BASE 0x2B0
enum mdss_mpd_intf_index {
MDSS_MDP_NO_INTF,
diff --git a/drivers/video/msm/mdss/mdss_mdp_intf_writeback.c b/drivers/video/msm/mdss/mdss_mdp_intf_writeback.c
index a1f1bcc..2c0ddda 100644
--- a/drivers/video/msm/mdss/mdss_mdp_intf_writeback.c
+++ b/drivers/video/msm/mdss/mdss_mdp_intf_writeback.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. 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
@@ -265,6 +265,9 @@
ctx = (struct mdss_mdp_writeback_ctx *) ctl->priv_data;
if (ctx) {
+ mdss_mdp_set_intr_callback(ctx->intr_type, ctx->intf_num,
+ NULL, NULL);
+
ctl->priv_data = NULL;
ctx->ref_cnt--;
}
@@ -318,8 +321,6 @@
flush_bits = BIT(16); /* WB */
mdss_mdp_ctl_write(ctl, MDSS_MDP_REG_CTL_FLUSH, flush_bits);
- mdss_mdp_set_intr_callback(ctx->intr_type, ctx->intf_num,
- mdss_mdp_writeback_intr_done, ctx);
mdss_mdp_irq_enable(ctx->intr_type, ctx->intf_num);
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
@@ -353,6 +354,9 @@
ctx->wb_num = ctl->num; /* wb num should match ctl num */
ctx->initialized = false;
+ mdss_mdp_set_intr_callback(ctx->intr_type, ctx->intf_num,
+ mdss_mdp_writeback_intr_done, ctx);
+
if (ctx->type == MDSS_MDP_WRITEBACK_TYPE_ROTATOR)
ctl->prepare_fnc = mdss_mdp_writeback_prepare_rot;
else /* wfd or line mode */
diff --git a/drivers/video/msm/mdss/mdss_mdp_overlay.c b/drivers/video/msm/mdss/mdss_mdp_overlay.c
index d278554..3956228 100644
--- a/drivers/video/msm/mdss/mdss_mdp_overlay.c
+++ b/drivers/video/msm/mdss/mdss_mdp_overlay.c
@@ -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
@@ -39,14 +39,14 @@
{
struct mdss_mdp_pipe *pipe;
- pipe = mdss_mdp_pipe_get_locked(req->id);
- if (pipe == NULL) {
+ pipe = mdss_mdp_pipe_get(req->id);
+ if (IS_ERR_OR_NULL(pipe)) {
pr_err("invalid pipe ndx=%x\n", req->id);
- return -ENODEV;
+ return pipe ? PTR_ERR(pipe) : -ENODEV;
}
*req = pipe->req_data;
- mdss_mdp_pipe_unlock(pipe);
+ mdss_mdp_pipe_unmap(pipe);
return 0;
}
@@ -263,11 +263,11 @@
pipe = mdss_mdp_mixer_stage_pipe(mfd->ctl, mixer_mux, req->z_order);
if (pipe && pipe->ndx != req->id) {
- pr_err("stage %d taken by pnum=%d\n", req->z_order, pipe->num);
- return -EBUSY;
+ pr_debug("replacing pnum=%d at stage=%d mux=%d\n",
+ pipe->num, req->z_order, mixer_mux);
+ pipe->params_changed = true;
}
-
if (req->id == MSMFB_NEW_REQUEST) {
mixer = mdss_mdp_mixer_get(mfd->ctl, mixer_mux);
if (!mixer) {
@@ -280,12 +280,12 @@
else
pipe_type = MDSS_MDP_PIPE_TYPE_RGB;
- pipe = mdss_mdp_pipe_alloc_locked(pipe_type);
+ pipe = mdss_mdp_pipe_alloc(pipe_type);
/* VIG pipes can also support RGB format */
if (!pipe && pipe_type == MDSS_MDP_PIPE_TYPE_RGB) {
pipe_type = MDSS_MDP_PIPE_TYPE_VIG;
- pipe = mdss_mdp_pipe_alloc_locked(pipe_type);
+ pipe = mdss_mdp_pipe_alloc(pipe_type);
}
if (pipe == NULL) {
@@ -293,16 +293,23 @@
return -ENOMEM;
}
+ ret = mdss_mdp_pipe_map(pipe);
+ if (ret) {
+ pr_err("unable to map pipe=%d\n", pipe->num);
+ return ret;
+ }
+
mutex_lock(&mfd->lock);
list_add(&pipe->used_list, &mfd->pipes_used);
mutex_unlock(&mfd->lock);
pipe->mixer = mixer;
pipe->mfd = mfd;
+ pipe->play_cnt = 0;
} else {
- pipe = mdss_mdp_pipe_get_locked(req->id);
- if (pipe == NULL) {
+ pipe = mdss_mdp_pipe_get(req->id);
+ if (IS_ERR_OR_NULL(pipe)) {
pr_err("invalid pipe ndx=%x\n", req->id);
- return -ENODEV;
+ return pipe ? PTR_ERR(pipe) : -ENODEV;
}
}
@@ -353,7 +360,7 @@
*ppipe = pipe;
- mdss_mdp_pipe_unlock(pipe);
+ mdss_mdp_pipe_unmap(pipe);
return ret;
}
@@ -498,8 +505,8 @@
pipe_ndx = BIT(i);
if (pipe_ndx & ndx) {
unset_ndx |= pipe_ndx;
- pipe = mdss_mdp_pipe_get_locked(pipe_ndx);
- if (!pipe) {
+ pipe = mdss_mdp_pipe_get(pipe_ndx);
+ if (IS_ERR_OR_NULL(pipe)) {
pr_warn("unknown pipe ndx=%x\n", pipe_ndx);
continue;
}
@@ -508,6 +515,7 @@
list_add(&pipe->cleanup_list, &mfd->pipes_cleanup);
mutex_unlock(&mfd->lock);
mdss_mdp_mixer_pipe_unstage(pipe);
+ mdss_mdp_pipe_unmap(pipe);
}
}
return 0;
@@ -638,10 +646,10 @@
int ret;
u32 flags;
- pipe = mdss_mdp_pipe_get_locked(req->id);
- if (pipe == NULL) {
+ pipe = mdss_mdp_pipe_get(req->id);
+ if (IS_ERR_OR_NULL(pipe)) {
pr_err("pipe ndx=%x doesn't exist\n", req->id);
- return -ENODEV;
+ return pipe ? PTR_ERR(pipe) : -ENODEV;
}
pr_debug("ov queue pnum=%d\n", pipe->num);
@@ -664,7 +672,7 @@
mdss_mdp_overlay_free_buf(src_data);
}
ctl = pipe->mixer->ctl;
- mdss_mdp_pipe_unlock(pipe);
+ mdss_mdp_pipe_unmap(pipe);
return ret;
}
@@ -844,9 +852,12 @@
return;
}
- mdss_mdp_pipe_lock(pipe);
+ if (mdss_mdp_pipe_map(pipe)) {
+ pr_err("unable to map base pipe\n");
+ return;
+ }
ret = mdss_mdp_pipe_queue_data(pipe, &data);
- mdss_mdp_pipe_unlock(pipe);
+ mdss_mdp_pipe_unmap(pipe);
if (ret) {
pr_err("unable to queue data\n");
return;
@@ -859,9 +870,12 @@
pr_err("unable to allocate right base pipe\n");
return;
}
- mdss_mdp_pipe_lock(pipe);
+ if (mdss_mdp_pipe_map(pipe)) {
+ pr_err("unable to map right base pipe\n");
+ return;
+ }
ret = mdss_mdp_pipe_queue_data(pipe, &data);
- mdss_mdp_pipe_unlock(pipe);
+ mdss_mdp_pipe_unmap(pipe);
if (ret) {
pr_err("unable to queue right data\n");
return;
@@ -901,10 +915,15 @@
if (!ctl->set_vsync_handler)
return -ENOTSUPP;
+ rc = mutex_lock_interruptible(&ctl->lock);
+ if (rc)
+ return rc;
+
if (!ctl->power_on) {
pr_debug("fb%d vsync pending first update en=%d\n",
mfd->index, en);
mfd->vsync_pending = en;
+ mutex_unlock(&ctl->lock);
return 0;
}
@@ -925,6 +944,8 @@
rc = ctl->set_vsync_handler(ctl, NULL);
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
+ mutex_unlock(&ctl->lock);
+
return rc;
}
@@ -935,13 +956,20 @@
struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)fbi->par;
unsigned long flags;
u64 vsync_ticks;
+ unsigned long timeout;
int ret;
if (!mfd->ctl || !mfd->ctl->power_on)
return 0;
+ timeout = msecs_to_jiffies(VSYNC_PERIOD * 5);
+ if (mfd->ctl->play_cnt == 0) {
+ pr_debug("timegen enable still pending on fb%d\n", mfd->index);
+ timeout <<= 5;
+ }
+
ret = wait_for_completion_interruptible_timeout(&mfd->vsync_comp,
- msecs_to_jiffies(VSYNC_PERIOD * 5));
+ timeout);
if (ret <= 0) {
pr_warn("vsync wait on fb%d interrupted (%d)\n",
mfd->index, ret);
diff --git a/drivers/video/msm/mdss/mdss_mdp_pipe.c b/drivers/video/msm/mdss/mdss_mdp_pipe.c
index ade7cb4..4ece15d 100644
--- a/drivers/video/msm/mdss/mdss_mdp_pipe.c
+++ b/drivers/video/msm/mdss/mdss_mdp_pipe.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. 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
@@ -27,6 +27,8 @@
static struct mdss_mdp_pipe mdss_mdp_pipe_list[MDSS_MDP_MAX_SSPP];
+static int mdss_mdp_pipe_free(struct mdss_mdp_pipe *pipe);
+
static u32 mdss_mdp_smp_mmb_reserve(unsigned long *smp, size_t n)
{
u32 i, mmb;
@@ -157,47 +159,43 @@
return 0;
}
-void mdss_mdp_pipe_unlock(struct mdss_mdp_pipe *pipe)
+void mdss_mdp_pipe_unmap(struct mdss_mdp_pipe *pipe)
{
- atomic_dec(&pipe->ref_cnt);
- mutex_unlock(&pipe->lock);
+ int tmp;
+
+ tmp = atomic_dec_return(&pipe->ref_cnt);
+
+ WARN(tmp < 0, "Invalid unmap with ref_cnt=%d", tmp);
+ if (tmp == 0)
+ mdss_mdp_pipe_free(pipe);
}
-int mdss_mdp_pipe_lock(struct mdss_mdp_pipe *pipe)
+int mdss_mdp_pipe_map(struct mdss_mdp_pipe *pipe)
{
- if (atomic_inc_not_zero(&pipe->ref_cnt)) {
- if (mutex_lock_interruptible(&pipe->lock)) {
- atomic_dec(&pipe->ref_cnt);
- return -EINTR;
- }
- return 0;
+ if (!atomic_inc_not_zero(&pipe->ref_cnt)) {
+ pr_err("attempting to map unallocated pipe (%d)", pipe->num);
+ return -EINVAL;
}
- return -EINVAL;
+ return 0;
}
static struct mdss_mdp_pipe *mdss_mdp_pipe_init(u32 pnum)
{
- struct mdss_mdp_pipe *pipe = NULL;
+ struct mdss_mdp_pipe *pipe;
- if (atomic_read(&mdss_mdp_pipe_list[pnum].ref_cnt) == 0) {
- pipe = &mdss_mdp_pipe_list[pnum];
- memset(pipe, 0, sizeof(*pipe));
+ pipe = &mdss_mdp_pipe_list[pnum];
- mutex_init(&pipe->lock);
- atomic_set(&pipe->ref_cnt, 1);
+ if (atomic_cmpxchg(&pipe->ref_cnt, 0, 1) == 0) {
+ pipe->num = pnum;
+ pipe->type = mdss_res->pipe_type_map[pnum];
+ pipe->ndx = BIT(pnum);
- if (mdss_mdp_pipe_lock(pipe) == 0) {
- pipe->num = pnum;
- pipe->type = mdss_res->pipe_type_map[pnum];
- pipe->ndx = BIT(pnum);
+ pr_debug("ndx=%x pnum=%d\n", pipe->ndx, pipe->num);
- pr_debug("ndx=%x pnum=%d\n", pipe->ndx, pipe->num);
- } else {
- atomic_set(&pipe->ref_cnt, 0);
- pipe = NULL;
- }
+ return pipe;
}
- return pipe;
+
+ return NULL;
}
struct mdss_mdp_pipe *mdss_mdp_pipe_alloc_pnum(u32 pnum)
@@ -210,7 +208,7 @@
return pipe;
}
-struct mdss_mdp_pipe *mdss_mdp_pipe_alloc_locked(u32 type)
+struct mdss_mdp_pipe *mdss_mdp_pipe_alloc(u32 type)
{
struct mdss_mdp_pipe *pipe = NULL;
int pnum;
@@ -228,57 +226,60 @@
return pipe;
}
-struct mdss_mdp_pipe *mdss_mdp_pipe_get_locked(u32 ndx)
+struct mdss_mdp_pipe *mdss_mdp_pipe_get(u32 ndx)
{
struct mdss_mdp_pipe *pipe = NULL;
int i;
if (!ndx)
- return NULL;
+ return ERR_PTR(-EINVAL);
mutex_lock(&mdss_mdp_sspp_lock);
for (i = 0; i < MDSS_MDP_MAX_SSPP; i++) {
pipe = &mdss_mdp_pipe_list[i];
- if (ndx == pipe->ndx)
+ if (ndx == pipe->ndx) {
+ if (mdss_mdp_pipe_map(pipe))
+ pipe = ERR_PTR(-EACCES);
break;
+ }
}
mutex_unlock(&mdss_mdp_sspp_lock);
if (i == MDSS_MDP_MAX_SSPP)
- return NULL;
-
- if (mdss_mdp_pipe_lock(pipe))
- return NULL;
-
- if (pipe->ndx != ndx) {
- mdss_mdp_pipe_unlock(pipe);
- pipe = NULL;
- }
+ return ERR_PTR(-ENODEV);
return pipe;
}
-
-static void mdss_mdp_pipe_free(struct mdss_mdp_pipe *pipe)
-{
- mdss_mdp_smp_free(pipe);
- pipe->ndx = 0;
- atomic_dec(&pipe->ref_cnt);
- mdss_mdp_pipe_unlock(pipe);
-}
-
-int mdss_mdp_pipe_destroy(struct mdss_mdp_pipe *pipe)
+static int mdss_mdp_pipe_free(struct mdss_mdp_pipe *pipe)
{
pr_debug("ndx=%x pnum=%d ref_cnt=%d\n", pipe->ndx, pipe->num,
atomic_read(&pipe->ref_cnt));
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
- mdss_mdp_pipe_free(pipe);
+ mdss_mdp_smp_free(pipe);
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
return 0;
}
+int mdss_mdp_pipe_destroy(struct mdss_mdp_pipe *pipe)
+{
+ int tmp;
+
+ tmp = atomic_dec_return(&pipe->ref_cnt);
+
+ if (tmp != 0) {
+ pr_err("unable to free pipe %d while still in use (%d)\n",
+ pipe->num, tmp);
+ return -EBUSY;
+ }
+ mdss_mdp_pipe_free(pipe);
+
+ return 0;
+
+}
+
static inline void mdss_mdp_pipe_write(struct mdss_mdp_pipe *pipe,
u32 reg, u32 val)
{
@@ -583,43 +584,6 @@
return 0;
}
-static int mdss_mdp_vig_setup(struct mdss_mdp_pipe *pipe)
-{
- u32 opmode = 0;
-
- pr_debug("pnum=%x\n", pipe->num);
-
- /* CSC Post Processing enabled? */
- if (pipe->flags & MDP_OVERLAY_PP_CFG_EN) {
- if (pipe->pp_cfg.config_ops & MDP_OVERLAY_PP_CSC_CFG) {
- if (pipe->pp_cfg.csc_cfg.flags & MDP_CSC_FLAG_ENABLE)
- opmode |= 1 << 17; /* CSC_1_EN */
- if (pipe->pp_cfg.csc_cfg.flags & MDP_CSC_FLAG_YUV_IN)
- opmode |= 1 << 18; /* SRC_DATA=YCBCR */
- if (pipe->pp_cfg.csc_cfg.flags & MDP_CSC_FLAG_YUV_OUT)
- opmode |= 1 << 19; /* DST_DATA=YCBCR */
- /* only need to program once */
- if (pipe->play_cnt == 0)
- mdss_mdp_csc_setup_data(MDSS_MDP_BLOCK_SSPP,
- pipe->num, 1, &pipe->pp_cfg.csc_cfg);
- }
- } else {
- if (pipe->src_fmt->is_yuv)
- opmode |= (0 << 19) | /* DST_DATA=RGB */
- (1 << 18) | /* SRC_DATA=YCBCR */
- (1 << 17); /* CSC_1_EN */
- /* only need to program once */
- if (pipe->play_cnt == 0) {
- mdss_mdp_csc_setup(MDSS_MDP_BLOCK_SSPP, pipe->num, 1,
- MDSS_MDP_CSC_YUV2RGB);
- }
- }
-
- mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_VIG_OP_MODE, opmode);
-
- return 0;
-}
-
static void mdss_mdp_addr_add_offset(struct mdss_mdp_pipe *pipe,
struct mdss_mdp_data *data)
{
@@ -677,7 +641,7 @@
struct mdss_mdp_data *src_data)
{
int ret = 0;
- u32 params_changed;
+ u32 params_changed, opmode;
if (!pipe) {
pr_err("pipe not setup properly for queue\n");
@@ -711,8 +675,10 @@
goto done;
}
+ mdss_mdp_pipe_pp_setup(pipe, &opmode);
if (pipe->type == MDSS_MDP_PIPE_TYPE_VIG)
- mdss_mdp_vig_setup(pipe);
+ mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_VIG_OP_MODE,
+ opmode);
ret = mdss_mdp_smp_reserve(pipe);
if (ret) {
diff --git a/drivers/video/msm/mdss/mdss_mdp_pp.c b/drivers/video/msm/mdss/mdss_mdp_pp.c
index 1482935..242be60 100644
--- a/drivers/video/msm/mdss/mdss_mdp_pp.c
+++ b/drivers/video/msm/mdss/mdss_mdp_pp.c
@@ -1,5 +1,5 @@
/*
- * 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
@@ -131,6 +131,7 @@
u32 enhist_sts;
u32 dither_sts;
u32 gamut_sts;
+ u32 pgc_sts;
};
#define PP_FLAGS_DIRTY_PA 0x1
@@ -141,6 +142,7 @@
#define PP_FLAGS_DIRTY_DITHER 0x20
#define PP_FLAGS_DIRTY_GAMUT 0x40
#define PP_FLAGS_DIRTY_HIST_COL 0x80
+#define PP_FLAGS_DIRTY_PGC 0x100
#define PP_STS_ENABLE 0x1
#define PP_STS_GAMUT_FIRST 0x2
@@ -157,9 +159,10 @@
struct mdp_ar_gc_lut_data
gc_lut_b[MDSS_BLOCK_DISP_NUM][GC_LUT_SEGMENTS];
u32 enhist_lut[MDSS_BLOCK_DISP_NUM][ENHIST_LUT_ENTRIES];
- struct mdp_pa_cfg_data pa_disp_cfg[MDSS_BLOCK_DISP_NUM];
+ struct mdp_pa_cfg pa_disp_cfg[MDSS_BLOCK_DISP_NUM];
struct mdp_pcc_cfg_data pcc_disp_cfg[MDSS_BLOCK_DISP_NUM];
struct mdp_igc_lut_data igc_disp_cfg[MDSS_BLOCK_DISP_NUM];
+ struct mdp_pgc_lut_data argc_disp_cfg[MDSS_BLOCK_DISP_NUM];
struct mdp_pgc_lut_data pgc_disp_cfg[MDSS_BLOCK_DISP_NUM];
struct mdp_hist_lut_data enhist_disp_cfg[MDSS_BLOCK_DISP_NUM];
struct mdp_dither_cfg_data dither_disp_cfg[MDSS_BLOCK_DISP_NUM];
@@ -178,19 +181,29 @@
static DEFINE_MUTEX(mdss_mdp_hist_mutex);
static struct mdss_pp_res_type *mdss_pp_res;
-
static void pp_hist_read(u32 v_base, struct pp_hist_col_info *hist_info);
-
static void pp_update_pcc_regs(u32 offset,
struct mdp_pcc_cfg_data *cfg_ptr);
static void pp_update_igc_lut(struct mdp_igc_lut_data *cfg,
u32 offset, u32 blk_idx);
static void pp_update_gc_one_lut(u32 offset,
- struct mdp_ar_gc_lut_data *lut_data);
+ struct mdp_ar_gc_lut_data *lut_data);
static void pp_update_argc_lut(u32 offset,
- struct mdp_pgc_lut_data *config);
+ struct mdp_pgc_lut_data *config);
static void pp_update_hist_lut(u32 offset, struct mdp_hist_lut_data *cfg);
-
+static void pp_pa_config(unsigned long flags, u32 base,
+ struct pp_sts_type *pp_sts,
+ struct mdp_pa_cfg *pa_config);
+static void pp_pcc_config(unsigned long flags, u32 base,
+ struct pp_sts_type *pp_sts,
+ struct mdp_pcc_cfg_data *pcc_config);
+static void pp_igc_config(unsigned long flags, u32 base,
+ struct pp_sts_type *pp_sts,
+ struct mdp_igc_lut_data *igc_config,
+ u32 pipe_num);
+static void pp_enhist_config(unsigned long flags, u32 base,
+ struct pp_sts_type *pp_sts,
+ struct mdp_hist_lut_data *enhist_cfg);
int mdss_mdp_csc_setup_data(u32 block, u32 blk_idx, u32 tbl_idx,
struct mdp_csc_cfg *data)
@@ -280,51 +293,9 @@
data = &mdp_csc_convert[csc_type];
return mdss_mdp_csc_setup_data(block, blk_idx, tbl_idx, data);
}
-static int pp_mixer_setup(u32 disp_num, struct mdss_mdp_ctl *ctl,
- struct mdss_mdp_mixer *mixer)
-{
- u32 flags, offset, dspp_num, opmode = 0;
- struct mdp_pgc_lut_data *pgc_config;
- struct pp_sts_type *pp_sts;
- dspp_num = mixer->num;
- /* no corresponding dspp */
- if ((mixer->type != MDSS_MDP_MIXER_TYPE_INTF) ||
- (dspp_num >= MDSS_MDP_MAX_DSPP))
- return 0;
- if (disp_num < MDSS_BLOCK_DISP_NUM)
- flags = mdss_pp_res->pp_disp_flags[disp_num];
- else
- flags = 0;
- pp_sts = &mdss_pp_res->pp_dspp_sts[dspp_num];
- /* GC_LUT is in layer mixer */
- if (flags & PP_FLAGS_DIRTY_ARGC) {
- pgc_config = &mdss_pp_res->pgc_disp_cfg[disp_num];
- if (pgc_config->flags & MDP_PP_OPS_WRITE) {
- offset = MDSS_MDP_REG_LM_OFFSET(disp_num) +
- MDSS_MDP_REG_LM_GC_LUT_BASE;
- pp_update_argc_lut(offset, pgc_config);
- }
- if (pgc_config->flags & MDP_PP_OPS_DISABLE)
- pp_sts->argc_sts &= ~PP_STS_ENABLE;
- else if (pgc_config->flags & MDP_PP_OPS_ENABLE)
- pp_sts->argc_sts |= PP_STS_ENABLE;
- ctl->flush_bits |= BIT(6) << dspp_num; /* LAYER_MIXER */
- }
- /* update LM opmode if LM needs flush */
- if ((pp_sts->argc_sts & PP_STS_ENABLE) &&
- (ctl->flush_bits & (BIT(6) << dspp_num))) {
- offset = MDSS_MDP_REG_LM_OFFSET(dspp_num) +
- MDSS_MDP_REG_LM_OP_MODE;
- opmode = MDSS_MDP_REG_READ(offset);
- opmode |= (1 << 0); /* GC_LUT_EN */
- MDSS_MDP_REG_WRITE(offset, opmode);
- }
- return 0;
-}
static void pp_gamut_config(struct mdp_gamut_cfg_data *gamut_cfg,
- u32 base,
- u32 *gamut_sts)
+ u32 base, u32 *gamut_sts)
{
u32 offset;
int i, j;
@@ -358,30 +329,212 @@
*gamut_sts |= PP_STS_ENABLE;
}
-/* Note: Assumes that its inputs have been checked by calling function */
-static void pp_update_hist_lut(u32 offset, struct mdp_hist_lut_data *cfg)
+static void pp_pa_config(unsigned long flags, u32 base,
+ struct pp_sts_type *pp_sts,
+ struct mdp_pa_cfg *pa_config)
{
- int i;
- for (i = 0; i < ENHIST_LUT_ENTRIES; i++)
- MDSS_MDP_REG_WRITE(offset, cfg->data[i]);
- /* swap */
- MDSS_MDP_REG_WRITE(offset + 4, 1);
+ if (flags & PP_FLAGS_DIRTY_PA) {
+ if (pa_config->flags & MDP_PP_OPS_WRITE) {
+ MDSS_MDP_REG_WRITE(base, pa_config->hue_adj);
+ base += 4;
+ MDSS_MDP_REG_WRITE(base, pa_config->sat_adj);
+ base += 4;
+ MDSS_MDP_REG_WRITE(base, pa_config->val_adj);
+ base += 4;
+ MDSS_MDP_REG_WRITE(base, pa_config->cont_adj);
+ }
+ if (pa_config->flags & MDP_PP_OPS_DISABLE)
+ pp_sts->pa_sts &= ~PP_STS_ENABLE;
+ else if (pa_config->flags & MDP_PP_OPS_ENABLE)
+ pp_sts->pa_sts |= PP_STS_ENABLE;
+ }
+}
+
+static void pp_pcc_config(unsigned long flags, u32 base,
+ struct pp_sts_type *pp_sts,
+ struct mdp_pcc_cfg_data *pcc_config)
+{
+ if (flags & PP_FLAGS_DIRTY_PCC) {
+ if (pcc_config->ops & MDP_PP_OPS_WRITE)
+ pp_update_pcc_regs(base, pcc_config);
+
+ if (pcc_config->ops & MDP_PP_OPS_DISABLE)
+ pp_sts->pcc_sts &= ~PP_STS_ENABLE;
+ else if (pcc_config->ops & MDP_PP_OPS_ENABLE)
+ pp_sts->pcc_sts |= PP_STS_ENABLE;
+ }
+}
+
+static void pp_igc_config(unsigned long flags, u32 base,
+ struct pp_sts_type *pp_sts,
+ struct mdp_igc_lut_data *igc_config,
+ u32 pipe_num)
+{
+ u32 tbl_idx;
+ if (flags & PP_FLAGS_DIRTY_IGC) {
+ if (igc_config->ops & MDP_PP_OPS_WRITE)
+ pp_update_igc_lut(igc_config, base, pipe_num);
+
+ if (igc_config->ops & MDP_PP_IGC_FLAG_ROM0) {
+ pp_sts->pcc_sts |= PP_STS_ENABLE;
+ tbl_idx = 1;
+ } else if (igc_config->ops & MDP_PP_IGC_FLAG_ROM1) {
+ pp_sts->pcc_sts |= PP_STS_ENABLE;
+ tbl_idx = 2;
+ } else {
+ tbl_idx = 0;
+ }
+ pp_sts->igc_tbl_idx = tbl_idx;
+ if (igc_config->ops & MDP_PP_OPS_DISABLE)
+ pp_sts->igc_sts &= ~PP_STS_ENABLE;
+ else if (igc_config->ops & MDP_PP_OPS_ENABLE)
+ pp_sts->igc_sts |= PP_STS_ENABLE;
+ }
+}
+
+static void pp_enhist_config(unsigned long flags, u32 base,
+ struct pp_sts_type *pp_sts,
+ struct mdp_hist_lut_data *enhist_cfg)
+{
+ if (flags & PP_FLAGS_DIRTY_ENHIST) {
+ if (enhist_cfg->ops & MDP_PP_OPS_WRITE)
+ pp_update_hist_lut(base, enhist_cfg);
+
+ if (enhist_cfg->ops & MDP_PP_OPS_DISABLE)
+ pp_sts->enhist_sts &= ~PP_STS_ENABLE;
+ else if (enhist_cfg->ops & MDP_PP_OPS_ENABLE)
+ pp_sts->enhist_sts |= PP_STS_ENABLE;
+ }
+}
+
+static int pp_vig_pipe_setup(struct mdss_mdp_pipe *pipe, u32 *op)
+{
+ struct pp_sts_type pp_sts;
+ u32 opmode = 0, base = 0;
+ unsigned long flags = 0;
+
+ pr_debug("pnum=%x\n", pipe->num);
+
+ if ((pipe->flags & MDP_OVERLAY_PP_CFG_EN) &&
+ (pipe->pp_cfg.config_ops & MDP_OVERLAY_PP_CSC_CFG)) {
+ opmode |= !!(pipe->pp_cfg.csc_cfg.flags &
+ MDP_CSC_FLAG_ENABLE) << 17;
+ opmode |= !!(pipe->pp_cfg.csc_cfg.flags &
+ MDP_CSC_FLAG_YUV_IN) << 18;
+ opmode |= !!(pipe->pp_cfg.csc_cfg.flags &
+ MDP_CSC_FLAG_YUV_OUT) << 19;
+ /*
+ * TODO: Allow pipe to be programmed whenever new CSC is
+ * applied (i.e. dirty bit)
+ */
+ if (pipe->play_cnt == 0)
+ mdss_mdp_csc_setup_data(MDSS_MDP_BLOCK_SSPP,
+ pipe->num, 1, &pipe->pp_cfg.csc_cfg);
+ } else {
+ if (pipe->src_fmt->is_yuv)
+ opmode |= (0 << 19) | /* DST_DATA=RGB */
+ (1 << 18) | /* SRC_DATA=YCBCR */
+ (1 << 17); /* CSC_1_EN */
+ /*
+ * TODO: Needs to be part of dirty bit logic: if there is a
+ * previously configured pipe need to re-configure CSC matrix
+ */
+ if (pipe->play_cnt == 0) {
+ mdss_mdp_csc_setup(MDSS_MDP_BLOCK_SSPP, pipe->num, 1,
+ MDSS_MDP_CSC_YUV2RGB);
+ }
+ }
+
+ if (pipe->flags & MDP_OVERLAY_PP_CFG_EN) {
+ if (pipe->pp_cfg.config_ops & MDP_OVERLAY_PP_PA_CFG) {
+ flags = PP_FLAGS_DIRTY_PA;
+ base = MDSS_MDP_REG_SSPP_OFFSET(pipe->num) +
+ MDSS_MDP_REG_VIG_PA_BASE;
+ pp_sts.pa_sts = 0;
+ pp_pa_config(flags, base, &pp_sts,
+ &pipe->pp_cfg.pa_cfg);
+ if (pp_sts.pa_sts & PP_STS_ENABLE)
+ opmode |= (1 << 4); /* PA_EN */
+ }
+ }
+
+ *op = opmode;
+
+ return 0;
+}
+
+int mdss_mdp_pipe_pp_setup(struct mdss_mdp_pipe *pipe, u32 *op)
+{
+ int ret = 0;
+ if (!pipe)
+ return -ENODEV;
+
+ if (pipe->type == MDSS_MDP_PIPE_TYPE_VIG)
+ ret = pp_vig_pipe_setup(pipe, op);
+ else if (pipe->type == MDSS_MDP_PIPE_TYPE_RGB)
+ ret = -EINVAL;
+ else if (pipe->type == MDSS_MDP_PIPE_TYPE_DMA)
+ ret = -EINVAL;
+
+ return ret;
+}
+
+static int pp_mixer_setup(u32 disp_num, struct mdss_mdp_ctl *ctl,
+ struct mdss_mdp_mixer *mixer)
+{
+ u32 flags, offset, dspp_num, opmode = 0;
+ struct mdp_pgc_lut_data *pgc_config;
+ struct pp_sts_type *pp_sts;
+ dspp_num = mixer->num;
+
+ /* no corresponding dspp */
+ if ((mixer->type != MDSS_MDP_MIXER_TYPE_INTF) ||
+ (dspp_num >= MDSS_MDP_MAX_DSPP))
+ return 0;
+ if (disp_num < MDSS_BLOCK_DISP_NUM)
+ flags = mdss_pp_res->pp_disp_flags[disp_num];
+ else
+ flags = 0;
+
+ pp_sts = &mdss_pp_res->pp_dspp_sts[dspp_num];
+ /* GC_LUT is in layer mixer */
+ if (flags & PP_FLAGS_DIRTY_ARGC) {
+ pgc_config = &mdss_pp_res->argc_disp_cfg[disp_num];
+ if (pgc_config->flags & MDP_PP_OPS_WRITE) {
+ offset = MDSS_MDP_REG_LM_OFFSET(disp_num) +
+ MDSS_MDP_REG_LM_GC_LUT_BASE;
+ pp_update_argc_lut(offset, pgc_config);
+ }
+ if (pgc_config->flags & MDP_PP_OPS_DISABLE)
+ pp_sts->argc_sts &= ~PP_STS_ENABLE;
+ else if (pgc_config->flags & MDP_PP_OPS_ENABLE)
+ pp_sts->argc_sts |= PP_STS_ENABLE;
+ ctl->flush_bits |= BIT(6) << dspp_num; /* LAYER_MIXER */
+ }
+ /* update LM opmode if LM needs flush */
+ if ((pp_sts->argc_sts & PP_STS_ENABLE) &&
+ (ctl->flush_bits & (BIT(6) << dspp_num))) {
+ offset = MDSS_MDP_REG_LM_OFFSET(dspp_num) +
+ MDSS_MDP_REG_LM_OP_MODE;
+ opmode = MDSS_MDP_REG_READ(offset);
+ opmode |= (1 << 0); /* GC_LUT_EN */
+ MDSS_MDP_REG_WRITE(offset, opmode);
+ }
+ return 0;
}
static int pp_dspp_setup(u32 disp_num, struct mdss_mdp_ctl *ctl,
- struct mdss_mdp_mixer *mixer)
+ struct mdss_mdp_mixer *mixer)
{
u32 flags, base, offset, dspp_num, opmode = 0;
- struct mdp_pa_cfg_data *pa_config;
- struct mdp_pcc_cfg_data *pcc_config;
- struct mdp_igc_lut_data *igc_config;
- struct mdp_hist_lut_data *enhist_cfg;
struct mdp_dither_cfg_data *dither_cfg;
struct pp_hist_col_info *hist_info;
+ struct mdp_pgc_lut_data *pgc_config;
struct pp_sts_type *pp_sts;
- u32 data, tbl_idx, col_state;
+ u32 data, col_state;
unsigned long flag;
int i;
+
dspp_num = mixer->num;
/* no corresponding dspp */
if ((mixer->type != MDSS_MDP_MIXER_TYPE_INTF) ||
@@ -417,76 +570,32 @@
/* nothing to update */
if ((!flags) && (!(hist_info->col_en)))
return 0;
+
pp_sts = &mdss_pp_res->pp_dspp_sts[dspp_num];
- if (flags & PP_FLAGS_DIRTY_PA) {
- pa_config = &mdss_pp_res->pa_disp_cfg[disp_num];
- if (pa_config->pa_data.flags & MDP_PP_OPS_WRITE) {
- offset = base + MDSS_MDP_REG_DSPP_PA_BASE;
- MDSS_MDP_REG_WRITE(offset, pa_config->pa_data.hue_adj);
- offset += 4;
- MDSS_MDP_REG_WRITE(offset, pa_config->pa_data.sat_adj);
- offset += 4;
- MDSS_MDP_REG_WRITE(offset, pa_config->pa_data.val_adj);
- offset += 4;
- MDSS_MDP_REG_WRITE(offset, pa_config->pa_data.cont_adj);
- }
- if (pa_config->pa_data.flags & MDP_PP_OPS_DISABLE)
- pp_sts->pa_sts &= ~PP_STS_ENABLE;
- else if (pa_config->pa_data.flags & MDP_PP_OPS_ENABLE)
- pp_sts->pa_sts |= PP_STS_ENABLE;
- }
+
+ pp_pa_config(flags, base + MDSS_MDP_REG_DSPP_PA_BASE, pp_sts,
+ &mdss_pp_res->pa_disp_cfg[disp_num]);
+
+ pp_pcc_config(flags, base + MDSS_MDP_REG_DSPP_PCC_BASE, pp_sts,
+ &mdss_pp_res->pcc_disp_cfg[disp_num]);
+
+ pp_igc_config(flags, MDSS_MDP_REG_IGC_DSPP_BASE, pp_sts,
+ &mdss_pp_res->igc_disp_cfg[disp_num], dspp_num);
+
+ pp_enhist_config(flags, base + MDSS_MDP_REG_DSPP_HIST_LUT_BASE,
+ pp_sts, &mdss_pp_res->enhist_disp_cfg[disp_num]);
+
if (pp_sts->pa_sts & PP_STS_ENABLE)
opmode |= (1 << 20); /* PA_EN */
- if (flags & PP_FLAGS_DIRTY_PCC) {
- pcc_config = &mdss_pp_res->pcc_disp_cfg[disp_num];
- if (pcc_config->ops & MDP_PP_OPS_WRITE) {
- offset = base + MDSS_MDP_REG_DSPP_PCC_BASE;
- pp_update_pcc_regs(offset, pcc_config);
- }
- if (pcc_config->ops & MDP_PP_OPS_DISABLE)
- pp_sts->pcc_sts &= ~PP_STS_ENABLE;
- else if (pcc_config->ops & MDP_PP_OPS_ENABLE)
- pp_sts->pcc_sts |= PP_STS_ENABLE;
- }
+
if (pp_sts->pcc_sts & PP_STS_ENABLE)
opmode |= (1 << 4); /* PCC_EN */
- if (flags & PP_FLAGS_DIRTY_IGC) {
- igc_config = &mdss_pp_res->igc_disp_cfg[disp_num];
- if (igc_config->ops & MDP_PP_OPS_WRITE) {
- offset = MDSS_MDP_REG_IGC_DSPP_BASE;
- pp_update_igc_lut(igc_config, offset, dspp_num);
- }
- if (igc_config->ops & MDP_PP_IGC_FLAG_ROM0) {
- pp_sts->pcc_sts |= PP_STS_ENABLE;
- tbl_idx = 1;
- } else if (igc_config->ops & MDP_PP_IGC_FLAG_ROM1) {
- pp_sts->pcc_sts |= PP_STS_ENABLE;
- tbl_idx = 2;
- } else {
- tbl_idx = 0;
- }
- pp_sts->igc_tbl_idx = tbl_idx;
- if (igc_config->ops & MDP_PP_OPS_DISABLE)
- pp_sts->igc_sts &= ~PP_STS_ENABLE;
- else if (igc_config->ops & MDP_PP_OPS_ENABLE)
- pp_sts->igc_sts |= PP_STS_ENABLE;
- }
if (pp_sts->igc_sts & PP_STS_ENABLE) {
opmode |= (1 << 0) | /* IGC_LUT_EN */
(pp_sts->igc_tbl_idx << 1);
}
- if (flags & PP_FLAGS_DIRTY_ENHIST) {
- enhist_cfg = &mdss_pp_res->enhist_disp_cfg[disp_num];
- if (enhist_cfg->ops & MDP_PP_OPS_WRITE) {
- offset = base + MDSS_MDP_REG_DSPP_HIST_LUT_BASE;
- pp_update_hist_lut(offset, enhist_cfg);
- }
- if (enhist_cfg->ops & MDP_PP_OPS_DISABLE)
- pp_sts->enhist_sts &= ~PP_STS_ENABLE;
- else if (enhist_cfg->ops & MDP_PP_OPS_ENABLE)
- pp_sts->enhist_sts |= PP_STS_ENABLE;
- }
+
if (pp_sts->enhist_sts & PP_STS_ENABLE) {
opmode |= (1 << 19) | /* HIST_LUT_EN */
(1 << 20); /* PA_EN */
@@ -533,10 +642,25 @@
opmode |= (1 << 24); /* GAMUT_ORDER */
}
+ if (flags & PP_FLAGS_DIRTY_PGC) {
+ pgc_config = &mdss_pp_res->pgc_disp_cfg[disp_num];
+ if (pgc_config->flags & MDP_PP_OPS_WRITE) {
+ offset = base + MDSS_MDP_REG_DSPP_GC_BASE;
+ pp_update_argc_lut(offset, pgc_config);
+ }
+ if (pgc_config->flags & MDP_PP_OPS_DISABLE)
+ pp_sts->pgc_sts &= ~PP_STS_ENABLE;
+ else if (pgc_config->flags & MDP_PP_OPS_ENABLE)
+ pp_sts->pgc_sts |= PP_STS_ENABLE;
+ }
+ if (pp_sts->pgc_sts & PP_STS_ENABLE)
+ opmode |= (1 << 22);
+
MDSS_MDP_REG_WRITE(base + MDSS_MDP_REG_DSPP_OP_MODE, opmode);
ctl->flush_bits |= BIT(13 + dspp_num); /* DSPP */
return 0;
}
+
int mdss_mdp_pp_setup(struct mdss_mdp_ctl *ctl)
{
u32 disp_num;
@@ -656,7 +780,7 @@
*copyback = 1;
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
} else {
- mdss_pp_res->pa_disp_cfg[disp_num] = *config;
+ mdss_pp_res->pa_disp_cfg[disp_num] = config->pa_data;
mdss_pp_res->pp_disp_flags[disp_num] |= PP_FLAGS_DIRTY_PA;
}
@@ -1036,21 +1160,54 @@
pp_read_gc_one_lut(offset, config->b_data);
return ret;
}
+
+/* Note: Assumes that its inputs have been checked by calling function */
+static void pp_update_hist_lut(u32 offset, struct mdp_hist_lut_data *cfg)
+{
+ int i;
+ for (i = 0; i < ENHIST_LUT_ENTRIES; i++)
+ MDSS_MDP_REG_WRITE(offset, cfg->data[i]);
+ /* swap */
+ MDSS_MDP_REG_WRITE(offset + 4, 1);
+}
+
int mdss_mdp_argc_config(struct mdp_pgc_lut_data *config, u32 *copyback)
{
int ret = 0;
- u32 argc_offset, disp_num, dspp_num = 0;
+ u32 argc_offset = 0, disp_num, dspp_num = 0;
struct mdp_pgc_lut_data local_cfg;
+ struct mdp_pgc_lut_data *pgc_ptr;
u32 tbl_size;
- if ((config->block < MDP_LOGICAL_BLOCK_DISP_0) ||
- (config->block >= MDP_BLOCK_MAX))
+ if ((PP_BLOCK(config->block) < MDP_LOGICAL_BLOCK_DISP_0) ||
+ (PP_BLOCK(config->block) >= MDP_BLOCK_MAX))
return -EINVAL;
mutex_lock(&mdss_pp_mutex);
- disp_num = config->block - MDP_LOGICAL_BLOCK_DISP_0;
+
+ disp_num = PP_BLOCK(config->block) - MDP_LOGICAL_BLOCK_DISP_0;
+ switch (config->block & MDSS_PP_LOCATION_MASK) {
+ case MDSS_PP_LM_CFG:
+ argc_offset = MDSS_MDP_REG_LM_OFFSET(dspp_num) +
+ MDSS_MDP_REG_LM_GC_LUT_BASE;
+ pgc_ptr = &mdss_pp_res->argc_disp_cfg[disp_num];
+ mdss_pp_res->pp_disp_flags[disp_num] |=
+ PP_FLAGS_DIRTY_ARGC;
+ break;
+ case MDSS_PP_DSPP_CFG:
+ argc_offset = MDSS_MDP_REG_DSPP_OFFSET(dspp_num) +
+ MDSS_MDP_REG_DSPP_GC_BASE;
+ pgc_ptr = &mdss_pp_res->pgc_disp_cfg[disp_num];
+ mdss_pp_res->pp_disp_flags[disp_num] |=
+ PP_FLAGS_DIRTY_PGC;
+ break;
+ default:
+ goto argc_config_exit;
+ break;
+ }
tbl_size = GC_LUT_SEGMENTS * sizeof(struct mdp_ar_gc_lut_data);
+
if (config->flags & MDP_PP_OPS_READ) {
ret = pp_get_dspp_num(disp_num, &dspp_num);
if (ret) {
@@ -1059,10 +1216,6 @@
goto argc_config_exit;
}
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
-
- argc_offset = MDSS_MDP_REG_LM_OFFSET(dspp_num) +
- MDSS_MDP_REG_LM_GC_LUT_BASE;
- mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
local_cfg = *config;
local_cfg.r_data =
&mdss_pp_res->gc_lut_r[disp_num][0];
@@ -1104,14 +1257,14 @@
ret = -EFAULT;
goto argc_config_exit;
}
- mdss_pp_res->pgc_disp_cfg[disp_num] = *config;
- mdss_pp_res->pgc_disp_cfg[disp_num].r_data =
+
+ *pgc_ptr = *config;
+ pgc_ptr->r_data =
&mdss_pp_res->gc_lut_r[disp_num][0];
- mdss_pp_res->pgc_disp_cfg[disp_num].g_data =
+ pgc_ptr->g_data =
&mdss_pp_res->gc_lut_g[disp_num][0];
- mdss_pp_res->pgc_disp_cfg[disp_num].b_data =
+ pgc_ptr->b_data =
&mdss_pp_res->gc_lut_b[disp_num][0];
- mdss_pp_res->pp_disp_flags[disp_num] |= PP_FLAGS_DIRTY_ARGC;
}
argc_config_exit:
mutex_unlock(&mdss_pp_mutex);
diff --git a/drivers/video/msm/mdss/mdss_mdp_rotator.c b/drivers/video/msm/mdss/mdss_mdp_rotator.c
index 1e58269..a151b38 100644
--- a/drivers/video/msm/mdss/mdss_mdp_rotator.c
+++ b/drivers/video/msm/mdss/mdss_mdp_rotator.c
@@ -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
@@ -89,7 +89,7 @@
pipe = mdss_mdp_pipe_alloc_pnum(pnum);
- if (pipe)
+ if (!IS_ERR_OR_NULL(pipe))
pipe->mixer = mixer;
done:
if (!pipe)
diff --git a/drivers/video/msm/mdss/mdss_mdp_util.c b/drivers/video/msm/mdss/mdss_mdp_util.c
index 9f2df85..911e29f 100644
--- a/drivers/video/msm/mdss/mdss_mdp_util.c
+++ b/drivers/video/msm/mdss/mdss_mdp_util.c
@@ -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
@@ -29,6 +29,8 @@
#include "mdss_mdp.h"
#include "mdss_mdp_formats.h"
+#define DEFAULT_FRAME_RATE 60
+
enum {
MDP_INTR_VSYNC_INTF_0,
MDP_INTR_VSYNC_INTF_1,
@@ -76,7 +78,7 @@
void (*fnc_ptr)(void *), void *arg)
{
unsigned long flags;
- int index, ret;
+ int index;
index = mdss_mdp_intr2index(intr_type, intf_num);
if (index < 0) {
@@ -86,16 +88,13 @@
}
spin_lock_irqsave(&mdss_mdp_intr_lock, flags);
- if (!mdp_intr_cb[index].func) {
- mdp_intr_cb[index].func = fnc_ptr;
- mdp_intr_cb[index].arg = arg;
- ret = 0;
- } else {
- ret = -EBUSY;
- }
+ WARN(mdp_intr_cb[index].func && fnc_ptr,
+ "replacing current intr callback for ndx=%d\n", index);
+ mdp_intr_cb[index].func = fnc_ptr;
+ mdp_intr_cb[index].arg = arg;
spin_unlock_irqrestore(&mdss_mdp_intr_lock, flags);
- return ret;
+ return 0;
}
static inline void mdss_mdp_intr_done(int index)
@@ -433,3 +432,26 @@
return ret;
}
+
+u32 mdss_get_panel_framerate(struct msm_fb_data_type *mfd)
+{
+ u32 frame_rate = DEFAULT_FRAME_RATE;
+ u32 pixel_total;
+ struct mdss_panel_info *panel_info = mfd->panel_info;
+
+ if (panel_info->type == MIPI_VIDEO_PANEL) {
+ frame_rate = panel_info->mipi.frame_rate;
+ } else {
+ pixel_total = (panel_info->lcdc.h_back_porch +
+ panel_info->lcdc.h_front_porch +
+ panel_info->lcdc.h_pulse_width +
+ panel_info->xres) *
+ (panel_info->lcdc.v_back_porch +
+ panel_info->lcdc.v_front_porch +
+ panel_info->lcdc.v_pulse_width +
+ panel_info->yres);
+ if (pixel_total)
+ frame_rate = panel_info->clk_rate / pixel_total;
+ }
+ return frame_rate;
+}
diff --git a/drivers/video/msm/mdss/mhl_msc.c b/drivers/video/msm/mdss/mhl_msc.c
new file mode 100644
index 0000000..94f6d2b
--- /dev/null
+++ b/drivers/video/msm/mdss/mhl_msc.c
@@ -0,0 +1,489 @@
+/* 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/types.h>
+#include <linux/mhl_8334.h>
+#include <linux/vmalloc.h>
+#include <linux/input.h>
+#include "mhl_msc.h"
+
+static struct mhl_tx_ctrl *mhl_ctrl;
+static DEFINE_MUTEX(msc_send_workqueue_mutex);
+
+const char *devcap_reg_name[] = {
+ "DEV_STATE ",
+ "MHL_VERSION ",
+ "DEV_CAT ",
+ "ADOPTER_ID_H ",
+ "ADOPTER_ID_L ",
+ "VID_LINK_MODE ",
+ "AUD_LINK_MODE ",
+ "VIDEO_TYPE ",
+ "LOG_DEV_MAP ",
+ "BANDWIDTH ",
+ "FEATURE_FLAG ",
+ "DEVICE_ID_H ",
+ "DEVICE_ID_L ",
+ "SCRATCHPAD_SIZE ",
+ "INT_STAT_SIZE ",
+ "Reserved ",
+};
+
+static void mhl_print_devcap(u8 offset, u8 devcap)
+{
+ switch (offset) {
+ case DEVCAP_OFFSET_DEV_CAT:
+ pr_debug("DCAP: %02X %s: %02X DEV_TYPE=%X POW=%s\n",
+ offset, devcap_reg_name[offset], devcap,
+ devcap & 0x0F, (devcap & 0x10) ? "y" : "n");
+ break;
+ case DEVCAP_OFFSET_FEATURE_FLAG:
+ pr_debug("DCAP: %02X %s: %02X RCP=%s RAP=%s SP=%s\n",
+ offset, devcap_reg_name[offset], devcap,
+ (devcap & 0x01) ? "y" : "n",
+ (devcap & 0x02) ? "y" : "n",
+ (devcap & 0x04) ? "y" : "n");
+ break;
+ default:
+ pr_debug("DCAP: %02X %s: %02X\n",
+ offset, devcap_reg_name[offset], devcap);
+ break;
+ }
+}
+
+void mhl_register_msc(struct mhl_tx_ctrl *ctrl)
+{
+ if (ctrl)
+ mhl_ctrl = ctrl;
+}
+
+void mhl_msc_send_work(struct work_struct *work)
+{
+ struct mhl_tx_ctrl *mhl_ctrl =
+ container_of(work, struct mhl_tx_ctrl, mhl_msc_send_work);
+ struct msc_cmd_envelope *cmd_env;
+ int ret;
+ /*
+ * Remove item from the queue
+ * and schedule it
+ */
+ mutex_lock(&msc_send_workqueue_mutex);
+ while (!list_empty(&mhl_ctrl->list_cmd)) {
+ cmd_env = list_first_entry(&mhl_ctrl->list_cmd,
+ struct msc_cmd_envelope,
+ msc_queue_envelope);
+ list_del(&cmd_env->msc_queue_envelope);
+ mutex_unlock(&msc_send_workqueue_mutex);
+
+ ret = mhl_send_msc_command(mhl_ctrl, &cmd_env->msc_cmd_msg);
+ if (ret == -EAGAIN) {
+ int retry = 2;
+ while (retry--) {
+ ret = mhl_send_msc_command(
+ mhl_ctrl,
+ &cmd_env->msc_cmd_msg);
+ if (ret != -EAGAIN)
+ break;
+ }
+ }
+ if (ret == -EAGAIN)
+ pr_err("%s: send_msc_command retry out!\n", __func__);
+
+ vfree(cmd_env);
+ mutex_lock(&msc_send_workqueue_mutex);
+ }
+ mutex_unlock(&msc_send_workqueue_mutex);
+}
+
+int mhl_queue_msc_command(struct mhl_tx_ctrl *mhl_ctrl,
+ struct msc_command_struct *req,
+ int priority_send)
+{
+ struct msc_cmd_envelope *cmd_env;
+
+ mutex_lock(&msc_send_workqueue_mutex);
+ cmd_env = vmalloc(sizeof(struct msc_cmd_envelope));
+ if (!cmd_env) {
+ pr_err("%s: out of memory!\n", __func__);
+ return -ENOMEM;
+ }
+
+ memcpy(&cmd_env->msc_cmd_msg, req,
+ sizeof(struct msc_command_struct));
+
+ if (priority_send)
+ list_add(&cmd_env->msc_queue_envelope,
+ &mhl_ctrl->list_cmd);
+ else
+ list_add_tail(&cmd_env->msc_queue_envelope,
+ &mhl_ctrl->list_cmd);
+ mutex_unlock(&msc_send_workqueue_mutex);
+ queue_work(mhl_ctrl->msc_send_workqueue, &mhl_ctrl->mhl_msc_send_work);
+
+ return 0;
+}
+
+static int mhl_update_devcap(struct mhl_tx_ctrl *mhl_ctrl,
+ int offset, u8 devcap)
+{
+ if (!mhl_ctrl)
+ return -EFAULT;
+ if (offset < 0 || offset > 15)
+ return -EFAULT;
+ mhl_ctrl->devcap[offset] = devcap;
+ mhl_print_devcap(offset, mhl_ctrl->devcap[offset]);
+
+ return 0;
+}
+
+
+int mhl_msc_command_done(struct mhl_tx_ctrl *mhl_ctrl,
+ struct msc_command_struct *req)
+{
+ switch (req->command) {
+ case MHL_WRITE_STAT:
+ if (req->offset == MHL_STATUS_REG_LINK_MODE) {
+ if (req->payload.data[0]
+ & MHL_STATUS_PATH_ENABLED)
+ /* Enable TMDS output */
+ mhl_tmds_ctrl(mhl_ctrl, TMDS_ENABLE);
+ else
+ /* Disable TMDS output */
+ mhl_tmds_ctrl(mhl_ctrl, TMDS_DISABLE);
+ }
+ break;
+ case MHL_READ_DEVCAP:
+ mhl_update_devcap(mhl_ctrl,
+ req->offset, req->retval);
+ mhl_ctrl->devcap_state |= BIT(req->offset);
+ switch (req->offset) {
+ case MHL_DEV_CATEGORY_OFFSET:
+ if (req->retval & MHL_DEV_CATEGORY_POW_BIT)
+ pr_debug("%s: devcap pow bit set\n",
+ __func__);
+ else
+ pr_debug("%s: devcap pow bit unset\n",
+ __func__);
+ break;
+ case DEVCAP_OFFSET_MHL_VERSION:
+ case DEVCAP_OFFSET_INT_STAT_SIZE:
+ break;
+ }
+
+ break;
+ }
+ return 0;
+}
+
+int mhl_msc_send_set_int(struct mhl_tx_ctrl *mhl_ctrl,
+ u8 offset, u8 mask)
+{
+ struct msc_command_struct req;
+ req.command = MHL_SET_INT;
+ req.offset = offset;
+ req.payload.data[0] = mask;
+ return mhl_queue_msc_command(mhl_ctrl, &req, MSC_NORMAL_SEND);
+}
+
+int mhl_msc_send_write_stat(struct mhl_tx_ctrl *mhl_ctrl,
+ u8 offset, u8 value)
+{
+ struct msc_command_struct req;
+ req.command = MHL_WRITE_STAT;
+ req.offset = offset;
+ req.payload.data[0] = value;
+ return mhl_queue_msc_command(mhl_ctrl, &req, MSC_NORMAL_SEND);
+}
+
+int mhl_msc_send_msc_msg(struct mhl_tx_ctrl *mhl_ctrl,
+ u8 sub_cmd, u8 cmd_data)
+{
+ struct msc_command_struct req;
+ req.command = MHL_MSC_MSG;
+ req.payload.data[0] = sub_cmd;
+ req.payload.data[1] = cmd_data;
+ return mhl_queue_msc_command(mhl_ctrl, &req, MSC_NORMAL_SEND);
+}
+
+/*
+ * Certain MSC msgs such as RCPK, RCPE and RAPK
+ * should be transmitted as a high priority
+ * because these msgs should be sent within
+ * 1000ms of a receipt of RCP/RAP. So such msgs can
+ * be added to the head of msc cmd queue.
+ */
+static int mhl_msc_send_prior_msc_msg(struct mhl_tx_ctrl *mhl_ctrl,
+ u8 sub_cmd, u8 cmd_data)
+{
+ struct msc_command_struct req;
+ req.command = MHL_MSC_MSG;
+ req.payload.data[0] = sub_cmd;
+ req.payload.data[1] = cmd_data;
+ return mhl_queue_msc_command(mhl_ctrl, &req, MSC_PRIORITY_SEND);
+}
+
+
+int mhl_msc_read_devcap(struct mhl_tx_ctrl *mhl_ctrl, u8 offset)
+{
+ struct msc_command_struct req;
+ if (offset < 0 || offset > 15)
+ return -EFAULT;
+ req.command = MHL_READ_DEVCAP;
+ req.offset = offset;
+ req.payload.data[0] = 0;
+ return mhl_queue_msc_command(mhl_ctrl, &req, MSC_NORMAL_SEND);
+}
+
+int mhl_msc_read_devcap_all(struct mhl_tx_ctrl *mhl_ctrl)
+{
+ int offset;
+ int ret;
+
+ for (offset = 0; offset < DEVCAP_SIZE; offset++) {
+ ret = mhl_msc_read_devcap(mhl_ctrl, offset);
+ if (ret == -EBUSY)
+ pr_err("%s: queue busy!\n", __func__);
+ }
+ return ret;
+}
+
+
+static void mhl_handle_input(struct mhl_tx_ctrl *mhl_ctrl,
+ u8 key_code, u16 input_key_code)
+{
+ int key_press = (key_code & 0x80) == 0;
+
+ pr_debug("%s: send key events[%x][%d]\n",
+ __func__, key_code, key_press);
+ input_report_key(mhl_ctrl->input, input_key_code, key_press);
+ input_sync(mhl_ctrl->input);
+}
+
+
+
+int mhl_rcp_recv(struct mhl_tx_ctrl *mhl_ctrl, u8 key_code)
+{
+ u8 index = key_code & 0x7f;
+ u16 input_key_code;
+
+ if (!mhl_ctrl->rcp_key_code_tbl) {
+ pr_err("%s: RCP Key Code Table not initialized\n", __func__);
+ return -EINVAL;
+ }
+
+ input_key_code = mhl_ctrl->rcp_key_code_tbl[index];
+
+ if ((index < mhl_ctrl->rcp_key_code_tbl_len) &&
+ (input_key_code > 0)) {
+ /* prior send rcpk */
+ mhl_msc_send_prior_msc_msg(
+ mhl_ctrl,
+ MHL_MSC_MSG_RCPK,
+ key_code);
+
+ if (mhl_ctrl->input)
+ mhl_handle_input(mhl_ctrl, key_code, input_key_code);
+ } else {
+ /* prior send rcpe */
+ mhl_msc_send_prior_msc_msg(
+ mhl_ctrl,
+ MHL_MSC_MSG_RCPE,
+ MHL_RCPE_INEFFECTIVE_KEY_CODE);
+
+ /* send rcpk after rcpe send */
+ mhl_msc_send_prior_msc_msg(
+ mhl_ctrl,
+ MHL_MSC_MSG_RCPK,
+ key_code);
+ }
+ return 0;
+}
+
+
+static int mhl_rap_action(struct mhl_tx_ctrl *mhl_ctrl, u8 action_code)
+{
+ switch (action_code) {
+ case MHL_RAP_CONTENT_ON:
+ mhl_tmds_ctrl(mhl_ctrl, TMDS_ENABLE);
+ break;
+ case MHL_RAP_CONTENT_OFF:
+ mhl_tmds_ctrl(mhl_ctrl, TMDS_DISABLE);
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int mhl_rap_recv(struct mhl_tx_ctrl *mhl_ctrl, u8 action_code)
+{
+ u8 error_code;
+
+ switch (action_code) {
+ case MHL_RAP_POLL:
+ if (mhl_ctrl->tmds_enabled())
+ error_code = MHL_RAPK_NO_ERROR;
+ else
+ error_code = MHL_RAPK_UNSUPPORTED_ACTION_CODE;
+ break;
+ case MHL_RAP_CONTENT_ON:
+ case MHL_RAP_CONTENT_OFF:
+ mhl_rap_action(mhl_ctrl, action_code);
+ error_code = MHL_RAPK_NO_ERROR;
+ break;
+ default:
+ error_code = MHL_RAPK_UNRECOGNIZED_ACTION_CODE;
+ break;
+ }
+ /* prior send rapk */
+ return mhl_msc_send_prior_msc_msg(
+ mhl_ctrl,
+ MHL_MSC_MSG_RAPK,
+ error_code);
+}
+
+
+int mhl_msc_recv_msc_msg(struct mhl_tx_ctrl *mhl_ctrl,
+ u8 sub_cmd, u8 cmd_data)
+{
+ int rc = 0;
+ switch (sub_cmd) {
+ case MHL_MSC_MSG_RCP:
+ pr_debug("MHL: receive RCP(0x%02x)\n", cmd_data);
+ rc = mhl_rcp_recv(mhl_ctrl, cmd_data);
+ break;
+ case MHL_MSC_MSG_RCPK:
+ pr_debug("MHL: receive RCPK(0x%02x)\n", cmd_data);
+ break;
+ case MHL_MSC_MSG_RCPE:
+ pr_debug("MHL: receive RCPE(0x%02x)\n", cmd_data);
+ break;
+ case MHL_MSC_MSG_RAP:
+ pr_debug("MHL: receive RAP(0x%02x)\n", cmd_data);
+ rc = mhl_rap_recv(mhl_ctrl, cmd_data);
+ break;
+ case MHL_MSC_MSG_RAPK:
+ pr_debug("MHL: receive RAPK(0x%02x)\n", cmd_data);
+ break;
+ default:
+ break;
+ }
+ return rc;
+}
+
+int mhl_msc_recv_set_int(struct mhl_tx_ctrl *mhl_ctrl,
+ u8 offset, u8 set_int)
+{
+ if (offset >= 2)
+ return -EFAULT;
+
+ switch (offset) {
+ case 0:
+ if (set_int & MHL_INT_DCAP_CHG) {
+ /* peer dcap has changed */
+ mhl_ctrl->devcap_state = 0;
+ mhl_msc_read_devcap_all(mhl_ctrl);
+ }
+ if (set_int & MHL_INT_DSCR_CHG)
+ pr_debug("%s: dscr chg\n", __func__);
+ if (set_int & MHL_INT_REQ_WRT) {
+ /* SET_INT: GRT_WRT */
+ mhl_msc_send_set_int(
+ mhl_ctrl,
+ MHL_RCHANGE_INT,
+ MHL_INT_GRT_WRT);
+ }
+ if (set_int & MHL_INT_GRT_WRT)
+ pr_debug("%s: recvd req to permit/grant write",
+ __func__);
+ break;
+ case 1:
+ if (set_int & MHL_INT_EDID_CHG) {
+ /* peer EDID has changed
+ * toggle HPD to read EDID
+ */
+ pr_debug("%s: EDID CHG\n", __func__);
+ mhl_drive_hpd(mhl_ctrl, HPD_DOWN);
+ msleep(110);
+ mhl_drive_hpd(mhl_ctrl, HPD_UP);
+ }
+ }
+ return 0;
+}
+
+int mhl_msc_recv_write_stat(struct mhl_tx_ctrl *mhl_ctrl,
+ u8 offset, u8 value)
+{
+ if (offset >= 2)
+ return -EFAULT;
+
+ switch (offset) {
+ case 0:
+ /*
+ * connected device bits
+ * changed and DEVCAP READY
+ */
+ if (((value ^ mhl_ctrl->devcap_state) &
+ MHL_STATUS_DCAP_RDY)) {
+ if (value & MHL_STATUS_DCAP_RDY) {
+ mhl_ctrl->devcap_state = 0;
+ mhl_msc_read_devcap_all(mhl_ctrl);
+ } else {
+ /*
+ * peer dcap turned not ready
+ * use old devap state
+ */
+ pr_debug("%s: DCAP RDY bit cleared\n",
+ __func__);
+ }
+ }
+ break;
+ case 1:
+ /*
+ * connected device bits
+ * changed and PATH ENABLED
+ * bit set
+ */
+ if ((value ^ mhl_ctrl->path_en_state)
+ & MHL_STATUS_PATH_ENABLED) {
+ if (value & MHL_STATUS_PATH_ENABLED) {
+ if (mhl_ctrl->tmds_enabled() &&
+ (mhl_ctrl->devcap[offset] &
+ MHL_FEATURE_RAP_SUPPORT)) {
+ mhl_msc_send_msc_msg(
+ mhl_ctrl,
+ MHL_MSC_MSG_RAP,
+ MHL_RAP_CONTENT_ON);
+ }
+ mhl_ctrl->path_en_state
+ |= (MHL_STATUS_PATH_ENABLED |
+ MHL_STATUS_CLK_MODE_NORMAL);
+ mhl_msc_send_write_stat(
+ mhl_ctrl,
+ MHL_STATUS_REG_LINK_MODE,
+ mhl_ctrl->path_en_state);
+ } else {
+ mhl_ctrl->path_en_state
+ &= ~(MHL_STATUS_PATH_ENABLED |
+ MHL_STATUS_CLK_MODE_NORMAL);
+ mhl_msc_send_write_stat(
+ mhl_ctrl,
+ MHL_STATUS_REG_LINK_MODE,
+ mhl_ctrl->path_en_state);
+ }
+ }
+ break;
+ }
+ mhl_ctrl->path_en_state = value;
+ return 0;
+}
diff --git a/drivers/video/msm/mdss/mhl_msc.h b/drivers/video/msm/mdss/mhl_msc.h
new file mode 100644
index 0000000..9a7b3d6
--- /dev/null
+++ b/drivers/video/msm/mdss/mhl_msc.h
@@ -0,0 +1,57 @@
+/* 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 __MHL_MSC_H__
+#define __MHL_MSC_H__
+#include <linux/mhl_8334.h>
+
+#define MAX_RCP_KEYS_SUPPORTED 256
+
+#define MSC_NORMAL_SEND 0
+#define MSC_PRIORITY_SEND 1
+
+#define TMDS_ENABLE 1
+#define TMDS_DISABLE 0
+
+/******************************************************************/
+/* the below APIs are implemented by the MSC functionality */
+int mhl_msc_command_done(struct mhl_tx_ctrl *mhl_ctrl,
+ struct msc_command_struct *req);
+
+int mhl_msc_send_set_int(struct mhl_tx_ctrl *mhl_ctrl,
+ u8 offset, u8 mask);
+
+int mhl_msc_send_write_stat(struct mhl_tx_ctrl *mhl_ctrl,
+ u8 offset, u8 value);
+int mhl_msc_send_msc_msg(struct mhl_tx_ctrl *mhl_ctrl,
+ u8 sub_cmd, u8 cmd_data);
+
+int mhl_msc_recv_set_int(struct mhl_tx_ctrl *mhl_ctrl, u8 offset, u8 set_int);
+
+int mhl_msc_recv_write_stat(struct mhl_tx_ctrl *mhl_ctrl,
+ u8 offset, u8 value);
+int mhl_msc_recv_msc_msg(struct mhl_tx_ctrl *mhl_ctrl,
+ u8 sub_cmd, u8 cmd_data);
+void mhl_msc_send_work(struct work_struct *work);
+
+/******************************************************************/
+/* Tx should implement these APIs */
+int mhl_send_msc_command(struct mhl_tx_ctrl *mhl_ctrl,
+ struct msc_command_struct *req);
+void mhl_drive_hpd(struct mhl_tx_ctrl *mhl_ctrl, uint8_t to_state);
+void mhl_tmds_ctrl(struct mhl_tx_ctrl *ctrl, uint8_t on);
+/******************************************************************/
+/* MHL driver registers ctrl with MSC */
+void mhl_register_msc(struct mhl_tx_ctrl *ctrl);
+
+#endif /* __MHL_MSC_H__ */
diff --git a/drivers/video/msm/mdss/mhl_sii8334.c b/drivers/video/msm/mdss/mhl_sii8334.c
index f3be983..7baeef5 100644
--- a/drivers/video/msm/mdss/mhl_sii8334.c
+++ b/drivers/video/msm/mdss/mhl_sii8334.c
@@ -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
@@ -18,6 +18,8 @@
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/types.h>
+#include <linux/vmalloc.h>
+#include <linux/input.h>
#include <linux/usb/msm_hsusb.h>
#include <linux/mhl_8334.h>
@@ -27,6 +29,7 @@
#include "mdss.h"
#include "mdss_panel.h"
#include "mdss_io_util.h"
+#include "mhl_msc.h"
#define MHL_DRIVER_NAME "sii8334"
#define COMPATIBLE_NAME "qcom,mhl-sii8334"
@@ -34,39 +37,142 @@
#define pr_debug_intr(...) pr_debug("\n")
-enum mhl_gpio_type {
- MHL_TX_RESET_GPIO,
- MHL_TX_INTR_GPIO,
- MHL_TX_PMIC_PWR_GPIO,
- MHL_TX_MAX_GPIO,
-};
+#define MSC_START_BIT_MSC_CMD (0x01 << 0)
+#define MSC_START_BIT_VS_CMD (0x01 << 1)
+#define MSC_START_BIT_READ_REG (0x01 << 2)
+#define MSC_START_BIT_WRITE_REG (0x01 << 3)
+#define MSC_START_BIT_WRITE_BURST (0x01 << 4)
-enum mhl_vreg_type {
- MHL_TX_3V_VREG,
- MHL_TX_MAX_VREG,
-};
-
-struct mhl_tx_platform_data {
- /* Data filled from device tree nodes */
- struct dss_gpio *gpios[MHL_TX_MAX_GPIO];
- struct dss_vreg *vregs[MHL_TX_MAX_VREG];
- int irq;
-};
-
-struct mhl_tx_ctrl {
- struct platform_device *pdev;
- struct mhl_tx_platform_data *pdata;
- struct i2c_client *i2c_handle;
- uint8_t cur_state;
- uint8_t chip_rev_id;
- int mhl_mode;
- struct completion rgnd_done;
- void (*notify_usb_online)(int online);
- struct usb_ext_notification *mhl_info;
- bool disc_enabled;
- struct power_supply mhl_psy;
- bool vbus_active;
- int current_val;
+/* supported RCP key code */
+u16 support_rcp_key_code_tbl[] = {
+ KEY_ENTER, /* 0x00 Select */
+ KEY_UP, /* 0x01 Up */
+ KEY_DOWN, /* 0x02 Down */
+ KEY_LEFT, /* 0x03 Left */
+ KEY_RIGHT, /* 0x04 Right */
+ KEY_UNKNOWN, /* 0x05 Right-up */
+ KEY_UNKNOWN, /* 0x06 Right-down */
+ KEY_UNKNOWN, /* 0x07 Left-up */
+ KEY_UNKNOWN, /* 0x08 Left-down */
+ KEY_MENU, /* 0x09 Root Menu */
+ KEY_OPTION, /* 0x0A Setup Menu */
+ KEY_UNKNOWN, /* 0x0B Contents Menu */
+ KEY_UNKNOWN, /* 0x0C Favorite Menu */
+ KEY_EXIT, /* 0x0D Exit */
+ KEY_RESERVED, /* 0x0E */
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED, /* 0x1F */
+ KEY_NUMERIC_0, /* 0x20 NUMERIC_0 */
+ KEY_NUMERIC_1, /* 0x21 NUMERIC_1 */
+ KEY_NUMERIC_2, /* 0x22 NUMERIC_2 */
+ KEY_NUMERIC_3, /* 0x23 NUMERIC_3 */
+ KEY_NUMERIC_4, /* 0x24 NUMERIC_4 */
+ KEY_NUMERIC_5, /* 0x25 NUMERIC_5 */
+ KEY_NUMERIC_6, /* 0x26 NUMERIC_6 */
+ KEY_NUMERIC_7, /* 0x27 NUMERIC_7 */
+ KEY_NUMERIC_8, /* 0x28 NUMERIC_8 */
+ KEY_NUMERIC_9, /* 0x29 NUMERIC_9 */
+ KEY_DOT, /* 0x2A Dot */
+ KEY_ENTER, /* 0x2B Enter */
+ KEY_ESC, /* 0x2C Clear */
+ KEY_RESERVED, /* 0x2D */
+ KEY_RESERVED, /* 0x2E */
+ KEY_RESERVED, /* 0x2F */
+ KEY_UNKNOWN, /* 0x30 Channel Up */
+ KEY_UNKNOWN, /* 0x31 Channel Down */
+ KEY_UNKNOWN, /* 0x32 Previous Channel */
+ KEY_UNKNOWN, /* 0x33 Sound Select */
+ KEY_UNKNOWN, /* 0x34 Input Select */
+ KEY_UNKNOWN, /* 0x35 Show Information */
+ KEY_UNKNOWN, /* 0x36 Help */
+ KEY_UNKNOWN, /* 0x37 Page Up */
+ KEY_UNKNOWN, /* 0x38 Page Down */
+ KEY_RESERVED, /* 0x39 */
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED, /* 0x3F */
+ KEY_RESERVED, /* 0x40 */
+ KEY_VOLUMEUP, /* 0x41 Volume Up */
+ KEY_VOLUMEDOWN, /* 0x42 Volume Down */
+ KEY_MUTE, /* 0x43 Mute */
+ KEY_PLAY, /* 0x44 Play */
+ KEY_STOP, /* 0x45 Stop */
+ KEY_PAUSE, /* 0x46 Pause */
+ KEY_UNKNOWN, /* 0x47 Record */
+ KEY_REWIND, /* 0x48 Rewind */
+ KEY_FASTFORWARD, /* 0x49 Fast Forward */
+ KEY_UNKNOWN, /* 0x4A Eject */
+ KEY_FORWARD, /* 0x4B Forward */
+ KEY_BACK, /* 0x4C Backward */
+ KEY_RESERVED, /* 0x4D */
+ KEY_RESERVED,
+ KEY_RESERVED, /* 0x4F */
+ KEY_UNKNOWN, /* 0x50 Angle */
+ KEY_UNKNOWN, /* 0x51 Subtitle */
+ KEY_RESERVED, /* 0x52 */
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED, /* 0x5F */
+ KEY_PLAYPAUSE, /* 0x60 Play Function */
+ KEY_PLAYPAUSE, /* 0x61 Pause_Play Function */
+ KEY_UNKNOWN, /* 0x62 Record Function */
+ KEY_PAUSE, /* 0x63 Pause Record Function */
+ KEY_STOP, /* 0x64 Stop Function */
+ KEY_MUTE, /* 0x65 Mute Function */
+ KEY_UNKNOWN, /* 0x66 Restore Volume Function */
+ KEY_UNKNOWN, /* 0x67 Tune Function */
+ KEY_UNKNOWN, /* 0x68 Select Media Function */
+ KEY_RESERVED, /* 0x69 */
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED, /* 0x70 */
+ KEY_BLUE, /* 0x71 F1 */
+ KEY_RED, /* 0x72 F2 */
+ KEY_GREEN, /* 0x73 F3 */
+ KEY_YELLOW, /* 0x74 F4 */
+ KEY_UNKNOWN, /* 0x75 F5 */
+ KEY_RESERVED, /* 0x76 */
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED, /* 0x7D */
+ KEY_VENDOR, /* Vendor Specific */
+ KEY_RESERVED, /* 0x7F */
};
@@ -84,13 +190,12 @@
static irqreturn_t mhl_tx_isr(int irq, void *dev_id);
static void switch_mode(struct mhl_tx_ctrl *mhl_ctrl,
enum mhl_st_type to_mode);
-static void mhl_drive_hpd(struct mhl_tx_ctrl *mhl_ctrl,
- uint8_t to_state);
-
static void mhl_init_reg_settings(struct mhl_tx_ctrl *mhl_ctrl,
- bool mhl_disc_en);
+ bool mhl_disc_en);
-static int mhl_i2c_reg_read(struct i2c_client *client,
+static uint8_t store_tmds_state;
+
+int mhl_i2c_reg_read(struct i2c_client *client,
uint8_t slave_addr_index, uint8_t reg_offset)
{
int rc = -1;
@@ -107,7 +212,7 @@
}
-static int mhl_i2c_reg_write(struct i2c_client *client,
+int mhl_i2c_reg_write(struct i2c_client *client,
uint8_t slave_addr_index, uint8_t reg_offset,
uint8_t value)
{
@@ -115,7 +220,7 @@
reg_offset, &value);
}
-static void mhl_i2c_reg_modify(struct i2c_client *client,
+void mhl_i2c_reg_modify(struct i2c_client *client,
uint8_t slave_addr_index, uint8_t reg_offset,
uint8_t mask, uint8_t val)
{
@@ -350,11 +455,11 @@
MHL_SII_REG_NAME_WR(REG_INTR5_MASK, 0x00);
/* Unmask CBUS1 Intrs */
- MHL_SII_CBUS_WR(0x0009,
+ MHL_SII_REG_NAME_WR(REG_CBUS_INTR_ENABLE,
BIT2 | BIT3 | BIT4 | BIT5 | BIT6);
/* Unmask CBUS2 Intrs */
- MHL_SII_CBUS_WR(0x001F, BIT2 | BIT3);
+ MHL_SII_REG_NAME_WR(REG_CBUS_MSC_INT2_ENABLE, BIT2 | BIT3);
for (i = 0; i < 4; i++) {
/*
@@ -369,7 +474,6 @@
*/
MHL_SII_CBUS_WR((0xF0 + i), 0xFF);
}
- return;
}
static void init_cbus_regs(struct i2c_client *client)
@@ -576,6 +680,7 @@
/* Force HPD to 0 when not in MHL mode. */
mhl_drive_hpd(mhl_ctrl, HPD_DOWN);
+ mhl_tmds_ctrl(mhl_ctrl, TMDS_DISABLE);
/*
* Change TMDS termination to high impedance
* on disconnection.
@@ -592,7 +697,31 @@
}
}
-static void mhl_drive_hpd(struct mhl_tx_ctrl *mhl_ctrl, uint8_t to_state)
+uint8_t check_tmds_enabled(void)
+{
+ return store_tmds_state;
+}
+
+void mhl_tmds_ctrl(struct mhl_tx_ctrl *mhl_ctrl, uint8_t on)
+{
+ struct i2c_client *client = mhl_ctrl->i2c_handle;
+ if (on) {
+ MHL_SII_REG_NAME_MOD(REG_TMDS_CCTRL, BIT4, BIT4);
+ mhl_drive_hpd(mhl_ctrl, HPD_UP);
+ /*
+ * store the state to be used
+ * before responding to RAP msgs
+ * this needs to be obtained from
+ * hdmi driver
+ */
+ store_tmds_state = 1;
+ } else {
+ MHL_SII_REG_NAME_MOD(REG_TMDS_CCTRL, BIT4, 0x00);
+ store_tmds_state = 0;
+ }
+}
+
+void mhl_drive_hpd(struct mhl_tx_ctrl *mhl_ctrl, uint8_t to_state)
{
struct i2c_client *client = mhl_ctrl->i2c_handle;
@@ -600,15 +729,6 @@
if (to_state == HPD_UP) {
/*
* Drive HPD to UP state
- *
- * The below two reg configs combined
- * enable TMDS output.
- */
-
- /* Enable TMDS on TMDS_CCTRL */
- MHL_SII_REG_NAME_MOD(REG_TMDS_CCTRL, BIT4, BIT4);
-
- /*
* Set HPD_OUT_OVR_EN = HPD State
* EDID read and Un-force HPD (from low)
* propogate to src let HPD float by clearing
@@ -616,15 +736,9 @@
*/
MHL_SII_REG_NAME_MOD(REG_INT_CTRL, BIT4, 0x00);
} else {
- /*
- * Drive HPD to DOWN state
- * Disable TMDS Output on REG_TMDS_CCTRL
- * Enable/Disable TMDS output (MHL TMDS output only)
- */
+ /* Drive HPD to DOWN state */
MHL_SII_REG_NAME_MOD(REG_INT_CTRL, BIT4, BIT4);
- MHL_SII_REG_NAME_MOD(REG_TMDS_CCTRL, BIT4, 0x00);
}
- return;
}
static void mhl_msm_connection(struct mhl_tx_ctrl *mhl_ctrl)
@@ -655,7 +769,19 @@
val = MHL_SII_PAGE3_RD(0x10);
MHL_SII_PAGE3_WR(0x10, val | BIT0);
- return;
+ /*
+ * indicate DCAP_RDY and DCAP_CHG
+ * to the peer only after
+ * msm conn has been established
+ */
+ mhl_msc_send_write_stat(mhl_ctrl,
+ MHL_STATUS_REG_CONNECTED_RDY,
+ MHL_STATUS_DCAP_RDY);
+
+ mhl_msc_send_set_int(mhl_ctrl,
+ MHL_RCHANGE_INT,
+ MHL_INT_DCAP_CHG);
+
}
static void mhl_msm_disconnection(struct mhl_tx_ctrl *mhl_ctrl)
@@ -668,7 +794,6 @@
MHL_SII_PAGE3_WR(0x30, 0xD0);
switch_mode(mhl_ctrl, POWER_STATE_D3);
- return;
}
static int mhl_msm_read_rgnd_int(struct mhl_tx_ctrl *mhl_ctrl)
@@ -676,8 +801,8 @@
uint8_t rgnd_imp;
struct i2c_client *client = mhl_ctrl->i2c_handle;
/* DISC STATUS REG 2 */
- rgnd_imp = (mhl_i2c_reg_read(client,
- TX_PAGE_3, 0x001C) & (BIT1 | BIT0));
+ rgnd_imp = (mhl_i2c_reg_read(client, TX_PAGE_3, 0x001C) &
+ (BIT1 | BIT0));
pr_debug("imp range read=%02X\n", (int)rgnd_imp);
if (0x02 == rgnd_imp) {
@@ -820,8 +945,6 @@
release_usb_switch_open(mhl_ctrl);
}
MHL_SII_REG_NAME_WR(REG_INTR4, status);
-
- return;
}
static void mhl_misc_isr(struct mhl_tx_ctrl *mhl_ctrl)
@@ -861,9 +984,225 @@
*/
cbus_stat = MHL_SII_CBUS_RD(0x0D);
if (BIT6 & cbus_stat)
- mhl_drive_hpd(mhl_ctrl, HPD_UP);
+ mhl_tmds_ctrl(mhl_ctrl, TMDS_ENABLE);
}
- return;
+}
+
+static void mhl_sii_cbus_process_errors(struct i2c_client *client,
+ u8 int_status)
+{
+ u8 abort_reason = 0;
+
+ if (int_status & BIT2) {
+ abort_reason = MHL_SII_REG_NAME_RD(REG_DDC_ABORT_REASON);
+ pr_debug("%s: CBUS DDC Abort Reason(0x%02x)\n",
+ __func__, abort_reason);
+ }
+ if (int_status & BIT5) {
+ abort_reason = MHL_SII_REG_NAME_RD(REG_PRI_XFR_ABORT_REASON);
+ pr_debug("%s: CBUS MSC Requestor Abort Reason(0x%02x)\n",
+ __func__, abort_reason);
+ MHL_SII_REG_NAME_WR(REG_PRI_XFR_ABORT_REASON, 0xFF);
+ }
+ if (int_status & BIT6) {
+ abort_reason = MHL_SII_REG_NAME_RD(
+ REG_CBUS_PRI_FWR_ABORT_REASON);
+ pr_debug("%s: CBUS MSC Responder Abort Reason(0x%02x)\n",
+ __func__, abort_reason);
+ MHL_SII_REG_NAME_WR(REG_CBUS_PRI_FWR_ABORT_REASON, 0xFF);
+ }
+}
+
+int mhl_send_msc_command(struct mhl_tx_ctrl *mhl_ctrl,
+ struct msc_command_struct *req)
+{
+ int timeout;
+ u8 start_bit = 0x00;
+ u8 *burst_data;
+ int i;
+ struct i2c_client *client = mhl_ctrl->i2c_handle;
+
+ if (mhl_ctrl->cur_state != POWER_STATE_D0_MHL) {
+ pr_debug("%s: power_state:%02x CBUS(0x0A):%02x\n",
+ __func__,
+ mhl_ctrl->cur_state,
+ MHL_SII_REG_NAME_RD(REG_CBUS_BUS_STATUS));
+ return -EFAULT;
+ }
+
+ if (!req)
+ return -EFAULT;
+
+ pr_debug("%s: command=0x%02x offset=0x%02x %02x %02x",
+ __func__,
+ req->command,
+ req->offset,
+ req->payload.data[0],
+ req->payload.data[1]);
+
+ /* REG_CBUS_PRI_ADDR_CMD = REQ CBUS CMD or OFFSET */
+ MHL_SII_REG_NAME_WR(REG_CBUS_PRI_ADDR_CMD, req->offset);
+ MHL_SII_REG_NAME_WR(REG_CBUS_PRI_WR_DATA_1ST,
+ req->payload.data[0]);
+
+ switch (req->command) {
+ case MHL_SET_INT:
+ case MHL_WRITE_STAT:
+ start_bit = MSC_START_BIT_WRITE_REG;
+ break;
+ case MHL_READ_DEVCAP:
+ start_bit = MSC_START_BIT_READ_REG;
+ break;
+ case MHL_GET_STATE:
+ case MHL_GET_VENDOR_ID:
+ case MHL_SET_HPD:
+ case MHL_CLR_HPD:
+ case MHL_GET_SC1_ERRORCODE:
+ case MHL_GET_DDC_ERRORCODE:
+ case MHL_GET_MSC_ERRORCODE:
+ case MHL_GET_SC3_ERRORCODE:
+ start_bit = MSC_START_BIT_MSC_CMD;
+ MHL_SII_REG_NAME_WR(REG_CBUS_PRI_ADDR_CMD, req->command);
+ break;
+ case MHL_MSC_MSG:
+ start_bit = MSC_START_BIT_VS_CMD;
+ MHL_SII_REG_NAME_WR(REG_CBUS_PRI_WR_DATA_2ND,
+ req->payload.data[1]);
+ MHL_SII_REG_NAME_WR(REG_CBUS_PRI_ADDR_CMD, req->command);
+ break;
+ case MHL_WRITE_BURST:
+ start_bit = MSC_START_BIT_WRITE_BURST;
+ MHL_SII_REG_NAME_WR(REG_MSC_WRITE_BURST_LEN, req->length - 1);
+ if (!(req->payload.burst_data)) {
+ pr_err("%s: burst data is null!\n", __func__);
+ goto cbus_send_fail;
+ }
+ burst_data = req->payload.burst_data;
+ for (i = 0; i < req->length; i++, burst_data++)
+ MHL_SII_CBUS_WR(0xC0 + i, *burst_data);
+ break;
+ default:
+ pr_err("%s: unknown command! (%02x)\n",
+ __func__, req->command);
+ goto cbus_send_fail;
+ }
+
+ INIT_COMPLETION(mhl_ctrl->msc_cmd_done);
+ MHL_SII_REG_NAME_WR(REG_CBUS_PRI_START, start_bit);
+ timeout = wait_for_completion_interruptible_timeout
+ (&mhl_ctrl->msc_cmd_done, msecs_to_jiffies(T_ABORT_NEXT));
+ if (!timeout) {
+ pr_err("%s: cbus_command_send timed out!\n", __func__);
+ goto cbus_send_fail;
+ }
+
+ switch (req->command) {
+ case MHL_READ_DEVCAP:
+ req->retval = MHL_SII_REG_NAME_RD(REG_CBUS_PRI_RD_DATA_1ST);
+ break;
+ case MHL_MSC_MSG:
+ /* check if MSC_MSG NACKed */
+ if (MHL_SII_REG_NAME_RD(REG_MSC_WRITE_BURST_LEN) & BIT6)
+ return -EAGAIN;
+ default:
+ req->retval = 0;
+ break;
+ }
+ mhl_msc_command_done(mhl_ctrl, req);
+ pr_debug("%s: msc cmd done\n", __func__);
+ return 0;
+
+cbus_send_fail:
+ return -EFAULT;
+}
+
+static void mhl_cbus_isr(struct mhl_tx_ctrl *mhl_ctrl)
+{
+ uint8_t regval;
+ int req_done = 0;
+ uint8_t sub_cmd = 0x0;
+ uint8_t cmd_data = 0x0;
+ int msc_msg_recved = 0;
+ int rc = -1;
+ struct i2c_client *client = mhl_ctrl->i2c_handle;
+
+ regval = MHL_SII_REG_NAME_RD(REG_CBUS_INTR_STATUS);
+ if (regval == 0xff)
+ return;
+
+ if (regval)
+ MHL_SII_REG_NAME_WR(REG_CBUS_INTR_STATUS, regval);
+
+ pr_debug("%s: CBUS_INT = %02x\n", __func__, regval);
+
+ /* MSC_MSG (RCP/RAP) */
+ if (regval & BIT3) {
+ sub_cmd = MHL_SII_REG_NAME_RD(REG_CBUS_PRI_VS_CMD);
+ cmd_data = MHL_SII_REG_NAME_RD(REG_CBUS_PRI_VS_DATA);
+ msc_msg_recved = 1;
+ }
+ /* MSC_MT_ABRT/MSC_MR_ABRT/DDC_ABORT */
+ if (regval & (BIT6 | BIT5 | BIT2))
+ mhl_sii_cbus_process_errors(client, regval);
+
+ /* MSC_REQ_DONE */
+ if (regval & BIT4)
+ req_done = 1;
+
+ /* look for interrupts on CBUS_MSC_INT2 */
+ regval = MHL_SII_REG_NAME_RD(REG_CBUS_MSC_INT2_STATUS);
+
+ /* clear all interrupts */
+ if (regval)
+ MHL_SII_REG_NAME_WR(REG_CBUS_MSC_INT2_STATUS, regval);
+
+ pr_debug("%s: CBUS_MSC_INT2 = %02x\n", __func__, regval);
+
+ /* received SET_INT */
+ if (regval & BIT2) {
+ uint8_t intr;
+ intr = MHL_SII_REG_NAME_RD(REG_CBUS_SET_INT_0);
+ MHL_SII_REG_NAME_WR(REG_CBUS_SET_INT_0, intr);
+ mhl_msc_recv_set_int(mhl_ctrl, 0, intr);
+
+ pr_debug("%s: MHL_INT_0 = %02x\n", __func__, intr);
+ intr = MHL_SII_REG_NAME_RD(REG_CBUS_SET_INT_1);
+ MHL_SII_REG_NAME_WR(REG_CBUS_SET_INT_1, intr);
+ mhl_msc_recv_set_int(mhl_ctrl, 1, intr);
+
+ pr_debug("%s: MHL_INT_1 = %02x\n", __func__, intr);
+ MHL_SII_REG_NAME_WR(REG_CBUS_SET_INT_2, 0xFF);
+ MHL_SII_REG_NAME_WR(REG_CBUS_SET_INT_3, 0xFF);
+ }
+
+ /* received WRITE_STAT */
+ if (regval & BIT3) {
+ uint8_t stat;
+ stat = MHL_SII_REG_NAME_RD(REG_CBUS_WRITE_STAT_0);
+ mhl_msc_recv_write_stat(mhl_ctrl, 0, stat);
+
+ pr_debug("%s: MHL_STATUS_0 = %02x\n", __func__, stat);
+ stat = MHL_SII_REG_NAME_RD(REG_CBUS_WRITE_STAT_1);
+ mhl_msc_recv_write_stat(mhl_ctrl, 1, stat);
+ pr_debug("%s: MHL_STATUS_1 = %02x\n", __func__, stat);
+
+ MHL_SII_REG_NAME_WR(REG_CBUS_WRITE_STAT_0, 0xFF);
+ MHL_SII_REG_NAME_WR(REG_CBUS_WRITE_STAT_1, 0xFF);
+ MHL_SII_REG_NAME_WR(REG_CBUS_WRITE_STAT_2, 0xFF);
+ MHL_SII_REG_NAME_WR(REG_CBUS_WRITE_STAT_3, 0xFF);
+ }
+
+ /* received MSC_MSG */
+ if (msc_msg_recved) {
+ /*mhl msc recv msc msg*/
+ rc = mhl_msc_recv_msc_msg(mhl_ctrl, sub_cmd, cmd_data);
+ if (rc)
+ pr_err("MHL: mhl msc recv msc msg failed(%d)!\n", rc);
+ }
+ /* complete last command */
+ if (req_done)
+ complete_all(&mhl_ctrl->msc_cmd_done);
+
}
static void clear_all_intrs(struct i2c_client *client)
@@ -1002,11 +1341,11 @@
mhl_misc_isr(mhl_ctrl);
/*
- * Check for any peer messages for DCAP_CHG etc
+ * Check for any peer messages for DCAP_CHG, MSC etc
* Dispatch to have the CBUS module working only
* once connected.
- mhl_cbus_isr(mhl_ctrl);
*/
+ mhl_cbus_isr(mhl_ctrl);
mhl_hpd_stat_isr(mhl_ctrl);
}
@@ -1296,6 +1635,59 @@
* such tx specific
*/
mhl_ctrl->disc_enabled = false;
+ INIT_WORK(&mhl_ctrl->mhl_msc_send_work, mhl_msc_send_work);
+ mhl_ctrl->cur_state = POWER_STATE_D0_MHL;
+ INIT_LIST_HEAD(&mhl_ctrl->list_cmd);
+ init_completion(&mhl_ctrl->msc_cmd_done);
+ mhl_ctrl->msc_send_workqueue = create_singlethread_workqueue
+ ("mhl_msc_cmd_queue");
+
+ mhl_ctrl->input = input_allocate_device();
+ if (mhl_ctrl->input) {
+ int i;
+ struct input_dev *input = mhl_ctrl->input;
+
+ mhl_ctrl->rcp_key_code_tbl = vmalloc(
+ ARRAY_SIZE(support_rcp_key_code_tbl));
+ if (!mhl_ctrl->rcp_key_code_tbl) {
+ pr_err("%s: no alloc mem for rcp keycode tbl\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ memcpy(mhl_ctrl->rcp_key_code_tbl,
+ &support_rcp_key_code_tbl[0],
+ ARRAY_SIZE(support_rcp_key_code_tbl));
+ mhl_ctrl->rcp_key_code_tbl_len = ARRAY_SIZE(
+ support_rcp_key_code_tbl);
+
+ input->phys = "cbus/input0";
+ input->id.bustype = BUS_VIRTUAL;
+ input->id.vendor = 0x1095;
+ input->id.product = 0x8334;
+ input->id.version = 0xA;
+
+ input->name = "mhl-rcp";
+
+ input->keycode = support_rcp_key_code_tbl;
+ input->keycodesize = sizeof(u16);
+ input->keycodemax = ARRAY_SIZE(support_rcp_key_code_tbl);
+
+ input->evbit[0] = EV_KEY;
+ for (i = 0; i < ARRAY_SIZE(support_rcp_key_code_tbl); i++) {
+ if (support_rcp_key_code_tbl[i] > 1)
+ input_set_capability(input, EV_KEY,
+ support_rcp_key_code_tbl[i]);
+ }
+
+ if (input_register_device(input) < 0) {
+ pr_warn("%s: failed to register input device\n",
+ __func__);
+ input_free_device(input);
+ mhl_ctrl->input = NULL;
+ }
+ }
+
rc = mhl_tx_chip_init(mhl_ctrl);
if (rc) {
pr_err("%s: tx chip init failed [%d]\n",
@@ -1315,7 +1707,7 @@
client->dev.driver->name, mhl_ctrl);
if (rc) {
pr_err("request_threaded_irq failed, status: %d\n",
- rc);
+ rc);
goto failed_probe;
} else {
pr_debug("request_threaded_irq succeeded\n");
@@ -1353,6 +1745,8 @@
goto failed_probe;
}
mhl_ctrl->mhl_info = mhl_info;
+ mhl_register_msc(mhl_ctrl);
+ mhl_ctrl->tmds_enabled = check_tmds_enabled;
return 0;
failed_probe:
mhl_gpio_config(mhl_ctrl, 0);
diff --git a/drivers/video/msm/msm_fb.c b/drivers/video/msm/msm_fb.c
index e4e93eb..3841498 100644
--- a/drivers/video/msm/msm_fb.c
+++ b/drivers/video/msm/msm_fb.c
@@ -3,7 +3,7 @@
* Core MSM framebuffer driver.
*
* Copyright (C) 2007 Google Incorporated
- * Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2008-2013, The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@@ -1262,8 +1262,6 @@
((PAGE_SIZE - remainder)/fix->line_length) * mfd->fb_page;
var->bits_per_pixel = bpp * 8; /* FrameBuffer color depth */
- var->reserved[4] = mdp_get_panel_framerate(mfd);
-
/*
* id field for fb app
*/
@@ -1732,7 +1730,7 @@
}
mdp_set_dma_pan_info(info, dirtyPtr,
- (var->activate == FB_ACTIVATE_VBL));
+ (var->activate & FB_ACTIVATE_VBL));
mdp_dma_pan_update(info);
up(&msm_fb_pan_sem);
@@ -3249,6 +3247,10 @@
ret = mdp4_qseed_cfg((struct mdp_qseed_cfg_data *)
&pp_ptr->data.qseed_cfg_data);
break;
+ case mdp_op_calib_cfg:
+ ret = mdp4_calib_config((struct mdp_calib_config_data *)
+ &pp_ptr->data.calib_cfg);
+ break;
#endif
case mdp_bl_scale_cfg:
ret = mdp_bl_scale_config(mfd, (struct mdp_bl_scale_data *)
@@ -3281,6 +3283,24 @@
}
return ret;
}
+
+static int msmfb_get_metadata(struct msm_fb_data_type *mfd,
+ struct msmfb_metadata *metadata_ptr)
+{
+ int ret = 0;
+ switch (metadata_ptr->op) {
+ case metadata_op_frame_rate:
+ metadata_ptr->data.panel_frame_rate =
+ mdp_get_panel_framerate(mfd);
+ break;
+ default:
+ pr_warn("Unsupported request to MDP META IOCTL.\n");
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
static int msm_fb_ioctl(struct fb_info *info, unsigned int cmd,
unsigned long arg)
{
@@ -3583,6 +3603,8 @@
return ret;
ret = msmfb_handle_pp_ioctl(mfd, &mdp_pp);
+ if (ret == 1)
+ ret = copy_to_user(argp, &mdp_pp, sizeof(mdp_pp));
break;
case MSMFB_METADATA_SET:
@@ -3592,6 +3614,16 @@
ret = msmfb_handle_metadata_ioctl(mfd, &mdp_metadata);
break;
+ case MSMFB_METADATA_GET:
+ ret = copy_from_user(&mdp_metadata, argp, sizeof(mdp_metadata));
+ if (ret)
+ return ret;
+ ret = msmfb_get_metadata(mfd, &mdp_metadata);
+ if (!ret)
+ ret = copy_to_user(argp, &mdp_metadata,
+ sizeof(mdp_metadata));
+ break;
+
default:
MSM_FB_INFO("MDP: unknown ioctl (cmd=%x) received!\n", cmd);
ret = -EINVAL;
diff --git a/drivers/video/msm/vidc/1080p/ddl/vcd_ddl.c b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl.c
index 91cf3ae..582744c 100644
--- a/drivers/video/msm/vidc/1080p/ddl/vcd_ddl.c
+++ b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2012, Code Aurora Forum. 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
@@ -491,9 +491,6 @@
struct ddl_encoder_data *encoder =
&ddl->codec_data.encoder;
u32 vcd_status = VCD_S_SUCCESS;
- struct vcd_transc *transc;
- transc = (struct vcd_transc *)(ddl->client_data);
- DDL_MSG_LOW("%s: transc = 0x%x", __func__, (u32)ddl->client_data);
if (encoder->slice_delivery_info.enable) {
return ddl_encode_frame_batch(ddl_handle,
input_frame,
diff --git a/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_helper.c b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_helper.c
index 729fb2e..77faee3 100644
--- a/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_helper.c
+++ b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_helper.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2010-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
@@ -506,6 +506,8 @@
width_round_up = width;
height_round_up = height;
+ align = SZ_4K;
+
if (format == DDL_YUV_BUF_TYPE_TILE) {
width_round_up = DDL_ALIGN(width, DDL_TILE_ALIGN_WIDTH);
height_round_up = DDL_ALIGN(height, DDL_TILE_ALIGN_HEIGHT);
diff --git a/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_interrupt_handler.c b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_interrupt_handler.c
index 163af21..64cc570 100644
--- a/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_interrupt_handler.c
+++ b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_interrupt_handler.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2010-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
@@ -1811,8 +1811,10 @@
slice_output = (struct vidc_1080p_enc_slice_batch_out_param *)
(encoder->batch_frame.slice_batch_out.align_virtual_addr);
DDL_MSG_LOW(" after get no of slices = %d\n", num_slices_comp);
- if (slice_output == NULL)
+ if (slice_output == NULL) {
DDL_MSG_ERROR(" slice_output is NULL\n");
+ return; /* Bail out */
+ }
encoder->slice_delivery_info.num_slices_enc += num_slices_comp;
if (vidc_msg_timing) {
ddl_calc_core_proc_time_cnt(__func__, ENC_SLICE_OP_TIME,
diff --git a/drivers/video/msm/vidc/1080p/resource_tracker/vcd_res_tracker.c b/drivers/video/msm/vidc/1080p/resource_tracker/vcd_res_tracker.c
index d9cadef..c15218d 100644
--- a/drivers/video/msm/vidc/1080p/resource_tracker/vcd_res_tracker.c
+++ b/drivers/video/msm/vidc/1080p/resource_tracker/vcd_res_tracker.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2010-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
@@ -160,9 +160,13 @@
static void res_trk_pmem_free(struct ddl_buf_addr *addr)
{
struct ddl_context *ddl_context;
+
+ if (!addr)
+ return;
+
ddl_context = ddl_get_context();
if (ddl_context->video_ion_client) {
- if (addr && addr->alloc_handle) {
+ if (addr->alloc_handle) {
ion_free(ddl_context->video_ion_client,
addr->alloc_handle);
addr->alloc_handle = NULL;
diff --git a/drivers/video/msm/vidc/common/dec/vdec.c b/drivers/video/msm/vidc/common/dec/vdec.c
index 48f127a..afc5130 100644
--- a/drivers/video/msm/vidc/common/dec/vdec.c
+++ b/drivers/video/msm/vidc/common/dec/vdec.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2010-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
@@ -2485,7 +2485,7 @@
}
client_index = vid_dec_get_empty_client_index();
- if (client_index == -1) {
+ if (client_index < 0) {
ERR("%s() : No free clients client_index == -1\n", __func__);
rc = -ENOMEM;
goto client_failure;
diff --git a/drivers/video/msm/vidc/common/enc/venc.c b/drivers/video/msm/vidc/common/enc/venc.c
index c05e568..0648257 100644
--- a/drivers/video/msm/vidc/common/enc/venc.c
+++ b/drivers/video/msm/vidc/common/enc/venc.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2012, Code Aurora Forum. 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
@@ -35,6 +35,7 @@
#include "vcd_res_tracker_api.h"
#define VID_ENC_NAME "msm_vidc_enc"
+static char *node_name[2] = {"", "_sec"};
#if DEBUG
#define DBG(x...) printk(KERN_DEBUG x)
@@ -50,7 +51,6 @@
static struct class *vid_enc_class;
static long vid_enc_ioctl(struct file *file,
unsigned cmd, unsigned long arg);
-static int stop_cmd;
static s32 vid_enc_get_empty_client_index(void)
{
@@ -483,7 +483,7 @@
mutex_lock(&vid_enc_device_p->lock);
- if (!stop_cmd) {
+ if (!client_ctx->stop_called) {
vcd_status = vcd_stop(client_ctx->vcd_handle);
DBG("Waiting for VCD_STOP: Before Timeout\n");
if (!vcd_status) {
@@ -520,13 +520,13 @@
sizeof(struct video_client_ctx));
vid_enc_device_p->num_clients--;
- stop_cmd = 0;
+ client_ctx->stop_called = 0;
mutex_unlock(&vid_enc_device_p->lock);
return true;
}
-
-static int vid_enc_open(struct inode *inode, struct file *file)
+static int vid_enc_open_client(struct video_client_ctx **vid_clnt_ctx,
+ int flags)
{
s32 client_index;
struct video_client_ctx *client_ctx;
@@ -534,30 +534,34 @@
u8 client_count = 0;
INFO("\n msm_vidc_enc: Inside %s()", __func__);
-
- mutex_lock(&vid_enc_device_p->lock);
-
- stop_cmd = 0;
+ if (!vid_clnt_ctx) {
+ ERR("Invalid input\n");
+ rc = -EINVAL;
+ goto client_failure;
+ }
+ *vid_clnt_ctx = NULL;
client_count = vcd_get_num_of_clients();
if (client_count == VIDC_MAX_NUM_CLIENTS) {
- ERR("ERROR : vid_enc_open() max number of clients"
- "limit reached\n");
- mutex_unlock(&vid_enc_device_p->lock);
- return -ENODEV;
+ ERR("ERROR : vid_enc_open() max number of clients\n");
+ rc = -ENODEV;
+ goto client_failure;
}
DBG(" Virtual Address of ioremap is %p\n", vid_enc_device_p->virt_base);
if (!vid_enc_device_p->num_clients) {
- if (!vidc_load_firmware())
- return -ENODEV;
+ if (!vidc_load_firmware()) {
+ rc = -ENODEV;
+ goto client_failure;
+ }
}
client_index = vid_enc_get_empty_client_index();
- if (client_index == -1) {
+ if (client_index < 0) {
ERR("%s() : No free clients client_index == -1\n",
__func__);
- return -ENODEV;
+ rc = -ENODEV;
+ goto client_failure;
}
client_ctx =
@@ -573,27 +577,46 @@
client_ctx->user_ion_client = vcd_get_ion_client();
if (!client_ctx->user_ion_client) {
ERR("vcd_open ion get client failed");
- return -EFAULT;
+ rc = -EFAULT;
+ goto client_failure;
}
}
rc = vcd_open(vid_enc_device_p->device_handle, false,
- vid_enc_vcd_cb, client_ctx, 0);
+ vid_enc_vcd_cb, client_ctx, flags);
client_ctx->stop_msg = 0;
+ client_ctx->stop_called = 1;
if (!rc) {
wait_for_completion(&client_ctx->event);
if (client_ctx->event_status) {
ERR("callback for vcd_open returned error: %u",
client_ctx->event_status);
- mutex_unlock(&vid_enc_device_p->lock);
- return -EFAULT;
+ rc = -EFAULT;
+ goto client_failure;
}
} else {
ERR("vcd_open returned error: %u", rc);
- mutex_unlock(&vid_enc_device_p->lock);
- return rc;
+ goto client_failure;
}
- file->private_data = client_ctx;
+ *vid_clnt_ctx = client_ctx;
+client_failure:
+ return rc;
+}
+static int vid_enc_open(struct inode *inode, struct file *file)
+{
+ int rc = 0;
+ struct video_client_ctx *client_ctx = NULL;
+ INFO("msm_vidc_venc: Inside %s()", __func__);
+ mutex_lock(&vid_enc_device_p->lock);
+ rc = vid_enc_open_client(&client_ctx, 0);
+ if (rc)
+ pr_err("%s() open failed rc=%d\n", __func__, rc);
+ else if (!client_ctx) {
+ pr_err("%s() client_ctx is NULL\n", __func__);
+ rc = -ENOMEM;
+ }
+ if (!rc)
+ file->private_data = client_ctx;
mutex_unlock(&vid_enc_device_p->lock);
return rc;
}
@@ -610,12 +633,62 @@
INFO("\n msm_vidc_enc: Return from %s()", __func__);
return 0;
}
+static int vid_enc_open_secure(struct inode *inode, struct file *file)
+{
+ int rc = 0, vcd_status = 0;
+ struct video_client_ctx *client_ctx = NULL;
+ struct vcd_property_hdr vcd_property_hdr;
+ struct vcd_property_sps_pps_for_idr_enable idr_enable;
-static const struct file_operations vid_enc_fops = {
- .owner = THIS_MODULE,
- .open = vid_enc_open,
- .release = vid_enc_release,
- .unlocked_ioctl = vid_enc_ioctl,
+ INFO("msm_vidc_enc: Inside %s()", __func__);
+ mutex_lock(&vid_enc_device_p->lock);
+ rc = vid_enc_open_client(&client_ctx, VCD_CP_SESSION);
+ if (rc || !client_ctx) {
+ pr_err("%s() open failed rc=%d\n", __func__, rc);
+ if (!client_ctx)
+ rc = -ENOMEM;
+ goto error;
+ }
+ file->private_data = client_ctx;
+ vcd_property_hdr.prop_id = VCD_I_ENABLE_SPS_PPS_FOR_IDR;
+ vcd_property_hdr.sz =
+ sizeof(struct vcd_property_sps_pps_for_idr_enable);
+ idr_enable.sps_pps_for_idr_enable_flag = 1;
+ vcd_status = vcd_set_property(client_ctx->vcd_handle,
+ &vcd_property_hdr, &idr_enable);
+ if (vcd_status) {
+ ERR("Setting SPS with IDR failed\n");
+ rc = -EACCES;
+ goto close_client;
+ }
+
+ if (res_trk_open_secure_session()) {
+ rc = -EACCES;
+ goto close_client;
+ }
+ mutex_unlock(&vid_enc_device_p->lock);
+ return rc;
+
+close_client:
+ vid_enc_close_client(client_ctx);
+ ERR("Secure session operation failure\n");
+error:
+ mutex_unlock(&vid_enc_device_p->lock);
+ return rc;
+}
+static const struct file_operations vid_enc_fops[NUM_OF_DRIVER_NODES] = {
+ {
+ .owner = THIS_MODULE,
+ .open = vid_enc_open,
+ .release = vid_enc_release,
+ .unlocked_ioctl = vid_enc_ioctl,
+ },
+ {
+ .owner = THIS_MODULE,
+ .open = vid_enc_open_secure,
+ .release = vid_enc_release,
+ .unlocked_ioctl = vid_enc_ioctl,
+ },
};
void vid_enc_interrupt_deregister(void)
@@ -680,7 +753,7 @@
static int __init vid_enc_init(void)
{
- int rc = 0;
+ int rc = 0, i = 0, j = 0;
struct device *class_devp;
INFO("\n msm_vidc_enc: Inside %s()", __func__);
@@ -691,14 +764,13 @@
__func__);
return -ENOMEM;
}
-
- rc = alloc_chrdev_region(&vid_enc_dev_num, 0, 1, VID_ENC_NAME);
+ rc = alloc_chrdev_region(&vid_enc_dev_num, 0, NUM_OF_DRIVER_NODES,
+ VID_ENC_NAME);
if (rc < 0) {
ERR("%s: alloc_chrdev_region Failed rc = %d\n",
__func__, rc);
goto error_vid_enc_alloc_chrdev_region;
}
-
vid_enc_class = class_create(THIS_MODULE, VID_ENC_NAME);
if (IS_ERR(vid_enc_class)) {
rc = PTR_ERR(vid_enc_class);
@@ -706,32 +778,40 @@
__func__, rc);
goto error_vid_enc_class_create;
}
+ for (i = 0; i < NUM_OF_DRIVER_NODES; i++) {
+ class_devp = device_create(vid_enc_class, NULL,
+ (vid_enc_dev_num + i), NULL,
+ VID_ENC_NAME "%s", node_name[i]);
- class_devp = device_create(vid_enc_class, NULL,
- vid_enc_dev_num, NULL, VID_ENC_NAME);
+ if (IS_ERR(class_devp)) {
+ rc = PTR_ERR(class_devp);
+ ERR("%s: class device_create failed %d\n",
+ __func__, rc);
+ if (!i)
+ goto error_vid_enc_class_device_create;
+ else
+ goto error_vid_enc_cdev_add;
+ }
- if (IS_ERR(class_devp)) {
- rc = PTR_ERR(class_devp);
- ERR("%s: class device_create failed %d\n",
- __func__, rc);
- goto error_vid_enc_class_device_create;
- }
+ vid_enc_device_p->device[i] = class_devp;
- vid_enc_device_p->device = class_devp;
+ cdev_init(&vid_enc_device_p->cdev[i], &vid_enc_fops[i]);
+ vid_enc_device_p->cdev[i].owner = THIS_MODULE;
+ rc = cdev_add(&(vid_enc_device_p->cdev[i]),
+ (vid_enc_dev_num + i), 1);
- cdev_init(&vid_enc_device_p->cdev, &vid_enc_fops);
- vid_enc_device_p->cdev.owner = THIS_MODULE;
- rc = cdev_add(&(vid_enc_device_p->cdev), vid_enc_dev_num, 1);
-
- if (rc < 0) {
- ERR("%s: cdev_add failed %d\n",
- __func__, rc);
- goto error_vid_enc_cdev_add;
+ if (rc < 0) {
+ ERR("%s: cdev_add failed %d\n",
+ __func__, rc);
+ goto error_vid_enc_cdev_add;
+ }
}
vid_enc_vcd_init();
return 0;
error_vid_enc_cdev_add:
+ for (j = i-1; j >= 0; j--)
+ cdev_del(&(vid_enc_device_p->cdev[j]));
device_destroy(vid_enc_class, vid_enc_dev_num);
error_vid_enc_class_device_create:
class_destroy(vid_enc_class);
@@ -745,8 +825,10 @@
static void __exit vid_enc_exit(void)
{
+ int i = 0;
INFO("\n msm_vidc_enc: Inside %s()", __func__);
- cdev_del(&(vid_enc_device_p->cdev));
+ for (i = 0; i < NUM_OF_DRIVER_NODES; i++)
+ cdev_del(&(vid_enc_device_p->cdev[i]));
device_destroy(vid_enc_class, vid_enc_dev_num);
class_destroy(vid_enc_class);
unregister_chrdev_region(vid_enc_dev_num, 1);
@@ -944,7 +1026,8 @@
if (!result) {
ERR("setting VEN_IOCTL_CMD_START failed\n");
return -EIO;
- }
+ } else
+ client_ctx->stop_called = 0;
break;
}
case VEN_IOCTL_CMD_STOP:
@@ -955,7 +1038,7 @@
ERR("setting VEN_IOCTL_CMD_STOP failed\n");
return -EIO;
}
- stop_cmd = 1;
+ client_ctx->stop_called = 1;
break;
}
case VEN_IOCTL_CMD_PAUSE:
diff --git a/drivers/video/msm/vidc/common/enc/venc_internal.h b/drivers/video/msm/vidc/common/enc/venc_internal.h
index 17cefbb..b7b8c98 100644
--- a/drivers/video/msm/vidc/common/enc/venc_internal.h
+++ b/drivers/video/msm/vidc/common/enc/venc_internal.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2012, Code Aurora Forum. 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
@@ -18,6 +18,7 @@
#include <linux/cdev.h>
#include <media/msm/vidc_init.h>
+#define NUM_OF_DRIVER_NODES 2
#define VID_ENC_MAX_NUM_OF_BUFF 100
enum venc_buffer_dir{
@@ -32,8 +33,8 @@
struct vid_enc_dev {
- struct cdev cdev;
- struct device *device;
+ struct cdev cdev[NUM_OF_DRIVER_NODES];
+ struct device *device[NUM_OF_DRIVER_NODES];
resource_size_t phys_base;
void __iomem *virt_base;
unsigned int irq;
diff --git a/drivers/video/msm/vidc/common/vcd/vcd_api.c b/drivers/video/msm/vidc/common/vcd/vcd_api.c
index 0dbbf57..3d7474f 100644
--- a/drivers/video/msm/vidc/common/vcd/vcd_api.c
+++ b/drivers/video/msm/vidc/common/vcd/vcd_api.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2012, Code Aurora Forum. 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
@@ -117,36 +117,20 @@
int is_secure;
struct client_security_info sec_info;
int client_count = 0;
- int secure_session_running = 0;
+ int secure_session_running = 0, non_secure_runnung = 0;
is_secure = (flags & VCD_CP_SESSION) ? 1 : 0;
client_count = vcd_get_clients_security_info(&sec_info);
secure_session_running = (sec_info.secure_enc > 0) ||
(sec_info.secure_dec > 0);
- if (!decoding && is_secure) {
- if ((sec_info.secure_dec == 1))
- VCD_MSG_LOW("SE-SD: SUCCESS\n");
- else {
- VCD_MSG_LOW("SE is permitted only with SD: FAILURE\n");
- return -EACCES;
- }
- } else if (!decoding && !is_secure) {
+ non_secure_runnung = sec_info.non_secure_dec + sec_info.non_secure_enc;
+ if (!is_secure) {
if (secure_session_running) {
- VCD_MSG_LOW("SD-NSE: FAILURE\n");
- VCD_MSG_LOW("SE-NSE: FAILURE\n");
- return -EACCES;
- }
- } else if (decoding && is_secure) {
- if (client_count > 0) {
- VCD_MSG_LOW("S/NS-SD: FAILURE\n");
- if (sec_info.secure_enc > 0 ||
- sec_info.non_secure_enc > 0) {
- return -EAGAIN;
- }
+ pr_err("non secure session failed secure running\n");
return -EACCES;
}
} else {
- if (sec_info.secure_dec > 0) {
- VCD_MSG_LOW("SD-NSD: FAILURE\n");
+ if (non_secure_runnung) {
+ pr_err("Secure session failed non secure running\n");
return -EACCES;
}
}
diff --git a/drivers/video/msm/vidc/common/vcd/vcd_client_sm.c b/drivers/video/msm/vidc/common/vcd/vcd_client_sm.c
index 884050b..14c8030 100644
--- a/drivers/video/msm/vidc/common/vcd/vcd_client_sm.c
+++ b/drivers/video/msm/vidc/common/vcd/vcd_client_sm.c
@@ -1628,6 +1628,7 @@
if (!cctxt || to_state >= VCD_CLIENT_STATE_MAX) {
VCD_MSG_ERROR("Bad parameters. cctxt=%p, to_state=%d",
cctxt, to_state);
+ return;
}
state_ctxt = &cctxt->clnt_state;
diff --git a/drivers/video/msm/vidc/common/vcd/vcd_device_sm.c b/drivers/video/msm/vidc/common/vcd/vcd_device_sm.c
index f670a4a..9074358 100644
--- a/drivers/video/msm/vidc/common/vcd/vcd_device_sm.c
+++ b/drivers/video/msm/vidc/common/vcd/vcd_device_sm.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2010-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
@@ -36,6 +36,7 @@
if (!drv_ctxt || to_state >= VCD_DEVICE_STATE_MAX) {
VCD_MSG_ERROR("Bad parameters. drv_ctxt=%p, to_state=%d",
drv_ctxt, to_state);
+ return;
}
state_ctxt = &drv_ctxt->dev_state;
diff --git a/drivers/video/msm/vidc/common/vcd/vcd_scheduler.c b/drivers/video/msm/vidc/common/vcd/vcd_scheduler.c
index ab21bac..fe0e131 100644
--- a/drivers/video/msm/vidc/common/vcd/vcd_scheduler.c
+++ b/drivers/video/msm/vidc/common/vcd/vcd_scheduler.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2010-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
@@ -88,8 +88,13 @@
prop_hdr.sz = sizeof(cctxt->frm_p_units);
rc = ddl_get_property(cctxt->ddl_handle, &prop_hdr,
&cctxt->frm_p_units);
- VCD_FAILED_RETURN(rc,
- "Failed: Get DDL_I_FRAME_PROC_UNITS");
+ if (VCD_FAILED(rc)) {
+ kfree(sched_cctxt);
+ VCD_MSG_ERROR(
+ "Failed: Get DDL_I_FRAME_PROC_UNITS");
+ return rc;
+ }
+
if (cctxt->decoding) {
cctxt->frm_rate.fps_numerator =
VCD_DEC_INITIAL_FRAME_RATE;
@@ -99,8 +104,12 @@
prop_hdr.sz = sizeof(cctxt->frm_rate);
rc = ddl_get_property(cctxt->ddl_handle,
&prop_hdr, &cctxt->frm_rate);
- VCD_FAILED_RETURN(rc,
- "Failed: Get VCD_I_FRAME_RATE");
+ if (VCD_FAILED(rc)) {
+ kfree(sched_cctxt);
+ VCD_MSG_ERROR(
+ "Failed: Get VCD_I_FRAME_RATE");
+ return rc;
+ }
}
if (!cctxt->perf_set_by_client)
cctxt->reqd_perf_lvl = cctxt->frm_p_units *
diff --git a/drivers/video/msm/vidc/common/vcd/vcd_sub.c b/drivers/video/msm/vidc/common/vcd/vcd_sub.c
index 78d77d1..09cd91d 100644
--- a/drivers/video/msm/vidc/common/vcd/vcd_sub.c
+++ b/drivers/video/msm/vidc/common/vcd/vcd_sub.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2010-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
@@ -2022,6 +2022,11 @@
orig_frame = vcd_find_buffer_pool_entry(&cctxt->in_buf_pool,
transc->ip_buf_entry->virtual);
+ if (!orig_frame) {
+ rc = VCD_ERR_ILLEGAL_PARM;
+ VCD_FAILED_RETURN(rc, "Couldn't find buffer");
+ }
+
if ((transc->ip_buf_entry->frame.virtual !=
frame->vcd_frm.virtual)
|| !transc->ip_buf_entry->in_use) {
diff --git a/include/linux/coresight.h b/include/linux/coresight.h
index 0bdacffa..b167b44 100644
--- a/include/linux/coresight.h
+++ b/include/linux/coresight.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
@@ -30,7 +30,11 @@
#define CORESIGHT_COMPIDR2 (0xFF8)
#define CORESIGHT_COMPIDR3 (0xFFC)
+#define ETM_ARCH_V1_0 (0x00)
+#define ETM_ARCH_V1_2 (0x02)
#define ETM_ARCH_V3_3 (0x23)
+#define ETM_ARCH_V3_5 (0x25)
+#define PFT_ARCH_MAJOR (0x30)
#define PFT_ARCH_V1_1 (0x31)
enum coresight_clk_rate {
diff --git a/include/linux/diagchar.h b/include/linux/diagchar.h
index 43daaf2..4bd8885 100644
--- a/include/linux/diagchar.h
+++ b/include/linux/diagchar.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2008-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
@@ -112,7 +112,7 @@
/* This needs to be modified manually now, when we add
a new RANGE of SSIDs to the msg_mask_tbl */
#define MSG_MASK_TBL_CNT 24
-#define EVENT_LAST_ID 0x08C5
+#define EVENT_LAST_ID 0x099F
#define MSG_SSID_0 0
#define MSG_SSID_0_LAST 93
@@ -692,7 +692,7 @@
/* LOG CODES */
#define LOG_0 0x0
-#define LOG_1 0x1636
+#define LOG_1 0x1750
#define LOG_2 0x0
#define LOG_3 0x0
#define LOG_4 0x4910
diff --git a/include/linux/dvb/dmx.h b/include/linux/dvb/dmx.h
index 460cba3..53bbd5e 100644
--- a/include/linux/dvb/dmx.h
+++ b/include/linux/dvb/dmx.h
@@ -5,7 +5,7 @@
* & Ralph Metzler <ralph@convergence.de>
* for convergence integrated media GmbH
*
- * Copyright (c) 2012, Code Aurora Forum. 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 Lesser General Public License
@@ -250,6 +250,18 @@
/* Flags passed in filter events */
__u32 flags;
+
+ /*
+ * Number of TS packets with Transport Error Indicator (TEI)
+ * found while constructing the PES.
+ */
+ __u32 transport_error_indicator_counter;
+
+ /* Number of continuity errors found while constructing the PES */
+ __u32 continuity_error_counter;
+
+ /* Total number of TS packets holding the PES */
+ __u32 ts_packets_num;
};
/* Section info associated with DMX_EVENT_NEW_SECTION event */
diff --git a/include/linux/fb.h b/include/linux/fb.h
index f6a2923..d31cb68 100644
--- a/include/linux/fb.h
+++ b/include/linux/fb.h
@@ -279,7 +279,7 @@
__u32 vmode; /* see FB_VMODE_* */
__u32 rotate; /* angle we rotate counter clockwise */
__u32 colorspace; /* colorspace for FOURCC-based modes */
- __u32 reserved[5]; /* Reserved for future compatibility */
+ __u32 reserved[4]; /* Reserved for future compatibility */
};
struct fb_cmap {
diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h
index 8260ef7..4effce6 100644
--- a/include/linux/ipv6.h
+++ b/include/linux/ipv6.h
@@ -172,6 +172,7 @@
__s32 disable_ipv6;
__s32 accept_dad;
__s32 force_tllao;
+ __s32 accept_ra_prefix_route;
void *sysctl;
};
@@ -213,6 +214,7 @@
DEVCONF_DISABLE_IPV6,
DEVCONF_ACCEPT_DAD,
DEVCONF_FORCE_TLLAO,
+ DEVCONF_ACCEPT_RA_PREFIX_ROUTE,
DEVCONF_MAX
};
diff --git a/include/linux/mfd/wcd9xxx/core.h b/include/linux/mfd/wcd9xxx/core.h
index 4b2ad66..458f060 100644
--- a/include/linux/mfd/wcd9xxx/core.h
+++ b/include/linux/mfd/wcd9xxx/core.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, Code Aurora Forum. 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
@@ -148,6 +148,7 @@
struct mutex io_lock;
struct mutex xfer_lock;
struct mutex irq_lock;
+ struct mutex nested_irq_lock;
u8 version;
int reset_gpio;
@@ -156,6 +157,10 @@
int bytes, void *dest, bool interface_reg);
int (*write_dev)(struct wcd9xxx *wcd9xxx, unsigned short reg,
int bytes, void *src, bool interface_reg);
+ int (*post_reset)(struct wcd9xxx *wcd9xxx);
+
+ void *ssr_priv;
+ bool slim_device_bootup;
u32 num_of_supplies;
struct regulator_bulk_data *supplies;
@@ -200,6 +205,8 @@
bool wcd9xxx_lock_sleep(struct wcd9xxx *wcd9xxx);
void wcd9xxx_unlock_sleep(struct wcd9xxx *wcd9xxx);
+void wcd9xxx_nested_irq_lock(struct wcd9xxx *wcd9xxx);
+void wcd9xxx_nested_irq_unlock(struct wcd9xxx *wcd9xxx);
enum wcd9xxx_pm_state wcd9xxx_pm_cmpxchg(struct wcd9xxx *wcd9xxx,
enum wcd9xxx_pm_state o,
enum wcd9xxx_pm_state n);
diff --git a/include/linux/mfd/wcd9xxx/wcd9310_registers.h b/include/linux/mfd/wcd9xxx/wcd9310_registers.h
index 67c2a6b..46336e2 100644
--- a/include/linux/mfd/wcd9xxx/wcd9310_registers.h
+++ b/include/linux/mfd/wcd9xxx/wcd9310_registers.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. 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
@@ -898,37 +898,37 @@
#define TABLA_A_CDC_DEBUG_B6_CTL (0x0000036D)
#define TABLA_A_CDC_DEBUG_B6_CTL__POR (0x00000000)
#define TABLA_A_CDC_COMP1_B1_CTL (0x00000370)
-#define TABLA_A_CDC_COMP1_B1_CTL__POR (0x00000000)
+#define TABLA_A_CDC_COMP1_B1_CTL__POR (0x00000030)
#define TABLA_A_CDC_COMP1_B2_CTL (0x00000371)
-#define TABLA_A_CDC_COMP1_B2_CTL__POR (0x00000000)
+#define TABLA_A_CDC_COMP1_B2_CTL__POR (0x000000B5)
#define TABLA_A_CDC_COMP1_B3_CTL (0x00000372)
-#define TABLA_A_CDC_COMP1_B3_CTL__POR (0x00000000)
+#define TABLA_A_CDC_COMP1_B3_CTL__POR (0x00000028)
#define TABLA_A_CDC_COMP1_B4_CTL (0x00000373)
-#define TABLA_A_CDC_COMP1_B4_CTL__POR (0x00000000)
+#define TABLA_A_CDC_COMP1_B4_CTL__POR (0x0000003C)
#define TABLA_A_CDC_COMP1_B5_CTL (0x00000374)
-#define TABLA_A_CDC_COMP1_B5_CTL__POR (0x00000000)
+#define TABLA_A_CDC_COMP1_B5_CTL__POR (0x0000001F)
#define TABLA_A_CDC_COMP1_B6_CTL (0x00000375)
#define TABLA_A_CDC_COMP1_B6_CTL__POR (0x00000000)
#define TABLA_A_CDC_COMP1_SHUT_DOWN_STATUS (0x00000376)
#define TABLA_A_CDC_COMP1_SHUT_DOWN_STATUS__POR (0x00000000)
#define TABLA_A_CDC_COMP1_FS_CFG (0x00000377)
-#define TABLA_A_CDC_COMP1_FS_CFG__POR (0x00000000)
+#define TABLA_A_CDC_COMP1_FS_CFG__POR (0x0000001B)
#define TABLA_A_CDC_COMP2_B1_CTL (0x00000378)
-#define TABLA_A_CDC_COMP2_B1_CTL__POR (0x00000000)
+#define TABLA_A_CDC_COMP2_B1_CTL__POR (0x00000030)
#define TABLA_A_CDC_COMP2_B2_CTL (0x00000379)
-#define TABLA_A_CDC_COMP2_B2_CTL__POR (0x00000000)
+#define TABLA_A_CDC_COMP2_B2_CTL__POR (0x000000B5)
#define TABLA_A_CDC_COMP2_B3_CTL (0x0000037A)
-#define TABLA_A_CDC_COMP2_B3_CTL__POR (0x00000000)
+#define TABLA_A_CDC_COMP2_B3_CTL__POR (0x00000028)
#define TABLA_A_CDC_COMP2_B4_CTL (0x0000037B)
-#define TABLA_A_CDC_COMP2_B4_CTL__POR (0x00000000)
+#define TABLA_A_CDC_COMP2_B4_CTL__POR (0x0000003C)
#define TABLA_A_CDC_COMP2_B5_CTL (0x0000037C)
-#define TABLA_A_CDC_COMP2_B5_CTL__POR (0x00000000)
+#define TABLA_A_CDC_COMP2_B5_CTL__POR (0x0000001F)
#define TABLA_A_CDC_COMP2_B6_CTL (0x0000037D)
#define TABLA_A_CDC_COMP2_B6_CTL__POR (0x00000000)
#define TABLA_A_CDC_COMP2_SHUT_DOWN_STATUS (0x0000037E)
#define TABLA_A_CDC_COMP2_SHUT_DOWN_STATUS__POR (0x00000000)
#define TABLA_A_CDC_COMP2_FS_CFG (0x0000037F)
-#define TABLA_A_CDC_COMP2_FS_CFG__POR (0x00000000)
+#define TABLA_A_CDC_COMP2_FS_CFG__POR (0x0000001B)
#define TABLA_A_CDC_CONN_RX1_B1_CTL (0x00000380)
#define TABLA_A_CDC_CONN_RX1_B1_CTL__POR (0x00000000)
#define TABLA_A_CDC_CONN_RX1_B2_CTL (0x00000381)
diff --git a/include/linux/mhl_8334.h b/include/linux/mhl_8334.h
index d3597dc..cb74b73 100644
--- a/include/linux/mhl_8334.h
+++ b/include/linux/mhl_8334.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. 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
@@ -18,6 +18,7 @@
#include <linux/platform_device.h>
#include <mach/board.h>
#include <linux/mhl_devcap.h>
+#include <linux/power_supply.h>
#include <linux/mhl_defs.h>
#define MHL_DEVICE_NAME "sii8334"
@@ -96,6 +97,65 @@
struct msc_command_struct* (*msc_command_get_work) (void);
};
+#ifdef CONFIG_FB_MSM_MDSS_HDMI_MHL_SII8334
+enum mhl_gpio_type {
+ MHL_TX_RESET_GPIO,
+ MHL_TX_INTR_GPIO,
+ MHL_TX_PMIC_PWR_GPIO,
+ MHL_TX_MAX_GPIO,
+};
+
+enum mhl_vreg_type {
+ MHL_TX_3V_VREG,
+ MHL_TX_MAX_VREG,
+};
+
+
+struct mhl_tx_platform_data {
+ /* Data filled from device tree nodes */
+ struct dss_gpio *gpios[MHL_TX_MAX_GPIO];
+ struct dss_vreg *vregs[MHL_TX_MAX_VREG];
+ int irq;
+};
+
+struct mhl_tx_ctrl {
+ struct platform_device *pdev;
+ struct mhl_tx_platform_data *pdata;
+ struct i2c_client *i2c_handle;
+ uint8_t cur_state;
+ uint8_t chip_rev_id;
+ int mhl_mode;
+ struct completion rgnd_done;
+ void (*notify_usb_online)(int online);
+ struct usb_ext_notification *mhl_info;
+ bool disc_enabled;
+ struct power_supply mhl_psy;
+ bool vbus_active;
+ int current_val;
+ struct completion msc_cmd_done;
+ uint8_t devcap[16];
+ uint8_t devcap_state;
+ uint8_t path_en_state;
+ uint8_t (*tmds_enabled)(void);
+ struct work_struct mhl_msc_send_work;
+ struct list_head list_cmd;
+ struct input_dev *input;
+ struct workqueue_struct *msc_send_workqueue;
+ u16 *rcp_key_code_tbl;
+ size_t rcp_key_code_tbl_len;
+};
+
+int mhl_i2c_reg_read(struct i2c_client *client,
+ uint8_t slave_addr_index, uint8_t reg_offset);
+int mhl_i2c_reg_write(struct i2c_client *client,
+ uint8_t slave_addr_index, uint8_t reg_offset,
+ uint8_t value);
+void mhl_i2c_reg_modify(struct i2c_client *client,
+ uint8_t slave_addr_index, uint8_t reg_offset,
+ uint8_t mask, uint8_t val);
+
+#endif /* CONFIG_FB_MSM_MDSS_HDMI_MHL_SII8334 */
+
enum {
TX_PAGE_TPI = 0x00,
TX_PAGE_L0 = 0x01,
@@ -204,6 +264,7 @@
#define REG_TMDS_CSTAT ((TX_PAGE_3 << 16) | 0x0040)
+#define REG_CBUS_INTR_STATUS ((TX_PAGE_CBUS << 16) | 0x0008)
#define REG_CBUS_INTR_ENABLE ((TX_PAGE_CBUS << 16) | 0x0009)
#define REG_DDC_ABORT_REASON ((TX_PAGE_CBUS << 16) | 0x000B)
diff --git a/include/linux/mhl_defs.h b/include/linux/mhl_defs.h
index 062bdf9..f23be79 100644
--- a/include/linux/mhl_defs.h
+++ b/include/linux/mhl_defs.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. 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
@@ -149,6 +149,7 @@
#define MHL_RCPE_NO_ERROR 0x00
#define MHL_RCPE_UNSUPPORTED_KEY_CODE 0x01
+#define MHL_RCPE_INEFFECTIVE_KEY_CODE 0x01
#define MHL_RCPE_BUSY 0x02
#define MHL_RAPK_NO_ERROR 0x00
@@ -156,6 +157,8 @@
#define MHL_RAPK_UNSUPPORTED_ACTION_CODE 0x02
#define MHL_RAPK_BUSY 0x03
+#define T_ABORT_NEXT (2050)
+
/* MHL spec related defines*/
enum {
/* Command or Data byte acknowledge */
@@ -196,6 +199,8 @@
MHL_GET_SC3_ERRORCODE = 0x6D,
};
+/* Polling. */
+#define MHL_RAP_POLL 0x00
/* Turn content streaming ON. */
#define MHL_RAP_CONTENT_ON 0x10
/* Turn content streaming OFF. */
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index 4d004c2..b362d7a 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -306,31 +306,6 @@
#define BKOPS_SIZE_PERCENTAGE_TO_QUEUE_DELAYED_WORK 1 /* 1% */
};
-/**
- * struct mmc_async_event_stats - async events stats data
- *
- * @enabled A boolean indicating if the stats are initiated
- * and enabled
- * The rest of the members in this struct are counters which are
- * incremented at strategic locations in the async events flows.
- */
-struct mmc_async_event_stats {
- bool enabled;
- u32 cmd_retry;
- u32 new_request_flag;
- u32 null_fetched;
- u32 wakeup_new;
- u32 q_no_waiting;
- u32 done_flag;
- u32 no_mmc_request_action;
- u32 wakeup_mq_thread;
- u32 fetch_due_to_new_req;
- u32 returned_new_req;
- u32 done_when_new_req_event_on;
- u32 new_req_when_new_marked;
- bool print_in_read;
-};
-
/*
* MMC device
*/
@@ -401,12 +376,11 @@
struct dentry *debugfs_root;
struct mmc_part part[MMC_NUM_PHY_PARTITION]; /* physical partitions */
unsigned int nr_parts;
+ unsigned int part_curr;
struct mmc_wr_pack_stats wr_pack_stats; /* packed commands stats*/
struct mmc_bkops_info bkops_info;
- /* async events flow stats */
- struct mmc_async_event_stats async_event_stats;
};
/*
@@ -637,5 +611,5 @@
extern struct mmc_wr_pack_stats *mmc_blk_get_packed_statistics(
struct mmc_card *card);
extern void mmc_blk_init_packed_statistics(struct mmc_card *card);
-extern void mmc_blk_init_async_event_statistics(struct mmc_card *card);
+
#endif /* LINUX_MMC_CARD_H */
diff --git a/include/linux/mmc/ioctl.h b/include/linux/mmc/ioctl.h
index 1f5e689..befbdc2 100644
--- a/include/linux/mmc/ioctl.h
+++ b/include/linux/mmc/ioctl.h
@@ -47,6 +47,60 @@
#define MMC_IOC_CMD _IOWR(MMC_BLOCK_MAJOR, 0, struct mmc_ioc_cmd)
+/**
+ * There are four request types that are applicable for rpmb accesses- two
+ * under read category and two under write. They are
+ *
+ * Reads
+ * -------
+ * 1. Read Write Counter
+ * 2. Authenticated data read
+ *
+ *
+ * Writes
+ * -------
+ * 1. Provision RPMB key (though it might be done in a secure environment)
+ * 2. Authenticated data write
+ *
+ * While its given that the rpmb data frames are going to have that
+ * information encoded in it and the frames should be generated by a secure
+ * piece of code, the request types can be classified as above.
+ *
+ * So here are the set of commands that should be executed atomically in the
+ * ioctl for rpmb read operation
+ * 1. Switch partition
+ * 2. Set block count
+ * 3. Write data frame - CMD25 to write the rpmb data frame
+ * 4. Set block count
+ * 5. Read the data - CMD18 to do the actual read
+ *
+ * Similarly for rpmb write operation, these are the commands that should be
+ * executed atomically in the ioctl for rpmb write operation
+ * 1. Switch partition
+ * 2. Set block count
+ * 3. Write data frame - CMD25 to write the rpmb data frame with data
+ * 4. Set block count
+ * 5. Read the data - CMD25 to write rpmb data frame indicating that rpmb
+ * result register is about to be read
+ * 6. Set block count
+ * 7. Read rpmb result - CMD18 to read the rpmb result register
+ *
+ * Each of the above commands should be sent individually via struct mmc_ioc_cmd
+ * and fields like is_acmd that are not needed for rpmb operations will be
+ * ignored.
+ */
+#define MMC_IOC_MAX_RPMB_CMD 3
+struct mmc_ioc_rpmb {
+ struct mmc_ioc_cmd cmds[MMC_IOC_MAX_RPMB_CMD];
+};
+
+/*
+ * This ioctl is meant for use with rpmb partitions. This is needed since the
+ * access procedure for this particular partition is different from regular
+ * or normal partitions.
+ */
+#define MMC_IOC_RPMB_CMD _IOWR(MMC_BLOCK_MAJOR, 0, struct mmc_ioc_rpmb)
+
/*
* Since this ioctl is only meant to enhance (and not replace) normal access
* to the mmc bus device, an upper data transfer limit of MMC_IOC_MAX_BYTES
diff --git a/include/linux/msm_ion.h b/include/linux/msm_ion.h
index d423b26..14492ea 100644
--- a/include/linux/msm_ion.h
+++ b/include/linux/msm_ion.h
@@ -1,6 +1,6 @@
/*
*
- * Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@@ -75,6 +75,13 @@
#define ION_SECURE (1 << ION_HEAP_ID_RESERVED)
/**
+ * Flag for clients to force contiguous memort allocation
+ *
+ * Use of this flag is carefully monitored!
+ */
+#define ION_FORCE_CONTIGUOUS (1 << 30)
+
+/**
* Macro should be used with ion_heap_ids defined above.
*/
#define ION_HEAP(bit) (1 << (bit))
diff --git a/include/linux/msm_mdp.h b/include/linux/msm_mdp.h
index 7e67db0..56eda83 100644
--- a/include/linux/msm_mdp.h
+++ b/include/linux/msm_mdp.h
@@ -1,7 +1,7 @@
/* include/linux/msm_mdp.h
*
* Copyright (C) 2007 Google Incorporated
- * Copyright (c) 2012 Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2012-2013 The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@@ -75,6 +75,7 @@
#define MSMFB_BUFFER_SYNC _IOW(MSMFB_IOCTL_MAGIC, 164, struct mdp_buf_sync)
#define MSMFB_DISPLAY_COMMIT _IOW(MSMFB_IOCTL_MAGIC, 165, \
struct mdp_display_commit)
+#define MSMFB_METADATA_GET _IOW(MSMFB_IOCTL_MAGIC, 166, struct msmfb_metadata)
#define FB_TYPE_3D_PANEL 0x10101010
#define MDP_IMGTYPE2_START 0x10000
@@ -285,6 +286,18 @@
#define MDP_PP_IGC_FLAG_ROM0 0x10
#define MDP_PP_IGC_FLAG_ROM1 0x20
+#define MDSS_PP_DSPP_CFG 0x0000
+#define MDSS_PP_SSPP_CFG 0x4000
+#define MDSS_PP_LM_CFG 0x8000
+#define MDSS_PP_WB_CFG 0xC000
+
+#define MDSS_PP_LOCATION_MASK 0xC000
+#define MDSS_PP_LOGICAL_MASK 0x3FFF
+
+#define PP_LOCAT(var) ((var) & MDSS_PP_LOCATION_MASK)
+#define PP_BLOCK(var) ((var) & MDSS_PP_LOGICAL_MASK)
+
+
struct mdp_qseed_cfg {
uint32_t table_num;
uint32_t ops;
@@ -555,6 +568,7 @@
enum {
metadata_op_none,
metadata_op_base_blend,
+ metadata_op_frame_rate,
metadata_op_max
};
@@ -567,6 +581,7 @@
uint32_t flags;
union {
struct mdp_blend_cfg blend_cfg;
+ uint32_t panel_frame_rate;
} data;
};
diff --git a/include/linux/qmi_encdec.h b/include/linux/qmi_encdec.h
index 4c5f6d3..b1fd217 100644
--- a/include/linux/qmi_encdec.h
+++ b/include/linux/qmi_encdec.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
@@ -150,6 +150,15 @@
int qmi_kernel_decode(struct msg_desc *desc, void *out_c_struct,
void *in_buf, uint32_t in_buf_len);
+/**
+ * qmi_verify_max_msg_len() - Verify the maximum length of a QMI message
+ * @desc: Pointer to structure descriptor.
+ *
+ * @return: true if the maximum message length embedded in structure
+ * descriptor matches the calculated value, else false.
+ */
+bool qmi_verify_max_msg_len(struct msg_desc *desc);
+
#else
static inline int qmi_kernel_encode(struct msg_desc *desc,
void *out_buf, uint32_t out_buf_len,
@@ -164,6 +173,11 @@
{
return -EOPNOTSUPP;
}
+
+static inline bool qmi_verify_max_msg_len(struct msg_desc *desc)
+{
+ return false;
+}
#endif
#endif
diff --git a/include/linux/qpnp/qpnp-adc.h b/include/linux/qpnp/qpnp-adc.h
index 3ab7b9d..903cc3f 100644
--- a/include/linux/qpnp/qpnp-adc.h
+++ b/include/linux/qpnp/qpnp-adc.h
@@ -1,5 +1,5 @@
/*
- * 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
@@ -1228,6 +1228,12 @@
int32_t qpnp_iadc_read(enum qpnp_iadc_channels channel,
struct qpnp_iadc_result *result);
/**
+ * qpnp_iadc_get_rsense() - Reads the RDS resistance value from the
+ trim registers.
+ * @rsense: RDS resistance in nOhms.
+ */
+int32_t qpnp_iadc_get_rsense(int32_t *rsense);
+/**
* qpnp_iadc_get_gain_and_offset() - Performs gain calibration
* over 17.8571mV and offset over selected
* channel. Channel can be internal rsense,
@@ -1314,6 +1320,8 @@
{ return -ENXIO; }
static inline int32_t qpnp_adc_tm_is_ready(void)
{ return -ENXIO; }
+static inline int32_t qpnp_iadc_get_rsense(int32_t *rsense)
+{ return -ENXIO; }
#endif
#endif
diff --git a/include/linux/remote_spinlock.h b/include/linux/remote_spinlock.h
index 8d7c7e7..e39846f 100644
--- a/include/linux/remote_spinlock.h
+++ b/include/linux/remote_spinlock.h
@@ -1,4 +1,5 @@
-/* Copyright (c) 2008-2009, 2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2008-2009, 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
@@ -89,6 +90,9 @@
#define remote_spin_release_all(pid) \
_remote_spin_release_all(pid)
+#define remote_spin_owner(lock) \
+ _remote_spin_owner(&((lock)->remote))
+
typedef struct {
struct mutex local;
_remote_mutex_t remote;
diff --git a/include/linux/sync.h b/include/linux/sync.h
index 4c00f04..31ba6ec 100644
--- a/include/linux/sync.h
+++ b/include/linux/sync.h
@@ -16,6 +16,7 @@
#include <linux/types.h>
#ifdef __KERNEL__
+#include <linux/kref.h>
#include <linux/ktime.h>
#include <linux/list.h>
#include <linux/spinlock.h>
@@ -40,14 +41,14 @@
* -1 if a will signabl before b
* @free_pt: called before sync_pt is freed
* @release_obj: called before sync_timeline is freed
- * @print_obj: print aditional debug information about sync_timeline.
- * should not print a newline
- * @print_pt: print aditional debug information about sync_pt.
- * should not print a newline
+ * @print_obj: deprecated
+ * @print_pt: deprecated
* @fill_driver_data: write implmentation specific driver data to data.
* should return an error if there is not enough room
* as specified by size. This information is returned
* to userspace by SYNC_IOC_FENCE_INFO.
+ * @timeline_value_str: fill str with the value of the sync_timeline's counter
+ * @pt_value_str: fill str with the value of the sync_pt
*/
struct sync_timeline_ops {
const char *driver_name;
@@ -67,19 +68,27 @@
/* optional */
void (*release_obj)(struct sync_timeline *sync_timeline);
- /* optional */
+ /* deprecated */
void (*print_obj)(struct seq_file *s,
struct sync_timeline *sync_timeline);
- /* optional */
+ /* deprecated */
void (*print_pt)(struct seq_file *s, struct sync_pt *sync_pt);
/* optional */
int (*fill_driver_data)(struct sync_pt *syncpt, void *data, int size);
+
+ /* optional */
+ void (*timeline_value_str)(struct sync_timeline *timeline, char *str,
+ int size);
+
+ /* optional */
+ void (*pt_value_str)(struct sync_pt *pt, char *str, int size);
};
/**
* struct sync_timeline - sync object
+ * @kref: reference count on fence.
* @ops: ops that define the implementaiton of the sync_timeline
* @name: name of the sync_timeline. Useful for debugging
* @destoryed: set when sync_timeline is destroyed
@@ -90,6 +99,7 @@
* @sync_timeline_list: membership in global sync_timeline_list
*/
struct sync_timeline {
+ struct kref kref;
const struct sync_timeline_ops *ops;
char name[32];
@@ -110,6 +120,7 @@
* @parent: sync_timeline to which this sync_pt belongs
* @child_list: membership in sync_timeline.child_list_head
* @active_list: membership in sync_timeline.active_list_head
+ * @signaled_list: membership in temorary signaled_list on stack
* @fence: sync_fence to which the sync_pt belongs
* @pt_list: membership in sync_fence.pt_list_head
* @status: 1: signaled, 0:active, <0: error
@@ -121,6 +132,7 @@
struct list_head child_list;
struct list_head active_list;
+ struct list_head signaled_list;
struct sync_fence *fence;
struct list_head pt_list;
@@ -134,6 +146,7 @@
/**
* struct sync_fence - sync fence
* @file: file representing this fence
+ * @kref: referenace count on fence.
* @name: name of sync_fence. Useful for debugging
* @pt_list_head: list of sync_pts in ths fence. immutable once fence
* is created
@@ -146,6 +159,7 @@
*/
struct sync_fence {
struct file *file;
+ struct kref kref;
char name[32];
/* this list is immutable once the fence is created */
@@ -323,8 +337,8 @@
* @fence: fence to wait on
* @tiemout: timeout in ms
*
- * Wait for @fence to be signaled or have an error. Waits indefintly
- * if @timeout = 0
+ * Wait for @fence to be signaled or have an error. Waits indefinitely
+ * if @timeout < 0
*/
int sync_fence_wait(struct sync_fence *fence, long timeout);
@@ -383,9 +397,9 @@
/**
* DOC: SYNC_IOC_WAIT - wait for a fence to signal
*
- * pass timeout in milliseconds.
+ * pass timeout in milliseconds. Waits indefinitely timeout < 0.
*/
-#define SYNC_IOC_WAIT _IOW(SYNC_IOC_MAGIC, 0, __u32)
+#define SYNC_IOC_WAIT _IOW(SYNC_IOC_MAGIC, 0, __s32)
/**
* DOC: SYNC_IOC_MERGE - merge two fences
diff --git a/include/linux/test-iosched.h b/include/linux/test-iosched.h
index b933069..3690160 100644
--- a/include/linux/test-iosched.h
+++ b/include/linux/test-iosched.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. 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
@@ -38,6 +38,7 @@
typedef char* (get_test_case_str_fn) (struct test_data *);
typedef void (blk_dev_test_init_fn) (void);
typedef void (blk_dev_test_exit_fn) (void);
+typedef struct gendisk* (get_rq_disk_fn) (void);
/**
* enum test_state - defines the state of the test
@@ -132,6 +133,8 @@
* @test_duration: A jiffies value saved for timing
* calculations
* @data: Test specific private data
+ * @test_byte_count: Total number of bytes dispatched in
+ * the test
*/
struct test_info {
int testcase;
@@ -142,7 +145,9 @@
post_test_fn *post_test_fn;
get_test_case_str_fn *get_test_case_str_fn;
unsigned long test_duration;
+ get_rq_disk_fn *get_rq_disk_fn;
void *data;
+ unsigned long test_byte_count;
};
/**
@@ -229,6 +234,7 @@
extern int test_iosched_start_test(struct test_info *t_info);
extern void test_iosched_mark_test_completion(void);
+extern void check_test_completion(void);
extern int test_iosched_add_unique_test_req(int is_err_expcted,
enum req_unique_type req_unique,
int start_sec, int nr_sects, rq_end_io_fn *end_req_io);
diff --git a/include/linux/usb/msm_hsusb.h b/include/linux/usb/msm_hsusb.h
index 9240277..c588420 100644
--- a/include/linux/usb/msm_hsusb.h
+++ b/include/linux/usb/msm_hsusb.h
@@ -412,6 +412,7 @@
unsigned int power_budget;
int pmic_gpio_dp_irq;
unsigned int dock_connect_irq;
+ bool use_sec_phy;
};
/**
@@ -473,6 +474,7 @@
* @active_conn_num: number of active pipe connections.
* @usb_base_address: BAM physical address.
* @ignore_core_reset_ack: BAM can ignore ACK from USB core during PIPE RESET
+ * @disable_clk_gating: Disable clock gating
*/
struct msm_usb_bam_platform_data {
struct usb_bam_pipe_connect *connections;
@@ -482,6 +484,7 @@
u32 usb_base_address;
bool ignore_core_reset_ack;
bool reset_on_connect[MAX_BAMS];
+ bool disable_clk_gating;
};
/**
diff --git a/include/linux/usb/msm_hsusb_hw.h b/include/linux/usb/msm_hsusb_hw.h
index 6e36f56..900fc00 100644
--- a/include/linux/usb/msm_hsusb_hw.h
+++ b/include/linux/usb/msm_hsusb_hw.h
@@ -28,6 +28,7 @@
#define USB_OTGSC (MSM_USB_BASE + 0x01A4)
#define USB_USBMODE (MSM_USB_BASE + 0x01A8)
#define USB_PHY_CTRL (MSM_USB_BASE + 0x0240)
+#define USB_PHY_CTRL2 (MSM_USB_BASE + 0x0278)
#define USBCMD_RESET 2
#define USB_USBINTR (MSM_USB_BASE + 0x0148)
diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h
index bc25e24..9d38db32 100644
--- a/include/linux/videodev2.h
+++ b/include/linux/videodev2.h
@@ -1498,6 +1498,7 @@
V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE = 0,
V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_MB = 1,
V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES = 2,
+ V4L2_MPEG_VIDEO_MULTI_SLICE_GOB = 3,
};
#define V4L2_CID_MPEG_VIDEO_VBV_SIZE (V4L2_CID_MPEG_BASE+222)
#define V4L2_CID_MPEG_VIDEO_DEC_PTS (V4L2_CID_MPEG_BASE+223)
@@ -1845,7 +1846,8 @@
V4L2_CID_MPEG_VIDC_PERF_LEVEL_PERFORMANCE = 0,
V4L2_CID_MPEG_VIDC_PERF_LEVEL_TURBO = 1,
};
-
+#define V4L2_CID_MPEG_VIDEO_MULTI_SLICE_GOB \
+ (V4L2_CID_MPEG_MSM_VIDC_BASE+27)
/* Camera class control IDs */
#define V4L2_CID_CAMERA_CLASS_BASE (V4L2_CTRL_CLASS_CAMERA | 0x900)
#define V4L2_CID_CAMERA_CLASS (V4L2_CTRL_CLASS_CAMERA | 1)
diff --git a/include/media/Kbuild b/include/media/Kbuild
index fc764eb..43cc3b9 100644
--- a/include/media/Kbuild
+++ b/include/media/Kbuild
@@ -9,3 +9,7 @@
header-y += msm_jpeg.h
header-y += msm_media_info.h
header-y += msm_vidc.h
+header-y += msmb_camera.h
+header-y += msm_cam_sensor.h
+header-y += msmb_isp.h
+header-y += msmb_ispif.h
diff --git a/include/media/msm_cam_sensor.h b/include/media/msm_cam_sensor.h
new file mode 100644
index 0000000..a96a067
--- /dev/null
+++ b/include/media/msm_cam_sensor.h
@@ -0,0 +1,303 @@
+#ifndef __LINUX_MSM_CAM_SENSOR_H
+#define __LINUX_MSM_CAM_SENSOR_H
+
+#ifdef MSM_CAMERA_BIONIC
+#include <sys/types.h>
+#endif
+#include <linux/types.h>
+#include <linux/v4l2-mediabus.h>
+#include <linux/i2c.h>
+
+#define I2C_SEQ_REG_SETTING_MAX 5
+#define I2C_SEQ_REG_DATA_MAX 20
+#define MAX_CID 16
+
+#define MSM_SENSOR_MCLK_8HZ 8000000
+#define MSM_SENSOR_MCLK_16HZ 16000000
+#define MSM_SENSOR_MCLK_24HZ 24000000
+
+#define GPIO_OUT_LOW (0 << 1)
+#define GPIO_OUT_HIGH (1 << 1)
+
+#define CSI_EMBED_DATA 0x12
+#define CSI_RESERVED_DATA_0 0x13
+#define CSI_YUV422_8 0x1E
+#define CSI_RAW8 0x2A
+#define CSI_RAW10 0x2B
+#define CSI_RAW12 0x2C
+
+#define CSI_DECODE_6BIT 0
+#define CSI_DECODE_8BIT 1
+#define CSI_DECODE_10BIT 2
+#define CSI_DECODE_DPCM_10_8_10 5
+
+#define MAX_SENSOR_NAME 32
+
+enum msm_camera_i2c_reg_addr_type {
+ MSM_CAMERA_I2C_BYTE_ADDR = 1,
+ MSM_CAMERA_I2C_WORD_ADDR,
+};
+
+enum msm_camera_i2c_data_type {
+ MSM_CAMERA_I2C_BYTE_DATA = 1,
+ MSM_CAMERA_I2C_WORD_DATA,
+ MSM_CAMERA_I2C_SET_BYTE_MASK,
+ MSM_CAMERA_I2C_UNSET_BYTE_MASK,
+ MSM_CAMERA_I2C_SET_WORD_MASK,
+ MSM_CAMERA_I2C_UNSET_WORD_MASK,
+ MSM_CAMERA_I2C_SET_BYTE_WRITE_MASK_DATA,
+};
+
+enum msm_sensor_power_seq_type_t {
+ SENSOR_CLK,
+ SENSOR_GPIO,
+ SENSOR_VREG,
+ SENSOR_I2C_MUX,
+};
+
+enum msm_sensor_clk_type_t {
+ SENSOR_CAM_MCLK,
+ SENSOR_CAM_CLK,
+ SENSOR_CAM_CLK_MAX,
+};
+
+enum msm_sensor_power_seq_gpio_t {
+ SENSOR_GPIO_RESET,
+ SENSOR_GPIO_STANDBY,
+ SENSOR_GPIO_MAX,
+};
+
+enum msm_camera_vreg_name_t {
+ CAM_VDIG,
+ CAM_VIO,
+ CAM_VANA,
+ CAM_VAF,
+ CAM_VREG_MAX,
+};
+
+enum msm_sensor_resolution_t {
+ MSM_SENSOR_RES_FULL,
+ MSM_SENSOR_RES_QTR,
+ MSM_SENSOR_RES_2,
+ MSM_SENSOR_RES_3,
+ MSM_SENSOR_RES_4,
+ MSM_SENSOR_RES_5,
+ MSM_SENSOR_RES_6,
+ MSM_SENSOR_RES_7,
+ MSM_SENSOR_INVALID_RES,
+};
+
+enum sensor_sub_module_t {
+ SUB_MODULE_SENSOR,
+ SUB_MODULE_CHROMATIX,
+ SUB_MODULE_ACTUATOR,
+ SUB_MODULE_EEPROM,
+ SUB_MODULE_LED_FLASH,
+ SUB_MODULE_STROBE_FLASH,
+ SUB_MODULE_CSIPHY,
+ SUB_MODULE_CSIPHY_3D,
+ SUB_MODULE_CSID,
+ SUB_MODULE_CSID_3D,
+ SUB_MODULE_MAX,
+};
+
+enum csid_cfg_type_t {
+ CSID_INIT,
+ CSID_CFG,
+ CSID_RELEASE,
+};
+
+enum csiphy_cfg_type_t {
+ CSIPHY_INIT,
+ CSIPHY_CFG,
+ CSIPHY_RELEASE,
+};
+
+enum camera_vreg_type {
+ REG_LDO,
+ REG_VS,
+ REG_GPIO,
+};
+
+struct msm_sensor_power_setting {
+ enum msm_sensor_power_seq_type_t seq_type;
+ uint16_t seq_val;
+ long config_val;
+ uint16_t delay;
+ void *data[10];
+};
+
+struct msm_sensor_power_setting_array {
+ struct msm_sensor_power_setting *power_setting;
+ uint16_t size;
+};
+
+struct msm_sensor_id_info_t {
+ uint16_t sensor_id_reg_addr;
+ uint16_t sensor_id;
+};
+
+struct msm_camera_sensor_slave_info {
+ uint16_t slave_addr;
+ enum msm_camera_i2c_reg_addr_type addr_type;
+ struct msm_sensor_id_info_t sensor_id_info;
+ struct msm_sensor_power_setting_array power_setting_array;
+};
+
+struct msm_camera_i2c_reg_array {
+ uint16_t reg_addr;
+ uint16_t reg_data;
+};
+
+struct msm_camera_i2c_reg_setting {
+ struct msm_camera_i2c_reg_array *reg_setting;
+ uint16_t size;
+ enum msm_camera_i2c_reg_addr_type addr_type;
+ enum msm_camera_i2c_data_type data_type;
+ uint16_t delay;
+};
+
+struct msm_camera_i2c_seq_reg_array {
+ uint16_t reg_addr;
+ uint8_t reg_data[I2C_SEQ_REG_DATA_MAX];
+ uint16_t reg_data_size;
+};
+
+struct msm_camera_i2c_seq_reg_setting {
+ struct msm_camera_i2c_seq_reg_array *reg_setting;
+ uint16_t size;
+ enum msm_camera_i2c_reg_addr_type addr_type;
+ uint16_t delay;
+};
+
+struct msm_camera_csid_vc_cfg {
+ uint8_t cid;
+ uint8_t dt;
+ uint8_t decode_format;
+};
+
+struct msm_camera_csid_lut_params {
+ uint8_t num_cid;
+ struct msm_camera_csid_vc_cfg *vc_cfg[MAX_CID];
+};
+
+struct msm_camera_csid_params {
+ uint8_t lane_cnt;
+ uint16_t lane_assign;
+ uint8_t phy_sel;
+ struct msm_camera_csid_lut_params lut_params;
+};
+
+struct msm_camera_csiphy_params {
+ uint8_t lane_cnt;
+ uint8_t settle_cnt;
+ uint16_t lane_mask;
+ uint8_t combo_mode;
+};
+
+struct msm_camera_csi2_params {
+ struct msm_camera_csid_params csid_params;
+ struct msm_camera_csiphy_params csiphy_params;
+};
+
+struct msm_camera_csi_lane_params {
+ uint16_t csi_lane_assign;
+ uint16_t csi_lane_mask;
+};
+
+struct csi_lane_params_t {
+ uint16_t csi_lane_assign;
+ uint8_t csi_lane_mask;
+ uint8_t csi_if;
+ uint8_t csid_core[2];
+ uint8_t csi_phy_sel;
+};
+
+struct msm_sensor_info_t {
+ char sensor_name[MAX_SENSOR_NAME];
+ int32_t session_id;
+ int32_t subdev_id[SUB_MODULE_MAX];
+};
+
+struct camera_vreg_t {
+ const char *reg_name;
+ enum camera_vreg_type type;
+ int min_voltage;
+ int max_voltage;
+ int op_mode;
+ uint32_t delay;
+};
+
+enum camb_position_t {
+ BACK_CAMERA_B,
+ FRONT_CAMERA_B,
+};
+
+enum camerab_mode_t {
+ CAMERA_MODE_2D_B = (1<<0),
+ CAMERA_MODE_3D_B = (1<<1)
+};
+
+struct msm_sensor_init_params {
+ /* mask of modes supported: 2D, 3D */
+ int modes_supported;
+ /* sensor position: front, back */
+ enum camb_position_t position;
+ /* sensor mount angle */
+ uint32_t sensor_mount_angle;
+};
+
+struct sensorb_cfg_data {
+ int cfgtype;
+ union {
+ struct msm_sensor_info_t sensor_info;
+ struct msm_sensor_init_params sensor_init_params;
+ void *setting;
+ } cfg;
+};
+
+struct csid_cfg_data {
+ enum csid_cfg_type_t cfgtype;
+ union {
+ uint32_t csid_version;
+ struct msm_camera_csid_params *csid_params;
+ } cfg;
+};
+
+struct csiphy_cfg_data {
+ enum csiphy_cfg_type_t cfgtype;
+ union {
+ struct msm_camera_csiphy_params *csiphy_params;
+ struct msm_camera_csi_lane_params *csi_lane_params;
+ } cfg;
+};
+
+enum msm_sensor_cfg_type_t {
+ CFG_SET_SLAVE_INFO,
+ CFG_WRITE_I2C_ARRAY,
+ CFG_WRITE_I2C_SEQ_ARRAY,
+ CFG_POWER_UP,
+ CFG_POWER_DOWN,
+ CFG_SET_STOP_STREAM_SETTING,
+ CFG_GET_SENSOR_INFO,
+ CFG_GET_SENSOR_INIT_PARAMS,
+};
+
+#define VIDIOC_MSM_SENSOR_CFG \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 1, struct sensorb_cfg_data)
+
+#define VIDIOC_MSM_SENSOR_RELEASE \
+ _IO('V', BASE_VIDIOC_PRIVATE + 2)
+
+#define VIDIOC_MSM_SENSOR_GET_SUBDEV_ID \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 3, uint32_t)
+
+#define VIDIOC_MSM_CSIPHY_IO_CFG \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 4, struct csid_cfg_data)
+
+#define VIDIOC_MSM_CSID_IO_CFG \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 5, struct csiphy_cfg_data)
+
+#define MSM_V4L2_PIX_FMT_META v4l2_fourcc('M', 'E', 'T', 'A') /* META */
+
+#endif /* __LINUX_MSM_CAM_SENSOR_H */
diff --git a/include/media/msm_camera.h b/include/media/msm_camera.h
index 971c9b3..9c310a9 100644
--- a/include/media/msm_camera.h
+++ b/include/media/msm_camera.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009-2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2009-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
@@ -1595,6 +1595,19 @@
REG_GPIO,
};
+enum msm_camera_vreg_name_t {
+ CAM_VDIG,
+ CAM_VIO,
+ CAM_VANA,
+ CAM_VAF,
+ CAM_VREG_MAX,
+};
+
+struct msm_camera_csi_lane_params {
+ uint16_t csi_lane_assign;
+ uint16_t csi_lane_mask;
+};
+
struct camera_vreg_t {
const char *reg_name;
enum camera_vreg_type type;
diff --git a/include/media/msmb_camera.h b/include/media/msmb_camera.h
new file mode 100644
index 0000000..732f60b
--- /dev/null
+++ b/include/media/msmb_camera.h
@@ -0,0 +1,159 @@
+#ifndef __LINUX_MSMB_CAMERA_H
+#define __LINUX_MSMB_CAMERA_H
+
+#include <linux/videodev2.h>
+#include <linux/types.h>
+#include <linux/ioctl.h>
+
+#define MSM_CAM_V4L2_IOCTL_NOTIFY \
+ _IOW('V', BASE_VIDIOC_PRIVATE + 30, struct v4l2_event)
+
+#define MSM_CAM_V4L2_IOCTL_NOTIFY_META \
+ _IOW('V', BASE_VIDIOC_PRIVATE + 31, struct v4l2_event)
+
+#define MSM_CAM_V4L2_IOCTL_CMD_ACK \
+ _IOW('V', BASE_VIDIOC_PRIVATE + 32, struct v4l2_event)
+
+#define QCAMERA_DEVICE_GROUP_ID 1
+#define QCAMERA_VNODE_GROUP_ID 2
+#define MSM_CAMERA_NAME "msm_camera"
+#define MSM_CONFIGURATION_NAME "msm_config"
+
+#define MSM_CAMERA_SUBDEV_CSIPHY 0
+#define MSM_CAMERA_SUBDEV_CSID 1
+#define MSM_CAMERA_SUBDEV_ISPIF 2
+#define MSM_CAMERA_SUBDEV_VFE 3
+#define MSM_CAMERA_SUBDEV_AXI 4
+#define MSM_CAMERA_SUBDEV_VPE 5
+#define MSM_CAMERA_SUBDEV_SENSOR 6
+#define MSM_CAMERA_SUBDEV_ACTUATOR 7
+#define MSM_CAMERA_SUBDEV_EEPROM 8
+#define MSM_CAMERA_SUBDEV_CPP 9
+#define MSM_CAMERA_SUBDEV_CCI 10
+#define MSM_CAMERA_SUBDEV_LED_FLASH 11
+#define MSM_CAMERA_SUBDEV_STROBE_FLASH 12
+
+#define MSM_MAX_CAMERA_SENSORS 5
+
+/* featur base */
+#define MSM_CAMERA_FEATURE_BASE 0x00010000
+#define MSM_CAMERA_FEATURE_SHUTDOWN (MSM_CAMERA_FEATURE_BASE + 1)
+
+#define MSM_CAMERA_STATUS_BASE 0x00020000
+#define MSM_CAMERA_STATUS_FAIL (MSM_CAMERA_STATUS_BASE + 1)
+#define MSM_CAMERA_STATUS_SUCCESS (MSM_CAMERA_STATUS_BASE + 2)
+
+/* event type */
+#define MSM_CAMERA_V4L2_EVENT_TYPE (V4L2_EVENT_PRIVATE_START + 0x00002000)
+
+/* event id */
+#define MSM_CAMERA_EVENT_MIN 0
+#define MSM_CAMERA_NEW_SESSION (MSM_CAMERA_EVENT_MIN + 1)
+#define MSM_CAMERA_DEL_SESSION (MSM_CAMERA_EVENT_MIN + 2)
+#define MSM_CAMERA_SET_PARM (MSM_CAMERA_EVENT_MIN + 3)
+#define MSM_CAMERA_GET_PARM (MSM_CAMERA_EVENT_MIN + 4)
+#define MSM_CAMERA_MAPPING_CFG (MSM_CAMERA_EVENT_MIN + 5)
+#define MSM_CAMERA_MAPPING_SES (MSM_CAMERA_EVENT_MIN + 6)
+#define MSM_CAMERA_MSM_NOTIFY (MSM_CAMERA_EVENT_MIN + 7)
+#define MSM_CAMERA_EVENT_MAX (MSM_CAMERA_EVENT_MIN + 8)
+
+/* data.command */
+#define MSM_CAMERA_PRIV_S_CROP (V4L2_CID_PRIVATE_BASE + 1)
+#define MSM_CAMERA_PRIV_G_CROP (V4L2_CID_PRIVATE_BASE + 2)
+#define MSM_CAMERA_PRIV_G_FMT (V4L2_CID_PRIVATE_BASE + 3)
+#define MSM_CAMERA_PRIV_S_FMT (V4L2_CID_PRIVATE_BASE + 4)
+#define MSM_CAMERA_PRIV_TRY_FMT (V4L2_CID_PRIVATE_BASE + 5)
+#define MSM_CAMERA_PRIV_METADATA (V4L2_CID_PRIVATE_BASE + 6)
+#define MSM_CAMERA_PRIV_QUERY_CAP (V4L2_CID_PRIVATE_BASE + 7)
+#define MSM_CAMERA_PRIV_STREAM_ON (V4L2_CID_PRIVATE_BASE + 8)
+#define MSM_CAMERA_PRIV_STREAM_OFF (V4L2_CID_PRIVATE_BASE + 9)
+#define MSM_CAMERA_PRIV_NEW_STREAM (V4L2_CID_PRIVATE_BASE + 10)
+#define MSM_CAMERA_PRIV_DEL_STREAM (V4L2_CID_PRIVATE_BASE + 11)
+#define MSM_CAMERA_PRIV_SHUTDOWN (V4L2_CID_PRIVATE_BASE + 12)
+#define MSM_CAMERA_PRIV_STREAM_INFO_SYNC \
+ (V4L2_CID_PRIVATE_BASE + 13)
+
+/* data.status - success */
+#define MSM_CAMERA_CMD_SUCESS 0x00000001
+#define MSM_CAMERA_BUF_MAP_SUCESS 0x00000002
+
+/* data.status - error */
+#define MSM_CAMERA_ERR_EVT_BASE 0x00010000
+#define MSM_CAMERA_ERR_CMD_FAIL (MSM_CAMERA_ERR_EVT_BASE + 1)
+#define MSM_CAMERA_ERR_MAPPING (MSM_CAMERA_ERR_EVT_BASE + 2)
+
+/* The msm_v4l2_event_data structure should match the
+ * v4l2_event.u.data field.
+ * should not exceed 16 elements */
+struct msm_v4l2_event_data {
+ /*word 0*/
+ unsigned int command;
+ /*word 1*/
+ unsigned int status;
+ /*word 2*/
+ unsigned int session_id;
+ /*word 3*/
+ unsigned int stream_id;
+ /*word 4*/
+ unsigned int map_op;
+ /*word 5*/
+ unsigned int map_buf_idx;
+ /*word 6*/
+ unsigned int notify;
+ /*word 7*/
+ unsigned int nop1;
+ /*word 8*/
+ unsigned int nop2;
+ /*word 9*/
+ unsigned int nop3;
+ /*word 10*/
+ unsigned int nop4;
+ /*word 11*/
+ unsigned int nop5;
+ /*word 12*/
+ unsigned int nop6;
+ /*word 13*/
+ unsigned int nop7;
+ /*word 14*/
+ unsigned int nop8;
+ /*word 15*/
+ unsigned int nop9;
+};
+
+/* map to v4l2_format.fmt.raw_data */
+struct msm_v4l2_format_data {
+ enum v4l2_buf_type type;
+ unsigned int width;
+ unsigned int height;
+ unsigned int pixelformat; /* FOURCC */
+ unsigned char num_planes;
+ unsigned int plane_sizes[VIDEO_MAX_PLANES];
+};
+
+/* MSM Four-character-code (FOURCC) */
+#define msm_v4l2_fourcc(a, b, c, d)\
+ ((__u32)(a) | ((__u32)(b) << 8) | ((__u32)(c) << 16) |\
+ ((__u32)(d) << 24))
+
+/* Composite stats */
+#define MSM_V4L2_PIX_FMT_STATS_COMB v4l2_fourcc('S', 'T', 'C', 'M')
+/* AEC stats */
+#define MSM_V4L2_PIX_FMT_STATS_AE v4l2_fourcc('S', 'T', 'A', 'E')
+/* AF stats */
+#define MSM_V4L2_PIX_FMT_STATS_AF v4l2_fourcc('S', 'T', 'A', 'F')
+/* AWB stats */
+#define MSM_V4L2_PIX_FMT_STATS_AWB v4l2_fourcc('S', 'T', 'W', 'B')
+/* IHIST stats */
+#define MSM_V4L2_PIX_FMT_STATS_IHST v4l2_fourcc('I', 'H', 'S', 'T')
+/* Column count stats */
+#define MSM_V4L2_PIX_FMT_STATS_CS v4l2_fourcc('S', 'T', 'C', 'S')
+/* Row count stats */
+#define MSM_V4L2_PIX_FMT_STATS_RS v4l2_fourcc('S', 'T', 'R', 'S')
+/* Bayer Grid stats */
+#define MSM_V4L2_PIX_FMT_STATS_BG v4l2_fourcc('S', 'T', 'B', 'G')
+/* Bayer focus stats */
+#define MSM_V4L2_PIX_FMT_STATS_BF v4l2_fourcc('S', 'T', 'B', 'F')
+/* Bayer hist stats */
+#define MSM_V4L2_PIX_FMT_STATS_BHST v4l2_fourcc('B', 'H', 'S', 'T')
+
+#endif /* __LINUX_MSMB_CAMERA_H */
diff --git a/include/media/msmb_isp.h b/include/media/msmb_isp.h
new file mode 100644
index 0000000..4e92f70
--- /dev/null
+++ b/include/media/msmb_isp.h
@@ -0,0 +1,323 @@
+#ifndef __MSMB_ISP__
+#define __MSMB_ISP__
+
+#include <linux/videodev2.h>
+
+#define MAX_PLANES_PER_STREAM 3
+#define MAX_NUM_STREAM 7
+
+#define ISP_VERSION_40 40
+#define ISP_VERSION_32 32
+#define ISP_NATIVE_BUF_BIT 0x10000
+#define ISP_STATS_STREAM_BIT 0x80000000
+
+enum ISP_START_PIXEL_PATTERN {
+ ISP_BAYER_RGRGRG,
+ ISP_BAYER_GRGRGR,
+ ISP_BAYER_BGBGBG,
+ ISP_BAYER_GBGBGB,
+ ISP_YUV_YCbYCr,
+ ISP_YUV_YCrYCb,
+ ISP_YUV_CbYCrY,
+ ISP_YUV_CrYCbY,
+ ISP_PIX_PATTERN_MAX
+};
+
+enum msm_vfe_plane_fmt {
+ Y_PLANE,
+ CB_PLANE,
+ CR_PLANE,
+ CRCB_PLANE,
+ CBCR_PLANE,
+ VFE_PLANE_FMT_MAX
+};
+
+enum msm_vfe_input_src {
+ VFE_PIX_0,
+ VFE_RAW_0,
+ VFE_RAW_1,
+ VFE_RAW_2,
+ VFE_SRC_MAX,
+};
+
+enum msm_vfe_axi_stream_src {
+ PIX_ENCODER,
+ PIX_VIEWFINDER,
+ CAMIF_RAW,
+ IDEAL_RAW,
+ RDI,
+ VFE_AXI_SRC_MAX
+};
+
+enum msm_vfe_frame_skip_pattern {
+ NO_SKIP,
+ EVERY_2FRAME,
+ EVERY_4FRAME,
+ EVERY_8FRAME,
+ EVERY_16FRAME,
+ EVERY_32FRAME,
+ MAX_SKIP,
+};
+
+enum msm_vfe_camif_input {
+ CAMIF_DISABLED,
+ CAMIF_PAD_REG_INPUT,
+ CAMIF_MIDDI_INPUT,
+ CAMIF_MIPI_INPUT,
+};
+
+struct msm_vfe_camif_cfg {
+ uint32_t lines_per_frame;
+ uint32_t pixels_per_line;
+ uint32_t first_pixel;
+ uint32_t last_pixel;
+ uint32_t first_line;
+ uint32_t last_line;
+ uint32_t epoch_line0;
+ uint32_t epoch_line1;
+ enum msm_vfe_camif_input camif_input;
+};
+
+enum msm_vfe_inputmux {
+ CAMIF,
+ TESTGEN,
+ EXTERNAL_READ,
+};
+
+struct msm_vfe_pix_cfg {
+ struct msm_vfe_camif_cfg camif_cfg;
+ enum msm_vfe_inputmux input_mux;
+ enum ISP_START_PIXEL_PATTERN pixel_pattern;
+};
+
+struct msm_vfe_input_cfg {
+ union {
+ struct msm_vfe_pix_cfg pix_cfg;
+ } d;
+ enum msm_vfe_input_src input_src;
+
+};
+
+struct msm_vfe_axi_plane_cfg {
+ uint32_t output_width; /*Include padding*/
+ uint32_t output_height;
+ uint32_t output_stride;
+ uint32_t output_scan_lines;
+ uint32_t output_plane_format; /*Y/Cb/Cr/CbCr*/
+
+ uint8_t csid_src; /*RDI 0-2*/
+ uint8_t rdi_cid;/*CID 1-16*/
+};
+
+struct msm_vfe_axi_stream_request_cmd {
+ uint32_t session_id;
+ uint32_t stream_id;
+ uint32_t output_format;/*Planar/RAW/Misc*/
+ enum msm_vfe_axi_stream_src stream_src; /*CAMIF/IDEAL/RDIs*/
+ struct msm_vfe_axi_plane_cfg plane_cfg[MAX_PLANES_PER_STREAM];
+
+ uint32_t burst_count;
+ uint32_t hfr_mode;
+ uint8_t frame_base;
+
+ uint32_t init_frame_drop; /*MAX 31 Frames*/
+ enum msm_vfe_frame_skip_pattern frame_skip_pattern;
+ uint8_t buf_divert; /* if TRUE no vb2 buf done. */
+ /*Return values*/
+ uint32_t axi_stream_handle;
+};
+
+struct msm_vfe_axi_stream_release_cmd {
+ uint32_t stream_handle;
+};
+
+enum msm_vfe_axi_stream_cmd {
+ STOP_STREAM,
+ START_STREAM,
+};
+
+struct msm_vfe_axi_stream_cfg_cmd {
+ uint8_t num_streams;
+ uint32_t stream_handle[MAX_NUM_STREAM];
+ enum msm_vfe_axi_stream_cmd cmd;
+};
+
+enum msm_isp_stats_type {
+ MSM_ISP_STATS_AEC, /* legacy based AEC */
+ MSM_ISP_STATS_AF, /* legacy based AF */
+ MSM_ISP_STATS_AWB, /* legacy based AWB */
+ MSM_ISP_STATS_RS, /* legacy based RS */
+ MSM_ISP_STATS_CS, /* legacy based CS */
+ MSM_ISP_STATS_IHIST, /* legacy based HIST */
+ MSM_ISP_STATS_SKIN, /* legacy based SKIN */
+ MSM_ISP_STATS_BG, /* Bayer Grids */
+ MSM_ISP_STATS_BF, /* Bayer Focus */
+ MSM_ISP_STATS_BE, /* Bayer Exposure*/
+ MSM_ISP_STATS_BHIST, /* Bayer Hist */
+ MSM_ISP_STATS_MAX /* MAX */
+};
+
+struct msm_vfe_stats_stream_request_cmd {
+ uint32_t session_id;
+ uint32_t stream_id;
+ enum msm_isp_stats_type stats_type;
+ uint8_t comp_flag;
+ uint32_t framedrop_pattern;
+ uint32_t stream_handle;
+};
+struct msm_vfe_stats_stream_release_cmd {
+ uint32_t stream_handle;
+};
+struct msm_vfe_stats_stream_cfg_cmd {
+ uint8_t num_streams;
+ uint32_t stream_handle[MSM_ISP_STATS_MAX];
+ uint8_t enable;
+};
+enum msm_vfe_reg_cfg_type {
+ VFE_WRITE,
+ VFE_WRITE_MB,
+ VFE_READ,
+ VFE_WRITE_MASK,
+ VFE_CLEAR_MASK,
+ VFE_WRITE_AUTO_INCREMENT,
+};
+
+struct msm_vfe_cfg_cmd2 {
+ uint16_t num_cfg;
+ uint16_t cmd_len;
+ void __user *cfg_data;
+ void __user *cfg_cmd;
+};
+
+struct msm_vfe_reg_cfg_cmd {
+ uint32_t reg_offset;
+ uint32_t cmd_data;
+ uint32_t len;
+ enum msm_vfe_reg_cfg_type cmd_type;
+};
+
+struct msm_isp_buf_request {
+ uint32_t session_id;
+ uint32_t stream_id;
+ uint8_t num_buf;
+ uint32_t handle;
+};
+
+struct msm_isp_qbuf_info {
+ uint32_t handle;
+ int buf_idx;
+ /*Only used for prepare buffer*/
+ struct v4l2_buffer buffer;
+};
+
+struct msm_vfe_axi_src_state {
+ enum msm_vfe_input_src input_src;
+ uint32_t src_active;
+};
+
+enum msm_isp_event_idx {
+ ISP_REG_UPDATE = 0,
+ ISP_START_ACK = 1,
+ ISP_STOP_ACK = 2,
+ ISP_IRQ_VIOLATION = 3,
+ ISP_WM_BUS_OVERFLOW = 4,
+ ISP_STATS_OVERFLOW = 5,
+ ISP_CAMIF_ERROR = 6,
+ ISP_STATS_NOTIFY = 7,
+ ISP_SOF = 8,
+ ISP_EOF = 9,
+ ISP_BUF_DIVERT = 10,
+ ISP_EVENT_MAX = 11
+};
+
+#define ISP_EVENT_BASE (V4L2_EVENT_PRIVATE_START + 1)
+#define ISP_EVENT_REG_UPDATE (ISP_EVENT_BASE + ISP_REG_UPDATE)
+#define ISP_EVENT_START_ACK (ISP_EVENT_BASE + ISP_START_ACK)
+#define ISP_EVENT_STOP_ACK (ISP_EVENT_BASE + ISP_STOP_ACK)
+#define ISP_EVENT_IRQ_VIOLATION (ISP_EVENT_BASE + ISP_IRQ_VIOLATION)
+#define ISP_EVENT_WM_BUS_OVERFLOW (ISP_EVENT_BASE + ISP_WM_BUS_OVERFLOW)
+#define ISP_EVENT_STATS_OVERFLOW (ISP_EVENT_BASE + ISP_STATS_OVERFLOW)
+#define ISP_EVENT_CAMIF_ERROR (ISP_EVENT_BASE + ISP_CAMIF_ERROR)
+#define ISP_EVENT_STATS_NOTIFY (ISP_EVENT_BASE + ISP_STATS_NOTIFY)
+#define ISP_EVENT_SOF (ISP_EVENT_BASE + ISP_SOF)
+#define ISP_EVENT_EOF (ISP_EVENT_BASE + ISP_EOF)
+#define ISP_EVENT_BUF_DIVERT (ISP_EVENT_BASE + ISP_BUF_DIVERT)
+
+
+/* The msm_v4l2_event_data structure should match the
+ * v4l2_event.u.data field.
+ * should not exceed 64 bytes */
+
+struct msm_isp_buf_event {
+ uint32_t session_id;
+ uint32_t stream_id;
+ uint32_t handle;
+ int8_t buf_idx;
+};
+struct msm_isp_stats_event {
+ uint32_t stats_mask; /* 4 bytes */
+ uint8_t stats_buf_idxs[MSM_ISP_STATS_MAX]; /* 11 bytes */
+};
+
+struct msm_isp_stream_ack {
+ uint32_t session_id;
+ uint32_t stream_id;
+ uint32_t handle;
+};
+
+struct msm_isp_event_data {
+ struct timeval timestamp;
+ /* if pix is a src frame_id is from camif */
+ uint32_t frame_id;
+ union {
+ /* START_ACK, STOP_ACK */
+ struct msm_isp_stream_ack stream_ack;
+ /* REG_UPDATE_TRIGGER, bus over flow */
+ enum msm_vfe_input_src input_src;
+ /* stats notify */
+ struct msm_isp_stats_event stats;
+ /* IRQ_VIOLATION, STATS_OVER_FLOW, WM_OVER_FLOW */
+ uint32_t irq_status_mask;
+ struct msm_isp_buf_event buf_done;
+ } u; /* union can have max 52 bytes */
+};
+
+#define VIDIOC_MSM_VFE_REG_CFG \
+ _IOWR('V', BASE_VIDIOC_PRIVATE, struct msm_vfe_cfg_cmd2)
+
+#define VIDIOC_MSM_ISP_REQUEST_BUF \
+ _IOWR('V', BASE_VIDIOC_PRIVATE+1, struct msm_isp_buf_request)
+
+#define VIDIOC_MSM_ISP_ENQUEUE_BUF \
+ _IOWR('V', BASE_VIDIOC_PRIVATE+2, struct msm_isp_qbuf_info)
+
+#define VIDIOC_MSM_ISP_RELEASE_BUF \
+ _IOWR('V', BASE_VIDIOC_PRIVATE+3, struct msm_isp_buf_request)
+
+#define VIDIOC_MSM_ISP_REQUEST_STREAM \
+ _IOWR('V', BASE_VIDIOC_PRIVATE+4, struct msm_vfe_axi_stream_request_cmd)
+
+#define VIDIOC_MSM_ISP_CFG_STREAM \
+ _IOWR('V', BASE_VIDIOC_PRIVATE+5, struct msm_vfe_axi_stream_cfg_cmd)
+
+#define VIDIOC_MSM_ISP_RELEASE_STREAM \
+ _IOWR('V', BASE_VIDIOC_PRIVATE+6, struct msm_vfe_axi_stream_release_cmd)
+
+#define VIDIOC_MSM_ISP_INPUT_CFG \
+ _IOWR('V', BASE_VIDIOC_PRIVATE+7, struct msm_vfe_input_cfg)
+
+#define VIDIOC_MSM_ISP_SET_SRC_STATE \
+ _IOWR('V', BASE_VIDIOC_PRIVATE+8, struct msm_vfe_axi_src_state)
+
+#define VIDIOC_MSM_ISP_REQUEST_STATS_STREAM \
+ _IOWR('V', BASE_VIDIOC_PRIVATE+9, \
+ struct msm_vfe_stats_stream_request_cmd)
+
+#define VIDIOC_MSM_ISP_CFG_STATS_STREAM \
+ _IOWR('V', BASE_VIDIOC_PRIVATE+10, struct msm_vfe_stats_stream_cfg_cmd)
+
+#define VIDIOC_MSM_ISP_RELEASE_STATS_STREAM \
+ _IOWR('V', BASE_VIDIOC_PRIVATE+11, \
+ struct msm_vfe_stats_stream_release_cmd)
+
+#endif /* __MSMB_ISP__ */
diff --git a/include/media/msmb_ispif.h b/include/media/msmb_ispif.h
new file mode 100644
index 0000000..fc27ef6
--- /dev/null
+++ b/include/media/msmb_ispif.h
@@ -0,0 +1,103 @@
+#ifndef MSM_CAM_ISPIF_H
+#define MSM_CAM_ISPIF_H
+
+#define CSID_VERSION_V2 0x02000011
+#define CSID_VERSION_V3 0x30000000
+
+enum msm_ispif_vfe_intf {
+ VFE0,
+ VFE1,
+ VFE_MAX
+};
+#define VFE0_MASK (1 << VFE0)
+#define VFE1_MASK (1 << VFE1)
+
+enum msm_ispif_intftype {
+ PIX0,
+ RDI0,
+ PIX1,
+ RDI1,
+ RDI2,
+ INTF_MAX
+};
+#define PIX0_MASK (1 << PIX0)
+#define PIX1_MASK (1 << PIX1)
+#define RDI0_MASK (1 << RDI0)
+#define RDI1_MASK (1 << RDI1)
+#define RDI2_MASK (1 << RDI2)
+
+
+enum msm_ispif_vc {
+ VC0,
+ VC1,
+ VC2,
+ VC3,
+ VC_MAX
+};
+
+enum msm_ispif_cid {
+ CID0,
+ CID1,
+ CID2,
+ CID3,
+ CID4,
+ CID5,
+ CID6,
+ CID7,
+ CID8,
+ CID9,
+ CID10,
+ CID11,
+ CID12,
+ CID13,
+ CID14,
+ CID15,
+ CID_MAX
+};
+
+enum msm_ispif_csid {
+ CSID0,
+ CSID1,
+ CSID2,
+ CSID3,
+ CSID_MAX
+};
+
+struct msm_ispif_params_entry {
+ enum msm_ispif_intftype intftype;
+ int num_cids;
+ enum msm_ispif_cid cids[3];
+ enum msm_ispif_csid csid;
+};
+
+struct msm_ispif_param_data {
+ enum msm_ispif_vfe_intf vfe_intf;
+ uint32_t num;
+ struct msm_ispif_params_entry entries[INTF_MAX];
+};
+
+enum ispif_cfg_type_t {
+ ISPIF_CLK_ENABLE,
+ ISPIF_CLK_DISABLE,
+ ISPIF_INIT,
+ ISPIF_CFG,
+ ISPIF_START_FRAME_BOUNDARY,
+ ISPIF_STOP_FRAME_BOUNDARY,
+ ISPIF_STOP_IMMEDIATELY,
+ ISPIF_RELEASE,
+ ISPIF_ENABLE_REG_DUMP,
+};
+
+struct ispif_cfg_data {
+ enum ispif_cfg_type_t cfg_type;
+ union {
+ int reg_dump; /* ISPIF_ENABLE_REG_DUMP */
+ uint32_t csid_version; /* ISPIF_INIT */
+ struct msm_ispif_param_data params; /* CFG, START, STOP */
+ };
+};
+
+#define VIDIOC_MSM_ISPIF_CFG \
+ _IOWR('V', BASE_VIDIOC_PRIVATE, struct ispif_cfg_data)
+
+#endif /* MSM_CAM_ISPIF_H */
diff --git a/include/media/vcap_fmt.h b/include/media/vcap_fmt.h
index 2787e8d..b2a8d10 100644
--- a/include/media/vcap_fmt.h
+++ b/include/media/vcap_fmt.h
@@ -1,16 +1,3 @@
-/* Copyright (c) 2012, Code Aurora Forum. 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 VCAP_FMT_H
#define VCAP_FMT_H
#include <linux/videodev2.h>
diff --git a/include/sound/apr_audio-v2.h b/include/sound/apr_audio-v2.h
index 88acdfc..3571fad 100644
--- a/include/sound/apr_audio-v2.h
+++ b/include/sound/apr_audio-v2.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. 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
@@ -6369,4 +6369,135 @@
/*bharath, adsp_error_codes.h */
+/* LPASS clock for I2S Interface */
+
+/* Supported OSR clock values */
+#define Q6AFE_LPASS_OSR_CLK_12_P288_MHZ 0xBB8000
+#define Q6AFE_LPASS_OSR_CLK_8_P192_MHZ 0x7D0000
+#define Q6AFE_LPASS_OSR_CLK_6_P144_MHZ 0x5DC000
+#define Q6AFE_LPASS_OSR_CLK_4_P096_MHZ 0x3E8000
+#define Q6AFE_LPASS_OSR_CLK_3_P072_MHZ 0x2EE000
+#define Q6AFE_LPASS_OSR_CLK_2_P048_MHZ 0x1F4000
+#define Q6AFE_LPASS_OSR_CLK_1_P536_MHZ 0x177000
+#define Q6AFE_LPASS_OSR_CLK_1_P024_MHZ 0xFA000
+#define Q6AFE_LPASS_OSR_CLK_768_kHZ 0xBB800
+#define Q6AFE_LPASS_OSR_CLK_512_kHZ 0x7D000
+#define Q6AFE_LPASS_OSR_CLK_DISABLE 0x0
+
+/* Supported Bit clock values */
+#define Q6AFE_LPASS_IBIT_CLK_8_P192_MHZ 0x7D0000
+#define Q6AFE_LPASS_IBIT_CLK_6_P144_MHZ 0x5DC000
+#define Q6AFE_LPASS_IBIT_CLK_4_P096_MHZ 0x3E8000
+#define Q6AFE_LPASS_IBIT_CLK_3_P072_MHZ 0x2EE000
+#define Q6AFE_LPASS_IBIT_CLK_2_P048_MHZ 0x1F4000
+#define Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ 0x177000
+#define Q6AFE_LPASS_IBIT_CLK_1_P024_MHZ 0xFA000
+#define Q6AFE_LPASS_IBIT_CLK_768_KHZ 0xBB800
+#define Q6AFE_LPASS_IBIT_CLK_512_KHZ 0x7D000
+#define Q6AFE_LPASS_IBIT_CLK_DISABLE 0x0
+
+/* Supported LPASS CLK sources */
+#define Q6AFE_LPASS_CLK_SRC_EXTERNAL 0
+#define Q6AFE_LPASS_CLK_SRC_INTERNAL 1
+
+/* Supported LPASS CLK root*/
+#define Q6AFE_LPASS_CLK_ROOT_DEFAULT 0
+
+enum afe_lpass_clk_mode {
+ Q6AFE_LPASS_MODE_BOTH_INVALID,
+ Q6AFE_LPASS_MODE_CLK1_VALID,
+ Q6AFE_LPASS_MODE_CLK2_VALID,
+ Q6AFE_LPASS_MODE_BOTH_VALID,
+} __packed;
+
+struct afe_clk_cfg {
+/* Minor version used for tracking the version of the I2S
+ * configuration interface.
+ * Supported values: #AFE_API_VERSION_I2S_CONFIG
+ */
+ u32 i2s_cfg_minor_version;
+
+/* clk value 1 in MHz. */
+ u32 clk_val1;
+
+/* clk value 2 in MHz. */
+ u32 clk_val2;
+
+/* clk_src
+ * #Q6AFE_LPASS_CLK_SRC_EXTERNAL
+ * #Q6AFE_LPASS_CLK_SRC_INTERNAL
+ */
+
+ u16 clk_src;
+
+/* clk_root -0 for default */
+ u16 clk_root;
+
+/* clk_set_mode
+ * #Q6AFE_LPASS_MODE_BOTH_INVALID
+ * #Q6AFE_LPASS_MODE_CLK1_VALID
+ * #Q6AFE_LPASS_MODE_CLK2_VALID
+ * #Q6AFE_LPASS_MODE_BOTH_VALID
+ */
+ u16 clk_set_mode;
+
+/* This param id is used to configure I2S clk */
+ u16 reserved;
+} __packed;
+
+/* This param id is used to configure I2S clk */
+#define AFE_PARAM_ID_LPAIF_CLK_CONFIG 0x00010238
+
+
+struct afe_lpass_clk_config_command {
+ struct apr_hdr hdr;
+ struct afe_port_cmd_set_param_v2 param;
+ struct afe_port_param_data_v2 pdata;
+ struct afe_clk_cfg clk_cfg;
+} __packed;
+
+enum afe_lpass_digital_clk_src {
+ Q6AFE_LPASS_DIGITAL_ROOT_INVALID,
+ Q6AFE_LPASS_DIGITAL_ROOT_PRI_MI2S_OSR,
+ Q6AFE_LPASS_DIGITAL_ROOT_SEC_MI2S_OSR,
+ Q6AFE_LPASS_DIGITAL_ROOT_TER_MI2S_OSR,
+ Q6AFE_LPASS_DIGITAL_ROOT_QUAD_MI2S_OSR,
+ Q6AFE_LPASS_DIGITAL_ROOT_CDC_ROOT_CLK,
+} __packed;
+
+/* This param id is used to configure internal clk */
+#define AFE_PARAM_ID_INTERNAL_DIGIATL_CDC_CLK_CONFIG 0x00010239
+
+struct afe_digital_clk_cfg {
+/* Minor version used for tracking the version of the I2S
+ * configuration interface.
+ * Supported values: #AFE_API_VERSION_I2S_CONFIG
+ */
+ u32 i2s_cfg_minor_version;
+
+/* clk value in MHz. */
+ u32 clk_val;
+
+/* INVALID
+ * PRI_MI2S_OSR
+ * SEC_MI2S_OSR
+ * TER_MI2S_OSR
+ * QUAD_MI2S_OSR
+ * DIGT_CDC_ROOT
+ */
+ u16 clk_root;
+
+/* This field must be set to zero. */
+ u16 reserved;
+} __packed;
+
+
+struct afe_lpass_digital_clk_config_command {
+ struct apr_hdr hdr;
+ struct afe_port_cmd_set_param_v2 param;
+ struct afe_port_param_data_v2 pdata;
+ struct afe_digital_clk_cfg clk_cfg;
+} __packed;
+
+
#endif /*_APR_AUDIO_V2_H_ */
diff --git a/include/sound/msm-dai-q6-v2.h b/include/sound/msm-dai-q6-v2.h
index 4ecd435..c34a397 100644
--- a/include/sound/msm-dai-q6-v2.h
+++ b/include/sound/msm-dai-q6-v2.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. 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
@@ -26,8 +26,7 @@
#define MSM_TERT_MI2S 2
#define MSM_QUAT_MI2S 3
-struct msm_dai_auxpcm_pdata {
- const char *clk;
+struct msm_dai_auxpcm_config {
u16 mode;
u16 sync;
u16 frame;
@@ -40,6 +39,12 @@
int pcm_clk_rate;
};
+struct msm_dai_auxpcm_pdata {
+ void *clk_cfg;
+ struct msm_dai_auxpcm_config mode_8k;
+ struct msm_dai_auxpcm_config mode_16k;
+};
+
struct msm_mi2s_pdata {
u16 rx_sd_lines;
u16 tx_sd_lines;
diff --git a/include/sound/q6adm-v2.h b/include/sound/q6adm-v2.h
index fdc3cb9..6e5e649 100644
--- a/include/sound/q6adm-v2.h
+++ b/include/sound/q6adm-v2.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. 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
@@ -49,4 +49,8 @@
int adm_get_copp_id(int port_id);
+void adm_set_multi_ch_map(char *channel_map);
+
+void adm_get_multi_ch_map(char *channel_map);
+
#endif /* __Q6_ADM_V2_H__ */
diff --git a/include/sound/q6afe-v2.h b/include/sound/q6afe-v2.h
index 3da152c..e39d45c 100644
--- a/include/sound/q6afe-v2.h
+++ b/include/sound/q6afe-v2.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. 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
@@ -155,4 +155,9 @@
int afe_pseudo_port_start_nowait(u16 port_id);
int afe_pseudo_port_stop_nowait(u16 port_id);
+int afe_set_lpass_clock(u16 port_id, struct afe_clk_cfg *cfg);
+int afe_set_lpass_internal_digital_codec_clock(u16 port_id,
+ struct afe_digital_clk_cfg *cfg);
+int q6afe_check_osr_clk_freq(u32 freq);
+
#endif /* __Q6AFE_V2_H__ */
diff --git a/include/sound/q6asm-v2.h b/include/sound/q6asm-v2.h
index a436a6e..876d815 100644
--- a/include/sound/q6asm-v2.h
+++ b/include/sound/q6asm-v2.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. 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
@@ -262,7 +262,8 @@
uint32_t rate, uint32_t channels);
int q6asm_media_format_block_multi_ch_pcm(struct audio_client *ac,
- uint32_t rate, uint32_t channels);
+ uint32_t rate, uint32_t channels,
+ bool use_default_chmap, char *channel_map);
int q6asm_media_format_block_aac(struct audio_client *ac,
struct asm_aac_cfg *cfg);
diff --git a/include/sound/q6asm.h b/include/sound/q6asm.h
index 6b4c17b..dcdd816 100644
--- a/include/sound/q6asm.h
+++ b/include/sound/q6asm.h
@@ -74,10 +74,12 @@
/* Enable Sample_Rate/Channel_Mode notification event from Decoder */
#define SR_CM_NOTIFY_ENABLE 0x0004
-#define ASYNC_IO_MODE 0x0002
-#define SYNC_IO_MODE 0x0001
-#define NO_TIMESTAMP 0xFF00
-#define SET_TIMESTAMP 0x0000
+#define TUN_WRITE_IO_MODE 0x0008 /* tunnel read write mode */
+#define TUN_READ_IO_MODE 0x0004 /* tunnel read write mode */
+#define ASYNC_IO_MODE 0x0002
+#define SYNC_IO_MODE 0x0001
+#define NO_TIMESTAMP 0xFF00
+#define SET_TIMESTAMP 0x0000
#define SOFT_PAUSE_ENABLE 1
#define SOFT_PAUSE_DISABLE 0
diff --git a/include/sound/q6audio-v2.h b/include/sound/q6audio-v2.h
index 1a5dce1..fd6a490 100644
--- a/include/sound/q6audio-v2.h
+++ b/include/sound/q6audio-v2.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. 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
@@ -21,6 +21,8 @@
int q6audio_validate_port(u16 port_id);
+int q6audio_is_digital_pcm_interface(u16 port_id);
+
int q6audio_get_port_id(u16 port_id);
#endif
diff --git a/include/trace/events/sync.h b/include/trace/events/sync.h
new file mode 100644
index 0000000..f31bc63
--- /dev/null
+++ b/include/trace/events/sync.h
@@ -0,0 +1,82 @@
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM sync
+
+#if !defined(_TRACE_SYNC_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_SYNC_H
+
+#include <linux/sync.h>
+#include <linux/tracepoint.h>
+
+TRACE_EVENT(sync_timeline,
+ TP_PROTO(struct sync_timeline *timeline),
+
+ TP_ARGS(timeline),
+
+ TP_STRUCT__entry(
+ __string(name, timeline->name)
+ __array(char, value, 32)
+ ),
+
+ TP_fast_assign(
+ __assign_str(name, timeline->name);
+ if (timeline->ops->timeline_value_str) {
+ timeline->ops->timeline_value_str(timeline,
+ __entry->value,
+ sizeof(__entry->value));
+ } else {
+ __entry->value[0] = '\0';
+ }
+ ),
+
+ TP_printk("name=%s value=%s", __get_str(name), __entry->value)
+);
+
+TRACE_EVENT(sync_wait,
+ TP_PROTO(struct sync_fence *fence, int begin),
+
+ TP_ARGS(fence, begin),
+
+ TP_STRUCT__entry(
+ __string(name, fence->name)
+ __field(s32, status)
+ __field(u32, begin)
+ ),
+
+ TP_fast_assign(
+ __assign_str(name, fence->name);
+ __entry->status = fence->status;
+ __entry->begin = begin;
+ ),
+
+ TP_printk("%s name=%s state=%d", __entry->begin ? "begin" : "end",
+ __get_str(name), __entry->status)
+);
+
+TRACE_EVENT(sync_pt,
+ TP_PROTO(struct sync_pt *pt),
+
+ TP_ARGS(pt),
+
+ TP_STRUCT__entry(
+ __string(timeline, pt->parent->name)
+ __array(char, value, 32)
+ ),
+
+ TP_fast_assign(
+ __assign_str(timeline, pt->parent->name);
+ if (pt->parent->ops->pt_value_str) {
+ pt->parent->ops->pt_value_str(pt,
+ __entry->value,
+ sizeof(__entry->value));
+ } else {
+ __entry->value[0] = '\0';
+ }
+ ),
+
+ TP_printk("name=%s value=%s", __get_str(timeline), __entry->value)
+ );
+
+#endif /* if !defined(_TRACE_SYNC_H) || defined(TRACE_HEADER_MULTI_READ) */
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
diff --git a/kernel/panic.c b/kernel/panic.c
index 8c6babc..4716d16 100644
--- a/kernel/panic.c
+++ b/kernel/panic.c
@@ -23,6 +23,7 @@
#include <linux/init.h>
#include <linux/nmi.h>
#include <linux/dmi.h>
+#include <linux/coresight.h>
#define PANIC_TIMER_STEP 100
#define PANIC_BLINK_SPD 18
@@ -80,6 +81,7 @@
long i, i_next = 0;
int state = 0;
+ coresight_abort();
/*
* Disable local interrupts. This will prevent panic_smp_self_stop
* from deadlocking the first cpu that invokes the panic, since
diff --git a/kernel/sysctl_binary.c b/kernel/sysctl_binary.c
index d42c279..acb60be 100644
--- a/kernel/sysctl_binary.c
+++ b/kernel/sysctl_binary.c
@@ -524,6 +524,7 @@
{ CTL_INT, NET_IPV6_ACCEPT_RA_RT_INFO_MAX_PLEN, "accept_ra_rt_info_max_plen" },
{ CTL_INT, NET_IPV6_PROXY_NDP, "proxy_ndp" },
{ CTL_INT, NET_IPV6_ACCEPT_SOURCE_ROUTE, "accept_source_route" },
+ { CTL_INT, NET_IPV6_ACCEPT_RA_PREFIX_ROUTE, "accept_ra_prefix_route" },
{}
};
diff --git a/lib/qmi_encdec.c b/lib/qmi_encdec.c
index 40273d0..3f618cb 100644
--- a/lib/qmi_encdec.c
+++ b/lib/qmi_encdec.c
@@ -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
@@ -82,7 +82,7 @@
static int _qmi_kernel_encode(struct elem_info *ei_array,
void *out_buf, void *in_c_struct,
- int enc_level);
+ uint32_t out_buf_len, int enc_level);
static int _qmi_kernel_decode(struct elem_info *ei_array,
void *out_c_struct,
@@ -90,6 +90,71 @@
int dec_level);
/**
+ * qmi_calc_max_msg_len() - Calculate the maximum length of a QMI message
+ * @ei_array: Struct info array describing the structure.
+ * @level: Level to identify the depth of the nested structures.
+ *
+ * @return: expected maximum length of the QMI message or 0 on failure.
+ */
+static int qmi_calc_max_msg_len(struct elem_info *ei_array,
+ int level)
+{
+ int max_msg_len = 0;
+ struct elem_info *temp_ei;
+
+ if (!ei_array)
+ return max_msg_len;
+
+ for (temp_ei = ei_array; temp_ei->data_type != QMI_EOTI; temp_ei++) {
+ /* Flag to identify the optional element is not encoded */
+ if (temp_ei->data_type == QMI_OPT_FLAG)
+ continue;
+
+ if (temp_ei->data_type == QMI_DATA_LEN) {
+ max_msg_len += (temp_ei->elem_size == sizeof(uint8_t) ?
+ sizeof(uint8_t) : sizeof(uint16_t));
+ continue;
+ } else if (temp_ei->data_type == QMI_STRUCT) {
+ max_msg_len += qmi_calc_max_msg_len(temp_ei->ei_array,
+ (level + 1));
+ } else {
+ max_msg_len += (temp_ei->elem_len * temp_ei->elem_size);
+ }
+
+ /*
+ * Type & Length info. not prepended for elements in the
+ * nested structure.
+ */
+ if (level == 1)
+ max_msg_len += (TLV_TYPE_SIZE + TLV_LEN_SIZE);
+ }
+ return max_msg_len;
+}
+
+/**
+ * qmi_verify_max_msg_len() - Verify the maximum length of a QMI message
+ * @desc: Pointer to structure descriptor.
+ *
+ * @return: true if the maximum message length embedded in structure
+ * descriptor matches the calculated value, else false.
+ */
+bool qmi_verify_max_msg_len(struct msg_desc *desc)
+{
+ int calc_max_msg_len;
+
+ if (!desc)
+ return false;
+
+ calc_max_msg_len = qmi_calc_max_msg_len(desc->ei_array, 1);
+ if (calc_max_msg_len != desc->max_msg_len) {
+ pr_err("%s: Calc. len %d != Passed len %d\n",
+ __func__, calc_max_msg_len, desc->max_msg_len);
+ return false;
+ }
+ return true;
+}
+
+/**
* qmi_kernel_encode() - Encode to QMI message wire format
* @desc: Pointer to structure descriptor.
* @out_buf: Buffer to hold the encoded QMI message.
@@ -103,6 +168,7 @@
void *in_c_struct)
{
int enc_level = 1;
+ int ret, calc_max_msg_len;
if (!desc || !desc->ei_array)
return -EINVAL;
@@ -113,8 +179,14 @@
if (desc->max_msg_len < out_buf_len)
return -ETOOSMALL;
- return _qmi_kernel_encode(desc->ei_array, out_buf,
- in_c_struct, enc_level);
+ ret = _qmi_kernel_encode(desc->ei_array, out_buf,
+ in_c_struct, out_buf_len, enc_level);
+ if (ret == -ETOOSMALL) {
+ calc_max_msg_len = qmi_calc_max_msg_len(desc->ei_array, 1);
+ pr_err("%s: Calc. len %d != Out buf len %d\n",
+ __func__, calc_max_msg_len, out_buf_len);
+ }
+ return ret;
}
EXPORT_SYMBOL(qmi_kernel_encode);
@@ -152,6 +224,7 @@
* @buf_dst: Buffer to store the encoded information.
* @buf_src: Buffer containing the elements to be encoded.
* @elem_len: Number of elements, in the buf_src, to be encoded.
+ * @out_buf_len: Available space in the encode buffer.
* @enc_level: Depth of the nested structure from the main structure.
*
* @return: Mumber of bytes of encoded information, on success.
@@ -165,14 +238,16 @@
*/
static int qmi_encode_struct_elem(struct elem_info *ei_array,
void *buf_dst, void *buf_src,
- uint32_t elem_len, int enc_level)
+ uint32_t elem_len, uint32_t out_buf_len,
+ int enc_level)
{
int i, rc, encoded_bytes = 0;
struct elem_info *temp_ei = ei_array;
for (i = 0; i < elem_len; i++) {
- rc = _qmi_kernel_encode(temp_ei->ei_array,
- buf_dst, buf_src, enc_level);
+ rc = _qmi_kernel_encode(temp_ei->ei_array, buf_dst, buf_src,
+ (out_buf_len - encoded_bytes),
+ enc_level);
if (rc < 0) {
pr_err("%s: STRUCT Encode failure\n", __func__);
return rc;
@@ -214,6 +289,7 @@
* @ei_array: Struct info array describing the structure to be encoded.
* @out_buf: Buffer to hold the encoded QMI message.
* @in_c_struct: Pointer to the C structure to be encoded.
+ * @out_buf_len: Available space in the encode buffer.
* @enc_level: Encode level to indicate the depth of the nested structure,
* within the main structure, being encoded.
*
@@ -222,7 +298,7 @@
*/
static int _qmi_kernel_encode(struct elem_info *ei_array,
void *out_buf, void *in_c_struct,
- int enc_level)
+ uint32_t out_buf_len, int enc_level)
{
struct elem_info *temp_ei = ei_array;
uint8_t opt_flag_value = 0;
@@ -268,6 +344,13 @@
memcpy(&data_len_value, buf_src, temp_ei->elem_size);
data_len_sz = temp_ei->elem_size == sizeof(uint8_t) ?
sizeof(uint8_t) : sizeof(uint16_t);
+ /* Check to avoid out of range buffer access */
+ if ((data_len_sz + encoded_bytes + TLV_LEN_SIZE +
+ TLV_TYPE_SIZE) > out_buf_len) {
+ pr_err("%s: Too Small Buffer @DATA_LEN\n",
+ __func__);
+ return -ETOOSMALL;
+ }
rc = qmi_encode_basic_elem(buf_dst, &data_len_value,
1, data_len_sz);
if (data_len_value) {
@@ -285,6 +368,14 @@
case QMI_UNSIGNED_8_BYTE:
case QMI_SIGNED_2_BYTE_ENUM:
case QMI_SIGNED_4_BYTE_ENUM:
+ /* Check to avoid out of range buffer access */
+ if (((data_len_value * temp_ei->elem_size) +
+ encoded_bytes + TLV_LEN_SIZE + TLV_TYPE_SIZE) >
+ out_buf_len) {
+ pr_err("%s: Too Small Buffer @data_type:%d\n",
+ __func__, temp_ei->data_type);
+ return -ETOOSMALL;
+ }
rc = qmi_encode_basic_elem(buf_dst, buf_src,
data_len_value, temp_ei->elem_size);
QMI_ENCODE_LOG_ELEM(enc_level, data_len_value,
@@ -295,7 +386,8 @@
case QMI_STRUCT:
rc = qmi_encode_struct_elem(temp_ei, buf_dst, buf_src,
- data_len_value, (enc_level + 1));
+ data_len_value, (out_buf_len - encoded_bytes),
+ (enc_level + 1));
if (rc < 0)
return rc;
UPDATE_ENCODE_VARIABLES(temp_ei, buf_dst,
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index 7dc95af..73ac1b0 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -913,14 +913,12 @@
* This array describes the order lists are fallen back to when
* the free lists for the desirable migrate type are depleted
*/
-static int fallbacks[MIGRATE_TYPES][4] = {
+static int fallbacks[MIGRATE_TYPES][3] = {
[MIGRATE_UNMOVABLE] = { MIGRATE_RECLAIMABLE, MIGRATE_MOVABLE, MIGRATE_RESERVE },
[MIGRATE_RECLAIMABLE] = { MIGRATE_UNMOVABLE, MIGRATE_MOVABLE, MIGRATE_RESERVE },
-#ifdef CONFIG_CMA
- [MIGRATE_MOVABLE] = { MIGRATE_CMA, MIGRATE_RECLAIMABLE, MIGRATE_UNMOVABLE, MIGRATE_RESERVE },
- [MIGRATE_CMA] = { MIGRATE_RESERVE }, /* Never used */
-#else
[MIGRATE_MOVABLE] = { MIGRATE_RECLAIMABLE, MIGRATE_UNMOVABLE, MIGRATE_RESERVE },
+#ifdef CONFIG_CMA
+ [MIGRATE_CMA] = { MIGRATE_RESERVE }, /* Never used */
#endif
[MIGRATE_RESERVE] = { MIGRATE_RESERVE }, /* Never used */
[MIGRATE_ISOLATE] = { MIGRATE_RESERVE }, /* Never used */
@@ -1043,17 +1041,10 @@
* pages to the preferred allocation list. If falling
* back for a reclaimable kernel allocation, be more
* aggressive about taking ownership of free pages
- *
- * On the other hand, never change migration
- * type of MIGRATE_CMA pageblocks nor move CMA
- * pages on different free lists. We don't
- * want unmovable pages to be allocated from
- * MIGRATE_CMA areas.
*/
- if (!is_migrate_cma(migratetype) &&
- (unlikely(current_order >= pageblock_order / 2) ||
- start_migratetype == MIGRATE_RECLAIMABLE ||
- page_group_by_mobility_disabled)) {
+ if (unlikely(current_order >= pageblock_order / 2) ||
+ start_migratetype == MIGRATE_RECLAIMABLE ||
+ page_group_by_mobility_disabled) {
int pages;
pages = move_freepages_block(zone, page,
start_migratetype);
@@ -1072,14 +1063,12 @@
rmv_page_order(page);
/* Take ownership for orders >= pageblock_order */
- if (current_order >= pageblock_order &&
- !is_migrate_cma(migratetype))
+ if (current_order >= pageblock_order)
change_pageblock_range(page, current_order,
start_migratetype);
expand(zone, page, order, current_order, area,
- is_migrate_cma(migratetype)
- ? migratetype : start_migratetype);
+ start_migratetype);
trace_mm_page_alloc_extfrag(page, order, current_order,
start_migratetype, migratetype);
@@ -1100,21 +1089,27 @@
{
struct page *page;
-retry_reserve:
- page = __rmqueue_smallest(zone, order, migratetype);
+#ifdef CONFIG_CMA
+ if (migratetype == MIGRATE_MOVABLE)
+ migratetype = MIGRATE_CMA;
+#endif
- if (unlikely(!page) && migratetype != MIGRATE_RESERVE) {
- page = __rmqueue_fallback(zone, order, migratetype);
+ for(;;) {
+ page = __rmqueue_smallest(zone, order, migratetype);
+ if (likely(page) || migratetype == MIGRATE_RESERVE)
+ break;
- /*
- * Use MIGRATE_RESERVE rather than fail an allocation. goto
- * is used because __rmqueue_smallest is an inline function
- * and we want just one call site
- */
- if (!page) {
- migratetype = MIGRATE_RESERVE;
- goto retry_reserve;
+ if (is_migrate_cma(migratetype)) {
+ migratetype = MIGRATE_MOVABLE;
+ continue;
}
+
+ page = __rmqueue_fallback(zone, order, migratetype);
+ if (page)
+ break;
+
+ /* Use MIGRATE_RESERVE rather than fail an allocation. */
+ migratetype = MIGRATE_RESERVE;
}
trace_mm_page_alloc_zone_locked(page, order, migratetype);
@@ -2791,6 +2786,31 @@
#define K(x) ((x) << (PAGE_SHIFT-10))
+static void show_migration_types(unsigned char type)
+{
+ static const char types[MIGRATE_TYPES] = {
+ [MIGRATE_UNMOVABLE] = 'U',
+ [MIGRATE_RECLAIMABLE] = 'E',
+ [MIGRATE_MOVABLE] = 'M',
+ [MIGRATE_RESERVE] = 'R',
+#ifdef CONFIG_CMA
+ [MIGRATE_CMA] = 'C',
+#endif
+ [MIGRATE_ISOLATE] = 'I',
+ };
+ char tmp[MIGRATE_TYPES + 1];
+ char *p = tmp;
+ int i;
+
+ for (i = 0; i < MIGRATE_TYPES; i++) {
+ if (type & (1 << i))
+ *p++ = types[i];
+ }
+
+ *p = '\0';
+ printk("(%s) ", tmp);
+}
+
/*
* Show free area list (used inside shift_scroll-lock stuff)
* We also calculate the percentage fragmentation. We do this by counting the
@@ -2919,6 +2939,7 @@
for_each_populated_zone(zone) {
unsigned long nr[MAX_ORDER], flags, order, total = 0;
+ unsigned char types[MAX_ORDER];
if (skip_free_areas_node(filter, zone_to_nid(zone)))
continue;
@@ -2927,12 +2948,24 @@
spin_lock_irqsave(&zone->lock, flags);
for (order = 0; order < MAX_ORDER; order++) {
- nr[order] = zone->free_area[order].nr_free;
+ struct free_area *area = &zone->free_area[order];
+ int type;
+
+ nr[order] = area->nr_free;
total += nr[order] << order;
+
+ types[order] = 0;
+ for (type = 0; type < MIGRATE_TYPES; type++) {
+ if (!list_empty(&area->free_list[type]))
+ types[order] |= 1 << type;
+ }
}
spin_unlock_irqrestore(&zone->lock, flags);
- for (order = 0; order < MAX_ORDER; order++)
+ for (order = 0; order < MAX_ORDER; order++) {
printk("%lu*%lukB ", nr[order], K(1UL) << order);
+ if (nr[order])
+ show_migration_types(types[order]);
+ }
printk("= %lukB\n", K(total));
}
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index f897841..5bb2847 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -36,6 +36,8 @@
* YOSHIFUJI Hideaki @USAGI : improved source address
* selection; consider scope,
* status etc.
+ * Harout S. Hedeshian : procfs flag to toggle automatic
+ * addition of prefix route
*/
#include <linux/errno.h>
@@ -197,6 +199,7 @@
.accept_source_route = 0, /* we do not accept RH0 by default. */
.disable_ipv6 = 0,
.accept_dad = 1,
+ .accept_ra_prefix_route = 1,
};
static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {
@@ -231,6 +234,7 @@
.accept_source_route = 0, /* we do not accept RH0 by default. */
.disable_ipv6 = 0,
.accept_dad = 1,
+ .accept_ra_prefix_route = 1,
};
/* IPv6 Wildcard Address and Loopback Address defined by RFC2553 */
@@ -1908,8 +1912,10 @@
flags |= RTF_EXPIRES;
expires = jiffies_to_clock_t(rt_expires);
}
- addrconf_prefix_route(&pinfo->prefix, pinfo->prefix_len,
- dev, expires, flags);
+ if (dev->ip6_ptr->cnf.accept_ra_prefix_route) {
+ addrconf_prefix_route(&pinfo->prefix,
+ pinfo->prefix_len, dev, expires, flags);
+ }
}
if (rt)
dst_release(&rt->dst);
@@ -4598,6 +4604,13 @@
.proc_handler = proc_dointvec
},
{
+ .procname = "accept_ra_prefix_route",
+ .data = &ipv6_devconf.accept_ra_prefix_route,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec,
+ },
+ {
/* sentinel */
}
},
diff --git a/sound/soc/codecs/wcd9310.c b/sound/soc/codecs/wcd9310.c
index e672cdb..85214ec 100644
--- a/sound/soc/codecs/wcd9310.c
+++ b/sound/soc/codecs/wcd9310.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, Code Aurora Forum. 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
@@ -99,7 +99,8 @@
RX_MIX1_INP_SEL_RX7,
};
-#define TABLA_COMP_DIGITAL_GAIN_OFFSET 3
+#define TABLA_COMP_DIGITAL_GAIN_HP_OFFSET 3
+#define TABLA_COMP_DIGITAL_GAIN_LINEOUT_OFFSET 6
#define TABLA_MCLK_RATE_12288KHZ 12288000
#define TABLA_MCLK_RATE_9600KHZ 9600000
@@ -127,7 +128,9 @@
#define TABLA_MBHC_GND_MIC_SWAP_THRESHOLD 2
-#define TABLA_ACQUIRE_LOCK(x) do { mutex_lock(&x); } while (0)
+#define TABLA_ACQUIRE_LOCK(x) do { \
+ mutex_lock_nested(&x, SINGLE_DEPTH_NESTING); \
+} while (0)
#define TABLA_RELEASE_LOCK(x) do { mutex_unlock(&x); } while (0)
static const DECLARE_TLV_DB_SCALE(digital_gain, 0, 1, 0);
@@ -188,6 +191,16 @@
COMPANDER_FS_MAX,
};
+enum {
+ COMP_SHUTDWN_TIMEOUT_PCM_1 = 0,
+ COMP_SHUTDWN_TIMEOUT_PCM_240,
+ COMP_SHUTDWN_TIMEOUT_PCM_480,
+ COMP_SHUTDWN_TIMEOUT_PCM_960,
+ COMP_SHUTDWN_TIMEOUT_PCM_1440,
+ COMP_SHUTDWN_TIMEOUT_PCM_2880,
+ COMP_SHUTDWN_TIMEOUT_PCM_5760,
+};
+
/* Flags to track of PA and DAC state.
* PA and DAC should be tracked separately as AUXPGA loopback requires
* only PA to be turned on without DAC being on. */
@@ -203,6 +216,7 @@
u32 peak_det_timeout;
u32 rms_meter_div_fact;
u32 rms_meter_resamp_fact;
+ u32 shutdown_timeout;
};
/* Data used by MBHC */
@@ -325,7 +339,7 @@
struct wcd9xxx_pdata *pdata;
u32 anc_slot;
-
+ bool anc_func;
bool no_mic_headset_override;
/* Delayed work to report long button press */
struct delayed_work mbhc_btn_dwork;
@@ -399,7 +413,7 @@
static const u32 comp_shift[] = {
0,
- 2,
+ 1,
};
static const int comp_rx_path[] = {
@@ -412,28 +426,43 @@
COMPANDER_MAX,
};
-static const struct comp_sample_dependent_params comp_samp_params[] = {
+static const struct comp_sample_dependent_params
+ comp_samp_params[COMPANDER_FS_MAX] = {
{
- .peak_det_timeout = 0x2,
- .rms_meter_div_fact = 0x8 << 4,
- .rms_meter_resamp_fact = 0x21,
- },
- {
- .peak_det_timeout = 0x3,
+ .peak_det_timeout = 0x6,
.rms_meter_div_fact = 0x9 << 4,
- .rms_meter_resamp_fact = 0x28,
+ .rms_meter_resamp_fact = 0x06,
+ .shutdown_timeout = COMP_SHUTDWN_TIMEOUT_PCM_240 << 3,
},
-
{
- .peak_det_timeout = 0x5,
+ .peak_det_timeout = 0x7,
+ .rms_meter_div_fact = 0xA << 4,
+ .rms_meter_resamp_fact = 0x0C,
+ .shutdown_timeout = COMP_SHUTDWN_TIMEOUT_PCM_480 << 3,
+ },
+ {
+ .peak_det_timeout = 0x8,
+ .rms_meter_div_fact = 0xB << 4,
+ .rms_meter_resamp_fact = 0x30,
+ .shutdown_timeout = COMP_SHUTDWN_TIMEOUT_PCM_960 << 3,
+ },
+ {
+ .peak_det_timeout = 0x9,
.rms_meter_div_fact = 0xB << 4,
.rms_meter_resamp_fact = 0x28,
+ .shutdown_timeout = COMP_SHUTDWN_TIMEOUT_PCM_1440 << 3,
},
-
{
- .peak_det_timeout = 0x5,
- .rms_meter_div_fact = 0xB << 4,
- .rms_meter_resamp_fact = 0x28,
+ .peak_det_timeout = 0xA,
+ .rms_meter_div_fact = 0xC << 4,
+ .rms_meter_resamp_fact = 0x50,
+ .shutdown_timeout = COMP_SHUTDWN_TIMEOUT_PCM_2880 << 3,
+ },
+ {
+ .peak_det_timeout = 0xB,
+ .rms_meter_div_fact = 0xC << 4,
+ .rms_meter_resamp_fact = 0x50,
+ .shutdown_timeout = COMP_SHUTDWN_TIMEOUT_PCM_5760 << 3,
},
};
@@ -484,6 +513,8 @@
snd_soc_update_bits(codec, TABLA_A_CDC_CLSG_CTL, 0x08, 0x00);
snd_soc_update_bits(codec, TABLA_A_CDC_CLK_OTHR_CTL, 0x01,
0x00);
+ snd_soc_update_bits(codec, TABLA_A_CDC_CLK_OTHR_RESET_CTL, 0x10,
+ 0x00);
snd_soc_update_bits(codec, TABLA_A_CP_STATIC, 0x08, 0x00);
break;
}
@@ -508,6 +539,48 @@
return 0;
}
+static int tabla_get_anc_func(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
+ ucontrol->value.integer.value[0] = (tabla->anc_func == true ? 1 : 0);
+ return 0;
+}
+
+static int tabla_put_anc_func(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = &codec->dapm;
+
+ mutex_lock(&dapm->codec->mutex);
+
+ tabla->anc_func = (!ucontrol->value.integer.value[0] ? false : true);
+
+ dev_dbg(codec->dev, "%s: anc_func %x", __func__, tabla->anc_func);
+
+ if (tabla->anc_func == true) {
+ snd_soc_dapm_enable_pin(dapm, "ANC HPHR");
+ snd_soc_dapm_enable_pin(dapm, "ANC HPHL");
+ snd_soc_dapm_enable_pin(dapm, "ANC HEADPHONE");
+ snd_soc_dapm_disable_pin(dapm, "HPHR");
+ snd_soc_dapm_disable_pin(dapm, "HPHL");
+ snd_soc_dapm_disable_pin(dapm, "HEADPHONE");
+ } else {
+ snd_soc_dapm_disable_pin(dapm, "ANC HPHR");
+ snd_soc_dapm_disable_pin(dapm, "ANC HPHL");
+ snd_soc_dapm_disable_pin(dapm, "ANC HEADPHONE");
+ snd_soc_dapm_enable_pin(dapm, "HPHR");
+ snd_soc_dapm_enable_pin(dapm, "HPHL");
+ snd_soc_dapm_enable_pin(dapm, "HEADPHONE");
+ }
+ snd_soc_dapm_sync(dapm);
+ mutex_unlock(&dapm->codec->mutex);
+ return 0;
+}
+
static int tabla_pa_gain_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -726,7 +799,7 @@
static int tabla_compander_gain_offset(
struct snd_soc_codec *codec, u32 enable,
- unsigned int reg, int mask, int event)
+ unsigned int reg, int mask, int event, u32 comp)
{
int pa_mode = snd_soc_read(codec, reg) & mask;
int gain_offset = 0;
@@ -736,10 +809,21 @@
* if PMD && pa_mode is comp -> offset is -3: PMU compander is on.
*/
- if (SND_SOC_DAPM_EVENT_ON(event) && (enable != 0))
- gain_offset = TABLA_COMP_DIGITAL_GAIN_OFFSET;
- if (SND_SOC_DAPM_EVENT_OFF(event) && (pa_mode == 0))
- gain_offset = -TABLA_COMP_DIGITAL_GAIN_OFFSET;
+ if (SND_SOC_DAPM_EVENT_ON(event) && (enable != 0)) {
+ if (comp == COMPANDER_1)
+ gain_offset = TABLA_COMP_DIGITAL_GAIN_HP_OFFSET;
+ if (comp == COMPANDER_2)
+ gain_offset = TABLA_COMP_DIGITAL_GAIN_LINEOUT_OFFSET;
+ }
+ if (SND_SOC_DAPM_EVENT_OFF(event) && (pa_mode == 0)) {
+ if (comp == COMPANDER_1)
+ gain_offset = -TABLA_COMP_DIGITAL_GAIN_HP_OFFSET;
+ if (comp == COMPANDER_2)
+ gain_offset = -TABLA_COMP_DIGITAL_GAIN_LINEOUT_OFFSET;
+
+ }
+ pr_debug("%s: compander #%d gain_offset %d\n",
+ __func__, comp + 1, gain_offset);
return gain_offset;
}
@@ -762,38 +846,38 @@
if (compander == COMPANDER_1) {
gain_offset = tabla_compander_gain_offset(codec, enable,
- TABLA_A_RX_HPH_L_GAIN, mask, event);
+ TABLA_A_RX_HPH_L_GAIN, mask, event, compander);
snd_soc_update_bits(codec, TABLA_A_RX_HPH_L_GAIN, mask, value);
gain = snd_soc_read(codec, TABLA_A_CDC_RX1_VOL_CTL_B2_CTL);
snd_soc_update_bits(codec, TABLA_A_CDC_RX1_VOL_CTL_B2_CTL,
0xFF, gain - gain_offset);
gain_offset = tabla_compander_gain_offset(codec, enable,
- TABLA_A_RX_HPH_R_GAIN, mask, event);
+ TABLA_A_RX_HPH_R_GAIN, mask, event, compander);
snd_soc_update_bits(codec, TABLA_A_RX_HPH_R_GAIN, mask, value);
gain = snd_soc_read(codec, TABLA_A_CDC_RX2_VOL_CTL_B2_CTL);
snd_soc_update_bits(codec, TABLA_A_CDC_RX2_VOL_CTL_B2_CTL,
0xFF, gain - gain_offset);
} else if (compander == COMPANDER_2) {
gain_offset = tabla_compander_gain_offset(codec, enable,
- TABLA_A_RX_LINE_1_GAIN, mask, event);
+ TABLA_A_RX_LINE_1_GAIN, mask, event, compander);
snd_soc_update_bits(codec, TABLA_A_RX_LINE_1_GAIN, mask, value);
gain = snd_soc_read(codec, TABLA_A_CDC_RX3_VOL_CTL_B2_CTL);
snd_soc_update_bits(codec, TABLA_A_CDC_RX3_VOL_CTL_B2_CTL,
0xFF, gain - gain_offset);
gain_offset = tabla_compander_gain_offset(codec, enable,
- TABLA_A_RX_LINE_3_GAIN, mask, event);
+ TABLA_A_RX_LINE_3_GAIN, mask, event, compander);
snd_soc_update_bits(codec, TABLA_A_RX_LINE_3_GAIN, mask, value);
gain = snd_soc_read(codec, TABLA_A_CDC_RX4_VOL_CTL_B2_CTL);
snd_soc_update_bits(codec, TABLA_A_CDC_RX4_VOL_CTL_B2_CTL,
0xFF, gain - gain_offset);
gain_offset = tabla_compander_gain_offset(codec, enable,
- TABLA_A_RX_LINE_2_GAIN, mask, event);
+ TABLA_A_RX_LINE_2_GAIN, mask, event, compander);
snd_soc_update_bits(codec, TABLA_A_RX_LINE_2_GAIN, mask, value);
gain = snd_soc_read(codec, TABLA_A_CDC_RX5_VOL_CTL_B2_CTL);
snd_soc_update_bits(codec, TABLA_A_CDC_RX5_VOL_CTL_B2_CTL,
0xFF, gain - gain_offset);
gain_offset = tabla_compander_gain_offset(codec, enable,
- TABLA_A_RX_LINE_4_GAIN, mask, event);
+ TABLA_A_RX_LINE_4_GAIN, mask, event, compander);
snd_soc_update_bits(codec, TABLA_A_RX_LINE_4_GAIN, mask, value);
gain = snd_soc_read(codec, TABLA_A_CDC_RX6_VOL_CTL_B2_CTL);
snd_soc_update_bits(codec, TABLA_A_CDC_RX6_VOL_CTL_B2_CTL,
@@ -823,10 +907,11 @@
int comp = ((struct soc_multi_mixer_control *)
kcontrol->private_value)->max;
int value = ucontrol->value.integer.value[0];
-
+ pr_debug("%s: compander #%d enable %d\n",
+ __func__, comp + 1, value);
if (value == tabla->comp_enabled[comp]) {
pr_debug("%s: compander #%d enable %d no change\n",
- __func__, comp, value);
+ __func__, comp + 1, value);
return 0;
}
tabla->comp_enabled[comp] = value;
@@ -841,34 +926,44 @@
struct snd_soc_codec *codec = w->codec;
struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
u32 rate = tabla->comp_fs[w->shift];
-
+ u32 status;
+ unsigned long timeout;
+ pr_debug("%s: compander #%d enable %d event %d\n",
+ __func__, w->shift + 1,
+ tabla->comp_enabled[w->shift], event);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
if (tabla->comp_enabled[w->shift] != 0) {
/* Enable both L/R compander clocks */
snd_soc_update_bits(codec,
TABLA_A_CDC_CLK_RX_B2_CTL,
- 0x03 << comp_shift[w->shift],
- 0x03 << comp_shift[w->shift]);
- /* Clar the HALT for the compander*/
+ 1 << comp_shift[w->shift],
+ 1 << comp_shift[w->shift]);
+ /* Clear the HALT for the compander*/
snd_soc_update_bits(codec,
TABLA_A_CDC_COMP1_B1_CTL +
w->shift * 8, 1 << 2, 0);
/* Toggle compander reset bits*/
snd_soc_update_bits(codec,
TABLA_A_CDC_CLK_OTHR_RESET_CTL,
- 0x03 << comp_shift[w->shift],
- 0x03 << comp_shift[w->shift]);
+ 1 << comp_shift[w->shift],
+ 1 << comp_shift[w->shift]);
snd_soc_update_bits(codec,
TABLA_A_CDC_CLK_OTHR_RESET_CTL,
- 0x03 << comp_shift[w->shift], 0);
+ 1 << comp_shift[w->shift], 0);
tabla_config_gain_compander(codec, w->shift, 1, event);
+ /* Compander enable -> 0x370/0x378*/
+ snd_soc_update_bits(codec, TABLA_A_CDC_COMP1_B1_CTL +
+ w->shift * 8, 0x03, 0x03);
/* Update the RMS meter resampling*/
snd_soc_update_bits(codec,
TABLA_A_CDC_COMP1_B3_CTL +
w->shift * 8, 0xFF, 0x01);
+ snd_soc_update_bits(codec,
+ TABLA_A_CDC_COMP1_B2_CTL +
+ w->shift * 8, 0xF0, 0x50);
/* Wait for 1ms*/
- usleep_range(1000, 1000);
+ usleep_range(5000, 5000);
}
break;
case SND_SOC_DAPM_POST_PMU:
@@ -885,31 +980,59 @@
snd_soc_update_bits(codec, TABLA_A_CDC_COMP1_B3_CTL +
w->shift * 8, 0xFF,
comp_samp_params[rate].rms_meter_resamp_fact);
- /* Compander enable -> 0x370/0x378*/
snd_soc_update_bits(codec, TABLA_A_CDC_COMP1_B1_CTL +
- w->shift * 8, 0x03, 0x03);
+ w->shift * 8, 0x38,
+ comp_samp_params[rate].shutdown_timeout);
}
break;
case SND_SOC_DAPM_PRE_PMD:
- /* Halt the compander*/
- snd_soc_update_bits(codec, TABLA_A_CDC_COMP1_B1_CTL +
- w->shift * 8, 1 << 2, 1 << 2);
+ if (tabla->comp_enabled[w->shift] != 0) {
+ status = snd_soc_read(codec,
+ TABLA_A_CDC_COMP1_SHUT_DOWN_STATUS +
+ w->shift * 8);
+ pr_debug("%s: compander #%d shutdown status %d in event %d\n",
+ __func__, w->shift + 1, status, event);
+ /* Halt the compander*/
+ snd_soc_update_bits(codec, TABLA_A_CDC_COMP1_B1_CTL +
+ w->shift * 8, 1 << 2, 1 << 2);
+ }
break;
case SND_SOC_DAPM_POST_PMD:
- /* Restore the gain */
- tabla_config_gain_compander(codec, w->shift,
- tabla->comp_enabled[w->shift], event);
- /* Disable the compander*/
- snd_soc_update_bits(codec, TABLA_A_CDC_COMP1_B1_CTL +
- w->shift * 8, 0x03, 0x00);
- /* Turn off the clock for compander in pair*/
- snd_soc_update_bits(codec, TABLA_A_CDC_CLK_RX_B2_CTL,
- 0x03 << comp_shift[w->shift], 0);
+ if (tabla->comp_enabled[w->shift] != 0) {
+ /* Wait up to a second for shutdown complete */
+ timeout = jiffies + HZ;
+ do {
+ status = snd_soc_read(codec,
+ TABLA_A_CDC_COMP1_SHUT_DOWN_STATUS +
+ w->shift * 8);
+ if (status == 0x3)
+ break;
+ usleep_range(5000, 5000);
+ } while (!(time_after(jiffies, timeout)));
+ /* Restore the gain */
+ tabla_config_gain_compander(codec, w->shift,
+ tabla->comp_enabled[w->shift],
+ event);
+ /* Disable the compander*/
+ snd_soc_update_bits(codec, TABLA_A_CDC_COMP1_B1_CTL +
+ w->shift * 8, 0x03, 0x00);
+ /* Turn off the clock for compander in pair*/
+ snd_soc_update_bits(codec, TABLA_A_CDC_CLK_RX_B2_CTL,
+ 0x03 << comp_shift[w->shift], 0);
+ /* Clear the HALT for the compander*/
+ snd_soc_update_bits(codec,
+ TABLA_A_CDC_COMP1_B1_CTL +
+ w->shift * 8, 1 << 2, 0);
+ }
break;
}
return 0;
}
+static const char *const tabla_anc_func_text[] = {"OFF", "ON"};
+static const struct soc_enum tabla_anc_func_enum =
+ SOC_ENUM_SINGLE_EXT(2, tabla_anc_func_text);
+
static const char *tabla_ear_pa_gain_text[] = {"POS_6_DB", "POS_2_DB"};
static const struct soc_enum tabla_ear_pa_gain_enum[] = {
SOC_ENUM_SINGLE_EXT(2, tabla_ear_pa_gain_text),
@@ -1053,6 +1176,8 @@
SOC_SINGLE_EXT("ANC Slot", SND_SOC_NOPM, 0, 0, 100, tabla_get_anc_slot,
tabla_put_anc_slot),
+ SOC_ENUM_EXT("ANC Function", tabla_anc_func_enum, tabla_get_anc_func,
+ tabla_put_anc_func),
SOC_ENUM("TX1 HPF cut off", cf_dec1_enum),
SOC_ENUM("TX2 HPF cut off", cf_dec2_enum),
SOC_ENUM("TX3 HPF cut off", cf_dec3_enum),
@@ -2399,111 +2524,6 @@
return 0;
}
-static int tabla_codec_enable_anc(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
-{
- struct snd_soc_codec *codec = w->codec;
- const char *filename;
- const struct firmware *fw;
- int i;
- int ret;
- int num_anc_slots;
- struct anc_header *anc_head;
- struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
- u32 anc_writes_size = 0;
- int anc_size_remaining;
- u32 *anc_ptr;
- u16 reg;
- u8 mask, val, old_val;
-
- pr_debug("%s %d\n", __func__, event);
- switch (event) {
- case SND_SOC_DAPM_PRE_PMU:
-
- filename = "wcd9310/wcd9310_anc.bin";
-
- ret = request_firmware(&fw, filename, codec->dev);
- if (ret != 0) {
- dev_err(codec->dev, "Failed to acquire ANC data: %d\n",
- ret);
- return -ENODEV;
- }
-
- if (fw->size < sizeof(struct anc_header)) {
- dev_err(codec->dev, "Not enough data\n");
- release_firmware(fw);
- return -ENOMEM;
- }
-
- /* First number is the number of register writes */
- anc_head = (struct anc_header *)(fw->data);
- anc_ptr = (u32 *)((u32)fw->data + sizeof(struct anc_header));
- anc_size_remaining = fw->size - sizeof(struct anc_header);
- num_anc_slots = anc_head->num_anc_slots;
-
- if (tabla->anc_slot >= num_anc_slots) {
- dev_err(codec->dev, "Invalid ANC slot selected\n");
- release_firmware(fw);
- return -EINVAL;
- }
-
- for (i = 0; i < num_anc_slots; i++) {
-
- if (anc_size_remaining < TABLA_PACKED_REG_SIZE) {
- dev_err(codec->dev, "Invalid register format\n");
- release_firmware(fw);
- return -EINVAL;
- }
- anc_writes_size = (u32)(*anc_ptr);
- anc_size_remaining -= sizeof(u32);
- anc_ptr += 1;
-
- if (anc_writes_size * TABLA_PACKED_REG_SIZE
- > anc_size_remaining) {
- dev_err(codec->dev, "Invalid register format\n");
- release_firmware(fw);
- return -ENOMEM;
- }
-
- if (tabla->anc_slot == i)
- break;
-
- anc_size_remaining -= (anc_writes_size *
- TABLA_PACKED_REG_SIZE);
- anc_ptr += anc_writes_size;
- }
- if (i == num_anc_slots) {
- dev_err(codec->dev, "Selected ANC slot not present\n");
- release_firmware(fw);
- return -ENOMEM;
- }
-
- for (i = 0; i < anc_writes_size; i++) {
- TABLA_CODEC_UNPACK_ENTRY(anc_ptr[i], reg,
- mask, val);
- old_val = snd_soc_read(codec, reg);
- snd_soc_write(codec, reg, (old_val & ~mask) |
- (val & mask));
- }
- release_firmware(fw);
-
- TABLA_ACQUIRE_LOCK(tabla->codec_resource_lock);
- /* if MBHC polling is active, set TX7_MBHC_EN bit 7 */
- if (tabla->mbhc_polling_active)
- snd_soc_update_bits(codec, TABLA_A_TX_7_MBHC_EN, 0x80,
- 0x80);
- TABLA_RELEASE_LOCK(tabla->codec_resource_lock);
- break;
- case SND_SOC_DAPM_POST_PMD:
- /* unset TX7_MBHC_EN bit 7 */
- snd_soc_update_bits(codec, TABLA_A_TX_7_MBHC_EN, 0x80, 0x00);
-
- snd_soc_write(codec, TABLA_A_CDC_CLK_ANC_RESET_CTL, 0xFF);
- snd_soc_write(codec, TABLA_A_CDC_CLK_ANC_CLK_EN_CTL, 0);
- break;
- }
- return 0;
-}
/* called under codec_resource_lock acquisition */
static void tabla_codec_start_hs_polling(struct snd_soc_codec *codec)
@@ -2558,6 +2578,9 @@
return;
}
+ snd_soc_update_bits(codec, tabla->mbhc_bias_regs.ctl_reg, 0x01, 0x01);
+ msleep(250);
+ snd_soc_update_bits(codec, tabla->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
pr_debug("%s: leave\n", __func__);
}
@@ -3203,6 +3226,160 @@
WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
}
+static int tabla_codec_enable_anc(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ const char *filename;
+ const struct firmware *fw;
+ int i;
+ int ret;
+ int num_anc_slots;
+ struct anc_header *anc_head;
+ struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
+ u32 anc_writes_size = 0;
+ int anc_size_remaining;
+ u32 *anc_ptr;
+ u16 reg;
+ u8 mask, val, old_val;
+ u8 mbhc_micb_ctl_val;
+
+ pr_debug("%s: DAPM Event %d ANC func is %d\n",
+ __func__, event, tabla->anc_func);
+
+ if (tabla->anc_func == 0)
+ return 0;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ mbhc_micb_ctl_val = snd_soc_read(codec,
+ tabla->mbhc_bias_regs.ctl_reg);
+
+ if (!(mbhc_micb_ctl_val & 0x80)) {
+ TABLA_ACQUIRE_LOCK(tabla->codec_resource_lock);
+ tabla_codec_switch_micbias(codec, 1);
+ TABLA_RELEASE_LOCK(tabla->codec_resource_lock);
+ }
+
+ filename = "wcd9310/wcd9310_anc.bin";
+
+ ret = request_firmware(&fw, filename, codec->dev);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to acquire ANC data: %d\n",
+ ret);
+ return -ENODEV;
+ }
+
+ if (fw->size < sizeof(struct anc_header)) {
+ dev_err(codec->dev, "Not enough data\n");
+ release_firmware(fw);
+ return -ENOMEM;
+ }
+
+ /* First number is the number of register writes */
+ anc_head = (struct anc_header *)(fw->data);
+ anc_ptr = (u32 *)((u32)fw->data + sizeof(struct anc_header));
+ anc_size_remaining = fw->size - sizeof(struct anc_header);
+ num_anc_slots = anc_head->num_anc_slots;
+
+ if (tabla->anc_slot >= num_anc_slots) {
+ dev_err(codec->dev, "Invalid ANC slot selected\n");
+ release_firmware(fw);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < num_anc_slots; i++) {
+
+ if (anc_size_remaining < TABLA_PACKED_REG_SIZE) {
+ dev_err(codec->dev, "Invalid register format\n");
+ release_firmware(fw);
+ return -EINVAL;
+ }
+ anc_writes_size = (u32)(*anc_ptr);
+ anc_size_remaining -= sizeof(u32);
+ anc_ptr += 1;
+
+ if (anc_writes_size * TABLA_PACKED_REG_SIZE
+ > anc_size_remaining) {
+ dev_err(codec->dev, "Invalid register format\n");
+ release_firmware(fw);
+ return -ENOMEM;
+ }
+
+ if (tabla->anc_slot == i)
+ break;
+
+ anc_size_remaining -= (anc_writes_size *
+ TABLA_PACKED_REG_SIZE);
+ anc_ptr += anc_writes_size;
+ }
+ if (i == num_anc_slots) {
+ dev_err(codec->dev, "Selected ANC slot not present\n");
+ release_firmware(fw);
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < anc_writes_size; i++) {
+ TABLA_CODEC_UNPACK_ENTRY(anc_ptr[i], reg,
+ mask, val);
+ old_val = snd_soc_read(codec, reg);
+ snd_soc_write(codec, reg, (old_val & ~mask) |
+ (val & mask));
+ }
+ usleep_range(10000, 10000);
+ snd_soc_update_bits(codec, TABLA_A_RX_HPH_CNP_EN, 0x30, 0x30);
+ msleep(30);
+ release_firmware(fw);
+ TABLA_ACQUIRE_LOCK(tabla->codec_resource_lock);
+ /* if MBHC polling is active, set TX7_MBHC_EN bit 7 */
+ if (tabla->mbhc_polling_active)
+ snd_soc_update_bits(codec, TABLA_A_TX_7_MBHC_EN, 0x80,
+ 0x80);
+ TABLA_RELEASE_LOCK(tabla->codec_resource_lock);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* schedule work is required because at the time HPH PA DAPM
+ * event callback is called by DAPM framework, CODEC dapm mutex
+ * would have been locked while snd_soc_jack_report also
+ * attempts to acquire same lock.
+ */
+ if (w->shift == 5) {
+ clear_bit(TABLA_HPHL_PA_OFF_ACK,
+ &tabla->hph_pa_dac_state);
+ clear_bit(TABLA_HPHL_DAC_OFF_ACK,
+ &tabla->hph_pa_dac_state);
+ if (tabla->hph_status & SND_JACK_OC_HPHL)
+ schedule_work(&tabla->hphlocp_work);
+ } else if (w->shift == 4) {
+ clear_bit(TABLA_HPHR_PA_OFF_ACK,
+ &tabla->hph_pa_dac_state);
+ clear_bit(TABLA_HPHR_DAC_OFF_ACK,
+ &tabla->hph_pa_dac_state);
+ if (tabla->hph_status & SND_JACK_OC_HPHR)
+ schedule_work(&tabla->hphrocp_work);
+ }
+
+ TABLA_ACQUIRE_LOCK(tabla->codec_resource_lock);
+ tabla_codec_switch_micbias(codec, 0);
+ TABLA_RELEASE_LOCK(tabla->codec_resource_lock);
+
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_update_bits(codec, TABLA_A_RX_HPH_CNP_EN, 0x30, 0x00);
+ msleep(40);
+ /* unset TX7_MBHC_EN bit 7 */
+ snd_soc_update_bits(codec, TABLA_A_TX_7_MBHC_EN, 0x80, 0x00);
+ snd_soc_update_bits(codec, TABLA_A_CDC_ANC1_CTL, 0x01, 0x00);
+ snd_soc_update_bits(codec, TABLA_A_CDC_ANC2_CTL, 0x01, 0x00);
+ msleep(20);
+ snd_soc_write(codec, TABLA_A_CDC_CLK_ANC_RESET_CTL, 0x0F);
+ snd_soc_write(codec, TABLA_A_CDC_CLK_ANC_CLK_EN_CTL, 0);
+ snd_soc_write(codec, TABLA_A_CDC_CLK_ANC_RESET_CTL, 0xFF);
+ break;
+ }
+ return 0;
+}
+
static int tabla_hph_pa_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
@@ -3512,7 +3689,6 @@
{"ANC1 FB MUX", "EAR_HPH_L", "RX1 MIX2"},
{"ANC1 FB MUX", "EAR_LINE_1", "RX2 MIX2"},
- {"ANC", NULL, "ANC1 FB MUX"},
/* Headset (RX MIX1 and RX MIX2) */
{"HEADPHONE", NULL, "HPHL"},
@@ -3527,19 +3703,26 @@
{"HPHL DAC", NULL, "CP"},
{"HPHR DAC", NULL, "CP"},
- {"ANC", NULL, "ANC1 MUX"},
- {"ANC", NULL, "ANC2 MUX"},
+ {"ANC HEADPHONE", NULL, "ANC HPHL"},
+ {"ANC HEADPHONE", NULL, "ANC HPHR"},
+
+ {"ANC HPHL", NULL, "HPHL_PA_MIXER"},
+ {"ANC HPHR", NULL, "HPHR_PA_MIXER"},
+
{"ANC1 MUX", "ADC1", "ADC1"},
{"ANC1 MUX", "ADC2", "ADC2"},
{"ANC1 MUX", "ADC3", "ADC3"},
{"ANC1 MUX", "ADC4", "ADC4"},
+ {"ANC1 MUX", "DMIC1", "DMIC1"},
+ {"ANC1 MUX", "DMIC2", "DMIC2"},
+ {"ANC1 MUX", "DMIC3", "DMIC3"},
+ {"ANC1 MUX", "DMIC4", "DMIC4"},
{"ANC2 MUX", "ADC1", "ADC1"},
{"ANC2 MUX", "ADC2", "ADC2"},
{"ANC2 MUX", "ADC3", "ADC3"},
{"ANC2 MUX", "ADC4", "ADC4"},
- {"ANC", NULL, "CDC_CONN"},
-
+ {"ANC HPHR", NULL, "CDC_CONN"},
{"DAC1", "Switch", "RX1 CHAIN"},
{"HPHL DAC", "Switch", "RX1 CHAIN"},
{"HPHR DAC", NULL, "RX2 CHAIN"},
@@ -3566,8 +3749,8 @@
{"RX1 CHAIN", NULL, "RX1 MIX2"},
{"RX2 CHAIN", NULL, "RX2 MIX2"},
- {"RX1 CHAIN", NULL, "ANC"},
- {"RX2 CHAIN", NULL, "ANC"},
+ {"RX1 MIX2", NULL, "ANC1 MUX"},
+ {"RX2 MIX2", NULL, "ANC2 MUX"},
{"CP", NULL, "RX_BIAS"},
{"LINEOUT1 DAC", NULL, "RX_BIAS"},
@@ -3972,6 +4155,14 @@
(reg <= TABLA_A_CDC_IIR2_COEF_B5_CTL))
return 1;
+ /* ANC filter registers are not cacheable */
+ if ((reg >= TABLA_A_CDC_ANC1_FILT1_B1_CTL) &&
+ (reg <= TABLA_A_CDC_ANC1_FILT2_B3_CTL))
+ return 1;
+ if ((reg >= TABLA_A_CDC_ANC2_FILT1_B1_CTL) &&
+ (reg <= TABLA_A_CDC_ANC2_FILT2_B3_CTL))
+ return 1;
+
/* Digital gain register is not cacheable so we have to write
* the setting even it is the same
*/
@@ -3982,6 +4173,10 @@
if (reg == TABLA_A_RX_HPH_L_STATUS || reg == TABLA_A_RX_HPH_R_STATUS)
return 1;
+ if (reg == TABLA_A_CDC_COMP1_SHUT_DOWN_STATUS ||
+ reg == TABLA_A_CDC_COMP2_SHUT_DOWN_STATUS)
+ return 1;
+
return 0;
}
@@ -5200,9 +5395,13 @@
SND_SOC_DAPM_MUX("ANC1 MUX", SND_SOC_NOPM, 0, 0, &anc1_mux),
SND_SOC_DAPM_MUX("ANC2 MUX", SND_SOC_NOPM, 0, 0, &anc2_mux),
- SND_SOC_DAPM_MIXER_E("ANC", SND_SOC_NOPM, 0, 0, NULL, 0,
- tabla_codec_enable_anc, SND_SOC_DAPM_PRE_PMU |
- SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_OUTPUT("ANC HEADPHONE"),
+ SND_SOC_DAPM_PGA_E("ANC HPHL", SND_SOC_NOPM, 0, 0, NULL, 0,
+ tabla_codec_enable_anc,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("ANC HPHR", SND_SOC_NOPM, 0, 0, NULL, 0,
+ tabla_codec_enable_anc, SND_SOC_DAPM_PRE_PMU),
+
SND_SOC_DAPM_MUX("ANC1 FB MUX", SND_SOC_NOPM, 0, 0, &anc1_fb_mux),
@@ -6695,6 +6894,13 @@
* only report the mic line
*/
tabla_codec_report_plug(codec, 1, SND_JACK_HEADSET);
+ if (!tabla->mbhc_micbias_switched &&
+ tabla_is_hph_pa_on(codec)) {
+ /*If the headphone path is on, switch the micbias
+ to VDDIO to avoid noise due to button polling */
+ tabla_codec_switch_micbias(codec, 1);
+ pr_debug("%s: HPH path is still up\n", __func__);
+ }
msleep(100);
tabla_codec_start_hs_polling(codec);
} else if (plug_type == PLUG_TYPE_HIGH_HPH) {
@@ -7117,7 +7323,6 @@
} else if (plug_type == PLUG_TYPE_HEADSET) {
pr_debug("%s: Headset detected\n", __func__);
tabla_codec_report_plug(codec, 1, SND_JACK_HEADSET);
-
/* avoid false button press detect */
msleep(50);
tabla_codec_start_hs_polling(codec);
@@ -7524,6 +7729,7 @@
{
bool insert;
struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
+ struct wcd9xxx *core = dev_get_drvdata(codec->dev->parent);
bool is_removed = false;
pr_debug("%s: enter\n", __func__);
@@ -7533,6 +7739,7 @@
usleep_range(TABLA_GPIO_IRQ_DEBOUNCE_TIME_US,
TABLA_GPIO_IRQ_DEBOUNCE_TIME_US);
+ wcd9xxx_nested_irq_lock(core);
TABLA_ACQUIRE_LOCK(tabla->codec_resource_lock);
/* cancel pending button press */
@@ -7599,11 +7806,13 @@
0x08, 0x00);
/* Turn off override */
tabla_turn_onoff_override(codec, false);
+ tabla_codec_switch_micbias(codec, 0);
}
}
tabla->in_gpio_handler = false;
TABLA_RELEASE_LOCK(tabla->codec_resource_lock);
+ wcd9xxx_nested_irq_unlock(core);
pr_debug("%s: leave\n", __func__);
}
@@ -8563,6 +8772,12 @@
"tabla_gpio_irq_resend");
tabla->gpio_irq_resend = false;
+ mutex_lock(&dapm->codec->mutex);
+ snd_soc_dapm_disable_pin(dapm, "ANC HPHL");
+ snd_soc_dapm_disable_pin(dapm, "ANC HPHR");
+ snd_soc_dapm_disable_pin(dapm, "ANC HEADPHONE");
+ snd_soc_dapm_sync(dapm);
+ mutex_unlock(&dapm->codec->mutex);
#ifdef CONFIG_DEBUG_FS
if (ret == 0) {
diff --git a/sound/soc/codecs/wcd9320.c b/sound/soc/codecs/wcd9320.c
index 1546b9e..41a9cc5 100644
--- a/sound/soc/codecs/wcd9320.c
+++ b/sound/soc/codecs/wcd9320.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. 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
@@ -4892,12 +4892,60 @@
}
EXPORT_SYMBOL_GPL(taiko_hs_detect);
+static int taiko_post_reset_cb(struct wcd9xxx *wcd9xxx)
+{
+ int ret = 0;
+ struct snd_soc_codec *codec;
+ struct taiko_priv *taiko;
+
+ codec = (struct snd_soc_codec *)(wcd9xxx->ssr_priv);
+ taiko = snd_soc_codec_get_drvdata(codec);
+ mutex_lock(&codec->mutex);
+ WCD9XXX_BCL_LOCK(&taiko->resmgr);
+
+ if (codec->reg_def_copy) {
+ pr_debug("%s: Update ASOC cache", __func__);
+ kfree(codec->reg_cache);
+ codec->reg_cache = kmemdup(codec->reg_def_copy,
+ codec->reg_size, GFP_KERNEL);
+ }
+
+ wcd9xxx_resmgr_post_ssr(&taiko->resmgr);
+ if (spkr_drv_wrnd == 1)
+ snd_soc_update_bits(codec, TAIKO_A_SPKR_DRV_EN, 0x80, 0x80);
+ WCD9XXX_BCL_UNLOCK(&taiko->resmgr);
+
+ taiko_update_reg_defaults(codec);
+ taiko_codec_init_reg(codec);
+ ret = taiko_handle_pdata(taiko);
+ if (IS_ERR_VALUE(ret))
+ pr_err("%s: bad pdata\n", __func__);
+
+ wcd9xxx_mbhc_deinit(&taiko->mbhc);
+ ret = wcd9xxx_mbhc_init(&taiko->mbhc, &taiko->resmgr, codec);
+ if (ret)
+ pr_err("%s: mbhc init failed %d\n", __func__, ret);
+ else
+ wcd9xxx_mbhc_start(&taiko->mbhc, taiko->mbhc.mbhc_cfg);
+ mutex_unlock(&codec->mutex);
+ return ret;
+}
+
+
static struct wcd9xxx_reg_address taiko_reg_address = {
.micb_4_mbhc = TAIKO_A_MICB_4_MBHC,
.micb_4_int_rbias = TAIKO_A_MICB_4_INT_RBIAS,
.micb_4_ctl = TAIKO_A_MICB_4_CTL,
};
+static int wcd9xxx_ssr_register(struct wcd9xxx *control,
+ int (*post_reset_cb)(struct wcd9xxx *wcd9xxx), void *priv)
+{
+ control->post_reset = post_reset_cb;
+ control->ssr_priv = priv;
+ return 0;
+}
+
static int taiko_codec_probe(struct snd_soc_codec *codec)
{
struct wcd9xxx *control;
@@ -4912,6 +4960,8 @@
codec->control_data = dev_get_drvdata(codec->dev->parent);
control = codec->control_data;
+ wcd9xxx_ssr_register(control, taiko_post_reset_cb, (void *)codec);
+
dev_info(codec->dev, "%s()\n", __func__);
taiko = kzalloc(sizeof(struct taiko_priv), GFP_KERNEL);
diff --git a/sound/soc/codecs/wcd9xxx-mbhc.c b/sound/soc/codecs/wcd9xxx-mbhc.c
index 0f2a19c..8acc334 100644
--- a/sound/soc/codecs/wcd9xxx-mbhc.c
+++ b/sound/soc/codecs/wcd9xxx-mbhc.c
@@ -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
@@ -3177,23 +3177,28 @@
mbhc->resmgr = resmgr;
mbhc->resmgr->mbhc = mbhc;
- ret = snd_soc_jack_new(codec, "Headset Jack", WCD9XXX_JACK_MASK,
- &mbhc->headset_jack);
- if (ret) {
- pr_err("%s: Failed to create new jack\n", __func__);
- return ret;
- }
+ if (mbhc->headset_jack.jack == NULL) {
+ ret = snd_soc_jack_new(codec, "Headset Jack", WCD9XXX_JACK_MASK,
+ &mbhc->headset_jack);
+ if (ret) {
+ pr_err("%s: Failed to create new jack\n", __func__);
+ return ret;
+ }
- ret = snd_soc_jack_new(codec, "Button Jack", WCD9XXX_JACK_BUTTON_MASK,
- &mbhc->button_jack);
- if (ret) {
- pr_err("Failed to create new jack\n");
- return ret;
- }
+ ret = snd_soc_jack_new(codec, "Button Jack",
+ WCD9XXX_JACK_BUTTON_MASK,
+ &mbhc->button_jack);
+ if (ret) {
+ pr_err("Failed to create new jack\n");
+ return ret;
+ }
- INIT_DELAYED_WORK(&mbhc->mbhc_firmware_dwork, wcd9xxx_mbhc_fw_read);
- INIT_DELAYED_WORK(&mbhc->mbhc_btn_dwork, wcd9xxx_btn_lpress_fn);
- INIT_DELAYED_WORK(&mbhc->mbhc_insert_dwork, wcd9xxx_mbhc_insert_work);
+ INIT_DELAYED_WORK(&mbhc->mbhc_firmware_dwork,
+ wcd9xxx_mbhc_fw_read);
+ INIT_DELAYED_WORK(&mbhc->mbhc_btn_dwork, wcd9xxx_btn_lpress_fn);
+ INIT_DELAYED_WORK(&mbhc->mbhc_insert_dwork,
+ wcd9xxx_mbhc_insert_work);
+ }
/* Register event notifier */
mbhc->nblock.notifier_call = wcd9xxx_event_notify;
@@ -3294,6 +3299,11 @@
wcd9xxx_free_irq(cdata, WCD9XXX_IRQ_MBHC_REMOVAL, mbhc);
wcd9xxx_free_irq(cdata, WCD9XXX_IRQ_MBHC_INSERTION, mbhc);
+ wcd9xxx_free_irq(cdata, WCD9XXX_IRQ_MBHC_JACK_SWITCH, mbhc);
+ wcd9xxx_free_irq(cdata, WCD9XXX_IRQ_HPH_PA_OCPL_FAULT, mbhc);
+ wcd9xxx_free_irq(cdata, WCD9XXX_IRQ_HPH_PA_OCPR_FAULT, mbhc);
+ wcd9xxx_free_irq(cdata, WCD9XXX_IRQ_MBHC_RELEASE, mbhc);
+
if (mbhc->mbhc_fw)
release_firmware(mbhc->mbhc_fw);
diff --git a/sound/soc/codecs/wcd9xxx-resmgr.c b/sound/soc/codecs/wcd9xxx-resmgr.c
index 3952dd5..17edd4a 100644
--- a/sound/soc/codecs/wcd9xxx-resmgr.c
+++ b/sound/soc/codecs/wcd9xxx-resmgr.c
@@ -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
@@ -116,7 +116,8 @@
/* Notify bg mode change */
wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_PRE_BG_OFF);
/* Disable bg */
- snd_soc_write(resmgr->codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x00);
+ snd_soc_update_bits(resmgr->codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL,
+ 0x03, 0x00);
usleep_range(100, 100);
/* Notify bg mode change */
wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_POST_BG_OFF);
@@ -194,6 +195,49 @@
pr_debug("%s: leave\n", __func__);
}
+void wcd9xxx_resmgr_post_ssr(struct wcd9xxx_resmgr *resmgr)
+{
+ int old_bg_audio_users, old_bg_mbhc_users;
+ int old_clk_rco_users, old_clk_mclk_users;
+
+ pr_debug("%s: enter\n", __func__);
+ WCD9XXX_BCL_ASSERT_LOCKED(resmgr);
+
+ old_bg_audio_users = resmgr->bg_audio_users;
+ old_bg_mbhc_users = resmgr->bg_mbhc_users;
+ old_clk_rco_users = resmgr->clk_rco_users;
+ old_clk_mclk_users = resmgr->clk_mclk_users;
+ resmgr->bg_audio_users = 0;
+ resmgr->bg_mbhc_users = 0;
+ resmgr->bandgap_type = WCD9XXX_BANDGAP_OFF;
+ resmgr->clk_rco_users = 0;
+ resmgr->clk_mclk_users = 0;
+ resmgr->clk_type = WCD9XXX_CLK_OFF;
+
+ if (old_bg_audio_users) {
+ while (old_bg_audio_users--)
+ wcd9xxx_resmgr_get_bandgap(resmgr,
+ WCD9XXX_BANDGAP_AUDIO_MODE);
+ }
+
+ if (old_bg_mbhc_users) {
+ while (old_bg_mbhc_users--)
+ wcd9xxx_resmgr_get_bandgap(resmgr,
+ WCD9XXX_BANDGAP_MBHC_MODE);
+ }
+
+ if (old_clk_mclk_users) {
+ while (old_clk_mclk_users--)
+ wcd9xxx_resmgr_get_clk_block(resmgr, WCD9XXX_CLK_MCLK);
+ }
+
+ if (old_clk_rco_users) {
+ while (old_clk_rco_users--)
+ wcd9xxx_resmgr_get_clk_block(resmgr, WCD9XXX_CLK_RCO);
+ }
+ pr_debug("%s: leave\n", __func__);
+}
+
/*
* wcd9xxx_resmgr_get_bandgap : Vote for bandgap ref
* choice : WCD9XXX_BANDGAP_AUDIO_MODE, WCD9XXX_BANDGAP_MBHC_MODE
diff --git a/sound/soc/codecs/wcd9xxx-resmgr.h b/sound/soc/codecs/wcd9xxx-resmgr.h
index 2d04102..6c30eeb 100644
--- a/sound/soc/codecs/wcd9xxx-resmgr.h
+++ b/sound/soc/codecs/wcd9xxx-resmgr.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
@@ -158,6 +158,7 @@
enum wcd9xxx_cfilt_sel cfilt_sel);
void wcd9xxx_resmgr_bcl_lock(struct wcd9xxx_resmgr *resmgr);
+void wcd9xxx_resmgr_post_ssr(struct wcd9xxx_resmgr *resmgr);
#define WCD9XXX_BCL_LOCK(resmgr) \
{ \
pr_debug("%s: Acquiring BCL\n", __func__); \
diff --git a/sound/soc/msm/msm-dai-q6.c b/sound/soc/msm/msm-dai-q6.c
index 8cc0eaa..7381677 100644
--- a/sound/soc/msm/msm-dai-q6.c
+++ b/sound/soc/msm/msm-dai-q6.c
@@ -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
@@ -743,15 +743,14 @@
{
int rc = 0;
+ struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev);
mutex_lock(&aux_pcm_mutex);
-
- if (aux_pcm_count == 0) {
- dev_dbg(dai->dev, "%s(): dai->id %d aux_pcm_count is 0. Just"
- " return\n", __func__, dai->id);
+ dev_dbg(dai->dev, "%s dai->id = %d", __func__, dai->id);
+ if (!test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
mutex_unlock(&aux_pcm_mutex);
return;
}
-
+ clear_bit(STATUS_PORT_STARTED, dai_data->status_mask);
aux_pcm_count--;
if (aux_pcm_count > 0) {
@@ -866,24 +865,13 @@
unsigned long pcm_clk_rate;
mutex_lock(&aux_pcm_mutex);
-
- if (aux_pcm_count == 2) {
- dev_dbg(dai->dev, "%s(): dai->id %d aux_pcm_count is 2. Just"
- " return.\n", __func__, dai->id);
- mutex_unlock(&aux_pcm_mutex);
- return 0;
- } else if (aux_pcm_count > 2) {
- dev_err(dai->dev, "%s(): ERROR: dai->id %d"
- " aux_pcm_count = %d > 2\n",
- __func__, dai->id, aux_pcm_count);
- mutex_unlock(&aux_pcm_mutex);
- return 0;
- }
-
+ set_bit(STATUS_PORT_STARTED,
+ dai_data->status_mask);
+ dev_dbg(dai->dev, "%s dai->id = %d", __func__, dai->id);
aux_pcm_count++;
- if (aux_pcm_count == 2) {
- dev_dbg(dai->dev, "%s(): dai->id %d aux_pcm_count = %d after "
- " increment\n", __func__, dai->id, aux_pcm_count);
+ if (aux_pcm_count >= 2) {
+ dev_dbg(dai->dev, "%s(): dai->id %d aux_pcm_count = %d >= 2\n",
+ __func__, dai->id, aux_pcm_count);
mutex_unlock(&aux_pcm_mutex);
return 0;
}
diff --git a/sound/soc/msm/msm-pcm-lpa.c b/sound/soc/msm/msm-pcm-lpa.c
index ff0fb3b..a5be2e0 100644
--- a/sound/soc/msm/msm-pcm-lpa.c
+++ b/sound/soc/msm/msm-pcm-lpa.c
@@ -331,7 +331,7 @@
atomic_set(&prtd->stop, 1);
runtime->private_data = prtd;
lpa_audio.prtd = prtd;
- lpa_set_volume(lpa_audio.volume);
+ lpa_set_volume(0);
ret = q6asm_set_softpause(lpa_audio.prtd->audio_client, &softpause);
if (ret < 0)
pr_err("%s: Send SoftPause Param failed ret=%d\n",
diff --git a/sound/soc/msm/msm-pcm-q6.c b/sound/soc/msm/msm-pcm-q6.c
index 74136dc..8e29b5c 100644
--- a/sound/soc/msm/msm-pcm-q6.c
+++ b/sound/soc/msm/msm-pcm-q6.c
@@ -97,6 +97,25 @@
.mask = 0,
};
+static void msm_pcm_route_event_handler(enum msm_pcm_routing_event event,
+ void *priv_data)
+{
+ struct msm_audio *prtd = priv_data;
+
+ BUG_ON(!prtd);
+
+ pr_debug("%s: event %x\n", __func__, event);
+
+ switch (event) {
+ case MSM_PCM_RT_EVT_BUF_RECFG:
+ q6asm_cmd(prtd->audio_client, CMD_PAUSE);
+ q6asm_cmd(prtd->audio_client, CMD_FLUSH);
+ q6asm_run(prtd->audio_client, 0, 0, 0);
+ default:
+ break;
+ }
+}
+
static void event_handler(uint32_t opcode,
uint32_t token, uint32_t *payload, void *priv)
{
@@ -143,18 +162,33 @@
pr_debug("cmd[%d]=0x%08x\n", i, *ptrmem);
in_frame_info[token][0] = payload[2];
in_frame_info[token][1] = payload[3];
- prtd->pcm_irq_pos += in_frame_info[token][0];
- pr_debug("pcm_irq_pos=%d\n", prtd->pcm_irq_pos);
- if (atomic_read(&prtd->start))
- snd_pcm_period_elapsed(substream);
- if (atomic_read(&prtd->in_count) <= prtd->periods)
- atomic_inc(&prtd->in_count);
- wake_up(&the_locks.read_wait);
- if (prtd->mmap_flag
- && q6asm_is_cpu_buf_avail_nolock(OUT,
+
+ /* assume data size = 0 during flushing */
+ if (in_frame_info[token][0]) {
+ prtd->pcm_irq_pos += in_frame_info[token][0];
+ pr_debug("pcm_irq_pos=%d\n", prtd->pcm_irq_pos);
+ if (atomic_read(&prtd->start))
+ snd_pcm_period_elapsed(substream);
+ if (atomic_read(&prtd->in_count) <= prtd->periods)
+ atomic_inc(&prtd->in_count);
+ wake_up(&the_locks.read_wait);
+ if (prtd->mmap_flag &&
+ q6asm_is_cpu_buf_avail_nolock(OUT,
prtd->audio_client,
&size, &idx))
- q6asm_read_nolock(prtd->audio_client);
+ q6asm_read_nolock(prtd->audio_client);
+ } else {
+ pr_debug("%s: reclaim flushed buf in_count %x\n",
+ __func__, atomic_read(&prtd->in_count));
+ atomic_inc(&prtd->in_count);
+ if (atomic_read(&prtd->in_count) == prtd->periods) {
+ pr_info("%s: reclaimed all bufs\n", __func__);
+ if (atomic_read(&prtd->start))
+ snd_pcm_period_elapsed(substream);
+ wake_up(&the_locks.read_wait);
+ }
+ }
+
break;
}
case APR_BASIC_RSP_RESULT: {
@@ -627,6 +661,7 @@
struct audio_buffer *buf;
int dir, ret;
int format = FORMAT_LINEAR_PCM;
+ struct msm_pcm_routing_evt event;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
dir = IN;
@@ -650,10 +685,13 @@
pr_debug("%s: session ID %d\n", __func__,
prtd->audio_client->session);
prtd->session_id = prtd->audio_client->session;
- msm_pcm_routing_reg_phy_stream(soc_prtd->dai_link->be_id,
- prtd->audio_client->perf_mode,
- prtd->session_id, substream->stream);
- }
+ event.event_func = msm_pcm_route_event_handler;
+ event.priv_data = (void *) prtd;
+ msm_pcm_routing_reg_phy_stream_v2(soc_prtd->dai_link->be_id,
+ prtd->audio_client->perf_mode,
+ prtd->session_id,
+ substream->stream, event);
+ }
ret = q6asm_audio_client_buf_alloc_contiguous(dir,
prtd->audio_client,
diff --git a/sound/soc/msm/msm-pcm-routing.c b/sound/soc/msm/msm-pcm-routing.c
index 8237d81..6cf74d5 100644
--- a/sound/soc/msm/msm-pcm-routing.c
+++ b/sound/soc/msm/msm-pcm-routing.c
@@ -41,6 +41,12 @@
bool perf_mode;
};
+struct msm_pcm_routing_fdai_data {
+ u16 be_srate; /* track prior backend sample rate for flushing purpose */
+ int strm_id; /* ASM stream ID */
+ struct msm_pcm_routing_evt event_info;
+};
+
#define INVALID_SESSION -1
#define SESSION_TYPE_RX 0
#define SESSION_TYPE_TX 1
@@ -242,25 +248,35 @@
/* Track ASM playback & capture sessions of DAI */
-static int fe_dai_map[MSM_FRONTEND_DAI_MM_SIZE][2] = {
+static struct msm_pcm_routing_fdai_data
+ fe_dai_map[MSM_FRONTEND_DAI_MM_SIZE][2] = {
/* MULTIMEDIA1 */
- {INVALID_SESSION, INVALID_SESSION},
+ {{0, INVALID_SESSION, {NULL, NULL} },
+ {0, INVALID_SESSION, {NULL, NULL} } },
/* MULTIMEDIA2 */
- {INVALID_SESSION, INVALID_SESSION},
+ {{0, INVALID_SESSION, {NULL, NULL} },
+ {0, INVALID_SESSION, {NULL, NULL} } },
/* MULTIMEDIA3 */
- {INVALID_SESSION, INVALID_SESSION},
+ {{0, INVALID_SESSION, {NULL, NULL} },
+ {0, INVALID_SESSION, {NULL, NULL} } },
/* MULTIMEDIA4 */
- {INVALID_SESSION, INVALID_SESSION},
+ {{0, INVALID_SESSION, {NULL, NULL} },
+ {0, INVALID_SESSION, {NULL, NULL} } },
/* MULTIMEDIA5 */
- {INVALID_SESSION, INVALID_SESSION},
+ {{0, INVALID_SESSION, {NULL, NULL} },
+ {0, INVALID_SESSION, {NULL, NULL} } },
/* MULTIMEDIA6 */
- {INVALID_SESSION, INVALID_SESSION},
+ {{0, INVALID_SESSION, {NULL, NULL} },
+ {0, INVALID_SESSION, {NULL, NULL} } },
/* MULTIMEDIA7*/
- {INVALID_SESSION, INVALID_SESSION},
+ {{0, INVALID_SESSION, {NULL, NULL} },
+ {0, INVALID_SESSION, {NULL, NULL} } },
/* MULTIMEDIA8 */
- {INVALID_SESSION, INVALID_SESSION},
+ {{0, INVALID_SESSION, {NULL, NULL} },
+ {0, INVALID_SESSION, {NULL, NULL} } },
/* PSEUDO */
- {INVALID_SESSION, INVALID_SESSION},
+ {{0, INVALID_SESSION, {NULL, NULL} },
+ {0, INVALID_SESSION, {NULL, NULL} } },
};
static uint8_t is_be_dai_extproc(int be_dai)
@@ -320,10 +336,12 @@
}
mutex_lock(&routing_lock);
+
if (enable)
- fe_dai_map[fedai_id][session_type] = dspst_id;
+ fe_dai_map[fedai_id][session_type].strm_id = dspst_id;
else
- fe_dai_map[fedai_id][session_type] = INVALID_SESSION;
+ fe_dai_map[fedai_id][session_type].strm_id = INVALID_SESSION;
+
for (i = 0; i < MSM_BACKEND_DAI_MAX; i++) {
if (!is_be_dai_extproc(i) &&
(afe_get_port_type(msm_bedais[i].port_id) == port_type) &&
@@ -422,7 +440,7 @@
mutex_lock(&routing_lock);
payload.num_copps = 0; /* only RX needs to use payload */
- fe_dai_map[fedai_id][session_type] = dspst_id;
+ fe_dai_map[fedai_id][session_type].strm_id = dspst_id;
/* re-enable EQ if active */
if (eq_data[fedai_id].enable)
msm_send_eq_values(fedai_id);
@@ -472,6 +490,19 @@
mutex_unlock(&routing_lock);
}
+void msm_pcm_routing_reg_phy_stream_v2(int fedai_id, bool perf_mode,
+ int dspst_id, int stream_type,
+ struct msm_pcm_routing_evt event_info)
+{
+ msm_pcm_routing_reg_phy_stream(fedai_id, perf_mode, dspst_id,
+ stream_type);
+
+ if (stream_type == SNDRV_PCM_STREAM_PLAYBACK)
+ fe_dai_map[fedai_id][SESSION_TYPE_RX].event_info = event_info;
+ else
+ fe_dai_map[fedai_id][SESSION_TYPE_TX].event_info = event_info;
+
+}
void msm_pcm_routing_dereg_pseudo_stream(int fedai_id, int dspst_id)
{
@@ -530,8 +561,8 @@
}
}
- fe_dai_map[fedai_id][session_type] = INVALID_SESSION;
-
+ fe_dai_map[fedai_id][session_type].strm_id = INVALID_SESSION;
+ fe_dai_map[fedai_id][session_type].be_srate = 0;
mutex_unlock(&routing_lock);
}
@@ -556,6 +587,7 @@
{
int session_type, path_type;
u32 channels;
+ struct msm_pcm_routing_fdai_data *fdai;
pr_debug("%s: reg %x val %x set %x\n", __func__, reg, val, set);
@@ -582,11 +614,24 @@
voc_start_playback(set);
set_bit(val, &msm_bedais[reg].fe_sessions);
- if (msm_bedais[reg].active && fe_dai_map[val][session_type] !=
+ fdai = &fe_dai_map[val][session_type];
+ if (msm_bedais[reg].active && fdai->strm_id !=
INVALID_SESSION) {
channels = msm_bedais[reg].channel;
+ if (session_type == SESSION_TYPE_TX && fdai->be_srate &&
+ (fdai->be_srate != msm_bedais[reg].sample_rate)) {
+ pr_debug("%s: flush strm %d due diff BE rates\n",
+ __func__, fdai->strm_id);
+
+ if (fdai->event_info.event_func)
+ fdai->event_info.event_func(
+ MSM_PCM_RT_EVT_BUF_RECFG,
+ fdai->event_info.priv_data);
+ fdai->be_srate = 0; /* might not need it */
+ }
+
if ((session_type == SESSION_TYPE_RX) &&
((channels == 1) || (channels == 2))
&& msm_bedais[reg].perf_mode) {
@@ -614,7 +659,7 @@
msm_pcm_routing_build_matrix(val,
- fe_dai_map[val][session_type], path_type);
+ fdai->strm_id, path_type);
srs_port_id = msm_bedais[reg].port_id;
srs_send_params(srs_port_id, 1, 0);
}
@@ -623,11 +668,13 @@
(msm_bedais[reg].port_id == VOICE_PLAYBACK_TX))
voc_start_playback(set);
clear_bit(val, &msm_bedais[reg].fe_sessions);
- if (msm_bedais[reg].active && fe_dai_map[val][session_type] !=
+ fdai = &fe_dai_map[val][session_type];
+ if (msm_bedais[reg].active && fdai->strm_id !=
INVALID_SESSION) {
+ fdai->be_srate = msm_bedais[reg].sample_rate;
adm_close(msm_bedais[reg].port_id);
msm_pcm_routing_build_matrix(val,
- fe_dai_map[val][session_type], path_type);
+ fdai->strm_id, path_type);
}
}
if ((msm_bedais[reg].port_id == VOICE_RECORD_RX)
@@ -1212,12 +1259,12 @@
static void msm_send_eq_values(int eq_idx)
{
int result;
- struct audio_client *ac =
- q6asm_get_audio_client(fe_dai_map[eq_idx][SESSION_TYPE_RX]);
+ struct audio_client *ac = q6asm_get_audio_client(
+ fe_dai_map[eq_idx][SESSION_TYPE_RX].strm_id);
if (ac == NULL) {
pr_err("%s: Could not get audio client for session: %d\n",
- __func__, fe_dai_map[eq_idx][SESSION_TYPE_RX]);
+ __func__, fe_dai_map[eq_idx][SESSION_TYPE_RX].strm_id);
goto done;
}
@@ -3081,7 +3128,9 @@
mutex_lock(&routing_lock);
for_each_set_bit(i, &bedai->fe_sessions, MSM_FRONTEND_DAI_MM_SIZE) {
- if (fe_dai_map[i][session_type] != INVALID_SESSION) {
+ if (fe_dai_map[i][session_type].strm_id != INVALID_SESSION) {
+ fe_dai_map[i][session_type].be_srate =
+ bedai->sample_rate;
adm_close(bedai->port_id);
srs_port_id = -1;
}
@@ -3104,6 +3153,7 @@
struct msm_pcm_routing_bdai_data *bedai;
u32 channels;
bool playback, capture;
+ struct msm_pcm_routing_fdai_data *fdai;
if (be_id >= MSM_BACKEND_DAI_MAX) {
pr_err("%s: unexpected be_id %d\n", __func__, be_id);
@@ -3135,7 +3185,21 @@
capture = substream->stream == SNDRV_PCM_STREAM_CAPTURE;
for_each_set_bit(i, &bedai->fe_sessions, MSM_FRONTEND_DAI_MM_SIZE) {
- if (fe_dai_map[i][session_type] != INVALID_SESSION) {
+ fdai = &fe_dai_map[i][session_type];
+ if (fdai->strm_id != INVALID_SESSION) {
+ if (session_type == SESSION_TYPE_TX && fdai->be_srate &&
+ (fdai->be_srate != bedai->sample_rate)) {
+ pr_debug("%s: flush strm %d due diff BE rates\n",
+ __func__,
+ fdai->strm_id);
+
+ if (fdai->event_info.event_func)
+ fdai->event_info.event_func(
+ MSM_PCM_RT_EVT_BUF_RECFG,
+ fdai->event_info.priv_data);
+ fdai->be_srate = 0; /* might not need it */
+ }
+
channels = bedai->channel;
if (bedai->port_id == PSEUDOPORT_01) {
adm_multi_ch_copp_pseudo_open_v3(bedai->port_id,
@@ -3169,7 +3233,7 @@
DEFAULT_COPP_TOPOLOGY);
msm_pcm_routing_build_matrix(i,
- fe_dai_map[i][session_type], path_type);
+ fdai->strm_id, path_type);
srs_port_id = bedai->port_id;
srs_send_params(srs_port_id, 1, 0);
}
diff --git a/sound/soc/msm/msm-pcm-routing.h b/sound/soc/msm/msm-pcm-routing.h
index 0c0d3b4..b571483 100644
--- a/sound/soc/msm/msm-pcm-routing.h
+++ b/sound/soc/msm/msm-pcm-routing.h
@@ -113,6 +113,10 @@
MSM_BACKEND_DAI_MAX,
};
+enum msm_pcm_routing_event {
+ MSM_PCM_RT_EVT_BUF_RECFG,
+ MSM_PCM_RT_EVT_MAX,
+};
/* dai_id: front-end ID,
* dspst_id: DSP audio stream ID
* stream_type: playback or capture
@@ -128,6 +132,15 @@
void msm_pcm_routing_dereg_pseudo_stream(int fedai_id, int dspst_id);
+struct msm_pcm_routing_evt {
+ void (*event_func)(enum msm_pcm_routing_event, void *);
+ void *priv_data;
+};
+
+void msm_pcm_routing_reg_phy_stream_v2(int fedai_id, bool perf_mode,
+ int dspst_id, int stream_type,
+ struct msm_pcm_routing_evt event_info);
+
void msm_pcm_routing_dereg_phy_stream(int fedai_id, int stream_type);
int lpa_set_volume(unsigned volume);
diff --git a/sound/soc/msm/msm8974.c b/sound/soc/msm/msm8974.c
index 633066f..c5b2a52 100644
--- a/sound/soc/msm/msm8974.c
+++ b/sound/soc/msm/msm8974.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. 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
@@ -28,6 +28,7 @@
#include <mach/socinfo.h>
#include <qdsp6v2/msm-pcm-routing-v2.h>
#include "../codecs/wcd9320.h"
+#include <linux/io.h>
#define DRV_NAME "msm8974-asoc-taiko"
@@ -40,15 +41,21 @@
#define BTSCO_RATE_8KHZ 8000
#define BTSCO_RATE_16KHZ 16000
+static int msm8974_auxpcm_rate = 8000;
#define LO_1_SPK_AMP 0x1
#define LO_3_SPK_AMP 0x2
#define LO_2_SPK_AMP 0x4
#define LO_4_SPK_AMP 0x8
-#define GPIO_AUX_PCM_DOUT 43
-#define GPIO_AUX_PCM_DIN 44
-#define GPIO_AUX_PCM_SYNC 45
-#define GPIO_AUX_PCM_CLK 46
+#define LPAIF_OFFSET 0xFE000000
+#define LPAIF_PRI_MODE_MUXSEL (LPAIF_OFFSET + 0x2B000)
+#define LPAIF_SEC_MODE_MUXSEL (LPAIF_OFFSET + 0x2C000)
+#define LPAIF_TER_MODE_MUXSEL (LPAIF_OFFSET + 0x2D000)
+#define LPAIF_QUAD_MODE_MUXSEL (LPAIF_OFFSET + 0x2E000)
+
+#define I2S_PCM_SEL 1
+#define I2S_PCM_SEL_OFFSET 1
+
#define WCD9XXX_MBHC_DEF_BUTTONS 8
#define WCD9XXX_MBHC_DEF_RLOADS 5
@@ -57,6 +64,13 @@
/* It takes about 13ms for Class-D PAs to ramp-up */
#define EXT_CLASS_D_EN_DELAY 13000
+#define NUM_OF_AUXPCM_GPIOS 4
+
+static const char *const auxpcm_rate_text[] = {"rate_8000", "rate_16000"};
+static const struct soc_enum msm8974_auxpcm_enum[] = {
+ SOC_ENUM_SINGLE_EXT(2, auxpcm_rate_text),
+};
+
void *def_taiko_mbhc_cal(void);
static int msm_snd_enable_codec_ext_clk(struct snd_soc_codec *codec, int enable,
bool dapm);
@@ -75,11 +89,34 @@
.swap_gnd_mic = NULL,
};
+struct msm_auxpcm_gpio {
+ unsigned gpio_no;
+ const char *gpio_name;
+};
+
+struct msm_auxpcm_ctrl {
+ struct msm_auxpcm_gpio *pin_data;
+ u32 cnt;
+};
+
struct msm8974_asoc_mach_data {
int mclk_gpio;
u32 mclk_freq;
+ struct msm_auxpcm_ctrl *pri_auxpcm_ctrl;
};
+#define GPIO_NAME_INDEX 0
+#define DT_PARSE_INDEX 1
+
+static char *msm_auxpcm_gpio_name[][2] = {
+ {"PRIM_AUXPCM_CLK", "prim-auxpcm-gpio-clk"},
+ {"PRIM_AUXPCM_SYNC", "prim-auxpcm-gpio-sync"},
+ {"PRIM_AUXPCM_DIN", "prim-auxpcm-gpio-din"},
+ {"PRIM_AUXPCM_DOUT", "prim-auxpcm-gpio-dout"},
+};
+
+void *lpaif_pri_muxsel_virt_addr;
+
/* Shared channel numbers for Slimbus ports that connect APQ to MDM. */
enum {
SLIM_1_RX_1 = 145, /* BT-SCO and USB TX */
@@ -100,6 +137,7 @@
static int msm_btsco_rate = BTSCO_RATE_8KHZ;
static int msm_btsco_ch = 1;
+static int msm_hdmi_rx_ch = 2;
static struct mutex cdc_mclk_mutex;
static struct q_clkdiv *codec_clk;
@@ -376,6 +414,8 @@
static const char *const spk_function[] = {"Off", "On"};
static const char *const slim0_rx_ch_text[] = {"One", "Two"};
static const char *const slim0_tx_ch_text[] = {"One", "Two", "Three", "Four"};
+static char const *hdmi_rx_ch_text[] = {"Two", "Three", "Four", "Five",
+ "Six", "Seven", "Eight"};
static const struct soc_enum msm_enum[] = {
SOC_ENUM_SINGLE_EXT(2, spk_function),
@@ -451,6 +491,30 @@
return 0;
}
+static int msm_hdmi_rx_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_hdmi_rx_ch = %d\n", __func__,
+ msm_hdmi_rx_ch);
+ ucontrol->value.integer.value[0] = msm_hdmi_rx_ch - 2;
+
+ return 0;
+}
+
+static int msm_hdmi_rx_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_hdmi_rx_ch = ucontrol->value.integer.value[0] + 2;
+ if (msm_hdmi_rx_ch > 8) {
+ pr_err("%s: channels exceeded 8.Limiting to max channels-8\n",
+ __func__);
+ msm_hdmi_rx_ch = 8;
+ }
+ pr_debug("%s: msm_hdmi_rx_ch = %d\n", __func__, msm_hdmi_rx_ch);
+
+ return 1;
+}
+
static const struct snd_kcontrol_new int_btsco_rate_mixer_controls[] = {
SOC_ENUM_EXT("Internal BTSCO SampleRate", msm_btsco_enum[0],
msm_btsco_rate_get, msm_btsco_rate_put),
@@ -471,6 +535,30 @@
return 0;
}
+static int msm8974_auxpcm_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = msm8974_auxpcm_rate;
+ return 0;
+}
+
+static int msm8974_auxpcm_rate_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 0:
+ msm8974_auxpcm_rate = 8000;
+ break;
+ case 1:
+ msm8974_auxpcm_rate = 16000;
+ break;
+ default:
+ msm8974_auxpcm_rate = 8000;
+ break;
+ }
+ return 0;
+}
+
static int msm_auxpcm_be_params_fixup(struct snd_soc_pcm_runtime *rtd,
struct snd_pcm_hw_params *params)
{
@@ -480,8 +568,7 @@
struct snd_interval *channels =
hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
- /* PCM only supports mono output with 8khz sample rate */
- rate->min = rate->max = 8000;
+ rate->min = rate->max = msm8974_auxpcm_rate;
channels->min = channels->max = 1;
return 0;
@@ -511,64 +598,94 @@
pr_debug("%s channels->min %u channels->max %u ()\n", __func__,
channels->min, channels->max);
+ if (channels->max < 2)
+ channels->min = channels->max = 2;
rate->min = rate->max = 48000;
+ channels->min = channels->max = msm_hdmi_rx_ch;
return 0;
}
-static int msm_aux_pcm_get_gpios(void)
+static int msm_aux_pcm_get_gpios(struct snd_pcm_substream *substream)
{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_card *card = rtd->card;
+ struct msm8974_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card);
+ struct msm_auxpcm_ctrl *auxpcm_ctrl = NULL;
+ struct msm_auxpcm_gpio *pin_data = NULL;
int ret = 0;
+ int i;
+ int j;
- pr_debug("%s\n", __func__);
-
- ret = gpio_request(GPIO_AUX_PCM_DOUT, "AUX PCM DOUT");
- if (ret < 0) {
- pr_err("%s: Failed to request gpio(%d): AUX PCM DOUT",
- __func__, GPIO_AUX_PCM_DOUT);
- goto fail_dout;
+ if (pdata == NULL) {
+ pr_err("%s: pdata is NULL\n", __func__);
+ ret = -EINVAL;
+ goto err;
}
- ret = gpio_request(GPIO_AUX_PCM_DIN, "AUX PCM DIN");
- if (ret < 0) {
- pr_err("%s: Failed to request gpio(%d): AUX PCM DIN",
- __func__, GPIO_AUX_PCM_DIN);
- goto fail_din;
+ auxpcm_ctrl = pdata->pri_auxpcm_ctrl;
+
+ if (auxpcm_ctrl == NULL || auxpcm_ctrl->pin_data == NULL) {
+ pr_err("%s: Ctrl pointers are NULL\n", __func__);
+ ret = -EINVAL;
+ goto err;
+ }
+ pin_data = auxpcm_ctrl->pin_data;
+ for (i = 0; i < auxpcm_ctrl->cnt; i++, pin_data++) {
+ ret = gpio_request(pin_data->gpio_no,
+ pin_data->gpio_name);
+ pr_debug("%s: gpio = %d, gpio name = %s\n"
+ "ret = %d\n", __func__,
+ pin_data->gpio_no,
+ pin_data->gpio_name,
+ ret);
+ if (ret) {
+ pr_err("%s: Failed to request gpio %d\n",
+ __func__, pin_data->gpio_no);
+ /* Release all GPIOs on failure */
+ for (j = i; j >= 0; j--)
+ gpio_free(pin_data->gpio_no);
+ goto err;
+ }
}
- ret = gpio_request(GPIO_AUX_PCM_SYNC, "AUX PCM SYNC");
- if (ret < 0) {
- pr_err("%s: Failed to request gpio(%d): AUX PCM SYNC",
- __func__, GPIO_AUX_PCM_SYNC);
- goto fail_sync;
- }
- ret = gpio_request(GPIO_AUX_PCM_CLK, "AUX PCM CLK");
- if (ret < 0) {
- pr_err("%s: Failed to request gpio(%d): AUX PCM CLK",
- __func__, GPIO_AUX_PCM_CLK);
- goto fail_clk;
- }
-
- return 0;
-
-fail_clk:
- gpio_free(GPIO_AUX_PCM_SYNC);
-fail_sync:
- gpio_free(GPIO_AUX_PCM_DIN);
-fail_din:
- gpio_free(GPIO_AUX_PCM_DOUT);
-fail_dout:
-
+err:
return ret;
}
-static int msm_aux_pcm_free_gpios(void)
-{
- gpio_free(GPIO_AUX_PCM_DIN);
- gpio_free(GPIO_AUX_PCM_DOUT);
- gpio_free(GPIO_AUX_PCM_SYNC);
- gpio_free(GPIO_AUX_PCM_CLK);
- return 0;
+static int msm_aux_pcm_free_gpios(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_card *card = rtd->card;
+ struct msm8974_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card);
+ struct msm_auxpcm_ctrl *auxpcm_ctrl = NULL;
+ struct msm_auxpcm_gpio *pin_data = NULL;
+ int ret = 0;
+ int i;
+
+ if (pdata == NULL) {
+ pr_err("%s: pdata is NULL\n", __func__);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ auxpcm_ctrl = pdata->pri_auxpcm_ctrl;
+
+ if (auxpcm_ctrl == NULL || auxpcm_ctrl->pin_data == NULL) {
+ pr_err("%s: Ctrl pointers are NULL\n", __func__);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ pin_data = auxpcm_ctrl->pin_data;
+ for (i = 0; i < auxpcm_ctrl->cnt; i++, pin_data++) {
+ gpio_free(pin_data->gpio_no);
+ pr_debug("%s: gpio = %d, gpio_name = %s\n",
+ __func__, pin_data->gpio_no,
+ pin_data->gpio_name);
+ }
+err:
+ return ret;
}
static int msm_auxpcm_startup(struct snd_pcm_substream *substream)
@@ -577,8 +694,16 @@
pr_debug("%s(): substream = %s, auxpcm_rsc_ref counter = %d\n",
__func__, substream->name, atomic_read(&auxpcm_rsc_ref));
- if (atomic_inc_return(&auxpcm_rsc_ref) == 1)
- ret = msm_aux_pcm_get_gpios();
+
+ if (atomic_inc_return(&auxpcm_rsc_ref) == 1) {
+ if (lpaif_pri_muxsel_virt_addr != NULL)
+ iowrite32(I2S_PCM_SEL << I2S_PCM_SEL_OFFSET,
+ lpaif_pri_muxsel_virt_addr);
+ else
+ pr_err("%s lpaif_pri_muxsel_virt_addr is NULL\n",
+ __func__);
+ ret = msm_aux_pcm_get_gpios(substream);
+ }
if (ret < 0) {
pr_err("%s: Aux PCM GPIO request failed\n", __func__);
return -EINVAL;
@@ -592,7 +717,7 @@
pr_debug("%s(): substream = %s, auxpcm_rsc_ref counter = %d\n",
__func__, substream->name, atomic_read(&auxpcm_rsc_ref));
if (atomic_dec_return(&auxpcm_rsc_ref) == 0)
- msm_aux_pcm_free_gpios();
+ msm_aux_pcm_free_gpios(substream);
}
static struct snd_soc_ops msm_auxpcm_be_ops = {
.startup = msm_auxpcm_startup,
@@ -647,15 +772,20 @@
SOC_ENUM_SINGLE_EXT(2, spk_function),
SOC_ENUM_SINGLE_EXT(2, slim0_rx_ch_text),
SOC_ENUM_SINGLE_EXT(4, slim0_tx_ch_text),
+ SOC_ENUM_SINGLE_EXT(7, hdmi_rx_ch_text),
};
static const struct snd_kcontrol_new msm_snd_controls[] = {
SOC_ENUM_EXT("Speaker Function", msm_snd_enum[0], msm8974_get_spk,
- msm8974_set_spk),
+ msm8974_set_spk),
SOC_ENUM_EXT("SLIM_0_RX Channels", msm_snd_enum[1],
- msm_slim_0_rx_ch_get, msm_slim_0_rx_ch_put),
+ msm_slim_0_rx_ch_get, msm_slim_0_rx_ch_put),
SOC_ENUM_EXT("SLIM_0_TX Channels", msm_snd_enum[2],
- msm_slim_0_tx_ch_get, msm_slim_0_tx_ch_put),
+ msm_slim_0_tx_ch_get, msm_slim_0_tx_ch_put),
+ SOC_ENUM_EXT("AUX PCM SampleRate", msm8974_auxpcm_enum[0],
+ msm8974_auxpcm_rate_get, msm8974_auxpcm_rate_put),
+ SOC_ENUM_EXT("HDMI_RX Channels", msm_snd_enum[3],
+ msm_hdmi_rx_ch_get, msm_hdmi_rx_ch_put),
};
static int msm_audrx_init(struct snd_soc_pcm_runtime *rtd)
@@ -775,21 +905,21 @@
btn_high = wcd9xxx_mbhc_cal_btn_det_mp(btn_cfg,
MBHC_BTN_DET_V_BTN_HIGH);
btn_low[0] = -50;
- btn_high[0] = 10;
- btn_low[1] = 11;
- btn_high[1] = 52;
- btn_low[2] = 53;
- btn_high[2] = 94;
- btn_low[3] = 95;
- btn_high[3] = 133;
- btn_low[4] = 134;
- btn_high[4] = 171;
- btn_low[5] = 172;
- btn_high[5] = 208;
- btn_low[6] = 209;
- btn_high[6] = 244;
- btn_low[7] = 245;
- btn_high[7] = 330;
+ btn_high[0] = 20;
+ btn_low[1] = 21;
+ btn_high[1] = 63;
+ btn_low[2] = 64;
+ btn_high[2] = 106;
+ btn_low[3] = 107;
+ btn_high[3] = 146;
+ btn_low[4] = 146;
+ btn_high[4] = 186;
+ btn_low[5] = 187;
+ btn_high[5] = 221;
+ btn_low[6] = 222;
+ btn_high[6] = 253;
+ btn_low[7] = 254;
+ btn_high[7] = 500;
n_ready = wcd9xxx_mbhc_cal_btn_det_mp(btn_cfg, MBHC_BTN_DET_N_READY);
n_ready[0] = 80;
n_ready[1] = 68;
@@ -867,6 +997,7 @@
{
pr_debug("%s(): substream = %s stream = %d\n", __func__,
substream->name, substream->stream);
+
}
static struct snd_soc_ops msm8974_be_ops = {
@@ -1363,6 +1494,69 @@
.name = "msm8974-taiko-snd-card",
};
+static int msm8974_dtparse_auxpcm(struct platform_device *pdev,
+ struct msm8974_asoc_mach_data **pdata)
+{
+ int ret = 0;
+ int i = 0;
+ struct msm_auxpcm_gpio *pin_data = NULL;
+ struct msm_auxpcm_ctrl *ctrl;
+ unsigned int gpio_no[NUM_OF_AUXPCM_GPIOS];
+ enum of_gpio_flags flags = OF_GPIO_ACTIVE_LOW;
+ int prim_cnt = 0;
+
+ pin_data = devm_kzalloc(&pdev->dev, (ARRAY_SIZE(gpio_no) *
+ sizeof(struct msm_auxpcm_gpio)),
+ GFP_KERNEL);
+ if (!pin_data) {
+ dev_err(&pdev->dev, "No memory for gpio\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(gpio_no); i++) {
+ gpio_no[i] = of_get_named_gpio_flags(pdev->dev.of_node,
+ msm_auxpcm_gpio_name[i][DT_PARSE_INDEX],
+ 0, &flags);
+
+ if (gpio_no[i] > 0) {
+ pin_data[i].gpio_name =
+ msm_auxpcm_gpio_name[prim_cnt][GPIO_NAME_INDEX];
+ pin_data[i].gpio_no = gpio_no[i];
+ dev_dbg(&pdev->dev, "%s:GPIO gpio[%s] =\n"
+ "0x%x\n", __func__,
+ pin_data[i].gpio_name,
+ pin_data[i].gpio_no);
+ prim_cnt++;
+ } else {
+ dev_err(&pdev->dev, "%s:Invalid AUXPCM GPIO[%s]= %x\n",
+ __func__,
+ msm_auxpcm_gpio_name[i][GPIO_NAME_INDEX],
+ gpio_no[i]);
+ ret = -ENODEV;
+ goto err;
+ }
+ }
+
+ ctrl = devm_kzalloc(&pdev->dev,
+ sizeof(struct msm_auxpcm_ctrl), GFP_KERNEL);
+ if (!ctrl) {
+ dev_err(&pdev->dev, "No memory for gpio\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ ctrl->pin_data = pin_data;
+ ctrl->cnt = prim_cnt;
+ (*pdata)->pri_auxpcm_ctrl = ctrl;
+ return ret;
+
+err:
+ if (pin_data)
+ devm_kfree(&pdev->dev, pin_data);
+ return ret;
+}
+
static int msm8974_prepare_codec_mclk(struct snd_soc_card *card)
{
struct msm8974_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card);
@@ -1413,6 +1607,13 @@
goto err;
}
+ ret = msm8974_dtparse_auxpcm(pdev, &pdata);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "%s: Auxpcm pin data parse failed\n", __func__);
+ goto err;
+ }
+
card->dev = &pdev->dev;
platform_set_drvdata(pdev, card);
snd_soc_card_set_drvdata(card, pdata);
@@ -1489,6 +1690,12 @@
spdev = pdev;
ext_spk_amp_regulator = NULL;
+ lpaif_pri_muxsel_virt_addr = ioremap(LPAIF_PRI_MODE_MUXSEL, 4);
+ if (lpaif_pri_muxsel_virt_addr == NULL) {
+ pr_err("%s Pri muxsel virt addr is null\n", __func__);
+ ret = -EINVAL;
+ goto err;
+ }
return 0;
err:
devm_kfree(&pdev->dev, pdata);
@@ -1506,7 +1713,7 @@
gpio_free(pdata->mclk_gpio);
if (ext_spk_amp_gpio >= 0)
gpio_free(ext_spk_amp_gpio);
-
+ iounmap(lpaif_pri_muxsel_virt_addr);
snd_soc_unregister_card(card);
return 0;
diff --git a/sound/soc/msm/qdsp6/q6adm.c b/sound/soc/msm/qdsp6/q6adm.c
index 2d8d9ca..4c50b53 100644
--- a/sound/soc/msm/qdsp6/q6adm.c
+++ b/sound/soc/msm/qdsp6/q6adm.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2012, Code Aurora Forum. 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
@@ -16,6 +16,7 @@
#include <linux/jiffies.h>
#include <linux/uaccess.h>
#include <linux/atomic.h>
+#include <linux/err.h>
#include <mach/qdsp6v2/audio_dev_ctl.h>
#include <mach/qdsp6v2/audio_acdb.h>
@@ -54,6 +55,14 @@
int index;
pr_debug("SRS - %s", __func__);
+
+ index = afe_get_port_index(port_id);
+
+ if (IS_ERR_VALUE(index)) {
+ pr_err("%s: invald port id\n", __func__);
+ return index;
+ }
+
switch (srs_tech_id) {
case SRS_ID_GLOBAL: {
struct srs_trumedia_params_GLOBAL *glb_params = NULL;
@@ -199,7 +208,6 @@
open->hdr.src_port = port_id;
open->hdr.dest_svc = APR_SVC_ADM;
open->hdr.dest_domain = APR_DOMAIN_ADSP;
- index = afe_get_port_index(port_id);
open->hdr.dest_port = atomic_read(&this_adm.copp_id[index]);
open->hdr.token = port_id;
open->hdr.opcode = ADM_CMD_SET_PARAMS;
@@ -1259,6 +1267,8 @@
int ret = 0, i = 0;
/* Assumes port_ids have already been validated during adm_open */
int index = afe_get_port_index(copp_id);
+ int copp_cnt;
+
if (index < 0 || index >= AFE_MAX_PORTS) {
pr_err("%s: invalid port idx %d token %d\n",
__func__, index, copp_id);
@@ -1281,9 +1291,19 @@
route.hdr.opcode = ADM_CMD_MATRIX_MAP_ROUTINGS;
route.num_sessions = 1;
route.session[0].id = session_id;
- route.session[0].num_copps = num_copps;
- for (i = 0; i < num_copps; i++) {
+ if (num_copps < ADM_MAX_COPPS) {
+ copp_cnt = num_copps;
+ } else {
+ copp_cnt = ADM_MAX_COPPS;
+ /* print out warning for now as playback/capture to/from
+ * COPPs more than maximum allowed is extremely unlikely
+ */
+ pr_warn("%s: max out routable COPPs\n", __func__);
+ }
+
+ route.session[0].num_copps = copp_cnt;
+ for (i = 0; i < copp_cnt; i++) {
int tmp;
port_id[i] = afe_convert_virtual_to_portid(port_id[i]);
@@ -1296,7 +1316,8 @@
route.session[0].copp_id[i] =
atomic_read(&this_adm.copp_id[tmp]);
}
- if (num_copps % 2)
+
+ if (copp_cnt % 2)
route.session[0].copp_id[i] = 0;
switch (path) {
diff --git a/sound/soc/msm/qdsp6/q6afe.c b/sound/soc/msm/qdsp6/q6afe.c
index 2d44a41..9558fa4 100644
--- a/sound/soc/msm/qdsp6/q6afe.c
+++ b/sound/soc/msm/qdsp6/q6afe.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2012, Code Aurora Forum. 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
@@ -1587,7 +1587,7 @@
goto afe_error;
}
- if (param[1] < 0 || param[1] > 100) {
+ if (param[1] > 100) {
pr_err("%s: Error, volume shoud be 0 to 100"
" percentage param = %lu\n",
__func__, param[1]);
diff --git a/sound/soc/msm/qdsp6/q6asm.c b/sound/soc/msm/qdsp6/q6asm.c
index 52e481a..8fd5840 100644
--- a/sound/soc/msm/qdsp6/q6asm.c
+++ b/sound/soc/msm/qdsp6/q6asm.c
@@ -220,7 +220,7 @@
int rc = 0;
pr_debug("%s: Session id %d\n", __func__, ac->session);
mutex_lock(&ac->cmd_lock);
- if (ac->io_mode == SYNC_IO_MODE) {
+ if (ac->io_mode & SYNC_IO_MODE) {
port = &ac->port[dir];
if (!port->buf) {
mutex_unlock(&ac->cmd_lock);
@@ -351,7 +351,7 @@
if (!ac || !ac->session)
return;
pr_debug("%s: Session id %d\n", __func__, ac->session);
- if (ac->io_mode == SYNC_IO_MODE) {
+ if (ac->io_mode & SYNC_IO_MODE) {
for (loopcnt = 0; loopcnt <= OUT; loopcnt++) {
port = &ac->port[loopcnt];
if (!port->buf)
@@ -386,14 +386,20 @@
pr_err("%s APR handle NULL\n", __func__);
return -EINVAL;
}
- if ((mode == ASYNC_IO_MODE) || (mode == SYNC_IO_MODE)) {
- ac->io_mode = mode;
- pr_debug("%s:Set Mode to %d\n", __func__, ac->io_mode);
- return 0;
+
+ if (mode == ASYNC_IO_MODE) {
+ ac->io_mode &= ~SYNC_IO_MODE;
+ ac->io_mode |= ASYNC_IO_MODE;
+ } else if (mode == SYNC_IO_MODE) {
+ ac->io_mode &= ~ASYNC_IO_MODE;
+ ac->io_mode |= SYNC_IO_MODE;
} else {
pr_err("%s:Not an valid IO Mode:%d\n", __func__, ac->io_mode);
return -EINVAL;
}
+
+ pr_debug("%s:Set Mode to %d\n", __func__, ac->io_mode);
+ return 0;
}
struct audio_client *q6asm_audio_client_alloc(app_cb cb, void *priv)
@@ -498,7 +504,7 @@
if (ac->session <= 0 || ac->session > 8)
goto fail;
- if (ac->io_mode == SYNC_IO_MODE) {
+ if (ac->io_mode & SYNC_IO_MODE) {
if (ac->port[dir].buf) {
pr_debug("%s: buffer already allocated\n", __func__);
return 0;
@@ -942,7 +948,7 @@
pr_debug("%s: Rxed opcode[0x%x] status[0x%x] token[%d]",
__func__, payload[0], payload[1],
data->token);
- if (ac->io_mode == SYNC_IO_MODE) {
+ if (ac->io_mode & SYNC_IO_MODE) {
if (port->buf == NULL) {
pr_err("%s: Unexpected Write Done\n",
__func__);
@@ -1024,7 +1030,7 @@
if (in_enable_flag)
in_cont_index++;
#endif
- if (ac->io_mode == SYNC_IO_MODE) {
+ if (ac->io_mode & SYNC_IO_MODE) {
if (port->buf == NULL) {
pr_err("%s: Unexpected Write Done\n", __func__);
return -EINVAL;
@@ -1203,7 +1209,7 @@
if (!ac || ((dir != IN) && (dir != OUT)))
return NULL;
- if (ac->io_mode == SYNC_IO_MODE) {
+ if (ac->io_mode & SYNC_IO_MODE) {
port = &ac->port[dir];
mutex_lock(&port->lock);
@@ -1297,7 +1303,7 @@
if (!ac || (dir != OUT))
return ret;
- if (ac->io_mode == SYNC_IO_MODE) {
+ if (ac->io_mode & SYNC_IO_MODE) {
port = &ac->port[dir];
mutex_lock(&port->lock);
@@ -1430,6 +1436,9 @@
rc);
goto fail_cmd;
}
+
+ ac->io_mode |= TUN_READ_IO_MODE;
+
return 0;
fail_cmd:
return -EINVAL;
@@ -1716,6 +1725,9 @@
pr_err("%s: format = %x not supported\n", __func__, format);
goto fail_cmd;
}
+
+ ac->io_mode |= TUN_WRITE_IO_MODE;
+
return 0;
fail_cmd:
return -EINVAL;
@@ -3440,7 +3452,7 @@
pr_err("APR handle NULL\n");
return -EINVAL;
}
- if (ac->io_mode == SYNC_IO_MODE) {
+ if (ac->io_mode & SYNC_IO_MODE) {
port = &ac->port[OUT];
q6asm_add_hdr(ac, &read.hdr, sizeof(read), FALSE);
@@ -3492,7 +3504,7 @@
pr_err("APR handle NULL\n");
return -EINVAL;
}
- if (ac->io_mode == SYNC_IO_MODE) {
+ if (ac->io_mode & SYNC_IO_MODE) {
port = &ac->port[OUT];
q6asm_add_hdr_async(ac, &read.hdr, sizeof(read), FALSE);
@@ -3516,7 +3528,7 @@
read.hdr.token = port->dsp_buf;
port->dsp_buf = (port->dsp_buf + 1) & (port->max_buf_cnt - 1);
- pr_debug("%s:buf add[0x%x] token[%d] uid[%d]\n", __func__,
+ pr_info("%s:buf add[0x%x] token[%d] uid[%d]\n", __func__,
read.buf_add,
read.hdr.token,
read.uid);
@@ -3677,7 +3689,7 @@
return -EINVAL;
}
pr_debug("%s: session[%d] len=%d", __func__, ac->session, len);
- if (ac->io_mode == SYNC_IO_MODE) {
+ if (ac->io_mode & SYNC_IO_MODE) {
port = &ac->port[IN];
q6asm_add_hdr(ac, &write.hdr, sizeof(write),
@@ -3759,7 +3771,7 @@
return -EINVAL;
}
pr_debug("%s: session[%d] len=%d", __func__, ac->session, len);
- if (ac->io_mode == SYNC_IO_MODE) {
+ if (ac->io_mode & SYNC_IO_MODE) {
port = &ac->port[IN];
q6asm_add_hdr_async(ac, &write.hdr, sizeof(write),
@@ -3961,9 +3973,11 @@
{
int cnt = 0;
int loopcnt = 0;
+ int used;
struct audio_port_data *port = NULL;
- if (ac->io_mode == SYNC_IO_MODE) {
+ if (ac->io_mode & SYNC_IO_MODE) {
+ used = (ac->io_mode & TUN_WRITE_IO_MODE ? 1 : 0);
mutex_lock(&ac->cmd_lock);
for (loopcnt = 0; loopcnt <= OUT; loopcnt++) {
port = &ac->port[loopcnt];
@@ -3973,7 +3987,7 @@
while (cnt >= 0) {
if (!port->buf)
continue;
- port->buf[cnt].used = 1;
+ port->buf[cnt].used = used;
cnt--;
}
}
diff --git a/sound/soc/msm/qdsp6v2/Makefile b/sound/soc/msm/qdsp6v2/Makefile
index 1d11907..58eec3c 100644
--- a/sound/soc/msm/qdsp6v2/Makefile
+++ b/sound/soc/msm/qdsp6v2/Makefile
@@ -1,6 +1,6 @@
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
-obj-y += q6adm.o q6afe.o q6asm.o q6audio-v2.o q6voice.o q6core.o
+obj-y += q6adm.o q6afe.o q6asm.o q6audio-v2.o q6voice.o q6core.o audio_acdb.o rtac.o
ocmem-audio-objs += audio_ocmem.o
obj-$(CONFIG_AUDIO_OCMEM) += ocmem-audio.o
diff --git a/sound/soc/msm/qdsp6v2/audio_acdb.c b/sound/soc/msm/qdsp6v2/audio_acdb.c
new file mode 100644
index 0000000..7061efa
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/audio_acdb.c
@@ -0,0 +1,967 @@
+/* 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
+ * 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/slab.h>
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/miscdevice.h>
+#include <linux/mutex.h>
+#include <linux/uaccess.h>
+#include <linux/msm_ion.h>
+#include <linux/mm.h>
+#include "audio_acdb.h"
+
+
+#define MAX_NETWORKS 15
+#define MAX_IOCTL_DATA (MAX_NETWORKS * 2)
+#define MAX_COL_SIZE 324
+
+#define ACDB_BLOCK_SIZE 4096
+#define NUM_VOCPROC_BLOCKS (6 * MAX_NETWORKS)
+#define ACDB_TOTAL_VOICE_ALLOCATION (ACDB_BLOCK_SIZE * NUM_VOCPROC_BLOCKS)
+
+
+struct sidetone_atomic_cal {
+ atomic_t enable;
+ atomic_t gain;
+};
+
+
+struct acdb_data {
+ struct mutex acdb_mutex;
+
+ /* ANC Cal */
+ struct acdb_atomic_cal_block anc_cal;
+
+ /* AudProc Cal */
+ atomic_t asm_topology;
+ atomic_t adm_topology[MAX_AUDPROC_TYPES];
+ struct acdb_atomic_cal_block audproc_cal[MAX_AUDPROC_TYPES];
+ struct acdb_atomic_cal_block audstrm_cal[MAX_AUDPROC_TYPES];
+ struct acdb_atomic_cal_block audvol_cal[MAX_AUDPROC_TYPES];
+
+ /* VocProc Cal */
+ atomic_t voice_rx_topology;
+ atomic_t voice_tx_topology;
+ struct acdb_atomic_cal_block vocproc_cal;
+ struct acdb_atomic_cal_block vocstrm_cal;
+ struct acdb_atomic_cal_block vocvol_cal;
+
+ /* Voice Column data */
+ struct acdb_atomic_cal_block vocproc_col_cal[MAX_VOCPROC_TYPES];
+ uint32_t *col_data[MAX_VOCPROC_TYPES];
+
+ /* VocProc dev cfg cal*/
+ struct acdb_atomic_cal_block vocproc_dev_cal;
+
+ /* AFE cal */
+ struct acdb_atomic_cal_block afe_cal[MAX_AUDPROC_TYPES];
+
+ /* Sidetone Cal */
+ struct sidetone_atomic_cal sidetone_cal;
+
+ /* Allocation information */
+ struct ion_client *ion_client;
+ struct ion_handle *ion_handle;
+ atomic_t map_handle;
+ atomic64_t paddr;
+ atomic64_t kvaddr;
+ atomic64_t mem_len;
+};
+
+static struct acdb_data acdb_data;
+static atomic_t usage_count;
+
+uint32_t get_voice_rx_topology(void)
+{
+ return atomic_read(&acdb_data.voice_rx_topology);
+}
+
+void store_voice_rx_topology(uint32_t topology)
+{
+ atomic_set(&acdb_data.voice_rx_topology, topology);
+}
+
+uint32_t get_voice_tx_topology(void)
+{
+ return atomic_read(&acdb_data.voice_tx_topology);
+}
+
+void store_voice_tx_topology(uint32_t topology)
+{
+ atomic_set(&acdb_data.voice_tx_topology, topology);
+}
+
+uint32_t get_adm_rx_topology(void)
+{
+ return atomic_read(&acdb_data.adm_topology[RX_CAL]);
+}
+
+void store_adm_rx_topology(uint32_t topology)
+{
+ atomic_set(&acdb_data.adm_topology[RX_CAL], topology);
+}
+
+uint32_t get_adm_tx_topology(void)
+{
+ return atomic_read(&acdb_data.adm_topology[TX_CAL]);
+}
+
+void store_adm_tx_topology(uint32_t topology)
+{
+ atomic_set(&acdb_data.adm_topology[TX_CAL], topology);
+}
+
+uint32_t get_asm_topology(void)
+{
+ return atomic_read(&acdb_data.asm_topology);
+}
+
+void store_asm_topology(uint32_t topology)
+{
+ atomic_set(&acdb_data.asm_topology, topology);
+}
+
+void get_voice_cal_allocation(struct acdb_cal_block *cal_block)
+{
+ cal_block->cal_size = ACDB_TOTAL_VOICE_ALLOCATION;
+ cal_block->cal_paddr =
+ atomic_read(&acdb_data.vocproc_cal.cal_paddr);
+ cal_block->cal_kvaddr =
+ atomic_read(&acdb_data.vocproc_cal.cal_kvaddr);
+}
+
+void get_anc_cal(struct acdb_cal_block *cal_block)
+{
+ pr_debug("%s\n", __func__);
+
+ if (cal_block == NULL) {
+ pr_err("ACDB=> NULL pointer sent to %s\n", __func__);
+ goto done;
+ }
+
+ cal_block->cal_size =
+ atomic_read(&acdb_data.anc_cal.cal_size);
+ cal_block->cal_paddr =
+ atomic_read(&acdb_data.anc_cal.cal_paddr);
+ cal_block->cal_kvaddr =
+ atomic_read(&acdb_data.anc_cal.cal_kvaddr);
+done:
+ return;
+}
+
+void store_anc_cal(struct cal_block *cal_block)
+{
+ pr_debug("%s,\n", __func__);
+
+ if (cal_block->cal_offset > atomic64_read(&acdb_data.mem_len)) {
+ pr_err("%s: offset %d is > mem_len %ld\n",
+ __func__, cal_block->cal_offset,
+ (long)atomic64_read(&acdb_data.mem_len));
+ goto done;
+ }
+
+ atomic_set(&acdb_data.anc_cal.cal_size,
+ cal_block->cal_size);
+ atomic_set(&acdb_data.anc_cal.cal_paddr,
+ cal_block->cal_offset + atomic64_read(&acdb_data.paddr));
+ atomic_set(&acdb_data.anc_cal.cal_kvaddr,
+ cal_block->cal_offset + atomic64_read(&acdb_data.kvaddr));
+done:
+ return;
+}
+
+void store_afe_cal(int32_t path, struct cal_block *cal_block)
+{
+ pr_debug("%s, path = %d\n", __func__, path);
+
+ if (cal_block->cal_offset > atomic64_read(&acdb_data.mem_len)) {
+ pr_err("%s: offset %d is > mem_len %ld\n",
+ __func__, cal_block->cal_offset,
+ (long)atomic64_read(&acdb_data.mem_len));
+ goto done;
+ }
+ if ((path >= MAX_AUDPROC_TYPES) || (path < 0)) {
+ pr_err("ACDB=> Bad path sent to %s, path: %d\n",
+ __func__, path);
+ goto done;
+ }
+
+ atomic_set(&acdb_data.afe_cal[path].cal_size,
+ cal_block->cal_size);
+ atomic_set(&acdb_data.afe_cal[path].cal_paddr,
+ cal_block->cal_offset + atomic64_read(&acdb_data.paddr));
+ atomic_set(&acdb_data.afe_cal[path].cal_kvaddr,
+ cal_block->cal_offset + atomic64_read(&acdb_data.kvaddr));
+done:
+ return;
+}
+
+void get_afe_cal(int32_t path, struct acdb_cal_block *cal_block)
+{
+ pr_debug("%s, path = %d\n", __func__, path);
+
+ if (cal_block == NULL) {
+ pr_err("ACDB=> NULL pointer sent to %s\n", __func__);
+ goto done;
+ }
+ if ((path >= MAX_AUDPROC_TYPES) || (path < 0)) {
+ pr_err("ACDB=> Bad path sent to %s, path: %d\n",
+ __func__, path);
+ goto done;
+ }
+
+ cal_block->cal_size =
+ atomic_read(&acdb_data.afe_cal[path].cal_size);
+ cal_block->cal_paddr =
+ atomic_read(&acdb_data.afe_cal[path].cal_paddr);
+ cal_block->cal_kvaddr =
+ atomic_read(&acdb_data.afe_cal[path].cal_kvaddr);
+done:
+ return;
+}
+
+void store_audproc_cal(int32_t path, struct cal_block *cal_block)
+{
+ pr_debug("%s, path = %d\n", __func__, path);
+
+ if (cal_block->cal_offset > atomic64_read(&acdb_data.mem_len)) {
+ pr_err("%s: offset %d is > mem_len %ld\n",
+ __func__, cal_block->cal_offset,
+ (long)atomic64_read(&acdb_data.mem_len));
+ goto done;
+ }
+ if (path >= MAX_AUDPROC_TYPES) {
+ pr_err("ACDB=> Bad path sent to %s, path: %d\n",
+ __func__, path);
+ goto done;
+ }
+
+ atomic_set(&acdb_data.audproc_cal[path].cal_size,
+ cal_block->cal_size);
+ atomic_set(&acdb_data.audproc_cal[path].cal_paddr,
+ cal_block->cal_offset + atomic64_read(&acdb_data.paddr));
+ atomic_set(&acdb_data.audproc_cal[path].cal_kvaddr,
+ cal_block->cal_offset + atomic64_read(&acdb_data.kvaddr));
+done:
+ return;
+}
+
+void get_audproc_cal(int32_t path, struct acdb_cal_block *cal_block)
+{
+ pr_debug("%s, path = %d\n", __func__, path);
+
+ if (cal_block == NULL) {
+ pr_err("ACDB=> NULL pointer sent to %s\n", __func__);
+ goto done;
+ }
+ if (path >= MAX_AUDPROC_TYPES) {
+ pr_err("ACDB=> Bad path sent to %s, path: %d\n",
+ __func__, path);
+ goto done;
+ }
+
+ cal_block->cal_size =
+ atomic_read(&acdb_data.audproc_cal[path].cal_size);
+ cal_block->cal_paddr =
+ atomic_read(&acdb_data.audproc_cal[path].cal_paddr);
+ cal_block->cal_kvaddr =
+ atomic_read(&acdb_data.audproc_cal[path].cal_kvaddr);
+done:
+ return;
+}
+
+void store_audstrm_cal(int32_t path, struct cal_block *cal_block)
+{
+ pr_debug("%s, path = %d\n", __func__, path);
+
+ if (cal_block->cal_offset > atomic64_read(&acdb_data.mem_len)) {
+ pr_err("%s: offset %d is > mem_len %ld\n",
+ __func__, cal_block->cal_offset,
+ (long)atomic64_read(&acdb_data.mem_len));
+ goto done;
+ }
+ if (path >= MAX_AUDPROC_TYPES) {
+ pr_err("ACDB=> Bad path sent to %s, path: %d\n",
+ __func__, path);
+ goto done;
+ }
+
+ atomic_set(&acdb_data.audstrm_cal[path].cal_size,
+ cal_block->cal_size);
+ atomic_set(&acdb_data.audstrm_cal[path].cal_paddr,
+ cal_block->cal_offset + atomic64_read(&acdb_data.paddr));
+ atomic_set(&acdb_data.audstrm_cal[path].cal_kvaddr,
+ cal_block->cal_offset + atomic64_read(&acdb_data.kvaddr));
+done:
+ return;
+}
+
+void get_audstrm_cal(int32_t path, struct acdb_cal_block *cal_block)
+{
+ pr_debug("%s, path = %d\n", __func__, path);
+
+ if (cal_block == NULL) {
+ pr_err("ACDB=> NULL pointer sent to %s\n", __func__);
+ goto done;
+ }
+ if (path >= MAX_AUDPROC_TYPES) {
+ pr_err("ACDB=> Bad path sent to %s, path: %d\n",
+ __func__, path);
+ goto done;
+ }
+
+ cal_block->cal_size =
+ atomic_read(&acdb_data.audstrm_cal[path].cal_size);
+ cal_block->cal_paddr =
+ atomic_read(&acdb_data.audstrm_cal[path].cal_paddr);
+ cal_block->cal_kvaddr =
+ atomic_read(&acdb_data.audstrm_cal[path].cal_kvaddr);
+done:
+ return;
+}
+
+void store_audvol_cal(int32_t path, struct cal_block *cal_block)
+{
+ pr_debug("%s, path = %d\n", __func__, path);
+
+ if (cal_block->cal_offset > atomic64_read(&acdb_data.mem_len)) {
+ pr_err("%s: offset %d is > mem_len %ld\n",
+ __func__, cal_block->cal_offset,
+ (long)atomic64_read(&acdb_data.mem_len));
+ goto done;
+ }
+ if (path >= MAX_AUDPROC_TYPES) {
+ pr_err("ACDB=> Bad path sent to %s, path: %d\n",
+ __func__, path);
+ goto done;
+ }
+
+ atomic_set(&acdb_data.audvol_cal[path].cal_size,
+ cal_block->cal_size);
+ atomic_set(&acdb_data.audvol_cal[path].cal_paddr,
+ cal_block->cal_offset + atomic64_read(&acdb_data.paddr));
+ atomic_set(&acdb_data.audvol_cal[path].cal_kvaddr,
+ cal_block->cal_offset + atomic64_read(&acdb_data.kvaddr));
+done:
+ return;
+}
+
+void get_audvol_cal(int32_t path, struct acdb_cal_block *cal_block)
+{
+ pr_debug("%s, path = %d\n", __func__, path);
+
+ if (cal_block == NULL) {
+ pr_err("ACDB=> NULL pointer sent to %s\n", __func__);
+ goto done;
+ }
+ if (path >= MAX_AUDPROC_TYPES || path < 0) {
+ pr_err("ACDB=> Bad path sent to %s, path: %d\n",
+ __func__, path);
+ goto done;
+ }
+
+ cal_block->cal_size =
+ atomic_read(&acdb_data.audvol_cal[path].cal_size);
+ cal_block->cal_paddr =
+ atomic_read(&acdb_data.audvol_cal[path].cal_paddr);
+ cal_block->cal_kvaddr =
+ atomic_read(&acdb_data.audvol_cal[path].cal_kvaddr);
+done:
+ return;
+}
+
+void store_voice_col_data(uint32_t vocproc_type, uint32_t cal_size,
+ uint32_t *cal_block)
+{
+ if (cal_size > MAX_COL_SIZE) {
+ pr_err("%s: col size is to big %d\n", __func__,
+ cal_size);
+ goto done;
+ }
+ if (copy_from_user(acdb_data.col_data[vocproc_type],
+ (void *)((uint8_t *)cal_block + sizeof(cal_size)),
+ cal_size)) {
+ pr_err("%s: fail to copy col size %d\n",
+ __func__, cal_size);
+ goto done;
+ }
+ atomic_set(&acdb_data.vocproc_col_cal[vocproc_type].cal_size,
+ cal_size);
+done:
+ return;
+}
+
+void get_voice_col_data(uint32_t vocproc_type,
+ struct acdb_cal_block *cal_block)
+{
+ if (cal_block == NULL) {
+ pr_err("ACDB=> NULL pointer sent to %s\n", __func__);
+ goto done;
+ }
+
+ cal_block->cal_size = atomic_read(&acdb_data.
+ vocproc_col_cal[vocproc_type].cal_size);
+ cal_block->cal_paddr = atomic_read(&acdb_data.
+ vocproc_col_cal[vocproc_type].cal_paddr);
+ cal_block->cal_kvaddr = atomic_read(&acdb_data.
+ vocproc_col_cal[vocproc_type].cal_kvaddr);
+done:
+ return;
+}
+
+void store_vocproc_dev_cfg_cal(struct cal_block *cal_block)
+{
+ pr_debug("%s\n", __func__);
+
+
+ if (cal_block->cal_offset >
+ atomic64_read(&acdb_data.mem_len)) {
+ pr_err("%s: offset %d is > mem_len %ld\n",
+ __func__, cal_block->cal_offset,
+ (long)atomic64_read(&acdb_data.mem_len));
+ atomic_set(&acdb_data.vocproc_dev_cal.cal_size, 0);
+ goto done;
+ }
+
+ atomic_set(&acdb_data.vocproc_dev_cal.cal_size,
+ cal_block->cal_size);
+ atomic_set(&acdb_data.vocproc_dev_cal.cal_paddr,
+ cal_block->cal_offset +
+ atomic64_read(&acdb_data.paddr));
+ atomic_set(&acdb_data.vocproc_dev_cal.cal_kvaddr,
+ cal_block->cal_offset +
+ atomic64_read(&acdb_data.kvaddr));
+
+done:
+ return;
+}
+
+void get_vocproc_dev_cfg_cal(struct acdb_cal_block *cal_block)
+{
+ pr_debug("%s\n", __func__);
+
+ cal_block->cal_size =
+ atomic_read(&acdb_data.vocproc_dev_cal.cal_size);
+ cal_block->cal_paddr =
+ atomic_read(&acdb_data.vocproc_dev_cal.cal_paddr);
+ cal_block->cal_kvaddr =
+ atomic_read(&acdb_data.vocproc_dev_cal.cal_kvaddr);
+}
+
+
+
+void store_vocproc_cal(struct cal_block *cal_block)
+{
+ pr_debug("%s\n", __func__);
+
+ if (cal_block->cal_offset >
+ atomic64_read(&acdb_data.mem_len)) {
+ pr_err("%s: offset %d is > mem_len %ld\n",
+ __func__, cal_block->cal_offset,
+ (long)atomic64_read(&acdb_data.mem_len));
+ atomic_set(&acdb_data.vocproc_cal.cal_size, 0);
+ goto done;
+ }
+
+ atomic_set(&acdb_data.vocproc_cal.cal_size,
+ cal_block->cal_size);
+ atomic_set(&acdb_data.vocproc_cal.cal_paddr,
+ cal_block->cal_offset +
+ atomic64_read(&acdb_data.paddr));
+ atomic_set(&acdb_data.vocproc_cal.cal_kvaddr,
+ cal_block->cal_offset +
+ atomic64_read(&acdb_data.kvaddr));
+
+done:
+ return;
+}
+
+void get_vocproc_cal(struct acdb_cal_block *cal_block)
+{
+ pr_debug("%s\n", __func__);
+
+ if (cal_block == NULL) {
+ pr_err("ACDB=> NULL pointer sent to %s\n", __func__);
+ goto done;
+ }
+
+ cal_block->cal_size =
+ atomic_read(&acdb_data.vocproc_cal.cal_size);
+ cal_block->cal_paddr =
+ atomic_read(&acdb_data.vocproc_cal.cal_paddr);
+ cal_block->cal_kvaddr =
+ atomic_read(&acdb_data.vocproc_cal.cal_kvaddr);
+done:
+ return;
+}
+
+void store_vocstrm_cal(struct cal_block *cal_block)
+{
+ pr_debug("%s\n", __func__);
+
+ if (cal_block->cal_offset >
+ atomic64_read(&acdb_data.mem_len)) {
+ pr_err("%s: offset %d is > mem_len %ld\n",
+ __func__, cal_block->cal_offset,
+ (long)atomic64_read(&acdb_data.mem_len));
+ atomic_set(&acdb_data.vocstrm_cal.cal_size, 0);
+ goto done;
+ }
+
+ atomic_set(&acdb_data.vocstrm_cal.cal_size,
+ cal_block->cal_size);
+ atomic_set(&acdb_data.vocstrm_cal.cal_paddr,
+ cal_block->cal_offset +
+ atomic64_read(&acdb_data.paddr));
+ atomic_set(&acdb_data.vocstrm_cal.cal_kvaddr,
+ cal_block->cal_offset +
+ atomic64_read(&acdb_data.kvaddr));
+
+done:
+ return;
+}
+
+void get_vocstrm_cal(struct acdb_cal_block *cal_block)
+{
+ pr_debug("%s\n", __func__);
+
+ if (cal_block == NULL) {
+ pr_err("ACDB=> NULL pointer sent to %s\n", __func__);
+ goto done;
+ }
+
+ cal_block->cal_size =
+ atomic_read(&acdb_data.vocstrm_cal.cal_size);
+ cal_block->cal_paddr =
+ atomic_read(&acdb_data.vocstrm_cal.cal_paddr);
+ cal_block->cal_kvaddr =
+ atomic_read(&acdb_data.vocstrm_cal.cal_kvaddr);
+done:
+ return;
+}
+
+void store_vocvol_cal(struct cal_block *cal_block)
+{
+ pr_debug("%s\n", __func__);
+
+ if (cal_block->cal_offset >
+ atomic64_read(&acdb_data.mem_len)) {
+ pr_err("%s: offset %d is > mem_len %ld\n",
+ __func__, cal_block->cal_offset,
+ (long)atomic64_read(&acdb_data.mem_len));
+ atomic_set(&acdb_data.vocvol_cal.cal_size, 0);
+ goto done;
+ }
+
+ atomic_set(&acdb_data.vocvol_cal.cal_size,
+ cal_block->cal_size);
+ atomic_set(&acdb_data.vocvol_cal.cal_paddr,
+ cal_block->cal_offset +
+ atomic64_read(&acdb_data.paddr));
+ atomic_set(&acdb_data.vocvol_cal.cal_kvaddr,
+ cal_block->cal_offset +
+ atomic64_read(&acdb_data.kvaddr));
+
+done:
+ return;
+}
+
+void get_vocvol_cal(struct acdb_cal_block *cal_block)
+{
+ pr_debug("%s\n", __func__);
+
+ if (cal_block == NULL) {
+ pr_err("ACDB=> NULL pointer sent to %s\n", __func__);
+ goto done;
+ }
+
+ cal_block->cal_size =
+ atomic_read(&acdb_data.vocvol_cal.cal_size);
+ cal_block->cal_paddr =
+ atomic_read(&acdb_data.vocvol_cal.cal_paddr);
+ cal_block->cal_kvaddr =
+ atomic_read(&acdb_data.vocvol_cal.cal_kvaddr);
+done:
+ return;
+}
+
+void store_sidetone_cal(struct sidetone_cal *cal_data)
+{
+ pr_debug("%s\n", __func__);
+
+ atomic_set(&acdb_data.sidetone_cal.enable, cal_data->enable);
+ atomic_set(&acdb_data.sidetone_cal.gain, cal_data->gain);
+}
+
+
+void get_sidetone_cal(struct sidetone_cal *cal_data)
+{
+ pr_debug("%s\n", __func__);
+
+ if (cal_data == NULL) {
+ pr_err("ACDB=> NULL pointer sent to %s\n", __func__);
+ goto done;
+ }
+
+ cal_data->enable = atomic_read(&acdb_data.sidetone_cal.enable);
+ cal_data->gain = atomic_read(&acdb_data.sidetone_cal.gain);
+done:
+ return;
+}
+
+static int acdb_open(struct inode *inode, struct file *f)
+{
+ s32 result = 0;
+ pr_debug("%s\n", __func__);
+
+ if (atomic64_read(&acdb_data.mem_len)) {
+ pr_debug("%s: ACDB opened but memory allocated, using existing allocation!\n",
+ __func__);
+ }
+
+ atomic_inc(&usage_count);
+ return result;
+}
+
+static int deregister_memory(void)
+{
+ int i;
+
+ if (atomic64_read(&acdb_data.mem_len)) {
+ mutex_lock(&acdb_data.acdb_mutex);
+ atomic64_set(&acdb_data.mem_len, 0);
+
+ for (i = 0; i < MAX_VOCPROC_TYPES; i++) {
+ kfree(acdb_data.col_data[i]);
+ acdb_data.col_data[i] = NULL;
+ }
+ ion_unmap_kernel(acdb_data.ion_client, acdb_data.ion_handle);
+ ion_free(acdb_data.ion_client, acdb_data.ion_handle);
+ ion_client_destroy(acdb_data.ion_client);
+ mutex_unlock(&acdb_data.acdb_mutex);
+ }
+ return 0;
+}
+
+static int register_memory(void)
+{
+ int result;
+ int i;
+ unsigned long paddr;
+ void *kvptr;
+ unsigned long kvaddr;
+ unsigned long mem_len;
+
+ mutex_lock(&acdb_data.acdb_mutex);
+ for (i = 0; i < MAX_VOCPROC_TYPES; i++) {
+ acdb_data.col_data[i] = kmalloc(MAX_COL_SIZE, GFP_KERNEL);
+ atomic_set(&acdb_data.vocproc_col_cal[i].cal_kvaddr,
+ (uint32_t)acdb_data.col_data[i]);
+ }
+
+ acdb_data.ion_client =
+ msm_ion_client_create(UINT_MAX, "audio_acdb_client");
+ if (IS_ERR_OR_NULL(acdb_data.ion_client)) {
+ pr_err("%s: Could not register ION client!!!\n", __func__);
+ result = PTR_ERR(acdb_data.ion_client);
+ goto err;
+ }
+
+ acdb_data.ion_handle = ion_import_dma_buf(acdb_data.ion_client,
+ atomic_read(&acdb_data.map_handle));
+ if (IS_ERR_OR_NULL(acdb_data.ion_handle)) {
+ pr_err("%s: Could not import map handle!!!\n", __func__);
+ result = PTR_ERR(acdb_data.ion_handle);
+ goto err_ion_client;
+ }
+
+ result = ion_phys(acdb_data.ion_client, acdb_data.ion_handle,
+ &paddr, (size_t *)&mem_len);
+ if (result != 0) {
+ pr_err("%s: Could not get phys addr!!!\n", __func__);
+ goto err_ion_handle;
+ }
+
+ kvptr = ion_map_kernel(acdb_data.ion_client,
+ acdb_data.ion_handle);
+ if (IS_ERR_OR_NULL(kvptr)) {
+ pr_err("%s: Could not get kernel virt addr!!!\n", __func__);
+ result = PTR_ERR(kvptr);
+ goto err_ion_handle;
+ }
+ kvaddr = (unsigned long)kvptr;
+ atomic64_set(&acdb_data.paddr, paddr);
+ atomic64_set(&acdb_data.kvaddr, kvaddr);
+ atomic64_set(&acdb_data.mem_len, mem_len);
+ mutex_unlock(&acdb_data.acdb_mutex);
+
+ pr_debug("%s done! paddr = 0x%lx, kvaddr = 0x%lx, len = x%lx\n",
+ __func__,
+ (long)atomic64_read(&acdb_data.paddr),
+ (long)atomic64_read(&acdb_data.kvaddr),
+ (long)atomic64_read(&acdb_data.mem_len));
+
+ return result;
+err_ion_handle:
+ ion_free(acdb_data.ion_client, acdb_data.ion_handle);
+err_ion_client:
+ ion_client_destroy(acdb_data.ion_client);
+err:
+ atomic64_set(&acdb_data.mem_len, 0);
+ mutex_unlock(&acdb_data.acdb_mutex);
+ return result;
+}
+static long acdb_ioctl(struct file *f,
+ unsigned int cmd, unsigned long arg)
+{
+ int32_t result = 0;
+ int32_t size;
+ int32_t map_fd;
+ uint32_t topology;
+ uint32_t data[MAX_IOCTL_DATA];
+ pr_debug("%s\n", __func__);
+
+ switch (cmd) {
+ case AUDIO_REGISTER_PMEM:
+ pr_debug("AUDIO_REGISTER_PMEM\n");
+ if (atomic_read(&acdb_data.mem_len)) {
+ deregister_memory();
+ pr_debug("Remove the existing memory\n");
+ }
+
+ if (copy_from_user(&map_fd, (void *)arg, sizeof(map_fd))) {
+ pr_err("%s: fail to copy memory handle!\n", __func__);
+ result = -EFAULT;
+ } else {
+ atomic_set(&acdb_data.map_handle, map_fd);
+ result = register_memory();
+ }
+ goto done;
+
+ case AUDIO_DEREGISTER_PMEM:
+ pr_debug("AUDIO_DEREGISTER_PMEM\n");
+ deregister_memory();
+ goto done;
+ case AUDIO_SET_VOICE_RX_TOPOLOGY:
+ if (copy_from_user(&topology, (void *)arg,
+ sizeof(topology))) {
+ pr_err("%s: fail to copy topology!\n", __func__);
+ result = -EFAULT;
+ }
+ store_voice_rx_topology(topology);
+ goto done;
+ case AUDIO_SET_VOICE_TX_TOPOLOGY:
+ if (copy_from_user(&topology, (void *)arg,
+ sizeof(topology))) {
+ pr_err("%s: fail to copy topology!\n", __func__);
+ result = -EFAULT;
+ }
+ store_voice_tx_topology(topology);
+ goto done;
+ case AUDIO_SET_ADM_RX_TOPOLOGY:
+ if (copy_from_user(&topology, (void *)arg,
+ sizeof(topology))) {
+ pr_err("%s: fail to copy topology!\n", __func__);
+ result = -EFAULT;
+ }
+ store_adm_rx_topology(topology);
+ goto done;
+ case AUDIO_SET_ADM_TX_TOPOLOGY:
+ if (copy_from_user(&topology, (void *)arg,
+ sizeof(topology))) {
+ pr_err("%s: fail to copy topology!\n", __func__);
+ result = -EFAULT;
+ }
+ store_adm_tx_topology(topology);
+ goto done;
+ case AUDIO_SET_ASM_TOPOLOGY:
+ if (copy_from_user(&topology, (void *)arg,
+ sizeof(topology))) {
+ pr_err("%s: fail to copy topology!\n", __func__);
+ result = -EFAULT;
+ }
+ store_asm_topology(topology);
+ goto done;
+ }
+
+ if (copy_from_user(&size, (void *) arg, sizeof(size))) {
+
+ result = -EFAULT;
+ goto done;
+ }
+
+ if (size <= 0) {
+ pr_err("%s: Invalid size sent to driver: %d\n",
+ __func__, size);
+ result = -EFAULT;
+ goto done;
+ }
+
+ switch (cmd) {
+ case AUDIO_SET_VOCPROC_COL_CAL:
+ store_voice_col_data(VOCPROC_CAL, size, (uint32_t *)arg);
+ goto done;
+ case AUDIO_SET_VOCSTRM_COL_CAL:
+ store_voice_col_data(VOCSTRM_CAL, size, (uint32_t *)arg);
+ goto done;
+ case AUDIO_SET_VOCVOL_COL_CAL:
+ store_voice_col_data(VOCVOL_CAL, size, (uint32_t *)arg);
+ goto done;
+ }
+
+ if (copy_from_user(data, (void *)(arg + sizeof(size)), size)) {
+
+ pr_err("%s: fail to copy table size %d\n", __func__, size);
+ result = -EFAULT;
+ goto done;
+ }
+
+ if (data == NULL) {
+ pr_err("%s: NULL pointer sent to driver!\n", __func__);
+ result = -EFAULT;
+ goto done;
+ }
+
+ if (size > sizeof(struct cal_block))
+ pr_err("%s: More cal data for ioctl 0x%x then expected, size received: %d\n",
+ __func__, cmd, size);
+
+ switch (cmd) {
+ case AUDIO_SET_AUDPROC_TX_CAL:
+ store_audproc_cal(TX_CAL, (struct cal_block *)data);
+ goto done;
+ case AUDIO_SET_AUDPROC_RX_CAL:
+ store_audproc_cal(RX_CAL, (struct cal_block *)data);
+ goto done;
+ case AUDIO_SET_AUDPROC_TX_STREAM_CAL:
+ store_audstrm_cal(TX_CAL, (struct cal_block *)data);
+ goto done;
+ case AUDIO_SET_AUDPROC_RX_STREAM_CAL:
+ store_audstrm_cal(RX_CAL, (struct cal_block *)data);
+ goto done;
+ case AUDIO_SET_AUDPROC_TX_VOL_CAL:
+ store_audvol_cal(TX_CAL, (struct cal_block *)data);
+ goto done;
+ case AUDIO_SET_AUDPROC_RX_VOL_CAL:
+ store_audvol_cal(RX_CAL, (struct cal_block *)data);
+ goto done;
+ case AUDIO_SET_AFE_TX_CAL:
+ store_afe_cal(TX_CAL, (struct cal_block *)data);
+ goto done;
+ case AUDIO_SET_AFE_RX_CAL:
+ store_afe_cal(RX_CAL, (struct cal_block *)data);
+ goto done;
+ case AUDIO_SET_VOCPROC_CAL:
+ store_vocproc_cal((struct cal_block *)data);
+ goto done;
+ case AUDIO_SET_VOCPROC_STREAM_CAL:
+ store_vocstrm_cal((struct cal_block *)data);
+ goto done;
+ case AUDIO_SET_VOCPROC_VOL_CAL:
+ store_vocvol_cal((struct cal_block *)data);
+ goto done;
+ case AUDIO_SET_VOCPROC_DEV_CFG_CAL:
+ store_vocproc_dev_cfg_cal((struct cal_block *)data);
+ goto done;
+ case AUDIO_SET_SIDETONE_CAL:
+ store_sidetone_cal((struct sidetone_cal *)data);
+ goto done;
+ case AUDIO_SET_ANC_CAL:
+ store_anc_cal((struct cal_block *)data);
+ goto done;
+ default:
+ pr_err("ACDB=> ACDB ioctl not found!\n");
+ }
+
+done:
+ return result;
+}
+
+static int acdb_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ int result = 0;
+ int size = vma->vm_end - vma->vm_start;
+
+ pr_debug("%s\n", __func__);
+
+ if (atomic64_read(&acdb_data.mem_len)) {
+ if (size <= atomic64_read(&acdb_data.mem_len)) {
+ vma->vm_page_prot = pgprot_noncached(
+ vma->vm_page_prot);
+ result = remap_pfn_range(vma,
+ vma->vm_start,
+ atomic64_read(&acdb_data.paddr) >> PAGE_SHIFT,
+ size,
+ vma->vm_page_prot);
+ } else {
+ pr_err("%s: Not enough memory!\n", __func__);
+ result = -ENOMEM;
+ }
+ } else {
+ pr_err("%s: memory is not allocated, yet!\n", __func__);
+ result = -ENODEV;
+ }
+
+ return result;
+}
+
+static int acdb_release(struct inode *inode, struct file *f)
+{
+ s32 result = 0;
+
+ atomic_dec(&usage_count);
+ atomic_read(&usage_count);
+
+ pr_debug("%s: ref count %d!\n", __func__,
+ atomic_read(&usage_count));
+
+ if (atomic_read(&usage_count) >= 1)
+ result = -EBUSY;
+ else
+ result = deregister_memory();
+
+ return result;
+}
+
+static const struct file_operations acdb_fops = {
+ .owner = THIS_MODULE,
+ .open = acdb_open,
+ .release = acdb_release,
+ .unlocked_ioctl = acdb_ioctl,
+ .mmap = acdb_mmap,
+};
+
+struct miscdevice acdb_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_acdb",
+ .fops = &acdb_fops,
+};
+
+static int __init acdb_init(void)
+{
+ memset(&acdb_data, 0, sizeof(acdb_data));
+ mutex_init(&acdb_data.acdb_mutex);
+ atomic_set(&usage_count, 0);
+
+ return misc_register(&acdb_misc);
+}
+
+static void __exit acdb_exit(void)
+{
+}
+
+module_init(acdb_init);
+module_exit(acdb_exit);
+
+MODULE_DESCRIPTION("SoC QDSP6v2 Audio ACDB driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/qdsp6v2/audio_acdb.h b/sound/soc/msm/qdsp6v2/audio_acdb.h
new file mode 100644
index 0000000..c31823b
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/audio_acdb.h
@@ -0,0 +1,63 @@
+/* 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
+ * 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 _AUDIO_ACDB_H
+#define _AUDIO_ACDB_H
+
+#include <linux/msm_audio_acdb.h>
+#include <sound/q6adm-v2.h>
+
+enum {
+ RX_CAL,
+ TX_CAL,
+ MAX_AUDPROC_TYPES
+};
+
+enum {
+ VOCPROC_CAL,
+ VOCSTRM_CAL,
+ VOCVOL_CAL,
+ MAX_VOCPROC_TYPES
+};
+
+struct acdb_cal_block {
+ uint32_t cal_size;
+ uint32_t cal_kvaddr;
+ uint32_t cal_paddr;
+};
+
+struct acdb_atomic_cal_block {
+ atomic_t cal_size;
+ atomic_t cal_kvaddr;
+ atomic_t cal_paddr;
+};
+
+uint32_t get_voice_rx_topology(void);
+uint32_t get_voice_tx_topology(void);
+uint32_t get_adm_rx_topology(void);
+uint32_t get_adm_tx_topology(void);
+uint32_t get_asm_topology(void);
+void get_voice_cal_allocation(struct acdb_cal_block *cal_block);
+void get_anc_cal(struct acdb_cal_block *cal_block);
+void get_afe_cal(int32_t path, struct acdb_cal_block *cal_block);
+void get_audproc_cal(int32_t path, struct acdb_cal_block *cal_block);
+void get_audstrm_cal(int32_t path, struct acdb_cal_block *cal_block);
+void get_audvol_cal(int32_t path, struct acdb_cal_block *cal_block);
+void get_voice_col_data(uint32_t vocproc_type,
+ struct acdb_cal_block *cal_block);
+void get_vocproc_dev_cfg_cal(struct acdb_cal_block *cal_block);
+void get_vocproc_cal(struct acdb_cal_block *cal_block);
+void get_vocstrm_cal(struct acdb_cal_block *cal_block);
+void get_vocvol_cal(struct acdb_cal_block *cal_block);
+void get_sidetone_cal(struct sidetone_cal *cal_data);
+
+#endif
diff --git a/sound/soc/msm/qdsp6v2/audio_ocmem.c b/sound/soc/msm/qdsp6v2/audio_ocmem.c
index f151e51..145f095 100644
--- a/sound/soc/msm/qdsp6v2/audio_ocmem.c
+++ b/sound/soc/msm/qdsp6v2/audio_ocmem.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. 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
@@ -590,7 +590,7 @@
pr_debug("%s\n", __func__);
audio_ocmem_lcl.audio_ocmem_workqueue =
alloc_workqueue("ocmem_audio_client_driver_audio",
- WQ_NON_REENTRANT, 0);
+ WQ_NON_REENTRANT | WQ_UNBOUND, 0);
if (!audio_ocmem_lcl.audio_ocmem_workqueue) {
pr_err("%s: Failed to create ocmem audio work queue\n",
__func__);
diff --git a/sound/soc/msm/qdsp6v2/msm-dai-q6-hdmi-v2.c b/sound/soc/msm/qdsp6v2/msm-dai-q6-hdmi-v2.c
index 48d9e1e..329d293 100644
--- a/sound/soc/msm/qdsp6v2/msm-dai-q6-hdmi-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-dai-q6-hdmi-v2.c
@@ -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
@@ -24,11 +24,20 @@
#include <sound/q6afe-v2.h>
#include <sound/msm-dai-q6-v2.h>
+#define HDMI_RX_CA_MAX 0x32
+
enum {
STATUS_PORT_STARTED, /* track if AFE port has started */
STATUS_MAX
};
+struct msm_hdmi_ca {
+ bool set_ca;
+ u32 ca;
+};
+
+static struct msm_hdmi_ca hdmi_ca = { false, 0x0 };
+
struct msm_dai_q6_hdmi_dai_data {
DECLARE_BITMAP(status_mask, STATUS_MAX);
u32 rate;
@@ -57,6 +66,20 @@
return 0;
}
+static int msm_dai_q6_hdmi_ca_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ hdmi_ca.ca = ucontrol->value.integer.value[0];
+ hdmi_ca.set_ca = true;
+ return 0;
+}
+
+static int msm_dai_q6_hdmi_ca_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = hdmi_ca.ca;
+ return 0;
+}
/* HDMI format field for AFE_PORT_MULTI_CHAN_HDMI_AUDIO_IF_CONFIG command
* 0: linear PCM
@@ -75,6 +98,10 @@
SOC_ENUM_EXT("HDMI RX Format", hdmi_config_enum[0],
msm_dai_q6_hdmi_format_get,
msm_dai_q6_hdmi_format_put),
+ SOC_SINGLE_MULTI_EXT("HDMI RX CA", SND_SOC_NOPM, 0,
+ HDMI_RX_CA_MAX, 0, 1,
+ msm_dai_q6_hdmi_ca_get,
+ msm_dai_q6_hdmi_ca_put),
};
/* Current implementation assumes hw_param is called once
@@ -152,6 +179,10 @@
struct msm_dai_q6_hdmi_dai_data *dai_data = dev_get_drvdata(dai->dev);
int rc = 0;
+ if (hdmi_ca.set_ca)
+ dai_data->port_config.hdmi_multi_ch.channel_allocation =
+ hdmi_ca.ca;
+
if (!test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
rc = afe_port_start(dai->id, &dai_data->port_config,
dai_data->rate);
@@ -186,6 +217,12 @@
rc = snd_ctl_add(dai->card->snd_card,
snd_ctl_new1(kcontrol, dai_data));
+
+ kcontrol = &hdmi_config_controls[1];
+
+ rc = snd_ctl_add(dai->card->snd_card,
+ snd_ctl_new1(kcontrol, dai_data));
+
return rc;
}
diff --git a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c
index a7a80a6..cf7f182 100644
--- a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. 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
@@ -28,11 +28,26 @@
#include <sound/pcm_params.h>
#include <mach/clk.h>
+static const struct afe_clk_cfg lpass_clk_cfg_default = {
+ AFE_API_VERSION_I2S_CONFIG,
+ Q6AFE_LPASS_OSR_CLK_2_P048_MHZ,
+ 0,
+ Q6AFE_LPASS_CLK_SRC_INTERNAL,
+ Q6AFE_LPASS_CLK_ROOT_DEFAULT,
+ Q6AFE_LPASS_MODE_CLK1_VALID,
+ 0,
+};
enum {
STATUS_PORT_STARTED, /* track if AFE port has started */
STATUS_MAX
};
+enum {
+ RATE_8KHZ,
+ RATE_16KHZ,
+ RATE_MAX_NUM_OF_AUX_PCM_RATES,
+};
+
struct msm_dai_q6_dai_data {
DECLARE_BITMAP(status_mask, STATUS_MAX);
u32 rate;
@@ -70,11 +85,6 @@
SOC_ENUM_SINGLE_EXT(4, mi2s_format),
};
-static struct clk *pcm_src_clk;
-static struct clk *pcm_branch_clk;
-static struct clk *pcm_oe_src_clk;
-static struct clk *pcm_oe_branch_clk;
-
static DEFINE_MUTEX(aux_pcm_mutex);
static int aux_pcm_count;
@@ -92,25 +102,49 @@
return -EINVAL;
}
dai_data->channels = params_channels(params);
-
- if (params_rate(params) != 8000) {
- dev_err(dai->dev, "AUX PCM supports only 8KHz sampling rate\n");
- return -EINVAL;
- }
dai_data->rate = params_rate(params);
- dai_data->port_config.pcm.pcm_cfg_minor_version =
+ switch (dai_data->rate) {
+ case 8000:
+ dai_data->port_config.pcm.pcm_cfg_minor_version =
AFE_API_VERSION_PCM_CONFIG;
- dai_data->port_config.pcm.aux_mode = auxpcm_pdata->mode;
- dai_data->port_config.pcm.sync_src = auxpcm_pdata->sync;
- dai_data->port_config.pcm.frame_setting = auxpcm_pdata->frame;
- dai_data->port_config.pcm.quantype = auxpcm_pdata->quant;
- dai_data->port_config.pcm.ctrl_data_out_enable = auxpcm_pdata->data;
- dai_data->port_config.pcm.sample_rate = dai_data->rate;
- dai_data->port_config.pcm.num_channels = dai_data->channels;
- dai_data->port_config.pcm.bit_width = 16;
- dai_data->port_config.pcm.slot_number_mapping[0] = auxpcm_pdata->slot;
-
+ dai_data->port_config.pcm.aux_mode = auxpcm_pdata->mode_8k.mode;
+ dai_data->port_config.pcm.sync_src = auxpcm_pdata->mode_8k.sync;
+ dai_data->port_config.pcm.frame_setting =
+ auxpcm_pdata->mode_8k.frame;
+ dai_data->port_config.pcm.quantype =
+ auxpcm_pdata->mode_8k.quant;
+ dai_data->port_config.pcm.ctrl_data_out_enable =
+ auxpcm_pdata->mode_8k.data;
+ dai_data->port_config.pcm.sample_rate = dai_data->rate;
+ dai_data->port_config.pcm.num_channels = dai_data->channels;
+ dai_data->port_config.pcm.bit_width = 16;
+ dai_data->port_config.pcm.slot_number_mapping[0] =
+ auxpcm_pdata->mode_8k.slot;
+ break;
+ case 16000:
+ dai_data->port_config.pcm.pcm_cfg_minor_version =
+ AFE_API_VERSION_PCM_CONFIG;
+ dai_data->port_config.pcm.aux_mode =
+ auxpcm_pdata->mode_16k.mode;
+ dai_data->port_config.pcm.sync_src =
+ auxpcm_pdata->mode_16k.sync;
+ dai_data->port_config.pcm.frame_setting =
+ auxpcm_pdata->mode_16k.frame;
+ dai_data->port_config.pcm.quantype =
+ auxpcm_pdata->mode_16k.quant;
+ dai_data->port_config.pcm.ctrl_data_out_enable =
+ auxpcm_pdata->mode_16k.data;
+ dai_data->port_config.pcm.sample_rate = dai_data->rate;
+ dai_data->port_config.pcm.num_channels = dai_data->channels;
+ dai_data->port_config.pcm.bit_width = 16;
+ dai_data->port_config.pcm.slot_number_mapping[0] =
+ auxpcm_pdata->mode_16k.slot;
+ break;
+ default:
+ dev_err(dai->dev, "AUX PCM supports only 8kHz and 16kHz sampling rate\n");
+ return -EINVAL;
+ }
return 0;
}
@@ -118,6 +152,9 @@
struct snd_soc_dai *dai)
{
int rc = 0;
+ struct afe_clk_cfg *lpass_pcm_src_clk = NULL;
+ struct afe_clk_cfg lpass_pcm_oe_clk;
+ struct msm_dai_auxpcm_pdata *auxpcm_pdata = NULL;
mutex_lock(&aux_pcm_mutex);
@@ -146,6 +183,9 @@
pr_debug("%s: dai->id = %d aux_pcm_count = %d\n", __func__,
dai->id, aux_pcm_count);
+ auxpcm_pdata = (struct msm_dai_auxpcm_pdata *)dai->dev->platform_data;
+ lpass_pcm_src_clk = (struct afe_clk_cfg *)auxpcm_pdata->clk_cfg;
+
rc = afe_close(PCM_RX); /* can block */
if (IS_ERR_VALUE(rc))
dev_err(dai->dev, "fail to close PCM_RX AFE port\n");
@@ -154,8 +194,14 @@
if (IS_ERR_VALUE(rc))
dev_err(dai->dev, "fail to close AUX PCM TX port\n");
- clk_disable_unprepare(pcm_branch_clk);
- clk_disable_unprepare(pcm_oe_branch_clk);
+ lpass_pcm_src_clk->clk_val1 = 0;
+ afe_set_lpass_clock(PCM_TX, lpass_pcm_src_clk);
+ afe_set_lpass_clock(PCM_RX, lpass_pcm_src_clk);
+
+ memcpy(&lpass_pcm_oe_clk, &lpass_clk_cfg_default,
+ sizeof(struct afe_clk_cfg));
+ lpass_pcm_oe_clk.clk_val1 = 0;
+ afe_set_lpass_clock(PCM_RX, &lpass_pcm_oe_clk);
mutex_unlock(&aux_pcm_mutex);
}
@@ -166,8 +212,12 @@
struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev);
struct msm_dai_auxpcm_pdata *auxpcm_pdata = NULL;
int rc = 0;
+ unsigned long pcm_clk_rate;
+ struct afe_clk_cfg lpass_pcm_oe_clk;
+ struct afe_clk_cfg *lpass_pcm_src_clk = NULL;
auxpcm_pdata = dai->dev->platform_data;
+ lpass_pcm_src_clk = (struct afe_clk_cfg *)auxpcm_pdata->clk_cfg;
mutex_lock(&aux_pcm_mutex);
@@ -210,27 +260,43 @@
* assert/deasset and afe_open sequence is not followed.
*/
- rc = clk_set_rate(pcm_src_clk, auxpcm_pdata->pcm_clk_rate);
+ if (dai_data->rate == 8000) {
+ pcm_clk_rate = auxpcm_pdata->mode_8k.pcm_clk_rate;
+ } else if (dai_data->rate == 16000) {
+ pcm_clk_rate = (auxpcm_pdata->mode_16k.pcm_clk_rate);
+ } else {
+ dev_err(dai->dev, "%s: Invalid AUX PCM rate %d\n", __func__,
+ dai_data->rate);
+ mutex_unlock(&aux_pcm_mutex);
+ return -EINVAL;
+ }
+
+ memcpy(lpass_pcm_src_clk, &lpass_clk_cfg_default,
+ sizeof(struct afe_clk_cfg));
+ lpass_pcm_src_clk->clk_val1 = pcm_clk_rate;
+
+ memcpy(&lpass_pcm_oe_clk, &lpass_clk_cfg_default,
+ sizeof(struct afe_clk_cfg));
+ lpass_pcm_oe_clk.clk_val1 = Q6AFE_LPASS_OSR_CLK_12_P288_MHZ;
+
+ rc = afe_set_lpass_clock(PCM_RX, lpass_pcm_src_clk);
if (rc < 0) {
- pr_err("%s: clk_set_rate failed\n", __func__);
+ pr_err("%s:afe_set_lpass_clock on RX pcm_src_clk failed\n",
+ __func__);
goto fail;
}
- rc = clk_prepare_enable(pcm_branch_clk);
- if (rc) {
- pr_err("%s: clk enable failed\n", __func__);
- goto fail;
- }
-
- rc = clk_set_rate(pcm_oe_src_clk, 24576000>>1);
+ rc = afe_set_lpass_clock(PCM_TX, lpass_pcm_src_clk);
if (rc < 0) {
- pr_err("%s: clk_set_rate on pcm oe failed\n", __func__);
+ pr_err("%s:afe_set_lpass_clock on TX pcm_src_clk failed\n",
+ __func__);
goto fail;
}
- rc = clk_prepare_enable(pcm_oe_branch_clk);
- if (rc) {
- pr_err("%s: clk enable pcm_oe_branch_clk failed\n", __func__);
+ rc = afe_set_lpass_clock(PCM_RX, &lpass_pcm_oe_clk);
+ if (rc < 0) {
+ pr_err("%s:afe_set_lpass_clock on pcm_oe_clk failed\n",
+ __func__);
goto fail;
}
@@ -238,9 +304,8 @@
afe_open(PCM_TX, &dai_data->port_config, dai_data->rate);
- mutex_unlock(&aux_pcm_mutex);
-
fail:
+ mutex_unlock(&aux_pcm_mutex);
return rc;
}
@@ -284,49 +349,6 @@
dai->dev->platform_data = auxpcm_pdata;
dai->id = dai->dev->id;
- mutex_lock(&aux_pcm_mutex);
-
- /*
- * The clk name for AUX PCM operation is passed as platform
- * data to the cpu driver, since cpu drive is unaware of any
- * boarc specific configuration.
- */
- if ((!pcm_src_clk) || (!pcm_branch_clk)) {
- pcm_src_clk = clk_get(dai->dev, auxpcm_pdata->clk);
-
- if (IS_ERR(pcm_src_clk)) {
- pr_err("%s: could not get pcm_src_clk\n", __func__);
- pcm_src_clk = NULL;
- return -ENODEV;
- }
-
- pcm_branch_clk = clk_get(dai->dev, "ibit_clk");
-
- if (IS_ERR(pcm_branch_clk)) {
- pr_err("%s: could not get pcm_branch_clk\n", __func__);
- pcm_branch_clk = NULL;
- return -ENODEV;
- }
- }
-
- if ((!pcm_oe_src_clk) || (!pcm_oe_branch_clk)) {
-
- pcm_oe_src_clk = clk_get(dai->dev, "core_oe_src_clk");
-
- if (IS_ERR(pcm_oe_src_clk)) {
- pr_err("%s: could not get pcm_oe_src_clk\n", __func__);
- pcm_oe_src_clk = NULL;
- return -ENODEV;
- }
-
- pcm_oe_branch_clk = clk_get(dai->dev, "core_oe_clk");
- if (IS_ERR(pcm_oe_branch_clk)) {
- pr_err("%s: could not get pcm_oe_clk\n", __func__);
- pcm_oe_branch_clk = NULL;
- return -ENODEV;
- }
- }
- mutex_unlock(&aux_pcm_mutex);
dai_data = kzalloc(sizeof(struct msm_dai_q6_dai_data), GFP_KERNEL);
@@ -1060,7 +1082,8 @@
{
int rc = 0;
struct msm_dai_auxpcm_pdata *auxpcm_pdata = NULL;
- u32 property_val;
+ struct afe_clk_cfg *clk_cfg = NULL;
+ uint32_t val_array[RATE_MAX_NUM_OF_AUX_PCM_RATES];
auxpcm_pdata = kzalloc(sizeof(struct msm_dai_auxpcm_pdata),
GFP_KERNEL);
@@ -1070,81 +1093,100 @@
return -ENOMEM;
}
- rc = of_property_read_string(pdev->dev.of_node,
- "qcom,msm-cpudai-auxpcm-clk",
- &auxpcm_pdata->clk);
- if (rc) {
- dev_err(&pdev->dev, "%s: qcom,msm-cpudai-auxpcm-clk missing in DT node\n",
- __func__);
- goto fail_free_plat;
- }
- rc = of_property_read_u32(pdev->dev.of_node,
- "qcom,msm-cpudai-auxpcm-mode", &property_val);
+ rc = of_property_read_u32_array(pdev->dev.of_node,
+ "qcom,msm-cpudai-auxpcm-mode",
+ val_array, RATE_MAX_NUM_OF_AUX_PCM_RATES);
if (rc) {
dev_err(&pdev->dev, "%s: qcom,msm-cpudai-auxpcm-mode missing in DT node\n",
__func__);
goto fail_free_plat;
}
- auxpcm_pdata->mode = (u16)property_val;
- rc = of_property_read_u32(pdev->dev.of_node,
- "qcom,msm-cpudai-auxpcm-sync", &property_val);
+ auxpcm_pdata->mode_8k.mode = (u16)val_array[RATE_8KHZ];
+ auxpcm_pdata->mode_16k.mode = (u16)val_array[RATE_16KHZ];
+
+ rc = of_property_read_u32_array(pdev->dev.of_node,
+ "qcom,msm-cpudai-auxpcm-sync",
+ val_array, RATE_MAX_NUM_OF_AUX_PCM_RATES);
if (rc) {
dev_err(&pdev->dev, "%s: qcom,msm-cpudai-auxpcm-sync missing in DT node\n",
__func__);
goto fail_free_plat;
}
- auxpcm_pdata->sync = (u16)property_val;
- rc = of_property_read_u32(pdev->dev.of_node,
- "qcom,msm-cpudai-auxpcm-frame", &property_val);
+ auxpcm_pdata->mode_8k.sync = (u16)val_array[RATE_8KHZ];
+ auxpcm_pdata->mode_16k.sync = (u16)val_array[RATE_16KHZ];
+
+ rc = of_property_read_u32_array(pdev->dev.of_node,
+ "qcom,msm-cpudai-auxpcm-frame",
+ val_array, RATE_MAX_NUM_OF_AUX_PCM_RATES);
+
if (rc) {
dev_err(&pdev->dev, "%s: qcom,msm-cpudai-auxpcm-frame missing in DT node\n",
__func__);
goto fail_free_plat;
}
- auxpcm_pdata->frame = (u16)property_val;
- rc = of_property_read_u32(pdev->dev.of_node,
- "qcom,msm-cpudai-auxpcm-quant", &property_val);
+ auxpcm_pdata->mode_8k.frame = (u16)val_array[RATE_8KHZ];
+ auxpcm_pdata->mode_16k.frame = (u16)val_array[RATE_16KHZ];
+
+ rc = of_property_read_u32_array(pdev->dev.of_node,
+ "qcom,msm-cpudai-auxpcm-quant",
+ val_array, RATE_MAX_NUM_OF_AUX_PCM_RATES);
if (rc) {
dev_err(&pdev->dev, "%s: qcom,msm-cpudai-auxpcm-quant missing in DT node\n",
__func__);
goto fail_free_plat;
}
- auxpcm_pdata->quant = (u16)property_val;
- rc = of_property_read_u32(pdev->dev.of_node,
- "qcom,msm-cpudai-auxpcm-slot", &property_val);
+ auxpcm_pdata->mode_8k.quant = (u16)val_array[RATE_8KHZ];
+ auxpcm_pdata->mode_16k.quant = (u16)val_array[RATE_16KHZ];
+
+ rc = of_property_read_u32_array(pdev->dev.of_node,
+ "qcom,msm-cpudai-auxpcm-slot",
+ val_array, RATE_MAX_NUM_OF_AUX_PCM_RATES);
if (rc) {
dev_err(&pdev->dev, "%s: qcom,msm-cpudai-auxpcm-slot missing in DT node\n",
__func__);
goto fail_free_plat;
}
- auxpcm_pdata->slot = (u16)property_val;
- rc = of_property_read_u32(pdev->dev.of_node,
- "qcom,msm-cpudai-auxpcm-data", &property_val);
+ auxpcm_pdata->mode_8k.slot = (u16)val_array[RATE_8KHZ];
+ auxpcm_pdata->mode_16k.slot = (u16)val_array[RATE_16KHZ];
+
+ rc = of_property_read_u32_array(pdev->dev.of_node,
+ "qcom,msm-cpudai-auxpcm-data",
+ val_array, RATE_MAX_NUM_OF_AUX_PCM_RATES);
if (rc) {
dev_err(&pdev->dev, "%s: qcom,msm-cpudai-auxpcm-data missing in DT node\n",
__func__);
goto fail_free_plat;
}
- auxpcm_pdata->data = (u16)property_val;
- rc = of_property_read_u32(pdev->dev.of_node,
+ auxpcm_pdata->mode_8k.data = (u16)val_array[RATE_8KHZ];
+ auxpcm_pdata->mode_16k.data = (u16)val_array[RATE_16KHZ];
+
+ rc = of_property_read_u32_array(pdev->dev.of_node,
"qcom,msm-cpudai-auxpcm-pcm-clk-rate",
- &auxpcm_pdata->pcm_clk_rate);
- if (rc) {
- dev_err(&pdev->dev, "%s: qcom,msm-cpudai-auxpcm-pcm-clk-rate missing in DT node\n",
- __func__);
+ val_array, RATE_MAX_NUM_OF_AUX_PCM_RATES);
+
+ auxpcm_pdata->mode_8k.pcm_clk_rate = (int)val_array[RATE_8KHZ];
+ auxpcm_pdata->mode_16k.pcm_clk_rate = (int)val_array[RATE_16KHZ];
+
+ clk_cfg = kzalloc(sizeof(struct afe_clk_cfg), GFP_KERNEL);
+ if (clk_cfg == NULL) {
+ pr_err("%s: Failed to allocate memory for clk cfg\n", __func__);
goto fail_free_plat;
}
+ auxpcm_pdata->clk_cfg = clk_cfg;
+
platform_set_drvdata(pdev, auxpcm_pdata);
rc = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
if (rc) {
dev_err(&pdev->dev, "%s: failed to add child nodes, rc=%d\n",
__func__, rc);
- goto fail_free_plat;
+ goto fail_free_plat1;
}
return rc;
+fail_free_plat1:
+ kfree(clk_cfg);
fail_free_plat:
kfree(auxpcm_pdata);
return rc;
@@ -1159,9 +1201,12 @@
static int __devexit msm_auxpcm_resource_remove(
struct platform_device *pdev)
{
- void *auxpcm_pdata;
+ struct msm_dai_auxpcm_pdata *auxpcm_pdata;
+ struct afe_clk_cfg *clk_cfg;
auxpcm_pdata = dev_get_drvdata(&pdev->dev);
+ clk_cfg = (struct afe_clk_cfg *)auxpcm_pdata->clk_cfg;
+ kfree(clk_cfg);
kfree(auxpcm_pdata);
return 0;
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c
index 1d6e106..fa8609e 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. 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
@@ -40,6 +40,12 @@
struct snd_pcm *pcm;
};
+struct snd_msm_volume {
+ struct msm_audio *prtd;
+ unsigned volume;
+};
+static struct snd_msm_volume pcm_audio = {NULL, 0x2000};
+
#define PLAYBACK_NUM_PERIODS 8
#define PLAYBACK_MAX_PERIOD_SIZE 12288
#define PLAYBACK_MIN_PERIOD_SIZE 2048
@@ -213,8 +219,11 @@
if (prtd->enabled)
return 0;
- ret = q6asm_media_format_block_pcm(prtd->audio_client, runtime->rate,
- runtime->channels);
+ ret = q6asm_media_format_block_multi_ch_pcm(prtd->audio_client,
+ runtime->rate,
+ runtime->channels,
+ !prtd->set_channel_map,
+ prtd->channel_map);
if (ret < 0)
pr_info("%s: CMD Format block failed\n", __func__);
@@ -365,7 +374,9 @@
}
prtd->dsp_cnt = 0;
+ prtd->set_channel_map = false;
runtime->private_data = prtd;
+ pcm_audio.prtd = prtd;
return 0;
}
@@ -449,7 +460,8 @@
prtd->audio_client);
msm_pcm_routing_dereg_phy_stream(soc_prtd->dai_link->be_id,
- SNDRV_PCM_STREAM_PLAYBACK);
+ SNDRV_PCM_STREAM_PLAYBACK);
+ pcm_audio.prtd = NULL;
q6asm_audio_client_free(prtd->audio_client);
kfree(prtd);
return 0;
@@ -691,13 +703,49 @@
.mmap = msm_pcm_mmap,
};
+static int pcm_chmap_ctl_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int i;
+ char channel_mapping[PCM_FORMAT_MAX_NUM_CHANNEL];
+
+ pr_debug("%s", __func__);
+ for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL; i++)
+ channel_mapping[i] = (char)(ucontrol->value.integer.value[i]);
+ if (pcm_audio.prtd) {
+ pcm_audio.prtd->set_channel_map = true;
+ memcpy(pcm_audio.prtd->channel_map, channel_mapping,
+ PCM_FORMAT_MAX_NUM_CHANNEL);
+ }
+ return 0;
+}
+
static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd)
{
struct snd_card *card = rtd->card->snd_card;
- int ret = 0;
+ struct snd_pcm *pcm = rtd->pcm->streams[0].pcm;
+ struct snd_pcm_chmap *chmap_info;
+ struct snd_kcontrol *kctl;
+ char device_num[3];
+ int i, ret = 0;
if (!card->dev->coherent_dma_mask)
card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+
+ pr_debug("%s, Channel map cntrl add\n", __func__);
+ ret = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ NULL, PCM_FORMAT_MAX_NUM_CHANNEL, 0,
+ &chmap_info);
+ if (ret < 0)
+ return ret;
+ kctl = chmap_info->kctl;
+ for (i = 0; i < kctl->count; i++)
+ kctl->vd[i].access |= SNDRV_CTL_ELEM_ACCESS_WRITE;
+ snprintf(device_num, sizeof(device_num), "%d", pcm->device);
+ strlcat(kctl->id.name, device_num, sizeof(kctl->id.name));
+ pr_debug("%s, Overwriting channel map control name to: %s",
+ __func__, kctl->id.name);
+ kctl->put = pcm_chmap_ctl_put;
return ret;
}
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.h b/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.h
index e25d356..9a770bf 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.h
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.h
@@ -1,7 +1,7 @@
/*
* Copyright (C) 2008 Google, Inc.
* Copyright (C) 2008 HTC Corporation
- * Copyright (c) 2012 Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2012-2013 The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@@ -82,6 +82,8 @@
int periods;
int mmap_flag;
atomic_t pending_buffer;
+ bool set_channel_map;
+ char channel_map[8];
};
struct output_meta_data_st {
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c
index 26f634b..6b957f6 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. 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
@@ -805,6 +805,31 @@
return 0;
}
+static int msm_routing_get_channel_map_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ char channel_map[PCM_FORMAT_MAX_NUM_CHANNEL];
+ int i;
+
+ adm_get_multi_ch_map(channel_map);
+ for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL; i++)
+ ucontrol->value.integer.value[i] = (unsigned) channel_map[i];
+ return 0;
+}
+
+static int msm_routing_put_channel_map_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ char channel_map[PCM_FORMAT_MAX_NUM_CHANNEL];
+ int i;
+
+ for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL; i++)
+ channel_map[i] = (char)(ucontrol->value.integer.value[i]);
+ adm_set_multi_ch_map(channel_map);
+
+ return 0;
+}
+
static int msm_routing_get_srs_trumedia_control(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -1577,6 +1602,12 @@
msm_routing_set_compressed_vol_mixer, compressed_rx_vol_gain),
};
+static const struct snd_kcontrol_new multi_ch_channel_map_mixer_controls[] = {
+ SOC_SINGLE_MULTI_EXT("Playback Channel Map", SND_SOC_NOPM, 0, 16,
+ 0, 8, msm_routing_get_channel_map_mixer,
+ msm_routing_put_channel_map_mixer),
+};
+
static const struct snd_kcontrol_new lpa_SRS_trumedia_controls[] = {
{.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "SRS TruMedia",
@@ -2491,6 +2522,10 @@
snd_soc_add_platform_controls(platform,
lpa_SRS_trumedia_controls_I2S,
ARRAY_SIZE(lpa_SRS_trumedia_controls_I2S));
+
+ snd_soc_add_platform_controls(platform,
+ multi_ch_channel_map_mixer_controls,
+ ARRAY_SIZE(multi_ch_channel_map_mixer_controls));
return 0;
}
diff --git a/sound/soc/msm/qdsp6v2/q6adm.c b/sound/soc/msm/qdsp6v2/q6adm.c
index 1e0ad9e..bc11304 100644
--- a/sound/soc/msm/qdsp6v2/q6adm.c
+++ b/sound/soc/msm/qdsp6v2/q6adm.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. 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
@@ -18,7 +18,6 @@
#include <linux/atomic.h>
#include <linux/wait.h>
-#include <mach/qdsp6v2/audio_acdb.h>
#include <mach/qdsp6v2/rtac.h>
#include <sound/apr_audio-v2.h>
@@ -27,6 +26,9 @@
#include <sound/q6audio-v2.h>
#include <sound/q6afe-v2.h>
+#include "audio_acdb.h"
+
+
#define TIMEOUT_MS 1000
#define RESET_COPP_ID 99
@@ -50,6 +52,15 @@
static struct adm_ctl this_adm;
+struct adm_multi_ch_map {
+ bool set_channel_map;
+ char channel_mapping[PCM_FORMAT_MAX_NUM_CHANNEL];
+};
+
+static struct adm_multi_ch_map multi_ch_map = { false,
+ {0, 0, 0, 0, 0, 0, 0, 0}
+ };
+
int srs_trumedia_open(int port_id, int srs_tech_id, void *srs_params)
{
struct adm_cmd_set_pp_params_inband_v5 *adm_params = NULL;
@@ -264,6 +275,21 @@
__func__, data->opcode, data->payload_size);
}
+void adm_set_multi_ch_map(char *channel_map)
+{
+ memcpy(multi_ch_map.channel_mapping, channel_map,
+ PCM_FORMAT_MAX_NUM_CHANNEL);
+ multi_ch_map.set_channel_map = true;
+}
+
+void adm_get_multi_ch_map(char *channel_map)
+{
+ if (multi_ch_map.set_channel_map) {
+ memcpy(channel_map, multi_ch_map.channel_mapping,
+ PCM_FORMAT_MAX_NUM_CHANNEL);
+ }
+}
+
static int32_t adm_callback(struct apr_client_data *data, void *priv)
{
uint32_t *payload;
@@ -741,6 +767,11 @@
channel_mode);
return -EINVAL;
}
+ if ((open.dev_num_channel > 2) &&
+ multi_ch_map.set_channel_map)
+ memcpy(open.dev_channel_mapping,
+ multi_ch_map.channel_mapping,
+ PCM_FORMAT_MAX_NUM_CHANNEL);
pr_debug("%s: port_id=%#x rate=%d topology_id=0x%X\n",
__func__, open.endpoint_id_1, open.sample_rate,
diff --git a/sound/soc/msm/qdsp6v2/q6afe.c b/sound/soc/msm/qdsp6v2/q6afe.c
index d836610..846c80e 100644
--- a/sound/soc/msm/qdsp6v2/q6afe.c
+++ b/sound/soc/msm/qdsp6v2/q6afe.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. 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
@@ -19,12 +19,12 @@
#include <linux/jiffies.h>
#include <linux/sched.h>
#include <linux/msm_ion.h>
-#include <mach/qdsp6v2/audio_acdb.h>
#include <sound/apr_audio-v2.h>
#include <sound/q6afe-v2.h>
-
#include <sound/q6audio-v2.h>
+#include "audio_acdb.h"
+
struct afe_ctl {
void *apr;
@@ -2019,6 +2019,175 @@
return ret;
}
+int afe_set_lpass_clock(u16 port_id, struct afe_clk_cfg *cfg)
+{
+ struct afe_lpass_clk_config_command clk_cfg;
+ int index = 0;
+ int ret = 0;
+
+ if (!cfg) {
+ pr_err("%s: clock cfg is NULL\n", __func__);
+ ret = -EINVAL;
+ return ret;
+ }
+ index = q6audio_get_port_index(port_id);
+ if (q6audio_is_digital_pcm_interface(port_id) < 0)
+ return -EINVAL;
+
+ ret = afe_q6_interface_prepare();
+ if (ret != 0)
+ return ret;
+
+ clk_cfg.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ clk_cfg.hdr.pkt_size = sizeof(clk_cfg);
+ clk_cfg.hdr.src_port = 0;
+ clk_cfg.hdr.dest_port = 0;
+ clk_cfg.hdr.token = index;
+
+ clk_cfg.hdr.opcode = AFE_PORT_CMD_SET_PARAM_V2;
+ clk_cfg.param.port_id = q6audio_get_port_id(port_id);
+ clk_cfg.param.payload_size = sizeof(clk_cfg) - sizeof(struct apr_hdr)
+ - sizeof(clk_cfg.param);
+ clk_cfg.param.payload_address_lsw = 0x00;
+ clk_cfg.param.payload_address_msw = 0x00;
+ clk_cfg.param.mem_map_handle = 0x00;
+ clk_cfg.pdata.module_id = AFE_MODULE_AUDIO_DEV_INTERFACE;
+ clk_cfg.pdata.param_id = AFE_PARAM_ID_LPAIF_CLK_CONFIG;
+ clk_cfg.pdata.param_size = sizeof(clk_cfg.clk_cfg);
+ clk_cfg.clk_cfg = *cfg;
+
+ pr_debug("%s: Minor version =%x clk val1 = %d\n"
+ "clk val2 = %d, clk src = %x\n"
+ "clk root = %x clk mode = %x resrv = %x\n"
+ "port id = %x\n",
+ __func__, cfg->i2s_cfg_minor_version,
+ cfg->clk_val1, cfg->clk_val2, cfg->clk_src,
+ cfg->clk_root, cfg->clk_set_mode,
+ cfg->reserved, q6audio_get_port_id(port_id));
+
+ atomic_set(&this_afe.state, 1);
+ atomic_set(&this_afe.status, 0);
+ ret = apr_send_pkt(this_afe.apr, (uint32_t *) &clk_cfg);
+ if (ret < 0) {
+ pr_err("%s: AFE enable 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) {
+ pr_err("%s: wait_event timeout\n", __func__);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+ if (atomic_read(&this_afe.status) != 0) {
+ pr_err("%s: config cmd failed\n", __func__);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+
+fail_cmd:
+ return ret;
+}
+
+int afe_set_lpass_internal_digital_codec_clock(u16 port_id,
+ struct afe_digital_clk_cfg *cfg)
+{
+ struct afe_lpass_digital_clk_config_command clk_cfg;
+ int index = 0;
+ int ret = 0;
+
+ if (!cfg) {
+ pr_err("%s: clock cfg is NULL\n", __func__);
+ ret = -EINVAL;
+ return ret;
+ }
+ index = q6audio_get_port_index(port_id);
+ if (q6audio_is_digital_pcm_interface(port_id) < 0)
+ return -EINVAL;
+
+ ret = afe_q6_interface_prepare();
+ if (ret != 0)
+ return ret;
+
+ clk_cfg.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ clk_cfg.hdr.pkt_size = sizeof(clk_cfg);
+ clk_cfg.hdr.src_port = 0;
+ clk_cfg.hdr.dest_port = 0;
+ clk_cfg.hdr.token = index;
+
+ clk_cfg.hdr.opcode = AFE_PORT_CMD_SET_PARAM_V2;
+ clk_cfg.param.port_id = q6audio_get_port_id(port_id);
+ clk_cfg.param.payload_size = sizeof(clk_cfg) - sizeof(struct apr_hdr)
+ - sizeof(clk_cfg.param);
+ clk_cfg.param.payload_address_lsw = 0x00;
+ clk_cfg.param.payload_address_msw = 0x00;
+ clk_cfg.param.mem_map_handle = 0x00;
+ clk_cfg.pdata.module_id = AFE_MODULE_AUDIO_DEV_INTERFACE;
+ clk_cfg.pdata.param_id = AFE_PARAM_ID_INTERNAL_DIGIATL_CDC_CLK_CONFIG;
+ clk_cfg.pdata.param_size = sizeof(clk_cfg.clk_cfg);
+ clk_cfg.clk_cfg = *cfg;
+
+ pr_debug("%s: Minor version =%x clk val = %d\n"
+ "clk root = %x resrv = %x port id = %x\n",
+ __func__, cfg->i2s_cfg_minor_version,
+ cfg->clk_val, cfg->clk_root, cfg->reserved,
+ q6audio_get_port_id(port_id));
+
+ atomic_set(&this_afe.state, 1);
+ atomic_set(&this_afe.status, 0);
+ ret = apr_send_pkt(this_afe.apr, (uint32_t *) &clk_cfg);
+ if (ret < 0) {
+ pr_err("%s: AFE enable 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) {
+ pr_err("%s: wait_event timeout\n", __func__);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+ if (atomic_read(&this_afe.status) != 0) {
+ pr_err("%s: config cmd failed\n", __func__);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+
+fail_cmd:
+ return ret;
+}
+
+int q6afe_check_osr_clk_freq(u32 freq)
+{
+ int ret = 0;
+ switch (freq) {
+ case Q6AFE_LPASS_OSR_CLK_12_P288_MHZ:
+ case Q6AFE_LPASS_OSR_CLK_8_P192_MHZ:
+ case Q6AFE_LPASS_OSR_CLK_6_P144_MHZ:
+ case Q6AFE_LPASS_OSR_CLK_4_P096_MHZ:
+ case Q6AFE_LPASS_OSR_CLK_3_P072_MHZ:
+ case Q6AFE_LPASS_OSR_CLK_2_P048_MHZ:
+ case Q6AFE_LPASS_OSR_CLK_1_P536_MHZ:
+ case Q6AFE_LPASS_OSR_CLK_1_P024_MHZ:
+ case Q6AFE_LPASS_OSR_CLK_768_kHZ:
+ case Q6AFE_LPASS_OSR_CLK_512_kHZ:
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
static int __init afe_init(void)
{
int i = 0;
diff --git a/sound/soc/msm/qdsp6v2/q6asm.c b/sound/soc/msm/qdsp6v2/q6asm.c
index 0cbd136..fc8bfeb 100644
--- a/sound/soc/msm/qdsp6v2/q6asm.c
+++ b/sound/soc/msm/qdsp6v2/q6asm.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
* Author: Brian Swetland <swetland@google.com>
*
* This software is licensed under the terms of the GNU General Public
@@ -34,13 +34,15 @@
#include <mach/memory.h>
#include <mach/debug_mm.h>
-#include <mach/qdsp6v2/audio_acdb.h>
#include <mach/qdsp6v2/rtac.h>
#include <mach/msm_subsystem_map.h>
#include <sound/apr_audio-v2.h>
#include <sound/q6asm-v2.h>
+#include "audio_acdb.h"
+
+
#define TRUE 0x01
#define FALSE 0x00
#define READDONE_IDX_STATUS 0
@@ -1849,12 +1851,12 @@
lchannel_mapping[3] = PCM_CHANNEL_LB;
lchannel_mapping[4] = PCM_CHANNEL_RB;
} else if (channels == 6) {
- channel_mapping[0] = PCM_CHANNEL_FC;
- channel_mapping[1] = PCM_CHANNEL_FL;
- channel_mapping[2] = PCM_CHANNEL_FR;
- channel_mapping[3] = PCM_CHANNEL_LB;
- channel_mapping[4] = PCM_CHANNEL_RB;
- channel_mapping[5] = PCM_CHANNEL_LFE;
+ lchannel_mapping[0] = PCM_CHANNEL_FC;
+ lchannel_mapping[1] = PCM_CHANNEL_FL;
+ lchannel_mapping[2] = PCM_CHANNEL_FR;
+ lchannel_mapping[3] = PCM_CHANNEL_LB;
+ lchannel_mapping[4] = PCM_CHANNEL_RB;
+ lchannel_mapping[5] = PCM_CHANNEL_LFE;
} else if (channels == 8) {
lchannel_mapping[0] = PCM_CHANNEL_FL;
lchannel_mapping[1] = PCM_CHANNEL_FR;
@@ -2164,6 +2166,56 @@
return -EINVAL;
}
+int q6asm_media_format_block_multi_ch_pcm(struct audio_client *ac,
+ uint32_t rate, uint32_t channels,
+ bool use_default_chmap, char *channel_map)
+{
+ struct asm_multi_channel_pcm_fmt_blk_v2 fmt;
+ u8 *channel_mapping;
+ int rc = 0;
+
+ pr_debug("%s:session[%d]rate[%d]ch[%d]\n", __func__, ac->session, rate,
+ channels);
+
+ q6asm_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE);
+
+ fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2;
+ fmt.fmt_blk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) -
+ sizeof(fmt.fmt_blk);
+ fmt.num_channels = channels;
+ fmt.bits_per_sample = 16;
+ fmt.sample_rate = rate;
+ fmt.is_signed = 1;
+
+ channel_mapping = fmt.channel_mapping;
+
+ memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL);
+
+ if (use_default_chmap) {
+ if (q6asm_map_channels(channel_mapping, channels)) {
+ pr_err("%s: map channels failed", __func__);
+ return -EINVAL;
+ }
+ } else {
+ memcpy(channel_mapping, channel_map,
+ PCM_FORMAT_MAX_NUM_CHANNEL);
+ }
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt);
+ if (rc < 0) {
+ pr_err("%s:Comamnd open failed\n", __func__);
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) == 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s:timeout. waited for format update\n", __func__);
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return -EINVAL;
+}
int q6asm_media_format_block_multi_aac(struct audio_client *ac,
struct asm_aac_cfg *cfg)
{
diff --git a/sound/soc/msm/qdsp6v2/q6audio-v2.c b/sound/soc/msm/qdsp6v2/q6audio-v2.c
index 5e2e618..985a33d 100644
--- a/sound/soc/msm/qdsp6v2/q6audio-v2.c
+++ b/sound/soc/msm/qdsp6v2/q6audio-v2.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. 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
@@ -123,6 +123,31 @@
return ret;
}
+int q6audio_is_digital_pcm_interface(u16 port_id)
+{
+ int ret = 0;
+
+ switch (port_id) {
+ case PRIMARY_I2S_RX:
+ case PRIMARY_I2S_TX:
+ case PCM_RX:
+ case PCM_TX:
+ case SECONDARY_I2S_RX:
+ case SECONDARY_I2S_TX:
+ case MI2S_RX:
+ case MI2S_TX:
+ case AFE_PORT_ID_TERTIARY_MI2S_TX:
+ case AFE_PORT_ID_TERTIARY_MI2S_RX:
+ case AFE_PORT_ID_QUATERNARY_MI2S_RX:
+ case AFE_PORT_ID_QUATERNARY_MI2S_TX:
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
int q6audio_validate_port(u16 port_id)
{
int ret;
diff --git a/sound/soc/msm/qdsp6v2/q6voice.c b/sound/soc/msm/qdsp6v2/q6voice.c
index 349fcf2..bd65213 100644
--- a/sound/soc/msm/qdsp6v2/q6voice.c
+++ b/sound/soc/msm/qdsp6v2/q6voice.c
@@ -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
@@ -18,7 +18,6 @@
#include <linux/mutex.h>
#include <asm/mach-types.h>
-#include <mach/qdsp6v2/audio_acdb.h>
#include <mach/qdsp6v2/rtac.h>
#include <mach/socinfo.h>
#include <mach/qdsp6v2/apr_tal.h>
@@ -26,8 +25,10 @@
#include "sound/apr_audio-v2.h"
#include "sound/q6afe-v2.h"
+#include "audio_acdb.h"
#include "q6voice.h"
+
#define TIMEOUT_MS 200
@@ -1456,7 +1457,7 @@
goto fail;
}
- get_all_vocstrm_cal(&cal_block);
+ get_vocstrm_cal(&cal_block);
if (cal_block.cal_size == 0) {
pr_err("%s: CVS cal size is 0\n", __func__);
@@ -1524,7 +1525,7 @@
goto fail;
}
- get_all_vocstrm_cal(&cal_block);
+ get_vocstrm_cal(&cal_block);
if (cal_block.cal_size == 0)
return 0;
@@ -1714,7 +1715,7 @@
goto fail;
}
- get_all_vocproc_cal(&cal_block);
+ get_vocproc_cal(&cal_block);
if (cal_block.cal_size == 0) {
pr_err("%s: CVP cal size is 0\n", __func__);
@@ -1782,7 +1783,7 @@
goto fail;
}
- get_all_vocproc_cal(&cal_block);
+ get_vocproc_cal(&cal_block);
if (cal_block.cal_size == 0)
return 0;
@@ -1843,7 +1844,7 @@
goto fail;
}
- get_all_vocvol_cal(&cal_block);
+ get_vocvol_cal(&cal_block);
if (cal_block.cal_size == 0) {
pr_err("%s: CVP vol cal size is 0\n", __func__);
@@ -1914,7 +1915,7 @@
goto fail;
}
- get_all_vocvol_cal(&cal_block);
+ get_vocvol_cal(&cal_block);
if (cal_block.cal_size == 0)
return 0;
@@ -4004,7 +4005,7 @@
if (data->payload_size) {
ptr = data->payload;
- pr_info("%x %x\n", ptr[0], ptr[1]);
+ pr_debug("%x %x\n", ptr[0], ptr[1]);
/* ping mvm service ACK */
switch (ptr[0]) {
case VSS_IMVM_CMD_CREATE_PASSIVE_CONTROL_SESSION:
@@ -4136,7 +4137,7 @@
if (data->payload_size) {
ptr = data->payload;
- pr_info("%x %x\n", ptr[0], ptr[1]);
+ pr_debug("%x %x\n", ptr[0], ptr[1]);
if (ptr[1] != 0) {
pr_err("%s: cmd = 0x%x returned error = 0x%x\n",
__func__, ptr[0], ptr[1]);
@@ -4379,7 +4380,7 @@
if (data->payload_size) {
ptr = data->payload;
- pr_info("%x %x\n", ptr[0], ptr[1]);
+ pr_debug("%x %x\n", ptr[0], ptr[1]);
if (ptr[1] != 0) {
pr_err("%s: cmd = 0x%x returned error = 0x%x\n",
__func__, ptr[0], ptr[1]);
diff --git a/arch/arm/mach-msm/qdsp6v2/rtac_v2.c b/sound/soc/msm/qdsp6v2/rtac.c
similarity index 98%
rename from arch/arm/mach-msm/qdsp6v2/rtac_v2.c
rename to sound/soc/msm/qdsp6v2/rtac.c
index 409d796..1f2a487 100644
--- a/arch/arm/mach-msm/qdsp6v2/rtac_v2.c
+++ b/sound/soc/msm/qdsp6v2/rtac.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. 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
@@ -19,13 +19,15 @@
#include <linux/mutex.h>
#include <linux/sched.h>
#include <linux/msm_audio_acdb.h>
-#include <asm/atomic.h>
-#include <mach/qdsp6v2/audio_acdb.h>
+#include <linux/atomic.h>
#include <mach/qdsp6v2/rtac.h>
-#include "q6audio_common.h"
+#include <sound/q6asm-v2.h>
#include <sound/q6afe-v2.h>
#include <sound/apr_audio-v2.h>
+#include "audio_acdb.h"
+
+
#ifndef CONFIG_RTAC
void rtac_add_adm_device(u32 port_id, u32 copp_id, u32 path_id, u32 popp_id) {}
@@ -1041,7 +1043,7 @@
module_init(rtac_init);
-MODULE_DESCRIPTION("MSM 8x60 Real-Time Audio Calibration driver");
+MODULE_DESCRIPTION("SoC QDSP6v2 Real-Time Audio Calibration driver");
MODULE_LICENSE("GPL v2");
#endif
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index 5ac643416..99047178 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -78,7 +78,7 @@
[snd_soc_dapm_pre] = 0,
[snd_soc_dapm_aif_in] = 1,
[snd_soc_dapm_aif_out] = 1,
- [snd_soc_dapm_adc] = 1,
+ [snd_soc_dapm_adc] = 5,
[snd_soc_dapm_hp] = 2,
[snd_soc_dapm_spk] = 2,
[snd_soc_dapm_out_drv] = 2,