Merge "defconfig: msm8226: enable deferred resume"
diff --git a/Documentation/devicetree/bindings/arm/msm/msm_bus.txt b/Documentation/devicetree/bindings/arm/msm/msm_bus.txt
index 9f74225..e6e54bf 100644
--- a/Documentation/devicetree/bindings/arm/msm/msm_bus.txt
+++ b/Documentation/devicetree/bindings/arm/msm/msm_bus.txt
@@ -100,8 +100,8 @@
(Can be Fixed/Limiter/Bypass/Regulator)
qcom,bimc,bw: Bandwidth limit for a BIMC master using dual modes.
This bandwidth is used to calculate Grant count and
- other parameters used in Limiter and Regular mode
- for static BKE configuration. It is defined in KBps.
+ other parameters used in Limiter and Regular mode.
+ for static BKE configuration. It is defined in KBytes/s.
qcom,bimc,gp: Grant Period for configuring a master in limiter
mode. This is an integer value in nano-seconds.
qcom,bimc,thmp: Medium threshold percentage for BIMC masters.
@@ -111,11 +111,12 @@
1 and 100.
qcom,thresh: Beyond this threshold frequency, the mode usage is
switched from mode specified by property qcom,mode
- to the one specified by qcom,mode-thresh. In case the
- requested IB value falls below this threshold, the mode
- is switched back to qcom,mode. Frequency is specified in
- KBps.
-
+ to the one specified by qcom,mode-thresh. These thresholds
+ can be setup in increasing order of thresholds, so the
+ requested IB is evaluated at each threshold level before
+ making the decision to switch QoS modes and applying the
+ corresponding qcom,bimc,bw limitig bw as needed.
+ This is specified in KBytes/s.
diff --git a/Documentation/devicetree/bindings/arm/msm/pm-8x60.txt b/Documentation/devicetree/bindings/arm/msm/pm-8x60.txt
index 795af3b..2fbe4ca 100644
--- a/Documentation/devicetree/bindings/arm/msm/pm-8x60.txt
+++ b/Documentation/devicetree/bindings/arm/msm/pm-8x60.txt
@@ -12,6 +12,7 @@
The required properties for PM-8x60 are:
- compatible: "qcom,pm-8x60"
+- qcom,lpm-levels: phandle for associated lpm_levels device.
The optional properties are:
@@ -39,4 +40,5 @@
reg = <0xfe800664 0x40>;
qcom,pc-mode = "tz_l2_int";
qcom,use-sync-timer;
+ qcom,lpm-levels = <&lpm_levels>;
};
diff --git a/Documentation/devicetree/bindings/fb/mdss-dsi-ctrl.txt b/Documentation/devicetree/bindings/fb/mdss-dsi-ctrl.txt
index e8b02cf..b88c3ce 100644
--- a/Documentation/devicetree/bindings/fb/mdss-dsi-ctrl.txt
+++ b/Documentation/devicetree/bindings/fb/mdss-dsi-ctrl.txt
@@ -6,8 +6,12 @@
Required properties:
- compatible: Must be "qcom,mdss-dsi-ctrl"
- cell-index: Specifies the controller used among the two controllers.
-- reg: Offset and length of the register regions(s) for the device.
+- reg: Base address and length of the different register
+ regions(s) required for DSI device functionality.
- reg-names: A list of strings that map in order to the list of regs.
+ "dsi_ctrl" - MDSS DSI controller register region
+ "dsi_phy" - MDSS DSI PHY register region
+ "mmss_misc_phys" - Register region for MMSS DSI clamps
- vdd-supply: Phandle for vdd regulator device node.
- vddio-supply: Phandle for vdd-io regulator device node.
- vdda-supply: Phandle for vreg regulator device node.
@@ -53,9 +57,10 @@
compatible = "qcom,mdss-dsi-ctrl";
label = "MDSS DSI CTRL->0";
cell-index = <0>;
- reg = <0xfd922800 0x600>,
+ reg = <0xfd922800 0x1f8>,
+ <0xfd922b00 0x2b0>,
<0xfd828000 0x108>;
- reg-names = "dsi_phys", "mmss_misc_phys";
+ reg-names = "dsi_ctrl", "dsi_phy", "mmss_misc_phys";
vdd-supply = <&pm8226_l15>;
vddio-supply = <&pm8226_l8>;
vdda-supply = <&pm8226_l4>;
diff --git a/Documentation/devicetree/bindings/fb/mdss-mdp.txt b/Documentation/devicetree/bindings/fb/mdss-mdp.txt
index c2b963f..cfcc48b 100644
--- a/Documentation/devicetree/bindings/fb/mdss-mdp.txt
+++ b/Documentation/devicetree/bindings/fb/mdss-mdp.txt
@@ -27,6 +27,14 @@
to the respective VIG pipes. Number of xin ids
defined should match the number of offsets
defined in property: qcom,mdss-pipe-vig-off
+- qcom,mdss-pipe-vig-clk-ctrl-off: Array of offsets describing clk control
+ offsets for dynamic clock gating. 1st value
+ in the array represents offset of the control
+ register. 2nd value represents bit offset within
+ control register and 3rd value represents bit
+ offset within status register. Number of tuples
+ defined should match the number of offsets
+ defined in property: qcom,mdss-pipe-vig-off
- qcom,mdss-pipe-rgb-off: Array of offsets for MDP source surface pipes of
type RGB, the offsets are calculated from
register "mdp_phys" defined in reg property.
@@ -42,6 +50,14 @@
to the respective RGB pipes. Number of xin ids
defined should match the number of offsets
defined in property: qcom,mdss-pipe-rgb-off
+- qcom,mdss-pipe-rgb-clk-ctrl-off: Array of offsets describing clk control
+ offsets for dynamic clock gating. 1st value
+ in the array represents offset of the control
+ register. 2nd value represents bit offset within
+ control register and 3rd value represents bit
+ offset within status register. Number of tuples
+ defined should match the number of offsets
+ defined in property: qcom,mdss-pipe-rgb-off
- qcom,mdss-pipe-dma-off: Array of offsets for MDP source surface pipes of
type DMA, the offsets are calculated from
register "mdp_phys" defined in reg property.
@@ -57,6 +73,14 @@
to the respective DMA pipes. Number of xin ids
defined should match the number of offsets
defined in property: qcom,mdss-pipe-dma-off
+- qcom,mdss-pipe-dma-clk-ctrl-off: Array of offsets describing clk control
+ offsets for dynamic clock gating. 1st value
+ in the array represents offset of the control
+ register. 2nd value represents bit offset within
+ control register and 3rd value represents bit
+ offset within status register. Number of tuples
+ defined should match the number of offsets
+ defined in property: qcom,mdss-pipe-dma-off
- qcom,mdss-smp-data: Array of shared memory pool data. There should
be only two values in this property. The first
value corresponds to the number of smp blocks
@@ -364,6 +388,19 @@
qcom,mdss-has-decimation;
qcom,mdss-has-wfd-blk;
+ qcom,mdss-pipe-vig-clk-ctrl-offsets = <0x3AC 0 0>,
+ <0x3B4 0 0>,
+ <0x3BC 0 0>,
+ <0x3C4 0 0>;
+
+ qcom,mdss-pipe-rgb-clk-ctrl-offsets = <0x3AC 4 8>,
+ <0x3B4 4 8>,
+ <0x3BC 4 8>,
+ <0x3C4 4 8>;
+
+ qcom,mdss-pipe-dma-clk-ctrl-offsets = <0x3AC 8 12>,
+ <0x3B4 8 12>;
+
qcom,mdss-ctl-off = <0x00000600 0x00000700 0x00000800
0x00000900 0x0000A00>;
qcom,mdss-mixer-intf-off = <0x00003200 0x00003600
diff --git a/Documentation/devicetree/bindings/hwmon/qpnp-adc-current.txt b/Documentation/devicetree/bindings/hwmon/qpnp-adc-current.txt
index d0cad52..9acf54a 100644
--- a/Documentation/devicetree/bindings/hwmon/qpnp-adc-current.txt
+++ b/Documentation/devicetree/bindings/hwmon/qpnp-adc-current.txt
@@ -33,6 +33,9 @@
apply the default RSENSE if conditions are met.
1 : Select this type to read the IADC, SMBB trim register and
manufacturer type and apply the default RSENSE if conditions are met.
+- qcom,pmic-revid : Phandle pointing to the revision peripheral node. Use it to query the
+ PMIC type and revision for applying the appropriate temperature
+ compensation parameters.
Channel node
NOTE: Atleast one Channel node is required.
diff --git a/Documentation/devicetree/bindings/hwmon/qpnp-adc-voltage.txt b/Documentation/devicetree/bindings/hwmon/qpnp-adc-voltage.txt
index dd0c440..83403ba 100644
--- a/Documentation/devicetree/bindings/hwmon/qpnp-adc-voltage.txt
+++ b/Documentation/devicetree/bindings/hwmon/qpnp-adc-voltage.txt
@@ -22,6 +22,9 @@
Optional properties:
- qcom,vadc-poll-eoc: Use polling instead of interrupts for End of Conversion completion.
+- qcom,pmic-revid : Phandle pointing to the revision peripheral node. Use it to query the
+ PMIC type and revision for applying the appropriate temperature
+ compensation parameters.
Client required property:
- qcom,<consumer name>-vadc : The phandle to the corresponding vadc device.
diff --git a/Documentation/devicetree/bindings/memory.txt b/Documentation/devicetree/bindings/memory.txt
index e98ee05..32f6a24 100644
--- a/Documentation/devicetree/bindings/memory.txt
+++ b/Documentation/devicetree/bindings/memory.txt
@@ -36,6 +36,7 @@
reg = <(baseaddr) (size)>;
(linux,contiguous-region);
(linux,default-contiguous-region);
+ (linux,memory-limit);
label = (unique_name);
};
@@ -48,6 +49,11 @@
linux,default-contiguous-region: property indicating that the region
is the default region for all contiguous memory
allocations, Linux specific (optional)
+linux,memory-limit: property specifying an upper bound on the physical address
+ of the region if the region is placed dynamically. If no limit
+ is specificed, the region may be placed anywhere in the physical
+ address space. 0 may be used to specify lowmem (i.e. the region
+ will be placed in the direct mapped lowmem region)
label: an internal name used for automatically associating the
cma region with a given device. The label is optional;
if the label is not given the client is responsible for
diff --git a/Documentation/devicetree/bindings/mmc/sdhci-msm.txt b/Documentation/devicetree/bindings/mmc/sdhci-msm.txt
index d502f78..8c41926 100644
--- a/Documentation/devicetree/bindings/mmc/sdhci-msm.txt
+++ b/Documentation/devicetree/bindings/mmc/sdhci-msm.txt
@@ -55,6 +55,9 @@
no default value that the driver assumes if this property
is not specified. So if this property is not specified,
then SDHC driver will not vote for PM QOS.
+ - qcom,dat1-mpm-int: specifies MPM interrupt number (e.g. sdhc_2 node below)
+ corresponding to DAT1 line of SDHC (used only if slot has dedicated
+ DAT1 MSM pin (not GPIO))
In the following, <supply> can be vdd (flash core voltage) or vdd-io (I/O voltage).
- qcom,<supply>-always-on - specifies whether supply should be kept "on" always.
@@ -165,4 +168,5 @@
<81 512 106496 212992>, /* 208 MB/s */
<81 512 2147483647 4294967295>; /* Max. bandwidth */
qcom,bus-bw-vectors-bps = <0 13631488 27262976 54525952 109051904 218103808 4294967295>;
+ qcom,dat1-mpm-int = <44>;
};
diff --git a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
index e672a14..401a2ec 100644
--- a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
+++ b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
@@ -140,6 +140,7 @@
FM Rx and TX port ID values from 12292 to 12293
incall record Rx and TX port ID values from 32771 to 32772
inCall Music Delivery port ID is 32773
+ incall Music 2 Delivery port ID is 32770
* msm-auxpcm
@@ -376,6 +377,11 @@
compatible = "qcom,msm-dai-q6-dev";
qcom,msm-dai-q6-dev-id = <32773>;
};
+
+ qcom,msm-dai-q6-incall-music-2-rx {
+ compatible = "qcom,msm-dai-q6-dev";
+ qcom,msm-dai-q6-dev-id = <32770>;
+ };
};
qcom,msm-pri-auxpcm {
diff --git a/Documentation/devicetree/bindings/sound/voice-svc.txt b/Documentation/devicetree/bindings/sound/voice-svc.txt
new file mode 100644
index 0000000..deca7f5
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/voice-svc.txt
@@ -0,0 +1,11 @@
+* Voice Service binding
+
+Required properties:
+- compatible : "qcom,msm-voice-svc"
+
+Example:
+
+ qcom,msm-voice-svc {
+ compatible = "qcom,msm-voice-svc";
+ };
+
diff --git a/Documentation/devicetree/bindings/usb/ice40-hcd.txt b/Documentation/devicetree/bindings/usb/ice40-hcd.txt
new file mode 100644
index 0000000..43d24dc
--- /dev/null
+++ b/Documentation/devicetree/bindings/usb/ice40-hcd.txt
@@ -0,0 +1,45 @@
+ICE40 FPGA based SPI-USB bridge
+
+Documentation/devicetree/bindings/spi/spi-bus.txt provides the details
+of the required and optional properties of a SPI slave device node.
+
+The purpose of this document is to provide the additional properties
+that are required to use the ICE40 FPGA based SPI slave device as a
+USB host controller.
+
+Required properties:
+- compatible : should be "lattice,ice40-spi-usb"
+- <supply-name>-supply: handle to the regulator device tree node
+ Required "supply-name" is "core-vcc" and "spi-vcc"
+- reset-gpio: gpio used to assert the bridge chip reset
+- slave-select-gpio: gpio used to select the slave during configuration
+ loading
+- config-done-gpio: gpio used to indicate the configuration status
+- vcc-en-gpio: gpio used to enable the chip power supply
+
+Optional properties:
+- interrupts: IRQ lines used by this controller
+- clk-en-gpio: gpio used to enable the 19.2 MHZ clock to the bridge
+ chip. If it is not present, assume that the clock is available on
+ the bridge chip board.
+- <supply-name>-supply: handle to the regulator device tree node
+ Optional "supply-name" is "gpio" used to power up the gpio bank
+ used by this device
+
+ spi@f9923000 {
+ lattice,spi-usb@3 {
+ compatible = "lattice,ice40-spi-usb";
+ reg = <3>;
+ spi-max-frequency = <50000000>;
+ spi-cpol = <1>;
+ spi-cpha = <1>;
+ interrupt-parent = <&msmgpio>;
+ interrupts = <121 0x8>;
+ core-vcc-supply = <&pm8226_l2>;
+ spi-vcc-supply = <&pm8226_l5>;
+ lattice,reset-gpio = <&msmgpio 114 0>;
+ lattice,slave-select-gpio = <&msmgpio 118 0>;
+ lattice,config-done-gpio = <&msmgpio 115 0>;
+ lattice,vcc-en-gpio = <&msmgpio 117 0>;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/usb/msm-hsusb.txt b/Documentation/devicetree/bindings/usb/msm-hsusb.txt
index 7d3d435..fcafe11 100644
--- a/Documentation/devicetree/bindings/usb/msm-hsusb.txt
+++ b/Documentation/devicetree/bindings/usb/msm-hsusb.txt
@@ -159,6 +159,7 @@
and no peripheral connected over dock during low power mode, fourth value represents
minimum value to vote when USB is operational, fifth item represents maximum value
to vote for USB is operational.
+- qcom,usb2-enable-uicc: If present, usb2 port will be used for uicc card connection.
Example MSM HSUSB EHCI controller device node :
ehci: qcom,ehci-host@f9a55000 {
@@ -172,6 +173,7 @@
qcom,usb2-enable-hsphy2;
qcom,usb2-power-budget = <500>;
qcom,vdd-voltage-level = <1 2 3 5 7>;
+ qcom,usb2-enable-uicc;
};
ANDROID USB:
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index b94b587..440dac1 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -29,6 +29,7 @@
idt Integrated Device Technologies, Inc.
intercontrol Inter Control Group
invn InvenSense Inc.
+lattice Lattice Semiconductor.
linux Linux-specific binding
kionix Kionix Inc.
marvell Marvell Technology Group Ltd.
diff --git a/Documentation/usb/ice40-hcd.txt b/Documentation/usb/ice40-hcd.txt
new file mode 100644
index 0000000..54f845e
--- /dev/null
+++ b/Documentation/usb/ice40-hcd.txt
@@ -0,0 +1,247 @@
+Introduction
+============
+
+USB UICC connectivity is required for MSM8x12. This SoC has only 1 USB
+controller which is used for peripheral mode and charging. Hence an external
+USB host controller over SPI is used to connect a USB UICC card. ICE40 FPGA
+based SPI to IC-USB (Inter-Chip USB) bridge chip is used.
+
+The ICE40 Host controller driver (ice40-hcd) is registered as a SPI protocol
+driver and interacts with the SPI subsystem on one side and interacts with the
+USB core on the other side.
+
+Hardware description
+====================
+
+The ICE40 devices are SRAM-based FPGAs. The SRAM memory cells are volatile,
+meaning that once power is removed from the device, its configuration is lost
+and must be reloaded on the next power-up. An on-chip non-volatile configuration
+memory or an external SPI flash are not used to store the configuration data due
+to increased power consumption. Instead, the software loads the configuration
+data through SPI interface after powering up the bridge chip. Once the
+configuration data is programmed successfully, the bridge chip will be ready for
+the USB host controller operations.
+
+The ICE40 device has an interrupt signal apart from the standard SPI signals
+CSn, SCLK, MOSI and MISO. It has support for 25 to 50 MHz frequencies. The
+maximum operating frequency during configuration loading is 25 MHz.
+
+The bridge chip requires two power supplies, SPI_VCC (1.8v - 3.3v) and VCC_CORE
+(1.2v). The SPI_VCC manages the SPI slave portion and VCC_CORE manages the USB
+serial engine (SIE) portion. It requires a 19.2 MHz reference clock and a
+32 MHz clock is required for remote wakeup detection during suspend.
+
+The configuration loading sequence:
+
+- Assert the RSTn pin. This keeps bridge chip in reset state after downloading
+the configuration data.
+- The bridge chip samples the SPI interface chip select pin during power-up and
+enters SPI slave mode if it is low. Drive the chip select pin low before
+powering up the bridge chip.
+- Power-up the bridge chip by enabling SPI_VCC and VCC_CORE
+- De-assert the chip select pin after 50 usec.
+- Transfer the configuration data over SPI. Note that the bridge chip requires
+49 dummy clock cycles after sending the data.
+- The bridge chip indicates the status of the configuration loading via config
+done pin. It may take 50 usec to assert this pin.
+
+The 19.2 MHz clock should be supplied before de-asserting the RSTn pin. A PLL
+is used to generate a 48MHz clock signal that then creates a 12MHz clock signal
+by a divider. When the PLLOK bit is set in USB Transfer Result register, it
+indicates that the PLL output is locked to the input reference clock. When it
+is 0, it indicates that the PLL is out of lock. It is recommended to assert the
+RSTn pin to re-synchronize the PLL to the reference clock when the PLL loses
+lock. The chip will be ready for the USB host controller operations after it is
+brought out of reset and PLL is synchronized to the reference clock.
+
+The software is responsible for initiating all the USB host transfers by writing
+the associated registers. The SIE in the bridge chip performs the USB host
+operations via the IC-USB bus based on the registers set by the software. The
+USB transfer results as well as the bus status like the peripheral connection,
+disconnection, resume, etc. are notified to software through the interrupt and
+the internal registers.
+
+The bridge chip provides the DP & DM pull-down resistor control to the software.
+The pull-down resistors are enabled automatically after the power up to force
+the SE0 condition on the bus. The software is required to disable these
+resistors before driving the reset on the bus. Control, Bulk and Interrupt
+transfers are supported. The data toggling states are not maintained in the
+hardware and should be serviced by the software. The bridge chip returns
+one of the following values for a USB transaction (SETUP/IN/OUT) via Transfer
+result register.
+
+xSUCCESS: Successful transfer.
+xBUSY: The SIE is busy with a USB transfer.
+xPKTERR: Packet Error (stuff, EOP).
+xPIDERR: PID check bits are incorrect.
+xNAK: Device returned NAK. This is not an error condition for IN/OUT. But it
+is an error condition for SETUP.
+xSTALL: Device returned STALL.
+xWRONGPID: Wrong PID is received. For example a IN transaction is attempted on
+OUT endpoint.
+xCRCERR: CRC error.
+xTOGERR: Toggle bit error. The SIE returns ACK when the toggle mismatch happens
+for IN transaction and returns this error code. Software should discard the
+data as it was received already in the previous transaction.
+xBADLEN: Too big packet size received.
+xTIMEOUT: Device failed to respond in time.
+
+Software description
+====================
+
+This driver is compiled as a module and is loaded by the userspace after
+getting the UICC card insertion event from the modem processor. The module is
+unloaded upon the UICC card removal.
+
+This driver registers as a SPI protocol driver. The SPI controller driver
+manages the chip select pin. This pin needs to be driven low before powering
+up the bridge chip. Hence this pin settings are overridden temporarily during
+the bridge chip power-up sequence. The original settings are restored before
+sending the configuration data to the bridge chip which acts as a SPI slave.
+Both pinctl and gpiomux framework allow this type of use case.
+
+The configuration data file is stored on the eMMC card. Firmware class API
+request_firmware() is used to read the configuration data file. The
+configuration data is then sent to the bridge chip via SPI interface. The
+bridge chip asserts the config done pin once the configuration is completed.
+
+The driver registers as a Full Speed (USB 1.1) HCD. The following methods
+are implemented that are part of hc_drive struct:
+
+reset: It is called one time by the core during HCD registration. The
+default address 0 is programmed and the line state is sampled to check if any
+device is connected. If any device is connected, the port flags are updated
+accordingly. As the module is loaded after the UICC card is inserted, the
+device would be present at this time.
+
+start: This method is called one time by the core during HCD registration.
+The bridge chip is programmed to transmit the SOFs.
+
+stop: The method is called one time by the core during HCD deregistration.
+The bridge chip is programmed to stop transmitting the SOFs.
+
+hub_control: This method is called by the core to manage the Root HUB. The
+hardware does not maintain port state. The software maintain the port
+state and provide the information to the core when required. The following
+HUB class requests are supported.
+
+- GetHubDescriptor: The HUB descriptor is sent to the core. Only 1 port
+is present. Over current protection and port power control are not supported.
+- SetPortFeature: The device reset and suspend are supported. The The DP & DM
+pull-down resistors are disabled before driving the reset as per the IC-USB
+spec. The reset signaling is stopped when the core queries the port status.
+- GetPortStatus: The device connection status is sent to the core. If a reset
+is in progress, it is stopped before returning the port status.
+- ClearPortFeature: The device resume (clear suspend) is supported.
+
+urb_enqueue: This method is called by the core to initiate a USB Control/Bulk
+transfer. If the endpoint private context is not present, it will be created to
+hold the endpoint number, host endpoint structure, transaction error count, halt
+state and unlink state. The URB is attached to the endpoint URB list. If the
+endpoint is not active, it is attached to the asynchronous schedule list and the
+work is scheduled to traverse this list. The traversal algorithm is explained
+later in this document.
+
+urb_dequeue: This method is called by the core when an URB is unlinked. If the
+endpoint is not active, the URB is unlinked immediately. Otherwise the endpoint
+is marked for unlink and URB is unlinked from the asynchronous schedule work.
+
+bus_suspend: This method is called by the core during root hub suspend. The SOFs
+are already stopped during the port suspend which happens before root hub
+suspend. Assert the RSTn pin to put the bridge chip in reset state and stop XO
+(19.2 MHz) clock.
+
+bus_resume: This method is called by the core during root hub resume. Turn on
+the XO clock and de-assert the RSTn signal to bring the chip out of reset.
+
+endpoint_disable: This method is called by the core during the device
+disconnect. All the URB are unlinked by this time, so free the endpoint private
+structure.
+
+Asynchronous scheduling:
+
+All the active endpoints are queued to the asynchronous schedule list. A worker
+thread iterates over this circular list and process the URBs. Processing an URB
+involves initiating multiple SETUP/IN/OUT transactions and checking the result.
+After receiving the DATA/ACK, the toggle bit is inverted.
+
+A URB is finished when any of the following events occur:
+
+- The entire data is received for an OUT endpoint or a short packet is received
+for an IN endpoint.
+- The endpoint is stalled by the device. -EPIPE is returned.
+- Transaction error is occurred consecutively 3 times. -EPROTO is returned.
+- A NAK received for a SETUP transaction.
+- The URB is unlinked.
+
+The next transaction is issued on the next endpoint (if available) irrespective
+of the result of the current transaction. But the IN/OUT transaction of data
+or status phase is attempted immediately after the SETUP transaction for a
+control endpoint. If a NAK is received for this transaction, the control
+transfer is resumed next time when the control endpoint is encountered in the
+asynchronous schedule list. This is to give the control transfers priority
+over the bulk transfers.
+
+The endpoint is marked as halted when a URB is finished due to transaction
+errors or stall condition. The halted endpoint is removed from the asynchronous
+schedule list. It will be added again next time when a URB is enqueued on this
+endpoint.
+
+This driver provides debugfs interface and exports a file called "command" under
+<debugfs root>/ice40 directory. The following strings can be echoed to this
+file.
+
+"poll": If the device is connected after the module is loaded, it will not be
+detected automatically. The bus is sampled when this string is echoed. If a
+device is connected, port flags are updated and core is notified about the
+device connect event.
+
+"rwtest": Function Address register is written and read back to validate the
+contents. This should NOT be used while the usb device is connected. This is
+strictly for debugging purpose.
+
+"dump": Dumps all the register values to the kernel log buffer.
+
+Design Goals:
+=============
+
+- Handle errors gracefully. Implement retry mechanism for transaction errors,
+memory failures. Mark HCD as dead for serious errors like SPI transaction
+errors to avoid further interactions with the attached USB device.
+- Keep the asynchronous schedule algorithm simple and efficient. Take advantage
+of the static configuration of the USB device. UICC cards has only CCID and Mass
+storage interfaces. These interface protocol allows only 1 active transfer on
+either in or out endpoint.
+- Add trace points to capture USB transactions.
+
+Driver parameters
+=================
+
+The driver is compiled as a module and it accepts the configuration data file
+name as a module param called "firmware". The default configuration file name
+is "ice40.bin".
+
+Config options
+==============
+
+Set CONFIG_USB_SL811_HCD to m to compile this driver as a module. The driver
+should not be compiled statically, because the configuration data is not
+available during kernel boot.
+
+To do
+=====
+
+- The bridge chip has 2 IN FIFO and 2 OUT FIFO. Implement double buffering.
+- The bridge chip has an interrupt to indicate the transaction (IN/OUT)
+completion. The current implementation uses polling for simplicity and to avoid
+interrupt latencies. Evaluate interrupt approach.
+- The bridge chip can be completely power collapsed during suspend to avoid
+leakage currents. As the bridge chip does not have any non-volatile memory,
+the configuration data needs to be loaded during resume. This method has higher
+power savings with higher resume latencies. Evaluate this approach.
+- Implement Interrupt transfers if required.
+- The request_firmware() API copies the configuration data file to the kernel
+virtual memory. This memory can't be used for DMA. The current implementation
+copies this data into contiguous physical memory which is allocated via
+kmalloc. If this memory allocation fails, try to allocate multiple pages
+and submit the SPI message with multiple transfers.
diff --git a/Documentation/vm/ksm.txt b/Documentation/vm/ksm.txt
index b392e49..8e54981 100644
--- a/Documentation/vm/ksm.txt
+++ b/Documentation/vm/ksm.txt
@@ -72,6 +72,13 @@
pages_unshared - how many pages unique but repeatedly checked for merging
pages_volatile - how many pages changing too fast to be placed in a tree
full_scans - how many times all mergeable areas have been scanned
+deferred_timer - whether to use deferred timers or not
+ e.g. "echo 1 > /sys/kernel/mm/ksm/deferred_timer"
+ Default: 0 (means, we are not using deferred timers. Users
+ might want to set deferred_timer option if they donot want
+ ksm thread to wakeup CPU to carryout ksm activities thus
+ gaining on battery while compromising slightly on memory
+ that could have been saved.)
A high ratio of pages_sharing to pages_shared indicates good sharing, but
a high ratio of pages_unshared to pages_sharing indicates wasted effort.
diff --git a/arch/arm/boot/dts/apq8084-camera-sensor-cdp.dtsi b/arch/arm/boot/dts/apq8084-camera-sensor-cdp.dtsi
index 5577d16..173ea63 100644
--- a/arch/arm/boot/dts/apq8084-camera-sensor-cdp.dtsi
+++ b/arch/arm/boot/dts/apq8084-camera-sensor-cdp.dtsi
@@ -104,6 +104,7 @@
qcom,csiphy-sd-index = <0>;
qcom,csid-sd-index = <0>;
qcom,actuator-src = <&actuator0>;
+ qcom,mount-angle = <90>;
cam_vdig-supply = <&pma8084_l27>;
cam_vio-supply = <&pma8084_lvs4>;
cam_vana-supply = <&pma8084_l17>;
@@ -125,6 +126,8 @@
qcom,gpio-req-tbl-label = "CAMIF_MCLK",
"CAM_RESET1",
"CAM_STANDBY";
+ qcom,sensor-position = <0>;
+ qcom,sensor-mode = <1>;
qcom,cci-master = <0>;
status = "ok";
};
@@ -153,6 +156,8 @@
qcom,gpio-req-tbl-flags = <1 0>;
qcom,gpio-req-tbl-label = "CAMIF_MCLK2",
"CAM_XSHUTDOWN";
+ qcom,sensor-position = <1>;
+ qcom,sensor-mode = <1>;
qcom,cci-master = <0>;
status = "ok";
};
diff --git a/arch/arm/boot/dts/apq8084-camera-sensor-mtp.dtsi b/arch/arm/boot/dts/apq8084-camera-sensor-mtp.dtsi
index 02d8b59..bfbbf54 100644
--- a/arch/arm/boot/dts/apq8084-camera-sensor-mtp.dtsi
+++ b/arch/arm/boot/dts/apq8084-camera-sensor-mtp.dtsi
@@ -126,6 +126,8 @@
qcom,gpio-req-tbl-label = "CAMIF_MCLK",
"CAM_RESET1",
"CAM_STANDBY";
+ qcom,sensor-position = <0>;
+ qcom,sensor-mode = <1>;
qcom,cci-master = <0>;
status = "ok";
};
@@ -153,6 +155,8 @@
qcom,gpio-req-tbl-flags = <1 0>;
qcom,gpio-req-tbl-label = "CAMIF_MCLK2",
"CAM_XSHUTDOWN";
+ qcom,sensor-position = <1>;
+ qcom,sensor-mode = <1>;
qcom,cci-master = <0>;
status = "ok";
};
diff --git a/arch/arm/boot/dts/apq8084-mdss.dtsi b/arch/arm/boot/dts/apq8084-mdss.dtsi
index 329ab32..665eedc 100644
--- a/arch/arm/boot/dts/apq8084-mdss.dtsi
+++ b/arch/arm/boot/dts/apq8084-mdss.dtsi
@@ -54,6 +54,17 @@
qcom,mdss-pipe-rgb-xin-id = <1 5 9 13>;
qcom,mdss-pipe-dma-xin-id = <2 10>;
+ qcom,mdss-pipe-vig-clk-ctrl-offsets = <0x3AC 0 0>,
+ <0x3B4 0 0>,
+ <0x3BC 0 0>,
+ <0x3C4 0 0>;
+ qcom,mdss-pipe-rgb-clk-ctrl-offsets = <0x3AC 4 8>,
+ <0x3B4 4 8>,
+ <0x3BC 4 8>,
+ <0x3C4 4 8>;
+ qcom,mdss-pipe-dma-clk-ctrl-offsets = <0x3AC 8 12>,
+ <0x3B4 8 12>;
+
qcom,mdss-smp-data = <44 8192>;
qcom,mdss-ctl-off = <0x00000600 0x00000700 0x00000800
diff --git a/arch/arm/boot/dts/dsi-panel-nt35590-720p-video.dtsi b/arch/arm/boot/dts/dsi-panel-nt35590-720p-video.dtsi
index 8d28996..9a18a31 100644
--- a/arch/arm/boot/dts/dsi-panel-nt35590-720p-video.dtsi
+++ b/arch/arm/boot/dts/dsi-panel-nt35590-720p-video.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014, The Linux Foundation. All rights reserved.
*
* This 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 @@
qcom,mdss-dsi-h-back-porch = <164>;
qcom,mdss-dsi-h-pulse-width = <8>;
qcom,mdss-dsi-h-sync-skew = <0>;
- qcom,mdss-dsi-v-back-porch = <1>;
+ qcom,mdss-dsi-v-back-porch = <11>;
qcom,mdss-dsi-v-front-porch = <6>;
qcom,mdss-dsi-v-pulse-width = <1>;
qcom,mdss-dsi-h-left-border = <0>;
@@ -504,7 +504,9 @@
29 01 00 00 00 00 02 6A 60
29 01 00 00 00 00 02 FF 00
29 01 00 00 78 00 02 29 00
- 29 01 00 00 78 00 02 53 2C];
+ 29 01 00 00 78 00 02 53 2C
+ 29 01 00 00 00 00 02 FF 00
+ 29 01 00 00 00 00 06 3B 03 06 03 02 02];
qcom,mdss-dsi-off-command = [05 01 00 00 32 00 02 28 00
05 01 00 00 78 00 02 10 00];
qcom,mdss-dsi-on-command-state = "dsi_lp_mode";
diff --git a/arch/arm/boot/dts/msm-pm8110.dtsi b/arch/arm/boot/dts/msm-pm8110.dtsi
index 9adbf81..7a60861 100644
--- a/arch/arm/boot/dts/msm-pm8110.dtsi
+++ b/arch/arm/boot/dts/msm-pm8110.dtsi
@@ -22,7 +22,7 @@
#address-cells = <1>;
#size-cells = <1>;
- qcom,revid@100 {
+ pm8110_revid: qcom,revid@100 {
compatible = "qcom,qpnp-revid";
reg = <0x100 0x100>;
};
@@ -219,6 +219,7 @@
qcom,adc-bit-resolution = <15>;
qcom,adc-vdd-reference = <1800>;
qcom,vadc-poll-eoc;
+ qcom,pmic-revid = <&pm8110_revid>;
chan@8 {
label = "die_temp";
@@ -268,6 +269,7 @@
qcom,iadc-vadc = <&pm8110_vadc>;
qcom,iadc-poll-eoc;
qcom,use-default-rds-trim = <1>;
+ qcom,pmic-revid = <&pm8110_revid>;
chan@0 {
label = "internal_rsense";
diff --git a/arch/arm/boot/dts/msm-pm8226.dtsi b/arch/arm/boot/dts/msm-pm8226.dtsi
index 41897da..08d3d05 100644
--- a/arch/arm/boot/dts/msm-pm8226.dtsi
+++ b/arch/arm/boot/dts/msm-pm8226.dtsi
@@ -364,6 +364,7 @@
qcom,adc-bit-resolution = <15>;
qcom,adc-vdd-reference = <1800>;
qcom,vadc-poll-eoc;
+ qcom,pmic-revid = <&pm8226_revid>;
chan@8 {
label = "die_temp";
@@ -424,6 +425,7 @@
qcom,iadc-vadc = <&pm8226_vadc>;
qcom,iadc-poll-eoc;
qcom,use-default-rds-trim = <0>;
+ qcom,pmic-revid = <&pm8226_revid>;
chan@0 {
label = "internal_rsense";
diff --git a/arch/arm/boot/dts/msm-pm8941.dtsi b/arch/arm/boot/dts/msm-pm8941.dtsi
index 94a4e83..a0e02f7 100644
--- a/arch/arm/boot/dts/msm-pm8941.dtsi
+++ b/arch/arm/boot/dts/msm-pm8941.dtsi
@@ -27,7 +27,7 @@
reg = <0x900 0x100>;
};
- qcom,revid@100 {
+ pm8941_revid: qcom,revid@100 {
compatible = "qcom,qpnp-revid";
reg = <0x100 0x100>;
};
@@ -577,6 +577,7 @@
qcom,adc-bit-resolution = <15>;
qcom,adc-vdd-reference = <1800>;
qcom,vadc-poll-eoc;
+ qcom,pmic-revid = <&pm8941_revid>;
chan@0 {
label = "usb_in";
@@ -824,6 +825,7 @@
qcom,iadc-vadc = <&pm8941_vadc>;
qcom,iadc-poll-eoc;
qcom,use-default-rds-trim = <0>;
+ qcom,pmic-revid = <&pm8941_revid>;
chan@0 {
label = "internal_rsense";
diff --git a/arch/arm/boot/dts/msm8226-720p-cdp.dtsi b/arch/arm/boot/dts/msm8226-720p-cdp.dtsi
index 00e90f3..66f9b18 100644
--- a/arch/arm/boot/dts/msm8226-720p-cdp.dtsi
+++ b/arch/arm/boot/dts/msm8226-720p-cdp.dtsi
@@ -35,6 +35,20 @@
};
};
+ i2c@f9925000 { /* BLSP1 QUP3 */
+ nfc-nci@0e {
+ compatible = "qcom,nfc-nci";
+ reg = <0x0e>;
+ qcom,irq-gpio = <&msmgpio 21 0x00>;
+ qcom,dis-gpio = <&msmgpio 20 0x00>;
+ qcom,clk-src = "BBCLK2";
+ interrupt-parent = <&msmgpio>;
+ interrupts = <21 0>;
+ qcom,clk-gpio = <&pm8226_gpios 3 0>;
+ };
+ };
+
+
gpio_keys {
compatible = "gpio-keys";
input-name = "gpio-keys";
@@ -372,6 +386,11 @@
};
gpio@c200 { /* GPIO 3 */
+ qcom,mode = <0>; /* QPNP_PIN_MODE_DIG_IN */
+ qcom,pull = <5>; /* QPNP_PIN_PULL_NO */
+ qcom,vin-sel = <2>; /* QPNP_PIN_VIN2 */
+ qcom,src-sel = <2>; /* QPNP_PIN_SEL_FUNC_1 */
+ qcom,master-en = <1>;
};
gpio@c300 { /* GPIO 4 */
diff --git a/arch/arm/boot/dts/msm8226-bus.dtsi b/arch/arm/boot/dts/msm8226-bus.dtsi
index 74b4a30..a2f91cf 100644
--- a/arch/arm/boot/dts/msm8226-bus.dtsi
+++ b/arch/arm/boot/dts/msm8226-bus.dtsi
@@ -998,7 +998,7 @@
qcom,fabclk-dual = "mem_clk";
qcom,fabclk-active = "mem_a_clk";
qcom,ntieredslaves = <0>;
- qcom,qos-freq = <4800>;
+ qcom,qos-freq = <19200>;
qcom,hw-sel = "BIMC";
qcom,rpm-en;
@@ -1008,12 +1008,18 @@
qcom,masterp = <0>;
qcom,tier = <2>;
qcom,hw-sel = "BIMC";
- qcom,mode = "Fixed";
+ qcom,mode = "Limiter";
qcom,qport = <0>;
qcom,ws = <10000>;
qcom,mas-hw-id = <0>;
qcom,prio-rd = <0>;
qcom,prio-wr = <0>;
+ qcom,mode-thresh = "Fixed";
+ qcom,thresh = <1800000>;
+ qcom,dual-conf;
+ qcom,bimc,bw = <450000>;
+ qcom,bimc,gp = <5000>;
+ qcom,bimc,thmp = <50>;
};
mas-mss-proc {
diff --git a/arch/arm/boot/dts/msm8226-camera-sensor-cdp.dtsi b/arch/arm/boot/dts/msm8226-camera-sensor-cdp.dtsi
index 4170255..05a3f07 100644
--- a/arch/arm/boot/dts/msm8226-camera-sensor-cdp.dtsi
+++ b/arch/arm/boot/dts/msm8226-camera-sensor-cdp.dtsi
@@ -198,6 +198,7 @@
reg = <0x0>;
qcom,csiphy-sd-index = <0>;
qcom,csid-sd-index = <0>;
+ qcom,mount-angle = <90>;
qcom,actuator-src = <&actuator0>;
qcom,led-flash-src = <&led_flash0>;
cam_vdig-supply = <&pm8226_l5>;
@@ -220,6 +221,8 @@
qcom,gpio-req-tbl-label = "CAMIF_MCLK",
"CAM_RESET1",
"CAM_STANDBY";
+ qcom,sensor-position = <0>;
+ qcom,sensor-mode = <0>;
qcom,cci-master = <0>;
status = "ok";
};
@@ -230,6 +233,7 @@
reg = <0x1>;
qcom,csiphy-sd-index = <1>;
qcom,csid-sd-index = <1>;
+ qcom,mount-angle = <0>;
cam_vdig-supply = <&pm8226_l5>;
cam_vana-supply = <&pm8226_l19>;
cam_vio-supply = <&pm8226_lvs1>;
@@ -248,6 +252,8 @@
qcom,gpio-req-tbl-label = "CAMIF_MCLK",
"CAM_RESET",
"CAM_STANDBY";
+ qcom,sensor-position = <1>;
+ qcom,sensor-mode = <1>;
qcom,cci-master = <0>;
status = "ok";
};
diff --git a/arch/arm/boot/dts/msm8226-camera-sensor-mtp.dtsi b/arch/arm/boot/dts/msm8226-camera-sensor-mtp.dtsi
index 97e7dff..a6af2c2 100644
--- a/arch/arm/boot/dts/msm8226-camera-sensor-mtp.dtsi
+++ b/arch/arm/boot/dts/msm8226-camera-sensor-mtp.dtsi
@@ -112,7 +112,7 @@
reg = <0x0>;
qcom,csiphy-sd-index = <0>;
qcom,csid-sd-index = <0>;
- qcom,mount-angle = <0>;
+ qcom,mount-angle = <270>;
qcom,actuator-src = <&actuator0>;
qcom,led-flash-src = <&led_flash0>;
cam_vdig-supply = <&pm8226_l5>;
@@ -136,6 +136,8 @@
qcom,gpio-req-tbl-label = "CAMIF_MCLK",
"CAM_RESET1",
"CAM_STANDBY";
+ qcom,sensor-position = <0>;
+ qcom,sensor-mode = <0>;
qcom,cci-master = <0>;
status = "ok";
};
@@ -166,6 +168,8 @@
qcom,gpio-req-tbl-label = "CAMIF_MCLK",
"CAM_RESET",
"CAM_STANDBY";
+ qcom,sensor-position = <1>;
+ qcom,sensor-mode = <0>;
qcom,cci-master = <0>;
status = "ok";
};
diff --git a/arch/arm/boot/dts/msm8226-camera-sensor-qrd.dtsi b/arch/arm/boot/dts/msm8226-camera-sensor-qrd.dtsi
index a553918..dab92ff 100644
--- a/arch/arm/boot/dts/msm8226-camera-sensor-qrd.dtsi
+++ b/arch/arm/boot/dts/msm8226-camera-sensor-qrd.dtsi
@@ -448,7 +448,7 @@
reg = <0x0>;
qcom,csiphy-sd-index = <0>;
qcom,csid-sd-index = <0>;
- qcom,mount-angle = <270>;
+ qcom,mount-angle = <90>;
qcom,actuator-src = <&actuator0>;
qcom,eeprom-src = <&eeprom0>;
qcom,led-flash-src = <&led_flash0>;
@@ -479,6 +479,8 @@
"CAM_STANDBY",
"CAM_VDIG",
"CAM_AF_PWDM";
+ qcom,sensor-position = <0>;
+ qcom,sensor-mode = <0>;
qcom,cci-master = <0>;
status = "ok";
};
@@ -516,6 +518,8 @@
qcom,gpio-set-tbl-num = <1 1>;
qcom,gpio-set-tbl-flags = <0 2>;
qcom,gpio-set-tbl-delay = <1000 4000>;
+ qcom,sensor-position = <1>;
+ qcom,sensor-mode = <0>;
qcom,cci-master = <0>;
status = "ok";
};
diff --git a/arch/arm/boot/dts/msm8226-mdss.dtsi b/arch/arm/boot/dts/msm8226-mdss.dtsi
index 2176d39..5edc43b 100644
--- a/arch/arm/boot/dts/msm8226-mdss.dtsi
+++ b/arch/arm/boot/dts/msm8226-mdss.dtsi
@@ -19,7 +19,7 @@
interrupts = <0 72 0>;
vdd-supply = <&gdsc_mdss>;
- qcom,max-bandwidth-low-kbps = <1660000>;
+ qcom,max-bandwidth-low-kbps = <1100000>;
qcom,max-bandwidth-high-kbps = <1660000>;
/* Bus Scale Settings */
@@ -32,8 +32,8 @@
<22 512 0 6400000>;
/* Fudge factors */
- qcom,mdss-ab-factor = <2 1>; /* 2 times */
- qcom,mdss-ib-factor = <6 5>; /* 1.2 times */
+ qcom,mdss-ab-factor = <1 1>; /* 1 times */
+ qcom,mdss-ib-factor = <2 1>; /* 2 times */
qcom,mdss-clk-factor = <5 4>; /* 1.25 times */
qcom,max-clk-rate = <200000000>;
@@ -48,6 +48,10 @@
qcom,mdss-pipe-rgb-xin-id = <1>;
qcom,mdss-pipe-dma-xin-id = <2>;
+ qcom,mdss-pipe-vig-clk-ctrl-offsets = <0x3AC 0 0>;
+ qcom,mdss-pipe-rgb-clk-ctrl-offsets = <0x3AC 4 8>;
+ qcom,mdss-pipe-dma-clk-ctrl-offsets = <0x3AC 8 12>;
+
qcom,mdss-smp-data = <7 4096>;
qcom,mdss-ctl-off = <0x00000600 0x00000700>;
@@ -95,9 +99,10 @@
compatible = "qcom,mdss-dsi-ctrl";
label = "MDSS DSI CTRL->0";
cell-index = <0>;
- reg = <0xfd922800 0x600>,
+ reg = <0xfd922800 0x1f8>,
+ <0xfd922b00 0x2b0>,
<0xfd828000 0x108>;
- reg-names = "dsi_phys", "mmss_misc_phys";
+ reg-names = "dsi_ctrl", "dsi_phy", "mmss_misc_phys";
qcom,mdss-fb-map = <&mdss_fb0>;
qcom,mdss-mdp = <&mdss_mdp>;
vdd-supply = <&pm8226_l15>;
diff --git a/arch/arm/boot/dts/msm8226-regulator.dtsi b/arch/arm/boot/dts/msm8226-regulator.dtsi
index 5e890d3..78e1a63 100644
--- a/arch/arm/boot/dts/msm8226-regulator.dtsi
+++ b/arch/arm/boot/dts/msm8226-regulator.dtsi
@@ -78,7 +78,7 @@
vdd-apc-supply = <&pm8226_s2>;
vdd-mx-supply = <&pm8226_l3_ao>;
- qcom,vdd-mx-vmax = <1350000>;
+ qcom,vdd-mx-vmax = <1337500>;
qcom,vdd-mx-vmin-method = <1>;
qcom,cpr-ref-clk = <19200>;
@@ -109,7 +109,7 @@
qcom,cpr-fuse-uplift-sel = <22 53 1 0 0>;
qcom,cpr-uplift-voltage = <50000>;
qcom,cpr-uplift-quotient = <0 0 120>;
- qcom,cpr-uplift-max-volt = <1350000>;
+ qcom,cpr-uplift-max-volt = <1330000>;
qcom,cpr-uplift-speed-bin = <1>;
qcom,speed-bin-fuse-sel = <22 0 3 0>;
};
diff --git a/arch/arm/boot/dts/msm8226-v1-pm.dtsi b/arch/arm/boot/dts/msm8226-v1-pm.dtsi
index d59fab3..10aff70 100644
--- a/arch/arm/boot/dts/msm8226-v1-pm.dtsi
+++ b/arch/arm/boot/dts/msm8226-v1-pm.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013 The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2014 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -105,7 +105,7 @@
qcom,L2-spm-is-apcs-master;
};
- qcom,lpm-levels {
+ lpm_levels: qcom,lpm-levels {
compatible = "qcom,lpm-levels";
qcom,default-l2-state = "l2_cache_active";
#address-cells = <1>;
@@ -300,6 +300,7 @@
qcom,pc-resets-timer;
qcom,cpus-as-clocks;
qcom,synced-clocks;
+ qcom,lpm-levels = <&lpm_levels>;
};
qcom,cpu-sleep-status@f9088008{
diff --git a/arch/arm/boot/dts/msm8226-v2-pm.dtsi b/arch/arm/boot/dts/msm8226-v2-pm.dtsi
index bc8fe5d..7af2c7f 100644
--- a/arch/arm/boot/dts/msm8226-v2-pm.dtsi
+++ b/arch/arm/boot/dts/msm8226-v2-pm.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013 The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2014 The Linux Foundation. All rights reserved.
*
* This 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,7 +107,7 @@
qcom,L2-spm-is-apcs-master;
};
- qcom,lpm-levels {
+ lpm_levels: qcom,lpm-levels {
compatible = "qcom,lpm-levels";
qcom,default-l2-state = "l2_cache_active";
#address-cells = <1>;
@@ -312,6 +312,7 @@
qcom,pc-resets-timer;
qcom,cpus-as-clocks;
qcom,synced-clocks;
+ qcom,lpm-levels = <&lpm_levels>;
};
qcom,cpu-sleep-status@f9088008{
diff --git a/arch/arm/boot/dts/msm8226.dtsi b/arch/arm/boot/dts/msm8226.dtsi
index cf8e3b3..5576b2b 100644
--- a/arch/arm/boot/dts/msm8226.dtsi
+++ b/arch/arm/boot/dts/msm8226.dtsi
@@ -499,6 +499,10 @@
compatible = "qti,msm-pcm-loopback";
};
+ qcom,msm-voice-svc {
+ compatible = "qcom,msm-voice-svc";
+ };
+
qcom,msm-dai-q6 {
compatible = "qcom,msm-dai-q6";
qcom,msm-dai-q6-sb-0-rx {
@@ -771,6 +775,7 @@
qcom,bus-width = <8>;
+ qcom,cpu-dma-latency-us = <701>;
qcom,msm-bus,name = "sdcc1";
qcom,msm-bus,num-cases = <8>;
qcom,msm-bus,num-paths = <1>;
@@ -798,6 +803,7 @@
qcom,bus-width = <8>;
+ qcom,cpu-dma-latency-us = <701>;
qcom,msm-bus,name = "sdhc1";
qcom,msm-bus,num-cases = <8>;
qcom,msm-bus,num-paths = <1>;
@@ -828,6 +834,7 @@
qcom,bus-width = <4>;
+ qcom,cpu-dma-latency-us = <701>;
qcom,msm-bus,name = "sdcc2";
qcom,msm-bus,num-cases = <8>;
qcom,msm-bus,num-paths = <1>;
@@ -855,6 +862,7 @@
qcom,bus-width = <4>;
+ qcom,cpu-dma-latency-us = <701>;
qcom,msm-bus,name = "sdhc2";
qcom,msm-bus,num-cases = <8>;
qcom,msm-bus,num-paths = <1>;
@@ -881,6 +889,7 @@
reg-names = "core_mem", "dml_mem", "bam_mem";
qcom,bus-width = <4>;
+ qcom,cpu-dma-latency-us = <701>;
gpios = <&msmgpio 44 0>, /* CLK */
<&msmgpio 43 0>, /* CMD */
<&msmgpio 42 0>, /* DATA0 */
@@ -924,6 +933,7 @@
reg-names = "hc_mem", "core_mem";
qcom,bus-width = <4>;
+ qcom,cpu-dma-latency-us = <701>;
gpios = <&msmgpio 44 0>, /* CLK */
<&msmgpio 43 0>, /* CMD */
<&msmgpio 42 0>, /* DATA0 */
diff --git a/arch/arm/boot/dts/msm8610-camera-sensor-cdp-mtp.dtsi b/arch/arm/boot/dts/msm8610-camera-sensor-cdp-mtp.dtsi
index 7f4197f..07b5da3 100644
--- a/arch/arm/boot/dts/msm8610-camera-sensor-cdp-mtp.dtsi
+++ b/arch/arm/boot/dts/msm8610-camera-sensor-cdp-mtp.dtsi
@@ -291,6 +291,9 @@
qcom,gpio-req-tbl-label = "CAMIF_MCLK",
"CAM_RESET1",
"CAM_STANDBY";
+ qcom,sensor-position = <0>;
+ qcom,sensor-mode = <0>;
+ qcom,cci-master = <0>;
status = "ok";
};
@@ -320,6 +323,9 @@
qcom,gpio-req-tbl-label = "CAMIF_MCLK",
"CAM_RESET",
"CAM_STANDBY";
+ qcom,sensor-position = <1>;
+ qcom,sensor-mode = <0>;
+ qcom,cci-master = <0>;
status = "ok";
};
diff --git a/arch/arm/boot/dts/msm8610-v1-pm.dtsi b/arch/arm/boot/dts/msm8610-v1-pm.dtsi
index dc1dc8b..adc66d7 100644
--- a/arch/arm/boot/dts/msm8610-v1-pm.dtsi
+++ b/arch/arm/boot/dts/msm8610-v1-pm.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013 The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2014 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -105,7 +105,7 @@
qcom,L2-spm-is-apcs-master;
};
- qcom,lpm-levels {
+ lpm_levels: qcom,lpm-levels {
compatible = "qcom,lpm-levels";
qcom,default-l2-state = "l2_cache_active";
#address-cells = <1>;
@@ -296,6 +296,7 @@
qcom,pc-resets-timer;
qcom,cpus-as-clocks;
qcom,synced-clocks;
+ qcom,lpm-levels = <&lpm_levels>;
};
qcom,cpu-sleep-status@f9088008{
diff --git a/arch/arm/boot/dts/msm8610-v2-pm.dtsi b/arch/arm/boot/dts/msm8610-v2-pm.dtsi
index 2859744..b69b061 100644
--- a/arch/arm/boot/dts/msm8610-v2-pm.dtsi
+++ b/arch/arm/boot/dts/msm8610-v2-pm.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013 The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2014 The Linux Foundation. All rights reserved.
*
* This 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,7 +107,7 @@
qcom,L2-spm-is-apcs-master;
};
- qcom,lpm-levels {
+ lpm_levels: qcom,lpm-levels {
compatible = "qcom,lpm-levels";
qcom,default-l2-state = "l2_cache_active";
#address-cells = <1>;
@@ -308,6 +308,7 @@
qcom,pc-resets-timer;
qcom,cpus-as-clocks;
qcom,synced-clocks;
+ qcom,lpm-levels = <&lpm_levels>;
};
qcom,cpu-sleep-status@f9088008{
diff --git a/arch/arm/boot/dts/msm8610.dtsi b/arch/arm/boot/dts/msm8610.dtsi
index 90e8fd6..53d2d4f 100644
--- a/arch/arm/boot/dts/msm8610.dtsi
+++ b/arch/arm/boot/dts/msm8610.dtsi
@@ -305,6 +305,7 @@
qcom,bus-width = <8>;
qcom,nonremovable;
qcom,bus-speed-mode = "HS200_1p8v", "DDR_1p8v";
+ qcom,cpu-dma-latency-us = <701>;
status = "disabled";
};
@@ -338,6 +339,7 @@
qcom,xpc;
qcom,bus-speed-mode = "SDR12", "SDR25", "SDR50", "DDR50", "SDR104";
qcom,current-limit = <800>;
+ qcom,cpu-dma-latency-us = <701>;
status = "disabled";
};
@@ -351,6 +353,7 @@
interrupt-names = "hc_irq", "pwr_irq";
qcom,bus-width = <8>;
+ qcom,cpu-dma-latency-us = <701>;
status = "disabled";
};
@@ -363,6 +366,7 @@
interrupt-names = "hc_irq", "pwr_irq";
qcom,bus-width = <4>;
+ qcom,cpu-dma-latency-us = <701>;
status = "disabled";
};
@@ -606,6 +610,7 @@
qcom,gpio-miso = <&msmgpio 87 0>;
qcom,gpio-clk = <&msmgpio 89 0>;
qcom,gpio-cs0 = <&msmgpio 88 0>;
+ qcom,gpio-cs2 = <&msmgpio 85 0>;
qcom,infinite-mode = <0>;
qcom,use-bam;
@@ -613,6 +618,21 @@
qcom,bam-consumer-pipe-index = <18>;
qcom,bam-producer-pipe-index = <19>;
qcom,master-id = <86>;
+
+ lattice,spi-usb@2 {
+ compatible = "lattice,ice40-spi-usb";
+ reg = <2>;
+ spi-max-frequency = <50000000>;
+ spi-cpol = <1>;
+ spi-cpha = <1>;
+ core-vcc-supply = <&pm8110_l2>;
+ spi-vcc-supply = <&pm8110_l6>;
+ gpio-supply = <&pm8110_l22>;
+ lattice,reset-gpio = <&msmgpio 95 0>;
+ lattice,slave-select-gpio = <&msmgpio 85 0>;
+ lattice,config-done-gpio = <&msmgpio 94 0>;
+ lattice,vcc-en-gpio = <&msmgpio 96 0>;
+ };
};
qcom,pronto@fb21b000 {
diff --git a/arch/arm/boot/dts/msm8612-qrd-camera-sensor.dtsi b/arch/arm/boot/dts/msm8612-qrd-camera-sensor.dtsi
index 4d50b36..551e007 100644
--- a/arch/arm/boot/dts/msm8612-qrd-camera-sensor.dtsi
+++ b/arch/arm/boot/dts/msm8612-qrd-camera-sensor.dtsi
@@ -121,6 +121,8 @@
qcom,gpio-req-tbl-label = "CAMIF_MCLK",
"CAM_RESET1",
"CAM_STANDBY";
+ qcom,sensor-position = <0>;
+ qcom,sensor-mode = <0>;
qcom,cci-master = <0>;
status = "ok";
};
@@ -149,6 +151,8 @@
qcom,gpio-req-tbl-label = "CAMIF_MCLK",
"CAM_RESET",
"CAM_STANDBY";
+ qcom,sensor-position = <1>;
+ qcom,sensor-mode = <0>;
qcom,cci-master = <0>;
status = "ok";
};
diff --git a/arch/arm/boot/dts/msm8926-camera-sensor-qrd.dtsi b/arch/arm/boot/dts/msm8926-camera-sensor-qrd.dtsi
index 8e053a9..6af862f 100644
--- a/arch/arm/boot/dts/msm8926-camera-sensor-qrd.dtsi
+++ b/arch/arm/boot/dts/msm8926-camera-sensor-qrd.dtsi
@@ -263,6 +263,8 @@
"CAM_RESET1",
"CAM_STANDBY",
"CAM_AF_PWDM";
+ qcom,sensor-position = <0>;
+ qcom,sensor-mode = <0>;
qcom,cci-master = <0>;
status = "ok";
};
@@ -296,6 +298,8 @@
"CAM_RESET",
"CAM_STANDBY",
"CAM_VDIG";
+ qcom,sensor-position = <1>;
+ qcom,sensor-mode = <0>;
qcom,cci-master = <0>;
status = "ok";
};
diff --git a/arch/arm/boot/dts/msm8926.dtsi b/arch/arm/boot/dts/msm8926.dtsi
index e866286..394f4a9 100644
--- a/arch/arm/boot/dts/msm8926.dtsi
+++ b/arch/arm/boot/dts/msm8926.dtsi
@@ -22,6 +22,11 @@
/ {
model = "Qualcomm MSM 8926";
compatible = "qcom,msm8926";
+
+};
+
+&qsecom_mem {
+ linux,memory-limit = <0x0>;
};
&soc {
diff --git a/arch/arm/boot/dts/msm8974-bus.dtsi b/arch/arm/boot/dts/msm8974-bus.dtsi
index b33b2b5..ba8b27e 100644
--- a/arch/arm/boot/dts/msm8974-bus.dtsi
+++ b/arch/arm/boot/dts/msm8974-bus.dtsi
@@ -1175,9 +1175,9 @@
qcom,prio-rd = <0>;
qcom,prio-wr = <0>;
qcom,mode-thresh = "Fixed";
- qcom,thresh = <2000000>;
+ qcom,thresh = <2000000 2456000>;
qcom,dual-conf;
- qcom,bimc,bw = <300000>;
+ qcom,bimc,bw = <300000 450000>;
qcom,bimc,gp = <5000>;
qcom,bimc,thmp = <50>;
};
@@ -1195,9 +1195,9 @@
qcom,prio-rd = <0>;
qcom,prio-wr = <0>;
qcom,mode-thresh = "Fixed";
- qcom,thresh = <2000000>;
+ qcom,thresh = <2000000 2456000>;
qcom,dual-conf;
- qcom,bimc,bw = <300000>;
+ qcom,bimc,bw = <300000 450000>;
qcom,bimc,gp = <5000>;
qcom,bimc,thmp = <50>;
};
diff --git a/arch/arm/boot/dts/msm8974-camera-sensor-cdp.dtsi b/arch/arm/boot/dts/msm8974-camera-sensor-cdp.dtsi
index bdc3bef..157c136 100644
--- a/arch/arm/boot/dts/msm8974-camera-sensor-cdp.dtsi
+++ b/arch/arm/boot/dts/msm8974-camera-sensor-cdp.dtsi
@@ -186,6 +186,7 @@
qcom,csid-sd-index = <0>;
qcom,actuator-src = <&actuator0>;
qcom,vdd-cx-supply = <&pm8841_s2>;
+ qcom,mount-angle = <90>;
qcom,vdd-cx-name = "qcom,vdd-cx";
cam_vdig-supply = <&pm8941_l3>;
cam_vana-supply = <&pm8941_l17>;
@@ -208,6 +209,8 @@
qcom,gpio-req-tbl-label = "CAMIF_MCLK",
"CAM_RESET1",
"CAM_STANDBY";
+ qcom,sensor-position = <0>;
+ qcom,sensor-mode = <0>;
qcom,cci-master = <0>;
status = "ok";
};
@@ -219,6 +222,7 @@
qcom,csiphy-sd-index = <1>;
qcom,csid-sd-index = <0>;
qcom,vdd-cx-supply = <&pm8841_s2>;
+ qcom,mount-angle = <90>;
qcom,vdd-cx-name = "qcom,vdd-cx";
cam_vdig-supply = <&pm8941_l3>;
cam_vana-supply = <&pm8941_l17>;
@@ -240,6 +244,8 @@
qcom,gpio-req-tbl-label = "CAMIF_MCLK",
"CAM_RESET1",
"CAM_STANDBY";
+ qcom,sensor-position = <1>;
+ qcom,sensor-mode = <0>;
qcom,cci-master = <0>;
status = "ok";
};
@@ -250,6 +256,7 @@
reg = <0x2>;
qcom,csiphy-sd-index = <2>;
qcom,csid-sd-index = <2>;
+ qcom,mount-angle = <90>;
qcom,vdd-cx-supply = <&pm8841_s2>;
qcom,vdd-cx-name = "qcom,vdd-cx";
cam_vdig-supply = <&pm8941_l3>;
@@ -268,6 +275,8 @@
qcom,gpio-req-tbl-flags = <1 0>;
qcom,gpio-req-tbl-label = "CAMIF_MCLK",
"CAM_RESET1";
+ qcom,sensor-position = <1>;
+ qcom,sensor-mode = <0>;
qcom,cci-master = <0>;
status = "ok";
};
diff --git a/arch/arm/boot/dts/msm8974-camera-sensor-dragonboard.dtsi b/arch/arm/boot/dts/msm8974-camera-sensor-dragonboard.dtsi
index 43b0d75..7ca986d 100644
--- a/arch/arm/boot/dts/msm8974-camera-sensor-dragonboard.dtsi
+++ b/arch/arm/boot/dts/msm8974-camera-sensor-dragonboard.dtsi
@@ -247,6 +247,8 @@
qcom,gpio-req-tbl-label = "CAMIF_MCLK",
"CAM_RESET1",
"CAM_STANDBY";
+ qcom,sensor-position = <1>;
+ qcom,sensor-mode = <0>;
qcom,cci-master = <0>;
status = "ok";
};
@@ -257,6 +259,7 @@
reg = <0x2>;
qcom,csiphy-sd-index = <2>;
qcom,csid-sd-index = <2>;
+ qcom,mount-angle = <180>;
qcom,vdd-cx-supply = <&pm8841_s2>;
qcom,vdd-cx-name = "qcom,vdd-cx";
cam_vdig-supply = <&pm8941_l3>;
@@ -278,6 +281,8 @@
qcom,gpio-req-tbl-flags = <1 0 0>;
qcom,gpio-req-tbl-label = "CAMIF_MCLK",
"CAM_RESET1";
+ qcom,sensor-position = <1>;
+ qcom,sensor-mode = <0>;
qcom,cci-master = <0>;
status = "ok";
};
diff --git a/arch/arm/boot/dts/msm8974-camera-sensor-fluid.dtsi b/arch/arm/boot/dts/msm8974-camera-sensor-fluid.dtsi
index 529d3ba..ecf5098 100644
--- a/arch/arm/boot/dts/msm8974-camera-sensor-fluid.dtsi
+++ b/arch/arm/boot/dts/msm8974-camera-sensor-fluid.dtsi
@@ -221,6 +221,8 @@
qcom,gpio-req-tbl-label = "CAMIF_MCLK",
"CAM_RESET1",
"CAM_STANDBY";
+ qcom,sensor-position = <0>;
+ qcom,sensor-mode = <0>;
qcom,cci-master = <0>;
status = "ok";
};
@@ -251,6 +253,8 @@
qcom,gpio-req-tbl-flags = <1 0>;
qcom,gpio-req-tbl-label = "CAMIF_MCLK",
"CAM_RESET1";
+ qcom,sensor-position = <1>;
+ qcom,sensor-mode = <0>;
qcom,cci-master = <1>;
status = "ok";
};
@@ -261,6 +265,7 @@
reg = <0x2>;
qcom,csiphy-sd-index = <2>;
qcom,csid-sd-index = <2>;
+ qcom,mount-angle = <90>;
qcom,vdd-cx-supply = <&pm8841_s2>;
qcom,vdd-cx-name = "qcom,vdd-cx";
cam_vdig-supply = <&pm8941_l3>;
diff --git a/arch/arm/boot/dts/msm8974-camera-sensor-liquid.dtsi b/arch/arm/boot/dts/msm8974-camera-sensor-liquid.dtsi
index 854e8f7..1b70557 100644
--- a/arch/arm/boot/dts/msm8974-camera-sensor-liquid.dtsi
+++ b/arch/arm/boot/dts/msm8974-camera-sensor-liquid.dtsi
@@ -209,6 +209,8 @@
qcom,gpio-req-tbl-label = "CAMIF_MCLK",
"CAM_RESET1",
"CAM_STANDBY";
+ qcom,sensor-position = <0>;
+ qcom,sensor-mode = <0>;
qcom,cci-master = <0>;
status = "ok";
};
@@ -238,6 +240,8 @@
qcom,gpio-req-tbl-flags = <1 0>;
qcom,gpio-req-tbl-label = "CAMIF_MCLK",
"CAM_RESET1";
+ qcom,sensor-position = <1>;
+ qcom,sensor-mode = <0>;
qcom,cci-master = <0>;
status = "ok";
};
@@ -248,6 +252,7 @@
reg = <0x2>;
qcom,csiphy-sd-index = <2>;
qcom,csid-sd-index = <2>;
+ qcom,mount-angle = <180>;
qcom,vdd-cx-supply = <&pm8841_s2>;
qcom,vdd-cx-name = "qcom,vdd-cx";
cam_vdig-supply = <&pm8941_l3>;
@@ -270,6 +275,8 @@
qcom,gpio-req-tbl-label = "CAMIF_MCLK",
"CAM_RESET1",
"CAM_STANDBY";
+ qcom,sensor-position = <1>;
+ qcom,sensor-mode = <0>;
qcom,cci-master = <0>;
status = "ok";
};
diff --git a/arch/arm/boot/dts/msm8974-camera-sensor-mtp.dtsi b/arch/arm/boot/dts/msm8974-camera-sensor-mtp.dtsi
index 59e1a7c..f3dff1a 100644
--- a/arch/arm/boot/dts/msm8974-camera-sensor-mtp.dtsi
+++ b/arch/arm/boot/dts/msm8974-camera-sensor-mtp.dtsi
@@ -222,6 +222,8 @@
qcom,gpio-req-tbl-label = "CAMIF_MCLK",
"CAM_RESET1",
"CAM_STANDBY";
+ qcom,sensor-position = <0>;
+ qcom,sensor-mode = <0>;
qcom,cci-master = <0>;
status = "ok";
};
@@ -251,6 +253,8 @@
qcom,gpio-req-tbl-flags = <1 0>;
qcom,gpio-req-tbl-label = "CAMIF_MCLK",
"CAM_RESET1";
+ qcom,sensor-position = <1>;
+ qcom,sensor-mode = <0>;
qcom,cci-master = <0>;
status = "ok";
};
@@ -278,6 +282,8 @@
qcom,gpio-req-tbl-flags = <1 0>;
qcom,gpio-req-tbl-label = "CAMIF_MCLK",
"CAM_RESET1";
+ qcom,sensor-position = <1>;
+ qcom,sensor-mode = <0>;
qcom,cci-master = <1>;
status = "ok";
};
diff --git a/arch/arm/boot/dts/msm8974-mdss.dtsi b/arch/arm/boot/dts/msm8974-mdss.dtsi
index 0e72446..d83a235 100644
--- a/arch/arm/boot/dts/msm8974-mdss.dtsi
+++ b/arch/arm/boot/dts/msm8974-mdss.dtsi
@@ -50,6 +50,15 @@
qcom,mdss-pipe-rgb-xin-id = <1 5 9>;
qcom,mdss-pipe-dma-xin-id = <2 10>;
+ qcom,mdss-pipe-vig-clk-ctrl-offsets = <0x3AC 0 0>,
+ <0x3B4 0 0>,
+ <0x3BC 0 0>;
+ qcom,mdss-pipe-rgb-clk-ctrl-offsets = <0x3AC 4 8>,
+ <0x3B4 4 8>,
+ <0x3BC 4 8>;
+ qcom,mdss-pipe-dma-clk-ctrl-offsets = <0x3AC 8 12>,
+ <0x3B4 8 12>;
+
qcom,mdss-smp-data = <22 4096>;
qcom,mdss-ctl-off = <0x00000600 0x00000700 0x00000800
@@ -113,9 +122,10 @@
compatible = "qcom,mdss-dsi-ctrl";
label = "MDSS DSI CTRL->0";
cell-index = <0>;
- reg = <0xfd922800 0x600>,
+ reg = <0xfd922800 0x1f8>,
+ <0xfd922b00 0x2b0>,
<0xfdf30000 0x108>;
- reg-names = "dsi_phys", "mmss_misc_phys";
+ reg-names = "dsi_ctrl", "dsi_phy", "mmss_misc_phys";
vdd-supply = <&pm8941_l22>;
vddio-supply = <&pm8941_l12>;
vdda-supply = <&pm8941_l2>;
@@ -171,9 +181,10 @@
compatible = "qcom,mdss-dsi-ctrl";
label = "MDSS DSI CTRL->1";
cell-index = <1>;
- reg = <0xfd922e00 0x600>,
+ reg = <0xfd922e00 0x1f8>,
+ <0xfd923100 0x2b0>,
<0xfdf30000 0x108>;
- reg-names = "dsi_phys", "mmss_misc_phys";
+ reg-names = "dsi_ctrl", "dsi_phy", "mmss_misc_phys";
vdd-supply = <&pm8941_l22>;
vddio-supply = <&pm8941_l12>;
vdda-supply = <&pm8941_l2>;
diff --git a/arch/arm/boot/dts/msm8974-v1-pm.dtsi b/arch/arm/boot/dts/msm8974-v1-pm.dtsi
index 886177d..516d068 100644
--- a/arch/arm/boot/dts/msm8974-v1-pm.dtsi
+++ b/arch/arm/boot/dts/msm8974-v1-pm.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
*
* This 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,7 +130,7 @@
qcom,L2-spm-is-apcs-master;
};
- qcom,lpm-levels {
+ lpm_levels: qcom,lpm-levels {
compatible = "qcom,lpm-levels";
qcom,default-l2-state = "l2_cache_retention";
#address-cells = <1>;
@@ -311,6 +311,7 @@
qcom,pc-mode = "tz_l2_int";
qcom,use-sync-timer;
qcom,cpus-as-clocks;
+ qcom,lpm-levels = <&lpm_levels>;
};
qcom,cpu-sleep-status@f9088008 {
diff --git a/arch/arm/boot/dts/msm8974-v2-pm.dtsi b/arch/arm/boot/dts/msm8974-v2-pm.dtsi
index 84a8c2d..cde5e5a9 100644
--- a/arch/arm/boot/dts/msm8974-v2-pm.dtsi
+++ b/arch/arm/boot/dts/msm8974-v2-pm.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -126,7 +126,7 @@
qcom,L2-spm-is-apcs-master;
};
- qcom,lpm-levels {
+ lpm_levels: qcom,lpm-levels {
compatible = "qcom,lpm-levels";
qcom,default-l2-state = "l2_cache_retention";
#address-cells = <1>;
@@ -324,6 +324,7 @@
qcom,pc-mode = "tz_l2_int";
qcom,use-sync-timer;
qcom,cpus-as-clocks;
+ qcom,lpm-levels = <&lpm_levels>;
qcom,pm-snoc-client {
compatible = "qcom,pm-snoc-client";
diff --git a/arch/arm/boot/dts/msm8974.dtsi b/arch/arm/boot/dts/msm8974.dtsi
index 23ddc8c..b3c7eb4 100644
--- a/arch/arm/boot/dts/msm8974.dtsi
+++ b/arch/arm/boot/dts/msm8974.dtsi
@@ -509,6 +509,7 @@
<78 512 800000 1600000>, /* 200 MB/s */
<78 512 2048000 4096000>; /* Max. bandwidth */
qcom,bus-bw-vectors-bps = <0 400000 20000000 25000000 50000000 100000000 200000000 4294967295>;
+ qcom,dat1-mpm-int = <42>;
status = "disable";
};
@@ -535,6 +536,7 @@
<81 512 800000 1600000>, /* 200 MB/s */
<81 512 2048000 4096000>; /* Max. bandwidth */
qcom,bus-bw-vectors-bps = <0 400000 20000000 25000000 50000000 100000000 200000000 4294967295>;
+ qcom,dat1-mpm-int = <44>;
status = "disable";
};
@@ -542,8 +544,17 @@
compatible = "qcom,sdhci-msm";
reg = <0xf9864900 0x11c>, <0xf9864000 0x800>;
reg-names = "hc_mem", "core_mem";
- interrupts = <0 127 0>, <0 224 0>;
- interrupt-names = "hc_irq", "pwr_irq";
+
+ #address-cells = <0>;
+ interrupt-parent = <&sdhc_3>;
+ interrupts = <0 1 2>;
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0xffffffff>;
+ interrupt-map = <0 &intc 0 127 0
+ 1 &intc 0 224 0
+ 2 &msmgpio 37 0x8>;
+ interrupt-names = "hc_irq", "pwr_irq", "sdiowakeup_irq";
+
gpios = <&msmgpio 40 0>, /* CLK */
<&msmgpio 39 0>, /* CMD */
<&msmgpio 38 0>, /* DATA0 */
@@ -575,8 +586,17 @@
compatible = "qcom,sdhci-msm";
reg = <0xf98e4900 0x11c>, <0xf98e4000 0x800>;
reg-names = "hc_mem", "core_mem";
- interrupts = <0 129 0>, <0 227 0>;
- interrupt-names = "hc_irq", "pwr_irq";
+
+ #address-cells = <0>;
+ interrupt-parent = <&sdhc_4>;
+ interrupts = <0 1 2>;
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0xffffffff>;
+ interrupt-map = <0 &intc 0 129 0
+ 1 &intc 0 227 0
+ 2 &msmgpio 95 0x8>;
+ interrupt-names = "hc_irq", "pwr_irq", "sdiowakeup_irq";
+
gpios = <&msmgpio 93 0>, /* CLK */
<&msmgpio 91 0>, /* CMD */
<&msmgpio 96 0>, /* DATA0 */
@@ -1520,7 +1540,9 @@
compatible = "qcom,cpubw";
qcom,cpu-mem-ports = <1 512>, <2 512>;
qcom,bw-tbl =
+ < 381 /* 50 MHz */ >,
< 572 /* 75 MHz */ >,
+ < 762 /* 100 MHz */ >,
< 1144 /* 150 MHz */ >,
< 1525 /* 200 MHz */ >,
< 2342 /* 307 MHz */ >,
@@ -1821,6 +1843,11 @@
compatible = "qcom,msm-dai-q6-dev";
qcom,msm-dai-q6-dev-id = <32773>;
};
+
+ qcom,msm-dai-q6-incall-music-2-rx {
+ compatible = "qcom,msm-dai-q6-dev";
+ qcom,msm-dai-q6-dev-id = <32770>;
+ };
};
qcom,msm-pri-auxpcm {
diff --git a/arch/arm/boot/dts/msm8974pro-pm.dtsi b/arch/arm/boot/dts/msm8974pro-pm.dtsi
index f735b65..0307e2a 100644
--- a/arch/arm/boot/dts/msm8974pro-pm.dtsi
+++ b/arch/arm/boot/dts/msm8974pro-pm.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -126,7 +126,7 @@
qcom,L2-spm-is-apcs-master;
};
- qcom,lpm-levels {
+ lpm_levels: qcom,lpm-levels {
compatible = "qcom,lpm-levels";
qcom,allow-synced-levels;
qcom,default-l2-state = "l2_cache_retention";
@@ -334,6 +334,7 @@
reg = <0xfe805664 0x40>;
qcom,pc-mode = "tz_l2_int";
qcom,cpus-as-clocks;
+ qcom,lpm-levels = <&lpm_levels>;
qcom,pm-snoc-client {
compatible = "qcom,pm-snoc-client";
diff --git a/arch/arm/boot/dts/msm8974pro.dtsi b/arch/arm/boot/dts/msm8974pro.dtsi
index a72ebb2..ae0547f 100644
--- a/arch/arm/boot/dts/msm8974pro.dtsi
+++ b/arch/arm/boot/dts/msm8974pro.dtsi
@@ -1578,13 +1578,13 @@
/* Off */
<26 512 0 0>, <89 604 0 0>,
/* Sub-SVS / SVS */
- <26 512 0 1600000>, <89 604 0 3200000>,
+ <26 512 1200000 2456000>, <89 604 0 3200000>,
/* SVS */
- <26 512 0 2456000>, <89 604 0 3200000>,
+ <26 512 1200000 2456000>, <89 604 0 3200000>,
/* low Nominal / SVS */
<26 512 0 3680000>, <89 604 0 3200000>,
/* SVS / low Nominal */
- <26 512 0 2456000>, <89 604 0 5280000>,
+ <26 512 1200000 2456000>, <89 604 0 5280000>,
/* low Nominal / low Nominal */
<26 512 0 3680000>, <89 604 0 5280000>,
/* Nominal / low Nominal */
@@ -1657,6 +1657,7 @@
};
&mdss_mdp {
+ qcom,max-bandwidth-low-kbps = <2750000>;
qcom,vbif-settings = <0x0004 0x00000001>;
qcom,mdss-wb-off = <0x00011100 0x00011500
diff --git a/arch/arm/boot/dts/msm9625-pm.dtsi b/arch/arm/boot/dts/msm9625-pm.dtsi
index 1e6cdf2..ec62cd4 100644
--- a/arch/arm/boot/dts/msm9625-pm.dtsi
+++ b/arch/arm/boot/dts/msm9625-pm.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
*
* This 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,7 +28,7 @@
3e 0f];
};
- qcom,lpm-levels {
+ lpm_levels: qcom,lpm-levels {
compatible = "qcom,lpm-levels";
qcom,no-l2-saw;
#address-cells = <1>;
@@ -167,6 +167,7 @@
reg = <0xfe805664 0x40>;
qcom,pc-mode = "tz_l2_ext";
qcom,use-sync-timer;
+ qcom,lpm-levels = <&lpm_levels>;
};
qcom,rpm-log@fc19dc00 {
diff --git a/arch/arm/boot/dts/msmsamarium-camera-sensor-cdp-interposer.dtsi b/arch/arm/boot/dts/msmsamarium-camera-sensor-cdp-interposer.dtsi
index 81640f8..0c25060 100644
--- a/arch/arm/boot/dts/msmsamarium-camera-sensor-cdp-interposer.dtsi
+++ b/arch/arm/boot/dts/msmsamarium-camera-sensor-cdp-interposer.dtsi
@@ -135,6 +135,8 @@
qcom,gpio-req-tbl-label = "CAMIF_MCLK",
"CAM_RESET1",
"CAM_STANDBY";
+ qcom,sensor-position = <0>;
+ qcom,sensor-mode = <0>;
qcom,cci-master = <0>;
status = "ok";
};
@@ -165,6 +167,8 @@
qcom,gpio-req-tbl-flags = <1 0>;
qcom,gpio-req-tbl-label = "CAMIF_MCLK2",
"CAM_XSHUTDOWN";
+ qcom,sensor-position = <1>;
+ qcom,sensor-mode = <0>;
qcom,cci-master = <0>;
status = "ok";
};
diff --git a/arch/arm/boot/dts/msmsamarium-camera-sensor-cdp.dtsi b/arch/arm/boot/dts/msmsamarium-camera-sensor-cdp.dtsi
index 27f4a99..637356b 100644
--- a/arch/arm/boot/dts/msmsamarium-camera-sensor-cdp.dtsi
+++ b/arch/arm/boot/dts/msmsamarium-camera-sensor-cdp.dtsi
@@ -136,6 +136,8 @@
qcom,gpio-req-tbl-label = "CAMIF_MCLK",
"CAM_RESET1",
"CAM_STANDBY";
+ qcom,sensor-position = <0>;
+ qcom,sensor-mode = <0>;
qcom,cci-master = <0>;
status = "ok";
};
@@ -165,6 +167,8 @@
qcom,gpio-req-tbl-flags = <1 0>;
qcom,gpio-req-tbl-label = "CAMIF_MCLK2",
"CAM_XSHUTDOWN";
+ qcom,sensor-position = <1>;
+ qcom,sensor-mode = <0>;
qcom,cci-master = <0>;
status = "ok";
};
diff --git a/arch/arm/configs/apq8084_defconfig b/arch/arm/configs/apq8084_defconfig
index 823e4f8..2f5e776 100644
--- a/arch/arm/configs/apq8084_defconfig
+++ b/arch/arm/configs/apq8084_defconfig
@@ -365,6 +365,7 @@
CONFIG_QPNP_CLKDIV=y
CONFIG_MSM_IOMMU_V1=y
CONFIG_IOMMU_PGTABLES_L2=y
+CONFIG_MSM_IOMMU_VBIF_CHECK=y
CONFIG_IOMMU_NON_SECURE=y
CONFIG_EXT2_FS=y
CONFIG_EXT2_FS_XATTR=y
diff --git a/arch/arm/configs/msm8226-perf_defconfig b/arch/arm/configs/msm8226-perf_defconfig
index 4dbb313..ef7a5db 100644
--- a/arch/arm/configs/msm8226-perf_defconfig
+++ b/arch/arm/configs/msm8226-perf_defconfig
@@ -272,8 +272,8 @@
CONFIG_INPUT_MISC=y
CONFIG_INPUT_UINPUT=y
CONFIG_INPUT_GPIO=m
-CONFIG_SERIAL_MSM_HSL=y
-CONFIG_SERIAL_MSM_HSL_CONSOLE=y
+#CONFIG_SERIAL_MSM_HSL is not set
+#CONFIG_SERIAL_MSM_HSL_CONSOLE is not set
CONFIG_DIAG_CHAR=y
CONFIG_HW_RANDOM=y
CONFIG_HW_RANDOM_MSM=y
@@ -428,6 +428,7 @@
CONFIG_QPNP_VIBRATOR=y
CONFIG_QPNP_REVID=y
CONFIG_MSM_IOMMU_V1=y
+CONFIG_MSM_IOMMU_VBIF_CHECK=y
CONFIG_EXT2_FS=y
CONFIG_EXT2_FS_XATTR=y
CONFIG_EXT3_FS=y
diff --git a/arch/arm/configs/msm8226_defconfig b/arch/arm/configs/msm8226_defconfig
index 1c402f3..d7719eb 100644
--- a/arch/arm/configs/msm8226_defconfig
+++ b/arch/arm/configs/msm8226_defconfig
@@ -333,6 +333,7 @@
CONFIG_MSMB_JPEG=y
CONFIG_MSM_VIDC_V4L2=y
CONFIG_MSM_WFD=y
+CONFIG_MSM_COMMON_LOG=y
# CONFIG_MEDIA_TUNER_SIMPLE is not set
# CONFIG_MEDIA_TUNER_TDA8290 is not set
# CONFIG_MEDIA_TUNER_TDA827X is not set
@@ -452,6 +453,7 @@
CONFIG_QPNP_VIBRATOR=y
CONFIG_QPNP_REVID=y
CONFIG_MSM_IOMMU_V1=y
+CONFIG_MSM_IOMMU_VBIF_CHECK=y
CONFIG_CORESIGHT=y
CONFIG_CORESIGHT_FUSE=y
CONFIG_CORESIGHT_TMC=y
diff --git a/arch/arm/configs/msm8610-perf_defconfig b/arch/arm/configs/msm8610-perf_defconfig
index 46de02f..9c583e3 100644
--- a/arch/arm/configs/msm8610-perf_defconfig
+++ b/arch/arm/configs/msm8610-perf_defconfig
@@ -212,6 +212,13 @@
CONFIG_BLK_DEV_LOOP=y
CONFIG_BLK_DEV_RAM=y
CONFIG_QSEECOM=y
+CONFIG_SCSI=y
+CONFIG_BLK_DEV_SD=y
+CONFIG_CHR_DEV_SG=y
+CONFIG_CHR_DEV_SCH=y
+CONFIG_SCSI_MULTI_LUN=y
+CONFIG_SCSI_LOGGING=y
+CONFIG_SCSI_SCAN_ASYNC=y
CONFIG_MD=y
CONFIG_BLK_DEV_DM=y
CONFIG_DM_CRYPT=y
@@ -244,8 +251,8 @@
CONFIG_INPUT_UINPUT=y
CONFIG_INPUT_GPIO=m
# CONFIG_INPUT_MOUSEDEV is not set
-CONFIG_SERIAL_MSM_HSL=y
-CONFIG_SERIAL_MSM_HSL_CONSOLE=y
+#CONFIG_SERIAL_MSM_HSL is not set
+#CONFIG_SERIAL_MSM_HSL_CONSOLE is not set
CONFIG_DIAG_CHAR=y
CONFIG_HW_RANDOM=y
CONFIG_HW_RANDOM_MSM=y
@@ -335,6 +342,12 @@
CONFIG_HID_MAGICMOUSE=y
CONFIG_HID_MICROSOFT=y
CONFIG_HID_ELECOM=y
+CONFIG_USB=y
+CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
+CONFIG_USB_SUSPEND=y
+CONFIG_USB_ICE40_HCD=m
+CONFIG_USB_CCID_BRIDGE=y
+CONFIG_USB_STORAGE=y
CONFIG_USB_GADGET=y
CONFIG_USB_GADGET_DEBUG_FILES=y
CONFIG_USB_GADGET_DEBUG_FS=y
diff --git a/arch/arm/configs/msm8610_defconfig b/arch/arm/configs/msm8610_defconfig
index 7af535d..6e86676 100644
--- a/arch/arm/configs/msm8610_defconfig
+++ b/arch/arm/configs/msm8610_defconfig
@@ -213,6 +213,13 @@
CONFIG_BLK_DEV_LOOP=y
CONFIG_BLK_DEV_RAM=y
CONFIG_QSEECOM=y
+CONFIG_SCSI=y
+CONFIG_BLK_DEV_SD=y
+CONFIG_CHR_DEV_SG=y
+CONFIG_CHR_DEV_SCH=y
+CONFIG_SCSI_MULTI_LUN=y
+CONFIG_SCSI_LOGGING=y
+CONFIG_SCSI_SCAN_ASYNC=y
CONFIG_MD=y
CONFIG_BLK_DEV_DM=y
CONFIG_DM_CRYPT=y
@@ -308,6 +315,7 @@
CONFIG_MSMB_JPEG=y
CONFIG_MSM_VIDC_V4L2=y
CONFIG_MSM_WFD=y
+CONFIG_MSM_COMMON_LOG=y
# CONFIG_MEDIA_TUNER_SIMPLE is not set
# CONFIG_MEDIA_TUNER_TDA8290 is not set
# CONFIG_MEDIA_TUNER_TDA827X is not set
@@ -357,6 +365,12 @@
CONFIG_HID_MAGICMOUSE=y
CONFIG_HID_MICROSOFT=y
CONFIG_HID_ELECOM=y
+CONFIG_USB=y
+CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
+CONFIG_USB_SUSPEND=y
+CONFIG_USB_ICE40_HCD=m
+CONFIG_USB_CCID_BRIDGE=y
+CONFIG_USB_STORAGE=y
CONFIG_USB_GADGET=y
CONFIG_USB_GADGET_DEBUG_FILES=y
CONFIG_USB_GADGET_DEBUG_FS=y
diff --git a/arch/arm/configs/msm8974-perf_defconfig b/arch/arm/configs/msm8974-perf_defconfig
index 915b1c3..b0dbb5e 100755
--- a/arch/arm/configs/msm8974-perf_defconfig
+++ b/arch/arm/configs/msm8974-perf_defconfig
@@ -343,10 +343,6 @@
CONFIG_VIDEO_V4L2_SUBDEV_API=y
CONFIG_DVB_CORE=m
# CONFIG_MSM_CAMERA is not set
-CONFIG_MT9M114=y
-CONFIG_OV2720=y
-CONFIG_IMX135=y
-CONFIG_IMX132=y
CONFIG_MSM_CAMERA_SENSOR=y
CONFIG_MSM_EEPROM=y
CONFIG_MSM_CPP=y
@@ -355,7 +351,6 @@
CONFIG_MSM_CSIPHY=y
CONFIG_MSM_CSID=y
CONFIG_MSM_ISPIF=y
-CONFIG_S5K3L1YX=y
CONFIG_MSMB_CAMERA=y
CONFIG_MSMB_JPEG=y
CONFIG_MSM_VIDC_V4L2=y
@@ -463,6 +458,7 @@
CONFIG_QPNP_COINCELL=y
CONFIG_MSM_IOMMU_V1=y
CONFIG_IOMMU_PGTABLES_L2=y
+CONFIG_MSM_IOMMU_VBIF_CHECK=y
CONFIG_MOBICORE_SUPPORT=m
CONFIG_MOBICORE_API=m
CONFIG_BIF=y
diff --git a/arch/arm/configs/msm8974_defconfig b/arch/arm/configs/msm8974_defconfig
index e72e5ff..b002e45 100755
--- a/arch/arm/configs/msm8974_defconfig
+++ b/arch/arm/configs/msm8974_defconfig
@@ -124,6 +124,7 @@
CONFIG_PACKET=y
CONFIG_UNIX=y
CONFIG_XFRM_USER=y
+CONFIG_NET_KEY=y
CONFIG_INET=y
CONFIG_IP_ADVANCED_ROUTER=y
CONFIG_IP_MULTIPLE_TABLES=y
@@ -132,6 +133,7 @@
CONFIG_IP_PNP_DHCP=y
CONFIG_INET_AH=y
CONFIG_INET_ESP=y
+CONFIG_INET_IPCOMP=y
# CONFIG_INET_XFRM_MODE_BEET is not set
# CONFIG_INET_LRO is not set
CONFIG_IPV6=y
@@ -222,6 +224,9 @@
CONFIG_IP6_NF_RAW=y
CONFIG_BRIDGE_NF_EBTABLES=y
CONFIG_BRIDGE_EBT_BROUTE=y
+CONFIG_L2TP=y
+CONFIG_L2TP_DEBUGFS=y
+CONFIG_L2TP_V3=y
CONFIG_BRIDGE=y
CONFIG_NET_SCHED=y
CONFIG_NET_SCH_HTB=y
@@ -283,6 +288,18 @@
CONFIG_KS8851=m
# CONFIG_MSM_RMNET is not set
CONFIG_MSM_RMNET_BAM=y
+CONFIG_PPP=y
+CONFIG_PPP_BSDCOMP=y
+CONFIG_PPP_DEFLATE=y
+CONFIG_PPP_FILTER=y
+CONFIG_PPP_MPPE=y
+CONFIG_PPP_MULTILINK=y
+CONFIG_PPPOE=y
+CONFIG_PPPOL2TP=y
+CONFIG_PPPOLAC=y
+CONFIG_PPPOPNS=y
+CONFIG_PPP_ASYNC=y
+CONFIG_PPP_SYNC_TTY=y
CONFIG_SLIP=y
CONFIG_SLIP_COMPRESSED=y
CONFIG_SLIP_MODE_SLIP6=y
@@ -350,10 +367,6 @@
CONFIG_VIDEO_V4L2_SUBDEV_API=y
CONFIG_DVB_CORE=m
# CONFIG_MSM_CAMERA is not set
-CONFIG_MT9M114=y
-CONFIG_OV2720=y
-CONFIG_IMX135=y
-CONFIG_IMX132=y
CONFIG_MSM_CAMERA_SENSOR=y
CONFIG_MSM_EEPROM=y
CONFIG_MSM_CPP=y
@@ -362,7 +375,6 @@
CONFIG_MSM_CSIPHY=y
CONFIG_MSM_CSID=y
CONFIG_MSM_ISPIF=y
-CONFIG_S5K3L1YX=y
CONFIG_MSMB_CAMERA=y
CONFIG_MSMB_JPEG=y
CONFIG_MSM_VIDC_V4L2=y
@@ -471,6 +483,7 @@
CONFIG_MSM_IOMMU_V1=y
CONFIG_MSM_IOMMU_PMON=y
CONFIG_IOMMU_PGTABLES_L2=y
+CONFIG_MSM_IOMMU_VBIF_CHECK=y
CONFIG_MOBICORE_SUPPORT=m
CONFIG_MOBICORE_API=m
CONFIG_CORESIGHT=y
diff --git a/arch/arm/include/asm/system_misc.h b/arch/arm/include/asm/system_misc.h
index 71f4827..0827df7 100644
--- a/arch/arm/include/asm/system_misc.h
+++ b/arch/arm/include/asm/system_misc.h
@@ -24,6 +24,7 @@
extern void disable_hlt(void);
extern void enable_hlt(void);
extern int get_hlt(void);
+extern char* (*arch_read_hardware_id)(void);
#endif /* !__ASSEMBLY__ */
diff --git a/arch/arm/kernel/arch_timer.c b/arch/arm/kernel/arch_timer.c
index 8c0a923..5a1ed75 100644
--- a/arch/arm/kernel/arch_timer.c
+++ b/arch/arm/kernel/arch_timer.c
@@ -493,8 +493,8 @@
clockevents_config_and_register(clk, arch_timer_rate,
0xf, 0x7fffffff);
- err = request_irq(arch_timer_spi, arch_timer_handler_mem, 0,
- "arch_timer", clk);
+ err = request_irq(arch_timer_spi, arch_timer_handler_mem,
+ IRQF_TIMER, "arch_timer", clk);
return err;
}
diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c
index 7298f9a..c110f0f 100644
--- a/arch/arm/kernel/setup.c
+++ b/arch/arm/kernel/setup.c
@@ -110,6 +110,9 @@
unsigned int cold_boot;
EXPORT_SYMBOL(cold_boot);
+char* (*arch_read_hardware_id)(void);
+EXPORT_SYMBOL(arch_read_hardware_id);
+
#ifdef MULTI_CPU
struct processor processor __read_mostly;
#endif
@@ -1108,7 +1111,10 @@
seq_puts(m, "\n");
- seq_printf(m, "Hardware\t: %s\n", machine_name);
+ if (!arch_read_hardware_id)
+ seq_printf(m, "Hardware\t: %s\n", machine_name);
+ else
+ seq_printf(m, "Hardware\t: %s\n", arch_read_hardware_id());
seq_printf(m, "Revision\t: %04x\n", system_rev);
seq_printf(m, "Serial\t\t: %08x%08x\n",
system_serial_high, system_serial_low);
diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c
index 5bca467..4a6e6db 100644
--- a/arch/arm/kernel/smp.c
+++ b/arch/arm/kernel/smp.c
@@ -696,6 +696,7 @@
void smp_send_reschedule(int cpu)
{
+ BUG_ON(cpu_is_offline(cpu));
smp_cross_call(cpumask_of(cpu), IPI_RESCHEDULE);
}
diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig
index b8444c1..6b551cd 100644
--- a/arch/arm/mach-msm/Kconfig
+++ b/arch/arm/mach-msm/Kconfig
@@ -3005,6 +3005,13 @@
help
Select if BLSP based UART Core v.14 or higher is present.
+config MSM_COMMON_LOG
+ bool "Use to export symbols for parsing memory dump"
+ help
+ Use this to export symbols to a memory dump table. This table
+ can be used by post analysis tools to extract information from
+ memory when device crashes.
+
config MSM_BOOT_STATS
bool "Use MSM boot stats reporting"
help
diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile
index 1c07e7d..5f7f0d9 100644
--- a/arch/arm/mach-msm/Makefile
+++ b/arch/arm/mach-msm/Makefile
@@ -100,6 +100,7 @@
obj-$(CONFIG_MACH_TROUT) += board-trout-rfkill.o
obj-$(CONFIG_MSM_BAM_DMUX) += bam_dmux.o
obj-$(CONFIG_MSM_SMD_LOGGING) += smem_log.o
+obj-$(CONFIG_MSM_COMMON_LOG) += common_log.o
obj-$(CONFIG_MSM_IPC_LOGGING) += ipc_logging.o
ifdef CONFIG_DEBUG_FS
obj-$(CONFIG_MSM_IPC_LOGGING) += ipc_logging_debug.o
diff --git a/arch/arm/mach-msm/board-8226-gpiomux.c b/arch/arm/mach-msm/board-8226-gpiomux.c
index fe7de31..08566bb 100644
--- a/arch/arm/mach-msm/board-8226-gpiomux.c
+++ b/arch/arm/mach-msm/board-8226-gpiomux.c
@@ -57,6 +57,42 @@
};
#endif
+static struct gpiomux_setting smsc_hub_act_cfg = {
+ .func = GPIOMUX_FUNC_GPIO,
+ .drv = GPIOMUX_DRV_8MA,
+ .pull = GPIOMUX_PULL_NONE,
+};
+
+static struct gpiomux_setting smsc_hub_susp_cfg = {
+ .func = GPIOMUX_FUNC_GPIO,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_NONE,
+};
+
+static struct msm_gpiomux_config smsc_hub_configs[] = {
+ {
+ .gpio = 114, /* reset_n */
+ .settings = {
+ [GPIOMUX_ACTIVE] = &smsc_hub_act_cfg,
+ [GPIOMUX_SUSPENDED] = &smsc_hub_susp_cfg,
+ },
+ },
+ {
+ .gpio = 8, /* clk_en */
+ .settings = {
+ [GPIOMUX_ACTIVE] = &smsc_hub_act_cfg,
+ [GPIOMUX_SUSPENDED] = &smsc_hub_susp_cfg,
+ },
+ },
+ {
+ .gpio = 9, /* int_n */
+ .settings = {
+ [GPIOMUX_ACTIVE] = &smsc_hub_act_cfg,
+ [GPIOMUX_SUSPENDED] = &smsc_hub_susp_cfg,
+ },
+ },
+};
+
#define KS8851_IRQ_GPIO 115
#if defined(CONFIG_KS8851) || defined(CONFIG_KS8851_MODULE)
@@ -940,6 +976,9 @@
}
msm_gpiomux_install(msm_hsic_configs, ARRAY_SIZE(msm_hsic_configs));
#endif
+ if (machine_is_msm8926() && of_board_is_mtp())
+ msm_gpiomux_install(smsc_hub_configs,
+ ARRAY_SIZE(smsc_hub_configs));
}
static void wcnss_switch_to_gpio(void)
diff --git a/arch/arm/mach-msm/board-8610-gpiomux.c b/arch/arm/mach-msm/board-8610-gpiomux.c
index 4234f2a..c91deb2 100644
--- a/arch/arm/mach-msm/board-8610-gpiomux.c
+++ b/arch/arm/mach-msm/board-8610-gpiomux.c
@@ -730,6 +730,61 @@
},
};
+static struct gpiomux_setting ice40_spi_cs_act_config = {
+ .func = GPIOMUX_FUNC_1,
+ .drv = GPIOMUX_DRV_6MA,
+ .pull = GPIOMUX_PULL_NONE,
+};
+
+static struct gpiomux_setting ice40_spi_cs_susp_config = {
+ .func = GPIOMUX_FUNC_GPIO,
+ .drv = GPIOMUX_DRV_6MA,
+ .pull = GPIOMUX_PULL_UP,
+};
+
+static struct gpiomux_setting ice40_act_config = {
+ .func = GPIOMUX_FUNC_GPIO,
+ .drv = GPIOMUX_DRV_8MA,
+ .pull = GPIOMUX_PULL_NONE,
+};
+
+static struct gpiomux_setting ice40_susp_config = {
+ .func = GPIOMUX_FUNC_GPIO,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_NONE,
+};
+
+static struct msm_gpiomux_config ice40_spi_usb_configs[] __initdata = {
+ {
+ .gpio = 85,
+ .settings = {
+ [GPIOMUX_ACTIVE] = &ice40_spi_cs_act_config,
+ [GPIOMUX_SUSPENDED] = &ice40_spi_cs_susp_config,
+ },
+ },
+ {
+ .gpio = 94,
+ .settings = {
+ [GPIOMUX_ACTIVE] = &ice40_act_config,
+ [GPIOMUX_SUSPENDED] = &ice40_susp_config,
+ },
+ },
+ {
+ .gpio = 95,
+ .settings = {
+ [GPIOMUX_ACTIVE] = &ice40_act_config,
+ [GPIOMUX_SUSPENDED] = &ice40_susp_config,
+ },
+ },
+ {
+ .gpio = 96,
+ .settings = {
+ [GPIOMUX_ACTIVE] = &ice40_act_config,
+ [GPIOMUX_SUSPENDED] = &ice40_susp_config,
+ },
+ },
+};
+
void __init msm8610_init_gpiomux(void)
{
int rc;
@@ -770,6 +825,10 @@
if (of_board_is_cdp())
msm_gpiomux_install(msm_cdc_dmic_configs,
ARRAY_SIZE(msm_cdc_dmic_configs));
+
+ if (of_board_is_cdp())
+ msm_gpiomux_install(ice40_spi_usb_configs,
+ ARRAY_SIZE(ice40_spi_usb_configs));
}
static void wcnss_switch_to_gpio(void)
diff --git a/arch/arm/mach-msm/clock-8226.c b/arch/arm/mach-msm/clock-8226.c
index d1f8666..7a7c008 100644
--- a/arch/arm/mach-msm/clock-8226.c
+++ b/arch/arm/mach-msm/clock-8226.c
@@ -3146,8 +3146,11 @@
CLK_LOOKUP("bus_clk", gcc_mss_q6_bimc_axi_clk.c, "fc880000.qcom,mss"),
CLK_LOOKUP("iface_clk", gcc_mss_cfg_ahb_clk.c, "fc880000.qcom,mss"),
CLK_LOOKUP("mem_clk", gcc_boot_rom_ahb_clk.c, "fc880000.qcom,mss"),
+
/* NFC */
- CLK_LOOKUP("ref_clk", cxo_d1_a_pin.c, "2-000e"),
+ CLK_LOOKUP("ref_clk", cxo_d1_a_pin.c, ""),
+ CLK_LOOKUP("ref_clk", cxo_d1_pin.c, "2-000e"),
+
/* PIL-PRONTO */
CLK_LOOKUP("xo", cxo_pil_pronto_clk.c, "fb21b000.qcom,pronto"),
@@ -3407,13 +3410,13 @@
/* MM sensor clocks */
CLK_LOOKUP("cam_src_clk", mclk0_clk_src.c, "6f.qcom,camera"),
- CLK_LOOKUP("cam_src_clk", mclk0_clk_src.c, "90.qcom,camera"),
+ CLK_LOOKUP("cam_src_clk", mclk1_clk_src.c, "90.qcom,camera"),
CLK_LOOKUP("cam_src_clk", mclk0_clk_src.c, "6d.qcom,camera"),
CLK_LOOKUP("cam_src_clk", mclk0_clk_src.c, "6a.qcom,camera"),
CLK_LOOKUP("cam_src_clk", mclk0_clk_src.c, "6c.qcom,camera"),
CLK_LOOKUP("cam_src_clk", mclk0_clk_src.c, "20.qcom,camera"),
CLK_LOOKUP("cam_clk", camss_mclk0_clk.c, "6f.qcom,camera"),
- CLK_LOOKUP("cam_clk", camss_mclk0_clk.c, "90.qcom,camera"),
+ CLK_LOOKUP("cam_clk", camss_mclk1_clk.c, "90.qcom,camera"),
CLK_LOOKUP("cam_clk", camss_mclk0_clk.c, "6d.qcom,camera"),
CLK_LOOKUP("cam_clk", camss_mclk0_clk.c, "6a.qcom,camera"),
CLK_LOOKUP("cam_clk", camss_mclk0_clk.c, "6c.qcom,camera"),
diff --git a/arch/arm/mach-msm/clock-8974.c b/arch/arm/mach-msm/clock-8974.c
index b42878d..1771090 100755
--- a/arch/arm/mach-msm/clock-8974.c
+++ b/arch/arm/mach-msm/clock-8974.c
@@ -2197,6 +2197,7 @@
.en_mask = BIT(5),
.base = &virt_bases[GCC_BASE],
.c = {
+ .parent = &ce1_clk_src.c,
.dbg_name = "gcc_ce1_clk",
.ops = &clk_ops_vote,
CLK_INIT(gcc_ce1_clk.c),
@@ -2233,6 +2234,7 @@
.en_mask = BIT(2),
.base = &virt_bases[GCC_BASE],
.c = {
+ .parent = &ce2_clk_src.c,
.dbg_name = "gcc_ce2_clk",
.ops = &clk_ops_vote,
CLK_INIT(gcc_ce2_clk.c),
diff --git a/arch/arm/mach-msm/clock-mdss-8974.c b/arch/arm/mach-msm/clock-mdss-8974.c
index 1fc7f1d..b63008f 100644
--- a/arch/arm/mach-msm/clock-mdss-8974.c
+++ b/arch/arm/mach-msm/clock-mdss-8974.c
@@ -1877,7 +1877,7 @@
if (vco_rate == 810000000) {
DSS_REG_W(mdss_edp_base, 0x0c, 0x18);
/* UNIPHY_PLL_LKDET_CFG2 */
- DSS_REG_W(mdss_edp_base, 0x64, 0x05);
+ DSS_REG_W(mdss_edp_base, 0x64, 0x0d);
/* UNIPHY_PLL_REFCLK_CFG */
DSS_REG_W(mdss_edp_base, 0x00, 0x00);
/* UNIPHY_PLL_SDM_CFG0 */
@@ -1899,7 +1899,7 @@
/* UNIPHY_PLL_SSC_CFG3 */
DSS_REG_W(mdss_edp_base, 0x58, 0x00);
/* UNIPHY_PLL_CAL_CFG0 */
- DSS_REG_W(mdss_edp_base, 0x6c, 0x0a);
+ DSS_REG_W(mdss_edp_base, 0x6c, 0x12);
/* UNIPHY_PLL_CAL_CFG2 */
DSS_REG_W(mdss_edp_base, 0x74, 0x01);
/* UNIPHY_PLL_CAL_CFG6 */
@@ -1924,7 +1924,7 @@
DSS_REG_W(mdss_edp_base, 0x28, 0x00);
} else if (vco_rate == 1350000000) {
/* UNIPHY_PLL_LKDET_CFG2 */
- DSS_REG_W(mdss_edp_base, 0x64, 0x05);
+ DSS_REG_W(mdss_edp_base, 0x64, 0x0d);
/* UNIPHY_PLL_REFCLK_CFG */
DSS_REG_W(mdss_edp_base, 0x00, 0x01);
/* UNIPHY_PLL_SDM_CFG0 */
@@ -1946,7 +1946,7 @@
/* UNIPHY_PLL_SSC_CFG3 */
DSS_REG_W(mdss_edp_base, 0x58, 0x00);
/* UNIPHY_PLL_CAL_CFG0 */
- DSS_REG_W(mdss_edp_base, 0x6c, 0x0a);
+ DSS_REG_W(mdss_edp_base, 0x6c, 0x12);
/* UNIPHY_PLL_CAL_CFG2 */
DSS_REG_W(mdss_edp_base, 0x74, 0x01);
/* UNIPHY_PLL_CAL_CFG6 */
diff --git a/arch/arm/mach-msm/common_log.c b/arch/arm/mach-msm/common_log.c
new file mode 100644
index 0000000..9491d9a
--- /dev/null
+++ b/arch/arm/mach-msm/common_log.c
@@ -0,0 +1,44 @@
+/* Copyright (c) 2014, The Linux Foundation. All rights reserved.
+ *
+ * This 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/init.h>
+#include <linux/kallsyms.h>
+#include <mach/msm_memory_dump.h>
+
+static void __init common_log_register(void)
+{
+ struct msm_client_dump dump;
+ char **log_bufp;
+ uint32_t *log_buf_lenp;
+
+ log_bufp = (char **)kallsyms_lookup_name("log_buf");
+ log_buf_lenp = (uint32_t *)kallsyms_lookup_name("log_buf_len");
+ if (!log_bufp || !log_buf_lenp) {
+ pr_err("common_log_register: Symbol log_buf not found!\n");
+ return;
+ }
+ dump.id = MSM_LOG_BUF;
+ dump.start_addr = virt_to_phys(*log_bufp);
+ dump.end_addr = virt_to_phys(*log_bufp + *log_buf_lenp);
+ if (msm_dump_table_register(&dump))
+ pr_err("common_log_register: Could not register log_bug.\n");
+}
+
+static int __init msm_common_log_init(void)
+{
+ common_log_register();
+ return 0;
+}
+late_initcall(msm_common_log_init);
diff --git a/arch/arm/mach-msm/cpr-regulator.c b/arch/arm/mach-msm/cpr-regulator.c
index d952f82..d15da59f 100644
--- a/arch/arm/mach-msm/cpr-regulator.c
+++ b/arch/arm/mach-msm/cpr-regulator.c
@@ -96,6 +96,8 @@
/* RBCPR Result status Register */
#define REG_RBCPR_RESULT_0 0xA0
+#define RBCPR_RESULT0_BUSY_SHIFT 19
+#define RBCPR_RESULT0_BUSY_MASK BIT(RBCPR_RESULT0_BUSY_SHIFT)
#define RBCPR_RESULT0_ERROR_STEPS_SHIFT 2
#define RBCPR_RESULT0_ERROR_STEPS_BITS 4
#define RBCPR_RESULT0_ERROR_STEPS_MASK ((1<<RBCPR_RESULT0_ERROR_STEPS_BITS)-1)
@@ -388,6 +390,22 @@
cpr_ctl_modify(cpr_vreg, RBCPR_CTL_LOOP_EN, 0);
}
+static bool cpr_ctl_is_enabled(struct cpr_regulator *cpr_vreg)
+{
+ u32 reg_val;
+
+ reg_val = cpr_read(cpr_vreg, REG_RBCPR_CTL);
+ return reg_val & RBCPR_CTL_LOOP_EN;
+}
+
+static bool cpr_ctl_is_busy(struct cpr_regulator *cpr_vreg)
+{
+ u32 reg_val;
+
+ reg_val = cpr_read(cpr_vreg, REG_RBCPR_RESULT_0);
+ return reg_val & RBCPR_RESULT0_BUSY_MASK;
+}
+
static void cpr_corner_save(struct cpr_regulator *cpr_vreg, int corner)
{
cpr_vreg->save_ctl[corner] = cpr_read(cpr_vreg, REG_RBCPR_CTL);
@@ -732,7 +750,13 @@
cpr_debug_irq("IRQ_STATUS = 0x%02X\n", reg_val);
- if (!cpr_is_allowed(cpr_vreg)) {
+ if (!cpr_ctl_is_enabled(cpr_vreg)) {
+ cpr_debug_irq("CPR is disabled\n");
+ goto _exit;
+ } else if (cpr_ctl_is_busy(cpr_vreg)) {
+ cpr_debug_irq("CPR measurement is not ready\n");
+ goto _exit;
+ } else if (!cpr_is_allowed(cpr_vreg)) {
reg_val = cpr_read(cpr_vreg, REG_RBCPR_CTL);
pr_err("Interrupt broken? RBCPR_CTL = 0x%02X\n", reg_val);
goto _exit;
@@ -897,7 +921,6 @@
mutex_lock(&cpr_vreg->cpr_mutex);
cpr_ctl_disable(cpr_vreg);
- disable_irq(cpr_vreg->cpr_irq);
cpr_irq_clr(cpr_vreg);
@@ -917,7 +940,6 @@
cpr_vreg->is_cpr_suspended = false;
cpr_irq_clr(cpr_vreg);
- enable_irq(cpr_vreg->cpr_irq);
cpr_ctl_enable(cpr_vreg, cpr_vreg->corner);
mutex_unlock(&cpr_vreg->cpr_mutex);
diff --git a/arch/arm/mach-msm/include/mach/iommu.h b/arch/arm/mach-msm/include/mach/iommu.h
index c4db727..0f69a7b 100644
--- a/arch/arm/mach-msm/include/mach/iommu.h
+++ b/arch/arm/mach-msm/include/mach/iommu.h
@@ -86,6 +86,7 @@
* struct msm_iommu_drvdata - A single IOMMU hardware instance
* @base: IOMMU config port base address (VA)
* @glb_base: IOMMU config port base address for global register space (VA)
+ * @phys_base: IOMMU physical base address.
* @ncb The number of contexts on this IOMMU
* @irq: Interrupt number
* @clk: The bus clock for this IOMMU hardware instance
@@ -108,6 +109,7 @@
*/
struct msm_iommu_drvdata {
void __iomem *base;
+ phys_addr_t phys_base;
void __iomem *glb_base;
int ncb;
int ttbr_split;
diff --git a/arch/arm/mach-msm/include/mach/iommu_hw-v1.h b/arch/arm/mach-msm/include/mach/iommu_hw-v1.h
index 04cd441..4509092 100644
--- a/arch/arm/mach-msm/include/mach/iommu_hw-v1.h
+++ b/arch/arm/mach-msm/include/mach/iommu_hw-v1.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
*
* This 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,17 +16,19 @@
#define CTX_SHIFT 12
#define CTX_OFFSET 0x8000
-#define GET_GLOBAL_REG(reg, base) (readl_relaxed((base) + (reg)))
-#define GET_CTX_REG(reg, base, ctx) \
- (readl_relaxed((base) + CTX_OFFSET + (reg) + ((ctx) << CTX_SHIFT)))
-#define GET_CTX_REG_L(reg, base, ctx) \
- (readll_relaxed((base) + CTX_OFFSET + (reg) + ((ctx) << CTX_SHIFT)))
+#define CTX_REG(reg, base, ctx) \
+ ((base) + CTX_OFFSET + (reg) + ((ctx) << CTX_SHIFT))
+#define GLB_REG(reg, base) \
+ ((base) + (reg))
+
+#define GET_GLOBAL_REG(reg, base) (readl_relaxed(GLB_REG(reg, base)))
+#define GET_CTX_REG(reg, base, ctx) (readl_relaxed(CTX_REG(reg, base, ctx)))
+#define GET_CTX_REG_L(reg, base, ctx) (readll_relaxed(CTX_REG(reg, base, ctx)))
#define SET_GLOBAL_REG(reg, base, val) writel_relaxed((val), ((base) + (reg)))
#define SET_CTX_REG(reg, base, ctx, val) \
- writel_relaxed((val), \
- ((base) + CTX_OFFSET + (reg) + ((ctx) << CTX_SHIFT)))
+ writel_relaxed((val), (CTX_REG(reg, base, ctx)))
/* Wrappers for numbered registers */
#define SET_GLOBAL_REG_N(b, n, r, v) SET_GLOBAL_REG((b), ((r) + (n << 2)), (v))
@@ -150,6 +152,11 @@
SET_GLOBAL_FIELD(b, MICRO_MMU_CTRL, HALT_REQ, v)
#define GET_MICRO_MMU_CTRL_IDLE(b) \
GET_GLOBAL_FIELD(b, MICRO_MMU_CTRL, IDLE)
+#define SET_MICRO_MMU_CTRL_RESERVED(b, v) \
+ SET_GLOBAL_FIELD(b, MICRO_MMU_CTRL, RESERVED, v)
+
+#define MMU_CTRL_IDLE (MICRO_MMU_CTRL_IDLE_MASK << MICRO_MMU_CTRL_IDLE_SHIFT)
+
#define SET_PREDICTIONDIS0(b, v) SET_GLOBAL_REG(PREDICTIONDIS0, (b), (v))
#define SET_PREDICTIONDIS1(b, v) SET_GLOBAL_REG(PREDICTIONDIS1, (b), (v))
#define SET_S1L1BFBLP0(b, v) SET_GLOBAL_REG(S1L1BFBLP0, (b), (v))
diff --git a/arch/arm/mach-msm/include/mach/msm_memory_dump.h b/arch/arm/mach-msm/include/mach/msm_memory_dump.h
index 89df485..13f54bf 100644
--- a/arch/arm/mach-msm/include/mach/msm_memory_dump.h
+++ b/arch/arm/mach-msm/include/mach/msm_memory_dump.h
@@ -27,6 +27,7 @@
MSM_ETM3_REG,
MSM_TMC0_REG, /* TMC_ETR */
MSM_TMC1_REG, /* TMC_ETF */
+ MSM_LOG_BUF,
MAX_NUM_CLIENTS,
};
diff --git a/arch/arm/mach-msm/include/mach/ocmem_priv.h b/arch/arm/mach-msm/include/mach/ocmem_priv.h
index 32d58d4..00aedb6 100644
--- a/arch/arm/mach-msm/include/mach/ocmem_priv.h
+++ b/arch/arm/mach-msm/include/mach/ocmem_priv.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -226,7 +226,6 @@
int process_dump(int, struct ocmem_handle *, unsigned long);
int ocmem_rdm_transfer(int, struct ocmem_map_list *,
unsigned long, int);
-int ocmem_clear(unsigned long, unsigned long);
unsigned long process_quota(int);
int ocmem_memory_off(int, unsigned long, unsigned long);
int ocmem_memory_on(int, unsigned long, unsigned long);
diff --git a/arch/arm/mach-msm/include/mach/socinfo.h b/arch/arm/mach-msm/include/mach/socinfo.h
index aeb32f8..24b5181 100644
--- a/arch/arm/mach-msm/include/mach/socinfo.h
+++ b/arch/arm/mach-msm/include/mach/socinfo.h
@@ -138,6 +138,11 @@
MSM_CPU_SAMARIUM,
};
+struct msm_soc_info {
+ enum msm_cpu generic_soc_type;
+ char *soc_id_string;
+};
+
enum pmic_model {
PMIC_MODEL_PM8058 = 13,
PMIC_MODEL_PM8028 = 14,
diff --git a/arch/arm/mach-msm/include/mach/subsystem_notif.h b/arch/arm/mach-msm/include/mach/subsystem_notif.h
index 5865eff..59e212f 100644
--- a/arch/arm/mach-msm/include/mach/subsystem_notif.h
+++ b/arch/arm/mach-msm/include/mach/subsystem_notif.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011, 2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011, 2013-2014, The Linux Foundation. All rights reserved.
*
* This 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 @@
SUBSYS_BEFORE_POWERUP,
SUBSYS_AFTER_POWERUP,
SUBSYS_RAMDUMP_NOTIFICATION,
+ SUBSYS_POWERUP_FAILURE,
SUBSYS_NOTIF_TYPE_COUNT
};
diff --git a/arch/arm/mach-msm/krait-regulator.c b/arch/arm/mach-msm/krait-regulator.c
index a291b90..b0a1a7c 100644
--- a/arch/arm/mach-msm/krait-regulator.c
+++ b/arch/arm/mach-msm/krait-regulator.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -708,8 +708,10 @@
static int switch_to_using_ldo(struct krait_power_vreg *kvreg)
{
- if (kvreg->mode == LDO_MODE
- && get_krait_ldo_uv(kvreg) == kvreg->uV - kvreg->ldo_delta_uV)
+ int uV = kvreg->uV - kvreg->ldo_delta_uV;
+ int ldo_uV = DIV_ROUND_UP(uV, KRAIT_LDO_STEP) * KRAIT_LDO_STEP;
+
+ if (kvreg->mode == LDO_MODE && get_krait_ldo_uv(kvreg) == ldo_uV)
return 0;
return smp_call_function_single(kvreg->cpu_num,
diff --git a/arch/arm/mach-msm/lpm_levels.c b/arch/arm/mach-msm/lpm_levels.c
index 7128017..bd28131 100644
--- a/arch/arm/mach-msm/lpm_levels.c
+++ b/arch/arm/mach-msm/lpm_levels.c
@@ -82,6 +82,7 @@
static struct lpm_system_state sys_state;
static bool suspend_in_progress;
+static int64_t suspend_time;
struct lpm_lookup_table {
uint32_t modes;
@@ -526,7 +527,7 @@
if (latency_us < pwr->latency_us)
continue;
- if (next_event_us)
+ if (next_event_us) {
if (next_event_us < pwr->latency_us)
continue;
@@ -535,6 +536,7 @@
next_wakeup_us = next_event_us
- pwr->latency_us;
}
+ }
if (next_wakeup_us <= pwr->time_overhead_us)
continue;
@@ -544,11 +546,11 @@
if (!dev->cpu && msm_rpm_waiting_for_ack())
break;
- if ((next_wakeup_us >> 10) > pwr->latency_us) {
+ if ((next_wakeup_us >> 10) > pwr->time_overhead_us) {
power = pwr->ss_power;
} else {
power = pwr->ss_power;
- power -= (pwr->latency_us * pwr->ss_power)
+ power -= (pwr->time_overhead_us * pwr->ss_power)
/ next_wakeup_us;
power += pwr->energy_overhead / next_wakeup_us;
}
@@ -778,6 +780,11 @@
static int lpm_suspend_prepare(void)
{
+ struct timespec ts;
+
+ getnstimeofday(&ts);
+ suspend_time = timespec_to_ns(&ts);
+
suspend_in_progress = true;
msm_mpm_suspend_prepare();
return 0;
@@ -785,6 +792,12 @@
static void lpm_suspend_wake(void)
{
+ struct timespec ts;
+
+ getnstimeofday(&ts);
+ suspend_time = timespec_to_ns(&ts) - suspend_time;
+ msm_pm_add_stat(MSM_PM_STAT_SUSPEND, suspend_time);
+
msm_mpm_suspend_wake();
suspend_in_progress = false;
}
diff --git a/arch/arm/mach-msm/mpm-of.c b/arch/arm/mach-msm/mpm-of.c
index 1fbd077..f1bf64f 100644
--- a/arch/arm/mach-msm/mpm-of.c
+++ b/arch/arm/mach-msm/mpm-of.c
@@ -531,6 +531,7 @@
void msm_mpm_exit_sleep(bool from_idle)
{
unsigned long pending;
+ uint32_t *enabled_intr;
int i;
int k;
@@ -539,12 +540,16 @@
return;
}
+ enabled_intr = from_idle ? msm_mpm_enabled_irq :
+ msm_mpm_wake_irq;
+
for (i = 0; i < MSM_MPM_REG_WIDTH; i++) {
pending = msm_mpm_read(MSM_MPM_REG_STATUS, i);
+ pending &= enabled_intr[i];
if (MSM_MPM_DEBUG_PENDING_IRQ & msm_mpm_debug_mask)
- pr_info("%s: pending.%d: 0x%08lx", __func__,
- i, pending);
+ pr_info("%s: enabled_intr pending.%d: 0x%08x 0x%08lx\n",
+ __func__, i, enabled_intr[i], pending);
k = find_first_bit(&pending, 32);
while (k < 32) {
@@ -678,7 +683,8 @@
return -ENXIO;
}
ret = devm_request_irq(&pdev->dev, dev->mpm_ipc_irq, msm_mpm_irq,
- IRQF_TRIGGER_RISING, pdev->name, msm_mpm_irq);
+ IRQF_TRIGGER_RISING | IRQF_NO_SUSPEND, pdev->name,
+ msm_mpm_irq);
if (ret) {
pr_info("%s(): request_irq failed errno: %d\n", __func__, ret);
diff --git a/arch/arm/mach-msm/msm-pm.c b/arch/arm/mach-msm/msm-pm.c
index f9a9343..cb65a70 100644
--- a/arch/arm/mach-msm/msm-pm.c
+++ b/arch/arm/mach-msm/msm-pm.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2010-2014, The Linux Foundation. All rights reserved.
*
* This 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,10 +25,12 @@
#include <linux/platform_device.h>
#include <linux/of_platform.h>
#include <linux/cpu_pm.h>
+#include <linux/remote_spinlock.h>
#include <asm/uaccess.h>
#include <asm/suspend.h>
#include <asm/cacheflush.h>
#include <asm/outercache.h>
+#include <mach/remote_spinlock.h>
#include <mach/scm.h>
#include <mach/msm_bus.h>
#include <mach/jtag.h>
@@ -117,6 +119,12 @@
DEFINE_PER_CPU(struct clk *, cpu_clks);
static struct clk *l2_clk;
+static int cpu_count;
+static DEFINE_SPINLOCK(cpu_cnt_lock);
+#define SCM_HANDOFF_LOCK_ID "S:7"
+static bool need_scm_handoff_lock;
+static remote_spinlock_t scm_handoff_lock;
+
static void (*msm_pm_disable_l2_fn)(void);
static void (*msm_pm_enable_l2_fn)(void);
static void (*msm_pm_flush_l2_fn)(void);
@@ -478,8 +486,30 @@
static int msm_pm_collapse(unsigned long unused)
{
uint32_t cpu = smp_processor_id();
+ enum msm_pm_l2_scm_flag flag = MSM_SCM_L2_ON;
- if (msm_pm_get_l2_flush_flag() == MSM_SCM_L2_OFF) {
+ spin_lock(&cpu_cnt_lock);
+ cpu_count++;
+ if (cpu_count == num_online_cpus())
+ flag = msm_pm_get_l2_flush_flag();
+
+ pr_debug("cpu:%d cores_in_pc:%d L2 flag: %d\n",
+ cpu, cpu_count, flag);
+
+ /*
+ * The scm_handoff_lock will be release by the secure monitor.
+ * It is used to serialize power-collapses from this point on,
+ * so that both Linux and the secure context have a consistent
+ * view regarding the number of running cpus (cpu_count).
+ *
+ * It must be acquired before releasing cpu_cnt_lock.
+ */
+ if (need_scm_handoff_lock)
+ remote_spin_lock_rlock_id(&scm_handoff_lock,
+ REMOTE_SPINLOCK_TID_START + cpu);
+ spin_unlock(&cpu_cnt_lock);
+
+ if (flag == MSM_SCM_L2_OFF) {
flush_cache_all();
if (msm_pm_flush_l2_fn)
msm_pm_flush_l2_fn();
@@ -491,8 +521,7 @@
msm_pc_inc_debug_count(cpu, MSM_PC_ENTRY_COUNTER);
- scm_call_atomic1(SCM_SVC_BOOT, SCM_CMD_TERMINATE_PC,
- msm_pm_get_l2_flush_flag());
+ scm_call_atomic1(SCM_SVC_BOOT, SCM_CMD_TERMINATE_PC, flag);
msm_pc_inc_debug_count(cpu, MSM_PC_FALLTHRU_COUNTER);
@@ -534,6 +563,12 @@
collapsed = save_cpu_regs ?
!cpu_suspend(0, msm_pm_collapse) : msm_pm_pc_hotplug();
+ if (save_cpu_regs) {
+ spin_lock(&cpu_cnt_lock);
+ cpu_count--;
+ BUG_ON(cpu_count > num_online_cpus());
+ spin_unlock(&cpu_cnt_lock);
+ }
msm_jtag_restore_state();
if (collapsed) {
@@ -764,17 +799,19 @@
pr_info("CPU%u: %s mode:%d\n",
smp_processor_id(), __func__, mode);
- time = sched_clock();
+ if (from_idle)
+ time = sched_clock();
+
if (execute[mode])
exit_stat = execute[mode](from_idle);
- time = sched_clock() - time;
- if (from_idle)
+
+ if (from_idle) {
+ time = sched_clock() - time;
msm_pm_ftrace_lpm_exit(smp_processor_id(), mode, collapsed);
- else
- exit_stat = MSM_PM_STAT_SUSPEND;
- if (exit_stat >= 0)
- msm_pm_add_stat(exit_stat, time);
- do_div(time, 1000);
+ if (exit_stat >= 0)
+ msm_pm_add_stat(exit_stat, time);
+ }
+
return collapsed;
}
@@ -1166,6 +1203,7 @@
struct resource *res = NULL;
int i;
struct msm_pm_init_data_type pdata_local;
+ struct device_node *lpm_node;
int ret = 0;
memset(&pdata_local, 0, sizeof(struct msm_pm_init_data_type));
@@ -1192,6 +1230,23 @@
msm_pc_debug_counters_phys = 0;
}
+ lpm_node = of_parse_phandle(pdev->dev.of_node, "qcom,lpm-levels", 0);
+ if (!lpm_node) {
+ pr_warn("Could not get qcom,lpm-levels handle\n");
+ return -EINVAL;
+ }
+ need_scm_handoff_lock = of_property_read_bool(lpm_node,
+ "qcom,allow-synced-levels");
+ if (need_scm_handoff_lock) {
+ ret = remote_spin_lock_init(&scm_handoff_lock,
+ SCM_HANDOFF_LOCK_ID);
+ if (ret) {
+ pr_err("%s: Failed initializing scm_handoff_lock (%d)\n",
+ __func__, ret);
+ return ret;
+ }
+ }
+
if (pdev->dev.of_node) {
enum msm_pm_pc_mode_type pc_mode;
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 5747f79..8cb3cf3 100644
--- a/arch/arm/mach-msm/msm_bus/msm_bus_bimc.c
+++ b/arch/arm/mach-msm/msm_bus/msm_bus_bimc.c
@@ -1828,6 +1828,53 @@
set_qos_mode(baddr, mas_index, 0, 0, 0);
}
+static void bimc_set_static_qos_bw(struct msm_bus_bimc_info *binfo,
+ int mport, struct msm_bus_bimc_qos_bw *qbw)
+{
+ int32_t bw_mbps, thh = 0, thm, thl, gc;
+ int32_t gp;
+ u64 temp;
+
+ if (binfo->qos_freq == 0) {
+ MSM_BUS_DBG("Zero QoS Frequency\n");
+ return;
+ }
+
+ if (!(qbw->bw && qbw->ws)) {
+ MSM_BUS_DBG("No QoS Bandwidth or Window size\n");
+ return;
+ }
+
+ /* Convert bandwidth to MBPS */
+ temp = qbw->bw;
+ bimc_div(&temp, 1000000);
+ bw_mbps = temp;
+
+ /* Grant period in clock cycles
+ * Grant period from bandwidth structure
+ * is in nano seconds, QoS freq is in KHz.
+ * Divide by 1000 to get clock cycles */
+ gp = (binfo->qos_freq * qbw->gp) / (1000 * NSEC_PER_USEC);
+
+ /* Grant count = BW in MBps * Grant period
+ * in micro seconds */
+ gc = bw_mbps * (qbw->gp / NSEC_PER_USEC);
+
+ /* Medium threshold = -((Medium Threshold percentage *
+ * Grant count) / 100) */
+ thm = -((qbw->thmp * gc) / 100);
+ qbw->thm = thm;
+
+ /* Low threshold = -(Grant count) */
+ thl = -gc;
+ qbw->thl = thl;
+
+ MSM_BUS_DBG("%s: BKE parameters: gp %d, gc %d, thm %d thl %d thh %d",
+ __func__, gp, gc, thm, thl, thh);
+
+ set_qos_bw_regs(binfo->base, mport, thh, thm, thl, gp, gc);
+}
+
static void msm_bus_bimc_config_master(
struct msm_bus_fabric_registration *fab_pdata,
struct msm_bus_inode_info *info,
@@ -1835,6 +1882,7 @@
{
int mode, i, ports;
struct msm_bus_bimc_info *binfo;
+ uint64_t bw = 0;
binfo = (struct msm_bus_bimc_info *)fab_pdata->hw_data;
ports = info->node_info->num_mports;
@@ -1843,11 +1891,17 @@
* Here check the details of dual configuration.
* Take actions based on different modes.
* Check for threshold if limiter mode, etc.
- */
- if (req_clk > info->node_info->th)
- mode = info->node_info->mode_thresh;
- else
+ */
+
+ if (req_clk <= info->node_info->th[0]) {
mode = info->node_info->mode;
+ bw = info->node_info->bimc_bw[0];
+ } else if ((info->node_info->num_thresh > 1) &&
+ (req_clk <= info->node_info->th[1])) {
+ mode = info->node_info->mode;
+ bw = info->node_info->bimc_bw[1];
+ } else
+ mode = info->node_info->mode_thresh;
switch (mode) {
case BIMC_QOS_MODE_BYPASS:
@@ -1858,9 +1912,24 @@
break;
case BIMC_QOS_MODE_REGULATOR:
case BIMC_QOS_MODE_LIMITER:
- for (i = 0; i < ports; i++)
+ for (i = 0; i < ports; i++) {
+ /* If not in fixed mode, update bandwidth */
+ if ((info->node_info->cur_lim_bw != bw)
+ && (mode != BIMC_QOS_MODE_FIXED)) {
+ struct msm_bus_bimc_qos_bw qbw;
+ qbw.ws = info->node_info->ws;
+ qbw.bw = bw;
+ qbw.gp = info->node_info->bimc_gp;
+ qbw.thmp = info->node_info->bimc_thmp;
+ bimc_set_static_qos_bw(binfo,
+ info->node_info->qport[i], &qbw);
+ info->node_info->cur_lim_bw = bw;
+ MSM_BUS_DBG("%s: Qos is %d reqclk %llu bw %llu",
+ __func__, mode, req_clk, bw);
+ }
bke_switch(binfo->base, info->node_info->qport[i],
BKE_ON, mode);
+ }
break;
default:
break;
@@ -1964,52 +2033,6 @@
return 0;
}
-static void bimc_set_static_qos_bw(struct msm_bus_bimc_info *binfo,
- int mport, struct msm_bus_bimc_qos_bw *qbw)
-{
- int32_t bw_mbps, thh = 0, thm, thl, gc;
- int32_t gp;
- u64 temp;
-
- if (binfo->qos_freq == 0) {
- MSM_BUS_DBG("Zero QoS Frequency\n");
- return;
- }
-
- if (!(qbw->bw && qbw->ws)) {
- MSM_BUS_DBG("No QoS Bandwidth or Window size\n");
- return;
- }
-
- /* Convert bandwidth to MBPS */
- temp = qbw->bw;
- bimc_div(&temp, 1000000);
- bw_mbps = temp;
-
- /* Grant period in clock cycles
- * Grant period from bandwidth structure
- * is in nano seconds, QoS freq is in KHz.
- * Divide by 1000 to get clock cycles */
- gp = (binfo->qos_freq * qbw->gp) / (1000 * NSEC_PER_USEC);
-
- /* Grant count = BW in MBps * Grant period
- * in micro seconds */
- gc = bw_mbps * (qbw->gp / NSEC_PER_USEC);
-
- /* Medium threshold = -((Medium Threshold percentage *
- * Grant count) / 100) */
- thm = -((qbw->thmp * gc) / 100);
- qbw->thm = thm;
-
- /* Low threshold = -(Grant count) */
- thl = -gc;
- qbw->thl = thl;
-
- MSM_BUS_DBG("%s: BKE parameters: gp %d, gc %d, thm %d thl %d thh %d",
- __func__, gp, gc, thm, thl, thh);
-
- set_qos_bw_regs(binfo->base, mport, thh, thm, thl, gp, gc);
-}
static void bimc_init_mas_reg(struct msm_bus_bimc_info *binfo,
struct msm_bus_inode_info *info,
@@ -2048,7 +2071,7 @@
if (mode != BIMC_QOS_MODE_FIXED) {
struct msm_bus_bimc_qos_bw qbw;
qbw.ws = info->node_info->ws;
- qbw.bw = info->node_info->bimc_bw;
+ qbw.bw = info->node_info->bimc_bw[0];
qbw.gp = info->node_info->bimc_gp;
qbw.thmp = info->node_info->bimc_thmp;
bimc_set_static_qos_bw(binfo,
@@ -2081,9 +2104,11 @@
* If the master supports dual configuration,
* configure registers for both modes
*/
- if (info->node_info->dual_conf)
+ if (info->node_info->dual_conf) {
bimc_init_mas_reg(binfo, info, qmode,
info->node_info->mode_thresh);
+ info->node_info->cur_lim_bw = 0;
+ }
bimc_init_mas_reg(binfo, info, qmode, info->node_info->mode);
return 0;
diff --git a/arch/arm/mach-msm/msm_bus/msm_bus_core.h b/arch/arm/mach-msm/msm_bus/msm_bus_core.h
index 7e4a513..557bcca 100644
--- a/arch/arm/mach-msm/msm_bus/msm_bus_core.h
+++ b/arch/arm/mach-msm/msm_bus/msm_bus_core.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2014 The Linux Foundation. All rights reserved.
*
* This 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,10 +82,12 @@
unsigned int prio_wr;
unsigned int prio1;
unsigned int prio0;
- u64 th;
+ unsigned int num_thresh;
+ u64 *th;
+ u64 cur_lim_bw;
unsigned int mode_thresh;
bool dual_conf;
- u64 bimc_bw;
+ u64 *bimc_bw;
u32 bimc_gp;
u32 bimc_thmp;
const char *name;
diff --git a/arch/arm/mach-msm/msm_bus/msm_bus_fabric.c b/arch/arm/mach-msm/msm_bus/msm_bus_fabric.c
index 626c5e8..0ffc194 100644
--- a/arch/arm/mach-msm/msm_bus/msm_bus_fabric.c
+++ b/arch/arm/mach-msm/msm_bus/msm_bus_fabric.c
@@ -364,7 +364,7 @@
{
struct msm_bus_fabric *fabric = to_msm_bus_fabric(fabdev);
void *sel_cdata;
- long rounded_rate;
+ long rounded_rate, cur_rate;
sel_cdata = fabric->cdata[ctx];
@@ -379,16 +379,20 @@
}
/* Enable clocks before accessing QoS registers */
- if (fabric->info.nodeclk[DUAL_CTX].clk)
+ if (fabric->info.nodeclk[DUAL_CTX].clk) {
if (fabric->info.nodeclk[DUAL_CTX].rate == 0) {
- rounded_rate = clk_round_rate(fabric->
- info.nodeclk[DUAL_CTX].clk, 1);
+ cur_rate = clk_get_rate(
+ fabric->info.nodeclk[DUAL_CTX].clk);
+ rounded_rate = clk_round_rate(
+ fabric->info.nodeclk[DUAL_CTX].clk,
+ cur_rate ? cur_rate : 1);
if (clk_set_rate(fabric->info.nodeclk[DUAL_CTX].clk,
rounded_rate))
MSM_BUS_ERR("Error: clk: en: Node: %d rate: %ld",
fabric->fabdev.id, rounded_rate);
clk_prepare_enable(fabric->info.nodeclk[DUAL_CTX].clk);
+ }
}
if (info->iface_clk.clk)
@@ -514,22 +518,26 @@
struct msm_bus_inode_info *info, uint64_t req_clk, uint64_t req_bw)
{
struct msm_bus_fabric *fabric = to_msm_bus_fabric(fabdev);
- long rounded_rate;
+ long rounded_rate, cur_rate;
if (fabdev->hw_algo.config_master == NULL)
return;
/* Enable clocks before accessing QoS registers */
- if (fabric->info.nodeclk[DUAL_CTX].clk)
+ if (fabric->info.nodeclk[DUAL_CTX].clk) {
if (fabric->info.nodeclk[DUAL_CTX].rate == 0) {
- rounded_rate = clk_round_rate(fabric->
- info.nodeclk[DUAL_CTX].clk, 1);
+ cur_rate = clk_get_rate(
+ fabric->info.nodeclk[DUAL_CTX].clk);
+ rounded_rate = clk_round_rate(
+ fabric->info.nodeclk[DUAL_CTX].clk,
+ cur_rate ? cur_rate : 1);
if (clk_set_rate(fabric->info.nodeclk[DUAL_CTX].clk,
rounded_rate))
MSM_BUS_ERR("Error: clk: en: Node: %d rate: %ld",
fabric->fabdev.id, rounded_rate);
clk_prepare_enable(fabric->info.nodeclk[DUAL_CTX].clk);
+ }
}
if (info->iface_clk.clk)
diff --git a/arch/arm/mach-msm/msm_bus/msm_bus_of.c b/arch/arm/mach-msm/msm_bus/msm_bus_of.c
index 52195c7..f857920 100644
--- a/arch/arm/mach-msm/msm_bus/msm_bus_of.c
+++ b/arch/arm/mach-msm/msm_bus/msm_bus_of.c
@@ -271,6 +271,54 @@
return NULL;
}
+static u64 *get_th_params(struct platform_device *pdev,
+ const struct device_node *node, const char *prop,
+ int *nports)
+{
+ int size = 0, ret;
+ u64 *ret_arr = NULL;
+ int *arr = NULL;
+ int i;
+
+ if (of_get_property(node, prop, &size)) {
+ *nports = size / sizeof(int);
+ } else {
+ pr_debug("Property %s not available\n", prop);
+ *nports = 0;
+ return NULL;
+ }
+
+ ret_arr = devm_kzalloc(&pdev->dev, (*nports * sizeof(u64)),
+ GFP_KERNEL);
+ arr = kzalloc(size, GFP_KERNEL);
+ if ((size > 0) && (ZERO_OR_NULL_PTR(arr)
+ || ZERO_OR_NULL_PTR(ret_arr))) {
+ pr_err("Error: Failed to alloc mem for %s\n", prop);
+ return NULL;
+ }
+
+ ret = of_property_read_u32_array(node, prop, (u32 *)arr, *nports);
+ if (ret) {
+ pr_err("Error in reading property: %s\n", prop);
+ goto err;
+ }
+
+ for (i = 0; i < *nports; i++)
+ ret_arr[i] = (uint64_t)KBTOB(arr[i]);
+
+ MSM_BUS_DBG("%s: num entries %d prop %s", __func__, *nports, prop);
+
+ for (i = 0; i < *nports; i++)
+ MSM_BUS_DBG("Th %d val %llu", i, ret_arr[i]);
+
+ kfree(arr);
+ return ret_arr;
+err:
+ devm_kfree(&pdev->dev, arr);
+ devm_kfree(&pdev->dev, ret_arr);
+ return NULL;
+}
+
static struct msm_bus_node_info *get_nodes(struct device_node *of_node,
struct platform_device *pdev,
struct msm_bus_fabric_registration *pdata)
@@ -278,7 +326,7 @@
struct msm_bus_node_info *info;
struct device_node *child_node = NULL;
int i = 0, ret;
- u32 temp;
+ int num_bw = 0;
for_each_child_of_node(of_node, child_node) {
i++;
@@ -353,36 +401,29 @@
of_property_read_u32(child_node, "qcom,buswidth",
&info[i].buswidth);
of_property_read_u32(child_node, "qcom,ws", &info[i].ws);
- ret = of_property_read_u32(child_node, "qcom,thresh",
- &temp);
- if (!ret)
- info[i].th = (uint64_t)KBTOB(temp);
- ret = of_property_read_u32(child_node, "qcom,bimc,bw",
- &temp);
- if (!ret)
- info[i].bimc_bw = (uint64_t)KBTOB(temp);
+ info[i].dual_conf =
+ of_property_read_bool(child_node, "qcom,dual-conf");
+
+
+ info[i].th = get_th_params(pdev, child_node, "qcom,thresh",
+ &info[i].num_thresh);
+
+ info[i].bimc_bw = get_th_params(pdev, child_node,
+ "qcom,bimc,bw", &num_bw);
+
+ if (num_bw != info[i].num_thresh) {
+ pr_err("%s:num_bw %d must equal num_thresh %d",
+ __func__, num_bw, info[i].num_thresh);
+ pr_err("%s:Err setting up dual conf for %s",
+ __func__, info[i].name);
+ goto err;
+ }
of_property_read_u32(child_node, "qcom,bimc,gp",
&info[i].bimc_gp);
of_property_read_u32(child_node, "qcom,bimc,thmp",
&info[i].bimc_thmp);
- ret = of_property_read_string(child_node, "qcom,mode",
- &sel_str);
- if (ret)
- info[i].mode = 0;
- else {
- ret = get_num(mode_sel_name, sel_str);
- if (ret < 0) {
- pr_err("Unknown mode :%s\n", sel_str);
- goto err;
- }
-
- info[i].mode = ret;
- }
-
- info[i].dual_conf =
- of_property_read_bool(child_node, "qcom,dual-conf");
ret = of_property_read_string(child_node, "qcom,mode-thresh",
&sel_str);
@@ -397,7 +438,21 @@
info[i].mode_thresh = ret;
MSM_BUS_DBG("AXI: THreshold mode set: %d\n",
- info[i].mode_thresh);
+ info[i].mode_thresh);
+ }
+
+ ret = of_property_read_string(child_node, "qcom,mode",
+ &sel_str);
+ if (ret)
+ info[i].mode = 0;
+ else {
+ ret = get_num(mode_sel_name, sel_str);
+ if (ret < 0) {
+ pr_err("Unknown mode :%s\n", sel_str);
+ goto err;
+ }
+
+ info[i].mode = ret;
}
ret = of_property_read_string(child_node, "qcom,perm-mode",
diff --git a/arch/arm/mach-msm/ocmem_core.c b/arch/arm/mach-msm/ocmem_core.c
index c186a5e..f753391 100644
--- a/arch/arm/mach-msm/ocmem_core.c
+++ b/arch/arm/mach-msm/ocmem_core.c
@@ -51,6 +51,7 @@
static struct ocmem_hw_region *region_ctrl;
static struct mutex region_ctrl_lock;
static void *ocmem_base;
+static void *ocmem_vbase;
#define OCMEM_V1_MACROS 8
#define OCMEM_V1_MACRO_SZ (SZ_64K)
@@ -562,6 +563,13 @@
ocmem_write(0x0, ocmem_base + OC_GFX_MPU_END);
}
+int ocmem_clear(unsigned long start, unsigned long size)
+{
+ memset((ocmem_vbase + start), 0x4D4D434F, size);
+ mb();
+ return 0;
+}
+
static int do_lock(enum ocmem_client id, unsigned long offset,
unsigned long len, enum region_mode mode)
{
@@ -1144,6 +1152,7 @@
pdata = platform_get_drvdata(pdev);
ocmem_base = pdata->reg_base;
+ ocmem_vbase = pdata->vbase;
rc = ocmem_enable_core_clock();
diff --git a/arch/arm/mach-msm/ocmem_rdm.c b/arch/arm/mach-msm/ocmem_rdm.c
index 4ff7212..9eac050 100644
--- a/arch/arm/mach-msm/ocmem_rdm.c
+++ b/arch/arm/mach-msm/ocmem_rdm.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
*
* This 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,38 +149,6 @@
return IRQ_HANDLED;
}
-#ifdef CONFIG_MSM_OCMEM_NONSECURE
-int ocmem_clear(unsigned long start, unsigned long size)
-{
- INIT_COMPLETION(dm_clear_event);
- /* Clear DM Mask */
- ocmem_write(DM_MASK_RESET, dm_base + DM_INTR_MASK);
- /* Clear DM Interrupts */
- ocmem_write(DM_INTR_RESET, dm_base + DM_INTR_CLR);
- /* DM CLR offset */
- ocmem_write(start, dm_base + DM_CLR_OFFSET);
- /* DM CLR size */
- ocmem_write(size, dm_base + DM_CLR_SIZE);
- /* Wipe out memory as "OCMM" */
- ocmem_write(0x4D4D434F, dm_base + DM_CLR_PATTERN);
- /* The offset, size and pattern for clearing must be set
- * before triggering the clearing engine
- */
- mb();
- /* Trigger Data Clear */
- ocmem_write(DM_CLR_ENABLE, dm_base + DM_CLR_TRIGGER);
-
- wait_for_completion(&dm_clear_event);
-
- return 0;
-}
-#else
-int ocmem_clear(unsigned long start, unsigned long size)
-{
- return 0;
-}
-#endif
-
/* Lock during transfers */
int ocmem_rdm_transfer(int id, struct ocmem_map_list *clist,
unsigned long start, int direction)
diff --git a/arch/arm/mach-msm/qdsp6v2/Makefile b/arch/arm/mach-msm/qdsp6v2/Makefile
index 6bd3efb..3d7638d 100644
--- a/arch/arm/mach-msm/qdsp6v2/Makefile
+++ b/arch/arm/mach-msm/qdsp6v2/Makefile
@@ -12,7 +12,7 @@
obj-$(CONFIG_FB_MSM_HDMI_MSM_PANEL) += lpa_if_hdmi.o
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 dsp_debug.o
+obj-$(CONFIG_MSM_QDSP6_APRV2) += apr.o apr_v2.o apr_tal.o dsp_debug.o voice_svc.o
ifdef CONFIG_ARCH_MSM9615
obj-y += audio_acdb.o
obj-y += rtac.o
diff --git a/arch/arm/mach-msm/qdsp6v2/apr.c b/arch/arm/mach-msm/qdsp6v2/apr.c
index 8d9ad29..937eeda 100644
--- a/arch/arm/mach-msm/qdsp6v2/apr.c
+++ b/arch/arm/mach-msm/qdsp6v2/apr.c
@@ -436,7 +436,7 @@
if (data.payload_size > 0)
data.payload = (char *)hdr + hdr_size;
- temp_port = ((data.src_port >> 8) * 8) + (data.src_port & 0xFF);
+ temp_port = ((data.dest_port >> 8) * 8) + (data.dest_port & 0xFF);
pr_debug("port = %d t_port = %d\n", data.src_port, temp_port);
if (c_svc->port_cnt && c_svc->port_fn[temp_port])
c_svc->port_fn[temp_port](&data, c_svc->port_priv[temp_port]);
diff --git a/arch/arm/mach-msm/qdsp6v2/msm_audio_ion.c b/arch/arm/mach-msm/qdsp6v2/msm_audio_ion.c
index 399e073..df7760a 100644
--- a/arch/arm/mach-msm/qdsp6v2/msm_audio_ion.c
+++ b/arch/arm/mach-msm/qdsp6v2/msm_audio_ion.c
@@ -344,7 +344,7 @@
pr_err("%s: ion import dma buffer failed\n",
__func__);
rc = -EINVAL;
- goto err_destroy_client;
+ goto err;
}
if (ionflag != NULL) {
@@ -380,10 +380,6 @@
err_ion_handle:
ion_free(client, *handle);
-err_destroy_client:
- msm_audio_ion_client_destroy(client);
- client = NULL;
- *handle = NULL;
err:
return rc;
}
diff --git a/arch/arm/mach-msm/qdsp6v2/voice_svc.c b/arch/arm/mach-msm/qdsp6v2/voice_svc.c
new file mode 100644
index 0000000..92b3003
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp6v2/voice_svc.c
@@ -0,0 +1,593 @@
+/* Copyright (c) 2014, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/cdev.h>
+#include <sound/voice_svc.h>
+#include <mach/qdsp6v2/apr_tal.h>
+#include <mach/qdsp6v2/apr.h>
+
+#define DRIVER_NAME "voice_svc"
+#define MINOR_NUMBER 1
+#define APR_MAX_RESPONSE 10
+
+#define MAX(a, b) ((a) >= (b) ? (a) : (b))
+
+struct voice_svc_device {
+ struct cdev *cdev;
+ struct device *dev;
+ int major;
+};
+
+struct voice_svc_prvt {
+ void* apr_q6_mvm;
+ void* apr_q6_cvs;
+ uint16_t response_count;
+ struct list_head response_queue;
+ wait_queue_head_t response_wait;
+ spinlock_t response_lock;
+};
+
+struct apr_data {
+ struct apr_hdr hdr;
+ __u8 payload[0];
+} __packed;
+
+struct apr_response_list {
+ struct list_head list;
+ struct voice_svc_cmd_response resp;
+};
+
+static struct voice_svc_device *voice_svc_dev;
+static struct class *voice_svc_class;
+dev_t device_num;
+
+static int32_t qdsp_apr_callback(struct apr_client_data *data, void *priv)
+{
+ struct voice_svc_prvt *prtd;
+ struct apr_response_list *response_list;
+ unsigned long spin_flags;
+
+ if ((data == NULL) || (priv == NULL)) {
+ pr_err("%s: data or priv is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ prtd = (struct voice_svc_prvt*)priv;
+
+ pr_debug("%s: data->opcode %x\n", __func__,
+ data->opcode);
+
+ if (data->opcode == RESET_EVENTS) {
+ if (data->reset_proc == APR_DEST_QDSP6) {
+ pr_debug("%s: Received reset event\n", __func__);
+
+ if (prtd->apr_q6_mvm != NULL) {
+ apr_reset(prtd->apr_q6_mvm);
+ prtd->apr_q6_mvm = NULL;
+ }
+
+ if (prtd->apr_q6_cvs != NULL) {
+ apr_reset(prtd->apr_q6_cvs);
+ prtd->apr_q6_cvs = NULL;
+ }
+ } else if (data->reset_proc ==APR_DEST_MODEM) {
+ pr_debug("%s: Received Modem reset event\n", __func__);
+ }
+ }
+
+ spin_lock_irqsave(&prtd->response_lock, spin_flags);
+
+ if (prtd->response_count < APR_MAX_RESPONSE) {
+ response_list = (struct apr_response_list *)kmalloc(
+ sizeof(struct apr_response_list) + data->payload_size,
+ GFP_ATOMIC);
+ if (response_list == NULL) {
+ pr_err("%s: kmalloc failed\n", __func__);
+
+ return -ENOMEM;
+ }
+
+ response_list->resp.src_port = data->src_port;
+ response_list->resp.dest_port = ((data->dest_port) >> 8);
+ response_list->resp.token = data->token;
+ response_list->resp.opcode = data->opcode;
+ response_list->resp.payload_size = data->payload_size;
+ if (data->payload != NULL && data->payload_size > 0) {
+ memcpy(response_list->resp.payload, data->payload,
+ data->payload_size);
+ }
+
+ list_add_tail(&response_list->list, &prtd->response_queue);
+ prtd->response_count++;
+
+ wake_up(&prtd->response_wait);
+ } else {
+ pr_err("%s: Response dropped since the queue is full\n", __func__);
+ }
+
+ spin_unlock_irqrestore(&prtd->response_lock, spin_flags);
+
+ return 0;
+}
+
+static void voice_svc_update_hdr(struct voice_svc_cmd_request* apr_req_data,
+ struct apr_data *aprdata,
+ struct voice_svc_prvt *prtd)
+{
+
+ aprdata->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, \
+ APR_HDR_LEN(sizeof(struct apr_hdr)),\
+ APR_PKT_VER);
+ aprdata->hdr.src_port = ((apr_req_data->src_port) << 8 | 0x0001);
+ aprdata->hdr.dest_port = apr_req_data->dest_port;
+ aprdata->hdr.token = apr_req_data->token;
+ aprdata->hdr.opcode = apr_req_data->opcode;
+ aprdata->hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ apr_req_data->payload_size);
+ memcpy(aprdata->payload, apr_req_data->payload,
+ apr_req_data->payload_size);
+}
+
+static int voice_svc_send_req(struct voice_svc_cmd_request *apr_request,
+ struct voice_svc_prvt *prtd)
+{
+ int ret = 0;
+ void *apr_handle = NULL;
+ struct apr_data *aprdata = NULL;
+ uint32_t user_payload_size = 0;
+
+ if (apr_request == NULL) {
+ pr_err("%s: apr_request is NULL\n", __func__);
+
+ ret = -EINVAL;
+ goto done;
+ }
+
+ user_payload_size = apr_request->payload_size;
+
+ aprdata = kmalloc(sizeof(struct apr_data) + user_payload_size,
+ GFP_KERNEL);
+
+ if (aprdata == NULL) {
+ pr_err("%s: aprdata kmalloc failed.", __func__);
+
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ voice_svc_update_hdr(apr_request, aprdata, prtd);
+
+ if (!strncmp(apr_request->svc_name, VOICE_SVC_CVS_STR,
+ MAX(sizeof(apr_request->svc_name), sizeof(VOICE_SVC_CVS_STR)))) {
+ apr_handle = prtd->apr_q6_cvs;
+ } else if (!strncmp(apr_request->svc_name, VOICE_SVC_MVM_STR,
+ MAX(sizeof(apr_request->svc_name), sizeof(VOICE_SVC_MVM_STR)))) {
+ apr_handle = prtd->apr_q6_mvm;
+ } else {
+ pr_err("%s: Invalid service %s\n", __func__,
+ apr_request->svc_name);
+
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ret = apr_send_pkt(apr_handle, (uint32_t *)aprdata);
+
+ if (ret < 0) {
+ pr_err("%s: Fail in sending SNDRV_VOICE_SVC_REQUEST\n",
+ __func__);
+ ret = -EINVAL;
+ } else {
+ pr_debug("%s: apr packet sent successfully %d\n",
+ __func__, ret);
+ ret = 0;
+ }
+
+done:
+ if (aprdata != NULL)
+ kfree(aprdata);
+
+ return ret;
+}
+static int voice_svc_reg(char *svc, uint32_t src_port,
+ struct voice_svc_prvt *prtd, void **handle)
+{
+ int ret = 0;
+
+ if (handle == NULL) {
+ pr_err("%s: handle is NULL\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (*handle != NULL) {
+ pr_err("%s: svc handle not NULL\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ *handle = apr_register("ADSP",
+ svc, qdsp_apr_callback,
+ ((src_port) << 8 | 0x0001),
+ prtd);
+
+ if (*handle == NULL) {
+ pr_err("%s: Unable to register %s\n",
+ __func__, svc);
+
+ ret = -EFAULT;
+ goto done;
+ }
+ pr_debug("%s: register %s successful\n",
+ __func__, svc);
+done:
+ return ret;
+}
+
+static int voice_svc_dereg(char *svc, void **handle)
+{
+ int ret = 0;
+ if (handle == NULL) {
+ pr_err("%s: handle is NULL\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ apr_deregister(*handle);
+ *handle = NULL;
+ pr_debug("%s: deregister %s successful\n",
+ __func__, svc);
+
+done:
+ return 0;
+}
+
+static int process_reg_cmd(struct voice_svc_register apr_reg_svc,
+ struct voice_svc_prvt *prtd)
+{
+ int ret = 0;
+ char *svc = NULL;
+ void **handle = NULL;
+
+ if (!strncmp(apr_reg_svc.svc_name, VOICE_SVC_MVM_STR,
+ MAX(sizeof(apr_reg_svc.svc_name), sizeof(VOICE_SVC_MVM_STR)))) {
+ svc = VOICE_SVC_MVM_STR;
+ handle = &prtd->apr_q6_mvm;
+ } else if (!strncmp(apr_reg_svc.svc_name, VOICE_SVC_CVS_STR,
+ MAX(sizeof(apr_reg_svc.svc_name), sizeof(VOICE_SVC_CVS_STR)))) {
+ svc = VOICE_SVC_CVS_STR;
+ handle = &prtd->apr_q6_cvs;
+ } else {
+ pr_err("%s: Invalid Service: %s\n", __func__,
+ apr_reg_svc.svc_name);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (*handle == NULL &&
+ apr_reg_svc.reg_flag) {
+ ret = voice_svc_reg(svc, apr_reg_svc.src_port, prtd,
+ handle);
+ } else if (handle != NULL &&
+ !apr_reg_svc.reg_flag) {
+ ret = voice_svc_dereg(svc, handle);
+ }
+
+done:
+ return ret;
+}
+
+static long voice_svc_ioctl(struct file *file, unsigned int cmd,
+ unsigned long u_arg)
+{
+ int ret = 0;
+ struct voice_svc_prvt *prtd;
+ struct voice_svc_register apr_reg_svc;
+ struct voice_svc_cmd_request *apr_request = NULL;
+ struct voice_svc_cmd_response *apr_response = NULL;
+ struct apr_response_list *resp;
+ void __user *arg = (void __user *)u_arg;
+ uint32_t user_payload_size = 0;
+ unsigned long spin_flags;
+
+ pr_debug("%s: cmd: %u\n", __func__, cmd);
+
+ prtd = (struct voice_svc_prvt*)file->private_data;
+
+ switch (cmd) {
+ case SNDRV_VOICE_SVC_REGISTER_SVC:
+ pr_debug("%s: size of struct: %d\n", __func__,
+ sizeof(apr_reg_svc));
+ if (copy_from_user(&apr_reg_svc, arg, sizeof(apr_reg_svc))) {
+ pr_err("%s: copy_from_user failed\n", __func__);
+
+ ret = -EFAULT;
+ goto done;
+ }
+
+ ret = process_reg_cmd(apr_reg_svc, prtd);
+
+ break;
+ case SNDRV_VOICE_SVC_CMD_REQUEST:
+ if (!access_ok(VERIFY_READ, arg,
+ sizeof(struct voice_svc_cmd_request))) {
+ pr_err("%s: Unable to read user data", __func__);
+
+ ret = -EFAULT;
+ goto done;
+ }
+
+ user_payload_size =
+ ((struct voice_svc_cmd_request*)arg)->payload_size;
+
+ apr_request = kmalloc(sizeof(struct voice_svc_cmd_request) +
+ user_payload_size, GFP_KERNEL);
+
+ if (apr_request == NULL) {
+ pr_err("%s: apr_request kmalloc failed.", __func__);
+
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ if (copy_from_user(apr_request, arg,
+ sizeof(struct voice_svc_cmd_request) +
+ user_payload_size)) {
+ pr_err("%s: copy from user failed, size %d\n", __func__,
+ sizeof(struct voice_svc_cmd_request) +
+ user_payload_size);
+
+ ret = -EFAULT;
+ goto done;
+ }
+
+ ret = voice_svc_send_req(apr_request, prtd);
+
+ break;
+
+ case SNDRV_VOICE_SVC_CMD_RESPONSE:
+ do {
+ if (!access_ok(VERIFY_READ, arg,
+ sizeof(struct voice_svc_cmd_response))) {
+ pr_err("%s: Unable to read user data",
+ __func__);
+
+ ret = -EFAULT;
+ goto done;
+ }
+
+ user_payload_size =
+ ((struct voice_svc_cmd_response*)arg)->payload_size;
+ pr_debug("%s: RESPONSE: user payload size %d",
+ __func__, user_payload_size);
+
+ spin_lock_irqsave(&prtd->response_lock, spin_flags);
+ if (!list_empty(&prtd->response_queue)) {
+ resp = list_first_entry(&prtd->response_queue,
+ struct apr_response_list, list);
+
+ if (user_payload_size <
+ resp->resp.payload_size) {
+ pr_err("%s: Invalid payload size %d,%d",
+ __func__, user_payload_size,
+ resp->resp.payload_size);
+ ret = -ENOMEM;
+ spin_unlock_irqrestore(
+ &prtd->response_lock,
+ spin_flags);
+ goto done;
+ }
+
+ if (!access_ok(VERIFY_WRITE, arg,
+ sizeof(struct voice_svc_cmd_response) +
+ resp->resp.payload_size)) {
+ ret = -EFAULT;
+ spin_unlock_irqrestore(
+ &prtd->response_lock,
+ spin_flags);
+ goto done;
+ }
+
+ if (copy_to_user(arg, &resp->resp,
+ sizeof(struct voice_svc_cmd_response) +
+ resp->resp.payload_size)) {
+ pr_err("%s: copy to user failed, size \
+ %d\n", __func__,
+ sizeof(struct voice_svc_cmd_response) +
+ resp->resp.payload_size);
+
+ ret = -EFAULT;
+ spin_unlock_irqrestore(
+ &prtd->response_lock,
+ spin_flags);
+ goto done;
+ }
+
+ prtd->response_count--;
+
+ list_del(&resp->list);
+ kfree(resp);
+ spin_unlock_irqrestore(&prtd->response_lock,
+ spin_flags);
+ goto done;
+ } else {
+ spin_unlock_irqrestore(&prtd->response_lock,
+ spin_flags);
+ wait_event_interruptible(prtd->response_wait,
+ !list_empty(&prtd->response_queue));
+ pr_debug("%s: Interupt recieved for response",
+ __func__);
+ }
+ } while(!apr_response);
+ break;
+ default:
+ pr_debug("%s: cmd: %u\n", __func__, cmd);
+ ret = -EINVAL;
+ }
+
+done:
+ if (apr_request != NULL)
+ kfree(apr_request);
+
+ return ret;
+}
+
+static int voice_svc_open(struct inode *inode, struct file *file)
+{
+ struct voice_svc_prvt *prtd = NULL;
+
+ prtd = kmalloc(sizeof(struct voice_svc_prvt), GFP_KERNEL);
+
+ if (prtd == NULL) {
+ pr_err("%s: kmalloc failed", __func__);
+
+ return -ENOMEM;
+ }
+
+ memset(prtd, 0, sizeof(struct voice_svc_prvt));
+ prtd->apr_q6_cvs = NULL;
+ prtd->apr_q6_mvm = NULL;
+ prtd->response_count = 0;
+
+ INIT_LIST_HEAD(&prtd->response_queue);
+ init_waitqueue_head(&prtd->response_wait);
+ spin_lock_init(&prtd->response_lock);
+
+ file->private_data = (void*)prtd;
+
+ return 0;
+}
+
+static int voice_svc_release(struct inode *inode, struct file *file)
+{
+ kfree(file->private_data);
+ return 0;
+}
+
+static const struct file_operations voice_svc_fops = {
+ .owner = THIS_MODULE,
+ .open = voice_svc_open,
+ .unlocked_ioctl = voice_svc_ioctl,
+ .release = voice_svc_release,
+};
+
+
+static int voice_svc_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+
+ voice_svc_dev = devm_kzalloc(&pdev->dev, sizeof(struct voice_svc_device),
+ GFP_KERNEL);
+ if (!voice_svc_dev) {
+ pr_err("%s: kzalloc failed\n", __func__);
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ ret = alloc_chrdev_region(&device_num, 0, MINOR_NUMBER, DRIVER_NAME);
+ if (ret) {
+ pr_err("%s: Failed to alloc chrdev\n", __func__);
+ ret = -ENODEV;
+ goto done;
+ }
+
+ voice_svc_dev->major = MAJOR(device_num);
+ voice_svc_class = class_create(THIS_MODULE, DRIVER_NAME);
+ if (IS_ERR(voice_svc_class)) {
+ ret = PTR_ERR(voice_svc_class);
+ pr_err("%s: Failed to create class; err = %d\n", __func__,
+ ret);
+ goto class_err;
+ }
+
+ voice_svc_dev->dev = device_create(voice_svc_class, NULL, device_num,
+ NULL, DRIVER_NAME);
+ if (IS_ERR(voice_svc_dev->dev)) {
+ ret = PTR_ERR(voice_svc_dev->dev);
+ pr_err("%s: Failed to create device; err = %d\n", __func__,
+ ret);
+ goto dev_err;
+ }
+
+ voice_svc_dev->cdev = cdev_alloc();
+ cdev_init(voice_svc_dev->cdev, &voice_svc_fops);
+ ret = cdev_add(voice_svc_dev->cdev, device_num, MINOR_NUMBER);
+ if (ret) {
+ pr_err("%s: Failed to register chrdev; err = %d\n", __func__,
+ ret);
+ goto add_err;
+ }
+ pr_debug("%s: Device created\n", __func__);
+ goto done;
+
+add_err:
+ cdev_del(voice_svc_dev->cdev);
+ device_destroy(voice_svc_class, device_num);
+dev_err:
+ class_destroy(voice_svc_class);
+class_err:
+ unregister_chrdev_region(0, MINOR_NUMBER);
+done:
+ return ret;
+}
+
+static int voice_svc_remove(struct platform_device *pdev)
+{
+ cdev_del(voice_svc_dev->cdev);
+ kfree(voice_svc_dev->cdev);
+ device_destroy(voice_svc_class, device_num);
+ class_destroy(voice_svc_class);
+ unregister_chrdev_region(0, MINOR_NUMBER);
+ kfree(voice_svc_dev);
+
+ return 0;
+}
+
+static struct of_device_id voice_svc_of_match[] = {
+ {.compatible = "qcom,msm-voice-svc"},
+ { }
+};
+MODULE_DEVICE_TABLE(of, voice_svc_of_match);
+
+static struct platform_driver voice_svc_driver = {
+ .probe = voice_svc_probe,
+ .remove = voice_svc_remove,
+ .driver = {
+ .name = "msm-voice-svc",
+ .owner = THIS_MODULE,
+ .of_match_table = voice_svc_of_match,
+ },
+};
+
+static int __init voice_svc_init(void)
+{
+ return platform_driver_register(&voice_svc_driver);
+}
+
+static void __exit voice_svc_exit(void)
+{
+ platform_driver_unregister(&voice_svc_driver);
+}
+
+module_init(voice_svc_init);
+module_exit(voice_svc_exit);
+
+MODULE_DESCRIPTION("Soc QDSP6v2 Audio APR driver");
+MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/mach-msm/smd_tty.c b/arch/arm/mach-msm/smd_tty.c
index 428d5b0..9cb26e1 100644
--- a/arch/arm/mach-msm/smd_tty.c
+++ b/arch/arm/mach-msm/smd_tty.c
@@ -551,7 +551,7 @@
static void smd_tty_close(struct tty_struct *tty, struct file *f)
{
- struct smd_tty_info *info = tty->driver_data;
+ struct smd_tty_info *info = smd_tty + tty->index;
tty_port_close(&info->port, tty, f);
}
diff --git a/arch/arm/mach-msm/socinfo.c b/arch/arm/mach-msm/socinfo.c
index ab03712..8e7adba 100644
--- a/arch/arm/mach-msm/socinfo.c
+++ b/arch/arm/mach-msm/socinfo.c
@@ -21,10 +21,12 @@
#include <linux/sys_soc.h>
#include <linux/slab.h>
#include <linux/stat.h>
+#include <linux/string.h>
#include <linux/sysdev.h>
#include <linux/types.h>
#include <asm/mach-types.h>
+#include <asm/system_misc.h>
#include <mach/socinfo.h>
#include <mach/msm_smem.h>
@@ -185,246 +187,247 @@
struct socinfo_v8 v8;
} *socinfo;
-static enum msm_cpu cpu_of_id[] = {
+static struct msm_soc_info cpu_of_id[] = {
/* 7x01 IDs */
- [1] = MSM_CPU_7X01,
- [16] = MSM_CPU_7X01,
- [17] = MSM_CPU_7X01,
- [18] = MSM_CPU_7X01,
- [19] = MSM_CPU_7X01,
- [23] = MSM_CPU_7X01,
- [25] = MSM_CPU_7X01,
- [26] = MSM_CPU_7X01,
- [32] = MSM_CPU_7X01,
- [33] = MSM_CPU_7X01,
- [34] = MSM_CPU_7X01,
- [35] = MSM_CPU_7X01,
+ [0] = {MSM_CPU_UNKNOWN, "Unknown CPU"},
+ [1] = {MSM_CPU_7X01, "MSM7X01"},
+ [16] = {MSM_CPU_7X01, "MSM7X01"},
+ [17] = {MSM_CPU_7X01, "MSM7X01"},
+ [18] = {MSM_CPU_7X01, "MSM7X01"},
+ [19] = {MSM_CPU_7X01, "MSM7X01"},
+ [23] = {MSM_CPU_7X01, "MSM7X01"},
+ [25] = {MSM_CPU_7X01, "MSM7X01"},
+ [26] = {MSM_CPU_7X01, "MSM7X01"},
+ [32] = {MSM_CPU_7X01, "MSM7X01"},
+ [33] = {MSM_CPU_7X01, "MSM7X01"},
+ [34] = {MSM_CPU_7X01, "MSM7X01"},
+ [35] = {MSM_CPU_7X01, "MSM7X01"},
/* 7x25 IDs */
- [20] = MSM_CPU_7X25,
- [21] = MSM_CPU_7X25, /* 7225 */
- [24] = MSM_CPU_7X25, /* 7525 */
- [27] = MSM_CPU_7X25, /* 7625 */
- [39] = MSM_CPU_7X25,
- [40] = MSM_CPU_7X25,
- [41] = MSM_CPU_7X25,
- [42] = MSM_CPU_7X25,
- [62] = MSM_CPU_7X25, /* 7625-1 */
- [63] = MSM_CPU_7X25, /* 7225-1 */
- [66] = MSM_CPU_7X25, /* 7225-2 */
+ [20] = {MSM_CPU_7X25, "MSM7X25"},
+ [21] = {MSM_CPU_7X25, "MSM7X25"},
+ [24] = {MSM_CPU_7X25, "MSM7X25"},
+ [27] = {MSM_CPU_7X25, "MSM7X25"},
+ [39] = {MSM_CPU_7X25, "MSM7X25"},
+ [40] = {MSM_CPU_7X25, "MSM7X25"},
+ [41] = {MSM_CPU_7X25, "MSM7X25"},
+ [42] = {MSM_CPU_7X25, "MSM7X25"},
+ [62] = {MSM_CPU_7X25, "MSM7X25"},
+ [63] = {MSM_CPU_7X25, "MSM7X25"},
+ [66] = {MSM_CPU_7X25, "MSM7X25"},
/* 7x27 IDs */
- [43] = MSM_CPU_7X27,
- [44] = MSM_CPU_7X27,
- [61] = MSM_CPU_7X27,
- [67] = MSM_CPU_7X27, /* 7227-1 */
- [68] = MSM_CPU_7X27, /* 7627-1 */
- [69] = MSM_CPU_7X27, /* 7627-2 */
+ [43] = {MSM_CPU_7X27, "MSM7X27"},
+ [44] = {MSM_CPU_7X27, "MSM7X27"},
+ [61] = {MSM_CPU_7X27, "MSM7X27"},
+ [67] = {MSM_CPU_7X27, "MSM7X27"},
+ [68] = {MSM_CPU_7X27, "MSM7X27"},
+ [69] = {MSM_CPU_7X27, "MSM7X27"},
/* 8x50 IDs */
- [30] = MSM_CPU_8X50,
- [36] = MSM_CPU_8X50,
- [37] = MSM_CPU_8X50,
- [38] = MSM_CPU_8X50,
+ [30] = {MSM_CPU_8X50, "MSM8X50"},
+ [36] = {MSM_CPU_8X50, "MSM8X50"},
+ [37] = {MSM_CPU_8X50, "MSM8X50"},
+ [38] = {MSM_CPU_8X50, "MSM8X50"},
/* 7x30 IDs */
- [59] = MSM_CPU_7X30,
- [60] = MSM_CPU_7X30,
+ [59] = {MSM_CPU_7X30, "MSM7X30"},
+ [60] = {MSM_CPU_7X30, "MSM7X30"},
/* 8x55 IDs */
- [74] = MSM_CPU_8X55,
- [75] = MSM_CPU_8X55,
- [85] = MSM_CPU_8X55,
+ [74] = {MSM_CPU_8X55, "MSM8X55"},
+ [75] = {MSM_CPU_8X55, "MSM8X55"},
+ [85] = {MSM_CPU_8X55, "MSM8X55"},
/* 8x60 IDs */
- [70] = MSM_CPU_8X60,
- [71] = MSM_CPU_8X60,
- [86] = MSM_CPU_8X60,
+ [70] = {MSM_CPU_8X60, "MSM8X60"},
+ [71] = {MSM_CPU_8X60, "MSM8X60"},
+ [86] = {MSM_CPU_8X60, "MSM8X60"},
/* 8960 IDs */
- [87] = MSM_CPU_8960,
+ [87] = {MSM_CPU_8960, "MSM8960"},
/* 7x25A IDs */
- [88] = MSM_CPU_7X25A,
- [89] = MSM_CPU_7X25A,
- [96] = MSM_CPU_7X25A,
+ [88] = {MSM_CPU_7X25A, "MSM7X25A"},
+ [89] = {MSM_CPU_7X25A, "MSM7X25A"},
+ [96] = {MSM_CPU_7X25A, "MSM7X25A"},
/* 7x27A IDs */
- [90] = MSM_CPU_7X27A,
- [91] = MSM_CPU_7X27A,
- [92] = MSM_CPU_7X27A,
- [97] = MSM_CPU_7X27A,
+ [90] = {MSM_CPU_7X27A, "MSM7X27A"},
+ [91] = {MSM_CPU_7X27A, "MSM7X27A"},
+ [92] = {MSM_CPU_7X27A, "MSM7X27A"},
+ [97] = {MSM_CPU_7X27A, "MSM7X27A"},
/* FSM9xxx ID */
- [94] = FSM_CPU_9XXX,
- [95] = FSM_CPU_9XXX,
+ [94] = {FSM_CPU_9XXX, "FSM9XXX"},
+ [95] = {FSM_CPU_9XXX, "FSM9XXX"},
/* 7x25AA ID */
- [98] = MSM_CPU_7X25AA,
- [99] = MSM_CPU_7X25AA,
- [100] = MSM_CPU_7X25AA,
+ [98] = {MSM_CPU_7X25AA, "MSM7X25AA"},
+ [99] = {MSM_CPU_7X25AA, "MSM7X25AA"},
+ [100] = {MSM_CPU_7X25AA, "MSM7X25AA"},
/* 7x27AA ID */
- [101] = MSM_CPU_7X27AA,
- [102] = MSM_CPU_7X27AA,
- [103] = MSM_CPU_7X27AA,
- [136] = MSM_CPU_7X27AA,
+ [101] = {MSM_CPU_7X27AA, "MSM7X27AA"},
+ [102] = {MSM_CPU_7X27AA, "MSM7X27AA"},
+ [103] = {MSM_CPU_7X27AA, "MSM7X27AA"},
+ [136] = {MSM_CPU_7X27AA, "MSM7X27AA"},
/* 9x15 ID */
- [104] = MSM_CPU_9615,
- [105] = MSM_CPU_9615,
- [106] = MSM_CPU_9615,
- [107] = MSM_CPU_9615,
- [171] = MSM_CPU_9615,
+ [104] = {MSM_CPU_9615, "MSM9615"},
+ [105] = {MSM_CPU_9615, "MSM9615"},
+ [106] = {MSM_CPU_9615, "MSM9615"},
+ [107] = {MSM_CPU_9615, "MSM9615"},
+ [171] = {MSM_CPU_9615, "MSM9615"},
/* 8064 IDs */
- [109] = MSM_CPU_8064,
+ [109] = {MSM_CPU_8064, "APQ8064"},
/* 8930 IDs */
- [116] = MSM_CPU_8930,
- [117] = MSM_CPU_8930,
- [118] = MSM_CPU_8930,
- [119] = MSM_CPU_8930,
- [179] = MSM_CPU_8930,
+ [116] = {MSM_CPU_8930, "MSM8930"},
+ [117] = {MSM_CPU_8930, "MSM8930"},
+ [118] = {MSM_CPU_8930, "MSM8930"},
+ [119] = {MSM_CPU_8930, "MSM8930"},
+ [179] = {MSM_CPU_8930, "MSM8930"},
/* 8627 IDs */
- [120] = MSM_CPU_8627,
- [121] = MSM_CPU_8627,
+ [120] = {MSM_CPU_8627, "MSM8627"},
+ [121] = {MSM_CPU_8627, "MSM8627"},
/* 8660A ID */
- [122] = MSM_CPU_8960,
+ [122] = {MSM_CPU_8960, "MSM8960"},
/* 8260A ID */
- [123] = MSM_CPU_8960,
+ [123] = {MSM_CPU_8960, "MSM8960"},
/* 8060A ID */
- [124] = MSM_CPU_8960,
+ [124] = {MSM_CPU_8960, "MSM8960"},
/* 8974 IDs */
- [126] = MSM_CPU_8974,
- [184] = MSM_CPU_8974,
- [185] = MSM_CPU_8974,
- [186] = MSM_CPU_8974,
+ [126] = {MSM_CPU_8974, "MSM8974"},
+ [184] = {MSM_CPU_8974, "MSM8974"},
+ [185] = {MSM_CPU_8974, "MSM8974"},
+ [186] = {MSM_CPU_8974, "MSM8974"},
/* 8974AA IDs */
- [208] = MSM_CPU_8974PRO_AA,
- [211] = MSM_CPU_8974PRO_AA,
- [214] = MSM_CPU_8974PRO_AA,
- [217] = MSM_CPU_8974PRO_AA,
+ [208] = {MSM_CPU_8974PRO_AA, "MSM8974PRO-AA"},
+ [211] = {MSM_CPU_8974PRO_AA, "MSM8974PRO-AA"},
+ [214] = {MSM_CPU_8974PRO_AA, "MSM8974PRO-AA"},
+ [217] = {MSM_CPU_8974PRO_AA, "MSM8974PRO-AA"},
/* 8974AB IDs */
- [209] = MSM_CPU_8974PRO_AB,
- [212] = MSM_CPU_8974PRO_AB,
- [215] = MSM_CPU_8974PRO_AB,
- [218] = MSM_CPU_8974PRO_AB,
+ [209] = {MSM_CPU_8974PRO_AB, "MSM8974PRO-AB"},
+ [212] = {MSM_CPU_8974PRO_AB, "MSM8974PRO-AB"},
+ [215] = {MSM_CPU_8974PRO_AB, "MSM8974PRO-AB"},
+ [218] = {MSM_CPU_8974PRO_AB, "MSM8974PRO-AB"},
/* 8974AC IDs */
- [194] = MSM_CPU_8974PRO_AC,
- [210] = MSM_CPU_8974PRO_AC,
- [213] = MSM_CPU_8974PRO_AC,
- [216] = MSM_CPU_8974PRO_AC,
+ [194] = {MSM_CPU_8974PRO_AC, "MSM8974PRO-AC"},
+ [210] = {MSM_CPU_8974PRO_AC, "MSM8974PRO-AC"},
+ [213] = {MSM_CPU_8974PRO_AC, "MSM8974PRO-AC"},
+ [216] = {MSM_CPU_8974PRO_AC, "MSM8974PRO-AC"},
/* 8625 IDs */
- [127] = MSM_CPU_8625,
- [128] = MSM_CPU_8625,
- [129] = MSM_CPU_8625,
- [137] = MSM_CPU_8625,
- [167] = MSM_CPU_8625,
+ [127] = {MSM_CPU_8625, "MSM8625"},
+ [128] = {MSM_CPU_8625, "MSM8625"},
+ [129] = {MSM_CPU_8625, "MSM8625"},
+ [137] = {MSM_CPU_8625, "MSM8625"},
+ [167] = {MSM_CPU_8625, "MSM8625"},
/* 8064 MPQ ID */
- [130] = MSM_CPU_8064,
+ [130] = {MSM_CPU_8064, "APQ8064"},
/* 7x25AB IDs */
- [131] = MSM_CPU_7X25AB,
- [132] = MSM_CPU_7X25AB,
- [133] = MSM_CPU_7X25AB,
- [135] = MSM_CPU_7X25AB,
+ [131] = {MSM_CPU_7X25AB, "MSM7X25AB"},
+ [132] = {MSM_CPU_7X25AB, "MSM7X25AB"},
+ [133] = {MSM_CPU_7X25AB, "MSM7X25AB"},
+ [135] = {MSM_CPU_7X25AB, "MSM7X25AB"},
/* 9625 IDs */
- [134] = MSM_CPU_9625,
- [148] = MSM_CPU_9625,
- [149] = MSM_CPU_9625,
- [150] = MSM_CPU_9625,
- [151] = MSM_CPU_9625,
- [152] = MSM_CPU_9625,
- [173] = MSM_CPU_9625,
- [174] = MSM_CPU_9625,
- [175] = MSM_CPU_9625,
+ [134] = {MSM_CPU_9625, "MSM9625"},
+ [148] = {MSM_CPU_9625, "MSM9625"},
+ [149] = {MSM_CPU_9625, "MSM9625"},
+ [150] = {MSM_CPU_9625, "MSM9625"},
+ [151] = {MSM_CPU_9625, "MSM9625"},
+ [152] = {MSM_CPU_9625, "MSM9625"},
+ [173] = {MSM_CPU_9625, "MSM9625"},
+ [174] = {MSM_CPU_9625, "MSM9625"},
+ [175] = {MSM_CPU_9625, "MSM9625"},
/* 8960AB IDs */
- [138] = MSM_CPU_8960AB,
- [139] = MSM_CPU_8960AB,
- [140] = MSM_CPU_8960AB,
- [141] = MSM_CPU_8960AB,
+ [138] = {MSM_CPU_8960AB, "MSM8960AB"},
+ [139] = {MSM_CPU_8960AB, "MSM8960AB"},
+ [140] = {MSM_CPU_8960AB, "MSM8960AB"},
+ [141] = {MSM_CPU_8960AB, "MSM8960AB"},
/* 8930AA IDs */
- [142] = MSM_CPU_8930AA,
- [143] = MSM_CPU_8930AA,
- [144] = MSM_CPU_8930AA,
- [160] = MSM_CPU_8930AA,
- [180] = MSM_CPU_8930AA,
+ [142] = {MSM_CPU_8930AA, "MSM8930AA"},
+ [143] = {MSM_CPU_8930AA, "MSM8930AA"},
+ [144] = {MSM_CPU_8930AA, "MSM8930AA"},
+ [160] = {MSM_CPU_8930AA, "MSM8930AA"},
+ [180] = {MSM_CPU_8930AA, "MSM8930AA"},
/* 8226 IDs */
- [145] = MSM_CPU_8226,
- [158] = MSM_CPU_8226,
- [159] = MSM_CPU_8226,
- [198] = MSM_CPU_8226,
- [199] = MSM_CPU_8226,
- [200] = MSM_CPU_8226,
- [205] = MSM_CPU_8226,
- [219] = MSM_CPU_8226,
- [220] = MSM_CPU_8226,
- [221] = MSM_CPU_8226,
- [222] = MSM_CPU_8226,
- [223] = MSM_CPU_8226,
- [224] = MSM_CPU_8226,
+ [145] = {MSM_CPU_8226, "MSM8626"},
+ [158] = {MSM_CPU_8226, "MSM8226"},
+ [159] = {MSM_CPU_8226, "MSM8526"},
+ [198] = {MSM_CPU_8226, "MSM8126"},
+ [199] = {MSM_CPU_8226, "APQ8026"},
+ [200] = {MSM_CPU_8226, "MSM8926"},
+ [205] = {MSM_CPU_8226, "MSM8326"},
+ [219] = {MSM_CPU_8226, "APQ8028"},
+ [220] = {MSM_CPU_8226, "MSM8128"},
+ [221] = {MSM_CPU_8226, "MSM8228"},
+ [222] = {MSM_CPU_8226, "MSM8528"},
+ [223] = {MSM_CPU_8226, "MSM8628"},
+ [224] = {MSM_CPU_8226, "MSM8928"},
/* 8092 IDs */
- [146] = MSM_CPU_8092,
+ [146] = {MSM_CPU_8092, "MSM8092"},
/* 8610 IDs */
- [147] = MSM_CPU_8610,
- [161] = MSM_CPU_8610,
- [162] = MSM_CPU_8610,
- [163] = MSM_CPU_8610,
- [164] = MSM_CPU_8610,
- [165] = MSM_CPU_8610,
- [166] = MSM_CPU_8610,
- [225] = MSM_CPU_8610,
- [226] = MSM_CPU_8610,
+ [147] = {MSM_CPU_8610, "MSM8610"},
+ [161] = {MSM_CPU_8610, "MSM8110"},
+ [162] = {MSM_CPU_8610, "MSM8210"},
+ [163] = {MSM_CPU_8610, "MSM8810"},
+ [164] = {MSM_CPU_8610, "MSM8212"},
+ [165] = {MSM_CPU_8610, "MSM8612"},
+ [166] = {MSM_CPU_8610, "MSM8112"},
+ [225] = {MSM_CPU_8610, "MSM8510"},
+ [226] = {MSM_CPU_8610, "MSM8512"},
/* 8064AB IDs */
- [153] = MSM_CPU_8064AB,
+ [153] = {MSM_CPU_8064AB, "APQ8064AB"},
/* 8930AB IDs */
- [154] = MSM_CPU_8930AB,
- [155] = MSM_CPU_8930AB,
- [156] = MSM_CPU_8930AB,
- [157] = MSM_CPU_8930AB,
- [181] = MSM_CPU_8930AB,
+ [154] = {MSM_CPU_8930AB, "MSM8930AB"},
+ [155] = {MSM_CPU_8930AB, "MSM8930AB"},
+ [156] = {MSM_CPU_8930AB, "MSM8930AB"},
+ [157] = {MSM_CPU_8930AB, "MSM8930AB"},
+ [181] = {MSM_CPU_8930AB, "MSM8930AB"},
/* 8625Q IDs */
- [168] = MSM_CPU_8625Q,
- [169] = MSM_CPU_8625Q,
- [170] = MSM_CPU_8625Q,
+ [168] = {MSM_CPU_8625Q, "MSM8225Q"},
+ [169] = {MSM_CPU_8625Q, "MSM8625Q"},
+ [170] = {MSM_CPU_8625Q, "MSM8125Q"},
/* 8064AA IDs */
- [172] = MSM_CPU_8064AA,
+ [172] = {MSM_CPU_8064AA, "APQ8064AA"},
/* 8084 IDs */
- [178] = MSM_CPU_8084,
+ [178] = {MSM_CPU_8084, "APQ8084"},
/* krypton IDs */
- [187] = MSM_CPU_KRYPTON,
+ [187] = {MSM_CPU_KRYPTON, "MSMKRYPTON"},
/* FSM9900 ID */
- [188] = FSM_CPU_9900,
+ [188] = {FSM_CPU_9900, "FSM9900"},
/* Samarium IDs */
- [195] = MSM_CPU_SAMARIUM,
+ [195] = {MSM_CPU_SAMARIUM, "MSMSAMARIUM"},
/* Uninitialized IDs are not known to run Linux.
MSM_CPU_UNKNOWN is set to 0 to ensure these IDs are
@@ -455,6 +458,25 @@
return (socinfo) ? socinfo->v1.build_id : NULL;
}
+static char *msm_read_hardware_id(void)
+{
+ static char msm_soc_str[128] = "Qualcomm ";
+ static bool string_generated = false;
+
+ if (string_generated)
+ return msm_soc_str;
+ if (!socinfo)
+ goto err_path;
+ if (!cpu_of_id[socinfo->v1.id].soc_id_string)
+ goto err_path;
+
+ string_generated = true;
+ return strncat(msm_soc_str, cpu_of_id[socinfo->v1.id].soc_id_string,
+ sizeof(msm_soc_str) - strlen(msm_soc_str));
+err_path:
+ return "UNKNOWN SOC TYPE";
+}
+
uint32_t socinfo_get_raw_id(void)
{
return socinfo ?
@@ -1437,14 +1459,16 @@
}
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];
+ if (socinfo_get_id() >= ARRAY_SIZE(cpu_of_id))
+ BUG_ON("New IDs added! ID => CPU mapping might need an update.\n");
+
+ else
+ cur_cpu = cpu_of_id[socinfo->v1.id].generic_soc_type;
boot_stats_init();
socinfo_print();
+ arch_read_hardware_id = msm_read_hardware_id;
return 0;
}
diff --git a/arch/arm/mach-msm/subsystem_restart.c b/arch/arm/mach-msm/subsystem_restart.c
index 01e0985..bc4eb76 100644
--- a/arch/arm/mach-msm/subsystem_restart.c
+++ b/arch/arm/mach-msm/subsystem_restart.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -466,13 +466,20 @@
pr_info("[%p]: Powering up %s\n", current, name);
init_completion(&dev->err_ready);
- if (dev->desc->powerup(dev->desc) < 0)
+
+ if (dev->desc->powerup(dev->desc) < 0) {
+ notify_each_subsys_device(&dev, 1, SUBSYS_POWERUP_FAILURE,
+ NULL);
panic("[%p]: Powerup error: %s!", current, name);
+ }
ret = wait_for_err_ready(dev);
- if (ret)
+ if (ret) {
+ notify_each_subsys_device(&dev, 1, SUBSYS_POWERUP_FAILURE,
+ NULL);
panic("[%p]: Timed out waiting for error ready: %s!",
current, name);
+ }
subsys_set_state(dev, SUBSYS_ONLINE);
}
@@ -500,8 +507,11 @@
init_completion(&subsys->err_ready);
ret = subsys->desc->start(subsys->desc);
- if (ret)
+ if (ret){
+ notify_each_subsys_device(&subsys, 1, SUBSYS_POWERUP_FAILURE,
+ NULL);
return ret;
+ }
if (subsys->desc->is_not_loadable) {
subsys_set_state(subsys, SUBSYS_ONLINE);
@@ -509,12 +519,14 @@
}
ret = wait_for_err_ready(subsys);
- if (ret)
+ if (ret) {
/* pil-boot succeeded but we need to shutdown
* the device because error ready timed out.
*/
+ notify_each_subsys_device(&subsys, 1, SUBSYS_POWERUP_FAILURE,
+ NULL);
subsys->desc->stop(subsys->desc);
- else
+ } else
subsys_set_state(subsys, SUBSYS_ONLINE);
return ret;
diff --git a/drivers/base/dma-contiguous.c b/drivers/base/dma-contiguous.c
index 606383a..0395681 100644
--- a/drivers/base/dma-contiguous.c
+++ b/drivers/base/dma-contiguous.c
@@ -47,6 +47,7 @@
unsigned long base_pfn;
unsigned long count;
unsigned long *bitmap;
+ struct mutex lock;
};
static DEFINE_MUTEX(cma_mutex);
@@ -191,6 +192,7 @@
ret = cma_activate_area(base_pfn, count);
if (ret)
goto error;
+ mutex_init(&cma->lock);
pr_debug("%s: returned %p\n", __func__, (void *)cma);
return cma;
@@ -212,6 +214,7 @@
unsigned long len;
__be32 *prop;
char *name;
+ phys_addr_t limit = MEMBLOCK_ALLOC_ANYWHERE;
if (!of_get_flat_dt_prop(node, "linux,contiguous-region", NULL))
return 0;
@@ -225,9 +228,13 @@
name = of_get_flat_dt_prop(node, "label", NULL);
- pr_info("Found %s, memory base %lx, size %ld MiB\n", uname,
- (unsigned long)base, (unsigned long)size / SZ_1M);
- dma_contiguous_reserve_area(size, &base, MEMBLOCK_ALLOC_ANYWHERE, name);
+ prop = of_get_flat_dt_prop(node, "linux,memory-limit", NULL);
+ if (prop)
+ limit = be32_to_cpu(prop[0]);
+
+ pr_info("Found %s, memory base %lx, size %ld MiB, limit %pa\n", uname,
+ (unsigned long)base, (unsigned long)size / SZ_1M, &limit);
+ dma_contiguous_reserve_area(size, &base, limit, name);
return 0;
}
@@ -454,6 +461,13 @@
return cma->base_pfn << PAGE_SHIFT;
}
+static void clear_cma_bitmap(struct cma *cma, unsigned long pfn, int count)
+{
+ mutex_lock(&cma->lock);
+ bitmap_clear(cma->bitmap, pfn - cma->base_pfn, count);
+ mutex_unlock(&cma->lock);
+}
+
/**
* dma_alloc_from_contiguous() - allocate pages from contiguous area
* @dev: Pointer to device for which the allocation is performed.
@@ -488,23 +502,35 @@
mask = (1 << align) - 1;
- mutex_lock(&cma_mutex);
for (;;) {
+ mutex_lock(&cma->lock);
pageno = bitmap_find_next_zero_area(cma->bitmap, cma->count,
start, count, mask);
- if (pageno >= cma->count)
+ if (pageno >= cma->count) {
+ mutex_unlock(&cma_mutex);
break;
+ }
+ bitmap_set(cma->bitmap, pageno, count);
+ /*
+ * It's safe to drop the lock here. We've marked this region for
+ * our exclusive use. If the migration fails we will take the
+ * lock again and unmark it.
+ */
+ mutex_unlock(&cma->lock);
pfn = cma->base_pfn + pageno;
+ mutex_lock(&cma_mutex);
ret = alloc_contig_range(pfn, pfn + count, MIGRATE_CMA);
+ mutex_unlock(&cma_mutex);
if (ret == 0) {
- bitmap_set(cma->bitmap, pageno, count);
page = pfn_to_page(pfn);
break;
} else if (ret != -EBUSY) {
+ clear_cma_bitmap(cma, pfn, count);
break;
}
+ clear_cma_bitmap(cma, pfn, count);
tries++;
trace_dma_alloc_contiguous_retry(tries);
@@ -514,7 +540,6 @@
start = pageno + mask + 1;
}
- mutex_unlock(&cma_mutex);
pr_debug("%s(): returned %p\n", __func__, page);
return page;
}
@@ -548,9 +573,7 @@
VM_BUG_ON(pfn + count > cma->base_pfn + cma->count);
free_contig_range(pfn, count);
- mutex_lock(&cma_mutex);
- bitmap_clear(cma->bitmap, pfn - cma->base_pfn, count);
- mutex_unlock(&cma_mutex);
+ clear_cma_bitmap(cma, pfn, count);
return true;
}
diff --git a/drivers/char/diag/diagchar.h b/drivers/char/diag/diagchar.h
index 606953d..99647a7 100644
--- a/drivers/char/diag/diagchar.h
+++ b/drivers/char/diag/diagchar.h
@@ -107,6 +107,10 @@
#define DIAG_STM_WCNSS 0x04
#define DIAG_STM_APPS 0x08
+#define DIAG_DIAG_STM 0x214
+
+#define BAD_PARAM_RESPONSE_MESSAGE 20
+
/*
* The status bit masks when received in a signal handler are to be
* used in conjunction with the peripheral list bit mask to determine the
diff --git a/drivers/char/diag/diagfwd.c b/drivers/char/diag/diagfwd.c
index 8cc7515..0bbb012 100644
--- a/drivers/char/diag/diagfwd.c
+++ b/drivers/char/diag/diagfwd.c
@@ -56,6 +56,8 @@
#define STM_RSP_SMD_COMPLY_INDEX 9
#define STM_RSP_NUM_BYTES 10
+#define STM_COMMAND_VALID 1
+
#define SMD_DRAIN_BUF_SIZE 4096
int diag_debug_buf_idx;
@@ -1136,20 +1138,44 @@
}
}
-int diag_process_stm_cmd(unsigned char *buf)
+int diag_process_stm_cmd(unsigned char *buf, unsigned char *dest_buf)
{
- uint8_t version = *(buf+STM_CMD_VERSION_OFFSET);
- uint8_t mask = *(buf+STM_CMD_MASK_OFFSET);
- uint8_t cmd = *(buf+STM_CMD_DATA_OFFSET);
+ uint8_t version, mask, cmd;
uint8_t rsp_supported = 0;
uint8_t rsp_smd_comply = 0;
- int valid_command = 1;
int i;
- /* Check if command is valid */
- if ((version != 1) || (mask == 0) || (0 != (mask >> 4)) ||
- (cmd != ENABLE_STM && cmd != DISABLE_STM)) {
- valid_command = 0;
+ if (!buf || !dest_buf) {
+ pr_err("diag: Invalid pointers buf: %p, dest_buf %p in %s\n",
+ buf, dest_buf, __func__);
+ return -EIO;
+ }
+
+ version = *(buf + STM_CMD_VERSION_OFFSET);
+ mask = *(buf + STM_CMD_MASK_OFFSET);
+ cmd = *(buf + STM_CMD_DATA_OFFSET);
+
+ /*
+ * Check if command is valid. If the command is asking for
+ * status, then the processor mask field is to be ignored.
+ */
+ if ((version != 1) || (cmd > STATUS_STM) ||
+ ((cmd != STATUS_STM) && ((mask == 0) || (0 != (mask >> 4))))) {
+ /* Command is invalid. Send bad param message response */
+ dest_buf[0] = BAD_PARAM_RESPONSE_MESSAGE;
+ for (i = 0; i < STM_CMD_NUM_BYTES; i++)
+ dest_buf[i+1] = *(buf + i);
+ return STM_CMD_NUM_BYTES+1;
+ } else if (cmd == STATUS_STM) {
+ /*
+ * Only the status is being queried, so fill in whether diag
+ * over stm is supported or not
+ */
+ for (i = 0; i < NUM_SMD_CONTROL_CHANNELS; i++)
+ if (driver->peripheral_supports_stm[i])
+ rsp_supported |= 1 << i;
+
+ rsp_supported |= DIAG_STM_APPS;
} else {
if (mask & DIAG_STM_MODEM)
diag_process_stm_mask(cmd, DIAG_STM_MODEM, MODEM_DATA,
@@ -1169,15 +1195,13 @@
}
for (i = 0; i < STM_CMD_NUM_BYTES; i++)
- driver->apps_rsp_buf[i] = *(buf+i);
+ dest_buf[i] = *(buf + i);
- driver->apps_rsp_buf[STM_RSP_VALID_INDEX] = valid_command;
- driver->apps_rsp_buf[STM_RSP_SUPPORTED_INDEX] = rsp_supported;
- driver->apps_rsp_buf[STM_RSP_SMD_COMPLY_INDEX] = rsp_smd_comply;
+ dest_buf[STM_RSP_VALID_INDEX] = STM_COMMAND_VALID;
+ dest_buf[STM_RSP_SUPPORTED_INDEX] = rsp_supported;
+ dest_buf[STM_RSP_SMD_COMPLY_INDEX] = rsp_smd_comply;
- encode_rsp_and_send(STM_RSP_NUM_BYTES-1);
-
- return 0;
+ return STM_RSP_NUM_BYTES;
}
int diag_apps_responds()
@@ -1273,8 +1297,13 @@
encode_rsp_and_send(7);
return 0;
} else if ((*buf == 0x4b) && (*(buf+1) == 0x12) &&
- (*(uint16_t *)(buf+2) == 0x020E)) {
- return diag_process_stm_cmd(buf);
+ (*(uint16_t *)(buf+2) == DIAG_DIAG_STM)) {
+ len = diag_process_stm_cmd(buf, driver->apps_rsp_buf);
+ if (len > 0) {
+ encode_rsp_and_send(len - 1);
+ return 0;
+ }
+ return len;
}
/* Check for Apps Only & get event mask request */
else if (diag_apps_responds() && *buf == 0x81) {
diff --git a/drivers/char/diag/diagfwd_cntl.h b/drivers/char/diag/diagfwd_cntl.h
index d79195c..7f5ea03 100644
--- a/drivers/char/diag/diagfwd_cntl.h
+++ b/drivers/char/diag/diagfwd_cntl.h
@@ -54,8 +54,9 @@
#define ENABLE_SEPARATE_CMDRSP 1
#define DISABLE_SEPARATE_CMDRSP 0
-#define ENABLE_STM 1
#define DISABLE_STM 0
+#define ENABLE_STM 1
+#define STATUS_STM 2
#define UPDATE_PERIPHERAL_STM_STATE 1
#define CLEAR_PERIPHERAL_STM_STATE 2
diff --git a/drivers/gpio/gpio-msm-common.c b/drivers/gpio/gpio-msm-common.c
index 3d18809..4a69cd5 100644
--- a/drivers/gpio/gpio-msm-common.c
+++ b/drivers/gpio/gpio-msm-common.c
@@ -112,16 +112,11 @@
* @wake_irqs: a bitmap for tracking which interrupt lines are enabled
* as wakeup sources. When the device is suspended, interrupts which are
* not wakeup sources are disabled.
- *
- * @dual_edge_irqs: a bitmap used to track which irqs are configured
- * as dual-edge, as this is not supported by the hardware and requires
- * some special handling in the driver.
*/
struct msm_gpio_dev {
struct gpio_chip gpio_chip;
unsigned long *enabled_irqs;
unsigned long *wake_irqs;
- unsigned long *dual_edge_irqs;
struct irq_domain *domain;
};
@@ -219,64 +214,11 @@
},
};
-static void switch_mpm_config(struct irq_data *d, unsigned val)
-{
- /* switch the configuration in the mpm as well */
- if (!msm_gpio_irq_extn.irq_set_type)
- return;
-
- if (val)
- msm_gpio_irq_extn.irq_set_type(d, IRQF_TRIGGER_FALLING);
- else
- msm_gpio_irq_extn.irq_set_type(d, IRQF_TRIGGER_RISING);
-}
-
-/* For dual-edge interrupts in software, since the hardware has no
- * such support:
- *
- * At appropriate moments, this function may be called to flip the polarity
- * settings of both-edge irq lines to try and catch the next edge.
- *
- * The attempt is considered successful if:
- * - the status bit goes high, indicating that an edge was caught, or
- * - the input value of the gpio doesn't change during the attempt.
- * If the value changes twice during the process, that would cause the first
- * test to fail but would force the second, as two opposite
- * transitions would cause a detection no matter the polarity setting.
- *
- * The do-loop tries to sledge-hammer closed the timing hole between
- * the initial value-read and the polarity-write - if the line value changes
- * during that window, an interrupt is lost, the new polarity setting is
- * incorrect, and the first success test will fail, causing a retry.
- *
- * Algorithm comes from Google's msmgpio driver, see mach-msm/gpio.c.
- */
-static void msm_gpio_update_dual_edge_pos(struct irq_data *d, unsigned gpio)
-{
- int loop_limit = 100;
- unsigned val, val2, intstat;
-
- do {
- val = __msm_gpio_get_inout(gpio);
- __msm_gpio_set_polarity(gpio, val);
- val2 = __msm_gpio_get_inout(gpio);
- intstat = __msm_gpio_get_intr_status(gpio);
- if (intstat || val == val2) {
- switch_mpm_config(d, val);
- return;
- }
- } while (loop_limit-- > 0);
- pr_err("%s: dual-edge irq failed to stabilize, %#08x != %#08x\n",
- __func__, val, val2);
-}
-
static void msm_gpio_irq_ack(struct irq_data *d)
{
int gpio = msm_irq_to_gpio(&msm_gpio.gpio_chip, d->irq);
__msm_gpio_set_intr_status(gpio);
- if (test_bit(gpio, msm_gpio.dual_edge_irqs))
- msm_gpio_update_dual_edge_pos(d, gpio);
mb();
}
@@ -327,29 +269,18 @@
spin_lock_irqsave(&tlmm_lock, irq_flags);
- if (flow_type & IRQ_TYPE_EDGE_BOTH) {
+ if (flow_type & IRQ_TYPE_EDGE_BOTH)
__irq_set_handler_locked(d->irq, handle_edge_irq);
- if ((flow_type & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH)
- __set_bit(gpio, msm_gpio.dual_edge_irqs);
- else
- __clear_bit(gpio, msm_gpio.dual_edge_irqs);
- } else {
+ else
__irq_set_handler_locked(d->irq, handle_level_irq);
- __clear_bit(gpio, msm_gpio.dual_edge_irqs);
- }
__msm_gpio_set_intr_cfg_type(gpio, flow_type);
- if ((flow_type & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH)
- msm_gpio_update_dual_edge_pos(d, gpio);
-
mb();
spin_unlock_irqrestore(&tlmm_lock, irq_flags);
- if ((flow_type & IRQ_TYPE_EDGE_BOTH) != IRQ_TYPE_EDGE_BOTH) {
- if (msm_gpio_irq_extn.irq_set_type)
- msm_gpio_irq_extn.irq_set_type(d, flow_type);
- }
+ if (msm_gpio_irq_extn.irq_set_type)
+ msm_gpio_irq_extn.irq_set_type(d, flow_type);
return 0;
}
@@ -609,17 +540,9 @@
, __func__);
return -ENOMEM;
}
- msm_gpio.dual_edge_irqs = devm_kzalloc(&pdev->dev, sizeof(unsigned long)
- * BITS_TO_LONGS(ngpio), GFP_KERNEL);
- if (!msm_gpio.dual_edge_irqs) {
- dev_err(&pdev->dev, "%s failed to allocated dual_edge_irqs bitmap\n"
- , __func__);
- return -ENOMEM;
- }
bitmap_zero(msm_gpio.enabled_irqs, ngpio);
bitmap_zero(msm_gpio.wake_irqs, ngpio);
- bitmap_zero(msm_gpio.dual_edge_irqs, ngpio);
ret = gpiochip_add(&msm_gpio.gpio_chip);
if (ret < 0)
return ret;
diff --git a/drivers/gpu/ion/ion_cma_secure_heap.c b/drivers/gpu/ion/ion_cma_secure_heap.c
index d375c00..da68d05 100644
--- a/drivers/gpu/ion/ion_cma_secure_heap.c
+++ b/drivers/gpu/ion/ion_cma_secure_heap.c
@@ -444,18 +444,19 @@
ret = ion_secure_cma_alloc_from_pool(sheap, &info->phys, len);
if (ret) {
+retry:
ret = ion_secure_cma_add_to_pool(sheap, len);
if (ret) {
+ mutex_unlock(&sheap->alloc_lock);
dev_err(sheap->dev, "Fail to allocate buffer\n");
goto err;
}
ret = ion_secure_cma_alloc_from_pool(sheap, &info->phys, len);
if (ret) {
/*
- * We just added memory to the pool, we shouldn't be
- * failing to get memory
+ * Lost the race with the shrinker, try again
*/
- BUG();
+ goto retry;
}
}
mutex_unlock(&sheap->alloc_lock);
diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c
index 7a92fe6..695095f 100644
--- a/drivers/gpu/msm/adreno.c
+++ b/drivers/gpu/msm/adreno.c
@@ -1960,6 +1960,10 @@
for (i = 6; i < FT_DETECT_REGS_COUNT; i++)
ft_detect_regs[i] = 0;
+ /* turn on hang interrupt for a330v2 by default */
+ if (adreno_is_a330v2(adreno_dev))
+ set_bit(ADRENO_DEVICE_HANG_INTR, &adreno_dev->priv);
+
ret = adreno_perfcounter_init(device);
if (ret)
goto done;
@@ -2423,9 +2427,12 @@
if (tmp != adreno_dev->fast_hang_detect) {
if (adreno_dev->fast_hang_detect) {
- if (adreno_dev->gpudev->fault_detect_start)
+ if (adreno_dev->gpudev->fault_detect_start &&
+ !kgsl_active_count_get(&adreno_dev->dev)) {
adreno_dev->gpudev->fault_detect_start(
adreno_dev);
+ kgsl_active_count_put(&adreno_dev->dev);
+ }
} else {
if (adreno_dev->gpudev->fault_detect_stop)
adreno_dev->gpudev->fault_detect_stop(
@@ -2534,6 +2541,88 @@
return snprintf(buf, PAGE_SIZE, "%d\n", _wake_timeout);
}
+/**
+ * _ft_hang_intr_status_store - Routine to enable/disable h/w hang interrupt
+ * @dev: device ptr
+ * @attr: Device attribute
+ * @buf: value to write
+ * @count: size of the value to write
+ */
+static ssize_t _ft_hang_intr_status_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned int new_setting, old_setting;
+ struct kgsl_device *device = kgsl_device_from_dev(dev);
+ struct adreno_device *adreno_dev;
+ int ret;
+ if (device == NULL)
+ return 0;
+ adreno_dev = ADRENO_DEVICE(device);
+
+ mutex_lock(&device->mutex);
+ ret = _ft_sysfs_store(buf, count, &new_setting);
+ if (ret != count)
+ goto done;
+ if (new_setting)
+ new_setting = 1;
+ old_setting =
+ (test_bit(ADRENO_DEVICE_HANG_INTR, &adreno_dev->priv) ? 1 : 0);
+ if (new_setting != old_setting) {
+ if (new_setting)
+ set_bit(ADRENO_DEVICE_HANG_INTR, &adreno_dev->priv);
+ else
+ clear_bit(ADRENO_DEVICE_HANG_INTR, &adreno_dev->priv);
+ /* Set the new setting based on device state */
+ switch (device->state) {
+ case KGSL_STATE_NAP:
+ case KGSL_STATE_SLEEP:
+ kgsl_pwrctrl_wake(device, 0);
+ case KGSL_STATE_ACTIVE:
+ adreno_dev->gpudev->irq_control(adreno_dev, 1);
+ /*
+ * For following states setting will be picked up on device
+ * start. Still need them in switch statement to differentiate
+ * from default
+ */
+ case KGSL_STATE_SLUMBER:
+ case KGSL_STATE_SUSPEND:
+ break;
+ default:
+ ret = -EACCES;
+ /* reset back to old setting on error */
+ if (new_setting)
+ clear_bit(ADRENO_DEVICE_HANG_INTR,
+ &adreno_dev->priv);
+ else
+ set_bit(ADRENO_DEVICE_HANG_INTR,
+ &adreno_dev->priv);
+ goto done;
+ }
+ }
+done:
+ mutex_unlock(&device->mutex);
+ return ret;
+}
+
+/**
+ * _ft_hang_intr_status_show() - Routine to read hardware hang interrupt
+ * enablement
+ * @dev: device ptr
+ * @attr: Device attribute
+ * @buf: value read
+ */
+static ssize_t _ft_hang_intr_status_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct adreno_device *adreno_dev = _get_adreno_dev(dev);
+ if (adreno_dev == NULL)
+ return 0;
+ return snprintf(buf, PAGE_SIZE, "%d\n",
+ test_bit(ADRENO_DEVICE_HANG_INTR, &adreno_dev->priv) ? 1 : 0);
+}
+
#define FT_DEVICE_ATTR(name) \
DEVICE_ATTR(name, 0644, _ ## name ## _show, _ ## name ## _store);
@@ -2541,6 +2630,7 @@
FT_DEVICE_ATTR(ft_pagefault_policy);
FT_DEVICE_ATTR(ft_fast_hang_detect);
FT_DEVICE_ATTR(ft_long_ib_detect);
+FT_DEVICE_ATTR(ft_hang_intr_status);
static DEVICE_INT_ATTR(wake_nice, 0644, _wake_nice);
static FT_DEVICE_ATTR(wake_timeout);
@@ -2552,6 +2642,7 @@
&dev_attr_ft_long_ib_detect,
&dev_attr_wake_nice.attr,
&dev_attr_wake_timeout,
+ &dev_attr_ft_hang_intr_status,
NULL,
};
@@ -2786,7 +2877,7 @@
* Return true if the RBBM status register for the GPU type indicates that the
* hardware is idle
*/
-static bool adreno_hw_isidle(struct kgsl_device *device)
+bool adreno_hw_isidle(struct kgsl_device *device)
{
unsigned int reg_rbbm_status;
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
@@ -2899,6 +2990,12 @@
rptr = adreno_get_rptr(&adreno_dev->ringbuffer);
+ /*
+ * wptr is updated when we add commands to ringbuffer, add a barrier
+ * to make sure updated wptr is compared to rptr
+ */
+ smp_mb();
+
if (rptr == adreno_dev->ringbuffer.wptr)
return adreno_hw_isidle(device);
diff --git a/drivers/gpu/msm/adreno.h b/drivers/gpu/msm/adreno.h
index e2ea262..976a355 100644
--- a/drivers/gpu/msm/adreno.h
+++ b/drivers/gpu/msm/adreno.h
@@ -217,6 +217,7 @@
ADRENO_DEVICE_PWRON_FIXUP = 1,
ADRENO_DEVICE_INITIALIZED = 2,
ADRENO_DEVICE_STARTED = 3,
+ ADRENO_DEVICE_HANG_INTR = 4,
};
#define PERFCOUNTER_FLAG_NONE 0x0
@@ -341,6 +342,7 @@
ADRENO_REG_TC_CNTL_STATUS,
ADRENO_REG_TP0_CHICKEN,
ADRENO_REG_RBBM_RBBM_CTL,
+ ADRENO_REG_UCHE_INVALIDATE0,
ADRENO_REG_REGISTER_MAX,
};
@@ -413,7 +415,9 @@
#define KGSL_FT_SKIPFRAME 3
#define KGSL_FT_DISABLE 4
#define KGSL_FT_TEMP_DISABLE 5
-#define KGSL_FT_DEFAULT_POLICY (BIT(KGSL_FT_REPLAY) + BIT(KGSL_FT_SKIPIB))
+#define KGSL_FT_THROTTLE 6
+#define KGSL_FT_DEFAULT_POLICY (BIT(KGSL_FT_REPLAY) + BIT(KGSL_FT_SKIPIB) \
+ + BIT(KGSL_FT_THROTTLE))
/* This internal bit is used to skip the PM dump on replayed command batches */
#define KGSL_FT_SKIP_PMDUMP 31
@@ -431,7 +435,8 @@
{ BIT(KGSL_FT_SKIPIB), "skipib" }, \
{ BIT(KGSL_FT_SKIPFRAME), "skipframe" }, \
{ BIT(KGSL_FT_DISABLE), "disable" }, \
- { BIT(KGSL_FT_TEMP_DISABLE), "temp" }
+ { BIT(KGSL_FT_TEMP_DISABLE), "temp" }, \
+ { BIT(KGSL_FT_THROTTLE), "throttle"}
extern struct adreno_gpudev adreno_a2xx_gpudev;
extern struct adreno_gpudev adreno_a3xx_gpudev;
@@ -461,6 +466,7 @@
void adreno_coresight_remove(struct platform_device *pdev);
int adreno_coresight_init(struct platform_device *pdev);
+bool adreno_hw_isidle(struct kgsl_device *device);
int adreno_idle(struct kgsl_device *device);
bool adreno_isidle(struct kgsl_device *device);
diff --git a/drivers/gpu/msm/adreno_a3xx.c b/drivers/gpu/msm/adreno_a3xx.c
index eed11c3..2faa278 100644
--- a/drivers/gpu/msm/adreno_a3xx.c
+++ b/drivers/gpu/msm/adreno_a3xx.c
@@ -3039,6 +3039,25 @@
return 0;
}
+static void a3xx_fatal_err_callback(struct adreno_device *adreno_dev, int bit)
+{
+ struct kgsl_device *device = &adreno_dev->dev;
+ const char *err = "";
+
+ switch (bit) {
+ case A3XX_INT_MISC_HANG_DETECT:
+ err = "MISC: GPU hang detected\n";
+ break;
+ default:
+ return;
+ }
+ KGSL_DRV_CRIT(device, "%s\n", err);
+ kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_OFF);
+
+ /* Trigger a fault in the dispatcher - this will effect a restart */
+ adreno_dispatcher_irq_fault(device);
+}
+
static void a3xx_err_callback(struct adreno_device *adreno_dev, int bit)
{
struct kgsl_device *device = &adreno_dev->dev;
@@ -3063,7 +3082,7 @@
/* Clear the error */
kgsl_regwrite(device, A3XX_RBBM_AHB_CMD, (1 << 3));
- goto done;
+ return;
}
case A3XX_INT_RBBM_REG_TIMEOUT:
err = "RBBM: AHB register timeout";
@@ -3100,14 +3119,10 @@
"CP | Protected mode error| %s | addr=%x\n",
reg & (1 << 24) ? "WRITE" : "READ",
(reg & 0x1FFFF) >> 2);
- goto done;
}
case A3XX_INT_CP_AHB_ERROR_HALT:
err = "ringbuffer AHB error interrupt";
break;
- case A3XX_INT_MISC_HANG_DETECT:
- err = "MISC: GPU hang detected";
- break;
case A3XX_INT_UCHE_OOB_ACCESS:
err = "UCHE: Out of bounds access";
break;
@@ -3115,11 +3130,6 @@
return;
}
KGSL_DRV_CRIT(device, "%s\n", err);
- kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_OFF);
-
-done:
- /* Trigger a fault in the dispatcher - this will effect a restart */
- adreno_dispatcher_irq_fault(device);
}
static void a3xx_cp_callback(struct adreno_device *adreno_dev, int irq)
@@ -3561,7 +3571,7 @@
A3XX_IRQ_CALLBACK(a3xx_err_callback), /* 21 - CP_AHB_ERROR_FAULT */
A3XX_IRQ_CALLBACK(NULL), /* 22 - Unused */
A3XX_IRQ_CALLBACK(NULL), /* 23 - Unused */
- A3XX_IRQ_CALLBACK(NULL), /* 24 - MISC_HANG_DETECT */
+ A3XX_IRQ_CALLBACK(a3xx_fatal_err_callback),/* 24 - MISC_HANG_DETECT */
A3XX_IRQ_CALLBACK(a3xx_err_callback), /* 25 - UCHE_OOB_ACCESS */
/* 26 to 31 - Unused */
};
@@ -3602,7 +3612,9 @@
struct kgsl_device *device = &adreno_dev->dev;
if (state)
- kgsl_regwrite(device, A3XX_RBBM_INT_0_MASK, A3XX_INT_MASK);
+ kgsl_regwrite(device, A3XX_RBBM_INT_0_MASK, A3XX_INT_MASK |
+ (test_bit(ADRENO_DEVICE_HANG_INTR, &adreno_dev->priv) ?
+ (1 << A3XX_INT_MISC_HANG_DETECT) : 0));
else
kgsl_regwrite(device, A3XX_RBBM_INT_0_MASK, 0);
}
@@ -4193,9 +4205,12 @@
/* Turn on hang detection - this spews a lot of useful information
* into the RBBM registers on a hang */
-
- kgsl_regwrite(device, A3XX_RBBM_INTERFACE_HANG_INT_CTL,
- (1 << 16) | 0xFFF);
+ if (adreno_is_a330v2(adreno_dev))
+ kgsl_regwrite(device, A3XX_RBBM_INTERFACE_HANG_INT_CTL,
+ (1 << 31) | 0xFFFF);
+ else
+ kgsl_regwrite(device, A3XX_RBBM_INTERFACE_HANG_INT_CTL,
+ (1 << 16) | 0xFFF);
/* Enable 64-byte cacheline size. HW Default is 32-byte (0x000000E0). */
kgsl_regwrite(device, A3XX_UCHE_CACHE_MODE_CONTROL_REG, 0x00000001);
@@ -4577,6 +4592,8 @@
ADRENO_REG_DEFINE(ADRENO_REG_TC_CNTL_STATUS, REG_TC_CNTL_STATUS),
ADRENO_REG_DEFINE(ADRENO_REG_TP0_CHICKEN, REG_TP0_CHICKEN),
ADRENO_REG_DEFINE(ADRENO_REG_RBBM_RBBM_CTL, A3XX_RBBM_RBBM_CTL),
+ ADRENO_REG_DEFINE(ADRENO_REG_UCHE_INVALIDATE0,
+ A3XX_UCHE_CACHE_INVALIDATE0_REG),
};
struct adreno_reg_offsets a3xx_reg_offsets = {
diff --git a/drivers/gpu/msm/adreno_dispatch.c b/drivers/gpu/msm/adreno_dispatch.c
index 48d0210..7aae397 100644
--- a/drivers/gpu/msm/adreno_dispatch.c
+++ b/drivers/gpu/msm/adreno_dispatch.c
@@ -33,6 +33,15 @@
/* Number of command batches sent at a time from a single context */
static unsigned int _context_cmdbatch_burst = 5;
+/*
+ * GFT throttle parameters. If GFT recovered more than
+ * X times in Y ms invalidate the context and do not attempt recovery.
+ * X -> _fault_throttle_burst
+ * Y -> _fault_throttle_time
+ */
+static unsigned int _fault_throttle_time = 3000;
+static unsigned int _fault_throttle_burst = 3;
+
/* Number of command batches inflight in the ringbuffer at any time */
static unsigned int _dispatcher_inflight = 15;
@@ -78,15 +87,24 @@
static inline bool _isidle(struct kgsl_device *device)
{
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
- unsigned int ts;
+ unsigned int ts, i;
+
+ if (!kgsl_pwrctrl_isenabled(device))
+ goto ret;
ts = kgsl_readtimestamp(device, NULL, KGSL_TIMESTAMP_RETIRED);
- if (adreno_isidle(device) == true &&
- (ts >= adreno_dev->ringbuffer.global_ts))
- return true;
+ /* If GPU HW status is idle return true */
+ if (adreno_hw_isidle(device) ||
+ (ts == adreno_dev->ringbuffer.global_ts))
+ goto ret;
return false;
+
+ret:
+ for (i = 0; i < FT_DETECT_REGS_COUNT; i++)
+ fault_detect_regs[i] = 0;
+ return true;
}
/**
@@ -929,6 +947,12 @@
if (dispatcher->inflight == 0) {
KGSL_DRV_WARN(device,
"dispatcher_do_fault with 0 inflight commands\n");
+ /*
+ * For certain faults like h/w fault the interrupts are
+ * turned off, re-enable here
+ */
+ if (kgsl_pwrctrl_isenabled(device))
+ kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_ON);
return 0;
}
@@ -1015,6 +1039,35 @@
cmdbatch = replay[0];
/*
+ * If GFT recovered more than X times in Y ms invalidate the context
+ * and do not attempt recovery.
+ * Example: X==3 and Y==3000 ms, GPU hung at 500ms, 1700ms, 25000ms and
+ * 3000ms for the same context, we will not try FT and invalidate the
+ * context @3000ms because context triggered GFT more than 3 times in
+ * last 3 seconds. If a context caused recoverable GPU hangs
+ * where 1st and 4th gpu hang are more than 3 seconds apart we
+ * won't disable GFT and invalidate the context.
+ */
+ if (test_bit(KGSL_FT_THROTTLE, &cmdbatch->fault_policy)) {
+ if (time_after(jiffies, (cmdbatch->context->fault_time
+ + msecs_to_jiffies(_fault_throttle_time)))) {
+ cmdbatch->context->fault_time = jiffies;
+ cmdbatch->context->fault_count = 1;
+ } else {
+ cmdbatch->context->fault_count++;
+ if (cmdbatch->context->fault_count >
+ _fault_throttle_burst) {
+ set_bit(KGSL_FT_DISABLE,
+ &cmdbatch->fault_policy);
+ pr_fault(device, cmdbatch,
+ "gpu fault threshold exceeded %d faults in %d msecs\n",
+ _fault_throttle_burst,
+ _fault_throttle_time);
+ }
+ }
+ }
+
+ /*
* If FT is disabled for this cmdbatch invalidate immediately
*/
@@ -1628,6 +1681,10 @@
static DISPATCHER_UINT_ATTR(context_queue_wait, 0644, 0, _context_queue_wait);
static DISPATCHER_UINT_ATTR(fault_detect_interval, 0644, 0,
_fault_timer_interval);
+static DISPATCHER_UINT_ATTR(fault_throttle_time, 0644, 0,
+ _fault_throttle_time);
+static DISPATCHER_UINT_ATTR(fault_throttle_burst, 0644, 0,
+ _fault_throttle_burst);
static struct attribute *dispatcher_attrs[] = {
&dispatcher_attr_inflight.attr,
@@ -1636,6 +1693,8 @@
&dispatcher_attr_cmdbatch_timeout.attr,
&dispatcher_attr_context_queue_wait.attr,
&dispatcher_attr_fault_detect_interval.attr,
+ &dispatcher_attr_fault_throttle_time.attr,
+ &dispatcher_attr_fault_throttle_burst.attr,
NULL,
};
diff --git a/drivers/gpu/msm/adreno_drawctxt.c b/drivers/gpu/msm/adreno_drawctxt.c
index 4db045a..136456a 100644
--- a/drivers/gpu/msm/adreno_drawctxt.c
+++ b/drivers/gpu/msm/adreno_drawctxt.c
@@ -607,7 +607,7 @@
struct adreno_context *context)
{
struct kgsl_device *device;
- unsigned int cmds[5];
+ unsigned int cmds[8];
if (adreno_dev == NULL || context == NULL)
return -EINVAL;
@@ -621,8 +621,14 @@
cmds[3] = device->memstore.gpuaddr +
KGSL_MEMSTORE_OFFSET(KGSL_MEMSTORE_GLOBAL, current_context);
cmds[4] = context->base.id;
+ /* Flush the UCHE for new context */
+ cmds[5] = cp_type0_packet(
+ adreno_getreg(adreno_dev, ADRENO_REG_UCHE_INVALIDATE0), 2);
+ cmds[6] = 0;
+ if (adreno_is_a3xx(adreno_dev))
+ cmds[7] = 0x90000000;
return adreno_ringbuffer_issuecmds(device, context,
- KGSL_CMD_FLAGS_NONE, cmds, 5);
+ KGSL_CMD_FLAGS_NONE, cmds, 8);
}
diff --git a/drivers/gpu/msm/kgsl_debugfs.c b/drivers/gpu/msm/kgsl_debugfs.c
index 318b10d..2d833e4 100644
--- a/drivers/gpu/msm/kgsl_debugfs.c
+++ b/drivers/gpu/msm/kgsl_debugfs.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2002,2008-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2002,2008-2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -260,8 +260,10 @@
kgsl_get_memory_usage(usage, sizeof(usage), m->flags);
- seq_printf(s, "%08x %08lx %8d %5d %5s %10s %16s %5d\n",
- m->gpuaddr, m->useraddr, m->size, entry->id, flags,
+ seq_printf(s, "%pK %pK %8zd %5d %5s %10s %16s %5d\n",
+ (unsigned long *) m->gpuaddr,
+ (unsigned long *) m->useraddr,
+ m->size, entry->id, flags,
memtype_str(entry->memtype), usage, m->sglen);
}
@@ -347,7 +349,7 @@
* So if debugfs is disabled in kernel, return as
* success.
*/
- dentry = debugfs_create_file("mem", 0400, private->debug_root, private,
+ dentry = debugfs_create_file("mem", 0444, private->debug_root, private,
&process_mem_fops);
if (IS_ERR(dentry)) {
diff --git a/drivers/gpu/msm/kgsl_device.h b/drivers/gpu/msm/kgsl_device.h
index 98fd731..fc4b77e 100644
--- a/drivers/gpu/msm/kgsl_device.h
+++ b/drivers/gpu/msm/kgsl_device.h
@@ -349,6 +349,8 @@
* is set.
* @flags: flags from userspace controlling the behavior of this context
* @pwr_constraint: power constraint from userspace for this context
+ * @fault_count: number of times gpu hanged in last _context_throttle_time ms
+ * @fault_time: time of the first gpu hang in last _context_throttle_time ms
*/
struct kgsl_context {
struct kref refcount;
@@ -367,6 +369,8 @@
unsigned int pagefault_ts;
unsigned int flags;
struct kgsl_pwr_constraint pwr_constraint;
+ unsigned int fault_count;
+ unsigned long fault_time;
};
/**
diff --git a/drivers/gpu/msm/kgsl_iommu.c b/drivers/gpu/msm/kgsl_iommu.c
index 2af8d27..69b953f 100755
--- a/drivers/gpu/msm/kgsl_iommu.c
+++ b/drivers/gpu/msm/kgsl_iommu.c
@@ -514,6 +514,9 @@
else
/* something went wrong with the event handling mechanism */
BUG_ON(1);
+
+ /* Free param we are done using it */
+ kfree(param);
}
/*
@@ -638,16 +641,18 @@
phys_addr_t pt_base)
{
struct kgsl_iommu_pt *iommu_pt = pt ? pt->priv : NULL;
- phys_addr_t domain_ptbase = iommu_pt ?
- iommu_get_pt_base_addr(iommu_pt->domain) : 0;
+ phys_addr_t domain_ptbase;
- /* Only compare the valid address bits of the pt_base */
- domain_ptbase &= KGSL_IOMMU_CTX_TTBR0_ADDR_MASK;
+ if (iommu_pt == NULL)
+ return 0;
+
+ domain_ptbase = iommu_get_pt_base_addr(iommu_pt->domain)
+ & KGSL_IOMMU_CTX_TTBR0_ADDR_MASK;
pt_base &= KGSL_IOMMU_CTX_TTBR0_ADDR_MASK;
- return domain_ptbase && pt_base &&
- (domain_ptbase == pt_base);
+ return (domain_ptbase == pt_base);
+
}
/*
diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c
index 656d7e2..fb9bdb1 100644
--- a/drivers/gpu/msm/kgsl_pwrctrl.c
+++ b/drivers/gpu/msm/kgsl_pwrctrl.c
@@ -78,7 +78,7 @@
};
static void kgsl_pwrctrl_clk(struct kgsl_device *device, int state,
- int requested_state);
+ int requested_state);
static void kgsl_pwrctrl_axi(struct kgsl_device *device, int state);
static void kgsl_pwrctrl_pwrrail(struct kgsl_device *device, int state);
@@ -883,7 +883,7 @@
}
}
-static void kgsl_pwrctrl_clk(struct kgsl_device *device, int state,
+void kgsl_pwrctrl_clk(struct kgsl_device *device, int state,
int requested_state)
{
struct kgsl_pwrctrl *pwr = &device->pwrctrl;
diff --git a/drivers/hwmon/qpnp-adc-common.c b/drivers/hwmon/qpnp-adc-common.c
index 7bc8773..37a11d2 100644
--- a/drivers/hwmon/qpnp-adc-common.c
+++ b/drivers/hwmon/qpnp-adc-common.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -986,6 +986,92 @@
}
EXPORT_SYMBOL(qpnp_vadc_check_result);
+int qpnp_adc_get_revid_version(struct device *dev)
+{
+ struct pmic_revid_data *revid_data;
+ struct device_node *revid_dev_node;
+
+ revid_dev_node = of_parse_phandle(dev->of_node,
+ "qcom,pmic-revid", 0);
+ if (!revid_dev_node) {
+ pr_debug("Missing qcom,pmic-revid property\n");
+ return -EINVAL;
+ }
+
+ revid_data = get_revid_data(revid_dev_node);
+ if (IS_ERR(revid_data)) {
+ pr_debug("revid error rc = %ld\n", PTR_ERR(revid_data));
+ return -EINVAL;
+ }
+
+ if ((revid_data->rev1 == PM8941_V3P1_REV1) &&
+ (revid_data->rev2 == PM8941_V3P1_REV2) &&
+ (revid_data->rev3 == PM8941_V3P1_REV3) &&
+ (revid_data->rev4 == PM8941_V3P1_REV4) &&
+ (revid_data->pmic_type == PM8941_V3P1_TYPE) &&
+ (revid_data->pmic_subtype == PM8941_V3P1_SUBTYPE))
+ return QPNP_REV_ID_8941_3_1;
+ else if ((revid_data->rev1 == PM8941_V3P0_REV1) &&
+ (revid_data->rev2 == PM8941_V3P0_REV2) &&
+ (revid_data->rev3 == PM8941_V3P0_REV3) &&
+ (revid_data->rev4 == PM8941_V3P0_REV4) &&
+ (revid_data->pmic_type == PM8941_V3P0_TYPE) &&
+ (revid_data->pmic_subtype == PM8941_V3P0_SUBTYPE))
+ return QPNP_REV_ID_8941_3_0;
+ else if ((revid_data->rev1 == PM8941_V2P0_REV1) &&
+ (revid_data->rev2 == PM8941_V2P0_REV2) &&
+ (revid_data->rev3 == PM8941_V2P0_REV3) &&
+ (revid_data->rev4 == PM8941_V2P0_REV4) &&
+ (revid_data->pmic_type == PM8941_V2P0_TYPE) &&
+ (revid_data->pmic_subtype == PM8941_V2P0_SUBTYPE))
+ return QPNP_REV_ID_8941_2_0;
+ else if ((revid_data->rev1 == PM8226_V2P2_REV1) &&
+ (revid_data->rev2 == PM8226_V2P2_REV2) &&
+ (revid_data->rev3 == PM8226_V2P2_REV3) &&
+ (revid_data->rev4 == PM8226_V2P2_REV4) &&
+ (revid_data->pmic_type == PM8226_V2P2_TYPE) &&
+ (revid_data->pmic_subtype == PM8226_V2P2_SUBTYPE))
+ return QPNP_REV_ID_8026_2_2;
+ else if ((revid_data->rev1 == PM8226_V2P1_REV1) &&
+ (revid_data->rev2 == PM8226_V2P1_REV2) &&
+ (revid_data->rev3 == PM8226_V2P1_REV3) &&
+ (revid_data->rev4 == PM8226_V2P1_REV4) &&
+ (revid_data->pmic_type == PM8226_V2P1_TYPE) &&
+ (revid_data->pmic_subtype == PM8226_V2P1_SUBTYPE))
+ return QPNP_REV_ID_8026_2_1;
+ else if ((revid_data->rev1 == PM8226_V2P0_REV1) &&
+ (revid_data->rev2 == PM8226_V2P0_REV2) &&
+ (revid_data->rev3 == PM8226_V2P0_REV3) &&
+ (revid_data->rev4 == PM8226_V2P0_REV4) &&
+ (revid_data->pmic_type == PM8226_V2P0_TYPE) &&
+ (revid_data->pmic_subtype == PM8226_V2P0_SUBTYPE))
+ return QPNP_REV_ID_8026_2_0;
+ else if ((revid_data->rev1 == PM8226_V1P0_REV1) &&
+ (revid_data->rev2 == PM8226_V1P0_REV2) &&
+ (revid_data->rev3 == PM8226_V1P0_REV3) &&
+ (revid_data->rev4 == PM8226_V1P0_REV4) &&
+ (revid_data->pmic_type == PM8226_V1P0_TYPE) &&
+ (revid_data->pmic_subtype == PM8226_V1P0_SUBTYPE))
+ return QPNP_REV_ID_8026_1_0;
+ else if ((revid_data->rev1 == PM8110_V1P0_REV1) &&
+ (revid_data->rev2 == PM8110_V1P0_REV2) &&
+ (revid_data->rev3 == PM8110_V1P0_REV3) &&
+ (revid_data->rev4 == PM8110_V1P0_REV4) &&
+ (revid_data->pmic_type == PM8110_V1P0_TYPE) &&
+ (revid_data->pmic_subtype == PM8110_V1P0_SUBTYPE))
+ return QPNP_REV_ID_8110_1_0;
+ else if ((revid_data->rev1 == PM8110_V2P0_REV1) &&
+ (revid_data->rev2 == PM8110_V2P0_REV2) &&
+ (revid_data->rev3 == PM8110_V2P0_REV3) &&
+ (revid_data->rev4 == PM8110_V2P0_REV4) &&
+ (revid_data->pmic_type == PM8110_V2P0_TYPE) &&
+ (revid_data->pmic_subtype == PM8110_V2P0_SUBTYPE))
+ return QPNP_REV_ID_8110_2_0;
+ else
+ return -EINVAL;
+}
+EXPORT_SYMBOL(qpnp_adc_get_revid_version);
+
int32_t qpnp_adc_get_devicetree_data(struct spmi_device *spmi,
struct qpnp_adc_drv *adc_qpnp)
{
diff --git a/drivers/hwmon/qpnp-adc-current.c b/drivers/hwmon/qpnp-adc-current.c
index 067a887..ec6d8ec 100644
--- a/drivers/hwmon/qpnp-adc-current.c
+++ b/drivers/hwmon/qpnp-adc-current.c
@@ -340,48 +340,8 @@
return 0;
}
-#define QPNP_IADC_PM8941_3_1_REV2 3
-#define QPNP_IADC_PM8941_3_1_REV3 2
-#define QPNP_IADC_PM8026_1_REV2 1
-#define QPNP_IADC_PM8026_1_REV3 2
-#define QPNP_IADC_PM8026_2_REV2 4
-#define QPNP_IADC_PM8026_2_REV3 2
-#define QPNP_IADC_PM8110_1_REV2 2
-#define QPNP_IADC_PM8110_1_REV3 2
-
-#define QPNP_IADC_REV_ID_8941_3_1 1
-#define QPNP_IADC_REV_ID_8026_1_0 2
-#define QPNP_IADC_REV_ID_8026_2_0 3
-#define QPNP_IADC_REV_ID_8110_1_0 4
-
-static void qpnp_temp_comp_version_check(struct qpnp_iadc_chip *iadc,
- int32_t *version)
-{
- if ((iadc->iadc_comp.revision_dig_major ==
- QPNP_IADC_PM8941_3_1_REV2) &&
- (iadc->iadc_comp.revision_ana_minor ==
- QPNP_IADC_PM8941_3_1_REV3))
- *version = QPNP_IADC_REV_ID_8941_3_1;
- else if ((iadc->iadc_comp.revision_dig_major ==
- QPNP_IADC_PM8026_1_REV2) &&
- (iadc->iadc_comp.revision_ana_minor ==
- QPNP_IADC_PM8026_1_REV3))
- *version = QPNP_IADC_REV_ID_8026_1_0;
- else if ((iadc->iadc_comp.revision_dig_major ==
- QPNP_IADC_PM8026_2_REV2) &&
- (iadc->iadc_comp.revision_ana_minor ==
- QPNP_IADC_PM8026_2_REV3))
- *version = QPNP_IADC_REV_ID_8026_2_0;
- else if ((iadc->iadc_comp.revision_dig_major ==
- QPNP_IADC_PM8110_1_REV2) &&
- (iadc->iadc_comp.revision_ana_minor ==
- QPNP_IADC_PM8110_1_REV3))
- *version = QPNP_IADC_REV_ID_8110_1_0;
- else
- *version = -EINVAL;
-
- return;
-}
+#define QPNP_IADC_PM8026_2_REV2 4
+#define QPNP_IADC_PM8026_2_REV3 2
#define QPNP_COEFF_1 969000
#define QPNP_COEFF_2 32
@@ -408,15 +368,19 @@
#define QPNP_COEFF_22 5000000
#define QPNP_COEFF_23 3722500
#define QPNP_COEFF_24 84
+#define QPNP_COEFF_25 33
+#define QPNP_COEFF_26 22
+#define QPNP_COEFF_27 53
+#define QPNP_COEFF_28 48
static int32_t qpnp_iadc_comp(int64_t *result, struct qpnp_iadc_chip *iadc,
int64_t die_temp)
{
int64_t temp_var = 0, sys_gain_coeff = 0, old;
int32_t coeff_a = 0, coeff_b = 0;
- int32_t version;
+ int version = 0;
- qpnp_temp_comp_version_check(iadc, &version);
+ version = qpnp_adc_get_revid_version(iadc->dev);
if (version == -EINVAL)
return 0;
@@ -431,7 +395,7 @@
iadc->iadc_comp.sys_gain;
switch (version) {
- case QPNP_IADC_REV_ID_8941_3_1:
+ case QPNP_REV_ID_8941_3_1:
switch (iadc->iadc_comp.id) {
case COMP_ID_GF:
if (!iadc->iadc_comp.ext_rsense) {
@@ -470,7 +434,60 @@
break;
}
break;
- case QPNP_IADC_REV_ID_8026_1_0:
+ case QPNP_REV_ID_8026_2_1:
+ case QPNP_REV_ID_8026_2_2:
+ /* pm8026 rev 2.1 and 2.2 */
+ switch (iadc->iadc_comp.id) {
+ case COMP_ID_GF:
+ if (!iadc->iadc_comp.ext_rsense) {
+ /* internal rsense */
+ if (*result < 0) {
+ /* charge */
+ coeff_a = 0;
+ coeff_b = 0;
+ } else {
+ coeff_a = QPNP_COEFF_25;
+ coeff_b = 0;
+ }
+ } else {
+ if (*result < 0) {
+ /* charge */
+ coeff_a = 0;
+ coeff_b = 0;
+ } else {
+ /* discharge */
+ coeff_a = 0;
+ coeff_b = 0;
+ }
+ }
+ break;
+ case COMP_ID_TSMC:
+ default:
+ if (!iadc->iadc_comp.ext_rsense) {
+ /* internal rsense */
+ if (*result < 0) {
+ /* charge */
+ coeff_a = 0;
+ coeff_b = 0;
+ } else {
+ coeff_a = QPNP_COEFF_26;
+ coeff_b = 0;
+ }
+ } else {
+ if (*result < 0) {
+ /* charge */
+ coeff_a = 0;
+ coeff_b = 0;
+ } else {
+ /* discharge */
+ coeff_a = 0;
+ coeff_b = 0;
+ }
+ }
+ break;
+ }
+ break;
+ case QPNP_REV_ID_8026_1_0:
/* pm8026 rev 1.0 */
switch (iadc->iadc_comp.id) {
case COMP_ID_GF:
@@ -522,7 +539,7 @@
break;
}
break;
- case QPNP_IADC_REV_ID_8110_1_0:
+ case QPNP_REV_ID_8110_1_0:
/* pm8110 rev 1.0 */
switch (iadc->iadc_comp.id) {
case COMP_ID_GF:
@@ -554,8 +571,41 @@
break;
}
break;
+ case QPNP_REV_ID_8110_2_0:
+ die_temp -= 25000;
+ /* pm8110 rev 2.0 */
+ switch (iadc->iadc_comp.id) {
+ case COMP_ID_GF:
+ if (!iadc->iadc_comp.ext_rsense) {
+ /* internal rsense */
+ if (*result < 0) {
+ /* charge */
+ coeff_a = 0;
+ coeff_b = 0;
+ } else {
+ coeff_a = QPNP_COEFF_27;
+ coeff_b = 0;
+ }
+ }
+ break;
+ case COMP_ID_SMIC:
+ default:
+ if (!iadc->iadc_comp.ext_rsense) {
+ /* internal rsense */
+ if (*result < 0) {
+ /* charge */
+ coeff_a = 0;
+ coeff_b = 0;
+ } else {
+ coeff_a = QPNP_COEFF_28;
+ coeff_b = 0;
+ }
+ }
+ break;
+ }
+ break;
default:
- case QPNP_IADC_REV_ID_8026_2_0:
+ case QPNP_REV_ID_8026_2_0:
/* pm8026 rev 1.0 */
coeff_a = 0;
coeff_b = 0;
@@ -578,7 +628,8 @@
temp_var = div64_s64(temp_var * sys_gain_coeff, 1000000);
*result = div64_s64(*result * 1000, temp_var);
}
- pr_debug("%lld compensated into %lld\n", old, *result);
+ pr_debug("%lld compensated into %lld, a: %d, b: %d, sys_gain: %lld\n",
+ old, *result, coeff_a, coeff_b, sys_gain_coeff);
return 0;
}
@@ -844,9 +895,10 @@
bool batfet_closed)
{
uint8_t rslt_lsb, rslt_msb;
- int32_t rc = 0;
+ int32_t rc = 0, version = 0;
uint16_t raw_data;
uint32_t mode_sel = 0;
+ bool iadc_offset_ch_batfet_check;
if (qpnp_iadc_is_valid(iadc) < 0)
return -EPROBE_DEFER;
@@ -868,13 +920,22 @@
iadc->adc->calib.gain_raw = raw_data;
/*
- * there is a features in the BMS where if the batfet is opened
- * the BMS reads from INTERNAL_RSENSE (channel 0) actually go to
+ * there is a features on PM8941 in the BMS where if the batfet is
+ * opened the BMS reads from INTERNAL_RSENSE (channel 0) actually go to
* OFFSET_CALIBRATION_CSP_CSN (channel 5). Hence if batfet is opened
* we have to calibrate based on OFFSET_CALIBRATION_CSP_CSN even for
* internal rsense.
*/
- if (!batfet_closed || iadc->external_rsense) {
+ version = qpnp_adc_get_revid_version(iadc->dev);
+ if ((version == QPNP_REV_ID_8941_3_1) ||
+ (version == QPNP_REV_ID_8941_3_0) ||
+ (version == QPNP_REV_ID_8941_2_0))
+ iadc_offset_ch_batfet_check = true;
+ else
+ iadc_offset_ch_batfet_check = false;
+
+ if ((iadc_offset_ch_batfet_check && !batfet_closed) ||
+ (iadc->external_rsense)) {
/* external offset calculation */
rc = qpnp_iadc_configure(iadc, OFFSET_CALIBRATION_CSP_CSN,
&raw_data, mode_sel);
@@ -1014,7 +1075,7 @@
int32_t qpnp_iadc_get_rsense(struct qpnp_iadc_chip *iadc, int32_t *rsense)
{
- uint8_t rslt_rsense;
+ uint8_t rslt_rsense = 0;
int32_t rc = 0, sign_bit = 0;
if (qpnp_iadc_is_valid(iadc) < 0)
@@ -1022,36 +1083,37 @@
if (iadc->external_rsense) {
*rsense = iadc->rsense;
- return rc;
- }
-
- if (iadc->default_internal_rsense) {
+ } else if (iadc->default_internal_rsense) {
*rsense = iadc->rsense_workaround_value;
- return rc;
- }
+ } else {
- rc = qpnp_iadc_read_reg(iadc, QPNP_IADC_NOMINAL_RSENSE, &rslt_rsense);
- if (rc < 0) {
- pr_err("qpnp adc rsense read failed with %d\n", rc);
- return rc;
- }
+ rc = qpnp_iadc_read_reg(iadc, QPNP_IADC_NOMINAL_RSENSE,
+ &rslt_rsense);
+ if (rc < 0) {
+ pr_err("qpnp adc rsense read failed with %d\n", rc);
+ return rc;
+ }
- pr_debug("rsense:0%x\n", rslt_rsense);
+ pr_debug("rsense:0%x\n", rslt_rsense);
- if (rslt_rsense & QPNP_RSENSE_MSB_SIGN_CHECK)
- sign_bit = 1;
+ if (rslt_rsense & QPNP_RSENSE_MSB_SIGN_CHECK)
+ sign_bit = 1;
- rslt_rsense &= ~QPNP_RSENSE_MSB_SIGN_CHECK;
+ rslt_rsense &= ~QPNP_RSENSE_MSB_SIGN_CHECK;
- if (sign_bit)
- *rsense = QPNP_IADC_INTERNAL_RSENSE_N_OHMS_FACTOR -
+ if (sign_bit)
+ *rsense = QPNP_IADC_INTERNAL_RSENSE_N_OHMS_FACTOR -
(rslt_rsense * QPNP_IADC_RSENSE_LSB_N_OHMS_PER_BIT);
- else
- *rsense = QPNP_IADC_INTERNAL_RSENSE_N_OHMS_FACTOR +
+ else
+ *rsense = QPNP_IADC_INTERNAL_RSENSE_N_OHMS_FACTOR +
(rslt_rsense * QPNP_IADC_RSENSE_LSB_N_OHMS_PER_BIT);
-
+ }
pr_debug("rsense value is %d\n", *rsense);
+ if (*rsense == 0)
+ pr_err("incorrect rsens value:%d rslt_rsense:%d\n",
+ *rsense, rslt_rsense);
+
return rc;
}
EXPORT_SYMBOL(qpnp_iadc_get_rsense);
@@ -1140,10 +1202,12 @@
result->result_uv = -result->result_uv;
result_current = -result_current;
}
+ result_current *= -1;
rc = qpnp_iadc_comp_result(iadc, &result_current);
if (rc < 0)
pr_err("Error during compensating the IADC\n");
rc = 0;
+ result_current *= -1;
result->result_ua = (int32_t) result_current;
fail:
@@ -1215,6 +1279,11 @@
if (qpnp_iadc_is_valid(iadc) < 0)
return -EPROBE_DEFER;
+ if ((iadc->adc->calib.gain_raw - iadc->adc->calib.offset_raw) == 0) {
+ pr_err("raw offset errors! run iadc calibration again\n");
+ return -EINVAL;
+ }
+
mutex_lock(&iadc->adc->adc_lock);
if (iadc->iadc_poll_eoc) {
@@ -1251,6 +1320,11 @@
result_current = i_result->result_uv;
result_current *= QPNP_IADC_NANO_VOLTS_FACTOR;
/* Intentional fall through. Process the result w/o comp */
+ if (!rsense_u_ohms) {
+ pr_err("rsense error=%d\n", rsense_u_ohms);
+ goto fail_release_vadc;
+ }
+
do_div(result_current, rsense_u_ohms);
if (sign) {
diff --git a/drivers/hwmon/qpnp-adc-voltage.c b/drivers/hwmon/qpnp-adc-voltage.c
index d462fb3..346a72d 100644
--- a/drivers/hwmon/qpnp-adc-voltage.c
+++ b/drivers/hwmon/qpnp-adc-voltage.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -512,63 +512,52 @@
#define QPNP_VBAT_COEFF_13 102640000
#define QPNP_VBAT_COEFF_14 22220000
#define QPNP_VBAT_COEFF_15 83060000
-
-#define QPNP_VADC_REV_ID_8941_3_1 1
-#define QPNP_VADC_REV_ID_8026_1_0 2
-#define QPNP_VADC_REV_ID_8026_2_0 3
-
-static void qpnp_temp_comp_version_check(struct qpnp_vadc_chip *vadc,
- int32_t *version)
-{
- if (vadc->revision_dig_major == 3 &&
- vadc->revision_ana_minor == 2)
- *version = QPNP_VADC_REV_ID_8941_3_1;
- else if (vadc->revision_dig_major == 1 &&
- vadc->revision_ana_minor == 2)
- *version = QPNP_VADC_REV_ID_8026_1_0;
- else if (vadc->revision_dig_major == 2 &&
- vadc->revision_ana_minor == 2)
- *version = QPNP_VADC_REV_ID_8026_2_0;
- else
- *version = -EINVAL;
-
- return;
-}
+#define QPNP_VBAT_COEFF_16 2810
+#define QPNP_VBAT_COEFF_17 5260
+#define QPNP_VBAT_COEFF_18 8027
+#define QPNP_VBAT_COEFF_19 2347
+#define QPNP_VBAT_COEFF_20 6043
+#define QPNP_VBAT_COEFF_21 1914
+#define QPNP_VBAT_OFFSET_SMIC 9446
+#define QPNP_VBAT_OFFSET_GF 9441
+#define QPNP_OCV_OFFSET_SMIC 4596
+#define QPNP_OCV_OFFSET_GF 5896
+#define QPNP_VBAT_COEFF_22 6800
+#define QPNP_VBAT_COEFF_23 3500
+#define QPNP_VBAT_COEFF_24 4360
+#define QPNP_VBAT_COEFF_25 8060
static int32_t qpnp_ocv_comp(int64_t *result,
struct qpnp_vadc_chip *vadc, int64_t die_temp)
{
int64_t temp_var = 0;
int64_t old = *result;
- int32_t version;
+ int version;
- qpnp_temp_comp_version_check(vadc, &version);
+ version = qpnp_adc_get_revid_version(vadc->dev);
if (version == -EINVAL)
return 0;
- if (die_temp < 25000)
- return 0;
-
- if (die_temp > 60000)
- die_temp = 60000;
+ if (version == QPNP_REV_ID_8026_2_2) {
+ if (die_temp > 25000)
+ return 0;
+ }
switch (version) {
- case QPNP_VADC_REV_ID_8941_3_1:
+ case QPNP_REV_ID_8941_3_1:
switch (vadc->id) {
case COMP_ID_TSMC:
- temp_var = (((die_temp *
- (-QPNP_VBAT_COEFF_4))
- + QPNP_VBAT_COEFF_5));
+ temp_var = ((die_temp - 25000) *
+ (-QPNP_VBAT_COEFF_4));
break;
default:
case COMP_ID_GF:
- temp_var = (((die_temp *
- (-QPNP_VBAT_COEFF_1))
- + QPNP_VBAT_COEFF_2));
+ temp_var = ((die_temp - 25000) *
+ (-QPNP_VBAT_COEFF_1));
break;
}
break;
- case QPNP_VADC_REV_ID_8026_1_0:
+ case QPNP_REV_ID_8026_1_0:
switch (vadc->id) {
case COMP_ID_TSMC:
temp_var = (((die_temp *
@@ -583,19 +572,55 @@
break;
}
break;
- case QPNP_VADC_REV_ID_8026_2_0:
+ case QPNP_REV_ID_8026_2_0:
+ case QPNP_REV_ID_8026_2_1:
switch (vadc->id) {
case COMP_ID_TSMC:
- temp_var = ((die_temp - 2500) *
+ temp_var = ((die_temp - 25000) *
(-QPNP_VBAT_COEFF_10));
break;
default:
case COMP_ID_GF:
- temp_var = ((die_temp - 2500) *
+ temp_var = ((die_temp - 25000) *
(-QPNP_VBAT_COEFF_8));
break;
}
break;
+ case QPNP_REV_ID_8026_2_2:
+ switch (vadc->id) {
+ case COMP_ID_TSMC:
+ *result -= QPNP_VBAT_COEFF_22;
+ temp_var = (die_temp - 25000) *
+ QPNP_VBAT_COEFF_24;
+ break;
+ default:
+ case COMP_ID_GF:
+ *result -= QPNP_VBAT_COEFF_22;
+ temp_var = (die_temp - 25000) *
+ QPNP_VBAT_COEFF_25;
+ break;
+ }
+ case QPNP_REV_ID_8110_2_0:
+ switch (vadc->id) {
+ case COMP_ID_SMIC:
+ *result -= QPNP_OCV_OFFSET_SMIC;
+ if (die_temp < 25000)
+ temp_var = QPNP_VBAT_COEFF_18;
+ else
+ temp_var = QPNP_VBAT_COEFF_19;
+ temp_var = (die_temp - 25000) * temp_var;
+ break;
+ default:
+ case COMP_ID_GF:
+ *result -= QPNP_OCV_OFFSET_GF;
+ if (die_temp < 25000)
+ temp_var = QPNP_VBAT_COEFF_20;
+ else
+ temp_var = QPNP_VBAT_COEFF_21;
+ temp_var = (die_temp - 25000) * temp_var;
+ break;
+ }
+ break;
default:
temp_var = 0;
break;
@@ -618,35 +643,36 @@
{
int64_t temp_var = 0;
int64_t old = *result;
- int32_t version;
+ int version;
- qpnp_temp_comp_version_check(vadc, &version);
+ version = qpnp_adc_get_revid_version(vadc->dev);
if (version == -EINVAL)
return 0;
- if (die_temp < 25000)
- return 0;
-
- /* min(die_temp_c, 60_degC) */
- if (die_temp > 60000)
- die_temp = 60000;
+ if (version != QPNP_REV_ID_8941_3_1) {
+ /* min(die_temp_c, 60_degC) */
+ if (die_temp > 60000)
+ die_temp = 60000;
+ }
switch (version) {
- case QPNP_VADC_REV_ID_8941_3_1:
+ case QPNP_REV_ID_8941_3_1:
switch (vadc->id) {
case COMP_ID_TSMC:
- temp_var = (die_temp *
+ temp_var = ((die_temp - 25000) *
(-QPNP_VBAT_COEFF_1));
break;
default:
case COMP_ID_GF:
- temp_var = (((die_temp *
- (-QPNP_VBAT_COEFF_6))
- + QPNP_VBAT_COEFF_7));
+ /* min(die_temp_c, 60_degC) */
+ if (die_temp > 60000)
+ die_temp = 60000;
+ temp_var = ((die_temp - 25000) *
+ (-QPNP_VBAT_COEFF_1));
break;
}
break;
- case QPNP_VADC_REV_ID_8026_1_0:
+ case QPNP_REV_ID_8026_1_0:
switch (vadc->id) {
case COMP_ID_TSMC:
temp_var = (((die_temp *
@@ -661,19 +687,47 @@
break;
}
break;
- case QPNP_VADC_REV_ID_8026_2_0:
+ case QPNP_REV_ID_8026_2_0:
+ case QPNP_REV_ID_8026_2_1:
switch (vadc->id) {
case COMP_ID_TSMC:
- temp_var = ((die_temp - 2500) *
+ temp_var = ((die_temp - 25000) *
(-QPNP_VBAT_COEFF_11));
break;
default:
case COMP_ID_GF:
- temp_var = ((die_temp - 2500) *
+ temp_var = ((die_temp - 25000) *
(-QPNP_VBAT_COEFF_9));
break;
}
break;
+ case QPNP_REV_ID_8026_2_2:
+ switch (vadc->id) {
+ case COMP_ID_TSMC:
+ *result -= QPNP_VBAT_COEFF_23;
+ temp_var = 0;
+ break;
+ default:
+ case COMP_ID_GF:
+ *result -= QPNP_VBAT_COEFF_23;
+ temp_var = 0;
+ break;
+ }
+ case QPNP_REV_ID_8110_2_0:
+ switch (vadc->id) {
+ case COMP_ID_SMIC:
+ *result -= QPNP_VBAT_OFFSET_SMIC;
+ temp_var = ((die_temp - 25000) *
+ (QPNP_VBAT_COEFF_17));
+ break;
+ default:
+ case COMP_ID_GF:
+ *result -= QPNP_VBAT_OFFSET_GF;
+ temp_var = ((die_temp - 25000) *
+ (QPNP_VBAT_COEFF_16));
+ break;
+ }
+ break;
default:
temp_var = 0;
break;
@@ -692,7 +746,7 @@
}
int32_t qpnp_vbat_sns_comp_result(struct qpnp_vadc_chip *vadc,
- int64_t *result)
+ int64_t *result, bool is_pon_ocv)
{
struct qpnp_vadc_result die_temp_result;
int rc = 0;
@@ -708,7 +762,12 @@
return rc;
}
- rc = qpnp_ocv_comp(result, vadc, die_temp_result.physical);
+ if (is_pon_ocv)
+ rc = qpnp_ocv_comp(result, vadc, die_temp_result.physical);
+ else
+ rc = qpnp_vbat_sns_comp(result, vadc,
+ die_temp_result.physical);
+
if (rc < 0)
pr_err("Error with vbat compensation\n");
diff --git a/drivers/input/misc/mma8x5x.c b/drivers/input/misc/mma8x5x.c
index 29e2d41..d576752 100644
--- a/drivers/input/misc/mma8x5x.c
+++ b/drivers/input/misc/mma8x5x.c
@@ -79,6 +79,7 @@
#define MMA_INT_ROUTING_CFG 0x01
#define MMA_POWER_CFG_MASK 0xFE
+#define MMA_ODR_MASK 0x38
struct sensor_regulator {
struct regulator *vreg;
@@ -405,7 +406,7 @@
if (result < 0)
goto out;
- val = (u8)result | val;
+ val = ((u8)result & ~MMA_ODR_MASK) | val;
result = i2c_smbus_write_byte_data(client, MMA8X5X_CTRL_REG1,
(val & MMA_POWER_CFG_MASK));
if (result < 0)
@@ -502,9 +503,12 @@
{
struct mma8x5x_data *pdata = container_of((struct delayed_work *)work,
struct mma8x5x_data, dwork);
- mma8x5x_report_data(pdata);
- schedule_delayed_work(&pdata->dwork,
- msecs_to_jiffies(pdata->poll_delay));
+
+ if ((pdata->active & MMA_STATE_MASK) == MMA_ACTIVED) {
+ mma8x5x_report_data(pdata);
+ schedule_delayed_work(&pdata->dwork,
+ msecs_to_jiffies(pdata->poll_delay));
+ }
}
static irqreturn_t mma8x5x_interrupt(int vec, void *data)
@@ -569,9 +573,9 @@
dev_err(&client->dev, "change device state failed!");
goto err_failed;
}
-
- schedule_delayed_work(&pdata->dwork,
- msecs_to_jiffies(pdata->poll_delay));
+ if (!pdata->use_int)
+ schedule_delayed_work(&pdata->dwork,
+ msecs_to_jiffies(pdata->poll_delay));
pdata->active = MMA_ACTIVED;
dev_dbg(&client->dev, "%s:mma enable setting active.\n",
@@ -579,8 +583,6 @@
}
} else if (enable == 0) {
if (pdata->active == MMA_ACTIVED) {
- cancel_delayed_work_sync(&pdata->dwork);
-
val = i2c_smbus_read_byte_data(client,
MMA8X5X_CTRL_REG1);
if (val < 0) {
@@ -595,7 +597,10 @@
dev_err(&client->dev, "change device state failed!");
goto err_failed;
}
-
+ /*
+ * Set standby state,
+ * polling work queue will stop after next call.
+ */
pdata->active = MMA_STANDBY;
dev_dbg(&client->dev, "%s:mma enable setting inactive.\n",
__func__);
diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index cd6989c..be7c3c6 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -1983,6 +1983,7 @@
const char *buf, size_t count)
{
struct mxt_data *data = dev_get_drvdata(dev);
+ struct device *adapter = data->client->adapter->dev.parent;
unsigned long value;
int err = 0;
@@ -2000,7 +2001,7 @@
if (atomic_read(&data->st_enabled) == 0)
break;
- pm_runtime_put(data->client->adapter->dev.parent);
+ pm_runtime_put(adapter);
atomic_set(&data->st_enabled, 0);
mxt_secure_touch_notify(data);
mxt_interrupt(data->client->irq, data);
@@ -2012,7 +2013,7 @@
break;
}
- if (pm_runtime_get(data->client->adapter->dev.parent) < 0) {
+ if (pm_runtime_get_sync(adapter) < 0) {
dev_err(&data->client->dev, "pm_runtime_get failed\n");
err = -EIO;
break;
diff --git a/drivers/input/touchscreen/ft5x06_ts.c b/drivers/input/touchscreen/ft5x06_ts.c
index 4b8a3d4..1da84fa 100644
--- a/drivers/input/touchscreen/ft5x06_ts.c
+++ b/drivers/input/touchscreen/ft5x06_ts.c
@@ -3,7 +3,7 @@
* FocalTech ft5x06 TouchScreen driver.
*
* Copyright (c) 2010 Focal tech Ltd.
- * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2014, 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 @@
#define FT_REG_THGROUP 0x80
#define FT_REG_ECC 0xCC
#define FT_REG_RESET_FW 0x07
-#define FT_REG_FW_MAJ_VER 0xB1
#define FT_REG_FW_MIN_VER 0xB2
#define FT_REG_FW_SUB_MIN_VER 0xB3
@@ -289,7 +288,7 @@
u8 reg_addr;
int err;
- reg_addr = FT_REG_FW_MAJ_VER;
+ reg_addr = FT_REG_FW_VER;
err = ft5x06_i2c_read(client, ®_addr, 1, &data->fw_ver[0], 1);
if (err < 0)
dev_err(&client->dev, "fw major version read failed");
@@ -877,6 +876,11 @@
u8 fw_file_maj, fw_file_min, fw_file_sub_min;
bool fw_upgrade = false;
+ if (data->suspended) {
+ dev_info(dev, "Device is in suspend state: Exit FW upgrade\n");
+ return -EBUSY;
+ }
+
rc = request_firmware(&fw, data->fw_name, dev);
if (rc < 0) {
dev_err(dev, "Request firmware failed - %s (%d)\n",
@@ -899,17 +903,10 @@
dev_info(dev, "New firmware: %d.%d.%d", fw_file_maj,
fw_file_min, fw_file_sub_min);
- if (force) {
+ if (force)
fw_upgrade = true;
- } else if (data->fw_ver[0] == fw_file_maj) {
- if (data->fw_ver[1] < fw_file_min)
- fw_upgrade = true;
- else if (data->fw_ver[2] < fw_file_sub_min)
- fw_upgrade = true;
- else
- dev_info(dev, "No need to upgrade\n");
- } else
- dev_info(dev, "Firmware versions do not match\n");
+ else if (data->fw_ver[0] < fw_file_maj)
+ fw_upgrade = true;
if (!fw_upgrade) {
dev_info(dev, "Exiting fw upgrade...\n");
diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
index 8e70129..cb1dbd4 100644
--- a/drivers/iommu/Kconfig
+++ b/drivers/iommu/Kconfig
@@ -87,6 +87,16 @@
section mappings and TLB misses should be quite infrequent.
Most people can probably say Y here.
+config MSM_IOMMU_VBIF_CHECK
+ bool "Enable support for VBIF check when IOMMU gets stuck"
+ depends on MSM_IOMMU
+ help
+ Enables an extra check in the IOMMU driver that logs debugging
+ information when TLB sync or iommu halt issue occurs. This helps
+ in debugging such issues.
+
+ If unsure, say N here.
+
config IOMMU_NON_SECURE
bool "Turns on programming of secure SMMU by kernel"
depends on MSM_IOMMU
diff --git a/drivers/iommu/msm_iommu-v1.c b/drivers/iommu/msm_iommu-v1.c
index 84f81bf..50581f8 100644
--- a/drivers/iommu/msm_iommu-v1.c
+++ b/drivers/iommu/msm_iommu-v1.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
*
* This 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/platform_device.h>
#include <linux/errno.h>
#include <linux/io.h>
+#include <linux/iopoll.h>
#include <linux/interrupt.h>
#include <linux/list.h>
#include <linux/mutex.h>
@@ -143,13 +144,141 @@
.iommu_lock_release = _iommu_lock_release,
};
-void iommu_halt(const struct msm_iommu_drvdata *iommu_drvdata)
+#ifdef CONFIG_MSM_IOMMU_VBIF_CHECK
+
+#define VBIF_XIN_HALT_CTRL0 0x200
+#define VBIF_XIN_HALT_CTRL1 0x204
+#define VBIF_AXI_HALT_CTRL0 0x208
+#define VBIF_AXI_HALT_CTRL1 0x20C
+
+static void __halt_vbif_xin(void __iomem *vbif_base)
+{
+ pr_err("Halting VBIF_XIN\n");
+ writel_relaxed(0xFFFFFFFF, vbif_base + VBIF_XIN_HALT_CTRL0);
+}
+
+static void __dump_vbif_state(void __iomem *base, void __iomem *vbif_base)
+{
+ unsigned int reg_val;
+
+ reg_val = readl_relaxed(base + MICRO_MMU_CTRL);
+ pr_err("Value of SMMU_IMPLDEF_MICRO_MMU_CTRL = 0x%x\n", reg_val);
+
+ reg_val = readl_relaxed(vbif_base + VBIF_XIN_HALT_CTRL0);
+ pr_err("Value of VBIF_XIN_HALT_CTRL0 = 0x%x\n", reg_val);
+ reg_val = readl_relaxed(vbif_base + VBIF_XIN_HALT_CTRL1);
+ pr_err("Value of VBIF_XIN_HALT_CTRL1 = 0x%x\n", reg_val);
+ reg_val = readl_relaxed(vbif_base + VBIF_AXI_HALT_CTRL0);
+ pr_err("Value of VBIF_AXI_HALT_CTRL0 = 0x%x\n", reg_val);
+ reg_val = readl_relaxed(vbif_base + VBIF_AXI_HALT_CTRL1);
+ pr_err("Value of VBIF_AXI_HALT_CTRL1 = 0x%x\n", reg_val);
+}
+
+static int __check_vbif_state(struct msm_iommu_drvdata const *drvdata)
+{
+ phys_addr_t addr = (phys_addr_t) (drvdata->phys_base
+ - (phys_addr_t) 0x4000);
+ void __iomem *base = ioremap(addr, 0x1000);
+ int ret = 0;
+
+ if (base) {
+ __dump_vbif_state(drvdata->base, base);
+ __halt_vbif_xin(drvdata->base);
+ __dump_vbif_state(drvdata->base, base);
+ iounmap(base);
+ } else {
+ pr_err("%s: Unable to ioremap\n", __func__);
+ ret = -ENOMEM;
+ }
+ return ret;
+}
+
+static void check_halt_state(struct msm_iommu_drvdata const *drvdata)
+{
+ int res;
+ unsigned int val;
+ void __iomem *base = drvdata->base;
+ char const *name = drvdata->name;
+
+ pr_err("Timed out waiting for IOMMU halt to complete for %s\n", name);
+ res = __check_vbif_state(drvdata);
+ if (res)
+ BUG();
+
+ pr_err("Checking if IOMMU halt completed for %s\n", name);
+
+ res = readl_tight_poll_timeout(
+ GLB_REG(MICRO_MMU_CTRL, base), val,
+ (val & MMU_CTRL_IDLE) == MMU_CTRL_IDLE, 5000000);
+
+ if (res) {
+ pr_err("Timed out (again) waiting for IOMMU halt to complete for %s\n",
+ name);
+ } else {
+ pr_err("IOMMU halt completed. VBIF FIFO most likely not getting drained by master\n");
+ }
+ BUG();
+}
+
+static void check_tlb_sync_state(struct msm_iommu_drvdata const *drvdata,
+ int ctx)
+{
+ int res;
+ unsigned int val;
+ void __iomem *base = drvdata->base;
+ char const *name = drvdata->name;
+
+ pr_err("Timed out waiting for TLB SYNC to complete for %s\n", name);
+ res = __check_vbif_state(drvdata);
+ if (res)
+ BUG();
+
+ pr_err("Checking if TLB sync completed for %s\n", name);
+
+ res = readl_tight_poll_timeout(CTX_REG(CB_TLBSTATUS, base, ctx), val,
+ (val & CB_TLBSTATUS_SACTIVE) == 0, 5000000);
+ if (res) {
+ pr_err("Timed out (again) waiting for TLB SYNC to complete for %s\n",
+ name);
+ } else {
+ pr_err("TLB Sync completed. VBIF FIFO most likely not getting drained by master\n");
+ }
+ BUG();
+}
+
+#else
+
+/*
+ * For targets without VBIF or for targets with the VBIF check disabled
+ * we directly just crash to capture the issue
+ */
+static void check_halt_state(struct msm_iommu_drvdata const *drvdata)
+{
+ BUG();
+}
+
+static void check_tlb_sync_state(struct msm_iommu_drvdata const *drvdata,
+ int ctx)
+{
+ BUG();
+}
+
+#endif
+
+void iommu_halt(struct msm_iommu_drvdata const *iommu_drvdata)
{
if (iommu_drvdata->halt_enabled) {
- SET_MICRO_MMU_CTRL_HALT_REQ(iommu_drvdata->base, 1);
+ unsigned int val;
+ void __iomem *base = iommu_drvdata->base;
+ int res;
- while (GET_MICRO_MMU_CTRL_IDLE(iommu_drvdata->base) == 0)
- cpu_relax();
+ SET_MICRO_MMU_CTRL_HALT_REQ(base, 1);
+ res = readl_tight_poll_timeout(
+ GLB_REG(MICRO_MMU_CTRL, base), val,
+ (val & MMU_CTRL_IDLE) == MMU_CTRL_IDLE, 5000000);
+
+ if (res)
+ check_halt_state(iommu_drvdata);
/* Ensure device is idle before continuing */
mb();
}
@@ -173,15 +302,19 @@
}
}
-static void __sync_tlb(void __iomem *base, int ctx)
+static void __sync_tlb(struct msm_iommu_drvdata *iommu_drvdata, int ctx)
{
+ unsigned int val;
+ unsigned int res;
+ void __iomem *base = iommu_drvdata->base;
+
SET_TLBSYNC(base, ctx, 0);
-
- /* No barrier needed due to register proximity */
- while (GET_CB_TLBSTATUS_SACTIVE(base, ctx))
- cpu_relax();
-
/* No barrier needed due to read dependency */
+
+ res = readl_tight_poll_timeout(CTX_REG(CB_TLBSTATUS, base, ctx), val,
+ (val & CB_TLBSTATUS_SACTIVE) == 0, 5000000);
+ if (res)
+ check_tlb_sync_state(iommu_drvdata, ctx);
}
static int __flush_iotlb_va(struct iommu_domain *domain, unsigned int va)
@@ -205,7 +338,7 @@
SET_TLBIVA(iommu_drvdata->base, ctx_drvdata->num,
ctx_drvdata->asid | (va & CB_TLBIVA_VA));
mb();
- __sync_tlb(iommu_drvdata->base, ctx_drvdata->num);
+ __sync_tlb(iommu_drvdata, ctx_drvdata->num);
__disable_clocks(iommu_drvdata);
}
fail:
@@ -232,7 +365,7 @@
SET_TLBIASID(iommu_drvdata->base, ctx_drvdata->num,
ctx_drvdata->asid);
mb();
- __sync_tlb(iommu_drvdata->base, ctx_drvdata->num);
+ __sync_tlb(iommu_drvdata, ctx_drvdata->num);
__disable_clocks(iommu_drvdata);
}
@@ -343,15 +476,14 @@
mb();
}
-static void __release_smg(void __iomem *base, int ctx)
+static void __release_smg(void __iomem *base)
{
int i, smt_size;
smt_size = GET_IDR0_NUMSMRG(base);
- /* Invalidate any SMGs associated with this context */
+ /* Invalidate all SMGs */
for (i = 0; i < smt_size; i++)
- if (GET_SMR_VALID(base, i) &&
- GET_S2CR_CBNDX(base, i) == ctx)
+ if (GET_SMR_VALID(base, i))
SET_SMR_VALID(base, i, 0);
}
@@ -394,17 +526,52 @@
}
}
+
+static int program_m2v_table(struct device *dev, void __iomem *base)
+{
+ struct msm_iommu_ctx_drvdata *ctx_drvdata = dev_get_drvdata(dev);
+ u32 *sids = ctx_drvdata->sids;
+ unsigned int ctx = ctx_drvdata->num;
+ int num = 0, i, smt_size;
+ int len = ctx_drvdata->nsid;
+
+ smt_size = GET_IDR0_NUMSMRG(base);
+ /* Program the M2V tables for this context */
+ for (i = 0; i < len / sizeof(*sids); i++) {
+ for (; num < smt_size; num++)
+ if (GET_SMR_VALID(base, num) == 0)
+ break;
+ BUG_ON(num >= smt_size);
+
+ SET_SMR_VALID(base, num, 1);
+ SET_SMR_MASK(base, num, 0);
+ SET_SMR_ID(base, num, sids[i]);
+
+ SET_S2CR_N(base, num, 0);
+ SET_S2CR_CBNDX(base, num, ctx);
+ SET_S2CR_MEMATTR(base, num, 0x0A);
+ /* Set security bit override to be Non-secure */
+ SET_S2CR_NSCFG(base, num, 3);
+ }
+
+ return 0;
+}
+
+static void program_all_m2v_tables(struct msm_iommu_drvdata *iommu_drvdata)
+{
+ device_for_each_child(iommu_drvdata->dev, iommu_drvdata->base,
+ program_m2v_table);
+}
+
static void __program_context(struct msm_iommu_drvdata *iommu_drvdata,
struct msm_iommu_ctx_drvdata *ctx_drvdata,
- struct msm_iommu_priv *priv, bool is_secure)
+ struct msm_iommu_priv *priv, bool is_secure,
+ bool program_m2v)
{
unsigned int prrr, nmrr;
- unsigned int pn;
- int num = 0, i, smt_size;
+ phys_addr_t pn;
void __iomem *base = iommu_drvdata->base;
unsigned int ctx = ctx_drvdata->num;
- u32 *sids = ctx_drvdata->sids;
- int len = ctx_drvdata->nsid;
phys_addr_t pgtable = __pa(priv->pt.fl_table);
__reset_context(base, ctx);
@@ -445,24 +612,9 @@
}
if (!is_secure) {
- smt_size = GET_IDR0_NUMSMRG(base);
- /* Program the M2V tables for this context */
- for (i = 0; i < len / sizeof(*sids); i++) {
- for (; num < smt_size; num++)
- if (GET_SMR_VALID(base, num) == 0)
- break;
- BUG_ON(num >= smt_size);
+ if (program_m2v)
+ program_all_m2v_tables(iommu_drvdata);
- SET_SMR_VALID(base, num, 1);
- SET_SMR_MASK(base, num, 0);
- SET_SMR_ID(base, num, sids[i]);
-
- SET_S2CR_N(base, num, 0);
- SET_S2CR_CBNDX(base, num, ctx);
- SET_S2CR_MEMATTR(base, num, 0x0A);
- /* Set security bit override to be Non-secure */
- SET_S2CR_NSCFG(base, num, 3);
- }
SET_CBAR_N(base, ctx, 0);
/* Stage 1 Context with Stage 2 bypass */
@@ -534,49 +686,55 @@
struct msm_iommu_drvdata *iommu_drvdata;
struct msm_iommu_ctx_drvdata *ctx_drvdata;
struct msm_iommu_ctx_drvdata *tmp_drvdata;
- int ret;
+ int ret = 0;
int is_secure;
+ bool set_m2v = false;
mutex_lock(&msm_iommu_lock);
priv = domain->priv;
if (!priv || !dev) {
ret = -EINVAL;
- goto fail;
+ goto unlock;
}
iommu_drvdata = dev_get_drvdata(dev->parent);
ctx_drvdata = dev_get_drvdata(dev);
if (!iommu_drvdata || !ctx_drvdata) {
ret = -EINVAL;
- goto fail;
+ goto unlock;
}
+ ++ctx_drvdata->attach_count;
+
+ if (ctx_drvdata->attach_count > 1)
+ goto already_attached;
+
if (!list_empty(&ctx_drvdata->attached_elm)) {
ret = -EBUSY;
- goto fail;
+ goto unlock;
}
list_for_each_entry(tmp_drvdata, &priv->list_attached, attached_elm)
if (tmp_drvdata == ctx_drvdata) {
ret = -EBUSY;
- goto fail;
+ goto unlock;
}
is_secure = iommu_drvdata->sec_id != -1;
ret = __enable_regulators(iommu_drvdata);
if (ret)
- goto fail;
+ goto unlock;
ret = apply_bus_vote(iommu_drvdata, 1);
if (ret)
- goto fail;
+ goto unlock;
ret = __enable_clocks(iommu_drvdata);
if (ret) {
__disable_regulators(iommu_drvdata);
- goto fail;
+ goto unlock;
}
/* We can only do this once */
@@ -591,16 +749,17 @@
if (ret) {
__disable_regulators(iommu_drvdata);
__disable_clocks(iommu_drvdata);
- goto fail;
+ goto unlock;
}
}
program_iommu_bfb_settings(iommu_drvdata->base,
iommu_drvdata->bfb_settings);
+ set_m2v = true;
}
iommu_halt(iommu_drvdata);
- __program_context(iommu_drvdata, ctx_drvdata, priv, is_secure);
+ __program_context(iommu_drvdata, ctx_drvdata, priv, is_secure, set_m2v);
iommu_resume(iommu_drvdata);
@@ -610,11 +769,12 @@
ctx_drvdata->attached_domain = domain;
++iommu_drvdata->ctx_attach_count;
+already_attached:
mutex_unlock(&msm_iommu_lock);
msm_iommu_attached(dev->parent);
return ret;
-fail:
+unlock:
mutex_unlock(&msm_iommu_lock);
return ret;
}
@@ -633,16 +793,22 @@
mutex_lock(&msm_iommu_lock);
priv = domain->priv;
if (!priv || !dev)
- goto fail;
+ goto unlock;
iommu_drvdata = dev_get_drvdata(dev->parent);
ctx_drvdata = dev_get_drvdata(dev);
if (!iommu_drvdata || !ctx_drvdata || !ctx_drvdata->attached_domain)
- goto fail;
+ goto unlock;
+
+ --ctx_drvdata->attach_count;
+ BUG_ON(ctx_drvdata->attach_count < 0);
+
+ if (ctx_drvdata->attach_count > 0)
+ goto unlock;
ret = __enable_clocks(iommu_drvdata);
if (ret)
- goto fail;
+ goto unlock;
is_secure = iommu_drvdata->sec_id != -1;
@@ -652,13 +818,15 @@
iommu_drvdata->asid[ctx_drvdata->asid - 1]--;
ctx_drvdata->asid = -1;
- iommu_halt(iommu_drvdata);
-
__reset_context(iommu_drvdata->base, ctx_drvdata->num);
- if (!is_secure)
- __release_smg(iommu_drvdata->base, ctx_drvdata->num);
- iommu_resume(iommu_drvdata);
+ /*
+ * Only reset the M2V tables on the very last detach */
+ if (!is_secure && iommu_drvdata->ctx_attach_count == 1) {
+ iommu_halt(iommu_drvdata);
+ __release_smg(iommu_drvdata->base);
+ iommu_resume(iommu_drvdata);
+ }
__disable_clocks(iommu_drvdata);
@@ -670,7 +838,7 @@
ctx_drvdata->attached_domain = NULL;
BUG_ON(iommu_drvdata->ctx_attach_count == 0);
--iommu_drvdata->ctx_attach_count;
-fail:
+unlock:
mutex_unlock(&msm_iommu_lock);
}
diff --git a/drivers/iommu/msm_iommu_dev-v1.c b/drivers/iommu/msm_iommu_dev-v1.c
index a9d164e..ff6b58c 100644
--- a/drivers/iommu/msm_iommu_dev-v1.c
+++ b/drivers/iommu/msm_iommu_dev-v1.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -299,6 +299,7 @@
if (!drvdata->base)
return -ENOMEM;
+ drvdata->phys_base = r->start;
drvdata->glb_base = drvdata->base;
if (of_get_property(pdev->dev.of_node, "vdd-supply", NULL)) {
diff --git a/drivers/leds/leds-qpnp.c b/drivers/leds/leds-qpnp.c
index d87520f..eba5ca8 100644
--- a/drivers/leds/leds-qpnp.c
+++ b/drivers/leds/leds-qpnp.c
@@ -691,7 +691,7 @@
{
int rc;
u8 val;
- int duty_us;
+ int duty_us, duty_ns, period_us;
if (led->cdev.brightness) {
if (led->cdev.brightness < led->mpp_cfg->min_brightness) {
@@ -710,13 +710,23 @@
}
}
if (led->mpp_cfg->pwm_mode == PWM_MODE) {
- pwm_disable(led->mpp_cfg->pwm_cfg->pwm_dev);
- duty_us = (led->mpp_cfg->pwm_cfg->pwm_period_us *
- led->cdev.brightness) / LED_FULL;
/*config pwm for brightness scaling*/
- rc = pwm_config_us(led->mpp_cfg->pwm_cfg->pwm_dev,
+ period_us = led->mpp_cfg->pwm_cfg->pwm_period_us;
+ if (period_us > INT_MAX / NSEC_PER_USEC) {
+ duty_us = (period_us * led->cdev.brightness) /
+ LED_FULL;
+ rc = pwm_config_us(
+ led->mpp_cfg->pwm_cfg->pwm_dev,
duty_us,
- led->mpp_cfg->pwm_cfg->pwm_period_us);
+ period_us);
+ } else {
+ duty_ns = ((period_us * NSEC_PER_USEC) /
+ LED_FULL) * led->cdev.brightness;
+ rc = pwm_config(
+ led->mpp_cfg->pwm_cfg->pwm_dev,
+ duty_ns,
+ period_us * NSEC_PER_USEC);
+ }
if (rc < 0) {
dev_err(&led->spmi_dev->dev, "Failed to " \
"configure pwm for new values\n");
@@ -1219,8 +1229,8 @@
static int qpnp_kpdbl_set(struct qpnp_led_data *led)
{
- int duty_us;
int rc;
+ int duty_us, duty_ns, period_us;
if (led->cdev.brightness) {
if (!led->kpdbl_cfg->pwm_cfg->blinking)
@@ -1237,11 +1247,22 @@
}
if (led->kpdbl_cfg->pwm_cfg->mode == PWM_MODE) {
- duty_us = (led->kpdbl_cfg->pwm_cfg->pwm_period_us *
- led->cdev.brightness) / KPDBL_MAX_LEVEL;
- rc = pwm_config_us(led->kpdbl_cfg->pwm_cfg->pwm_dev,
+ period_us = led->kpdbl_cfg->pwm_cfg->pwm_period_us;
+ if (period_us > INT_MAX / NSEC_PER_USEC) {
+ duty_us = (period_us * led->cdev.brightness) /
+ KPDBL_MAX_LEVEL;
+ rc = pwm_config_us(
+ led->kpdbl_cfg->pwm_cfg->pwm_dev,
duty_us,
- led->kpdbl_cfg->pwm_cfg->pwm_period_us);
+ period_us);
+ } else {
+ duty_ns = ((period_us * NSEC_PER_USEC) /
+ KPDBL_MAX_LEVEL) * led->cdev.brightness;
+ rc = pwm_config(
+ led->kpdbl_cfg->pwm_cfg->pwm_dev,
+ duty_ns,
+ period_us * NSEC_PER_USEC);
+ }
if (rc < 0) {
dev_err(&led->spmi_dev->dev, "pwm config failed\n");
return rc;
@@ -1262,7 +1283,7 @@
if (led->kpdbl_cfg->always_on) {
rc = pwm_config_us(led->kpdbl_cfg->pwm_cfg->pwm_dev, 0,
- led->kpdbl_cfg->pwm_cfg->pwm_period_us);
+ led->kpdbl_cfg->pwm_cfg->pwm_period_us);
if (rc < 0) {
dev_err(&led->spmi_dev->dev,
"pwm config failed\n");
@@ -1300,19 +1321,30 @@
static int qpnp_rgb_set(struct qpnp_led_data *led)
{
- int duty_us;
int rc;
+ int duty_us, duty_ns, period_us;
if (led->cdev.brightness) {
if (!led->rgb_cfg->pwm_cfg->blinking)
led->rgb_cfg->pwm_cfg->mode =
led->rgb_cfg->pwm_cfg->default_mode;
if (led->rgb_cfg->pwm_cfg->mode == PWM_MODE) {
- duty_us = (led->rgb_cfg->pwm_cfg->pwm_period_us *
- led->cdev.brightness) / LED_FULL;
- rc = pwm_config_us(led->rgb_cfg->pwm_cfg->pwm_dev,
+ period_us = led->rgb_cfg->pwm_cfg->pwm_period_us;
+ if (period_us > INT_MAX / NSEC_PER_USEC) {
+ duty_us = (period_us * led->cdev.brightness) /
+ LED_FULL;
+ rc = pwm_config_us(
+ led->rgb_cfg->pwm_cfg->pwm_dev,
duty_us,
- led->rgb_cfg->pwm_cfg->pwm_period_us);
+ period_us);
+ } else {
+ duty_ns = ((period_us * NSEC_PER_USEC) /
+ LED_FULL) * led->cdev.brightness;
+ rc = pwm_config(
+ led->rgb_cfg->pwm_cfg->pwm_dev,
+ duty_ns,
+ period_us * NSEC_PER_USEC);
+ }
if (rc < 0) {
dev_err(&led->spmi_dev->dev,
"pwm config failed\n");
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c
index e443e9a..c3fb176 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c
@@ -13,7 +13,7 @@
#include <linux/module.h>
#include <mach/iommu.h>
#include <linux/ratelimit.h>
-
+#include <asm/div64.h>
#include "msm_isp40.h"
#include "msm_isp_util.h"
#include "msm_isp_axi_util.h"
@@ -36,8 +36,8 @@
#define VFE40_8x26_VERSION 0x20000013
#define VFE40_8x26V2_VERSION 0x20010014
-#define VFE40_BURST_LEN 3
-#define VFE40_STATS_BURST_LEN 2
+#define VFE40_BURST_LEN 1
+#define VFE40_STATS_BURST_LEN 1
#define VFE40_UB_SIZE 1536
#define VFE40_EQUAL_SLICE_UB 228
#define VFE40_WM_BASE(idx) (0x6C + 0x24 * idx)
@@ -1078,7 +1078,6 @@
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) {
@@ -1090,9 +1089,11 @@
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;
+ uint64_t delta = 0;
+ uint64_t temp = (uint64_t)axi_data->wm_image_size[i] *
+ (uint64_t)prop_size;
+ do_div(temp, total_image_size);
+ delta = temp;
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);
@@ -1119,7 +1120,7 @@
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;
+ axi_data->wm_ub_cfg_policy = MSM_WM_UB_CFG_DEFAULT;
if (axi_data->wm_ub_cfg_policy == MSM_WM_UB_EQUAL_SLICING)
msm_vfe40_cfg_axi_ub_equal_slicing(vfe_dev);
else
@@ -1408,8 +1409,8 @@
}
static struct msm_vfe_axi_hardware_info msm_vfe40_axi_hw_info = {
- .num_wm = 5,
- .num_comp_mask = 2,
+ .num_wm = 7,
+ .num_comp_mask = 3,
.num_rdi = 3,
.num_rdi_master = 3,
.min_wm_ub = 64,
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c
index 206620c..a60fa09 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c
@@ -1294,7 +1294,15 @@
rc = msm_isp_axi_wait_for_cfg_done(vfe_dev, camif_update);
if (rc < 0) {
pr_err("%s: wait for config done failed\n", __func__);
- return rc;
+ for (i = 0; i < stream_cfg_cmd->num_streams; i++) {
+ stream_info = &axi_data->stream_info[
+ HANDLE_TO_IDX(
+ stream_cfg_cmd->stream_handle[i])];
+ stream_info->state = STOP_PENDING;
+ msm_isp_axi_stream_enable_cfg(
+ vfe_dev, stream_info);
+ stream_info->state = INACTIVE;
+ }
}
}
msm_isp_update_stream_bandwidth(vfe_dev);
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c
index ffe0b9c..cb46e9c 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c
@@ -13,6 +13,7 @@
#include <linux/io.h>
#include <media/v4l2-subdev.h>
#include <linux/ratelimit.h>
+#include <asm/div64.h>
#include "msm.h"
#include "msm_isp_util.h"
@@ -24,11 +25,10 @@
static DEFINE_MUTEX(bandwidth_mgr_mutex);
static struct msm_isp_bandwidth_mgr isp_bandwidth_mgr;
-#define MSM_ISP_MIN_AB 300000000
-#define MSM_ISP_MIN_IB 450000000
+#define MSM_ISP_MIN_AB 450000000
+#define MSM_ISP_MIN_IB 900000000
#define VFE40_8974V2_VERSION 0x1001001A
-
static struct msm_bus_vectors msm_isp_init_vectors[] = {
{
.src = MSM_BUS_MASTER_VFE,
@@ -217,7 +217,8 @@
{
uint32_t avtimer_msw_1st = 0, avtimer_lsw = 0;
uint32_t avtimer_msw_2nd = 0;
- uint8_t iter = 0;
+ uint64_t av_timer_tick = 0;
+
if (!vfe_dev->p_avtimer_msw || !vfe_dev->p_avtimer_lsw) {
pr_err("%s: ioremap failed\n", __func__);
return;
@@ -226,15 +227,10 @@
avtimer_msw_1st = msm_camera_io_r(vfe_dev->p_avtimer_msw);
avtimer_lsw = msm_camera_io_r(vfe_dev->p_avtimer_lsw);
avtimer_msw_2nd = msm_camera_io_r(vfe_dev->p_avtimer_msw);
- } while ((avtimer_msw_1st != avtimer_msw_2nd)
- && (iter++ < AVTIMER_ITERATION_CTR));
- /*Just return if the MSW TimeStamps don't converge after
- a few iterations Application needs to handle the zero TS values*/
- if (iter >= AVTIMER_ITERATION_CTR) {
- pr_err("%s: AVTimer MSW TS did not converge !!!\n", __func__);
- return;
- }
- time_stamp->vt_time.tv_sec = avtimer_msw_1st;
+ } while (avtimer_msw_1st != avtimer_msw_2nd);
+ av_timer_tick = ((uint64_t)avtimer_msw_1st << 32) | avtimer_lsw;
+ avtimer_lsw = do_div(av_timer_tick, USEC_PER_SEC);
+ time_stamp->vt_time.tv_sec = (uint32_t)(av_timer_tick);
time_stamp->vt_time.tv_usec = avtimer_lsw;
}
diff --git a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.c b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.c
index 87ad994..279a7dd 100755
--- a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.c
@@ -169,6 +169,7 @@
if (rc < 0) {
pr_err("%s Default sensor position %d\n", __func__, __LINE__);
sensordata->sensor_info->position = 0;
+ rc = 0;
}
rc = of_property_read_u32(of_node, "qcom,sensor-mode",
@@ -178,6 +179,7 @@
if (rc < 0) {
pr_err("%s Default sensor mode %d\n", __func__, __LINE__);
sensordata->sensor_info->modes_supported = 0;
+ rc = 0;
}
rc = msm_sensor_get_dt_csi_data(of_node, &sensordata->csi_lane_params);
@@ -573,6 +575,10 @@
s_ctrl->sensordata->sensor_info->is_mount_angle_valid;
cdata->cfg.sensor_info.sensor_mount_angle =
s_ctrl->sensordata->sensor_info->sensor_mount_angle;
+ cdata->cfg.sensor_info.position =
+ s_ctrl->sensordata->sensor_info->position;
+ cdata->cfg.sensor_info.modes_supported =
+ s_ctrl->sensordata->sensor_info->modes_supported;
CDBG("%s:%d sensor name %s\n", __func__, __LINE__,
cdata->cfg.sensor_info.sensor_name);
CDBG("%s:%d session id %d\n", __func__, __LINE__,
diff --git a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c
index f5be347..772ed0e 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c
@@ -27,6 +27,8 @@
#define CDBG(fmt, args...) pr_debug(fmt, ##args)
#endif
+#define SENSOR_MAX_MOUNTANGLE (360)
+
/* Static declaration */
static struct msm_sensor_ctrl_t *g_sctrl[MAX_CAMERAS];
@@ -260,6 +262,58 @@
return rc;
}
+static int32_t msm_sensor_fill_slave_info_init_params(
+ struct msm_camera_sensor_slave_info *slave_info,
+ struct msm_sensor_info_t *sensor_info)
+{
+ struct msm_sensor_init_params *sensor_init_params;
+ if (!slave_info || !sensor_info)
+ return -EINVAL;
+
+ if (!slave_info->is_init_params_valid)
+ return 0;
+
+ sensor_init_params = &slave_info->sensor_init_params;
+ if (INVALID_CAMERA_B != sensor_init_params->position)
+ sensor_info->position =
+ sensor_init_params->position;
+
+ if (SENSOR_MAX_MOUNTANGLE > sensor_init_params->sensor_mount_angle) {
+ sensor_info->sensor_mount_angle =
+ sensor_init_params->sensor_mount_angle;
+ sensor_info->is_mount_angle_valid = 1;
+ }
+
+ if (CAMERA_MODE_INVALID != sensor_init_params->modes_supported)
+ sensor_info->modes_supported =
+ sensor_init_params->modes_supported;
+
+ return 0;
+}
+
+
+static int32_t msm_sensor_validate_slave_info(
+ struct msm_sensor_info_t *sensor_info)
+{
+ if (INVALID_CAMERA_B == sensor_info->position) {
+ sensor_info->position = BACK_CAMERA_B;
+ pr_err("%s Set dafault sensor position%d\n",
+ __func__, __LINE__);
+ }
+ if (CAMERA_MODE_INVALID == sensor_info->modes_supported) {
+ sensor_info->modes_supported = CAMERA_MODE_2D_B;
+ pr_err("%s Set dafault sensor modes_supported%d\n",
+ __func__, __LINE__);
+ }
+ if (SENSOR_MAX_MOUNTANGLE < sensor_info->sensor_mount_angle) {
+ sensor_info->sensor_mount_angle = 0;
+ pr_err("%s Set dafault sensor mount angle%d\n",
+ __func__, __LINE__);
+ sensor_info->is_mount_angle_valid = 1;
+ }
+ return 0;
+}
+
/* static function definition */
int32_t msm_sensor_driver_probe(void *setting)
{
@@ -274,6 +328,7 @@
struct msm_camera_power_ctrl_t *power_info = NULL;
int c, end;
struct msm_sensor_power_setting power_down_setting_t;
+ unsigned long mount_pos = 0;
/* Validate input parameters */
if (!setting) {
@@ -304,6 +359,13 @@
CDBG("size %d", slave_info->power_setting_array.size);
CDBG("size down %d", slave_info->power_setting_array.size_down);
+ if (slave_info->is_init_params_valid) {
+ CDBG("position %d",
+ slave_info->sensor_init_params.position);
+ CDBG("mount %d",
+ slave_info->sensor_init_params.sensor_mount_angle);
+ }
+
/* Validate camera id */
if (slave_info->camera_id >= MAX_CAMERAS) {
pr_err("failed: invalid camera id %d max %d",
@@ -537,6 +599,25 @@
/* Power down */
s_ctrl->func_tbl->sensor_power_down(s_ctrl);
+ rc = msm_sensor_fill_slave_info_init_params(
+ slave_info,
+ s_ctrl->sensordata->sensor_info);
+ if (rc < 0) {
+ pr_err("%s Fill slave info failed", slave_info->sensor_name);
+ goto FREE_CAMERA_INFO;
+ }
+ rc = msm_sensor_validate_slave_info(s_ctrl->sensordata->sensor_info);
+ if (rc < 0) {
+ pr_err("%s Validate slave info failed",
+ slave_info->sensor_name);
+ goto FREE_CAMERA_INFO;
+ }
+ /* Update sensor mount angle and position in media entity flag */
+ mount_pos = s_ctrl->sensordata->sensor_info->position << 16;
+ mount_pos = mount_pos | ((s_ctrl->sensordata->sensor_info->
+ sensor_mount_angle / 90) << 8);
+ s_ctrl->msm_sd.sd.entity.flags = mount_pos | MEDIA_ENT_FL_DEFAULT;
+
/*Save sensor info*/
s_ctrl->sensordata->cam_slave_info = slave_info;
@@ -707,6 +788,23 @@
sensordata->sensor_info->is_mount_angle_valid = 1;
}
+ rc = of_property_read_u32(of_node, "qcom,sensor-position",
+ &sensordata->sensor_info->position);
+ if (rc < 0) {
+ pr_err("%s:%d Invalid sensor position\n", __func__, __LINE__);
+ sensordata->sensor_info->position = INVALID_CAMERA_B;
+ rc = 0;
+ }
+
+ rc = of_property_read_u32(of_node, "qcom,sensor-mode",
+ &sensordata->sensor_info->modes_supported);
+ if (rc < 0) {
+ pr_err("%s:%d Invalid sensor mode supported\n",
+ __func__, __LINE__);
+ sensordata->sensor_info->modes_supported = CAMERA_MODE_INVALID;
+ rc = 0;
+ }
+
/* Get vdd-cx regulator */
/*Optional property, don't return error if absent */
of_property_read_string(of_node, "qcom,vdd-cx-name",
diff --git a/drivers/media/platform/msm/vidc/hfi_packetization.c b/drivers/media/platform/msm/vidc/hfi_packetization.c
index 4b7a3be..cdc649f 100644
--- a/drivers/media/platform/msm/vidc/hfi_packetization.c
+++ b/drivers/media/platform/msm/vidc/hfi_packetization.c
@@ -1479,10 +1479,18 @@
pr_err("MARK LTR\n");
break;
}
- case HAL_PARAM_VENC_HIER_P_NUM_FRAMES:
+ case HAL_PARAM_VENC_HIER_P_MAX_ENH_LAYERS:
{
pkt->rg_property_data[0] =
- HFI_PROPERTY_PARAM_VENC_HIER_P_NUM_ENH_LAYER;
+ HFI_PROPERTY_PARAM_VENC_HIER_P_MAX_NUM_ENH_LAYER;
+ pkt->rg_property_data[1] = *(u32 *)pdata;
+ pkt->size += sizeof(u32) * 2;
+ break;
+ }
+ case HAL_CONFIG_VENC_HIER_P_NUM_FRAMES:
+ {
+ pkt->rg_property_data[0] =
+ HFI_PROPERTY_CONFIG_VENC_HIER_P_ENH_LAYER;
pkt->rg_property_data[1] = *(u32 *)pdata;
pkt->size += sizeof(u32) * 2;
break;
diff --git a/drivers/media/platform/msm/vidc/msm_vdec.c b/drivers/media/platform/msm/vidc/msm_vdec.c
index ea7d670..66d6878 100644
--- a/drivers/media/platform/msm/vidc/msm_vdec.c
+++ b/drivers/media/platform/msm/vidc/msm_vdec.c
@@ -693,11 +693,20 @@
int rc = 0;
int i;
struct hal_buffer_requirements *buff_req_buffer;
+
if (!inst || !f || !inst->core || !inst->core->device) {
dprintk(VIDC_ERR,
"Invalid input, inst = %p, format = %p\n", inst, f);
return -EINVAL;
}
+
+ rc = msm_comm_try_get_bufreqs(inst);
+ if (rc) {
+ dprintk(VIDC_ERR, "Getting buffer requirements failed: %d\n",
+ rc);
+ return rc;
+ }
+
hdev = inst->core->device;
if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
fmt = inst->fmts[CAPTURE_PORT];
diff --git a/drivers/media/platform/msm/vidc/msm_venc.c b/drivers/media/platform/msm/vidc/msm_venc.c
index d8b608437..030aa29 100644
--- a/drivers/media/platform/msm/vidc/msm_venc.c
+++ b/drivers/media/platform/msm/vidc/msm_venc.c
@@ -116,6 +116,7 @@
"Extradata input crop",
"Extradata digital zoom",
"Extradata aspect ratio",
+ "Extradata LTR",
"Extradata macroblock metadata",
};
@@ -1000,11 +1001,53 @@
return rc;
}
+static int msm_venc_enable_hier_p(struct msm_vidc_inst *inst)
+{
+ int num_enh_layers = 0;
+ u32 property_id = 0;
+ struct hfi_device *hdev = NULL;
+ int rc = 0;
+
+ if (!inst || !inst->core || !inst->core->device) {
+ dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
+ return -EINVAL;
+ }
+
+ if (inst->fmts[CAPTURE_PORT]->fourcc != V4L2_PIX_FMT_VP8)
+ return 0;
+
+ num_enh_layers = inst->capability.hier_p.max - 1;
+ if (!num_enh_layers)
+ return 0;
+
+ hdev = inst->core->device;
+ property_id = HAL_PARAM_VENC_HIER_P_MAX_ENH_LAYERS;
+
+ rc = call_hfi_op(hdev, session_set_property,
+ (void *)inst->session, property_id,
+ (void *)&num_enh_layers);
+ if (rc) {
+ dprintk(VIDC_ERR,
+ "%s: failed with error = %d\n", __func__, rc);
+ }
+ return rc;
+}
+
static inline int start_streaming(struct msm_vidc_inst *inst)
{
int rc = 0;
struct vb2_buf_entry *temp;
struct list_head *ptr, *next;
+
+ if (!inst || !inst->core || !inst->core->device) {
+ dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
+ return -EINVAL;
+ }
+
+ rc = msm_venc_enable_hier_p(inst);
+ if (rc)
+ return rc;
+
if (inst->capability.pixelprocess_capabilities &
HAL_VIDEO_ENCODER_SCALING_CAPABILITY)
rc = msm_comm_check_scaling_supported(inst);
@@ -2101,7 +2144,7 @@
pdata = &markltr;
break;
case V4L2_CID_MPEG_VIDC_VIDEO_HIER_P_NUM_LAYERS:
- property_id = HAL_PARAM_VENC_HIER_P_NUM_FRAMES;
+ property_id = HAL_CONFIG_VENC_HIER_P_NUM_FRAMES;
hier_p_layers = ctrl->val;
if (hier_p_layers > (inst->capability.hier_p.max - 1)) {
dprintk(VIDC_ERR,
@@ -2614,6 +2657,14 @@
"Invalid input, inst = %p, format = %p\n", inst, f);
return -EINVAL;
}
+
+ rc = msm_comm_try_get_bufreqs(inst);
+ if (rc) {
+ dprintk(VIDC_WARN, "Getting new buffer requirements failed: %d\n",
+ rc);
+ return rc;
+ }
+
if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
fmt = inst->fmts[CAPTURE_PORT];
height = inst->prop.height[CAPTURE_PORT];
@@ -2643,8 +2694,16 @@
buff_req_buffer->buffer_size : 0;
}
for (i = 0; i < fmt->num_planes; ++i) {
- inst->bufq[CAPTURE_PORT].vb2_bufq.plane_sizes[i] =
- f->fmt.pix_mp.plane_fmt[i].sizeimage;
+ if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ inst->bufq[OUTPUT_PORT].vb2_bufq.
+ plane_sizes[i] =
+ f->fmt.pix_mp.plane_fmt[i].sizeimage;
+ } else if (f->type ==
+ V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ inst->bufq[CAPTURE_PORT].vb2_bufq.
+ plane_sizes[i] =
+ f->fmt.pix_mp.plane_fmt[i].sizeimage;
+ }
}
} else {
dprintk(VIDC_ERR,
diff --git a/drivers/media/platform/msm/vidc/msm_vidc.c b/drivers/media/platform/msm/vidc/msm_vidc.c
index f0721c3..9dbecfb 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc.c
@@ -1343,7 +1343,6 @@
mutex_lock(&inst->lock);
}
mutex_unlock(&inst->lock);
- msm_smem_delete_client(inst->mem_client);
debugfs_remove_recursive(inst->debugfs_root);
}
}
@@ -1401,7 +1400,9 @@
for (i = 0; i < MAX_PORT_NUM; i++)
vb2_queue_release(&inst->bufq[i].vb2_bufq);
+ msm_smem_delete_client(inst->mem_client);
pr_info(VIDC_DBG_TAG "Closed video instance: %p\n", VIDC_INFO, inst);
kfree(inst);
+
return 0;
}
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.c b/drivers/media/platform/msm/vidc/msm_vidc_common.c
index 70114de..3430311 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_common.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_common.c
@@ -1196,6 +1196,8 @@
vb->v4l2_buf.flags |= V4L2_QCOM_BUF_DATA_CORRUPT;
if (fill_buf_done->flags1 & HAL_BUFFERFLAG_DROP_FRAME)
vb->v4l2_buf.flags |= V4L2_QCOM_BUF_DROP_FRAME;
+ if (fill_buf_done->flags1 & HAL_BUFFERFLAG_MBAFF)
+ vb->v4l2_buf.flags |= V4L2_MSM_BUF_FLAG_MBAFF;
switch (fill_buf_done->picture_type) {
case HAL_PICTURE_IDR:
vb->v4l2_buf.flags |= V4L2_QCOM_BUF_FLAG_IDRFRAME;
@@ -1772,10 +1774,16 @@
dprintk(VIDC_ERR, "%s invalid parameters", __func__);
return -EINVAL;
}
- if (inst->state == MSM_VIDC_CORE_INVALID ||
- inst->core->state == VIDC_CORE_INVALID) {
+
+ if (inst->core->state == VIDC_CORE_INVALID) {
dprintk(VIDC_ERR,
- "Core is in bad state can't do load res");
+ "Core is in bad state can't do load res\n");
+ return -EINVAL;
+ }
+
+ if (inst->state == MSM_VIDC_CORE_INVALID) {
+ dprintk(VIDC_ERR,
+ "Instance is in invalid state can't do load res\n");
return -EINVAL;
}
diff --git a/drivers/media/platform/msm/vidc/venus_hfi.c b/drivers/media/platform/msm/vidc/venus_hfi.c
index 4fcd20e..008407d 100644
--- a/drivers/media/platform/msm/vidc/venus_hfi.c
+++ b/drivers/media/platform/msm/vidc/venus_hfi.c
@@ -527,6 +527,25 @@
return rc;
}
+static void venus_hfi_set_registers(struct venus_hfi_device *device)
+{
+ struct reg_set *reg_set;
+ int i;
+
+ if (!device->res) {
+ dprintk(VIDC_ERR,
+ "device resources null, cannot set registers\n");
+ return;
+ }
+
+ reg_set = &device->res->reg_set;
+ for (i = 0; i < reg_set->count; i++) {
+ venus_hfi_write_register(device,
+ reg_set->reg_tbl[i].reg,
+ reg_set->reg_tbl[i].value, 0);
+ }
+}
+
static int venus_hfi_core_start_cpu(struct venus_hfi_device *device)
{
u32 ctrl_status = 0, count = 0, rc = 0;
@@ -962,6 +981,7 @@
device->power_enabled = 0;
--device->pwr_cnt;
+ dprintk(VIDC_INFO, "entering power collapse\n");
already_disabled:
return rc;
}
@@ -1001,18 +1021,45 @@
goto err_enable_clk;
}
+
+ /*
+ * Re-program all of the registers that get reset as a result of
+ * regulator_disable() and _enable()
+ */
+ venus_hfi_set_registers(device);
+
+ venus_hfi_write_register(device, VIDC_UC_REGION_ADDR,
+ (u32)device->iface_q_table.align_device_addr, 0);
+ venus_hfi_write_register(device,
+ VIDC_UC_REGION_SIZE, SHARED_QSIZE, 0);
+ venus_hfi_write_register(device, VIDC_CPU_CS_SCIACMDARG2,
+ (u32)device->iface_q_table.align_device_addr,
+ device->iface_q_table.align_virtual_addr);
+
+ if (!IS_ERR_OR_NULL(device->sfr.align_device_addr))
+ venus_hfi_write_register(device, VIDC_SFR_ADDR,
+ (u32)device->sfr.align_device_addr, 0);
+ if (!IS_ERR_OR_NULL(device->qdss.align_device_addr))
+ venus_hfi_write_register(device, VIDC_MMAP_ADDR,
+ (u32)device->qdss.align_device_addr, 0);
+
+ /* Reboot the firmware */
rc = venus_hfi_tzbsp_set_video_state(TZBSP_VIDEO_STATE_RESUME);
if (rc) {
dprintk(VIDC_ERR, "Failed to resume video core %d\n", rc);
goto err_set_video_state;
}
+
+ /* Wait for boot completion */
rc = venus_hfi_reset_core(device);
if (rc) {
dprintk(VIDC_ERR, "Failed to reset venus core");
goto err_reset_core;
}
+
device->power_enabled = 1;
++device->pwr_cnt;
+ dprintk(VIDC_INFO, "resuming from power collapse\n");
return rc;
err_reset_core:
venus_hfi_tzbsp_set_video_state(TZBSP_VIDEO_STATE_SUSPEND);
@@ -1497,25 +1544,6 @@
return -ENOMEM;
}
-static void venus_hfi_set_registers(struct venus_hfi_device *device)
-{
- struct reg_set *reg_set;
- int i;
-
- if (!device->res) {
- dprintk(VIDC_ERR,
- "device resources null, cannot set registers\n");
- return;
- }
-
- reg_set = &device->res->reg_set;
- for (i = 0; i < reg_set->count; i++) {
- venus_hfi_write_register(device,
- reg_set->reg_tbl[i].reg,
- reg_set->reg_tbl[i].value, 0);
- }
-}
-
static int venus_hfi_sys_set_debug(struct venus_hfi_device *device, int debug)
{
u8 packet[VIDC_IFACEQ_VAR_SMALL_PKT_SIZE];
@@ -1561,9 +1589,6 @@
dev->intr_status = 0;
INIT_LIST_HEAD(&dev->sess_head);
- mutex_init(&dev->read_lock);
- mutex_init(&dev->write_lock);
- mutex_init(&dev->session_lock);
venus_hfi_set_registers(dev);
if (!dev->hal_client) {
@@ -2028,6 +2053,10 @@
new_session = (struct hal_session *)
kzalloc(sizeof(struct hal_session), GFP_KERNEL);
+ if (!new_session) {
+ dprintk(VIDC_ERR, "new session fail: Out of memory\n");
+ return NULL;
+ }
new_session->session_id = (u32) session_id;
if (session_type == 1)
new_session->is_decoder = 0;
@@ -3301,7 +3330,6 @@
__func__, device);
return -EINVAL;
}
- mutex_init(&device->clk_pwr_lock);
device->clk_gating_level = VCODEC_CLK;
rc = venus_hfi_iommu_attach(device);
if (rc) {
@@ -3574,6 +3602,11 @@
goto error_createq_pm;
}
+ mutex_init(&hdevice->read_lock);
+ mutex_init(&hdevice->write_lock);
+ mutex_init(&hdevice->session_lock);
+ mutex_init(&hdevice->clk_pwr_lock);
+
if (hal_ctxt.dev_count == 0)
INIT_LIST_HEAD(&hal_ctxt.dev_head);
diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_api.h b/drivers/media/platform/msm/vidc/vidc_hfi_api.h
index c764758..d7350b6 100644
--- a/drivers/media/platform/msm/vidc/vidc_hfi_api.h
+++ b/drivers/media/platform/msm/vidc/vidc_hfi_api.h
@@ -44,6 +44,7 @@
#define HAL_BUFFERFLAG_READONLY 0x00000200
#define HAL_BUFFERFLAG_ENDOFSUBFRAME 0x00000400
#define HAL_BUFFERFLAG_EOSEQ 0x00200000
+#define HAL_BUFFERFLAG_MBAFF 0x08000000
#define HAL_BUFFERFLAG_DROP_FRAME 0x20000000
@@ -185,7 +186,8 @@
HAL_CONFIG_VENC_MARKLTRFRAME,
HAL_CONFIG_VENC_USELTRFRAME,
HAL_CONFIG_VENC_LTRPERIOD,
- HAL_PARAM_VENC_HIER_P_NUM_FRAMES,
+ HAL_CONFIG_VENC_HIER_P_NUM_FRAMES,
+ HAL_PARAM_VENC_HIER_P_MAX_ENH_LAYERS,
};
enum hal_domain {
diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_helper.h b/drivers/media/platform/msm/vidc/vidc_hfi_helper.h
index 5117266..7f4dd04 100644
--- a/drivers/media/platform/msm/vidc/vidc_hfi_helper.h
+++ b/drivers/media/platform/msm/vidc/vidc_hfi_helper.h
@@ -305,8 +305,6 @@
(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x018)
#define HFI_PROPERTY_PARAM_VENC_MULTIREF_P \
(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x019)
-#define HFI_PROPERTY_PARAM_VENC_HIER_P_NUM_ENH_LAYER \
- (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x01A)
#define HFI_PROPERTY_PARAM_VENC_H264_NAL_SVC_EXT \
(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x01B)
#define HFI_PROPERTY_PARAM_VENC_LTRMODE \
@@ -319,6 +317,8 @@
(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x01F)
#define HFI_PROPERTY_PARAM_VENC_MAX_NUM_B_FRAMES \
(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x020)
+#define HFI_PROPERTY_PARAM_VENC_HIER_P_MAX_NUM_ENH_LAYER \
+ (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x026)
#define HFI_PROPERTY_CONFIG_VENC_COMMON_START \
(HFI_DOMAIN_BASE_VENC + HFI_ARCH_COMMON_OFFSET + 0x6000)
#define HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE \
@@ -342,6 +342,8 @@
(HFI_PROPERTY_CONFIG_VENC_COMMON_START + 0x009)
#define HFI_PROPERTY_CONFIG_VENC_USELTRFRAME \
(HFI_PROPERTY_CONFIG_VENC_COMMON_START + 0x00A)
+#define HFI_PROPERTY_CONFIG_VENC_HIER_P_ENH_LAYER \
+ (HFI_PROPERTY_CONFIG_VENC_COMMON_START + 0x00B)
#define HFI_PROPERTY_CONFIG_VENC_LTRPERIOD \
(HFI_PROPERTY_CONFIG_VENC_COMMON_START + 0x00C)
#define HFI_PROPERTY_CONFIG_VPE_COMMON_START \
diff --git a/drivers/media/platform/msm/wfd/vsg-subdev.c b/drivers/media/platform/msm/wfd/vsg-subdev.c
index 433468e..960e45c 100644
--- a/drivers/media/platform/msm/wfd/vsg-subdev.c
+++ b/drivers/media/platform/msm/wfd/vsg-subdev.c
@@ -337,6 +337,7 @@
static int vsg_start(struct v4l2_subdev *sd)
{
struct vsg_context *context = NULL;
+ int rc = 0;
if (!sd) {
WFD_MSG_ERR("ERROR, invalid arguments into %s\n", __func__);
@@ -345,18 +346,24 @@
context = (struct vsg_context *)sd->dev_priv;
+ mutex_lock(&context->mutex);
if (context->state == VSG_STATE_STARTED) {
WFD_MSG_ERR("VSG not stopped, start not allowed\n");
- return -EINPROGRESS;
+ rc = -EINPROGRESS;
+ goto err_bad_state;
} else if (context->state == VSG_STATE_ERROR) {
WFD_MSG_ERR("VSG in error state, not allowed to restart\n");
- return -ENOTRECOVERABLE;
+ rc = -ENOTRECOVERABLE;
+ goto err_bad_state;
}
context->state = VSG_STATE_STARTED;
hrtimer_start(&context->threshold_timer, ns_to_ktime(context->
max_frame_interval), HRTIMER_MODE_REL);
- return 0;
+
+err_bad_state:
+ mutex_unlock(&context->mutex);
+ return rc;
}
static int vsg_stop(struct v4l2_subdev *sd)
diff --git a/drivers/media/radio/radio-iris.c b/drivers/media/radio/radio-iris.c
index 9f18508..02441ec 100644
--- a/drivers/media/radio/radio-iris.c
+++ b/drivers/media/radio/radio-iris.c
@@ -1892,13 +1892,6 @@
return;
}
- if (radio->mode == FM_RECV_TURNING_ON) {
- radio->mode = FM_RECV;
- iris_q_event(radio, IRIS_EVT_RADIO_READY);
- } else if (radio->mode == FM_TRANS_TURNING_ON) {
- radio->mode = FM_TRANS;
- iris_q_event(radio, IRIS_EVT_RADIO_READY);
- }
radio_hci_req_complete(hdev, rsp->status);
}
@@ -3777,7 +3770,19 @@
radio->mode = FM_OFF;
goto END;
} else {
- initialise_recv(radio);
+ retval = initialise_recv(radio);
+ if (retval < 0) {
+ FMDERR("Error while initialising"\
+ "radio %d\n", retval);
+ hci_cmd(HCI_FM_DISABLE_RECV_CMD,
+ radio->fm_hdev);
+ radio->mode = FM_OFF;
+ goto END;
+ }
+ }
+ if (radio->mode == FM_RECV_TURNING_ON) {
+ radio->mode = FM_RECV;
+ iris_q_event(radio, IRIS_EVT_RADIO_READY);
}
break;
case FM_TRANS:
@@ -3794,7 +3799,19 @@
radio->mode = FM_OFF;
goto END;
} else {
- initialise_trans(radio);
+ retval = initialise_trans(radio);
+ if (retval < 0) {
+ FMDERR("Error while initialising"\
+ "radio %d\n", retval);
+ hci_cmd(HCI_FM_DISABLE_TRANS_CMD,
+ radio->fm_hdev);
+ radio->mode = FM_OFF;
+ goto END;
+ }
+ }
+ if (radio->mode == FM_TRANS_TURNING_ON) {
+ radio->mode = FM_TRANS;
+ iris_q_event(radio, IRIS_EVT_RADIO_READY);
}
break;
case FM_OFF:
diff --git a/drivers/media/video/videobuf2-core.c b/drivers/media/video/videobuf2-core.c
index 668cc73..e5311ce 100644
--- a/drivers/media/video/videobuf2-core.c
+++ b/drivers/media/video/videobuf2-core.c
@@ -1104,9 +1104,13 @@
* consistent after getting driver's lock back.
*/
if (q->memory == V4L2_MEMORY_USERPTR) {
- mmap_sem = ¤t->active_mm->mmap_sem;
+ bool mm_exists = !!current->mm;
+
+ mmap_sem = mm_exists ? ¤t->mm->mmap_sem : NULL;
call_qop(q, wait_prepare, q);
- down_read(mmap_sem);
+ /* kthreads have no userspace, hence no pages to lock */
+ if (mmap_sem)
+ down_read(mmap_sem);
call_qop(q, wait_finish, q);
}
diff --git a/drivers/misc/qfp_fuse.c b/drivers/misc/qfp_fuse.c
index 3a088dc..f271f96 100644
--- a/drivers/misc/qfp_fuse.c
+++ b/drivers/misc/qfp_fuse.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011, 2014 The Linux Foundation. All rights reserved.
*
* This 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,7 +28,7 @@
/*
* Time QFPROM requires to reliably burn a fuse.
*/
-#define QFPROM_BLOW_TIMEOUT_US 10
+#define QFPROM_BLOW_TIMEOUT_US 20
#define QFPROM_BLOW_TIMER_OFFSET 0x2038
/*
* Denotes number of cycles required to blow the fuse.
@@ -42,6 +42,10 @@
#define QFP_FUSE_READY 0x01
#define QFP_FUSE_OFF 0x00
+#define QFP_FUSE_BUF_SIZE 64
+#define UINT32_MAX (0xFFFFFFFFU)
+
+
struct qfp_priv_t {
uint32_t base;
uint32_t end;
@@ -53,6 +57,23 @@
/* We need only one instance of this for the driver */
static struct qfp_priv_t *qfp_priv;
+static inline bool is_usr_req_valid(const struct qfp_fuse_req *req)
+{
+ uint32_t size = qfp_priv->end - qfp_priv->base;
+ uint32_t req_size;
+
+ if (req->size >= (UINT32_MAX / sizeof(uint32_t)))
+ return false;
+ req_size = req->size * sizeof(uint32_t);
+ if ((req_size == 0) || (req_size > size))
+ return false;
+ if (req->offset >= size)
+ return false;
+ if ((req->offset + req_size) > size)
+ return false;
+
+ return true;
+}
static int qfp_fuse_open(struct inode *inode, struct file *filp)
{
@@ -177,7 +198,9 @@
{
int err = 0;
struct qfp_fuse_req req;
- u32 *buf = NULL;
+ u32 fuse_buf[QFP_FUSE_BUF_SIZE];
+ u32 *buf = fuse_buf;
+ u32 *ptr = NULL;
int i;
/* Verify user arguments. */
@@ -199,25 +222,21 @@
}
/* Check for limits */
- if (!req.size) {
- pr_err("Request size zero.\n");
- err = -EFAULT;
+ if (is_usr_req_valid(&req) == false) {
+ pr_err("Invalid request\n");
+ err = -EINVAL;
break;
}
- if (qfp_priv->base + req.offset + (req.size - 1) * 4 >
- qfp_priv->end) {
- pr_err("Req size exceeds QFPROM addr space\n");
- err = -EFAULT;
- break;
- }
-
- /* Allocate memory for buffer */
- buf = kzalloc(req.size * 4, GFP_KERNEL);
- if (buf == NULL) {
- pr_alert("No memory for data\n");
- err = -ENOMEM;
- break;
+ if (req.size > QFP_FUSE_BUF_SIZE) {
+ /* Allocate memory for buffer */
+ ptr = kzalloc(req.size * 4, GFP_KERNEL);
+ if (ptr == NULL) {
+ pr_alert("No memory for data\n");
+ err = -ENOMEM;
+ break;
+ }
+ buf = ptr;
}
if (mutex_lock_interruptible(&qfp_priv->lock)) {
@@ -251,24 +270,21 @@
break;
}
/* Check for limits */
- if (!req.size) {
- pr_err("Request size zero.\n");
- err = -EFAULT;
- break;
- }
- if (qfp_priv->base + req.offset + (req.size - 1) * 4 >
- qfp_priv->end) {
- pr_err("Req size exceeds QFPROM space\n");
- err = -EFAULT;
+ if (is_usr_req_valid(&req) == false) {
+ pr_err("Invalid request\n");
+ err = -EINVAL;
break;
}
- /* Allocate memory for buffer */
- buf = kzalloc(4 * (req.size), GFP_KERNEL);
- if (buf == NULL) {
- pr_alert("No memory for data\n");
- err = -ENOMEM;
- break;
+ if (req.size > QFP_FUSE_BUF_SIZE) {
+ /* Allocate memory for buffer */
+ ptr = kzalloc(req.size * 4, GFP_KERNEL);
+ if (ptr == NULL) {
+ pr_alert("No memory for data\n");
+ err = -ENOMEM;
+ break;
+ }
+ buf = ptr;
}
/* Copy user data to local buffer */
@@ -296,7 +312,7 @@
pr_err("Invalid ioctl command.\n");
return -ENOTTY;
}
- kfree(buf);
+ kfree(ptr);
return err;
}
diff --git a/drivers/misc/qseecom.c b/drivers/misc/qseecom.c
index efafa23..7ad51e6 100644
--- a/drivers/misc/qseecom.c
+++ b/drivers/misc/qseecom.c
@@ -287,7 +287,7 @@
/* Get the handle of the shared fd */
svc->ihandle = ion_import_dma_buf(qseecom.ion_clnt,
listener->ifd_data_fd);
- if (svc->ihandle == NULL) {
+ if (IS_ERR_OR_NULL(svc->ihandle)) {
pr_err("Ion client could not retrieve the handle\n");
return -ENOMEM;
}
@@ -503,26 +503,31 @@
return;
}
-static void __qseecom_decrease_clk_ref_count(enum qseecom_ce_hw_instance ce)
+static int __qseecom_decrease_clk_ref_count(enum qseecom_ce_hw_instance ce)
{
struct qseecom_clk *qclk;
+ int ret = 0;
mutex_lock(&clk_access_lock);
if (ce == CLK_QSEE)
qclk = &qseecom.qsee;
else
qclk = &qseecom.ce_drv;
- if (qclk->clk_access_cnt == 0) {
- mutex_unlock(&clk_access_lock);
- return;
+ if (qclk->clk_access_cnt > 2) {
+ pr_err("Invalid clock ref count %d\n", qclk->clk_access_cnt);
+ ret = -EINVAL;
+ goto err_dec_ref_cnt;
}
- qclk->clk_access_cnt--;
+ if (qclk->clk_access_cnt == 2)
+ qclk->clk_access_cnt--;
+
+err_dec_ref_cnt:
mutex_unlock(&clk_access_lock);
- return;
+ return ret;
}
-static int qseecom_scale_bus_bandwidth_timer(uint32_t mode, uint32_t duration)
+static int qseecom_scale_bus_bandwidth_timer(uint32_t mode)
{
int32_t ret = 0;
int32_t request_mode = INACTIVE;
@@ -537,11 +542,23 @@
request_mode = mode;
}
- __qseecom_set_msm_bus_request(request_mode);
- if (qseecom.timer_running) {
- __qseecom_decrease_clk_ref_count(CLK_QSEE);
- del_timer_sync(&(qseecom.bw_scale_down_timer));
+ ret = __qseecom_set_msm_bus_request(request_mode);
+ if (ret) {
+ pr_err("set msm bus request failed (%d),request_mode (%d)\n",
+ ret, request_mode);
+ goto err_scale_timer;
}
+
+ if (qseecom.timer_running) {
+ ret = __qseecom_decrease_clk_ref_count(CLK_QSEE);
+ if (ret) {
+ pr_err("Failed to decrease clk ref count.\n");
+ goto err_scale_timer;
+ }
+ del_timer_sync(&(qseecom.bw_scale_down_timer));
+ qseecom.timer_running = false;
+ }
+err_scale_timer:
mutex_unlock(&qsee_bw_mutex);
return ret;
}
@@ -598,18 +615,23 @@
return ret;
}
+static void __qseecom_add_bw_scale_down_timer(uint32_t duration)
+{
+ mutex_lock(&qsee_bw_mutex);
+ qseecom.bw_scale_down_timer.expires = jiffies +
+ msecs_to_jiffies(duration);
+ add_timer(&(qseecom.bw_scale_down_timer));
+ qseecom.timer_running = true;
+ mutex_unlock(&qsee_bw_mutex);
+}
+
static void __qseecom_disable_clk_scale_down(struct qseecom_dev_handle *data)
{
if (!qseecom.support_bus_scaling)
qsee_disable_clock_vote(data, CLK_SFPB);
- else {
- mutex_lock(&qsee_bw_mutex);
- qseecom.bw_scale_down_timer.expires = jiffies +
- msecs_to_jiffies(QSEECOM_LOAD_APP_CRYPTO_TIMEOUT);
- add_timer(&(qseecom.bw_scale_down_timer));
- qseecom.timer_running = true;
- mutex_unlock(&qsee_bw_mutex);
- }
+ else
+ __qseecom_add_bw_scale_down_timer(
+ QSEECOM_LOAD_APP_CRYPTO_TIMEOUT);
return;
}
@@ -617,8 +639,9 @@
{
int ret = 0;
if (qseecom.support_bus_scaling) {
- qseecom_scale_bus_bandwidth_timer(
- MEDIUM, QSEECOM_LOAD_APP_CRYPTO_TIMEOUT);
+ ret = qseecom_scale_bus_bandwidth_timer(MEDIUM);
+ if (ret)
+ pr_err("Failed to set bw MEDIUM.\n");
} else {
ret = qsee_vote_for_clock(data, CLK_SFPB);
if (ret)
@@ -1168,10 +1191,9 @@
}
if (qseecom.support_bus_scaling) {
- qseecom_scale_bus_bandwidth_timer(HIGH,
- QSEECOM_SEND_CMD_CRYPTO_TIMEOUT);
+ ret = qseecom_scale_bus_bandwidth_timer(HIGH);
if (ret) {
- pr_err("Fail to set bw HIGH%d\n", ret);
+ pr_err("Fail to set bw HIGH\n");
return ret;
}
} else {
@@ -1203,15 +1225,9 @@
qsee_disable_clock_vote(data, CLK_DFAB);
qsee_disable_clock_vote(data, CLK_SFPB);
} else {
- mutex_lock(&qsee_bw_mutex);
- qseecom.bw_scale_down_timer.expires = jiffies +
- msecs_to_jiffies(
+ __qseecom_add_bw_scale_down_timer(
QSEECOM_SEND_CMD_CRYPTO_TIMEOUT);
- add_timer(&(qseecom.bw_scale_down_timer));
- qseecom.timer_running = true;
- mutex_unlock(&qsee_bw_mutex);
}
-
goto exit;
}
@@ -1239,12 +1255,8 @@
qsee_disable_clock_vote(data, CLK_DFAB);
qsee_disable_clock_vote(data, CLK_SFPB);
} else {
- mutex_lock(&qsee_bw_mutex);
- qseecom.bw_scale_down_timer.expires = jiffies +
- msecs_to_jiffies(QSEECOM_SEND_CMD_CRYPTO_TIMEOUT);
- add_timer(&(qseecom.bw_scale_down_timer));
- qseecom.timer_running = true;
- mutex_unlock(&qsee_bw_mutex);
+ __qseecom_add_bw_scale_down_timer(
+ QSEECOM_SEND_CMD_CRYPTO_TIMEOUT);
}
exit:
@@ -1560,7 +1572,7 @@
if (wait_event_freezable(this_lstnr->rcv_req_wq,
__qseecom_listener_has_rcvd_req(data,
this_lstnr))) {
- pr_warning("Interrupted: exiting Listener Service = %d\n",
+ pr_debug("Interrupted: exiting Listener Service = %d\n",
(uint32_t)data->listener.id);
/* woken up for different reason */
return -ERESTARTSYS;
@@ -2079,18 +2091,19 @@
mutex_lock(&app_access_lock);
atomic_inc(&data->ioctl_count);
- if (qseecom.support_bus_scaling)
- qseecom_scale_bus_bandwidth_timer(INACTIVE,
- QSEECOM_SEND_CMD_CRYPTO_TIMEOUT);
- ret = __qseecom_send_cmd(data, &req);
if (qseecom.support_bus_scaling) {
- mutex_lock(&qsee_bw_mutex);
- qseecom.bw_scale_down_timer.expires = jiffies +
- msecs_to_jiffies(QSEECOM_SEND_CMD_CRYPTO_TIMEOUT);
- add_timer(&(qseecom.bw_scale_down_timer));
- qseecom.timer_running = true;
- mutex_unlock(&qsee_bw_mutex);
+ ret = qseecom_scale_bus_bandwidth_timer(INACTIVE);
+ if (ret) {
+ pr_err("Failed to set bw.\n");
+ atomic_dec(&data->ioctl_count);
+ mutex_unlock(&app_access_lock);
+ return ret;
+ }
}
+ ret = __qseecom_send_cmd(data, &req);
+ if (qseecom.support_bus_scaling)
+ __qseecom_add_bw_scale_down_timer(
+ QSEECOM_SEND_CMD_CRYPTO_TIMEOUT);
atomic_dec(&data->ioctl_count);
mutex_unlock(&app_access_lock);
@@ -3210,20 +3223,20 @@
}
/* Only one client allowed here at a time */
mutex_lock(&app_access_lock);
- if (qseecom.support_bus_scaling)
- qseecom_scale_bus_bandwidth_timer(INACTIVE,
- QSEECOM_SEND_CMD_CRYPTO_TIMEOUT);
+ if (qseecom.support_bus_scaling) {
+ ret = qseecom_scale_bus_bandwidth_timer(INACTIVE);
+ if (ret) {
+ pr_err("Failed to set bw.\n");
+ ret = -EINVAL;
+ mutex_unlock(&app_access_lock);
+ break;
+ }
+ }
atomic_inc(&data->ioctl_count);
ret = qseecom_send_cmd(data, argp);
- if (qseecom.support_bus_scaling) {
- mutex_lock(&qsee_bw_mutex);
- qseecom.bw_scale_down_timer.expires = jiffies +
- msecs_to_jiffies(
+ if (qseecom.support_bus_scaling)
+ __qseecom_add_bw_scale_down_timer(
QSEECOM_SEND_CMD_CRYPTO_TIMEOUT);
- add_timer(&(qseecom.bw_scale_down_timer));
- qseecom.timer_running = true;
- mutex_unlock(&qsee_bw_mutex);
- }
atomic_dec(&data->ioctl_count);
wake_up_all(&data->abort_wq);
mutex_unlock(&app_access_lock);
@@ -3242,20 +3255,21 @@
}
/* Only one client allowed here at a time */
mutex_lock(&app_access_lock);
- if (qseecom.support_bus_scaling)
- qseecom_scale_bus_bandwidth_timer(INACTIVE,
- QSEECOM_SEND_CMD_CRYPTO_TIMEOUT);
+ if (qseecom.support_bus_scaling) {
+ ret = qseecom_scale_bus_bandwidth_timer(INACTIVE);
+ if (ret) {
+ pr_err("Failed to set bw.\n");
+ mutex_unlock(&app_access_lock);
+ ret = -EINVAL;
+ break;
+ }
+ }
atomic_inc(&data->ioctl_count);
ret = qseecom_send_modfd_cmd(data, argp);
- if (qseecom.support_bus_scaling) {
- mutex_lock(&qsee_bw_mutex);
- qseecom.bw_scale_down_timer.expires = jiffies +
- msecs_to_jiffies(
+ if (qseecom.support_bus_scaling)
+ __qseecom_add_bw_scale_down_timer(
QSEECOM_SEND_CMD_CRYPTO_TIMEOUT);
- add_timer(&(qseecom.bw_scale_down_timer));
- qseecom.timer_running = true;
- mutex_unlock(&qsee_bw_mutex);
- } atomic_dec(&data->ioctl_count);
+ atomic_dec(&data->ioctl_count);
wake_up_all(&data->abort_wq);
mutex_unlock(&app_access_lock);
if (ret)
@@ -3274,7 +3288,7 @@
ret = qseecom_receive_req(data);
atomic_dec(&data->ioctl_count);
wake_up_all(&data->abort_wq);
- if (ret)
+ if (ret && (ret != -ERESTARTSYS))
pr_err("failed qseecom_receive_req: %d\n", ret);
break;
}
@@ -4107,7 +4121,8 @@
qclk = &qseecom.qsee;
if (qseecom.cumulative_mode != INACTIVE) {
- ret = __qseecom_set_msm_bus_request(INACTIVE);
+ ret = msm_bus_scale_client_update_request(
+ qseecom.qsee_perf_client, INACTIVE);
if (ret)
pr_err("Fail to scale down bus\n");
}
@@ -4119,6 +4134,10 @@
clk_disable_unprepare(qclk->ce_core_clk);
if (qclk->ce_bus_clk != NULL)
clk_disable_unprepare(qclk->ce_bus_clk);
+ if (qseecom.timer_running) {
+ del_timer_sync(&(qseecom.bw_scale_down_timer));
+ qseecom.timer_running = false;
+ }
}
mutex_unlock(&clk_access_lock);
return 0;
@@ -4137,9 +4156,11 @@
mode = qseecom.cumulative_mode;
if (qseecom.cumulative_mode != INACTIVE) {
- ret = __qseecom_set_msm_bus_request(mode);
+ ret = msm_bus_scale_client_update_request(
+ qseecom.qsee_perf_client, qseecom.cumulative_mode);
if (ret)
- pr_err("Fail to scale down bus\n");
+ pr_err("Fail to scale up bus to %d\n",
+ qseecom.cumulative_mode);
}
mutex_lock(&clk_access_lock);
@@ -4165,6 +4186,11 @@
qclk->clk_access_cnt = 0;
goto ce_bus_clk_err;
}
+ qseecom.bw_scale_down_timer.expires = jiffies +
+ msecs_to_jiffies(QSEECOM_SEND_CMD_CRYPTO_TIMEOUT);
+ add_timer(&(qseecom.bw_scale_down_timer));
+ qseecom.timer_running = true;
+
}
mutex_unlock(&clk_access_lock);
return 0;
diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c
index b36faff..a6fcec5 100644
--- a/drivers/mmc/core/bus.c
+++ b/drivers/mmc/core/bus.c
@@ -421,10 +421,17 @@
else if (!mmc_card_sdio(card) && mmc_use_core_runtime_pm(card->host))
pm_runtime_enable(&card->dev);
+ if (mmc_card_sdio(card)) {
+ ret = device_init_wakeup(&card->dev, true);
+ if (ret)
+ pr_err("%s: %s: failed to init wakeup: %d\n",
+ mmc_hostname(card->host), __func__, ret);
+ }
ret = device_add(&card->dev);
if (ret)
return ret;
+ device_enable_async_suspend(&card->dev);
if (mmc_use_core_runtime_pm(card->host) && !mmc_card_sdio(card)) {
card->rpm_attrib.show = show_rpm_delay;
card->rpm_attrib.store = store_rpm_delay;
diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c
index c7fa876..c4b9724 100644
--- a/drivers/mmc/core/host.c
+++ b/drivers/mmc/core/host.c
@@ -77,16 +77,40 @@
{
struct mmc_host *host = cls_dev_to_mmc_host(dev);
int ret = 0;
+ unsigned long flags;
if (!mmc_use_core_pm(host))
return 0;
+ spin_lock_irqsave(&host->clk_lock, flags);
+ /*
+ * let the driver know that suspend is in progress and must
+ * be aborted on receiving a sdio card interrupt
+ */
+ host->dev_status = DEV_SUSPENDING;
+ spin_unlock_irqrestore(&host->clk_lock, flags);
if (!pm_runtime_suspended(dev)) {
ret = mmc_suspend_host(host);
if (ret < 0)
pr_err("%s: %s: failed: ret: %d\n", mmc_hostname(host),
__func__, ret);
}
+ /*
+ * If SDIO function driver doesn't want to power off the card,
+ * atleast turn off clocks to allow deep sleep.
+ */
+ if (!ret && host->card && mmc_card_sdio(host->card) &&
+ host->ios.clock) {
+ spin_lock_irqsave(&host->clk_lock, flags);
+ host->clk_old = host->ios.clock;
+ host->ios.clock = 0;
+ host->clk_gated = true;
+ spin_unlock_irqrestore(&host->clk_lock, flags);
+ mmc_set_ios(host);
+ }
+ spin_lock_irqsave(&host->clk_lock, flags);
+ host->dev_status = DEV_SUSPENDED;
+ spin_unlock_irqrestore(&host->clk_lock, flags);
return ret;
}
@@ -104,6 +128,7 @@
pr_err("%s: %s: failed: ret: %d\n", mmc_hostname(host),
__func__, ret);
}
+ host->dev_status = DEV_RESUMED;
return ret;
}
@@ -697,6 +722,7 @@
if (err)
return err;
+ device_enable_async_suspend(&host->class_dev);
led_trigger_register_simple(dev_name(&host->class_dev), &host->led);
#ifdef CONFIG_DEBUG_FS
diff --git a/drivers/mmc/core/quirks.c b/drivers/mmc/core/quirks.c
index 4407d91..d517205 100644
--- a/drivers/mmc/core/quirks.c
+++ b/drivers/mmc/core/quirks.c
@@ -50,6 +50,14 @@
#define SDIO_DEVICE_ID_MSM_QCA_AR6003_2 0x301
#endif
+#ifndef SDIO_DEVICE_ID_MSM_QCA_AR6004_1
+#define SDIO_DEVICE_ID_MSM_QCA_AR6004_1 0x400
+#endif
+
+#ifndef SDIO_DEVICE_ID_MSM_QCA_AR6004_2
+#define SDIO_DEVICE_ID_MSM_QCA_AR6004_2 0x401
+#endif
+
/*
* This hook just adds a quirk for all sdio devices
*/
@@ -78,6 +86,12 @@
SDIO_FIXUP(SDIO_VENDOR_ID_MSM_QCA, SDIO_DEVICE_ID_MSM_QCA_AR6003_2,
remove_quirk, MMC_QUIRK_BROKEN_CLK_GATING),
+ SDIO_FIXUP(SDIO_VENDOR_ID_MSM_QCA, SDIO_DEVICE_ID_MSM_QCA_AR6004_1,
+ remove_quirk, MMC_QUIRK_BROKEN_CLK_GATING),
+
+ SDIO_FIXUP(SDIO_VENDOR_ID_MSM_QCA, SDIO_DEVICE_ID_MSM_QCA_AR6004_2,
+ remove_quirk, MMC_QUIRK_BROKEN_CLK_GATING),
+
SDIO_FIXUP(SDIO_VENDOR_ID_TI, SDIO_DEVICE_ID_TI_WL1271,
add_quirk, MMC_QUIRK_NONSTD_FUNC_IF),
diff --git a/drivers/mmc/core/sdio_irq.c b/drivers/mmc/core/sdio_irq.c
index 3d8ceb4..2b48f77 100644
--- a/drivers/mmc/core/sdio_irq.c
+++ b/drivers/mmc/core/sdio_irq.c
@@ -85,6 +85,7 @@
struct sched_param param = { .sched_priority = 1 };
unsigned long period, idle_period;
int ret;
+ bool ws;
sched_setscheduler(current, SCHED_FIFO, ¶m);
@@ -118,6 +119,17 @@
ret = __mmc_claim_host(host, &host->sdio_irq_thread_abort);
if (ret)
break;
+ ws = false;
+ /*
+ * prevent suspend if it has started when scheduled;
+ * 100 msec (approx. value) should be enough for the system to
+ * resume and attend to the card's request
+ */
+ if ((host->dev_status == DEV_SUSPENDING) ||
+ (host->dev_status == DEV_SUSPENDED)) {
+ pm_wakeup_event(&host->card->dev, 100);
+ ws = true;
+ }
ret = process_sdio_pending_irqs(host);
host->sdio_irq_pending = false;
mmc_release_host(host);
@@ -154,6 +166,12 @@
host->ops->enable_sdio_irq(host, 1);
mmc_host_clk_release(host);
}
+ /*
+ * function drivers would have processed the event from card
+ * unless suspended, hence release wake source
+ */
+ if (ws && (host->dev_status == DEV_RESUMED))
+ pm_relax(&host->card->dev);
if (!kthread_should_stop())
schedule_timeout(period);
set_current_state(TASK_RUNNING);
diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c
index 739a237..9b41807 100644
--- a/drivers/mmc/host/sdhci-msm.c
+++ b/drivers/mmc/host/sdhci-msm.c
@@ -31,6 +31,7 @@
#include <linux/delay.h>
#include <linux/scatterlist.h>
#include <linux/slab.h>
+#include <linux/irq.h>
#include <linux/mmc/mmc.h>
#include <linux/pm.h>
#include <linux/pm_runtime.h>
@@ -38,10 +39,18 @@
#include <linux/dma-mapping.h>
#include <mach/gpio.h>
#include <mach/msm_bus.h>
+#include <mach/mpm.h>
#include <linux/iopoll.h>
#include "sdhci-pltfm.h"
+enum sdc_mpm_pin_state {
+ SDC_DAT1_DISABLE,
+ SDC_DAT1_ENABLE,
+ SDC_DAT1_ENWAKE,
+ SDC_DAT1_DISWAKE,
+};
+
#define SDHCI_VER_100 0x2B
#define CORE_HC_MODE 0x78
#define HC_MODE_EN 0x1
@@ -156,6 +165,9 @@
#define INVALID_TUNING_PHASE -1
+#define sdhci_is_valid_mpm_wakeup_int(_h) ((_h)->pdata->mpm_sdiowakeup_int >= 0)
+#define sdhci_is_valid_gpio_wakeup_int(_h) ((_h)->pdata->sdiowakeup_irq >= 0)
+
static const u32 tuning_block_64[] = {
0x00FF0FFF, 0xCCC3CCFF, 0xFFCC3CC3, 0xEFFEFFFE,
0xDDFFDFFF, 0xFBFFFBFF, 0xFF7FFFBF, 0xEFBDF777,
@@ -283,6 +295,8 @@
struct sdhci_msm_bus_voting_data *voting_data;
u32 *sup_clk_table;
unsigned char sup_clk_cnt;
+ int mpm_sdiowakeup_int;
+ int sdiowakeup_irq;
};
struct sdhci_msm_bus_vote {
@@ -318,6 +332,7 @@
bool calibration_done;
u8 saved_tuning_phase;
atomic_t controller_clock;
+ bool is_sdiowakeup_enabled;
};
enum vdd_io_level {
@@ -1338,7 +1353,7 @@
struct device_node *np = dev->of_node;
u32 bus_width = 0;
u32 cpu_dma_latency;
- int len, i;
+ int len, i, mpm_int;
int clk_table_len;
u32 *clk_table = NULL;
enum of_gpio_flags flags = OF_GPIO_ACTIVE_LOW;
@@ -1433,6 +1448,12 @@
if (of_get_property(np, "qcom,nonremovable", NULL))
pdata->nonremovable = true;
+ if (!of_property_read_u32(np, "qcom,dat1-mpm-int",
+ &mpm_int))
+ pdata->mpm_sdiowakeup_int = mpm_int;
+ else
+ pdata->mpm_sdiowakeup_int = -1;
+
return pdata;
out:
return NULL;
@@ -1933,6 +1954,39 @@
return ret;
}
+/*
+ * Acquire spin-lock host->lock before calling this function
+ */
+static void sdhci_msm_cfg_sdiowakeup_gpio_irq(struct sdhci_host *host,
+ bool enable)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+
+ if (enable && !msm_host->is_sdiowakeup_enabled)
+ enable_irq(msm_host->pdata->sdiowakeup_irq);
+ else if (!enable && msm_host->is_sdiowakeup_enabled)
+ disable_irq_nosync(msm_host->pdata->sdiowakeup_irq);
+ else
+ dev_warn(&msm_host->pdev->dev, "%s: wakeup to config: %d curr: %d\n",
+ __func__, enable, msm_host->is_sdiowakeup_enabled);
+ msm_host->is_sdiowakeup_enabled = enable;
+}
+
+static irqreturn_t sdhci_msm_sdiowakeup_irq(int irq, void *data)
+{
+ struct sdhci_host *host = (struct sdhci_host *)data;
+ unsigned long flags;
+
+ pr_debug("%s: irq (%d) received\n", __func__, irq);
+
+ spin_lock_irqsave(&host->lock, flags);
+ sdhci_msm_cfg_sdiowakeup_gpio_irq(host, false);
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ return IRQ_HANDLED;
+}
+
static irqreturn_t sdhci_msm_pwr_irq(int irq, void *data)
{
struct sdhci_host *host = (struct sdhci_host *)data;
@@ -2609,6 +2663,38 @@
.enable_controller_clock = sdhci_msm_enable_controller_clock,
};
+static int sdhci_msm_cfg_mpm_pin_wakeup(struct sdhci_host *host, unsigned mode)
+{
+ int ret = 0;
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ unsigned int pin = msm_host->pdata->mpm_sdiowakeup_int;
+
+ if (!pin)
+ return 0;
+
+ switch (mode) {
+ case SDC_DAT1_DISABLE:
+ ret = msm_mpm_enable_pin(pin, 0);
+ break;
+ case SDC_DAT1_ENABLE:
+ ret = msm_mpm_set_pin_type(pin, IRQ_TYPE_LEVEL_LOW);
+ if (!ret)
+ ret = msm_mpm_enable_pin(pin, 1);
+ break;
+ case SDC_DAT1_ENWAKE:
+ ret = msm_mpm_set_pin_wake(pin, 1);
+ break;
+ case SDC_DAT1_DISWAKE:
+ ret = msm_mpm_set_pin_wake(pin, 0);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
static int __devinit sdhci_msm_probe(struct platform_device *pdev)
{
struct sdhci_host *host;
@@ -2619,6 +2705,7 @@
u32 vdd_max_current;
u16 host_version;
u32 pwr, irq_status, irq_ctl;
+ unsigned long flags;
pr_debug("%s: Enter %s\n", dev_name(&pdev->dev), __func__);
msm_host = devm_kzalloc(&pdev->dev, sizeof(struct sdhci_msm_host),
@@ -2837,6 +2924,8 @@
host->quirks2 |= SDHCI_QUIRK2_RDWR_TX_ACTIVE_EOT;
}
+ host->quirks2 |= SDHCI_QUIRK2_IGN_DATA_END_BIT_ERROR;
+
/* Setup PWRCTL irq */
msm_host->pwr_irq = platform_get_irq_byname(pdev, "pwr_irq");
if (msm_host->pwr_irq < 0) {
@@ -2891,7 +2980,7 @@
msm_host->mmc->caps2 |= MMC_CAP2_STOP_REQUEST;
msm_host->mmc->caps2 |= MMC_CAP2_ASYNC_SDIO_IRQ_4BIT_MODE;
msm_host->mmc->caps2 |= MMC_CAP2_CORE_PM;
- msm_host->mmc->pm_caps |= MMC_PM_KEEP_POWER;
+ msm_host->mmc->pm_caps |= MMC_PM_KEEP_POWER | MMC_PM_WAKE_SDIO_IRQ;
if (msm_host->pdata->nonremovable)
msm_host->mmc->caps |= MMC_CAP_NONREMOVABLE;
@@ -2917,6 +3006,27 @@
dev_err(&pdev->dev, "%s: Failed to set dma mask\n", __func__);
}
+ msm_host->pdata->sdiowakeup_irq = platform_get_irq_byname(pdev,
+ "sdiowakeup_irq");
+ if (msm_host->pdata->sdiowakeup_irq >= 0) {
+ msm_host->is_sdiowakeup_enabled = true;
+ ret = request_irq(msm_host->pdata->sdiowakeup_irq,
+ sdhci_msm_sdiowakeup_irq,
+ IRQF_SHARED | IRQF_TRIGGER_LOW,
+ "sdhci-msm sdiowakeup", host);
+ if (ret) {
+ dev_err(&pdev->dev, "%s: request sdiowakeup IRQ %d: failed: %d\n",
+ __func__, msm_host->pdata->sdiowakeup_irq, ret);
+ msm_host->pdata->sdiowakeup_irq = -1;
+ msm_host->is_sdiowakeup_enabled = false;
+ goto free_cd_gpio;
+ } else {
+ spin_lock_irqsave(&host->lock, flags);
+ sdhci_msm_cfg_sdiowakeup_gpio_irq(host, false);
+ spin_unlock_irqrestore(&host->lock, flags);
+ }
+ }
+
ret = sdhci_add_host(host);
if (ret) {
dev_err(&pdev->dev, "Add host failed (%d)\n", ret);
@@ -2950,6 +3060,17 @@
else if (mmc_use_core_runtime_pm(host->mmc))
pm_runtime_enable(&pdev->dev);
+ if (msm_host->pdata->mpm_sdiowakeup_int != -1) {
+ ret = sdhci_msm_cfg_mpm_pin_wakeup(host, SDC_DAT1_ENABLE);
+ if (ret) {
+ pr_err("%s: enabling wakeup: failed: ret: %d\n",
+ mmc_hostname(host->mmc), ret);
+ ret = 0;
+ msm_host->pdata->mpm_sdiowakeup_int = -1;
+ }
+ }
+
+ device_enable_async_suspend(&pdev->dev);
/* Successful initialization */
goto out;
@@ -2961,6 +3082,8 @@
free_cd_gpio:
if (gpio_is_valid(msm_host->pdata->status_gpio))
mmc_cd_gpio_free(msm_host->mmc);
+ if (sdhci_is_valid_gpio_wakeup_int(msm_host))
+ free_irq(msm_host->pdata->sdiowakeup_irq, host);
vreg_deinit:
sdhci_msm_vreg_init(&pdev->dev, msm_host->pdata, false);
bus_unregister:
@@ -3006,6 +3129,12 @@
pm_runtime_disable(&pdev->dev);
sdhci_pltfm_free(pdev);
+ if (sdhci_is_valid_mpm_wakeup_int(msm_host))
+ sdhci_msm_cfg_mpm_pin_wakeup(host, SDC_DAT1_DISABLE);
+
+ if (sdhci_is_valid_gpio_wakeup_int(msm_host))
+ free_irq(msm_host->pdata->sdiowakeup_irq, host);
+
if (gpio_is_valid(msm_host->pdata->status_gpio))
mmc_cd_gpio_free(msm_host->mmc);
@@ -3021,13 +3150,75 @@
return 0;
}
+static int sdhci_msm_cfg_sdio_wakeup(struct sdhci_host *host, bool enable)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ unsigned long flags;
+ int ret = 0;
+
+ if (!(host->mmc->card && mmc_card_sdio(host->mmc->card) &&
+ (sdhci_is_valid_mpm_wakeup_int(msm_host) ||
+ sdhci_is_valid_gpio_wakeup_int(msm_host)) &&
+ mmc_card_wake_sdio_irq(host->mmc))) {
+ return 1;
+ }
+
+ spin_lock_irqsave(&host->lock, flags);
+ if (enable) {
+ /* configure DAT1 gpio if applicable */
+ if (sdhci_is_valid_gpio_wakeup_int(msm_host)) {
+ ret = enable_irq_wake(msm_host->pdata->sdiowakeup_irq);
+ if (!ret)
+ sdhci_msm_cfg_sdiowakeup_gpio_irq(host, true);
+ goto out;
+ } else {
+ ret = sdhci_msm_cfg_mpm_pin_wakeup(host,
+ SDC_DAT1_ENWAKE);
+ if (ret)
+ goto out;
+ ret = enable_irq_wake(host->irq);
+ if (ret)
+ sdhci_msm_cfg_mpm_pin_wakeup(host,
+ SDC_DAT1_DISWAKE);
+ }
+ } else {
+ if (sdhci_is_valid_gpio_wakeup_int(msm_host)) {
+ ret = disable_irq_wake(msm_host->pdata->sdiowakeup_irq);
+ sdhci_msm_cfg_sdiowakeup_gpio_irq(host, false);
+ } else {
+ ret = sdhci_msm_cfg_mpm_pin_wakeup(host,
+ SDC_DAT1_DISWAKE);
+ if (ret)
+ goto out;
+ ret = disable_irq_wake(host->irq);
+ }
+ }
+out:
+ if (ret)
+ pr_err("%s: %s: %sable wakeup: failed: %d gpio: %d mpm: %d\n",
+ mmc_hostname(host->mmc), __func__, enable ? "en" : "dis",
+ ret, msm_host->pdata->sdiowakeup_irq,
+ msm_host->pdata->mpm_sdiowakeup_int);
+ spin_unlock_irqrestore(&host->lock, flags);
+ return ret;
+}
+
static int sdhci_msm_runtime_suspend(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ int ret;
+
+ ret = sdhci_msm_cfg_sdio_wakeup(host, true);
+ /* pwr_irq is not monitored by mpm on suspend, hence disable it */
+ if (!ret)
+ goto skip_disable_host_irq;
disable_irq(host->irq);
+
+skip_disable_host_irq:
disable_irq(msm_host->pwr_irq);
/*
@@ -3048,10 +3239,17 @@
struct sdhci_host *host = dev_get_drvdata(dev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ int ret;
- enable_irq(msm_host->pwr_irq);
+ ret = sdhci_msm_cfg_sdio_wakeup(host, false);
+ if (!ret)
+ goto skip_enable_host_irq;
+
enable_irq(host->irq);
+skip_enable_host_irq:
+ enable_irq(msm_host->pwr_irq);
+
return 0;
}
@@ -3103,6 +3301,26 @@
out:
return ret;
}
+
+static int sdhci_msm_suspend_noirq(struct device *dev)
+{
+ struct sdhci_host *host = dev_get_drvdata(dev);
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ int ret = 0;
+
+ /*
+ * ksdioirqd may get scheduled after sdhc suspend, hence retry
+ * suspend in case the clocks are ON
+ */
+ if (atomic_read(&msm_host->clks_on)) {
+ pr_warn("%s: %s: clock ON after suspend, aborting suspend\n",
+ mmc_hostname(host->mmc), __func__);
+ ret = -EAGAIN;
+ }
+
+ return ret;
+}
#endif
#ifdef CONFIG_PM
@@ -3110,6 +3328,7 @@
SET_SYSTEM_SLEEP_PM_OPS(sdhci_msm_suspend, sdhci_msm_resume)
SET_RUNTIME_PM_OPS(sdhci_msm_runtime_suspend, sdhci_msm_runtime_resume,
NULL)
+ .suspend_noirq = sdhci_msm_suspend_noirq,
};
#define SDHCI_MSM_PMOPS (&sdhci_msm_pmops)
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 32f5220..32ff175 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -32,6 +32,7 @@
#include "sdhci.h"
#define DRIVER_NAME "sdhci"
+#define SDHCI_SUSPEND_TIMEOUT 300 /* 300 ms */
#define DBG(f, x...) \
pr_debug(DRIVER_NAME " [%s()]: " f, __func__,## x)
@@ -68,6 +69,12 @@
}
#endif
+static inline int sdhci_get_async_int_status(struct sdhci_host *host)
+{
+ return (sdhci_readw(host, SDHCI_HOST_CONTROL2) &
+ SDHCI_CTRL_ASYNC_INT_ENABLE) >> 14;
+}
+
static void sdhci_dump_state(struct sdhci_host *host)
{
struct mmc_host *mmc = host->mmc;
@@ -1503,7 +1510,8 @@
mrq->cmd->error = -EIO;
if (mrq->data)
mrq->data->error = -EIO;
- tasklet_schedule(&host->finish_tasklet);
+ mmc_request_done(host->mmc, mrq);
+ sdhci_runtime_pm_put(host);
return;
}
@@ -1578,6 +1586,33 @@
spin_unlock_irqrestore(&host->lock, flags);
}
+static void sdhci_cfg_async_intr(struct sdhci_host *host, bool enable)
+{
+ if (!host->async_int_supp)
+ return;
+
+ if (enable)
+ sdhci_writew(host,
+ sdhci_readw(host, SDHCI_HOST_CONTROL2) |
+ SDHCI_CTRL_ASYNC_INT_ENABLE,
+ SDHCI_HOST_CONTROL2);
+ else
+ sdhci_writew(host, sdhci_readw(host, SDHCI_HOST_CONTROL2) &
+ ~SDHCI_CTRL_ASYNC_INT_ENABLE,
+ SDHCI_HOST_CONTROL2);
+}
+
+static void sdhci_cfg_irq(struct sdhci_host *host, bool enable)
+{
+ if (enable && !host->irq_enabled) {
+ enable_irq(host->irq);
+ host->irq_enabled = true;
+ } else if (!enable && host->irq_enabled) {
+ disable_irq_nosync(host->irq);
+ host->irq_enabled = false;
+ }
+}
+
static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)
{
unsigned long flags;
@@ -1593,9 +1628,26 @@
return;
}
- if (ios->clock)
+ spin_lock_irqsave(&host->lock, flags);
+ /* lock is being released intermittently below, hence disable irq */
+ sdhci_cfg_irq(host, false);
+ spin_unlock_irqrestore(&host->lock, flags);
+ if (ios->clock) {
sdhci_set_clock(host, ios->clock);
-
+ if (host->async_int_supp && sdhci_get_async_int_status(host)) {
+ if (host->disable_sdio_irq_deferred) {
+ pr_debug("%s: %s: disable sdio irq\n",
+ mmc_hostname(host->mmc), __func__);
+ host->mmc->ops->enable_sdio_irq(host->mmc, 0);
+ host->disable_sdio_irq_deferred = false;
+ }
+ spin_lock_irqsave(&host->lock, flags);
+ sdhci_cfg_async_intr(host, false);
+ spin_unlock_irqrestore(&host->lock, flags);
+ pr_debug("%s: %s: unconfig async intr\n",
+ mmc_hostname(host->mmc), __func__);
+ }
+ }
/*
* The controller clocks may be off during power-up and we may end up
* enabling card clock before giving power to the card. Hence, during
@@ -1621,6 +1673,7 @@
}
spin_lock_irqsave(&host->lock, flags);
if (!host->clock) {
+ sdhci_cfg_irq(host, true);
spin_unlock_irqrestore(&host->lock, flags);
mutex_unlock(&host->ios_mutex);
return;
@@ -1780,9 +1833,18 @@
if (host->vmmc && vdd_bit != -1)
mmc_regulator_set_ocr(host->mmc, host->vmmc, vdd_bit);
}
- if (!ios->clock)
+ if (!ios->clock) {
+ if (host->async_int_supp && host->mmc->card &&
+ mmc_card_sdio(host->mmc->card)) {
+ sdhci_cfg_async_intr(host, true);
+ pr_debug("%s: %s: config async intr\n",
+ mmc_hostname(host->mmc), __func__);
+ }
sdhci_set_clock(host, ios->clock);
-
+ }
+ spin_lock_irqsave(&host->lock, flags);
+ sdhci_cfg_irq(host, true);
+ spin_unlock_irqrestore(&host->lock, flags);
mmiowb();
mutex_unlock(&host->ios_mutex);
}
@@ -1862,6 +1924,14 @@
if (host->flags & SDHCI_DEVICE_DEAD)
goto out;
+ if (!enable && !host->clock) {
+ pr_debug("%s: %s: defered disabling card intr\n",
+ host->mmc ? mmc_hostname(host->mmc) : "null",
+ __func__);
+ host->disable_sdio_irq_deferred = true;
+ return;
+ }
+
if (enable)
host->flags |= SDHCI_SDIO_IRQ_ENABLED;
else
@@ -2741,6 +2811,23 @@
return IRQ_HANDLED;
}
+ if (!host->clock && host->mmc->card &&
+ mmc_card_sdio(host->mmc->card)) {
+ /* SDIO async. interrupt is level-sensitive */
+ sdhci_cfg_irq(host, false);
+ pr_debug("%s: got async-irq: clocks: %d gated: %d host-irq[en:1/dis:0]: %d\n",
+ mmc_hostname(host->mmc), host->clock,
+ host->mmc->clk_gated, host->irq_enabled);
+ spin_unlock(&host->lock);
+ /* prevent suspend till the ksdioirqd runs or resume happens */
+ if ((host->mmc->dev_status == DEV_SUSPENDING) ||
+ (host->mmc->dev_status == DEV_SUSPENDED))
+ pm_wakeup_event(&host->mmc->card->dev,
+ SDHCI_SUSPEND_TIMEOUT);
+ else
+ mmc_signal_sdio_irq(host->mmc);
+ return IRQ_HANDLED;
+ }
intmask = sdhci_readl(host, SDHCI_INT_STATUS);
if (!intmask || intmask == 0xffffffff) {
@@ -2836,9 +2923,13 @@
/*
* We have to delay this as it calls back into the driver.
*/
- if (cardint)
+ if (cardint) {
+ /* clks are on, but suspend may be in progress */
+ if (host->mmc->dev_status == DEV_SUSPENDING)
+ pm_wakeup_event(&host->mmc->card->dev,
+ SDHCI_SUSPEND_TIMEOUT);
mmc_signal_sdio_irq(host->mmc);
-
+ }
return result;
}
@@ -3488,6 +3579,7 @@
if (ret)
goto untasklet;
+ host->irq_enabled = true;
host->vmmc = regulator_get(mmc_dev(mmc), "vmmc");
if (IS_ERR(host->vmmc)) {
pr_info("%s: no vmmc regulator found\n", mmc_hostname(mmc));
@@ -3531,8 +3623,12 @@
mmc_hostname(mmc), ret);
}
+ if (caps[0] & SDHCI_ASYNC_INTR)
+ host->async_int_supp = true;
mmc_add_host(mmc);
+ if (host->quirks2 & SDHCI_QUIRK2_IGN_DATA_END_BIT_ERROR)
+ sdhci_clear_set_irqs(host, SDHCI_INT_DATA_END_BIT, 0);
pr_info("%s: SDHCI controller on %s [%s] using %s\n",
mmc_hostname(mmc), host->hw_name, dev_name(mmc_dev(mmc)),
(host->flags & SDHCI_USE_ADMA) ? "ADMA" :
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index db4806d..8c2320b 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -175,6 +175,7 @@
#define SDHCI_CTRL_DRV_TYPE_D 0x0030
#define SDHCI_CTRL_EXEC_TUNING 0x0040
#define SDHCI_CTRL_TUNED_CLK 0x0080
+#define SDHCI_CTRL_ASYNC_INT_ENABLE 0x4000
#define SDHCI_CTRL_PRESET_VAL_ENABLE 0x8000
#define SDHCI_CAPABILITIES 0x40
@@ -195,6 +196,7 @@
#define SDHCI_CAN_VDD_300 0x02000000
#define SDHCI_CAN_VDD_180 0x04000000
#define SDHCI_CAN_64BIT 0x10000000
+#define SDHCI_ASYNC_INTR 0x20000000
#define SDHCI_SUPPORT_SDR50 0x00000001
#define SDHCI_SUPPORT_SDR104 0x00000002
diff --git a/drivers/net/usb/rmnet_usb_data.c b/drivers/net/usb/rmnet_usb_data.c
index b0db01e..84b3324 100644
--- a/drivers/net/usb/rmnet_usb_data.c
+++ b/drivers/net/usb/rmnet_usb_data.c
@@ -550,8 +550,7 @@
break;
default:
- dev_err(&unet->intf->dev, "[%s] error: "
- "rmnet_ioct called for unsupported cmd[%d]",
+ dev_dbg(&unet->intf->dev, "[%s] error: rmnet_ioctl called for unsupported cmd[0x%x]\n",
dev->name, cmd);
return -EINVAL;
}
diff --git a/drivers/net/wireless/wcnss/wcnss_wlan.c b/drivers/net/wireless/wcnss/wcnss_wlan.c
index 66875f7..d3d8d0e 100644
--- a/drivers/net/wireless/wcnss/wcnss_wlan.c
+++ b/drivers/net/wireless/wcnss/wcnss_wlan.c
@@ -40,6 +40,7 @@
#include <mach/msm_smd.h>
#include <mach/msm_iomap.h>
#include <mach/subsystem_restart.h>
+#include <mach/subsystem_notif.h>
#ifdef CONFIG_WCNSS_MEM_PRE_ALLOC
#include "wcnss_prealloc.h"
@@ -145,6 +146,21 @@
#define MSM_PRONTO_PLL_BASE 0xfb21b1c0
#define PRONTO_PLL_STATUS_OFFSET 0x1c
+#define MSM_PRONTO_MCU_BASE 0xfb080c00
+#define MCU_APB2PHY_STATUS_OFFSET 0xec
+#define MCU_CBR_CCAHB_ERR_OFFSET 0x380
+#define MCU_CBR_CAHB_ERR_OFFSET 0x384
+#define MCU_CBR_CCAHB_TIMEOUT_OFFSET 0x388
+#define MCU_CBR_CAHB_TIMEOUT_OFFSET 0x38c
+#define MCU_DBR_CDAHB_ERR_OFFSET 0x390
+#define MCU_DBR_DAHB_ERR_OFFSET 0x394
+#define MCU_DBR_CDAHB_TIMEOUT_OFFSET 0x398
+#define MCU_DBR_DAHB_TIMEOUT_OFFSET 0x39c
+#define MCU_FDBR_CDAHB_ERR_OFFSET 0x3a0
+#define MCU_FDBR_FDAHB_ERR_OFFSET 0x3a4
+#define MCU_FDBR_CDAHB_TIMEOUT_OFFSET 0x3a8
+#define MCU_FDBR_FDAHB_TIMEOUT_OFFSET 0x3ac
+
#define MSM_PRONTO_TXP_STATUS 0xfb08040c
#define MSM_PRONTO_TXP_PHY_ABORT 0xfb080488
#define MSM_PRONTO_BRDG_ERR_SRC 0xfb080fb0
@@ -189,6 +205,7 @@
/* max 20mhz channel count */
#define WCNSS_MAX_CH_NUM 45
+#define WCNSS_MAX_PIL_RETRY 3
#define VALID_VERSION(version) \
((strncmp(version, "INVALID", WCNSS_VERSION_LEN)) ? 1 : 0)
@@ -361,6 +378,7 @@
void __iomem *pronto_ccpu_base;
void __iomem *pronto_saw2_base;
void __iomem *pronto_pll_base;
+ void __iomem *pronto_mcu_base;
void __iomem *wlan_tx_status;
void __iomem *wlan_tx_phy_aborts;
void __iomem *wlan_brdg_err_source;
@@ -390,6 +408,7 @@
struct mutex vbat_monitor_mutex;
u16 unsafe_ch_count;
u16 unsafe_ch_list[WCNSS_MAX_CH_NUM];
+ void *wcnss_notif_hdle;
} *penv = NULL;
static ssize_t wcnss_wlan_macaddr_store(struct device *dev,
@@ -562,45 +581,47 @@
reg_addr = penv->msm_wcnss_base + PRONTO_PMU_SPARE_OFFSET;
reg = readl_relaxed(reg_addr);
- pr_info_ratelimited("%s: PRONTO_PMU_SPARE %08x\n", __func__, reg);
+ pr_err("PRONTO_PMU_SPARE %08x\n", reg);
reg_addr = penv->msm_wcnss_base + PRONTO_PMU_COM_CPU_CBCR_OFFSET;
reg = readl_relaxed(reg_addr);
- pr_info_ratelimited("%s: PRONTO_PMU_COM_CPU_CBCR %08x\n",
- __func__, reg);
+ pr_err("PRONTO_PMU_COM_CPU_CBCR %08x\n", reg);
reg_addr = penv->msm_wcnss_base + PRONTO_PMU_COM_AHB_CBCR_OFFSET;
reg = readl_relaxed(reg_addr);
- pr_info_ratelimited("%s: PRONTO_PMU_COM_AHB_CBCR %08x\n",
- __func__, reg);
+ pr_err("PRONTO_PMU_COM_AHB_CBCR %08x\n", reg);
reg_addr = penv->msm_wcnss_base + PRONTO_PMU_CFG_OFFSET;
reg = readl_relaxed(reg_addr);
- pr_info_ratelimited("%s: PRONTO_PMU_CFG %08x\n", __func__, reg);
+ pr_err("PRONTO_PMU_CFG %08x\n", reg);
reg_addr = penv->msm_wcnss_base + PRONTO_PMU_COM_CSR_OFFSET;
reg = readl_relaxed(reg_addr);
- pr_info_ratelimited("%s: PRONTO_PMU_COM_CSR %08x\n",
- __func__, reg);
+ pr_err("PRONTO_PMU_COM_CSR %08x\n", reg);
reg_addr = penv->msm_wcnss_base + PRONTO_PMU_SOFT_RESET_OFFSET;
reg = readl_relaxed(reg_addr);
- pr_info_ratelimited("%s: PRONTO_PMU_SOFT_RESET %08x\n",
- __func__, reg);
+ pr_err("PRONTO_PMU_SOFT_RESET %08x\n", reg);
reg_addr = penv->pronto_saw2_base + PRONTO_SAW2_SPM_STS_OFFSET;
reg = readl_relaxed(reg_addr);
- pr_info_ratelimited("%s: PRONTO_SAW2_SPM_STS %08x\n", __func__, reg);
+ pr_err("PRONTO_SAW2_SPM_STS %08x\n", reg);
+
+ reg_addr = penv->pronto_pll_base + PRONTO_PLL_STATUS_OFFSET;
+ reg = readl_relaxed(reg_addr);
+ pr_err("PRONTO_PLL_STATUS %08x\n", reg);
+
+ reg_addr = penv->msm_wcnss_base + PRONTO_PMU_CPU_AHB_CMD_RCGR_OFFSET;
+ reg4 = readl_relaxed(reg_addr);
+ pr_err("PMU_CPU_CMD_RCGR %08x\n", reg4);
reg_addr = penv->msm_wcnss_base + PRONTO_PMU_COM_GDSCR_OFFSET;
reg = readl_relaxed(reg_addr);
- pr_info_ratelimited("%s: PRONTO_PMU_COM_GDSCR %08x\n",
- __func__, reg);
+ pr_err("PRONTO_PMU_COM_GDSCR %08x\n", reg);
reg >>= 31;
if (!reg) {
- pr_info_ratelimited("%s: Cannot log, Pronto common SS is power collapsed\n",
- __func__);
+ pr_err("Cannot log, Pronto common SS is power collapsed\n");
return;
}
reg &= ~(PRONTO_PMU_COM_GDSCR_SW_COLLAPSE
@@ -614,35 +635,31 @@
reg_addr = penv->pronto_a2xb_base + A2XB_CFG_OFFSET;
reg = readl_relaxed(reg_addr);
- pr_info_ratelimited("%s: A2XB_CFG_OFFSET %08x\n", __func__, reg);
+ pr_err("A2XB_CFG_OFFSET %08x\n", reg);
reg_addr = penv->pronto_a2xb_base + A2XB_INT_SRC_OFFSET;
reg = readl_relaxed(reg_addr);
- pr_info_ratelimited("%s: A2XB_INT_SRC_OFFSET %08x\n", __func__, reg);
+ pr_err("A2XB_INT_SRC_OFFSET %08x\n", reg);
reg_addr = penv->pronto_a2xb_base + A2XB_ERR_INFO_OFFSET;
reg = readl_relaxed(reg_addr);
- pr_info_ratelimited("%s: A2XB_ERR_INFO_OFFSET %08x\n", __func__, reg);
+ pr_err("A2XB_ERR_INFO_OFFSET %08x\n", reg);
reg_addr = penv->pronto_ccpu_base + CCU_PRONTO_INVALID_ADDR_OFFSET;
reg = readl_relaxed(reg_addr);
- pr_info_ratelimited("%s: CCU_CCPU_INVALID_ADDR %08x\n", __func__, reg);
+ pr_err("CCU_CCPU_INVALID_ADDR %08x\n", reg);
reg_addr = penv->pronto_ccpu_base + CCU_PRONTO_LAST_ADDR0_OFFSET;
reg = readl_relaxed(reg_addr);
- pr_info_ratelimited("%s: CCU_CCPU_LAST_ADDR0 %08x\n", __func__, reg);
+ pr_err("CCU_CCPU_LAST_ADDR0 %08x\n", reg);
reg_addr = penv->pronto_ccpu_base + CCU_PRONTO_LAST_ADDR1_OFFSET;
reg = readl_relaxed(reg_addr);
- pr_info_ratelimited("%s: CCU_CCPU_LAST_ADDR1 %08x\n", __func__, reg);
+ pr_err("CCU_CCPU_LAST_ADDR1 %08x\n", reg);
reg_addr = penv->pronto_ccpu_base + CCU_PRONTO_LAST_ADDR2_OFFSET;
reg = readl_relaxed(reg_addr);
- pr_info_ratelimited("%s: CCU_CCPU_LAST_ADDR2 %08x\n", __func__, reg);
-
- reg_addr = penv->pronto_pll_base + PRONTO_PLL_STATUS_OFFSET;
- reg = readl_relaxed(reg_addr);
- pr_info_ratelimited("%s: PRONTO_PLL_STATUS %08x\n", __func__, reg);
+ pr_err("CCU_CCPU_LAST_ADDR2 %08x\n", reg);
tst_addr = penv->pronto_a2xb_base + A2XB_TSTBUS_OFFSET;
tst_ctrl_addr = penv->pronto_a2xb_base + A2XB_TSTBUS_CTRL_OFFSET;
@@ -652,24 +669,21 @@
reg = reg | WCNSS_TSTBUS_CTRL_EN | WCNSS_TSTBUS_CTRL_RDFIFO;
writel_relaxed(reg, tst_ctrl_addr);
reg = readl_relaxed(tst_addr);
- pr_info_ratelimited("%s: Read data FIFO testbus %08x\n",
- __func__, reg);
+ pr_err("Read data FIFO testbus %08x\n", reg);
/* command FIFO */
reg = 0;
reg = reg | WCNSS_TSTBUS_CTRL_EN | WCNSS_TSTBUS_CTRL_CMDFIFO;
writel_relaxed(reg, tst_ctrl_addr);
reg = readl_relaxed(tst_addr);
- pr_info_ratelimited("%s: Command FIFO testbus %08x\n",
- __func__, reg);
+ pr_err("Command FIFO testbus %08x\n", reg);
/* write data FIFO */
reg = 0;
reg = reg | WCNSS_TSTBUS_CTRL_EN | WCNSS_TSTBUS_CTRL_WRFIFO;
writel_relaxed(reg, tst_ctrl_addr);
reg = readl_relaxed(tst_addr);
- pr_info_ratelimited("%s: Rrite data FIFO testbus %08x\n",
- __func__, reg);
+ pr_err("Rrite data FIFO testbus %08x\n", reg);
/* AXIM SEL CFG0 */
reg = 0;
@@ -677,8 +691,7 @@
WCNSS_TSTBUS_CTRL_AXIM_CFG0;
writel_relaxed(reg, tst_ctrl_addr);
reg = readl_relaxed(tst_addr);
- pr_info_ratelimited("%s: AXIM SEL CFG0 testbus %08x\n",
- __func__, reg);
+ pr_err("AXIM SEL CFG0 testbus %08x\n", reg);
/* AXIM SEL CFG1 */
reg = 0;
@@ -686,8 +699,7 @@
WCNSS_TSTBUS_CTRL_AXIM_CFG1;
writel_relaxed(reg, tst_ctrl_addr);
reg = readl_relaxed(tst_addr);
- pr_info_ratelimited("%s: AXIM SEL CFG1 testbus %08x\n",
- __func__, reg);
+ pr_err("AXIM SEL CFG1 testbus %08x\n", reg);
/* CTRL SEL CFG0 */
reg = 0;
@@ -695,8 +707,7 @@
WCNSS_TSTBUS_CTRL_CTRL_CFG0;
writel_relaxed(reg, tst_ctrl_addr);
reg = readl_relaxed(tst_addr);
- pr_info_ratelimited("%s: CTRL SEL CFG0 testbus %08x\n",
- __func__, reg);
+ pr_err("CTRL SEL CFG0 testbus %08x\n", reg);
/* CTRL SEL CFG1 */
reg = 0;
@@ -704,7 +715,7 @@
WCNSS_TSTBUS_CTRL_CTRL_CFG1;
writel_relaxed(reg, tst_ctrl_addr);
reg = readl_relaxed(tst_addr);
- pr_info_ratelimited("%s: CTRL SEL CFG1 testbus %08x\n", __func__, reg);
+ pr_err("CTRL SEL CFG1 testbus %08x\n", reg);
reg_addr = penv->msm_wcnss_base + PRONTO_PMU_WLAN_BCR_OFFSET;
@@ -715,30 +726,79 @@
reg_addr = penv->msm_wcnss_base + PRONTO_PMU_WLAN_AHB_CBCR_OFFSET;
reg3 = readl_relaxed(reg_addr);
- pr_info_ratelimited("%s: PMU_WLAN_AHB_CBCR %08x\n", __func__, reg3);
-
- reg_addr = penv->msm_wcnss_base + PRONTO_PMU_CPU_AHB_CMD_RCGR_OFFSET;
- reg4 = readl_relaxed(reg_addr);
- pr_info_ratelimited("%s: PMU_CPU_CMD_RCGR %08x\n", __func__, reg4);
+ pr_err("PMU_WLAN_AHB_CBCR %08x\n", reg3);
if ((reg & PRONTO_PMU_WLAN_BCR_BLK_ARES) ||
(reg2 & PRONTO_PMU_WLAN_GDSCR_SW_COLLAPSE) ||
(!(reg4 & PRONTO_PMU_CPU_AHB_CMD_RCGR_ROOT_EN)) ||
(reg3 & PRONTO_PMU_WLAN_AHB_CBCR_CLK_OFF) ||
(!(reg3 & PRONTO_PMU_WLAN_AHB_CBCR_CLK_EN))) {
- pr_info_ratelimited("%s: Cannot log, wlan domain is power collapsed\n",
- __func__);
+ pr_err("Cannot log, wlan domain is power collapsed\n");
return;
}
+ msleep(50);
+
reg = readl_relaxed(penv->wlan_tx_phy_aborts);
- pr_info_ratelimited("%s: WLAN_TX_PHY_ABORTS %08x\n", __func__, reg);
+ pr_err("WLAN_TX_PHY_ABORTS %08x\n", reg);
+
+ reg_addr = penv->pronto_mcu_base + MCU_APB2PHY_STATUS_OFFSET;
+ reg = readl_relaxed(reg_addr);
+ pr_err("MCU_APB2PHY_STATUS %08x\n", reg);
+
+ reg_addr = penv->pronto_mcu_base + MCU_CBR_CCAHB_ERR_OFFSET;
+ reg = readl_relaxed(reg_addr);
+ pr_err("MCU_CBR_CCAHB_ERR %08x\n", reg);
+
+ reg_addr = penv->pronto_mcu_base + MCU_CBR_CAHB_ERR_OFFSET;
+ reg = readl_relaxed(reg_addr);
+ pr_err("MCU_CBR_CAHB_ERR %08x\n", reg);
+
+ reg_addr = penv->pronto_mcu_base + MCU_CBR_CCAHB_TIMEOUT_OFFSET;
+ reg = readl_relaxed(reg_addr);
+ pr_err("MCU_CBR_CCAHB_TIMEOUT %08x\n", reg);
+
+ reg_addr = penv->pronto_mcu_base + MCU_CBR_CAHB_TIMEOUT_OFFSET;
+ reg = readl_relaxed(reg_addr);
+ pr_err("MCU_CBR_CAHB_TIMEOUT %08x\n", reg);
+
+ reg_addr = penv->pronto_mcu_base + MCU_DBR_CDAHB_ERR_OFFSET;
+ reg = readl_relaxed(reg_addr);
+ pr_err("MCU_DBR_CDAHB_ERR %08x\n", reg);
+
+ reg_addr = penv->pronto_mcu_base + MCU_DBR_DAHB_ERR_OFFSET;
+ reg = readl_relaxed(reg_addr);
+ pr_err("MCU_DBR_DAHB_ERR %08x\n", reg);
+
+ reg_addr = penv->pronto_mcu_base + MCU_DBR_CDAHB_TIMEOUT_OFFSET;
+ reg = readl_relaxed(reg_addr);
+ pr_err("MCU_DBR_CDAHB_TIMEOUT %08x\n", reg);
+
+ reg_addr = penv->pronto_mcu_base + MCU_DBR_DAHB_TIMEOUT_OFFSET;
+ reg = readl_relaxed(reg_addr);
+ pr_err("MCU_DBR_DAHB_TIMEOUT %08x\n", reg);
+
+ reg_addr = penv->pronto_mcu_base + MCU_FDBR_CDAHB_ERR_OFFSET;
+ reg = readl_relaxed(reg_addr);
+ pr_err("MCU_FDBR_CDAHB_ERR %08x\n", reg);
+
+ reg_addr = penv->pronto_mcu_base + MCU_FDBR_FDAHB_ERR_OFFSET;
+ reg = readl_relaxed(reg_addr);
+ pr_err("MCU_FDBR_FDAHB_ERR %08x\n", reg);
+
+ reg_addr = penv->pronto_mcu_base + MCU_FDBR_CDAHB_TIMEOUT_OFFSET;
+ reg = readl_relaxed(reg_addr);
+ pr_err("MCU_FDBR_CDAHB_TIMEOUT %08x\n", reg);
+
+ reg_addr = penv->pronto_mcu_base + MCU_FDBR_FDAHB_TIMEOUT_OFFSET;
+ reg = readl_relaxed(reg_addr);
+ pr_err("MCU_FDBR_FDAHB_TIMEOUT %08x\n", reg);
reg = readl_relaxed(penv->wlan_brdg_err_source);
- pr_info_ratelimited("%s: WLAN_BRDG_ERR_SOURCE %08x\n", __func__, reg);
+ pr_err("WLAN_BRDG_ERR_SOURCE %08x\n", reg);
reg = readl_relaxed(penv->wlan_tx_status);
- pr_info_ratelimited("%s: WLAN_TX_STATUS %08x\n", __func__, reg);
+ pr_err("WLAN_TXP_STATUS %08x\n", reg);
reg = readl_relaxed(penv->alarms_txctl);
pr_err("ALARMS_TXCTL %08x\n", reg);
@@ -2068,6 +2128,7 @@
unsigned long wcnss_phys_addr;
int size = 0;
struct resource *res;
+ int pil_retry = 0;
int has_pronto_hw = of_property_read_bool(pdev->dev.of_node,
"qcom,has-pronto-hw");
@@ -2249,6 +2310,13 @@
pr_err("%s: ioremap alarms TACTL failed\n", __func__);
goto fail_ioremap11;
}
+ penv->pronto_mcu_base = ioremap(MSM_PRONTO_MCU_BASE, SZ_1K);
+ if (!penv->pronto_mcu_base) {
+ ret = -ENOMEM;
+ pr_err("%s: ioremap wcnss physical(mcu) failed\n",
+ __func__);
+ goto fail_ioremap12;
+ }
}
penv->adc_tm_dev = qpnp_get_adc_tm(&penv->pdev->dev, "wcnss");
if (IS_ERR(penv->adc_tm_dev)) {
@@ -2259,12 +2327,17 @@
penv->fw_vbatt_state = WCNSS_CONFIG_UNSPECIFIED;
}
- /* trigger initialization of the WCNSS */
- penv->pil = subsystem_get(WCNSS_PIL_DEVICE);
- if (IS_ERR(penv->pil)) {
- dev_err(&pdev->dev, "Peripheral Loader failed on WCNSS.\n");
- ret = PTR_ERR(penv->pil);
- wcnss_pronto_log_debug_regs();
+ do {
+ /* trigger initialization of the WCNSS */
+ penv->pil = subsystem_get(WCNSS_PIL_DEVICE);
+ if (IS_ERR(penv->pil)) {
+ dev_err(&pdev->dev, "Peripheral Loader failed on WCNSS.\n");
+ ret = PTR_ERR(penv->pil);
+ wcnss_pronto_log_debug_regs();
+ }
+ } while (pil_retry++ < WCNSS_MAX_PIL_RETRY && IS_ERR(penv->pil));
+
+ if (pil_retry >= WCNSS_MAX_PIL_RETRY) {
penv->pil = NULL;
goto fail_pil;
}
@@ -2274,6 +2347,9 @@
fail_pil:
if (penv->riva_ccu_base)
iounmap(penv->riva_ccu_base);
+ if (penv->pronto_mcu_base)
+ iounmap(penv->pronto_mcu_base);
+fail_ioremap12:
if (penv->alarms_tactl)
iounmap(penv->alarms_tactl);
fail_ioremap11:
@@ -2440,6 +2516,22 @@
}
+static int wcnss_notif_cb(struct notifier_block *this, unsigned long code,
+ void *ss_handle)
+{
+ pr_debug("%s: wcnss notification event: %lu\n", __func__, code);
+
+ if (SUBSYS_POWERUP_FAILURE == code)
+ wcnss_pronto_log_debug_regs();
+
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block wnb = {
+ .notifier_call = wcnss_notif_cb,
+};
+
+
static const struct file_operations wcnss_node_fops = {
.owner = THIS_MODULE,
.open = wcnss_node_open,
@@ -2479,6 +2571,13 @@
return -ENOENT;
}
+ /* register wcnss event notification */
+ penv->wcnss_notif_hdle = subsys_notif_register_notifier("wcnss", &wnb);
+ if (IS_ERR(penv->wcnss_notif_hdle)) {
+ pr_err("wcnss: register event notification failed!\n");
+ return PTR_ERR(penv->wcnss_notif_hdle);
+ }
+
mutex_init(&penv->dev_lock);
mutex_init(&penv->ctrl_lock);
mutex_init(&penv->vbat_monitor_mutex);
@@ -2503,6 +2602,8 @@
static int __devexit
wcnss_wlan_remove(struct platform_device *pdev)
{
+ if (penv->wcnss_notif_hdle)
+ subsys_notif_unregister_notifier(penv->wcnss_notif_hdle, &wnb);
wcnss_remove_sysfs(&pdev->dev);
penv = NULL;
return 0;
diff --git a/drivers/nfc/nfc-nci.c b/drivers/nfc/nfc-nci.c
index a44c06c..8cd4bd1 100644
--- a/drivers/nfc/nfc-nci.c
+++ b/drivers/nfc/nfc-nci.c
@@ -13,6 +13,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
+#include <linux/reboot.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/irq.h>
@@ -47,7 +48,7 @@
MODULE_DEVICE_TABLE(of, msm_match_table);
#define MAX_BUFFER_SIZE (780)
-#define PACKET_MAX_LENGTH (258)
+#define PACKET_MAX_LENGTH (258)
/* Read data */
#define PACKET_HEADER_SIZE_NCI (4)
#define PACKET_TYPE_NCI (16)
@@ -58,7 +59,7 @@
#define NTF_TIMEOUT (10000)
#define CORE_RESET_RSP_GID (0x60)
#define CORE_RESET_OID (0x00)
-#define CORE_RST_NTF_LENGTH (0x02)
+#define CORE_RST_NTF_LENGTH (0x02)
static void clk_req_update(struct work_struct *work);
@@ -115,6 +116,9 @@
static int ftm_werr_code;
+unsigned int disable_ctrl;
+bool region2_sent;
+
static void qca199x_init_stat(struct qca199x_dev *qca199x_dev)
{
qca199x_dev->count_irq = 0;
@@ -491,6 +495,12 @@
}
mutex_unlock(&qca199x_dev->read_mutex);
+ /* If we detect a Region2 command prior to power-down */
+ if ((tmp[0] == 0x2F) && (tmp[1] == 0x01) && (tmp[2] == 0x02) &&
+ (tmp[3] == 0x08) && (tmp[4] == 0x00)) {
+ region2_sent = true;
+ }
+
return ret;
}
@@ -606,7 +616,7 @@
if (r < 0)
goto err_req;
/*
- Also, set flag for initial NCI write following resetas
+ Also, set flag for initial NCI write following reset as
may wish to do some house keeping. Ensure no pending
messages in NFCC buffers which may be wrongly
construed as response to initial message
@@ -718,7 +728,7 @@
int r = 0;
unsigned short slave_addr = 0xE;
unsigned short curr_addr;
-
+ unsigned char raw_nci_wake[] = {0x10, 0x0F};
unsigned char raw_chip_version_addr = 0x00;
unsigned char raw_chip_rev_id_addr = 0x9C;
unsigned char raw_chip_version = 0xFF;
@@ -728,9 +738,22 @@
platform_data = qca199x_dev->client->dev.platform_data;
+ /*
+ * Always wake up chip when reading 0x9C, otherwise this
+ * register is not updated
+ */
+ curr_addr = qca199x_dev->client->addr;
+ qca199x_dev->client->addr = slave_addr;
+ r = nfc_i2c_write(qca199x_dev->client, &raw_nci_wake[0],
+ sizeof(raw_nci_wake));
+ r = sizeof(raw_nci_wake);
+ if (r != sizeof(raw_nci_wake))
+ goto invalid_wake_up;
+ qca199x_dev->state = NFCC_STATE_NORMAL_WAKE;
+
+ /* sleep to ensure the NFCC has time to wake up */
+ usleep(100);
if (arg == 0) {
- curr_addr = qca199x_dev->client->addr;
- qca199x_dev->client->addr = slave_addr;
r = nfc_i2c_write(qca199x_dev->client,
&raw_chip_version_addr, 1);
if (r < 0)
@@ -739,21 +762,19 @@
r = i2c_master_recv(qca199x_dev->client, &raw_chip_version, 1);
/* Restore original NFCC slave I2C address */
qca199x_dev->client->addr = curr_addr;
- }
- if (arg == 1) {
- curr_addr = qca199x_dev->client->addr;
- qca199x_dev->client->addr = slave_addr;
+ } else if (arg == 1) {
r = nfc_i2c_write(qca199x_dev->client,
&raw_chip_rev_id_addr, 1);
if (r < 0)
goto invalid_wr;
- usleep(10);
+ usleep(20);
r = i2c_master_recv(qca199x_dev->client, &raw_chip_version, 1);
/* Restore original NFCC slave I2C address */
qca199x_dev->client->addr = curr_addr;
}
-
return raw_chip_version;
+invalid_wake_up:
+ raw_chip_version = 0xFE;
invalid_wr:
raw_chip_version = 0xFF;
dev_err(&qca199x_dev->client->dev,
@@ -929,121 +950,117 @@
if (r < 0)
goto err_init;
- if (0x10 != (0x10 & buf)) {
- RAW(s73, 0x02);
+ RAW(s73, 0x02);
- r = nfc_i2c_write(client, &raw_s73[0], sizeof(raw_s73));
- if (r < 0)
- goto err_init;
-
- usleep(1000);
- RAW(1p8_CONTROL_011, XTAL_CLOCK | 0x01);
-
- r = nfc_i2c_write(client, &raw_1p8_CONTROL_011[0],
- sizeof(raw_1p8_CONTROL_011));
- if (r < 0)
- goto err_init;
-
- usleep(1000);
- RAW(1P8_CONTROL_010, (0x8));
- r = nfc_i2c_write(client, &raw_1P8_CONTROL_010[0],
- sizeof(raw_1P8_CONTROL_010));
- if (r < 0)
- goto err_init;
-
- usleep(10000); /* 10ms wait */
- RAW(1P8_CONTROL_010, (0xC));
- r = nfc_i2c_write(client, &raw_1P8_CONTROL_010[0],
- sizeof(raw_1P8_CONTROL_010));
- if (r < 0)
- goto err_init;
-
- usleep(100); /* 100uS wait */
- RAW(1P8_X0_0B0, (FREQ_SEL_19));
- r = nfc_i2c_write(client, &raw_1P8_X0_0B0[0],
- sizeof(raw_1P8_X0_0B0));
- if (r < 0)
- goto err_init;
-
- usleep(1000);
-
- /* PWR_EN = 1 */
- RAW(1P8_CONTROL_010, (0xd));
- r = nfc_i2c_write(client, &raw_1P8_CONTROL_010[0],
- sizeof(raw_1P8_CONTROL_010));
- if (r < 0)
- goto err_init;
-
-
- usleep(20000); /* 20ms wait */
- /* LS_EN = 1 */
- RAW(1P8_CONTROL_010, 0xF);
- r = nfc_i2c_write(client, &raw_1P8_CONTROL_010[0],
- sizeof(raw_1P8_CONTROL_010));
- if (r < 0)
- goto err_init;
-
- usleep(20000); /* 20ms wait */
-
- /* Enable the PMIC clock */
- RAW(1P8_PAD_CFG_CLK_REQ, (0x1));
- r = nfc_i2c_write(client, &raw_1P8_PAD_CFG_CLK_REQ[0],
- sizeof(raw_1P8_PAD_CFG_CLK_REQ));
- if (r < 0)
- goto err_init;
-
- usleep(1000);
-
- RAW(1P8_PAD_CFG_PWR_REQ, (0x1));
- r = nfc_i2c_write(client, &raw_1P8_PAD_CFG_PWR_REQ[0],
- sizeof(raw_1P8_PAD_CFG_PWR_REQ));
- if (r < 0)
- goto err_init;
-
- usleep(1000);
-
- RAW(slave2, 0x10);
- r = nfc_i2c_write(client, &raw_slave2[0], sizeof(raw_slave2));
- if (r < 0)
- goto err_init;
-
- usleep(1000);
-
- RAW(slave1, NCI_I2C_SLAVE);
- r = nfc_i2c_write(client, &raw_slave1[0], sizeof(raw_slave1));
- if (r < 0)
- goto err_init;
-
- usleep(1000);
-
- /* QCA199x NFCC CPU should now boot... */
- r = i2c_master_recv(client, &raw_slave1_rd, 1);
- /* Talk on NCI slave address NCI_I2C_SLAVE 0x2C*/
- client->addr = NCI_I2C_SLAVE;
-
- /*
- Start with small delay and then we will poll until we
- get a core reset notification - This is time for chip
- & NFCC controller to come-up.
- */
- usleep(1000); /* 1 ms */
-
- do {
- ret = i2c_master_recv(client, rsp, 5);
- /* Found core reset notification */
- if (((rsp[0] == CORE_RESET_RSP_GID) &&
- (rsp[1] == CORE_RESET_OID) &&
- (rsp[2] == CORE_RST_NTF_LENGTH))
- || time_taken == NTF_TIMEOUT) {
- core_reset_completed = true;
- }
- usleep(10); /* 10us sleep before retry */
- time_taken++;
- } while (!core_reset_completed);
- r = 0;
- } else {
+ r = nfc_i2c_write(client, &raw_s73[0], sizeof(raw_s73));
+ if (r < 0)
goto err_init;
- }
+
+ usleep(1000);
+ RAW(1p8_CONTROL_011, XTAL_CLOCK | 0x01);
+
+ r = nfc_i2c_write(client, &raw_1p8_CONTROL_011[0],
+ sizeof(raw_1p8_CONTROL_011));
+ if (r < 0)
+ goto err_init;
+
+ usleep(1000);
+ RAW(1P8_CONTROL_010, (0x8));
+ r = nfc_i2c_write(client, &raw_1P8_CONTROL_010[0],
+ sizeof(raw_1P8_CONTROL_010));
+ if (r < 0)
+ goto err_init;
+
+ usleep(10000); /* 10ms wait */
+ RAW(1P8_CONTROL_010, (0xC));
+ r = nfc_i2c_write(client, &raw_1P8_CONTROL_010[0],
+ sizeof(raw_1P8_CONTROL_010));
+ if (r < 0)
+ goto err_init;
+
+ usleep(100); /* 100uS wait */
+ RAW(1P8_X0_0B0, (FREQ_SEL_19));
+ r = nfc_i2c_write(client, &raw_1P8_X0_0B0[0],
+ sizeof(raw_1P8_X0_0B0));
+ if (r < 0)
+ goto err_init;
+
+ usleep(1000);
+
+ /* PWR_EN = 1 */
+ RAW(1P8_CONTROL_010, (0xd));
+ r = nfc_i2c_write(client, &raw_1P8_CONTROL_010[0],
+ sizeof(raw_1P8_CONTROL_010));
+ if (r < 0)
+ goto err_init;
+
+
+ usleep(20000); /* 20ms wait */
+ /* LS_EN = 1 */
+ RAW(1P8_CONTROL_010, 0xF);
+ r = nfc_i2c_write(client, &raw_1P8_CONTROL_010[0],
+ sizeof(raw_1P8_CONTROL_010));
+ if (r < 0)
+ goto err_init;
+
+ usleep(20000); /* 20ms wait */
+
+ /* Enable the PMIC clock */
+ RAW(1P8_PAD_CFG_CLK_REQ, (0x1));
+ r = nfc_i2c_write(client, &raw_1P8_PAD_CFG_CLK_REQ[0],
+ sizeof(raw_1P8_PAD_CFG_CLK_REQ));
+ if (r < 0)
+ goto err_init;
+
+ usleep(1000);
+
+ RAW(1P8_PAD_CFG_PWR_REQ, (0x1));
+ r = nfc_i2c_write(client, &raw_1P8_PAD_CFG_PWR_REQ[0],
+ sizeof(raw_1P8_PAD_CFG_PWR_REQ));
+ if (r < 0)
+ goto err_init;
+
+ usleep(1000);
+
+ RAW(slave2, 0x10);
+ r = nfc_i2c_write(client, &raw_slave2[0], sizeof(raw_slave2));
+ if (r < 0)
+ goto err_init;
+
+ usleep(1000);
+
+ RAW(slave1, NCI_I2C_SLAVE);
+ r = nfc_i2c_write(client, &raw_slave1[0], sizeof(raw_slave1));
+ if (r < 0)
+ goto err_init;
+
+ usleep(1000);
+
+ /* QCA199x NFCC CPU should now boot... */
+ r = i2c_master_recv(client, &raw_slave1_rd, 1);
+ /* Talk on NCI slave address NCI_I2C_SLAVE 0x2C*/
+ client->addr = NCI_I2C_SLAVE;
+
+ /*
+ Start with small delay and then we will poll until we
+ get a core reset notification - This is time for chip
+ & NFCC controller to come-up.
+ */
+ usleep(1000); /* 1 ms */
+
+ do {
+ ret = i2c_master_recv(client, rsp, 5);
+ /* Found core reset notification */
+ if (((rsp[0] == CORE_RESET_RSP_GID) &&
+ (rsp[1] == CORE_RESET_OID) &&
+ (rsp[2] == CORE_RST_NTF_LENGTH))
+ || time_taken == NTF_TIMEOUT) {
+ core_reset_completed = true;
+ }
+ usleep(10); /* 10us sleep before retry */
+ time_taken++;
+ } while (!core_reset_completed);
+ r = 0;
return r;
err_init:
r = 1;
@@ -1143,6 +1160,7 @@
pdata->dis_gpio = of_get_named_gpio(np, "qcom,dis-gpio", 0);
if ((!gpio_is_valid(pdata->dis_gpio)))
return -EINVAL;
+ disable_ctrl = pdata->dis_gpio;
pdata->irq_gpio = of_get_named_gpio(np, "qcom,irq-gpio", 0);
if ((!gpio_is_valid(pdata->irq_gpio)))
@@ -1246,8 +1264,12 @@
goto err_free_dev;
}
- /* Put device in ULPM */
- gpio_set_value(platform_data->dis_gpio, 0);
+ /* Guarantee that the NFCC starts in a clean state. */
+ gpio_set_value(platform_data->dis_gpio, 1);/* HPD */
+ usleep(200);
+ gpio_set_value(platform_data->dis_gpio, 0);/* ULPM */
+ usleep(200);
+
r = nfcc_hw_check(client, platform_data->reg);
if (r) {
/* We don't think there is hardware but just in case HPD */
@@ -1439,6 +1461,10 @@
}
i2c_set_clientdata(client, qca199x_dev);
gpio_set_value(platform_data->dis_gpio, 1);
+
+ /* To keep track if region2 command has been sent to controller */
+ region2_sent = false;
+
dev_dbg(&client->dev,
"nfc-nci probe: %s, probing qca1990 exited successfully\n",
__func__);
@@ -1513,17 +1539,52 @@
},
};
+
+static int nfcc_reboot(struct notifier_block *notifier, unsigned long val,
+ void *v)
+{
+ /*
+ * Set DISABLE=1 *ONLY* if the NFC service has been disabled.
+ * This will put NFCC into HPD(Hard Power Down) state for power
+ * saving when powering down(Low Batt. or Power off handset)
+ * If user requires NFC and CE mode when powered down(PD) the
+ * middleware puts NFCC into region2 prior to PD. In this case
+ * we DO NOT HPD chip as this will trash Region2 and CE support
+ * when handset is PD.
+ */
+ if (region2_sent == false) {
+ /* HPD the NFCC */
+ gpio_set_value(disable_ctrl, 1);
+ }
+ return NOTIFY_OK;
+}
+
+
+static struct notifier_block nfcc_notifier = {
+ .notifier_call = nfcc_reboot,
+ .next = NULL,
+ .priority = 0
+};
+
/*
* module load/unload record keeping
*/
static int __init qca199x_dev_init(void)
{
+ int ret;
+
+ ret = register_reboot_notifier(&nfcc_notifier);
+ if (ret) {
+ pr_err("cannot register reboot notifier (err=%d)\n", ret);
+ return ret;
+ }
return i2c_add_driver(&qca199x);
}
module_init(qca199x_dev_init);
static void __exit qca199x_dev_exit(void)
{
+ unregister_reboot_notifier(&nfcc_notifier);
i2c_del_driver(&qca199x);
}
module_exit(qca199x_dev_exit);
diff --git a/drivers/platform/msm/ipa/ipa.c b/drivers/platform/msm/ipa/ipa.c
index 20603f5..c93ec5f 100644
--- a/drivers/platform/msm/ipa/ipa.c
+++ b/drivers/platform/msm/ipa/ipa.c
@@ -544,6 +544,13 @@
retval = -EFAULT;
break;
}
+
+ if (((struct ipa_ioc_query_intf_tx_props *)header)->num_tx_props
+ > IPA_NUM_PROPS_MAX) {
+ retval = -EFAULT;
+ break;
+ }
+
pyld_sz = sz + ((struct ipa_ioc_query_intf_tx_props *)
header)->num_tx_props *
sizeof(struct ipa_ioc_tx_intf_prop);
@@ -572,6 +579,13 @@
retval = -EFAULT;
break;
}
+
+ if (((struct ipa_ioc_query_intf_rx_props *)header)->num_rx_props
+ > IPA_NUM_PROPS_MAX) {
+ retval = -EFAULT;
+ break;
+ }
+
pyld_sz = sz + ((struct ipa_ioc_query_intf_rx_props *)
header)->num_rx_props *
sizeof(struct ipa_ioc_rx_intf_prop);
diff --git a/drivers/platform/msm/ipa/ipa_intf.c b/drivers/platform/msm/ipa/ipa_intf.c
index 5ee1929..ea5c97f 100644
--- a/drivers/platform/msm/ipa/ipa_intf.c
+++ b/drivers/platform/msm/ipa/ipa_intf.c
@@ -60,6 +60,18 @@
return -EINVAL;
}
+ if (tx && tx->num_props > IPA_NUM_PROPS_MAX) {
+ IPAERR("invalid tx num_props=%d max=%d\n", tx->num_props,
+ IPA_NUM_PROPS_MAX);
+ return -EINVAL;
+ }
+
+ if (rx && rx->num_props > IPA_NUM_PROPS_MAX) {
+ IPAERR("invalid rx num_props=%d max=%d\n", rx->num_props,
+ IPA_NUM_PROPS_MAX);
+ return -EINVAL;
+ }
+
len = sizeof(struct ipa_intf);
intf = kzalloc(len, GFP_KERNEL);
if (intf == NULL) {
diff --git a/drivers/platform/msm/qpnp-power-on.c b/drivers/platform/msm/qpnp-power-on.c
index 0ef2639..462bd3b 100644
--- a/drivers/platform/msm/qpnp-power-on.c
+++ b/drivers/platform/msm/qpnp-power-on.c
@@ -24,6 +24,13 @@
#include <linux/log2.h>
#include <linux/qpnp/power-on.h>
+#define PMIC_VER_8941 0x01
+#define PMIC_VERSION_REG 0x0105
+#define PMIC_VERSION_REV4_REG 0x0103
+
+#define PMIC8941_V1_REV4 0x01
+#define PMIC8941_V2_REV4 0x02
+
/* Common PNP defines */
#define QPNP_PON_REVISION2(base) (base + 0x01)
@@ -36,6 +43,7 @@
#define QPNP_PON_REASON1(base) (base + 0x8)
#define QPNP_PON_WARM_RESET_REASON1(base) (base + 0xA)
#define QPNP_PON_WARM_RESET_REASON2(base) (base + 0xB)
+#define QPNP_POFF_REASON1(base) (base + 0xC)
#define QPNP_PON_KPDPWR_S1_TIMER(base) (base + 0x40)
#define QPNP_PON_KPDPWR_S2_TIMER(base) (base + 0x41)
#define QPNP_PON_KPDPWR_S2_CNTL(base) (base + 0x42)
@@ -93,7 +101,8 @@
#define QPNP_PON_S3_DBC_DELAY_MASK 0x07
#define QPNP_PON_RESET_TYPE_MAX 0xF
#define PON_S1_COUNT_MAX 0xF
-#define PON_REASON_MAX 8
+#define QPNP_PON_MIN_DBC_US (USEC_PER_SEC / 64)
+#define QPNP_PON_MAX_DBC_US (USEC_PER_SEC * 2)
#define QPNP_KEY_STATUS_DELAY msecs_to_jiffies(250)
#define QPNP_PON_REV_B 0x01
@@ -147,6 +156,26 @@
[7] = "Triggered from KPD (power key press)",
};
+static const char * const qpnp_poff_reason[] = {
+ [0] = "Triggered from SOFT (Software)",
+ [1] = "Triggered from PS_HOLD (PS_HOLD/MSM controlled shutdown)",
+ [2] = "Triggered from PMIC_WD (PMIC watchdog)",
+ [3] = "Triggered from GP1 (Keypad_Reset1)",
+ [4] = "Triggered from GP2 (Keypad_Reset2)",
+ [5] = "Triggered from KPDPWR_AND_RESIN"
+ "(Simultaneous power key and reset line)",
+ [6] = "Triggered from RESIN_N (Reset line/Volume Down Key)",
+ [7] = "Triggered from KPDPWR_N (Long Power Key hold)",
+ [8] = "N/A",
+ [9] = "N/A",
+ [10] = "N/A",
+ [11] = "Triggered from CHARGER (Charger ENUM_TIMER, BOOT_DONE)",
+ [12] = "Triggered from TFT (Thermal Fault Tolerance)",
+ [13] = "Triggered from UVLO (Under Voltage Lock Out)",
+ [14] = "Triggered from OTST3 (Overtemp)",
+ [15] = "Triggered from STAGE3 (Stage 3 reset)",
+};
+
static int
qpnp_pon_masked_write(struct qpnp_pon *pon, u16 addr, u8 mask, u8 val)
{
@@ -754,6 +783,8 @@
struct device_node *pp = NULL;
struct qpnp_pon_config *cfg;
u8 pon_ver;
+ u8 pmic_type;
+ u8 revid_rev4;
/* Check if it is rev B */
rc = spmi_ext_register_readl(pon->spmi->ctrl, pon->spmi->sid,
@@ -836,6 +867,38 @@
cfg->use_bark = of_property_read_bool(pp,
"qcom,use-bark");
+
+ rc = spmi_ext_register_readl(pon->spmi->ctrl,
+ pon->spmi->sid, PMIC_VERSION_REG,
+ &pmic_type, 1);
+
+ if (rc) {
+ dev_err(&pon->spmi->dev,
+ "Unable to read PMIC type\n");
+ return rc;
+ }
+
+ if (pmic_type == PMIC_VER_8941) {
+
+ rc = spmi_ext_register_readl(pon->spmi->ctrl,
+ pon->spmi->sid, PMIC_VERSION_REV4_REG,
+ &revid_rev4, 1);
+
+ if (rc) {
+ dev_err(&pon->spmi->dev,
+ "Unable to read PMIC revision ID\n");
+ return rc;
+ }
+
+ /*PM8941 V3 does not have harware bug. Hence
+ bark is not required from PMIC versions 3.0*/
+ if (!(revid_rev4 == PMIC8941_V1_REV4 ||
+ revid_rev4 == PMIC8941_V2_REV4)) {
+ cfg->support_reset = false;
+ cfg->use_bark = false;
+ }
+ }
+
if (cfg->use_bark) {
cfg->bark_irq = spmi_get_irq_byname(pon->spmi,
NULL, "resin-bark");
@@ -1035,9 +1098,10 @@
struct device_node *itr = NULL;
u32 delay = 0, s3_debounce = 0;
int rc, sys_reset, index;
- u8 pon_sts = 0;
+ u8 pon_sts = 0, buf[2];
const char *s3_src;
u8 s3_src_reg;
+ u16 poff_sts = 0;
pon = devm_kzalloc(&spmi->dev, sizeof(struct qpnp_pon),
GFP_KERNEL);
@@ -1085,14 +1149,38 @@
dev_err(&pon->spmi->dev, "Unable to read PON_RESASON1 reg\n");
return rc;
}
- index = ffs(pon_sts);
- if ((index > PON_REASON_MAX) || (index < 0))
- index = 0;
+ index = ffs(pon_sts) - 1;
cold_boot = !qpnp_pon_is_warm_reset();
- pr_info("PMIC@SID%d Power-on reason: %s and '%s' boot\n",
- pon->spmi->sid, index ? qpnp_pon_reason[index - 1] :
- "Unknown", cold_boot ? "cold" : "warm");
+ if (index >= ARRAY_SIZE(qpnp_pon_reason) || index < 0)
+ dev_info(&pon->spmi->dev,
+ "PMIC@SID%d Power-on reason: Unknown and '%s' boot\n",
+ pon->spmi->sid, cold_boot ? "cold" : "warm");
+ else
+ dev_info(&pon->spmi->dev,
+ "PMIC@SID%d Power-on reason: %s and '%s' boot\n",
+ pon->spmi->sid, qpnp_pon_reason[index],
+ cold_boot ? "cold" : "warm");
+
+ /* POFF reason */
+ rc = spmi_ext_register_readl(pon->spmi->ctrl, pon->spmi->sid,
+ QPNP_POFF_REASON1(pon->base),
+ buf, 2);
+ if (rc) {
+ dev_err(&pon->spmi->dev, "Unable to read POFF_RESASON regs\n");
+ return rc;
+ }
+ poff_sts = buf[0] | (buf[1] << 8);
+ index = ffs(poff_sts) - 1;
+ if (index >= ARRAY_SIZE(qpnp_poff_reason) || index < 0)
+ dev_info(&pon->spmi->dev,
+ "PMIC@SID%d: Unknown power-off reason\n",
+ pon->spmi->sid);
+ else
+ dev_info(&pon->spmi->dev,
+ "PMIC@SID%d: Power-off reason: %s\n",
+ pon->spmi->sid,
+ qpnp_poff_reason[index]);
rc = of_property_read_u32(pon->spmi->dev.of_node,
"qcom,pon-dbc-delay", &delay);
diff --git a/drivers/power/qpnp-bms.c b/drivers/power/qpnp-bms.c
index a0d9a24..ed0279e 100644
--- a/drivers/power/qpnp-bms.c
+++ b/drivers/power/qpnp-bms.c
@@ -491,7 +491,7 @@
}
static inline int convert_vbatt_raw_to_uv(struct qpnp_bms_chip *chip,
- uint16_t reading)
+ uint16_t reading, bool is_pon_ocv)
{
int64_t uv;
int rc;
@@ -500,7 +500,7 @@
pr_debug("%u raw converted into %lld uv\n", reading, uv);
uv = adjust_vbatt_reading(chip, uv);
pr_debug("adjusted into %lld uv\n", uv);
- rc = qpnp_vbat_sns_comp_result(chip->vadc_dev, &uv);
+ rc = qpnp_vbat_sns_comp_result(chip->vadc_dev, &uv, is_pon_ocv);
if (rc)
pr_debug("could not compensate vbatt\n");
pr_debug("compensated into %lld uv\n", uv);
@@ -699,7 +699,7 @@
static void convert_and_store_ocv(struct qpnp_bms_chip *chip,
struct raw_soc_params *raw,
- int batt_temp)
+ int batt_temp, bool is_pon_ocv)
{
int rc;
@@ -711,7 +711,7 @@
pr_err("Vadc reference voltage read failed, rc = %d\n", rc);
chip->prev_last_good_ocv_raw = raw->last_good_ocv_raw;
raw->last_good_ocv_uv = convert_vbatt_raw_to_uv(chip,
- raw->last_good_ocv_raw);
+ raw->last_good_ocv_raw, is_pon_ocv);
chip->last_ocv_uv = raw->last_good_ocv_uv;
chip->last_ocv_temp = batt_temp;
chip->software_cc_uah = 0;
@@ -1042,7 +1042,7 @@
mutex_unlock(&chip->bms_output_lock);
if (chip->prev_last_good_ocv_raw == OCV_RAW_UNINITIALIZED) {
- convert_and_store_ocv(chip, raw, batt_temp);
+ convert_and_store_ocv(chip, raw, batt_temp, true);
pr_debug("PON_OCV_UV = %d, cc = %llx\n",
chip->last_ocv_uv, raw->cc);
warm_reset = qpnp_pon_is_warm_reset();
@@ -1078,7 +1078,7 @@
pr_debug("EOC Battery full ocv_reading = 0x%x\n",
chip->ocv_reading_at_100);
} else if (chip->prev_last_good_ocv_raw != raw->last_good_ocv_raw) {
- convert_and_store_ocv(chip, raw, batt_temp);
+ convert_and_store_ocv(chip, raw, batt_temp, false);
/* forget the old cc value upon ocv */
chip->last_cc_uah = INT_MIN;
} else {
@@ -2205,7 +2205,12 @@
pr_err("adc vbat failed err = %d\n", rc);
return soc;
}
- if (soc == 0 && vbat_uv > chip->v_cutoff_uv) {
+
+ /* only clamp when discharging */
+ if (is_battery_charging(chip))
+ return soc;
+
+ if (soc <= 0 && vbat_uv > chip->v_cutoff_uv) {
pr_debug("clamping soc to 1, vbat (%d) > cutoff (%d)\n",
vbat_uv, chip->v_cutoff_uv);
return 1;
@@ -2418,8 +2423,13 @@
}
mutex_unlock(&chip->soc_invalidation_mutex);
- pr_debug("SOC before adjustment = %d\n", soc);
- new_calculated_soc = adjust_soc(chip, ¶ms, soc, batt_temp);
+ if (chip->first_time_calc_soc && !chip->shutdown_soc_invalid) {
+ pr_debug("Skip adjustment when shutdown SOC has been forced\n");
+ new_calculated_soc = soc;
+ } else {
+ pr_debug("SOC before adjustment = %d\n", soc);
+ new_calculated_soc = adjust_soc(chip, ¶ms, soc, batt_temp);
+ }
/* always clamp soc due to BMS hw/sw immaturities */
new_calculated_soc = clamp_soc_based_on_voltage(chip,
diff --git a/drivers/power/qpnp-charger.c b/drivers/power/qpnp-charger.c
index 0870202..6301724 100644
--- a/drivers/power/qpnp-charger.c
+++ b/drivers/power/qpnp-charger.c
@@ -650,24 +650,23 @@
return (batfet_closed_rt_sts & BAT_FET_ON_IRQ) ? 1 : 0;
}
-#define USB_VALID_BIT BIT(7)
static int
qpnp_chg_is_usb_chg_plugged_in(struct qpnp_chg_chip *chip)
{
- u8 usbin_valid_rt_sts;
+ u8 usb_chgpth_rt_sts;
int rc;
- rc = qpnp_chg_read(chip, &usbin_valid_rt_sts,
- chip->usb_chgpth_base + CHGR_STATUS , 1);
+ rc = qpnp_chg_read(chip, &usb_chgpth_rt_sts,
+ INT_RT_STS(chip->usb_chgpth_base), 1);
if (rc) {
pr_err("spmi read failed: addr=%03X, rc=%d\n",
- chip->usb_chgpth_base + CHGR_STATUS, rc);
+ INT_RT_STS(chip->usb_chgpth_base), rc);
return rc;
}
- pr_debug("chgr usb sts 0x%x\n", usbin_valid_rt_sts);
+ pr_debug("chgr usb sts 0x%x\n", usb_chgpth_rt_sts);
- return (usbin_valid_rt_sts & USB_VALID_BIT) ? 1 : 0;
+ return (usb_chgpth_rt_sts & USBIN_VALID_IRQ) ? 1 : 0;
}
static bool
@@ -686,10 +685,10 @@
return !!(buck_sts & IBAT_LOOP_IRQ);
}
-#define USB_VALID_MASK 0xC0
-#define USB_COARSE_DET 0x10
-#define USB_VALID_UVP_VALUE 0x00
-#define USB_VALID_OVP_VALUE 0x40
+#define USB_VALID_MASK 0xC0
+#define USB_VALID_IN_MASK BIT(7)
+#define USB_COARSE_DET 0x10
+#define USB_VALID_OVP_VALUE 0x40
static int
qpnp_chg_check_usb_coarse_det(struct qpnp_chg_chip *chip)
{
@@ -708,7 +707,8 @@
static int
qpnp_chg_check_usbin_health(struct qpnp_chg_chip *chip)
{
- u8 usbin_chg_rt_sts, usbin_health = 0;
+ u8 usbin_chg_rt_sts, usb_chgpth_rt_sts;
+ u8 usbin_health = 0;
int rc;
rc = qpnp_chg_read(chip, &usbin_chg_rt_sts,
@@ -720,13 +720,23 @@
return rc;
}
- pr_debug("chgr usb sts 0x%x\n", usbin_chg_rt_sts);
+ rc = qpnp_chg_read(chip, &usb_chgpth_rt_sts,
+ INT_RT_STS(chip->usb_chgpth_base) , 1);
+
+ if (rc) {
+ pr_err("spmi read failed: addr=%03X, rc=%d\n",
+ INT_RT_STS(chip->usb_chgpth_base), rc);
+ return rc;
+ }
+
+ pr_debug("chgr usb sts 0x%x, chgpth rt sts 0x%x\n",
+ usbin_chg_rt_sts, usb_chgpth_rt_sts);
if ((usbin_chg_rt_sts & USB_COARSE_DET) == USB_COARSE_DET) {
if ((usbin_chg_rt_sts & USB_VALID_MASK)
== USB_VALID_OVP_VALUE) {
usbin_health = USBIN_OVP;
pr_err("Over voltage charger inserted\n");
- } else if ((usbin_chg_rt_sts & USB_VALID_BIT) != 0) {
+ } else if ((usb_chgpth_rt_sts & USBIN_VALID_IRQ) != 0) {
usbin_health = USBIN_OK;
pr_debug("Valid charger inserted\n");
}
@@ -1067,7 +1077,8 @@
return rc;
}
- rc = override_dcin_ilimit(chip, 0);
+ if (enable)
+ rc = override_dcin_ilimit(chip, 0);
return rc;
}
@@ -5120,6 +5131,14 @@
qpnp_chg_force_run_on_batt(chip, chip->charging_disabled);
qpnp_chg_set_appropriate_vddmax(chip);
+ if (chip->parallel_ovp_mode) {
+ rc = override_dcin_ilimit(chip, 1);
+ if (rc) {
+ pr_err("Override DCIN LLIMIT %d\n", rc);
+ goto unregister_dc_psy;
+ }
+ }
+
rc = qpnp_chg_request_irqs(chip);
if (rc) {
pr_err("failed to request interrupts %d\n", rc);
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index 450c4fb..047bbc4 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -1989,8 +1989,8 @@
int regulator_set_voltage(struct regulator *regulator, int min_uV, int max_uV)
{
struct regulator_dev *rdev = regulator->rdev;
- int prev_min_uV, prev_max_uV;
int ret = 0;
+ int old_min_uV, old_max_uV;
mutex_lock(&rdev->mutex);
@@ -2013,24 +2013,28 @@
if (ret < 0)
goto out;
- prev_min_uV = regulator->min_uV;
- prev_max_uV = regulator->max_uV;
-
+ /* restore original values in case of error */
+ old_min_uV = regulator->min_uV;
+ old_max_uV = regulator->max_uV;
regulator->min_uV = min_uV;
regulator->max_uV = max_uV;
ret = regulator_check_consumers(rdev, &min_uV, &max_uV);
- if (ret < 0) {
- regulator->min_uV = prev_min_uV;
- regulator->max_uV = prev_max_uV;
- goto out;
- }
+ if (ret < 0)
+ goto out2;
ret = _regulator_do_set_voltage(rdev, min_uV, max_uV);
+ if (ret < 0)
+ goto out2;
out:
mutex_unlock(&rdev->mutex);
return ret;
+out2:
+ regulator->min_uV = old_min_uV;
+ regulator->max_uV = old_max_uV;
+ mutex_unlock(&rdev->mutex);
+ return ret;
}
EXPORT_SYMBOL_GPL(regulator_set_voltage);
diff --git a/drivers/rtc/alarm.c b/drivers/rtc/alarm.c
index 51e176f..fb97329 100644
--- a/drivers/rtc/alarm.c
+++ b/drivers/rtc/alarm.c
@@ -71,9 +71,11 @@
static bool suspended;
static long power_on_alarm;
+static void alarm_shutdown(struct platform_device *dev);
void set_power_on_alarm(long secs)
{
power_on_alarm = secs;
+ alarm_shutdown(NULL);
}
@@ -520,8 +522,11 @@
spin_lock_irqsave(&alarm_slock, flags);
- if (!power_on_alarm)
+ if (!power_on_alarm) {
+ spin_unlock_irqrestore(&alarm_slock, flags);
goto disable_alarm;
+ }
+ spin_unlock_irqrestore(&alarm_slock, flags);
rtc_read_time(alarm_rtc_dev, &rtc_time);
getnstimeofday(&wall_time);
@@ -549,11 +554,9 @@
pr_alarm(FLOW, "Power-on alarm set to %lu\n",
alarm_time);
- spin_unlock_irqrestore(&alarm_slock, flags);
return;
disable_alarm:
- spin_unlock_irqrestore(&alarm_slock, flags);
rtc_alarm_irq_enable(alarm_rtc_dev, 0);
}
diff --git a/drivers/rtc/qpnp-rtc.c b/drivers/rtc/qpnp-rtc.c
index d64b577..de86577 100644
--- a/drivers/rtc/qpnp-rtc.c
+++ b/drivers/rtc/qpnp-rtc.c
@@ -553,8 +553,15 @@
goto fail_rtc_enable;
}
+ rc = qpnp_read_wrapper(rtc_dd, &rtc_dd->alarm_ctrl_reg1,
+ rtc_dd->alarm_base + REG_OFFSET_ALARM_CTRL1, 1);
+ if (rc) {
+ dev_err(&spmi->dev,
+ "Read from Alarm control reg failed\n");
+ goto fail_rtc_enable;
+ }
/* Enable abort enable feature */
- rtc_dd->alarm_ctrl_reg1 = BIT_RTC_ABORT_ENABLE;
+ rtc_dd->alarm_ctrl_reg1 |= BIT_RTC_ABORT_ENABLE;
rc = qpnp_write_wrapper(rtc_dd, &rtc_dd->alarm_ctrl_reg1,
rtc_dd->alarm_base + REG_OFFSET_ALARM_CTRL1, 1);
if (rc) {
diff --git a/drivers/slimbus/slim-msm-ngd.c b/drivers/slimbus/slim-msm-ngd.c
index 0f8f1dd..aa61ab9 100644
--- a/drivers/slimbus/slim-msm-ngd.c
+++ b/drivers/slimbus/slim-msm-ngd.c
@@ -27,7 +27,6 @@
#include <linux/timer.h>
#include <mach/sps.h>
#include "slim-msm.h"
-#include <mach/qdsp6v2/apr.h>
#define NGD_SLIM_NAME "ngd_msm_ctrl"
#define SLIM_LA_MGR 0xFF
@@ -857,7 +856,7 @@
prev_state);
/* ADSP SSR, send device_up notifications */
if (prev_state == MSM_CTRL_DOWN)
- schedule_work(&dev->slave_notify);
+ complete(&dev->qmi.slave_notify);
} else if (ret == -EIO) {
pr_info("capability message NACKed, retrying");
if (retries < INIT_MX_RETRIES) {
@@ -1075,11 +1074,7 @@
while (!kthread_should_stop()) {
set_current_state(TASK_INTERRUPTIBLE);
- ret = wait_for_completion_interruptible(notify);
- if (ret) {
- dev_err(dev->dev, "rx thread wait err:%d", ret);
- continue;
- }
+ wait_for_completion(notify);
/* 1 irq notification per message */
if (dev->use_rx_msgqs != MSM_MSGQ_ENABLED) {
msm_slim_rx_dequeue(dev, (u8 *)buffer);
@@ -1108,31 +1103,49 @@
return 0;
}
-static void ngd_laddr_lookup(struct work_struct *work)
+static int ngd_notify_slaves(void *data)
{
- struct msm_slim_ctrl *dev =
- container_of(work, struct msm_slim_ctrl, slave_notify);
+ struct msm_slim_ctrl *dev = (struct msm_slim_ctrl *)data;
struct slim_controller *ctrl = &dev->ctrl;
struct slim_device *sbdev;
struct list_head *pos, *next;
- int i;
- slim_framer_booted(ctrl);
- mutex_lock(&ctrl->m_ctrl);
- list_for_each_safe(pos, next, &ctrl->devs) {
- int ret = 0;
- sbdev = list_entry(pos, struct slim_device, dev_list);
- 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);
+ int ret, i = 0;
+ while (!kthread_should_stop()) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ wait_for_completion(&dev->qmi.slave_notify);
+ /* Probe devices for first notification */
+ if (!i) {
+ dev->err = 0;
+ if (dev->dev->of_node)
+ of_register_slim_devices(&dev->ctrl);
+
+ /*
+ * Add devices registered with board-info now that
+ * controller is up
+ */
+ slim_ctrl_add_boarddevs(&dev->ctrl);
+ } else {
+ slim_framer_booted(ctrl);
}
+ i++;
mutex_lock(&ctrl->m_ctrl);
+ list_for_each_safe(pos, next, &ctrl->devs) {
+ sbdev = list_entry(pos, struct slim_device, dev_list);
+ 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);
}
- mutex_unlock(&ctrl->m_ctrl);
+ return 0;
}
static void ngd_adsp_down(struct work_struct *work)
@@ -1173,18 +1186,9 @@
struct resource *bam_mem;
struct resource *slim_mem;
struct resource *irq, *bam_irq;
- enum apr_subsys_state q6_state;
bool rxreg_access = false;
bool slim_mdm = false;
- q6_state = apr_get_q6_state();
- if (q6_state == APR_SUBSYS_DOWN) {
- dev_dbg(&pdev->dev, "defering %s, adsp_state %d\n", __func__,
- q6_state);
- return -EPROBE_DEFER;
- } else
- dev_dbg(&pdev->dev, "adsp is ready\n");
-
slim_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM,
"slimbus_physical");
if (!slim_mem) {
@@ -1290,6 +1294,7 @@
dev->use_tx_msgqs = MSM_MSGQ_RESET;
init_completion(&dev->rx_msgq_notify);
+ init_completion(&dev->qmi.slave_notify);
/* Register with framework */
ret = slim_add_numbered_controller(&dev->ctrl);
@@ -1311,6 +1316,7 @@
}
init_completion(&dev->qmi.qmi_comp);
+ dev->err = -EPROBE_DEFER;
pm_runtime_use_autosuspend(dev->dev);
pm_runtime_set_autosuspend_delay(dev->dev, MSM_SLIM_AUTOSUSPEND);
pm_runtime_set_suspended(dev->dev);
@@ -1326,7 +1332,6 @@
dev->mdm.ssr);
}
- 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;
@@ -1342,23 +1347,27 @@
/* 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");
+ "ngd_rx_thread%d", dev->ctrl.nr);
if (IS_ERR(dev->rx_msgq_thread)) {
ret = PTR_ERR(dev->rx_msgq_thread);
- dev_err(dev->dev, "Failed to start Rx message queue thread\n");
- goto err_thread_create_failed;
+ dev_err(dev->dev, "Failed to start Rx thread:%d\n", ret);
+ goto err_rx_thread_create_failed;
}
- if (pdev->dev.of_node)
- of_register_slim_devices(&dev->ctrl);
-
- /* Add devices registered with board-info now that controller is up */
- slim_ctrl_add_boarddevs(&dev->ctrl);
-
+ /* Start thread to probe, and notify slaves */
+ dev->qmi.slave_thread = kthread_run(ngd_notify_slaves, dev,
+ "ngd_notify_sl%d", dev->ctrl.nr);
+ if (IS_ERR(dev->qmi.slave_thread)) {
+ ret = PTR_ERR(dev->qmi.slave_thread);
+ dev_err(dev->dev, "Failed to start notifier thread:%d\n", ret);
+ goto err_notify_thread_create_failed;
+ }
dev_dbg(dev->dev, "NGD SB controller is up!\n");
return 0;
-err_thread_create_failed:
+err_notify_thread_create_failed:
+ kthread_stop(dev->rx_msgq_thread);
+err_rx_thread_create_failed:
qmi_svc_event_notifier_unregister(SLIMBUS_QMI_SVC_ID,
SLIMBUS_QMI_SVC_V1,
SLIMBUS_QMI_INS_ID, &dev->qmi.nb);
diff --git a/drivers/slimbus/slim-msm.h b/drivers/slimbus/slim-msm.h
index 2327b38..63178cc 100644
--- a/drivers/slimbus/slim-msm.h
+++ b/drivers/slimbus/slim-msm.h
@@ -203,6 +203,8 @@
struct msm_slim_qmi {
struct qmi_handle *handle;
struct task_struct *task;
+ struct task_struct *slave_thread;
+ struct completion slave_notify;
struct kthread_work kwork;
struct kthread_worker kworker;
struct completion qmi_comp;
@@ -261,7 +263,6 @@
struct completion ctrl_up;
int nsats;
u32 ver;
- struct work_struct slave_notify;
struct msm_slim_qmi qmi;
struct msm_slim_pdata pdata;
struct msm_slim_mdm mdm;
diff --git a/drivers/spi/spi_qsd.c b/drivers/spi/spi_qsd.c
index 39e81fa..d3e4612 100644
--- a/drivers/spi/spi_qsd.c
+++ b/drivers/spi/spi_qsd.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2008-2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -1980,14 +1980,17 @@
if (dd->use_rlock)
remote_mutex_lock(&dd->r_lock);
- if (!msm_spi_is_valid_state(dd)) {
+ spin_lock_irqsave(&dd->queue_lock, flags);
+ dd->transfer_pending = 1;
+ spin_unlock_irqrestore(&dd->queue_lock, flags);
+
+ if (dd->suspended || !msm_spi_is_valid_state(dd)) {
dev_err(dd->dev, "%s: SPI operational state not valid\n",
__func__);
status_error = 1;
}
-
spin_lock_irqsave(&dd->queue_lock, flags);
- dd->transfer_pending = 1;
+
while (!list_empty(&dd->queue)) {
dd->cur_msg = list_entry(dd->queue.next,
struct spi_message, queue);
diff --git a/drivers/spmi/qpnp-int.c b/drivers/spmi/qpnp-int.c
index 3e14333..9fc7299 100644
--- a/drivers/spmi/qpnp-int.c
+++ b/drivers/spmi/qpnp-int.c
@@ -184,6 +184,22 @@
return 0;
}
+static void qpnpint_irq_ack(struct irq_data *d)
+{
+ struct q_irq_data *irq_d = irq_data_get_irq_chip_data(d);
+ int rc;
+
+ pr_debug("hwirq %lu irq: %d\n", d->hwirq, d->irq);
+
+ rc = qpnpint_spmi_write(irq_d, QPNPINT_REG_LATCHED_CLR,
+ &irq_d->mask_shift, 1);
+ if (rc) {
+ pr_err_ratelimited("spmi write failure on irq %d, rc=%d\n",
+ d->irq, rc);
+ return;
+ }
+}
+
static void qpnpint_irq_mask(struct irq_data *d)
{
struct q_irq_data *irq_d = irq_data_get_irq_chip_data(d);
@@ -223,44 +239,10 @@
static void qpnpint_irq_mask_ack(struct irq_data *d)
{
- struct q_irq_data *irq_d = irq_data_get_irq_chip_data(d);
- struct q_chip_data *chip_d = irq_d->chip_d;
- struct q_perip_data *per_d = irq_d->per_d;
- int rc;
- uint8_t prev_int_en = per_d->int_en;
-
pr_debug("hwirq %lu irq: %d\n", d->hwirq, d->irq);
- if (!chip_d->cb) {
- pr_warn_ratelimited("No arbiter on bus=%u slave=%u offset=%u\n",
- chip_d->bus_nr, irq_d->spmi_slave,
- irq_d->spmi_offset);
- return;
- }
-
- per_d->int_en &= ~irq_d->mask_shift;
-
- if (prev_int_en && !(per_d->int_en)) {
- /*
- * no interrupt on this peripheral is enabled
- * ask the arbiter to ignore this peripheral
- */
- qpnpint_arbiter_op(d, irq_d, chip_d->cb->mask);
- }
-
- rc = qpnpint_spmi_write(irq_d, QPNPINT_REG_EN_CLR,
- &irq_d->mask_shift, 1);
- if (rc) {
- pr_err("spmi failure on irq %d\n", d->irq);
- return;
- }
-
- rc = qpnpint_spmi_write(irq_d, QPNPINT_REG_LATCHED_CLR,
- &irq_d->mask_shift, 1);
- if (rc) {
- pr_err("spmi failure on irq %d\n", d->irq);
- return;
- }
+ qpnpint_irq_mask(d);
+ qpnpint_irq_ack(d);
}
static void qpnpint_irq_unmask(struct irq_data *d)
@@ -269,6 +251,7 @@
struct q_chip_data *chip_d = irq_d->chip_d;
struct q_perip_data *per_d = irq_d->per_d;
int rc;
+ uint8_t buf[2];
uint8_t prev_int_en = per_d->int_en;
pr_debug("hwirq %lu irq: %d\n", d->hwirq, d->irq);
@@ -289,12 +272,29 @@
*/
qpnpint_arbiter_op(d, irq_d, chip_d->cb->unmask);
}
- rc = qpnpint_spmi_write(irq_d, QPNPINT_REG_EN_SET,
- &irq_d->mask_shift, 1);
+
+ /* Check the current state of the interrupt enable bit. */
+ rc = qpnpint_spmi_read(irq_d, QPNPINT_REG_EN_SET, buf, 1);
if (rc) {
- pr_err("spmi failure on irq %d\n", d->irq);
+ pr_err("SPMI read failure for IRQ %d, rc=%d\n", d->irq, rc);
return;
}
+
+ if (!(buf[0] & irq_d->mask_shift)) {
+ /*
+ * Since the interrupt is currently disabled, write to both the
+ * LATCHED_CLR and EN_SET registers so that a spurious interrupt
+ * cannot be triggered when the interrupt is enabled.
+ */
+ buf[0] = irq_d->mask_shift;
+ buf[1] = irq_d->mask_shift;
+ rc = qpnpint_spmi_write(irq_d, QPNPINT_REG_LATCHED_CLR, buf, 2);
+ if (rc) {
+ pr_err("SPMI write failure for IRQ %d, rc=%d\n", d->irq,
+ rc);
+ return;
+ }
+ }
}
static int qpnpint_irq_set_type(struct irq_data *d, unsigned int flow_type)
@@ -336,6 +336,11 @@
return rc;
}
+ if (flow_type & IRQ_TYPE_EDGE_BOTH)
+ __irq_set_handler_locked(d->irq, handle_edge_irq);
+ else
+ __irq_set_handler_locked(d->irq, handle_level_irq);
+
return 0;
}
@@ -363,6 +368,7 @@
static struct irq_chip qpnpint_chip = {
.name = "qpnp-int",
+ .irq_ack = qpnpint_irq_ack,
.irq_mask = qpnpint_irq_mask,
.irq_mask_ack = qpnpint_irq_mask_ack,
.irq_unmask = qpnpint_irq_unmask,
diff --git a/drivers/staging/android/ashmem.c b/drivers/staging/android/ashmem.c
index 7ca247a..41ebc1c 100644
--- a/drivers/staging/android/ashmem.c
+++ b/drivers/staging/android/ashmem.c
@@ -365,7 +365,9 @@
if (!sc->nr_to_scan)
return lru_count;
- mutex_lock(&ashmem_mutex);
+ if (!mutex_trylock(&ashmem_mutex))
+ return -1;
+
list_for_each_entry_safe(range, next, &ashmem_lru_list, lru) {
struct inode *inode = range->asma->file->f_dentry->d_inode;
loff_t start = range->pgstart * PAGE_SIZE;
diff --git a/drivers/thermal/thermal_sys.c b/drivers/thermal/thermal_sys.c
index 21863e8..8cd70ac 100644
--- a/drivers/thermal/thermal_sys.c
+++ b/drivers/thermal/thermal_sys.c
@@ -92,13 +92,43 @@
}
EXPORT_SYMBOL(sensor_get_id);
+static void init_sensor_trip(struct sensor_info *sensor)
+{
+ int ret = 0, i = 0;
+ enum thermal_trip_type type;
+
+ for (i = 0; ((sensor->max_idx == -1) ||
+ (sensor->min_idx == -1)) &&
+ (sensor->tz->ops->get_trip_type) &&
+ (i < sensor->tz->trips); i++) {
+
+ sensor->tz->ops->get_trip_type(sensor->tz, i, &type);
+ if (type == THERMAL_TRIP_CONFIGURABLE_HI)
+ sensor->max_idx = i;
+ if (type == THERMAL_TRIP_CONFIGURABLE_LOW)
+ sensor->min_idx = i;
+ type = 0;
+ }
+
+ ret = sensor->tz->ops->get_trip_temp(sensor->tz,
+ sensor->min_idx, &sensor->threshold_min);
+ if (ret)
+ pr_err("Unable to get MIN trip temp. sensor:%d err:%d\n",
+ sensor->sensor_id, ret);
+
+ ret = sensor->tz->ops->get_trip_temp(sensor->tz,
+ sensor->max_idx, &sensor->threshold_max);
+ if (ret)
+ pr_err("Unable to get MAX trip temp. sensor:%d err:%d\n",
+ sensor->sensor_id, ret);
+}
+
static int __update_sensor_thresholds(struct sensor_info *sensor)
{
long max_of_low_thresh = LONG_MIN;
long min_of_high_thresh = LONG_MAX;
struct sensor_threshold *pos, *var;
- enum thermal_trip_type type;
- int i, ret = 0;
+ int ret = 0;
if (!sensor->tz->ops->set_trip_temp ||
!sensor->tz->ops->activate_trip_type ||
@@ -108,19 +138,8 @@
goto update_done;
}
- for (i = 0; ((sensor->max_idx == -1) || (sensor->min_idx == -1)) &&
- (sensor->tz->ops->get_trip_type) && (i < sensor->tz->trips);
- i++) {
- sensor->tz->ops->get_trip_type(sensor->tz, i, &type);
- if (type == THERMAL_TRIP_CONFIGURABLE_HI)
- sensor->max_idx = i;
- if (type == THERMAL_TRIP_CONFIGURABLE_LOW)
- sensor->min_idx = i;
- sensor->tz->ops->get_trip_temp(sensor->tz,
- THERMAL_TRIP_CONFIGURABLE_LOW, &sensor->threshold_min);
- sensor->tz->ops->get_trip_temp(sensor->tz,
- THERMAL_TRIP_CONFIGURABLE_HI, &sensor->threshold_max);
- }
+ if ((sensor->max_idx == -1) || (sensor->min_idx == -1))
+ init_sensor_trip(sensor);
list_for_each_entry_safe(pos, var, &sensor->threshold_list, list) {
if (!pos->active)
@@ -368,7 +387,7 @@
sensor->sensor_id = tz->id;
sensor->tz = tz;
- sensor->threshold_min = 0;
+ sensor->threshold_min = LONG_MIN;
sensor->threshold_max = LONG_MAX;
sensor->max_idx = -1;
sensor->min_idx = -1;
diff --git a/drivers/tty/serial/msm_serial_hs.c b/drivers/tty/serial/msm_serial_hs.c
index 4058bec..a701ec8 100644
--- a/drivers/tty/serial/msm_serial_hs.c
+++ b/drivers/tty/serial/msm_serial_hs.c
@@ -251,7 +251,6 @@
struct workqueue_struct *hsuart_wq; /* hsuart workqueue */
struct mutex clk_mutex; /* mutex to guard against clock off/clock on */
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;
@@ -319,6 +318,7 @@
[UART_DM_TXFS] = 0x4c,
[UART_DM_RXFS] = 0x50,
[UART_DM_RX_TRANS_CTRL] = 0xcc,
+ [UART_DM_BCR] = 0xc8,
};
static struct of_device_id msm_hs_match_table[] = {
@@ -1838,12 +1838,13 @@
* Do the work buffer related work in BAM
* mode that is equivalent to legacy mode
*/
+ spin_lock_irqsave(&(msm_uport->uport.lock), flags);
- if (!msm_uport->tty_flush_receive)
+ if (!uart_circ_empty(tx_buf))
tx_buf->tail = (tx_buf->tail +
tx->tx_count) & ~UART_XMIT_SIZE;
else
- msm_uport->tty_flush_receive = false;
+ MSM_HS_DBG("%s:circ buffer is empty\n", __func__);
tx->dma_in_flight = 0;
@@ -1860,7 +1861,6 @@
if (uart_circ_chars_pending(tx_buf) < WAKEUP_CHARS)
uart_write_wakeup(uport);
- spin_lock_irqsave(&(msm_uport->uport.lock), flags);
if (msm_uport->tx.flush == FLUSH_STOP) {
msm_uport->tx.flush = FLUSH_SHUTDOWN;
wake_up(&msm_uport->tx.wait);
@@ -2036,14 +2036,6 @@
}
-static void msm_hs_flush_buffer(struct uart_port *uport)
-{
- struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
-
- if (msm_uport->tx.dma_in_flight)
- msm_uport->tty_flush_receive = true;
-}
-
/*
* Standard API, Break Signal
*
@@ -2281,11 +2273,9 @@
/* Do not update tx_buf.tail if uart_flush_buffer already
* called in serial core
*/
- if (!msm_uport->tty_flush_receive)
+ if (!uart_circ_empty(tx_buf))
tx_buf->tail = (tx_buf->tail +
tx->tx_count) & ~UART_XMIT_SIZE;
- else
- msm_uport->tty_flush_receive = false;
tx->dma_in_flight = 0;
@@ -2665,7 +2655,11 @@
}
}
- msm_hs_write(uport, UARTDM_BCR_ADDR, 0x003F);
+ data = (UARTDM_BCR_TX_BREAK_DISABLE | UARTDM_BCR_STALE_IRQ_EMPTY |
+ UARTDM_BCR_RX_DMRX_LOW_EN | UARTDM_BCR_RX_STAL_IRQ_DMRX_EQL |
+ UARTDM_BCR_RX_DMRX_1BYTE_RES_EN);
+ msm_hs_write(uport, UART_DM_BCR, data);
+
/* Set auto RFR Level */
data = msm_hs_read(uport, UART_DM_MR1);
data &= ~UARTDM_MR1_AUTO_RFR_LEVEL1_BMSK;
@@ -2708,8 +2702,6 @@
tx->tx_ready_int_en = 0;
tx->dma_in_flight = 0;
rx->rx_cmd_exec = false;
- msm_uport->tty_flush_receive = false;
- MSM_HS_DBG("%s: Setting tty_flush_receive to false\n", __func__);
if (!is_blsp_uart(msm_uport)) {
tx->xfer.complete_func = msm_hs_dmov_tx_callback;
@@ -3751,7 +3743,7 @@
.config_port = msm_hs_config_port,
.release_port = msm_hs_release_port,
.request_port = msm_hs_request_port,
- .flush_buffer = msm_hs_flush_buffer,
+ .flush_buffer = NULL,
.ioctl = msm_hs_ioctl,
};
diff --git a/drivers/tty/serial/msm_serial_hs_hwreg.h b/drivers/tty/serial/msm_serial_hs_hwreg.h
index d912b9f..064bbda 100644
--- a/drivers/tty/serial/msm_serial_hs_hwreg.h
+++ b/drivers/tty/serial/msm_serial_hs_hwreg.h
@@ -81,6 +81,7 @@
UART_DM_TXFS,
UART_DM_RXFS,
UART_DM_RX_TRANS_CTRL,
+ UART_DM_BCR,
UART_DM_LAST,
};
@@ -94,7 +95,11 @@
* UARTDM Core v1.4 STALE_IRQ_EMPTY bit defination
* Stale interrupt will fire if bit is set when RX-FIFO is empty
*/
+#define UARTDM_BCR_TX_BREAK_DISABLE 0x1
#define UARTDM_BCR_STALE_IRQ_EMPTY 0x2
+#define UARTDM_BCR_RX_DMRX_LOW_EN 0x4
+#define UARTDM_BCR_RX_STAL_IRQ_DMRX_EQL 0x10
+#define UARTDM_BCR_RX_DMRX_1BYTE_RES_EN 0x20
/* TRANSFER_CONTROL Register for UARTDM Core v1.4 */
#define UARTDM_RX_TRANS_CTRL_ADDR 0xcc
diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
index b1ec3fc..2a66c4c 100644
--- a/drivers/usb/Makefile
+++ b/drivers/usb/Makefile
@@ -28,6 +28,7 @@
obj-$(CONFIG_USB_IMX21_HCD) += host/
obj-$(CONFIG_USB_FSL_MPH_DR_OF) += host/
obj-$(CONFIG_USB_PEHCI_HCD) += host/
+obj-$(CONFIG_USB_ICE40_HCD) += host/
obj-$(CONFIG_USB_C67X00_HCD) += c67x00/
@@ -37,6 +38,7 @@
obj-$(CONFIG_USB_PRINTER) += class/
obj-$(CONFIG_USB_WDM) += class/
obj-$(CONFIG_USB_TMC) += class/
+obj-$(CONFIG_USB_CCID_BRIDGE) += class/
obj-$(CONFIG_USB_STORAGE) += storage/
obj-$(CONFIG_USB) += storage/
diff --git a/drivers/usb/gadget/ci13xxx_msm.c b/drivers/usb/gadget/ci13xxx_msm.c
index f1e4220..be30b5f 100644
--- a/drivers/usb/gadget/ci13xxx_msm.c
+++ b/drivers/usb/gadget/ci13xxx_msm.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2010-2014, The Linux Foundation. All rights reserved.
*
* This 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,11 +62,27 @@
struct ci13xxx *udc = _udc;
struct usb_phy *phy = udc->transceiver;
- if (phy && (phy->flags & ENABLE_DP_MANUAL_PULLUP))
+ if (phy && (phy->flags & ENABLE_DP_MANUAL_PULLUP)) {
+ u32 temp;
+
usb_phy_io_write(phy,
ULPI_MISC_A_VBUSVLDEXT |
ULPI_MISC_A_VBUSVLDEXTSEL,
ULPI_CLR(ULPI_MISC_A));
+
+ /* Notify LINK of VBUS LOW */
+ temp = readl_relaxed(USB_USBCMD);
+ temp &= ~USBCMD_SESS_VLD_CTRL;
+ writel_relaxed(temp, USB_USBCMD);
+
+ /*
+ * Add memory barrier as it is must to complete
+ * above USB PHY and Link register writes before
+ * moving ahead with USB peripheral mode enumeration,
+ * otherwise USB peripheral mode may not work.
+ */
+ mb();
+ }
}
/* Link power management will reduce power consumption by
@@ -345,6 +361,11 @@
return 0;
}
+void ci13xxx_msm_shutdown(struct platform_device *pdev)
+{
+ ci13xxx_pullup(&_udc->gadget, 0);
+}
+
void msm_hw_bam_disable(bool bam_disable)
{
u32 val;
@@ -364,6 +385,7 @@
.name = "msm_hsusb",
},
.remove = ci13xxx_msm_remove,
+ .shutdown = ci13xxx_msm_shutdown,
};
MODULE_ALIAS("platform:msm_hsusb");
diff --git a/drivers/usb/gadget/f_audio_source.c b/drivers/usb/gadget/f_audio_source.c
index 37f229b..33b645e 100644
--- a/drivers/usb/gadget/f_audio_source.c
+++ b/drivers/usb/gadget/f_audio_source.c
@@ -327,15 +327,22 @@
s64 msecs;
s64 frames;
ktime_t now;
+ unsigned long flags;
+ spin_lock_irqsave(&audio->lock, flags);
/* audio->substream will be null if we have been closed */
- if (!audio->substream)
+ if (!audio->substream) {
+ spin_unlock_irqrestore(&audio->lock, flags);
return;
+ }
/* audio->buffer_pos will be null if we have been stopped */
- if (!audio->buffer_pos)
+ if (!audio->buffer_pos) {
+ spin_unlock_irqrestore(&audio->lock, flags);
return;
+ }
runtime = audio->substream->runtime;
+ spin_unlock_irqrestore(&audio->lock, flags);
/* compute number of frames to send */
now = ktime_get();
@@ -359,8 +366,21 @@
while (frames > 0) {
req = audio_req_get(audio);
- if (!req)
+ spin_lock_irqsave(&audio->lock, flags);
+ /* audio->substream will be null if we have been closed */
+ if (!audio->substream) {
+ spin_unlock_irqrestore(&audio->lock, flags);
+ return;
+ }
+ /* audio->buffer_pos will be null if we have been stopped */
+ if (!audio->buffer_pos) {
+ spin_unlock_irqrestore(&audio->lock, flags);
+ return;
+ }
+ if (!req) {
+ spin_unlock_irqrestore(&audio->lock, flags);
break;
+ }
length = frames_to_bytes(runtime, frames);
if (length > IN_EP_MAX_PACKET_SIZE)
@@ -386,6 +406,7 @@
}
req->length = length;
+ spin_unlock_irqrestore(&audio->lock, flags);
ret = usb_ep_queue(audio->in_ep, req, GFP_ATOMIC);
if (ret < 0) {
pr_err("usb_ep_queue failed ret: %d\n", ret);
diff --git a/drivers/usb/gadget/f_rmnet.c b/drivers/usb/gadget/f_rmnet.c
index 2fa8c63..ebcec96 100644
--- a/drivers/usb/gadget/f_rmnet.c
+++ b/drivers/usb/gadget/f_rmnet.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-2014, The Linux Foundation. All rights reserved.
*
* This 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,7 +47,7 @@
/* control info */
struct list_head cpkt_resp_q;
- atomic_t notify_count;
+ unsigned long notify_count;
unsigned long cpkts_len;
};
@@ -605,7 +605,7 @@
list_del(&cpkt->list);
rmnet_free_ctrl_pkt(cpkt);
}
- atomic_set(&dev->notify_count, 0);
+ dev->notify_count = 0;
spin_unlock_irqrestore(&dev->lock, flags);
}
@@ -619,6 +619,7 @@
__func__, xport_to_str(dxport),
dev, dev->port_num);
+ usb_ep_fifo_flush(dev->notify);
frmnet_purge_responses(dev);
port_num = rmnet_ports[dev->port_num].data_xport_num;
@@ -754,7 +755,7 @@
return;
}
- if (atomic_inc_return(&dev->notify_count) != 1) {
+ if (++dev->notify_count != 1) {
spin_unlock_irqrestore(&dev->lock, flags);
return;
}
@@ -772,7 +773,14 @@
if (ret) {
spin_lock_irqsave(&dev->lock, flags);
if (!list_empty(&dev->cpkt_resp_q)) {
- atomic_dec(&dev->notify_count);
+ if (dev->notify_count > 0)
+ dev->notify_count--;
+ else {
+ pr_debug("%s: Invalid notify_count=%lu to decrement\n",
+ __func__, dev->notify_count);
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return;
+ }
cpkt = list_first_entry(&dev->cpkt_resp_q,
struct rmnet_ctrl_pkt, list);
list_del(&cpkt->list);
@@ -911,7 +919,9 @@
case -ECONNRESET:
case -ESHUTDOWN:
/* connection gone */
- atomic_set(&dev->notify_count, 0);
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->notify_count = 0;
+ spin_unlock_irqrestore(&dev->lock, flags);
break;
default:
pr_err("rmnet notify ep error %d\n", status);
@@ -920,14 +930,34 @@
if (!atomic_read(&dev->ctrl_online))
break;
- if (atomic_dec_and_test(&dev->notify_count))
+ spin_lock_irqsave(&dev->lock, flags);
+ if (dev->notify_count > 0) {
+ dev->notify_count--;
+ if (dev->notify_count == 0) {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ break;
+ }
+ } else {
+ pr_debug("%s: Invalid notify_count=%lu to decrement\n",
+ __func__, dev->notify_count);
+ spin_unlock_irqrestore(&dev->lock, flags);
break;
+ }
+ spin_unlock_irqrestore(&dev->lock, flags);
status = usb_ep_queue(dev->notify, req, GFP_ATOMIC);
if (status) {
spin_lock_irqsave(&dev->lock, flags);
if (!list_empty(&dev->cpkt_resp_q)) {
- atomic_dec(&dev->notify_count);
+ if (dev->notify_count > 0)
+ dev->notify_count--;
+ else {
+ pr_err("%s: Invalid notify_count=%lu to decrement\n",
+ __func__, dev->notify_count);
+ spin_unlock_irqrestore(&dev->lock,
+ flags);
+ break;
+ }
cpkt = list_first_entry(&dev->cpkt_resp_q,
struct rmnet_ctrl_pkt, list);
list_del(&cpkt->list);
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index 4357867..2a24bec 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -693,3 +693,16 @@
config USB_OCTEON2_COMMON
bool
default y if USB_OCTEON_EHCI || USB_OCTEON_OHCI
+
+config USB_ICE40_HCD
+ tristate "ICE40 FPGA based SPI to Inter-Chip USB host controller"
+ depends on USB && SPI
+ help
+ A driver for ICE40 FPGA based SPI to Inter-Chip USB host
+ controller. This driver registers as a SPI protocol driver
+ and interacts with the SPI subsystem on one side and interacts
+ with the USB core on the other side. Control and Bulk transfers
+ are supported.
+
+ To compile this driver a module, choose M here: the module
+ will be called "ice40-hcd".
diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile
index 7d35f5b..7c5b452 100644
--- a/drivers/usb/host/Makefile
+++ b/drivers/usb/host/Makefile
@@ -42,3 +42,4 @@
obj-$(CONFIG_USB_FSL_MPH_DR_OF) += fsl-mph-dr-of.o
obj-$(CONFIG_USB_OCTEON2_COMMON) += octeon2-common.o
obj-$(CONFIG_MIPS_ALCHEMY) += alchemy-common.o
+obj-$(CONFIG_USB_ICE40_HCD) += ice40-hcd.o
diff --git a/drivers/usb/host/ehci-msm2.c b/drivers/usb/host/ehci-msm2.c
index 7ae0a54..ebb226c 100644
--- a/drivers/usb/host/ehci-msm2.c
+++ b/drivers/usb/host/ehci-msm2.c
@@ -46,6 +46,10 @@
#define PDEV_NAME_LEN 20
+static bool uicc_card_present;
+module_param(uicc_card_present, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(uicc_card_present, "UICC card inserted");
+
struct msm_hcd {
struct ehci_hcd ehci;
spinlock_t wakeup_lock;
@@ -295,6 +299,11 @@
static void msm_ehci_vbus_power(struct msm_hcd *mhcd, bool on)
{
int ret;
+ const struct msm_usb_host_platform_data *pdata;
+
+ pdata = mhcd->dev->platform_data;
+ if (pdata && pdata->is_uicc)
+ return;
if (!mhcd->vbus) {
pr_err("vbus is NULL.");
@@ -352,6 +361,10 @@
pdata = mhcd->dev->platform_data;
+ /* For uicc card connection, external vbus is not required */
+ if (pdata && pdata->is_uicc)
+ return 0;
+
if (!init) {
if (pdata && pdata->dock_connect_irq)
free_irq(pdata->dock_connect_irq, mhcd);
@@ -680,6 +693,8 @@
unsigned long timeout;
int ret;
u32 portsc;
+ const struct msm_usb_host_platform_data *pdata;
+ u32 func_ctrl;
if (atomic_read(&mhcd->in_lpm)) {
dev_dbg(mhcd->dev, "%s called in lpm\n", __func__);
@@ -696,6 +711,14 @@
return -EBUSY;
}
+ pdata = mhcd->dev->platform_data;
+ if (pdata && pdata->is_uicc) {
+ /* put the controller in non-driving mode */
+ func_ctrl = msm_ulpi_read(mhcd, ULPI_FUNC_CTRL);
+ func_ctrl &= ~ULPI_FUNC_CTRL_OPMODE_MASK;
+ func_ctrl |= ULPI_FUNC_CTRL_OPMODE_NONDRIVING;
+ msm_ulpi_write(mhcd, func_ctrl, ULPI_FUNC_CTRL);
+ }
/* If port is enabled wait 5ms for PHCD to come up. Reset PHY
* and link if it fails to do so.
* If port is not enabled set the PHCD bit and poll for it to
@@ -800,6 +823,8 @@
unsigned temp;
int ret;
unsigned long flags;
+ u32 func_ctrl;
+ const struct msm_usb_host_platform_data *pdata;
if (!atomic_read(&mhcd->in_lpm)) {
dev_dbg(mhcd->dev, "%s called in !in_lpm\n", __func__);
@@ -869,6 +894,14 @@
}
skip_phy_resume:
+ pdata = mhcd->dev->platform_data;
+ if (pdata && pdata->is_uicc) {
+ /* put the controller in normal mode */
+ func_ctrl = msm_ulpi_read(mhcd, ULPI_FUNC_CTRL);
+ func_ctrl &= ~ULPI_FUNC_CTRL_OPMODE_MASK;
+ func_ctrl |= ULPI_FUNC_CTRL_OPMODE_NORMAL;
+ msm_ulpi_write(mhcd, func_ctrl, ULPI_FUNC_CTRL);
+ }
usb_hcd_resume_root_hub(hcd);
atomic_set(&mhcd->in_lpm, 0);
@@ -1323,6 +1356,8 @@
pdata->resume_gpio = of_get_named_gpio(node, "qcom,resume-gpio", 0);
if (pdata->resume_gpio < 0)
pdata->resume_gpio = 0;
+ pdata->is_uicc = of_property_read_bool(node,
+ "qcom,usb2-enable-uicc");
return pdata;
}
@@ -1339,6 +1374,14 @@
dev_dbg(&pdev->dev, "ehci_msm2 probe\n");
+ /*
+ * Fail probe in case of uicc till userspace activates driver through
+ * sysfs entry.
+ */
+ if (!uicc_card_present && pdev->dev.of_node && of_property_read_bool(
+ pdev->dev.of_node, "qcom,usb2-enable-uicc"))
+ return -ENODEV;
+
if (pdev->dev.of_node) {
dev_dbg(&pdev->dev, "device tree enabled\n");
pdev->dev.platform_data = ehci_msm2_dt_to_pdata(pdev);
@@ -1612,6 +1655,10 @@
if (mhcd->resume_gpio)
gpio_free(mhcd->resume_gpio);
+ /* If the device was removed no need to call pm_runtime_disable */
+ if (pdev->dev.power.power_state.event != PM_EVENT_INVALID)
+ pm_runtime_disable(&pdev->dev);
+
device_init_wakeup(&pdev->dev, 0);
pm_runtime_set_suspended(&pdev->dev);
diff --git a/drivers/usb/host/ice40-hcd.c b/drivers/usb/host/ice40-hcd.c
new file mode 100644
index 0000000..7b0a789
--- /dev/null
+++ b/drivers/usb/host/ice40-hcd.c
@@ -0,0 +1,2140 @@
+/*
+ * Copyright (c) 2014, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2001-2004 by David Brownell
+ *
+ * This 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.
+ *
+ */
+
+/*
+ * Root HUB management and Asynchronous scheduling traversal
+ * Based on ehci-hub.c and ehci-q.c
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/ktime.h>
+#include <linux/uaccess.h>
+#include <linux/debugfs.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/spinlock.h>
+#include <linux/firmware.h>
+#include <linux/spi/spi.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/ch11.h>
+
+#include <asm/unaligned.h>
+#include <mach/gpiomux.h>
+
+#define CREATE_TRACE_POINTS
+#include <trace/events/ice40.h>
+
+#define FADDR_REG 0x00 /* R/W: Device address */
+#define HCMD_REG 0x01 /* R/W: Host transfer command */
+#define XFRST_REG 0x02 /* R: Transfer status */
+#define IRQ_REG 0x03 /* R/C: IRQ status */
+#define IEN_REG 0x04 /* R/W: IRQ enable */
+#define CTRL0_REG 0x05 /* R/W: Host control command */
+#define CTRL1_REG 0x06 /* R/W: Host control command */
+#define WBUF0_REG 0x10 /* W: Tx fifo 0 */
+#define WBUF1_REG 0x11 /* W: Tx fifo 1 */
+#define SUBUF_REG 0x12 /* W: SETUP fifo */
+#define WBLEN_REG 0x13 /* W: Tx fifo size */
+#define RBUF0_REG 0x18 /* R: Rx fifo 0 */
+#define RBUF1_REG 0x19 /* R: Rx fifo 1 */
+#define RBLEN_REG 0x1B /* R: Rx fifo size */
+
+#define WRITE_CMD(addr) ((addr << 3) | 1)
+#define READ_CMD(addr) ((addr << 3) | 0)
+
+/* Host controller command register definitions */
+#define HCMD_EP(ep) (ep & 0xF)
+#define HCMD_BSEL(sel) (sel << 4)
+#define HCMD_TOGV(toggle) (toggle << 5)
+#define HCMD_PT(token) (token << 6)
+
+/* Transfer status register definitions */
+#define XFR_MASK(xfr) (xfr & 0xF)
+#define XFR_SUCCESS 0x0
+#define XFR_BUSY 0x1
+#define XFR_PKTERR 0x2
+#define XFR_PIDERR 0x3
+#define XFR_NAK 0x4
+#define XFR_STALL 0x5
+#define XFR_WRONGPID 0x6
+#define XFR_CRCERR 0x7
+#define XFR_TOGERR 0x8
+#define XFR_BADLEN 0x9
+#define XFR_TIMEOUT 0xA
+
+#define LINE_STATE(xfr) ((xfr & 0x30) >> 4) /* D+, D- */
+#define DPST BIT(5)
+#define DMST BIT(4)
+#define PLLOK BIT(6)
+#define R64B BIT(7)
+
+/* Interrupt enable/status register definitions */
+#define RESET_IRQ BIT(0)
+#define RESUME_IRQ BIT(1)
+#define SUSP_IRQ BIT(3)
+#define DISCONNECT_IRQ BIT(4)
+#define CONNECT_IRQ BIT(5)
+#define FRAME_IRQ BIT(6)
+#define XFR_IRQ BIT(7)
+
+/* Control 0 register definitions */
+#define RESET_CTRL BIT(0)
+#define FRAME_RESET_CTRL BIT(1)
+#define DET_BUS_CTRL BIT(2)
+#define RESUME_CTRL BIT(3)
+#define SOFEN_CTRL BIT(4)
+#define DM_PD_CTRL BIT(6)
+#define DP_PD_CTRL BIT(7)
+#define HRST_CTRL BIT(5)
+
+/* Control 1 register definitions */
+#define INT_EN_CTRL BIT(0)
+
+enum ice40_xfr_type {
+ FIRMWARE_XFR,
+ REG_WRITE_XFR,
+ REG_READ_XFR,
+ SETUP_XFR,
+ DATA_IN_XFR,
+ DATA_OUT_XFR,
+};
+
+enum ice40_ep_phase {
+ SETUP_PHASE = 1,
+ DATA_PHASE,
+ STATUS_PHASE,
+};
+
+struct ice40_ep {
+ u8 xcat_err;
+ bool unlinking;
+ bool halted;
+ struct usb_host_endpoint *ep;
+ struct list_head ep_list;
+};
+
+struct ice40_hcd {
+ spinlock_t lock;
+
+ struct mutex wlock;
+ struct mutex rlock;
+
+ u8 devnum;
+ u32 port_flags;
+ u8 ctrl0;
+ u8 wblen0;
+
+ enum ice40_ep_phase ep0_state;
+ struct usb_hcd *hcd;
+
+ struct list_head async_list;
+ struct workqueue_struct *wq;
+ struct work_struct async_work;
+
+ int reset_gpio;
+ int slave_select_gpio;
+ int config_done_gpio;
+ int vcc_en_gpio;
+ int clk_en_gpio;
+
+ struct regulator *core_vcc;
+ struct regulator *spi_vcc;
+ struct regulator *gpio_vcc;
+ bool powered;
+
+ struct dentry *dbg_root;
+ bool pcd_pending;
+
+ /* SPI stuff later */
+ struct spi_device *spi;
+
+ struct spi_message *fmsg;
+ struct spi_transfer *fmsg_xfr; /* size 1 */
+
+ struct spi_message *wmsg;
+ struct spi_transfer *wmsg_xfr; /* size 1 */
+ u8 *w_tx_buf;
+ u8 *w_rx_buf;
+
+ struct spi_message *rmsg;
+ struct spi_transfer *rmsg_xfr; /* size 1 */
+ u8 *r_tx_buf;
+ u8 *r_rx_buf;
+
+ struct spi_message *setup_msg;
+ struct spi_transfer *setup_xfr; /* size 2 */
+ u8 *setup_buf; /* size 1 for SUBUF */
+
+ struct spi_message *in_msg;
+ struct spi_transfer *in_xfr; /* size 2 */
+ u8 *in_buf; /* size 2 for reading from RBUF0 */
+
+ struct spi_message *out_msg;
+ struct spi_transfer *out_xfr; /* size 2 */
+ u8 *out_buf; /* size 1 for writing WBUF0 */
+};
+
+static char fw_name[16] = "ice40.bin";
+module_param_string(fw, fw_name, sizeof(fw_name), S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(fw, "firmware blob file name");
+
+static bool debugger;
+module_param(debugger, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debugger, "true to use the debug port");
+
+static inline struct ice40_hcd *hcd_to_ihcd(struct usb_hcd *hcd)
+{
+ return *((struct ice40_hcd **) hcd->hcd_priv);
+}
+
+static void ice40_spi_reg_write(struct ice40_hcd *ihcd, u8 val, u8 addr)
+{
+ int ret;
+
+ /*
+ * Register Write Pattern:
+ * TX: 1st byte is CMD (register + write), 2nd byte is value
+ * RX: Ignore
+ *
+ * The Mutex is to protect concurrent register writes as
+ * we have only 1 SPI message struct.
+ */
+
+ mutex_lock(&ihcd->wlock);
+
+ ihcd->w_tx_buf[0] = WRITE_CMD(addr);
+ ihcd->w_tx_buf[1] = val;
+ ret = spi_sync(ihcd->spi, ihcd->wmsg);
+ if (ret < 0) /* should not happen */
+ pr_err("failed. val = %d addr = %d\n", val, addr);
+
+ trace_ice40_reg_write(addr, val, ihcd->w_tx_buf[0],
+ ihcd->w_tx_buf[1], ret);
+
+ mutex_unlock(&ihcd->wlock);
+}
+
+static int ice40_spi_reg_read(struct ice40_hcd *ihcd, u8 addr)
+{
+ int ret;
+
+ /*
+ * Register Read Pattern:
+ * TX: 1st byte is CMD (register + read)
+ * RX: 1st, 2nd byte Ignore, 3rd byte value.
+ *
+ * The Mutex is to protect concurrent register reads as
+ * we have only 1 SPI message struct.
+ */
+
+ mutex_lock(&ihcd->rlock);
+
+ ihcd->r_tx_buf[0] = READ_CMD(addr);
+ ret = spi_sync(ihcd->spi, ihcd->rmsg);
+ if (ret < 0)
+ pr_err("failed. addr = %d\n", addr);
+ else
+ ret = ihcd->r_rx_buf[2];
+
+ trace_ice40_reg_read(addr, ihcd->r_tx_buf[0], ret);
+
+ mutex_unlock(&ihcd->rlock);
+
+ return ret;
+}
+
+static int ice40_poll_xfer(struct ice40_hcd *ihcd, int usecs)
+{
+ ktime_t start = ktime_get();
+ u8 val, retry = 0;
+ u8 ret = ~0; /* time out */
+
+again:
+
+ /*
+ * The SPI transaction may take tens of usec. Use ktime
+ * based checks rather than loop count.
+ */
+ do {
+ val = ice40_spi_reg_read(ihcd, XFRST_REG);
+
+ if (XFR_MASK(val) != XFR_BUSY)
+ return val;
+
+ } while (ktime_us_delta(ktime_get(), start) < usecs);
+
+ /*
+ * The SPI transaction involves a context switch. For any
+ * reason, if we are scheduled out more than usecs after
+ * the 1st read, this extra read will help.
+ */
+ if (!retry) {
+ retry = 1;
+ goto again;
+ }
+
+ return ret;
+}
+
+static int
+ice40_handshake(struct ice40_hcd *ihcd, u8 reg, u8 mask, u8 done, int usecs)
+{
+ ktime_t start = ktime_get();
+ u8 val, retry = 0;
+
+again:
+ do {
+ val = ice40_spi_reg_read(ihcd, reg);
+ val &= mask;
+
+ if (val == done)
+ return 0;
+
+ } while (ktime_us_delta(ktime_get(), start) < usecs);
+
+ if (!retry) {
+ retry = 1;
+ goto again;
+ }
+
+ return -ETIMEDOUT;
+}
+
+
+static const char hcd_name[] = "ice40-hcd";
+
+static int ice40_reset(struct usb_hcd *hcd)
+{
+ struct ice40_hcd *ihcd = hcd_to_ihcd(hcd);
+ u8 ctrl, status;
+ int ret = 0;
+
+ /*
+ * Program the defualt address 0. The device address is
+ * re-programmed after SET_ADDRESS in URB handling path.
+ */
+ ihcd->devnum = 0;
+ ice40_spi_reg_write(ihcd, 0, FADDR_REG);
+
+ ihcd->wblen0 = ~0;
+ /*
+ * Read the line state. This driver is loaded after the
+ * UICC card insertion. So the line state should indicate
+ * that a Full-speed device is connected. Return error
+ * if there is no device connected.
+ *
+ * There can be no device connected during debug. A debugfs
+ * file is provided to sample the bus line and update the
+ * port flags accordingly.
+ */
+
+ if (debugger)
+ goto out;
+
+ ctrl = ice40_spi_reg_read(ihcd, CTRL0_REG);
+ ice40_spi_reg_write(ihcd, ctrl | DET_BUS_CTRL, CTRL0_REG);
+
+ ret = ice40_handshake(ihcd, CTRL0_REG, DET_BUS_CTRL, 0, 5000);
+ if (ret) {
+ pr_err("bus detection failed\n");
+ goto out;
+ }
+
+ status = ice40_spi_reg_read(ihcd, XFRST_REG);
+ pr_debug("line state (D+, D-) is %d\n", LINE_STATE(status));
+
+ if (status & DPST) {
+ pr_debug("Full speed device connected\n");
+ ihcd->port_flags |= USB_PORT_STAT_CONNECTION;
+ } else {
+ pr_err("No device connected\n");
+ ret = -ENODEV;
+ }
+out:
+ return ret;
+}
+
+static int ice40_run(struct usb_hcd *hcd)
+{
+ struct ice40_hcd *ihcd = hcd_to_ihcd(hcd);
+
+ /*
+ * HCD_FLAG_POLL_RH flag is not set by us. Core will not poll
+ * for the port status periodically. This uses_new_polling
+ * flag tells core that this hcd will call usb_hcd_poll_rh_status
+ * upon port change.
+ */
+ hcd->uses_new_polling = 1;
+
+ /*
+ * Cache the ctrl0 register to avoid multiple reads. This register
+ * is written during reset and resume.
+ */
+ ihcd->ctrl0 = ice40_spi_reg_read(ihcd, CTRL0_REG);
+ ihcd->ctrl0 |= SOFEN_CTRL;
+ ice40_spi_reg_write(ihcd, ihcd->ctrl0, CTRL0_REG);
+
+ return 0;
+}
+
+static void ice40_stop(struct usb_hcd *hcd)
+{
+ struct ice40_hcd *ihcd = hcd_to_ihcd(hcd);
+
+ cancel_work_sync(&ihcd->async_work);
+}
+
+/*
+ * The _Error looks odd. But very helpful when looking for
+ * any errors in logs.
+ */
+static char __maybe_unused *xfr_status_string(int status)
+{
+ switch (XFR_MASK(status)) {
+ case XFR_SUCCESS:
+ return "Ack";
+ case XFR_BUSY:
+ return "Busy_Error";
+ case XFR_PKTERR:
+ return "Pkt_Error";
+ case XFR_PIDERR:
+ return "PID_Error";
+ case XFR_NAK:
+ return "Nak";
+ case XFR_STALL:
+ return "Stall_Error";
+ case XFR_WRONGPID:
+ return "WrongPID_Error";
+ case XFR_CRCERR:
+ return "CRC_Error";
+ case XFR_TOGERR:
+ return "Togg_Error";
+ case XFR_BADLEN:
+ return "BadLen_Error";
+ case XFR_TIMEOUT:
+ return "Timeout_Error";
+ default:
+ return "Unknown_Error";
+ }
+}
+
+static int ice40_xfer_setup(struct ice40_hcd *ihcd, struct urb *urb)
+{
+ struct usb_host_endpoint *ep = urb->ep;
+ struct ice40_ep *iep = ep->hcpriv;
+ void *buf = urb->setup_packet;
+ int ret, status;
+ u8 cmd;
+
+ /*
+ * SETUP transaction Handling:
+ * - copy the setup buffer to SUBUF fifo
+ * - Program HCMD register to initiate the SETP transaction.
+ * - poll for completion by reading XFRST register.
+ * - Interpret the result.
+ */
+
+ ihcd->setup_buf[0] = WRITE_CMD(SUBUF_REG);
+ ihcd->setup_xfr[1].tx_buf = buf;
+ ihcd->setup_xfr[1].len = sizeof(struct usb_ctrlrequest);
+
+ ret = spi_sync(ihcd->spi, ihcd->setup_msg);
+ if (ret < 0) {
+ pr_err("SPI transfer failed\n");
+ status = ret = -EIO;
+ goto out;
+ }
+
+ cmd = HCMD_PT(2) | HCMD_TOGV(0) | HCMD_BSEL(0) | HCMD_EP(0);
+ ice40_spi_reg_write(ihcd, cmd, HCMD_REG);
+
+ status = ice40_poll_xfer(ihcd, 1000);
+ switch (XFR_MASK(status)) {
+ case XFR_SUCCESS:
+ iep->xcat_err = 0;
+ ret = 0;
+ break;
+ case XFR_NAK: /* Device should not return Nak for SETUP */
+ case XFR_STALL:
+ iep->xcat_err = 0;
+ ret = -EPIPE;
+ break;
+ case XFR_PKTERR:
+ case XFR_PIDERR:
+ case XFR_WRONGPID:
+ case XFR_CRCERR:
+ case XFR_TIMEOUT:
+ if (++iep->xcat_err < 8)
+ ret = -EINPROGRESS;
+ else
+ ret = -EPROTO;
+ break;
+ default:
+ pr_err("transaction timed out\n");
+ ret = -EIO;
+ }
+
+out:
+ trace_ice40_setup(xfr_status_string(status), ret);
+ return ret;
+}
+
+static int ice40_xfer_in(struct ice40_hcd *ihcd, struct urb *urb)
+{
+ struct usb_host_endpoint *ep = urb->ep;
+ struct usb_device *udev = urb->dev;
+ u32 total_len = urb->transfer_buffer_length;
+ u16 maxpacket = usb_endpoint_maxp(&ep->desc);
+ u8 epnum = usb_pipeendpoint(urb->pipe);
+ bool is_out = usb_pipeout(urb->pipe);
+ struct ice40_ep *iep = ep->hcpriv;
+ u8 cmd, status, len = 0, t, expected_len;
+ void *buf;
+ int ret;
+ bool short_packet = true;
+
+ if (epnum == 0 && ihcd->ep0_state == STATUS_PHASE) {
+ expected_len = 0;
+ buf = NULL;
+ t = 1; /* STATUS PHASE is always DATA1 */
+ } else {
+ expected_len = min_t(u32, maxpacket,
+ total_len - urb->actual_length);
+ buf = urb->transfer_buffer + urb->actual_length;
+ t = usb_gettoggle(udev, epnum, is_out);
+ }
+
+ /*
+ * IN transaction Handling:
+ * - Program HCMD register to initiate the IN transaction.
+ * - poll for completion by reading XFRST register.
+ * - Interpret the result.
+ * - If ACK is received and we expect some data, read RBLEN
+ * - Read the data from RBUF
+ */
+
+ cmd = HCMD_PT(0) | HCMD_TOGV(t) | HCMD_BSEL(0) | HCMD_EP(epnum);
+ ice40_spi_reg_write(ihcd, cmd, HCMD_REG);
+
+ status = ice40_poll_xfer(ihcd, 1000);
+ switch (XFR_MASK(status)) {
+ case XFR_SUCCESS:
+ usb_dotoggle(udev, epnum, is_out);
+ iep->xcat_err = 0;
+ ret = 0;
+ if ((expected_len == 64) && (status & R64B))
+ short_packet = false;
+ break;
+ case XFR_NAK:
+ iep->xcat_err = 0;
+ ret = -EINPROGRESS;
+ break;
+ case XFR_TOGERR:
+ /*
+ * Peripheral had missed the previous Ack and sent
+ * the same packet again. Ack is sent by the hardware.
+ * As the data is received already, ignore this
+ * event.
+ */
+ ret = -EINPROGRESS;
+ break;
+ case XFR_PKTERR:
+ case XFR_PIDERR:
+ case XFR_WRONGPID:
+ case XFR_CRCERR:
+ case XFR_TIMEOUT:
+ if (++iep->xcat_err < 8)
+ ret = -EINPROGRESS;
+ else
+ ret = -EPROTO;
+ break;
+ case XFR_STALL:
+ ret = -EPIPE;
+ break;
+ case XFR_BADLEN:
+ ret = -EOVERFLOW;
+ break;
+ default:
+ pr_err("transaction timed out\n");
+ ret = -EIO;
+ }
+
+ /*
+ * Proceed further only if Ack is received and
+ * we are expecting some data.
+ */
+ if (ret || !expected_len)
+ goto out;
+
+ if (short_packet)
+ len = ice40_spi_reg_read(ihcd, RBLEN_REG);
+ else
+ len = 64;
+
+ /* babble condition */
+ if (len > expected_len) {
+ pr_err("overflow condition\n");
+ ret = -EOVERFLOW;
+ goto out;
+ }
+
+ /*
+ * zero len packet received. nothing to read from
+ * FIFO.
+ */
+ if (len == 0) {
+ ret = 0;
+ goto out;
+ }
+
+ ihcd->in_buf[0] = READ_CMD(RBUF0_REG);
+
+ ihcd->in_xfr[1].rx_buf = buf;
+ ihcd->in_xfr[1].len = len;
+
+ ret = spi_sync(ihcd->spi, ihcd->in_msg);
+ if (ret < 0) {
+ pr_err("SPI transfer failed\n");
+ ret = -EIO;
+ goto out;
+ }
+
+ urb->actual_length += len;
+ if ((urb->actual_length == total_len) ||
+ (len < expected_len))
+ ret = 0; /* URB completed */
+ else
+ ret = -EINPROGRESS; /* still pending */
+out:
+ trace_ice40_in(epnum, xfr_status_string(status), len,
+ expected_len, ret);
+ return ret;
+}
+
+static int ice40_xfer_out(struct ice40_hcd *ihcd, struct urb *urb)
+{
+ struct usb_host_endpoint *ep = urb->ep;
+ struct usb_device *udev = urb->dev;
+ u32 total_len = urb->transfer_buffer_length;
+ u16 maxpacket = usb_endpoint_maxp(&ep->desc);
+ u8 epnum = usb_pipeendpoint(urb->pipe);
+ bool is_out = usb_pipeout(urb->pipe);
+ struct ice40_ep *iep = ep->hcpriv;
+ u8 cmd, status, len, t;
+ void *buf;
+ int ret;
+
+ if (epnum == 0 && ihcd->ep0_state == STATUS_PHASE) {
+ len = 0;
+ buf = NULL;
+ t = 1; /* STATUS PHASE is always DATA1 */
+ } else {
+ len = min_t(u32, maxpacket, total_len - urb->actual_length);
+ buf = urb->transfer_buffer + urb->actual_length;
+ t = usb_gettoggle(udev, epnum, is_out);
+ }
+
+ /*
+ * OUT transaction Handling:
+ * - If we need to send data, write the data to WBUF Fifo
+ * - Program the WBLEN register
+ * - Program HCMD register to initiate the OUT transaction.
+ * - poll for completion by reading XFRST register.
+ * - Interpret the result.
+ */
+
+
+ if (!len)
+ goto no_data;
+
+ ihcd->out_buf[0] = WRITE_CMD(WBUF0_REG);
+
+ ihcd->out_xfr[1].tx_buf = buf;
+ ihcd->out_xfr[1].len = len;
+
+ ret = spi_sync(ihcd->spi, ihcd->out_msg);
+ if (ret < 0) {
+ pr_err("SPI transaction failed\n");
+ status = ret = -EIO;
+ goto out;
+ }
+
+no_data:
+ /*
+ * Cache the WBLEN register and update it only if it
+ * is changed from the previous value.
+ */
+ if (len != ihcd->wblen0) {
+ ice40_spi_reg_write(ihcd, len, WBLEN_REG);
+ ihcd->wblen0 = len;
+ }
+
+ cmd = HCMD_PT(1) | HCMD_TOGV(t) | HCMD_BSEL(0) | HCMD_EP(epnum);
+ ice40_spi_reg_write(ihcd, cmd, HCMD_REG);
+
+ status = ice40_poll_xfer(ihcd, 1000);
+ switch (XFR_MASK(status)) {
+ case XFR_SUCCESS:
+ usb_dotoggle(udev, epnum, is_out);
+ urb->actual_length += len;
+ iep->xcat_err = 0;
+ if (!len || (urb->actual_length == total_len))
+ ret = 0; /* URB completed */
+ else
+ ret = -EINPROGRESS; /* pending */
+ break;
+ case XFR_NAK:
+ iep->xcat_err = 0;
+ ret = -EINPROGRESS;
+ break;
+ case XFR_PKTERR:
+ case XFR_PIDERR:
+ case XFR_WRONGPID:
+ case XFR_CRCERR:
+ case XFR_TIMEOUT:
+ if (++iep->xcat_err < 8)
+ ret = -EINPROGRESS;
+ else
+ ret = -EPROTO;
+ break;
+ case XFR_STALL:
+ ret = -EPIPE;
+ break;
+ case XFR_BADLEN:
+ ret = -EOVERFLOW;
+ break;
+ default:
+ pr_err("transaction timed out\n");
+ ret = -EIO;
+ }
+
+out:
+ trace_ice40_out(epnum, xfr_status_string(status), len, ret);
+ return ret;
+}
+
+static int ice40_process_urb(struct ice40_hcd *ihcd, struct urb *urb)
+{
+ struct usb_device *udev = urb->dev;
+ u8 devnum = usb_pipedevice(urb->pipe);
+ bool is_out = usb_pipeout(urb->pipe);
+ u32 total_len = urb->transfer_buffer_length;
+ int ret = 0;
+
+ /*
+ * The USB device address can be reset to 0 by core temporarily
+ * during reset recovery process. Don't assume anything about
+ * device address. The device address is programmed as 0 by
+ * default. If the device address is different to the previous
+ * cached value, re-program it here before proceeding. The device
+ * address register (FADDR) holds the value across multiple
+ * transactions and we support only one device.
+ */
+ if (ihcd->devnum != devnum) {
+ ice40_spi_reg_write(ihcd, devnum, FADDR_REG);
+ ihcd->devnum = devnum;
+ }
+
+ switch (usb_pipetype(urb->pipe)) {
+ case PIPE_CONTROL:
+ switch (ihcd->ep0_state) {
+ case SETUP_PHASE:
+ trace_ice40_ep0("SETUP");
+ ret = ice40_xfer_setup(ihcd, urb);
+ if (ret)
+ break;
+ if (total_len) {
+ ihcd->ep0_state = DATA_PHASE;
+ /*
+ * Data stage always begin with
+ * DATA1 PID.
+ */
+ usb_settoggle(udev, 0, is_out, 1);
+ } else {
+ ihcd->ep0_state = STATUS_PHASE;
+ goto do_status;
+ }
+ /* fall through */
+ case DATA_PHASE:
+ trace_ice40_ep0("DATA");
+ if (is_out)
+ ret = ice40_xfer_out(ihcd, urb);
+ else
+ ret = ice40_xfer_in(ihcd, urb);
+ if (ret)
+ break;
+ /* DATA Phase is completed successfully */
+ ihcd->ep0_state = STATUS_PHASE;
+ /* fall through */
+ case STATUS_PHASE:
+do_status:
+ trace_ice40_ep0("STATUS");
+ /* zero len DATA transfers have IN status */
+ if (!total_len || is_out)
+ ret = ice40_xfer_in(ihcd, urb);
+ else
+ ret = ice40_xfer_out(ihcd, urb);
+ if (ret)
+ break;
+ ihcd->ep0_state = SETUP_PHASE;
+ break;
+ default:
+ pr_err("unknown stage for a control transfer\n");
+ break;
+ }
+ break;
+ case PIPE_BULK:
+ if (is_out)
+ ret = ice40_xfer_out(ihcd, urb);
+ else
+ ret = ice40_xfer_in(ihcd, urb);
+ /*
+ * We may have to support zero len packet terminations
+ * for URB_ZERO_PACKET URBs.
+ */
+ break;
+ default:
+ pr_err("IN/ISO transfers not supported\n");
+ break;
+ }
+
+ return ret;
+}
+
+/* Must be called with spin lock and interrupts disabled */
+static void ice40_complete_urb(struct usb_hcd *hcd, struct urb *urb, int status)
+{
+ struct ice40_hcd *ihcd = hcd_to_ihcd(hcd);
+ struct usb_host_endpoint *ep = urb->ep;
+ struct ice40_ep *iep = ep->hcpriv;
+ struct urb *first_urb;
+ bool needs_update = false;
+ bool control = usb_pipecontrol(urb->pipe);
+
+ /*
+ * If the active URB i.e the first URB in the ep list is being
+ * removed, clear the transaction error count. If it is a control
+ * URB ep0_state needs to be reset to SETUP_PHASE.
+ */
+ first_urb = list_first_entry(&ep->urb_list, struct urb, urb_list);
+ if (urb == first_urb)
+ needs_update = true;
+
+ usb_hcd_unlink_urb_from_ep(hcd, urb);
+ spin_unlock(&ihcd->lock);
+ trace_ice40_urb_done(urb, status);
+ usb_hcd_giveback_urb(ihcd->hcd, urb, status);
+ spin_lock(&ihcd->lock);
+
+ if (needs_update) {
+ iep->xcat_err = 0;
+ if (control)
+ ihcd->ep0_state = SETUP_PHASE;
+ }
+}
+
+static void ice40_async_work(struct work_struct *work)
+{
+ struct ice40_hcd *ihcd = container_of(work,
+ struct ice40_hcd, async_work);
+ struct usb_hcd *hcd = ihcd->hcd;
+ struct list_head *tmp, *uent, *utmp;
+ struct ice40_ep *iep;
+ struct usb_host_endpoint *ep;
+ struct urb *urb;
+ unsigned long flags;
+ int status;
+
+ /*
+ * Traverse the active endpoints circularly and process URBs.
+ * If any endpoint is marked for unlinking, the URBs are
+ * completed here. The endpoint is removed from active list
+ * if a URB is retired with -EPIPE/-EPROTO errors.
+ */
+
+ spin_lock_irqsave(&ihcd->lock, flags);
+
+ if (list_empty(&ihcd->async_list))
+ goto out;
+
+ iep = list_first_entry(&ihcd->async_list, struct ice40_ep, ep_list);
+ while (1) {
+ ep = iep->ep;
+
+ urb = list_first_entry(&ep->urb_list, struct urb, urb_list);
+ if (urb->unlinked) {
+ status = urb->unlinked;
+ } else {
+ spin_unlock_irqrestore(&ihcd->lock, flags);
+ status = ice40_process_urb(ihcd, urb);
+ spin_lock_irqsave(&ihcd->lock, flags);
+ }
+
+ if ((status == -EPIPE) || (status == -EPROTO))
+ iep->halted = true;
+
+ if (status != -EINPROGRESS)
+ ice40_complete_urb(hcd, urb, status);
+
+ if (iep->unlinking) {
+ list_for_each_safe(uent, utmp, &ep->urb_list) {
+ urb = list_entry(uent, struct urb, urb_list);
+ if (urb->unlinked)
+ ice40_complete_urb(hcd, urb, 0);
+ }
+ iep->unlinking = false;
+ }
+
+ tmp = iep->ep_list.next;
+ if (list_empty(&ep->urb_list) || iep->halted) {
+ list_del_init(&iep->ep_list);
+
+ if (list_empty(&ihcd->async_list))
+ break;
+ }
+
+ if (tmp == &ihcd->async_list)
+ tmp = tmp->next;
+ iep = list_entry(tmp, struct ice40_ep, ep_list);
+ }
+out:
+ spin_unlock_irqrestore(&ihcd->lock, flags);
+}
+
+static int
+ice40_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags)
+{
+ struct ice40_hcd *ihcd = hcd_to_ihcd(hcd);
+ struct usb_device *udev = urb->dev;
+ struct usb_host_endpoint *ep = urb->ep;
+ bool is_out = usb_pipeout(urb->pipe);
+ u8 epnum = usb_pipeendpoint(urb->pipe);
+ struct ice40_ep *iep;
+ unsigned long flags;
+ int ret;
+
+ /*
+ * This bridge chip supports only Full-speed. So ISO is not
+ * supported. Interrupt support is not implemented as there
+ * is no use case.
+ */
+ if (usb_pipeisoc(urb->pipe) || usb_pipeint(urb->pipe)) {
+ pr_debug("iso and int xfers not supported\n");
+ ret = -ENOTSUPP;
+ goto out;
+ }
+
+ spin_lock_irqsave(&ihcd->lock, flags);
+
+ ret = usb_hcd_link_urb_to_ep(hcd, urb);
+ if (ret)
+ goto rel_lock;
+
+ trace_ice40_urb_enqueue(urb);
+
+ iep = ep->hcpriv;
+ if (!iep) {
+ iep = kzalloc(sizeof(struct ice40_ep), GFP_ATOMIC);
+ if (!iep) {
+ pr_debug("fail to allocate iep\n");
+ ret = -ENOMEM;
+ goto unlink;
+ }
+ ep->hcpriv = iep;
+ INIT_LIST_HEAD(&iep->ep_list);
+ iep->ep = ep;
+ usb_settoggle(udev, epnum, is_out, 0);
+ if (usb_pipecontrol(urb->pipe))
+ ihcd->ep0_state = SETUP_PHASE;
+ }
+
+ /*
+ * We expect the interface driver to clear the stall condition
+ * before queueing another URB. For example mass storage
+ * device may STALL a bulk endpoint for un-supported command.
+ * The storage driver clear the STALL condition before queueing
+ * another URB.
+ */
+ iep->halted = false;
+ if (list_empty(&iep->ep_list))
+ list_add_tail(&iep->ep_list, &ihcd->async_list);
+
+ queue_work(ihcd->wq, &ihcd->async_work);
+
+ spin_unlock_irqrestore(&ihcd->lock, flags);
+
+ return 0;
+unlink:
+ usb_hcd_unlink_urb_from_ep(hcd, urb);
+rel_lock:
+ spin_unlock_irqrestore(&ihcd->lock, flags);
+out:
+ return ret;
+}
+
+static int
+ice40_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
+{
+ struct ice40_hcd *ihcd = hcd_to_ihcd(hcd);
+ struct usb_host_endpoint *ep = urb->ep;
+ struct ice40_ep *iep;
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&ihcd->lock, flags);
+
+ ret = usb_hcd_check_unlink_urb(hcd, urb, status);
+ if (ret)
+ goto rel_lock;
+
+ trace_ice40_urb_dequeue(urb);
+ iep = ep->hcpriv;
+
+ /*
+ * If the endpoint is not in asynchronous schedule, complete
+ * the URB immediately. Otherwise mark it as being unlinked.
+ * The asynchronous schedule work will take care of completing
+ * the URB when this endpoint is encountered during traversal.
+ */
+ if (list_empty(&iep->ep_list))
+ ice40_complete_urb(hcd, urb, status);
+ else
+ iep->unlinking = true;
+
+rel_lock:
+ spin_unlock_irqrestore(&ihcd->lock, flags);
+ return ret;
+}
+
+static void
+ice40_endpoint_disable(struct usb_hcd *hcd, struct usb_host_endpoint *ep)
+{
+ struct ice40_ep *iep = ep->hcpriv;
+
+ /*
+ * If there is no I/O on this endpoint before, ep->hcpriv
+ * will be NULL. nothing to do in this case.
+ */
+ if (!iep)
+ return;
+
+ if (!list_empty(&ep->urb_list))
+ pr_err("trying to disable an non-empty endpoint\n");
+
+ kfree(iep);
+ ep->hcpriv = NULL;
+}
+
+
+static int ice40_hub_status_data(struct usb_hcd *hcd, char *buf)
+{
+ struct ice40_hcd *ihcd = hcd_to_ihcd(hcd);
+ int ret = 0;
+
+ /*
+ * core calls hub_status_method during suspend/resume.
+ * return 0 if there is no port change. pcd_pending
+ * is set to true when a device is connected and line
+ * state is sampled via debugfs command. clear this
+ * flag after returning the port change status.
+ */
+ if (ihcd->pcd_pending) {
+ *buf = (1 << 1);
+ ret = 1;
+ ihcd->pcd_pending = false;
+ }
+
+ return ret;
+}
+
+static void ice40_hub_descriptor(struct usb_hub_descriptor *desc)
+{
+ /* There is nothing special about us!! */
+ desc->bDescLength = 9;
+ desc->bDescriptorType = 0x29;
+ desc->bNbrPorts = 1;
+ desc->wHubCharacteristics = cpu_to_le16(HUB_CHAR_NO_LPSM |
+ HUB_CHAR_NO_OCPM);
+ desc->bPwrOn2PwrGood = 0;
+ desc->bHubContrCurrent = 0;
+ desc->u.hs.DeviceRemovable[0] = 0;
+ desc->u.hs.DeviceRemovable[1] = ~0;
+}
+
+static int
+ice40_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
+ u16 wIndex, char *buf, u16 wLength)
+{
+ int ret = 0;
+ u8 ctrl;
+ struct ice40_hcd *ihcd = hcd_to_ihcd(hcd);
+
+ /*
+ * We have only 1 port. No special locking is required while
+ * handling root hub commands. The bridge chip does not maintain
+ * any port states. Maintain different port states in software.
+ */
+ switch (typeReq) {
+ case ClearPortFeature:
+ if (wIndex != 1 || wLength != 0)
+ goto error;
+ switch (wValue) {
+ case USB_PORT_FEAT_SUSPEND:
+ /*
+ * The device is resumed as part of the root hub
+ * resume to simplify the resume sequence. so
+ * we may simply return from here. If device is
+ * resumed before root hub is suspended, this
+ * flags will be cleared here.
+ */
+ if (!(ihcd->port_flags & USB_PORT_STAT_SUSPEND))
+ break;
+ ihcd->port_flags &= ~USB_PORT_STAT_SUSPEND;
+ break;
+ case USB_PORT_FEAT_ENABLE:
+ ihcd->port_flags &= ~USB_PORT_STAT_ENABLE;
+ break;
+ case USB_PORT_FEAT_POWER:
+ ihcd->port_flags &= ~USB_PORT_STAT_POWER;
+ break;
+ case USB_PORT_FEAT_C_CONNECTION:
+ ihcd->port_flags &= ~(USB_PORT_STAT_C_CONNECTION << 16);
+ break;
+ case USB_PORT_FEAT_C_ENABLE:
+ case USB_PORT_FEAT_C_SUSPEND:
+ case USB_PORT_FEAT_C_OVER_CURRENT:
+ case USB_PORT_FEAT_C_RESET:
+ /* nothing special here */
+ break;
+ default:
+ goto error;
+ }
+ break;
+ case GetHubDescriptor:
+ ice40_hub_descriptor((struct usb_hub_descriptor *) buf);
+ break;
+ case GetHubStatus:
+ put_unaligned_le32(0, buf);
+ break;
+ case GetPortStatus:
+ if (wIndex != 1)
+ goto error;
+
+ /*
+ * Core resets the device and requests port status to
+ * stop the reset signaling. If there is a reset in
+ * progress, finish it here.
+ */
+ ctrl = ice40_spi_reg_read(ihcd, CTRL0_REG);
+ if (!(ctrl & RESET_CTRL))
+ ihcd->port_flags &= ~USB_PORT_STAT_RESET;
+
+ put_unaligned_le32(ihcd->port_flags, buf);
+ break;
+ case SetPortFeature:
+ if (wIndex != 1 || wLength != 0)
+ goto error;
+ switch (wValue) {
+ case USB_PORT_FEAT_SUSPEND:
+ if (ihcd->port_flags & USB_PORT_STAT_RESET)
+ goto error;
+ if (!(ihcd->port_flags & USB_PORT_STAT_ENABLE))
+ goto error;
+ /* SOFs will be stopped during root hub suspend */
+ ihcd->port_flags |= USB_PORT_STAT_SUSPEND;
+ break;
+ case USB_PORT_FEAT_POWER:
+ ihcd->port_flags |= USB_PORT_STAT_POWER;
+ break;
+ case USB_PORT_FEAT_RESET:
+ /* Good time to enable the port */
+ ice40_spi_reg_write(ihcd, ihcd->ctrl0 |
+ RESET_CTRL, CTRL0_REG);
+ ihcd->port_flags |= USB_PORT_STAT_RESET;
+ ihcd->port_flags |= USB_PORT_STAT_ENABLE;
+ break;
+ default:
+ goto error;
+ }
+ break;
+ default:
+error:
+ /* "protocol stall" on error */
+ ret = -EPIPE;
+ }
+
+ trace_ice40_hub_control(typeReq, wValue, wIndex, wLength, ret);
+ return ret;
+}
+
+static void ice40_spi_power_off(struct ice40_hcd *ihcd);
+static int ice40_bus_suspend(struct usb_hcd *hcd)
+{
+ struct ice40_hcd *ihcd = hcd_to_ihcd(hcd);
+
+ trace_ice40_bus_suspend(0); /* start */
+
+ /* This happens only during debugging */
+ if (!ihcd->devnum) {
+ pr_debug("device still not connected. abort suspend\n");
+ trace_ice40_bus_suspend(2); /* failure */
+ return -EAGAIN;
+ }
+ /*
+ * Stop sending the SOFs on downstream port. The device
+ * finds the bus idle and enter suspend. The device
+ * takes ~3 msec to enter suspend.
+ */
+ ihcd->ctrl0 &= ~SOFEN_CTRL;
+ ice40_spi_reg_write(ihcd, ihcd->ctrl0, CTRL0_REG);
+ usleep_range(4500, 5000);
+
+ /*
+ * Power collapse the bridge chip to avoid the leakage
+ * current.
+ */
+ ice40_spi_power_off(ihcd);
+
+ trace_ice40_bus_suspend(1); /* successful */
+ pm_relax(&ihcd->spi->dev);
+ return 0;
+}
+
+static int ice40_spi_load_fw(struct ice40_hcd *ihcd);
+static int ice40_bus_resume(struct usb_hcd *hcd)
+{
+ struct ice40_hcd *ihcd = hcd_to_ihcd(hcd);
+ u8 ctrl0;
+ int ret, i;
+
+ pm_stay_awake(&ihcd->spi->dev);
+ trace_ice40_bus_resume(0); /* start */
+ /*
+ * Power up the bridge chip and load the configuration file.
+ * Re-program the previous settings. For now we need to
+ * update the device address only.
+ */
+
+ for (i = 0; i < 3; i++) {
+ ret = ice40_spi_load_fw(ihcd);
+ if (!ret)
+ break;
+ }
+
+ if (ret) {
+ pr_err("Load firmware failed with ret: %d\n", ret);
+ return ret;
+ }
+
+ ice40_spi_reg_write(ihcd, ihcd->devnum, FADDR_REG);
+ ihcd->wblen0 = ~0;
+
+ /*
+ * Program the bridge chip to drive resume signaling. The SOFs
+ * are automatically transmitted after resume completion. It
+ * will take ~20 msec for resume completion.
+ */
+ ice40_spi_reg_write(ihcd, ihcd->ctrl0 | RESUME_CTRL, CTRL0_REG);
+ usleep_range(20000, 21000);
+ ret = ice40_handshake(ihcd, CTRL0_REG, RESUME_CTRL, 0, 5000);
+ if (ret) {
+ pr_err("resume failed\n");
+ trace_ice40_bus_resume(2); /* failure */
+ return -ENODEV;
+ }
+
+ ctrl0 = ice40_spi_reg_read(ihcd, CTRL0_REG);
+ if (!(ctrl0 & SOFEN_CTRL)) {
+ pr_err("SOFs are not transmitted after resume\n");
+ trace_ice40_bus_resume(3); /* failure */
+ return -ENODEV;
+ }
+
+ ihcd->port_flags &= ~USB_PORT_STAT_SUSPEND;
+ ihcd->ctrl0 |= SOFEN_CTRL;
+
+ trace_ice40_bus_resume(1); /* success */
+ return 0;
+}
+
+static void ice40_set_autosuspend_delay(struct usb_device *dev)
+{
+ /*
+ * Immediate suspend for root hub and 500 msec auto-suspend
+ * timeout for the card.
+ */
+ if (!dev->parent)
+ pm_runtime_set_autosuspend_delay(&dev->dev, 0);
+ else
+ pm_runtime_set_autosuspend_delay(&dev->dev, 500);
+}
+
+static const struct hc_driver ice40_hc_driver = {
+ .description = hcd_name,
+ .product_desc = "ICE40 SPI Host Controller",
+ .hcd_priv_size = sizeof(struct ice40_hcd *),
+ .flags = HCD_USB11,
+
+ /* setup and clean up */
+ .reset = ice40_reset,
+ .start = ice40_run,
+ .stop = ice40_stop,
+
+ /* endpoint and I/O routines */
+ .urb_enqueue = ice40_urb_enqueue,
+ .urb_dequeue = ice40_urb_dequeue,
+ .endpoint_disable = ice40_endpoint_disable,
+
+ /* Root hub operations */
+ .hub_status_data = ice40_hub_status_data,
+ .hub_control = ice40_hub_control,
+ .bus_suspend = ice40_bus_suspend,
+ .bus_resume = ice40_bus_resume,
+
+ .set_autosuspend_delay = ice40_set_autosuspend_delay,
+};
+
+static int ice40_spi_parse_dt(struct ice40_hcd *ihcd)
+{
+ struct device_node *node = ihcd->spi->dev.of_node;
+ int ret = 0;
+
+ if (!node) {
+ pr_err("device specific info missing\n");
+ ret = -ENODEV;
+ goto out;
+ }
+
+ ihcd->reset_gpio = of_get_named_gpio(node, "lattice,reset-gpio", 0);
+ if (ihcd->reset_gpio < 0) {
+ pr_err("reset gpio is missing\n");
+ ret = ihcd->reset_gpio;
+ goto out;
+ }
+
+ ihcd->slave_select_gpio = of_get_named_gpio(node,
+ "lattice,slave-select-gpio", 0);
+ if (ihcd->slave_select_gpio < 0) {
+ pr_err("slave select gpio is missing\n");
+ ret = ihcd->slave_select_gpio;
+ goto out;
+ }
+
+ ihcd->config_done_gpio = of_get_named_gpio(node,
+ "lattice,config-done-gpio", 0);
+ if (ihcd->config_done_gpio < 0) {
+ pr_err("config done gpio is missing\n");
+ ret = ihcd->config_done_gpio;
+ goto out;
+ }
+
+ ihcd->vcc_en_gpio = of_get_named_gpio(node, "lattice,vcc-en-gpio", 0);
+ if (ihcd->vcc_en_gpio < 0) {
+ pr_err("vcc enable gpio is missing\n");
+ ret = ihcd->vcc_en_gpio;
+ goto out;
+ }
+
+ /*
+ * When clk-en-gpio is present, it is used to enable the 19.2 MHz
+ * clock from MSM to the bridge chip. Otherwise on-board clock
+ * is used.
+ */
+ ihcd->clk_en_gpio = of_get_named_gpio(node, "lattice,clk-en-gpio", 0);
+ if (ihcd->clk_en_gpio < 0)
+ ihcd->clk_en_gpio = 0;
+out:
+ return ret;
+}
+
+static void ice40_spi_power_off(struct ice40_hcd *ihcd)
+{
+ if (!ihcd->powered)
+ return;
+
+ gpio_direction_output(ihcd->vcc_en_gpio, 0);
+ regulator_disable(ihcd->core_vcc);
+ regulator_disable(ihcd->spi_vcc);
+ if (ihcd->gpio_vcc)
+ regulator_disable(ihcd->gpio_vcc);
+ if (ihcd->clk_en_gpio)
+ gpio_direction_output(ihcd->clk_en_gpio, 0);
+
+ ihcd->powered = false;
+}
+
+static int ice40_spi_power_up(struct ice40_hcd *ihcd)
+{
+ int ret;
+
+ if (ihcd->clk_en_gpio) {
+ ret = gpio_direction_output(ihcd->clk_en_gpio, 1);
+ if (ret < 0) {
+ pr_err("fail to enabel clk %d\n", ret);
+ goto out;
+ }
+ }
+
+ if (ihcd->gpio_vcc) {
+ ret = regulator_enable(ihcd->gpio_vcc); /* 1.8 V */
+ if (ret < 0) {
+ pr_err("fail to enable gpio vcc\n");
+ goto disable_clk;
+ }
+ }
+
+ ret = regulator_enable(ihcd->spi_vcc); /* 1.8 V */
+ if (ret < 0) {
+ pr_err("fail to enable spi vcc\n");
+ goto disable_gpio_vcc;
+ }
+
+ ret = regulator_enable(ihcd->core_vcc); /* 1.2 V */
+ if (ret < 0) {
+ pr_err("fail to enable core vcc\n");
+ goto disable_spi_vcc;
+ }
+
+ ret = gpio_direction_output(ihcd->vcc_en_gpio, 1);
+ if (ret < 0) {
+ pr_err("fail to assert vcc gpio\n");
+ goto disable_core_vcc;
+ }
+
+ ihcd->powered = true;
+
+ return 0;
+
+disable_core_vcc:
+ regulator_disable(ihcd->core_vcc);
+disable_spi_vcc:
+ regulator_disable(ihcd->spi_vcc);
+disable_gpio_vcc:
+ if (ihcd->gpio_vcc)
+ regulator_disable(ihcd->gpio_vcc);
+disable_clk:
+ if (ihcd->clk_en_gpio)
+ gpio_direction_output(ihcd->clk_en_gpio, 0);
+out:
+ return ret;
+}
+
+static struct gpiomux_setting slave_select_setting = {
+ .func = GPIOMUX_FUNC_GPIO,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_NONE,
+ .dir = GPIOMUX_OUT_LOW,
+};
+
+static int ice40_spi_cache_fw(struct ice40_hcd *ihcd)
+{
+ const struct firmware *fw;
+ void *buf;
+ size_t buf_len;
+ int ret;
+
+ ret = request_firmware(&fw, fw_name, &ihcd->spi->dev);
+ if (ret < 0) {
+ pr_err("fail to get the firmware\n");
+ goto out;
+ }
+
+ pr_debug("received firmware size = %zu\n", fw->size);
+
+ /*
+ * The bridge expects additional clock cycles after
+ * receiving the configuration data. We don't have a
+ * direct control over SPI clock. Add extra bytes
+ * to the confiration data.
+ */
+ buf_len = fw->size + 16;
+ buf = devm_kzalloc(&ihcd->spi->dev, buf_len, GFP_KERNEL);
+ if (!buf) {
+ pr_err("fail to allocate firmware buffer\n");
+ ret = -ENOMEM;
+ goto release;
+ }
+
+ /*
+ * The firmware buffer can not be used for DMA as it
+ * is not physically contiguous. We copy the data
+ * in kmalloc buffer. This buffer will be freed only
+ * during unbind or rmmod.
+ */
+ memcpy(buf, fw->data, fw->size);
+ release_firmware(fw);
+
+ /*
+ * The bridge supports only 25 MHz during configuration
+ * file loading.
+ */
+ ihcd->fmsg_xfr[0].tx_buf = buf;
+ ihcd->fmsg_xfr[0].len = buf_len;
+ ihcd->fmsg_xfr[0].speed_hz = 25000000;
+
+ return 0;
+
+release:
+ release_firmware(fw);
+out:
+ return ret;
+}
+
+static int ice40_spi_load_fw(struct ice40_hcd *ihcd)
+{
+ int ret, i;
+ struct gpiomux_setting active_old_setting, suspend_old_setting;
+
+ ret = gpio_direction_output(ihcd->reset_gpio, 0);
+ if (ret < 0) {
+ pr_err("fail to assert reset %d\n", ret);
+ goto out;
+ }
+
+ ret = gpio_direction_output(ihcd->vcc_en_gpio, 0);
+ if (ret < 0) {
+ pr_err("fail to de-assert vcc_en gpio %d\n", ret);
+ goto out;
+ }
+
+ /*
+ * The bridge chip samples the chip select signal during
+ * power-up. If it is low, it enters SPI slave mode and
+ * accepts the configuration data from us. The chip
+ * select signal is managed by the SPI controller driver.
+ * We temporarily override the chip select config to
+ * drive it low. The SPI bus needs to be locked down during
+ * this period to avoid other slave data going to our
+ * bridge chip. Disable the SPI runtime suspend for exclusive
+ * chip select access.
+ */
+ pm_runtime_get_sync(ihcd->spi->master->dev.parent);
+
+ spi_bus_lock(ihcd->spi->master);
+
+ ret = msm_gpiomux_write(ihcd->slave_select_gpio, GPIOMUX_SUSPENDED,
+ &slave_select_setting, &suspend_old_setting);
+ if (ret < 0) {
+ pr_err("fail to override suspend setting and select slave %d\n",
+ ret);
+ spi_bus_unlock(ihcd->spi->master);
+ pm_runtime_put_noidle(ihcd->spi->master->dev.parent);
+ goto out;
+ }
+
+ ret = msm_gpiomux_write(ihcd->slave_select_gpio, GPIOMUX_ACTIVE,
+ &slave_select_setting, &active_old_setting);
+ if (ret < 0) {
+ pr_err("fail to override active setting and select slave %d\n",
+ ret);
+ spi_bus_unlock(ihcd->spi->master);
+ pm_runtime_put_noidle(ihcd->spi->master->dev.parent);
+ goto out;
+ }
+
+ ret = ice40_spi_power_up(ihcd);
+ if (ret < 0) {
+ pr_err("fail to power up the chip\n");
+ spi_bus_unlock(ihcd->spi->master);
+ pm_runtime_put_noidle(ihcd->spi->master->dev.parent);
+ goto out;
+ }
+
+
+ /*
+ * The databook says 1200 usec is required before the
+ * chip becomes ready for the SPI transfer.
+ */
+ usleep_range(1200, 1250);
+
+ ret = msm_gpiomux_write(ihcd->slave_select_gpio, GPIOMUX_SUSPENDED,
+ &suspend_old_setting, NULL);
+ if (ret < 0) {
+ pr_err("fail to rewrite suspend setting %d\n", ret);
+ spi_bus_unlock(ihcd->spi->master);
+ pm_runtime_put_noidle(ihcd->spi->master->dev.parent);
+ goto power_off;
+ }
+
+ ret = msm_gpiomux_write(ihcd->slave_select_gpio, GPIOMUX_ACTIVE,
+ &active_old_setting, NULL);
+ if (ret < 0) {
+ pr_err("fail to rewrite active setting %d\n", ret);
+ spi_bus_unlock(ihcd->spi->master);
+ pm_runtime_put_noidle(ihcd->spi->master->dev.parent);
+ goto power_off;
+ }
+
+ pm_runtime_put_noidle(ihcd->spi->master->dev.parent);
+
+ ret = spi_sync_locked(ihcd->spi, ihcd->fmsg);
+
+ spi_bus_unlock(ihcd->spi->master);
+
+ if (ret < 0) {
+ pr_err("spi write failed\n");
+ goto power_off;
+ }
+
+ for (i = 0; i < 1000; i++) {
+ ret = gpio_get_value(ihcd->config_done_gpio);
+ if (ret) {
+ pr_debug("config done asserted %d\n", i);
+ break;
+ }
+ udelay(1);
+ }
+
+ if (ret <= 0) {
+ pr_err("config done not asserted\n");
+ ret = -ENODEV;
+ goto power_off;
+ }
+
+ ret = gpio_direction_output(ihcd->reset_gpio, 1);
+ if (ret < 0) {
+ pr_err("fail to assert reset %d\n", ret);
+ goto power_off;
+ }
+ udelay(50);
+
+ ret = ice40_spi_reg_read(ihcd, XFRST_REG);
+ pr_debug("XFRST val is %x\n", ret);
+ if (!(ret & PLLOK)) {
+ pr_err("The PLL2 is not synchronized\n");
+ goto power_off;
+ }
+
+ pr_info("Firmware load success\n");
+
+ return 0;
+
+power_off:
+ ice40_spi_power_off(ihcd);
+out:
+ return ret;
+}
+
+static int ice40_spi_init_regulators(struct ice40_hcd *ihcd)
+{
+ int ret;
+
+ ihcd->spi_vcc = devm_regulator_get(&ihcd->spi->dev, "spi-vcc");
+ if (IS_ERR(ihcd->spi_vcc)) {
+ ret = PTR_ERR(ihcd->spi_vcc);
+ if (ret != -EPROBE_DEFER)
+ pr_err("fail to get spi-vcc %d\n", ret);
+ goto out;
+ }
+
+ ret = regulator_set_voltage(ihcd->spi_vcc, 1800000, 1800000);
+ if (ret < 0) {
+ pr_err("fail to set spi-vcc %d\n", ret);
+ goto out;
+ }
+
+ ihcd->core_vcc = devm_regulator_get(&ihcd->spi->dev, "core-vcc");
+ if (IS_ERR(ihcd->core_vcc)) {
+ ret = PTR_ERR(ihcd->core_vcc);
+ if (ret != -EPROBE_DEFER)
+ pr_err("fail to get core-vcc %d\n", ret);
+ goto out;
+ }
+
+ ret = regulator_set_voltage(ihcd->core_vcc, 1200000, 1200000);
+ if (ret < 0) {
+ pr_err("fail to set core-vcc %d\n", ret);
+ goto out;
+ }
+
+ if (!of_get_property(ihcd->spi->dev.of_node, "gpio-supply", NULL))
+ goto out;
+
+ ihcd->gpio_vcc = devm_regulator_get(&ihcd->spi->dev, "gpio");
+ if (IS_ERR(ihcd->gpio_vcc)) {
+ ret = PTR_ERR(ihcd->gpio_vcc);
+ if (ret != -EPROBE_DEFER)
+ pr_err("fail to get gpio_vcc %d\n", ret);
+ goto out;
+ }
+
+ ret = regulator_set_voltage(ihcd->gpio_vcc, 1800000, 1800000);
+ if (ret < 0) {
+ pr_err("fail to set gpio_vcc %d\n", ret);
+ goto out;
+ }
+
+out:
+ return ret;
+}
+
+static int ice40_spi_request_gpios(struct ice40_hcd *ihcd)
+{
+ int ret;
+
+ ret = devm_gpio_request(&ihcd->spi->dev, ihcd->reset_gpio,
+ "ice40_reset");
+ if (ret < 0) {
+ pr_err("fail to request reset gpio\n");
+ goto out;
+ }
+
+ ret = devm_gpio_request(&ihcd->spi->dev, ihcd->config_done_gpio,
+ "ice40_config_done");
+ if (ret < 0) {
+ pr_err("fail to request config_done gpio\n");
+ goto out;
+ }
+
+ ret = devm_gpio_request(&ihcd->spi->dev, ihcd->vcc_en_gpio,
+ "ice40_vcc_en");
+ if (ret < 0) {
+ pr_err("fail to request vcc_en gpio\n");
+ goto out;
+ }
+
+ if (ihcd->clk_en_gpio) {
+
+ ret = devm_gpio_request(&ihcd->spi->dev, ihcd->clk_en_gpio,
+ "ice40_clk_en");
+ if (ret < 0)
+ pr_err("fail to request clk_en gpio\n");
+ }
+
+out:
+ return ret;
+}
+
+static int
+ice40_spi_init_one_xfr(struct ice40_hcd *ihcd, enum ice40_xfr_type type)
+{
+ struct spi_message **m;
+ struct spi_transfer **t;
+ int n;
+
+ switch (type) {
+ case FIRMWARE_XFR:
+ m = &ihcd->fmsg;
+ t = &ihcd->fmsg_xfr;
+ n = 1;
+ break;
+ case REG_WRITE_XFR:
+ m = &ihcd->wmsg;
+ t = &ihcd->wmsg_xfr;
+ n = 1;
+ break;
+ case REG_READ_XFR:
+ m = &ihcd->rmsg;
+ t = &ihcd->rmsg_xfr;
+ n = 1;
+ break;
+ case SETUP_XFR:
+ m = &ihcd->setup_msg;
+ t = &ihcd->setup_xfr;
+ n = 2;
+ break;
+ case DATA_IN_XFR:
+ m = &ihcd->in_msg;
+ t = &ihcd->in_xfr;
+ n = 2;
+ break;
+ case DATA_OUT_XFR:
+ m = &ihcd->out_msg;
+ t = &ihcd->out_xfr;
+ n = 2;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ *m = devm_kzalloc(&ihcd->spi->dev, sizeof(**m), GFP_KERNEL);
+ if (*m == NULL)
+ goto out;
+
+ *t = devm_kzalloc(&ihcd->spi->dev, n * sizeof(**t), GFP_KERNEL);
+ if (*t == NULL)
+ goto out;
+
+ spi_message_init_with_transfers(*m, *t, n);
+
+ return 0;
+out:
+ return -ENOMEM;
+}
+
+static int ice40_spi_init_xfrs(struct ice40_hcd *ihcd)
+{
+ int ret = -ENOMEM;
+
+ ret = ice40_spi_init_one_xfr(ihcd, FIRMWARE_XFR);
+ if (ret < 0)
+ goto out;
+
+ ret = ice40_spi_init_one_xfr(ihcd, REG_WRITE_XFR);
+ if (ret < 0)
+ goto out;
+
+ ihcd->w_tx_buf = devm_kzalloc(&ihcd->spi->dev, 2, GFP_KERNEL);
+ if (!ihcd->w_tx_buf)
+ goto out;
+
+ ihcd->w_rx_buf = devm_kzalloc(&ihcd->spi->dev, 2, GFP_KERNEL);
+ if (!ihcd->w_rx_buf)
+ goto out;
+
+ ihcd->wmsg_xfr[0].tx_buf = ihcd->w_tx_buf;
+ ihcd->wmsg_xfr[0].rx_buf = ihcd->w_rx_buf;
+ ihcd->wmsg_xfr[0].len = 2;
+
+ ret = ice40_spi_init_one_xfr(ihcd, REG_READ_XFR);
+ if (ret < 0)
+ goto out;
+
+ ihcd->r_tx_buf = devm_kzalloc(&ihcd->spi->dev, 3, GFP_KERNEL);
+ if (!ihcd->r_tx_buf)
+ goto out;
+
+ ihcd->r_rx_buf = devm_kzalloc(&ihcd->spi->dev, 3, GFP_KERNEL);
+ if (!ihcd->r_rx_buf)
+ goto out;
+
+ ihcd->rmsg_xfr[0].tx_buf = ihcd->r_tx_buf;
+ ihcd->rmsg_xfr[0].rx_buf = ihcd->r_rx_buf;
+ ihcd->rmsg_xfr[0].len = 3;
+
+ ret = ice40_spi_init_one_xfr(ihcd, SETUP_XFR);
+ if (ret < 0)
+ goto out;
+
+ ihcd->setup_buf = devm_kzalloc(&ihcd->spi->dev, 1, GFP_KERNEL);
+ if (!ihcd->setup_buf)
+ goto out;
+ ihcd->setup_xfr[0].tx_buf = ihcd->setup_buf;
+ ihcd->setup_xfr[0].len = 1;
+
+ ret = ice40_spi_init_one_xfr(ihcd, DATA_IN_XFR);
+ if (ret < 0)
+ goto out;
+ ihcd->in_buf = devm_kzalloc(&ihcd->spi->dev, 2, GFP_KERNEL);
+ if (!ihcd->in_buf)
+ goto out;
+ ihcd->in_xfr[0].tx_buf = ihcd->in_buf;
+ ihcd->in_xfr[0].len = 2;
+
+ ret = ice40_spi_init_one_xfr(ihcd, DATA_OUT_XFR);
+ if (ret < 0)
+ goto out;
+ ihcd->out_buf = devm_kzalloc(&ihcd->spi->dev, 1, GFP_KERNEL);
+ if (!ihcd->out_buf)
+ goto out;
+ ihcd->out_xfr[0].tx_buf = ihcd->out_buf;
+ ihcd->out_xfr[0].len = 1;
+
+ return 0;
+
+out:
+ return -ENOMEM;
+}
+
+static int ice40_dbg_cmd_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, NULL, inode->i_private);
+}
+
+static ssize_t ice40_dbg_cmd_write(struct file *file, const char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct seq_file *s = file->private_data;
+ struct ice40_hcd *ihcd = s->private;
+ char buf[32];
+ int ret;
+ u8 status, addr;
+
+ memset(buf, 0x00, sizeof(buf));
+
+ if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) {
+ ret = -EFAULT;
+ goto out;
+ }
+
+ if (!strcmp(buf, "poll")) {
+ if (!HCD_RH_RUNNING(ihcd->hcd)) {
+ ret = -EAGAIN;
+ goto out;
+ }
+ /*
+ * The bridge chip supports interrupt for device
+ * connect and disconnect. We don;t have a real
+ * use case of connect/disconnect. This debugfs
+ * interface provides a way to enumerate the
+ * attached device.
+ */
+ ice40_spi_reg_write(ihcd, ihcd->ctrl0 |
+ DET_BUS_CTRL, CTRL0_REG);
+ ice40_handshake(ihcd, CTRL0_REG, DET_BUS_CTRL, 0, 5000);
+ status = ice40_spi_reg_read(ihcd, XFRST_REG);
+ if ((status & DPST)) {
+ ihcd->port_flags |= USB_PORT_STAT_CONNECTION;
+ ihcd->port_flags |= USB_PORT_STAT_C_CONNECTION << 16;
+ ihcd->pcd_pending = true;
+ usb_hcd_poll_rh_status(ihcd->hcd);
+ } else if (ihcd->port_flags & USB_PORT_STAT_CONNECTION) {
+ ihcd->port_flags &= ~USB_PORT_STAT_ENABLE;
+ ihcd->port_flags &= ~USB_PORT_STAT_CONNECTION;
+ ihcd->port_flags |= (USB_PORT_STAT_C_CONNECTION << 16);
+ ihcd->pcd_pending = true;
+ usb_hcd_poll_rh_status(ihcd->hcd);
+ }
+ } else if (!strcmp(buf, "rwtest")) {
+ ihcd->devnum = 1;
+ ice40_spi_reg_write(ihcd, 0x1, FADDR_REG);
+ addr = ice40_spi_reg_read(ihcd, FADDR_REG);
+ pr_info("addr written was 0x1 read as %x\n", addr);
+ } else if (!strcmp(buf, "force_disconnect")) {
+ if (!HCD_RH_RUNNING(ihcd->hcd)) {
+ ret = -EAGAIN;
+ goto out;
+ }
+ /*
+ * Forcfully disconnect the device. This is required
+ * for simulating the disconnect on a USB port which
+ * does not have pull-down resistors.
+ */
+ ihcd->port_flags &= ~USB_PORT_STAT_ENABLE;
+ ihcd->port_flags &= ~USB_PORT_STAT_CONNECTION;
+ ihcd->port_flags |= (USB_PORT_STAT_C_CONNECTION << 16);
+ ihcd->pcd_pending = true;
+ usb_hcd_poll_rh_status(ihcd->hcd);
+ } else if (!strcmp(buf, "config_test")) {
+ ice40_spi_power_off(ihcd);
+ ret = ice40_spi_load_fw(ihcd);
+ if (ret) {
+ pr_err("config load failed\n");
+ goto out;
+ }
+ } else {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = count;
+out:
+ return ret;
+}
+
+const struct file_operations ice40_dbg_cmd_ops = {
+ .open = ice40_dbg_cmd_open,
+ .write = ice40_dbg_cmd_write,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int ice40_debugfs_init(struct ice40_hcd *ihcd)
+{
+ struct dentry *dir;
+ int ret = 0;
+
+ dir = debugfs_create_dir("ice40_hcd", NULL);
+
+ if (!dir || IS_ERR(dir)) {
+ ret = -ENODEV;
+ goto out;
+ }
+
+ ihcd->dbg_root = dir;
+
+ dir = debugfs_create_file("command", S_IWUSR, ihcd->dbg_root, ihcd,
+ &ice40_dbg_cmd_ops);
+
+ if (!dir) {
+ debugfs_remove_recursive(ihcd->dbg_root);
+ ihcd->dbg_root = NULL;
+ ret = -ENODEV;
+ }
+
+out:
+ return ret;
+}
+
+static int ice40_spi_probe(struct spi_device *spi)
+{
+ struct ice40_hcd *ihcd;
+ int ret;
+
+ ihcd = devm_kzalloc(&spi->dev, sizeof(*ihcd), GFP_KERNEL);
+ if (!ihcd) {
+ pr_err("fail to allocate ihcd\n");
+ ret = -ENOMEM;
+ goto out;
+ }
+ ihcd->spi = spi;
+
+ ret = ice40_spi_parse_dt(ihcd);
+ if (ret) {
+ pr_err("fail to parse dt node\n");
+ goto out;
+ }
+
+ ret = ice40_spi_init_regulators(ihcd);
+ if (ret) {
+ pr_err("fail to init regulators\n");
+ goto out;
+ }
+
+ ret = ice40_spi_request_gpios(ihcd);
+ if (ret) {
+ pr_err("fail to request gpios\n");
+ goto out;
+ }
+
+ spin_lock_init(&ihcd->lock);
+ INIT_LIST_HEAD(&ihcd->async_list);
+ INIT_WORK(&ihcd->async_work, ice40_async_work);
+ mutex_init(&ihcd->wlock);
+ mutex_init(&ihcd->rlock);
+
+ /*
+ * Enable all our trace points. Useful in debugging card
+ * enumeration issues.
+ */
+ ret = trace_set_clr_event(__stringify(TRACE_SYSTEM), NULL, 1);
+ if (ret < 0)
+ pr_err("fail to enable trace points with %d\n", ret);
+
+ ihcd->wq = create_singlethread_workqueue("ice40_wq");
+ if (!ihcd->wq) {
+ pr_err("fail to create workqueue\n");
+ ret = -ENOMEM;
+ goto destroy_mutex;
+ }
+
+ ret = ice40_spi_init_xfrs(ihcd);
+ if (ret) {
+ pr_err("fail to init spi xfrs %d\n", ret);
+ goto destroy_wq;
+ }
+
+ ret = ice40_spi_cache_fw(ihcd);
+ if (ret) {
+ pr_err("fail to cache fw %d\n", ret);
+ goto destroy_wq;
+ }
+
+ ret = ice40_spi_load_fw(ihcd);
+ if (ret) {
+ pr_err("fail to load fw %d\n", ret);
+ goto destroy_wq;
+ }
+
+ ihcd->hcd = usb_create_hcd(&ice40_hc_driver, &spi->dev, "ice40");
+ if (!ihcd->hcd) {
+ pr_err("fail to alloc hcd\n");
+ ret = -ENOMEM;
+ goto power_off;
+ }
+ *((struct ice40_hcd **) ihcd->hcd->hcd_priv) = ihcd;
+
+ ret = usb_add_hcd(ihcd->hcd, 0, 0);
+
+ if (ret < 0) {
+ pr_err("fail to add HCD\n");
+ goto put_hcd;
+ }
+
+ ice40_debugfs_init(ihcd);
+
+ /*
+ * We manage the power states of the bridge chip
+ * as part of root hub suspend/resume. We don't
+ * need to implement any additional runtime PM
+ * methods.
+ */
+ pm_runtime_no_callbacks(&spi->dev);
+ pm_runtime_set_active(&spi->dev);
+ pm_runtime_enable(&spi->dev);
+
+ /*
+ * This does not mean bridge chip can wakeup the
+ * system from sleep. It's activity can prevent
+ * or abort the system sleep. The device_init_wakeup
+ * creates the wakeup source for us which we will
+ * use to control system sleep.
+ */
+ device_init_wakeup(&spi->dev, 1);
+ pm_stay_awake(&spi->dev);
+
+ pr_debug("success\n");
+
+ return 0;
+
+put_hcd:
+ usb_put_hcd(ihcd->hcd);
+power_off:
+ ice40_spi_power_off(ihcd);
+destroy_wq:
+ destroy_workqueue(ihcd->wq);
+destroy_mutex:
+ mutex_destroy(&ihcd->rlock);
+ mutex_destroy(&ihcd->wlock);
+out:
+ pr_info("ice40_spi_probe failed\n");
+ return ret;
+}
+
+static int ice40_spi_remove(struct spi_device *spi)
+{
+ struct usb_hcd *hcd = spi_get_drvdata(spi);
+ struct ice40_hcd *ihcd = hcd_to_ihcd(hcd);
+
+ debugfs_remove_recursive(ihcd->dbg_root);
+
+ usb_remove_hcd(hcd);
+ usb_put_hcd(hcd);
+ destroy_workqueue(ihcd->wq);
+ ice40_spi_power_off(ihcd);
+
+ pm_runtime_disable(&spi->dev);
+ pm_relax(&spi->dev);
+
+ return 0;
+}
+
+static struct of_device_id ice40_spi_of_match_table[] = {
+ { .compatible = "lattice,ice40-spi-usb", },
+ {},
+};
+
+static struct spi_driver ice40_spi_driver = {
+ .driver = {
+ .name = "ice40_spi",
+ .owner = THIS_MODULE,
+ .of_match_table = ice40_spi_of_match_table,
+ },
+ .probe = ice40_spi_probe,
+ .remove = ice40_spi_remove,
+};
+
+module_spi_driver(ice40_spi_driver);
+
+MODULE_DESCRIPTION("ICE40 FPGA based SPI-USB bridge HCD");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/otg/msm_otg.c b/drivers/usb/otg/msm_otg.c
index 15a9ab0..350fd41 100644
--- a/drivers/usb/otg/msm_otg.c
+++ b/drivers/usb/otg/msm_otg.c
@@ -1381,6 +1381,16 @@
return -ENXIO;
}
+static void msm_otg_set_online_status(struct msm_otg *motg)
+{
+ if (!psy)
+ dev_dbg(motg->phy.dev, "no usb power supply registered\n");
+
+ /* Set power supply online status to false */
+ if (power_supply_set_online(psy, false))
+ dev_dbg(motg->phy.dev, "error setting power supply property\n");
+}
+
static void msm_otg_notify_charger(struct msm_otg *motg, unsigned mA)
{
struct usb_gadget *g = motg->phy.otg->gadget;
@@ -1400,6 +1410,13 @@
"Failed notifying %d charger type to PMIC\n",
motg->chg_type);
+ /*
+ * This condition will be true when usb cable is disconnected
+ * during bootup before charger detection mechanism starts.
+ */
+ if (motg->online && motg->cur_power == 0 && mA == 0)
+ msm_otg_set_online_status(motg);
+
if (motg->cur_power == mA)
return;
@@ -2683,6 +2700,7 @@
/* Turn off VDP_SRC */
ulpi_write(otg->phy, 0x2, 0x86);
}
+ msm_chg_block_off(motg);
msm_otg_reset(otg->phy);
/*
* There is a small window where ID interrupt
diff --git a/drivers/video/msm/mdss/dsi_host_v2.c b/drivers/video/msm/mdss/dsi_host_v2.c
index 884f7f2..e62cc59 100644
--- a/drivers/video/msm/mdss/dsi_host_v2.c
+++ b/drivers/video/msm/mdss/dsi_host_v2.c
@@ -203,6 +203,13 @@
struct mdss_dsi_ctrl_pdata *ctrl =
(struct mdss_dsi_ctrl_pdata *)ptr;
+ spin_lock(&ctrl->mdp_lock);
+
+ if (ctrl->dsi_irq_mask == 0) {
+ spin_unlock(&ctrl->mdp_lock);
+ return IRQ_HANDLED;
+ }
+
isr = MIPI_INP(dsi_host_private->dsi_base + DSI_INT_CTRL);
MIPI_OUTP(dsi_host_private->dsi_base + DSI_INT_CTRL, isr);
@@ -213,22 +220,20 @@
msm_dsi_error(dsi_host_private->dsi_base);
}
- spin_lock(&ctrl->mdp_lock);
-
if (isr & DSI_INTR_VIDEO_DONE)
complete(&ctrl->video_comp);
if (isr & DSI_INTR_CMD_DMA_DONE)
complete(&ctrl->dma_comp);
- spin_unlock(&ctrl->mdp_lock);
-
if (isr & DSI_INTR_BTA_DONE)
complete(&ctrl->bta_comp);
if (isr & DSI_INTR_CMD_MDP_DONE)
complete(&ctrl->mdp_comp);
+ spin_unlock(&ctrl->mdp_lock);
+
return IRQ_HANDLED;
}
@@ -236,6 +241,13 @@
struct mdss_dsi_ctrl_pdata *ctrl)
{
int ret;
+ u32 isr;
+
+ msm_dsi_ahb_ctrl(1);
+ isr = MIPI_INP(dsi_host_private->dsi_base + DSI_INT_CTRL);
+ isr &= ~DSI_INTR_ALL_MASK;
+ MIPI_OUTP(dsi_host_private->dsi_base + DSI_INT_CTRL, isr);
+ msm_dsi_ahb_ctrl(0);
ret = devm_request_irq(dev, irq_no, msm_dsi_isr_handler,
IRQF_DISABLED, "DSI", ctrl);
@@ -1510,14 +1522,6 @@
__func__, __LINE__);
rc = -ENODEV;
goto error_irq_resource;
- } else {
- rc = msm_dsi_irq_init(&pdev->dev, mdss_dsi_mres->start,
- ctrl_pdata);
- if (rc) {
- dev_err(&pdev->dev, "%s: failed to init irq, rc=%d\n",
- __func__, rc);
- goto error_irq_resource;
- }
}
rc = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
@@ -1580,6 +1584,14 @@
msm_dsi_ctrl_init(ctrl_pdata);
+ rc = msm_dsi_irq_init(&pdev->dev, mdss_dsi_mres->start,
+ ctrl_pdata);
+ if (rc) {
+ dev_err(&pdev->dev, "%s: failed to init irq, rc=%d\n",
+ __func__, rc);
+ goto error_device_register;
+ }
+
rc = dsi_panel_device_register_v2(pdev, ctrl_pdata);
if (rc) {
pr_err("%s: dsi panel dev reg failed\n", __func__);
diff --git a/drivers/video/msm/mdss/dsi_host_v2.h b/drivers/video/msm/mdss/dsi_host_v2.h
index b297452..0f3ea8d 100644
--- a/drivers/video/msm/mdss/dsi_host_v2.h
+++ b/drivers/video/msm/mdss/dsi_host_v2.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
*
* This 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 @@
#define DSI_INTR_CMD_MDP_DONE BIT(8)
#define DSI_INTR_CMD_DMA_DONE_MASK BIT(1)
#define DSI_INTR_CMD_DMA_DONE BIT(0)
+#define DSI_INTR_ALL_MASK 0x2220202
#define DSI_BTA_TERM BIT(1)
diff --git a/drivers/video/msm/mdss/dsi_status_v2.c b/drivers/video/msm/mdss/dsi_status_v2.c
index d62ddf3..565401d 100644
--- a/drivers/video/msm/mdss/dsi_status_v2.c
+++ b/drivers/video/msm/mdss/dsi_status_v2.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
*
* This 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,19 @@
mdp3_session = pdsi_status->mfd->mdp.private1;
mutex_lock(&mdp3_session->lock);
- ret = ctrl_pdata->check_status(ctrl_pdata);
+ if (!mdp3_session->status) {
+ pr_info("display off already\n");
+ mutex_unlock(&mdp3_session->lock);
+ return;
+ }
+
+ if (mdp3_session->wait_for_dma_done)
+ ret = mdp3_session->wait_for_dma_done(mdp3_session);
+
+ if (!ret)
+ ret = ctrl_pdata->check_status(ctrl_pdata);
+ else
+ pr_err("wait_for_dma_done error\n");
mutex_unlock(&mdp3_session->lock);
diff --git a/drivers/video/msm/mdss/mdp3_ctrl.c b/drivers/video/msm/mdss/mdp3_ctrl.c
index b324130..6ebe06a 100644
--- a/drivers/video/msm/mdss/mdp3_ctrl.c
+++ b/drivers/video/msm/mdss/mdp3_ctrl.c
@@ -157,6 +157,7 @@
{
struct mdp3_session_data *session = (struct mdp3_session_data *)arg;
schedule_work(&session->dma_done_work);
+ complete(&session->dma_completion);
}
void vsync_count_down(void *arg)
@@ -705,6 +706,7 @@
mdp3_session->vsync_enabled = 0;
atomic_set(&mdp3_session->vsync_countdown, 0);
mdp3_session->clk_on = 0;
+ mdp3_session->in_splash_screen = 0;
off_error:
mdp3_session->status = 0;
mdp3_bufq_deinit(&mdp3_session->bufq_out);
@@ -757,6 +759,7 @@
mdp3_dma->vsync_enable(mdp3_dma, &vsync_client);
mdp3_session->first_commit = true;
+ mdp3_session->in_splash_screen = 0;
reset_error:
mutex_unlock(&mdp3_session->lock);
@@ -798,7 +801,7 @@
rc = mdp3_dma->stop(mdp3_dma, mdp3_session->intf);
if (rc) {
- pr_err("fail to stop the MDP3 dma\n");
+ pr_err("fail to stop the MDP3 dma %d\n", rc);
goto reset_error;
}
@@ -845,6 +848,7 @@
mdp3_dma->vsync_enable(mdp3_dma, &vsync_client);
mdp3_session->first_commit = true;
+ mdp3_session->in_splash_screen = 0;
reset_error:
mutex_unlock(&mdp3_session->lock);
@@ -897,9 +901,7 @@
dma->source_config.stride = stride;
dma->output_config.pack_pattern =
mdp3_ctrl_get_pack_pattern(req->src.format);
- mdp3_clk_enable(1, 0);
- mdp3_session->dma->dma_config_source(dma);
- mdp3_clk_enable(0, 0);
+ dma->update_src_cfg = true;
}
mdp3_session->overlay.id = 1;
req->id = 1;
@@ -923,14 +925,6 @@
mutex_lock(&mdp3_session->lock);
if (mdp3_session->overlay.id == ndx && ndx == 1) {
- struct mdp3_dma *dma = mdp3_session->dma;
- dma->source_config.format = format;
- dma->source_config.stride = fix->line_length;
- dma->output_config.pack_pattern =
- mdp3_ctrl_get_pack_pattern(mfd->fb_imgType);
- mdp3_clk_enable(1, 0);
- mdp3_session->dma->dma_config_source(dma);
- mdp3_clk_enable(0, 0);
mdp3_session->overlay.id = MSMFB_NEW_REQUEST;
mdp3_bufq_deinit(&mdp3_session->bufq_in);
} else {
@@ -1009,9 +1003,13 @@
}
panel = mdp3_session->panel;
- if (!mdp3_iommu_is_attached(MDP3_CLIENT_DMA_P)) {
+ if (mdp3_session->in_splash_screen) {
pr_debug("continuous splash screen, IOMMU not attached\n");
- mdp3_ctrl_reset(mfd);
+ rc = mdp3_ctrl_reset(mfd);
+ if (rc) {
+ pr_err("fail to reset display\n");
+ return -EINVAL;
+ }
reset_done = true;
}
@@ -1042,7 +1040,8 @@
MDP_NOTIFY_FRAME_DONE);
}
}
-
+ mdp3_session->dma_active = 1;
+ init_completion(&mdp3_session->dma_completion);
mdp3_ctrl_notify(mdp3_session, MDP_NOTIFY_FRAME_FLUSHED);
mdp3_bufq_push(&mdp3_session->bufq_out, data);
}
@@ -1090,9 +1089,13 @@
if (!mdp3_session || !mdp3_session->dma)
return;
- if (!mdp3_iommu_is_attached(MDP3_CLIENT_DMA_P)) {
+ if (mdp3_session->in_splash_screen) {
pr_debug("continuous splash screen, IOMMU not attached\n");
- mdp3_ctrl_reset(mfd);
+ rc = mdp3_ctrl_reset(mfd);
+ if (rc) {
+ pr_err("fail to reset display\n");
+ return;
+ }
}
mutex_lock(&mdp3_session->lock);
@@ -1132,6 +1135,8 @@
MDP_NOTIFY_FRAME_DONE);
}
}
+ mdp3_session->dma_active = 1;
+ init_completion(&mdp3_session->dma_completion);
mdp3_ctrl_notify(mdp3_session, MDP_NOTIFY_FRAME_FLUSHED);
} else {
pr_debug("mdp3_ctrl_pan_display no memory, stop interface");
@@ -1753,6 +1758,23 @@
return rc;
}
+int mdp3_wait_for_dma_done(struct mdp3_session_data *session)
+{
+ int rc = 0;
+
+ if (session->dma_active) {
+ rc = wait_for_completion_timeout(&session->dma_completion,
+ KOFF_TIMEOUT);
+ if (rc > 0) {
+ session->dma_active = 0;
+ rc = 0;
+ } else if (rc == 0) {
+ rc = -ETIME;
+ }
+ }
+ return rc;
+}
+
int mdp3_ctrl_init(struct msm_fb_data_type *mfd)
{
struct device *dev = mfd->fbi->dev;
@@ -1827,6 +1849,9 @@
mdp3_session->vsync_timer.data = (u32)mdp3_session;
mdp3_session->vsync_period = 1000 / mfd->panel_info->mipi.frame_rate;
mfd->mdp.private1 = mdp3_session;
+ init_completion(&mdp3_session->dma_completion);
+ if (intf_type != MDP3_DMA_OUTPUT_SEL_DSI_VIDEO)
+ mdp3_session->wait_for_dma_done = mdp3_wait_for_dma_done;
rc = sysfs_create_group(&dev->kobj, &vsync_fs_attr_group);
if (rc) {
@@ -1851,6 +1876,7 @@
if (mdp3_get_cont_spash_en()) {
mdp3_session->clk_on = 1;
+ mdp3_session->in_splash_screen = 1;
mdp3_ctrl_notifier_register(mdp3_session,
&mdp3_session->mfd->mdp_sync_pt_data.notifier);
}
diff --git a/drivers/video/msm/mdss/mdp3_ctrl.h b/drivers/video/msm/mdss/mdp3_ctrl.h
index cfad1d3..20649fc 100644
--- a/drivers/video/msm/mdss/mdp3_ctrl.h
+++ b/drivers/video/msm/mdss/mdp3_ctrl.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -61,6 +61,11 @@
int vsync_enabled;
atomic_t vsync_countdown; /* Used to count down */
+ bool in_splash_screen;
+
+ bool dma_active;
+ struct completion dma_completion;
+ int (*wait_for_dma_done)(struct mdp3_session_data *session);
};
int mdp3_ctrl_init(struct msm_fb_data_type *mfd);
diff --git a/drivers/video/msm/mdss/mdp3_dma.c b/drivers/video/msm/mdss/mdp3_dma.c
index 800c4b3..8a13de2 100644
--- a/drivers/video/msm/mdss/mdp3_dma.c
+++ b/drivers/video/msm/mdss/mdp3_dma.c
@@ -18,7 +18,7 @@
#include "mdp3_hwio.h"
#define DMA_STOP_POLL_SLEEP_US 1000
-#define DMA_STOP_POLL_TIMEOUT_US 32000
+#define DMA_STOP_POLL_TIMEOUT_US 200000
#define DMA_HISTO_RESET_TIMEOUT_MS 40
#define DMA_LUT_CONFIG_MASK 0xfffffbe8
#define DMA_CCS_CONFIG_MASK 0xfffffc17
@@ -605,6 +605,13 @@
}
}
}
+ if (dma->update_src_cfg) {
+ if (dma->output_config.out_sel ==
+ MDP3_DMA_OUTPUT_SEL_DSI_VIDEO && intf->active)
+ pr_err("configuring dma source while dma is active\n");
+ dma->dma_config_source(dma);
+ dma->update_src_cfg = false;
+ }
spin_lock_irqsave(&dma->dma_lock, flag);
MDP3_REG_WRITE(MDP3_REG_DMA_P_IBUF_ADDR, (u32)buf);
dma->source_config.buf = buf;
@@ -961,6 +968,7 @@
dma->vsync_client.handler = NULL;
dma->vsync_client.arg = NULL;
dma->histo_state = MDP3_DMA_HISTO_STATE_IDLE;
+ dma->update_src_cfg = false;
memset(&dma->cursor, 0, sizeof(dma->cursor));
memset(&dma->ccs_config, 0, sizeof(dma->ccs_config));
diff --git a/drivers/video/msm/mdss/mdp3_dma.h b/drivers/video/msm/mdss/mdp3_dma.h
index 207168f..80ebb9b 100644
--- a/drivers/video/msm/mdss/mdp3_dma.h
+++ b/drivers/video/msm/mdss/mdp3_dma.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -259,6 +259,7 @@
int histo_state;
struct mdp3_dma_histogram_data histo_data;
unsigned int vsync_status;
+ bool update_src_cfg;
int (*dma_config)(struct mdp3_dma *dma,
struct mdp3_dma_source *source_config,
diff --git a/drivers/video/msm/mdss/mdss_dsi.c b/drivers/video/msm/mdss/mdss_dsi.c
index 7b4b065..6de0df6 100644
--- a/drivers/video/msm/mdss/mdss_dsi.c
+++ b/drivers/video/msm/mdss/mdss_dsi.c
@@ -321,7 +321,7 @@
ctrl_pdata, ctrl_pdata->ndx);
if (pdata->panel_info.type == MIPI_CMD_PANEL)
- mdss_dsi_clk_ctrl(ctrl_pdata, 1);
+ mdss_dsi_clk_ctrl(ctrl_pdata, DSI_ALL_CLKS, 1);
/* disable DSI controller */
mdss_dsi_controller_cfg(0, pdata);
@@ -329,7 +329,7 @@
/* disable DSI phy */
mdss_dsi_phy_disable(ctrl_pdata);
- mdss_dsi_clk_ctrl(ctrl_pdata, 0);
+ mdss_dsi_clk_ctrl(ctrl_pdata, DSI_ALL_CLKS, 0);
ret = mdss_dsi_panel_power_on(pdata, 0);
if (ret) {
@@ -465,14 +465,20 @@
goto error;
}
- if (__mdss_dsi_clk_enabled(ctrl_pdata)) {
+ if (__mdss_dsi_clk_enabled(ctrl_pdata, DSI_LINK_CLKS)) {
pr_err("%s: cannot enter ulps mode if dsi clocks are on\n",
__func__);
ret = -EPERM;
goto error;
}
- mdss_dsi_clk_ctrl(ctrl_pdata, 1);
+ ret = mdss_dsi_clk_ctrl(ctrl_pdata, DSI_ALL_CLKS, 1);
+ if (ret) {
+ pr_err("%s: Failed to enable clocks. rc=%d\n",
+ __func__, ret);
+ goto error;
+ }
+
/*
* ULPS Entry Request.
* Wait for a short duration to ensure that the lanes
@@ -480,6 +486,14 @@
*/
MIPI_OUTP(ctrl_pdata->ctrl_base + 0x0AC, 0x01F);
usleep(100);
+ lane_status = MIPI_INP(ctrl_pdata->ctrl_base + 0xA8);
+ if (lane_status & 0x1F00) {
+ pr_err("%s: ULPS entry req failed. Lane status=0x%08x\n",
+ __func__, lane_status);
+ ret = -EINVAL;
+ mdss_dsi_clk_ctrl(ctrl_pdata, DSI_ALL_CLKS, 0);
+ goto error;
+ }
/* Enable MMSS DSI Clamps */
MIPI_OUTP(ctrl_pdata->mmss_misc_io.base + 0x14, 0x3FF);
@@ -491,10 +505,17 @@
/* disable DSI controller */
mdss_dsi_controller_cfg(0, pdata);
- lane_status = MIPI_INP(ctrl_pdata->ctrl_base + 0xA8),
- mdss_dsi_clk_ctrl(ctrl_pdata, 0);
+ mdss_dsi_clk_ctrl(ctrl_pdata, DSI_ALL_CLKS, 0);
ctrl_pdata->ulps = true;
} else if (ctrl_pdata->ulps) {
+ ret = mdss_dsi_clk_ctrl(ctrl_pdata, DSI_BUS_CLKS, 1);
+ if (ret) {
+ pr_err("%s: Failed to enable bus clocks. rc=%d\n",
+ __func__, ret);
+ goto error;
+ }
+
+ MIPI_OUTP(ctrl_pdata->mmss_misc_io.base + 0x108, 0x0);
mdss_dsi_phy_init(pdata);
__mdss_dsi_ctrl_setup(pdata);
@@ -503,9 +524,28 @@
mdss_dsi_op_mode_config(pdata->panel_info.mipi.mode,
pdata);
+ /*
+ * ULPS Entry Request. This is needed because, after power
+ * collapse and reset, the DSI controller resets back to
+ * idle state and not ULPS.
+ * Wait for a short duration to ensure that the lanes
+ * enter ULP state.
+ */
+ MIPI_OUTP(ctrl_pdata->ctrl_base + 0x0AC, 0x01F);
+ usleep(100);
+
/* Disable MMSS DSI Clamps */
+ MIPI_OUTP(ctrl_pdata->mmss_misc_io.base + 0x14, 0x3FF);
MIPI_OUTP(ctrl_pdata->mmss_misc_io.base + 0x14, 0x0);
+ ret = mdss_dsi_clk_ctrl(ctrl_pdata, DSI_LINK_CLKS, 1);
+ if (ret) {
+ pr_err("%s: Failed to enable link clocks. rc=%d\n",
+ __func__, ret);
+ mdss_dsi_clk_ctrl(ctrl_pdata, DSI_BUS_CLKS, 0);
+ goto error;
+ }
+
/*
* ULPS Exit Request
* Hardware requirement is to wait for at least 1ms
@@ -520,7 +560,9 @@
*/
usleep(100);
- lane_status = MIPI_INP(ctrl_pdata->ctrl_base + 0xA8),
+ lane_status = MIPI_INP(ctrl_pdata->ctrl_base + 0xA8);
+ mdss_dsi_clk_ctrl(ctrl_pdata, DSI_LINK_CLKS, 0);
+ mdss_dsi_clk_ctrl(ctrl_pdata, DSI_BUS_CLKS, 0);
ctrl_pdata->ulps = false;
}
@@ -535,21 +577,31 @@
int enable)
{
int rc;
- struct mdss_dsi_ctrl_pdata *sctrl = NULL;
+ struct mdss_dsi_ctrl_pdata *mctrl = NULL;
- if (ctrl->flags & DSI_FLAG_CLOCK_MASTER)
- sctrl = mdss_dsi_ctrl_slave(ctrl);
+ if (&ctrl->mmss_misc_io == NULL) {
+ pr_err("%s: mmss_misc_io is NULL. ULPS not valid\n", __func__);
+ return -EINVAL;
+ }
- if (sctrl) {
- pr_debug("%s: configuring ulps (%s) for slave ctrl\n",
- __func__, (enable ? "on" : "off"));
- rc = mdss_dsi_ulps_config_sub(sctrl, enable);
+ if (mdss_dsi_is_slave_ctrl(ctrl)) {
+ mctrl = mdss_dsi_get_master_ctrl();
+ if (!mctrl) {
+ pr_err("%s: Unable to get master control\n", __func__);
+ return -EINVAL;
+ }
+ }
+
+ if (mctrl) {
+ pr_debug("%s: configuring ulps (%s) for master ctrl%d\n",
+ __func__, (enable ? "on" : "off"), ctrl->ndx);
+ rc = mdss_dsi_ulps_config_sub(mctrl, enable);
if (rc)
return rc;
}
- pr_debug("%s: configuring ulps (%s) for master ctrl\n",
- __func__, (enable ? "on" : "off"));
+ pr_debug("%s: configuring ulps (%s) for ctrl%d\n",
+ __func__, (enable ? "on" : "off"), ctrl->ndx);
return mdss_dsi_ulps_config_sub(ctrl, enable);
}
@@ -585,7 +637,7 @@
return ret;
}
- ret = mdss_dsi_bus_clk_start(ctrl_pdata);
+ mdss_dsi_clk_ctrl(ctrl_pdata, DSI_BUS_CLKS, 1);
if (ret) {
pr_err("%s: failed to enable bus clocks. rc=%d\n", __func__,
ret);
@@ -602,9 +654,9 @@
mdss_dsi_phy_sw_reset((ctrl_pdata->ctrl_base));
mdss_dsi_phy_init(pdata);
- mdss_dsi_bus_clk_stop(ctrl_pdata);
+ mdss_dsi_clk_ctrl(ctrl_pdata, DSI_BUS_CLKS, 0);
- mdss_dsi_clk_ctrl(ctrl_pdata, 1);
+ mdss_dsi_clk_ctrl(ctrl_pdata, DSI_ALL_CLKS, 1);
__mdss_dsi_ctrl_setup(pdata);
mdss_dsi_sw_reset(pdata);
@@ -630,7 +682,7 @@
}
if (pdata->panel_info.type == MIPI_CMD_PANEL)
- mdss_dsi_clk_ctrl(ctrl_pdata, 0);
+ mdss_dsi_clk_ctrl(ctrl_pdata, DSI_ALL_CLKS, 0);
pr_debug("%s-:\n", __func__);
return 0;
@@ -834,8 +886,8 @@
MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x0004,
dsi_ctrl);
mdss_dsi_controller_cfg(true, pdata);
- mdss_dsi_clk_ctrl(ctrl_pdata, 0);
- mdss_dsi_clk_ctrl(ctrl_pdata, 1);
+ mdss_dsi_clk_ctrl(ctrl_pdata, DSI_ALL_CLKS, 0);
+ mdss_dsi_clk_ctrl(ctrl_pdata, DSI_ALL_CLKS, 1);
dsi_ctrl |= 0x2;
MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x0004,
dsi_ctrl);
@@ -1190,6 +1242,8 @@
mdss_dsi_put_dt_vreg_data(&pdev->dev, &ctrl_pdata->power_data);
mfd = platform_get_drvdata(pdev);
msm_dss_iounmap(&ctrl_pdata->mmss_misc_io);
+ msm_dss_iounmap(&ctrl_pdata->phy_io);
+ msm_dss_iounmap(&ctrl_pdata->ctrl_io);
return 0;
}
@@ -1200,7 +1254,6 @@
{
int rc = 0;
u32 index;
- struct resource *mdss_dsi_mres;
rc = of_property_read_u32(pdev->dev.of_node, "cell-index", &index);
if (rc) {
@@ -1228,31 +1281,32 @@
return -EPERM;
}
- mdss_dsi_mres = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!mdss_dsi_mres) {
- pr_err("%s:%d unable to get the DSI ctrl resources",
+ rc = msm_dss_ioremap_byname(pdev, &ctrl->ctrl_io, "dsi_ctrl");
+ if (rc) {
+ pr_err("%s:%d unable to remap dsi ctrl resources",
__func__, __LINE__);
- return -ENOMEM;
+ return rc;
}
- ctrl->ctrl_base = ioremap(mdss_dsi_mres->start,
- resource_size(mdss_dsi_mres));
- if (!(ctrl->ctrl_base)) {
- pr_err("%s:%d unable to remap dsi resources",
+ ctrl->ctrl_base = ctrl->ctrl_io.base;
+ ctrl->reg_size = ctrl->ctrl_io.len;
+
+ rc = msm_dss_ioremap_byname(pdev, &ctrl->phy_io, "dsi_phy");
+ if (rc) {
+ pr_err("%s:%d unable to remap dsi phy resources",
__func__, __LINE__);
- return -ENOMEM;
+ return rc;
}
- ctrl->reg_size = resource_size(mdss_dsi_mres);
-
- pr_info("%s: dsi base=%x size=%x\n",
- __func__, (int)ctrl->ctrl_base, ctrl->reg_size);
+ pr_info("%s: ctrl_base=%p ctrl_size=%x phy_base=%p phy_size=%x\n",
+ __func__, ctrl->ctrl_base, ctrl->reg_size, ctrl->phy_io.base,
+ ctrl->phy_io.len);
rc = msm_dss_ioremap_byname(pdev, &ctrl->mmss_misc_io,
"mmss_misc_phys");
if (rc) {
- pr_err("%s:%d mmss_misc IO remap failed\n", __func__, __LINE__);
- return rc;
+ pr_debug("%s:%d mmss_misc IO remap failed\n",
+ __func__, __LINE__);
}
return 0;
@@ -1492,7 +1546,7 @@
return rc;
}
- mdss_dsi_clk_ctrl(ctrl_pdata, 1);
+ mdss_dsi_clk_ctrl(ctrl_pdata, DSI_ALL_CLKS, 1);
ctrl_pdata->ctrl_state |=
(CTRL_STATE_PANEL_INIT | CTRL_STATE_MDP_ACTIVE);
} else {
diff --git a/drivers/video/msm/mdss/mdss_dsi.h b/drivers/video/msm/mdss/mdss_dsi.h
index 57b0e75..b0b884f 100644
--- a/drivers/video/msm/mdss/mdss_dsi.h
+++ b/drivers/video/msm/mdss/mdss_dsi.h
@@ -151,8 +151,8 @@
#define DSI_CMD_TERM BIT(0)
extern struct device dsi_dev;
-extern int mdss_dsi_clk_on;
extern u32 dsi_irq;
+extern struct mdss_dsi_ctrl_pdata *ctrl_list[];
struct dsiphy_pll_divider_config {
u32 clk_rate;
@@ -223,12 +223,18 @@
DSI_CTRL_MAX,
};
+/* DSI controller #0 is always treated as a master in broadcast mode */
+#define DSI_CTRL_MASTER DSI_CTRL_0
+#define DSI_CTRL_SLAVE DSI_CTRL_1
+
+#define DSI_BUS_CLKS BIT(0)
+#define DSI_LINK_CLKS BIT(1)
+#define DSI_ALL_CLKS ((DSI_BUS_CLKS) | (DSI_LINK_CLKS))
+
#define DSI_EV_PLL_UNLOCKED 0x0001
#define DSI_EV_MDP_FIFO_UNDERFLOW 0x0002
#define DSI_EV_MDP_BUSY_RELEASE 0x80000000
-#define DSI_FLAG_CLOCK_MASTER 0x80000000
-
struct mdss_dsi_ctrl_pdata {
int ndx; /* panel_num */
int (*on) (struct mdss_panel_data *pdata);
@@ -238,10 +244,12 @@
int (*cmdlist_commit)(struct mdss_dsi_ctrl_pdata *ctrl, int from_mdp);
struct mdss_panel_data panel_data;
unsigned char *ctrl_base;
+ struct dss_io_data ctrl_io;
struct dss_io_data mmss_misc_io;
+ struct dss_io_data phy_io;
int reg_size;
- u32 clk_cnt;
- int clk_cnt_sub;
+ u32 bus_clk_cnt;
+ u32 link_clk_cnt;
u32 flags;
struct clk *mdp_core_clk;
struct clk *ahb_clk;
@@ -253,7 +261,6 @@
u8 ctrl_state;
int panel_mode;
int irq_cnt;
- int mdss_dsi_clk_on;
int rst_gpio;
int disp_en_gpio;
int disp_te_gpio;
@@ -312,20 +319,14 @@
void mdss_dsi_cmd_mdp_start(struct mdss_dsi_ctrl_pdata *ctrl);
void mdss_dsi_cmd_bta_sw_trigger(struct mdss_panel_data *pdata);
void mdss_dsi_ack_err_status(struct mdss_dsi_ctrl_pdata *ctrl);
-void mdss_dsi_clk_ctrl(struct mdss_dsi_ctrl_pdata *ctrl, int enable);
-int mdss_dsi_link_clk_start(struct mdss_dsi_ctrl_pdata *ctrl);
-void mdss_dsi_link_clk_stop(struct mdss_dsi_ctrl_pdata *ctrl);
-int mdss_dsi_bus_clk_start(struct mdss_dsi_ctrl_pdata *ctrl);
-void mdss_dsi_bus_clk_stop(struct mdss_dsi_ctrl_pdata *ctrl);
+int mdss_dsi_clk_ctrl(struct mdss_dsi_ctrl_pdata *ctrl,
+ u8 clk_type, int enable);
void mdss_dsi_clk_req(struct mdss_dsi_ctrl_pdata *ctrl,
int enable);
void mdss_dsi_controller_cfg(int enable,
struct mdss_panel_data *pdata);
void mdss_dsi_sw_reset(struct mdss_panel_data *pdata);
-struct mdss_dsi_ctrl_pdata *mdss_dsi_ctrl_slave(
- struct mdss_dsi_ctrl_pdata *ctrl);
-
irqreturn_t mdss_dsi_isr(int irq, void *ptr);
void mdss_dsi_irq_handler_config(struct mdss_dsi_ctrl_pdata *ctrl_pdata);
@@ -351,9 +352,52 @@
int mdss_dsi_cmdlist_commit(struct mdss_dsi_ctrl_pdata *ctrl, int from_mdp);
void mdss_dsi_cmdlist_kickoff(int intf);
int mdss_dsi_bta_status_check(struct mdss_dsi_ctrl_pdata *ctrl);
-bool __mdss_dsi_clk_enabled(struct mdss_dsi_ctrl_pdata *ctrl);
+bool __mdss_dsi_clk_enabled(struct mdss_dsi_ctrl_pdata *ctrl, u8 clk_type);
int mdss_dsi_panel_init(struct device_node *node,
struct mdss_dsi_ctrl_pdata *ctrl_pdata,
bool cmd_cfg_cont_splash);
+
+static inline bool mdss_dsi_broadcast_mode_enabled(void)
+{
+ return ctrl_list[DSI_CTRL_MASTER]->shared_pdata.broadcast_enable &&
+ ctrl_list[DSI_CTRL_SLAVE] &&
+ ctrl_list[DSI_CTRL_SLAVE]->shared_pdata.broadcast_enable;
+}
+
+static inline struct mdss_dsi_ctrl_pdata *mdss_dsi_get_master_ctrl(void)
+{
+ if (mdss_dsi_broadcast_mode_enabled())
+ return ctrl_list[DSI_CTRL_MASTER];
+ else
+ return NULL;
+}
+
+static inline struct mdss_dsi_ctrl_pdata *mdss_dsi_get_slave_ctrl(void)
+{
+ if (mdss_dsi_broadcast_mode_enabled())
+ return ctrl_list[DSI_CTRL_SLAVE];
+ else
+ return NULL;
+}
+
+static inline bool mdss_dsi_is_master_ctrl(struct mdss_dsi_ctrl_pdata *ctrl)
+{
+ return mdss_dsi_broadcast_mode_enabled() &&
+ (ctrl->ndx == DSI_CTRL_MASTER);
+}
+
+static inline bool mdss_dsi_is_slave_ctrl(struct mdss_dsi_ctrl_pdata *ctrl)
+{
+ return mdss_dsi_broadcast_mode_enabled() &&
+ (ctrl->ndx == DSI_CTRL_SLAVE);
+}
+
+static inline struct mdss_dsi_ctrl_pdata *mdss_dsi_get_ctrl_by_index(int ndx)
+{
+ if (ndx >= DSI_CTRL_MAX)
+ return NULL;
+
+ return ctrl_list[ndx];
+}
#endif /* MDSS_DSI_H */
diff --git a/drivers/video/msm/mdss/mdss_dsi_host.c b/drivers/video/msm/mdss/mdss_dsi_host.c
index c2fbc1a..a570914 100644
--- a/drivers/video/msm/mdss/mdss_dsi_host.c
+++ b/drivers/video/msm/mdss/mdss_dsi_host.c
@@ -29,10 +29,7 @@
#define VSYNC_PERIOD 17
-static struct mdss_dsi_ctrl_pdata *left_ctrl_pdata;
-
-static struct mdss_dsi_ctrl_pdata *ctrl_list[DSI_CTRL_MAX];
-
+struct mdss_dsi_ctrl_pdata *ctrl_list[DSI_CTRL_MAX];
struct mdss_hw mdss_dsi0_hw = {
.hw_ndx = MDSS_HW_DSI0,
@@ -72,14 +69,6 @@
void mdss_dsi_ctrl_init(struct mdss_dsi_ctrl_pdata *ctrl)
{
- if (ctrl->shared_pdata.broadcast_enable)
- if (ctrl->panel_data.panel_info.pdest
- == DISPLAY_1) {
- pr_debug("%s: Broadcast mode enabled.\n",
- __func__);
- left_ctrl_pdata = ctrl;
- }
-
if (ctrl->panel_data.panel_info.pdest == DISPLAY_1) {
mdss_dsi0_hw.ptr = (void *)(ctrl);
ctrl->dsi_hw = &mdss_dsi0_hw;
@@ -90,11 +79,9 @@
ctrl->ndx = DSI_CTRL_1;
}
- ctrl_list[ctrl->ndx] = ctrl; /* keep it */
+ ctrl->panel_mode = ctrl->panel_data.panel_info.mipi.mode;
- if (ctrl->shared_pdata.broadcast_enable)
- if (ctrl->ndx == DSI_CTRL_1)
- ctrl->flags |= DSI_FLAG_CLOCK_MASTER;
+ ctrl_list[ctrl->ndx] = ctrl; /* keep it */
if (mdss_register_irq(ctrl->dsi_hw))
pr_err("%s: mdss_register_irq failed.\n", __func__);
@@ -121,22 +108,6 @@
}
}
-struct mdss_dsi_ctrl_pdata *mdss_dsi_ctrl_slave(
- struct mdss_dsi_ctrl_pdata *ctrl)
-{
- int ndx;
- struct mdss_dsi_ctrl_pdata *sctrl = NULL;
-
- /* only two controllers */
- ndx = ctrl->ndx;
- ndx += 1;
- ndx %= DSI_CTRL_MAX;
- sctrl = ctrl_list[ndx];
-
- return sctrl;
-
-}
-
void mdss_dsi_clk_req(struct mdss_dsi_ctrl_pdata *ctrl, int enable)
{
if (enable == 0) {
@@ -146,22 +117,26 @@
mutex_unlock(&ctrl->cmd_mutex);
}
- mdss_dsi_clk_ctrl(ctrl, enable);
+ mdss_dsi_clk_ctrl(ctrl, DSI_ALL_CLKS, enable);
}
void mdss_dsi_pll_relock(struct mdss_dsi_ctrl_pdata *ctrl)
{
int i, cnt;
- cnt = ctrl->clk_cnt;
+ /*
+ * todo: this code does not work very well with dual
+ * dsi use cases. Need to fix this eventually.
+ */
+ cnt = ctrl->link_clk_cnt;
/* disable dsi clk */
for (i = 0; i < cnt; i++)
- mdss_dsi_clk_ctrl(ctrl, 0);
+ mdss_dsi_clk_ctrl(ctrl, DSI_LINK_CLKS, 0);
/* enable dsi clk */
for (i = 0; i < cnt; i++)
- mdss_dsi_clk_ctrl(ctrl, 1);
+ mdss_dsi_clk_ctrl(ctrl, DSI_LINK_CLKS, 1);
}
void mdss_dsi_enable_irq(struct mdss_dsi_ctrl_pdata *ctrl, u32 term)
@@ -269,8 +244,6 @@
pinfo->rgb_swap = DSI_RGB_SWAP_RGB;
- ctrl_pdata->panel_mode = pinfo->mode;
-
if (pinfo->mode == DSI_VIDEO_MODE) {
data = 0;
if (pinfo->pulse_mode_hsa_he)
@@ -341,7 +314,7 @@
/* from frame buffer, low power mode */
/* DSI_COMMAND_MODE_DMA_CTRL */
- if (ctrl_pdata->shared_pdata.broadcast_enable)
+ if (mdss_dsi_broadcast_mode_enabled())
MIPI_OUTP(ctrl_pdata->ctrl_base + 0x3C, 0x94000000);
else
MIPI_OUTP(ctrl_pdata->ctrl_base + 0x3C, 0x14000000);
@@ -544,6 +517,7 @@
{
u32 dsi_ctrl, intr_ctrl;
struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL;
+ struct mdss_dsi_ctrl_pdata *mctrl = NULL;
if (pdata == NULL) {
pr_err("%s: Invalid input data\n", __func__);
@@ -553,12 +527,15 @@
ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata,
panel_data);
- if (ctrl_pdata->shared_pdata.broadcast_enable)
- if (pdata->panel_info.pdest == DISPLAY_1) {
- pr_debug("%s: Broadcast mode. 1st ctrl\n",
- __func__);
- return;
- }
+ /*
+ * In broadcast mode, the configuration for master controller
+ * would be done when the slave controller is configured
+ */
+ if (mdss_dsi_is_master_ctrl(ctrl_pdata)) {
+ pr_debug("%s: Broadcast mode enabled. skipping config for ctrl%d\n",
+ __func__, ctrl_pdata->ndx);
+ return;
+ }
dsi_ctrl = MIPI_INP((ctrl_pdata->ctrl_base) + 0x0004);
/*If Video enabled, Keep Video and Cmd mode ON */
@@ -579,17 +556,22 @@
DSI_INTR_CMD_MDP_DONE_MASK | DSI_INTR_BTA_DONE_MASK;
}
- if (ctrl_pdata->shared_pdata.broadcast_enable)
- if ((pdata->panel_info.pdest == DISPLAY_2)
- && (left_ctrl_pdata != NULL)) {
- MIPI_OUTP(left_ctrl_pdata->ctrl_base + 0x0110,
- intr_ctrl); /* DSI_INTL_CTRL */
- MIPI_OUTP(left_ctrl_pdata->ctrl_base + 0x0004,
- dsi_ctrl);
+ /* Ensure that for slave controller, master is also configured */
+ if (mdss_dsi_is_slave_ctrl(ctrl_pdata)) {
+ mctrl = mdss_dsi_get_master_ctrl();
+ if (mctrl) {
+ pr_debug("%s: configuring ctrl%d\n", __func__,
+ mctrl->ndx);
+ MIPI_OUTP(mctrl->ctrl_base + 0x0110, intr_ctrl);
+ MIPI_OUTP(mctrl->ctrl_base + 0x0004, dsi_ctrl);
+ } else {
+ pr_warn("%s: Unable to get master control\n",
+ __func__);
}
+ }
- MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x0110,
- intr_ctrl); /* DSI_INTL_CTRL */
+ pr_debug("%s: configuring ctrl%d\n", __func__, ctrl_pdata->ndx);
+ MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x0110, intr_ctrl);
MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x0004, dsi_ctrl);
wmb();
}
@@ -640,7 +622,7 @@
pr_debug("%s: Checking BTA status\n", __func__);
- mdss_dsi_clk_ctrl(ctrl_pdata, 1);
+ mdss_dsi_clk_ctrl(ctrl_pdata, DSI_ALL_CLKS, 1);
spin_lock_irqsave(&ctrl_pdata->mdp_lock, flag);
INIT_COMPLETION(ctrl_pdata->bta_comp);
mdss_dsi_enable_irq(ctrl_pdata, DSI_BTA_TERM);
@@ -655,7 +637,7 @@
pr_err("%s: DSI BTA error: %i\n", __func__, ret);
}
- mdss_dsi_clk_ctrl(ctrl_pdata, 0);
+ mdss_dsi_clk_ctrl(ctrl_pdata, DSI_ALL_CLKS, 0);
pr_debug("%s: BTA done with ret: %d\n", __func__, ret);
return ret;
@@ -744,6 +726,40 @@
return tot;
}
+/**
+ * __mdss_dsi_cmd_mode_config() - Enable/disable command mode engine
+ * @ctrl: pointer to the dsi controller structure
+ * @enable: true to enable command mode, false to disable command mode
+ *
+ * This function can be used to temporarily enable the command mode
+ * engine (even for video mode panels) so as to transfer any dma commands to
+ * the panel. It can also be used to disable the command mode engine
+ * when no longer needed.
+ *
+ * Return: true, if there was a mode switch to command mode for video mode
+ * panels.
+ */
+static inline bool __mdss_dsi_cmd_mode_config(
+ struct mdss_dsi_ctrl_pdata *ctrl, bool enable)
+{
+ bool mode_changed = false;
+ u32 dsi_ctrl;
+
+ dsi_ctrl = MIPI_INP((ctrl->ctrl_base) + 0x0004);
+ /* if currently in video mode, enable command mode */
+ if (enable) {
+ if ((dsi_ctrl) & BIT(1)) {
+ MIPI_OUTP((ctrl->ctrl_base) + 0x0004,
+ dsi_ctrl | BIT(2));
+ mode_changed = true;
+ }
+ } else {
+ MIPI_OUTP((ctrl->ctrl_base) + 0x0004, dsi_ctrl & ~BIT(2));
+ }
+
+ return mode_changed;
+}
+
/*
* mdss_dsi_cmds_tx:
* thread context only
@@ -751,61 +767,49 @@
int mdss_dsi_cmds_tx(struct mdss_dsi_ctrl_pdata *ctrl,
struct dsi_cmd_desc *cmds, int cnt)
{
- u32 dsi_ctrl, data;
- int video_mode, ret = 0;
- u32 left_dsi_ctrl = 0;
- bool left_ctrl_restore = false;
+ int ret = 0;
+ bool ctrl_restore = false, mctrl_restore = false;
+ struct mdss_dsi_ctrl_pdata *mctrl = NULL;
- if (ctrl->shared_pdata.broadcast_enable) {
- if (ctrl->ndx == DSI_CTRL_0) {
- pr_debug("%s: Broadcast mode. 1st ctrl\n",
- __func__);
- return 0;
- }
+ /*
+ * In broadcast mode, the configuration for master controller
+ * would be done when the slave controller is configured
+ */
+ if (mdss_dsi_is_master_ctrl(ctrl)) {
+ pr_debug("%s: Broadcast mode enabled. skipping config for ctrl%d\n",
+ __func__, ctrl->ndx);
+ return 0;
}
- if (ctrl->shared_pdata.broadcast_enable) {
- if ((ctrl->ndx == DSI_CTRL_1)
- && (left_ctrl_pdata != NULL)) {
- left_dsi_ctrl = MIPI_INP(left_ctrl_pdata->ctrl_base
- + 0x0004);
- video_mode =
- left_dsi_ctrl & 0x02; /* VIDEO_MODE_EN */
- if (video_mode) {
- data = left_dsi_ctrl | 0x04; /* CMD_MODE_EN */
- MIPI_OUTP(left_ctrl_pdata->ctrl_base + 0x0004,
- data);
- left_ctrl_restore = true;
- }
- }
+ /*
+ * Turn on cmd mode in order to transmit the commands.
+ * For video mode, do not send cmds more than one pixel line,
+ * since it only transmit it during BLLP.
+ * Ensure that for slave controller, master is also configured
+ */
+ if (mdss_dsi_is_slave_ctrl(ctrl)) {
+ mctrl = mdss_dsi_get_master_ctrl();
+ if (!mctrl)
+ pr_warn("%s: Unable to get master control\n",
+ __func__);
+ else
+ mctrl_restore = __mdss_dsi_cmd_mode_config(mctrl, 1);
}
- /* turn on cmd mode
- * for video mode, do not send cmds more than
- * one pixel line, since it only transmit it
- * during BLLP.
- */
- dsi_ctrl = MIPI_INP((ctrl->ctrl_base) + 0x0004);
- video_mode = dsi_ctrl & 0x02; /* VIDEO_MODE_EN */
- if (video_mode) {
- data = dsi_ctrl | 0x04; /* CMD_MODE_EN */
- MIPI_OUTP((ctrl->ctrl_base) + 0x0004, data);
- }
+ ctrl_restore = __mdss_dsi_cmd_mode_config(ctrl, 1);
ret = mdss_dsi_cmds2buf_tx(ctrl, cmds, cnt);
if (IS_ERR_VALUE(ret)) {
- pr_err("%s: failed to call\n",
- __func__);
+ pr_err("%s: failed to call\n", __func__);
cnt = -EINVAL;
}
- if (left_ctrl_restore)
- MIPI_OUTP(left_ctrl_pdata->ctrl_base + 0x0004,
- left_dsi_ctrl); /*restore */
+ if (mctrl_restore)
+ __mdss_dsi_cmd_mode_config(mctrl, 0);
- if (video_mode)
- MIPI_OUTP((ctrl->ctrl_base) + 0x0004,
- dsi_ctrl); /* restore */
+ if (ctrl_restore)
+ __mdss_dsi_cmd_mode_config(ctrl, 0);
+
return cnt;
}
@@ -838,45 +842,35 @@
int short_response, diff, pkt_size, ret = 0;
struct dsi_buf *tp, *rp;
char cmd;
- u32 dsi_ctrl, data;
- int video_mode;
- u32 left_dsi_ctrl = 0;
- bool left_ctrl_restore = false;
+ bool ctrl_restore = false, mctrl_restore = false;
+ struct mdss_dsi_ctrl_pdata *mctrl = NULL;
- if (ctrl->shared_pdata.broadcast_enable) {
- if (ctrl->ndx == DSI_CTRL_0) {
- pr_debug("%s: Broadcast mode. 1st ctrl\n",
- __func__);
- return 0;
- }
+ /*
+ * In broadcast mode, the configuration for master controller
+ * would be done when the slave controller is configured
+ */
+ if (mdss_dsi_is_master_ctrl(ctrl)) {
+ pr_debug("%s: Broadcast mode enabled. skipping config for ctrl%d\n",
+ __func__, ctrl->ndx);
+ return 0;
}
- if (ctrl->shared_pdata.broadcast_enable) {
- if ((ctrl->ndx == DSI_CTRL_1)
- && (left_ctrl_pdata != NULL)) {
- left_dsi_ctrl = MIPI_INP(left_ctrl_pdata->ctrl_base
- + 0x0004);
- video_mode = left_dsi_ctrl & 0x02; /* VIDEO_MODE_EN */
- if (video_mode) {
- data = left_dsi_ctrl | 0x04; /* CMD_MODE_EN */
- MIPI_OUTP(left_ctrl_pdata->ctrl_base + 0x0004,
- data);
- left_ctrl_restore = true;
- }
- }
+ /*
+ * Turn on cmd mode in order to transmit the commands.
+ * For video mode, do not send cmds more than one pixel line,
+ * since it only transmit it during BLLP.
+ * Ensure that for slave controller, master is also configured
+ */
+ if (mdss_dsi_is_slave_ctrl(ctrl)) {
+ mctrl = mdss_dsi_get_master_ctrl();
+ if (!mctrl)
+ pr_warn("%s: Unable to get master control\n",
+ __func__);
+ else
+ mctrl_restore = __mdss_dsi_cmd_mode_config(mctrl, 1);
}
- /* turn on cmd mode
- * for video mode, do not send cmds more than
- * one pixel line, since it only transmit it
- * during BLLP.
- */
- dsi_ctrl = MIPI_INP((ctrl->ctrl_base) + 0x0004);
- video_mode = dsi_ctrl & 0x02; /* VIDEO_MODE_EN */
- if (video_mode) {
- data = dsi_ctrl | 0x04; /* CMD_MODE_EN */
- MIPI_OUTP((ctrl->ctrl_base) + 0x0004, data);
- }
+ ctrl_restore = __mdss_dsi_cmd_mode_config(ctrl, 1);
if (rlen == 0) {
short_response = 1;
@@ -1003,12 +997,11 @@
rp->len = 0;
}
end:
- if (left_ctrl_restore)
- MIPI_OUTP(left_ctrl_pdata->ctrl_base + 0x0004,
- left_dsi_ctrl); /*restore */
- if (video_mode)
- MIPI_OUTP((ctrl->ctrl_base) + 0x0004,
- dsi_ctrl); /* restore */
+ if (mctrl_restore)
+ __mdss_dsi_cmd_mode_config(mctrl, 0);
+
+ if (ctrl_restore)
+ __mdss_dsi_cmd_mode_config(ctrl, 0);
return rp->len;
}
@@ -1022,6 +1015,7 @@
int domain = MDSS_IOMMU_DOMAIN_UNSECURE;
char *bp;
unsigned long size, addr;
+ struct mdss_dsi_ctrl_pdata *mctrl = NULL;
bp = tp->data;
@@ -1043,24 +1037,27 @@
INIT_COMPLETION(ctrl->dma_comp);
- if (ctrl->shared_pdata.broadcast_enable)
- if ((ctrl->ndx == DSI_CTRL_1)
- && (left_ctrl_pdata != NULL)) {
- MIPI_OUTP(left_ctrl_pdata->ctrl_base + 0x048, addr);
- MIPI_OUTP(left_ctrl_pdata->ctrl_base + 0x04c, len);
+ /* Ensure that for slave controller, master is also configured */
+ if (mdss_dsi_is_slave_ctrl(ctrl)) {
+ mctrl = mdss_dsi_get_master_ctrl();
+ if (mctrl) {
+ MIPI_OUTP(mctrl->ctrl_base + 0x048, addr);
+ MIPI_OUTP(mctrl->ctrl_base + 0x04c, len);
+ } else {
+ pr_warn("%s: Unable to get master control\n",
+ __func__);
}
+ }
MIPI_OUTP((ctrl->ctrl_base) + 0x048, addr);
MIPI_OUTP((ctrl->ctrl_base) + 0x04c, len);
wmb();
- if (ctrl->shared_pdata.broadcast_enable)
- if ((ctrl->ndx == DSI_CTRL_1)
- && (left_ctrl_pdata != NULL)) {
- MIPI_OUTP(left_ctrl_pdata->ctrl_base + 0x090, 0x01);
- }
+ /* Trigger on master controller as well */
+ if (mctrl)
+ MIPI_OUTP(mctrl->ctrl_base + 0x090, 0x01);
- MIPI_OUTP((ctrl->ctrl_base) + 0x090, 0x01); /* trigger */
+ MIPI_OUTP((ctrl->ctrl_base) + 0x090, 0x01);
wmb();
ret = wait_for_completion_timeout(&ctrl->dma_comp,
@@ -1210,13 +1207,11 @@
len = mdss_dsi_cmds_rx(ctrl, req->cmds, req->rlen);
memcpy(req->rbuf, rp->data, rp->len);
/*
- * For dual DSI cases, early return of controller - 0
+ * For dual DSI cases, early return of master ctrl
* is valid. Hence, for those cases the return value
* is zero even though we don't send any commands.
- *
*/
- if ((ctrl->shared_pdata.broadcast_enable &&
- ctrl->ndx == DSI_CTRL_0) || (len != 0))
+ if (mdss_dsi_is_master_ctrl(ctrl) || (len != 0))
ret = 0;
} else {
pr_err("%s: No rx buffer provided\n", __func__);
@@ -1253,14 +1248,14 @@
mdss_bus_bandwidth_ctrl(1);
pr_debug("%s: from_mdp=%d pid=%d\n", __func__, from_mdp, current->pid);
- mdss_dsi_clk_ctrl(ctrl, 1);
+ mdss_dsi_clk_ctrl(ctrl, DSI_ALL_CLKS, 1);
if (req->flags & CMD_REQ_RX)
ret = mdss_dsi_cmdlist_rx(ctrl, req);
else
ret = mdss_dsi_cmdlist_tx(ctrl, req);
- mdss_dsi_clk_ctrl(ctrl, 0);
+ mdss_dsi_clk_ctrl(ctrl, DSI_ALL_CLKS, 0);
mdss_bus_bandwidth_ctrl(0);
need_lock:
@@ -1465,6 +1460,7 @@
u32 isr;
struct mdss_dsi_ctrl_pdata *ctrl =
(struct mdss_dsi_ctrl_pdata *)ptr;
+ struct mdss_dsi_ctrl_pdata *mctrl = NULL;
if (!ctrl->ctrl_base)
pr_err("%s:%d DSI base adr no Initialized",
@@ -1473,16 +1469,19 @@
isr = MIPI_INP(ctrl->ctrl_base + 0x0110);/* DSI_INTR_CTRL */
MIPI_OUTP(ctrl->ctrl_base + 0x0110, isr);
- if (ctrl->shared_pdata.broadcast_enable)
- if ((ctrl->panel_data.panel_info.pdest == DISPLAY_2)
- && (left_ctrl_pdata != NULL)) {
+ if (mdss_dsi_is_slave_ctrl(ctrl)) {
+ mctrl = mdss_dsi_get_master_ctrl();
+ if (mctrl) {
u32 isr0;
- isr0 = MIPI_INP(left_ctrl_pdata->ctrl_base
- + 0x0110);/* DSI_INTR_CTRL */
+ isr0 = MIPI_INP(mctrl->ctrl_base + 0x0110);
if (isr0 & DSI_INTR_CMD_DMA_DONE)
- MIPI_OUTP(left_ctrl_pdata->ctrl_base + 0x0110,
+ MIPI_OUTP(mctrl->ctrl_base + 0x0110,
DSI_INTR_CMD_DMA_DONE);
+ } else {
+ pr_warn("%s: Unable to get master control\n",
+ __func__);
}
+ }
pr_debug("%s: ndx=%d isr=%x\n", __func__, ctrl->ndx, isr);
diff --git a/drivers/video/msm/mdss/mdss_dsi_panel.c b/drivers/video/msm/mdss/mdss_dsi_panel.c
index 5415a7e..f450dec 100644
--- a/drivers/video/msm/mdss/mdss_dsi_panel.c
+++ b/drivers/video/msm/mdss/mdss_dsi_panel.c
@@ -317,17 +317,6 @@
return rc;
}
-static struct mdss_dsi_ctrl_pdata *get_rctrl_data(struct mdss_panel_data *pdata)
-{
- if (!pdata || !pdata->next) {
- pr_err("%s: Invalid panel data\n", __func__);
- return NULL;
- }
-
- return container_of(pdata->next, struct mdss_dsi_ctrl_pdata,
- panel_data);
-}
-
static void mdss_dsi_panel_bl_ctrl(struct mdss_panel_data *pdata,
u32 bl_level)
{
@@ -359,15 +348,15 @@
break;
case BL_DCS_CMD:
mdss_dsi_panel_bklt_dcs(ctrl_pdata, bl_level);
- if (ctrl_pdata->shared_pdata.broadcast_enable &&
- ctrl_pdata->ndx == DSI_CTRL_0) {
- struct mdss_dsi_ctrl_pdata *rctrl_pdata = NULL;
- rctrl_pdata = get_rctrl_data(pdata);
- if (!rctrl_pdata) {
- pr_err("%s: Right ctrl data NULL\n", __func__);
+ if (mdss_dsi_is_master_ctrl(ctrl_pdata)) {
+ struct mdss_dsi_ctrl_pdata *sctrl =
+ mdss_dsi_get_slave_ctrl();
+ if (!sctrl) {
+ pr_err("%s: Invalid slave ctrl data\n",
+ __func__);
return;
}
- mdss_dsi_panel_bklt_dcs(rctrl_pdata, bl_level);
+ mdss_dsi_panel_bklt_dcs(sctrl, bl_level);
}
break;
default:
diff --git a/drivers/video/msm/mdss/mdss_dsi_status.c b/drivers/video/msm/mdss/mdss_dsi_status.c
index f0c4f4c..fd7f3fd 100644
--- a/drivers/video/msm/mdss/mdss_dsi_status.c
+++ b/drivers/video/msm/mdss/mdss_dsi_status.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -86,6 +86,15 @@
mutex_lock(ctl->shared_lock);
mutex_lock(&mdp5_data->ov_lock);
+ if (pdsi_status->mfd->shutdown_pending) {
+ mutex_unlock(&mdp5_data->ov_lock);
+ if (ctl->shared_lock)
+ mutex_unlock(ctl->shared_lock);
+ pr_err("%s: DSI turning off, avoiding BTA status check\n",
+ __func__);
+ return;
+ }
+
/*
* For the command mode panels, we return pan display
* IOCTL on vsync interrupt. So, after vsync interrupt comes
diff --git a/drivers/video/msm/mdss/mdss_fb.c b/drivers/video/msm/mdss/mdss_fb.c
index 252a86e..1aec58e 100644
--- a/drivers/video/msm/mdss/mdss_fb.c
+++ b/drivers/video/msm/mdss/mdss_fb.c
@@ -828,6 +828,8 @@
}
break;
}
+ /* Notify listeners */
+ sysfs_notify(&mfd->fbi->dev->kobj, NULL, "show_blank_event");
return ret;
}
diff --git a/drivers/video/msm/mdss/mdss_hdmi_hdcp.c b/drivers/video/msm/mdss/mdss_hdmi_hdcp.c
index e56e9fa..2240941 100644
--- a/drivers/video/msm/mdss/mdss_hdmi_hdcp.c
+++ b/drivers/video/msm/mdss/mdss_hdmi_hdcp.c
@@ -1050,7 +1050,10 @@
}
if (++hdcp_ctrl->auth_retries == AUTH_RETRIES_TIME) {
- hdmi_hdcp_off(hdcp_ctrl);
+ mutex_lock(hdcp_ctrl->init_data.mutex);
+ hdcp_ctrl->hdcp_state = HDCP_STATE_INACTIVE;
+ mutex_unlock(hdcp_ctrl->init_data.mutex);
+
hdcp_ctrl->auth_retries = 0;
ret = -ERANGE;
}
@@ -1077,13 +1080,6 @@
return 0;
}
- ret = hdmi_msm_if_abort_reauth(hdcp_ctrl);
-
- if (ret) {
- DEV_ERR("%s: abort reauthentication!\n", __func__);
- return ret;
- }
-
/*
* Disable HPD circuitry.
* This is needed to reset the HDCP cipher engine so that when we
@@ -1109,6 +1105,13 @@
DSS_REG_R(hdcp_ctrl->init_data.core_io,
HDMI_HPD_CTRL) | BIT(28));
+ ret = hdmi_msm_if_abort_reauth(hdcp_ctrl);
+
+ if (ret) {
+ DEV_ERR("%s: abort reauthentication!\n", __func__);
+ return ret;
+ }
+
/* Restart authentication attempt */
DEV_DBG("%s: %s: Scheduling work to start HDCP authentication",
__func__, HDCP_STATE_NAME);
diff --git a/drivers/video/msm/mdss/mdss_hdmi_tx.c b/drivers/video/msm/mdss/mdss_hdmi_tx.c
index cd2f8e4..2b409f5 100644
--- a/drivers/video/msm/mdss/mdss_hdmi_tx.c
+++ b/drivers/video/msm/mdss/mdss_hdmi_tx.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2010-2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -1127,6 +1127,17 @@
DEV_DBG("%s: Got HPD interrupt\n", __func__);
if (hdmi_ctrl->hpd_state) {
+ /*
+ * If a down stream device or bridge chip is attached to hdmi
+ * Tx core output, it is likely that it might be powering the
+ * hpd module ON/OFF on cable connect/disconnect as it would
+ * have its own mechanism of detecting cable. Flush power off
+ * work is needed in case there is any race condidtion between
+ * power off and on during fast cable plug in/out.
+ */
+ if (hdmi_ctrl->ds_registered)
+ flush_work(&hdmi_ctrl->power_off_work);
+
if (hdmi_tx_enable_power(hdmi_ctrl, HDMI_TX_DDC_PM, true)) {
DEV_ERR("%s: Failed to enable ddc power\n", __func__);
return;
@@ -1143,6 +1154,13 @@
hdmi_tx_set_audio_switch_node(hdmi_ctrl, 0, false);
hdmi_tx_wait_for_audio_engine(hdmi_ctrl);
+ if (!hdmi_ctrl->panel_power_on) {
+ if (hdmi_tx_enable_power(hdmi_ctrl, HDMI_TX_DDC_PM,
+ false))
+ DEV_WARN("%s: Failed to disable ddc power\n",
+ __func__);
+ }
+
hdmi_tx_send_cable_notification(hdmi_ctrl, 0);
DEV_INFO("%s: sense cable DISCONNECTED: state switch to %d\n",
__func__, hdmi_ctrl->sdev.state);
@@ -2344,6 +2362,8 @@
ops->set_mhl_max_pclk = hdmi_tx_set_mhl_max_pclk;
ops->set_upstream_hpd = hdmi_tx_set_mhl_hpd;
+ hdmi_ctrl->ds_registered = true;
+
return 0;
}
diff --git a/drivers/video/msm/mdss/mdss_hdmi_tx.h b/drivers/video/msm/mdss/mdss_hdmi_tx.h
index 8233ba8..54d80dc 100644
--- a/drivers/video/msm/mdss/mdss_hdmi_tx.h
+++ b/drivers/video/msm/mdss/mdss_hdmi_tx.h
@@ -83,6 +83,7 @@
struct work_struct cable_notify_work;
bool hdcp_feature_on;
+ bool ds_registered;
u32 present_hdcp;
u8 spd_vendor_name[9];
diff --git a/drivers/video/msm/mdss/mdss_mdp.c b/drivers/video/msm/mdss/mdss_mdp.c
index cfa594c..8a215bc 100644
--- a/drivers/video/msm/mdss/mdss_mdp.c
+++ b/drivers/video/msm/mdss/mdss_mdp.c
@@ -296,6 +296,7 @@
pr_debug("Disable HW=%d irq ena=%d mask=%x\n", hw->hw_ndx,
mdss_res->irq_ena, mdss_res->irq_mask);
+ spin_lock(&mdss_lock);
if (!(mdss_res->irq_mask & ndx_bit)) {
pr_warn("MDSS HW ndx=%d is NOT set, mask=%x, hist mask=%x\n",
hw->hw_ndx, mdss_res->mdp_irq_mask,
@@ -307,6 +308,7 @@
disable_irq_nosync(mdss_res->irq);
}
}
+ spin_unlock(&mdss_lock);
}
EXPORT_SYMBOL(mdss_disable_irq_nosync);
@@ -505,7 +507,16 @@
spin_unlock_irqrestore(&mdp_lock, irq_flags);
}
-/* called from interrupt context */
+/**
+ * mdss_mdp_irq_disable_nosync() - disable mdp irq
+ * @intr_type: mdp interface type
+ * @intf_num: mdp interface num
+ *
+ * This fucntion is called from interrupt context
+ * mdp_lock is already held at up stream (mdss_irq_handler)
+ * therefore spin_lock(&mdp_lock) is not allowed here
+ *
+*/
void mdss_mdp_irq_disable_nosync(u32 intr_type, u32 intf_num)
{
u32 irq;
@@ -1001,8 +1012,9 @@
writel_relaxed(1, offset + 16);
}
- mdata->nmax_concurrent_ad_hw = (mdata->mdp_rev <= MDSS_MDP_HW_REV_102) ?
- 1 : 2;
+ mdata->nmax_concurrent_ad_hw =
+ (mdata->mdp_rev < MDSS_MDP_HW_REV_103) ? 1 : 2;
+
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
pr_debug("MDP hw init done\n");
@@ -1578,6 +1590,61 @@
return 0;
}
+static int mdss_mdp_parse_dt_pipe_clk_ctrl(struct platform_device *pdev,
+ char *prop_name, struct mdss_mdp_pipe *pipe_list, u32 npipes)
+{
+ int rc = 0;
+ size_t len;
+ const u32 *arr;
+
+ arr = of_get_property(pdev->dev.of_node, prop_name, &len);
+ if (arr) {
+ int i, j;
+
+ len /= sizeof(u32);
+ for (i = 0, j = 0; i < len; j++) {
+ struct mdss_mdp_pipe *pipe = NULL;
+
+ if (j >= npipes) {
+ pr_err("invalid clk ctrl enries for prop: %s\n",
+ prop_name);
+ return -EINVAL;
+ }
+
+ pipe = &pipe_list[j];
+
+ pipe->clk_ctrl.reg_off = be32_to_cpu(arr[i++]);
+ pipe->clk_ctrl.bit_off = be32_to_cpu(arr[i++]);
+
+ /* status register is next in line to ctrl register */
+ pipe->clk_status.reg_off = pipe->clk_ctrl.reg_off + 4;
+ pipe->clk_status.bit_off = be32_to_cpu(arr[i++]);
+
+ pr_debug("%s[%d]: ctrl: reg_off: 0x%x bit_off: %d\n",
+ prop_name, j, pipe->clk_ctrl.reg_off,
+ pipe->clk_ctrl.bit_off);
+ pr_debug("%s[%d]: status: reg_off: 0x%x bit_off: %d\n",
+ prop_name, j, pipe->clk_status.reg_off,
+ pipe->clk_status.bit_off);
+ }
+ if (j != npipes) {
+ pr_err("%s: %d entries found. required %d\n",
+ prop_name, j, npipes);
+ for (i = 0; i < npipes; i++) {
+ memset(&pipe_list[i].clk_ctrl, 0,
+ sizeof(pipe_list[i].clk_ctrl));
+ memset(&pipe_list[i].clk_status, 0,
+ sizeof(pipe_list[i].clk_status));
+ }
+ rc = -EINVAL;
+ }
+ } else {
+ pr_err("error mandatory property '%s' not found\n", prop_name);
+ rc = -EINVAL;
+ }
+
+ return rc;
+}
static int mdss_mdp_parse_dt_pipe(struct platform_device *pdev)
{
@@ -1761,6 +1828,25 @@
setup_cnt += mdata->nrgb_pipes - DEFAULT_TOTAL_RGB_PIPES;
}
+ rc = mdss_mdp_parse_dt_pipe_clk_ctrl(pdev,
+ "qcom,mdss-pipe-vig-clk-ctrl-offsets", mdata->vig_pipes,
+ mdata->nvig_pipes);
+ if (rc)
+ goto parse_fail;
+
+ rc = mdss_mdp_parse_dt_pipe_clk_ctrl(pdev,
+ "qcom,mdss-pipe-rgb-clk-ctrl-offsets", mdata->rgb_pipes,
+ mdata->nrgb_pipes);
+ if (rc)
+ goto parse_fail;
+
+ rc = mdss_mdp_parse_dt_pipe_clk_ctrl(pdev,
+ "qcom,mdss-pipe-dma-clk-ctrl-offsets", mdata->dma_pipes,
+ mdata->ndma_pipes);
+ if (rc)
+ goto parse_fail;
+
+
goto parse_done;
parse_fail:
diff --git a/drivers/video/msm/mdss/mdss_mdp.h b/drivers/video/msm/mdss/mdss_mdp.h
index 9a469a4..adee7b50 100644
--- a/drivers/video/msm/mdss/mdss_mdp.h
+++ b/drivers/video/msm/mdss/mdss_mdp.h
@@ -349,6 +349,11 @@
DECLARE_BITMAP(fixed, MAX_DRV_SUP_MMB_BLKS);
};
+struct mdss_mdp_shared_reg_ctrl {
+ u32 reg_off;
+ u32 bit_off;
+};
+
struct mdss_mdp_pipe {
u32 num;
u32 type;
@@ -356,6 +361,9 @@
char __iomem *base;
u32 ftch_id;
u32 xin_id;
+ struct mdss_mdp_shared_reg_ctrl clk_ctrl;
+ struct mdss_mdp_shared_reg_ctrl clk_status;
+
atomic_t ref_cnt;
u32 play_cnt;
int pid;
@@ -452,6 +460,16 @@
};
#define is_vig_pipe(_pipe_id_) ((_pipe_id_) <= MDSS_MDP_SSPP_VIG2)
+
+static inline struct mdss_mdp_ctl *mdss_mdp_get_split_ctl(
+ struct mdss_mdp_ctl *ctl)
+{
+ if (ctl && ctl->mixer_right && (ctl->mixer_right->ctl != ctl))
+ return ctl->mixer_right->ctl;
+
+ return NULL;
+}
+
static inline void mdss_mdp_ctl_write(struct mdss_mdp_ctl *ctl,
u32 reg, u32 val)
{
@@ -533,7 +551,8 @@
struct mdss_mdp_pipe **left_plist, int left_cnt,
struct mdss_mdp_pipe **right_plist, int right_cnt);
int mdss_mdp_perf_calc_pipe(struct mdss_mdp_pipe *pipe,
- struct mdss_mdp_perf_params *perf, struct mdss_mdp_img_rect *roi);
+ struct mdss_mdp_perf_params *perf, struct mdss_mdp_img_rect *roi,
+ bool apply_fudge);
int mdss_mdp_ctl_notify(struct mdss_mdp_ctl *ctl, int event);
void mdss_mdp_ctl_notifier_register(struct mdss_mdp_ctl *ctl,
struct notifier_block *notifier);
@@ -629,10 +648,10 @@
int mdss_mdp_ctl_addr_setup(struct mdss_data_type *mdata, u32 *ctl_offsets,
u32 *wb_offsets, u32 len);
+int mdss_mdp_pipe_fetch_halt(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,
struct mdss_mdp_data *src_data);
-int mdss_mdp_pipe_fetch_halt(struct mdss_mdp_pipe *pipe);
int mdss_mdp_data_check(struct mdss_mdp_data *data,
struct mdss_mdp_plane_sizes *ps);
diff --git a/drivers/video/msm/mdss/mdss_mdp_ctl.c b/drivers/video/msm/mdss/mdss_mdp_ctl.c
index fa64448..2c1beab 100644
--- a/drivers/video/msm/mdss/mdss_mdp_ctl.c
+++ b/drivers/video/msm/mdss/mdss_mdp_ctl.c
@@ -303,6 +303,7 @@
* @pipe: Source pipe struct containing updated pipe params
* @perf: Structure containing values that should be updated for
* performance tuning
+ * @apply_fudge: Boolean to determine if mdp clock fudge is applicable
*
* Function calculates the minimum required performance calculations in order
* to avoid MDP underflow. The calculations are based on the way MDP
@@ -310,7 +311,8 @@
* (MDP clock requirement) based on frame size and scaling requirements.
*/
int mdss_mdp_perf_calc_pipe(struct mdss_mdp_pipe *pipe,
- struct mdss_mdp_perf_params *perf, struct mdss_mdp_img_rect *roi)
+ struct mdss_mdp_perf_params *perf, struct mdss_mdp_img_rect *roi,
+ bool apply_fudge)
{
struct mdss_mdp_mixer *mixer;
int fps = DEFAULT_FRAME_RATE;
@@ -384,7 +386,10 @@
perf->bw_overlap = (quota / dst.h) * v_total;
}
- perf->mdp_clk_rate = mdss_mdp_clk_fudge_factor(mixer, rate);
+ if (apply_fudge)
+ perf->mdp_clk_rate = mdss_mdp_clk_fudge_factor(mixer, rate);
+ else
+ perf->mdp_clk_rate = rate;
prefill_params.smp_bytes = mdss_mdp_smp_get_size(pipe);
prefill_params.xres = xres;
@@ -440,6 +445,8 @@
u64 bw_overlap[MDSS_MDP_MAX_STAGE] = { 0 };
u32 v_region[MDSS_MDP_MAX_STAGE * 2] = { 0 };
u32 prefill_bytes = 0;
+ struct mdss_data_type *mdata = mdss_mdp_get_mdata();
+ bool apply_fudge = true;
BUG_ON(num_pipes > MDSS_MDP_MAX_STAGE);
@@ -469,13 +476,36 @@
memset(bw_overlap, 0, sizeof(u64) * MDSS_MDP_MAX_STAGE);
memset(v_region, 0, sizeof(u32) * MDSS_MDP_MAX_STAGE * 2);
+ /*
+ * Apply this logic only for 8x26 to reduce clock rate
+ * for single video playback use case
+ */
+ if (IS_MDSS_MAJOR_MINOR_SAME(mdata->mdp_rev, MDSS_MDP_HW_REV_101)
+ && mixer->type == MDSS_MDP_MIXER_TYPE_INTF) {
+ u32 npipes = 0;
+ for (i = 0; i < MDSS_MDP_MAX_STAGE; i++) {
+ pipe = mixer->stage_pipe[i];
+ if (pipe) {
+ if (npipes) {
+ apply_fudge = true;
+ break;
+ }
+ npipes++;
+ apply_fudge = !(pipe->src_fmt->is_yuv)
+ || !(pipe->flags
+ & MDP_SOURCE_ROTATED_90);
+ }
+ }
+ }
+
for (i = 0; i < num_pipes; i++) {
struct mdss_mdp_perf_params tmp;
pipe = pipe_list[i];
if (pipe == NULL)
continue;
- if (mdss_mdp_perf_calc_pipe(pipe, &tmp, &mixer->roi))
+ if (mdss_mdp_perf_calc_pipe(pipe, &tmp, &mixer->roi,
+ apply_fudge))
continue;
prefill_bytes += tmp.prefill_bytes;
bw_overlap[i] = tmp.bw_overlap;
@@ -622,9 +652,6 @@
}
perf->bw_ctl = max(perf->bw_prefill, perf->bw_overlap);
-
- if (ctl->is_video_mode)
- perf->bw_ctl = IB_FUDGE_FACTOR(perf->bw_ctl);
}
int mdss_mdp_perf_bw_check(struct mdss_mdp_ctl *ctl,
@@ -667,6 +694,9 @@
left_plist, (left_plist ? MDSS_MDP_MAX_STAGE : 0),
right_plist, (right_plist ? MDSS_MDP_MAX_STAGE : 0));
+ if (ctl->is_video_mode)
+ perf->bw_ctl = IB_FUDGE_FACTOR(perf->bw_ctl);
+
pr_debug("ctl=%d clk_rate=%u\n", ctl->num, perf->mdp_clk_rate);
pr_debug("bw_overlap=%llu bw_prefill=%llu prefill_byptes=%d\n",
perf->bw_overlap, perf->bw_prefill, perf->prefill_bytes);
@@ -1133,15 +1163,6 @@
return 0;
}
-static inline struct mdss_mdp_ctl *mdss_mdp_get_split_ctl(
- struct mdss_mdp_ctl *ctl)
-{
- if (ctl && ctl->mixer_right && (ctl->mixer_right->ctl != ctl))
- return ctl->mixer_right->ctl;
-
- return NULL;
-}
-
int mdss_mdp_ctl_splash_finish(struct mdss_mdp_ctl *ctl, bool handoff)
{
struct mdss_mdp_ctl *sctl = mdss_mdp_get_split_ctl(ctl);
diff --git a/drivers/video/msm/mdss/mdss_mdp_hwio.h b/drivers/video/msm/mdss/mdss_mdp_hwio.h
index bff56d2..4b9ea20 100644
--- a/drivers/video/msm/mdss/mdss_mdp_hwio.h
+++ b/drivers/video/msm/mdss/mdss_mdp_hwio.h
@@ -21,10 +21,6 @@
#define ENHIST_LUT_ENTRIES 256
#define HIST_V_SIZE 256
-#define MDSS_MDP_HW_REV_100 0x10000000
-#define MDSS_MDP_HW_REV_102 0x10020000
-#define MDSS_MDP_HW_REV_103 0x10030000
-
#define MDSS_MDP_FETCH_CONFIG_RESET_VALUE 0x00000087
#define MDSS_REG_HW_VERSION 0x0
diff --git a/drivers/video/msm/mdss/mdss_mdp_intf_cmd.c b/drivers/video/msm/mdss/mdss_mdp_intf_cmd.c
index 8c7dc29..96da27e 100644
--- a/drivers/video/msm/mdss/mdss_mdp_intf_cmd.c
+++ b/drivers/video/msm/mdss/mdss_mdp_intf_cmd.c
@@ -200,13 +200,17 @@
{
unsigned long flags;
struct mdss_data_type *mdata = mdss_mdp_get_mdata();
+
+ if (!ctx->panel_on)
+ return;
+
mutex_lock(&ctx->clk_mtx);
if (!ctx->clk_enabled) {
ctx->clk_enabled = 1;
if (cancel_delayed_work_sync(&ctx->ulps_work))
pr_debug("deleted pending ulps work\n");
- mdss_mdp_ctl_intf_event
- (ctx->ctl, MDSS_EVENT_PANEL_CLK_CTRL, (void *)1);
+
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
if (ctx->ulps) {
if (mdss_mdp_cmd_tearcheck_setup(ctx->ctl, 1))
@@ -216,7 +220,9 @@
ctx->ulps = false;
}
- mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
+ mdss_mdp_ctl_intf_event
+ (ctx->ctl, MDSS_EVENT_PANEL_CLK_CTRL, (void *)1);
+
mdss_mdp_hist_intr_setup(&mdata->hist_intr, MDSS_IRQ_RESUME);
}
spin_lock_irqsave(&ctx->clk_lock, flags);
@@ -277,6 +283,10 @@
if (!ctx->vsync_enabled) {
if (ctx->rdptr_enabled)
ctx->rdptr_enabled--;
+
+ /* keep clk on during kickoff */
+ if (ctx->rdptr_enabled == 0 && ctx->koff_cnt)
+ ctx->rdptr_enabled++;
}
if (ctx->rdptr_enabled == 0) {
@@ -418,6 +428,7 @@
{
struct mdss_mdp_cmd_ctx *ctx;
unsigned long flags;
+ bool enable_rdptr = false;
ctx = (struct mdss_mdp_cmd_ctx *) ctl->priv_data;
if (!ctx) {
@@ -429,12 +440,14 @@
if (!handle->enabled) {
handle->enabled = true;
list_add(&handle->list, &ctx->vsync_handlers);
- if (!handle->cmd_post_flush)
- ctx->vsync_enabled = 1;
+
+ enable_rdptr = !handle->cmd_post_flush;
+ if (enable_rdptr)
+ ctx->vsync_enabled++;
}
spin_unlock_irqrestore(&ctx->clk_lock, flags);
- if (!handle->cmd_post_flush)
+ if (enable_rdptr)
mdss_mdp_cmd_clk_on(ctx);
return 0;
@@ -443,11 +456,8 @@
static int mdss_mdp_cmd_remove_vsync_handler(struct mdss_mdp_ctl *ctl,
struct mdss_mdp_vsync_handler *handle)
{
-
struct mdss_mdp_cmd_ctx *ctx;
unsigned long flags;
- struct mdss_mdp_vsync_handler *tmp;
- int num_rdptr_vsync = 0;
ctx = (struct mdss_mdp_cmd_ctx *) ctl->priv_data;
if (!ctx) {
@@ -455,19 +465,17 @@
return -ENODEV;
}
-
spin_lock_irqsave(&ctx->clk_lock, flags);
if (handle->enabled) {
handle->enabled = false;
list_del_init(&handle->list);
- }
- list_for_each_entry(tmp, &ctx->vsync_handlers, list) {
- if (!tmp->cmd_post_flush)
- num_rdptr_vsync++;
- }
- if (!num_rdptr_vsync) {
- ctx->vsync_enabled = 0;
- ctx->rdptr_enabled = VSYNC_EXPIRE_TICK;
+
+ if (!handle->cmd_post_flush) {
+ if (ctx->vsync_enabled)
+ ctx->vsync_enabled--;
+ else
+ WARN(1, "unbalanced vsync disable");
+ }
}
spin_unlock_irqrestore(&ctx->clk_lock, flags);
return 0;
@@ -563,22 +571,22 @@
WARN(rc, "intf %d panel on error (%d)\n", ctl->intf_num, rc);
}
- mdss_mdp_cmd_set_partial_roi(ctl);
+ spin_lock_irqsave(&ctx->clk_lock, flags);
+ ctx->koff_cnt++;
+ spin_unlock_irqrestore(&ctx->clk_lock, flags);
mdss_mdp_cmd_clk_on(ctx);
+ mdss_mdp_cmd_set_partial_roi(ctl);
+
/*
* tx dcs command if had any
*/
mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_DSI_CMDLIST_KOFF,
(void *)&ctx->recovery);
-
INIT_COMPLETION(ctx->pp_comp);
mdss_mdp_irq_enable(MDSS_MDP_IRQ_PING_PONG_COMP, ctx->pp_num);
mdss_mdp_ctl_write(ctl, MDSS_MDP_REG_CTL_START, 1);
- spin_lock_irqsave(&ctx->clk_lock, flags);
- ctx->koff_cnt++;
- spin_unlock_irqrestore(&ctx->clk_lock, flags);
mb();
return 0;
diff --git a/drivers/video/msm/mdss/mdss_mdp_intf_video.c b/drivers/video/msm/mdss/mdss_mdp_intf_video.c
index 55a4a4d..f8bdc04 100644
--- a/drivers/video/msm/mdss/mdss_mdp_intf_video.c
+++ b/drivers/video/msm/mdss/mdss_mdp_intf_video.c
@@ -298,6 +298,7 @@
{
struct mdss_mdp_video_ctx *ctx;
struct mdss_mdp_vsync_handler *tmp, *handle;
+ struct mdss_mdp_ctl *sctl;
int rc;
u32 frame_rate = 0;
@@ -335,6 +336,10 @@
mdss_mdp_irq_disable(MDSS_MDP_IRQ_INTF_UNDER_RUN,
ctl->intf_num);
+ sctl = mdss_mdp_get_split_ctl(ctl);
+ if (sctl)
+ mdss_mdp_irq_disable(MDSS_MDP_IRQ_INTF_UNDER_RUN,
+ sctl->intf_num);
}
list_for_each_entry_safe(handle, tmp, &ctx->vsync_handlers, list)
@@ -641,6 +646,7 @@
static int mdss_mdp_video_display(struct mdss_mdp_ctl *ctl, void *arg)
{
struct mdss_mdp_video_ctx *ctx;
+ struct mdss_mdp_ctl *sctl;
int rc;
pr_debug("kickoff ctl=%d\n", ctl->num);
@@ -674,6 +680,11 @@
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
mdss_mdp_irq_enable(MDSS_MDP_IRQ_INTF_UNDER_RUN, ctl->intf_num);
+ sctl = mdss_mdp_get_split_ctl(ctl);
+ if (sctl)
+ mdss_mdp_irq_enable(MDSS_MDP_IRQ_INTF_UNDER_RUN,
+ sctl->intf_num);
+
mdp_video_write(ctx, MDSS_MDP_REG_INTF_TIMING_ENGINE_EN, 1);
wmb();
diff --git a/drivers/video/msm/mdss/mdss_mdp_overlay.c b/drivers/video/msm/mdss/mdss_mdp_overlay.c
index 7420f95..7628380 100644
--- a/drivers/video/msm/mdss/mdss_mdp_overlay.c
+++ b/drivers/video/msm/mdss/mdss_mdp_overlay.c
@@ -268,7 +268,7 @@
int rc;
for (;;) {
- rc = mdss_mdp_perf_calc_pipe(pipe, &perf, NULL);
+ rc = mdss_mdp_perf_calc_pipe(pipe, &perf, NULL, true);
if (!rc && (perf.mdp_clk_rate <= mdata->max_mdp_clk_rate))
break;
@@ -321,10 +321,6 @@
rc, src, pipe->dst.h);
return rc;
}
- pipe->scale.init_phase_x[0] = (pipe->scale.phase_step_x[0] -
- (1 << PHASE_STEP_SHIFT)) / 2;
- pipe->scale.init_phase_y[0] = (pipe->scale.phase_step_y[0] -
- (1 << PHASE_STEP_SHIFT)) / 2;
return rc;
}
@@ -770,17 +766,16 @@
LIST_HEAD(destroy_pipes);
mutex_lock(&mfd->lock);
- __mdss_mdp_overlay_free_list_purge(mfd);
-
list_for_each_entry_safe(pipe, tmp, &mdp5_data->pipes_cleanup,
cleanup_list) {
list_move(&pipe->cleanup_list, &destroy_pipes);
+
+ /* make sure pipe fetch has been halted before freeing buffer */
mdss_mdp_pipe_fetch_halt(pipe);
- mdss_mdp_overlay_free_buf(&pipe->back_buf);
- __mdss_mdp_overlay_free_list_add(mfd, &pipe->front_buf);
- pipe->mfd = NULL;
}
+ __mdss_mdp_overlay_free_list_purge(mfd);
+
list_for_each_entry(pipe, &mdp5_data->pipes_used, used_list) {
if (pipe->back_buf.num_planes) {
/* make back buffer active */
@@ -788,9 +783,20 @@
swap(pipe->back_buf, pipe->front_buf);
}
}
- mutex_unlock(&mfd->lock);
- list_for_each_entry_safe(pipe, tmp, &destroy_pipes, cleanup_list)
+
+ list_for_each_entry_safe(pipe, tmp, &destroy_pipes, cleanup_list) {
+ /*
+ * in case of secure UI, the buffer needs to be released as
+ * soon as session is closed.
+ */
+ if (pipe->flags & MDP_SECURE_DISPLAY_OVERLAY_SESSION)
+ mdss_mdp_overlay_free_buf(&pipe->front_buf);
+ else
+ __mdss_mdp_overlay_free_list_add(mfd, &pipe->front_buf);
+ mdss_mdp_overlay_free_buf(&pipe->back_buf);
mdss_mdp_pipe_destroy(pipe);
+ }
+ mutex_unlock(&mfd->lock);
}
static void __mdss_mdp_handoff_cleanup_pipes(struct msm_fb_data_type *mfd,
@@ -974,6 +980,11 @@
int ret = 0;
int sd_in_pipe = 0;
+ if (!ctl) {
+ pr_warn("kickoff on fb=%d without a ctl attched\n", mfd->index);
+ return ret;
+ }
+
if (ctl->shared_lock)
mutex_lock(ctl->shared_lock);
@@ -1774,7 +1785,7 @@
vsync_ticks = ktime_to_ns(mdp5_data->vsync_time);
pr_debug("fb%d vsync=%llu", mfd->index, vsync_ticks);
- ret = scnprintf(buf, PAGE_SIZE, "VSYNC=%llu", vsync_ticks);
+ ret = scnprintf(buf, PAGE_SIZE, "VSYNC=%llu\n", vsync_ticks);
return ret;
}
@@ -2693,6 +2704,56 @@
return 0;
}
+static int __mdss_mdp_ctl_handoff(struct mdss_mdp_ctl *ctl,
+ struct mdss_data_type *mdata)
+{
+ int rc = 0;
+ int i, j;
+ u32 mixercfg;
+ struct mdss_mdp_pipe *pipe = NULL;
+
+ if (!ctl || !mdata)
+ return -EINVAL;
+
+ for (i = 0; i < mdata->nmixers_intf; i++) {
+ mixercfg = mdss_mdp_ctl_read(ctl, MDSS_MDP_REG_CTL_LAYER(i));
+ pr_debug("for lm%d mixercfg = 0x%09x\n", i, mixercfg);
+
+ j = MDSS_MDP_SSPP_VIG0;
+ for (; j < MDSS_MDP_MAX_SSPP && mixercfg; j++) {
+ u32 cfg = j * 3;
+ if ((j == MDSS_MDP_SSPP_VIG3) ||
+ (j == MDSS_MDP_SSPP_RGB3)) {
+ /* Add 2 to account for Cursor & Border bits */
+ cfg += 2;
+ }
+ if (mixercfg & (0x7 << cfg)) {
+ pr_debug("Pipe %d staged\n", j);
+ pipe = mdss_mdp_pipe_search(mdata, BIT(j));
+ if (!pipe) {
+ pr_warn("Invalid pipe %d staged\n", j);
+ continue;
+ }
+
+ rc = mdss_mdp_pipe_handoff(pipe);
+ if (rc) {
+ pr_err("Failed to handoff pipe%d\n",
+ pipe->num);
+ goto exit;
+ }
+
+ rc = mdss_mdp_mixer_handoff(ctl, i, pipe);
+ if (rc) {
+ pr_err("failed to handoff mix%d\n", i);
+ goto exit;
+ }
+ }
+ }
+ }
+exit:
+ return rc;
+}
+
/**
* mdss_mdp_overlay_handoff() - Read MDP registers to handoff an active ctl path
* @mfd: Msm frame buffer structure associated with the fb device.
@@ -2708,10 +2769,8 @@
int rc = 0;
struct mdss_data_type *mdata = mfd_to_mdata(mfd);
struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
- int i, j;
- u32 reg;
- struct mdss_mdp_pipe *pipe = NULL;
struct mdss_mdp_ctl *ctl = NULL;
+ struct mdss_mdp_ctl *sctl = NULL;
if (!mdp5_data->ctl) {
ctl = __mdss_mdp_overlay_ctl_init(mfd);
@@ -2735,38 +2794,23 @@
ctl->clk_rate = mdss_mdp_get_clk_rate(MDSS_CLK_MDP_SRC);
pr_debug("Set the ctl clock rate to %d Hz\n", ctl->clk_rate);
- for (i = 0; i < mdata->nmixers_intf; i++) {
- reg = mdss_mdp_ctl_read(ctl, MDSS_MDP_REG_CTL_LAYER(i));
- pr_debug("for lm%d reg = 0x%09x\n", i, reg);
- for (j = MDSS_MDP_SSPP_VIG0; j < MDSS_MDP_MAX_SSPP; j++) {
- u32 cfg = j * 3;
- if ((j == MDSS_MDP_SSPP_VIG3) ||
- (j == MDSS_MDP_SSPP_RGB3)) {
- /* Add 2 to account for Cursor & Border bits */
- cfg += 2;
- }
- if (reg & (0x7 << cfg)) {
- pr_debug("Pipe %d staged\n", j);
- pipe = mdss_mdp_pipe_search(mdata, BIT(j));
- if (!pipe) {
- pr_warn("Invalid pipe %d staged\n", j);
- continue;
- }
+ rc = __mdss_mdp_ctl_handoff(ctl, mdata);
+ if (rc) {
+ pr_err("primary ctl handoff failed. rc=%d\n", rc);
+ goto error;
+ }
- rc = mdss_mdp_pipe_handoff(pipe);
- if (rc) {
- pr_err("Failed to handoff pipe num %d\n"
- , pipe->num);
- goto error;
- }
-
- rc = mdss_mdp_mixer_handoff(ctl, i, pipe);
- if (rc) {
- pr_err("failed to handoff mixer num %d\n"
- , i);
- goto error;
- }
- }
+ if (mfd->split_display) {
+ sctl = mdss_mdp_get_split_ctl(ctl);
+ if (!sctl) {
+ pr_err("cannot get secondary ctl. fail the handoff\n");
+ rc = -EPERM;
+ goto error;
+ }
+ rc = __mdss_mdp_ctl_handoff(sctl, mdata);
+ if (rc) {
+ pr_err("secondary ctl handoff failed. rc=%d\n", rc);
+ goto error;
}
}
diff --git a/drivers/video/msm/mdss/mdss_mdp_pipe.c b/drivers/video/msm/mdss/mdss_mdp_pipe.c
index b6f9b17..50bee17 100644
--- a/drivers/video/msm/mdss/mdss_mdp_pipe.c
+++ b/drivers/video/msm/mdss/mdss_mdp_pipe.c
@@ -28,6 +28,12 @@
#define PIPE_HALT_TIMEOUT_US 0x4000
+/* following offsets are relative to ctrl register bit offset */
+#define CLK_FORCE_ON_OFFSET 0x0
+#define CLK_FORCE_OFF_OFFSET 0x1
+/* following offsets are relative to status register bit offset */
+#define CLK_STATUS_OFFSET 0x0
+
static DEFINE_MUTEX(mdss_mdp_sspp_lock);
static DEFINE_MUTEX(mdss_mdp_smp_lock);
@@ -210,7 +216,7 @@
struct mdss_mdp_plane_sizes ps;
int i;
int rc = 0, rot_mode = 0, wb_mixer = 0;
- u32 nlines, format;
+ u32 nlines, format, seg_w;
u16 width;
width = pipe->src.w >> pipe->horz_deci;
@@ -222,19 +228,17 @@
return rc;
/*
* Override fetch strides with SMP buffer size for both the
- * planes
+ * planes. BWC line buffer needs to be divided into 16
+ * segments and every segment is aligned to format
+ * specific RAU size
*/
+ seg_w = DIV_ROUND_UP(pipe->src.w, 16);
if (pipe->src_fmt->fetch_planes == MDSS_MDP_PLANE_INTERLEAVED) {
- /*
- * BWC line buffer needs to be divided into 16
- * segments and every segment is aligned to format
- * specific RAU size
- */
- ps.ystride[0] = ALIGN(pipe->src.w / 16 , 32) * 16 *
- ps.rau_h[0] * pipe->src_fmt->bpp;
+ ps.ystride[0] = ALIGN(seg_w, 32) * 16 * ps.rau_h[0] *
+ pipe->src_fmt->bpp;
ps.ystride[1] = 0;
} else {
- u32 bwc_width = ALIGN(pipe->src.w / 16, 64) * 16;
+ u32 bwc_width = ALIGN(seg_w, 64) * 16;
ps.ystride[0] = bwc_width * ps.rau_h[0];
ps.ystride[1] = bwc_width * ps.rau_h[1];
/*
@@ -545,7 +549,7 @@
}
if (pipe && mdss_mdp_pipe_fetch_halt(pipe)) {
- pr_err("%d failed because vbif client is in bad state\n",
+ pr_err("%d failed because pipe is in bad state\n",
pipe->num);
atomic_dec(&pipe->ref_cnt);
return NULL;
@@ -677,6 +681,7 @@
if (pipe->play_cnt) {
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
+ mdss_mdp_pipe_fetch_halt(pipe);
mdss_mdp_pipe_sspp_term(pipe);
mdss_mdp_smp_free(pipe);
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
@@ -692,6 +697,53 @@
return 0;
}
+static int mdss_mdp_is_pipe_idle(struct mdss_mdp_pipe *pipe,
+ bool ignore_force_on)
+{
+ u32 reg_val;
+ u32 vbif_idle_mask, forced_on_mask, clk_status_idle_mask;
+ bool is_idle = false, is_forced_on;
+ struct mdss_data_type *mdata = mdss_mdp_get_mdata();
+
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
+
+ forced_on_mask = BIT(pipe->clk_ctrl.bit_off + CLK_FORCE_ON_OFFSET);
+ reg_val = readl_relaxed(mdata->mdp_base + pipe->clk_ctrl.reg_off);
+ is_forced_on = (reg_val & forced_on_mask) ? true : false;
+
+ pr_debug("pipe#:%d clk_ctrl: 0x%x forced_on_mask: 0x%x\n", pipe->num,
+ reg_val, forced_on_mask);
+ /* if forced on then no need to check status */
+ if (!is_forced_on) {
+ clk_status_idle_mask =
+ BIT(pipe->clk_status.bit_off + CLK_STATUS_OFFSET);
+ reg_val = readl_relaxed(mdata->mdp_base +
+ pipe->clk_status.reg_off);
+
+ if (reg_val & clk_status_idle_mask)
+ is_idle = false;
+
+ pr_debug("pipe#:%d clk_status:0x%x clk_status_idle_mask:0x%x\n",
+ pipe->num, reg_val, clk_status_idle_mask);
+ }
+
+ if (!ignore_force_on && (is_forced_on || !is_idle))
+ goto exit;
+
+ vbif_idle_mask = BIT(pipe->xin_id + 16);
+ reg_val = readl_relaxed(mdata->vbif_base + MMSS_VBIF_XIN_HALT_CTRL1);
+
+ if (reg_val & vbif_idle_mask)
+ is_idle = true;
+
+ pr_debug("pipe#:%d XIN_HALT_CTRL1: 0x%x\n", pipe->num, reg_val);
+
+exit:
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
+
+ return is_idle;
+}
+
/**
* mdss_mdp_pipe_fetch_halt() - Halt VBIF client corresponding to specified pipe
* @pipe: pointer to the pipe data structure which needs to be halted.
@@ -711,18 +763,15 @@
u32 reg_val, idle_mask, status;
struct mdss_data_type *mdata = mdss_mdp_get_mdata();
- mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
-
- idle_mask = BIT(pipe->xin_id + 16);
- reg_val = readl_relaxed(mdata->vbif_base + MMSS_VBIF_XIN_HALT_CTRL1);
-
- is_idle = (reg_val & idle_mask) ? true : false;
+ is_idle = mdss_mdp_is_pipe_idle(pipe, true);
if (!is_idle) {
- pr_debug("%pS: pipe%d is not idle. xin_id=%d halt_ctrl1=0x%x\n",
- __builtin_return_address(0), pipe->num, pipe->xin_id,
- reg_val);
+ pr_err("%pS: pipe%d is not idle. xin_id=%d\n",
+ __builtin_return_address(0), pipe->num, pipe->xin_id);
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
mutex_lock(&mdata->reg_lock);
+ idle_mask = BIT(pipe->xin_id + 16);
+
reg_val = readl_relaxed(mdata->vbif_base +
MMSS_VBIF_XIN_HALT_CTRL0);
writel_relaxed(reg_val | BIT(pipe->xin_id),
@@ -743,9 +792,10 @@
MMSS_VBIF_XIN_HALT_CTRL0);
writel_relaxed(reg_val & ~BIT(pipe->xin_id),
mdata->vbif_base + MMSS_VBIF_XIN_HALT_CTRL0);
+
mutex_unlock(&mdata->reg_lock);
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
}
- mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
return rc;
}
@@ -1065,7 +1115,7 @@
static int mdss_mdp_pipe_solidfill_setup(struct mdss_mdp_pipe *pipe)
{
int ret;
- u32 secure, format;
+ u32 secure, format, unpack;
pr_debug("solid fill setup on pnum=%d\n", pipe->num);
@@ -1078,9 +1128,13 @@
format = MDSS_MDP_FMT_SOLID_FILL;
secure = (pipe->flags & MDP_SECURE_OVERLAY_SESSION ? 0xF : 0x0);
+ /* support ARGB color format only */
+ unpack = (C3_ALPHA << 24) | (C2_R_Cr << 16) |
+ (C1_B_Cb << 8) | (C0_G_Y << 0);
mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC_FORMAT, format);
mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC_CONSTANT_COLOR,
pipe->bg_color);
+ mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC_UNPACK_PATTERN, unpack);
mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC_ADDR_SW_STATUS, secure);
return 0;
diff --git a/drivers/video/msm/mdss/mdss_mdp_pp.c b/drivers/video/msm/mdss/mdss_mdp_pp.c
index 5c9ad9c..37b71c7 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-2013, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -1306,19 +1306,19 @@
goto error;
}
+ mutex_lock(&hist_info->hist_mutex);
+ spin_lock_irqsave(&hist_info->hist_lock, flag);
if (hist_info->col_en) {
*op |= op_flags;
- mutex_lock(&hist_info->hist_mutex);
- spin_lock_irqsave(&hist_info->hist_lock, flag);
col_state = hist_info->col_state;
if (col_state == HIST_IDLE) {
/* Kick off collection */
writel_relaxed(1, base + kick_base);
hist_info->col_state = HIST_START;
}
- spin_unlock_irqrestore(&hist_info->hist_lock, flag);
- mutex_unlock(&hist_info->hist_mutex);
}
+ spin_unlock_irqrestore(&hist_info->hist_lock, flag);
+ mutex_unlock(&hist_info->hist_mutex);
ret = 0;
error:
return ret;
@@ -1625,7 +1625,7 @@
*/
int mdss_mdp_pp_resume(struct mdss_mdp_ctl *ctl, u32 dspp_num)
{
- u32 flags = 0, disp_num, bl;
+ u32 flags = 0, disp_num, bl, ret = 0;
struct pp_sts_type pp_sts;
struct mdss_ad_info *ad;
struct mdss_data_type *mdata = ctl->mdata;
@@ -1636,7 +1636,9 @@
disp_num = ctl->mfd->index;
if (dspp_num < mdata->nad_cfgs) {
- ad = &mdata->ad_cfgs[dspp_num];
+ ret = mdss_mdp_get_ad(ctl->mfd, &ad);
+ if (ret)
+ return ret;
if (PP_AD_STATE_CFG & ad->state)
pp_ad_cfg_write(&mdata->ad_off[dspp_num], ad);
@@ -1748,6 +1750,8 @@
&mdss_pp_res->dspp_hist[i].hist_mutex);
spin_lock_init(
&mdss_pp_res->dspp_hist[i].hist_lock);
+ init_completion(
+ &mdss_pp_res->dspp_hist[i].comp);
}
}
}
@@ -1756,6 +1760,7 @@
for (i = 0; i < mdata->nvig_pipes; i++) {
mutex_init(&vig[i].pp_res.hist.hist_mutex);
spin_lock_init(&vig[i].pp_res.hist.hist_lock);
+ init_completion(&vig[i].pp_res.hist.comp);
}
if (!mdata->pp_bus_hdl) {
pp_bus_pdata = &mdp_pp_bus_scale_table;
@@ -2885,22 +2890,23 @@
mutex_lock(&hist_info->hist_mutex);
/* check if it is idle */
+ spin_lock_irqsave(&hist_info->hist_lock, flag);
if (hist_info->col_en) {
+ spin_unlock_irqrestore(&hist_info->hist_lock, flag);
pr_info("%s Hist collection has already been enabled %d",
__func__, (u32) ctl_base);
ret = -EINVAL;
goto exit;
}
- hist_info->frame_cnt = req->frame_cnt;
- init_completion(&hist_info->comp);
- hist_info->hist_cnt_read = 0;
- hist_info->hist_cnt_sent = 0;
- hist_info->hist_cnt_time = 0;
- spin_lock_irqsave(&hist_info->hist_lock, flag);
hist_info->read_request = 0;
hist_info->col_state = HIST_RESET;
hist_info->col_en = true;
spin_unlock_irqrestore(&hist_info->hist_lock, flag);
+ hist_info->frame_cnt = req->frame_cnt;
+ INIT_COMPLETION(hist_info->comp);
+ hist_info->hist_cnt_read = 0;
+ hist_info->hist_cnt_sent = 0;
+ hist_info->hist_cnt_time = 0;
mdss_mdp_hist_intr_req(&mdata->hist_intr, 3 << shift_bit, true);
writel_relaxed(req->frame_cnt, ctl_base + 8);
/* Kick out reset start */
@@ -3011,17 +3017,18 @@
struct mdss_data_type *mdata = mdss_mdp_get_mdata();
mutex_lock(&hist_info->hist_mutex);
+ spin_lock_irqsave(&hist_info->hist_lock, flag);
if (hist_info->col_en == false) {
+ spin_unlock_irqrestore(&hist_info->hist_lock, flag);
pr_debug("Histogram already disabled (%d)", (u32) ctl_base);
ret = -EINVAL;
goto exit;
}
- complete_all(&hist_info->comp);
- spin_lock_irqsave(&hist_info->hist_lock, flag);
hist_info->col_en = false;
hist_info->col_state = HIST_UNKNOWN;
spin_unlock_irqrestore(&hist_info->hist_lock, flag);
mdss_mdp_hist_intr_req(&mdata->hist_intr, done_bit, false);
+ complete_all(&hist_info->comp);
writel_relaxed(BIT(1), ctl_base);/* cancel */
ret = 0;
exit:
@@ -3262,12 +3269,13 @@
struct mdss_mdp_pipe *pipe;
mutex_lock(&hist_info->hist_mutex);
+ spin_lock_irqsave(&hist_info->hist_lock, flag);
if ((hist_info->col_en == 0) ||
(hist_info->col_state == HIST_UNKNOWN)) {
+ spin_unlock_irqrestore(&hist_info->hist_lock, flag);
ret = -EINVAL;
goto hist_collect_exit;
}
- spin_lock_irqsave(&hist_info->hist_lock, flag);
/* wait for hist done if cache has no data */
if (hist_info->col_state != HIST_READY) {
spin_unlock_irqrestore(&hist_info->hist_lock, flag);
@@ -3283,9 +3291,9 @@
&(hist_info->comp), timeout);
mutex_lock(&hist_info->hist_mutex);
+ spin_lock_irqsave(&hist_info->hist_lock, flag);
if (wait_ret == 0) {
ret = -ETIMEDOUT;
- spin_lock_irqsave(&hist_info->hist_lock, flag);
pr_debug("bin collection timedout, state %d",
hist_info->col_state);
/*
@@ -3300,37 +3308,33 @@
*/
hist_info->hist_cnt_time++;
hist_info->col_state = HIST_READY;
- spin_unlock_irqrestore(&hist_info->hist_lock, flag);
} else if (wait_ret < 0) {
+ spin_unlock_irqrestore(&hist_info->hist_lock, flag);
ret = -EINTR;
pr_debug("%s: bin collection interrupted",
__func__);
goto hist_collect_exit;
}
- if (hist_info->col_state != HIST_READY) {
+ if (hist_info->col_state != HIST_READY &&
+ hist_info->col_state != HIST_UNKNOWN) {
ret = -ENODATA;
- spin_lock_irqsave(&hist_info->hist_lock, flag);
hist_info->col_state = HIST_READY;
- spin_unlock_irqrestore(&hist_info->hist_lock, flag);
pr_debug("%s: state is not ready: %d",
__func__, hist_info->col_state);
}
- } else {
- spin_unlock_irqrestore(&hist_info->hist_lock, flag);
}
- spin_lock_irqsave(&hist_info->hist_lock, flag);
if (hist_info->col_state == HIST_READY) {
+ hist_info->col_state = HIST_IDLE;
spin_unlock_irqrestore(&hist_info->hist_lock, flag);
v_base = ctl_base + 0x1C;
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
sum = pp_hist_read(v_base, hist_info);
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
- spin_lock_irqsave(&hist_info->hist_lock, flag);
if (expect_sum && sum != expect_sum)
ret = -ENODATA;
- hist_info->col_state = HIST_IDLE;
+ } else {
+ spin_unlock_irqrestore(&hist_info->hist_lock, flag);
}
- spin_unlock_irqrestore(&hist_info->hist_lock, flag);
hist_collect_exit:
mutex_unlock(&hist_info->hist_mutex);
return ret;
diff --git a/drivers/video/msm/mdss/msm_mdss_io_8974.c b/drivers/video/msm/mdss/msm_mdss_io_8974.c
index a0663e3..834ef66 100644
--- a/drivers/video/msm/mdss/msm_mdss_io_8974.c
+++ b/drivers/video/msm/mdss/msm_mdss_io_8974.c
@@ -259,7 +259,7 @@
return 0;
}
-int mdss_dsi_bus_clk_start(struct mdss_dsi_ctrl_pdata *ctrl_pdata)
+static int mdss_dsi_bus_clk_start(struct mdss_dsi_ctrl_pdata *ctrl_pdata)
{
int rc = 0;
@@ -303,7 +303,7 @@
return rc;
}
-void mdss_dsi_bus_clk_stop(struct mdss_dsi_ctrl_pdata *ctrl_pdata)
+static void mdss_dsi_bus_clk_stop(struct mdss_dsi_ctrl_pdata *ctrl_pdata)
{
if (ctrl_pdata->mmss_misc_ahb_clk)
clk_disable_unprepare(ctrl_pdata->mmss_misc_ahb_clk);
@@ -408,11 +408,6 @@
pr_debug("%s: ndx=%d\n", __func__, ctrl_pdata->ndx);
- if (ctrl_pdata->mdss_dsi_clk_on) {
- pr_info("%s: mdss_dsi_clks already ON\n", __func__);
- return 0;
- }
-
rc = clk_enable(ctrl_pdata->esc_clk);
if (rc) {
pr_err("%s: Failed to enable dsi esc clk\n", __func__);
@@ -431,8 +426,6 @@
goto pixel_clk_err;
}
- ctrl_pdata->mdss_dsi_clk_on = 1;
-
return rc;
pixel_clk_err:
@@ -452,19 +445,12 @@
pr_debug("%s: ndx=%d\n", __func__, ctrl_pdata->ndx);
- if (ctrl_pdata->mdss_dsi_clk_on == 0) {
- pr_info("%s: mdss_dsi_clks already OFF\n", __func__);
- return;
- }
-
clk_disable(ctrl_pdata->esc_clk);
clk_disable(ctrl_pdata->pixel_clk);
clk_disable(ctrl_pdata->byte_clk);
-
- ctrl_pdata->mdss_dsi_clk_on = 0;
}
-int mdss_dsi_link_clk_start(struct mdss_dsi_ctrl_pdata *ctrl)
+static int mdss_dsi_link_clk_start(struct mdss_dsi_ctrl_pdata *ctrl)
{
int rc = 0;
@@ -494,90 +480,191 @@
return rc;
}
-void mdss_dsi_link_clk_stop(struct mdss_dsi_ctrl_pdata *ctrl)
+static void mdss_dsi_link_clk_stop(struct mdss_dsi_ctrl_pdata *ctrl)
{
mdss_dsi_link_clk_disable(ctrl);
mdss_dsi_link_clk_unprepare(ctrl);
}
-static void mdss_dsi_clk_ctrl_sub(struct mdss_dsi_ctrl_pdata *ctrl, int enable)
+static int __mdss_dsi_update_clk_cnt(u32 *clk_cnt, int enable)
{
int changed = 0;
if (enable) {
- if (ctrl->clk_cnt_sub == 0)
+ if (*clk_cnt == 0)
changed++;
- ctrl->clk_cnt_sub++;
+ (*clk_cnt)++;
} else {
- if (ctrl->clk_cnt_sub) {
- ctrl->clk_cnt_sub--;
- if (ctrl->clk_cnt_sub == 0)
+ if (*clk_cnt != 0) {
+ (*clk_cnt)--;
+ if (*clk_cnt == 0)
changed++;
} else {
- pr_debug("%s: Can not be turned off\n", __func__);
+ pr_debug("%s: clk cnt already zero\n", __func__);
}
}
- pr_debug("%s: ndx=%d clk_cnt_sub=%d changed=%d enable=%d\n",
- __func__, ctrl->ndx, ctrl->clk_cnt_sub, changed, enable);
- if (changed) {
- if (enable) {
- if (mdss_dsi_bus_clk_start(ctrl) == 0)
- mdss_dsi_link_clk_start(ctrl);
- } else {
- mdss_dsi_link_clk_stop(ctrl);
- mdss_dsi_bus_clk_stop(ctrl);
+ return changed;
+}
+
+static int mdss_dsi_clk_ctrl_sub(struct mdss_dsi_ctrl_pdata *ctrl,
+ u8 clk_type, int enable)
+{
+ int rc = 0;
+
+ pr_debug("%s: ndx=%d clk_type=%08x enable=%d\n", __func__,
+ ctrl->ndx, clk_type, enable);
+
+ if (enable) {
+ if (clk_type & DSI_BUS_CLKS) {
+ rc = mdss_dsi_bus_clk_start(ctrl);
+ if (rc) {
+ pr_err("Failed to start bus clocks. rc=%d\n",
+ rc);
+ goto error;
+ }
}
+ if (clk_type & DSI_LINK_CLKS) {
+ rc = mdss_dsi_link_clk_start(ctrl);
+ if (rc) {
+ pr_err("Failed to start link clocks. rc=%d\n",
+ rc);
+ if (clk_type & DSI_BUS_CLKS)
+ mdss_dsi_bus_clk_stop(ctrl);
+ goto error;
+ }
+ }
+ } else {
+ if (clk_type & DSI_LINK_CLKS)
+ mdss_dsi_link_clk_stop(ctrl);
+ if (clk_type & DSI_BUS_CLKS)
+ mdss_dsi_bus_clk_stop(ctrl);
}
+
+error:
+ return rc;
}
static DEFINE_MUTEX(dsi_clk_lock); /* per system */
-bool __mdss_dsi_clk_enabled(struct mdss_dsi_ctrl_pdata *ctrl)
+bool __mdss_dsi_clk_enabled(struct mdss_dsi_ctrl_pdata *ctrl, u8 clk_type)
{
- bool enabled;
+ bool bus_enabled = true;
+ bool link_enabled = true;
+
mutex_lock(&dsi_clk_lock);
- enabled = ctrl->clk_cnt ? true : false;
+ if (clk_type & DSI_BUS_CLKS)
+ bus_enabled = ctrl->bus_clk_cnt ? true : false;
+ if (clk_type & DSI_LINK_CLKS)
+ link_enabled = ctrl->link_clk_cnt ? true : false;
mutex_unlock(&dsi_clk_lock);
- return enabled;
+ return bus_enabled && link_enabled;
}
-void mdss_dsi_clk_ctrl(struct mdss_dsi_ctrl_pdata *ctrl, int enable)
+int mdss_dsi_clk_ctrl(struct mdss_dsi_ctrl_pdata *ctrl,
+ u8 clk_type, int enable)
{
- int changed = 0;
- struct mdss_dsi_ctrl_pdata *sctrl = NULL;
+ int rc = 0;
+ int changed = 0, m_changed = 0;
+ struct mdss_dsi_ctrl_pdata *mctrl = NULL;
+
+ if (!ctrl) {
+ pr_err("%s: Invalid arg\n", __func__);
+ return -EINVAL;
+ }
+
+ /*
+ * In broadcast mode, we need to enable clocks for the
+ * master controller as well when enabling clocks for the
+ * slave controller
+ */
+ if (mdss_dsi_is_slave_ctrl(ctrl)) {
+ mctrl = mdss_dsi_get_master_ctrl();
+ if (!mctrl)
+ pr_warn("%s: Unable to get master control\n", __func__);
+ }
+
+ pr_debug("%s++: ndx=%d clk_type=%d bus_clk_cnt=%d link_clk_cnt=%d",
+ __func__, ctrl->ndx, clk_type, ctrl->bus_clk_cnt,
+ ctrl->link_clk_cnt);
+ pr_debug("%s++: mctrl=%s m_bus_clk_cnt=%d m_link_clk_cnt=%d\n, enable=%d\n",
+ __func__, mctrl ? "yes" : "no", mctrl ? mctrl->bus_clk_cnt : -1,
+ mctrl ? mctrl->link_clk_cnt : -1, enable);
mutex_lock(&dsi_clk_lock);
- if (enable) {
- if (ctrl->clk_cnt == 0)
- changed++;
- ctrl->clk_cnt++;
- } else {
- if (ctrl->clk_cnt) {
- ctrl->clk_cnt--;
- if (ctrl->clk_cnt == 0)
- changed++;
- } else {
- pr_debug("%s: Can not be turned off\n", __func__);
- }
+ if (clk_type & DSI_BUS_CLKS) {
+ changed = __mdss_dsi_update_clk_cnt(&ctrl->bus_clk_cnt,
+ enable);
+ if (changed && mctrl)
+ m_changed = __mdss_dsi_update_clk_cnt(
+ &mctrl->bus_clk_cnt, enable);
}
- pr_debug("%s: ndx=%d clk_cnt=%d changed=%d enable=%d\n",
- __func__, ctrl->ndx, ctrl->clk_cnt, changed, enable);
- if (ctrl->flags & DSI_FLAG_CLOCK_MASTER)
- sctrl = mdss_dsi_ctrl_slave(ctrl);
+ if (clk_type & DSI_LINK_CLKS) {
+ changed += __mdss_dsi_update_clk_cnt(&ctrl->link_clk_cnt,
+ enable);
+ if (changed && mctrl)
+ m_changed += __mdss_dsi_update_clk_cnt(
+ &mctrl->link_clk_cnt, enable);
+ }
if (changed) {
- if (enable && sctrl)
- mdss_dsi_clk_ctrl_sub(sctrl, enable);
+ if (enable && m_changed) {
+ rc = mdss_dsi_clk_ctrl_sub(mctrl, clk_type, enable);
+ if (rc) {
+ pr_err("Failed to start mctrl clocks. rc=%d\n",
+ rc);
+ goto error_mctrl_start;
+ }
+ }
- mdss_dsi_clk_ctrl_sub(ctrl, enable);
+ rc = mdss_dsi_clk_ctrl_sub(ctrl, clk_type, enable);
+ if (rc) {
+ pr_err("Failed to %s ctrl clocks. rc=%d\n",
+ (enable ? "start" : "stop"), rc);
+ goto error_ctrl;
+ }
- if (!enable && sctrl)
- mdss_dsi_clk_ctrl_sub(sctrl, enable);
+ if (!enable && m_changed) {
+ rc = mdss_dsi_clk_ctrl_sub(mctrl, clk_type, enable);
+ if (rc) {
+ pr_err("Failed to stop mctrl clocks. rc=%d\n",
+ rc);
+ goto error_mctrl_stop;
+ }
+ }
}
+ goto no_error;
+
+error_mctrl_stop:
+ mdss_dsi_clk_ctrl_sub(ctrl, clk_type, enable ? 0 : 1);
+error_ctrl:
+ mdss_dsi_clk_ctrl_sub(mctrl, clk_type, 0);
+error_mctrl_start:
+ if (clk_type & DSI_BUS_CLKS) {
+ if (mctrl)
+ __mdss_dsi_update_clk_cnt(&mctrl->bus_clk_cnt,
+ enable ? 0 : 1);
+ __mdss_dsi_update_clk_cnt(&ctrl->bus_clk_cnt, enable ? 0 : 1);
+ }
+ if (clk_type & DSI_LINK_CLKS) {
+ if (mctrl)
+ __mdss_dsi_update_clk_cnt(&mctrl->link_clk_cnt,
+ enable ? 0 : 1);
+ __mdss_dsi_update_clk_cnt(&ctrl->link_clk_cnt, enable ? 0 : 1);
+ }
+
+no_error:
mutex_unlock(&dsi_clk_lock);
+ pr_debug("%s++: ndx=%d clk_type=%d bus_clk_cnt=%d link_clk_cnt=%d changed=%d",
+ __func__, ctrl->ndx, clk_type, ctrl->bus_clk_cnt,
+ ctrl->link_clk_cnt, changed);
+ pr_debug("%s++: mctrl=%s m_bus_clk_cnt=%d m_link_clk_cnt=%d\n, m_changed=%d, enable=%d\n",
+ __func__, mctrl ? "yes" : "no", mctrl ? mctrl->bus_clk_cnt : -1,
+ mctrl ? mctrl->link_clk_cnt : -1, m_changed, enable);
+
+ return rc;
}
void mdss_dsi_phy_sw_reset(unsigned char *ctrl_base)
@@ -594,29 +681,39 @@
void mdss_dsi_phy_disable(struct mdss_dsi_ctrl_pdata *ctrl)
{
- static struct mdss_dsi_ctrl_pdata *left_ctrl;
+ struct mdss_dsi_ctrl_pdata *ctrl0 = NULL;
if (ctrl == NULL) {
pr_err("%s: Invalid input data\n", __func__);
return;
}
- if (left_ctrl &&
- (ctrl->panel_data.panel_info.pdest == DISPLAY_1))
+ /*
+ * In dual-dsi configuration, the phy should be disabled for the
+ * first controller only when the second controller is disabled.
+ * This is true regardless of whether broadcast mode is enabled
+ * or not.
+ */
+ if ((ctrl->ndx == DSI_CTRL_0) &&
+ mdss_dsi_get_ctrl_by_index(DSI_CTRL_1)) {
+ pr_debug("%s: Dual dsi detected. skipping config for ctrl%d\n",
+ __func__, ctrl->ndx);
return;
-
- if (left_ctrl &&
- (ctrl->panel_data.panel_info.pdest
- ==
- DISPLAY_2)) {
- MIPI_OUTP(left_ctrl->ctrl_base + 0x0470,
- 0x000);
- MIPI_OUTP(left_ctrl->ctrl_base + 0x0598,
- 0x000);
}
- MIPI_OUTP(ctrl->ctrl_base + 0x0470, 0x000);
- MIPI_OUTP(ctrl->ctrl_base + 0x0598, 0x000);
+ if (ctrl->ndx == DSI_CTRL_1) {
+ ctrl0 = mdss_dsi_get_ctrl_by_index(DSI_CTRL_0);
+ if (ctrl0) {
+ MIPI_OUTP(ctrl0->phy_io.base + 0x0170, 0x000);
+ MIPI_OUTP(ctrl0->phy_io.base + 0x0298, 0x000);
+ } else {
+ pr_warn("%s: Unable to get control%d\n",
+ __func__, DSI_CTRL_0);
+ }
+ }
+
+ MIPI_OUTP(ctrl->phy_io.base + 0x0170, 0x000);
+ MIPI_OUTP(ctrl->phy_io.base + 0x0298, 0x000);
/*
* Wait for the registers writes to complete in order to
@@ -629,7 +726,7 @@
{
struct mdss_dsi_phy_ctrl *pd;
int i, off, ln, offset;
- struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL;
+ struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL, *temp_ctrl = NULL;
ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata,
panel_data);
@@ -637,67 +734,75 @@
pr_err("%s: Invalid input data\n", __func__);
return;
}
+ temp_ctrl = ctrl_pdata;
pd = &(((ctrl_pdata->panel_data).panel_info.mipi).dsi_phy_db);
/* Strength ctrl 0 */
- MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x0484, pd->strength[0]);
+ MIPI_OUTP((ctrl_pdata->phy_io.base) + 0x0184, pd->strength[0]);
- /* phy regulator ctrl settings. Both the DSI controller
- have one regulator */
- if ((ctrl_pdata->panel_data).panel_info.pdest == DISPLAY_1)
- off = 0x0580;
- else
- off = 0x0580 - 0x600;
+ /*
+ * Phy regulator ctrl settings.
+ * In dual dsi configuration, the second controller also uses
+ * the regulators of the first controller, irrespective of whether
+ * broadcast mode is enabled or not.
+ */
+ if (ctrl_pdata->ndx == DSI_CTRL_1) {
+ temp_ctrl = mdss_dsi_get_ctrl_by_index(DSI_CTRL_0);
+ if (!temp_ctrl) {
+ pr_err("%s: Unable to get master ctrl\n", __func__);
+ return;
+ }
+ }
/* Regulator ctrl 0 */
- MIPI_OUTP((ctrl_pdata->ctrl_base) + off + (4 * 0), 0x0);
+ MIPI_OUTP((temp_ctrl->phy_io.base) + 0x280, 0x0);
/* Regulator ctrl - CAL_PWR_CFG */
- MIPI_OUTP((ctrl_pdata->ctrl_base) + off + (4 * 6), pd->regulator[6]);
+ MIPI_OUTP((temp_ctrl->phy_io.base) + 0x298, pd->regulator[6]);
/* Regulator ctrl - TEST */
- MIPI_OUTP((ctrl_pdata->ctrl_base) + off + (4 * 5), pd->regulator[5]);
+ MIPI_OUTP((temp_ctrl->phy_io.base) + 0x294, pd->regulator[5]);
/* Regulator ctrl 3 */
- MIPI_OUTP((ctrl_pdata->ctrl_base) + off + (4 * 3), pd->regulator[3]);
+ MIPI_OUTP((temp_ctrl->phy_io.base) + 0x28c, pd->regulator[3]);
/* Regulator ctrl 2 */
- MIPI_OUTP((ctrl_pdata->ctrl_base) + off + (4 * 2), pd->regulator[2]);
+ MIPI_OUTP((temp_ctrl->phy_io.base) + 0x288, pd->regulator[2]);
/* Regulator ctrl 1 */
- MIPI_OUTP((ctrl_pdata->ctrl_base) + off + (4 * 1), pd->regulator[1]);
+ MIPI_OUTP((temp_ctrl->phy_io.base) + 0x284, pd->regulator[1]);
/* Regulator ctrl 0 */
- MIPI_OUTP((ctrl_pdata->ctrl_base) + off + (4 * 0), pd->regulator[0]);
+ MIPI_OUTP((temp_ctrl->phy_io.base) + 0x280, pd->regulator[0]);
/* Regulator ctrl 4 */
- MIPI_OUTP((ctrl_pdata->ctrl_base) + off + (4 * 4), pd->regulator[4]);
+ MIPI_OUTP((temp_ctrl->phy_io.base) + 0x290, pd->regulator[4]);
/* LDO ctrl 0 */
if ((ctrl_pdata->panel_data).panel_info.pdest == DISPLAY_1)
- MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x4dc, 0x00);
+ MIPI_OUTP((ctrl_pdata->phy_io.base) + 0x1dc, 0x00);
else
- MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x4dc, 0x00);
+ MIPI_OUTP((ctrl_pdata->phy_io.base) + 0x1dc, 0x00);
- off = 0x0440; /* phy timing ctrl 0 - 11 */
+ off = 0x0140; /* phy timing ctrl 0 - 11 */
for (i = 0; i < 12; i++) {
- MIPI_OUTP((ctrl_pdata->ctrl_base) + off, pd->timing[i]);
+ MIPI_OUTP((ctrl_pdata->phy_io.base) + off, pd->timing[i]);
wmb();
off += 4;
}
/* MMSS_DSI_0_PHY_DSIPHY_CTRL_1 */
- MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x0474, 0x00);
+ MIPI_OUTP((ctrl_pdata->phy_io.base) + 0x0174, 0x00);
/* MMSS_DSI_0_PHY_DSIPHY_CTRL_0 */
- MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x0470, 0x5f);
+ MIPI_OUTP((ctrl_pdata->phy_io.base) + 0x0170, 0x5f);
wmb();
/* Strength ctrl 1 */
- MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x0488, pd->strength[1]);
+ MIPI_OUTP((ctrl_pdata->phy_io.base) + 0x0188, pd->strength[1]);
wmb();
/* 4 lanes + clk lane configuration */
/* lane config n * (0 - 4) & DataPath setup */
for (ln = 0; ln < 5; ln++) {
- off = 0x0300 + (ln * 0x40);
+ off = (ln * 0x40);
for (i = 0; i < 9; i++) {
offset = i + (ln * 9);
- MIPI_OUTP((ctrl_pdata->ctrl_base) + off,
+ MIPI_OUTP((ctrl_pdata->phy_io.base) + off,
pd->lanecfg[offset]);
wmb();
off += 4;
@@ -705,19 +810,19 @@
}
/* MMSS_DSI_0_PHY_DSIPHY_CTRL_0 */
- MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x0470, 0x5f);
+ MIPI_OUTP((ctrl_pdata->phy_io.base) + 0x0170, 0x5f);
wmb();
/* DSI_0_PHY_DSIPHY_GLBL_TEST_CTRL */
if ((ctrl_pdata->panel_data).panel_info.pdest == DISPLAY_1)
- MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x04d4, 0x01);
+ MIPI_OUTP((ctrl_pdata->phy_io.base) + 0x01d4, 0x01);
else
- MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x04d4, 0x00);
+ MIPI_OUTP((ctrl_pdata->phy_io.base) + 0x01d4, 0x00);
wmb();
- off = 0x04b4; /* phy BIST ctrl 0 - 5 */
+ off = 0x01b4; /* phy BIST ctrl 0 - 5 */
for (i = 0; i < 6; i++) {
- MIPI_OUTP((ctrl_pdata->ctrl_base) + off, pd->bistctrl[i]);
+ MIPI_OUTP((ctrl_pdata->phy_io.base) + off, pd->bistctrl[i]);
wmb();
off += 4;
}
diff --git a/include/linux/devfreq.h b/include/linux/devfreq.h
index 70b3eef..3ab25c3 100644
--- a/include/linux/devfreq.h
+++ b/include/linux/devfreq.h
@@ -51,10 +51,10 @@
* bound (greatest lower bound)
*/
#define DEVFREQ_FLAG_LEAST_UPPER_BOUND 0x1
-#define DEVFREQ_FLAG_WAKEUP_MAXFREQ 0x2
-#define DEVFREQ_FLAG_FAST_HINT 0x2
-#define DEVFREQ_FLAG_SLOW_HINT 0x4
+#define DEVFREQ_FLAG_FAST_HINT 0x2
+#define DEVFREQ_FLAG_SLOW_HINT 0x4
+#define DEVFREQ_FLAG_WAKEUP_MAXFREQ 0x8
/**
* struct devfreq_governor_data - mapping to per device governor data
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index bff056d..e53c350 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -197,6 +197,12 @@
void *handler_priv;
};
+enum dev_state {
+ DEV_SUSPENDING = 1,
+ DEV_SUSPENDED,
+ DEV_RESUMED,
+};
+
struct mmc_host {
struct device *parent;
struct device class_dev;
@@ -421,6 +427,7 @@
struct delayed_work work;
enum mmc_load state;
} clk_scaling;
+ enum dev_state dev_status;
unsigned long private[0] ____cacheline_aligned;
};
diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h
index 08ca341..65a3fe1 100644
--- a/include/linux/mmc/sdhci.h
+++ b/include/linux/mmc/sdhci.h
@@ -160,6 +160,11 @@
*/
#define SDHCI_QUIRK2_DIVIDE_TOUT_BY_4 (1 << 9)
+/*
+ * Some SDHC controllers are unable to handle data-end bit error in
+ * 1-bit mode of SDIO.
+ */
+#define SDHCI_QUIRK2_IGN_DATA_END_BIT_ERROR (1<<9)
int irq; /* Device IRQ */
void __iomem *ioaddr; /* Mapped address */
@@ -252,6 +257,9 @@
struct mutex ios_mutex;
enum sdhci_power_policy power_policy;
+ bool irq_enabled; /* host irq status flag */
+ bool async_int_supp; /* async support to rxv int, when clks are off */
+ bool disable_sdio_irq_deferred; /* status of disabling sdio irq */
u32 auto_cmd_err_sts;
unsigned long private[0] ____cacheline_aligned;
};
diff --git a/include/linux/msm_ipa.h b/include/linux/msm_ipa.h
index b2229d3..9368d8f 100644
--- a/include/linux/msm_ipa.h
+++ b/include/linux/msm_ipa.h
@@ -64,6 +64,11 @@
#define IPA_RESOURCE_NAME_MAX 20
/**
+ * max number of interface properties
+ */
+#define IPA_NUM_PROPS_MAX 20
+
+/**
* size of the mac address
*/
#define IPA_MAC_ADDR_SIZE 6
diff --git a/include/linux/msm_mdp.h b/include/linux/msm_mdp.h
index e332368..3ec92e6 100644
--- a/include/linux/msm_mdp.h
+++ b/include/linux/msm_mdp.h
@@ -85,6 +85,30 @@
#define MDP_IMGTYPE2_START 0x10000
#define MSMFB_DRIVER_VERSION 0xF9E8D701
+/* HW Revisions for different MDSS targets */
+#define MDSS_GET_MAJOR(rev) ((rev) >> 28)
+#define MDSS_GET_MINOR(rev) (((rev) >> 16) & 0xFFF)
+#define MDSS_GET_STEP(rev) ((rev) & 0xFFFF)
+#define MDSS_GET_MAJOR_MINOR(rev) ((rev) >> 16)
+
+#define IS_MDSS_MAJOR_MINOR_SAME(rev1, rev2) \
+ (MDSS_GET_MAJOR_MINOR((rev1)) == MDSS_GET_MAJOR_MINOR((rev2)))
+
+#define MDSS_MDP_REV(major, minor, step) \
+ ((((major) & 0x000F) << 28) | \
+ (((minor) & 0x0FFF) << 16) | \
+ ((step) & 0xFFFF))
+
+#define MDSS_MDP_HW_REV_100 MDSS_MDP_REV(1, 0, 0) /* 8974 v1.0 */
+#define MDSS_MDP_HW_REV_101 MDSS_MDP_REV(1, 1, 0) /* 8x26 v1.0 */
+#define MDSS_MDP_HW_REV_101_1 MDSS_MDP_REV(1, 1, 1) /* 8x26 v2.0, 8926 v1.0 */
+#define MDSS_MDP_HW_REV_101_2 MDSS_MDP_REV(1, 1, 2) /* 8926 v2.0 */
+#define MDSS_MDP_HW_REV_102 MDSS_MDP_REV(1, 2, 0) /* 8974 v2.0 */
+#define MDSS_MDP_HW_REV_102_1 MDSS_MDP_REV(1, 2, 1) /* 8974 v3.0 (Pro) */
+#define MDSS_MDP_HW_REV_103 MDSS_MDP_REV(1, 3, 0) /* 8084 v1.0 */
+#define MDSS_MDP_HW_REV_103_1 MDSS_MDP_REV(1, 3, 1) /* 8084 v1.1 */
+#define MDSS_MDP_HW_REV_200 MDSS_MDP_REV(2, 0, 0) /* 8092 v1.0 */
+
enum {
NOTIFY_UPDATE_START,
NOTIFY_UPDATE_STOP,
diff --git a/include/linux/qpnp-revid.h b/include/linux/qpnp-revid.h
index 3cf9f1c..3d271f0 100644
--- a/include/linux/qpnp-revid.h
+++ b/include/linux/qpnp-revid.h
@@ -13,6 +13,13 @@
#ifndef __QPNP_REVID
#define __QPNP_REVID
+#define PM8226_V2P2_REV1 0x00
+#define PM8226_V2P2_REV2 0x00
+#define PM8226_V2P2_REV3 0x02
+#define PM8226_V2P2_REV4 0x02
+#define PM8226_V2P2_TYPE 0x51
+#define PM8226_V2P2_SUBTYPE 0x04
+
#define PM8226_V2P1_REV1 0x00
#define PM8226_V2P1_REV2 0x00
#define PM8226_V2P1_REV3 0x01
diff --git a/include/linux/qpnp/qpnp-adc.h b/include/linux/qpnp/qpnp-adc.h
index 13eb461..7ba4148 100644
--- a/include/linux/qpnp/qpnp-adc.h
+++ b/include/linux/qpnp/qpnp-adc.h
@@ -20,6 +20,7 @@
#include <linux/kernel.h>
#include <linux/list.h>
+#include <linux/qpnp-revid.h>
/**
* enum qpnp_vadc_channels - QPNP AMUX arbiter channels
*/
@@ -1002,15 +1003,27 @@
* @chan_prop - Represent the channel properties of the ADC.
*/
struct qpnp_adc_amux_properties {
- uint32_t amux_channel;
- uint32_t decimation;
- uint32_t mode_sel;
- uint32_t hw_settle_time;
- uint32_t fast_avg_setup;
- enum qpnp_vadc_trigger trigger_channel;
+ uint32_t amux_channel;
+ uint32_t decimation;
+ uint32_t mode_sel;
+ uint32_t hw_settle_time;
+ uint32_t fast_avg_setup;
+ enum qpnp_vadc_trigger trigger_channel;
struct qpnp_vadc_chan_properties chan_prop[0];
};
+/* SW index's for PMIC type and version used by QPNP VADC and IADC */
+#define QPNP_REV_ID_8941_3_1 1
+#define QPNP_REV_ID_8026_1_0 2
+#define QPNP_REV_ID_8026_2_0 3
+#define QPNP_REV_ID_8110_1_0 4
+#define QPNP_REV_ID_8026_2_1 5
+#define QPNP_REV_ID_8110_2_0 6
+#define QPNP_REV_ID_8026_2_2 7
+#define QPNP_REV_ID_8941_3_0 8
+#define QPNP_REV_ID_8941_2_0 9
+
+
/* Public API */
#if defined(CONFIG_SENSORS_QPNP_ADC_VOLTAGE) \
|| defined(CONFIG_SENSORS_QPNP_ADC_VOLTAGE_MODULE)
@@ -1366,9 +1379,16 @@
* qpnp_vadc_sns_comp_result() - Compensate vbatt readings based on temperature
* @dev: Structure device for qpnp vadc
* @result: Voltage in uV that needs compensation.
+ * @is_pon_ocv: Whether the reading is from a power on OCV or not
*/
int32_t qpnp_vbat_sns_comp_result(struct qpnp_vadc_chip *dev,
- int64_t *result);
+ int64_t *result, bool is_pon_ocv);
+/**
+ * qpnp_adc_get_revid_version() - Obtain the PMIC number and revision.
+ * @dev: Structure device node.
+ * returns internal mapped PMIC number and revision id.
+ */
+int qpnp_adc_get_revid_version(struct device *dev);
#else
static inline int32_t qpnp_vadc_read(struct qpnp_vadc_chip *dev,
uint32_t channel,
@@ -1482,6 +1502,8 @@
static inline int32_t qpnp_vbat_sns_comp_result(struct qpnp_vadc_chip *dev,
int64_t *result)
{ return -ENXIO; }
+static inline int qpnp_adc_get_revid_version(struct device *dev)
+{ return -ENXIO; }
#endif
/* Public API */
diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h
index fa702ae..9d13091 100644
--- a/include/linux/spi/spi.h
+++ b/include/linux/spi/spi.h
@@ -588,6 +588,26 @@
list_del(&t->transfer_list);
}
+/**
+ * spi_message_init_with_transfers - Initialize spi_message and append transfers
+ * @m: spi_message to be initialized
+ * @xfers: An array of spi transfers
+ * @num_xfers: Number of items in the xfer array
+ *
+ * This function initializes the given spi_message and adds each spi_transfer in
+ * the given array to the message.
+ */
+static inline void
+spi_message_init_with_transfers(struct spi_message *m,
+struct spi_transfer *xfers, unsigned int num_xfers)
+{
+ unsigned int i;
+
+ spi_message_init(m);
+ for (i = 0; i < num_xfers; ++i)
+ spi_message_add_tail(&xfers[i], m);
+}
+
/* It's fine to embed message and transaction structures in other data
* structures so long as you don't free them while they're in use.
*/
@@ -680,6 +700,30 @@
return spi_sync(spi, &m);
}
+/**
+ * spi_sync_transfer - synchronous SPI data transfer
+ * @spi: device with which data will be exchanged
+ * @xfers: An array of spi_transfers
+ * @num_xfers: Number of items in the xfer array
+ * Context: can sleep
+ *
+ * Does a synchronous SPI data transfer of the given spi_transfer array.
+ *
+ * For more specific semantics see spi_sync().
+ *
+ * It returns zero on success, else a negative error code.
+ */
+static inline int
+spi_sync_transfer(struct spi_device *spi, struct spi_transfer *xfers,
+ unsigned int num_xfers)
+{
+ struct spi_message msg;
+
+ spi_message_init_with_transfers(&msg, xfers, num_xfers);
+
+ return spi_sync(spi, &msg);
+}
+
/* this copies txbuf and rxbuf data; for small transfers only! */
extern int spi_write_then_read(struct spi_device *spi,
const void *txbuf, unsigned n_tx,
diff --git a/include/linux/usb/msm_hsusb.h b/include/linux/usb/msm_hsusb.h
index d16448a..195800f 100644
--- a/include/linux/usb/msm_hsusb.h
+++ b/include/linux/usb/msm_hsusb.h
@@ -523,6 +523,7 @@
bool use_sec_phy;
bool no_selective_suspend;
int resume_gpio;
+ bool is_uicc;
};
/**
diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h
index 81d5b9c..f446f51 100644
--- a/include/linux/videodev2.h
+++ b/include/linux/videodev2.h
@@ -708,6 +708,7 @@
#define V4L2_QCOM_BUF_INPUT_UNSUPPORTED 0x200000
#define V4L2_QCOM_BUF_FLAG_EOS 0x2000
#define V4L2_QCOM_BUF_FLAG_READONLY 0x400000
+#define V4L2_MSM_BUF_FLAG_MBAFF 0x800000
/*
* O V E R L A Y P R E V I E W
diff --git a/include/media/msm_cam_sensor.h b/include/media/msm_cam_sensor.h
index a88a71d..8199643 100644
--- a/include/media/msm_cam_sensor.h
+++ b/include/media/msm_cam_sensor.h
@@ -246,16 +246,6 @@
MASTER_MAX,
};
-struct msm_camera_sensor_slave_info {
- char sensor_name[32];
- char eeprom_name[32];
- char actuator_name[32];
- enum msm_sensor_camera_id_t camera_id;
- 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;
@@ -343,6 +333,7 @@
enum camb_position_t {
BACK_CAMERA_B,
FRONT_CAMERA_B,
+ INVALID_CAMERA_B,
};
struct msm_sensor_info_t {
@@ -366,7 +357,8 @@
enum camerab_mode_t {
CAMERA_MODE_2D_B = (1<<0),
- CAMERA_MODE_3D_B = (1<<1)
+ CAMERA_MODE_3D_B = (1<<1),
+ CAMERA_MODE_INVALID = (1<<2),
};
struct msm_sensor_init_params {
@@ -378,6 +370,19 @@
uint32_t sensor_mount_angle;
};
+struct msm_camera_sensor_slave_info {
+ char sensor_name[32];
+ char eeprom_name[32];
+ char actuator_name[32];
+ enum msm_sensor_camera_id_t camera_id;
+ 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;
+ uint8_t is_init_params_valid;
+ struct msm_sensor_init_params sensor_init_params;
+};
+
struct sensorb_cfg_data {
int cfgtype;
union {
diff --git a/include/net/tcp.h b/include/net/tcp.h
index 50660b3..dce56a6 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -131,6 +131,8 @@
* most likely due to retrans in 3WHS.
*/
+#define TCP_DELACK_SEG 1 /*Number of full MSS to receive before Acking RFC2581*/
+
#define TCP_RESOURCE_PROBE_INTERVAL ((unsigned)(HZ/2U)) /* Maximal interval between probes
* for local resources.
*/
@@ -253,6 +255,10 @@
extern int sysctl_tcp_thin_linear_timeouts;
extern int sysctl_tcp_thin_dupack;
+/* sysctl variables for controlling various tcp parameters */
+extern int sysctl_tcp_delack_seg;
+extern int sysctl_tcp_use_userconfig;
+
extern atomic_long_t tcp_memory_allocated;
extern struct percpu_counter tcp_sockets_allocated;
extern int tcp_memory_pressure;
@@ -346,6 +352,10 @@
struct pipe_inode_info *pipe, size_t len,
unsigned int flags);
+extern int tcp_use_userconfig_sysctl_handler(struct ctl_table *, int,
+ void __user *, size_t *, loff_t *);
+extern int tcp_proc_delayed_ack_control(struct ctl_table *, int,
+ void __user *, size_t *, loff_t *);
static inline void tcp_dec_quickack_mode(struct sock *sk,
const unsigned int pkts)
{
diff --git a/include/sound/Kbuild b/include/sound/Kbuild
index 60847b0..aeccfed 100644
--- a/include/sound/Kbuild
+++ b/include/sound/Kbuild
@@ -13,3 +13,4 @@
header-y += compress_offload.h
header-y += lsm_params.h
header-y += voice_params.h
+header-y += voice_svc.h
diff --git a/include/sound/apr_audio-v2.h b/include/sound/apr_audio-v2.h
index 69944a6..1c6ea04 100644
--- a/include/sound/apr_audio-v2.h
+++ b/include/sound/apr_audio-v2.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -579,6 +579,15 @@
/* Clients must set this field to zero.*/
} __packed;
+#define ASM_PARAM_ID_AAC_STEREO_MIX_COEFF_SELECTION_FLAG_V2 (0x00010DD8)
+
+struct asm_aac_stereo_mix_coeff_selection_param_v2 {
+ struct apr_hdr hdr;
+ u32 param_id;
+ u32 param_size;
+ u32 aac_stereo_mix_coeff_flag;
+} __packed;
+
/* Allows a client to connect the desired stream to
* the desired AFE port through the stream router
*
diff --git a/include/sound/voice_svc.h b/include/sound/voice_svc.h
new file mode 100644
index 0000000..7045018
--- /dev/null
+++ b/include/sound/voice_svc.h
@@ -0,0 +1,46 @@
+#ifndef __VOICE_SVC_H__
+#define __VOICE_SVC_H__
+
+#include <linux/types.h>
+#include <linux/ioctl.h>
+
+#define VOICE_SVC_DRIVER_NAME "voice_svc"
+
+#define VOICE_SVC_MVM_STR "MVM"
+#define VOICE_SVC_CVS_STR "CVS"
+#define MAX_APR_SERVICE_NAME_LEN 64
+
+struct voice_svc_register {
+ char svc_name[MAX_APR_SERVICE_NAME_LEN];
+ __u32 src_port;
+ __u8 reg_flag;
+};
+
+struct voice_svc_cmd_response {
+ __u32 src_port;
+ __u32 dest_port;
+ __u32 token;
+ __u32 opcode;
+ __u32 payload_size;
+ __u8 payload[0];
+};
+
+struct voice_svc_cmd_request {
+ char svc_name[MAX_APR_SERVICE_NAME_LEN];
+ __u32 src_port;
+ __u32 dest_port;
+ __u32 token;
+ __u32 opcode;
+ __u32 payload_size;
+ __u8 payload[0];
+};
+
+#define VOICE_SVC_MAGIC 'N'
+
+#define SNDRV_VOICE_SVC_REGISTER_SVC _IOWR(VOICE_SVC_MAGIC, \
+ 0x01, struct voice_svc_register)
+#define SNDRV_VOICE_SVC_CMD_RESPONSE _IOWR(VOICE_SVC_MAGIC, \
+ 0x02, struct voice_svc_cmd_response)
+#define SNDRV_VOICE_SVC_CMD_REQUEST _IOWR(VOICE_SVC_MAGIC, \
+ 0x03, struct voice_svc_cmd_request)
+#endif
diff --git a/include/trace/events/ice40.h b/include/trace/events/ice40.h
new file mode 100644
index 0000000..c0649a8
--- /dev/null
+++ b/include/trace/events/ice40.h
@@ -0,0 +1,310 @@
+/*
+ * Copyright (c) 2014, The Linux Foundation. All rights reserved.
+ *
+ * This 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.
+ */
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM ice40
+
+#if !defined(_TRACE_ICE40_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_ICE40_H
+
+#include <linux/tracepoint.h>
+#include <linux/usb.h>
+
+TRACE_EVENT(ice40_reg_write,
+
+ TP_PROTO(u8 addr, u8 val, u8 cmd0, u8 cmd1, int ret),
+
+ TP_ARGS(addr, val, cmd0, cmd1, ret),
+
+ TP_STRUCT__entry(
+ __field(u8, addr)
+ __field(u8, val)
+ __field(u8, cmd0)
+ __field(u8, cmd1)
+ __field(int, ret)
+ ),
+
+ TP_fast_assign(
+ __entry->addr = addr;
+ __entry->val = val;
+ __entry->cmd0 = cmd0;
+ __entry->cmd1 = cmd1;
+ __entry->ret = ret;
+ ),
+
+ TP_printk("addr = %x val = %x cmd0 = %x cmd1 = %x ret = %d",
+ __entry->addr, __entry->val, __entry->cmd0,
+ __entry->cmd1, __entry->ret)
+);
+
+TRACE_EVENT(ice40_reg_read,
+
+ TP_PROTO(u8 addr, u8 cmd0, int ret),
+
+ TP_ARGS(addr, cmd0, ret),
+
+ TP_STRUCT__entry(
+ __field(u8, addr)
+ __field(u8, cmd0)
+ __field(int, ret)
+ ),
+
+ TP_fast_assign(
+ __entry->addr = addr;
+ __entry->cmd0 = cmd0;
+ __entry->ret = ret;
+ ),
+
+ TP_printk("addr = %x cmd0 = %x ret = %x", __entry->addr,
+ __entry->cmd0, __entry->ret)
+);
+
+TRACE_EVENT(ice40_hub_control,
+
+ TP_PROTO(u16 req, u16 val, u16 index, u16 len, int ret),
+
+ TP_ARGS(req, val, index, len, ret),
+
+ TP_STRUCT__entry(
+ __field(u16, req)
+ __field(u16, val)
+ __field(u16, index)
+ __field(u16, len)
+ __field(int, ret)
+ ),
+
+ TP_fast_assign(
+ __entry->req = req;
+ __entry->val = val;
+ __entry->index = index;
+ __entry->len = len;
+ __entry->ret = ret;
+ ),
+
+ TP_printk("req = %x val = %x index = %x len = %x ret = %d",
+ __entry->req, __entry->val, __entry->index,
+ __entry->len, __entry->ret)
+);
+
+TRACE_EVENT(ice40_ep0,
+
+ TP_PROTO(const char *state),
+
+ TP_ARGS(state),
+
+ TP_STRUCT__entry(
+ __string(state, state)
+ ),
+
+ TP_fast_assign(
+ __assign_str(state, state);
+ ),
+
+ TP_printk("ep0 state: %s", __get_str(state))
+);
+
+TRACE_EVENT(ice40_urb_enqueue,
+
+ TP_PROTO(struct urb *urb),
+
+ TP_ARGS(urb),
+
+ TP_STRUCT__entry(
+ __field(u16, epnum)
+ __field(u8, dir)
+ __field(u8, type)
+ __field(u32, len)
+ ),
+
+ TP_fast_assign(
+ __entry->epnum = usb_pipeendpoint(urb->pipe);
+ __entry->dir = usb_urb_dir_in(urb);
+ __entry->type = usb_pipebulk(urb->pipe);
+ __entry->len = urb->transfer_buffer_length;
+ ),
+
+ TP_printk("URB_LOG: E: ep %d %s %s len %d", __entry->epnum,
+ __entry->dir ? "In" : "Out",
+ __entry->type ? "Bulk" : "ctrl",
+ __entry->len)
+);
+
+TRACE_EVENT(ice40_urb_dequeue,
+
+ TP_PROTO(struct urb *urb),
+
+ TP_ARGS(urb),
+
+ TP_STRUCT__entry(
+ __field(u16, epnum)
+ __field(u8, dir)
+ __field(u8, type)
+ __field(u32, len)
+ __field(int, reason)
+ ),
+
+ TP_fast_assign(
+ __entry->epnum = usb_pipeendpoint(urb->pipe);
+ __entry->dir = usb_urb_dir_in(urb);
+ __entry->type = usb_pipebulk(urb->pipe);
+ __entry->len = urb->transfer_buffer_length;
+ __entry->reason = urb->unlinked;
+ ),
+
+ TP_printk("URB_LOG: D: ep %d %s %s len %d reason %d",
+ __entry->epnum,
+ __entry->dir ? "In" : "Out",
+ __entry->type ? "Bulk" : "ctrl",
+ __entry->len, __entry->reason)
+);
+
+TRACE_EVENT(ice40_urb_done,
+
+ TP_PROTO(struct urb *urb, int result),
+
+ TP_ARGS(urb, result),
+
+ TP_STRUCT__entry(
+ __field(int, result)
+ __field(u16, epnum)
+ __field(u8, dir)
+ __field(u8, type)
+ __field(u32, len)
+ __field(u32, actual)
+ ),
+
+ TP_fast_assign(
+ __entry->result = result;
+ __entry->epnum = usb_pipeendpoint(urb->pipe);
+ __entry->dir = usb_urb_dir_in(urb);
+ __entry->type = usb_pipebulk(urb->pipe);
+ __entry->len = urb->transfer_buffer_length;
+ __entry->actual = urb->actual_length;
+ ),
+
+ TP_printk("URB_LOG: C: ep %d %s %s len %d actual %d result %d",
+ __entry->epnum, __entry->dir ? "In" : "Out",
+ __entry->type ? "Bulk" : "ctrl", __entry->len,
+ __entry->actual, __entry->result)
+);
+
+TRACE_EVENT(ice40_bus_suspend,
+
+ TP_PROTO(u8 status),
+
+ TP_ARGS(status),
+
+ TP_STRUCT__entry(
+ __field(u8, status)
+ ),
+
+ TP_fast_assign(
+ __entry->status = status;
+ ),
+
+ TP_printk("bus_suspend status %d", __entry->status)
+);
+
+TRACE_EVENT(ice40_bus_resume,
+
+ TP_PROTO(u8 status),
+
+ TP_ARGS(status),
+
+ TP_STRUCT__entry(
+ __field(u8, status)
+ ),
+
+ TP_fast_assign(
+ __entry->status = status;
+ ),
+
+ TP_printk("bus_resume status %d", __entry->status)
+);
+
+TRACE_EVENT(ice40_setup,
+
+ TP_PROTO(const char *token, int ret),
+
+ TP_ARGS(token, ret),
+
+ TP_STRUCT__entry(
+ __string(token, token)
+ __field(int, ret)
+ ),
+
+ TP_fast_assign(
+ __assign_str(token, token);
+ __entry->ret = ret;
+ ),
+
+ TP_printk("Trace: SETUP %s ret %d",
+ __get_str(token), __entry->ret)
+);
+
+TRACE_EVENT(ice40_in,
+
+ TP_PROTO(u16 ep, const char *token, u8 len, u8 expected, int ret),
+
+ TP_ARGS(ep, token, len, expected, ret),
+
+ TP_STRUCT__entry(
+ __field(u16, ep)
+ __string(token, token)
+ __field(u8, len)
+ __field(u8, expected)
+ __field(int, ret)
+ ),
+
+ TP_fast_assign(
+ __entry->ep = ep;
+ __assign_str(token, token);
+ __entry->len = len;
+ __entry->expected = expected;
+ __entry->ret = ret;
+ ),
+
+ TP_printk("Trace: %d IN %s len %d expected %d ret %d",
+ __entry->ep, __get_str(token),
+ __entry->len, __entry->expected,
+ __entry->ret)
+);
+
+TRACE_EVENT(ice40_out,
+
+ TP_PROTO(u16 ep, const char *token, u8 len, int ret),
+
+ TP_ARGS(ep, token, len, ret),
+
+ TP_STRUCT__entry(
+ __field(u16, ep)
+ __string(token, token)
+ __field(u8, len)
+ __field(int, ret)
+ ),
+
+ TP_fast_assign(
+ __entry->ep = ep;
+ __assign_str(token, token);
+ __entry->len = len;
+ __entry->ret = ret;
+ ),
+
+ TP_printk("Trace: %d OUT %s len %d ret %d",
+ __entry->ep, __get_str(token),
+ __entry->len, __entry->ret)
+);
+#endif /* if !defined(_TRACE_ICE40_H) || defined(TRACE_HEADER_MULTI_READ) */
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index 700d2ae..f320017 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -384,7 +384,7 @@
void tracing_off(void)
{
if (global_trace.buffer)
- ring_buffer_record_on(global_trace.buffer);
+ ring_buffer_record_off(global_trace.buffer);
/*
* This flag is only looked at when buffers haven't been
* allocated yet. We don't really care about the race
diff --git a/mm/compaction.c b/mm/compaction.c
index 673142d..35bb243 100644
--- a/mm/compaction.c
+++ b/mm/compaction.c
@@ -243,7 +243,6 @@
{
int nr_scanned = 0, total_isolated = 0;
struct page *cursor, *valid_page = NULL;
- unsigned long nr_strict_required = end_pfn - blockpfn;
unsigned long flags;
bool locked = false;
@@ -256,11 +255,12 @@
nr_scanned++;
if (!pfn_valid_within(blockpfn))
- continue;
+ goto isolate_fail;
+
if (!valid_page)
valid_page = page;
if (!PageBuddy(page))
- continue;
+ goto isolate_fail;
/*
* The zone lock must be held to isolate freepages.
@@ -281,12 +281,10 @@
/* Recheck this is a buddy page under lock */
if (!PageBuddy(page))
- continue;
+ goto isolate_fail;
/* Found a free page, break it into order-0 pages */
isolated = split_free_page(page);
- if (!isolated && strict)
- break;
total_isolated += isolated;
for (i = 0; i < isolated; i++) {
list_add(&page->lru, freelist);
@@ -297,7 +295,13 @@
if (isolated) {
blockpfn += isolated - 1;
cursor += isolated - 1;
+ continue;
}
+
+isolate_fail:
+ if (strict)
+ break;
+
}
trace_mm_compaction_isolate_freepages(nr_scanned, total_isolated);
@@ -307,7 +311,7 @@
* pages requested were isolated. If there were any failures, 0 is
* returned and CMA will fail.
*/
- if (strict && nr_strict_required > total_isolated)
+ if (strict && blockpfn < end_pfn)
total_isolated = 0;
if (locked)
diff --git a/mm/ksm.c b/mm/ksm.c
index 47c8853..fa73fc6 100644
--- a/mm/ksm.c
+++ b/mm/ksm.c
@@ -189,6 +189,9 @@
/* Milliseconds ksmd should sleep between batches */
static unsigned int ksm_thread_sleep_millisecs = 20;
+/* Boolean to indicate whether to use deferred timer or not */
+static bool use_deferred_timer;
+
#define KSM_RUN_STOP 0
#define KSM_RUN_MERGE 1
#define KSM_RUN_UNMERGE 2
@@ -1427,6 +1430,41 @@
}
}
+static void process_timeout(unsigned long __data)
+{
+ wake_up_process((struct task_struct *)__data);
+}
+
+static signed long __sched deferred_schedule_timeout(signed long timeout)
+{
+ struct timer_list timer;
+ unsigned long expire;
+
+ __set_current_state(TASK_INTERRUPTIBLE);
+ if (timeout < 0) {
+ pr_err("schedule_timeout: wrong timeout value %lx\n",
+ timeout);
+ __set_current_state(TASK_RUNNING);
+ goto out;
+ }
+
+ expire = timeout + jiffies;
+
+ setup_deferrable_timer_on_stack(&timer, process_timeout,
+ (unsigned long)current);
+ mod_timer(&timer, expire);
+ schedule();
+ del_singleshot_timer_sync(&timer);
+
+ /* Remove the timer from the object tracker */
+ destroy_timer_on_stack(&timer);
+
+ timeout = expire - jiffies;
+
+out:
+ return timeout < 0 ? 0 : timeout;
+}
+
static int ksmd_should_run(void)
{
return (ksm_run & KSM_RUN_MERGE) && !list_empty(&ksm_mm_head.mm_list);
@@ -1446,7 +1484,11 @@
try_to_freeze();
if (ksmd_should_run()) {
- schedule_timeout_interruptible(
+ if (use_deferred_timer)
+ deferred_schedule_timeout(
+ msecs_to_jiffies(ksm_thread_sleep_millisecs));
+ else
+ schedule_timeout_interruptible(
msecs_to_jiffies(ksm_thread_sleep_millisecs));
} else {
wait_event_freezable(ksm_thread_wait,
@@ -1926,6 +1968,26 @@
}
KSM_ATTR(run);
+static ssize_t deferred_timer_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ return snprintf(buf, 8, "%d\n", use_deferred_timer);
+}
+
+static ssize_t deferred_timer_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned long enable;
+ int err;
+
+ err = kstrtoul(buf, 10, &enable);
+ use_deferred_timer = enable;
+
+ return count;
+}
+KSM_ATTR(deferred_timer);
+
static ssize_t pages_shared_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
@@ -1980,6 +2042,7 @@
&pages_unshared_attr.attr,
&pages_volatile_attr.attr,
&full_scans_attr.attr,
+ &deferred_timer_attr.attr,
NULL,
};
diff --git a/net/ipv4/ping.c b/net/ipv4/ping.c
index 7f38d35..a8d7ed0 100644
--- a/net/ipv4/ping.c
+++ b/net/ipv4/ping.c
@@ -896,13 +896,13 @@
sin6->sin6_port = 0;
sin6->sin6_addr = ip6->saddr;
+ sin6->sin6_flowinfo = 0;
if (np->sndflow)
sin6->sin6_flowinfo =
*(__be32 *)ip6 & IPV6_FLOWINFO_MASK;
- if (__ipv6_addr_needs_scope_id(
- ipv6_addr_type(&sin6->sin6_addr)))
- sin6->sin6_scope_id = IP6CB(skb)->iif;
+ sin6->sin6_scope_id = ipv6_iface_scope_id(&sin6->sin6_addr,
+ IP6CB(skb)->iif);
if (inet6_sk(sk)->rxopt.all)
pingv6_ops.datagram_recv_ctl(sk, msg, skb);
diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c
index 7a7724d..6bd622f 100644
--- a/net/ipv4/sysctl_net_ipv4.c
+++ b/net/ipv4/sysctl_net_ipv4.c
@@ -36,6 +36,10 @@
static int ip_ttl_max = 255;
static int ip_ping_group_range_min[] = { 0, 0 };
static int ip_ping_group_range_max[] = { GID_T_MAX, GID_T_MAX };
+static int tcp_delack_seg_min = TCP_DELACK_MIN;
+static int tcp_delack_seg_max = 60;
+static int tcp_use_userconfig_min;
+static int tcp_use_userconfig_max = 1;
/* Update system visible IP port range */
static void set_local_port_range(int range[2])
@@ -699,6 +703,25 @@
.proc_handler = proc_dointvec_minmax,
.extra1 = &zero
},
+ {
+ .procname = "tcp_delack_seg",
+ .data = &sysctl_tcp_delack_seg,
+ .maxlen = sizeof(sysctl_tcp_delack_seg),
+ .mode = 0644,
+ .proc_handler = tcp_proc_delayed_ack_control,
+ .extra1 = &tcp_delack_seg_min,
+ .extra2 = &tcp_delack_seg_max,
+ },
+ {
+ .procname = "tcp_use_userconfig",
+ .data = &sysctl_tcp_use_userconfig,
+ .maxlen = sizeof(sysctl_tcp_use_userconfig),
+ .mode = 0644,
+ .proc_handler = tcp_use_userconfig_sysctl_handler,
+ .extra1 = &tcp_use_userconfig_min,
+ .extra2 = &tcp_use_userconfig_max,
+ },
+
{ }
};
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index 74a286c..706899e 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -294,6 +294,12 @@
EXPORT_SYMBOL(sysctl_tcp_rmem);
EXPORT_SYMBOL(sysctl_tcp_wmem);
+int sysctl_tcp_delack_seg __read_mostly = TCP_DELACK_SEG;
+EXPORT_SYMBOL(sysctl_tcp_delack_seg);
+
+int sysctl_tcp_use_userconfig __read_mostly;
+EXPORT_SYMBOL(sysctl_tcp_use_userconfig);
+
atomic_long_t tcp_memory_allocated; /* Current allocated memory. */
EXPORT_SYMBOL(tcp_memory_allocated);
@@ -1213,8 +1219,11 @@
/* Delayed ACKs frequently hit locked sockets during bulk
* receive. */
if (icsk->icsk_ack.blocked ||
- /* Once-per-two-segments ACK was not sent by tcp_input.c */
- tp->rcv_nxt - tp->rcv_wup > icsk->icsk_ack.rcv_mss ||
+ /* Once-per-sysctl_tcp_delack_seg segments
+ * ACK was not sent by tcp_input.c
+ */
+ tp->rcv_nxt - tp->rcv_wup > (icsk->icsk_ack.rcv_mss) *
+ sysctl_tcp_delack_seg ||
/*
* If this read emptied read buffer, we send ACK, if
* connection is not bidirectional, user drained
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index 257b617..7c3612b 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -5047,7 +5047,8 @@
struct tcp_sock *tp = tcp_sk(sk);
/* More than one full frame received... */
- if (((tp->rcv_nxt - tp->rcv_wup) > inet_csk(sk)->icsk_ack.rcv_mss &&
+ if (((tp->rcv_nxt - tp->rcv_wup) > (inet_csk(sk)->icsk_ack.rcv_mss) *
+ sysctl_tcp_delack_seg &&
/* ... and right edge of window advances far enough.
* (tcp_recvmsg() will send ACK otherwise). Or...
*/
diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c
index 34d4a02..d1b4792 100644
--- a/net/ipv4/tcp_timer.c
+++ b/net/ipv4/tcp_timer.c
@@ -34,7 +34,39 @@
static void tcp_write_timer(unsigned long);
static void tcp_delack_timer(unsigned long);
-static void tcp_keepalive_timer (unsigned long data);
+static void tcp_keepalive_timer(unsigned long data);
+
+/*Function to reset tcp_ack related sysctl on resetting master control */
+void set_tcp_default(void)
+{
+ sysctl_tcp_delack_seg = TCP_DELACK_SEG;
+}
+
+/*sysctl handler for tcp_ack realted master control */
+int tcp_proc_delayed_ack_control(ctl_table *table, int write,
+ void __user *buffer, size_t *length, loff_t *ppos)
+{
+ int ret = proc_dointvec_minmax(table, write, buffer, length, ppos);
+
+ /* The ret value will be 0 if the input validation is successful
+ * and the values are written to sysctl table. If not, the stack
+ * will continue to work with currently configured values
+ */
+ return ret;
+}
+
+/*sysctl handler for tcp_ack realted master control */
+int tcp_use_userconfig_sysctl_handler(ctl_table *table, int write,
+ void __user *buffer, size_t *length, loff_t *ppos)
+{
+ int ret = proc_dointvec_minmax(table, write, buffer, length, ppos);
+
+ if (write && ret == 0) {
+ if (!sysctl_tcp_use_userconfig)
+ set_tcp_default();
+ }
+ return ret;
+}
void tcp_init_xmit_timers(struct sock *sk)
{
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index b28b7eb..c868a74 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -3816,6 +3816,11 @@
struct lsm_network_audit net = {0,};
u32 tsid = task_sid(task);
+ if (unlikely(!sksec)) {
+ pr_warn("SELinux: sksec is NULL, socket is already freed\n");
+ return -EINVAL;
+ }
+
if (sksec->sid == SECINITSID_KERNEL)
return 0;
diff --git a/sound/soc/codecs/msm8x10-wcd.c b/sound/soc/codecs/msm8x10-wcd.c
index 452bbab..2286e34 100644
--- a/sound/soc/codecs/msm8x10-wcd.c
+++ b/sound/soc/codecs/msm8x10-wcd.c
@@ -1200,6 +1200,13 @@
MSM8X10_WCD_A_CDC_TX2_VOL_CTL_GAIN,
-84, 40, digital_gain),
+ SOC_SINGLE_TLV("ADC1 Volume", MSM8X10_WCD_A_TX_1_EN, 2,
+ 19, 0, analog_gain),
+ SOC_SINGLE_TLV("ADC2 Volume", MSM8X10_WCD_A_TX_2_EN, 2,
+ 19, 0, analog_gain),
+ SOC_SINGLE_TLV("ADC3 Volume", MSM8X10_WCD_A_TX_3_EN, 2,
+ 19, 0, analog_gain),
+
SOC_SINGLE_S8_TLV("IIR1 INP1 Volume",
MSM8X10_WCD_A_CDC_IIR1_GAIN_B1_CTL,
-84, 40, digital_gain),
@@ -2354,6 +2361,10 @@
struct snd_kcontrol *kcontrol, int event)
{
switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_update_bits(w->codec, MSM8X10_WCD_A_CDC_ANA_CLK_CTL,
+ 0x4, 0x4);
+ break;
case SND_SOC_DAPM_POST_PMU:
dev_dbg(w->codec->dev,
"%s: Sleeping 20ms after enabling EAR PA\n",
@@ -2364,6 +2375,8 @@
dev_dbg(w->codec->dev,
"%s: Sleeping 20ms after disabling EAR PA\n",
__func__);
+ snd_soc_update_bits(w->codec, MSM8X10_WCD_A_CDC_ANA_CLK_CTL,
+ 0x4, 0x0);
msleep(20);
break;
}
@@ -2375,7 +2388,7 @@
SND_SOC_DAPM_OUTPUT("EAR"),
SND_SOC_DAPM_PGA_E("EAR PA", MSM8X10_WCD_A_RX_EAR_EN, 4, 0, NULL, 0,
- msm8x10_wcd_codec_enable_ear_pa,
+ msm8x10_wcd_codec_enable_ear_pa, SND_SOC_DAPM_PRE_PMU |
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_MIXER("DAC1", MSM8X10_WCD_A_RX_EAR_EN, 6, 0, dac1_switch,
@@ -2464,7 +2477,7 @@
SND_SOC_DAPM_SUPPLY("HPHL CLK", MSM8X10_WCD_A_CDC_ANA_CLK_CTL,
1, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("EAR CLK", MSM8X10_WCD_A_CDC_ANA_CLK_CTL,
- 2, 0, NULL, 0),
+ 6, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("LINEOUT CLK", MSM8X10_WCD_A_CDC_ANA_CLK_CTL,
3, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("SPK CLK", MSM8X10_WCD_A_CDC_ANA_CLK_CTL,
diff --git a/sound/soc/codecs/wcd9306.c b/sound/soc/codecs/wcd9306.c
index d84ba90..b201f0b 100644
--- a/sound/soc/codecs/wcd9306.c
+++ b/sound/soc/codecs/wcd9306.c
@@ -43,6 +43,8 @@
#define TAPAN_HPH_PA_SETTLE_COMP_OFF 13000
#define DAPM_MICBIAS2_EXTERNAL_STANDALONE "MIC BIAS2 External Standalone"
+#define TAPAN_VALIDATE_RX_SBPORT_RANGE(port) ((port >= 16) && (port <= 20))
+#define TAPAN_CONVERT_RX_SBPORT_ID(port) (port - 16) /* RX1 port ID = 0 */
#define TAPAN_VDD_CX_OPTIMAL_UA 10000
#define TAPAN_VDD_CX_SLEEP_UA 2000
@@ -3236,6 +3238,8 @@
}
#define TAPAN_FORMATS (SNDRV_PCM_FMTBIT_S16_LE)
+#define TAPAN_FORMATS_S16_S24_LE (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FORMAT_S24_LE)
static int tapan_write(struct snd_soc_codec *codec, unsigned int reg,
unsigned int value)
{
@@ -3643,6 +3647,68 @@
return 0;
}
+static void tapan_set_rxsb_port_format(struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct tapan_priv *tapan_p = snd_soc_codec_get_drvdata(codec);
+ struct wcd9xxx_codec_dai_data *cdc_dai;
+ struct wcd9xxx_ch *ch;
+ int port;
+ u8 bit_sel;
+ u16 sb_ctl_reg, field_shift;
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ bit_sel = 0x2;
+ tapan_p->dai[dai->id].bit_width = 16;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ bit_sel = 0x0;
+ tapan_p->dai[dai->id].bit_width = 24;
+ break;
+ default:
+ dev_err(codec->dev, "Invalid format %x\n",
+ params_format(params));
+ return;
+ }
+
+ cdc_dai = &tapan_p->dai[dai->id];
+
+ list_for_each_entry(ch, &cdc_dai->wcd9xxx_ch_list, list) {
+ port = wcd9xxx_get_slave_port(ch->ch_num);
+
+ if (IS_ERR_VALUE(port) ||
+ !TAPAN_VALIDATE_RX_SBPORT_RANGE(port)) {
+ dev_warn(codec->dev,
+ "%s: invalid port ID %d returned for RX DAI\n",
+ __func__, port);
+ return;
+ }
+
+ port = TAPAN_CONVERT_RX_SBPORT_ID(port);
+
+ if (port <= 3) {
+ sb_ctl_reg = TAPAN_A_CDC_CONN_RX_SB_B1_CTL;
+ field_shift = port << 1;
+ } else if (port <= 4) {
+ sb_ctl_reg = TAPAN_A_CDC_CONN_RX_SB_B2_CTL;
+ field_shift = (port - 4) << 1;
+ } else { /* should not happen */
+ dev_warn(codec->dev,
+ "%s: bad port ID %d\n", __func__, port);
+ return;
+ }
+
+ dev_dbg(codec->dev, "%s: sb_ctl_reg %x field_shift %x\n"
+ "bit_sel %x\n", __func__, sb_ctl_reg, field_shift,
+ bit_sel);
+ snd_soc_update_bits(codec, sb_ctl_reg, 0x3 << field_shift,
+ bit_sel << field_shift);
+ }
+}
+
+
static int tapan_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
@@ -3755,29 +3821,7 @@
snd_soc_update_bits(codec, TAPAN_A_CDC_CLK_I2S_CTL,
0x03, (rx_fs_rate >> 0x05));
} else {
- switch (params_format(params)) {
- case SNDRV_PCM_FORMAT_S16_LE:
- snd_soc_update_bits(codec,
- TAPAN_A_CDC_CONN_RX_SB_B1_CTL,
- 0xFF, 0xAA);
- snd_soc_update_bits(codec,
- TAPAN_A_CDC_CONN_RX_SB_B2_CTL,
- 0xFF, 0x2A);
- tapan->dai[dai->id].bit_width = 16;
- break;
- case SNDRV_PCM_FORMAT_S24_LE:
- snd_soc_update_bits(codec,
- TAPAN_A_CDC_CONN_RX_SB_B1_CTL,
- 0xFF, 0x00);
- snd_soc_update_bits(codec,
- TAPAN_A_CDC_CONN_RX_SB_B2_CTL,
- 0xFF, 0x00);
- tapan->dai[dai->id].bit_width = 24;
- break;
- default:
- dev_err(codec->dev, "Invalid format\n");
- break;
- }
+ tapan_set_rxsb_port_format(params, dai);
tapan->dai[dai->id].rate = params_rate(params);
}
break;
@@ -3894,7 +3938,7 @@
.playback = {
.stream_name = "AIF1 Playback",
.rates = WCD9306_RATES,
- .formats = TAPAN_FORMATS,
+ .formats = TAPAN_FORMATS_S16_S24_LE,
.rate_max = 192000,
.rate_min = 8000,
.channels_min = 1,
@@ -3922,7 +3966,7 @@
.playback = {
.stream_name = "AIF2 Playback",
.rates = WCD9306_RATES,
- .formats = TAPAN_FORMATS,
+ .formats = TAPAN_FORMATS_S16_S24_LE,
.rate_min = 8000,
.rate_max = 192000,
.channels_min = 1,
@@ -3964,7 +4008,7 @@
.playback = {
.stream_name = "AIF3 Playback",
.rates = WCD9306_RATES,
- .formats = TAPAN_FORMATS,
+ .formats = TAPAN_FORMATS_S16_S24_LE,
.rate_min = 8000,
.rate_max = 192000,
.channels_min = 1,
@@ -4194,6 +4238,23 @@
return 0;
}
+static int tapan_codec_iir_mux_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ pr_debug("%s: event = %d\n", __func__, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_write(codec, w->reg, snd_soc_read(codec, w->reg));
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_write(codec, w->reg, snd_soc_read(codec, w->reg));
+ break;
+ }
+ return 0;
+}
+
static int tapan_codec_dsm_mux_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
@@ -4681,7 +4742,10 @@
SND_SOC_DAPM_POST_PMD),
/* Sidetone */
- SND_SOC_DAPM_MUX("IIR1 INP1 MUX", SND_SOC_NOPM, 0, 0, &iir1_inp1_mux),
+ SND_SOC_DAPM_MUX_E("IIR1 INP1 MUX", TAPAN_A_CDC_IIR1_GAIN_B1_CTL, 0, 0,
+ &iir1_inp1_mux, tapan_codec_iir_mux_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
SND_SOC_DAPM_PGA("IIR1", TAPAN_A_CDC_CLK_SD_CTL, 0, 0, NULL, 0),
/* AUX PGA */
diff --git a/sound/soc/codecs/wcd9320.c b/sound/soc/codecs/wcd9320.c
index b72590f..9ef2ea4 100644
--- a/sound/soc/codecs/wcd9320.c
+++ b/sound/soc/codecs/wcd9320.c
@@ -4260,6 +4260,10 @@
if (reg == TAIKO_A_RX_HPH_L_STATUS || reg == TAIKO_A_RX_HPH_R_STATUS)
return 1;
+ /* HPH PA Enable */
+ if (reg == TAIKO_A_RX_HPH_CNP_EN)
+ return 1;
+
if (reg == TAIKO_A_MBHC_INSERT_DET_STATUS)
return 1;
@@ -6797,18 +6801,21 @@
snd_soc_card_change_online_state(codec->card, 1);
mutex_lock(&codec->mutex);
- 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);
- }
+
+ taiko_update_reg_defaults(codec);
+ if (wcd9xxx->mclk_rate == TAIKO_MCLK_CLK_12P288MHZ)
+ snd_soc_update_bits(codec, TAIKO_A_CHIP_CTL, 0x06, 0x0);
+ else if (wcd9xxx->mclk_rate == TAIKO_MCLK_CLK_9P6MHZ)
+ snd_soc_update_bits(codec, TAIKO_A_CHIP_CTL, 0x06, 0x2);
+ taiko_codec_init_reg(codec);
if (spkr_drv_wrnd == 1)
snd_soc_update_bits(codec, TAIKO_A_SPKR_DRV_EN, 0x80, 0x80);
- taiko_update_reg_defaults(codec);
- taiko_codec_init_reg(codec);
+ codec->cache_sync = true;
+ snd_soc_cache_sync(codec);
+ codec->cache_sync = false;
+
ret = taiko_handle_pdata(taiko);
if (IS_ERR_VALUE(ret))
pr_err("%s: bad pdata\n", __func__);
@@ -7208,6 +7215,11 @@
{
struct platform_device *pdev = to_platform_device(dev);
struct taiko_priv *taiko = platform_get_drvdata(pdev);
+
+ if (!taiko) {
+ dev_err(dev, "%s: taiko private data is NULL\n", __func__);
+ return -EINVAL;
+ }
dev_dbg(dev, "%s: system resume\n", __func__);
/* Notify */
wcd9xxx_resmgr_notifier_call(&taiko->resmgr, WCD9XXX_EVENT_POST_RESUME);
diff --git a/sound/soc/codecs/wcd9xxx-common.c b/sound/soc/codecs/wcd9xxx-common.c
index b104a6b..eb3a0ba 100644
--- a/sound/soc/codecs/wcd9xxx-common.c
+++ b/sound/soc/codecs/wcd9xxx-common.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -651,6 +651,29 @@
}
EXPORT_SYMBOL(wcd9xxx_restore_registers);
+static void wcd9xxx_dynamic_bypass_buck_ctrl(struct snd_soc_codec *cdc,
+ bool enable)
+{
+ int i;
+ const struct wcd9xxx_reg_mask_val reg_set[] = {
+ {WCD9XXX_A_BUCK_MODE_3, (0x1 << 3), (enable << 3)},
+ {WCD9XXX_A_BUCK_MODE_5, (0x1 << 1), (enable << 1)},
+ {WCD9XXX_A_BUCK_MODE_5, 0x1, enable}
+ };
+ if (!enable) {
+ snd_soc_update_bits(cdc, WCD9XXX_A_BUCK_MODE_1,
+ (0x1 << 3), 0x00);
+ snd_soc_update_bits(cdc, WCD9XXX_A_BUCK_MODE_4,
+ 0xFF, BUCK_VREF_2V);
+ }
+ for (i = 0; i < ARRAY_SIZE(reg_set); i++)
+ snd_soc_update_bits(cdc, reg_set[i].reg, reg_set[i].mask,
+ reg_set[i].val);
+
+ /* 50us sleep is reqd. as per the class H HW design sequence */
+ usleep_range(BUCK_SETTLE_TIME_US, BUCK_SETTLE_TIME_US+10);
+}
+
static void wcd9xxx_set_buck_mode(struct snd_soc_codec *codec, u8 buck_vref)
{
int i;
@@ -807,6 +830,239 @@
__func__);
}
+static void wcd9xxx_ncp_bypass_enable(struct snd_soc_codec *cdc, bool enable)
+{
+ snd_soc_update_bits(cdc, WCD9XXX_A_NCP_STATIC, 0x10, (enable << 4));
+ /* 50us sleep is reqd. as per the class H HW design sequence */
+ usleep_range(BUCK_SETTLE_TIME_US, BUCK_SETTLE_TIME_US+10);
+}
+
+static void wcd9xxx_clsh_set_Iest(struct snd_soc_codec *codec,
+ u8 value)
+{
+ snd_soc_update_bits(codec, WCD9XXX_A_BUCK_MODE_5,
+ 0x01, (0x01 & 0x03));
+ snd_soc_update_bits(codec, WCD9XXX_A_BUCK_MODE_5,
+ 0xFC, (value << 2));
+}
+
+static void wcd9xxx_clsh_state_hph_ear(struct snd_soc_codec *codec,
+ struct wcd9xxx_clsh_cdc_data *clsh_d,
+ u8 req_state, bool is_enable)
+{
+ int compute_pa = 0;
+
+ dev_dbg(codec->dev, "%s: enter %s\n", __func__,
+ is_enable ? "enable" : "disable");
+
+ if (is_enable) {
+ /*
+ * The below check condition is required to make sure
+ * functions inside if condition will execute only once.
+ */
+ if ((clsh_d->state == WCD9XXX_CLSH_STATE_EAR) ||
+ (req_state == WCD9XXX_CLSH_STATE_EAR)) {
+ wcd9xxx_dynamic_bypass_buck_ctrl(codec, false);
+ wcd9xxx_ncp_bypass_enable(codec, true);
+ }
+ switch (req_state) {
+ case WCD9XXX_CLSH_STATE_HPHL:
+ compute_pa = CLSH_COMPUTE_HPH_L;
+ break;
+ case WCD9XXX_CLSH_STATE_HPHR:
+ compute_pa = CLSH_COMPUTE_HPH_R;
+ break;
+ case WCD9XXX_CLSH_STATE_EAR:
+ compute_pa = CLSH_COMPUTE_EAR;
+ break;
+ default:
+ dev_dbg(codec->dev,
+ "%s:Invalid state:0x%x,enable:0x%x\n",
+ __func__, req_state, is_enable);
+ break;
+ }
+ wcd9xxx_clsh_comp_req(codec, clsh_d, compute_pa, true);
+
+ dev_dbg(codec->dev, "%s: Enabled hph+ear mode clsh\n",
+ __func__);
+ } else {
+ switch (req_state) {
+ case WCD9XXX_CLSH_STATE_HPHL:
+ compute_pa = CLSH_COMPUTE_HPH_L;
+ break;
+ case WCD9XXX_CLSH_STATE_HPHR:
+ compute_pa = CLSH_COMPUTE_HPH_R;
+ break;
+ case WCD9XXX_CLSH_STATE_EAR:
+ compute_pa = CLSH_COMPUTE_EAR;
+ break;
+ default:
+ dev_dbg(codec->dev,
+ "%s:Invalid state:0x%x,enable:0x%x\n",
+ __func__, req_state, is_enable);
+ break;
+ }
+ wcd9xxx_clsh_comp_req(codec, clsh_d, compute_pa, false);
+
+ if (((clsh_d->state & (~req_state)) ==
+ WCD9XXX_CLSH_STATE_EAR) ||
+ (req_state == WCD9XXX_CLSH_STATE_EAR)) {
+ wcd9xxx_ncp_bypass_enable(codec, false);
+ wcd9xxx_dynamic_bypass_buck_ctrl(codec, true);
+ }
+ }
+}
+
+static void wcd9xxx_clsh_state_hph_lo(struct snd_soc_codec *codec,
+ struct wcd9xxx_clsh_cdc_data *clsh_d,
+ u8 req_state, bool is_enable)
+{
+
+ dev_dbg(codec->dev, "%s: enter %s\n", __func__,
+ is_enable ? "enable" : "disable");
+ if (is_enable) {
+ if ((clsh_d->state == WCD9XXX_CLSH_STATE_LO) ||
+ (req_state == WCD9XXX_CLSH_STATE_LO)) {
+ wcd9xxx_dynamic_bypass_buck_ctrl(codec, false);
+ wcd9xxx_enable_buck(codec, clsh_d, true);
+ wcd9xxx_set_fclk_get_ncp(codec, clsh_d,
+ NCP_FCLK_LEVEL_8);
+ if (req_state & WCD9XXX_CLSH_STATE_HPH_ST) {
+ wcd9xxx_ncp_bypass_enable(codec, true);
+ wcd9xxx_enable_clsh_block(codec, clsh_d, true);
+ wcd9xxx_chargepump_request(codec, true);
+ wcd9xxx_enable_anc_delay(codec, true);
+ }
+ }
+ if (req_state == WCD9XXX_CLSH_STATE_HPHL)
+ wcd9xxx_clsh_comp_req(codec, clsh_d,
+ CLSH_COMPUTE_HPH_L, true);
+ if (req_state == WCD9XXX_CLSH_STATE_HPHR)
+ wcd9xxx_clsh_comp_req(codec, clsh_d,
+ CLSH_COMPUTE_HPH_R, true);
+ } else {
+ switch (req_state) {
+ case WCD9XXX_CLSH_STATE_LO:
+ snd_soc_update_bits(codec, WCD9XXX_A_NCP_STATIC,
+ 0x20, 0x00);
+ wcd9xxx_dynamic_bypass_buck_ctrl(codec, true);
+ break;
+ case WCD9XXX_CLSH_STATE_HPHL:
+ wcd9xxx_clsh_comp_req(codec, clsh_d,
+ CLSH_COMPUTE_HPH_L, false);
+ break;
+ case WCD9XXX_CLSH_STATE_HPHR:
+ wcd9xxx_clsh_comp_req(codec, clsh_d,
+ CLSH_COMPUTE_HPH_R, false);
+ break;
+ default:
+ dev_dbg(codec->dev,
+ "%s:Invalid state:0x%x,enable:0x%x\n",
+ __func__, req_state, is_enable);
+ break;
+ }
+ if ((req_state == WCD9XXX_CLSH_STATE_LO) ||
+ ((clsh_d->state & (~req_state)) == WCD9XXX_CLSH_STATE_LO)) {
+ wcd9xxx_set_fclk_put_ncp(codec, clsh_d,
+ NCP_FCLK_LEVEL_8);
+ wcd9xxx_ncp_bypass_enable(codec, false);
+
+ if (req_state & WCD9XXX_CLSH_STATE_HPH_ST) {
+ usleep_range(BUCK_SETTLE_TIME_US,
+ BUCK_SETTLE_TIME_US + 10);
+ if (clsh_d->buck_mv ==
+ WCD9XXX_CDC_BUCK_MV_1P8) {
+ wcd9xxx_enable_buck(codec, clsh_d,
+ false);
+ wcd9xxx_ncp_bypass_enable(codec, true);
+ } else {
+ /*
+ *NCP settle time recommended by codec
+ *specification
+ */
+ usleep_range(NCP_SETTLE_TIME_US,
+ NCP_SETTLE_TIME_US + 10);
+ wcd9xxx_clsh_set_Iest(codec, 0x02);
+ }
+ snd_soc_update_bits(codec,
+ WCD9XXX_A_BUCK_MODE_1,
+ 0x04, 0x00);
+ snd_soc_update_bits(codec,
+ WCD9XXX_A_BUCK_MODE_4,
+ 0xFF, BUCK_VREF_1P8V);
+ }
+ }
+ }
+}
+
+static void wcd9xxx_clsh_state_ear_lo(struct snd_soc_codec *codec,
+ struct wcd9xxx_clsh_cdc_data *clsh_d,
+ u8 req_state, bool is_enable)
+{
+
+ dev_dbg(codec->dev, "%s: enter %s\n", __func__,
+ is_enable ? "enable" : "disable");
+ if (is_enable) {
+ wcd9xxx_dynamic_bypass_buck_ctrl(codec, false);
+ wcd9xxx_enable_buck(codec, clsh_d, true);
+ if (req_state & WCD9XXX_CLSH_STATE_EAR) {
+ wcd9xxx_set_fclk_get_ncp(codec, clsh_d,
+ NCP_FCLK_LEVEL_8);
+ wcd9xxx_ncp_bypass_enable(codec, true);
+ wcd9xxx_enable_clsh_block(codec, clsh_d, true);
+ wcd9xxx_chargepump_request(codec, true);
+ wcd9xxx_enable_anc_delay(codec, true);
+ wcd9xxx_clsh_comp_req(codec, clsh_d,
+ CLSH_COMPUTE_EAR, true);
+ }
+ } else {
+ wcd9xxx_set_fclk_put_ncp(codec, clsh_d, NCP_FCLK_LEVEL_8);
+ wcd9xxx_ncp_bypass_enable(codec, false);
+ if (req_state & WCD9XXX_CLSH_STATE_LO) {
+ snd_soc_update_bits(codec, WCD9XXX_A_NCP_STATIC,
+ 0x20, 0x00);
+ wcd9xxx_dynamic_bypass_buck_ctrl(codec, true);
+ } else if (req_state & WCD9XXX_CLSH_STATE_EAR) {
+ wcd9xxx_clsh_comp_req(codec, clsh_d, CLSH_COMPUTE_EAR,
+ false);
+ /*sleep 5ms*/
+ if (clsh_d->buck_mv == WCD9XXX_CDC_BUCK_MV_1P8) {
+ wcd9xxx_enable_buck(codec, clsh_d, false);
+ wcd9xxx_ncp_bypass_enable(codec, true);
+ } else {
+ /* NCP settle time recommended by codec spec */
+ usleep_range(NCP_SETTLE_TIME_US,
+ NCP_SETTLE_TIME_US + 10);
+ wcd9xxx_clsh_set_Iest(codec, 0x02);
+ }
+ snd_soc_update_bits(codec, WCD9XXX_A_BUCK_MODE_1,
+ 0x04, 0x00);
+ snd_soc_update_bits(codec, WCD9XXX_A_BUCK_MODE_4,
+ 0xFF, BUCK_VREF_1P8V);
+ }
+ }
+}
+
+static void wcd9xxx_clsh_state_hph_ear_lo(struct snd_soc_codec *codec,
+ struct wcd9xxx_clsh_cdc_data *clsh_d,
+ u8 req_state, bool is_enable)
+{
+ dev_dbg(codec->dev, "%s: enter %s\n", __func__,
+ is_enable ? "enable" : "disable");
+
+ if (req_state & WCD9XXX_CLSH_STATE_HPHL)
+ wcd9xxx_clsh_comp_req(codec, clsh_d, CLSH_COMPUTE_HPH_L,
+ is_enable);
+
+ if (req_state & WCD9XXX_CLSH_STATE_HPHR)
+ wcd9xxx_clsh_comp_req(codec, clsh_d, CLSH_COMPUTE_HPH_R,
+ is_enable);
+
+ if (req_state & WCD9XXX_CLSH_STATE_EAR)
+ wcd9xxx_clsh_comp_req(codec, clsh_d, CLSH_COMPUTE_EAR,
+ is_enable);
+}
+
static void wcd9xxx_clsh_state_ear(struct snd_soc_codec *codec,
struct wcd9xxx_clsh_cdc_data *clsh_d,
u8 req_state, bool is_enable)
@@ -895,10 +1151,20 @@
pr_debug("%s: enter %s\n", __func__, is_enable ? "enable" : "disable");
if (is_enable) {
- wcd9xxx_clsh_comp_req(codec, clsh_d, CLSH_COMPUTE_HPH_L, true);
- wcd9xxx_clsh_comp_req(codec, clsh_d, CLSH_COMPUTE_HPH_R, true);
+ if (req_state == WCD9XXX_CLSH_STATE_HPHL)
+ wcd9xxx_clsh_comp_req(codec, clsh_d,
+ CLSH_COMPUTE_HPH_L, true);
+ if (req_state == WCD9XXX_CLSH_STATE_HPHR)
+ wcd9xxx_clsh_comp_req(codec, clsh_d,
+ CLSH_COMPUTE_HPH_R, true);
} else {
dev_dbg(codec->dev, "%s: stub fallback to hph_st\n", __func__);
+ if (req_state == WCD9XXX_CLSH_STATE_HPHL)
+ wcd9xxx_clsh_comp_req(codec, clsh_d,
+ CLSH_COMPUTE_HPH_L, false);
+ if (req_state == WCD9XXX_CLSH_STATE_HPHR)
+ wcd9xxx_clsh_comp_req(codec, clsh_d,
+ CLSH_COMPUTE_HPH_R, false);
}
}
@@ -952,6 +1218,47 @@
WARN_ON(1);
}
+/*
+ * Function: wcd9xxx_clsh_is_state_valid
+ * Params: state
+ * Description:
+ * Provides information on valid states of Class H configuration
+ */
+static int wcd9xxx_clsh_is_state_valid(u8 state)
+{
+ switch (state) {
+ case WCD9XXX_CLSH_STATE_IDLE:
+ case WCD9XXX_CLSH_STATE_EAR:
+ case WCD9XXX_CLSH_STATE_HPHL:
+ case WCD9XXX_CLSH_STATE_HPHR:
+ case WCD9XXX_CLSH_STATE_HPH_ST:
+ case WCD9XXX_CLSH_STATE_LO:
+ case WCD9XXX_CLSH_STATE_HPHL_EAR:
+ case WCD9XXX_CLSH_STATE_HPHR_EAR:
+ case WCD9XXX_CLSH_STATE_HPH_ST_EAR:
+ case WCD9XXX_CLSH_STATE_HPHL_LO:
+ case WCD9XXX_CLSH_STATE_HPHR_LO:
+ case WCD9XXX_CLSH_STATE_HPH_ST_LO:
+ case WCD9XXX_CLSH_STATE_EAR_LO:
+ case WCD9XXX_CLSH_STATE_HPHL_EAR_LO:
+ case WCD9XXX_CLSH_STATE_HPHR_EAR_LO:
+ case WCD9XXX_CLSH_STATE_HPH_ST_EAR_LO:
+ return 1;
+ default:
+ break;
+ }
+ return 0;
+}
+
+/*
+ * Function: wcd9xxx_clsh_fsm
+ * Params: codec, cdc_clsh_d, req_state, req_type, clsh_event
+ * Description:
+ * This function handles PRE DAC and POST DAC conditions of different devices
+ * and updates class H configuration of different combination of devices
+ * based on validity of their states. cdc_clsh_d will contain current
+ * class h state information
+ */
void wcd9xxx_clsh_fsm(struct snd_soc_codec *codec,
struct wcd9xxx_clsh_cdc_data *cdc_clsh_d,
u8 req_state, bool req_type, u8 clsh_event)
@@ -967,10 +1274,25 @@
old_state = cdc_clsh_d->state;
new_state = old_state | req_state;
- (*clsh_state_fp[req_state]) (codec, cdc_clsh_d, req_state,
+ if (!wcd9xxx_clsh_is_state_valid(new_state)) {
+ dev_dbg(codec->dev,
+ "%s: classH not a valid new state: %s\n",
+ __func__,
+ state_to_str(new_state, msg0, sizeof(msg0)));
+ return;
+ }
+ if (new_state == old_state) {
+ dev_dbg(codec->dev,
+ "%s: classH already in requested state: %s\n",
+ __func__,
+ state_to_str(new_state, msg0, sizeof(msg0)));
+ return;
+ }
+ (*clsh_state_fp[new_state]) (codec, cdc_clsh_d, req_state,
req_type);
cdc_clsh_d->state = new_state;
- dev_dbg(codec->dev, "%s: ClassH state transition from %s to %s\n",
+ dev_dbg(codec->dev,
+ "%s: ClassH state transition from %s to %s\n",
__func__, state_to_str(old_state, msg0, sizeof(msg0)),
state_to_str(cdc_clsh_d->state, msg1, sizeof(msg1)));
@@ -981,7 +1303,23 @@
new_state = old_state & (~req_state);
if (new_state < NUM_CLSH_STATES) {
- (*clsh_state_fp[req_state]) (codec, cdc_clsh_d,
+ if (!wcd9xxx_clsh_is_state_valid(old_state)) {
+ dev_dbg(codec->dev,
+ "%s:Invalid old state:%s\n",
+ __func__,
+ state_to_str(old_state, msg0,
+ sizeof(msg0)));
+ return;
+ }
+ if (new_state == old_state) {
+ dev_dbg(codec->dev,
+ "%s: clsH already in old state: %s\n",
+ __func__,
+ state_to_str(new_state, msg0,
+ sizeof(msg0)));
+ return;
+ }
+ (*clsh_state_fp[old_state]) (codec, cdc_clsh_d,
req_state,
req_type);
cdc_clsh_d->state = new_state;
@@ -992,7 +1330,7 @@
sizeof(msg1)));
} else {
- dev_dbg(codec->dev, "%s: wrong new state = %x\n",
+ dev_dbg(codec->dev, "%s:wrong new state=0x%x\n",
__func__, new_state);
}
} else if (!(cdc_clsh_d->state & WCD9XXX_CLSH_STATE_LO)) {
@@ -1023,6 +1361,23 @@
clsh_state_fp[WCD9XXX_CLSH_STATE_HPH_ST] =
wcd9xxx_clsh_state_hph_st;
clsh_state_fp[WCD9XXX_CLSH_STATE_LO] = wcd9xxx_clsh_state_lo;
+ clsh_state_fp[WCD9XXX_CLSH_STATE_HPHL_EAR] =
+ wcd9xxx_clsh_state_hph_ear;
+ clsh_state_fp[WCD9XXX_CLSH_STATE_HPHR_EAR] =
+ wcd9xxx_clsh_state_hph_ear;
+ clsh_state_fp[WCD9XXX_CLSH_STATE_HPH_ST_EAR] =
+ wcd9xxx_clsh_state_hph_ear;
+ clsh_state_fp[WCD9XXX_CLSH_STATE_HPHL_LO] = wcd9xxx_clsh_state_hph_lo;
+ clsh_state_fp[WCD9XXX_CLSH_STATE_HPHR_LO] = wcd9xxx_clsh_state_hph_lo;
+ clsh_state_fp[WCD9XXX_CLSH_STATE_HPH_ST_LO] =
+ wcd9xxx_clsh_state_hph_lo;
+ clsh_state_fp[WCD9XXX_CLSH_STATE_EAR_LO] = wcd9xxx_clsh_state_ear_lo;
+ clsh_state_fp[WCD9XXX_CLSH_STATE_HPHL_EAR_LO] =
+ wcd9xxx_clsh_state_hph_ear_lo;
+ clsh_state_fp[WCD9XXX_CLSH_STATE_HPHR_EAR_LO] =
+ wcd9xxx_clsh_state_hph_ear_lo;
+ clsh_state_fp[WCD9XXX_CLSH_STATE_HPH_ST_EAR_LO] =
+ wcd9xxx_clsh_state_hph_ear_lo;
}
EXPORT_SYMBOL_GPL(wcd9xxx_clsh_init);
diff --git a/sound/soc/codecs/wcd9xxx-common.h b/sound/soc/codecs/wcd9xxx-common.h
index 324f6e9..13f91ed 100644
--- a/sound/soc/codecs/wcd9xxx-common.h
+++ b/sound/soc/codecs/wcd9xxx-common.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
*
* This 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,7 +35,7 @@
#define WCD9XXX_CLSH_STATE_HPHL (0x01 << 1)
#define WCD9XXX_CLSH_STATE_HPHR (0x01 << 2)
#define WCD9XXX_CLSH_STATE_LO (0x01 << 3)
-#define NUM_CLSH_STATES ((0x01 << 4) - 1)
+#define NUM_CLSH_STATES (0x01 << 4)
#define WCD9XXX_DMIC_SAMPLE_RATE_DIV_2 0x0
#define WCD9XXX_DMIC_SAMPLE_RATE_DIV_3 0x1
@@ -56,6 +56,34 @@
#define WCD9XXX_CLSH_STATE_HPH_ST (WCD9XXX_CLSH_STATE_HPHL | \
WCD9XXX_CLSH_STATE_HPHR)
+#define WCD9XXX_CLSH_STATE_HPHL_EAR (WCD9XXX_CLSH_STATE_HPHL | \
+ WCD9XXX_CLSH_STATE_EAR)
+#define WCD9XXX_CLSH_STATE_HPHR_EAR (WCD9XXX_CLSH_STATE_HPHR | \
+ WCD9XXX_CLSH_STATE_EAR)
+
+#define WCD9XXX_CLSH_STATE_HPH_ST_EAR (WCD9XXX_CLSH_STATE_HPH_ST | \
+ WCD9XXX_CLSH_STATE_EAR)
+
+#define WCD9XXX_CLSH_STATE_HPHL_LO (WCD9XXX_CLSH_STATE_HPHL | \
+ WCD9XXX_CLSH_STATE_LO)
+#define WCD9XXX_CLSH_STATE_HPHR_LO (WCD9XXX_CLSH_STATE_HPHR | \
+ WCD9XXX_CLSH_STATE_LO)
+
+#define WCD9XXX_CLSH_STATE_HPH_ST_LO (WCD9XXX_CLSH_STATE_HPH_ST | \
+ WCD9XXX_CLSH_STATE_LO)
+
+#define WCD9XXX_CLSH_STATE_EAR_LO (WCD9XXX_CLSH_STATE_EAR | \
+ WCD9XXX_CLSH_STATE_LO)
+
+#define WCD9XXX_CLSH_STATE_HPHL_EAR_LO (WCD9XXX_CLSH_STATE_HPHL | \
+ WCD9XXX_CLSH_STATE_EAR | \
+ WCD9XXX_CLSH_STATE_LO)
+#define WCD9XXX_CLSH_STATE_HPHR_EAR_LO (WCD9XXX_CLSH_STATE_HPHR | \
+ WCD9XXX_CLSH_STATE_EAR | \
+ WCD9XXX_CLSH_STATE_LO)
+#define WCD9XXX_CLSH_STATE_HPH_ST_EAR_LO (WCD9XXX_CLSH_STATE_HPH_ST | \
+ WCD9XXX_CLSH_STATE_EAR | \
+ WCD9XXX_CLSH_STATE_LO)
struct wcd9xxx_reg_mask_val {
u16 reg;
diff --git a/sound/soc/msm/msm-dai-fe.c b/sound/soc/msm/msm-dai-fe.c
index 2b43ab6..045a6d0 100644
--- a/sound/soc/msm/msm-dai-fe.c
+++ b/sound/soc/msm/msm-dai-fe.c
@@ -899,6 +899,30 @@
.ops = &msm_fe_dai_ops,
.name = "LSM8",
},
+ {
+ .playback = {
+ .stream_name = "VoWLAN Playback",
+ .aif_name = "VoWLAN_DL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .capture = {
+ .stream_name = "VoWLAN Capture",
+ .aif_name = "VoWLAN_UL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "VoWLAN",
+ },
};
static __devinit int msm_fe_dai_dev_probe(struct platform_device *pdev)
diff --git a/sound/soc/msm/msm8226.c b/sound/soc/msm/msm8226.c
index 17e99dd..cc27fc0 100644
--- a/sound/soc/msm/msm8226.c
+++ b/sound/soc/msm/msm8226.c
@@ -1607,7 +1607,7 @@
.codec_name = "snd-soc-dummy",
.be_id = MSM_FRONTEND_DAI_LSM8,
},
- {
+ {/* hw:x,28 */
.name = "INT_HFP_BT Hostless",
.stream_name = "INT_HFP_BT Hostless",
.cpu_dai_name = "INT_HFP_BT_HOSTLESS",
@@ -1622,7 +1622,7 @@
.codec_dai_name = "snd-soc-dummy-dai",
.codec_name = "snd-soc-dummy",
},
- {
+ {/* hw:x,29 */
.name = "MSM8226 HFP TX",
.stream_name = "MultiMedia6",
.cpu_dai_name = "MultiMedia6",
@@ -1638,6 +1638,21 @@
.ignore_pmdown_time = 1,
.be_id = MSM_FRONTEND_DAI_MULTIMEDIA6,
},
+ {/* hw:x,30 */
+ .name = "VoWLAN",
+ .stream_name = "VoWLAN",
+ .cpu_dai_name = "VoWLAN",
+ .platform_name = "msm-pcm-voice",
+ .dynamic = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_VOWLAN,
+ },
/* Backend BT/FM DAI Links */
{
.name = LPASS_BE_INT_BT_SCO_RX,
diff --git a/sound/soc/msm/msm8974.c b/sound/soc/msm/msm8974.c
index ca5e217..9c01a08 100644
--- a/sound/soc/msm/msm8974.c
+++ b/sound/soc/msm/msm8974.c
@@ -2397,6 +2397,21 @@
.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
.ops = &msm8974_slimbus_2_be_ops,
},
+ {
+ .name = "MSM8974 Media9",
+ .stream_name = "MultiMedia9",
+ .cpu_dai_name = "MultiMedia9",
+ .platform_name = "msm-pcm-dsp.0",
+ .dynamic = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA9,
+ },
/* Backend BT/FM DAI Links */
{
.name = LPASS_BE_INT_BT_SCO_RX,
@@ -2688,6 +2703,19 @@
.be_hw_params_fixup = msm_be_hw_params_fixup,
.ignore_suspend = 1,
},
+ /* Incall Music 2 BACK END DAI Link */
+ {
+ .name = LPASS_BE_VOICE2_PLAYBACK_TX,
+ .stream_name = "Voice2 Farend Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.32770",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .be_id = MSM_BACKEND_DAI_VOICE2_PLAYBACK_TX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ignore_suspend = 1,
+ }
};
static struct snd_soc_dai_link msm8974_hdmi_dai_link[] = {
diff --git a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c
index 1434970..8b2c443 100644
--- a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c
@@ -57,6 +57,7 @@
struct msm_dai_q6_dai_data {
DECLARE_BITMAP(status_mask, STATUS_MAX);
+ DECLARE_BITMAP(hwfree_status, STATUS_MAX);
u32 rate;
u32 channels;
u32 bitwidth;
@@ -1510,6 +1511,11 @@
set_bit(STATUS_PORT_STARTED,
dai_data->status_mask);
}
+ if (!test_bit(STATUS_PORT_STARTED, dai_data->hwfree_status)) {
+ set_bit(STATUS_PORT_STARTED, dai_data->hwfree_status);
+ dev_dbg(dai->dev, "%s: set hwfree_status to started\n",
+ __func__);
+ }
return rc;
}
@@ -1525,7 +1531,6 @@
struct msm_dai_q6_dai_data *dai_data = &mi2s_dai_config->mi2s_dai_data;
struct afe_param_id_i2s_cfg *i2s = &dai_data->port_config.i2s;
-
dai_data->channels = params_channels(params);
switch (dai_data->channels) {
case 8:
@@ -1602,10 +1607,14 @@
dai_data->port_config.i2s.i2s_cfg_minor_version =
AFE_API_VERSION_I2S_CONFIG;
dai_data->port_config.i2s.sample_rate = dai_data->rate;
- if (test_bit(STATUS_PORT_STARTED,
- mi2s_dai_data->rx_dai.mi2s_dai_data.status_mask) ||
+ if ((test_bit(STATUS_PORT_STARTED,
+ mi2s_dai_data->rx_dai.mi2s_dai_data.status_mask) &&
test_bit(STATUS_PORT_STARTED,
- mi2s_dai_data->tx_dai.mi2s_dai_data.status_mask)) {
+ mi2s_dai_data->rx_dai.mi2s_dai_data.hwfree_status)) ||
+ (test_bit(STATUS_PORT_STARTED,
+ mi2s_dai_data->tx_dai.mi2s_dai_data.status_mask) &&
+ test_bit(STATUS_PORT_STARTED,
+ mi2s_dai_data->tx_dai.mi2s_dai_data.hwfree_status))) {
if ((mi2s_dai_data->tx_dai.mi2s_dai_data.rate !=
mi2s_dai_data->rx_dai.mi2s_dai_data.rate) ||
(mi2s_dai_data->rx_dai.mi2s_dai_data.bitwidth !=
@@ -1669,6 +1678,23 @@
return 0;
}
+static int msm_dai_q6_mi2s_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct msm_dai_q6_mi2s_dai_data *mi2s_dai_data =
+ dev_get_drvdata(dai->dev);
+ struct msm_dai_q6_dai_data *dai_data =
+ (substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+ &mi2s_dai_data->rx_dai.mi2s_dai_data :
+ &mi2s_dai_data->tx_dai.mi2s_dai_data);
+
+ if (test_bit(STATUS_PORT_STARTED, dai_data->hwfree_status)) {
+ clear_bit(STATUS_PORT_STARTED, dai_data->hwfree_status);
+ dev_dbg(dai->dev, "%s: clear hwfree_status\n", __func__);
+ }
+ return 0;
+}
+
static void msm_dai_q6_mi2s_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
@@ -1696,12 +1722,15 @@
dev_err(dai->dev, "fail to close AFE port\n");
clear_bit(STATUS_PORT_STARTED, dai_data->status_mask);
}
+ if (test_bit(STATUS_PORT_STARTED, dai_data->hwfree_status))
+ clear_bit(STATUS_PORT_STARTED, dai_data->hwfree_status);
}
static struct snd_soc_dai_ops msm_dai_q6_mi2s_ops = {
.startup = msm_dai_q6_mi2s_startup,
.prepare = msm_dai_q6_mi2s_prepare,
.hw_params = msm_dai_q6_mi2s_hw_params,
+ .hw_free = msm_dai_q6_mi2s_hw_free,
.set_fmt = msm_dai_q6_mi2s_set_fmt,
.shutdown = msm_dai_q6_mi2s_shutdown,
};
diff --git a/sound/soc/msm/qdsp6v2/msm-dolby-dap-config.c b/sound/soc/msm/qdsp6v2/msm-dolby-dap-config.c
index 63ac5d3..161904c 100644
--- a/sound/soc/msm/qdsp6v2/msm-dolby-dap-config.c
+++ b/sound/soc/msm/qdsp6v2/msm-dolby-dap-config.c
@@ -211,6 +211,14 @@
DOLBY_ENDDEP_PARAM_VMB_OFFSET},
{-320, -320, 144}
},
+ {PROXY, 6, DOLBY_ENDP_EXT_SPEAKERS,
+ {DOLBY_PARAM_ID_DVLO, DOLBY_PARAM_ID_DVLI, DOLBY_PARAM_ID_VMB},
+ {DOLBY_ENDDEP_PARAM_DVLO_LENGTH, DOLBY_ENDDEP_PARAM_DVLI_LENGTH,
+ DOLBY_ENDDEP_PARAM_VMB_LENGTH},
+ {DOLBY_ENDDEP_PARAM_DVLO_OFFSET, DOLBY_ENDDEP_PARAM_DVLI_OFFSET,
+ DOLBY_ENDDEP_PARAM_VMB_OFFSET},
+ {-320, -320, 144}
+ },
{FM, 2, DOLBY_ENDP_HDMI,
{DOLBY_PARAM_ID_DVLO, DOLBY_PARAM_ID_DVLI, DOLBY_PARAM_ID_VMB},
{DOLBY_ENDDEP_PARAM_DVLO_LENGTH, DOLBY_ENDDEP_PARAM_DVLI_LENGTH,
@@ -409,7 +417,8 @@
for (idx = 0; idx < NUM_DOLBY_ENDP_DEVICE; idx++) {
if (dolby_dap_endp_params[idx].device ==
dolby_dap_params_states.device) {
- if (dolby_dap_params_states.device == AUX_DIGITAL) {
+ if (dolby_dap_params_states.device == AUX_DIGITAL ||
+ dolby_dap_params_states.device == PROXY) {
if (dolby_dap_endp_params[idx].device_ch_caps ==
device_channels)
break;
diff --git a/sound/soc/msm/qdsp6v2/msm-dolby-dap-config.h b/sound/soc/msm/qdsp6v2/msm-dolby-dap-config.h
index 4544fea..14586f4 100644
--- a/sound/soc/msm/qdsp6v2/msm-dolby-dap-config.h
+++ b/sound/soc/msm/qdsp6v2/msm-dolby-dap-config.h
@@ -249,7 +249,7 @@
#define DOLBY_AUTO_ENDDEP_IDX (MAX_DOLBY_PARAMS+4)
#define TOTAL_LENGTH_DOLBY_PARAM 745
-#define NUM_DOLBY_ENDP_DEVICE 23
+#define NUM_DOLBY_ENDP_DEVICE 24
#define DOLBY_VIS_PARAM_HEADER_SIZE 25
#define DOLBY_INVALID_PORT_ID -1
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c
index 4aa1bce..157d63a 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c
@@ -688,6 +688,8 @@
session_id = voc_get_session_id(VOICE_SESSION_NAME);
else if (val == MSM_FRONTEND_DAI_VOLTE)
session_id = voc_get_session_id(VOLTE_SESSION_NAME);
+ else if (val == MSM_FRONTEND_DAI_VOWLAN)
+ session_id = voc_get_session_id(VOWLAN_SESSION_NAME);
else if (val == MSM_FRONTEND_DAI_VOICE2)
session_id = voc_get_session_id(VOICE2_SESSION_NAME);
else if (val == MSM_FRONTEND_DAI_QCHAT)
@@ -723,9 +725,9 @@
if (voc_get_route_flag(session_id, RX_PATH) &&
voc_get_route_flag(session_id, TX_PATH))
- voc_enable_cvp(session_id);
+ voc_enable_device(session_id);
} else {
- voc_disable_cvp(session_id);
+ voc_disable_device(session_id);
}
} else {
voc_set_route_flag(session_id, TX_PATH, set);
@@ -734,9 +736,9 @@
msm_bedais[reg].port_id, DEV_TX);
if (voc_get_route_flag(session_id, RX_PATH) &&
voc_get_route_flag(session_id, TX_PATH))
- voc_enable_cvp(session_id);
+ voc_enable_device(session_id);
} else {
- voc_disable_cvp(session_id);
+ voc_disable_device(session_id);
}
}
}
@@ -2110,6 +2112,9 @@
SOC_SINGLE_EXT("VoLTE", MSM_BACKEND_DAI_PRI_I2S_RX,
MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("VoWLAN", MSM_BACKEND_DAI_PRI_I2S_RX,
+ MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
SOC_SINGLE_EXT("DTMF", MSM_BACKEND_DAI_PRI_I2S_RX,
MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer,
msm_routing_put_voice_mixer),
@@ -2131,6 +2136,9 @@
SOC_SINGLE_EXT("VoLTE", MSM_BACKEND_DAI_SEC_I2S_RX,
MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("VoWLAN", MSM_BACKEND_DAI_SEC_I2S_RX,
+ MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
SOC_SINGLE_EXT("DTMF", MSM_BACKEND_DAI_SEC_I2S_RX,
MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer,
msm_routing_put_voice_mixer),
@@ -2152,6 +2160,9 @@
SOC_SINGLE_EXT("VoLTE", MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("VoWLAN", MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+ MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
SOC_SINGLE_EXT("DTMF", MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer,
msm_routing_put_voice_mixer),
@@ -2176,6 +2187,9 @@
SOC_SINGLE_EXT("VoLTE", MSM_BACKEND_DAI_SLIMBUS_0_RX ,
MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("VoWLAN", MSM_BACKEND_DAI_SLIMBUS_0_RX ,
+ MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
SOC_SINGLE_EXT("DTMF", MSM_BACKEND_DAI_SLIMBUS_0_RX ,
MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer,
msm_routing_put_voice_mixer),
@@ -2200,6 +2214,9 @@
SOC_SINGLE_EXT("VoLTE", MSM_BACKEND_DAI_INT_BT_SCO_RX ,
MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("VoWLAN", MSM_BACKEND_DAI_INT_BT_SCO_RX ,
+ MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
SOC_SINGLE_EXT("DTMF", MSM_BACKEND_DAI_INT_BT_SCO_RX ,
MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer,
msm_routing_put_voice_mixer),
@@ -2224,6 +2241,9 @@
SOC_SINGLE_EXT("VoLTE", MSM_BACKEND_DAI_MI2S_RX,
MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("VoWLAN", MSM_BACKEND_DAI_MI2S_RX,
+ MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
SOC_SINGLE_EXT("DTMF", MSM_BACKEND_DAI_MI2S_RX,
MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer,
msm_routing_put_voice_mixer),
@@ -2248,6 +2268,9 @@
SOC_SINGLE_EXT("VoLTE", MSM_BACKEND_DAI_AFE_PCM_RX,
MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("VoWLAN", MSM_BACKEND_DAI_AFE_PCM_RX,
+ MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
SOC_SINGLE_EXT("DTMF", MSM_BACKEND_DAI_AFE_PCM_RX,
MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer,
msm_routing_put_voice_mixer),
@@ -2272,6 +2295,9 @@
SOC_SINGLE_EXT("VoLTE", MSM_BACKEND_DAI_AUXPCM_RX,
MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("VoWLAN", MSM_BACKEND_DAI_AUXPCM_RX,
+ MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
SOC_SINGLE_EXT("DTMF", MSM_BACKEND_DAI_AUXPCM_RX,
MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer,
msm_routing_put_voice_mixer),
@@ -2293,6 +2319,9 @@
SOC_SINGLE_EXT("VoLTE", MSM_BACKEND_DAI_SEC_AUXPCM_RX,
MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("VoWLAN", MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+ MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
SOC_SINGLE_EXT("DTMF", MSM_BACKEND_DAI_SEC_AUXPCM_RX,
MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer,
msm_routing_put_voice_mixer),
@@ -2314,6 +2343,9 @@
SOC_SINGLE_EXT("VoLTE", MSM_BACKEND_DAI_HDMI_RX,
MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("VoWLAN", MSM_BACKEND_DAI_HDMI_RX,
+ MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
SOC_SINGLE_EXT("Voice Stub", MSM_BACKEND_DAI_HDMI_RX,
MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
msm_routing_put_voice_stub_mixer),
@@ -2418,6 +2450,33 @@
msm_routing_put_voice_mixer),
};
+static const struct snd_kcontrol_new tx_vowlan_mixer_controls[] = {
+ SOC_SINGLE_EXT("PRI_TX_VoWLAN", MSM_BACKEND_DAI_PRI_I2S_TX,
+ MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("SLIM_0_TX_VoWLAN", MSM_BACKEND_DAI_SLIMBUS_0_TX,
+ MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX_VoWLAN",
+ MSM_BACKEND_DAI_INT_BT_SCO_TX, MSM_FRONTEND_DAI_VOWLAN, 1, 0,
+ msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("AFE_PCM_TX_VoWLAN", MSM_BACKEND_DAI_AFE_PCM_TX,
+ MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("AUX_PCM_TX_VoWLAN", MSM_BACKEND_DAI_AUXPCM_TX,
+ MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("SEC_AUX_PCM_TX_VoWLAN", MSM_BACKEND_DAI_SEC_AUXPCM_TX,
+ MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("MI2S_TX_VoWLAN", MSM_BACKEND_DAI_MI2S_TX,
+ MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("PRI_MI2S_TX_VoWLAN", MSM_BACKEND_DAI_PRI_MI2S_TX,
+ MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+};
+
static const struct snd_kcontrol_new tx_voip_mixer_controls[] = {
SOC_SINGLE_EXT("PRI_TX_Voip", MSM_BACKEND_DAI_PRI_I2S_TX,
MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
@@ -3156,6 +3215,8 @@
SND_SOC_DAPM_AIF_OUT("VOICE2_UL", "Voice2 Capture", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_IN("VoLTE_DL", "VoLTE Playback", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_OUT("VoLTE_UL", "VoLTE Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("VoWLAN_DL", "VoWLAN Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("VoWLAN_UL", "VoWLAN Capture", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_OUT("VOIP_UL", "VoIP Capture", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_IN("SLIM0_DL_HL", "SLIMBUS0_HOSTLESS Playback",
0, 0, 0, 0),
@@ -3425,6 +3486,9 @@
SND_SOC_DAPM_MIXER("VoLTE_Tx Mixer",
SND_SOC_NOPM, 0, 0, tx_volte_mixer_controls,
ARRAY_SIZE(tx_volte_mixer_controls)),
+ SND_SOC_DAPM_MIXER("VoWLAN_Tx Mixer",
+ SND_SOC_NOPM, 0, 0, tx_vowlan_mixer_controls,
+ ARRAY_SIZE(tx_vowlan_mixer_controls)),
SND_SOC_DAPM_MIXER("INTERNAL_BT_SCO_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
int_bt_sco_rx_mixer_controls, ARRAY_SIZE(int_bt_sco_rx_mixer_controls)),
SND_SOC_DAPM_MIXER("INTERNAL_FM_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
@@ -3706,6 +3770,7 @@
{"PRI_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
{"PRI_RX_Voice Mixer", "Voice2", "VOICE2_DL"},
{"PRI_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
+ {"PRI_RX_Voice Mixer", "VoWLAN", "VoWLAN_DL"},
{"PRI_RX_Voice Mixer", "Voip", "VOIP_DL"},
{"PRI_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"},
{"PRI_RX_Voice Mixer", "QCHAT", "QCHAT_DL"},
@@ -3714,6 +3779,7 @@
{"SEC_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
{"SEC_RX_Voice Mixer", "Voice2", "VOICE2_DL"},
{"SEC_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
+ {"SEC_RX_Voice Mixer", "VoWLAN", "VoWLAN_DL"},
{"SEC_RX_Voice Mixer", "Voip", "VOIP_DL"},
{"SEC_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"},
{"SEC_RX_Voice Mixer", "QCHAT", "QCHAT_DL"},
@@ -3722,6 +3788,7 @@
{"SEC_MI2S_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
{"SEC_MI2S_RX_Voice Mixer", "Voice2", "VOICE2_DL"},
{"SEC_MI2S_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
+ {"SEC_MI2S_RX_Voice Mixer", "VoWLAN", "VoWLAN_DL"},
{"SEC_MI2S_RX_Voice Mixer", "Voip", "VOIP_DL"},
{"SEC_MI2S_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"},
{"SEC_MI2S_RX_Voice Mixer", "QCHAT", "QCHAT_DL"},
@@ -3730,6 +3797,7 @@
{"SLIM_0_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
{"SLIM_0_RX_Voice Mixer", "Voice2", "VOICE2_DL"},
{"SLIM_0_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
+ {"SLIM_0_RX_Voice Mixer", "VoWLAN", "VoWLAN_DL"},
{"SLIM_0_RX_Voice Mixer", "Voip", "VOIP_DL"},
{"SLIM_0_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"},
{"SLIM_0_RX_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"},
@@ -3739,6 +3807,7 @@
{"INTERNAL_BT_SCO_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
{"INTERNAL_BT_SCO_RX_Voice Mixer", "Voice2", "VOICE2_DL"},
{"INTERNAL_BT_SCO_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
+ {"INTERNAL_BT_SCO_RX_Voice Mixer", "VoWLAN", "VoWLAN_DL"},
{"INTERNAL_BT_SCO_RX_Voice Mixer", "Voip", "VOIP_DL"},
{"INTERNAL_BT_SCO_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"},
{"INTERNAL_BT_SCO_RX_Voice Mixer", "QCHAT", "QCHAT_DL"},
@@ -3747,6 +3816,7 @@
{"AFE_PCM_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
{"AFE_PCM_RX_Voice Mixer", "Voice2", "VOICE2_DL"},
{"AFE_PCM_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
+ {"AFE_PCM_RX_Voice Mixer", "VoWLAN", "VoWLAN_DL"},
{"AFE_PCM_RX_Voice Mixer", "Voip", "VOIP_DL"},
{"AFE_PCM_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"},
{"AFE_PCM_RX_Voice Mixer", "QCHAT", "QCHAT_DL"},
@@ -3755,6 +3825,7 @@
{"AUX_PCM_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
{"AUX_PCM_RX_Voice Mixer", "Voice2", "VOICE2_DL"},
{"AUX_PCM_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
+ {"AUX_PCM_RX_Voice Mixer", "VoWLAN", "VoWLAN_DL"},
{"AUX_PCM_RX_Voice Mixer", "Voip", "VOIP_DL"},
{"AUX_PCM_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"},
{"AUX_PCM_RX_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"},
@@ -3763,6 +3834,7 @@
{"SEC_AUX_PCM_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
{"SEC_AUX_PCM_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
+ {"SEC_AUX_PCM_RX_Voice Mixer", "VoWLAN", "VoWLAN_DL"},
{"SEC_AUX_PCM_RX_Voice Mixer", "Voip", "VOIP_DL"},
{"SEC_AUX_PCM_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"},
{"SEC_AUX_PCM_RX_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"},
@@ -3772,6 +3844,7 @@
{"HDMI_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
{"HDMI_RX_Voice Mixer", "Voice2", "VOICE2_DL"},
{"HDMI_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
+ {"HDMI_RX_Voice Mixer", "VoWLAN", "VoWLAN_DL"},
{"HDMI_RX_Voice Mixer", "Voip", "VOIP_DL"},
{"HDMI_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"},
{"HDMI_RX_Voice Mixer", "QCHAT", "QCHAT_DL"},
@@ -3782,6 +3855,7 @@
{"MI2S_RX_Voice Mixer", "Voice2", "VOICE2_DL"},
{"MI2S_RX_Voice Mixer", "Voip", "VOIP_DL"},
{"MI2S_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
+ {"MI2S_RX_Voice Mixer", "VoWLAN", "VoWLAN_DL"},
{"MI2S_RX_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"},
{"MI2S_RX_Voice Mixer", "QCHAT", "QCHAT_DL"},
{"MI2S_RX", NULL, "MI2S_RX_Voice Mixer"},
@@ -3791,6 +3865,8 @@
{"VOC_EXT_EC MUX", "TERT_MI2S_TX" , "TERT_MI2S_TX"},
{"VOC_EXT_EC MUX", "QUAT_MI2S_TX" , "QUAT_MI2S_TX"},
{"CS-VOICE_UL1", NULL, "VOC_EXT_EC MUX"},
+ {"VOIP_UL", NULL, "VOC_EXT_EC MUX"},
+ {"VoLTE_UL", NULL, "VOC_EXT_EC MUX"},
{"Voice_Tx Mixer", "PRI_TX_Voice", "PRI_I2S_TX"},
{"Voice_Tx Mixer", "PRI_MI2S_TX_Voice", "PRI_MI2S_TX"},
@@ -3819,6 +3895,16 @@
{"VoLTE_Tx Mixer", "SEC_AUX_PCM_TX_VoLTE", "SEC_AUX_PCM_TX"},
{"VoLTE_Tx Mixer", "MI2S_TX_VoLTE", "MI2S_TX"},
{"VoLTE_UL", NULL, "VoLTE_Tx Mixer"},
+
+ {"VoWLAN_Tx Mixer", "PRI_TX_VoWLAN", "PRI_I2S_TX"},
+ {"VoWLAN_Tx Mixer", "SLIM_0_TX_VoWLAN", "SLIMBUS_0_TX"},
+ {"VoWLAN_Tx Mixer", "INTERNAL_BT_SCO_TX_VoWLAN", "INT_BT_SCO_TX"},
+ {"VoWLAN_Tx Mixer", "AFE_PCM_TX_VoWLAN", "PCM_TX"},
+ {"VoWLAN_Tx Mixer", "AUX_PCM_TX_VoWLAN", "AUX_PCM_TX"},
+ {"VoWLAN_Tx Mixer", "SEC_AUX_PCM_TX_VoWLAN", "SEC_AUX_PCM_TX"},
+ {"VoWLAN_Tx Mixer", "MI2S_TX_VoWLAN", "MI2S_TX"},
+ {"VoWLAN_UL", NULL, "VoWLAN_Tx Mixer"},
+
{"Voip_Tx Mixer", "PRI_TX_Voip", "PRI_I2S_TX"},
{"Voip_Tx Mixer", "MI2S_TX_Voip", "MI2S_TX"},
{"Voip_Tx Mixer", "SLIM_0_TX_Voip", "SLIMBUS_0_TX"},
@@ -4183,7 +4269,7 @@
path_type,
bedai->sample_rate,
channels,
- topology, false,
+ topology, fe_dai_perf_mode[i][session_type],
bits_per_sample);
}
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h
index f2b0436..87e44b2 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h
@@ -89,6 +89,7 @@
MSM_FRONTEND_DAI_LSM6,
MSM_FRONTEND_DAI_LSM7,
MSM_FRONTEND_DAI_LSM8,
+ MSM_FRONTEND_DAI_VOWLAN,
MSM_FRONTEND_DAI_MAX,
};
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-voice-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-voice-v2.c
index 1074d76..c54d03c 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-voice-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-voice-v2.c
@@ -75,6 +75,14 @@
return false;
}
+static bool is_vowlan(struct msm_voice *pvowlan)
+{
+ if (pvowlan == &voice_info[VOWLAN_SESSION_INDEX])
+ return true;
+ else
+ return false;
+}
+
static uint32_t get_session_id(struct msm_voice *pvoc)
{
uint32_t session_id = 0;
@@ -85,6 +93,8 @@
session_id = voc_get_session_id(VOICE2_SESSION_NAME);
else if (is_qchat(pvoc))
session_id = voc_get_session_id(QCHAT_SESSION_NAME);
+ else if (is_vowlan(pvoc))
+ session_id = voc_get_session_id(VOWLAN_SESSION_NAME);
else
session_id = voc_get_session_id(VOICE_SESSION_NAME);
@@ -134,6 +144,10 @@
voice = &voice_info[QCHAT_SESSION_INDEX];
pr_debug("%s: Open QCHAT Substream Id=%s\n",
__func__, substream->pcm->id);
+ } else if (!strncmp("VoWLAN", substream->pcm->id, 6)) {
+ voice = &voice_info[VOWLAN_SESSION_INDEX];
+ pr_debug("%s: Open VoWLAN Substream Id=%s\n",
+ __func__, substream->pcm->id);
} else {
voice = &voice_info[VOICE_SESSION_INDEX];
pr_debug("%s: Open VOICE Substream Id=%s\n",
@@ -391,12 +405,37 @@
pr_debug("%s: mute=%d session_id=%#x ramp_duration=%d\n", __func__,
mute, session_id, ramp_duration);
- voc_set_tx_mute(session_id, TX_PATH, mute, ramp_duration);
+ ret = voc_set_tx_mute(session_id, TX_PATH, mute, ramp_duration);
done:
return ret;
}
+static int msm_voice_tx_device_mute_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ret = 0;
+ int mute = ucontrol->value.integer.value[0];
+ uint32_t session_id = ucontrol->value.integer.value[1];
+ int ramp_duration = ucontrol->value.integer.value[2];
+
+ if ((mute < 0) || (mute > 1) || (ramp_duration < 0) ||
+ (ramp_duration > MAX_RAMP_DURATION)) {
+ pr_err(" %s Invalid arguments", __func__);
+
+ ret = -EINVAL;
+ goto done;
+ }
+
+ pr_debug("%s: mute=%d session_id=%#x ramp_duration=%d\n", __func__,
+ mute, session_id, ramp_duration);
+
+ ret = voc_set_device_mute(session_id, VSS_IVOLUME_DIRECTION_TX,
+ mute, ramp_duration);
+
+done:
+ return ret;
+}
static int msm_voice_rx_device_mute_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
@@ -406,8 +445,8 @@
uint32_t session_id = ucontrol->value.integer.value[1];
int ramp_duration = ucontrol->value.integer.value[2];
- if ((mute < 0) || (mute > 1) || (ramp_duration < 0)
- || (ramp_duration > MAX_RAMP_DURATION)) {
+ if ((mute < 0) || (mute > 1) || (ramp_duration < 0) ||
+ (ramp_duration > MAX_RAMP_DURATION)) {
pr_err(" %s Invalid arguments", __func__);
ret = -EINVAL;
@@ -415,9 +454,10 @@
}
pr_debug("%s: mute=%d session_id=%#x ramp_duration=%d\n", __func__,
- mute, session_id, ramp_duration);
+ mute, session_id, ramp_duration);
- voc_set_rx_device_mute(session_id, mute, ramp_duration);
+ voc_set_device_mute(session_id, VSS_IVOLUME_DIRECTION_RX,
+ mute, ramp_duration);
done:
return ret;
@@ -448,6 +488,7 @@
voc_set_tty_mode(voc_get_session_id(VOICE_SESSION_NAME), tty_mode);
voc_set_tty_mode(voc_get_session_id(VOICE2_SESSION_NAME), tty_mode);
voc_set_tty_mode(voc_get_session_id(VOLTE_SESSION_NAME), tty_mode);
+ voc_set_tty_mode(voc_get_session_id(VOWLAN_SESSION_NAME), tty_mode);
return 0;
}
@@ -470,6 +511,8 @@
static struct snd_kcontrol_new msm_voice_controls[] = {
SOC_SINGLE_MULTI_EXT("Voice Rx Device Mute", SND_SOC_NOPM, 0, VSID_MAX,
0, 3, NULL, msm_voice_rx_device_mute_put),
+ SOC_SINGLE_MULTI_EXT("Voice Tx Device Mute", SND_SOC_NOPM, 0, VSID_MAX,
+ 0, 3, NULL, msm_voice_tx_device_mute_put),
SOC_SINGLE_MULTI_EXT("Voice Tx Mute", SND_SOC_NOPM, 0, VSID_MAX,
0, 3, NULL, msm_voice_mute_put),
SOC_SINGLE_MULTI_EXT("Voice Rx Gain", SND_SOC_NOPM, 0, VSID_MAX, 0, 3,
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-voice-v2.h b/sound/soc/msm/qdsp6v2/msm-pcm-voice-v2.h
index f199be6..62c5732 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-voice-v2.h
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-voice-v2.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
*
* This 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 @@
VOLTE_SESSION_INDEX,
VOICE2_SESSION_INDEX,
QCHAT_SESSION_INDEX,
+ VOWLAN_SESSION_INDEX,
VOICE_SESSION_INDEX_MAX,
};
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-voip-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-voip-v2.c
index e3c8944..6b32064 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-voip-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-voip-v2.c
@@ -47,8 +47,27 @@
#define MODE_AMR_WB 0xD
#define MODE_PCM 0xC
#define MODE_4GV_NW 0xE
+#define MODE_G711 0xA
+#define MODE_G711A 0xF
-#define VOIP_MODE_MAX MODE_4GV_NW
+enum msm_audio_g711a_frame_type {
+ MVS_G711A_SPEECH_GOOD,
+ MVS_G711A_SID,
+ MVS_G711A_NO_DATA,
+ MVS_G711A_ERASURE
+};
+
+enum msm_audio_g711a_mode {
+ MVS_G711A_MODE_MULAW,
+ MVS_G711A_MODE_ALAW
+};
+
+enum msm_audio_g711_mode {
+ MVS_G711_MODE_MULAW,
+ MVS_G711_MODE_ALAW
+};
+
+#define VOIP_MODE_MAX MODE_G711A
#define VOIP_RATE_MAX 23850
enum format {
@@ -153,7 +172,7 @@
uint32_t evrc_max_rate;
};
-static int voip_get_media_type(uint32_t mode,
+static int voip_get_media_type(uint32_t mode, uint32_t rate_type,
unsigned int samp_rate,
unsigned int *media_type);
static int voip_get_rate_type(uint32_t mode,
@@ -358,6 +377,81 @@
list_add_tail(&buf_node->list, &prtd->out_queue);
break;
}
+ case MODE_G711:
+ case MODE_G711A:{
+ /* G711 frames are 10ms each, but the DSP works with
+ * 20ms frames and sends two 10ms frames per buffer.
+ * Extract the two frames and put them in separate
+ * buffers.
+ */
+ /* Remove the first DSP frame info header.
+ * Header format: G711A
+ * Bits 0-1: Frame type
+ * Bits 2-3: Frame rate
+ *
+ * Header format: G711
+ * Bits 2-3: Frame rate
+ */
+ if (prtd->mode == MODE_G711A)
+ buf_node->frame.frm_hdr.frame_type =
+ (*voc_pkt) & 0x03;
+ buf_node->frame.frm_hdr.timestamp = timestamp;
+ voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN;
+
+ /* There are two frames in the buffer. Length of the
+ * first frame:
+ */
+ buf_node->frame.pktlen = (pkt_len -
+ 2 * DSP_FRAME_HDR_LEN) / 2;
+
+ memcpy(&buf_node->frame.voc_pkt[0],
+ voc_pkt,
+ buf_node->frame.pktlen);
+ voc_pkt = voc_pkt + buf_node->frame.pktlen;
+
+ list_add_tail(&buf_node->list, &prtd->out_queue);
+
+ /* Get another buffer from the free Q and fill in the
+ * second frame.
+ */
+ if (!list_empty(&prtd->free_out_queue)) {
+ buf_node =
+ list_first_entry(&prtd->free_out_queue,
+ struct voip_buf_node,
+ list);
+ list_del(&buf_node->list);
+
+ /* Remove the second DSP frame info header.
+ * Header format:
+ * Bits 0-1: Frame type
+ * Bits 2-3: Frame rate
+ */
+
+ if (prtd->mode == MODE_G711A)
+ buf_node->frame.frm_hdr.frame_type =
+ (*voc_pkt) & 0x03;
+ buf_node->frame.frm_hdr.timestamp = timestamp;
+ voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN;
+
+ /* There are two frames in the buffer. Length
+ * of the second frame:
+ */
+ buf_node->frame.pktlen = (pkt_len -
+ 2 * DSP_FRAME_HDR_LEN) / 2;
+
+ memcpy(&buf_node->frame.voc_pkt[0],
+ voc_pkt,
+ buf_node->frame.pktlen);
+
+ list_add_tail(&buf_node->list,
+ &prtd->out_queue);
+ } else {
+ /* Drop the second frame */
+ pr_err("%s: UL data dropped, read is slow\n",
+ __func__);
+ }
+ break;
+ }
default: {
buf_node->frame.frm_hdr.timestamp = timestamp;
buf_node->frame.pktlen = pkt_len;
@@ -389,6 +483,8 @@
unsigned long dsp_flags;
uint32_t rate_type;
uint32_t frame_rate;
+ u32 pkt_len;
+ u8 *voc_addr = NULL;
if (prtd->playback_substream == NULL)
return;
@@ -454,6 +550,70 @@
list_add_tail(&buf_node->list, &prtd->free_in_queue);
break;
}
+ case MODE_G711:
+ case MODE_G711A:{
+ /* G711 frames are 10ms each but the DSP expects 20ms
+ * worth of data, so send two 10ms frames per buffer.
+ */
+ /* Add the first DSP frame info header. Header format:
+ * Bits 0-1: Frame type
+ * Bits 2-3: Frame rate
+ */
+ voc_addr = voc_pkt;
+ voc_pkt = voc_pkt + sizeof(uint32_t);
+
+ *voc_pkt = ((prtd->rate_type & 0x0F) << 2) |
+ (buf_node->frame.frm_hdr.frame_type & 0x03);
+ voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN;
+
+ pkt_len = buf_node->frame.pktlen + DSP_FRAME_HDR_LEN;
+
+ memcpy(voc_pkt,
+ &buf_node->frame.voc_pkt[0],
+ buf_node->frame.pktlen);
+ voc_pkt = voc_pkt + buf_node->frame.pktlen;
+
+ list_add_tail(&buf_node->list, &prtd->free_in_queue);
+
+ if (!list_empty(&prtd->in_queue)) {
+ /* Get the second buffer. */
+ buf_node = list_first_entry(&prtd->in_queue,
+ struct voip_buf_node,
+ list);
+ list_del(&buf_node->list);
+
+ /* Add the second DSP frame info header.
+ * Header format:
+ * Bits 0-1: Frame type
+ * Bits 2-3: Frame rate
+ */
+ *voc_pkt = ((prtd->rate_type & 0x0F) << 2) |
+ (buf_node->frame.frm_hdr.frame_type & 0x03);
+ voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN;
+
+ pkt_len = pkt_len + buf_node->frame.pktlen +
+ DSP_FRAME_HDR_LEN;
+
+ memcpy(voc_pkt,
+ &buf_node->frame.voc_pkt[0],
+ buf_node->frame.pktlen);
+
+ list_add_tail(&buf_node->list,
+ &prtd->free_in_queue);
+ } else {
+ /* Only 10ms worth of data is available, signal
+ * erasure frame.
+ */
+ *voc_pkt = ((prtd->rate_type & 0x0F) << 2) |
+ (MVS_G711A_ERASURE & 0x03);
+
+ pkt_len = pkt_len + DSP_FRAME_HDR_LEN;
+ pr_debug("%s, Only 10ms read, erase 2nd frame\n",
+ __func__);
+ }
+ *((uint32_t *)voc_addr) = pkt_len;
+ break;
+ }
default: {
*((uint32_t *)voc_pkt) = buf_node->frame.pktlen;
voc_pkt = voc_pkt + sizeof(uint32_t);
@@ -829,10 +989,12 @@
pr_debug("%s(): mode=%d, playback sample rate=%d, capture sample rate=%d\n",
__func__, prtd->mode, prtd->play_samp_rate, prtd->cap_samp_rate);
- if ((runtime->format != FORMAT_S16_LE) && ((prtd->mode == MODE_PCM) ||
- (prtd->mode == MODE_AMR) || (prtd->mode == MODE_AMR_WB) ||
+ if ((runtime->format != FORMAT_S16_LE &&
+ runtime->format != FORMAT_SPECIAL) &&
+ ((prtd->mode == MODE_AMR) || (prtd->mode == MODE_AMR_WB) ||
(prtd->mode == MODE_IS127) || (prtd->mode == MODE_4GV_NB) ||
- (prtd->mode == MODE_4GV_WB) || (prtd->mode == MODE_4GV_NW))) {
+ (prtd->mode == MODE_4GV_WB) || (prtd->mode == MODE_4GV_NW) ||
+ (prtd->mode == MODE_G711) || (prtd->mode == MODE_G711A))) {
pr_err("%s(): mode:%d and format:%u are not matched\n",
__func__, prtd->mode, (uint32_t)runtime->format);
@@ -840,21 +1002,19 @@
goto done;
}
- ret = voip_get_media_type(prtd->mode,
- prtd->play_samp_rate,
- &media_type);
- if (ret < 0) {
- pr_err("%s(): fail at getting media_type, ret=%d\n",
- __func__, ret);
+ if (runtime->format != FORMAT_S16_LE && (prtd->mode == MODE_PCM)) {
+ pr_err("%s(): mode:%d and format:%u are not matched\n",
+ __func__, prtd->mode, runtime->format);
- ret = -EINVAL;
+ ret = -EINVAL;
goto done;
}
- pr_debug("%s(): media_type=%d\n", __func__, media_type);
if ((prtd->mode == MODE_PCM) ||
(prtd->mode == MODE_AMR) ||
- (prtd->mode == MODE_AMR_WB)) {
+ (prtd->mode == MODE_AMR_WB) ||
+ (prtd->mode == MODE_G711) ||
+ (prtd->mode == MODE_G711A)) {
ret = voip_get_rate_type(prtd->mode,
prtd->rate,
&rate_type);
@@ -909,6 +1069,19 @@
pr_debug("%s(): min rate=%d, max rate=%d\n",
__func__, evrc_min_rate_type, evrc_max_rate_type);
}
+ ret = voip_get_media_type(prtd->mode,
+ prtd->rate_type,
+ prtd->play_samp_rate,
+ &media_type);
+ if (ret < 0) {
+ pr_err("%s(): fail at getting media_type, ret=%d\n",
+ __func__, ret);
+
+ ret = -EINVAL;
+ goto done;
+ }
+ pr_debug("%s(): media_type=%d\n", __func__, media_type);
+
if ((prtd->play_samp_rate == 8000) &&
(prtd->cap_samp_rate == 8000))
voc_config_vocoder(media_type, rate_type,
@@ -1285,6 +1458,10 @@
}
break;
}
+ case MODE_G711:
+ case MODE_G711A:
+ *rate_type = rate;
+ break;
default:
pr_err("wrong mode type.\n");
ret = -EINVAL;
@@ -1294,9 +1471,9 @@
return ret;
}
-static int voip_get_media_type(uint32_t mode,
- unsigned int samp_rate,
- unsigned int *media_type)
+static int voip_get_media_type(uint32_t mode, uint32_t rate_type,
+ unsigned int samp_rate,
+ unsigned int *media_type)
{
int ret = 0;
@@ -1327,6 +1504,13 @@
case MODE_4GV_NW: /* EVRC-NW */
*media_type = VSS_MEDIA_ID_4GV_NW_MODEM;
break;
+ case MODE_G711:
+ case MODE_G711A:
+ if (rate_type == MVS_G711A_MODE_MULAW)
+ *media_type = VSS_MEDIA_ID_G711_MULAW;
+ else
+ *media_type = VSS_MEDIA_ID_G711_ALAW;
+ break;
default:
pr_debug(" input mode is not supported\n");
ret = -EINVAL;
diff --git a/sound/soc/msm/qdsp6v2/q6asm.c b/sound/soc/msm/qdsp6v2/q6asm.c
index 74b79dd..0d19657 100644
--- a/sound/soc/msm/qdsp6v2/q6asm.c
+++ b/sound/soc/msm/qdsp6v2/q6asm.c
@@ -393,67 +393,59 @@
struct list_head *ptr, *next;
int result;
int size = 4096;
+
+ if (!set_custom_topology)
+ return;
+
get_asm_custom_topology(&cal_block);
if (cal_block.cal_size == 0) {
- pr_debug("%s: no cal to send addr= 0x%x\n",
- __func__, cal_block.cal_paddr);
- goto done;
+ pr_debug("%s: no cal to send addr= 0x%pa\n",
+ __func__, &cal_block.cal_paddr);
+ return;
}
- if (set_custom_topology) {
- if (common_client.mmap_apr == NULL) {
- common_client.mmap_apr = q6asm_mmap_apr_reg();
- common_client.apr = common_client.mmap_apr;
- if (common_client.mmap_apr == NULL) {
- pr_err("%s: q6asm_mmap_apr_reg failed\n",
- __func__);
- result = -EPERM;
- goto done;
- }
- }
- /* Only call this once */
- set_custom_topology = 0;
+ common_client.mmap_apr = q6asm_mmap_apr_reg();
+ common_client.apr = common_client.mmap_apr;
+ if (common_client.mmap_apr == NULL) {
+ pr_err("%s: q6asm_mmap_apr_reg failed\n",
+ __func__);
+ result = -EPERM;
+ goto mmap_fail;
+ }
+ /* Only call this once */
+ set_custom_topology = 0;
- /* Use first asm buf to map memory */
- if (common_client.port[IN].buf == NULL) {
- pr_err("%s: common buf is NULL\n",
- __func__);
- goto done;
- }
- common_client.port[IN].buf->phys = cal_block.cal_paddr;
+ /* Use first asm buf to map memory */
+ if (common_client.port[IN].buf == NULL) {
+ pr_err("%s: common buf is NULL\n",
+ __func__);
+ goto err_map;
+ }
+ common_client.port[IN].buf->phys = cal_block.cal_paddr;
- result = q6asm_memory_map_regions(&common_client,
- IN, size, 1, 1);
- if (result < 0) {
- pr_err("%s: mmap did not work! addr = 0x%x, size = %d\n",
- __func__, cal_block.cal_paddr,
- cal_block.cal_size);
- goto done;
- }
+ result = q6asm_memory_map_regions(&common_client,
+ IN, size, 1, 1);
+ if (result < 0) {
+ pr_err("%s: mmap did not work! addr = 0x%pa, size = %zd\n",
+ __func__, &cal_block.cal_paddr,
+ cal_block.cal_size);
+ goto err_map;
+ }
- list_for_each_safe(ptr, next,
- &common_client.port[IN].mem_map_handle) {
- buf_node = list_entry(ptr, struct asm_buffer_node,
- list);
- if (buf_node->buf_addr_lsw == cal_block.cal_paddr) {
- topology_map_handle = buf_node->mmap_hdl;
- break;
- }
- }
-
- result = q6asm_mmap_apr_dereg();
- if (result < 0) {
- pr_err("%s: q6asm_mmap_apr_dereg failed, err %d\n",
- __func__, result);
- } else {
- common_client.mmap_apr = NULL;
+ list_for_each_safe(ptr, next,
+ &common_client.port[IN].mem_map_handle) {
+ buf_node = list_entry(ptr, struct asm_buffer_node,
+ list);
+ if (buf_node->buf_addr_lsw == cal_block.cal_paddr) {
+ topology_map_handle = buf_node->mmap_hdl;
+ break;
}
}
q6asm_add_hdr_custom_topology(ac, &asm_top.hdr,
APR_PKT_SIZE(APR_HDR_SIZE,
sizeof(asm_top)), TRUE);
-
+ atomic_set(&ac->cmd_state, 1);
asm_top.hdr.opcode = ASM_CMD_ADD_TOPOLOGIES;
asm_top.payload_addr_lsw = cal_block.cal_paddr;
asm_top.payload_addr_msw = 0;
@@ -468,7 +460,7 @@
if (result < 0) {
pr_err("%s: Set topologies failed payload = 0x%x\n",
__func__, cal_block.cal_paddr);
- goto done;
+ goto err_unmap;
}
result = wait_event_timeout(ac->cmd_wait,
@@ -476,10 +468,15 @@
if (!result) {
pr_err("%s: Set topologies failed after timedout payload = 0x%x\n",
__func__, cal_block.cal_paddr);
- goto done;
+ goto err_unmap;
}
-
-done:
+ return;
+err_unmap:
+ q6asm_memory_unmap_regions(ac, IN);
+err_map:
+ q6asm_mmap_apr_dereg();
+ set_custom_topology = 1;
+mmap_fail:
return;
}
@@ -768,6 +765,7 @@
apr_deregister(ac->apr);
ac->apr = NULL;
ac->mmap_apr = NULL;
+ rtac_set_asm_handle(ac->session, ac->apr);
q6asm_session_free(ac);
q6asm_mmap_apr_dereg();
@@ -857,7 +855,7 @@
if (ac->apr == NULL) {
pr_err("%s Registration with APR failed\n", __func__);
mutex_unlock(&session_lock);
- goto fail;
+ goto fail_apr1;
}
ac->apr2 = apr_register("ADSP", "ASM", \
(apr_fn)q6asm_callback,\
@@ -867,16 +865,17 @@
if (ac->apr2 == NULL) {
pr_err("%s Registration with APR-2 failed\n", __func__);
mutex_unlock(&session_lock);
- goto fail;
+ goto fail_apr2;
}
+
rtac_set_asm_handle(n, ac->apr);
pr_debug("%s Registering the common port with APR\n", __func__);
ac->mmap_apr = q6asm_mmap_apr_reg();
if (ac->mmap_apr == NULL) {
mutex_unlock(&session_lock);
- goto fail;
- }
+ goto fail_mmap;
+ }
init_waitqueue_head(&ac->cmd_wait);
init_waitqueue_head(&ac->time_wait);
@@ -899,9 +898,12 @@
mutex_unlock(&session_lock);
return ac;
-fail:
- q6asm_audio_client_free(ac);
- return NULL;
+fail_mmap:
+ apr_deregister(ac->apr2);
+fail_apr2:
+ apr_deregister(ac->apr);
+fail_apr1:
+ q6asm_session_free(ac);
fail_session:
kfree(ac);
return NULL;
@@ -1115,12 +1117,20 @@
payload = data->payload;
if (data->opcode == RESET_EVENTS) {
+ struct audio_client *ac_mmap = (struct audio_client *)priv;
+ if (ac_mmap == NULL) {
+ pr_err("%s ac or priv NULL\n", __func__);
+ return -EINVAL;
+ }
pr_debug("%s: Reset event is received: %d %d apr[%p]\n",
__func__,
data->reset_event,
data->reset_proc,
this_mmap.apr);
+ atomic_set(&this_mmap.ref_cnt, 0);
apr_reset(this_mmap.apr);
+ this_mmap.apr = NULL;
+ ac_mmap->mmap_apr = NULL;
for (; i <= OUT; i++) {
list_for_each_safe(ptr, next,
&common_client.port[i].mem_map_handle) {
@@ -1135,7 +1145,6 @@
}
pr_debug("%s:Clearing custom topology\n", __func__);
}
- this_mmap.apr = NULL;
reset_custom_topology_flags();
set_custom_topology = 1;
topology_map_handle = 0;
@@ -1626,7 +1635,6 @@
hdr->dest_port = ((ac->session << 8) & 0xFF00) | (stream_id);
if (cmd_flg) {
hdr->token = ac->session;
- atomic_set(&ac->cmd_state, 1);
}
hdr->pkt_size = pkt_size;
mutex_unlock(&ac->cmd_lock);
@@ -1667,7 +1675,6 @@
hdr->dest_port = ((ac->session << 8) & 0xFF00) | (stream_id);
if (cmd_flg) {
hdr->token = ac->session;
- atomic_set(&ac->cmd_state, 1);
}
hdr->pkt_size = pkt_size;
return;
@@ -1711,7 +1718,6 @@
hdr->dest_port = 0;
if (cmd_flg) {
hdr->token = ((ac->session << 8) | 0x0001) ;
- atomic_set(&ac->cmd_state, 1);
}
hdr->pkt_size = pkt_size;
mutex_unlock(&ac->cmd_lock);
@@ -1728,7 +1734,6 @@
hdr->dest_port = 0;
if (cmd_flg) {
hdr->token = token;
- atomic_set(&ac->cmd_state, 1);
}
hdr->pkt_size = pkt_size;
return;
@@ -1748,6 +1753,7 @@
pr_debug("%s:session[%d]", __func__, ac->session);
q6asm_add_hdr(ac, &open.hdr, sizeof(open), TRUE);
+ atomic_set(&ac->cmd_state, 1);
open.hdr.opcode = ASM_STREAM_CMD_OPEN_READ_V3;
/* Stream prio : High, provide meta info with encoded frames */
open.src_endpointype = ASM_END_POINT_DEVICE_MATRIX;
@@ -1841,7 +1847,7 @@
format);
q6asm_stream_add_hdr(ac, &open.hdr, sizeof(open), TRUE, stream_id);
-
+ atomic_set(&ac->cmd_state, 1);
/*
* Updated the token field with stream/session for compressed playback
* Platform driver must know the the stream with which the command is
@@ -1959,6 +1965,7 @@
ac->io_mode |= NT_MODE;
q6asm_add_hdr(ac, &open.hdr, sizeof(open), TRUE);
+ atomic_set(&ac->cmd_state, 1);
open.hdr.opcode = ASM_STREAM_CMD_OPEN_READWRITE_V2;
open.mode_flags = BUFFER_META_ENABLE;
@@ -2068,6 +2075,7 @@
pr_debug("%s: session[%d]", __func__, ac->session);
q6asm_add_hdr(ac, &open.hdr, sizeof(open), TRUE);
+ atomic_set(&ac->cmd_state, 1);
open.hdr.opcode = ASM_STREAM_CMD_OPEN_LOOPBACK_V2;
open.mode_flags = 0;
@@ -2110,6 +2118,7 @@
}
pr_debug("%s session[%d]", __func__, ac->session);
q6asm_add_hdr(ac, &run.hdr, sizeof(run), TRUE);
+ atomic_set(&ac->cmd_state, 1);
run.hdr.opcode = ASM_SESSION_CMD_RUN_V2;
run.flags = flags;
@@ -2147,7 +2156,7 @@
}
pr_debug("session[%d]", ac->session);
q6asm_stream_add_hdr_async(ac, &run.hdr, sizeof(run), TRUE, stream_id);
-
+ atomic_set(&ac->cmd_state, 1);
run.hdr.opcode = ASM_SESSION_CMD_RUN_V2;
run.flags = flags;
run.time_lsw = lsw_ts;
@@ -2189,6 +2198,7 @@
sample_rate, channels, bit_rate, mode, format);
q6asm_add_hdr(ac, &enc_cfg.hdr, sizeof(enc_cfg), TRUE);
+ atomic_set(&ac->cmd_state, 1);
enc_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM;
enc_cfg.encdec.param_id = ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2;
@@ -2229,6 +2239,7 @@
pr_debug("%s: Session %d, num_channels = %d\n",
__func__, ac->session, num_channels);
q6asm_add_hdr(ac, &chan_map.hdr, sizeof(chan_map), TRUE);
+ atomic_set(&ac->cmd_state, 1);
chan_map.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM;
chan_map.encdec.param_id = ASM_PARAM_ID_DEC_OUTPUT_CHAN_MAP;
chan_map.encdec.param_size = sizeof(struct asm_dec_out_chan_map_param) -
@@ -2273,6 +2284,7 @@
ac->session, rate, channels);
q6asm_add_hdr(ac, &enc_cfg.hdr, sizeof(enc_cfg), TRUE);
+ atomic_set(&ac->cmd_state, 1);
enc_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM;
enc_cfg.encdec.param_id = ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2;
enc_cfg.encdec.param_size = sizeof(enc_cfg) - sizeof(enc_cfg.hdr) -
@@ -2334,7 +2346,7 @@
ac->session, rate, channels);
q6asm_add_hdr(ac, &enc_cfg.hdr, sizeof(enc_cfg), TRUE);
-
+ atomic_set(&ac->cmd_state, 1);
enc_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM;
enc_cfg.encdec.param_id = ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2;
enc_cfg.encdec.param_size = sizeof(enc_cfg) - sizeof(enc_cfg.hdr) -
@@ -2433,6 +2445,7 @@
pr_debug("%s: Session %d\n", __func__, ac->session);
q6asm_add_hdr(ac, &sbrps.hdr, sizeof(sbrps), TRUE);
+ atomic_set(&ac->cmd_state, 1);
sbrps.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM;
sbrps.encdec.param_id = ASM_PARAM_ID_AAC_SBR_PS_FLAG;
@@ -2474,6 +2487,7 @@
__func__, ac->session, sce_left, sce_right);
q6asm_add_hdr(ac, &dual_mono.hdr, sizeof(dual_mono), TRUE);
+ atomic_set(&ac->cmd_state, 1);
dual_mono.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM;
dual_mono.encdec.param_id = ASM_PARAM_ID_AAC_DUAL_MONO_MAPPING;
@@ -2505,8 +2519,36 @@
/* Support for selecting stereo mixing coefficients for B family not done */
int q6asm_cfg_aac_sel_mix_coef(struct audio_client *ac, uint32_t mix_coeff)
{
- /* To Be Done */
+ struct asm_aac_stereo_mix_coeff_selection_param_v2 aac_mix_coeff;
+ int rc = 0;
+
+ q6asm_add_hdr(ac, &aac_mix_coeff.hdr, sizeof(aac_mix_coeff), TRUE);
+ atomic_set(&ac->cmd_state, 1);
+ aac_mix_coeff.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM;
+ aac_mix_coeff.param_id =
+ ASM_PARAM_ID_AAC_STEREO_MIX_COEFF_SELECTION_FLAG_V2;
+ aac_mix_coeff.param_size =
+ sizeof(struct asm_aac_stereo_mix_coeff_selection_param_v2);
+ aac_mix_coeff.aac_stereo_mix_coeff_flag = mix_coeff;
+ pr_debug("%s, mix_coeff = %u", __func__, mix_coeff);
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &aac_mix_coeff);
+ if (rc < 0) {
+ pr_err("%s:Command opcode[0x%x]paramid[0x%x] failed\n",
+ __func__, ASM_STREAM_CMD_SET_ENCDEC_PARAM,
+ ASM_PARAM_ID_AAC_STEREO_MIX_COEFF_SELECTION_FLAG_V2);
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) == 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s:timeout opcode[0x%x]\n",
+ __func__, aac_mix_coeff.hdr.opcode);
+ rc = -ETIMEDOUT;
+ goto fail_cmd;
+ }
return 0;
+fail_cmd:
+ return rc;
}
int q6asm_enc_cfg_blk_qcelp(struct audio_client *ac, uint32_t frames_per_buf,
@@ -2522,6 +2564,7 @@
reduced_rate_level, rate_modulation_cmd);
q6asm_add_hdr(ac, &enc_cfg.hdr, sizeof(enc_cfg), TRUE);
+ atomic_set(&ac->cmd_state, 1);
enc_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM;
enc_cfg.encdec.param_id = ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2;
enc_cfg.encdec.param_size = sizeof(struct asm_v13k_enc_cfg) -
@@ -2563,6 +2606,7 @@
frames_per_buf, min_rate, max_rate, rate_modulation_cmd);
q6asm_add_hdr(ac, &enc_cfg.hdr, sizeof(enc_cfg), TRUE);
+ atomic_set(&ac->cmd_state, 1);
enc_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM;
enc_cfg.encdec.param_id = ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2;
enc_cfg.encdec.param_size = sizeof(struct asm_evrc_enc_cfg) -
@@ -2602,6 +2646,7 @@
__func__, ac->session, frames_per_buf, band_mode, dtx_enable);
q6asm_add_hdr(ac, &enc_cfg.hdr, sizeof(enc_cfg), TRUE);
+ atomic_set(&ac->cmd_state, 1);
enc_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM;
enc_cfg.encdec.param_id = ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2;
enc_cfg.encdec.param_size = sizeof(struct asm_amrnb_enc_cfg) -
@@ -2639,6 +2684,7 @@
__func__, ac->session, frames_per_buf, band_mode, dtx_enable);
q6asm_add_hdr(ac, &enc_cfg.hdr, sizeof(enc_cfg), TRUE);
+ atomic_set(&ac->cmd_state, 1);
enc_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM;
enc_cfg.encdec.param_id = ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2;
enc_cfg.encdec.param_size = sizeof(struct asm_amrwb_enc_cfg) -
@@ -2679,6 +2725,7 @@
channels);
q6asm_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE);
+ atomic_set(&ac->cmd_state, 1);
fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2;
fmt.fmt_blk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) -
@@ -2739,6 +2786,7 @@
channels);
q6asm_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE);
+ atomic_set(&ac->cmd_state, 1);
fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2;
fmt.fmt_blk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) -
@@ -2807,7 +2855,7 @@
cfg->sample_rate, cfg->ch_cfg);
q6asm_stream_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE, stream_id);
-
+ atomic_set(&ac->cmd_state, 1);
/*
* Updated the token field with stream/session for compressed playback
* Platform driver must know the the stream with which the command is
@@ -2883,6 +2931,7 @@
wma_cfg->ch_mask, wma_cfg->encode_opt);
q6asm_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE);
+ atomic_set(&ac->cmd_state, 1);
fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2;
fmt.fmtblk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) -
@@ -2928,6 +2977,7 @@
wmapro_cfg->adv_encode_opt, wmapro_cfg->adv_encode_opt2);
q6asm_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE);
+ atomic_set(&ac->cmd_state, 1);
fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2;
fmt.fmtblk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) -
@@ -2975,6 +3025,7 @@
cfg->num_channels);
q6asm_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE);
+ atomic_set(&ac->cmd_state, 1);
fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2;
fmt.fmtblk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) -
@@ -3006,6 +3057,7 @@
pr_debug("%s: session[%d]param_id[%d]param_value[%d]", __func__,
ac->session, param_id, param_value);
q6asm_add_hdr(ac, &ddp_cfg.hdr, sizeof(ddp_cfg), TRUE);
+ atomic_set(&ac->cmd_state, 1);
ddp_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM;
ddp_cfg.encdec.param_id = param_id;
ddp_cfg.encdec.param_size = sizeof(struct asm_dec_ddp_endp_param_v2) -
@@ -3066,6 +3118,7 @@
mmap_region_cmd;
q6asm_add_mmaphdr(ac, &mmap_regions->hdr, cmd_size,
TRUE, ((ac->session << 8) | dir));
+ atomic_set(&ac->cmd_state, 1);
mmap_regions->hdr.opcode = ASM_CMD_SHARED_MEM_MAP_REGIONS;
mmap_regions->mem_pool_id = ADSP_MEMORY_MAP_SHMEM8_4K_POOL;
mmap_regions->num_regions = bufcnt & 0x00ff;
@@ -3129,7 +3182,7 @@
q6asm_add_mmaphdr(ac, &mem_unmap.hdr,
sizeof(struct avs_cmd_shared_mem_unmap_regions),
TRUE, ((ac->session << 8) | dir));
-
+ atomic_set(&ac->cmd_state, 1);
mem_unmap.hdr.opcode = ASM_CMD_SHARED_MEM_UNMAP_REGIONS;
list_for_each_safe(ptr, next, &ac->port[dir].mem_map_handle) {
buf_node = list_entry(ptr, struct asm_buffer_node,
@@ -3227,6 +3280,7 @@
mmap_region_cmd;
q6asm_add_mmaphdr(ac, &mmap_regions->hdr, cmd_size, TRUE,
((ac->session << 8) | dir));
+ atomic_set(&ac->cmd_state, 1);
pr_debug("mmap_region=0x%p token=0x%x\n",
mmap_regions, ((ac->session << 8) | dir));
@@ -3308,6 +3362,7 @@
cmd_size = sizeof(struct avs_cmd_shared_mem_unmap_regions);
q6asm_add_mmaphdr(ac, &mem_unmap.hdr, cmd_size,
TRUE, ((ac->session << 8) | dir));
+ atomic_set(&ac->cmd_state, 1);
port = &ac->port[dir];
buf_add = (uint32_t)port->buf->phys;
mem_unmap.hdr.opcode = ASM_CMD_SHARED_MEM_UNMAP_REGIONS;
@@ -3365,6 +3420,7 @@
sz = sizeof(struct asm_volume_ctrl_lr_chan_gain);
q6asm_add_hdr_async(ac, &lrgain.hdr, sz, TRUE);
+ atomic_set(&ac->cmd_state, 1);
lrgain.hdr.opcode = ASM_STREAM_CMD_SET_PP_PARAMS_V2;
lrgain.param.data_payload_addr_lsw = 0;
lrgain.param.data_payload_addr_msw = 0;
@@ -3413,6 +3469,7 @@
sz = sizeof(struct asm_volume_ctrl_mute_config);
q6asm_add_hdr_async(ac, &mute.hdr, sz, TRUE);
+ atomic_set(&ac->cmd_state, 1);
mute.hdr.opcode = ASM_STREAM_CMD_SET_PP_PARAMS_V2;
mute.param.data_payload_addr_lsw = 0;
mute.param.data_payload_addr_msw = 0;
@@ -3460,6 +3517,7 @@
sz = sizeof(struct asm_volume_ctrl_master_gain);
q6asm_add_hdr_async(ac, &vol.hdr, sz, TRUE);
+ atomic_set(&ac->cmd_state, 1);
vol.hdr.opcode = ASM_STREAM_CMD_SET_PP_PARAMS_V2;
vol.param.data_payload_addr_lsw = 0;
vol.param.data_payload_addr_msw = 0;
@@ -3508,6 +3566,7 @@
sz = sizeof(struct asm_soft_pause_params);
q6asm_add_hdr_async(ac, &softpause.hdr, sz, TRUE);
+ atomic_set(&ac->cmd_state, 1);
softpause.hdr.opcode = ASM_STREAM_CMD_SET_PP_PARAMS_V2;
softpause.param.data_payload_addr_lsw = 0;
@@ -3561,6 +3620,7 @@
sz = sizeof(struct asm_soft_step_volume_params);
q6asm_add_hdr_async(ac, &softvol.hdr, sz, TRUE);
+ atomic_set(&ac->cmd_state, 1);
softvol.hdr.opcode = ASM_STREAM_CMD_SET_PP_PARAMS_V2;
softvol.param.data_payload_addr_lsw = 0;
softvol.param.data_payload_addr_msw = 0;
@@ -3619,6 +3679,7 @@
sz = sizeof(struct asm_eq_params);
eq_params = (struct msm_audio_eq_stream_config *) eq_p;
q6asm_add_hdr(ac, &eq.hdr, sz, TRUE);
+ atomic_set(&ac->cmd_state, 1);
eq.hdr.opcode = ASM_STREAM_CMD_SET_PP_PARAMS_V2;
eq.param.data_payload_addr_lsw = 0;
@@ -3696,6 +3757,11 @@
mutex_lock(&port->lock);
dsp_buf = port->dsp_buf;
+ if (port->buf == NULL) {
+ pr_err("%s buf is NULL\n", __func__);
+ mutex_unlock(&port->lock);
+ return -EINVAL;
+ }
ab = &port->buf[dsp_buf];
pr_debug("%s:session[%d]dsp-buf[%d][%p]cpu_buf[%d][%p]\n",
@@ -3821,7 +3887,6 @@
q6asm_stream_add_hdr_async(
ac, &write.hdr, sizeof(write), FALSE, ac->stream_id);
-
port = &ac->port[IN];
ab = &port->buf[port->dsp_buf];
@@ -4130,6 +4195,7 @@
q6asm_add_hdr_async(ac, &hdr, (sizeof(struct apr_hdr) +
sizeof(struct asm_stream_cmd_set_pp_params_v2) +
params_length), TRUE);
+ atomic_set(&ac->cmd_state, 1);
hdr.opcode = ASM_STREAM_CMD_SET_PP_PARAMS_V2;
payload_params.data_payload_addr_lsw = 0;
payload_params.data_payload_addr_msw = 0;
@@ -4170,7 +4236,7 @@
return -EINVAL;
}
q6asm_stream_add_hdr(ac, &hdr, sizeof(hdr), TRUE, stream_id);
-
+ atomic_set(&ac->cmd_state, 1);
/*
* Updated the token field with stream/session for compressed playback
* Platform driver must know the the stream with which the command is
@@ -4279,7 +4345,7 @@
return -EINVAL;
}
q6asm_stream_add_hdr_async(ac, &hdr, sizeof(hdr), TRUE, stream_id);
-
+ atomic_set(&ac->cmd_state, 1);
/*
* Updated the token field with stream/session for compressed playback
* Platform driver must know the the stream with which the command is
@@ -4437,6 +4503,7 @@
pr_debug("%s:session[%d]enable[%d]\n", __func__,
ac->session, enable);
q6asm_add_hdr(ac, &tx_overflow.hdr, sizeof(tx_overflow), TRUE);
+ atomic_set(&ac->cmd_state, 1);
tx_overflow.hdr.opcode = \
ASM_SESSION_CMD_REGISTER_FORX_OVERFLOW_EVENTS;
diff --git a/sound/soc/msm/qdsp6v2/q6voice.c b/sound/soc/msm/qdsp6v2/q6voice.c
index 61a262f..1ca9b1a 100644
--- a/sound/soc/msm/qdsp6v2/q6voice.c
+++ b/sound/soc/msm/qdsp6v2/q6voice.c
@@ -94,6 +94,8 @@
static int voice_alloc_oob_mem_table(void);
static int voice_alloc_and_map_cal_mem(struct voice_data *v);
static int voice_alloc_and_map_oob_mem(struct voice_data *v);
+static int voc_disable_cvp(uint32_t session_id);
+static int voc_enable_cvp(uint32_t session_id);
static struct voice_data *voice_get_session_by_idx(int idx);
@@ -141,6 +143,7 @@
case VOLTE_SESSION_VSID:
case VOIP_SESSION_VSID:
case QCHAT_SESSION_VSID:
+ case VOWLAN_SESSION_VSID:
case ALL_SESSION_VSID:
ret = true;
break;
@@ -234,6 +237,9 @@
} else if (session_id ==
common.voice[VOC_PATH_QCHAT_PASSIVE].session_id) {
session_name = QCHAT_SESSION_NAME;
+ } else if (session_id ==
+ common.voice[VOC_PATH_VOWLAN_PASSIVE].session_id) {
+ session_name = VOWLAN_SESSION_NAME;
} else if (session_id == common.voice[VOC_PATH_FULL].session_id) {
session_name = VOIP_SESSION_NAME;
}
@@ -256,6 +262,9 @@
else if (!strncmp(name, "QCHAT session", 13))
session_id =
common.voice[VOC_PATH_QCHAT_PASSIVE].session_id;
+ else if (!strncmp(name, "VoWLAN session", 14))
+ session_id =
+ common.voice[VOC_PATH_VOWLAN_PASSIVE].session_id;
else
session_id = common.voice[VOC_PATH_FULL].session_id;
@@ -291,6 +300,10 @@
v = &common.voice[VOC_PATH_QCHAT_PASSIVE];
break;
+ case VOWLAN_SESSION_VSID:
+ v = &common.voice[VOC_PATH_VOWLAN_PASSIVE];
+ break;
+
case ALL_SESSION_VSID:
break;
@@ -331,6 +344,10 @@
idx = VOC_PATH_QCHAT_PASSIVE;
break;
+ case VOWLAN_SESSION_VSID:
+ idx = VOC_PATH_VOWLAN_PASSIVE;
+ break;
+
case ALL_SESSION_VSID:
idx = MAX_VOC_SESSIONS - 1;
break;
@@ -375,6 +392,11 @@
return (session_id == common.voice[VOC_PATH_QCHAT_PASSIVE].session_id);
}
+static bool is_vowlan_session(u32 session_id)
+{
+ return (session_id == common.voice[VOC_PATH_VOWLAN_PASSIVE].session_id);
+}
+
static bool is_voc_state_active(int voc_state)
{
if ((voc_state == VOC_RUN) ||
@@ -433,6 +455,7 @@
common.voice[VOC_PATH_VOICE2_PASSIVE].session_id = VOICE2_SESSION_VSID;
common.voice[VOC_PATH_FULL].session_id = VOIP_SESSION_VSID;
common.voice[VOC_PATH_QCHAT_PASSIVE].session_id = QCHAT_SESSION_VSID;
+ common.voice[VOC_PATH_VOWLAN_PASSIVE].session_id = VOWLAN_SESSION_VSID;
}
static int voice_apr_register(void)
@@ -661,6 +684,10 @@
strlcpy(mvm_session_cmd.mvm_session.name,
QCHAT_SESSION_VSID_STR,
sizeof(mvm_session_cmd.mvm_session.name));
+ } else if (is_vowlan_session(v->session_id)) {
+ strlcpy(mvm_session_cmd.mvm_session.name,
+ VOWLAN_SESSION_VSID_STR,
+ sizeof(mvm_session_cmd.mvm_session.name));
} else {
strlcpy(mvm_session_cmd.mvm_session.name,
"default modem voice",
@@ -753,6 +780,10 @@
strlcpy(cvs_session_cmd.cvs_session.name,
QCHAT_SESSION_VSID_STR,
sizeof(cvs_session_cmd.cvs_session.name));
+ } else if (is_vowlan_session(v->session_id)) {
+ strlcpy(cvs_session_cmd.cvs_session.name,
+ VOWLAN_SESSION_VSID_STR,
+ sizeof(cvs_session_cmd.cvs_session.name));
} else {
strlcpy(cvs_session_cmd.cvs_session.name,
"default modem voice",
@@ -1052,39 +1083,37 @@
}
mvm_handle = voice_get_mvm_handle(v);
- if (v->tty_mode) {
- /* send tty mode cmd to mvm */
- mvm_tty_mode_cmd.hdr.hdr_field = APR_HDR_FIELD(
- APR_MSG_TYPE_SEQ_CMD,
- APR_HDR_LEN(APR_HDR_SIZE),
- APR_PKT_VER);
- mvm_tty_mode_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
- sizeof(mvm_tty_mode_cmd) -
- APR_HDR_SIZE);
- pr_debug("%s: pkt size = %d\n",
- __func__, mvm_tty_mode_cmd.hdr.pkt_size);
- mvm_tty_mode_cmd.hdr.src_port =
- voice_get_idx_for_session(v->session_id);
- mvm_tty_mode_cmd.hdr.dest_port = mvm_handle;
- mvm_tty_mode_cmd.hdr.token = 0;
- mvm_tty_mode_cmd.hdr.opcode = VSS_ISTREAM_CMD_SET_TTY_MODE;
- mvm_tty_mode_cmd.tty_mode.mode = v->tty_mode;
- pr_debug("tty mode =%d\n", mvm_tty_mode_cmd.tty_mode.mode);
+ /* send tty mode cmd to mvm */
+ mvm_tty_mode_cmd.hdr.hdr_field = APR_HDR_FIELD(
+ APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ mvm_tty_mode_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(mvm_tty_mode_cmd) -
+ APR_HDR_SIZE);
+ pr_debug("%s: pkt size = %d\n",
+ __func__, mvm_tty_mode_cmd.hdr.pkt_size);
+ mvm_tty_mode_cmd.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
+ mvm_tty_mode_cmd.hdr.dest_port = mvm_handle;
+ mvm_tty_mode_cmd.hdr.token = 0;
+ mvm_tty_mode_cmd.hdr.opcode = VSS_ISTREAM_CMD_SET_TTY_MODE;
+ mvm_tty_mode_cmd.tty_mode.mode = v->tty_mode;
+ pr_debug("tty mode =%d\n", mvm_tty_mode_cmd.tty_mode.mode);
- v->mvm_state = CMD_STATUS_FAIL;
- ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_tty_mode_cmd);
- if (ret < 0) {
- pr_err("%s: Error %d sending SET_TTY_MODE\n",
- __func__, ret);
- goto fail;
- }
- ret = wait_event_timeout(v->mvm_wait,
- (v->mvm_state == CMD_STATUS_SUCCESS),
- msecs_to_jiffies(TIMEOUT_MS));
- if (!ret) {
- pr_err("%s: wait_event timeout\n", __func__);
- goto fail;
- }
+ v->mvm_state = CMD_STATUS_FAIL;
+ ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_tty_mode_cmd);
+ if (ret < 0) {
+ pr_err("%s: Error %d sending SET_TTY_MODE\n",
+ __func__, ret);
+ goto fail;
+ }
+ ret = wait_event_timeout(v->mvm_wait,
+ (v->mvm_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+ goto fail;
}
return 0;
fail:
@@ -3137,8 +3166,7 @@
voice_send_netid_timing_cmd(v);
}
- /* enable slowtalk if st_enable is set */
- if (v->st_enable)
+ if (v->st_enable && !v->tty_mode)
voice_send_set_pp_enable_cmd(v,
MODULE_ID_VOICE_MODULE_ST,
v->st_enable);
@@ -4350,7 +4378,7 @@
return ret;
}
-int voc_disable_cvp(uint32_t session_id)
+static int voc_disable_cvp(uint32_t session_id)
{
struct voice_data *v = voice_get_session(session_id);
int ret = 0;
@@ -4385,7 +4413,7 @@
return ret;
}
-int voc_enable_cvp(uint32_t session_id)
+static int voc_enable_cvp(uint32_t session_id)
{
struct voice_data *v = voice_get_session(session_id);
int ret = 0;
@@ -4457,10 +4485,8 @@
goto fail;
}
- /* Send tty mode if tty device is used */
voice_send_tty_mode_cmd(v);
- /* enable slowtalk */
- if (v->st_enable)
+ if (v->st_enable && !v->tty_mode)
voice_send_set_pp_enable_cmd(v,
MODULE_ID_VOICE_MODULE_ST,
v->st_enable);
@@ -4541,8 +4567,8 @@
return ret;
}
-int voc_set_rx_device_mute(uint32_t session_id, uint32_t mute,
- uint32_t ramp_duration)
+int voc_set_device_mute(uint32_t session_id, uint32_t dir, uint32_t mute,
+ uint32_t ramp_duration)
{
struct voice_data *v = NULL;
int ret = 0;
@@ -4552,16 +4578,23 @@
while (voice_itr_get_next_session(&itr, &v)) {
if (v != NULL) {
mutex_lock(&v->lock);
- v->dev_rx.dev_mute = mute;
- v->dev_rx.dev_mute_ramp_duration_ms =
+ if (dir == VSS_IVOLUME_DIRECTION_TX) {
+ v->dev_tx.dev_mute = mute;
+ v->dev_tx.dev_mute_ramp_duration_ms =
ramp_duration;
+ } else {
+ v->dev_rx.dev_mute = mute;
+ v->dev_rx.dev_mute_ramp_duration_ms =
+ ramp_duration;
+ }
+
if (((v->voc_state == VOC_RUN) ||
(v->voc_state == VOC_STANDBY)) &&
(v->lch_mode == 0))
ret = voice_send_device_mute_cmd(v,
- VSS_IVOLUME_DIRECTION_RX,
- v->dev_rx.dev_mute,
- ramp_duration);
+ dir,
+ mute,
+ ramp_duration);
mutex_unlock(&v->lock);
} else {
pr_err("%s: invalid session_id 0x%x\n", __func__,
@@ -4653,8 +4686,8 @@
v->st_enable = enable;
if (v->voc_state == VOC_RUN) {
- if (module_id ==
- MODULE_ID_VOICE_MODULE_ST)
+ if ((module_id == MODULE_ID_VOICE_MODULE_ST) &&
+ (!v->tty_mode))
ret = voice_send_set_pp_enable_cmd(v,
MODULE_ID_VOICE_MODULE_ST,
enable);
@@ -4877,6 +4910,110 @@
return ret;
}
+int voc_disable_device(uint32_t session_id)
+{
+ struct voice_data *v = voice_get_session(session_id);
+ int ret = 0;
+
+ pr_debug("%s: voc state=%d\n", __func__, v->voc_state);
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ mutex_lock(&v->lock);
+ if (v->voc_state == VOC_RUN) {
+ ret = voice_pause_voice_call(v);
+ if (ret < 0) {
+ pr_err("%s: Pause Voice Call failed for session 0x%x, err %d!\n",
+ __func__, v->session_id, ret);
+ goto done;
+ }
+ rtac_remove_voice(voice_get_cvs_handle(v));
+ voice_send_cvp_deregister_vol_cal_cmd(v);
+ voice_send_cvp_deregister_cal_cmd(v);
+ voice_send_cvp_deregister_dev_cfg_cmd(v);
+
+ v->voc_state = VOC_CHANGE;
+ } else {
+ pr_debug("%s: called in voc state=%d, No_OP\n",
+ __func__, v->voc_state);
+ }
+
+ if (common.ec_ref_ext)
+ voc_set_ext_ec_ref(AFE_PORT_INVALID, false);
+done:
+ mutex_unlock(&v->lock);
+
+ return ret;
+}
+
+int voc_enable_device(uint32_t session_id)
+{
+ struct voice_data *v = voice_get_session(session_id);
+ int ret = 0;
+
+ pr_debug("%s: voc state=%d\n", __func__, v->voc_state);
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ mutex_lock(&v->lock);
+ if (v->voc_state == VOC_CHANGE) {
+ ret = voice_send_tty_mode_cmd(v);
+ if (ret < 0) {
+ pr_err("%s: Sending TTY mode failed, ret=%d\n",
+ __func__, ret);
+ /* Not a critical error, allow voice call to continue */
+ }
+
+ if (v->tty_mode) {
+ /* disable slowtalk */
+ voice_send_set_pp_enable_cmd(v,
+ MODULE_ID_VOICE_MODULE_ST,
+ 0);
+ } else {
+ /* restore slowtalk */
+ voice_send_set_pp_enable_cmd(v,
+ MODULE_ID_VOICE_MODULE_ST,
+ v->st_enable);
+ }
+
+ ret = voice_send_set_device_cmd(v);
+ if (ret < 0) {
+ pr_err("%s: Set device failed, ret=%d\n",
+ __func__, ret);
+ goto done;
+ }
+
+ voice_send_cvp_register_dev_cfg_cmd(v);
+ voice_send_cvp_register_cal_cmd(v);
+ voice_send_cvp_register_vol_cal_cmd(v);
+
+ rtac_add_voice(voice_get_cvs_handle(v),
+ voice_get_cvp_handle(v),
+ v->dev_rx.port_id, v->dev_tx.port_id,
+ v->session_id);
+
+ ret = voice_send_start_voice_cmd(v);
+ if (ret < 0) {
+ pr_err("%s: Fail in sending START_VOICE, ret=%d\n",
+ __func__, ret);
+ goto done;
+ }
+ v->voc_state = VOC_RUN;
+ } else {
+ pr_debug("%s: called in voc state=%d, No_OP\n",
+ __func__, v->voc_state);
+ }
+
+done:
+ mutex_unlock(&v->lock);
+
+ return ret;
+}
+
int voc_set_lch(uint32_t session_id, enum voice_lch_mode lch_mode)
{
struct voice_data *v = voice_get_session(session_id);
diff --git a/sound/soc/msm/qdsp6v2/q6voice.h b/sound/soc/msm/qdsp6v2/q6voice.h
index 9efc9fc..c6f3482 100644
--- a/sound/soc/msm/qdsp6v2/q6voice.h
+++ b/sound/soc/msm/qdsp6v2/q6voice.h
@@ -1319,7 +1319,7 @@
void *buf;
};
-#define MAX_VOC_SESSIONS 5
+#define MAX_VOC_SESSIONS 6
struct common_data {
/* these default values are for all devices */
@@ -1393,21 +1393,25 @@
#define VOC_PATH_VOLTE_PASSIVE 2
#define VOC_PATH_VOICE2_PASSIVE 3
#define VOC_PATH_QCHAT_PASSIVE 4
+#define VOC_PATH_VOWLAN_PASSIVE 5
#define MAX_SESSION_NAME_LEN 32
-#define VOICE_SESSION_NAME "Voice session"
-#define VOIP_SESSION_NAME "VoIP session"
-#define VOLTE_SESSION_NAME "VoLTE session"
-#define VOICE2_SESSION_NAME "Voice2 session"
-#define QCHAT_SESSION_NAME "QCHAT session"
+#define VOICE_SESSION_NAME "Voice session"
+#define VOIP_SESSION_NAME "VoIP session"
+#define VOLTE_SESSION_NAME "VoLTE session"
+#define VOICE2_SESSION_NAME "Voice2 session"
+#define QCHAT_SESSION_NAME "QCHAT session"
+#define VOWLAN_SESSION_NAME "VoWLAN session"
#define VOICE2_SESSION_VSID_STR "10DC1000"
#define QCHAT_SESSION_VSID_STR "10803000"
+#define VOWLAN_SESSION_VSID_STR "10002000"
#define VOICE_SESSION_VSID 0x10C01000
#define VOICE2_SESSION_VSID 0x10DC1000
#define VOLTE_SESSION_VSID 0x10C02000
#define VOIP_SESSION_VSID 0x10004000
#define QCHAT_SESSION_VSID 0x10803000
+#define VOWLAN_SESSION_VSID 0x10002000
#define ALL_SESSION_VSID 0xFFFFFFFF
#define VSID_MAX ALL_SESSION_VSID
@@ -1440,11 +1444,9 @@
uint32_t ramp_duration);
int voc_set_tx_mute(uint32_t session_id, uint32_t dir, uint32_t mute,
uint32_t ramp_duration);
-int voc_set_rx_device_mute(uint32_t session_id, uint32_t mute,
- uint32_t ramp_duration);
+int voc_set_device_mute(uint32_t session_id, uint32_t dir, uint32_t mute,
+ uint32_t ramp_duration);
int voc_get_rx_device_mute(uint32_t session_id);
-int voc_disable_cvp(uint32_t session_id);
-int voc_enable_cvp(uint32_t session_id);
int voc_set_route_flag(uint32_t session_id, uint8_t path_dir, uint8_t set);
uint8_t voc_get_route_flag(uint32_t session_id, uint8_t path_dir);
int voc_enable_dtmf_rx_detection(uint32_t session_id, uint32_t enable);
@@ -1466,5 +1468,7 @@
int voice_get_idx_for_session(u32 session_id);
int voc_set_ext_ec_ref(uint16_t port_id, bool state);
int voc_update_amr_vocoder_rate(uint32_t session_id);
+int voc_disable_device(uint32_t session_id);
+int voc_enable_device(uint32_t session_id);
#endif
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index 2f9e319..56efb97 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -2308,6 +2308,7 @@
if (ret < 0) {
dev_err(dapm->dev, "Failed to add route %s->%s\n",
route->source, route->sink);
+ mutex_unlock(&dapm->card->dapm_mutex);
return ret;
}
route++;
@@ -3017,6 +3018,7 @@
dev_err(dapm->dev,
"ASoC: Failed to create DAPM control %s: %d\n",
widget->name, ret);
+ mutex_unlock(&dapm->card->dapm_mutex);
return ret;
}
widget++;