Merge "mmc: remove the second argument of k[un]map_atomic()"
diff --git a/Documentation/devicetree/bindings/leds/leds-qpnp.txt b/Documentation/devicetree/bindings/leds/leds-qpnp.txt
index d7290e0..10732cf 100644
--- a/Documentation/devicetree/bindings/leds/leds-qpnp.txt
+++ b/Documentation/devicetree/bindings/leds/leds-qpnp.txt
@@ -24,14 +24,14 @@
Optional properties for WLED:
- qcom,num-strings: number of wled strings supported
-- qcom,ovp_val: over voltage protection threshold,
+- qcom,ovp-val: over voltage protection threshold,
follows enum wled_ovp_threshold
-- qcom,boost_curr_lim: boot currnet limit, follows enum wled_current_bost_limit
-- qcom,ctrl_delay_us: delay in activation of led
-- qcom,dig_mod_gen_en: digital module generator
-- qcom,cs_out_en: current sink output enable
-- qcom,op_fdbck: selection of output as feedback for the boost
-- qcom,cp_select: high pole capacitance
+- qcom,boost-curr-lim: boot currnet limit, follows enum wled_current_bost_limit
+- qcom,ctrl-delay-us: delay in activation of led
+- qcom,dig-mod-gen-en: digital module generator
+- qcom,cs-out-en: current sink output enable
+- qcom,op-fdbck: selection of output as feedback for the boost
+- qcom,cp-select: high pole capacitance
- linux,default-trigger: trigger the led from external modules such as display
- qcom,default-state: default state of the led, should be "on" or "off"
diff --git a/Documentation/devicetree/bindings/misc/ti_drv2667.txt b/Documentation/devicetree/bindings/misc/ti_drv2667.txt
new file mode 100644
index 0000000..3a8f4b3
--- /dev/null
+++ b/Documentation/devicetree/bindings/misc/ti_drv2667.txt
@@ -0,0 +1,44 @@
+TI DRV2667 is a haptic controller chip. It can drive piezo haptics
+and can operate in two modes - analog and digital.
+
+Required properties:
+
+-compatible : should be "ti,drv2667".
+-reg : i2c address to be used.
+-vdd-supply : regulator to power the chip.
+-vdd-i2c-supply : regulator to power i2c bus.
+
+Optional properties:
+
+-ti,label : Name to be registered with timedoutput class.
+-ti,mode : Mode to be supported, 0 to 3 - FIFO, RAM, WAVE and ANALOG.
+-ti,wav-seq : Wave Sequence composed of 11 bytes - wave form id,
+ Header size, start upper byte, start lower byte,
+ stop upper byte, stop lower byte, repeat count,
+ amplitude, frequency, duration and envelope
+-ti,gain : Gain to be programmed for the chip.
+-ti,idle-timeout-ms : Idle timeout in ms to be programmed for the chip to go into
+ low power mode after finishing its operation.
+-ti,max-runtime-ms : Maximum time in ms for which chip can drive haptics.
+
+Example:
+ i2c@f9967000 {
+ ti-drv2667@59 {
+ compatible = "ti,drv2667";
+ reg = <0x59>;
+ vdd-supply = <&drv2667_vreg>;
+ vdd-i2c-supply = <&pm8941_s3>;
+ ti,label = "vibrator";
+ ti,gain = <3>;
+ ti,idle-timeout-ms = <20>;
+ ti,max-runtime-ms = <15000>;
+ ti,mode = <2>;
+ ti,wav-seq = [
+ /* wave form id */
+ 01
+ /* header size, start and stop bytes */
+ 05 80 06 00 09
+ /* repeat, amp, freq, duration, envelope */
+ 01 ff 19 02 00];
+ };
+ };
diff --git a/Documentation/devicetree/bindings/net/wireless/ath/ath6kl.txt b/Documentation/devicetree/bindings/net/wireless/ath/ath6kl.txt
new file mode 100644
index 0000000..7b9feae
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/wireless/ath/ath6kl.txt
@@ -0,0 +1,24 @@
+* Qualcomm Atheros mobile chipsets
+
+Required properties:
+ - compatible: Can be "qca,ar6004-sdio" for SDIO device and
+ "qca,ar6004-hsic" for HSIC devcie.
+ - qca,chip-pwd-l-gpios: specify GPIO for CHIP_PWD_L.
+
+Optional Properties:
+ - cell-index: WLAN Hardware index.
+ - qca,pm-enable-gpios: Specify this GPIO if internal PMU needs to be used.
+ - qca,ar6004-vbatt-supply: Specify this if VBATT is provided through a
+ regulator.
+ - qca,ar6004-vdd-io-supply: Specify this if VDD-IO is provided through a
+ regulator.
+
+Example:
+
+ wlan0: qca,wlan {
+ cell-index = <0>;
+ compatible = "qca,ar6004-sdio";
+ qca,chip-pwd-l-gpios = <&msmgpio 62 0>;
+ qca,pm-enable-gpios = <&pm8019_gpios 3 0x0>;
+ qca,ar6004-vdd-io-supply = <&pm8019_l11>;
+ };
diff --git a/Documentation/devicetree/bindings/power/qpnp-bms.txt b/Documentation/devicetree/bindings/power/qpnp-bms.txt
index 1d3c2db..4d571eb 100644
--- a/Documentation/devicetree/bindings/power/qpnp-bms.txt
+++ b/Documentation/devicetree/bindings/power/qpnp-bms.txt
@@ -36,12 +36,34 @@
- qcom,bms-adjust-soc-low-threshold : The low threshold for the "flat portion"
of the charging curve. The BMS will not adjust SoC
based on voltage during this time.
-- qcom,bms-adjust-soc-high-threshold : The high threshold for the "flat
+- qcom,bms-adjust-soc-high-threshold : The high threshold for the "flat
portion" of the charging curve. The BMS will not
adjust SoC based on voltage during this time.
+- qcom,bms-low-soc-calculate-soc-threshold : The SoC threshold for when
+ the period calculate_soc work speeds up. This ensures
+ SoC is updated in userspace constantly when we are near
+ shutdown.
+- qcom,bms-low-soc-calculate-soc-ms : The time period between subsequent
+ SoC recalculations when the current SoC is below
+ qcom,bms-low-soc-calculate-soc-threshold.
+- qcom,bms-soc-calculate-soc-ms : The time period between subsequent SoC
+ recalculations when the current SoC is above or equal
+ qcom,bms-low-soc-calculate-soc-threshold.
- qcom,bms-chg-term-ua : current in micro-amps when charging is considered done.
As soon as current passes this point, charging is
stopped.
+- qcom,bms-batt-type: Type of battery used. This is an integer that corresponds
+ to the enum defined in
+ include/linux/mfd/pm8xxx/batterydata-lib.h
+
+Optional properties:
+- qcom,bms-ignore-shutdown-soc: A boolean that controls whether BMS will
+ try to force the startup SoC to be the same as the
+ shutdown SoC. Defining it will make BMS ignore the
+ shutdown SoC.
+- qcom,bms-use-voltage-soc : A boolean that controls whether BMS will use
+ voltage-based SoC instead of a coulomb counter based
+ one. Voltage-based SoC will not guarantee linearity.
Example:
bms@4000 {
@@ -76,5 +98,10 @@
qcom,bms-shutdown-soc-valid-limit = <20>;
qcom,bms-adjust-soc-low-threshold = <25>;
qcom,bms-adjust-soc-high-threshold = <45>;
+ qcom,bms-low-soc-calculate-soc-threshold = <15>;
+ qcom,bms-low-soc-calculate-soc-ms = <5000>;
+ qcom,bms-calculate-soc-ms = <20000>;
qcom,bms-chg-term-ua = <100000>;
+ qcom,bms-batt-type = <0>;
+ qcom,bms-ignore-shutdown-soc;
};
diff --git a/Documentation/devicetree/bindings/prng/msm-rng.txt b/Documentation/devicetree/bindings/prng/msm-rng.txt
index 3d55808..28dfe50 100644
--- a/Documentation/devicetree/bindings/prng/msm-rng.txt
+++ b/Documentation/devicetree/bindings/prng/msm-rng.txt
@@ -4,9 +4,13 @@
- compatible : Should be "qcom,msm-rng"
- reg : Offset and length of the register set for the device
+Optional property:
+- qcom,msm-rng-iface-clk : If the device uses iface-clk.
+
Example:
- qcom,msm-rng@f9bff000 {
- compatible = "qcom,msm-rng";
- reg = <0xf9bff000 0x200>;
- };
+ qcom,msm-rng@f9bff000 {
+ compatible = "qcom,msm-rng";
+ reg = <0xf9bff000 0x200>;
+ qcom,msm-rng-iface-clk;
+ };
diff --git a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
index bf605ec..2c74415 100644
--- a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
+++ b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
@@ -381,3 +381,48 @@
taiko-mclk-clk = <&pm8941_clkdiv1>;
qcom,taiko-mclk-clk-freq = <9600000>;
};
+
+* msm-dai-mi2s
+
+[First Level Nodes]
+
+Required properties:
+
+ - compatible : "msm-dai-mi2s"
+
+ [Second Level Nodes]
+
+Required properties:
+
+ - compatible : "qcom,msm-dai-q6-mi2s"
+ - qcom,msm-dai-q6-mi2s-dev-id: MSM or MDM can use Slimbus or I2S interface to transfer data
+ to (WCD9XXX) codec. If slimbus interface is used then
+ "msm-dai-q6" needs to be filled with correct data for slimbus
+ interface. The sections "msm-dai-mi2s" is used by MDM or MSM
+ to use I2S interface with codec. This section is used by CPU
+ driver in ASOC MSM to configure MI2S interface. MSM internally
+ has multiple MI2S namely Primary, Secondary, Tertiary and
+ Quaternary MI2S. They are represented with id 0, 1, 2, 3
+ respectively. The field "qcom,msm-dai-q6-mi2s-dev-id" represents
+ which of the MI2S block is used. These MI2S are connected to I2S
+ interface.
+
+ - qcom,msm-mi2s-rx-lines: Each MI2S interface in MSM has one or more SD lines. These lines
+ are used for data transfer between codec and MSM. This element in
+ indicates which output RX lines are used in the MI2S interface.
+
+ - qcom,msm-mi2s-tx-lines: Each MI2S interface in MSM has one or more SD lines. These lines
+ are used for data transfer between codec and MSM. This element in
+ indicates which input TX lines are used in the MI2S interface.
+
+Example:
+
+qcom,msm-dai-mi2s {
+ compatible = "qcom,msm-dai-mi2s";
+ qcom,msm-dai-q6-mi2s-prim {
+ compatible = "qcom,msm-dai-q6-mi2s";
+ qcom,msm-dai-q6-mi2s-dev-id = <0>;
+ qcom,msm-mi2s-rx-lines = <2>;
+ qcom,msm-mi2s-tx-lines = <1>;
+ };
+};
\ No newline at end of file
diff --git a/arch/arm/boot/dts/msm-pm8941.dtsi b/arch/arm/boot/dts/msm-pm8941.dtsi
index 4ec5684..8018e6a 100644
--- a/arch/arm/boot/dts/msm-pm8941.dtsi
+++ b/arch/arm/boot/dts/msm-pm8941.dtsi
@@ -90,7 +90,11 @@
qcom,bms-shutdown-soc-valid-limit = <20>;
qcom,bms-adjust-soc-low-threshold = <25>;
qcom,bms-adjust-soc-high-threshold = <45>;
+ qcom,bms-low-soc-calculate-soc-threshold = <15>;
+ qcom,bms-low-soc-calculate-soc-ms = <5000>;
+ qcom,bms-calculate-soc-ms = <20000>;
qcom,bms-chg-term-ua = <100000>;
+ qcom,bms-batt-type = <0>;
};
clkdiv@5b00 {
diff --git a/arch/arm/boot/dts/msm8910.dtsi b/arch/arm/boot/dts/msm8910.dtsi
index 1f3c1d8..2a2e764 100644
--- a/arch/arm/boot/dts/msm8910.dtsi
+++ b/arch/arm/boot/dts/msm8910.dtsi
@@ -73,13 +73,25 @@
interrupts = <0 123 0>;
interrupt-names = "core_irq";
+ vdd-supply = <&pm8110_l17>;
+ qcom,vdd-always-on;
+ qcom,vdd-lpm-sup;
+ qcom,vdd-voltage-level = <2900000 2900000>;
+ qcom,vdd-current-level = <9000 400000>;
+
+ vdd-io-supply = <&pm8110_l6>;
+ qcom,vdd-io-always-on;
+ qcom,vdd-io-lpm-sup;
+ qcom,vdd-io-voltage-level = <1800000 1800000>;
+ qcom,vdd-io-current-level = <9000 60000>;
+
qcom,pad-pull-on = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
qcom,pad-pull-off = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
qcom,pad-drv-on = <0x7 0x4 0x4>; /* 16mA, 10mA, 10mA */
qcom,pad-drv-off = <0x0 0x0 0x0>; /* 2mA, 2mA, 2mA */
qcom,clk-rates = <400000 25000000 50000000 100000000 200000000>;
- qcom,sup-voltages = <2950 2950>;
+ qcom,sup-voltages = <2900 2900>;
qcom,bus-width = <8>;
qcom,nonremovable;
qcom,bus-speed-mode = "HS200_1p8v", "DDR_1p8v";
@@ -93,6 +105,14 @@
interrupts = <0 125 0>;
interrupt-names = "core_irq";
+ vdd-supply = <&pm8110_l18>;
+ qcom,vdd-voltage-level = <2950000 2950000>;
+ qcom,vdd-current-level = <9000 400000>;
+
+ vdd-io-supply = <&pm8110_l21>;
+ qcom,vdd-io-voltage-level = <1800000 2950000>;
+ qcom,vdd-io-current-level = <9000 50000>;
+
qcom,pad-pull-on = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
qcom,pad-pull-off = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
qcom,pad-drv-on = <0x7 0x4 0x4>; /* 16mA, 10mA, 10mA */
diff --git a/arch/arm/boot/dts/msm8974-camera-sensor-liquid.dtsi b/arch/arm/boot/dts/msm8974-camera-sensor-liquid.dtsi
new file mode 100644
index 0000000..25f79f8
--- /dev/null
+++ b/arch/arm/boot/dts/msm8974-camera-sensor-liquid.dtsi
@@ -0,0 +1,98 @@
+
+/*
+ * Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+&cci {
+
+ qcom,camera@6e {
+ compatible = "qcom,s5k3l1yx";
+ reg = <0x6e 0x0>;
+ qcom,csi-if = <1>;
+ qcom,csid-core = <0>;
+ qcom,flash-type = <0>;
+ qcom,mount-angle = <0>;
+ qcom,sensor-name = "s5k3l1yx";
+ cam_vdig-supply = <&pm8941_l3>;
+ cam_vana-supply = <&pm8941_l17>;
+ cam_vio-supply = <&pm8941_lvs3>;
+ cam_vaf-supply = <&pm8941_l23>;
+ qcom,cam-vreg-name = "cam_vdig", "cam_vana", "cam_vio",
+ "cam_vaf";
+ qcom,cam-vreg-type = <0 0 1 0>;
+ qcom,cam-vreg-min-voltage = <1225000 2850000 0 3000000>;
+ qcom,cam-vreg-max-voltage = <1225000 2850000 0 3000000>;
+ qcom,cam-vreg-op-mode = <105000 80000 0 100000>;
+ qcom,gpio-no-mux = <0>;
+ gpios = <&msmgpio 15 0>,
+ <&msmgpio 19 0>,
+ <&msmgpio 20 0>,
+ <&msmgpio 90 0>;
+ qcom,gpio-common-tbl-num = <0 1 2>;
+ qcom,gpio-common-tbl-flags = <1 1 1>;
+ qcom,gpio-common-tbl-label = "CAMIF_MCLK",
+ "CAMIF_I2C_DATA",
+ "CAMIF_I2C_CLK";
+ qcom,gpio-req-tbl-num = <3>;
+ qcom,gpio-req-tbl-flags = <0>;
+ qcom,gpio-req-tbl-label = "CAM_RESET1";
+ qcom,gpio-set-tbl-num = <3 3>;
+ qcom,gpio-set-tbl-flags = <0 2>;
+ qcom,gpio-set-tbl-delay = <1000 30000>;
+ qcom,csi-lane-assign = <0x4320>;
+ qcom,csi-lane-mask = <0x1F>;
+ qcom,csi-phy-sel = <0>;
+ qcom,camera-type = <0>;
+ qcom,sensor-type = <0>;
+ status = "ok";
+ };
+
+ qcom,camera@6c {
+ compatible = "qcom,ov2720";
+ reg = <0x6c 0x0>;
+ qcom,csi-if = <1>;
+ qcom,csid-core = <0>;
+ qcom,flash-type = <0>;
+ qcom,mount-angle = <180>;
+ qcom,sensor-name = "ov2720";
+ cam_vdig-supply = <&pm8941_l3>;
+ cam_vana-supply = <&pm8941_l17>;
+ cam_vio-supply = <&pm8941_lvs2>;
+ qcom,cam-vreg-name = "cam_vdig", "cam_vana", "cam_vio";
+ qcom,cam-vreg-type = <0 0 1>;
+ qcom,cam-vreg-min-voltage = <1225000 2850000 0>;
+ qcom,cam-vreg-max-voltage = <1225000 2850000 0>;
+ qcom,cam-vreg-op-mode = <105000 80000 0>;
+ qcom,gpio-no-mux = <0>;
+ gpios = <&msmgpio 17 0>,
+ <&msmgpio 19 0>,
+ <&msmgpio 20 0>,
+ <&msmgpio 18 0>;
+ qcom,gpio-common-tbl-num = <0 1 2>;
+ qcom,gpio-common-tbl-flags = <1 1 1>;
+ qcom,gpio-common-tbl-label = "CAMIF_MCLK",
+ "CAMIF_I2C_DATA",
+ "CAMIF_I2C_CLK";
+ qcom,gpio-req-tbl-num = <3>;
+ qcom,gpio-req-tbl-flags = <0>;
+ qcom,gpio-req-tbl-label = "CAM_RESET1";
+ qcom,gpio-set-tbl-num = <3 3>;
+ qcom,gpio-set-tbl-flags = <0 2>;
+ qcom,gpio-set-tbl-delay = <1000 4000>;
+ qcom,csi-lane-assign = <0x4320>;
+ qcom,csi-lane-mask = <0x7>;
+ qcom,csi-phy-sel = <2>;
+ qcom,camera-type = <1>;
+ qcom,sensor-type = <0>;
+ status = "ok";
+ };
+};
diff --git a/arch/arm/boot/dts/msm8974-camera-sensor.dtsi b/arch/arm/boot/dts/msm8974-camera-sensor.dtsi
new file mode 100644
index 0000000..d804355
--- /dev/null
+++ b/arch/arm/boot/dts/msm8974-camera-sensor.dtsi
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+&cci {
+
+ qcom,camera@6e {
+ compatible = "qcom,s5k3l1yx";
+ reg = <0x6e 0x0>;
+ qcom,csi-if = <1>;
+ qcom,csid-core = <0>;
+ qcom,flash-type = <0>;
+ qcom,mount-angle = <90>;
+ qcom,sensor-name = "s5k3l1yx";
+ cam_vdig-supply = <&pm8941_l3>;
+ cam_vana-supply = <&pm8941_l17>;
+ cam_vio-supply = <&pm8941_lvs3>;
+ cam_vaf-supply = <&pm8941_l23>;
+ qcom,cam-vreg-name = "cam_vdig", "cam_vana", "cam_vio",
+ "cam_vaf";
+ qcom,cam-vreg-type = <0 0 1 0>;
+ qcom,cam-vreg-min-voltage = <1225000 2850000 0 3000000>;
+ qcom,cam-vreg-max-voltage = <1225000 2850000 0 3000000>;
+ qcom,cam-vreg-op-mode = <105000 80000 0 100000>;
+ qcom,gpio-no-mux = <0>;
+ gpios = <&msmgpio 15 0>,
+ <&msmgpio 19 0>,
+ <&msmgpio 20 0>,
+ <&msmgpio 90 0>;
+ qcom,gpio-common-tbl-num = <0 1 2>;
+ qcom,gpio-common-tbl-flags = <1 1 1>;
+ qcom,gpio-common-tbl-label = "CAMIF_MCLK",
+ "CAMIF_I2C_DATA",
+ "CAMIF_I2C_CLK";
+ qcom,gpio-req-tbl-num = <3>;
+ qcom,gpio-req-tbl-flags = <0>;
+ qcom,gpio-req-tbl-label = "CAM_RESET1";
+ qcom,gpio-set-tbl-num = <3 3>;
+ qcom,gpio-set-tbl-flags = <0 2>;
+ qcom,gpio-set-tbl-delay = <1000 30000>;
+ qcom,csi-lane-assign = <0x4320>;
+ qcom,csi-lane-mask = <0x1F>;
+ qcom,csi-phy-sel = <0>;
+ qcom,camera-type = <0>;
+ qcom,sensor-type = <0>;
+ status = "ok";
+ };
+
+ qcom,camera@6c {
+ compatible = "qcom,ov2720";
+ reg = <0x6c 0x0>;
+ qcom,csi-if = <1>;
+ qcom,csid-core = <0>;
+ qcom,flash-type = <0>;
+ qcom,mount-angle = <180>;
+ qcom,sensor-name = "ov2720";
+ cam_vdig-supply = <&pm8941_l3>;
+ cam_vana-supply = <&pm8941_l17>;
+ cam_vio-supply = <&pm8941_lvs3>;
+ qcom,cam-vreg-name = "cam_vdig", "cam_vana", "cam_vio";
+ qcom,cam-vreg-type = <0 0 1>;
+ qcom,cam-vreg-min-voltage = <1225000 2850000 0>;
+ qcom,cam-vreg-max-voltage = <1225000 2850000 0>;
+ qcom,cam-vreg-op-mode = <105000 80000 0>;
+ qcom,gpio-no-mux = <0>;
+ gpios = <&msmgpio 17 0>,
+ <&msmgpio 19 0>,
+ <&msmgpio 20 0>,
+ <&msmgpio 18 0>;
+ qcom,gpio-common-tbl-num = <0 1 2>;
+ qcom,gpio-common-tbl-flags = <1 1 1>;
+ qcom,gpio-common-tbl-label = "CAMIF_MCLK",
+ "CAMIF_I2C_DATA",
+ "CAMIF_I2C_CLK";
+ qcom,gpio-req-tbl-num = <3>;
+ qcom,gpio-req-tbl-flags = <0>;
+ qcom,gpio-req-tbl-label = "CAM_RESET1";
+ qcom,gpio-set-tbl-num = <3 3>;
+ qcom,gpio-set-tbl-flags = <0 2>;
+ qcom,gpio-set-tbl-delay = <1000 4000>;
+ qcom,csi-lane-assign = <0x4320>;
+ qcom,csi-lane-mask = <0x7>;
+ qcom,csi-phy-sel = <2>;
+ qcom,camera-type = <1>;
+ qcom,sensor-type = <0>;
+ status = "ok";
+ };
+};
diff --git a/arch/arm/boot/dts/msm8974-camera.dtsi b/arch/arm/boot/dts/msm8974-camera.dtsi
index fd652a0..4b96811 100644
--- a/arch/arm/boot/dts/msm8974-camera.dtsi
+++ b/arch/arm/boot/dts/msm8974-camera.dtsi
@@ -165,7 +165,7 @@
vdd-supply = <&gdsc_vfe>;
};
- qcom,cci@fda0C000 {
+ cci: qcom,cci@fda0C000 {
cell-index = <0>;
compatible = "qcom,cci";
reg = <0xfda0C000 0x1000>;
@@ -177,83 +177,11 @@
interrupt-names = "cci";
qcom,camera@6e {
- compatible = "qcom,s5k3l1yx";
- reg = <0x6e 0x0>;
- qcom,csi-if = <1>;
- qcom,csid-core = <0>;
- qcom,flash-type = <0>;
- qcom,mount-angle = <90>;
- qcom,sensor-name = "s5k3l1yx";
- cam_vdig-supply = <&pm8941_l3>;
- cam_vana-supply = <&pm8941_l17>;
- cam_vio-supply = <&pm8941_lvs3>;
- cam_vaf-supply = <&pm8941_l23>;
- qcom,cam-vreg-name = "cam_vdig", "cam_vana", "cam_vio",
- "cam_vaf";
- qcom,cam-vreg-type = <0 0 1 0>;
- qcom,cam-vreg-min-voltage = <1225000 2850000 0 3000000>;
- qcom,cam-vreg-max-voltage = <1225000 2850000 0 3000000>;
- qcom,cam-vreg-op-mode = <105000 80000 0 100000>;
- qcom,gpio-no-mux = <0>;
- gpios = <&msmgpio 15 0>,
- <&msmgpio 19 0>,
- <&msmgpio 20 0>,
- <&msmgpio 90 0>;
- qcom,gpio-common-tbl-num = <0 1 2>;
- qcom,gpio-common-tbl-flags = <1 1 1>;
- qcom,gpio-common-tbl-label = "CAMIF_MCLK",
- "CAMIF_I2C_DATA",
- "CAMIF_I2C_CLK";
- qcom,gpio-req-tbl-num = <3>;
- qcom,gpio-req-tbl-flags = <0>;
- qcom,gpio-req-tbl-label = "CAM_RESET1";
- qcom,gpio-set-tbl-num = <3 3>;
- qcom,gpio-set-tbl-flags = <0 2>;
- qcom,gpio-set-tbl-delay = <1000 30000>;
- qcom,csi-lane-assign = <0x4320>;
- qcom,csi-lane-mask = <0x1F>;
- qcom,csi-phy-sel = <0>;
- qcom,camera-type = <0>;
- qcom,sensor-type = <0>;
+ status = "disable";
};
qcom,camera@6c {
- compatible = "qcom,ov2720";
- reg = <0x6c 0x0>;
- qcom,csi-if = <1>;
- qcom,csid-core = <0>;
- qcom,flash-type = <0>;
- qcom,mount-angle = <0>;
- qcom,sensor-name = "ov2720";
- cam_vdig-supply = <&pm8941_l3>;
- cam_vana-supply = <&pm8941_l17>;
- cam_vio-supply = <&pm8941_lvs3>;
- qcom,cam-vreg-name = "cam_vdig", "cam_vana", "cam_vio";
- qcom,cam-vreg-type = <0 0 1>;
- qcom,cam-vreg-min-voltage = <1225000 2850000 0>;
- qcom,cam-vreg-max-voltage = <1225000 2850000 0>;
- qcom,cam-vreg-op-mode = <105000 80000 0>;
- qcom,gpio-no-mux = <0>;
- gpios = <&msmgpio 16 0>,
- <&msmgpio 19 0>,
- <&msmgpio 20 0>,
- <&msmgpio 92 0>;
- qcom,gpio-common-tbl-num = <0 1 2>;
- qcom,gpio-common-tbl-flags = <1 1 1>;
- qcom,gpio-common-tbl-label = "CAMIF_MCLK",
- "CAMIF_I2C_DATA",
- "CAMIF_I2C_CLK";
- qcom,gpio-req-tbl-num = <3>;
- qcom,gpio-req-tbl-flags = <0>;
- qcom,gpio-req-tbl-label = "CAM_RESET1";
- qcom,gpio-set-tbl-num = <3 3>;
- qcom,gpio-set-tbl-flags = <0 2>;
- qcom,gpio-set-tbl-delay = <1000 4000>;
- qcom,csi-lane-assign = <0x4320>;
- qcom,csi-lane-mask = <0x7>;
- qcom,csi-phy-sel = <1>;
- qcom,camera-type = <1>;
- qcom,sensor-type = <0>;
+ status = "disable";
};
qcom,camera@90 {
diff --git a/arch/arm/boot/dts/msm8974-cdp.dtsi b/arch/arm/boot/dts/msm8974-cdp.dtsi
index 6004d15..b2c73e1 100644
--- a/arch/arm/boot/dts/msm8974-cdp.dtsi
+++ b/arch/arm/boot/dts/msm8974-cdp.dtsi
@@ -11,6 +11,7 @@
*/
/include/ "dsi-panel-toshiba-720p-video.dtsi"
+/include/ "msm8974-camera-sensor.dtsi"
/ {
serial@f991e000 {
diff --git a/arch/arm/boot/dts/msm8974-fluid.dtsi b/arch/arm/boot/dts/msm8974-fluid.dtsi
index 938bc22..9092dd0 100644
--- a/arch/arm/boot/dts/msm8974-fluid.dtsi
+++ b/arch/arm/boot/dts/msm8974-fluid.dtsi
@@ -11,6 +11,7 @@
*/
/include/ "dsi-panel-toshiba-720p-video.dtsi"
+/include/ "msm8974-camera-sensor.dtsi"
/ {
serial@f991e000 {
diff --git a/arch/arm/boot/dts/msm8974-liquid.dtsi b/arch/arm/boot/dts/msm8974-liquid.dtsi
index 68207af..a34694e 100644
--- a/arch/arm/boot/dts/msm8974-liquid.dtsi
+++ b/arch/arm/boot/dts/msm8974-liquid.dtsi
@@ -10,6 +10,8 @@
* GNU General Public License for more details.
*/
+/include/ "msm8974-camera-sensor-liquid.dtsi"
+
/ {
serial@f991e000 {
status = "ok";
@@ -176,6 +178,7 @@
compatible = "regulator-fixed";
regulator-name = "ext_5v";
gpio = <&pm8941_mpps 2 0>;
+ startup-delay-us = <12000>;
enable-active-high;
};
};
diff --git a/arch/arm/boot/dts/msm8974-mtp.dtsi b/arch/arm/boot/dts/msm8974-mtp.dtsi
index 6553fc0..1c6302c 100644
--- a/arch/arm/boot/dts/msm8974-mtp.dtsi
+++ b/arch/arm/boot/dts/msm8974-mtp.dtsi
@@ -11,6 +11,7 @@
*/
/include/ "dsi-panel-toshiba-720p-video.dtsi"
+/include/ "msm8974-camera-sensor.dtsi"
/ {
serial@f991e000 {
diff --git a/arch/arm/boot/dts/msm8974-rumi.dtsi b/arch/arm/boot/dts/msm8974-rumi.dtsi
index 5a16be7..33656cd 100644
--- a/arch/arm/boot/dts/msm8974-rumi.dtsi
+++ b/arch/arm/boot/dts/msm8974-rumi.dtsi
@@ -10,6 +10,8 @@
* GNU General Public License for more details.
*/
+/include/ "msm8974-camera-sensor.dtsi"
+
/ {
timer {
clock-frequency = <5000000>;
diff --git a/arch/arm/boot/dts/msm8974-sim.dtsi b/arch/arm/boot/dts/msm8974-sim.dtsi
index 41e37de..8e8b3c3 100644
--- a/arch/arm/boot/dts/msm8974-sim.dtsi
+++ b/arch/arm/boot/dts/msm8974-sim.dtsi
@@ -11,6 +11,7 @@
*/
/include/ "dsi-panel-sim-video.dtsi"
+/include/ "msm8974-camera-sensor.dtsi"
/ {
qcom,mdss_dsi@fd922800 {
diff --git a/arch/arm/boot/dts/msm9625.dtsi b/arch/arm/boot/dts/msm9625.dtsi
index 26f8ab7..a4bec1b 100644
--- a/arch/arm/boot/dts/msm9625.dtsi
+++ b/arch/arm/boot/dts/msm9625.dtsi
@@ -98,25 +98,26 @@
interrupt-names = "bam_irq";
};
- spi@f9928000 {
+ spi@f9924000 {
+ cell-index = <0>;
compatible = "qcom,spi-qup-v2";
- reg = <0xf9928000 0x1000>;
- interrupts = <0 100 0>;
- spi-max-frequency = <24000000>;
+ reg = <0xf9924000 0x1000>;
+ interrupts = <0 96 0>;
+ spi-max-frequency = <25000000>;
#address-cells = <1>;
#size-cells = <0>;
- gpios = <&msmgpio 23 0>, /* CLK */
- <&msmgpio 21 0>, /* MISO */
- <&msmgpio 20 0>; /* MOSI */
+ gpios = <&msmgpio 7 0>, /* CLK */
+ <&msmgpio 5 0>, /* MISO */
+ <&msmgpio 4 0>; /* MOSI */
- cs-gpios = <&msmgpio 69 0>;
+ cs-gpios = <&msmgpio 6 0>;
ethernet-switch@0 {
compatible = "simtec,ks8851";
reg = <0>;
interrupt-parent = <&msmgpio>;
interrupts = <75 0>;
- spi-max-frequency = <5000000>;
+ spi-max-frequency = <4800000>;
};
};
@@ -268,6 +269,12 @@
qcom,sensors = <5>;
qcom,slope = <3200 3200 3200 3200 3200>;
};
+
+ qcom,msm-rng@f9bff000 {
+ compatible = "qcom,msm-rng";
+ reg = <0xf9bff000 0x200>;
+ qcom,msm-rng-iface-clk;
+ };
};
/include/ "msm-pm8019-rpm-regulator.dtsi"
@@ -329,10 +336,4 @@
qcom,hw-settle-time = <0>;
qcom,fast-avg-setup = <0>;
};
-
- qcom,msm-rng@f9bff000 {
- compatible = "qcom,msm-rng";
- reg = <0xf9bff000 0x200>;
- qcom,msm-rng-iface-clk;
- };
};
diff --git a/arch/arm/configs/msm9625_defconfig b/arch/arm/configs/msm9625_defconfig
index 4b84923..b9add04 100644
--- a/arch/arm/configs/msm9625_defconfig
+++ b/arch/arm/configs/msm9625_defconfig
@@ -121,6 +121,7 @@
CONFIG_SERIAL_MSM_HSL_CONSOLE=y
CONFIG_DIAG_CHAR=y
CONFIG_HW_RANDOM=y
+CONFIG_HW_RANDOM_MSM=y
CONFIG_I2C=y
CONFIG_I2C_CHARDEV=y
CONFIG_I2C_QUP=y
@@ -203,3 +204,50 @@
CONFIG_LIBCRC32C=y
CONFIG_ENABLE_DEFAULT_TRACERS=y
CONFIG_MSM_QDSS=y
+CONFIG_IP_MULTICAST=y
+CONFIG_IP_ADVANCED_ROUTER=y
+CONFIG_IP_MULTIPLE_TABLES=y
+CONFIG_IP_MROUTE=y
+CONFIG_IP_PIMSM_V2=y
+CONFIG_IPV6_MULTIPLE_TABLES=y
+CONFIG_IPV6_SUBTREES=y
+CONFIG_NETFILTER_XT_MARK=y
+CONFIG_NETFILTER_XT_CONNMARK=y
+CONFIG_NETFILTER_XT_MATCH_ADDRTYPE=y
+CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y
+CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y
+CONFIG_NETFILTER_XT_MATCH_IPRANGE=y
+CONFIG_NETFILTER_XT_MATCH_MULTIPORT=y
+CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y
+CONFIG_IP_SET=y
+CONFIG_NF_CONNTRACK_IPV4=y
+CONFIG_IP_NF_IPTABLES=y
+CONFIG_IP_NF_MATCH_AH=y
+CONFIG_IP_NF_MATCH_ECN=y
+CONFIG_IP_NF_MATCH_TTL=y
+CONFIG_IP_NF_FILTER=y
+CONFIG_IP_NF_TARGET_REJECT=y
+CONFIG_IP_NF_TARGET_REJECT_SKERR=y
+CONFIG_IP_NF_TARGET_ULOG=y
+CONFIG_NF_NAT=y
+CONFIG_IP_NF_TARGET_MASQUERADE=y
+CONFIG_IP_NF_TARGET_NETMAP=y
+CONFIG_IP_NF_TARGET_REDIRECT=y
+CONFIG_IP_NF_MANGLE=y
+CONFIG_IP_NF_TARGET_ECN=y
+CONFIG_IP_NF_TARGET_TTL=y
+CONFIG_IP_NF_RAW=y
+CONFIG_NF_CONNTRACK_IPV6=y
+CONFIG_IP6_NF_IPTABLES=y
+CONFIG_IP6_NF_MATCH_AH=y
+CONFIG_IP6_NF_MATCH_FRAG=y
+CONFIG_IP6_NF_MATCH_OPTS=y
+CONFIG_IP6_NF_MATCH_HL=y
+CONFIG_IP6_NF_MATCH_IPV6HEADER=y
+CONFIG_IP6_NF_MATCH_MH=y
+CONFIG_IP6_NF_MATCH_RT=y
+CONFIG_IP6_NF_FILTER=y
+CONFIG_IP6_NF_TARGET_REJECT=y
+CONFIG_IP6_NF_TARGET_REJECT_SKERR=y
+CONFIG_IP6_NF_MANGLE=y
+CONFIG_IP6_NF_RAW=y
diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile
index 88ed433..0c2f8ae 100644
--- a/arch/arm/mach-msm/Makefile
+++ b/arch/arm/mach-msm/Makefile
@@ -282,6 +282,7 @@
obj-$(CONFIG_MACH_MSM8930_MTP) += board-8930-all.o board-8930-regulator-pm8038.o board-8930-regulator-pm8917.o
obj-$(CONFIG_MACH_MSM8930_FLUID) += board-8930-all.o board-8930-regulator-pm8038.o board-8930-regulator-pm8917.o
obj-$(CONFIG_PM8921_BMS) += bms-batterydata.o bms-batterydata-desay.o batterydata-lib.o
+obj-$(CONFIG_QPNP_BMS) += bms-batterydata.o bms-batterydata-desay.o batterydata-lib.o
obj-$(CONFIG_MACH_APQ8064_CDP) += board-8064-all.o board-8064-regulator.o
obj-$(CONFIG_MACH_APQ8064_MTP) += board-8064-all.o board-8064-regulator.o
obj-$(CONFIG_MACH_APQ8064_LIQUID) += board-8064-all.o board-8064-regulator.o
diff --git a/arch/arm/mach-msm/acpuclock-krait.c b/arch/arm/mach-msm/acpuclock-krait.c
index bf57eab..b9d0527 100644
--- a/arch/arm/mach-msm/acpuclock-krait.c
+++ b/arch/arm/mach-msm/acpuclock-krait.c
@@ -33,6 +33,7 @@
#include <mach/rpm-regulator.h>
#include <mach/rpm-regulator-smd.h>
#include <mach/msm_bus.h>
+#include <mach/msm_dcvs.h>
#include "acpuclock.h"
#include "acpuclock-krait.h"
@@ -951,6 +952,17 @@
static void __init cpufreq_table_init(void) {}
#endif
+static void __init dcvs_freq_init(void)
+{
+ int i;
+
+ for (i = 0; drv.acpu_freq_tbl[i].speed.khz != 0; i++)
+ if (drv.acpu_freq_tbl[i].use_for_scaling)
+ msm_dcvs_register_cpu_freq(
+ drv.acpu_freq_tbl[i].speed.khz,
+ drv.acpu_freq_tbl[i].vdd_core / 1000);
+}
+
static int __cpuinit acpuclk_cpu_callback(struct notifier_block *nfb,
unsigned long action, void *hcpu)
{
@@ -1156,6 +1168,7 @@
hw_init();
cpufreq_table_init();
+ dcvs_freq_init();
acpuclk_register(&acpuclk_krait_data);
register_hotcpu_notifier(&acpuclk_cpu_notifier);
diff --git a/arch/arm/mach-msm/board-8064-pmic.c b/arch/arm/mach-msm/board-8064-pmic.c
index a358bba..fe2d2d2 100644
--- a/arch/arm/mach-msm/board-8064-pmic.c
+++ b/arch/arm/mach-msm/board-8064-pmic.c
@@ -419,14 +419,14 @@
static struct pm8xxx_ccadc_platform_data
apq8064_pm8xxx_ccadc_pdata = {
- .r_sense = 10,
+ .r_sense_uohm = 10000,
.calib_delay_ms = 600000,
};
static struct pm8921_bms_platform_data
apq8064_pm8921_bms_pdata __devinitdata = {
.battery_type = BATT_UNKNOWN,
- .r_sense = 10,
+ .r_sense_uohm = 10000,
.v_cutoff = 3400,
.max_voltage_uv = MAX_VOLTAGE_MV * 1000,
.rconn_mohm = 18,
diff --git a/arch/arm/mach-msm/board-8064.c b/arch/arm/mach-msm/board-8064.c
index 314bfd0..95246a7 100644
--- a/arch/arm/mach-msm/board-8064.c
+++ b/arch/arm/mach-msm/board-8064.c
@@ -693,12 +693,6 @@
static struct msm_bus_vectors hsic_init_vectors[] = {
{
.src = MSM_BUS_MASTER_SPS,
- .dst = MSM_BUS_SLAVE_EBI_CH0,
- .ab = 0,
- .ib = 0,
- },
- {
- .src = MSM_BUS_MASTER_SPS,
.dst = MSM_BUS_SLAVE_SPS,
.ab = 0,
.ib = 0,
@@ -709,15 +703,9 @@
static struct msm_bus_vectors hsic_max_vectors[] = {
{
.src = MSM_BUS_MASTER_SPS,
- .dst = MSM_BUS_SLAVE_EBI_CH0,
- .ab = 60000000, /* At least 480Mbps on bus. */
- .ib = 960000000, /* MAX bursts rate */
- },
- {
- .src = MSM_BUS_MASTER_SPS,
.dst = MSM_BUS_SLAVE_SPS,
.ab = 0,
- .ib = 512000000, /*vote for 64Mhz dfab clk rate*/
+ .ib = 256000000, /*vote for 32Mhz dfab clk rate*/
},
};
@@ -2551,6 +2539,7 @@
&msm_pil_vidc,
&msm_gss,
&apq8064_rtb_device,
+ &apq8064_dcvs_device,
&apq8064_msm_gov_device,
&apq8064_device_cache_erp,
&msm8960_device_ebi1_ch0_erp,
@@ -3406,6 +3395,7 @@
static void __init apq8064_common_init(void)
{
u32 platform_version = socinfo_get_platform_version();
+ struct msm_rpmrs_level rpmrs_level;
if (socinfo_get_pmic_model() == PMIC_MODEL_PM8917)
apq8064_pm8917_pdata_fixup();
@@ -3482,8 +3472,12 @@
}
enable_ddr3_regulator();
- msm_hsic_pdata.swfi_latency =
- msm_rpmrs_levels[0].latency_us;
+ rpmrs_level =
+ msm_rpmrs_levels[MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT];
+ msm_hsic_pdata.swfi_latency = rpmrs_level.latency_us;
+ rpmrs_level =
+ msm_rpmrs_levels[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE];
+ msm_hsic_pdata.standalone_latency = rpmrs_level.latency_us;
if (machine_is_apq8064_mtp()) {
msm_hsic_pdata.log2_irq_thresh = 5,
apq8064_device_hsic_host.dev.platform_data = &msm_hsic_pdata;
diff --git a/arch/arm/mach-msm/board-8930-camera.c b/arch/arm/mach-msm/board-8930-camera.c
index 083d5ab..be55031 100644
--- a/arch/arm/mach-msm/board-8930-camera.c
+++ b/arch/arm/mach-msm/board-8930-camera.c
@@ -244,7 +244,7 @@
.src = MSM_BUS_MASTER_VFE,
.dst = MSM_BUS_SLAVE_EBI_CH0,
.ab = 27648000,
- .ib = 110592000,
+ .ib = 2656000000UL,
},
{
.src = MSM_BUS_MASTER_VPE,
@@ -265,7 +265,7 @@
.src = MSM_BUS_MASTER_VFE,
.dst = MSM_BUS_SLAVE_EBI_CH0,
.ab = 274406400,
- .ib = 561807360,
+ .ib = 2656000000UL,
},
{
.src = MSM_BUS_MASTER_VPE,
@@ -285,8 +285,8 @@
{
.src = MSM_BUS_MASTER_VFE,
.dst = MSM_BUS_SLAVE_EBI_CH0,
- .ab = 274423680,
- .ib = 1097694720,
+ .ab = 600000000,
+ .ib = 2656000000UL,
},
{
.src = MSM_BUS_MASTER_VPE,
@@ -306,8 +306,8 @@
{
.src = MSM_BUS_MASTER_VFE,
.dst = MSM_BUS_SLAVE_EBI_CH0,
- .ab = 302071680,
- .ib = 1208286720,
+ .ab = 600000000,
+ .ib = 2656000000UL,
},
{
.src = MSM_BUS_MASTER_VPE,
@@ -327,8 +327,8 @@
{
.src = MSM_BUS_MASTER_VFE,
.dst = MSM_BUS_SLAVE_EBI_CH0,
- .ab = 348192000,
- .ib = 617103360,
+ .ab = 600000000,
+ .ib = 4264000000UL,
},
{
.src = MSM_BUS_MASTER_VPE,
@@ -349,7 +349,7 @@
.src = MSM_BUS_MASTER_VFE,
.dst = MSM_BUS_SLAVE_EBI_CH0,
.ab = 302071680,
- .ib = 1208286720,
+ .ib = 2656000000UL,
},
{
.src = MSM_BUS_MASTER_VPE,
@@ -365,6 +365,27 @@
},
};
+static struct msm_bus_vectors cam_adv_video_vectors[] = {
+ {
+ .src = MSM_BUS_MASTER_VFE,
+ .dst = MSM_BUS_SLAVE_EBI_CH0,
+ .ab = 274406400,
+ .ib = 2656000000UL,
+ },
+ {
+ .src = MSM_BUS_MASTER_VPE,
+ .dst = MSM_BUS_SLAVE_EBI_CH0,
+ .ab = 206807040,
+ .ib = 488816640,
+ },
+ {
+ .src = MSM_BUS_MASTER_JPEG_ENC,
+ .dst = MSM_BUS_SLAVE_EBI_CH0,
+ .ab = 0,
+ .ib = 0,
+ },
+};
+
static struct msm_bus_paths cam_bus_client_config[] = {
{
@@ -395,6 +416,11 @@
ARRAY_SIZE(cam_dual_vectors),
cam_dual_vectors,
},
+ {
+ ARRAY_SIZE(cam_adv_video_vectors),
+ cam_adv_video_vectors,
+ },
+
};
static struct msm_bus_scale_pdata cam_bus_client_pdata = {
diff --git a/arch/arm/mach-msm/board-8930-pmic.c b/arch/arm/mach-msm/board-8930-pmic.c
index d35a907..618f83b 100644
--- a/arch/arm/mach-msm/board-8930-pmic.c
+++ b/arch/arm/mach-msm/board-8930-pmic.c
@@ -437,7 +437,7 @@
};
static struct pm8xxx_ccadc_platform_data pm8xxx_ccadc_pdata = {
- .r_sense = 10,
+ .r_sense_uohm = 10000,
.calib_delay_ms = 600000,
};
@@ -465,7 +465,7 @@
static struct pm8921_bms_platform_data pm8921_bms_pdata __devinitdata = {
.battery_type = BATT_UNKNOWN,
- .r_sense = 10,
+ .r_sense_uohm = 10000,
.v_cutoff = 3400,
.max_voltage_uv = MAX_VOLTAGE_MV * 1000,
.shutdown_soc_valid_limit = 20,
diff --git a/arch/arm/mach-msm/board-8960-pmic.c b/arch/arm/mach-msm/board-8960-pmic.c
index 1a3d90d..2071a55 100644
--- a/arch/arm/mach-msm/board-8960-pmic.c
+++ b/arch/arm/mach-msm/board-8960-pmic.c
@@ -424,7 +424,7 @@
static struct pm8921_bms_platform_data pm8921_bms_pdata __devinitdata = {
.battery_type = BATT_UNKNOWN,
- .r_sense = 10,
+ .r_sense_uohm = 10000,
.v_cutoff = 3400,
.max_voltage_uv = MAX_VOLTAGE_MV * 1000,
.rconn_mohm = 18,
@@ -552,7 +552,7 @@
};
static struct pm8xxx_ccadc_platform_data pm8xxx_ccadc_pdata = {
- .r_sense = 10,
+ .r_sense_uohm = 10000,
.calib_delay_ms = 600000,
};
diff --git a/arch/arm/mach-msm/board-9615.c b/arch/arm/mach-msm/board-9615.c
index e0a6b4d..35b68b1 100644
--- a/arch/arm/mach-msm/board-9615.c
+++ b/arch/arm/mach-msm/board-9615.c
@@ -924,6 +924,7 @@
&msm_cpudai_auxpcm_tx,
&msm_cpudai_sec_auxpcm_rx,
&msm_cpudai_sec_auxpcm_tx,
+ &msm_cpudai_stub,
#if defined(CONFIG_CRYPTO_DEV_QCRYPTO) || \
defined(CONFIG_CRYPTO_DEV_QCRYPTO_MODULE)
diff --git a/arch/arm/mach-msm/board-9625-gpiomux.c b/arch/arm/mach-msm/board-9625-gpiomux.c
index 6f36ef2..8f07ff0 100644
--- a/arch/arm/mach-msm/board-9625-gpiomux.c
+++ b/arch/arm/mach-msm/board-9625-gpiomux.c
@@ -25,13 +25,13 @@
};
static struct gpiomux_setting gpio_spi_cs_config = {
- .func = GPIOMUX_FUNC_9,
+ .func = GPIOMUX_FUNC_1,
.drv = GPIOMUX_DRV_12MA,
.pull = GPIOMUX_PULL_NONE,
};
static struct gpiomux_setting gpio_spi_config = {
- .func = GPIOMUX_FUNC_2,
+ .func = GPIOMUX_FUNC_1,
.drv = GPIOMUX_DRV_12MA,
.pull = GPIOMUX_PULL_NONE,
};
@@ -44,6 +44,30 @@
static struct msm_gpiomux_config msm_blsp_configs[] __initdata = {
{
+ .gpio = 4, /* BLSP1 QUP2 SPI_DATA_MOSI */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &gpio_spi_config,
+ },
+ },
+ {
+ .gpio = 5, /* BLSP1 QUP2 SPI_DATA_MISO */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &gpio_spi_config,
+ },
+ },
+ {
+ .gpio = 6, /* BLSP1 QUP2 SPI_CS_N */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &gpio_spi_cs_config,
+ },
+ },
+ {
+ .gpio = 7, /* BLSP1 QUP2 SPI_CLK */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &gpio_spi_config,
+ },
+ },
+ {
.gpio = 8, /* BLSP1 UART TX */
.settings = {
[GPIOMUX_SUSPENDED] = &gpio_uart_config,
@@ -67,31 +91,6 @@
[GPIOMUX_SUSPENDED] = &gpio_i2c_config,
},
},
- {
- .gpio = 69, /* BLSP6 QUP SPI_CS_N */
- .settings = {
- [GPIOMUX_SUSPENDED] = &gpio_spi_cs_config,
- },
- },
- {
- .gpio = 20, /* BLSP6 QUP SPI_DATA_MOSI */
- .settings = {
- [GPIOMUX_SUSPENDED] = &gpio_spi_config,
- },
- },
- {
- .gpio = 21, /* BLSP6 QUP SPI_DATA_MISO */
- .settings = {
- [GPIOMUX_SUSPENDED] = &gpio_spi_config,
- },
- },
- {
- .gpio = 23, /* BLSP6 QUP SPI_CLK */
- .settings = {
- [GPIOMUX_SUSPENDED] = &gpio_spi_config,
- },
- },
-
};
static struct gpiomux_setting sdc3_clk_active_cfg = {
diff --git a/arch/arm/mach-msm/board-9625.c b/arch/arm/mach-msm/board-9625.c
index 3e5fc9d..8e8d3e7 100644
--- a/arch/arm/mach-msm/board-9625.c
+++ b/arch/arm/mach-msm/board-9625.c
@@ -101,8 +101,6 @@
static struct of_dev_auxdata msm9625_auxdata_lookup[] __initdata = {
OF_DEV_AUXDATA("qcom,msm-lsuart-v14", 0xF991F000, \
"msm_serial_hsl.0", NULL),
- OF_DEV_AUXDATA("qcom,spi-qup-v2", 0xF9928000, \
- "spi_qsd.1", NULL),
OF_DEV_AUXDATA("qcom,spmi-pmic-arb", 0xFC4C0000, \
"spmi-pmic-arb.0", NULL),
OF_DEV_AUXDATA("qcom,msm-sdcc", 0xF98A4000, \
diff --git a/arch/arm/mach-msm/clock-8960.c b/arch/arm/mach-msm/clock-8960.c
index 5e5e274..ca47ea1 100644
--- a/arch/arm/mach-msm/clock-8960.c
+++ b/arch/arm/mach-msm/clock-8960.c
@@ -6328,12 +6328,12 @@
*/
/*
* Initialize MM AHB registers: Enable the FPB clock and disable HW
- * gating on 8627 and 8960 for all clocks. Also set VFE_AHB's
+ * gating on 8627, 8960 and 8930ab for all clocks. Also set VFE_AHB's
* FORCE_CORE_ON bit to prevent its memory from being collapsed when
* the clock is halted. The sleep and wake-up delays are set to safe
* values.
*/
- if (cpu_is_msm8627() || cpu_is_msm8960ab()) {
+ if (cpu_is_msm8627() || cpu_is_msm8960ab() || cpu_is_msm8930ab()) {
rmwreg(0x00000003, AHB_EN_REG, 0x6C000103);
writel_relaxed(0x000007F9, AHB_EN2_REG);
} else {
@@ -6353,7 +6353,7 @@
* delays to safe values. */
if (cpu_is_msm8960ab() || (cpu_is_msm8960() &&
SOCINFO_VERSION_MAJOR(socinfo_get_version()) < 3) ||
- cpu_is_msm8627()) {
+ cpu_is_msm8627() || cpu_is_msm8930ab()) {
rmwreg(0x000007F9, MAXI_EN_REG, 0x0803FFFF);
rmwreg(0x3027FCFF, MAXI_EN2_REG, 0x3A3FFFFF);
} else {
@@ -6366,12 +6366,13 @@
if (cpu_is_apq8064() || cpu_is_apq8064ab())
rmwreg(0x019FECFF, MAXI_EN5_REG, 0x01FFEFFF);
- if (cpu_is_msm8930() || cpu_is_msm8930aa() || cpu_is_msm8627())
+ if (cpu_is_msm8930() || cpu_is_msm8930aa() || cpu_is_msm8627() ||
+ cpu_is_msm8930ab())
rmwreg(0x000004FF, MAXI_EN5_REG, 0x00000FFF);
if (cpu_is_msm8960ab())
rmwreg(0x009FE000, MAXI_EN5_REG, 0x01FFE000);
- if (cpu_is_msm8627())
+ if (cpu_is_msm8627() || cpu_is_msm8930ab())
rmwreg(0x000003C7, SAXI_EN_REG, 0x00003FFF);
else if (cpu_is_msm8960ab())
rmwreg(0x000001C6, SAXI_EN_REG, 0x00001DF6);
@@ -6412,7 +6413,7 @@
rmwreg(0x00000001, DSI2_PIXEL_CC2_REG, 0x00000001);
if (cpu_is_msm8960() || cpu_is_msm8930() || cpu_is_msm8930aa() ||
- cpu_is_msm8627())
+ cpu_is_msm8627() || cpu_is_msm8930ab())
rmwreg(0x80FF0000, TV_CC_REG, 0xE1FFC010);
if (cpu_is_msm8960ab())
rmwreg(0x00000000, TV_CC_REG, 0x00004010);
@@ -6674,7 +6675,7 @@
}
clk_set_rate(&usb_fs1_src_clk.c, 60000000);
if (cpu_is_msm8960ab() || cpu_is_msm8960() || cpu_is_msm8930() ||
- cpu_is_msm8930aa() || cpu_is_msm8627())
+ cpu_is_msm8930aa() || cpu_is_msm8627() || cpu_is_msm8930ab())
clk_set_rate(&usb_fs2_src_clk.c, 60000000);
clk_set_rate(&usb_hsic_xcvr_fs_clk.c, 60000000);
clk_set_rate(&usb_hsic_hsic_src_clk.c, 480000000);
diff --git a/arch/arm/mach-msm/clock-8974.c b/arch/arm/mach-msm/clock-8974.c
index 777e0bf..1395f05 100644
--- a/arch/arm/mach-msm/clock-8974.c
+++ b/arch/arm/mach-msm/clock-8974.c
@@ -5159,10 +5159,10 @@
/* MM sensor clocks */
CLK_LOOKUP("cam_src_clk", mclk0_clk_src.c, "6e.qcom,camera"),
- CLK_LOOKUP("cam_src_clk", mclk1_clk_src.c, "6c.qcom,camera"),
+ CLK_LOOKUP("cam_src_clk", mclk2_clk_src.c, "6c.qcom,camera"),
CLK_LOOKUP("cam_src_clk", mclk1_clk_src.c, "90.qcom,camera"),
CLK_LOOKUP("cam_clk", camss_mclk0_clk.c, "6e.qcom,camera"),
- CLK_LOOKUP("cam_clk", camss_mclk1_clk.c, "6c.qcom,camera"),
+ CLK_LOOKUP("cam_clk", camss_mclk2_clk.c, "6c.qcom,camera"),
CLK_LOOKUP("cam_clk", camss_mclk1_clk.c, "90.qcom,camera"),
CLK_LOOKUP("cam_clk", camss_mclk1_clk.c, ""),
CLK_LOOKUP("cam_clk", camss_mclk2_clk.c, ""),
diff --git a/arch/arm/mach-msm/clock-9625.c b/arch/arm/mach-msm/clock-9625.c
index 0b8919b..9f6a00c 100644
--- a/arch/arm/mach-msm/clock-9625.c
+++ b/arch/arm/mach-msm/clock-9625.c
@@ -2027,13 +2027,13 @@
CLK_LOOKUP("dma_bam_pclk", gcc_bam_dma_ahb_clk.c, "msm_sps"),
CLK_LOOKUP("iface_clk", gcc_blsp1_ahb_clk.c, "msm_serial_hsl.0"),
- CLK_LOOKUP("iface_clk", gcc_blsp1_ahb_clk.c, "spi_qsd.1"),
+ CLK_LOOKUP("iface_clk", gcc_blsp1_ahb_clk.c, "f9924000.spi"),
CLK_LOOKUP("iface_clk", gcc_blsp1_ahb_clk.c, "f9925000.i2c"),
CLK_LOOKUP("iface_clk", gcc_blsp1_ahb_clk.c, ""),
CLK_LOOKUP("core_clk", gcc_blsp1_qup1_i2c_apps_clk.c, ""),
- CLK_LOOKUP("core_clk", gcc_blsp1_qup1_spi_apps_clk.c, "spi_qsd.1"),
+ CLK_LOOKUP("core_clk", gcc_blsp1_qup1_spi_apps_clk.c, ""),
CLK_LOOKUP("core_clk", gcc_blsp1_qup2_i2c_apps_clk.c, ""),
- CLK_LOOKUP("core_clk", gcc_blsp1_qup2_spi_apps_clk.c, ""),
+ CLK_LOOKUP("core_clk", gcc_blsp1_qup2_spi_apps_clk.c, "f9924000.spi"),
CLK_LOOKUP("core_clk", gcc_blsp1_qup3_i2c_apps_clk.c, "f9925000.i2c"),
CLK_LOOKUP("core_clk", gcc_blsp1_qup3_spi_apps_clk.c, ""),
CLK_LOOKUP("core_clk", gcc_blsp1_qup4_i2c_apps_clk.c, ""),
@@ -2058,6 +2058,7 @@
CLK_LOOKUP("core_clk", gcc_gp2_clk.c, ""),
CLK_LOOKUP("core_clk", gcc_gp3_clk.c, ""),
+ CLK_LOOKUP("iface_clk", gcc_prng_ahb_clk.c, "f9bff000.qcom,msm-rng"),
CLK_LOOKUP("core_src_clk", ipa_clk_src.c, "fd4c0000.qcom,ipa"),
CLK_LOOKUP("core_clk", gcc_ipa_clk.c, "fd4c0000.qcom,ipa"),
CLK_LOOKUP("bus_clk", gcc_sys_noc_ipa_axi_clk.c, "fd4c0000.qcom,ipa"),
@@ -2085,10 +2086,15 @@
/* LPASS clocks */
CLK_LOOKUP("core_clk", audio_core_slimbus_core_clk.c, "fe12f000.slim"),
CLK_LOOKUP("iface_clk", audio_core_slimbus_lfabif_clk.c, ""),
- CLK_LOOKUP("core_clk", audio_core_lpaif_pri_clk_src.c, ""),
- CLK_LOOKUP("osr_clk", audio_core_lpaif_pri_osr_clk.c, ""),
- CLK_LOOKUP("ebit_clk", audio_core_lpaif_pri_ebit_clk.c, ""),
- CLK_LOOKUP("ibit_clk", audio_core_lpaif_pri_ibit_clk.c, ""),
+
+ CLK_LOOKUP("core_clk", audio_core_lpaif_pri_clk_src.c,
+ "msm-dai-q6-mi2s.0"),
+ CLK_LOOKUP("osr_clk", audio_core_lpaif_pri_osr_clk.c,
+ "msm-dai-q6-mi2s.0"),
+ CLK_LOOKUP("ebit_clk", audio_core_lpaif_pri_ebit_clk.c,
+ "msm-dai-q6-mi2s.0"),
+ CLK_LOOKUP("ibit_clk", audio_core_lpaif_pri_ibit_clk.c,
+ "msm-dai-q6-mi2s.0"),
CLK_LOOKUP("core_clk", audio_core_lpaif_sec_clk_src.c, ""),
CLK_LOOKUP("osr_clk", audio_core_lpaif_sec_osr_clk.c, ""),
CLK_LOOKUP("ebit_clk", audio_core_lpaif_sec_ebit_clk.c, ""),
diff --git a/arch/arm/mach-msm/devices-8064.c b/arch/arm/mach-msm/devices-8064.c
index abc0e6a..14edbcf 100644
--- a/arch/arm/mach-msm/devices-8064.c
+++ b/arch/arm/mach-msm/devices-8064.c
@@ -2726,18 +2726,26 @@
.resource = i2s_mdm_resources,
};
-static struct msm_dcvs_freq_entry apq8064_freq[] = {
- { 384000, 900, 0, 0, 0},
- { 594000, 950, 0, 0, 0},
- { 702000, 975, 0, 0, 0},
- {1026000, 1075, 0, 0, 0},
- {1242000, 1150, 0, 100, 100},
- {1458000, 1188, 0, 100, 100},
- {1512000, 1200, 1, 100, 100},
+static struct msm_dcvs_sync_rule apq8064_dcvs_sync_rules[] = {
+ {1026000, 400000},
+ {384000, 200000},
+ {0, 128000},
+};
+
+static struct msm_dcvs_platform_data apq8064_dcvs_data = {
+ .sync_rules = apq8064_dcvs_sync_rules,
+ .num_sync_rules = ARRAY_SIZE(apq8064_dcvs_sync_rules),
+};
+
+struct platform_device apq8064_dcvs_device = {
+ .name = "dcvs",
+ .id = -1,
+ .dev = {
+ .platform_data = &apq8064_dcvs_data,
+ },
};
static struct msm_dcvs_core_info apq8064_core_info = {
- .freq_tbl = &apq8064_freq[0],
.num_cores = 4,
.sensors = (int[]){7, 8, 9, 10},
.thermal_poll_ms = 60000,
@@ -2772,7 +2780,7 @@
},
.power_param = {
.current_temp = 25,
- .num_freq = ARRAY_SIZE(apq8064_freq),
+ .num_freq = 0, /* set at runtime */
}
};
diff --git a/arch/arm/mach-msm/devices-9615.c b/arch/arm/mach-msm/devices-9615.c
index 1ba408f..3888a4e 100644
--- a/arch/arm/mach-msm/devices-9615.c
+++ b/arch/arm/mach-msm/devices-9615.c
@@ -584,6 +584,10 @@
.name = "msm-voip-dsp",
.id = -1,
};
+struct platform_device msm_cpudai_stub = {
+ .name = "msm-dai-stub",
+ .id = -1,
+};
struct platform_device msm_dtmf = {
.name = "msm-pcm-dtmf",
.id = -1,
diff --git a/arch/arm/mach-msm/devices-iommu.c b/arch/arm/mach-msm/devices-iommu.c
index 091a8e8..983b13e 100644
--- a/arch/arm/mach-msm/devices-iommu.c
+++ b/arch/arm/mach-msm/devices-iommu.c
@@ -360,25 +360,25 @@
static struct msm_iommu_dev gfx3d_iommu = {
.name = "gfx3d",
.ncb = 3,
- .ttbr_split = 1,
+ .ttbr_split = 0,
};
static struct msm_iommu_dev gfx3d1_iommu = {
.name = "gfx3d1",
.ncb = 3,
- .ttbr_split = 1,
+ .ttbr_split = 0,
};
static struct msm_iommu_dev gfx2d0_iommu = {
.name = "gfx2d0",
.ncb = 2,
- .ttbr_split = 1,
+ .ttbr_split = 0,
};
static struct msm_iommu_dev gfx2d1_iommu = {
.name = "gfx2d1",
.ncb = 2,
- .ttbr_split = 1,
+ .ttbr_split = 0,
};
static struct msm_iommu_dev vcap_iommu = {
diff --git a/arch/arm/mach-msm/devices.h b/arch/arm/mach-msm/devices.h
index cd99628..eae01aa 100644
--- a/arch/arm/mach-msm/devices.h
+++ b/arch/arm/mach-msm/devices.h
@@ -250,6 +250,8 @@
extern struct platform_device msm_i2s_cpudai1;
extern struct platform_device msm_i2s_cpudai4;
extern struct platform_device msm_i2s_cpudai5;
+extern struct platform_device msm_cpudai_stub;
+
extern struct platform_device msm_pil_q6v3;
extern struct platform_device msm_pil_modem;
extern struct platform_device msm_pil_tzapps;
@@ -408,6 +410,7 @@
extern struct platform_device *msm_8974_stub_regulator_devices[];
extern int msm_8974_stub_regulator_devices_len;
+extern struct platform_device apq8064_dcvs_device;
extern struct platform_device apq8064_msm_gov_device;
extern struct platform_device msm_bus_8930_apps_fabric;
diff --git a/arch/arm/mach-msm/include/mach/camera.h b/arch/arm/mach-msm/include/mach/camera.h
index 14fd092..3dfa659c 100644
--- a/arch/arm/mach-msm/include/mach/camera.h
+++ b/arch/arm/mach-msm/include/mach/camera.h
@@ -605,6 +605,7 @@
S_DEFAULT,
S_LIVESHOT,
S_DUAL,
+ S_ADV_VIDEO,
S_EXIT
};
diff --git a/arch/arm/mach-msm/include/mach/irqs-9625.h b/arch/arm/mach-msm/include/mach/irqs-9625.h
index f50606d..abafc23 100644
--- a/arch/arm/mach-msm/include/mach/irqs-9625.h
+++ b/arch/arm/mach-msm/include/mach/irqs-9625.h
@@ -28,7 +28,7 @@
#define TLMM_MSM_SUMMARY_IRQ (GIC_SPI_START + 208)
#define NR_MSM_IRQS 288
-#define NR_GPIO_IRQS 88
+#define NR_GPIO_IRQS 76
#define NR_BOARD_IRQS 0
#define NR_TLMM_MSM_DIR_CONN_IRQ 8 /*Need to Verify this Count*/
#define NR_MSM_GPIOS NR_GPIO_IRQS
diff --git a/arch/arm/mach-msm/include/mach/msm_dcvs.h b/arch/arm/mach-msm/include/mach/msm_dcvs.h
index e81cee4..c29b57a 100644
--- a/arch/arm/mach-msm/include/mach/msm_dcvs.h
+++ b/arch/arm/mach-msm/include/mach/msm_dcvs.h
@@ -36,12 +36,36 @@
MSM_DCVS_DISABLE_HIGH_LATENCY_MODES,
};
+struct msm_dcvs_sync_rule {
+ unsigned long cpu_khz;
+ unsigned long gpu_floor_khz;
+};
+
+struct msm_dcvs_platform_data {
+ struct msm_dcvs_sync_rule *sync_rules;
+ unsigned num_sync_rules;
+};
+
struct msm_gov_platform_data {
struct msm_dcvs_core_info *info;
int latency;
};
/**
+ * msm_dcvs_register_cpu_freq
+ * @freq: the frequency value to register
+ * @voltage: the operating voltage (in mV) associated with the above frequency
+ *
+ * Register a cpu frequency and its operating voltage with dcvs.
+ */
+#ifdef CONFIG_MSM_DCVS
+void msm_dcvs_register_cpu_freq(uint32_t freq, uint32_t voltage);
+#else
+static inline void msm_dcvs_register_cpu_freq(uint32_t freq, uint32_t voltage)
+{}
+#endif
+
+/**
* msm_dcvs_idle
* @dcvs_core_id: The id returned by msm_dcvs_register_core
* @state: The enter/exit idle state the core is in
@@ -98,6 +122,7 @@
unsigned int (*get_frequency)(int type_core_num),
int (*idle_enable)(int type_core_num,
enum msm_core_control_event event),
+ int (*set_floor_frequency)(int type_core_num, unsigned int freq),
int sensor);
/**
diff --git a/arch/arm/mach-msm/mpm-8625.c b/arch/arm/mach-msm/mpm-8625.c
index c70ff5c..aaac476 100644
--- a/arch/arm/mach-msm/mpm-8625.c
+++ b/arch/arm/mach-msm/mpm-8625.c
@@ -101,6 +101,7 @@
static uint16_t msm_bypassed_apps_irqs[] = {
MSM8625_INT_CPR_IRQ0,
+ MSM8625_INT_L2CC_INTR,
};
/* Check IRQ falls into bypassed list are not */
diff --git a/arch/arm/mach-msm/msm_dcvs.c b/arch/arm/mach-msm/msm_dcvs.c
index c1c05af..2736870 100644
--- a/arch/arm/mach-msm/msm_dcvs.c
+++ b/arch/arm/mach-msm/msm_dcvs.c
@@ -23,6 +23,7 @@
#include <linux/stringify.h>
#include <linux/debugfs.h>
#include <linux/msm_tsens.h>
+#include <linux/platform_device.h>
#include <asm/atomic.h>
#include <asm/page.h>
#include <mach/msm_dcvs.h>
@@ -33,6 +34,8 @@
#define __info(f, ...) pr_info("MSM_DCVS: %s: " f, __func__, __VA_ARGS__)
#define MAX_PENDING (5)
+#define CORE_FLAG_TEMP_UPDATE 0x1
+
struct core_attribs {
struct kobj_attribute freq_change_us;
@@ -123,12 +126,14 @@
unsigned int (*get_frequency)(int type_core_num);
int (*idle_enable)(int type_core_num,
enum msm_core_control_event event);
+ int (*set_floor_frequency)(int type_core_num, unsigned int freq);
spinlock_t pending_freq_lock;
int pending_freq;
struct hrtimer slack_timer;
struct delayed_work temperature_work;
+ int flags;
};
static int msm_dcvs_enabled = 1;
@@ -140,6 +145,11 @@
static struct kobject *cores_kobj;
+#define DCVS_MAX_NUM_FREQS 15
+static struct msm_dcvs_freq_entry cpu_freq_tbl[DCVS_MAX_NUM_FREQS];
+static unsigned num_cpu_freqs;
+static struct msm_dcvs_platform_data *dcvs_pdata;
+
static void force_stop_slack_timer(struct dcvs_core *core)
{
unsigned long flags;
@@ -246,6 +256,35 @@
spin_unlock_irqrestore(&core->idle_state_change_lock, flags2);
}
+static void apply_gpu_floor(int cpu_freq)
+{
+ int i;
+ int gpu_floor_freq = 0;
+ struct dcvs_core *gpu;
+
+ if (!dcvs_pdata)
+ return;
+
+ for (i = 0; i < dcvs_pdata->num_sync_rules; i++)
+ if (cpu_freq > dcvs_pdata->sync_rules[i].cpu_khz) {
+ gpu_floor_freq =
+ dcvs_pdata->sync_rules[i].gpu_floor_khz;
+ break;
+ }
+
+ if (!gpu_floor_freq)
+ return;
+
+ for (i = GPU_OFFSET; i < CORES_MAX; i++) {
+ gpu = &core_list[i];
+ if (gpu->dcvs_core_id == -1)
+ continue;
+ if (gpu->set_floor_frequency)
+ gpu->set_floor_frequency(gpu->type_core_num,
+ gpu_floor_freq);
+ }
+}
+
static int __msm_dcvs_change_freq(struct dcvs_core *core)
{
int ret = 0;
@@ -277,6 +316,10 @@
spin_unlock_irqrestore(&core->pending_freq_lock, flags);
+ if (core->type == MSM_DCVS_CORE_TYPE_CPU &&
+ core->type_core_num == 0)
+ apply_gpu_floor(requested_freq);
+
/**
* Call the frequency sink driver to change the frequency
* We will need to get back the actual frequency in KHz and
@@ -353,6 +396,9 @@
unsigned long temp = 0;
int interval_ms;
+ if (!(core->flags & CORE_FLAG_TEMP_UPDATE))
+ return;
+
tsens_dev.sensor_num = core->sensor;
ret = tsens_get_temp(&tsens_dev, &temp);
if (!temp) {
@@ -825,6 +871,37 @@
return &core_list[offset];
}
+void msm_dcvs_register_cpu_freq(uint32_t freq, uint32_t voltage)
+{
+ BUG_ON(freq == 0 || voltage == 0 ||
+ num_cpu_freqs == DCVS_MAX_NUM_FREQS);
+
+ cpu_freq_tbl[num_cpu_freqs].freq = freq;
+ cpu_freq_tbl[num_cpu_freqs].voltage = voltage;
+
+ num_cpu_freqs++;
+}
+
+static void update_cpu_dcvs_params(struct msm_dcvs_core_info *info)
+{
+ int i;
+
+ BUG_ON(num_cpu_freqs == 0);
+
+ info->freq_tbl = cpu_freq_tbl;
+ info->power_param.num_freq = num_cpu_freqs;
+
+ if (!dcvs_pdata || dcvs_pdata->num_sync_rules == 0)
+ return;
+
+ /* the first sync rule shows what the turbo frequencies are -
+ * these frequencies need energy offsets set */
+ for (i = 0; i < DCVS_MAX_NUM_FREQS && cpu_freq_tbl[i].freq != 0; i++)
+ if (cpu_freq_tbl[i].freq > dcvs_pdata->sync_rules[0].cpu_khz) {
+ cpu_freq_tbl[i].active_energy_offset = 100;
+ cpu_freq_tbl[i].leakage_energy_offset = 100;
+ }
+}
int msm_dcvs_register_core(
enum msm_dcvs_core_type type,
@@ -834,6 +911,7 @@
unsigned int (*get_frequency)(int type_core_num),
int (*idle_enable)(int type_core_num,
enum msm_core_control_event event),
+ int (*set_floor_frequency)(int type_core_num, unsigned int freq),
int sensor)
{
int ret = -EINVAL;
@@ -857,9 +935,13 @@
core->set_frequency = set_frequency;
core->get_frequency = get_frequency;
core->idle_enable = idle_enable;
+ core->set_floor_frequency = set_floor_frequency;
core->pending_freq = STOP_FREQ_CHANGE;
core->info = info;
+ if (type == MSM_DCVS_CORE_TYPE_CPU)
+ update_cpu_dcvs_params(info);
+
memcpy(&core->algo_param, &info->algo_param,
sizeof(struct msm_dcvs_algo_param));
@@ -913,10 +995,6 @@
core->task = kthread_run(msm_dcvs_do_freq, (void *)core,
"msm_dcvs/%d", core->dcvs_core_id);
ret = core->dcvs_core_id;
-
- INIT_DELAYED_WORK(&core->temperature_work, msm_dcvs_report_temp_work);
- schedule_delayed_work(&core->temperature_work,
- msecs_to_jiffies(info->thermal_poll_ms));
return ret;
bail:
core->dcvs_core_id = -1;
@@ -985,6 +1063,10 @@
}
force_start_slack_timer(core, timer_interval_us);
+ core->flags |= CORE_FLAG_TEMP_UPDATE;
+ INIT_DELAYED_WORK(&core->temperature_work, msm_dcvs_report_temp_work);
+ schedule_delayed_work(&core->temperature_work,
+ msecs_to_jiffies(core->info->thermal_poll_ms));
core->idle_enable(core->type_core_num, MSM_DCVS_ENABLE_IDLE_PULSE);
return 0;
@@ -1011,6 +1093,9 @@
return ret;
}
+ core->flags &= ~CORE_FLAG_TEMP_UPDATE;
+ cancel_delayed_work(&core->temperature_work);
+
core->idle_enable(core->type_core_num, MSM_DCVS_DISABLE_IDLE_PULSE);
/* Notify TZ to stop receiving idle info for the core */
ret = msm_dcvs_scm_event(core->dcvs_core_id, MSM_DCVS_SCM_DCVS_ENABLE,
@@ -1109,11 +1194,29 @@
}
late_initcall(msm_dcvs_late_init);
+static int __devinit dcvs_probe(struct platform_device *pdev)
+{
+ if (pdev->dev.platform_data)
+ dcvs_pdata = pdev->dev.platform_data;
+
+ return 0;
+}
+
+static struct platform_driver dcvs_driver = {
+ .probe = dcvs_probe,
+ .driver = {
+ .name = "dcvs",
+ .owner = THIS_MODULE,
+ },
+};
+
static int __init msm_dcvs_early_init(void)
{
int ret = 0;
int i;
+ platform_driver_register(&dcvs_driver);
+
if (!msm_dcvs_enabled) {
__info("Not enabled (%d)\n", msm_dcvs_enabled);
return 0;
diff --git a/arch/arm/mach-msm/msm_mpdecision.c b/arch/arm/mach-msm/msm_mpdecision.c
index a65b3ee..407be6a 100644
--- a/arch/arm/mach-msm/msm_mpdecision.c
+++ b/arch/arm/mach-msm/msm_mpdecision.c
@@ -123,6 +123,7 @@
static unsigned long last_nr;
static int num_present_hundreds;
+static ktime_t last_down_time;
#define RQ_AVG_INSIGNIFICANT_BITS 3
static bool ok_to_update_tz(int nr, int last_nr)
@@ -380,14 +381,15 @@
}
}
- for_each_possible_cpu(cpu) {
- if (!(atomic_read(&msm_mpd.algo_cpu_mask) & (1 << cpu))
- && cpu_online(cpu)) {
- bring_down_cpu(cpu);
- if (!cpu_online(cpu))
- goto restart;
- }
- }
+ if (ktime_to_ns(ktime_sub(ktime_get(), last_down_time)) >
+ 100 * NSEC_PER_MSEC)
+ for_each_possible_cpu(cpu)
+ if (!(atomic_read(&msm_mpd.algo_cpu_mask) &
+ (1 << cpu)) && cpu_online(cpu)) {
+ bring_down_cpu(cpu);
+ last_down_time = ktime_get();
+ break;
+ }
msm_mpd.hpupdate = HPUPDATE_WAITING;
}
diff --git a/arch/arm/mach-msm/no-pm.c b/arch/arm/mach-msm/no-pm.c
index d38b416..d460c70 100644
--- a/arch/arm/mach-msm/no-pm.c
+++ b/arch/arm/mach-msm/no-pm.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2010-2011, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -12,13 +12,15 @@
*/
#include <linux/module.h>
-
+#include <asm/proc-fns.h>
#include <mach/cpuidle.h>
#include "idle.h"
#include "pm.h"
void arch_idle(void)
-{ }
+{
+ cpu_do_idle();
+}
void msm_pm_set_platform_data(struct msm_pm_platform_data *data, int count)
{ }
diff --git a/arch/arm/mach-msm/pil-pronto.c b/arch/arm/mach-msm/pil-pronto.c
index 49fe182..6ae7544 100644
--- a/arch/arm/mach-msm/pil-pronto.c
+++ b/arch/arm/mach-msm/pil-pronto.c
@@ -31,6 +31,7 @@
#include "peripheral-loader.h"
#include "scm-pas.h"
+#include "ramdump.h"
#define PRONTO_PMU_COMMON_GDSCR 0x24
#define PRONTO_PMU_COMMON_GDSCR_SW_COLLAPSE BIT(0)
@@ -81,6 +82,7 @@
bool crash;
struct delayed_work cancel_vote_work;
int irq;
+ struct ramdump_device *ramdump_dev;
};
static int pil_pronto_make_proxy_vote(struct pil_desc *pil)
@@ -359,9 +361,19 @@
smsm_change_state(SMSM_APPS_STATE, SMSM_RESET, SMSM_RESET);
}
-static int wcnss_ramdump(int enable, const struct subsys_desc *crashed_subsys)
+static struct ramdump_segment pronto_segments[] = {
+ { 0x0D200000, 0x0D980000 - 0x0D200000 }
+};
+
+static int wcnss_ramdump(int enable, const struct subsys_desc *subsys)
{
- return 0;
+ struct pronto_data *drv = subsys_to_drv(subsys);
+
+ if (enable)
+ return do_ramdump(drv->ramdump_dev, pronto_segments,
+ ARRAY_SIZE(pronto_segments));
+ else
+ return 0;
}
static int __devinit pil_pronto_probe(struct platform_device *pdev)
@@ -464,6 +476,12 @@
if (ret < 0)
goto err_irq;
+ drv->ramdump_dev = create_ramdump_device("pronto", &pdev->dev);
+ if (!drv->ramdump_dev) {
+ ret = -ENOMEM;
+ goto err_irq;
+ }
+
/* Initialize common_ss GDSCR to wait 4 cycles between states */
regval = readl_relaxed(drv->base + PRONTO_PMU_COMMON_GDSCR)
& PRONTO_PMU_COMMON_GDSCR_SW_COLLAPSE;
@@ -489,6 +507,7 @@
smsm_state_cb_deregister(SMSM_WCNSS_STATE, SMSM_RESET,
smsm_state_cb_hdlr, drv);
pil_desc_release(&drv->desc);
+ destroy_ramdump_device(drv->ramdump_dev);
return 0;
}
diff --git a/arch/arm/mach-msm/qdsp5/audio_acdb.c b/arch/arm/mach-msm/qdsp5/audio_acdb.c
index 16f23f4..d7a4607 100644
--- a/arch/arm/mach-msm/qdsp5/audio_acdb.c
+++ b/arch/arm/mach-msm/qdsp5/audio_acdb.c
@@ -2575,6 +2575,7 @@
memset(&acdb_data, 0, sizeof(acdb_data));
spin_lock_init(&acdb_data.dsp_lock);
+ init_waitqueue_head(&acdb_data.wait);
acdb_data.cb_thread_task = kthread_run(acdb_calibrate_device,
NULL, "acdb_cb_thread");
@@ -2590,7 +2591,6 @@
MM_ERR("RTC ACDB=>INIT Failure\n");
#endif
- init_waitqueue_head(&acdb_data.wait);
return misc_register(&acdb_misc);
err:
diff --git a/arch/arm/mm/cache-pl310-erp.c b/arch/arm/mm/cache-pl310-erp.c
index ad75143..191060f 100644
--- a/arch/arm/mm/cache-pl310-erp.c
+++ b/arch/arm/mm/cache-pl310-erp.c
@@ -34,6 +34,7 @@
unsigned int slverr;
unsigned int decerr;
void __iomem *base;
+ unsigned int intr_mask_reg;
};
#define ECNTR BIT(0)
@@ -128,19 +129,20 @@
static void pl310_mask_int(struct pl310_drv_data *p, bool enable)
{
- uint16_t mask;
-
+ /* L2CC register contents needs to be saved
+ * as it's power rail will be removed during suspend
+ */
if (enable)
- mask = 0x1FF;
+ p->intr_mask_reg = 0x1FF;
else
- mask = 0x0;
+ p->intr_mask_reg = 0x0;
- writel_relaxed(mask, p->base + L2X0_INTR_MASK);
+ writel_relaxed(p->intr_mask_reg, p->base + L2X0_INTR_MASK);
/* Make sure Mask is updated */
mb();
- pr_debug("Mask interrupt %x\n",
+ pr_debug("Mask interrupt 0%x\n",
readl_relaxed(p->base + L2X0_INTR_MASK));
}
@@ -258,12 +260,42 @@
return 0;
}
+#ifdef CONFIG_PM
+static int pl310_suspend(struct device *dev)
+{
+ struct pl310_drv_data *p = dev_get_drvdata(dev);
+
+ disable_irq(p->irq);
+
+ return 0;
+}
+
+static int pl310_resume_early(struct device *dev)
+{
+ struct pl310_drv_data *p = dev_get_drvdata(dev);
+
+ pl310_mask_int(p, true);
+
+ enable_irq(p->irq);
+
+ return 0;
+}
+
+static const struct dev_pm_ops pl310_cache_pm_ops = {
+ .suspend = pl310_suspend,
+ .resume_early = pl310_resume_early,
+};
+#endif
+
static struct platform_driver pl310_cache_erp_driver = {
.probe = pl310_cache_erp_probe,
.remove = __devexit_p(pl310_cache_erp_remove),
.driver = {
.name = MODULE_NAME,
.owner = THIS_MODULE,
+#ifdef CONFIG_PM
+ .pm = &pl310_cache_pm_ops,
+#endif
},
};
diff --git a/drivers/char/diag/diagchar_core.c b/drivers/char/diag/diagchar_core.c
index fe61d2d..19c6ed2 100644
--- a/drivers/char/diag/diagchar_core.c
+++ b/drivers/char/diag/diagchar_core.c
@@ -380,6 +380,22 @@
(*count_entries)++;
}
+#ifdef CONFIG_DIAG_BRIDGE_CODE
+uint16_t diag_get_remote_device_mask(void)
+{
+ uint16_t remote_dev = 0;
+
+ if (driver->hsic_inited)
+ remote_dev |= (1 << 0);
+ if (driver->diag_smux_enabled)
+ remote_dev |= (1 << 1);
+
+ return remote_dev;
+}
+#else
+inline uint16_t diag_get_remote_device_mask(void) { return 0; }
+#endif
+
long diagchar_ioctl(struct file *filp,
unsigned int iocmd, unsigned long ioarg)
{
@@ -583,6 +599,10 @@
}
}
}
+ if (driver->logging_mode == SOCKET_MODE)
+ driver->socket_process = current;
+ if (driver->logging_mode == CALLBACK_MODE)
+ driver->callback_process = current;
if (driver->logging_mode == UART_MODE ||
driver->logging_mode == SOCKET_MODE ||
driver->logging_mode == CALLBACK_MODE) {
@@ -590,10 +610,6 @@
driver->mask_check = 0;
driver->logging_mode = MEMORY_DEVICE_MODE;
}
- if (driver->logging_mode == SOCKET_MODE)
- driver->socket_process = current;
- if (driver->logging_mode == CALLBACK_MODE)
- driver->callback_process = current;
driver->logging_process_id = current->tgid;
mutex_unlock(&driver->diagchar_mutex);
if (temp == MEMORY_DEVICE_MODE && driver->logging_mode
@@ -695,6 +711,13 @@
}
#endif /* DIAG over USB */
success = 1;
+ } else if (iocmd == DIAG_IOCTL_REMOTE_DEV) {
+ uint16_t remote_dev = diag_get_remote_device_mask();
+
+ if (copy_to_user((void *)ioarg, &remote_dev, sizeof(uint16_t)))
+ success = -EFAULT;
+ else
+ success = 1;
}
return success;
@@ -1206,8 +1229,9 @@
if (err) {
/*Free the buffer right away if write failed */
diagmem_free(driver, buf_hdlc, POOL_TYPE_HDLC);
- diagmem_free(driver, (unsigned char *)driver->
- write_ptr_svc, POOL_TYPE_WRITE_STRUCT);
+ if (driver->logging_mode == USB_MODE)
+ diagmem_free(driver, (unsigned char *)driver->
+ write_ptr_svc, POOL_TYPE_WRITE_STRUCT);
ret = -EIO;
goto fail_free_hdlc;
}
@@ -1234,8 +1258,9 @@
if (err) {
/*Free the buffer right away if write failed */
diagmem_free(driver, buf_hdlc, POOL_TYPE_HDLC);
- diagmem_free(driver, (unsigned char *)driver->
- write_ptr_svc, POOL_TYPE_WRITE_STRUCT);
+ if (driver->logging_mode == USB_MODE)
+ diagmem_free(driver, (unsigned char *)driver->
+ write_ptr_svc, POOL_TYPE_WRITE_STRUCT);
ret = -EIO;
goto fail_free_hdlc;
}
@@ -1259,8 +1284,9 @@
if (err) {
/*Free the buffer right away if write failed */
diagmem_free(driver, buf_hdlc, POOL_TYPE_HDLC);
- diagmem_free(driver, (unsigned char *)driver->
- write_ptr_svc, POOL_TYPE_WRITE_STRUCT);
+ if (driver->logging_mode == USB_MODE)
+ diagmem_free(driver, (unsigned char *)driver->
+ write_ptr_svc, POOL_TYPE_WRITE_STRUCT);
ret = -EIO;
goto fail_free_hdlc;
}
diff --git a/drivers/char/diag/diagfwd_hsic.c b/drivers/char/diag/diagfwd_hsic.c
index 56f2fae..7aef01f 100644
--- a/drivers/char/diag/diagfwd_hsic.c
+++ b/drivers/char/diag/diagfwd_hsic.c
@@ -195,7 +195,7 @@
if (actual_size < 0)
pr_err("DIAG in %s: actual_size: %d\n", __func__, actual_size);
- if (driver->usb_mdm_connected)
+ if (driver->usb_mdm_connected && (driver->logging_mode == USB_MODE))
queue_work(driver->diag_bridge_wq, &driver->diag_read_mdm_work);
}
@@ -462,7 +462,7 @@
* If there is no write of the usb mdm data on the
* hsic channel
*/
- if (!driver->in_busy_hsic_write)
+ if (!driver->in_busy_hsic_write && (driver->logging_mode == USB_MODE))
queue_work(driver->diag_bridge_wq, &driver->diag_read_mdm_work);
return 0;
@@ -552,9 +552,9 @@
* If for some reason there was no mdm channel read initiated,
* queue up the reading of data from the mdm channel
*/
- if (!driver->in_busy_hsic_read_on_device)
- queue_work(driver->diag_bridge_wq,
- &driver->diag_read_mdm_work);
+ if (!driver->in_busy_hsic_read_on_device &&
+ (driver->logging_mode == USB_MODE))
+ queue_work(driver->diag_bridge_wq, &driver->diag_read_mdm_work);
}
static int diag_hsic_probe(struct platform_device *pdev)
diff --git a/drivers/char/hw_random/msm_rng.c b/drivers/char/hw_random/msm_rng.c
index 60ca44f..f2e5439 100644
--- a/drivers/char/hw_random/msm_rng.c
+++ b/drivers/char/hw_random/msm_rng.c
@@ -181,7 +181,12 @@
msm_rng_dev->base = base;
/* create a handle for clock control */
- msm_rng_dev->prng_clk = clk_get(&pdev->dev, "core_clk");
+ if ((pdev->dev.of_node) && (of_property_read_bool(pdev->dev.of_node,
+ "qcom,msm-rng-iface-clk")))
+ msm_rng_dev->prng_clk = clk_get(&pdev->dev,
+ "iface_clk");
+ else
+ msm_rng_dev->prng_clk = clk_get(&pdev->dev, "core_clk");
if (IS_ERR(msm_rng_dev->prng_clk)) {
dev_err(&pdev->dev, "failed to register clock source\n");
error = -EPERM;
diff --git a/drivers/cpufreq/cpufreq_gov_msm.c b/drivers/cpufreq/cpufreq_gov_msm.c
index 6ddbf4e..8f086aa 100644
--- a/drivers/cpufreq/cpufreq_gov_msm.c
+++ b/drivers/cpufreq/cpufreq_gov_msm.c
@@ -253,6 +253,7 @@
msm_dcvs_freq_set,
msm_dcvs_freq_get,
msm_dcvs_idle_notifier,
+ NULL,
sensor);
if (gov->dcvs_core_id < 0) {
pr_err("Unable to register core for %d\n", cpu);
diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c
index 67cb34a..0109d26 100644
--- a/drivers/gpu/msm/adreno.c
+++ b/drivers/gpu/msm/adreno.c
@@ -253,6 +253,13 @@
if (result)
goto unmap_memstore_desc;
+ /*
+ * Set the mpu end to the last "normal" global memory we use.
+ * For the IOMMU, this will be used to restrict access to the
+ * mapped registers.
+ */
+ device->mh.mpu_range = device->mmu.setstate_memory.gpuaddr +
+ device->mmu.setstate_memory.size;
return result;
unmap_memstore_desc:
diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c
index b8adbe67..ca2ad19 100644
--- a/drivers/gpu/msm/kgsl.c
+++ b/drivers/gpu/msm/kgsl.c
@@ -853,13 +853,6 @@
dev_priv->device = device;
filep->private_data = dev_priv;
- /* Get file (per process) private struct */
- dev_priv->process_priv = kgsl_get_process_private(dev_priv);
- if (dev_priv->process_priv == NULL) {
- result = -ENOMEM;
- goto err_freedevpriv;
- }
-
mutex_lock(&device->mutex);
kgsl_check_suspended(device);
@@ -871,21 +864,38 @@
if (result) {
mutex_unlock(&device->mutex);
- goto err_putprocess;
+ goto err_freedevpriv;
}
kgsl_pwrctrl_set_state(device, KGSL_STATE_ACTIVE);
}
device->open_count++;
mutex_unlock(&device->mutex);
+ /*
+ * Get file (per process) private struct. This must be done
+ * after the first start so that the global pagetable mappings
+ * are set up before we create the per-process pagetable.
+ */
+ dev_priv->process_priv = kgsl_get_process_private(dev_priv);
+ if (dev_priv->process_priv == NULL) {
+ result = -ENOMEM;
+ goto err_stop;
+ }
+
KGSL_DRV_INFO(device, "Initialized %s: mmu=%s pagetable_count=%d\n",
device->name, kgsl_mmu_enabled() ? "on" : "off",
kgsl_pagetable_count);
return result;
-err_putprocess:
- kgsl_put_process_private(device, dev_priv->process_priv);
+err_stop:
+ mutex_lock(&device->mutex);
+ device->open_count--;
+ if (device->open_count == 0) {
+ result = device->ftbl->stop(device);
+ kgsl_pwrctrl_set_state(device, KGSL_STATE_INIT);
+ }
+ mutex_unlock(&device->mutex);
err_freedevpriv:
filep->private_data = NULL;
kfree(dev_priv);
diff --git a/drivers/gpu/msm/kgsl_debugfs.c b/drivers/gpu/msm/kgsl_debugfs.c
index 52097dc..f731cfc 100644
--- a/drivers/gpu/msm/kgsl_debugfs.c
+++ b/drivers/gpu/msm/kgsl_debugfs.c
@@ -190,7 +190,7 @@
entry = rb_entry(node, struct kgsl_mem_entry, node);
m = &entry->memdesc;
- flags[0] = m->priv & KGSL_MEMDESC_GLOBAL ? 'g' : '-';
+ flags[0] = kgsl_memdesc_is_global(m) ? 'g' : '-';
flags[1] = m->flags & KGSL_MEMFLAGS_GPUREADONLY ? 'r' : '-';
flags[2] = get_alignflag(m);
flags[3] = '\0';
diff --git a/drivers/gpu/msm/kgsl_iommu.c b/drivers/gpu/msm/kgsl_iommu.c
index 07ea48e..1bccd4d 100644
--- a/drivers/gpu/msm/kgsl_iommu.c
+++ b/drivers/gpu/msm/kgsl_iommu.c
@@ -702,6 +702,70 @@
}
}
+/*
+ * kgsl_iommu_setup_regs - map iommu registers into a pagetable
+ * @mmu: Pointer to mmu structure
+ * @pt: the pagetable
+ *
+ * To do pagetable switches from the GPU command stream, the IOMMU
+ * registers need to be mapped into the GPU's pagetable. This function
+ * is used differently on different targets. On 8960, the registers
+ * are mapped into every pagetable during kgsl_setup_pt(). On
+ * all other targets, the registers are mapped only into the second
+ * context bank.
+ *
+ * Return - 0 on success else error code
+ */
+static int kgsl_iommu_setup_regs(struct kgsl_mmu *mmu,
+ struct kgsl_pagetable *pt)
+{
+ int status;
+ int i = 0;
+ struct kgsl_iommu *iommu = mmu->priv;
+
+ if (!msm_soc_version_supports_iommu_v1())
+ return 0;
+
+ for (i = 0; i < iommu->unit_count; i++) {
+ iommu->iommu_units[i].reg_map.priv |= KGSL_MEMDESC_GLOBAL;
+ status = kgsl_mmu_map(pt,
+ &(iommu->iommu_units[i].reg_map),
+ GSL_PT_PAGE_RV | GSL_PT_PAGE_WV);
+ if (status) {
+ iommu->iommu_units[i].reg_map.priv &=
+ ~KGSL_MEMDESC_GLOBAL;
+ goto err;
+ }
+ }
+ return 0;
+err:
+ for (i--; i >= 0; i--) {
+ kgsl_mmu_unmap(pt,
+ &(iommu->iommu_units[i].reg_map));
+ iommu->iommu_units[i].reg_map.priv &= ~KGSL_MEMDESC_GLOBAL;
+ }
+ return status;
+}
+
+/*
+ * kgsl_iommu_cleanup_regs - unmap iommu registers from a pagetable
+ * @mmu: Pointer to mmu structure
+ * @pt: the pagetable
+ *
+ * Removes mappings created by kgsl_iommu_setup_regs().
+ *
+ * Return - 0 on success else error code
+ */
+static void kgsl_iommu_cleanup_regs(struct kgsl_mmu *mmu,
+ struct kgsl_pagetable *pt)
+{
+ struct kgsl_iommu *iommu = mmu->priv;
+ int i;
+ for (i = 0; i < iommu->unit_count; i++)
+ kgsl_mmu_unmap(pt, &(iommu->iommu_units[i].reg_map));
+}
+
+
static int kgsl_iommu_init(struct kgsl_mmu *mmu)
{
/*
@@ -744,6 +808,15 @@
KGSL_IOMMU_SETSTATE_NOP_OFFSET,
cp_nop_packet(1));
+ if (cpu_is_msm8960()) {
+ /*
+ * 8960 doesn't have a second context bank, so the IOMMU
+ * registers must be mapped into every pagetable.
+ */
+ iommu_ops.mmu_setup_pt = kgsl_iommu_setup_regs;
+ iommu_ops.mmu_cleanup_pt = kgsl_iommu_cleanup_regs;
+ }
+
dev_info(mmu->device->dev, "|%s| MMU type set for device is IOMMU\n",
__func__);
done:
@@ -766,9 +839,6 @@
static int kgsl_iommu_setup_defaultpagetable(struct kgsl_mmu *mmu)
{
int status = 0;
- int i = 0;
- struct kgsl_iommu *iommu = mmu->priv;
- struct kgsl_pagetable *pagetable = NULL;
/* If chip is not 8960 then we use the 2nd context bank for pagetable
* switching on the 3D side for which a separate table is allocated */
@@ -779,6 +849,9 @@
status = -ENOMEM;
goto err;
}
+ status = kgsl_iommu_setup_regs(mmu, mmu->priv_bank_table);
+ if (status)
+ goto err;
}
mmu->defaultpagetable = kgsl_mmu_getpagetable(KGSL_MMU_GLOBAL_PT);
/* Return error if the default pagetable doesn't exist */
@@ -786,31 +859,10 @@
status = -ENOMEM;
goto err;
}
- pagetable = mmu->priv_bank_table ? mmu->priv_bank_table :
- mmu->defaultpagetable;
- /* Map the IOMMU regsiters to only defaultpagetable */
- if (msm_soc_version_supports_iommu_v1()) {
- for (i = 0; i < iommu->unit_count; i++) {
- iommu->iommu_units[i].reg_map.priv |=
- KGSL_MEMDESC_GLOBAL;
- status = kgsl_mmu_map(pagetable,
- &(iommu->iommu_units[i].reg_map),
- GSL_PT_PAGE_RV | GSL_PT_PAGE_WV);
- if (status) {
- iommu->iommu_units[i].reg_map.priv &=
- ~KGSL_MEMDESC_GLOBAL;
- goto err;
- }
- }
- }
return status;
err:
- for (i--; i >= 0; i--) {
- kgsl_mmu_unmap(pagetable,
- &(iommu->iommu_units[i].reg_map));
- iommu->iommu_units[i].reg_map.priv &= ~KGSL_MEMDESC_GLOBAL;
- }
if (mmu->priv_bank_table) {
+ kgsl_iommu_cleanup_regs(mmu, mmu->priv_bank_table);
kgsl_mmu_putpagetable(mmu->priv_bank_table);
mmu->priv_bank_table = NULL;
}
@@ -839,10 +891,12 @@
* a225, hence we still keep the MMU active on 8960 */
if (cpu_is_msm8960()) {
struct kgsl_mh *mh = &(mmu->device->mh);
+ BUG_ON(iommu->iommu_units[0].reg_map.gpuaddr != 0 &&
+ mh->mpu_base > iommu->iommu_units[0].reg_map.gpuaddr);
kgsl_regwrite(mmu->device, MH_MMU_CONFIG, 0x00000001);
+
kgsl_regwrite(mmu->device, MH_MMU_MPU_END,
- mh->mpu_base +
- iommu->iommu_units[0].reg_map.gpuaddr);
+ mh->mpu_base + mh->mpu_range);
} else {
kgsl_regwrite(mmu->device, MH_MMU_CONFIG, 0x00000000);
}
@@ -915,14 +969,12 @@
"with err: %d\n", iommu_pt->domain, gpuaddr,
range, ret);
-#ifdef CONFIG_KGSL_PER_PROCESS_PAGE_TABLE
/*
* Flushing only required if per process pagetables are used. With
* global case, flushing will happen inside iommu_map function
*/
- if (!ret && msm_soc_version_supports_iommu_v1())
+ if (!ret && kgsl_mmu_is_perprocess())
*tlb_flags = UINT_MAX;
-#endif
return 0;
}
@@ -1003,22 +1055,23 @@
{
struct kgsl_iommu *iommu = mmu->priv;
int i;
- for (i = 0; i < iommu->unit_count; i++) {
- struct kgsl_pagetable *pagetable = (mmu->priv_bank_table ?
- mmu->priv_bank_table : mmu->defaultpagetable);
- if (iommu->iommu_units[i].reg_map.gpuaddr)
- kgsl_mmu_unmap(pagetable,
- &(iommu->iommu_units[i].reg_map));
- if (iommu->iommu_units[i].reg_map.hostptr)
- iounmap(iommu->iommu_units[i].reg_map.hostptr);
- kgsl_sg_free(iommu->iommu_units[i].reg_map.sg,
- iommu->iommu_units[i].reg_map.sglen);
+
+ if (mmu->priv_bank_table != NULL) {
+ kgsl_iommu_cleanup_regs(mmu, mmu->priv_bank_table);
+ kgsl_mmu_putpagetable(mmu->priv_bank_table);
}
- if (mmu->priv_bank_table)
- kgsl_mmu_putpagetable(mmu->priv_bank_table);
- if (mmu->defaultpagetable)
+ if (mmu->defaultpagetable != NULL)
kgsl_mmu_putpagetable(mmu->defaultpagetable);
+
+ for (i = 0; i < iommu->unit_count; i++) {
+ struct kgsl_memdesc *reg_map = &iommu->iommu_units[i].reg_map;
+
+ if (reg_map->hostptr)
+ iounmap(reg_map->hostptr);
+ kgsl_sg_free(reg_map->sg, reg_map->sglen);
+ }
+
kfree(iommu);
return 0;
@@ -1149,6 +1202,9 @@
.mmu_get_num_iommu_units = kgsl_iommu_get_num_iommu_units,
.mmu_pt_equal = kgsl_iommu_pt_equal,
.mmu_get_pt_base_addr = kgsl_iommu_get_pt_base_addr,
+ /* These callbacks will be set on some chipsets */
+ .mmu_setup_pt = NULL,
+ .mmu_cleanup_pt = NULL,
};
struct kgsl_mmu_pt_ops iommu_pt_ops = {
diff --git a/drivers/gpu/msm/kgsl_mmu.c b/drivers/gpu/msm/kgsl_mmu.c
index 68cd167..6fe119d 100644
--- a/drivers/gpu/msm/kgsl_mmu.c
+++ b/drivers/gpu/msm/kgsl_mmu.c
@@ -36,17 +36,18 @@
static int kgsl_cleanup_pt(struct kgsl_pagetable *pt)
{
int i;
- /* For IOMMU only unmap the global structures to global pt */
- if ((KGSL_MMU_TYPE_NONE != kgsl_mmu_type) &&
- (KGSL_MMU_TYPE_IOMMU == kgsl_mmu_type) &&
- (KGSL_MMU_GLOBAL_PT != pt->name) &&
- (KGSL_MMU_PRIV_BANK_TABLE_NAME != pt->name))
- return 0;
+ struct kgsl_device *device;
+
for (i = 0; i < KGSL_DEVICE_MAX; i++) {
- struct kgsl_device *device = kgsl_driver.devp[i];
+ device = kgsl_driver.devp[i];
if (device)
device->ftbl->cleanup_pt(device, pt);
}
+ /* Only the 3d device needs mmu specific pt entries */
+ device = kgsl_driver.devp[KGSL_DEVICE_3D0];
+ if (device->mmu.mmu_ops->mmu_cleanup_pt != NULL)
+ device->mmu.mmu_ops->mmu_cleanup_pt(&device->mmu, pt);
+
return 0;
}
@@ -55,21 +56,23 @@
{
int i = 0;
int status = 0;
+ struct kgsl_device *device;
- /* For IOMMU only map the global structures to global pt */
- if ((KGSL_MMU_TYPE_NONE != kgsl_mmu_type) &&
- (KGSL_MMU_TYPE_IOMMU == kgsl_mmu_type) &&
- (KGSL_MMU_GLOBAL_PT != pt->name) &&
- (KGSL_MMU_PRIV_BANK_TABLE_NAME != pt->name))
- return 0;
for (i = 0; i < KGSL_DEVICE_MAX; i++) {
- struct kgsl_device *device = kgsl_driver.devp[i];
+ device = kgsl_driver.devp[i];
if (device) {
status = device->ftbl->setup_pt(device, pt);
if (status)
goto error_pt;
}
}
+ /* Only the 3d device needs mmu specific pt entries */
+ device = kgsl_driver.devp[KGSL_DEVICE_3D0];
+ if (device->mmu.mmu_ops->mmu_setup_pt != NULL) {
+ status = device->mmu.mmu_ops->mmu_setup_pt(&device->mmu, pt);
+ if (status)
+ goto error_pt;
+ }
return status;
error_pt:
while (i >= 0) {
@@ -309,22 +312,6 @@
return ret;
}
-unsigned int kgsl_mmu_get_ptsize(void)
-{
- /*
- * For IOMMU, we could do up to 4G virtual range if we wanted to, but
- * it makes more sense to return a smaller range and leave the rest of
- * the virtual range for future improvements
- */
-
- if (KGSL_MMU_TYPE_GPU == kgsl_mmu_type)
- return CONFIG_MSM_KGSL_PAGE_TABLE_SIZE;
- else if (KGSL_MMU_TYPE_IOMMU == kgsl_mmu_type)
- return SZ_2G - KGSL_PAGETABLE_BASE;
- else
- return 0;
-}
-
int
kgsl_mmu_get_ptname_from_ptbase(struct kgsl_mmu *mmu, unsigned int pt_base)
{
@@ -480,7 +467,7 @@
goto err_kgsl_pool;
}
- if (gen_pool_add(pagetable->pool, KGSL_PAGETABLE_BASE,
+ if (gen_pool_add(pagetable->pool, kgsl_mmu_get_base_addr(),
ptsize, -1)) {
KGSL_CORE_ERR("gen_pool_add failed\n");
goto err_pool;
@@ -528,11 +515,7 @@
if (KGSL_MMU_TYPE_NONE == kgsl_mmu_type)
return (void *)(-1);
-#ifndef CONFIG_KGSL_PER_PROCESS_PAGE_TABLE
- name = KGSL_MMU_GLOBAL_PT;
-#endif
- /* We presently do not support per-process for IOMMU-v2 */
- if (!msm_soc_version_supports_iommu_v1())
+ if (!kgsl_mmu_is_perprocess())
name = KGSL_MMU_GLOBAL_PT;
pt = kgsl_get_pagetable(name);
@@ -589,15 +572,6 @@
*/
}
-static inline struct gen_pool *
-_get_pool(struct kgsl_pagetable *pagetable, unsigned int flags)
-{
- if (pagetable->kgsl_pool &&
- (KGSL_MEMDESC_GLOBAL & flags))
- return pagetable->kgsl_pool;
- return pagetable->pool;
-}
-
int
kgsl_mmu_map(struct kgsl_pagetable *pagetable,
struct kgsl_memdesc *memdesc,
@@ -628,28 +602,48 @@
size = kgsl_sg_size(memdesc->sg, memdesc->sglen);
- /* Allocate from kgsl pool if it exists for global mappings */
- pool = _get_pool(pagetable, memdesc->priv);
+ pool = pagetable->pool;
- /* Allocate aligned virtual addresses for iommu. This allows
- * more efficient pagetable entries if the physical memory
- * is also aligned. Don't do this for GPUMMU, because
- * the address space is so small.
- */
- if (KGSL_MMU_TYPE_IOMMU == kgsl_mmu_get_mmutype() &&
- kgsl_memdesc_get_align(memdesc) > 0)
- page_align = kgsl_memdesc_get_align(memdesc);
-
- memdesc->gpuaddr = gen_pool_alloc_aligned(pool, size, page_align);
- if (memdesc->gpuaddr == 0) {
- KGSL_CORE_ERR("gen_pool_alloc(%d) failed from pool: %s\n",
- size,
- (pool == pagetable->kgsl_pool) ?
- "kgsl_pool" : "general_pool");
- KGSL_CORE_ERR(" [%d] allocated=%d, entries=%d\n",
- pagetable->name, pagetable->stats.mapped,
- pagetable->stats.entries);
- return -ENOMEM;
+ if (KGSL_MMU_TYPE_IOMMU == kgsl_mmu_get_mmutype()) {
+ /* Allocate aligned virtual addresses for iommu. This allows
+ * more efficient pagetable entries if the physical memory
+ * is also aligned. Don't do this for GPUMMU, because
+ * the address space is so small.
+ */
+ if (kgsl_memdesc_get_align(memdesc) > 0)
+ page_align = kgsl_memdesc_get_align(memdesc);
+ if (kgsl_memdesc_is_global(memdesc)) {
+ /*
+ * Only the default pagetable has a kgsl_pool, and
+ * it is responsible for creating the mapping for
+ * each global buffer. The mapping will be reused
+ * in all other pagetables and it must already exist
+ * when we're creating other pagetables which do not
+ * have a kgsl_pool.
+ */
+ pool = pagetable->kgsl_pool;
+ if (pool == NULL && memdesc->gpuaddr == 0) {
+ KGSL_CORE_ERR(
+ "No address for global mapping into pt %d\n",
+ pagetable->name);
+ return -EINVAL;
+ }
+ }
+ }
+ if (pool) {
+ memdesc->gpuaddr = gen_pool_alloc_aligned(pool, size,
+ page_align);
+ if (memdesc->gpuaddr == 0) {
+ KGSL_CORE_ERR("gen_pool_alloc(%d) failed, pool: %s\n",
+ size,
+ (pool == pagetable->kgsl_pool) ?
+ "kgsl_pool" : "general_pool");
+ KGSL_CORE_ERR(" [%d] allocated=%d, entries=%d\n",
+ pagetable->name,
+ pagetable->stats.mapped,
+ pagetable->stats.entries);
+ return -ENOMEM;
+ }
}
if (KGSL_MMU_TYPE_IOMMU != kgsl_mmu_get_mmutype())
@@ -676,7 +670,8 @@
err_free_gpuaddr:
spin_unlock(&pagetable->lock);
- gen_pool_free(pool, memdesc->gpuaddr, size);
+ if (pool)
+ gen_pool_free(pool, memdesc->gpuaddr, size);
memdesc->gpuaddr = 0;
return ret;
}
@@ -711,14 +706,20 @@
spin_unlock(&pagetable->lock);
- pool = _get_pool(pagetable, memdesc->priv);
- gen_pool_free(pool, memdesc->gpuaddr, size);
+ pool = pagetable->pool;
+
+ if (KGSL_MMU_TYPE_IOMMU == kgsl_mmu_get_mmutype()
+ && kgsl_memdesc_is_global(memdesc)) {
+ pool = pagetable->kgsl_pool;
+ }
+ if (pool)
+ gen_pool_free(pool, memdesc->gpuaddr, size);
/*
* Don't clear the gpuaddr on global mappings because they
* may be in use by other pagetables
*/
- if (!(memdesc->priv & KGSL_MEMDESC_GLOBAL))
+ if (!kgsl_memdesc_is_global(memdesc))
memdesc->gpuaddr = 0;
return 0;
}
diff --git a/drivers/gpu/msm/kgsl_mmu.h b/drivers/gpu/msm/kgsl_mmu.h
index b8b9149..b8eff60 100644
--- a/drivers/gpu/msm/kgsl_mmu.h
+++ b/drivers/gpu/msm/kgsl_mmu.h
@@ -13,13 +13,14 @@
#ifndef __KGSL_MMU_H
#define __KGSL_MMU_H
+#include <mach/iommu.h>
+
/*
- * These defines control the split between ttbr1 and ttbr0 pagetables of IOMMU
- * and what ranges of memory we map to them
+ * These defines control the address range for allocations that
+ * are mapped into all pagetables.
*/
#define KGSL_IOMMU_GLOBAL_MEM_BASE 0xC0000000
#define KGSL_IOMMU_GLOBAL_MEM_SIZE SZ_4M
-#define KGSL_IOMMU_TTBR1_SPLIT 2
#define KGSL_MMU_ALIGN_SHIFT 13
#define KGSL_MMU_ALIGN_MASK (~((1 << KGSL_MMU_ALIGN_SHIFT) - 1))
@@ -148,6 +149,10 @@
unsigned int (*mmu_get_pt_base_addr)
(struct kgsl_mmu *mmu,
struct kgsl_pagetable *pt);
+ int (*mmu_setup_pt) (struct kgsl_mmu *mmu,
+ struct kgsl_pagetable *pt);
+ void (*mmu_cleanup_pt) (struct kgsl_mmu *mmu,
+ struct kgsl_pagetable *pt);
};
struct kgsl_mmu_pt_ops {
@@ -209,7 +214,6 @@
int kgsl_mmu_enabled(void);
void kgsl_mmu_set_mmutype(char *mmutype);
enum kgsl_mmutype kgsl_mmu_get_mmutype(void);
-unsigned int kgsl_mmu_get_ptsize(void);
int kgsl_mmu_gpuaddr_in_range(unsigned int gpuaddr);
/*
@@ -321,4 +325,58 @@
return 0;
}
+/*
+ * kgsl_mmu_is_perprocess() - Runtime check for per-process
+ * pagetables.
+ *
+ * Returns non-zero if per-process pagetables are enabled,
+ * 0 if not.
+ */
+#ifdef CONFIG_KGSL_PER_PROCESS_PAGE_TABLE
+static inline int kgsl_mmu_is_perprocess(void)
+{
+
+ /* We presently do not support per-process for IOMMU-v2 */
+ return (kgsl_mmu_get_mmutype() != KGSL_MMU_TYPE_IOMMU)
+ || msm_soc_version_supports_iommu_v1();
+}
+#else
+static inline int kgsl_mmu_is_perprocess(void)
+{
+ return 0;
+}
+#endif
+
+/*
+ * kgsl_mmu_base_addr() - Get gpu virtual address base.
+ *
+ * Returns the start address of the gpu
+ * virtual address space.
+ */
+static inline unsigned int kgsl_mmu_get_base_addr(void)
+{
+ return KGSL_PAGETABLE_BASE;
+}
+
+/*
+ * kgsl_mmu_get_ptsize() - Get gpu pagetable size
+ *
+ * Returns the usable size of the gpu address space.
+ */
+static inline unsigned int kgsl_mmu_get_ptsize(void)
+{
+ /*
+ * For IOMMU, we could do up to 4G virtual range if we wanted to, but
+ * it makes more sense to return a smaller range and leave the rest of
+ * the virtual range for future improvements
+ */
+ enum kgsl_mmutype mmu_type = kgsl_mmu_get_mmutype();
+
+ if (KGSL_MMU_TYPE_GPU == mmu_type)
+ return CONFIG_MSM_KGSL_PAGE_TABLE_SIZE;
+ else if (KGSL_MMU_TYPE_IOMMU == mmu_type)
+ return SZ_2G;
+ return 0;
+}
+
#endif /* __KGSL_MMU_H */
diff --git a/drivers/gpu/msm/kgsl_pwrscale_msm.c b/drivers/gpu/msm/kgsl_pwrscale_msm.c
index b302bee..c680d57 100644
--- a/drivers/gpu/msm/kgsl_pwrscale_msm.c
+++ b/drivers/gpu/msm/kgsl_pwrscale_msm.c
@@ -23,6 +23,8 @@
struct kgsl_device *device;
int enabled;
unsigned int cur_freq;
+ unsigned int req_level;
+ int floor_level;
struct msm_dcvs_core_info *core_info;
int gpu_busy;
int dcvs_core_id;
@@ -69,7 +71,39 @@
return 0;
mutex_lock(&device->mutex);
- kgsl_pwrctrl_pwrlevel_change(device, i);
+ priv->req_level = i;
+ if (priv->req_level <= priv->floor_level) {
+ kgsl_pwrctrl_pwrlevel_change(device, priv->req_level);
+ priv->cur_freq = pwr->pwrlevels[pwr->active_pwrlevel].gpu_freq;
+ }
+ mutex_unlock(&device->mutex);
+
+ /* return current frequency in kHz */
+ return priv->cur_freq / 1000;
+}
+
+static int msm_set_min_freq(int core_num, unsigned int freq)
+{
+ int i, delta = 5000000;
+ struct msm_priv *priv = the_msm_priv;
+ struct kgsl_device *device = priv->device;
+ struct kgsl_pwrctrl *pwr = &device->pwrctrl;
+
+ /* msm_dcvs manager uses frequencies in kHz */
+ freq *= 1000;
+ for (i = 0; i < pwr->num_pwrlevels; i++)
+ if (abs(pwr->pwrlevels[i].gpu_freq - freq) < delta)
+ break;
+ if (i == pwr->num_pwrlevels)
+ return 0;
+
+ mutex_lock(&device->mutex);
+ priv->floor_level = i;
+ if (priv->floor_level <= priv->req_level)
+ kgsl_pwrctrl_pwrlevel_change(device, priv->floor_level);
+ else if (priv->floor_level > priv->req_level)
+ kgsl_pwrctrl_pwrlevel_change(device, priv->req_level);
+
priv->cur_freq = pwr->pwrlevels[pwr->active_pwrlevel].gpu_freq;
mutex_unlock(&device->mutex);
@@ -170,6 +204,7 @@
priv->core_info = pdata->core_info;
tbl = priv->core_info->freq_tbl;
+ priv->floor_level = pwr->num_pwrlevels - 1;
/* Fill in frequency table from low to high, reversing order. */
low_level = pwr->num_pwrlevels - KGSL_PWRLEVEL_LAST_OFFSET;
for (i = 0; i <= low_level; i++)
@@ -180,6 +215,7 @@
0,
priv->core_info,
msm_set_freq, msm_get_freq, msm_idle_enable,
+ msm_set_min_freq,
priv->core_info->sensors[0]);
if (priv->dcvs_core_id < 0) {
KGSL_PWR_ERR(device, "msm_dcvs_register_core failed");
diff --git a/drivers/gpu/msm/kgsl_sharedmem.h b/drivers/gpu/msm/kgsl_sharedmem.h
index 92a6f27..53e88be 100644
--- a/drivers/gpu/msm/kgsl_sharedmem.h
+++ b/drivers/gpu/msm/kgsl_sharedmem.h
@@ -157,6 +157,17 @@
return 0;
}
+/*
+ * kgsl_memdesc_is_global - is this a globally mapped buffer?
+ * @memdesc: the memdesc
+ *
+ * Returns nonzero if this is a global mapping, 0 otherwise
+ */
+static inline int kgsl_memdesc_is_global(const struct kgsl_memdesc *memdesc)
+{
+ return (memdesc->priv & KGSL_MEMDESC_GLOBAL) != 0;
+}
+
static inline int
kgsl_allocate(struct kgsl_memdesc *memdesc,
struct kgsl_pagetable *pagetable, size_t size)
diff --git a/drivers/gpu/msm/z180.c b/drivers/gpu/msm/z180.c
index 712bc60..258dcfa 100644
--- a/drivers/gpu/msm/z180.c
+++ b/drivers/gpu/msm/z180.c
@@ -260,6 +260,13 @@
GSL_PT_PAGE_RV);
if (result)
goto error_unmap_memstore;
+ /*
+ * Set the mpu end to the last "normal" global memory we use.
+ * For the IOMMU, this will be used to restrict access to the
+ * mapped registers.
+ */
+ device->mh.mpu_range = z180_dev->ringbuffer.cmdbufdesc.gpuaddr +
+ z180_dev->ringbuffer.cmdbufdesc.size;
return result;
error_unmap_dummy:
diff --git a/drivers/media/dvb/dvb-core/demux.h b/drivers/media/dvb/dvb-core/demux.h
index 7190af8..b7ace53 100644
--- a/drivers/media/dvb/dvb-core/demux.h
+++ b/drivers/media/dvb/dvb-core/demux.h
@@ -336,6 +336,8 @@
void* priv; /* Pointer to private data of the API client */
struct data_buffer dvr_input; /* DVR input buffer */
+ struct dentry *debugfs_demux_dir; /* debugfs dir */
+
int (*open) (struct dmx_demux* demux);
int (*close) (struct dmx_demux* demux);
int (*write) (struct dmx_demux *demux, const char *buf, size_t count);
diff --git a/drivers/media/dvb/dvb-core/dmxdev.c b/drivers/media/dvb/dvb-core/dmxdev.c
index 625eb78..71642a5 100644
--- a/drivers/media/dvb/dvb-core/dmxdev.c
+++ b/drivers/media/dvb/dvb-core/dmxdev.c
@@ -32,6 +32,8 @@
#include <linux/wait.h>
#include <linux/mm.h>
#include <asm/uaccess.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
#include "dmxdev.h"
static int debug;
@@ -3164,6 +3166,73 @@
.fops = &dvb_dvr_fops
};
+
+/**
+ * debugfs service to print active filters information.
+ */
+static int dvb_dmxdev_dbgfs_print(struct seq_file *s, void *p)
+{
+ int i;
+ struct dmxdev *dmxdev = s->private;
+ struct dmxdev_filter *filter;
+ int active_count = 0;
+ struct dmx_buffer_status buffer_status;
+ const char *pes_feeds[] = {"DEC", "PES", "DVR", "REC"};
+
+ if (!dmxdev)
+ return 0;
+
+ for (i = 0; i < dmxdev->filternum; i++) {
+ filter = &dmxdev->filter[i];
+ if (filter->state >= DMXDEV_STATE_GO) {
+ active_count++;
+
+ seq_printf(s, "filter_%02d - ", i);
+
+ if (filter->type == DMXDEV_TYPE_SEC) {
+ seq_printf(s, "type: SEC, ");
+ seq_printf(s, "PID %04d ",
+ filter->params.sec.pid);
+ } else {
+ seq_printf(s, "type: %s, ",
+ pes_feeds[filter->params.pes.output]);
+ seq_printf(s, "PID: %04d ",
+ filter->params.pes.pid);
+ }
+
+ if (0 == dvb_dmxdev_get_buffer_status(
+ filter, &buffer_status)) {
+ seq_printf(s, "buffer size: %08d, ",
+ buffer_status.size);
+ seq_printf(s, "buffer fullness: %08d\n",
+ buffer_status.fullness);
+ seq_printf(s, "buffer error: %08d\n",
+ buffer_status.error);
+ }
+ }
+ }
+
+ if (!active_count)
+ seq_printf(s, "No active filters\n");
+
+ seq_printf(s, "\n");
+
+ return 0;
+}
+
+static int dvb_dmxdev_dbgfs_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, dvb_dmxdev_dbgfs_print, inode->i_private);
+}
+
+static const struct file_operations dbgfs_filters_fops = {
+ .open = dvb_dmxdev_dbgfs_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
int dvb_dmxdev_init(struct dmxdev *dmxdev, struct dvb_adapter *dvb_adapter)
{
int i;
@@ -3206,6 +3275,11 @@
INIT_WORK(&dmxdev->dvr_input_work,
dvr_input_work_func);
+ if (dmxdev->demux->debugfs_demux_dir)
+ debugfs_create_file("filters", S_IRUGO,
+ dmxdev->demux->debugfs_demux_dir, dmxdev,
+ &dbgfs_filters_fops);
+
return 0;
}
diff --git a/drivers/media/dvb/dvb-core/dvb_demux.c b/drivers/media/dvb/dvb-core/dvb_demux.c
index de7b28d..2c5294f 100644
--- a/drivers/media/dvb/dvb-core/dvb_demux.c
+++ b/drivers/media/dvb/dvb-core/dvb_demux.c
@@ -1732,19 +1732,20 @@
"demux%d",
dvb_demux_index++);
- dvbdemux->debugfs_demux_dir = debugfs_create_dir(dvbdemux->alias, NULL);
+ dvbdemux->dmx.debugfs_demux_dir =
+ debugfs_create_dir(dvbdemux->alias, NULL);
- if (dvbdemux->debugfs_demux_dir != NULL) {
+ if (dvbdemux->dmx.debugfs_demux_dir != NULL) {
debugfs_create_u32(
"total_processing_time",
S_IRUGO|S_IWUGO,
- dvbdemux->debugfs_demux_dir,
+ dvbdemux->dmx.debugfs_demux_dir,
&dvbdemux->total_process_time);
debugfs_create_u32(
"total_crc_time",
S_IRUGO|S_IWUGO,
- dvbdemux->debugfs_demux_dir,
+ dvbdemux->dmx.debugfs_demux_dir,
&dvbdemux->total_crc_time);
}
@@ -1817,9 +1818,10 @@
void dvb_dmx_release(struct dvb_demux *dvbdemux)
{
- if (dvbdemux->debugfs_demux_dir != NULL)
- debugfs_remove_recursive(dvbdemux->debugfs_demux_dir);
+ if (dvbdemux->dmx.debugfs_demux_dir != NULL)
+ debugfs_remove_recursive(dvbdemux->dmx.debugfs_demux_dir);
+ dvb_demux_index--;
vfree(dvbdemux->cnt_storage);
vfree(dvbdemux->filter);
vfree(dvbdemux->feed);
diff --git a/drivers/media/dvb/dvb-core/dvb_demux.h b/drivers/media/dvb/dvb-core/dvb_demux.h
index 5a32363..706cd0c 100644
--- a/drivers/media/dvb/dvb-core/dvb_demux.h
+++ b/drivers/media/dvb/dvb-core/dvb_demux.h
@@ -174,7 +174,6 @@
u32 total_process_time;
u32 total_crc_time;
- struct dentry *debugfs_demux_dir;
};
int dvb_dmx_init(struct dvb_demux *dvbdemux);
diff --git a/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_common.c b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_common.c
index 18c3767..51d66cd 100644
--- a/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_common.c
+++ b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_common.c
@@ -477,29 +477,29 @@
mpq_demux->hw_notification_size = 0;
mpq_demux->decoder_tsp_drop_count = 0;
- if (mpq_demux->demux.debugfs_demux_dir != NULL) {
+ if (mpq_demux->demux.dmx.debugfs_demux_dir != NULL) {
debugfs_create_u32(
"hw_notification_rate",
S_IRUGO|S_IWUGO,
- mpq_demux->demux.debugfs_demux_dir,
+ mpq_demux->demux.dmx.debugfs_demux_dir,
&mpq_demux->hw_notification_rate);
debugfs_create_u32(
"hw_notification_count",
S_IRUGO|S_IWUGO,
- mpq_demux->demux.debugfs_demux_dir,
+ mpq_demux->demux.dmx.debugfs_demux_dir,
&mpq_demux->hw_notification_count);
debugfs_create_u32(
"hw_notification_size",
S_IRUGO|S_IWUGO,
- mpq_demux->demux.debugfs_demux_dir,
+ mpq_demux->demux.dmx.debugfs_demux_dir,
&mpq_demux->hw_notification_size);
debugfs_create_u32(
"decoder_tsp_drop_count",
S_IRUGO|S_IWUGO,
- mpq_demux->demux.debugfs_demux_dir,
+ mpq_demux->demux.dmx.debugfs_demux_dir,
&mpq_demux->decoder_tsp_drop_count);
}
}
diff --git a/drivers/media/video/msm/io/msm_io_8960.c b/drivers/media/video/msm/io/msm_io_8960.c
index 808cc32..1b56578 100644
--- a/drivers/media/video/msm/io/msm_io_8960.c
+++ b/drivers/media/video/msm/io/msm_io_8960.c
@@ -103,6 +103,14 @@
} else
CDBG("%s: Bus Client NOT Registered!!!\n", __func__);
break;
+ case S_ADV_VIDEO:
+ if (bus_perf_client) {
+ rc = msm_bus_scale_client_update_request(
+ bus_perf_client, 7);
+ CDBG("%s: S_ADV_VIDEO rc = %d\n", __func__, rc);
+ } else
+ CDBG("%s: Bus Client NOT Registered!!!\n", __func__);
+ break;
case S_DEFAULT:
break;
default:
diff --git a/drivers/media/video/msm/vfe/msm_vfe32.c b/drivers/media/video/msm/vfe/msm_vfe32.c
index db0db36..a382d53 100644
--- a/drivers/media/video/msm/vfe/msm_vfe32.c
+++ b/drivers/media/video/msm/vfe/msm_vfe32.c
@@ -1464,8 +1464,10 @@
CDBG("VFE opertaion mode = 0x%x, output mode = 0x%x\n",
vfe32_ctrl->share_ctrl->operation_mode,
vfe32_ctrl->share_ctrl->outpath.output_mode);
- msm_camera_io_w_mb(1, vfe32_ctrl->share_ctrl->vfebase +
- VFE_CAMIF_COMMAND);
+ msm_camera_io_w_mb(1, vfe32_ctrl->share_ctrl->vfebase +
+ VFE_CAMIF_COMMAND);
+ msm_camera_io_w_mb(VFE_AXI_CFG_MASK,
+ vfe32_ctrl->share_ctrl->vfebase + VFE_AXI_CFG);
}
static int vfe32_start_recording(
@@ -5789,7 +5791,7 @@
void axi_start(struct msm_cam_media_controller *pmctl,
struct axi_ctrl_t *axi_ctrl, struct msm_camera_vfe_params_t vfe_params)
{
- int rc = 0;
+ int rc = 0, bus_vector_idx = 0;
uint32_t reg_update = 0;
uint32_t vfe_mode =
(axi_ctrl->share_ctrl->current_mode &
@@ -5811,9 +5813,22 @@
pmctl->sdata->pdata->cam_bus_scale_table, S_CAPTURE);
break;
case AXI_CMD_RECORD:
+ if (cpu_is_msm8930() || cpu_is_msm8930aa() ||
+ cpu_is_msm8930ab()) {
+ if (axi_ctrl->share_ctrl->current_mode &
+ VFE_OUTPUTS_PREVIEW_AND_VIDEO
+ || axi_ctrl->share_ctrl->current_mode &
+ VFE_OUTPUTS_VIDEO_AND_PREVIEW)
+ bus_vector_idx = S_VIDEO;
+ else
+ bus_vector_idx = S_ADV_VIDEO;
+ } else {
+ bus_vector_idx = S_VIDEO;
+ }
if (!axi_ctrl->share_ctrl->dual_enabled)
msm_camio_bus_scale_cfg(
- pmctl->sdata->pdata->cam_bus_scale_table, S_VIDEO);
+ pmctl->sdata->pdata->cam_bus_scale_table,
+ bus_vector_idx);
return;
case AXI_CMD_ZSL:
if (!axi_ctrl->share_ctrl->dual_enabled)
@@ -6046,6 +6061,8 @@
uint32_t vfe_mode =
axi_ctrl->share_ctrl->current_mode & ~(VFE_OUTPUTS_RDI0|
VFE_OUTPUTS_RDI1);
+ int bus_vector_idx = 0;
+
switch (vfe_params.cmd_type) {
case AXI_CMD_PREVIEW:
case AXI_CMD_CAPTURE:
@@ -6059,9 +6076,17 @@
pmctl->sdata->pdata->cam_bus_scale_table, S_PREVIEW);
return;
case AXI_CMD_LIVESHOT:
- if (!axi_ctrl->share_ctrl->dual_enabled)
+ if (!axi_ctrl->share_ctrl->dual_enabled) {
+ bus_vector_idx = S_VIDEO;
+
+ if (cpu_is_msm8930() || cpu_is_msm8930aa() ||
+ cpu_is_msm8930ab())
+ bus_vector_idx = S_ADV_VIDEO;
+
msm_camio_bus_scale_cfg(
- pmctl->sdata->pdata->cam_bus_scale_table, S_VIDEO);
+ pmctl->sdata->pdata->cam_bus_scale_table,
+ bus_vector_idx);
+ }
return;
default:
return;
diff --git a/drivers/media/video/msm/vfe/msm_vfe32.h b/drivers/media/video/msm/vfe/msm_vfe32.h
index f985221..169c34e 100644
--- a/drivers/media/video/msm/vfe/msm_vfe32.h
+++ b/drivers/media/video/msm/vfe/msm_vfe32.h
@@ -913,6 +913,7 @@
#define VFE_DMI_ADDR 0x0000059C
#define VFE_DMI_DATA_HI 0x000005A0
#define VFE_DMI_DATA_LO 0x000005A4
+#define VFE_AXI_CFG 0x00000600
#define VFE_BUS_IO_FORMAT_CFG 0x000006F8
#define VFE_PIXEL_IF_CFG 0x000006FC
#define VFE_RDI0_CFG 0x00000734
@@ -923,6 +924,8 @@
#define VFE33_DMI_DATA_HI 0x000005A0
#define VFE33_DMI_DATA_LO 0x000005A4
+#define VFE_AXI_CFG_MASK 0xFFFFFFFF
+
#define VFE32_OUTPUT_MODE_PT BIT(0)
#define VFE32_OUTPUT_MODE_S BIT(1)
#define VFE32_OUTPUT_MODE_V BIT(2)
diff --git a/drivers/media/video/msm_vidc/vidc_hal_interrupt_handler.c b/drivers/media/video/msm_vidc/vidc_hal_interrupt_handler.c
index ba599ec..200f5d3 100644
--- a/drivers/media/video/msm_vidc/vidc_hal_interrupt_handler.c
+++ b/drivers/media/video/msm_vidc/vidc_hal_interrupt_handler.c
@@ -315,16 +315,22 @@
{
struct hfi_buffer_requirements *hfi_buf_req;
u32 req_bytes;
- enum vidc_status rc = VIDC_ERR_NONE;
dprintk(VIDC_DBG, "Entered ");
+ if (!prop) {
+ dprintk(VIDC_ERR,
+ "hal_process_sess_get_prop_buf_req:bad_prop: %p",
+ prop);
+ return;
+ }
req_bytes = prop->size - sizeof(
struct hfi_msg_session_property_info_packet);
- if (req_bytes == 0 || (req_bytes % sizeof(
- struct hfi_buffer_requirements))) {
+ if (!req_bytes || (req_bytes % sizeof(
+ struct hfi_buffer_requirements)) ||
+ (!prop->rg_property_data[1])) {
dprintk(VIDC_ERR,
- "hal_process_sess_get_prop_buf_req:bad_pkt_size: %d",
+ "hal_process_sess_get_prop_buf_req:bad_pkt: %d",
req_bytes);
return;
}
@@ -332,15 +338,14 @@
hfi_buf_req = (struct hfi_buffer_requirements *)
&prop->rg_property_data[1];
- while (req_bytes != 0) {
- if ((hfi_buf_req->buffer_count_min > hfi_buf_req->
- buffer_count_actual)
- || (hfi_buf_req->buffer_alignment == 0)
- || (hfi_buf_req->buffer_size == 0)) {
- dprintk(VIDC_ERR, "hal_process_sess_get_prop_buf_req:"
- "bad_buf_req");
- rc = VIDC_ERR_FAIL;
- }
+ while (req_bytes) {
+ if ((hfi_buf_req->buffer_size) &&
+ ((hfi_buf_req->buffer_count_min > hfi_buf_req->
+ buffer_count_actual)))
+ dprintk(VIDC_WARN,
+ "hal_process_sess_get_prop_buf_req:"
+ "bad_buf_req");
+
dprintk(VIDC_DBG, "got buffer requirements for: %d",
hfi_buf_req->buffer_type);
switch (hfi_buf_req->buffer_type) {
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 6ab3a66..93a3237 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -664,6 +664,17 @@
This adds support for connecting devices like mouse in HSIC
Host mode.
+config TI_DRV2667
+ tristate "TI's DRV2667 haptic controller support"
+ depends on I2C
+ help
+ The DRV2667 is a piezo haptic controller chip. It can drive
+ piezo haptics either in digital mode or analog mode. This chip
+ can be used in variety of devices to provide haptic support.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ti_drv2667.
+
source "drivers/misc/c2port/Kconfig"
source "drivers/misc/eeprom/Kconfig"
source "drivers/misc/cb710/Kconfig"
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index e92e119..8395ef4 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -72,3 +72,4 @@
obj-$(CONFIG_PMIC8058_XOADC) += pmic8058-xoadc.o
obj-$(CONFIG_QSEECOM) += qseecom.o
obj-$(CONFIG_QFP_FUSE) += qfp_fuse.o
+obj-$(CONFIG_TI_DRV2667) += ti_drv2667.o
diff --git a/drivers/misc/qseecom.c b/drivers/misc/qseecom.c
index 1972845..c9c4e70 100644
--- a/drivers/misc/qseecom.c
+++ b/drivers/misc/qseecom.c
@@ -1339,12 +1339,10 @@
/* Populate the remaining parameters */
load_req.qsee_cmd_id = QSEOS_APP_START_COMMAND;
memcpy(load_req.app_name, appname, MAX_APP_NAME_SIZE);
- mutex_lock(&app_access_lock);
ret = qsee_vote_for_clock(CLK_SFPB);
if (ret) {
kzfree(img_data);
pr_warning("Unable to vote for SFPB clock");
- mutex_unlock(&app_access_lock);
return -EIO;
}
@@ -1356,7 +1354,6 @@
if (ret) {
pr_err("scm_call to load failed : ret %d\n", ret);
qsee_disable_clock_vote(CLK_SFPB);
- mutex_unlock(&app_access_lock);
return -EIO;
}
@@ -1380,7 +1377,6 @@
break;
}
qsee_disable_clock_vote(CLK_SFPB);
- mutex_unlock(&app_access_lock);
return ret;
}
@@ -1695,6 +1691,21 @@
}
EXPORT_SYMBOL(qseecom_send_command);
+int qseecom_set_bandwidth(struct qseecom_handle *handle, bool high)
+{
+ if ((handle == NULL) || (handle->dev == NULL)) {
+ pr_err("No valid kernel client\n");
+ return -EINVAL;
+ }
+ if (high)
+ return qsee_vote_for_clock(CLK_DFAB);
+ else {
+ qsee_disable_clock_vote(CLK_DFAB);
+ return 0;
+ }
+}
+EXPORT_SYMBOL(qseecom_set_bandwidth);
+
static int qseecom_send_resp(void)
{
qseecom.send_resp_flag = 1;
diff --git a/drivers/misc/qseecom_kernel.h b/drivers/misc/qseecom_kernel.h
index bfa5709..0c93ef2 100644
--- a/drivers/misc/qseecom_kernel.h
+++ b/drivers/misc/qseecom_kernel.h
@@ -31,6 +31,6 @@
int qseecom_shutdown_app(struct qseecom_handle **handle);
int qseecom_send_command(struct qseecom_handle *handle, void *send_buf,
uint32_t sbuf_len, void *resp_buf, uint32_t rbuf_len);
-
+int qseecom_set_bandwidth(struct qseecom_handle *handle, bool high);
#endif /* __QSEECOM_KERNEL_H_ */
diff --git a/drivers/misc/ti_drv2667.c b/drivers/misc/ti_drv2667.c
new file mode 100644
index 0000000..554799c
--- /dev/null
+++ b/drivers/misc/ti_drv2667.c
@@ -0,0 +1,679 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/pm.h>
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/regulator/consumer.h>
+#include <linux/i2c/ti_drv2667.h>
+#include "../staging/android/timed_output.h"
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+#include <linux/earlysuspend.h>
+#define DRV2667_SUS_LEVEL 1
+#endif
+
+#define DRV2667_STATUS_REG 0x00
+#define DRV2667_CNTL1_REG 0x01
+#define DRV2667_CNTL2_REG 0x02
+#define DRV2667_WAV_SEQ3_REG 0x03
+#define DRV2667_FIFO_REG 0x0B
+#define DRV2667_PAGE_REG 0xFF
+
+#define DRV2667_STANDBY_MASK 0xBF
+#define DRV2667_INPUT_MUX_MASK 0x04
+#define DRV2667_GAIN_MASK 0xFC
+#define DRV2667_GAIN_SHIFT 0
+#define DRV2667_TIMEOUT_MASK 0xF3
+#define DRV2667_TIMEOUT_SHIFT 2
+#define DRV2667_GO_MASK 0x01
+#define DRV2667_FIFO_SIZE 100
+#define DRV2667_VIB_START_VAL 0x7F
+#define DRV2667_REG_PAGE_ID 0x00
+#define DRV2667_FIFO_CHUNK_MS 10
+#define DRV2667_BYTES_PER_MS 8
+
+#define DRV2667_WAV_SEQ_ID_IDX 1
+#define DRV2667_WAV_SEQ_REP_IDX 6
+#define DRV2667_WAV_SEQ_FREQ_IDX 8
+#define DRV2667_WAV_SEQ_FREQ_MIN 8
+#define DRV2667_WAV_SEQ_DUR_IDX 9
+
+#define DRV2667_MIN_IDLE_TIMEOUT_MS 5
+#define DRV2667_MAX_IDLE_TIMEOUT_MS 20
+
+#define DRV2667_VTG_MIN_UV 3000000
+#define DRV2667_VTG_MAX_UV 5500000
+#define DRV2667_VTG_CURR_UA 24000
+#define DRV2667_I2C_VTG_MIN_UV 1800000
+#define DRV2667_I2C_VTG_MAX_UV 1800000
+#define DRV2667_I2C_CURR_UA 9630
+
+/* supports 3 modes in digital - fifo, ram and wave */
+enum drv2667_modes {
+ FIFO_MODE = 0,
+ RAM_SEQ_MODE,
+ WAV_SEQ_MODE,
+ ANALOG_MODE,
+};
+
+struct drv2667_data {
+ struct i2c_client *client;
+ struct timed_output_dev dev;
+ struct hrtimer timer;
+ struct work_struct work;
+ struct mutex lock;
+ struct regulator *vdd;
+ struct regulator *vdd_i2c;
+ u32 max_runtime_ms;
+ u32 runtime_left;
+ u8 buf[DRV2667_FIFO_SIZE + 1];
+ u8 cntl2_val;
+ enum drv2667_modes mode;
+ u32 time_chunk_ms;
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ struct early_suspend es;
+#endif
+};
+
+static int drv2667_read_reg(struct i2c_client *client, u32 reg)
+{
+ int rc;
+
+ rc = i2c_smbus_read_byte_data(client, reg);
+ if (rc < 0)
+ dev_err(&client->dev, "i2c reg read for 0x%x failed\n", reg);
+ return rc;
+}
+
+static int drv2667_write_reg(struct i2c_client *client, u32 reg, u8 val)
+{
+ int rc;
+
+ rc = i2c_smbus_write_byte_data(client, reg, val);
+ if (rc < 0)
+ dev_err(&client->dev, "i2c reg write for 0x%xfailed\n", reg);
+
+ return rc;
+}
+
+static void drv2667_dump_regs(struct drv2667_data *data, char *label)
+{
+ dev_dbg(&data->client->dev,
+ "%s: reg0x00 = 0x%x, reg0x01 = 0x%x reg0x02 = 0x%x", label,
+ drv2667_read_reg(data->client, DRV2667_STATUS_REG),
+ drv2667_read_reg(data->client, DRV2667_CNTL1_REG),
+ drv2667_read_reg(data->client, DRV2667_CNTL2_REG));
+}
+
+static void drv2667_worker(struct work_struct *work)
+{
+ struct drv2667_data *data;
+ int rc = 0;
+ u8 val;
+
+ data = container_of(work, struct drv2667_data, work);
+
+ if (data->mode == WAV_SEQ_MODE) {
+ if (data->runtime_left)
+ val = data->cntl2_val | DRV2667_GO_MASK;
+ else
+ val = data->cntl2_val & ~DRV2667_GO_MASK;
+ rc = drv2667_write_reg(data->client, DRV2667_CNTL2_REG, val);
+ } else if (data->mode == FIFO_MODE) {
+ /* data is played at 8khz */
+ if (data->runtime_left < data->time_chunk_ms)
+ val = data->runtime_left * DRV2667_BYTES_PER_MS;
+ else
+ val = data->time_chunk_ms * DRV2667_BYTES_PER_MS;
+
+ rc = i2c_master_send(data->client, data->buf, val + 1);
+ }
+
+ if (rc < 0)
+ dev_err(&data->client->dev, "i2c send message failed\n");
+}
+
+static void drv2667_enable(struct timed_output_dev *dev, int runtime)
+{
+ struct drv2667_data *data = container_of(dev, struct drv2667_data, dev);
+ unsigned long time_ms;
+
+ if (runtime > data->max_runtime_ms) {
+ dev_dbg(&data->client->dev, "Invalid runtime\n");
+ runtime = data->max_runtime_ms;
+ }
+
+ mutex_lock(&data->lock);
+ hrtimer_cancel(&data->timer);
+ data->runtime_left = runtime;
+ if (data->runtime_left < data->time_chunk_ms)
+ time_ms = runtime * NSEC_PER_MSEC;
+ else
+ time_ms = data->time_chunk_ms * NSEC_PER_MSEC;
+ hrtimer_start(&data->timer, ktime_set(0, time_ms), HRTIMER_MODE_REL);
+ schedule_work(&data->work);
+ mutex_unlock(&data->lock);
+}
+
+static int drv2667_get_time(struct timed_output_dev *dev)
+{
+ struct drv2667_data *data = container_of(dev, struct drv2667_data, dev);
+
+ if (hrtimer_active(&data->timer))
+ return data->runtime_left +
+ ktime_to_ms(hrtimer_get_remaining(&data->timer));
+ return 0;
+}
+
+static enum hrtimer_restart drv2667_timer(struct hrtimer *timer)
+{
+ struct drv2667_data *data;
+ int time_ms;
+
+ data = container_of(timer, struct drv2667_data, timer);
+ if (data->runtime_left <= data->time_chunk_ms) {
+ data->runtime_left = 0;
+ schedule_work(&data->work);
+ return HRTIMER_NORESTART;
+ }
+
+ data->runtime_left -= data->time_chunk_ms;
+ if (data->runtime_left < data->time_chunk_ms)
+ time_ms = data->runtime_left * NSEC_PER_MSEC;
+ else
+ time_ms = data->time_chunk_ms * NSEC_PER_MSEC;
+
+ hrtimer_forward_now(&data->timer, ktime_set(0, time_ms));
+ schedule_work(&data->work);
+ return HRTIMER_RESTART;
+}
+
+static int drv2667_vreg_config(struct drv2667_data *data, bool on)
+{
+ int rc = 0;
+
+ if (!on)
+ goto deconfig_vreg;
+
+ data->vdd = regulator_get(&data->client->dev, "vdd");
+ if (IS_ERR(data->vdd)) {
+ rc = PTR_ERR(data->vdd);
+ dev_err(&data->client->dev, "unable to request vdd\n");
+ return rc;
+ }
+
+ if (regulator_count_voltages(data->vdd) > 0) {
+ rc = regulator_set_voltage(data->vdd,
+ DRV2667_VTG_MIN_UV, DRV2667_VTG_MAX_UV);
+ if (rc < 0) {
+ dev_err(&data->client->dev,
+ "vdd set voltage failed(%d)\n", rc);
+ goto put_vdd;
+ }
+ }
+
+ data->vdd_i2c = regulator_get(&data->client->dev, "vdd-i2c");
+ if (IS_ERR(data->vdd_i2c)) {
+ rc = PTR_ERR(data->vdd_i2c);
+ dev_err(&data->client->dev, "unable to request vdd for i2c\n");
+ goto reset_vdd_volt;
+ }
+
+ if (regulator_count_voltages(data->vdd_i2c) > 0) {
+ rc = regulator_set_voltage(data->vdd_i2c,
+ DRV2667_I2C_VTG_MIN_UV, DRV2667_I2C_VTG_MAX_UV);
+ if (rc < 0) {
+ dev_err(&data->client->dev,
+ "vdd_i2c set voltage failed(%d)\n", rc);
+ goto put_vdd_i2c;
+ }
+ }
+
+ return rc;
+
+deconfig_vreg:
+ if (regulator_count_voltages(data->vdd_i2c) > 0)
+ regulator_set_voltage(data->vdd_i2c, 0, DRV2667_I2C_VTG_MAX_UV);
+put_vdd_i2c:
+ regulator_put(data->vdd_i2c);
+reset_vdd_volt:
+ if (regulator_count_voltages(data->vdd) > 0)
+ regulator_set_voltage(data->vdd, 0, DRV2667_VTG_MAX_UV);
+put_vdd:
+ regulator_put(data->vdd);
+ return rc;
+}
+
+static int reg_set_optimum_mode_check(struct regulator *reg, int load_uA)
+{
+ return (regulator_count_voltages(reg) > 0) ?
+ regulator_set_optimum_mode(reg, load_uA) : 0;
+}
+
+
+static int drv2667_vreg_on(struct drv2667_data *data, bool on)
+{
+ int rc = 0;
+
+ if (!on)
+ goto vreg_off;
+
+ rc = reg_set_optimum_mode_check(data->vdd, DRV2667_VTG_CURR_UA);
+ if (rc < 0) {
+ dev_err(&data->client->dev,
+ "Regulator vdd set_opt failed rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = regulator_enable(data->vdd);
+ if (rc < 0) {
+ dev_err(&data->client->dev, "enable vdd failed\n");
+ return rc;
+ }
+
+ rc = reg_set_optimum_mode_check(data->vdd_i2c, DRV2667_I2C_CURR_UA);
+ if (rc < 0) {
+ dev_err(&data->client->dev,
+ "Regulator vdd_i2c set_opt failed rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = regulator_enable(data->vdd_i2c);
+ if (rc < 0) {
+ dev_err(&data->client->dev, "enable vdd_i2c failed\n");
+ goto disable_vdd;
+ }
+
+ return rc;
+vreg_off:
+ regulator_disable(data->vdd_i2c);
+disable_vdd:
+ regulator_disable(data->vdd);
+ return rc;
+}
+
+#ifdef CONFIG_PM
+static int drv2667_suspend(struct device *dev)
+{
+ struct drv2667_data *data = dev_get_drvdata(dev);
+ u8 val;
+ int rc;
+
+ hrtimer_cancel(&data->timer);
+ cancel_work_sync(&data->work);
+
+ /* set standby */
+ val = data->cntl2_val | ~DRV2667_STANDBY_MASK;
+ rc = drv2667_write_reg(data->client, DRV2667_CNTL2_REG, val);
+ if (rc < 0)
+ dev_err(dev, "unable to set standby\n");
+
+ /* turn regulators off */
+ drv2667_vreg_on(data, false);
+ return 0;
+}
+
+static int drv2667_resume(struct device *dev)
+{
+ struct drv2667_data *data = dev_get_drvdata(dev);
+ int rc;
+
+ /* turn regulators on */
+ rc = drv2667_vreg_on(data, true);
+ if (rc < 0) {
+ dev_err(dev, "unable to turn regulators on\n");
+ return rc;
+ }
+
+ /* clear standby */
+ rc = drv2667_write_reg(data->client,
+ DRV2667_CNTL2_REG, data->cntl2_val);
+ if (rc < 0) {
+ dev_err(dev, "unable to clear standby\n");
+ goto vreg_off;
+ }
+
+ return 0;
+vreg_off:
+ drv2667_vreg_on(data, false);
+ return rc;
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void drv2667_early_suspend(struct early_suspend *es)
+{
+ struct drv2667_data *data = container_of(es, struct drv2667_data, es);
+
+ drv2667_suspend(&data->client->dev);
+}
+
+static void drv2667_late_resume(struct early_suspend *es)
+{
+ struct drv2667_data *data = container_of(es, struct drv2667_data, es);
+
+ drv2667_resume(&data->client->dev);
+}
+#endif
+
+static const struct dev_pm_ops drv2667_pm_ops = {
+#ifndef CONFIG_HAS_EARLYSUSPEND
+ .suspend = drv2667_suspend,
+ .resume = drv2667_resume,
+#endif
+};
+#endif
+
+#ifdef CONFIG_OF
+static int drv2667_parse_dt(struct device *dev, struct drv2667_pdata *pdata)
+{
+ struct property *prop;
+ int rc;
+ u32 temp;
+
+ rc = of_property_read_string(dev->of_node, "ti,label", &pdata->name);
+ /* set vibrator as default name */
+ if (rc < 0)
+ pdata->name = "vibrator";
+
+ rc = of_property_read_u32(dev->of_node, "ti,gain", &temp);
+ /* set gain as 0 */
+ if (rc < 0)
+ pdata->gain = 0;
+ else
+ pdata->gain = (u8) temp;
+
+ rc = of_property_read_u32(dev->of_node, "ti,mode", &temp);
+ /* set FIFO mode as default */
+ if (rc < 0)
+ pdata->mode = FIFO_MODE;
+ else
+ pdata->mode = (u8) temp;
+
+ /* read wave sequence */
+ if (pdata->mode == WAV_SEQ_MODE) {
+ prop = of_find_property(dev->of_node, "ti,wav-seq", &temp);
+ if (!prop) {
+ dev_err(dev, "wav seq data not found");
+ return -ENODEV;
+ } else if (temp != DRV2667_WAV_SEQ_LEN) {
+ dev_err(dev, "Invalid length of wav seq data\n");
+ return -EINVAL;
+ }
+ memcpy(pdata->wav_seq, prop->value, DRV2667_WAV_SEQ_LEN);
+ }
+
+ rc = of_property_read_u32(dev->of_node, "ti,idle-timeout-ms", &temp);
+ /* configure minimum idle timeout */
+ if (rc < 0)
+ pdata->idle_timeout_ms = DRV2667_MIN_IDLE_TIMEOUT_MS;
+ else
+ pdata->idle_timeout_ms = (u8) temp;
+
+ rc = of_property_read_u32(dev->of_node, "ti,max-runtime-ms",
+ &pdata->max_runtime_ms);
+ /* configure one sec as default time */
+ if (rc < 0)
+ pdata->max_runtime_ms = MSEC_PER_SEC;
+
+ return 0;
+}
+#else
+static int drv2667_parse_dt(struct device *dev, struct drv2667_pdata *pdata)
+{
+ return -ENODEV;
+}
+#endif
+
+static int __devinit drv2667_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct drv2667_data *data;
+ struct drv2667_pdata *pdata;
+ int rc, i;
+ u8 val, fifo_seq_val, reg;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ dev_err(&client->dev, "i2c is not supported\n");
+ return -EIO;
+ }
+
+ if (client->dev.of_node) {
+ pdata = devm_kzalloc(&client->dev,
+ sizeof(struct drv2667_pdata), GFP_KERNEL);
+ if (!pdata) {
+ dev_err(&client->dev, "unable to allocate pdata\n");
+ return -ENOMEM;
+ }
+ /* parse DT */
+ rc = drv2667_parse_dt(&client->dev, pdata);
+ if (rc) {
+ dev_err(&client->dev, "DT parsing failed\n");
+ return rc;
+ }
+ } else {
+ pdata = client->dev.platform_data;
+ if (!pdata) {
+ dev_err(&client->dev, "invalid pdata\n");
+ return -EINVAL;
+ }
+ }
+
+ data = devm_kzalloc(&client->dev, sizeof(struct drv2667_data),
+ GFP_KERNEL);
+ if (!data) {
+ dev_err(&client->dev, "unable to allocate memory\n");
+ return -ENOMEM;
+ }
+
+ i2c_set_clientdata(client, data);
+
+ data->client = client;
+ data->max_runtime_ms = pdata->max_runtime_ms;
+ mutex_init(&data->lock);
+ INIT_WORK(&data->work, drv2667_worker);
+ hrtimer_init(&data->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ data->timer.function = drv2667_timer;
+ data->mode = pdata->mode;
+
+ /* configure voltage regulators */
+ rc = drv2667_vreg_config(data, true);
+ if (rc) {
+ dev_err(&client->dev, "unable to configure regulators\n");
+ goto destroy_mutex;
+ }
+
+ /* turn on voltage regulators */
+ rc = drv2667_vreg_on(data, true);
+ if (rc) {
+ dev_err(&client->dev, "unable to turn on regulators\n");
+ goto deconfig_vreg;
+ }
+
+ rc = drv2667_read_reg(client, DRV2667_CNTL2_REG);
+ if (rc < 0)
+ goto vreg_off;
+
+ /* set timeout, clear standby */
+ val = (u8) rc;
+
+ if (pdata->idle_timeout_ms < DRV2667_MIN_IDLE_TIMEOUT_MS ||
+ pdata->idle_timeout_ms > DRV2667_MAX_IDLE_TIMEOUT_MS ||
+ (pdata->idle_timeout_ms % DRV2667_MIN_IDLE_TIMEOUT_MS)) {
+ dev_err(&client->dev, "Invalid idle timeout\n");
+ goto vreg_off;
+ }
+
+ val = (val & DRV2667_TIMEOUT_MASK) |
+ ((pdata->idle_timeout_ms / DRV2667_MIN_IDLE_TIMEOUT_MS - 1) <<
+ DRV2667_TIMEOUT_SHIFT);
+
+ val &= DRV2667_STANDBY_MASK;
+
+ rc = drv2667_write_reg(client, DRV2667_CNTL2_REG, val);
+ if (rc < 0)
+ goto vreg_off;
+
+ /* cache control2 val */
+ data->cntl2_val = val;
+
+ /* program drv2667 registers */
+ rc = drv2667_read_reg(client, DRV2667_CNTL1_REG);
+ if (rc < 0)
+ goto vreg_off;
+
+ /* gain and input mode */
+ val = (u8) rc;
+
+ /* remove this check after adding support for these modes */
+ if (data->mode == ANALOG_MODE || data->mode == RAM_SEQ_MODE) {
+ dev_err(&data->client->dev, "Mode not supported\n");
+ goto vreg_off;
+ } else
+ val &= ~DRV2667_INPUT_MUX_MASK; /* set digital mode */
+
+ val = (val & DRV2667_GAIN_MASK) | (pdata->gain << DRV2667_GAIN_SHIFT);
+
+ rc = drv2667_write_reg(client, DRV2667_CNTL1_REG, val);
+ if (rc < 0)
+ goto vreg_off;
+
+ if (data->mode == FIFO_MODE) {
+ /* Load a predefined pattern for FIFO mode */
+ data->buf[0] = DRV2667_FIFO_REG;
+ fifo_seq_val = DRV2667_VIB_START_VAL;
+
+ for (i = 1; i < DRV2667_FIFO_SIZE - 1; i++, fifo_seq_val++)
+ data->buf[i] = fifo_seq_val;
+
+ data->time_chunk_ms = DRV2667_FIFO_CHUNK_MS;
+ } else if (data->mode == WAV_SEQ_MODE) {
+ u8 freq, rep, dur;
+
+ /* program wave sequence from pdata */
+ /* id to wave sequence 3, set page */
+ rc = drv2667_write_reg(client, DRV2667_WAV_SEQ3_REG,
+ pdata->wav_seq[DRV2667_WAV_SEQ_ID_IDX]);
+ if (rc < 0)
+ goto vreg_off;
+
+ /* set page to wave form sequence */
+ rc = drv2667_write_reg(client, DRV2667_PAGE_REG,
+ pdata->wav_seq[DRV2667_WAV_SEQ_ID_IDX]);
+ if (rc < 0)
+ goto vreg_off;
+
+ /* program waveform sequence */
+ for (reg = 0, i = 1; i < DRV2667_WAV_SEQ_LEN - 1; i++, reg++) {
+ rc = drv2667_write_reg(client, reg, pdata->wav_seq[i]);
+ if (rc < 0)
+ goto vreg_off;
+ }
+
+ /* set page back to normal register space */
+ rc = drv2667_write_reg(client, DRV2667_PAGE_REG,
+ DRV2667_REG_PAGE_ID);
+ if (rc < 0)
+ goto vreg_off;
+
+ freq = pdata->wav_seq[DRV2667_WAV_SEQ_FREQ_IDX];
+ rep = pdata->wav_seq[DRV2667_WAV_SEQ_REP_IDX];
+ dur = pdata->wav_seq[DRV2667_WAV_SEQ_DUR_IDX];
+
+ data->time_chunk_ms = (rep * dur * MSEC_PER_SEC) /
+ (freq * DRV2667_WAV_SEQ_FREQ_MIN);
+ }
+
+ drv2667_dump_regs(data, "new");
+
+ /* register with timed output class */
+ data->dev.name = pdata->name;
+ data->dev.get_time = drv2667_get_time;
+ data->dev.enable = drv2667_enable;
+
+ rc = timed_output_dev_register(&data->dev);
+ if (rc) {
+ dev_err(&client->dev, "unable to register with timed_output\n");
+ goto vreg_off;
+ }
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ data->es.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + DRV2667_SUS_LEVEL;
+ data->es.suspend = drv2667_early_suspend;
+ data->es.resume = drv2667_late_resume;
+ register_early_suspend(&data->es);
+#endif
+ return 0;
+
+vreg_off:
+ drv2667_vreg_on(data, false);
+deconfig_vreg:
+ drv2667_vreg_config(data, false);
+destroy_mutex:
+ mutex_destroy(&data->lock);
+ return rc;
+}
+
+static int __devexit drv2667_remove(struct i2c_client *client)
+{
+ struct drv2667_data *data = i2c_get_clientdata(client);
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ unregister_early_suspend(&data->es);
+#endif
+ mutex_destroy(&data->lock);
+ timed_output_dev_unregister(&data->dev);
+ hrtimer_cancel(&data->timer);
+ cancel_work_sync(&data->work);
+ drv2667_vreg_on(data, false);
+ drv2667_vreg_config(data, false);
+
+ return 0;
+}
+
+static const struct i2c_device_id drv2667_id_table[] = {
+ {"drv2667", 0},
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, drv2667_id_table);
+
+#ifdef CONFIG_OF
+static const struct of_device_id drv2667_of_id_table[] = {
+ {.compatible = "ti, drv2667"},
+ { },
+};
+#else
+#define drv2667_of_id_table NULL
+#endif
+
+static struct i2c_driver drv2667_i2c_driver = {
+ .driver = {
+ .name = "drv2667",
+ .owner = THIS_MODULE,
+ .of_match_table = drv2667_of_id_table,
+#ifdef CONFIG_PM
+ .pm = &drv2667_pm_ops,
+#endif
+ },
+ .probe = drv2667_probe,
+ .remove = __devexit_p(drv2667_remove),
+ .id_table = drv2667_id_table,
+};
+
+module_i2c_driver(drv2667_i2c_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("TI DRV2667 chip driver");
diff --git a/drivers/mmc/core/debugfs.c b/drivers/mmc/core/debugfs.c
index ddb562e..84a26a1 100644
--- a/drivers/mmc/core/debugfs.c
+++ b/drivers/mmc/core/debugfs.c
@@ -186,6 +186,46 @@
DEFINE_SIMPLE_ATTRIBUTE(mmc_clock_fops, mmc_clock_opt_get, mmc_clock_opt_set,
"%llu\n");
+static int mmc_max_clock_get(void *data, u64 *val)
+{
+ struct mmc_host *host = data;
+
+ if (!host)
+ return -EINVAL;
+
+ *val = host->f_max;
+
+ return 0;
+}
+
+static int mmc_max_clock_set(void *data, u64 val)
+{
+ struct mmc_host *host = data;
+ int err = -EINVAL;
+ unsigned long freq = val;
+ unsigned int old_freq;
+
+ if (!host || (val < host->f_min))
+ goto out;
+
+ mmc_claim_host(host);
+ if (host->bus_ops && host->bus_ops->change_bus_speed) {
+ old_freq = host->f_max;
+ host->f_max = freq;
+
+ err = host->bus_ops->change_bus_speed(host, &freq);
+
+ if (err)
+ host->f_max = old_freq;
+ }
+ mmc_release_host(host);
+out:
+ return err;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(mmc_max_clock_fops, mmc_max_clock_get,
+ mmc_max_clock_set, "%llu\n");
+
void mmc_add_host_debugfs(struct mmc_host *host)
{
struct dentry *root;
@@ -208,6 +248,10 @@
&mmc_clock_fops))
goto err_node;
+ if (!debugfs_create_file("max_clock", S_IRUSR | S_IWUSR, root, host,
+ &mmc_max_clock_fops))
+ goto err_node;
+
#ifdef CONFIG_MMC_CLKGATE
if (!debugfs_create_u32("clk_delay", (S_IRUSR | S_IWUSR),
root, &host->clk_delay))
diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c
index 0c634ca..4ee18c75 100644
--- a/drivers/mtd/mtdpart.c
+++ b/drivers/mtd/mtdpart.c
@@ -536,15 +536,6 @@
#ifndef CONFIG_MTD_LAZYECCSTATS
part_fill_badblockstats(&(slave->mtd));
#endif
- if (master->_block_isbad) {
- uint64_t offs = 0;
-
- while (offs < slave->mtd.size) {
- if (mtd_block_isbad(master, offs + slave->offset))
- slave->mtd.ecc_stats.badblocks++;
- offs += slave->mtd.erasesize;
- }
- }
out_register:
return slave;
diff --git a/drivers/power/pm8921-bms.c b/drivers/power/pm8921-bms.c
index 81be519..703aca9 100644
--- a/drivers/power/pm8921-bms.c
+++ b/drivers/power/pm8921-bms.c
@@ -87,7 +87,7 @@
struct pm8921_bms_chip {
struct device *dev;
struct dentry *dent;
- unsigned int r_sense;
+ int r_sense_uohm;
unsigned int v_cutoff;
unsigned int fcc;
struct single_row_lut *fcc_temp_lut;
@@ -500,15 +500,15 @@
#define SLEEP_CLK_HZ 32764
#define SECONDS_PER_HOUR 3600
/**
- * ccmicrovolt_to_nvh -
+ * ccmicrovolt_to_uvh -
* @cc_uv: coulumb counter converted to uV
*
- * RETURNS: coulumb counter based charge in nVh
- * (nano Volt Hour)
+ * RETURNS: coulumb counter based charge in uVh
+ * (micro Volt Hour)
*/
-static s64 ccmicrovolt_to_nvh(s64 cc_uv)
+static s64 ccmicrovolt_to_uvh(s64 cc_uv)
{
- return div_s64(cc_uv * CC_READING_TICKS * 1000,
+ return div_s64(cc_uv * CC_READING_TICKS,
SLEEP_CLK_HZ * SECONDS_PER_HOUR);
}
@@ -618,7 +618,7 @@
convert_vbatt_raw_to_uv(the_chip, usb_chg, vbat_raw, vbat_uv);
convert_vsense_to_uv(the_chip, vsense_raw, &vsense_uv);
- *ibat_ua = vsense_uv * 1000 / (int)the_chip->r_sense;
+ *ibat_ua = div_s64((s64)vsense_uv * 1000000LL, the_chip->r_sense_uohm);
pr_debug("vsense_raw = 0x%x vbat_raw = 0x%x"
" ibat_ua = %d vbat_uv = %d\n",
@@ -840,7 +840,7 @@
*/
static void calculate_cc_uah(struct pm8921_bms_chip *chip, int cc, int *val)
{
- int64_t cc_voltage_uv, cc_nvh, cc_uah;
+ int64_t cc_voltage_uv, cc_uvh, cc_uah;
cc_voltage_uv = cc;
cc_voltage_uv -= chip->cc_reading_at_100;
@@ -850,9 +850,9 @@
cc_voltage_uv = cc_to_microvolt(chip, cc_voltage_uv);
cc_voltage_uv = pm8xxx_cc_adjust_for_gain(cc_voltage_uv);
pr_debug("cc_voltage_uv = %lld microvolts\n", cc_voltage_uv);
- cc_nvh = ccmicrovolt_to_nvh(cc_voltage_uv);
- pr_debug("cc_nvh = %lld nano_volt_hour\n", cc_nvh);
- cc_uah = div_s64(cc_nvh, chip->r_sense);
+ cc_uvh = ccmicrovolt_to_uvh(cc_voltage_uv);
+ pr_debug("cc_uvh = %lld micro_volt_hour\n", cc_uvh);
+ cc_uah = div_s64(cc_uvh * 1000000LL, chip->r_sense_uohm);
*val = cc_uah;
}
@@ -2071,25 +2071,25 @@
int pm8921_bms_get_battery_current(int *result_ua)
{
- int vsense;
+ int vsense_uv;
if (!the_chip) {
pr_err("called before initialization\n");
return -EINVAL;
}
- if (the_chip->r_sense == 0) {
+ if (the_chip->r_sense_uohm == 0) {
pr_err("r_sense is zero\n");
return -EINVAL;
}
mutex_lock(&the_chip->bms_output_lock);
pm_bms_lock_output_data(the_chip);
- read_vsense_avg(the_chip, &vsense);
+ read_vsense_avg(the_chip, &vsense_uv);
pm_bms_unlock_output_data(the_chip);
mutex_unlock(&the_chip->bms_output_lock);
- pr_debug("vsense=%duV\n", vsense);
+ pr_debug("vsense=%duV\n", vsense_uv);
/* cast for signed division */
- *result_ua = vsense * 1000 / (int)the_chip->r_sense;
+ *result_ua = div_s64(vsense_uv * 1000000LL, the_chip->r_sense_uohm);
pr_debug("ibat=%duA\n", *result_ua);
return 0;
}
@@ -2897,7 +2897,7 @@
mutex_init(&chip->bms_output_lock);
mutex_init(&chip->last_ocv_uv_mutex);
chip->dev = &pdev->dev;
- chip->r_sense = pdata->r_sense;
+ chip->r_sense_uohm = pdata->r_sense_uohm;
chip->v_cutoff = pdata->v_cutoff;
chip->max_voltage_uv = pdata->max_voltage_uv;
chip->chg_term_ua = pdata->chg_term_ua;
diff --git a/drivers/power/pm8xxx-ccadc.c b/drivers/power/pm8xxx-ccadc.c
index 1b9426a..a586f3d8 100644
--- a/drivers/power/pm8xxx-ccadc.c
+++ b/drivers/power/pm8xxx-ccadc.c
@@ -72,7 +72,7 @@
unsigned int revision;
unsigned int calib_delay_ms;
int eoc_irq;
- int r_sense;
+ int r_sense_uohm;
struct delayed_work calib_ccadc_work;
};
@@ -562,7 +562,8 @@
return rc;
}
- *bat_current_ua = voltage_uv * 1000/the_chip->r_sense;
+ *bat_current_ua = div_s64((s64)voltage_uv * 1000000LL,
+ the_chip->r_sense_uohm);
/*
* ccadc reads +ve current when the battery is charging
* We need to return -ve if the battery is charging
@@ -675,7 +676,7 @@
chip->dev = &pdev->dev;
chip->revision = pm8xxx_get_revision(chip->dev->parent);
chip->eoc_irq = res->start;
- chip->r_sense = pdata->r_sense;
+ chip->r_sense_uohm = pdata->r_sense_uohm;
chip->calib_delay_ms = pdata->calib_delay_ms;
calib_ccadc_read_offset_and_gain(chip,
diff --git a/drivers/power/qpnp-bms.c b/drivers/power/qpnp-bms.c
index 7b4d97e..0c070d6 100644
--- a/drivers/power/qpnp-bms.c
+++ b/drivers/power/qpnp-bms.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -10,7 +10,7 @@
* GNU General Public License for more details.
*/
-#define pr_fmt(fmt) "%s: " fmt, __func__
+#define pr_fmt(fmt) "BMS: %s: " fmt, __func__
#include <linux/module.h>
#include <linux/types.h>
@@ -21,26 +21,15 @@
#include <linux/of_device.h>
#include <linux/power_supply.h>
#include <linux/spmi.h>
-
-/* Interrupt offsets */
-#define INT_RT_STS(base) (base + 0x10)
-#define INT_SET_TYPE(base) (base + 0x11)
-#define INT_POLARITY_HIGH(base) (base + 0x12)
-#define INT_POLARITY_LOW(base) (base + 0x13)
-#define INT_LATCHED_CLR(base) (base + 0x14)
-#define INT_EN_SET(base) (base + 0x15)
-#define INT_EN_CLR(base) (base + 0x16)
-#define INT_LATCHED_STS(base) (base + 0x18)
-#define INT_PENDING_STS(base) (base + 0x19)
-#define INT_MID_SEL(base) (base + 0x1A)
-#define INT_PRIORITY(base) (base + 0x1B)
+#include <linux/qpnp/qpnp-adc.h>
+#include <linux/mfd/pm8xxx/batterydata-lib.h>
/* BMS Register Offsets */
#define BMS1_REVISION1 0x0
#define BMS1_REVISION2 0x1
#define BMS1_STATUS1 0x8
#define BMS1_MODE_CTL 0X40
-/* Columb counter clear registers */
+/* Coulomb counter clear registers */
#define BMS1_CC_DATA_CTL 0x42
#define BMS1_CC_CLEAR_CTRL 0x43
/* OCV limit registers */
@@ -60,7 +49,7 @@
#define BMS1_OCV_FOR_R_DATA1 0x81
#define BMS1_VSENSE_FOR_R_DATA0 0x82
#define BMS1_VSENSE_FOR_R_DATA1 0x83
-/* Columb counter data */
+/* Coulomb counter data */
#define BMS1_CC_DATA0 0x8A
#define BMS1_CC_DATA1 0x8B
#define BMS1_CC_DATA2 0x8C
@@ -71,16 +60,39 @@
#define BMS1_OCV_FOR_SOC_DATA1 0x91
#define BMS1_VSENSE_PON_DATA0 0x94
#define BMS1_VSENSE_PON_DATA1 0x95
+#define BMS1_VSENSE_AVG_DATA0 0x98
+#define BMS1_VSENSE_AVG_DATA1 0x99
#define BMS1_VBAT_AVG_DATA0 0x9E
#define BMS1_VBAT_AVG_DATA1 0x9F
/* Extra bms registers */
#define BMS1_BMS_DATA_REG_0 0xB0
-#define BMS1_BMS_DATA_REG_1 0xB1
-#define BMS1_BMS_DATA_REG_2 0xB2
+#define IAVG_STORAGE_REG 0xB1
+#define SOC_STORAGE_REG 0xB2
#define BMS1_BMS_DATA_REG_3 0xB3
+/* Configuration for saving of shutdown soc/iavg */
+#define IGNORE_SOC_TEMP_DECIDEG 50
+#define IAVG_STEP_SIZE_MA 50
+#define IAVG_START 600
+#define SOC_ZERO 0xFF
+
#define QPNP_BMS_DEV_NAME "qcom,qpnp-bms"
+struct soc_params {
+ int fcc_uah;
+ int cc_uah;
+ int rbatt;
+ int iavg_ua;
+ int uuc_uah;
+ int ocv_charge_uah;
+};
+
+struct raw_soc_params {
+ uint16_t last_good_ocv_raw;
+ int64_t cc;
+ int last_good_ocv_uv;
+};
+
struct qpnp_bms_chip {
struct device *dev;
struct power_supply bms_psy;
@@ -93,13 +105,49 @@
bool online;
/* platform data */
unsigned int r_sense_mohm;
- unsigned int v_cutoff;
- unsigned int max_voltage;
+ unsigned int v_cutoff_uv;
+ unsigned int max_voltage_uv;
unsigned int r_conn_mohm;
int shutdown_soc_valid_limit;
int adjust_soc_low_threshold;
int adjust_soc_high_threshold;
- int chg_term;
+ int chg_term_ua;
+ enum battery_type batt_type;
+ unsigned int fcc;
+ struct single_row_lut *fcc_temp_lut;
+ struct single_row_lut *fcc_sf_lut;
+ struct pc_temp_ocv_lut *pc_temp_ocv_lut;
+ struct sf_lut *pc_sf_lut;
+ struct sf_lut *rbatt_sf_lut;
+ int default_rbatt_mohm;
+
+ struct delayed_work calculate_soc_delayed_work;
+
+ struct mutex bms_output_lock;
+ struct mutex last_ocv_uv_mutex;
+
+ unsigned int start_percent;
+ unsigned int end_percent;
+ bool ignore_shutdown_soc;
+ int shutdown_soc_invalid;
+ int shutdown_soc;
+ int shutdown_iavg_ma;
+
+ int low_soc_calc_threshold;
+ int low_soc_calculate_soc_ms;
+ int calculate_soc_ms;
+
+ unsigned int vadc_v0625;
+ unsigned int vadc_v1250;
+
+ int prev_iavg_ua;
+ int prev_uuc_iavg_ma;
+ int prev_pc_unusable;
+ int ibat_at_cv_ua;
+ int soc_at_cv;
+ int prev_chg_soc;
+ int calculated_soc;
+ int last_vbat_read_uv;
};
static struct of_device_id qpnp_bms_match_table[] = {
@@ -119,6 +167,38 @@
POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
};
+static bool use_voltage_soc;
+
+/* module params */
+static int bms_param_set_bool(const char *val, const struct kernel_param *kp)
+{
+ int rc;
+ struct power_supply *bms_psy;
+
+ rc = param_set_bool(val, kp);
+ if (rc) {
+ pr_err("failed to set %s, rc = %d\n", kp->name, rc);
+ return rc;
+ }
+
+ bms_psy = power_supply_get_by_name("bms");
+
+ if (bms_psy)
+ power_supply_changed(bms_psy);
+ else
+ pr_debug("%s changed but bms has not been initialized yet\n",
+ kp->name);
+
+ return 0;
+}
+
+static struct kernel_param_ops bms_param_ops = {
+ .set = bms_param_set_bool,
+ .get = param_get_bool,
+};
+
+module_param_cb(use_voltage_soc, &bms_param_ops, &use_voltage_soc, 0644);
+
static int qpnp_read_wrapper(struct qpnp_bms_chip *chip, u8 *val,
u16 base, int count)
{
@@ -126,31 +206,323 @@
struct spmi_device *spmi = chip->spmi;
rc = spmi_ext_register_readl(spmi->ctrl, spmi->sid, base, val, count);
- if (rc)
+ if (rc) {
pr_err("SPMI read failed rc=%d\n", rc);
-
+ return rc;
+ }
return 0;
}
+static int qpnp_write_wrapper(struct qpnp_bms_chip *chip, u8 *val,
+ u16 base, int count)
+{
+ int rc;
+ struct spmi_device *spmi = chip->spmi;
+
+ rc = spmi_ext_register_writel(spmi->ctrl, spmi->sid, base, val, count);
+ if (rc) {
+ pr_err("SPMI write failed rc=%d\n", rc);
+ return rc;
+ }
+ return 0;
+}
+
+static int qpnp_masked_write(struct qpnp_bms_chip *chip, u16 addr,
+ u8 mask, u8 val)
+{
+ int rc;
+ u8 reg;
+
+ rc = qpnp_read_wrapper(chip, ®, chip->base + addr, 1);
+ if (rc) {
+ pr_err("read failed addr = %03X, rc = %d\n",
+ chip->base + addr, rc);
+ return rc;
+ }
+ reg &= ~mask;
+ reg |= val & mask;
+ rc = qpnp_write_wrapper(chip, ®, chip->base + addr, 1);
+ if (rc) {
+ pr_err("write failed addr = %03X, val = %02x, mask = %02x, reg = %02x, rc = %d\n",
+ chip->base + addr, val, mask, reg, rc);
+ return rc;
+ }
+ return 0;
+}
+
+#define HOLD_OREG_DATA BIT(0)
+static int lock_output_data(struct qpnp_bms_chip *chip)
+{
+ int rc;
+
+ rc = qpnp_masked_write(chip, BMS1_CC_DATA_CTL,
+ HOLD_OREG_DATA, HOLD_OREG_DATA);
+ if (rc) {
+ pr_err("couldnt lock bms output rc = %d\n", rc);
+ return rc;
+ }
+ return 0;
+}
+
+static int unlock_output_data(struct qpnp_bms_chip *chip)
+{
+ int rc;
+
+ rc = qpnp_masked_write(chip, BMS1_CC_DATA_CTL, HOLD_OREG_DATA, 0);
+ if (rc) {
+ pr_err("fail to unlock BMS_CONTROL rc = %d\n", rc);
+ return rc;
+ }
+ return 0;
+}
+
+#define V_PER_BIT_MUL_FACTOR 97656
+#define V_PER_BIT_DIV_FACTOR 1000
+#define VADC_INTRINSIC_OFFSET 0x6000
+
+static int vadc_reading_to_uv(unsigned int reading)
+{
+ if (reading <= VADC_INTRINSIC_OFFSET)
+ return 0;
+
+ return (reading - VADC_INTRINSIC_OFFSET)
+ * V_PER_BIT_MUL_FACTOR / V_PER_BIT_DIV_FACTOR;
+}
+
+#define VADC_CALIB_UV 625000
+#define VBATT_MUL_FACTOR 3
+
+static int adjust_vbatt_reading(struct qpnp_bms_chip *chip,
+ unsigned int reading_uv)
+{
+ s64 numerator, denominator;
+
+ if (reading_uv == 0)
+ return 0;
+
+ /* don't adjust if not calibrated */
+ if (chip->vadc_v0625 == 0 || chip->vadc_v1250 == 0) {
+ pr_debug("No cal yet return %d\n",
+ VBATT_MUL_FACTOR * reading_uv);
+ return VBATT_MUL_FACTOR * reading_uv;
+ }
+
+ numerator = ((s64)reading_uv - chip->vadc_v0625)
+ * VADC_CALIB_UV;
+ denominator = (s64)chip->vadc_v1250 - chip->vadc_v0625;
+ if (denominator == 0)
+ return reading_uv * VBATT_MUL_FACTOR;
+ return (VADC_CALIB_UV + div_s64(numerator, denominator))
+ * VBATT_MUL_FACTOR;
+}
+
+static inline int convert_vbatt_raw_to_uv(struct qpnp_bms_chip *chip,
+ uint16_t reading)
+{
+ return adjust_vbatt_reading(chip, vadc_reading_to_uv(reading));
+}
+
+#define CC_READING_RESOLUTION_N 542535
+#define CC_READING_RESOLUTION_D 100000
+static int cc_reading_to_uv(int16_t reading)
+{
+ return div_s64(reading * CC_READING_RESOLUTION_N,
+ CC_READING_RESOLUTION_D);
+}
+
+#define QPNP_ADC_GAIN_NV 17857LL
+static s64 cc_adjust_for_gain(s64 uv, s64 gain)
+{
+ s64 result_uv;
+
+ pr_debug("adjusting_uv = %lld\n", uv);
+ pr_debug("adjusting by factor: %lld/%lld = %lld%%\n",
+ QPNP_ADC_GAIN_NV, gain,
+ div_s64(QPNP_ADC_GAIN_NV * 100LL, gain));
+
+ result_uv = div_s64(uv * QPNP_ADC_GAIN_NV, gain);
+ pr_debug("result_uv = %lld\n", result_uv);
+ return result_uv;
+}
+
+static int convert_vsense_to_uv(struct qpnp_bms_chip *chip,
+ int16_t reading)
+{
+ return cc_adjust_for_gain(cc_reading_to_uv(reading), QPNP_ADC_GAIN_NV);
+}
+
+static int read_vsense_avg(struct qpnp_bms_chip *chip, int *result_uv)
+{
+ int rc;
+ int16_t reading;
+
+ rc = qpnp_read_wrapper(chip, (u8 *)&reading,
+ chip->base + BMS1_VSENSE_AVG_DATA0, 2);
+
+ if (rc) {
+ pr_err("fail to read VSENSE_AVG rc = %d\n", rc);
+ return rc;
+ }
+
+ *result_uv = convert_vsense_to_uv(chip, reading);
+ return 0;
+}
+
+static int get_battery_current(struct qpnp_bms_chip *chip, int *result_ua)
+{
+ int vsense_uv = 0;
+
+ if (chip->r_sense_mohm == 0) {
+ pr_err("r_sense is zero\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&chip->bms_output_lock);
+ lock_output_data(chip);
+ read_vsense_avg(chip, &vsense_uv);
+ unlock_output_data(chip);
+ mutex_unlock(&chip->bms_output_lock);
+
+ pr_debug("vsense_uv=%duV\n", vsense_uv);
+ /* cast for signed division */
+ *result_ua = vsense_uv * 1000 / (int)chip->r_sense_mohm;
+ pr_debug("ibat=%duA\n", *result_ua);
+ return 0;
+}
+
+static int get_battery_voltage(int *result_uv)
+{
+ int rc;
+ struct qpnp_vadc_result adc_result;
+
+ rc = qpnp_vadc_read(VBAT_SNS, &adc_result);
+ if (rc) {
+ pr_err("error reading adc channel = %d, rc = %d\n",
+ VBAT_SNS, rc);
+ return rc;
+ }
+ pr_debug("mvolts phy = %lld meas = 0x%llx\n", adc_result.physical,
+ adc_result.measurement);
+ *result_uv = (int)adc_result.physical;
+ return 0;
+}
+
+static int read_soc_params_raw(struct qpnp_bms_chip *chip,
+ struct raw_soc_params *raw)
+{
+ /* TODO add real reads */
+ return 0;
+}
+
+static int calculate_state_of_charge(struct qpnp_bms_chip *chip,
+ struct raw_soc_params *raw,
+ int batt_temp)
+{
+ chip->calculated_soc = 50;
+ return chip->calculated_soc;
+}
+
+static void read_vbat(struct qpnp_bms_chip *chip)
+{
+ int rc;
+ struct qpnp_vadc_result result;
+
+ rc = qpnp_vadc_read(VBAT_SNS, &result);
+ if (rc) {
+ pr_err("error reading vadc VBAT_SNS = %d, rc = %d\n",
+ VBAT_SNS, rc);
+ return;
+ }
+ chip->last_vbat_read_uv = (int)result.physical;
+}
+
+static void calculate_soc_work(struct work_struct *work)
+{
+ struct qpnp_bms_chip *chip = container_of(work,
+ struct qpnp_bms_chip,
+ calculate_soc_delayed_work.work);
+ int batt_temp, rc, soc;
+ struct qpnp_vadc_result result;
+ struct raw_soc_params raw;
+
+ read_vbat(chip);
+
+ rc = qpnp_vadc_read(LR_MUX1_BATT_THERM, &result);
+ if (rc) {
+ pr_err("error reading vadc LR_MUX1_BATT_THERM = %d, rc = %d\n",
+ LR_MUX1_BATT_THERM, rc);
+ return;
+ }
+ pr_debug("batt_temp phy = %lld meas = 0x%llx\n", result.physical,
+ result.measurement);
+ batt_temp = (int)result.physical;
+
+ mutex_lock(&chip->last_ocv_uv_mutex);
+ read_soc_params_raw(chip, &raw);
+ soc = calculate_state_of_charge(chip, &raw, batt_temp);
+ mutex_unlock(&chip->last_ocv_uv_mutex);
+
+ if (soc < chip->low_soc_calc_threshold)
+ schedule_delayed_work(&chip->calculate_soc_delayed_work,
+ round_jiffies_relative(msecs_to_jiffies
+ (chip->low_soc_calculate_soc_ms)));
+ else
+ schedule_delayed_work(&chip->calculate_soc_delayed_work,
+ round_jiffies_relative(msecs_to_jiffies
+ (chip->calculate_soc_ms)));
+}
+
+static int calculate_soc_from_voltage(struct qpnp_bms_chip *chip)
+{
+ int voltage_range_uv, voltage_remaining_uv, voltage_based_soc;
+
+ if (chip->last_vbat_read_uv < 0)
+ read_vbat(chip);
+
+ voltage_range_uv = chip->max_voltage_uv - chip->v_cutoff_uv;
+ voltage_remaining_uv = chip->last_vbat_read_uv - chip->v_cutoff_uv;
+ voltage_based_soc = voltage_remaining_uv * 100 / voltage_range_uv;
+
+ return clamp(voltage_based_soc, 0, 100);
+}
+
/* Returns capacity as a SoC percentage between 0 and 100 */
static int get_prop_bms_capacity(struct qpnp_bms_chip *chip)
{
- /* return 50 until a real algorithm is implemented */
- return 50;
+ if (use_voltage_soc)
+ return calculate_soc_from_voltage(chip);
+
+ return chip->calculated_soc;
}
/* Returns instantaneous current in uA */
static int get_prop_bms_current_now(struct qpnp_bms_chip *chip)
{
/* temporarily return 0 until a real algorithm is put in */
- return 0;
+ int rc, result_ua;
+
+ rc = get_battery_current(chip, &result_ua);
+ if (rc) {
+ pr_err("failed to get current: %d\n", rc);
+ return rc;
+ }
+ return result_ua;
}
/* Returns full charge design in uAh */
static int get_prop_bms_charge_full_design(struct qpnp_bms_chip *chip)
{
- /* temporarily return 0 until a real algorithm is put in */
- return 0;
+ return chip->fcc;
+}
+
+static bool get_prop_bms_online(struct qpnp_bms_chip *chip)
+{
+ return chip->online;
+}
+
+static int get_prop_bms_status(struct qpnp_bms_chip *chip)
+{
+ return chip->charger_status;
}
static void set_prop_bms_online(struct qpnp_bms_chip *chip, bool online)
@@ -184,6 +556,12 @@
case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
val->intval = get_prop_bms_charge_full_design(chip);
break;
+ case POWER_SUPPLY_PROP_STATUS:
+ val->intval = get_prop_bms_status(chip);
+ break;
+ case POWER_SUPPLY_PROP_ONLINE:
+ val->intval = get_prop_bms_online(chip);
+ break;
default:
return -EINVAL;
}
@@ -210,24 +588,202 @@
return 0;
}
-#define SPMI_PROPERTY_READ(chip_prop, qpnp_spmi_property, retval, errlabel)\
+static void read_shutdown_soc_and_iavg(struct qpnp_bms_chip *chip)
+{
+ int rc;
+ u8 temp;
+
+ if (chip->ignore_shutdown_soc) {
+ chip->shutdown_soc_invalid = 1;
+ chip->shutdown_soc = 0;
+ chip->shutdown_iavg_ma = 0;
+ } else {
+ rc = qpnp_read_wrapper(chip, &temp,
+ chip->base + IAVG_STORAGE_REG, 1);
+ if (rc) {
+ pr_err("failed to read addr = %d %d assuming %d\n",
+ chip->base + IAVG_STORAGE_REG, rc,
+ IAVG_START);
+ chip->shutdown_iavg_ma = IAVG_START;
+ } else {
+ if (temp == 0) {
+ chip->shutdown_iavg_ma = IAVG_START;
+ } else {
+ chip->shutdown_iavg_ma = IAVG_START
+ + IAVG_STEP_SIZE_MA * (temp + 1);
+ }
+ }
+
+ rc = qpnp_read_wrapper(chip, &temp,
+ chip->base + SOC_STORAGE_REG, 1);
+ if (rc) {
+ pr_err("failed to read addr = %d %d\n",
+ chip->base + SOC_STORAGE_REG, rc);
+ } else {
+ chip->shutdown_soc = temp;
+
+ if (chip->shutdown_soc == 0) {
+ pr_debug("No shutdown soc available\n");
+ chip->shutdown_soc_invalid = 1;
+ chip->shutdown_iavg_ma = 0;
+ } else if (chip->shutdown_soc == SOC_ZERO) {
+ chip->shutdown_soc = 0;
+ }
+ }
+ }
+
+ pr_debug("shutdown_soc = %d shutdown_iavg = %d shutdown_soc_invalid = %d\n",
+ chip->shutdown_soc,
+ chip->shutdown_iavg_ma,
+ chip->shutdown_soc_invalid);
+}
+
+#define PALLADIUM_ID_MIN 0x7F40
+#define PALLADIUM_ID_MAX 0x7F5A
+#define DESAY_5200_ID_MIN 0x7F7F
+#define DESAY_5200_ID_MAX 0x802F
+static int32_t read_battery_id(struct qpnp_bms_chip *chip)
+{
+ int rc;
+ struct qpnp_vadc_result result;
+
+ rc = qpnp_vadc_read(LR_MUX2_BAT_ID, &result);
+ if (rc) {
+ pr_err("error reading batt id channel = %d, rc = %d\n",
+ LR_MUX2_BAT_ID, rc);
+ return rc;
+ }
+ pr_debug("batt_id phy = %lld meas = 0x%llx\n", result.physical,
+ result.measurement);
+ pr_debug("raw_code = 0x%x\n", result.adc_code);
+ return result.adc_code;
+}
+
+static int set_battery_data(struct qpnp_bms_chip *chip)
+{
+ int64_t battery_id;
+
+ if (chip->batt_type == BATT_DESAY)
+ goto desay;
+ else if (chip->batt_type == BATT_PALLADIUM)
+ goto palladium;
+
+ battery_id = read_battery_id(chip);
+ if (battery_id < 0) {
+ pr_err("cannot read battery id err = %lld\n", battery_id);
+ return battery_id;
+ }
+
+ if (is_between(PALLADIUM_ID_MIN, PALLADIUM_ID_MAX, battery_id)) {
+ goto palladium;
+ } else if (is_between(DESAY_5200_ID_MIN, DESAY_5200_ID_MAX,
+ battery_id)) {
+ goto desay;
+ } else {
+ pr_warn("invalid battid, palladium 1500 assumed batt_id %llx\n",
+ battery_id);
+ goto palladium;
+ }
+
+palladium:
+ chip->fcc = palladium_1500_data.fcc;
+ chip->fcc_temp_lut = palladium_1500_data.fcc_temp_lut;
+ chip->fcc_sf_lut = palladium_1500_data.fcc_sf_lut;
+ chip->pc_temp_ocv_lut = palladium_1500_data.pc_temp_ocv_lut;
+ chip->pc_sf_lut = palladium_1500_data.pc_sf_lut;
+ chip->rbatt_sf_lut = palladium_1500_data.rbatt_sf_lut;
+ chip->default_rbatt_mohm
+ = palladium_1500_data.default_rbatt_mohm;
+ goto check_lut;
+desay:
+ chip->fcc = desay_5200_data.fcc;
+ chip->fcc_temp_lut = desay_5200_data.fcc_temp_lut;
+ chip->pc_temp_ocv_lut = desay_5200_data.pc_temp_ocv_lut;
+ chip->pc_sf_lut = desay_5200_data.pc_sf_lut;
+ chip->rbatt_sf_lut = desay_5200_data.rbatt_sf_lut;
+ chip->default_rbatt_mohm = desay_5200_data.default_rbatt_mohm;
+ goto check_lut;
+check_lut:
+ if (chip->pc_temp_ocv_lut == NULL) {
+ pr_err("temp ocv lut table is NULL\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+#define SPMI_PROP_READ(chip_prop, qpnp_spmi_property, retval) \
do { \
- retval = of_property_read_u32(spmi->dev.of_node, \
+ retval = of_property_read_u32(chip->spmi->dev.of_node, \
"qcom,bms-" qpnp_spmi_property, \
&chip->chip_prop); \
if (retval) { \
pr_err("Error reading " #qpnp_spmi_property \
" property %d\n", rc); \
- goto errlabel; \
+ return -EINVAL; \
} \
} while (0)
+static inline int bms_read_properties(struct qpnp_bms_chip *chip)
+{
+ int rc;
+
+ SPMI_PROP_READ(r_sense_mohm, "r-sense-mohm", rc);
+ SPMI_PROP_READ(v_cutoff_uv, "v-cutoff-uv", rc);
+ SPMI_PROP_READ(max_voltage_uv, "max-voltage-uv", rc);
+ SPMI_PROP_READ(r_conn_mohm, "r-conn-mohm", rc);
+ SPMI_PROP_READ(chg_term_ua, "chg-term-ua", rc);
+ SPMI_PROP_READ(shutdown_soc_valid_limit,
+ "shutdown-soc-valid-limit", rc);
+ SPMI_PROP_READ(adjust_soc_high_threshold,
+ "adjust-soc-high-threshold", rc);
+ SPMI_PROP_READ(adjust_soc_low_threshold,
+ "adjust-soc-low-threshold", rc);
+ SPMI_PROP_READ(batt_type, "batt-type", rc);
+ SPMI_PROP_READ(low_soc_calc_threshold,
+ "low-soc-calculate-soc-threshold", rc);
+ SPMI_PROP_READ(low_soc_calculate_soc_ms,
+ "low-soc-calculate-soc-ms", rc);
+ SPMI_PROP_READ(calculate_soc_ms, "calculate-soc-ms", rc);
+ chip->ignore_shutdown_soc = of_property_read_bool(
+ chip->spmi->dev.of_node,
+ "qcom,bms-ignore-shutdown-soc");
+ use_voltage_soc = of_property_read_bool(chip->spmi->dev.of_node,
+ "qcom,bms-use-voltage-soc");
+
+ if (chip->adjust_soc_low_threshold >= 45)
+ chip->adjust_soc_low_threshold = 45;
+
+ pr_debug("dts data: r_sense_mohm:%d, v_cutoff_uv:%d, max_v:%d\n",
+ chip->r_sense_mohm, chip->v_cutoff_uv,
+ chip->max_voltage_uv);
+ pr_debug("r_conn:%d, shutdown_soc: %d, adjust_soc_low:%d\n",
+ chip->r_conn_mohm, chip->shutdown_soc_valid_limit,
+ chip->adjust_soc_low_threshold);
+ pr_debug("adjust_soc_high:%d, chg_term_ua:%d, batt_type:%d\n",
+ chip->adjust_soc_high_threshold, chip->chg_term_ua,
+ chip->batt_type);
+ pr_debug("ignore_shutdown_soc:%d, use_voltage_soc:%d\n",
+ chip->ignore_shutdown_soc, use_voltage_soc);
+
+ return 0;
+}
+
+static inline void bms_initialize_constants(struct qpnp_bms_chip *chip)
+{
+ chip->start_percent = -EINVAL;
+ chip->end_percent = -EINVAL;
+ chip->prev_pc_unusable = -EINVAL;
+ chip->soc_at_cv = -EINVAL;
+ chip->calculated_soc = -EINVAL;
+ chip->last_vbat_read_uv = -EINVAL;
+}
+
static int __devinit
qpnp_bms_probe(struct spmi_device *spmi)
{
struct qpnp_bms_chip *chip;
struct resource *bms_resource;
- int rc;
+ int rc, vbatt;
chip = kzalloc(sizeof *chip, GFP_KERNEL);
@@ -239,6 +795,9 @@
chip->dev = &(spmi->dev);
chip->spmi = spmi;
+ mutex_init(&chip->bms_output_lock);
+ mutex_init(&chip->last_ocv_uv_mutex);
+
bms_resource = spmi_get_resource(spmi, NULL, IORESOURCE_MEM, 0);
if (!bms_resource) {
dev_err(&spmi->dev, "Unable to get BMS base address\n");
@@ -260,29 +819,30 @@
goto error_read;
}
- SPMI_PROPERTY_READ(r_sense_mohm, "r-sense-mohm", rc, error_read);
- SPMI_PROPERTY_READ(v_cutoff, "v-cutoff-uv", rc, error_read);
- SPMI_PROPERTY_READ(max_voltage, "max-voltage-uv", rc, error_read);
- SPMI_PROPERTY_READ(r_conn_mohm, "r-conn-mohm", rc, error_read);
- SPMI_PROPERTY_READ(shutdown_soc_valid_limit,
- "shutdown-soc-valid-limit", rc, error_read);
- SPMI_PROPERTY_READ(adjust_soc_low_threshold,
- "adjust-soc-low-threshold", rc, error_read);
- SPMI_PROPERTY_READ(adjust_soc_high_threshold,
- "adjust-soc-high-threshold", rc, error_read);
- SPMI_PROPERTY_READ(chg_term, "chg-term-ua", rc, error_read);
+ rc = set_battery_data(chip);
+ if (rc) {
+ pr_err("Bad battery data %d\n", rc);
+ goto error_read;
+ }
- pr_debug("dts data: r_sense_mohm:%d, v_cutoff:%d, max_v:%d, r_conn:%d, shutdown_soc: %d, adjust_soc_low:%d, adjust_soc_high:%d, chg_term:%d\n",
- chip->r_sense_mohm, chip->v_cutoff,
- chip->max_voltage, chip->r_conn_mohm,
- chip->shutdown_soc_valid_limit,
- chip->adjust_soc_low_threshold,
- chip->adjust_soc_high_threshold,
- chip->chg_term);
+ rc = bms_read_properties(chip);
+ if (rc) {
+ pr_err("Unable to read all bms properties, rc = %d\n", rc);
+ goto error_read;
+ }
+
+ bms_initialize_constants(chip);
+
+ INIT_DELAYED_WORK(&chip->calculate_soc_delayed_work,
+ calculate_soc_work);
+
+ read_shutdown_soc_and_iavg(chip);
dev_set_drvdata(&spmi->dev, chip);
device_init_wakeup(&spmi->dev, 1);
+ calculate_soc_work(&(chip->calculate_soc_delayed_work.work));
+
/* setup & register the battery power supply */
chip->bms_psy.name = "bms";
chip->bms_psy.type = POWER_SUPPLY_TYPE_BMS;
@@ -302,6 +862,12 @@
goto unregister_dc;
}
+ vbatt = 0;
+ get_battery_voltage(&vbatt);
+
+ pr_info("OK battery_capacity_at_boot=%d vbatt = %d\n",
+ get_prop_bms_capacity(chip),
+ vbatt);
pr_info("probe success\n");
return 0;
diff --git a/drivers/usb/gadget/u_smd.c b/drivers/usb/gadget/u_smd.c
index ce285a3..effe418 100644
--- a/drivers/usb/gadget/u_smd.c
+++ b/drivers/usb/gadget/u_smd.c
@@ -72,6 +72,7 @@
struct smd_port_info *pi;
struct delayed_work connect_work;
+ struct work_struct disconnect_work;
/* At present, smd does not notify
* control bit change info from modem
@@ -589,6 +590,20 @@
}
}
+static void gsmd_disconnect_work(struct work_struct *w)
+{
+ struct gsmd_port *port;
+ struct smd_port_info *pi;
+
+ port = container_of(w, struct gsmd_port, disconnect_work);
+ pi = port->pi;
+
+ pr_debug("%s: port:%p port#%d\n", __func__, port, port->port_num);
+
+ smd_close(port->pi->ch);
+ port->pi->ch = NULL;
+}
+
static void gsmd_notify_modem(void *gptr, u8 portno, int ctrl_bits)
{
struct gsmd_port *port;
@@ -731,10 +746,8 @@
~port->cbits_to_modem);
}
- if (port->pi->ch) {
- smd_close(port->pi->ch);
- port->pi->ch = NULL;
- }
+ if (port->pi->ch)
+ queue_work(gsmd_wq, &port->disconnect_work);
}
#define SMD_CH_MAX_LEN 20
@@ -819,6 +832,7 @@
INIT_WORK(&port->pull, gsmd_tx_pull);
INIT_DELAYED_WORK(&port->connect_work, gsmd_connect_work);
+ INIT_WORK(&port->disconnect_work, gsmd_disconnect_work);
smd_ports[portno].port = port;
pdrv = &smd_ports[portno].pdrv;
diff --git a/drivers/usb/host/ehci-msm-hsic.c b/drivers/usb/host/ehci-msm-hsic.c
index 7b616e4..2d69a98 100644
--- a/drivers/usb/host/ehci-msm-hsic.c
+++ b/drivers/usb/host/ehci-msm-hsic.c
@@ -324,7 +324,7 @@
#define ULPI_IO_TIMEOUT_USEC (10 * 1000)
#define USB_PHY_VDD_DIG_VOL_NONE 0 /*uV */
-#define USB_PHY_VDD_DIG_VOL_MIN 1000000 /* uV */
+#define USB_PHY_VDD_DIG_VOL_MIN 945000 /* uV */
#define USB_PHY_VDD_DIG_VOL_MAX 1320000 /* uV */
#define HSIC_DBG1_REG 0x38
@@ -641,6 +641,7 @@
int cnt = 0, ret;
u32 val;
int none_vol, max_vol;
+ struct msm_hsic_host_platform_data *pdata = mehci->dev->platform_data;
if (atomic_read(&mehci->in_lpm)) {
dev_dbg(mehci->dev, "%s called in lpm\n", __func__);
@@ -719,6 +720,10 @@
enable_irq_wake(mehci->wakeup_irq);
enable_irq(mehci->wakeup_irq);
+ if (pdata && pdata->standalone_latency)
+ pm_qos_update_request(&mehci->pm_qos_req_dma,
+ PM_QOS_DEFAULT_VALUE);
+
wake_unlock(&mehci->wlock);
dev_info(mehci->dev, "HSIC-USB in low power mode\n");
@@ -733,12 +738,17 @@
unsigned temp;
int min_vol, max_vol;
unsigned long flags;
+ struct msm_hsic_host_platform_data *pdata = mehci->dev->platform_data;
if (!atomic_read(&mehci->in_lpm)) {
dev_dbg(mehci->dev, "%s called in !in_lpm\n", __func__);
return 0;
}
+ if (pdata && pdata->standalone_latency)
+ pm_qos_update_request(&mehci->pm_qos_req_dma,
+ pdata->standalone_latency + 1);
+
spin_lock_irqsave(&mehci->wakeup_lock, flags);
if (mehci->wakeup_irq_enabled) {
disable_irq_wake(mehci->wakeup_irq);
@@ -1031,9 +1041,9 @@
pm_qos_update_request(&mehci->pm_qos_req_dma,
pdata->swfi_latency + 1);
wait_for_completion(&mehci->gpt0_completion);
- if (pdata && pdata->swfi_latency)
+ if (pdata && pdata->standalone_latency)
pm_qos_update_request(&mehci->pm_qos_req_dma,
- PM_QOS_DEFAULT_VALUE);
+ pdata->standalone_latency + 1);
spin_lock_irq(&ehci->lock);
} else {
dbg_log_event(NULL, "FPR: Tightloop", 0);
@@ -1680,9 +1690,9 @@
__mehci = mehci;
- if (pdata && pdata->swfi_latency)
+ if (pdata && pdata->standalone_latency)
pm_qos_add_request(&mehci->pm_qos_req_dma,
- PM_QOS_CPU_DMA_LATENCY, PM_QOS_DEFAULT_VALUE);
+ PM_QOS_CPU_DMA_LATENCY, pdata->standalone_latency + 1);
/*
* This pdev->dev is assigned parent of root-hub by USB core,
@@ -1721,7 +1731,7 @@
struct msm_hsic_hcd *mehci = hcd_to_hsic(hcd);
struct msm_hsic_host_platform_data *pdata = mehci->dev->platform_data;
- if (pdata && pdata->swfi_latency)
+ if (pdata && pdata->standalone_latency)
pm_qos_remove_request(&mehci->pm_qos_req_dma);
if (mehci->peripheral_status_irq)
diff --git a/drivers/usb/otg/msm_otg.c b/drivers/usb/otg/msm_otg.c
index d04c234..23a9499 100644
--- a/drivers/usb/otg/msm_otg.c
+++ b/drivers/usb/otg/msm_otg.c
@@ -1137,11 +1137,20 @@
if (legacy_power_supply) {
/* legacy support */
- if (host_mode)
+ if (host_mode) {
power_supply_set_scope(psy, POWER_SUPPLY_SCOPE_SYSTEM);
- else
+ } else {
power_supply_set_scope(psy, POWER_SUPPLY_SCOPE_DEVICE);
- return;
+ /*
+ * VBUS comparator is disabled by PMIC charging driver
+ * when SYSTEM scope is selected. For ID_GND->ID_A
+ * transition, give 50 msec delay so that PMIC charger
+ * driver detect the VBUS and ready for accepting
+ * charging current value from USB.
+ */
+ if (test_bit(ID_A, &motg->inputs))
+ msleep(50);
+ }
} else {
motg->host_mode = host_mode;
power_supply_changed(psy);
diff --git a/drivers/video/msm/mdp4_overlay_dsi_cmd.c b/drivers/video/msm/mdp4_overlay_dsi_cmd.c
index 0015403..0289016 100644
--- a/drivers/video/msm/mdp4_overlay_dsi_cmd.c
+++ b/drivers/video/msm/mdp4_overlay_dsi_cmd.c
@@ -683,9 +683,12 @@
vctrl->wait_vsync_cnt++;
spin_unlock_irqrestore(&vctrl->spin_lock, flags);
- ret = wait_for_completion_interruptible(&vctrl->vsync_comp);
- if (ret)
- return ret;
+ ret = wait_for_completion_interruptible_timeout(&vctrl->vsync_comp,
+ msecs_to_jiffies(VSYNC_PERIOD * 4));
+ if (ret <= 0) {
+ vctrl->wait_vsync_cnt = 0;
+ return -EBUSY;
+ }
spin_lock_irqsave(&vctrl->spin_lock, flags);
vsync_tick = ktime_to_ns(vctrl->vsync_time);
diff --git a/drivers/video/msm/mdp4_overlay_dsi_video.c b/drivers/video/msm/mdp4_overlay_dsi_video.c
index 501c4e6..503376b 100644
--- a/drivers/video/msm/mdp4_overlay_dsi_video.c
+++ b/drivers/video/msm/mdp4_overlay_dsi_video.c
@@ -395,9 +395,12 @@
INIT_COMPLETION(vctrl->vsync_comp);
vctrl->wait_vsync_cnt++;
spin_unlock_irqrestore(&vctrl->spin_lock, flags);
- ret = wait_for_completion_interruptible(&vctrl->vsync_comp);
- if (ret)
- return ret;
+ ret = wait_for_completion_interruptible_timeout(&vctrl->vsync_comp,
+ msecs_to_jiffies(VSYNC_PERIOD * 4));
+ if (ret <= 0) {
+ vctrl->wait_vsync_cnt = 0;
+ return -EBUSY;
+ }
spin_lock_irqsave(&vctrl->spin_lock, flags);
vsync_tick = ktime_to_ns(vctrl->vsync_time);
diff --git a/drivers/video/msm/mdp4_overlay_dtv.c b/drivers/video/msm/mdp4_overlay_dtv.c
index 2d48781..dbf2357 100644
--- a/drivers/video/msm/mdp4_overlay_dtv.c
+++ b/drivers/video/msm/mdp4_overlay_dtv.c
@@ -337,9 +337,12 @@
vctrl->wait_vsync_cnt++;
spin_unlock_irqrestore(&vctrl->spin_lock, flags);
- ret = wait_for_completion_interruptible(&vctrl->vsync_comp);
- if (ret)
- return ret;
+ ret = wait_for_completion_interruptible_timeout(&vctrl->vsync_comp,
+ msecs_to_jiffies(VSYNC_PERIOD * 4));
+ if (ret <= 0) {
+ vctrl->wait_vsync_cnt = 0;
+ return -EBUSY;
+ }
spin_lock_irqsave(&vctrl->spin_lock, flags);
vg1fd = vctrl->vg1fd;
diff --git a/drivers/video/msm/mdp4_overlay_lcdc.c b/drivers/video/msm/mdp4_overlay_lcdc.c
index df5c262..09d1fa9 100644
--- a/drivers/video/msm/mdp4_overlay_lcdc.c
+++ b/drivers/video/msm/mdp4_overlay_lcdc.c
@@ -380,9 +380,12 @@
INIT_COMPLETION(vctrl->vsync_comp);
vctrl->wait_vsync_cnt++;
spin_unlock_irqrestore(&vctrl->spin_lock, flags);
- ret = wait_for_completion_interruptible(&vctrl->vsync_comp);
- if (ret)
- return ret;
+ ret = wait_for_completion_interruptible_timeout(&vctrl->vsync_comp,
+ msecs_to_jiffies(VSYNC_PERIOD * 4));
+ if (ret <= 0) {
+ vctrl->wait_vsync_cnt = 0;
+ return -EBUSY;
+ }
spin_lock_irqsave(&vctrl->spin_lock, flags);
vsync_tick = ktime_to_ns(vctrl->vsync_time);
diff --git a/drivers/video/msm/mdss/mdss_mdp.h b/drivers/video/msm/mdss/mdss_mdp.h
index 808babb..72871aa 100644
--- a/drivers/video/msm/mdss/mdss_mdp.h
+++ b/drivers/video/msm/mdss/mdss_mdp.h
@@ -229,6 +229,7 @@
u8 mixer_stage;
u8 is_fg;
u8 alpha;
+ u8 overfetch_disable;
u32 transp;
struct msm_fb_data_type *mfd;
diff --git a/drivers/video/msm/mdss/mdss_mdp_ctl.c b/drivers/video/msm/mdss/mdss_mdp_ctl.c
index b5f6ddf..f660375 100644
--- a/drivers/video/msm/mdss/mdss_mdp_ctl.c
+++ b/drivers/video/msm/mdss/mdss_mdp_ctl.c
@@ -707,7 +707,7 @@
static int mdss_mdp_mixer_setup(struct mdss_mdp_ctl *ctl,
struct mdss_mdp_mixer *mixer)
{
- struct mdss_mdp_pipe *pipe, *bgpipe = NULL;
+ struct mdss_mdp_pipe *pipe;
u32 off, blend_op, blend_stage;
u32 mixercfg = 0, blend_color_out = 0, bgalpha = 0;
int stage;
@@ -717,26 +717,24 @@
pr_debug("setup mixer=%d\n", mixer->num);
- for (stage = MDSS_MDP_STAGE_BASE; stage < MDSS_MDP_MAX_STAGE; stage++) {
+ pipe = mixer->stage_pipe[MDSS_MDP_STAGE_BASE];
+ if (pipe == NULL) {
+ mixercfg = MDSS_MDP_LM_BORDER_COLOR;
+ } else {
+ mixercfg = 1 << (3 * pipe->num);
+ if (pipe->src_fmt->alpha_enable)
+ bgalpha = 1;
+ }
+
+ for (stage = MDSS_MDP_STAGE_0; stage < MDSS_MDP_MAX_STAGE; stage++) {
pipe = mixer->stage_pipe[stage];
- if (pipe == NULL) {
- if (stage == MDSS_MDP_STAGE_BASE)
- mixercfg |= MDSS_MDP_LM_BORDER_COLOR;
+ if (pipe == NULL)
continue;
- }
if (stage != pipe->mixer_stage) {
mixer->stage_pipe[stage] = NULL;
continue;
}
- mixercfg |= stage << (3 * pipe->num);
-
- if (stage == MDSS_MDP_STAGE_BASE) {
- bgpipe = pipe;
- if (pipe->src_fmt->alpha_enable)
- bgalpha = 1;
- continue;
- }
blend_stage = stage - MDSS_MDP_STAGE_0;
off = MDSS_MDP_REG_LM_OFFSET(mixer->num) +
@@ -744,10 +742,8 @@
if (pipe->is_fg) {
bgalpha = 0;
- if (bgpipe) {
- mixercfg &= ~(0x7 << (3 * bgpipe->num));
- mixercfg |= MDSS_MDP_LM_BORDER_COLOR;
- }
+ mixercfg = MDSS_MDP_LM_BORDER_COLOR;
+
blend_op = (MDSS_MDP_BLEND_FG_ALPHA_FG_CONST |
MDSS_MDP_BLEND_BG_ALPHA_BG_CONST);
/* keep fg alpha */
@@ -778,6 +774,8 @@
stage);
}
+ mixercfg |= stage << (3 * pipe->num);
+
MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_LM_OP_MODE, blend_op);
MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_LM_BLEND_FG_ALPHA,
pipe->alpha);
diff --git a/drivers/video/msm/mdss/mdss_mdp_overlay.c b/drivers/video/msm/mdss/mdss_mdp_overlay.c
index e4621d4..569e381 100644
--- a/drivers/video/msm/mdss/mdss_mdp_overlay.c
+++ b/drivers/video/msm/mdss/mdss_mdp_overlay.c
@@ -318,6 +318,7 @@
pipe->is_fg = req->is_fg;
pipe->alpha = req->alpha;
pipe->transp = req->transp_mask;
+ pipe->overfetch_disable = fmt->is_yuv;
pipe->req_data = *req;
diff --git a/drivers/video/msm/mdss/mdss_mdp_pipe.c b/drivers/video/msm/mdss/mdss_mdp_pipe.c
index c28bcfd..3b04633 100644
--- a/drivers/video/msm/mdss/mdss_mdp_pipe.c
+++ b/drivers/video/msm/mdss/mdss_mdp_pipe.c
@@ -502,6 +502,11 @@
ystride1 = (pipe->src_planes.ystride[2]) |
(pipe->src_planes.ystride[3] << 16);
+ if (pipe->overfetch_disable) {
+ img_size = src_size;
+ src_xy = 0;
+ }
+
mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC_IMG_SIZE, img_size);
mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC_SIZE, src_size);
mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC_XY, src_xy);
@@ -607,6 +612,28 @@
return 0;
}
+static void mdss_mdp_addr_add_offset(struct mdss_mdp_pipe *pipe,
+ struct mdss_mdp_data *data)
+{
+ data->p[0].addr += pipe->src.x +
+ (pipe->src.y * pipe->src_planes.ystride[0]);
+ if (data->num_planes > 1) {
+ u8 hmap[] = { 1, 2, 1, 2 };
+ u8 vmap[] = { 1, 1, 2, 2 };
+ u16 xoff = pipe->src.x / hmap[pipe->src_fmt->chroma_sample];
+ u16 yoff = pipe->src.y / vmap[pipe->src_fmt->chroma_sample];
+
+ if (data->num_planes == 2) /* pseudo planar */
+ xoff *= 2;
+ data->p[1].addr += xoff + (yoff * pipe->src_planes.ystride[1]);
+
+ if (data->num_planes > 2) { /* planar */
+ data->p[2].addr += xoff +
+ (yoff * pipe->src_planes.ystride[2]);
+ }
+ }
+}
+
static int mdss_mdp_src_addr_setup(struct mdss_mdp_pipe *pipe,
struct mdss_mdp_data *data)
{
@@ -622,6 +649,9 @@
if (ret)
return ret;
+ if (pipe->overfetch_disable)
+ mdss_mdp_addr_add_offset(pipe, data);
+
/* planar format expects YCbCr, swap chroma planes if YCrCb */
if (!is_rot && (pipe->src_fmt->fetch_planes == MDSS_MDP_PLANE_PLANAR) &&
(pipe->src_fmt->element[0] == C2_R_Cr))
diff --git a/drivers/video/msm/vidc/1080p/ddl/vcd_ddl.c b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl.c
index d5b195d..a82feb9 100644
--- a/drivers/video/msm/vidc/1080p/ddl/vcd_ddl.c
+++ b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl.c
@@ -198,7 +198,8 @@
ddl->client_state = DDL_CLIENT_OPEN;
ddl->codec_data.hdr.decoding = decoding;
ddl->decoding = decoding;
- ddl_set_default_meta_data_hdr(ddl);
+ if (!res_trk_check_for_sec_session())
+ ddl_set_default_meta_data_hdr(ddl);
ddl_set_initial_default_values(ddl);
*ddl_handle = (u32 *) ddl;
} else {
diff --git a/include/linux/diagchar.h b/include/linux/diagchar.h
index bb5f394..45d7a3e 100644
--- a/include/linux/diagchar.h
+++ b/include/linux/diagchar.h
@@ -43,6 +43,7 @@
#define DIAG_IOCTL_DCI_REG 23
#define DIAG_IOCTL_DCI_STREAM_INIT 24
#define DIAG_IOCTL_DCI_HEALTH_STATS 25
+#define DIAG_IOCTL_REMOTE_DEV 32
/* PC Tools IDs */
#define APQ8060_TOOLS_ID 4062
diff --git a/include/linux/i2c/ti_drv2667.h b/include/linux/i2c/ti_drv2667.h
new file mode 100644
index 0000000..0ae0e5e
--- /dev/null
+++ b/include/linux/i2c/ti_drv2667.h
@@ -0,0 +1,26 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __TI_DRV2667__
+
+#define DRV2667_WAV_SEQ_LEN 11
+
+struct drv2667_pdata {
+ const char *name;
+ u8 mode;
+ /* support one waveform for now */
+ u8 wav_seq[DRV2667_WAV_SEQ_LEN];
+ u8 gain;
+ u8 idle_timeout_ms;
+ u32 max_runtime_ms;
+};
+#endif
diff --git a/include/linux/mfd/pm8xxx/batterydata-lib.h b/include/linux/mfd/pm8xxx/batterydata-lib.h
index c55e47e..afa1843 100644
--- a/include/linux/mfd/pm8xxx/batterydata-lib.h
+++ b/include/linux/mfd/pm8xxx/batterydata-lib.h
@@ -102,7 +102,8 @@
};
#if defined(CONFIG_PM8921_BMS) || \
- defined(CONFIG_PM8921_BMS_MODULE)
+ defined(CONFIG_PM8921_BMS_MODULE) || \
+ defined(CONFIG_QPNP_BMS)
extern struct bms_battery_data palladium_1500_data;
extern struct bms_battery_data desay_5200_data;
diff --git a/include/linux/mfd/pm8xxx/ccadc.h b/include/linux/mfd/pm8xxx/ccadc.h
index 29f7a62..fc31f89 100644
--- a/include/linux/mfd/pm8xxx/ccadc.h
+++ b/include/linux/mfd/pm8xxx/ccadc.h
@@ -19,11 +19,11 @@
/**
* struct pm8xxx_ccadc_platform_data -
- * @r_sense: sense resistor value in (mOhms)
+ * @r_sense_uohm: sense resistor value in (micro Ohms)
* @calib_delay_ms: how often should the adc calculate gain and offset
*/
struct pm8xxx_ccadc_platform_data {
- int r_sense;
+ int r_sense_uohm;
unsigned int calib_delay_ms;
};
diff --git a/include/linux/mfd/pm8xxx/pm8921-bms.h b/include/linux/mfd/pm8xxx/pm8921-bms.h
index ba70c96..82ec57d 100644
--- a/include/linux/mfd/pm8xxx/pm8921-bms.h
+++ b/include/linux/mfd/pm8xxx/pm8921-bms.h
@@ -30,7 +30,7 @@
/**
* struct pm8921_bms_platform_data -
* @batt_type: allows to force chose battery calibration data
- * @r_sense: sense resistor value in (mOhms)
+ * @r_sense_uohm: sense resistor value in (micro Ohms)
* @i_test: current at which the unusable charger cutoff is to be
* calculated or the peak system current (mA)
* @v_cutoff: the loaded voltage at which the battery
@@ -41,7 +41,7 @@
struct pm8921_bms_platform_data {
struct pm8xxx_bms_core_data bms_cdata;
enum battery_type battery_type;
- unsigned int r_sense;
+ int r_sense_uohm;
unsigned int i_test;
unsigned int v_cutoff;
unsigned int max_voltage_uv;
diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h
index 89a8421..e0d9072 100644
--- a/include/linux/nl80211.h
+++ b/include/linux/nl80211.h
@@ -548,6 +548,14 @@
* @NL80211_CMD_SET_NOACK_MAP: sets a bitmap for the individual TIDs whether
* No Acknowledgement Policy should be applied.
*
+ * @NL80211_CMD_UPDATE_FT_IES: Pass down the most up-to-date Fast Transition
+ * Information Element to the WLAN driver
+ *
+ * @NL80211_CMD_FT_EVENT: Send a Fast transition event from the WLAN driver
+ * to the supplicant. This will carry the target AP's MAC address along
+ * with the relevant Information Elements. This event to report received
+ * FT IEs( MDIE, FTIE,RSN IE, TIE, RICIE).
+ *
* @NL80211_CMD_MAX: highest used command number
* @__NL80211_CMD_AFTER_LAST: internal use
*/
@@ -689,6 +697,9 @@
NL80211_CMD_SET_NOACK_MAP,
+ NL80211_CMD_UPDATE_FT_IES,
+ NL80211_CMD_FT_EVENT,
+
/* add new commands above here */
/* used to define NL80211_CMD_MAX below */
@@ -1264,6 +1275,10 @@
* @NL80211_ATTR_BG_SCAN_PERIOD: Background scan period in seconds
* or 0 to disable background scan.
*
+ * @NL80211_ATTR_MDID: Mobility Domain Identifier
+ *
+ * @NL80211_ATTR_IE_RIC: Resource Information Container Information Element
+ *
* @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use
*/
@@ -1515,6 +1530,9 @@
NL80211_ATTR_BG_SCAN_PERIOD,
+ NL80211_ATTR_MDID,
+ NL80211_ATTR_IE_RIC,
+
/* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST,
diff --git a/include/linux/usb/msm_hsusb.h b/include/linux/usb/msm_hsusb.h
index 578b9f9..d6fbc64 100644
--- a/include/linux/usb/msm_hsusb.h
+++ b/include/linux/usb/msm_hsusb.h
@@ -397,7 +397,12 @@
unsigned data;
struct msm_bus_scale_pdata *bus_scale_table;
unsigned log2_irq_thresh;
+
+ /*swfi latency is required while driving resume on to the bus */
u32 swfi_latency;
+
+ /*standalone latency is required when HSCI is active*/
+ u32 standalone_latency;
};
struct msm_usb_host_platform_data {
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index a57c9f9..5c1daf3 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -1318,6 +1318,21 @@
};
/**
+ * struct cfg80211_update_ft_ies_params - FT IE Information
+ *
+ * This structure provides information needed to update the fast transition IE
+ *
+ * @md: The Mobility Domain ID, 2 Octet value
+ * @ie: Fast Transition IEs
+ * @ie_len: Length of ft_ie in octets
+ */
+struct cfg80211_update_ft_ies_params {
+ u16 md;
+ u8 *ie;
+ size_t ie_len;
+};
+
+/**
* struct cfg80211_ops - backend description for wireless configuration
*
* This struct is registered by fullmac card drivers and/or wireless stacks
@@ -1697,6 +1712,8 @@
u16 noack_map);
struct ieee80211_channel *(*get_channel)(struct wiphy *wiphy);
+ int (*update_ft_ies)(struct wiphy *wiphy, struct net_device *dev,
+ struct cfg80211_update_ft_ies_params *ftie);
};
/*
@@ -3348,6 +3365,32 @@
*/
u16 cfg80211_calculate_bitrate(struct rate_info *rate);
+/**
+ * struct cfg80211_ft_event - FT Information Elements
+ * @dev: network device
+ * @ies: FT IEs
+ * @ies_len: length of the FT IE in bytes
+ * @target_ap: target AP's MAC address
+ * @ric_ies: RIC IE
+ * @ric_ies_len: length of the RIC IE in bytes
+ */
+struct cfg80211_ft_event_params {
+ u8 *ies;
+ size_t ies_len;
+ u8 target_ap[ETH_ALEN];
+ u8 *ric_ies;
+ size_t ric_ies_len;
+};
+
+/**
+ * cfg80211_ft_event - notify userspace about FT IE and RIC IE
+ * @dev: network device
+ * @cfg80211_ft_event_params: IE information
+ */
+int cfg80211_ft_event(struct net_device *dev,
+ struct cfg80211_ft_event_params ft_event);
+
+
/* Logging, debugging and troubleshooting/diagnostic helpers. */
/* wiphy_printk helpers, similar to dev_printk */
diff --git a/include/sound/msm-dai-q6-v2.h b/include/sound/msm-dai-q6-v2.h
index 3d5ffdd..6c60318 100644
--- a/include/sound/msm-dai-q6-v2.h
+++ b/include/sound/msm-dai-q6-v2.h
@@ -20,6 +20,10 @@
#define MSM_MI2S_SD3 (1 << 3)
#define MSM_MI2S_CAP_RX 0
#define MSM_MI2S_CAP_TX 1
+#define MSM_PRIM_MI2S 0
+#define MSM_SEC_MI2S 1
+#define MSM_TERT_MI2S 2
+#define MSM_QUAD_MI2S 3
struct msm_dai_auxpcm_pdata {
const char *clk;
@@ -35,6 +39,11 @@
int pcm_clk_rate;
};
+struct msm_mi2s_pdata {
+ u16 rx_sd_lines;
+ u16 tx_sd_lines;
+};
+
struct msm_i2s_data {
u32 capability; /* RX or TX */
u16 sd_lines;
diff --git a/include/sound/q6afe-v2.h b/include/sound/q6afe-v2.h
index e107130..1324f8a 100644
--- a/include/sound/q6afe-v2.h
+++ b/include/sound/q6afe-v2.h
@@ -16,6 +16,7 @@
#define IN 0x000
#define OUT 0x001
#define MSM_AFE_MONO 0
+#define MSM_AFE_CH_STEREO 1
#define MSM_AFE_MONO_RIGHT 1
#define MSM_AFE_MONO_LEFT 2
#define MSM_AFE_STEREO 3
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 22a4dbe..fa2469e 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -1426,6 +1426,11 @@
} else {
u16 flags;
+ if (!(pi->conn)) {
+ kfree_skb(skb);
+ return;
+ }
+
bt_cb(skb)->force_active = pi->force_active;
BT_DBG("Sending on BR/EDR connection %p", pi->conn->hcon);
diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
index f5a7ac3..dd99041 100644
--- a/net/wireless/mlme.c
+++ b/net/wireless/mlme.c
@@ -954,3 +954,16 @@
return nl80211_unexpected_4addr_frame(dev, addr, gfp);
}
EXPORT_SYMBOL(cfg80211_rx_unexpected_4addr_frame);
+
+int cfg80211_ft_event(struct net_device *dev,
+ struct cfg80211_ft_event_params ft_event)
+{
+ int err = 0;
+ struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
+ struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+
+ nl80211_ft_event(rdev, dev, ft_event);
+
+ return err;
+}
+EXPORT_SYMBOL(cfg80211_ft_event);
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index e322d4d..0410707 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -206,6 +206,9 @@
[NL80211_ATTR_NOACK_MAP] = { .type = NLA_U16 },
[NL80211_ATTR_INACTIVITY_TIMEOUT] = { .type = NLA_U16 },
[NL80211_ATTR_BG_SCAN_PERIOD] = { .type = NLA_U16 },
+ [NL80211_ATTR_MDID] = { .type = NLA_U16 },
+ [NL80211_ATTR_IE_RIC] = { .type = NLA_BINARY,
+ .len = IEEE80211_MAX_DATA_LEN },
};
/* policy for the key attributes */
@@ -6299,6 +6302,26 @@
return 0;
}
+static int nl80211_update_ft_ies(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct cfg80211_update_ft_ies_params ft_params;
+ struct net_device *dev = info->user_ptr[1];
+
+ if (!info->attrs[NL80211_ATTR_MDID])
+ return -EINVAL;
+
+ ft_params.md = nla_get_u16(info->attrs[NL80211_ATTR_MDID]);
+
+ if (!info->attrs[NL80211_ATTR_IE])
+ return -EINVAL;
+
+ ft_params.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
+ ft_params.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
+
+ return rdev->ops->update_ft_ies(&rdev->wiphy, dev, &ft_params);
+}
+
#define NL80211_FLAG_NEED_WIPHY 0x01
#define NL80211_FLAG_NEED_NETDEV 0x02
#define NL80211_FLAG_NEED_RTNL 0x04
@@ -6887,6 +6910,14 @@
.internal_flags = NL80211_FLAG_NEED_NETDEV |
NL80211_FLAG_NEED_RTNL,
},
+ {
+ .cmd = NL80211_CMD_UPDATE_FT_IES,
+ .doit = nl80211_update_ft_ies,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
};
@@ -8080,6 +8111,47 @@
.notifier_call = nl80211_netlink_notify,
};
+void nl80211_ft_event(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev, struct cfg80211_ft_event_params ft_event)
+{
+ struct sk_buff *msg;
+ void *hdr;
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!msg)
+ return;
+
+ hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FT_EVENT);
+ if (!hdr) {
+ nlmsg_free(msg);
+ return;
+ }
+
+ NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
+ if (ft_event.target_ap)
+ NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, ft_event.target_ap);
+ if (ft_event.ies)
+ NLA_PUT(msg, NL80211_ATTR_IE, ft_event.ies_len, ft_event.ies);
+ if (ft_event.ric_ies)
+ NLA_PUT(msg, NL80211_ATTR_IE_RIC, ft_event.ric_ies_len,
+ ft_event.ric_ies);
+
+ if (genlmsg_end(msg, hdr) < 0) {
+ nlmsg_free(msg);
+ return;
+ }
+
+ genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
+ nl80211_mlme_mcgrp.id, GFP_KERNEL);
+ return;
+
+ nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+ nlmsg_free(msg);
+}
+
+
/* initialisation/exit functions */
int nl80211_init(void)
diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h
index 4ffe50d..ffd4c8a 100644
--- a/net/wireless/nl80211.h
+++ b/net/wireless/nl80211.h
@@ -123,4 +123,8 @@
bool nl80211_unexpected_4addr_frame(struct net_device *dev,
const u8 *addr, gfp_t gfp);
+void nl80211_ft_event(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev,
+ struct cfg80211_ft_event_params ft_event);
+
#endif /* __NET_WIRELESS_NL80211_H */
diff --git a/sound/soc/msm/mdm9615.c b/sound/soc/msm/mdm9615.c
index 0125c1a..59e220d 100644
--- a/sound/soc/msm/mdm9615.c
+++ b/sound/soc/msm/mdm9615.c
@@ -2028,6 +2028,15 @@
.be_id = MSM_FRONTEND_DAI_DTMF_RX,
.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
},
+ {
+ .name = "DTMF TX",
+ .stream_name = "DTMF TX",
+ .cpu_dai_name = "msm-dai-stub",
+ .platform_name = "msm-pcm-dtmf",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .ignore_suspend = 1,
+ },
/* Backend BT DAI Links */
{
diff --git a/sound/soc/msm/msm-dai-fe.c b/sound/soc/msm/msm-dai-fe.c
index 51c9ed7..4165254 100644
--- a/sound/soc/msm/msm-dai-fe.c
+++ b/sound/soc/msm/msm-dai-fe.c
@@ -498,15 +498,25 @@
.name = "VoLTE",
},
{
+ .playback = {
+ .stream_name = "MI2S_RX_HOSTLESS Playback",
+ .aif_name = "MI2S_DL_HL",
+ .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 = "MI2S_TX Hostless Capture",
.aif_name = "MI2S_UL_HL",
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.channels_min = 1,
- .channels_max = 8,
- .rate_min = 8000,
- .rate_max = 48000,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
},
.ops = &msm_fe_dai_ops,
.name = "MI2S_TX_HOSTLESS",
diff --git a/sound/soc/msm/msm-pcm-dtmf.c b/sound/soc/msm/msm-pcm-dtmf.c
index 04ab7a9..94cc1ca 100644
--- a/sound/soc/msm/msm-pcm-dtmf.c
+++ b/sound/soc/msm/msm-pcm-dtmf.c
@@ -12,13 +12,86 @@
#include <linux/init.h>
#include <linux/module.h>
+#include <linux/time.h>
+#include <linux/wait.h>
#include <linux/platform_device.h>
-#include <linux/of_device.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
#include <sound/core.h>
#include <sound/soc.h>
#include <sound/pcm.h>
#include <sound/q6afe.h>
+#include "msm-pcm-q6.h"
+#include "msm-pcm-routing.h"
+#include "qdsp6/q6voice.h"
+
+enum {
+ DTMF_IN_RX,
+ DTMF_IN_TX,
+};
+
+enum format {
+ FORMAT_S16_LE = 2
+};
+
+struct dtmf_det_info {
+ char session[MAX_SESSION_NAME_LEN];
+ uint8_t dir;
+ uint16_t high_freq;
+ uint16_t low_freq;
+};
+
+struct dtmf_buf_node {
+ struct list_head list;
+ struct dtmf_det_info dtmf_det_pkt;
+};
+
+enum dtmf_state {
+ DTMF_GEN_RX_STOPPED,
+ DTMF_GEN_RX_STARTED,
+};
+
+#define DTMF_MAX_Q_LEN 10
+#define DTMF_PKT_SIZE sizeof(struct dtmf_det_info)
+
+struct dtmf_drv_info {
+ enum dtmf_state state;
+ struct snd_pcm_substream *capture_substream;
+
+ struct list_head out_queue;
+ struct list_head free_out_queue;
+
+ wait_queue_head_t out_wait;
+
+ struct mutex lock;
+ spinlock_t dsp_lock;
+
+ uint8_t capture_start;
+ uint8_t capture_instance;
+
+ unsigned int pcm_capture_size;
+ unsigned int pcm_capture_count;
+ unsigned int pcm_capture_irq_pos;
+ unsigned int pcm_capture_buf_pos;
+};
+
+static struct snd_pcm_hardware msm_pcm_hardware = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 1,
+ .buffer_bytes_max = (sizeof(struct dtmf_buf_node) * DTMF_MAX_Q_LEN),
+ .period_bytes_min = DTMF_PKT_SIZE,
+ .period_bytes_max = DTMF_PKT_SIZE,
+ .periods_min = DTMF_MAX_Q_LEN,
+ .periods_max = DTMF_MAX_Q_LEN,
+ .fifo_size = 0,
+};
+
static int msm_dtmf_rx_generate_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -33,7 +106,7 @@
return 0;
}
-static int msm_dtmf_rx_generate_get(struct snd_kcontrol *kcontrol,
+static int msm_dtmf_rx_generate_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
pr_debug("%s:\n", __func__);
@@ -41,11 +114,55 @@
return 0;
}
+static int msm_dtmf_detect_voice_rx_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int enable = ucontrol->value.integer.value[0];
+
+ pr_debug("%s: enable=%d\n", __func__, enable);
+ voc_enable_dtmf_rx_detection(voc_get_session_id(VOICE_SESSION_NAME),
+ enable);
+
+ return 0;
+}
+
+static int msm_dtmf_detect_voice_rx_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = 0;
+ return 0;
+}
+
+static int msm_dtmf_detect_volte_rx_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int enable = ucontrol->value.integer.value[0];
+
+ pr_debug("%s: enable=%d\n", __func__, enable);
+ voc_enable_dtmf_rx_detection(voc_get_session_id(VOLTE_SESSION_NAME),
+ enable);
+
+ return 0;
+}
+
+static int msm_dtmf_detect_volte_rx_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = 0;
+ return 0;
+}
+
static struct snd_kcontrol_new msm_dtmf_controls[] = {
SOC_SINGLE_MULTI_EXT("DTMF_Generate Rx Low High Duration Gain",
SND_SOC_NOPM, 0, 5000, 0, 4,
msm_dtmf_rx_generate_get,
msm_dtmf_rx_generate_put),
+ SOC_SINGLE_EXT("DTMF_Detect Rx Voice enable", SND_SOC_NOPM, 0, 1, 0,
+ msm_dtmf_detect_voice_rx_get,
+ msm_dtmf_detect_voice_rx_put),
+ SOC_SINGLE_EXT("DTMF_Detect Rx VoLTE enable", SND_SOC_NOPM, 0, 1, 0,
+ msm_dtmf_detect_volte_rx_get,
+ msm_dtmf_detect_volte_rx_put),
};
static int msm_pcm_dtmf_probe(struct snd_soc_platform *platform)
@@ -55,16 +172,384 @@
return 0;
}
-static struct snd_pcm_ops msm_pcm_ops = {};
+static void dtmf_rx_detected_cb(uint8_t *pkt,
+ char *session,
+ void *private_data)
+{
+ struct dtmf_buf_node *buf_node = NULL;
+ struct vss_istream_evt_rx_dtmf_detected *dtmf_det_pkt =
+ (struct vss_istream_evt_rx_dtmf_detected *)pkt;
+ struct dtmf_drv_info *prtd = private_data;
+ unsigned long dsp_flags;
+
+ pr_debug("%s\n", __func__);
+ if (prtd->capture_substream == NULL)
+ return;
+
+ /* Copy dtmf detected info into out_queue. */
+ spin_lock_irqsave(&prtd->dsp_lock, dsp_flags);
+ /* discarding dtmf detection info till start is received */
+ if (!list_empty(&prtd->free_out_queue) && prtd->capture_start) {
+ buf_node = list_first_entry(&prtd->free_out_queue,
+ struct dtmf_buf_node, list);
+ list_del(&buf_node->list);
+ buf_node->dtmf_det_pkt.high_freq = dtmf_det_pkt->high_freq;
+ buf_node->dtmf_det_pkt.low_freq = dtmf_det_pkt->low_freq;
+ if (session != NULL)
+ strlcpy(buf_node->dtmf_det_pkt.session,
+ session, MAX_SESSION_NAME_LEN);
+
+ buf_node->dtmf_det_pkt.dir = DTMF_IN_RX;
+ pr_debug("high =%d, low=%d session=%s\n",
+ buf_node->dtmf_det_pkt.high_freq,
+ buf_node->dtmf_det_pkt.low_freq,
+ buf_node->dtmf_det_pkt.session);
+ list_add_tail(&buf_node->list, &prtd->out_queue);
+ prtd->pcm_capture_irq_pos += prtd->pcm_capture_count;
+ spin_unlock_irqrestore(&prtd->dsp_lock, dsp_flags);
+ snd_pcm_period_elapsed(prtd->capture_substream);
+ } else {
+ spin_unlock_irqrestore(&prtd->dsp_lock, dsp_flags);
+ pr_err("DTMF detection pkt in Rx dropped, no free node available\n");
+ }
+
+ wake_up(&prtd->out_wait);
+}
+
+static int msm_pcm_capture_copy(struct snd_pcm_substream *substream,
+ int channel, snd_pcm_uframes_t hwoff,
+ void __user *buf, snd_pcm_uframes_t frames)
+{
+ int ret = 0;
+ int count = 0;
+ struct dtmf_buf_node *buf_node = NULL;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct dtmf_drv_info *prtd = runtime->private_data;
+ unsigned long dsp_flags;
+
+ count = frames_to_bytes(runtime, frames);
+
+ ret = wait_event_interruptible_timeout(prtd->out_wait,
+ (!list_empty(&prtd->out_queue)),
+ 1 * HZ);
+
+ if (ret > 0) {
+ if (count <= DTMF_PKT_SIZE) {
+ spin_lock_irqsave(&prtd->dsp_lock, dsp_flags);
+ buf_node = list_first_entry(&prtd->out_queue,
+ struct dtmf_buf_node, list);
+ list_del(&buf_node->list);
+ spin_unlock_irqrestore(&prtd->dsp_lock, dsp_flags);
+ ret = copy_to_user(buf,
+ &buf_node->dtmf_det_pkt,
+ count);
+ if (ret) {
+ pr_err("%s: Copy to user retuned %d\n",
+ __func__, ret);
+ ret = -EFAULT;
+ }
+ spin_lock_irqsave(&prtd->dsp_lock, dsp_flags);
+ list_add_tail(&buf_node->list,
+ &prtd->free_out_queue);
+ spin_unlock_irqrestore(&prtd->dsp_lock, dsp_flags);
+
+ } else {
+ pr_err("%s: Read count %d > DTMF_PKT_SIZE\n",
+ __func__, count);
+ ret = -ENOMEM;
+ }
+ } else if (ret == 0) {
+ pr_err("%s: No UL data available\n", __func__);
+ ret = -ETIMEDOUT;
+ } else {
+ pr_err("%s: Read was interrupted\n", __func__);
+ ret = -ERESTARTSYS;
+ }
+ return ret;
+}
+
+static int msm_pcm_copy(struct snd_pcm_substream *substream, int a,
+ snd_pcm_uframes_t hwoff, void __user *buf, snd_pcm_uframes_t frames)
+{
+ int ret = 0;
+ pr_debug("%s() DTMF\n", __func__);
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ ret = msm_pcm_capture_copy(substream, a, hwoff, buf, frames);
+
+ return ret;
+}
+
+static int msm_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct dtmf_drv_info *prtd = NULL;
+ int ret = 0;
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ prtd = kzalloc(sizeof(struct dtmf_drv_info), GFP_KERNEL);
+
+ if (prtd == NULL) {
+ pr_err("Failed to allocate memory for msm_audio\n");
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ mutex_init(&prtd->lock);
+ spin_lock_init(&prtd->dsp_lock);
+ init_waitqueue_head(&prtd->out_wait);
+ INIT_LIST_HEAD(&prtd->out_queue);
+ INIT_LIST_HEAD(&prtd->free_out_queue);
+
+ runtime->hw = msm_pcm_hardware;
+
+ ret = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0)
+ pr_info("snd_pcm_hw_constraint_integer failed\n");
+
+ prtd->capture_substream = substream;
+ prtd->capture_instance++;
+ runtime->private_data = prtd;
+ }
+
+done:
+ return ret;
+}
+
+static int msm_pcm_close(struct snd_pcm_substream *substream)
+{
+ int ret = 0;
+ struct list_head *ptr = NULL;
+ struct list_head *next = NULL;
+ struct dtmf_buf_node *buf_node = NULL;
+ struct snd_dma_buffer *c_dma_buf;
+ struct snd_pcm_substream *c_substream;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct dtmf_drv_info *prtd = runtime->private_data;
+ unsigned long dsp_flags;
+
+ pr_debug("%s() DTMF\n", __func__);
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ mutex_lock(&prtd->lock);
+ wake_up(&prtd->out_wait);
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ prtd->capture_instance--;
+
+ if (!prtd->capture_instance) {
+ if (prtd->state == DTMF_GEN_RX_STARTED) {
+ prtd->state = DTMF_GEN_RX_STOPPED;
+ voc_disable_dtmf_det_on_active_sessions();
+ voc_register_dtmf_rx_detection_cb(NULL, NULL);
+ }
+ /* release all buffer */
+ /* release out_queue and free_out_queue */
+ pr_debug("release all buffer\n");
+ c_substream = prtd->capture_substream;
+ if (c_substream == NULL) {
+ pr_debug("c_substream is NULL\n");
+ mutex_unlock(&prtd->lock);
+ return -EINVAL;
+ }
+
+ c_dma_buf = &c_substream->dma_buffer;
+ if (c_dma_buf == NULL) {
+ pr_debug("c_dma_buf is NULL.\n");
+ mutex_unlock(&prtd->lock);
+ return -EINVAL;
+ }
+
+ if (c_dma_buf->area != NULL) {
+ spin_lock_irqsave(&prtd->dsp_lock, dsp_flags);
+ list_for_each_safe(ptr, next,
+ &prtd->out_queue) {
+ buf_node = list_entry(ptr,
+ struct dtmf_buf_node, list);
+ list_del(&buf_node->list);
+ }
+
+ list_for_each_safe(ptr, next,
+ &prtd->free_out_queue) {
+ buf_node = list_entry(ptr,
+ struct dtmf_buf_node, list);
+ list_del(&buf_node->list);
+ }
+
+ spin_unlock_irqrestore(&prtd->dsp_lock,
+ dsp_flags);
+ dma_free_coherent(c_substream->pcm->card->dev,
+ runtime->hw.buffer_bytes_max,
+ c_dma_buf->area,
+ c_dma_buf->addr);
+ c_dma_buf->area = NULL;
+ }
+ }
+ prtd->capture_substream = NULL;
+ mutex_unlock(&prtd->lock);
+ }
+
+ return ret;
+}
+
+static int msm_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct dtmf_drv_info *prtd = runtime->private_data;
+ struct snd_dma_buffer *dma_buf = &substream->dma_buffer;
+ struct dtmf_buf_node *buf_node = NULL;
+ int i = 0, offset = 0;
+ int ret = 0;
+
+ pr_debug("%s: DTMF\n", __func__);
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ mutex_lock(&prtd->lock);
+ dma_buf->dev.type = SNDRV_DMA_TYPE_DEV;
+ dma_buf->dev.dev = substream->pcm->card->dev;
+ dma_buf->private_data = NULL;
+
+ dma_buf->area = dma_alloc_coherent(substream->pcm->card->dev,
+ runtime->hw.buffer_bytes_max,
+ &dma_buf->addr, GFP_KERNEL);
+ if (!dma_buf->area) {
+ pr_err("%s:MSM DTMF dma_alloc failed\n", __func__);
+ mutex_unlock(&prtd->lock);
+ return -ENOMEM;
+ }
+
+ dma_buf->bytes = runtime->hw.buffer_bytes_max;
+ memset(dma_buf->area, 0, runtime->hw.buffer_bytes_max);
+
+ for (i = 0; i < DTMF_MAX_Q_LEN; i++) {
+ pr_debug("node =%d\n", i);
+ buf_node = (void *) dma_buf->area + offset;
+ list_add_tail(&buf_node->list,
+ &prtd->free_out_queue);
+ offset = offset + sizeof(struct dtmf_buf_node);
+ }
+
+ snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+ mutex_unlock(&prtd->lock);
+ }
+
+ return ret;
+}
+
+static int msm_pcm_capture_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct dtmf_drv_info *prtd = runtime->private_data;
+
+ pr_debug("%s: DTMF\n", __func__);
+ prtd->pcm_capture_size = snd_pcm_lib_buffer_bytes(substream);
+ prtd->pcm_capture_count = snd_pcm_lib_period_bytes(substream);
+ prtd->pcm_capture_irq_pos = 0;
+ prtd->pcm_capture_buf_pos = 0;
+ return 0;
+}
+
+static int msm_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct dtmf_drv_info *prtd = runtime->private_data;
+
+ pr_debug("%s: DTMF\n", __func__);
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ mutex_lock(&prtd->lock);
+
+ msm_pcm_capture_prepare(substream);
+
+ if (runtime->format != FORMAT_S16_LE) {
+ pr_err("format:%u doesnt match %d\n",
+ (uint32_t)runtime->format, FORMAT_S16_LE);
+ mutex_unlock(&prtd->lock);
+ return -EINVAL;
+ }
+
+ if (prtd->capture_instance &&
+ (prtd->state != DTMF_GEN_RX_STARTED)) {
+ voc_register_dtmf_rx_detection_cb(dtmf_rx_detected_cb,
+ prtd);
+ prtd->state = DTMF_GEN_RX_STARTED;
+ }
+ mutex_unlock(&prtd->lock);
+ }
+
+ return 0;
+}
+
+static int msm_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ int ret = 0;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct dtmf_drv_info *prtd = runtime->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ pr_debug("%s: Trigger start\n", __func__);
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ prtd->capture_start = 1;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ pr_debug("SNDRV_PCM_TRIGGER_STOP\n");
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ prtd->capture_start = 0;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static snd_pcm_uframes_t msm_pcm_pointer(struct snd_pcm_substream *substream)
+{
+ snd_pcm_uframes_t ret = 0;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct dtmf_drv_info *prtd = runtime->private_data;
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ if (prtd->pcm_capture_irq_pos >= prtd->pcm_capture_size)
+ prtd->pcm_capture_irq_pos = 0;
+ ret = bytes_to_frames(runtime, (prtd->pcm_capture_irq_pos));
+ }
+
+ return ret;
+}
+
+static struct snd_pcm_ops msm_pcm_ops = {
+ .open = msm_pcm_open,
+ .copy = msm_pcm_copy,
+ .hw_params = msm_pcm_hw_params,
+ .close = msm_pcm_close,
+ .prepare = msm_pcm_prepare,
+ .trigger = msm_pcm_trigger,
+ .pointer = msm_pcm_pointer,
+};
+
+static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_card *card = rtd->card->snd_card;
+ int ret = 0;
+
+ if (!card->dev->coherent_dma_mask)
+ card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+ return ret;
+}
static struct snd_soc_platform_driver msm_soc_platform = {
.ops = &msm_pcm_ops,
+ .pcm_new = msm_asoc_pcm_new,
.probe = msm_pcm_dtmf_probe,
};
static __devinit int msm_pcm_probe(struct platform_device *pdev)
{
- pr_debug("%s: dev name %s\n", __func__, dev_name(&pdev->dev));
return snd_soc_register_platform(&pdev->dev,
&msm_soc_platform);
}
diff --git a/sound/soc/msm/msm-pcm-routing.c b/sound/soc/msm/msm-pcm-routing.c
index 1ddb6c9..23eee9d 100644
--- a/sound/soc/msm/msm-pcm-routing.c
+++ b/sound/soc/msm/msm-pcm-routing.c
@@ -1597,8 +1597,8 @@
MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
msm_routing_put_voice_stub_mixer),
SOC_SINGLE_EXT("DTMF", MSM_BACKEND_DAI_PRI_I2S_RX,
- MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_stub_mixer,
- msm_routing_put_voice_stub_mixer),
+ MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
};
static const struct snd_kcontrol_new sec_i2s_rx_voice_mixer_controls[] = {
diff --git a/sound/soc/msm/qdsp6/q6voice.c b/sound/soc/msm/qdsp6/q6voice.c
index cb2e39b..3b1e722 100644
--- a/sound/soc/msm/qdsp6/q6voice.c
+++ b/sound/soc/msm/qdsp6/q6voice.c
@@ -146,6 +146,21 @@
v->cvp_handle = cvp_handle;
}
+char *voc_get_session_name(u16 session_id)
+{
+ char *session_name = NULL;
+
+ if (session_id == common.voice[VOC_PATH_PASSIVE].session_id) {
+ session_name = VOICE_SESSION_NAME;
+ } else if (session_id ==
+ common.voice[VOC_PATH_VOLTE_PASSIVE].session_id) {
+ session_name = VOLTE_SESSION_NAME;
+ } else if (session_id == common.voice[VOC_PATH_FULL].session_id) {
+ session_name = VOIP_SESSION_NAME;
+ }
+ return session_name;
+}
+
uint16_t voc_get_session_id(char *name)
{
u16 session_id = 0;
@@ -894,6 +909,105 @@
return 0;
}
+static int voice_send_dtmf_rx_detection_cmd(struct voice_data *v,
+ uint32_t enable)
+{
+ int ret = 0;
+ void *apr_cvs;
+ u16 cvs_handle;
+ struct cvs_set_rx_dtmf_detection_cmd cvs_dtmf_rx_detection;
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+ return -EINVAL;
+ }
+ apr_cvs = common.apr_q6_cvs;
+
+ if (!apr_cvs) {
+ pr_err("%s: apr_cvs is NULL.\n", __func__);
+ return -EINVAL;
+ }
+
+ cvs_handle = voice_get_cvs_handle(v);
+
+ /* Set SET_DTMF_RX_DETECTION */
+ cvs_dtmf_rx_detection.hdr.hdr_field =
+ APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ cvs_dtmf_rx_detection.hdr.pkt_size =
+ APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(cvs_dtmf_rx_detection) - APR_HDR_SIZE);
+ cvs_dtmf_rx_detection.hdr.src_port = v->session_id;
+ cvs_dtmf_rx_detection.hdr.dest_port = cvs_handle;
+ cvs_dtmf_rx_detection.hdr.token = 0;
+ cvs_dtmf_rx_detection.hdr.opcode =
+ VSS_ISTREAM_CMD_SET_RX_DTMF_DETECTION;
+ cvs_dtmf_rx_detection.cvs_dtmf_det.enable = enable;
+
+ v->cvs_state = CMD_STATUS_FAIL;
+
+ ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_dtmf_rx_detection);
+ if (ret < 0) {
+ pr_err("%s: Error %d sending SET_DTMF_RX_DETECTION\n",
+ __func__,
+ ret);
+ return -EINVAL;
+ }
+
+ ret = wait_event_timeout(v->cvs_wait,
+ (v->cvs_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+void voc_disable_dtmf_det_on_active_sessions(void)
+{
+ struct voice_data *v = NULL;
+ int i;
+ for (i = 0; i < MAX_VOC_SESSIONS; i++) {
+ v = &common.voice[i];
+ if ((v->dtmf_rx_detect_en) &&
+ ((v->voc_state == VOC_RUN) ||
+ (v->voc_state == VOC_CHANGE) ||
+ (v->voc_state == VOC_STANDBY))) {
+ pr_debug("disable dtmf det on ses_id=%d\n",
+ v->session_id);
+ voice_send_dtmf_rx_detection_cmd(v, 0);
+ }
+ }
+}
+
+int voc_enable_dtmf_rx_detection(uint16_t session_id, uint32_t enable)
+{
+ struct voice_data *v = voice_get_session(session_id);
+ int ret = 0;
+
+ if (v == NULL) {
+ pr_err("%s: invalid session_id 0x%x\n", __func__, session_id);
+ return -EINVAL;
+ }
+
+ mutex_lock(&v->lock);
+ v->dtmf_rx_detect_en = enable;
+
+ if ((v->voc_state == VOC_RUN) ||
+ (v->voc_state == VOC_CHANGE) ||
+ (v->voc_state == VOC_STANDBY))
+ ret = voice_send_dtmf_rx_detection_cmd(v,
+ v->dtmf_rx_detect_en);
+
+ mutex_unlock(&v->lock);
+
+ return ret;
+}
+
static int voice_config_cvs_vocoder(struct voice_data *v)
{
int ret = 0;
@@ -2207,6 +2321,9 @@
if (v->rec_info.rec_enable)
voice_cvs_start_record(v, v->rec_info.rec_mode);
+ if (v->dtmf_rx_detect_en)
+ voice_send_dtmf_rx_detection_cmd(v, v->dtmf_rx_detect_en);
+
rtac_add_voice(voice_get_cvs_handle(v),
voice_get_cvp_handle(v),
v->dev_rx.port_id, v->dev_tx.port_id,
@@ -2445,6 +2562,10 @@
/* send stop voice cmd */
voice_send_stop_voice_cmd(v);
+ /* send stop dtmf detecton cmd */
+ if (v->dtmf_rx_detect_en)
+ voice_send_dtmf_rx_detection_cmd(v, 0);
+
/* Clear mute setting */
v->dev_tx.mute = common.default_mute_val;
@@ -3700,6 +3821,13 @@
common.mvs_info.private_data = private_data;
}
+void voc_register_dtmf_rx_detection_cb(dtmf_rx_det_cb_fn dtmf_rx_ul_cb,
+ void *private_data)
+{
+ common.dtmf_info.dtmf_rx_ul_cb = dtmf_rx_ul_cb;
+ common.dtmf_info.private_data = private_data;
+}
+
void voc_config_vocoder(uint32_t media_type,
uint32_t rate,
uint32_t network_type,
@@ -3876,6 +4004,7 @@
case VSS_ISTREAM_CMD_STOP_PLAYBACK:
case VSS_ISTREAM_CMD_START_RECORD:
case VSS_ISTREAM_CMD_STOP_RECORD:
+ case VSS_ISTREAM_CMD_SET_RX_DTMF_DETECTION:
pr_debug("%s: cmd = 0x%x\n", __func__, ptr[0]);
v->cvs_state = CMD_STATUS_SUCCESS;
wake_up(&v->cvs_wait);
@@ -3947,8 +4076,30 @@
} else if (data->opcode == VOICE_EVT_GET_PARAM_ACK) {
rtac_make_voice_callback(RTAC_CVS, data->payload,
data->payload_size);
- } else
+ } else if (data->opcode == VSS_ISTREAM_EVT_RX_DTMF_DETECTED) {
+ struct vss_istream_evt_rx_dtmf_detected *dtmf_rx_detected;
+ uint32_t *voc_pkt = data->payload;
+ uint32_t pkt_len = data->payload_size;
+
+ if ((voc_pkt != NULL) &&
+ (pkt_len ==
+ sizeof(struct vss_istream_evt_rx_dtmf_detected))) {
+
+ dtmf_rx_detected =
+ (struct vss_istream_evt_rx_dtmf_detected *) voc_pkt;
+ pr_debug("RX_DTMF_DETECTED low_freq=%d high_freq=%d\n",
+ dtmf_rx_detected->low_freq,
+ dtmf_rx_detected->high_freq);
+ if (c->dtmf_info.dtmf_rx_ul_cb)
+ c->dtmf_info.dtmf_rx_ul_cb((uint8_t *)voc_pkt,
+ voc_get_session_name(v->session_id),
+ c->dtmf_info.private_data);
+ } else {
+ pr_err("Invalid packet\n");
+ }
+ } else {
pr_debug("Unknown opcode 0x%x\n", data->opcode);
+ }
fail:
return 0;
@@ -3971,7 +4122,6 @@
v = voice_get_session(data->dest_port);
if (v == NULL) {
pr_err("%s: v is NULL\n", __func__);
-
return -EINVAL;
}
@@ -4141,6 +4291,7 @@
common.voice[i].dev_tx.port_id = 1;
common.voice[i].dev_rx.port_id = 0;
common.voice[i].sidetone_gain = 0x512;
+ common.voice[i].dtmf_rx_detect_en = 0;
common.voice[i].voc_state = VOC_INIT;
diff --git a/sound/soc/msm/qdsp6/q6voice.h b/sound/soc/msm/qdsp6/q6voice.h
index 34b1b52..2fc2266 100644
--- a/sound/soc/msm/qdsp6/q6voice.h
+++ b/sound/soc/msm/qdsp6/q6voice.h
@@ -567,6 +567,55 @@
/* Reserved, set to 0. */
};
+/*
+ * Event sent by the stream to the client that enables Rx DTMF
+ * detection whenever DTMF is detected in the Rx path.
+ *
+ * The DTMF detection feature can only be used to detect DTMF
+ * frequencies as listed in the vss_istream_evt_rx_dtmf_detected_t
+ * structure.
+ */
+
+#define VSS_ISTREAM_EVT_RX_DTMF_DETECTED (0x0001101A)
+
+struct vss_istream_cmd_set_rx_dtmf_detection {
+ /*
+ * Enables/disables Rx DTMF detection
+ *
+ * Possible values are
+ * 0 - disable
+ * 1 - enable
+ *
+ */
+ uint32_t enable;
+};
+
+#define VSS_ISTREAM_CMD_SET_RX_DTMF_DETECTION (0x00011027)
+
+struct vss_istream_evt_rx_dtmf_detected {
+ uint16_t low_freq;
+ /*
+ * Detected low frequency. Possible values:
+ * 697 Hz
+ * 770 Hz
+ * 852 Hz
+ * 941 Hz
+ */
+ uint16_t high_freq;
+ /*
+ * Detected high frequency. Possible values:
+ * 1209 Hz
+ * 1336 Hz
+ * 1477 Hz
+ * 1633 Hz
+ */
+};
+
+struct cvs_set_rx_dtmf_detection_cmd {
+ struct apr_hdr hdr;
+ struct vss_istream_cmd_set_rx_dtmf_detection cvs_dtmf_det;
+} __packed;
+
struct cvs_create_passive_ctl_session_cmd {
struct apr_hdr hdr;
struct vss_istream_cmd_create_passive_control_session_t cvs_session;
@@ -858,6 +907,10 @@
uint32_t *pkt_len,
void *private_data);
+/* CB for DTMF RX Detection */
+typedef void (*dtmf_rx_det_cb_fn)(uint8_t *pkt,
+ char *session,
+ void *private_data);
struct mvs_driver_info {
uint32_t media_type;
@@ -869,6 +922,11 @@
void *private_data;
};
+struct dtmf_driver_info {
+ dtmf_rx_det_cb_fn dtmf_rx_ul_cb;
+ void *private_data;
+};
+
struct incall_rec_info {
uint32_t rec_enable;
uint32_t rec_mode;
@@ -915,6 +973,8 @@
/* FENC enable value */
uint32_t fens_enable;
+ uint32_t dtmf_rx_detect_en;
+
struct voice_dev_route_state voc_route_state;
u16 session_id;
@@ -961,6 +1021,8 @@
struct mvs_driver_info mvs_info;
+ struct dtmf_driver_info dtmf_info;
+
struct voice_data voice[MAX_VOC_SESSIONS];
};
@@ -968,6 +1030,9 @@
dl_cb_fn dl_cb,
void *private_data);
+void voc_register_dtmf_rx_detection_cb(dtmf_rx_det_cb_fn dtmf_rx_ul_cb,
+ void *private_data);
+
void voc_config_vocoder(uint32_t media_type,
uint32_t rate,
uint32_t network_type,
@@ -1005,11 +1070,20 @@
int voc_enable_cvp(uint16_t session_id);
int voc_set_route_flag(uint16_t session_id, uint8_t path_dir, uint8_t set);
uint8_t voc_get_route_flag(uint16_t session_id, uint8_t path_dir);
+int voc_enable_dtmf_rx_detection(uint16_t session_id, uint32_t enable);
+void voc_disable_dtmf_det_on_active_sessions(void);
+#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 SGLTE_SESSION_NAME "SGLTE session"
+
+#define VOC_PATH_PASSIVE 0
+#define VOC_PATH_FULL 1
+#define VOC_PATH_VOLTE_PASSIVE 2
+#define VOC_PATH_SGLTE_PASSIVE 3
+
uint16_t voc_get_session_id(char *name);
int voc_start_playback(uint32_t set);
diff --git a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c
index 7399053..621d24b 100644
--- a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c
@@ -37,9 +37,39 @@
DECLARE_BITMAP(status_mask, STATUS_MAX);
u32 rate;
u32 channels;
+ u32 bitwidth;
union afe_port_config port_config;
};
+struct msm_dai_q6_mi2s_dai_config {
+ u16 pdata_mi2s_lines;
+ struct msm_dai_q6_dai_data mi2s_dai_data;
+};
+
+struct msm_dai_q6_mi2s_dai_data {
+ struct msm_dai_q6_mi2s_dai_config tx_dai;
+ struct msm_dai_q6_mi2s_dai_config rx_dai;
+ struct snd_pcm_hw_constraint_list rate_constraint;
+ struct snd_pcm_hw_constraint_list bitwidth_constraint;
+};
+
+/* MI2S format field for AFE_PORT_CMD_I2S_CONFIG command
+ * 0: linear PCM
+ * 1: non-linear PCM
+ * 2: PCM data in IEC 60968 container
+ * 3: compressed data in IEC 60958 container
+ */
+static const char *const mi2s_format[] = {
+ "LPCM",
+ "Compr",
+ "LPCM-60958",
+ "Compr-60958"
+};
+
+static const struct soc_enum mi2s_config_enum[] = {
+ SOC_ENUM_SINGLE_EXT(4, mi2s_format),
+};
+
static struct clk *pcm_src_clk;
static struct clk *pcm_branch_clk;
static struct clk *pcm_oe_src_clk;
@@ -1155,6 +1185,611 @@
.remove = msm_dai_q6_dai_remove,
};
+static int msm_dai_q6_mi2s_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data;
+ int value = ucontrol->value.integer.value[0];
+ dai_data->port_config.i2s.data_format = value;
+ pr_debug("%s: value = %d, channel = %d, line = %d\n",
+ __func__, value, dai_data->port_config.i2s.mono_stereo,
+ dai_data->port_config.i2s.channel_mode);
+ return 0;
+}
+
+static int msm_dai_q6_mi2s_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data;
+ ucontrol->value.integer.value[0] =
+ dai_data->port_config.i2s.data_format;
+ return 0;
+}
+
+static const struct snd_kcontrol_new mi2s_config_controls[] = {
+ SOC_ENUM_EXT("PRI MI2S RX Format", mi2s_config_enum[0],
+ msm_dai_q6_mi2s_format_get,
+ msm_dai_q6_mi2s_format_put),
+ SOC_ENUM_EXT("SEC RX Format", mi2s_config_enum[0],
+ msm_dai_q6_mi2s_format_get,
+ msm_dai_q6_mi2s_format_put),
+ SOC_ENUM_EXT("PRI MI2S TX Format", mi2s_config_enum[0],
+ msm_dai_q6_mi2s_format_get,
+ msm_dai_q6_mi2s_format_put),
+};
+
+static int msm_dai_q6_dai_mi2s_probe(struct snd_soc_dai *dai)
+{
+ struct msm_dai_q6_mi2s_dai_data *mi2s_dai_data =
+ dev_get_drvdata(dai->dev);
+ struct snd_kcontrol *kcontrol = NULL;
+ int rc = 0;
+
+ if (mi2s_dai_data->rx_dai.mi2s_dai_data.port_config.i2s.channel_mode) {
+ kcontrol = snd_ctl_new1(&mi2s_config_controls[0],
+ &mi2s_dai_data->rx_dai.mi2s_dai_data);
+ rc = snd_ctl_add(dai->card->snd_card, kcontrol);
+
+ if (IS_ERR_VALUE(rc)) {
+ dev_err(dai->dev, "%s: err add RX fmt ctl\n", __func__);
+ goto rtn;
+ }
+ }
+ if (mi2s_dai_data->tx_dai.mi2s_dai_data.port_config.i2s.channel_mode) {
+ rc = snd_ctl_add(dai->card->snd_card,
+ snd_ctl_new1(&mi2s_config_controls[2],
+ &mi2s_dai_data->tx_dai.mi2s_dai_data));
+
+ if (IS_ERR_VALUE(rc)) {
+ if (kcontrol)
+ snd_ctl_remove(dai->card->snd_card, kcontrol);
+ dev_err(dai->dev, "%s: err add TX fmt ctl\n", __func__);
+ }
+ }
+rtn:
+ return rc;
+}
+
+
+static int msm_dai_q6_dai_mi2s_remove(struct snd_soc_dai *dai)
+{
+ struct msm_dai_q6_mi2s_dai_data *mi2s_dai_data =
+ dev_get_drvdata(dai->dev);
+ int rc;
+
+ /* If AFE port is still up, close it */
+ if (test_bit(STATUS_PORT_STARTED,
+ mi2s_dai_data->rx_dai.mi2s_dai_data.status_mask)) {
+ rc = afe_close(MI2S_RX); /* can block */
+ if (IS_ERR_VALUE(rc))
+ dev_err(dai->dev, "fail to close MI2S_RX port\n");
+ clear_bit(STATUS_PORT_STARTED,
+ mi2s_dai_data->rx_dai.mi2s_dai_data.status_mask);
+ }
+ if (test_bit(STATUS_PORT_STARTED,
+ mi2s_dai_data->tx_dai.mi2s_dai_data.status_mask)) {
+ rc = afe_close(MI2S_TX); /* can block */
+ if (IS_ERR_VALUE(rc))
+ dev_err(dai->dev, "fail to close MI2S_TX port\n");
+ clear_bit(STATUS_PORT_STARTED,
+ mi2s_dai_data->tx_dai.mi2s_dai_data.status_mask);
+ }
+ kfree(mi2s_dai_data);
+ snd_soc_unregister_dai(dai->dev);
+ return 0;
+}
+
+static int msm_dai_q6_mi2s_startup(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);
+
+ dev_dbg(dai->dev, "%s: cnst list %p\n", __func__,
+ mi2s_dai_data->rate_constraint.list);
+
+ if (mi2s_dai_data->rate_constraint.list) {
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &mi2s_dai_data->rate_constraint);
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
+ &mi2s_dai_data->bitwidth_constraint);
+ }
+
+ return 0;
+}
+
+
+static int msm_mi2s_get_port_id(u32 mi2s_id, int stream, u16 *port_id)
+{
+ int ret = 0;
+
+ switch (stream) {
+ case SNDRV_PCM_STREAM_PLAYBACK:
+ switch (mi2s_id) {
+ case MSM_PRIM_MI2S:
+ *port_id = MI2S_RX;
+ break;
+ default:
+ ret = -1;
+ break;
+ }
+ break;
+ case SNDRV_PCM_STREAM_CAPTURE:
+ switch (mi2s_id) {
+ case MSM_PRIM_MI2S:
+ *port_id = MI2S_TX;
+ break;
+ default:
+ ret = -1;
+ break;
+ }
+ break;
+ default:
+ ret = -1;
+ break;
+ }
+ pr_debug("%s: port_id = %x\n", __func__, *port_id);
+ return ret;
+}
+
+static int msm_dai_q6_mi2s_prepare(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);
+ u16 port_id = 0;
+ int rc = 0;
+
+ dev_dbg(dai->dev, "%s: device name %s dai id %x,port id = %x\n",
+ __func__, dai->name, dai->id, port_id);
+
+ if (msm_mi2s_get_port_id(dai->id, substream->stream,
+ &port_id) != 0) {
+ dev_err(dai->dev, "%s: Invalid Port ID\n", __func__);
+ return -EINVAL;
+ }
+
+ if (!test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
+ /* PORT START should be set if prepare called
+ * in active state.
+ */
+ rc = afe_port_start(port_id, &dai_data->port_config,
+ dai_data->rate);
+
+ if (IS_ERR_VALUE(rc))
+ dev_err(dai->dev, "fail to open AFE port %x\n",
+ dai->id);
+ else
+ set_bit(STATUS_PORT_STARTED,
+ dai_data->status_mask);
+ }
+ return rc;
+}
+
+static int msm_dai_q6_mi2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct msm_dai_q6_mi2s_dai_data *mi2s_dai_data =
+ dev_get_drvdata(dai->dev);
+ struct msm_dai_q6_mi2s_dai_config *mi2s_dai_config =
+ (substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+ &mi2s_dai_data->rx_dai : &mi2s_dai_data->tx_dai);
+ struct msm_dai_q6_dai_data *dai_data = &mi2s_dai_config->mi2s_dai_data;
+
+ dai_data->channels = params_channels(params);
+ switch (dai_data->channels) {
+ case 8:
+ case 7:
+ if (mi2s_dai_config->pdata_mi2s_lines < AFE_PORT_I2S_8CHS)
+ goto error_invalid_data;
+ dai_data->port_config.i2s.channel_mode = AFE_PORT_I2S_8CHS;
+ break;
+ case 6:
+ case 5:
+ if (mi2s_dai_config->pdata_mi2s_lines < AFE_PORT_I2S_6CHS)
+ goto error_invalid_data;
+ dai_data->port_config.i2s.channel_mode = AFE_PORT_I2S_6CHS;
+ break;
+ case 4:
+ case 3:
+ if (mi2s_dai_config->pdata_mi2s_lines < AFE_PORT_I2S_QUAD01)
+ goto error_invalid_data;
+ if (mi2s_dai_config->pdata_mi2s_lines == AFE_PORT_I2S_QUAD23)
+ dai_data->port_config.i2s.channel_mode =
+ mi2s_dai_config->pdata_mi2s_lines;
+ else
+ dai_data->port_config.i2s.channel_mode =
+ AFE_PORT_I2S_QUAD01;
+ break;
+ case 2:
+ case 1:
+ if (mi2s_dai_config->pdata_mi2s_lines < AFE_PORT_I2S_SD0)
+ goto error_invalid_data;
+ switch (mi2s_dai_config->pdata_mi2s_lines) {
+ case AFE_PORT_I2S_SD0:
+ case AFE_PORT_I2S_SD1:
+ case AFE_PORT_I2S_SD2:
+ case AFE_PORT_I2S_SD3:
+ dai_data->port_config.i2s.channel_mode =
+ mi2s_dai_config->pdata_mi2s_lines;
+ break;
+ case AFE_PORT_I2S_QUAD01:
+ case AFE_PORT_I2S_6CHS:
+ case AFE_PORT_I2S_8CHS:
+ dai_data->port_config.i2s.channel_mode =
+ AFE_PORT_I2S_SD0;
+ break;
+ case AFE_PORT_I2S_QUAD23:
+ dai_data->port_config.i2s.channel_mode =
+ AFE_PORT_I2S_SD2;
+ break;
+ }
+ if (dai_data->channels == 2)
+ dai_data->port_config.i2s.mono_stereo =
+ MSM_AFE_CH_STEREO;
+ else
+ dai_data->port_config.i2s.mono_stereo = MSM_AFE_MONO;
+ break;
+ default:
+ goto error_invalid_data;
+ }
+ dai_data->rate = params_rate(params);
+ dai_data->port_config.i2s.bit_width = 16;
+ dai_data->bitwidth = 16;
+ 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 (!mi2s_dai_data->rate_constraint.list) {
+ mi2s_dai_data->rate_constraint.list = &dai_data->rate;
+ mi2s_dai_data->bitwidth_constraint.list = &dai_data->bitwidth;
+ }
+
+ pr_debug("%s: dai_data->channels = %d, line = %d\n"
+ ",mono_stereo =%x sample rate = %x\n", __func__,
+ dai_data->channels, dai_data->port_config.i2s.channel_mode,
+ dai_data->port_config.i2s.mono_stereo, dai_data->rate);
+ return 0;
+error_invalid_data:
+ pr_debug("%s: dai_data->channels = %d, line = %d\n", __func__,
+ dai_data->channels, dai_data->port_config.i2s.channel_mode);
+ return -EINVAL;
+}
+
+
+static int msm_dai_q6_mi2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct msm_dai_q6_mi2s_dai_data *mi2s_dai_data =
+ dev_get_drvdata(dai->dev);
+
+ 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)) {
+ dev_err(dai->dev, "%s: err chg i2s mode while dai running",
+ __func__);
+ return -EPERM;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ mi2s_dai_data->rx_dai.mi2s_dai_data.port_config.i2s.ws_src = 1;
+ mi2s_dai_data->tx_dai.mi2s_dai_data.port_config.i2s.ws_src = 1;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ mi2s_dai_data->rx_dai.mi2s_dai_data.port_config.i2s.ws_src = 0;
+ mi2s_dai_data->tx_dai.mi2s_dai_data.port_config.i2s.ws_src = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void msm_dai_q6_mi2s_shutdown(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);
+ u16 port_id = 0;
+ int rc = 0;
+
+ if (msm_mi2s_get_port_id(dai->id, substream->stream,
+ &port_id) != 0) {
+ dev_err(dai->dev, "%s: Invalid Port ID\n", __func__);
+ }
+
+ dev_dbg(dai->dev, "%s: device name %s port id = %x\n",
+ __func__, dai->name, port_id);
+
+ if (test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
+ rc = afe_close(port_id);
+ if (IS_ERR_VALUE(rc))
+ 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,
+ mi2s_dai_data->rx_dai.mi2s_dai_data.status_mask) &&
+ !test_bit(STATUS_PORT_STARTED,
+ mi2s_dai_data->rx_dai.mi2s_dai_data.status_mask)) {
+ mi2s_dai_data->rate_constraint.list = NULL;
+ mi2s_dai_data->bitwidth_constraint.list = NULL;
+ }
+}
+
+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,
+ .set_fmt = msm_dai_q6_mi2s_set_fmt,
+ .shutdown = msm_dai_q6_mi2s_shutdown,
+};
+
+/* Channel min and max are initialized base on platform data */
+static struct snd_soc_dai_driver msm_dai_q6_mi2s_dai = {
+ .playback = {
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .capture = {
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_dai_q6_mi2s_ops,
+ .probe = msm_dai_q6_dai_mi2s_probe,
+ .remove = msm_dai_q6_dai_mi2s_remove,
+};
+
+
+static int msm_dai_q6_mi2s_get_lineconfig(u16 sd_lines, u16 *config_ptr,
+ unsigned int *ch_cnt)
+{
+ u8 num_of_sd_lines;
+
+ num_of_sd_lines = num_of_bits_set(sd_lines);
+ switch (num_of_sd_lines) {
+ case 0:
+ pr_debug("%s: no line is assigned\n", __func__);
+ break;
+ case 1:
+ switch (sd_lines) {
+ case MSM_MI2S_SD0:
+ *config_ptr = AFE_PORT_I2S_SD0;
+ break;
+ case MSM_MI2S_SD1:
+ *config_ptr = AFE_PORT_I2S_SD1;
+ break;
+ case MSM_MI2S_SD2:
+ *config_ptr = AFE_PORT_I2S_SD2;
+ break;
+ case MSM_MI2S_SD3:
+ *config_ptr = AFE_PORT_I2S_SD3;
+ break;
+ default:
+ pr_err("%s: invalid SD line\n",
+ __func__);
+ goto error_invalid_data;
+ }
+ break;
+ case 2:
+ switch (sd_lines) {
+ case MSM_MI2S_SD0 | MSM_MI2S_SD1:
+ *config_ptr = AFE_PORT_I2S_QUAD01;
+ break;
+ case MSM_MI2S_SD2 | MSM_MI2S_SD3:
+ *config_ptr = AFE_PORT_I2S_QUAD23;
+ break;
+ default:
+ pr_err("%s: invalid SD line\n",
+ __func__);
+ goto error_invalid_data;
+ }
+ break;
+ case 3:
+ switch (sd_lines) {
+ case MSM_MI2S_SD0 | MSM_MI2S_SD1 | MSM_MI2S_SD2:
+ *config_ptr = AFE_PORT_I2S_6CHS;
+ break;
+ default:
+ pr_err("%s: invalid SD lines\n",
+ __func__);
+ goto error_invalid_data;
+ }
+ break;
+ case 4:
+ switch (sd_lines) {
+ case MSM_MI2S_SD0 | MSM_MI2S_SD1 | MSM_MI2S_SD2 | MSM_MI2S_SD3:
+ *config_ptr = AFE_PORT_I2S_8CHS;
+ break;
+ default:
+ pr_err("%s: invalid SD lines\n",
+ __func__);
+ goto error_invalid_data;
+ }
+ break;
+ default:
+ pr_err("%s: invalid SD lines\n", __func__);
+ goto error_invalid_data;
+ }
+ *ch_cnt = num_of_sd_lines;
+ return 0;
+
+error_invalid_data:
+ return -EINVAL;
+}
+
+static int msm_dai_q6_mi2s_platform_data_validation(
+ struct platform_device *pdev, struct snd_soc_dai_driver *dai_driver)
+{
+ struct msm_dai_q6_mi2s_dai_data *dai_data = dev_get_drvdata(&pdev->dev);
+ struct msm_mi2s_pdata *mi2s_pdata =
+ (struct msm_mi2s_pdata *) pdev->dev.platform_data;
+ unsigned int ch_cnt;
+ int rc = 0;
+ u16 sd_line;
+
+ if (mi2s_pdata == NULL) {
+ pr_err("%s: mi2s_pdata NULL", __func__);
+ return -EINVAL;
+ }
+
+ rc = msm_dai_q6_mi2s_get_lineconfig(mi2s_pdata->rx_sd_lines,
+ &sd_line, &ch_cnt);
+
+ if (IS_ERR_VALUE(rc)) {
+ dev_err(&pdev->dev, "invalid MI2S RX sd line config\n");
+ goto rtn;
+ }
+
+ if (ch_cnt) {
+ dai_data->rx_dai.mi2s_dai_data.port_config.i2s.channel_mode =
+ mi2s_pdata->rx_sd_lines;
+ dai_data->rx_dai.pdata_mi2s_lines = mi2s_pdata->rx_sd_lines;
+ dai_driver->playback.channels_min = 1;
+ dai_driver->playback.channels_max = ch_cnt << 1;
+ } else {
+ dai_driver->playback.channels_min = 0;
+ dai_driver->playback.channels_max = 0;
+ }
+ rc = msm_dai_q6_mi2s_get_lineconfig(mi2s_pdata->tx_sd_lines,
+ &sd_line, &ch_cnt);
+
+ if (IS_ERR_VALUE(rc)) {
+ dev_err(&pdev->dev, "invalid MI2S TX sd line config\n");
+ goto rtn;
+ }
+
+ if (ch_cnt) {
+ dai_data->tx_dai.mi2s_dai_data.port_config.i2s.channel_mode =
+ mi2s_pdata->tx_sd_lines;
+ dai_data->tx_dai.pdata_mi2s_lines = mi2s_pdata->tx_sd_lines;
+ dai_driver->capture.channels_min = 1;
+ dai_driver->capture.channels_max = ch_cnt << 1;
+ } else {
+ dai_driver->capture.channels_min = 0;
+ dai_driver->capture.channels_max = 0;
+ }
+
+ dev_dbg(&pdev->dev, "%s: playback sdline %x capture sdline %x\n",
+ __func__, dai_data->rx_dai.pdata_mi2s_lines,
+ dai_data->tx_dai.pdata_mi2s_lines);
+ dev_dbg(&pdev->dev, "%s: playback ch_max %d capture ch_mx %d\n",
+ __func__, dai_driver->playback.channels_max,
+ dai_driver->capture.channels_max);
+rtn:
+ return rc;
+}
+
+static __devinit int msm_dai_q6_mi2s_dev_probe(struct platform_device *pdev)
+{
+ struct msm_dai_q6_mi2s_dai_data *dai_data;
+ const char *q6_mi2s_dev_id = "qcom,msm-dai-q6-mi2s-dev-id";
+ u32 tx_line = 0;
+ u32 rx_line = 0;
+ u32 mi2s_intf = 0;
+ struct msm_mi2s_pdata *mi2s_pdata;
+ int rc = 0;
+
+
+ rc = of_property_read_u32(pdev->dev.of_node, q6_mi2s_dev_id,
+ &mi2s_intf);
+ if (rc) {
+ dev_err(&pdev->dev,
+ "%s: missing %x in dt node\n", __func__, mi2s_intf);
+ return rc;
+ }
+
+ if (mi2s_intf > MSM_QUAD_MI2S) {
+ dev_err(&pdev->dev, "%s: Invalid MI2S ID from Device Tree\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (mi2s_intf == MSM_PRIM_MI2S) {
+ dev_set_name(&pdev->dev, "%s.%d", "msm-dai-q6-mi2s",
+ MSM_PRIM_MI2S);
+ pdev->id = MSM_PRIM_MI2S;
+ }
+
+ mi2s_pdata = kzalloc(sizeof(struct msm_mi2s_pdata), GFP_KERNEL);
+ if (!mi2s_pdata) {
+ dev_err(&pdev->dev, "fail to allocate mi2s_pdata data\n");
+ rc = -ENOMEM;
+ goto rtn;
+ }
+
+ dev_dbg(&pdev->dev, "dev name %s dev id %x\n", dev_name(&pdev->dev),
+ pdev->id);
+
+ rc = of_property_read_u32(pdev->dev.of_node, "qcom,msm-mi2s-rx-lines",
+ &rx_line);
+ if (rc) {
+ dev_err(&pdev->dev, "%s: Rx line from DT file %s\n", __func__,
+ "qcom,msm-mi2s-rx-lines");
+ return rc;
+ }
+
+ rc = of_property_read_u32(pdev->dev.of_node, "qcom,msm-mi2s-tx-lines",
+ &tx_line);
+ if (rc) {
+ dev_err(&pdev->dev, "%s: Tx line from DT file %s\n", __func__,
+ "qcom,msm-mi2s-tx-lines");
+ return rc;
+ }
+ dev_dbg(&pdev->dev, "dev name %s Rx line %x , Tx ine %x\n",
+ dev_name(&pdev->dev), rx_line, tx_line);
+ mi2s_pdata->rx_sd_lines = rx_line;
+ mi2s_pdata->tx_sd_lines = tx_line;
+ dai_data = kzalloc(sizeof(struct msm_dai_q6_mi2s_dai_data),
+ GFP_KERNEL);
+ if (!dai_data) {
+ dev_err(&pdev->dev, "fail to allocate dai data\n");
+ rc = -ENOMEM;
+ goto rtn;
+ } else
+ dev_set_drvdata(&pdev->dev, dai_data);
+ pdev->dev.platform_data = mi2s_pdata;
+ rc = msm_dai_q6_mi2s_platform_data_validation(pdev,
+ &msm_dai_q6_mi2s_dai);
+ if (IS_ERR_VALUE(rc))
+ goto err_pdata;
+ dai_data->rate_constraint.count = 1;
+ dai_data->bitwidth_constraint.count = 1;
+ rc = snd_soc_register_dai(&pdev->dev, &msm_dai_q6_mi2s_dai);
+ if (IS_ERR_VALUE(rc))
+ goto err_pdata;
+ return 0;
+err_pdata:
+ dev_err(&pdev->dev, "fail to msm_dai_q6_mi2s_dev_probe\n");
+ kfree(dai_data);
+rtn:
+ return rc;
+}
+
+static __devexit int msm_dai_q6_mi2s_dev_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_dai(&pdev->dev);
+ return 0;
+}
+
static int msm_dai_q6_dev_probe(struct platform_device *pdev)
{
int rc, id;
@@ -1281,6 +1916,57 @@
},
};
+static int msm_dai_mi2s_q6_probe(struct platform_device *pdev)
+{
+ int rc;
+ rc = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
+ if (rc) {
+ dev_err(&pdev->dev, "%s: failed to add child nodes, rc=%d\n",
+ __func__, rc);
+ } else
+ dev_dbg(&pdev->dev, "%s: added child node\n", __func__);
+ return rc;
+}
+
+static int msm_dai_mi2s_q6_remove(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static const struct of_device_id msm_dai_mi2s_dt_match[] = {
+ { .compatible = "qcom,msm-dai-mi2s", },
+ { }
+};
+
+MODULE_DEVICE_TABLE(of, msm_dai_mi2s_dt_match);
+
+static struct platform_driver msm_dai_mi2s_q6 = {
+ .probe = msm_dai_mi2s_q6_probe,
+ .remove = msm_dai_mi2s_q6_remove,
+ .driver = {
+ .name = "msm-dai-mi2s",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_dai_mi2s_dt_match,
+ },
+};
+
+static const struct of_device_id msm_dai_q6_mi2s_dev_dt_match[] = {
+ { .compatible = "qcom,msm-dai-q6-mi2s", },
+ { }
+};
+
+MODULE_DEVICE_TABLE(of, msm_dai_q6_mi2s_dev_dt_match);
+
+static struct platform_driver msm_dai_q6_mi2s_driver = {
+ .probe = msm_dai_q6_mi2s_dev_probe,
+ .remove = __devexit_p(msm_dai_q6_mi2s_dev_remove),
+ .driver = {
+ .name = "msm-dai-q6-mi2s",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_dai_q6_mi2s_dev_dt_match,
+ },
+};
+
static int __init msm_dai_q6_init(void)
{
int rc;
@@ -1293,26 +1979,44 @@
if (rc) {
pr_err("%s: fail to register cpu dai driver\n", __func__);
- platform_driver_unregister(&msm_auxpcm_dev_driver);
- goto fail;
+ goto aux_pcm_resource_fail;
}
rc = platform_driver_register(&msm_dai_q6);
if (rc) {
pr_err("%s: fail to register dai q6 driver", __func__);
- platform_driver_unregister(&msm_auxpcm_dev_driver);
- platform_driver_unregister(&msm_auxpcm_resource_driver);
- goto fail;
+ goto dai_q6_fail;
}
rc = platform_driver_register(&msm_dai_q6_dev);
if (rc) {
pr_err("%s: fail to register dai q6 dev driver", __func__);
- platform_driver_unregister(&msm_dai_q6);
- platform_driver_unregister(&msm_auxpcm_dev_driver);
- platform_driver_unregister(&msm_auxpcm_resource_driver);
- goto fail;
+ goto dai_q6_dev_fail;
}
+
+ rc = platform_driver_register(&msm_dai_q6_mi2s_driver);
+ if (rc) {
+ pr_err("%s: fail to register dai MI2S dev drv\n", __func__);
+ goto dai_q6_mi2s_drv_fail;
+ }
+
+ rc = platform_driver_register(&msm_dai_mi2s_q6);
+ if (rc) {
+ pr_err("%s: fail to register dai MI2S\n", __func__);
+ goto dai_mi2s_q6_fail;
+ }
+ return rc;
+
+dai_mi2s_q6_fail:
+ platform_driver_unregister(&msm_dai_q6_mi2s_driver);
+dai_q6_mi2s_drv_fail:
+ platform_driver_unregister(&msm_dai_q6_dev);
+dai_q6_dev_fail:
+ platform_driver_unregister(&msm_dai_q6);
+dai_q6_fail:
+ platform_driver_unregister(&msm_auxpcm_resource_driver);
+aux_pcm_resource_fail:
+ platform_driver_unregister(&msm_auxpcm_dev_driver);
fail:
return rc;
}
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c
index 4ab3340..2e0c229 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c
@@ -1352,6 +1352,9 @@
SOC_SINGLE_EXT("SLIM_1_TX", MSM_BACKEND_DAI_MI2S_RX,
MSM_BACKEND_DAI_SLIMBUS_1_TX, 1, 0, msm_routing_get_port_mixer,
msm_routing_put_port_mixer),
+ SOC_SINGLE_EXT("MI2S_TX", MSM_BACKEND_DAI_MI2S_RX,
+ MSM_BACKEND_DAI_MI2S_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
};
static const struct snd_kcontrol_new fm_switch_mixer_controls =
@@ -1608,6 +1611,9 @@
SND_SOC_DAPM_AIF_OUT("MI2S_UL_HL", "MI2S_TX_HOSTLESS Capture",
0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("MI2S_DL_HL", "MI2S_RX_HOSTLESS Playback",
+ 0, 0, 0, 0),
+
/* Backend AIF */
/* Stream name equals to backend dai link stream name
*/
@@ -1933,6 +1939,8 @@
{"INTFM_UL_HL", NULL, "INT_FM_TX"},
{"AUX_PCM_RX", NULL, "AUXPCM_DL_HL"},
{"AUXPCM_UL_HL", NULL, "AUX_PCM_TX"},
+ {"MI2S_RX", NULL, "MI2S_DL_HL"},
+ {"MI2S_UL_HL", NULL, "MI2S_TX"},
{"PCM_RX_DL_HL", "Switch", "SLIM0_DL_HL"},
{"PCM_RX", NULL, "PCM_RX_DL_HL"},
{"MI2S_UL_HL", NULL, "MI2S_TX"},
@@ -1961,6 +1969,9 @@
{"SLIMBUS_1_RX Mixer", "Voice Stub", "VOICE_STUB_DL"},
{"SLIMBUS_1_RX", NULL, "SLIMBUS_1_RX Mixer"},
{"INTERNAL_BT_SCO_RX_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"},
+ {"MI2S_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
+ {"MI2S_RX_Voice Mixer", "Voip", "VOIP_DL"},
+ {"MI2S_RX", NULL, "MI2S_RX_Voice Mixer"},
{"MI2S_RX_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"},
{"MI2S_RX", NULL, "MI2S_RX_Voice Mixer"},
@@ -1983,6 +1994,7 @@
{"SEC_I2S_RX", NULL, "SEC_I2S_RX Port Mixer"},
{"MI2S_RX Port Mixer", "SLIM_1_TX", "SLIMBUS_1_TX"},
+ {"MI2S_RX Port Mixer", "MI2S_TX", "MI2S_TX"},
{"MI2S_RX", NULL, "MI2S_RX Port Mixer"},
/* Backend Enablement */
diff --git a/sound/soc/msm/qdsp6v2/q6afe.c b/sound/soc/msm/qdsp6v2/q6afe.c
index 4819e0a..8d8ff5d 100644
--- a/sound/soc/msm/qdsp6v2/q6afe.c
+++ b/sound/soc/msm/qdsp6v2/q6afe.c
@@ -630,6 +630,11 @@
int ret = 0;
int index = 0;
+ if (rx_port == MI2S_RX)
+ rx_port = AFE_PORT_ID_PRIMARY_MI2S_RX;
+ if (tx_port == MI2S_TX)
+ tx_port = AFE_PORT_ID_PRIMARY_MI2S_TX;
+
ret = afe_q6_interface_prepare();
if (ret != 0)
return ret;