Merge "mmc: sdhci: remove too large timeout warning"
diff --git a/Documentation/devicetree/bindings/arm/msm/smem.txt b/Documentation/devicetree/bindings/arm/msm/smem.txt
new file mode 100644
index 0000000..a38984c
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/msm/smem.txt
@@ -0,0 +1,107 @@
+Qualcomm Shared Memory
+
+[Root level node]
+Required properties:
+-compatible : should be "qcom,smem"
+-reg : the location and size of smem, the irq register base memory, and
+ optionally any auxiliary smem areas
+-reg-names : "smem" - string to identify the shared memory region
+ "irq-reg-base" - string to identify the irq register region
+ "aux-mem1", "aux-mem2", "aux-mem3", ... - optional strings to
+ identify any auxiliary shared memory regions
+
+[Second level nodes]
+
+qcom,smd
+Required properties:
+-compatible : should be "qcom,smd"
+-qcom,smd-edge : the smd edge
+-qcom,smd-irq-offset : the offset into the irq register base memory for sending
+ interrupts
+-qcom,smd-irq-bitmask : the sending irq bitmask
+-interrupts : the receiving interrupt line
+
+Optional properties:
+-qcom,pil-string : the name to use when loading this edge
+-qcom,irq-no-suspend: configure the incoming irq line as active during suspend
+
+qcom,smsm
+Required properties:
+-compatible : should be "qcom,smsm"
+-qcom,smsm-edge : the smsm edge
+-qcom,smsm-irq-offset : the offset into the irq register base memory for sending
+ interrupts
+-qcom,smsm-irq-bitmask : the sending irq bitmask
+-interrupts : the receiving interrupt line
+
+
+Example:
+
+ qcom,smem@fa00000 {
+ compatible = "qcom,smem";
+ reg = <0xfa00000 0x200000>,
+ <0xfa006000 0x1000>,
+ <0xfc428000 0x4000>;
+ reg-names = "smem", "irq-reg-base", "aux-mem1";
+
+ qcom,smd-modem {
+ compatible = "qcom,smd";
+ qcom,smd-edge = <0>;
+ qcom,smd-irq-offset = <0x8>;
+ qcom,smd-irq-bitmask = <0x1000>;
+ qcom,pil-string = "modem";
+ interrupts = <0 25 1>;
+ };
+
+ qcom,smsm-modem {
+ compatible = "qcom,smsm";
+ qcom,smsm-edge = <0>;
+ qcom,smsm-irq-offset = <0x8>;
+ qcom,smsm-irq-bitmask = <0x2000>;
+ interrupts = <0 26 1>;
+ };
+
+ qcom,smd-adsp {
+ compatible = "qcom,smd";
+ qcom,smd-edge = <1>;
+ qcom,smd-irq-offset = <0x8>;
+ qcom,smd-irq-bitmask = <0x100>;
+ qcom,pil-string = "adsp";
+ interrupts = <0 156 1>;
+ };
+
+ qcom,smsm-adsp {
+ compatible = "qcom,smsm";
+ qcom,smsm-edge = <1>;
+ qcom,smsm-irq-offset = <0x8>;
+ qcom,smsm-irq-bitmask = <0x200>;
+ interrupts = <0 157 1>;
+ };
+
+ qcom,smd-wcnss {
+ compatible = "qcom,smd";
+ qcom,smd-edge = <6>;
+ qcom,smd-irq-offset = <0x8>;
+ qcom,smd-irq-bitmask = <0x20000>;
+ qcom,pil-string = "wcnss";
+ interrupts = <0 142 1>;
+ };
+
+ qcom,smsm-wcnss {
+ compatible = "qcom,smsm";
+ qcom,smsm-edge = <6>;
+ qcom,smsm-irq-offset = <0x8>;
+ qcom,smsm-irq-bitmask = <0x80000>;
+ interrupts = <0 144 1>;
+ };
+
+ qcom,smd-rpm {
+ compatible = "qcom,smd";
+ qcom,smd-edge = <15>;
+ qcom,smd-irq-offset = <0x8>;
+ qcom,smd-irq-bitmask = <0x1>;
+ interrupts = <0 168 1>;
+ qcom,irq-no-syspend;
+ };
+ };
+
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/Kconfig b/arch/arm/Kconfig
index 5d5f9de..89c7417 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -264,6 +264,29 @@
def_bool y
depends on BUG
+config GENERIC_TIME_VSYSCALL
+ bool "Enable gettimeofday updates"
+ depends on CPU_V7
+ help
+ Enables updating the kernel user helper area with the xtime struct
+ data for gettimeofday via kernel user helpers.
+
+config ARM_USE_USER_ACCESSIBLE_TIMERS
+ bool "Enables mapping a timer counter page to user space"
+ depends on USE_USER_ACCESSIBLE_TIMERS && GENERIC_TIME_VSYSCALL
+ help
+ Enables ARM-specific user-accessible timers via a shared
+ memory page containing the cycle counter.
+
+config ARM_USER_ACCESSIBLE_TIMER_BASE
+ hex "Base address of user-accessible timer counter page"
+ default 0xfffef000
+ depends on ARM_USE_USER_ACCESSIBLE_TIMERS
+ help
+ Specify the base user-space virtual address where the user-accessible
+ timer counter page should be mapped by the kernel. User-space apps
+ will read directly from the page at this address.
+
source "init/Kconfig"
source "kernel/Kconfig.freezer"
diff --git a/arch/arm/boot/dts/msm-pm8941.dtsi b/arch/arm/boot/dts/msm-pm8941.dtsi
index 4ec5684..bf1c971 100644
--- a/arch/arm/boot/dts/msm-pm8941.dtsi
+++ b/arch/arm/boot/dts/msm-pm8941.dtsi
@@ -90,7 +90,12 @@
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-use-voltage-soc;
};
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..e1b2863 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 {
@@ -152,6 +153,43 @@
vdd-phy-supply = <&spi_eth_vreg>;
};
};
+
+ sound {
+ compatible = "qcom,msm8974-audio-taiko";
+ qcom,model = "msm8974-taiko-cdp-snd-card";
+
+ qcom,audio-routing =
+ "RX_BIAS", "MCLK",
+ "LDO_H", "MCLK",
+ "Ext Spk Bottom Pos", "LINEOUT1",
+ "Ext Spk Bottom Neg", "LINEOUT3",
+ "Ext Spk Top Pos", "LINEOUT2",
+ "Ext Spk Top Neg", "LINEOUT4",
+ "AMIC1", "MIC BIAS1 Internal1",
+ "MIC BIAS1 Internal1", "Handset Mic",
+ "AMIC2", "MIC BIAS2 External",
+ "MIC BIAS2 External", "Headset Mic",
+ "AMIC3", "MIC BIAS2 External",
+ "MIC BIAS2 External", "ANCRight Headset Mic",
+ "AMIC4", "MIC BIAS2 External",
+ "MIC BIAS2 External", "ANCLeft Headset Mic",
+ "DMIC1", "MIC BIAS1 External",
+ "MIC BIAS1 External", "Digital Mic1",
+ "DMIC2", "MIC BIAS1 External",
+ "MIC BIAS1 External", "Digital Mic2",
+ "DMIC3", "MIC BIAS3 External",
+ "MIC BIAS3 External", "Digital Mic3",
+ "DMIC4", "MIC BIAS3 External",
+ "MIC BIAS3 External", "Digital Mic4",
+ "DMIC5", "MIC BIAS4 External",
+ "MIC BIAS4 External", "Digital Mic5",
+ "DMIC6", "MIC BIAS4 External",
+ "MIC BIAS4 External", "Digital Mic6";
+
+ qcom,cdc-mclk-gpios = <&pm8941_gpios 15 0>;
+ taiko-mclk-clk = <&pm8941_clkdiv1>;
+ qcom,taiko-mclk-clk-freq = <9600000>;
+ };
};
&sdcc2 {
diff --git a/arch/arm/boot/dts/msm8974-fluid.dtsi b/arch/arm/boot/dts/msm8974-fluid.dtsi
index 938bc22..15fb799 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 {
@@ -152,6 +153,43 @@
vdd-phy-supply = <&spi_eth_vreg>;
};
};
+
+ sound {
+ compatible = "qcom,msm8974-audio-taiko";
+ qcom,model = "msm8974-taiko-fluid-snd-card";
+
+ qcom,audio-routing =
+ "RX_BIAS", "MCLK",
+ "LDO_H", "MCLK",
+ "Ext Spk Bottom Pos", "LINEOUT1",
+ "Ext Spk Bottom Neg", "LINEOUT3",
+ "Ext Spk Top Pos", "LINEOUT2",
+ "Ext Spk Top Neg", "LINEOUT4",
+ "AMIC1", "MIC BIAS1 Internal1",
+ "MIC BIAS1 Internal1", "Handset Mic",
+ "AMIC2", "MIC BIAS2 External",
+ "MIC BIAS2 External", "Headset Mic",
+ "AMIC3", "MIC BIAS2 External",
+ "MIC BIAS2 External", "ANCRight Headset Mic",
+ "AMIC4", "MIC BIAS2 External",
+ "MIC BIAS2 External", "ANCLeft Headset Mic",
+ "DMIC1", "MIC BIAS1 External",
+ "MIC BIAS1 External", "Digital Mic1",
+ "DMIC2", "MIC BIAS1 External",
+ "MIC BIAS1 External", "Digital Mic2",
+ "DMIC3", "MIC BIAS3 External",
+ "MIC BIAS3 External", "Digital Mic3",
+ "DMIC4", "MIC BIAS3 External",
+ "MIC BIAS3 External", "Digital Mic4",
+ "DMIC5", "MIC BIAS4 External",
+ "MIC BIAS4 External", "Digital Mic5",
+ "DMIC6", "MIC BIAS4 External",
+ "MIC BIAS4 External", "Digital Mic6";
+
+ qcom,cdc-mclk-gpios = <&pm8941_gpios 15 0>;
+ taiko-mclk-clk = <&pm8941_clkdiv1>;
+ qcom,taiko-mclk-clk-freq = <9600000>;
+ };
};
&sdcc1 {
diff --git a/arch/arm/boot/dts/msm8974-liquid.dtsi b/arch/arm/boot/dts/msm8974-liquid.dtsi
index 68207af..f391621 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";
@@ -76,6 +78,32 @@
status = "ok";
};
+ drv2667_vreg: drv2667_vdd_vreg {
+ compatible = "regulator-fixed";
+ regulator-name = "vdd_drv2667";
+ };
+
+ i2c@f9967000 {
+ ti-drv2667@59 {
+ compatible = "ti,drv2667";
+ reg = <0x59>;
+ vdd-supply = <&drv2667_vreg>;
+ vdd-i2c-supply = <&pm8941_s3>;
+ ti,label = "vibrator";
+ ti,gain = <2>;
+ 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];
+ };
+ };
+
i2c@f9924000 {
atmel_mxt_ts@4a {
compatible = "atmel,mxt-ts";
@@ -176,6 +204,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..e2c80c2 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 {
@@ -152,6 +153,43 @@
vdd-phy-supply = <&spi_eth_vreg>;
};
};
+
+ sound {
+ compatible = "qcom,msm8974-audio-taiko";
+ qcom,model = "msm8974-taiko-mtp-snd-card";
+
+ qcom,audio-routing =
+ "RX_BIAS", "MCLK",
+ "LDO_H", "MCLK",
+ "Ext Spk Bottom Pos", "LINEOUT1",
+ "Ext Spk Bottom Neg", "LINEOUT3",
+ "Ext Spk Top Pos", "LINEOUT2",
+ "Ext Spk Top Neg", "LINEOUT4",
+ "AMIC1", "MIC BIAS1 Internal1",
+ "MIC BIAS1 Internal1", "Handset Mic",
+ "AMIC2", "MIC BIAS2 External",
+ "MIC BIAS2 External", "Headset Mic",
+ "AMIC3", "MIC BIAS2 External",
+ "MIC BIAS2 External", "ANCRight Headset Mic",
+ "AMIC4", "MIC BIAS2 External",
+ "MIC BIAS2 External", "ANCLeft Headset Mic",
+ "DMIC1", "MIC BIAS1 External",
+ "MIC BIAS1 External", "Digital Mic1",
+ "DMIC2", "MIC BIAS1 External",
+ "MIC BIAS1 External", "Digital Mic2",
+ "DMIC3", "MIC BIAS3 External",
+ "MIC BIAS3 External", "Digital Mic3",
+ "DMIC4", "MIC BIAS3 External",
+ "MIC BIAS3 External", "Digital Mic4",
+ "DMIC5", "MIC BIAS4 External",
+ "MIC BIAS4 External", "Digital Mic5",
+ "DMIC6", "MIC BIAS4 External",
+ "MIC BIAS4 External", "Digital Mic6";
+
+ qcom,cdc-mclk-gpios = <&pm8941_gpios 15 0>;
+ taiko-mclk-clk = <&pm8941_clkdiv1>;
+ qcom,taiko-mclk-clk-freq = <9600000>;
+ };
};
&sdcc2 {
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/msm8974.dtsi b/arch/arm/boot/dts/msm8974.dtsi
index 0e30129..20249be 100644
--- a/arch/arm/boot/dts/msm8974.dtsi
+++ b/arch/arm/boot/dts/msm8974.dtsi
@@ -1216,6 +1216,74 @@
compatible = "qcom,msm-mem-hole";
qcom,memblock-remove = <0x8400000 0x7b00000>; /* Address and Size of Hole */
};
+
+ qcom,smem@fa00000 {
+ compatible = "qcom,smem";
+ reg = <0xfa00000 0x200000>,
+ <0xfa006000 0x1000>,
+ <0xfc428000 0x4000>;
+ reg-names = "smem", "irq-reg-base", "aux-mem1";
+
+ qcom,smd-modem {
+ compatible = "qcom,smd";
+ qcom,smd-edge = <0>;
+ qcom,smd-irq-offset = <0x8>;
+ qcom,smd-irq-bitmask = <0x1000>;
+ qcom,pil-string = "modem";
+ interrupts = <0 25 1>;
+ };
+
+ qcom,smsm-modem {
+ compatible = "qcom,smsm";
+ qcom,smsm-edge = <0>;
+ qcom,smsm-irq-offset = <0x8>;
+ qcom,smsm-irq-bitmask = <0x2000>;
+ interrupts = <0 26 1>;
+ };
+
+ qcom,smd-adsp {
+ compatible = "qcom,smd";
+ qcom,smd-edge = <1>;
+ qcom,smd-irq-offset = <0x8>;
+ qcom,smd-irq-bitmask = <0x100>;
+ qcom,pil-string = "adsp";
+ interrupts = <0 156 1>;
+ };
+
+ qcom,smsm-adsp {
+ compatible = "qcom,smsm";
+ qcom,smsm-edge = <1>;
+ qcom,smsm-irq-offset = <0x8>;
+ qcom,smsm-irq-bitmask = <0x200>;
+ interrupts = <0 157 1>;
+ };
+
+ qcom,smd-wcnss {
+ compatible = "qcom,smd";
+ qcom,smd-edge = <6>;
+ qcom,smd-irq-offset = <0x8>;
+ qcom,smd-irq-bitmask = <0x20000>;
+ qcom,pil-string = "wcnss";
+ interrupts = <0 142 1>;
+ };
+
+ qcom,smsm-wcnss {
+ compatible = "qcom,smsm";
+ qcom,smsm-edge = <6>;
+ qcom,smsm-irq-offset = <0x8>;
+ qcom,smsm-irq-bitmask = <0x80000>;
+ interrupts = <0 144 1>;
+ };
+
+ qcom,smd-rpm {
+ compatible = "qcom,smd";
+ qcom,smd-edge = <15>;
+ qcom,smd-irq-offset = <0x8>;
+ qcom,smd-irq-bitmask = <0x1>;
+ interrupts = <0 168 1>;
+ qcom,irq-no-suspend;
+ };
+ };
};
/include/ "msm-pm8x41-rpm-regulator.dtsi"
diff --git a/arch/arm/boot/dts/msm9625-cdp.dts b/arch/arm/boot/dts/msm9625-cdp.dts
index ba5bbcf..232fba7 100644
--- a/arch/arm/boot/dts/msm9625-cdp.dts
+++ b/arch/arm/boot/dts/msm9625-cdp.dts
@@ -37,6 +37,14 @@
summit,temperature-max = <3>; /* 45 C */
};
};
+
+ 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>;
+ };
};
/* PM8019 GPIO and MPP configuration */
diff --git a/arch/arm/boot/dts/msm9625-mtp.dts b/arch/arm/boot/dts/msm9625-mtp.dts
index 7780686..faf86d4 100644
--- a/arch/arm/boot/dts/msm9625-mtp.dts
+++ b/arch/arm/boot/dts/msm9625-mtp.dts
@@ -37,6 +37,14 @@
summit,temperature-max = <3>; /* 45 C */
};
};
+
+ 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>;
+ };
};
/* PM8019 GPIO and MPP configuration */
diff --git a/arch/arm/boot/dts/msm9625.dtsi b/arch/arm/boot/dts/msm9625.dtsi
index 26f8ab7..b79f370 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,177 @@
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;
+ };
+
+ wcd9xxx_intc: wcd9xxx-irq {
+ compatible = "qcom,wcd9xxx-irq";
+ interrupt-controller;
+ #interrupt-cells = <1>;
+ interrupt-parent = <&msmgpio>;
+ interrupts = <20 0>;
+ interrupt-names = "cdc-int";
+ };
+
+ i2c@f9925000 {
+ cell-index = <3>;
+ compatible = "qcom,i2c-qup";
+ reg = <0xf9925000 0x1000>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg-names = "qup_phys_addr";
+ interrupts = <0 97 0>;
+ interrupt-names = "qup_err_intr";
+ qcom,i2c-bus-freq = <100000>;
+ qcom,i2c-src-freq = <24000000>;
+
+ wcd9xxx_codec@0d{
+ compatible = "qcom,wcd9xxx-i2c";
+ reg = <0x0d>;
+ qcom,cdc-reset-gpio = <&msmgpio 22 0>;
+ interrupt-parent = <&wcd9xxx_intc>;
+ interrupts = <0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28>;
+ cdc-vdd-buck-supply = <&pm8019_l11>;
+ qcom,cdc-vdd-buck-voltage = <1800000 1800000>;
+ qcom,cdc-vdd-buck-current = <25000>;
+
+ cdc-vdd-tx-h-supply = <&pm8019_l11>;
+ qcom,cdc-vdd-tx-h-voltage = <1800000 1800000>;
+ qcom,cdc-vdd-tx-h-current = <25000>;
+
+ cdc-vdd-rx-h-supply = <&pm8019_l11>;
+ qcom,cdc-vdd-rx-h-voltage = <1800000 1800000>;
+ qcom,cdc-vdd-rx-h-current = <25000>;
+
+ cdc-vddpx-1-supply = <&pm8019_l11>;
+ qcom,cdc-vddpx-1-voltage = <1800000 1800000>;
+ qcom,cdc-vddpx-1-current = <10000>;
+
+ cdc-vdd-a-1p2v-supply = <&pm8019_l9>;
+ qcom,cdc-vdd-a-1p2v-voltage = <1200000 1200000>;
+ qcom,cdc-vdd-a-1p2v-current = <10000>;
+
+ cdc-vddcx-1-supply = <&pm8019_l9>;
+ qcom,cdc-vddcx-1-voltage = <1200000 1200000>;
+ qcom,cdc-vddcx-1-current = <10000>;
+
+ cdc-vddcx-2-supply = <&pm8019_l9>;
+ qcom,cdc-vddcx-2-voltage = <1200000 1200000>;
+ qcom,cdc-vddcx-2-current = <10000>;
+
+ qcom,cdc-micbias-ldoh-v = <0x3>;
+ qcom,cdc-micbias-cfilt1-mv = <1800>;
+ qcom,cdc-micbias-cfilt2-mv = <2700>;
+ qcom,cdc-micbias-cfilt3-mv = <1800>;
+ qcom,cdc-micbias1-cfilt-sel = <0x0>;
+ qcom,cdc-micbias2-cfilt-sel = <0x1>;
+ qcom,cdc-micbias3-cfilt-sel = <0x2>;
+ qcom,cdc-micbias4-cfilt-sel = <0x2>;
+ };
+
+ wcd9xxx_codec@77{
+ compatible = "qcom,wcd9xxx-i2c";
+ reg = <0x77>;
+ };
+
+ wcd9xxx_codec@66{
+ compatible = "qcom,wcd9xxx-i2c";
+ reg = <0x66>;
+ };
+
+ wcd9xxx_codec@55{
+ compatible = "qcom,wcd9xxx-i2c";
+ reg = <0x55>;
+ };
+ };
+
+ sound {
+ compatible = "qcom,mdm9625-audio-taiko";
+ qcom,model = "mdm9625-taiko-i2s-snd-card";
+
+ qcom,audio-routing =
+ "RX_BIAS", "MCLK",
+ "LDO_H", "MCLK",
+ "Ext Spk Bottom Pos", "LINEOUT1",
+ "Ext Spk Bottom Neg", "LINEOUT3",
+ "Ext Spk Top Pos", "LINEOUT2",
+ "Ext Spk Top Neg", "LINEOUT4",
+ "AMIC1", "MIC BIAS1 External",
+ "MIC BIAS1 External", "Handset Mic",
+ "AMIC2", "MIC BIAS2 External",
+ "MIC BIAS2 External", "Headset Mic",
+ "AMIC3", "MIC BIAS3 Internal1",
+ "MIC BIAS3 Internal1", "ANCRight Headset Mic",
+ "AMIC4", "MIC BIAS1 Internal2",
+ "MIC BIAS1 Internal2", "ANCLeft Headset Mic",
+ "DMIC1", "MIC BIAS1 External",
+ "MIC BIAS1 External", "Digital Mic1",
+ "DMIC2", "MIC BIAS1 External",
+ "MIC BIAS1 External", "Digital Mic2",
+ "DMIC3", "MIC BIAS3 External",
+ "MIC BIAS3 External", "Digital Mic3",
+ "DMIC4", "MIC BIAS3 External",
+ "MIC BIAS3 External", "Digital Mic4",
+ "DMIC5", "MIC BIAS4 External",
+ "MIC BIAS4 External", "Digital Mic5",
+ "DMIC6", "MIC BIAS4 External",
+ "MIC BIAS4 External", "Digital Mic6";
+ qcom,taiko-mclk-clk-freq = <12288000>;
+ };
+
+ qcom,msm-adsp-loader {
+ compatible = "qcom,adsp-loader";
+ };
+
+ qcom,msm-pcm {
+ compatible = "qcom,msm-pcm-dsp";
+ };
+
+ qcom,msm-pcm-routing {
+ compatible = "qcom,msm-pcm-routing";
+ };
+
+ qcom,msm-compr-dsp {
+ compatible = "qcom,msm-compr-dsp";
+ };
+
+ qcom,msm-voip-dsp {
+ compatible = "qcom,msm-voip-dsp";
+ };
+
+ qcom,msm-pcm-voice {
+ compatible = "qcom,msm-pcm-voice";
+ };
+
+ qcom,msm-dai-fe {
+ compatible = "qcom,msm-dai-fe";
+ };
+
+ qcom,msm-pcm-afe {
+ compatible = "qcom,msm-pcm-afe";
+ };
+
+ qcom,msm-pcm-hostless {
+ compatible = "qcom,msm-pcm-hostless";
+ };
+
+ 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>;
+ };
+ };
+
+ qcom,msm-dai-q6 {
+ compatible = "qcom,msm-dai-q6";
+ };
};
/include/ "msm-pm8019-rpm-regulator.dtsi"
@@ -329,10 +501,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/include/asm/mach/map.h b/arch/arm/include/asm/mach/map.h
index cd5be28..f705388 100644
--- a/arch/arm/include/asm/mach/map.h
+++ b/arch/arm/include/asm/mach/map.h
@@ -37,6 +37,7 @@
#define MT_MEMORY_RW 16
#define MT_MEMORY_RX 17
#define MT_MEMORY_DMA_READY 18
+#define MT_DEVICE_USER_ACCESSIBLE 19
#ifdef CONFIG_MMU
extern void iotable_init(struct map_desc *, int);
diff --git a/arch/arm/include/asm/user_accessible_timer.h b/arch/arm/include/asm/user_accessible_timer.h
new file mode 100644
index 0000000..c6d7bd4
--- /dev/null
+++ b/arch/arm/include/asm/user_accessible_timer.h
@@ -0,0 +1,49 @@
+/* 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 _ARM_KERNEL_USER_ACCESSIBLE_TIMER_H_
+#define _ARM_KERNEL_USER_ACCESSIBLE_TIMER_H_
+
+#define ARM_USER_ACCESSIBLE_TIMERS_INVALID_PAGE -1
+
+extern unsigned long zero_pfn;
+
+#ifdef CONFIG_ARM_USE_USER_ACCESSIBLE_TIMERS
+#ifndef CONFIG_ARM_USER_ACCESSIBLE_TIMER_BASE
+#define CONFIG_ARM_USER_ACCESSIBLE_TIMER_BASE 0xfffef000
+#endif
+extern void setup_user_timer_offset(unsigned long addr);
+extern int get_timer_page_address(void);
+static inline int get_user_accessible_timers_base(void)
+{
+ return CONFIG_ARM_USER_ACCESSIBLE_TIMER_BASE;
+}
+extern void set_user_accessible_timer_flag(bool flag);
+#else
+#define CONFIG_ARM_USER_ACCESSIBLE_TIMER_BASE 0
+static inline void setup_user_timer_offset(unsigned long addr)
+{
+}
+static inline int get_timer_page_address(void)
+{
+ return ARM_USER_ACCESSIBLE_TIMERS_INVALID_PAGE;
+}
+static inline int get_user_accessible_timers_base(void)
+{
+ return 0;
+}
+static inline void set_user_accessible_timer_flag(bool flag)
+{
+}
+#endif
+
+#endif
diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile
index 22b0f1e..fc00a23 100644
--- a/arch/arm/kernel/Makefile
+++ b/arch/arm/kernel/Makefile
@@ -63,6 +63,7 @@
obj-$(CONFIG_SWP_EMULATE) += swp_emulate.o
CFLAGS_swp_emulate.o := -Wa,-march=armv7-a
obj-$(CONFIG_HAVE_HW_BREAKPOINT) += hw_breakpoint.o
+obj-$(CONFIG_GENERIC_TIME_VSYSCALL) += update_vsyscall_arm.o
obj-$(CONFIG_CPU_XSCALE) += xscale-cp0.o
obj-$(CONFIG_CPU_XSC3) += xscale-cp0.o
@@ -73,6 +74,7 @@
obj-$(CONFIG_HW_PERF_EVENTS) += perf_event.o
AFLAGS_iwmmxt.o := -Wa,-mcpu=iwmmxt
obj-$(CONFIG_ARM_CPU_TOPOLOGY) += topology.o
+obj-$(CONFIG_ARM_USE_USER_ACCESSIBLE_TIMERS) += user_accessible_timer.o
ifneq ($(CONFIG_ARCH_EBSA110),y)
obj-y += io.o
diff --git a/arch/arm/kernel/entry-armv.S b/arch/arm/kernel/entry-armv.S
index 7a8c2d6..ddd421c 100644
--- a/arch/arm/kernel/entry-armv.S
+++ b/arch/arm/kernel/entry-armv.S
@@ -764,6 +764,97 @@
.align 5
.globl __kuser_helper_start
__kuser_helper_start:
+#ifdef GENERIC_TIME_VSYSCALL
+/*
+ * Reference declaration:
+ *
+ * extern struct timezone __kernel_helper_gtod_timezone
+ * extern unsigned int __kernel_helper_gtod_seqnum
+ *
+ * Definition and user space usage example:
+ *
+ * #define __kernel_helper_gtod_timezone (*(unsigned int*)0xffff0f20)
+ * #define __kernel_helper_gtod_seqnum (*(unsigned int*)0xffff0f28)
+ *
+ * unsigned int prelock, postlock ;
+ * do {
+ * prelock = __kernel_helper_gtod_seqnum;
+ * memcpy(&tz, (void*)&(__kernel_helper_gtod_timezone),
+ * sizeof(struct timezone)) ;
+ * postlock = __kernel_helper_gtod_seqnum;
+ * } while (prelock != postlock);
+ *
+ * 0xffff0f20-3: tz_minuteswest
+ * 0xffff0f24-7: tz_dsttime
+ * 0xffff0f28-b: sequence #.
+ * 0xffff0f30-3: offset into CONFIG_USER_ACCESSIBLE_TIMER_BASE to get the timer.
+ * 0xffff0f34-7: Feature flag
+ * 0xffff0f38-b: wall-to-mononic: tv_sec
+ * 0xffff0f3c-f: wall-to-mononic: tv_nsec
+ */
+ .globl __kuser_gtod_timezone
+__kuser_gtod_timezone: @0xffff0f20
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ /* This offset is where the flag to enable the
+ * user accessible timers is located.
+ */
+ .word 0
+ .word 0
+ .word 0
+ .align 5
+
+/*
+ * Reference declaration:
+ *
+ * extern struct timeval __kernel_helper_gtod_timeval
+ * extern unsigned int __kernel_helper_gtod_seqnum
+ *
+ * Definition and user space usage example:
+ *
+ * #define __kernel_helper_gtod_timeval (*(unsigned int*)0xffff0f40)
+ * #define __kernel_helper_gtod_seqnum (*(unsigned int*)0xffff0f48)
+ *
+ * unsigned int prelock, postlock ;
+ * struct gtod {
+ * uint64_t cycle_last;
+ * uint64_t mask;
+ * uint32_t mult;
+ * uint32_t shift;
+ * uint32_t tv_sec;
+ * uint32_t tv_nsec;
+ * };
+ * struct gtod gdtod;
+ *
+ * do {
+ * prelock = __kernel_helper_gtod_seqnum;
+ * memcpy(&gdtod, (void*)&(__kernel_helper_gtod_timeval),
+ * sizeof(struct gtod)) ;
+ * postlock = __kernel_helper_gtod_seqnum;
+ * } while (prelock != postlock);
+ *
+ * 0xffff0f40-7: cycle_last
+ * 0xffff0f48-f: mask
+ * 0xffff0f50-3: mult
+ * 0xffff0f54-7: shift
+ * 0xffff0f58-b: tv_sec
+ * 0xffff0f5c-f: tv_nsec
+ */
+ .globl __kuser_gtod_timeval
+__kuser_gtod_timeval: @0xffff0f40
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .align 5
+#endif
/*
* Due to the length of some sequences, __kuser_cmpxchg64 spans 2 regular
diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c
index af21496..ac17480 100644
--- a/arch/arm/kernel/process.c
+++ b/arch/arm/kernel/process.c
@@ -680,6 +680,11 @@
const char *arch_vma_name(struct vm_area_struct *vma)
{
- return (vma == &gate_vma) ? "[vectors]" : NULL;
+ if (vma == &gate_vma)
+ return "[vectors]";
+ else if (vma == get_user_timers_vma(NULL))
+ return "[timers]";
+ else
+ return NULL;
}
#endif
diff --git a/arch/arm/kernel/update_vsyscall_arm.c b/arch/arm/kernel/update_vsyscall_arm.c
new file mode 100644
index 0000000..51f47ae
--- /dev/null
+++ b/arch/arm/kernel/update_vsyscall_arm.c
@@ -0,0 +1,100 @@
+/* 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/export.h>
+#include <linux/clocksource.h>
+#include <linux/time.h>
+#include "update_vsyscall_arm.h"
+/*
+ * See entry-armv.S for the offsets into the kernel user helper for
+ * these fields.
+ */
+#define ARM_VSYSCALL_TIMER_TZ 0xf20
+#define ARM_VSYSCALL_TIMER_SEQ 0xf28
+#define ARM_VSYSCALL_TIMER_OFFSET 0xf30
+#define ARM_VSYSCALL_TIMER_WTM_TV_SEC 0xf38
+#define ARM_VSYSCALL_TIMER_WTM_TV_NSEC 0xf3c
+#define ARM_VSYSCALL_TIMER_CYCLE_LAST 0xf40
+#define ARM_VSYSCALL_TIMER_MASK 0xf48
+#define ARM_VSYSCALL_TIMER_MULT 0xf50
+#define ARM_VSYSCALL_TIMER_SHIFT 0xf54
+#define ARM_VSYSCALL_TIMER_TV_SEC 0xf58
+#define ARM_VSYSCALL_TIMER_TV_NSEC 0xf5c
+
+struct kernel_gtod_t {
+ u64 cycle_last;
+ u64 mask;
+ u32 mult;
+ u32 shift;
+ u32 tv_sec;
+ u32 tv_nsec;
+};
+
+struct kernel_tz_t {
+ u32 tz_minuteswest;
+ u32 tz_dsttime;
+};
+
+struct kernel_wtm_t {
+ u32 tv_sec;
+ u32 tv_nsec;
+};
+
+/*
+ * Updates the kernel user helper area with the current timespec
+ * data, as well as additional fields needed to calculate
+ * gettimeofday, clock_gettime, etc.
+ */
+void
+update_vsyscall(struct timespec *ts, struct timespec *wtm,
+ struct clocksource *c, u32 mult)
+{
+ unsigned long vectors = (unsigned long)vectors_page;
+ unsigned long flags;
+ unsigned *seqnum = (unsigned *)(vectors + ARM_VSYSCALL_TIMER_SEQ);
+ struct kernel_gtod_t *dgtod = (struct kernel_gtod_t *)(vectors +
+ ARM_VSYSCALL_TIMER_CYCLE_LAST);
+ struct kernel_wtm_t *dgwtm = (struct kernel_wtm_t *)(vectors +
+ ARM_VSYSCALL_TIMER_WTM_TV_SEC);
+
+ write_seqlock_irqsave(&kuh_time_lock, flags);
+ *seqnum = kuh_time_lock.sequence;
+ dgtod->cycle_last = c->cycle_last;
+ dgtod->mask = c->mask;
+ dgtod->mult = c->mult;
+ dgtod->shift = c->shift;
+ dgtod->tv_sec = ts->tv_sec;
+ dgtod->tv_nsec = ts->tv_nsec;
+ dgwtm->tv_sec = wtm->tv_sec;
+ dgwtm->tv_nsec = wtm->tv_nsec;
+ *seqnum = kuh_time_lock.sequence + 1;
+ write_sequnlock_irqrestore(&kuh_time_lock, flags);
+}
+EXPORT_SYMBOL(update_vsyscall);
+
+void
+update_vsyscall_tz(void)
+{
+ unsigned long vectors = (unsigned long)vectors_page;
+ unsigned long flags;
+ unsigned *seqnum = (unsigned *)(vectors + ARM_VSYSCALL_TIMER_SEQ);
+ struct kernel_tz_t *dgtod = (struct kernel_tz_t *)(vectors +
+ ARM_VSYSCALL_TIMER_TZ);
+
+ write_seqlock_irqsave(&kuh_time_lock, flags);
+ *seqnum = kuh_time_lock.sequence;
+ dgtod->tz_minuteswest = sys_tz.tz_minuteswest;
+ dgtod->tz_dsttime = sys_tz.tz_dsttime;
+ *seqnum = kuh_time_lock.sequence + 1;
+ write_sequnlock_irqrestore(&kuh_time_lock, flags);
+}
+EXPORT_SYMBOL(update_vsyscall_tz);
diff --git a/arch/arm/kernel/update_vsyscall_arm.h b/arch/arm/kernel/update_vsyscall_arm.h
new file mode 100644
index 0000000..d06ca56
--- /dev/null
+++ b/arch/arm/kernel/update_vsyscall_arm.h
@@ -0,0 +1,23 @@
+/* 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/export.h>
+#include <linux/clocksource.h>
+#include <linux/time.h>
+
+extern void *vectors_page;
+extern struct timezone sys_tz;
+
+/*
+ * This read-write spinlock protects us from races in SMP while
+ * updating the kernel user helper-embedded time.
+ */
+__cacheline_aligned_in_smp DEFINE_SEQLOCK(kuh_time_lock);
diff --git a/arch/arm/kernel/user_accessible_timer.c b/arch/arm/kernel/user_accessible_timer.c
new file mode 100644
index 0000000..c550c03
--- /dev/null
+++ b/arch/arm/kernel/user_accessible_timer.c
@@ -0,0 +1,132 @@
+/* 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/export.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <asm/user_accessible_timer.h>
+#include <asm/traps.h>
+
+#define USER_ACCESS_TIMER_OFFSET 0xf30
+#define USER_ACCESS_FEATURE_OFFSET 0xf34
+#define USER_ACCESS_FEATURE_FLAG 0xffff0f20
+
+static struct vm_area_struct user_timers_vma;
+static int __init user_timers_vma_init(void)
+{
+ user_timers_vma.vm_start = CONFIG_ARM_USER_ACCESSIBLE_TIMER_BASE;
+ user_timers_vma.vm_end = CONFIG_ARM_USER_ACCESSIBLE_TIMER_BASE
+ + PAGE_SIZE;
+ user_timers_vma.vm_page_prot = PAGE_READONLY;
+ user_timers_vma.vm_flags = VM_READ | VM_MAYREAD;
+ return 0;
+}
+arch_initcall(user_timers_vma_init);
+
+int in_user_timers_area(struct mm_struct *mm, unsigned long addr)
+{
+ return (addr >= user_timers_vma.vm_start) &&
+ (addr < user_timers_vma.vm_end);
+}
+EXPORT_SYMBOL(in_user_timers_area);
+
+struct vm_area_struct *get_user_timers_vma(struct mm_struct *mm)
+{
+ return &user_timers_vma;
+}
+EXPORT_SYMBOL(get_user_timers_vma);
+
+int get_user_timer_page(struct vm_area_struct *vma,
+ struct mm_struct *mm, unsigned long start, unsigned int gup_flags,
+ struct page **pages, int idx, int *goto_next_page)
+{
+ /* Replicates the earlier work done in mm/memory.c */
+ unsigned long pg = start & PAGE_MASK;
+ pgd_t *pgd;
+ pud_t *pud;
+ pmd_t *pmd;
+ pte_t *pte;
+
+ /* Unset this flag -- this only gets activated if the
+ * caller should go straight to the next_page label on
+ * return.
+ */
+ *goto_next_page = 0;
+
+ /* user gate pages are read-only */
+ if (gup_flags & FOLL_WRITE)
+ return idx ? : -EFAULT;
+ if (pg > TASK_SIZE)
+ pgd = pgd_offset_k(pg);
+ else
+ pgd = pgd_offset_gate(mm, pg);
+ BUG_ON(pgd_none(*pgd));
+ pud = pud_offset(pgd, pg);
+ BUG_ON(pud_none(*pud));
+ pmd = pmd_offset(pud, pg);
+ if (pmd_none(*pmd))
+ return idx ? : -EFAULT;
+ VM_BUG_ON(pmd_trans_huge(*pmd));
+ pte = pte_offset_map(pmd, pg);
+ if (pte_none(*pte)) {
+ pte_unmap(pte);
+ return idx ? : -EFAULT;
+ }
+ vma = get_user_timers_vma(mm);
+ if (pages) {
+ struct page *page;
+
+ page = vm_normal_page(vma, start, *pte);
+ if (!page) {
+ if (!(gup_flags & FOLL_DUMP) &&
+ zero_pfn == pte_pfn(*pte))
+ page = pte_page(*pte);
+ else {
+ pte_unmap(pte);
+ return idx ? : -EFAULT;
+ }
+ }
+ pages[idx] = page;
+ get_page(page);
+ }
+ pte_unmap(pte);
+ /* In this case, set the next page */
+ *goto_next_page = 1;
+ return 0;
+}
+EXPORT_SYMBOL(get_user_timer_page);
+
+void setup_user_timer_offset(unsigned long addr)
+{
+#if defined(CONFIG_CPU_USE_DOMAINS)
+ unsigned long vectors = CONFIG_VECTORS_BASE;
+#else
+ unsigned long vectors = (unsigned long)vectors_page;
+#endif
+ unsigned long *timer_offset = (unsigned long *)(vectors +
+ USER_ACCESS_TIMER_OFFSET);
+ *timer_offset = addr;
+}
+EXPORT_SYMBOL(setup_user_timer_offset);
+
+void set_user_accessible_timer_flag(bool flag)
+{
+#if defined(CONFIG_CPU_USE_DOMAINS)
+ unsigned long vectors = CONFIG_VECTORS_BASE;
+#else
+ unsigned long vectors = (unsigned long)vectors_page;
+#endif
+ unsigned long *timer_offset = (unsigned long *)(vectors +
+ USER_ACCESS_FEATURE_OFFSET);
+ *timer_offset = (flag ? USER_ACCESS_FEATURE_FLAG : 0);
+}
+EXPORT_SYMBOL(set_user_accessible_timer_flag);
diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig
index dbb4328..a0868c7 100644
--- a/arch/arm/mach-msm/Kconfig
+++ b/arch/arm/mach-msm/Kconfig
@@ -177,6 +177,10 @@
select ARM_HAS_SG_CHAIN
select MSM_KRAIT_WFE_FIXUP
select MSM_ULTRASOUND_A
+ select GENERIC_TIME_VSYSCALL
+ select USE_USER_ACCESSIBLE_TIMERS
+ select ARM_USE_USER_ACCESSIBLE_TIMERS
+ select MSM_USE_USER_ACCESSIBLE_TIMERS
config ARCH_MSM8930
bool "MSM8930"
@@ -209,6 +213,10 @@
select HOLES_IN_ZONE if SPARSEMEM
select ARM_HAS_SG_CHAIN
select MSM_KRAIT_WFE_FIXUP
+ select GENERIC_TIME_VSYSCALL
+ select USE_USER_ACCESSIBLE_TIMERS
+ select ARM_USE_USER_ACCESSIBLE_TIMERS
+ select MSM_USE_USER_ACCESSIBLE_TIMERS
config ARCH_APQ8064
bool "APQ8064"
@@ -237,6 +245,10 @@
select ARM_HAS_SG_CHAIN
select MSM_KRAIT_WFE_FIXUP
select MSM_ULTRASOUND_A
+ select GENERIC_TIME_VSYSCALL
+ select USE_USER_ACCESSIBLE_TIMERS
+ select ARM_USE_USER_ACCESSIBLE_TIMERS
+ select MSM_USE_USER_ACCESSIBLE_TIMERS
config ARCH_MSM8974
bool "MSM8974"
@@ -349,6 +361,9 @@
select SPARSE_IRQ
select MSM_MULTIMEDIA_USE_ION
select MSM_RPM_STATS_LOG
+ select MSM_QDSP6_APRV2
+ select MSM_QDSP6V2_CODECS
+ select MSM_AUDIO_QDSP6V2 if SND_SOC
config ARCH_MSM8910
bool "MSM8910"
@@ -2661,4 +2676,12 @@
if apps is not responding and holding lock with irqs disabled.
Modem will then generate an raise a FIQ on this line before sending
SMSM reset.
+
+config MSM_USE_USER_ACCESSIBLE_TIMERS
+ bool "Enables mapping an MSM timer counter page to user space."
+ depends on ARM_USE_USER_ACCESSIBLE_TIMERS
+ help
+ Enables MSM-specific user accessible timers via a shared
+ memory page containing the cycle counter.
+
endif
diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile
index 88ed433..8690df8 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
@@ -397,6 +398,8 @@
obj-$(CONFIG_MSM_ENABLE_WDOG_DEBUG_CONTROL) += wdog_debug.o
+obj-$(CONFIG_MSM_USE_USER_ACCESSIBLE_TIMERS) += timer_page.o
+
ifdef CONFIG_MSM_CPR
obj-$(CONFIG_DEBUG_FS) += msm_cpr-debug.o
endif
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-display.c b/arch/arm/mach-msm/board-8930-display.c
index a0bfabf..7e477b1 100644
--- a/arch/arm/mach-msm/board-8930-display.c
+++ b/arch/arm/mach-msm/board-8930-display.c
@@ -740,11 +740,29 @@
return 0;
if (on) {
+ if (!(hdmi_msm_data.is_mhl_enabled)) {
+ rc = gpio_request(HDMI_MHL_MUX_GPIO, "MHL_HDMI_MUX");
+ if (rc < 0) {
+ pr_err("gpio hdmi_mhl mux req failed:%d\n",
+ rc);
+ return rc;
+ }
+ rc = gpio_direction_output(HDMI_MHL_MUX_GPIO, 1);
+ if (rc < 0) {
+ pr_err("set gpio hdmi_mhl dir failed:%d\n",
+ rc);
+ goto error0;
+ }
+ gpio_set_value(HDMI_MHL_MUX_GPIO, 1);
+ pr_debug("set gpio hdmi mhl mux %d to 1\n",
+ HDMI_MHL_MUX_GPIO);
+ }
+
rc = gpio_request(100, "HDMI_DDC_CLK");
if (rc) {
pr_err("'%s'(%d) gpio_request failed, rc=%d\n",
"HDMI_DDC_CLK", 100, rc);
- return rc;
+ goto error0;
}
rc = gpio_request(101, "HDMI_DDC_DATA");
if (rc) {
@@ -760,6 +778,8 @@
}
pr_debug("%s(on): success\n", __func__);
} else {
+ if (!(hdmi_msm_data.is_mhl_enabled))
+ gpio_free(HDMI_MHL_MUX_GPIO);
gpio_free(100);
gpio_free(101);
gpio_free(102);
@@ -773,6 +793,9 @@
gpio_free(101);
error1:
gpio_free(100);
+error0:
+ if (!(hdmi_msm_data.is_mhl_enabled))
+ gpio_free(HDMI_MHL_MUX_GPIO);
return rc;
}
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-8930-regulator-pm8038.c b/arch/arm/mach-msm/board-8930-regulator-pm8038.c
index 727c4c6..c74dc26 100644
--- a/arch/arm/mach-msm/board-8930-regulator-pm8038.c
+++ b/arch/arm/mach-msm/board-8930-regulator-pm8038.c
@@ -490,7 +490,7 @@
/* ID a_on pd ss min_uV max_uV supply sys_uA freq fm ss_fm */
RPM_SMPS(S1, 0, 1, 1, 500000, 1150000, NULL, 100000, 4p80, AUTO, LPM),
RPM_SMPS(S2, 1, 1, 1, 1400000, 1400000, NULL, 100000, 1p60, AUTO, LPM),
- RPM_SMPS(S3, 0, 1, 1, 1150000, 1150000, NULL, 100000, 3p20, AUTO, LPM),
+ RPM_SMPS(S3, 0, 1, 1, 1150000, 1150000, NULL, 100000, 3p20, AUTO, AUTO),
RPM_SMPS(S4, 1, 1, 1, 1950000, 2200000, NULL, 100000, 1p60, AUTO, LPM),
/* ID a_on pd ss min_uV max_uV supply sys_uA init_ip */
diff --git a/arch/arm/mach-msm/board-8930.c b/arch/arm/mach-msm/board-8930.c
index 1b487fa..ece65d6 100644
--- a/arch/arm/mach-msm/board-8930.c
+++ b/arch/arm/mach-msm/board-8930.c
@@ -112,7 +112,6 @@
#define KS8851_IRQ_GPIO 90
#define HAP_SHIFT_LVL_OE_GPIO 47
-#define HDMI_MHL_MUX_GPIO 73
#define MHL_GPIO_INT 72
#define MHL_GPIO_RESET 71
#define MHL_GPIO_PWR_EN 5
diff --git a/arch/arm/mach-msm/board-8930.h b/arch/arm/mach-msm/board-8930.h
index 055576f..dbcfa9d 100644
--- a/arch/arm/mach-msm/board-8930.h
+++ b/arch/arm/mach-msm/board-8930.h
@@ -164,5 +164,7 @@
#define MSM_8930_GSBI10_QUP_I2C_BUS_ID 10
#define MSM_8930_GSBI12_QUP_I2C_BUS_ID 12
+#define HDMI_MHL_MUX_GPIO 73
+
extern struct msm_rtb_platform_data msm8930_rtb_pdata;
extern struct msm_cache_dump_platform_data msm8930_cache_dump_pdata;
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-8974-gpiomux.c b/arch/arm/mach-msm/board-8974-gpiomux.c
index 50b59e1..89ad4ef 100644
--- a/arch/arm/mach-msm/board-8974-gpiomux.c
+++ b/arch/arm/mach-msm/board-8974-gpiomux.c
@@ -152,6 +152,26 @@
};
+static struct gpiomux_setting mhl_suspend_config = {
+ .func = GPIOMUX_FUNC_GPIO,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_DOWN,
+};
+
+static struct gpiomux_setting mhl_active_1_cfg = {
+ .func = GPIOMUX_FUNC_1,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_UP,
+ .dir = GPIOMUX_OUT_HIGH,
+};
+
+static struct gpiomux_setting mhl_active_2_cfg = {
+ .func = GPIOMUX_FUNC_1,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_UP,
+};
+
+
static struct gpiomux_setting hdmi_suspend_cfg = {
.func = GPIOMUX_FUNC_GPIO,
.drv = GPIOMUX_DRV_2MA,
@@ -170,6 +190,34 @@
.pull = GPIOMUX_PULL_DOWN,
};
+static struct msm_gpiomux_config msm_mhl_configs[] __initdata = {
+ {
+ /* mhl-sii8334 pwr */
+ .gpio = 12,
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &mhl_suspend_config,
+ [GPIOMUX_ACTIVE] = &mhl_active_1_cfg,
+ },
+ },
+ {
+ /* mhl-sii8334 intr */
+ .gpio = 82,
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &mhl_suspend_config,
+ [GPIOMUX_ACTIVE] = &mhl_active_1_cfg,
+ },
+ },
+ {
+ /* mhl-sii8334 reset */
+ .gpio = 8,
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &mhl_suspend_config,
+ [GPIOMUX_ACTIVE] = &mhl_active_2_cfg,
+ },
+ },
+};
+
+
static struct msm_gpiomux_config msm_hdmi_configs[] __initdata = {
{
.gpio = 31,
@@ -575,4 +623,5 @@
msm_gpiomux_install(msm_taiko_config, ARRAY_SIZE(msm_taiko_config));
msm_gpiomux_install(msm_hdmi_configs, ARRAY_SIZE(msm_hdmi_configs));
+ msm_gpiomux_install(msm_mhl_configs, ARRAY_SIZE(msm_mhl_configs));
}
diff --git a/arch/arm/mach-msm/board-8974.c b/arch/arm/mach-msm/board-8974.c
index 98a82b1..b092a53 100644
--- a/arch/arm/mach-msm/board-8974.c
+++ b/arch/arm/mach-msm/board-8974.c
@@ -66,168 +66,6 @@
return MEMTYPE_EBI1;
}
-static struct resource smd_resource[] = {
- {
- .name = "modem_smd_in",
- .start = 32 + 25, /* mss_sw_to_kpss_ipc_irq0 */
- .flags = IORESOURCE_IRQ,
- },
- {
- .name = "modem_smsm_in",
- .start = 32 + 26, /* mss_sw_to_kpss_ipc_irq1 */
- .flags = IORESOURCE_IRQ,
- },
- {
- .name = "adsp_smd_in",
- .start = 32 + 156, /* lpass_to_kpss_ipc_irq0 */
- .flags = IORESOURCE_IRQ,
- },
- {
- .name = "adsp_smsm_in",
- .start = 32 + 157, /* lpass_to_kpss_ipc_irq1 */
- .flags = IORESOURCE_IRQ,
- },
- {
- .name = "wcnss_smd_in",
- .start = 32 + 142, /* WcnssAppsSmdMedIrq */
- .flags = IORESOURCE_IRQ,
- },
- {
- .name = "wcnss_smsm_in",
- .start = 32 + 144, /* RivaAppsWlanSmsmIrq */
- .flags = IORESOURCE_IRQ,
- },
- {
- .name = "rpm_smd_in",
- .start = 32 + 168, /* rpm_to_kpss_ipc_irq4 */
- .flags = IORESOURCE_IRQ,
- },
-};
-
-static struct smd_subsystem_config smd_config_list[] = {
- {
- .irq_config_id = SMD_MODEM,
- .subsys_name = "modem",
- .edge = SMD_APPS_MODEM,
-
- .smd_int.irq_name = "modem_smd_in",
- .smd_int.flags = IRQF_TRIGGER_RISING,
- .smd_int.irq_id = -1,
- .smd_int.device_name = "smd_dev",
- .smd_int.dev_id = 0,
- .smd_int.out_bit_pos = 1 << 12,
- .smd_int.out_base = (void __iomem *)MSM_APCS_GCC_BASE,
- .smd_int.out_offset = 0x8,
-
- .smsm_int.irq_name = "modem_smsm_in",
- .smsm_int.flags = IRQF_TRIGGER_RISING,
- .smsm_int.irq_id = -1,
- .smsm_int.device_name = "smsm_dev",
- .smsm_int.dev_id = 0,
- .smsm_int.out_bit_pos = 1 << 13,
- .smsm_int.out_base = (void __iomem *)MSM_APCS_GCC_BASE,
- .smsm_int.out_offset = 0x8,
- },
- {
- .irq_config_id = SMD_Q6,
- .subsys_name = "adsp",
- .edge = SMD_APPS_QDSP,
-
- .smd_int.irq_name = "adsp_smd_in",
- .smd_int.flags = IRQF_TRIGGER_RISING,
- .smd_int.irq_id = -1,
- .smd_int.device_name = "smd_dev",
- .smd_int.dev_id = 0,
- .smd_int.out_bit_pos = 1 << 8,
- .smd_int.out_base = (void __iomem *)MSM_APCS_GCC_BASE,
- .smd_int.out_offset = 0x8,
-
- .smsm_int.irq_name = "adsp_smsm_in",
- .smsm_int.flags = IRQF_TRIGGER_RISING,
- .smsm_int.irq_id = -1,
- .smsm_int.device_name = "smsm_dev",
- .smsm_int.dev_id = 0,
- .smsm_int.out_bit_pos = 1 << 9,
- .smsm_int.out_base = (void __iomem *)MSM_APCS_GCC_BASE,
- .smsm_int.out_offset = 0x8,
- },
- {
- .irq_config_id = SMD_WCNSS,
- .subsys_name = "wcnss",
- .edge = SMD_APPS_WCNSS,
-
- .smd_int.irq_name = "wcnss_smd_in",
- .smd_int.flags = IRQF_TRIGGER_RISING,
- .smd_int.irq_id = -1,
- .smd_int.device_name = "smd_dev",
- .smd_int.dev_id = 0,
- .smd_int.out_bit_pos = 1 << 17,
- .smd_int.out_base = (void __iomem *)MSM_APCS_GCC_BASE,
- .smd_int.out_offset = 0x8,
-
- .smsm_int.irq_name = "wcnss_smsm_in",
- .smsm_int.flags = IRQF_TRIGGER_RISING,
- .smsm_int.irq_id = -1,
- .smsm_int.device_name = "smsm_dev",
- .smsm_int.dev_id = 0,
- .smsm_int.out_bit_pos = 1 << 19,
- .smsm_int.out_base = (void __iomem *)MSM_APCS_GCC_BASE,
- .smsm_int.out_offset = 0x8,
- },
- {
- .irq_config_id = SMD_RPM,
- .subsys_name = NULL, /* do not use PIL to load RPM */
- .edge = SMD_APPS_RPM,
-
- .smd_int.irq_name = "rpm_smd_in",
- .smd_int.flags = IRQF_TRIGGER_RISING | IRQF_NO_SUSPEND,
- .smd_int.irq_id = -1,
- .smd_int.device_name = "smd_dev",
- .smd_int.dev_id = 0,
- .smd_int.out_bit_pos = 1 << 0,
- .smd_int.out_base = (void __iomem *)MSM_APCS_GCC_BASE,
- .smd_int.out_offset = 0x8,
-
- .smsm_int.irq_name = NULL, /* RPM does not support SMSM */
- .smsm_int.flags = 0,
- .smsm_int.irq_id = 0,
- .smsm_int.device_name = NULL,
- .smsm_int.dev_id = 0,
- .smsm_int.out_bit_pos = 0,
- .smsm_int.out_base = NULL,
- .smsm_int.out_offset = 0,
- },
-};
-
-static struct smd_smem_regions aux_smem_areas[] = {
- {
- .phys_addr = (void *)(0xfc428000),
- .size = 0x4000,
- },
-};
-
-static struct smd_subsystem_restart_config smd_ssr_cfg = {
- .disable_smsm_reset_handshake = 1,
-};
-
-static struct smd_platform smd_platform_data = {
- .num_ss_configs = ARRAY_SIZE(smd_config_list),
- .smd_ss_configs = smd_config_list,
- .smd_ssr_config = &smd_ssr_cfg,
- .num_smem_areas = ARRAY_SIZE(aux_smem_areas),
- .smd_smem_areas = aux_smem_areas,
-};
-
-struct platform_device msm_device_smd_8974 = {
- .name = "msm_smd",
- .id = -1,
- .resource = smd_resource,
- .num_resources = ARRAY_SIZE(smd_resource),
- .dev = {
- .platform_data = &smd_platform_data,
- }
-};
-
static struct reserve_info msm8974_reserve_info __initdata = {
.memtype_reserve_table = msm8974_reserve_table,
.paddr_to_memtype = msm8974_paddr_to_memtype,
@@ -426,11 +264,6 @@
ARRAY_SIZE(msm_bus_8974_devices));
};
-void __init msm8974_add_devices(void)
-{
- platform_device_register(&msm_device_smd_8974);
-}
-
/*
* Used to satisfy dependencies for devices that need to be
* run early or in a particular order. Most likely your device doesn't fall
@@ -536,7 +369,6 @@
regulator_has_full_constraints();
of_platform_populate(NULL, of_default_bus_match_table, adata, NULL);
- msm8974_add_devices();
msm8974_add_drivers();
}
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..9102875 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,72 @@
[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 mi2s_active_cfg = {
+ .func = GPIOMUX_FUNC_1,
+ .drv = GPIOMUX_DRV_8MA,
+ .pull = GPIOMUX_PULL_NONE,
+};
+
+static struct gpiomux_setting mi2s_suspend_cfg = {
+ .func = GPIOMUX_FUNC_GPIO,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_DOWN,
+};
+
+static struct gpiomux_setting codec_reset = {
+ .func = GPIOMUX_FUNC_GPIO,
+ .drv = GPIOMUX_DRV_6MA,
+ .pull = GPIOMUX_PULL_NONE,
+ .dir = GPIOMUX_OUT_LOW,
+};
+
+static struct msm_gpiomux_config mdm9625_mi2s_configs[] __initdata = {
+ {
+ .gpio = 12, /* mi2s ws */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &mi2s_suspend_cfg,
+ [GPIOMUX_ACTIVE] = &mi2s_active_cfg,
+ },
+ },
+ {
+ .gpio = 15, /* mi2s sclk */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &mi2s_suspend_cfg,
+ [GPIOMUX_ACTIVE] = &mi2s_active_cfg,
+ },
+ },
+ {
+ .gpio = 14, /* mi2s dout */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &mi2s_suspend_cfg,
+ [GPIOMUX_ACTIVE] = &mi2s_active_cfg,
+ },
+ },
+ {
+ .gpio = 13, /* mi2s din */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &mi2s_suspend_cfg,
+ [GPIOMUX_ACTIVE] = &mi2s_active_cfg,
+ },
+ },
+ {
+ .gpio = 71, /* mi2s mclk */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &mi2s_suspend_cfg,
+ [GPIOMUX_ACTIVE] = &mi2s_active_cfg,
+ },
+ },
+};
+
+static struct msm_gpiomux_config mdm9625_cdc_reset_config[] __initdata = {
+ {
+ .gpio = 22, /* SYS_RST_N */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &codec_reset,
+ },
+ }
};
static struct gpiomux_setting sdc3_clk_active_cfg = {
@@ -208,4 +273,8 @@
msm_gpiomux_install(sdc3_configs, ARRAY_SIZE(sdc3_configs));
msm_gpiomux_install(wlan_ath6kl_configs,
ARRAY_SIZE(wlan_ath6kl_configs));
+ msm_gpiomux_install(mdm9625_mi2s_configs,
+ ARRAY_SIZE(mdm9625_mi2s_configs));
+ msm_gpiomux_install(mdm9625_cdc_reset_config,
+ ARRAY_SIZE(mdm9625_cdc_reset_config));
}
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/include/mach/qdsp6v2/audio_acdb.h b/arch/arm/mach-msm/include/mach/qdsp6v2/audio_acdb.h
index d34536d..88cb94a 100644
--- a/arch/arm/mach-msm/include/mach/qdsp6v2/audio_acdb.h
+++ b/arch/arm/mach-msm/include/mach/qdsp6v2/audio_acdb.h
@@ -14,7 +14,7 @@
#define _AUDIO_ACDB_H
#include <linux/msm_audio_acdb.h>
-#ifdef CONFIG_ARCH_MSM8974
+#if defined CONFIG_ARCH_MSM8974 || defined CONFIG_ARCH_MSM9625
#include <sound/q6adm-v2.h>
#else
#include <sound/q6adm.h>
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/mach-msm/qdsp6v2/q6audio_common.h b/arch/arm/mach-msm/qdsp6v2/q6audio_common.h
index e4291e7..3bc8454 100644
--- a/arch/arm/mach-msm/qdsp6v2/q6audio_common.h
+++ b/arch/arm/mach-msm/qdsp6v2/q6audio_common.h
@@ -15,7 +15,7 @@
#ifndef __Q6_AUDIO_COMMON_H__
#define __Q6_AUDIO_COMMON_H__
-#ifdef CONFIG_ARCH_MSM8974
+#if defined(CONFIG_ARCH_MSM8974) || defined(CONFIG_ARCH_MSM9625)
#include <sound/apr_audio-v2.h>
#include <sound/q6asm-v2.h>
#else
diff --git a/arch/arm/mach-msm/smd.c b/arch/arm/mach-msm/smd.c
index b1dd1db..7427899 100644
--- a/arch/arm/mach-msm/smd.c
+++ b/arch/arm/mach-msm/smd.c
@@ -36,6 +36,8 @@
#include <linux/notifier.h>
#include <linux/sort.h>
#include <linux/suspend.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
#include <mach/msm_smd.h>
#include <mach/msm_iomap.h>
#include <mach/system.h>
@@ -3502,6 +3504,295 @@
return err_ret;
}
+static int __devinit parse_smd_devicetree(struct device_node *node,
+ void *irq_out_base)
+{
+ uint32_t edge;
+ char *key;
+ int ret;
+ uint32_t irq_offset;
+ uint32_t irq_bitmask;
+ uint32_t irq_line;
+ unsigned long irq_flags = IRQF_TRIGGER_RISING;
+ const char *pilstr;
+ struct interrupt_config_item *private_irq;
+
+ key = "qcom,smd-edge";
+ ret = of_property_read_u32(node, key, &edge);
+ if (ret)
+ goto missing_key;
+ SMD_DBG("%s: %s = %d", __func__, key, edge);
+
+ key = "qcom,smd-irq-offset";
+ ret = of_property_read_u32(node, key, &irq_offset);
+ if (ret)
+ goto missing_key;
+ SMD_DBG("%s: %s = %x", __func__, key, irq_offset);
+
+ key = "qcom,smd-irq-bitmask";
+ ret = of_property_read_u32(node, key, &irq_bitmask);
+ if (ret)
+ goto missing_key;
+ SMD_DBG("%s: %s = %x", __func__, key, irq_bitmask);
+
+ key = "interrupts";
+ irq_line = irq_of_parse_and_map(node, 0);
+ if (!irq_line)
+ goto missing_key;
+ SMD_DBG("%s: %s = %d", __func__, key, irq_line);
+
+ key = "qcom,pil-string";
+ pilstr = of_get_property(node, key, NULL);
+ if (pilstr)
+ SMD_DBG("%s: %s = %s", __func__, key, pilstr);
+
+ key = "qcom,irq-no-suspend";
+ ret = of_property_read_bool(node, key);
+ if (ret)
+ irq_flags |= IRQF_NO_SUSPEND;
+
+ private_irq = &private_intr_config[edge_to_pids[edge].remote_pid].smd;
+ private_irq->out_bit_pos = irq_bitmask;
+ private_irq->out_offset = irq_offset;
+ private_irq->out_base = irq_out_base;
+ private_irq->irq_id = irq_line;
+
+ ret = request_irq(irq_line,
+ private_irq->irq_handler,
+ irq_flags,
+ "smd_dev",
+ NULL);
+ if (ret < 0) {
+ pr_err("%s: request_irq() failed on %d\n", __func__, irq_line);
+ return ret;
+ } else {
+ ret = enable_irq_wake(irq_line);
+ if (ret < 0)
+ pr_err("%s: enable_irq_wake() failed on %d\n", __func__,
+ irq_line);
+ }
+
+ if (pilstr)
+ strlcpy(edge_to_pids[edge].subsys_name, pilstr,
+ SMD_MAX_CH_NAME_LEN);
+
+ return 0;
+
+missing_key:
+ pr_err("%s: missing key: %s", __func__, key);
+ return -ENODEV;
+}
+
+static int __devinit parse_smsm_devicetree(struct device_node *node,
+ void *irq_out_base)
+{
+ uint32_t edge;
+ char *key;
+ int ret;
+ uint32_t irq_offset;
+ uint32_t irq_bitmask;
+ uint32_t irq_line;
+ struct interrupt_config_item *private_irq;
+
+ key = "qcom,smsm-edge";
+ ret = of_property_read_u32(node, key, &edge);
+ if (ret)
+ goto missing_key;
+ SMD_DBG("%s: %s = %d", __func__, key, edge);
+
+ key = "qcom,smsm-irq-offset";
+ ret = of_property_read_u32(node, key, &irq_offset);
+ if (ret)
+ goto missing_key;
+ SMD_DBG("%s: %s = %x", __func__, key, irq_offset);
+
+ key = "qcom,smsm-irq-bitmask";
+ ret = of_property_read_u32(node, key, &irq_bitmask);
+ if (ret)
+ goto missing_key;
+ SMD_DBG("%s: %s = %x", __func__, key, irq_bitmask);
+
+ key = "interrupts";
+ irq_line = irq_of_parse_and_map(node, 0);
+ if (!irq_line)
+ goto missing_key;
+ SMD_DBG("%s: %s = %d", __func__, key, irq_line);
+
+ private_irq = &private_intr_config[edge_to_pids[edge].remote_pid].smsm;
+ private_irq->out_bit_pos = irq_bitmask;
+ private_irq->out_offset = irq_offset;
+ private_irq->out_base = irq_out_base;
+ private_irq->irq_id = irq_line;
+
+ ret = request_irq(irq_line,
+ private_irq->irq_handler,
+ IRQF_TRIGGER_RISING,
+ "smsm_dev",
+ NULL);
+ if (ret < 0) {
+ pr_err("%s: request_irq() failed on %d\n", __func__, irq_line);
+ return ret;
+ } else {
+ ret = enable_irq_wake(irq_line);
+ if (ret < 0)
+ pr_err("%s: enable_irq_wake() failed on %d\n", __func__,
+ irq_line);
+ }
+
+ return 0;
+
+missing_key:
+ pr_err("%s: missing key: %s", __func__, key);
+ return -ENODEV;
+}
+
+static void __devinit unparse_smd_devicetree(struct device_node *node)
+{
+ uint32_t irq_line;
+
+ irq_line = irq_of_parse_and_map(node, 0);
+
+ free_irq(irq_line, NULL);
+}
+
+static void __devinit unparse_smsm_devicetree(struct device_node *node)
+{
+ uint32_t irq_line;
+
+ irq_line = irq_of_parse_and_map(node, 0);
+
+ free_irq(irq_line, NULL);
+}
+
+static int __devinit smd_core_devicetree_init(struct platform_device *pdev)
+{
+ char *key;
+ struct resource *r;
+ void *irq_out_base;
+ void *aux_mem_base;
+ uint32_t aux_mem_size;
+ int temp_string_size = 11; /* max 3 digit count */
+ char temp_string[temp_string_size];
+ int count;
+ struct device_node *node;
+ int ret;
+ const char *compatible;
+ int subnode_num = 0;
+
+ disable_smsm_reset_handshake = 1;
+
+ key = "irq-reg-base";
+ r = platform_get_resource_byname(pdev, IORESOURCE_MEM, key);
+ if (!r) {
+ pr_err("%s: missing '%s'\n", __func__, key);
+ return -ENODEV;
+ }
+ irq_out_base = (void *)(r->start);
+ SMD_DBG("%s: %s = %p", __func__, key, irq_out_base);
+
+ count = 1;
+ while (1) {
+ scnprintf(temp_string, temp_string_size, "aux-mem%d", count);
+ r = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ temp_string);
+ if (!r)
+ break;
+
+ ++num_smem_areas;
+ ++count;
+ if (count > 999) {
+ pr_err("%s: max num aux mem regions reached\n",
+ __func__);
+ break;
+ }
+ }
+
+ if (num_smem_areas) {
+ smem_areas = kmalloc(sizeof(struct smem_area) * num_smem_areas,
+ GFP_KERNEL);
+ if (!smem_areas) {
+ pr_err("%s: smem areas kmalloc failed\n", __func__);
+ num_smem_areas = 0;
+ return -ENOMEM;
+ }
+ count = 1;
+ while (1) {
+ scnprintf(temp_string, temp_string_size, "aux-mem%d",
+ count);
+ r = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ temp_string);
+ if (!r)
+ break;
+ aux_mem_base = (void *)(r->start);
+ aux_mem_size = (uint32_t)(resource_size(r));
+ SMD_DBG("%s: %s = %p %x", __func__, temp_string,
+ aux_mem_base, aux_mem_size);
+ smem_areas[count - 1].phys_addr = aux_mem_base;
+ smem_areas[count - 1].size = aux_mem_size;
+ smem_areas[count - 1].virt_addr = ioremap_nocache(
+ (unsigned long)(smem_areas[count-1].phys_addr),
+ smem_areas[count - 1].size);
+ if (!smem_areas[count - 1].virt_addr) {
+ pr_err("%s: ioremap_nocache() of addr:%p size: %x\n",
+ __func__,
+ smem_areas[count - 1].phys_addr,
+ smem_areas[count - 1].size);
+ ret = -ENOMEM;
+ goto free_smem_areas;
+ }
+
+ ++count;
+ if (count > 999) {
+ pr_err("%s: max num aux mem regions reached\n",
+ __func__);
+ break;
+ }
+ }
+ sort(smem_areas, num_smem_areas,
+ sizeof(struct smem_area),
+ sort_cmp_func, NULL);
+ }
+
+ for_each_child_of_node(pdev->dev.of_node, node) {
+ compatible = of_get_property(node, "compatible", NULL);
+ if (!strcmp(compatible, "qcom,smd")) {
+ ret = parse_smd_devicetree(node, irq_out_base);
+ if (ret)
+ goto rollback_subnodes;
+ } else if (!strcmp(compatible, "qcom,smsm")) {
+ ret = parse_smsm_devicetree(node, irq_out_base);
+ if (ret)
+ goto rollback_subnodes;
+ } else {
+ pr_err("%s: invalid child node named: %s\n", __func__,
+ compatible);
+ ret = -ENODEV;
+ goto rollback_subnodes;
+ }
+ ++subnode_num;
+ }
+
+ return 0;
+
+rollback_subnodes:
+ count = 0;
+ for_each_child_of_node(pdev->dev.of_node, node) {
+ if (count >= subnode_num)
+ break;
+ ++count;
+ compatible = of_get_property(node, "compatible", NULL);
+ if (!strcmp(compatible, "qcom,smd"))
+ unparse_smd_devicetree(node);
+ else
+ unparse_smsm_devicetree(node);
+ }
+free_smem_areas:
+ num_smem_areas = 0;
+ kfree(smem_areas);
+ smem_areas = NULL;
+ return ret;
+}
+
static int __devinit msm_smd_probe(struct platform_device *pdev)
{
int ret;
@@ -3522,8 +3813,12 @@
if (pdev) {
if (pdev->dev.of_node) {
- pr_err("SMD: Device tree not currently supported\n");
- return -ENODEV;
+ ret = smd_core_devicetree_init(pdev);
+ if (ret) {
+ pr_err("%s: device tree init failed\n",
+ __func__);
+ return ret;
+ }
} else if (pdev->dev.platform_data) {
ret = smd_core_platform_init(pdev);
if (ret) {
@@ -3600,11 +3895,17 @@
}
late_initcall(modem_restart_late_init);
+static struct of_device_id msm_smem_match_table[] = {
+ { .compatible = "qcom,smem" },
+ {},
+};
+
static struct platform_driver msm_smd_driver = {
.probe = msm_smd_probe,
.driver = {
.name = MODULE_NAME,
.owner = THIS_MODULE,
+ .of_match_table = msm_smem_match_table,
},
};
diff --git a/arch/arm/mach-msm/timer.c b/arch/arm/mach-msm/timer.c
index b61604a..3af066d 100644
--- a/arch/arm/mach-msm/timer.c
+++ b/arch/arm/mach-msm/timer.c
@@ -23,12 +23,14 @@
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/percpu.h>
+#include <linux/mm.h>
#include <asm/localtimer.h>
#include <asm/mach/time.h>
#include <asm/hardware/gic.h>
#include <asm/sched_clock.h>
#include <asm/smp_plat.h>
+#include <asm/user_accessible_timer.h>
#include <mach/msm_iomap.h>
#include <mach/irqs.h>
#include <mach/socinfo.h>
@@ -1161,6 +1163,16 @@
}
msm_sched_clock_init();
+ if (use_user_accessible_timers()) {
+ if (cpu_is_msm8960() || cpu_is_msm8930() || cpu_is_apq8064()) {
+ struct msm_clock *gtclock = &msm_clocks[MSM_CLOCK_GPT];
+ void __iomem *addr = gtclock->regbase +
+ TIMER_COUNT_VAL + global_timer_offset;
+ setup_user_timer_offset(virt_to_phys(addr)&0xfff);
+ set_user_accessible_timer_flag(true);
+ }
+ }
+
#ifdef ARCH_HAS_READ_CURRENT_TIMER
if (is_smp()) {
__raw_writel(1,
diff --git a/arch/arm/mach-msm/timer_page.c b/arch/arm/mach-msm/timer_page.c
new file mode 100644
index 0000000..24d2a35
--- /dev/null
+++ b/arch/arm/mach-msm/timer_page.c
@@ -0,0 +1,36 @@
+/* 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/mm.h>
+#include <linux/export.h>
+#include <asm/user_accessible_timer.h>
+#include "mach/socinfo.h"
+#include "mach/msm_iomap.h"
+
+#include "timer.h"
+
+inline int get_timer_page_address(void)
+{
+ if (!use_user_accessible_timers())
+ return ARM_USER_ACCESSIBLE_TIMERS_INVALID_PAGE;
+
+ if (cpu_is_msm8960())
+ return MSM8960_TMR0_PHYS;
+ else if (cpu_is_msm8930())
+ return MSM8930_TMR0_PHYS;
+ else if (cpu_is_apq8064())
+ return APQ8064_TMR0_PHYS;
+ else
+ return ARM_USER_ACCESSIBLE_TIMERS_INVALID_PAGE;
+}
+EXPORT_SYMBOL(get_timer_page_address);
+
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/arch/arm/mm/init.c b/arch/arm/mm/init.c
index 57f41ca..e2cd0120 100644
--- a/arch/arm/mm/init.c
+++ b/arch/arm/mm/init.c
@@ -771,6 +771,9 @@
printk(KERN_NOTICE "Virtual kernel memory layout:\n"
" vector : 0x%08lx - 0x%08lx (%4ld kB)\n"
+#ifdef CONFIG_ARM_USE_USER_ACCESSIBLE_TIMERS
+ " timers : 0x%08lx - 0x%08lx (%4ld kB)\n"
+#endif
#ifdef CONFIG_HAVE_TCM
" DTCM : 0x%08lx - 0x%08lx (%4ld kB)\n"
" ITCM : 0x%08lx - 0x%08lx (%4ld kB)\n"
@@ -791,6 +794,11 @@
MLK(UL(CONFIG_VECTORS_BASE), UL(CONFIG_VECTORS_BASE) +
(PAGE_SIZE)),
+#ifdef CONFIG_ARM_USE_USER_ACCESSIBLE_TIMERS
+ MLK(UL(CONFIG_ARM_USER_ACCESSIBLE_TIMER_BASE),
+ UL(CONFIG_ARM_USER_ACCESSIBLE_TIMER_BASE)
+ + (PAGE_SIZE)),
+#endif
#ifdef CONFIG_HAVE_TCM
MLK(DTCM_OFFSET, (unsigned long) dtcm_end),
MLK(ITCM_OFFSET, (unsigned long) itcm_end),
diff --git a/arch/arm/mm/mmu.c b/arch/arm/mm/mmu.c
index bae23b0..1cb6cba 100644
--- a/arch/arm/mm/mmu.c
+++ b/arch/arm/mm/mmu.c
@@ -33,6 +33,8 @@
#include <asm/mach/arch.h>
#include <asm/mach/map.h>
+#include <asm/user_accessible_timer.h>
+
#include "mm.h"
/*
@@ -309,6 +311,13 @@
.prot_l1 = PMD_TYPE_TABLE,
.domain = DOMAIN_KERNEL,
},
+ [MT_DEVICE_USER_ACCESSIBLE] = {
+ .prot_pte = PROT_PTE_DEVICE | L_PTE_MT_DEV_SHARED |
+ L_PTE_SHARED | L_PTE_USER | L_PTE_RDONLY,
+ .prot_l1 = PMD_TYPE_TABLE,
+ .prot_sect = PROT_SECT_DEVICE | PMD_SECT_S,
+ .domain = DOMAIN_IO,
+ },
};
const struct mem_type *get_mem_type(unsigned int type)
@@ -764,7 +773,9 @@
const struct mem_type *type;
pgd_t *pgd;
- if (md->virtual != vectors_base() && md->virtual < TASK_SIZE) {
+ if ((md->virtual != vectors_base() &&
+ md->virtual != get_user_accessible_timers_base()) &&
+ md->virtual < TASK_SIZE) {
printk(KERN_WARNING "BUG: not creating mapping for 0x%08llx"
" at 0x%08lx in user region\n",
(long long)__pfn_to_phys((u64)md->pfn), md->virtual);
@@ -1203,6 +1214,20 @@
mdesc->map_io();
fill_pmd_gaps();
+ if (use_user_accessible_timers()) {
+ /*
+ * Generate a mapping for the timer page.
+ */
+ int page_addr = get_timer_page_address();
+ if (page_addr != ARM_USER_ACCESSIBLE_TIMERS_INVALID_PAGE) {
+ map.pfn = __phys_to_pfn(page_addr);
+ map.virtual = CONFIG_ARM_USER_ACCESSIBLE_TIMER_BASE;
+ map.length = PAGE_SIZE;
+ map.type = MT_DEVICE_USER_ACCESSIBLE;
+ create_mapping(&map, false);
+ }
+ }
+
/*
* Finally flush the caches and tlb to ensure that we're in a
* consistent state wrt the writebuffer. This also ensures that
diff --git a/drivers/char/diag/diagchar.h b/drivers/char/diag/diagchar.h
index 28d0565..de3cf52 100644
--- a/drivers/char/diag/diagchar.h
+++ b/drivers/char/diag/diagchar.h
@@ -29,6 +29,7 @@
#define IN_BUF_SIZE 16384
#define MAX_IN_BUF_SIZE 32768
#define MAX_SYNC_OBJ_NAME_SIZE 32
+#define UINT32_MAX UINT_MAX
/* Size of the buffer used for deframing a packet
reveived from the PC tool*/
#define HDLC_MAX 4096
diff --git a/drivers/char/diag/diagchar_core.c b/drivers/char/diag/diagchar_core.c
index fe61d2d..7b17ce4 100644
--- a/drivers/char/diag/diagchar_core.c
+++ b/drivers/char/diag/diagchar_core.c
@@ -358,7 +358,7 @@
}
void diag_add_reg(int j, struct bindpkt_params *params,
- int *success, int *count_entries)
+ int *success, unsigned int *count_entries)
{
*success = 1;
driver->table[j].cmd_code = params->cmd_code;
@@ -380,82 +380,172 @@
(*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)
{
- int i, j, count_entries = 0, temp;
- int success = -1;
+ int i, j, temp, success = -1, status;
+ unsigned int count_entries = 0, interim_count = 0;
void *temp_buf;
uint16_t support_list = 0;
- struct diag_dci_client_tbl *params =
- kzalloc(sizeof(struct diag_dci_client_tbl), GFP_KERNEL);
+ struct diag_dci_client_tbl *dci_params;
struct diag_dci_health_stats stats;
- int status;
if (iocmd == DIAG_IOCTL_COMMAND_REG) {
- struct bindpkt_params_per_process *pkt_params =
- (struct bindpkt_params_per_process *) ioarg;
+ struct bindpkt_params_per_process pkt_params;
+ struct bindpkt_params *params;
+ struct bindpkt_params *head_params;
+ if (copy_from_user(&pkt_params, (void *)ioarg,
+ sizeof(struct bindpkt_params_per_process))) {
+ return -EFAULT;
+ }
+ if ((UINT32_MAX/sizeof(struct bindpkt_params)) <
+ pkt_params.count) {
+ pr_warning("diag: integer overflow while multiply\n");
+ return -EFAULT;
+ }
+ params = kzalloc(pkt_params.count*sizeof(
+ struct bindpkt_params), GFP_KERNEL);
+ if (!params) {
+ pr_err("diag: unable to alloc memory\n");
+ return -ENOMEM;
+ } else
+ head_params = params;
+
+ if (copy_from_user(params, pkt_params.params,
+ pkt_params.count*sizeof(struct bindpkt_params))) {
+ kfree(head_params);
+ return -EFAULT;
+ }
mutex_lock(&driver->diagchar_mutex);
for (i = 0; i < diag_max_reg; i++) {
if (driver->table[i].process_id == 0) {
- diag_add_reg(i, pkt_params->params,
- &success, &count_entries);
- if (pkt_params->count > count_entries) {
- pkt_params->params++;
+ diag_add_reg(i, params, &success,
+ &count_entries);
+ if (pkt_params.count > count_entries) {
+ params++;
} else {
mutex_unlock(&driver->diagchar_mutex);
+ kfree(head_params);
return success;
}
}
}
if (i < diag_threshold_reg) {
/* Increase table size by amount required */
- diag_max_reg += pkt_params->count -
+ if (pkt_params.count >= count_entries) {
+ interim_count = pkt_params.count -
count_entries;
+ } else {
+ pr_warning("diag: error in params count\n");
+ kfree(head_params);
+ mutex_unlock(&driver->diagchar_mutex);
+ return -EFAULT;
+ }
+ if (UINT32_MAX - diag_max_reg >=
+ interim_count) {
+ diag_max_reg += interim_count;
+ } else {
+ pr_warning("diag: Integer overflow\n");
+ kfree(head_params);
+ mutex_unlock(&driver->diagchar_mutex);
+ return -EFAULT;
+ }
/* Make sure size doesnt go beyond threshold */
if (diag_max_reg > diag_threshold_reg) {
diag_max_reg = diag_threshold_reg;
pr_info("diag: best case memory allocation\n");
}
+ if (UINT32_MAX/sizeof(struct diag_master_table) <
+ diag_max_reg) {
+ pr_warning("diag: integer overflow\n");
+ kfree(head_params);
+ mutex_unlock(&driver->diagchar_mutex);
+ return -EFAULT;
+ }
temp_buf = krealloc(driver->table,
diag_max_reg*sizeof(struct
diag_master_table), GFP_KERNEL);
if (!temp_buf) {
- diag_max_reg -= pkt_params->count -
- count_entries;
- pr_alert("diag: Insufficient memory for reg.");
+ pr_alert("diag: Insufficient memory for reg.\n");
mutex_unlock(&driver->diagchar_mutex);
+
+ if (pkt_params.count >= count_entries) {
+ interim_count = pkt_params.count -
+ count_entries;
+ } else {
+ pr_warning("diag: params count error\n");
+ mutex_unlock(&driver->diagchar_mutex);
+ kfree(head_params);
+ return -EFAULT;
+ }
+ if (diag_max_reg >= interim_count) {
+ diag_max_reg -= interim_count;
+ } else {
+ pr_warning("diag: Integer underflow\n");
+ mutex_unlock(&driver->diagchar_mutex);
+ kfree(head_params);
+ return -EFAULT;
+ }
+ kfree(head_params);
return 0;
} else {
driver->table = temp_buf;
}
for (j = i; j < diag_max_reg; j++) {
- diag_add_reg(j, pkt_params->params,
- &success, &count_entries);
- if (pkt_params->count > count_entries) {
- pkt_params->params++;
+ diag_add_reg(j, params, &success,
+ &count_entries);
+ if (pkt_params.count > count_entries) {
+ params++;
} else {
mutex_unlock(&driver->diagchar_mutex);
+ kfree(head_params);
return success;
}
}
+ kfree(head_params);
mutex_unlock(&driver->diagchar_mutex);
} else {
mutex_unlock(&driver->diagchar_mutex);
+ kfree(head_params);
pr_err("Max size reached, Pkt Registration failed for"
" Process %d", current->tgid);
}
success = 0;
} else if (iocmd == DIAG_IOCTL_GET_DELAYED_RSP_ID) {
- struct diagpkt_delay_params *delay_params =
- (struct diagpkt_delay_params *) ioarg;
-
- if ((delay_params->rsp_ptr) &&
- (delay_params->size == sizeof(delayed_rsp_id)) &&
- (delay_params->num_bytes_ptr)) {
- *((uint16_t *)delay_params->rsp_ptr) =
- DIAGPKT_NEXT_DELAYED_RSP_ID(delayed_rsp_id);
- *(delay_params->num_bytes_ptr) = sizeof(delayed_rsp_id);
+ struct diagpkt_delay_params delay_params;
+ uint16_t interim_rsp_id;
+ int interim_size;
+ if (copy_from_user(&delay_params, (void *)ioarg,
+ sizeof(struct diagpkt_delay_params)))
+ return -EFAULT;
+ if ((delay_params.rsp_ptr) &&
+ (delay_params.size == sizeof(delayed_rsp_id)) &&
+ (delay_params.num_bytes_ptr)) {
+ interim_rsp_id = DIAGPKT_NEXT_DELAYED_RSP_ID(
+ delayed_rsp_id);
+ if (copy_to_user((void *)delay_params.rsp_ptr,
+ &interim_rsp_id, sizeof(uint16_t)))
+ return -EFAULT;
+ interim_size = sizeof(delayed_rsp_id);
+ if (copy_to_user((void *)delay_params.num_bytes_ptr,
+ &interim_size, sizeof(int)))
+ return -EFAULT;
success = 0;
}
} else if (iocmd == DIAG_IOCTL_DCI_REG) {
@@ -463,7 +553,13 @@
return DIAG_DCI_NO_REG;
if (driver->num_dci_client >= MAX_DCI_CLIENTS)
return DIAG_DCI_NO_REG;
- if (copy_from_user(params, (void *)ioarg,
+ dci_params = kzalloc(sizeof(struct diag_dci_client_tbl),
+ GFP_KERNEL);
+ if (dci_params == NULL) {
+ pr_err("diag: unable to alloc memory\n");
+ return -ENOMEM;
+ }
+ if (copy_from_user(dci_params, (void *)ioarg,
sizeof(struct diag_dci_client_tbl)))
return -EFAULT;
mutex_lock(&driver->dci_mutex);
@@ -476,9 +572,9 @@
if (driver->dci_client_tbl[i].client == NULL) {
driver->dci_client_tbl[i].client = current;
driver->dci_client_tbl[i].list =
- params->list;
+ dci_params->list;
driver->dci_client_tbl[i].signal_type =
- params->signal_type;
+ dci_params->signal_type;
create_dci_log_mask_tbl(driver->
dci_client_tbl[i].dci_log_mask);
create_dci_event_mask_tbl(driver->
@@ -496,6 +592,7 @@
}
}
mutex_unlock(&driver->dci_mutex);
+ kfree(dci_params);
return driver->dci_client_id;
} else if (iocmd == DIAG_IOCTL_DCI_DEINIT) {
success = -1;
@@ -520,25 +617,29 @@
} else if (iocmd == DIAG_IOCTL_DCI_SUPPORT) {
if (driver->ch_dci)
support_list = support_list | DIAG_CON_MPSS;
- *(uint16_t *)ioarg = support_list;
+ if (copy_to_user((void *)ioarg, &support_list,
+ sizeof(uint16_t)))
+ return -EFAULT;
return DIAG_DCI_NO_ERROR;
} else if (iocmd == DIAG_IOCTL_DCI_HEALTH_STATS) {
if (copy_from_user(&stats, (void *)ioarg,
sizeof(struct diag_dci_health_stats)))
return -EFAULT;
for (i = 0; i < MAX_DCI_CLIENTS; i++) {
- params = &(driver->dci_client_tbl[i]);
- if (params->client &&
- params->client->tgid == current->tgid) {
- stats.dropped_logs = params->dropped_logs;
- stats.dropped_events = params->dropped_events;
- stats.received_logs = params->received_logs;
- stats.received_events = params->received_events;
+ dci_params = &(driver->dci_client_tbl[i]);
+ if (dci_params->client &&
+ dci_params->client->tgid == current->tgid) {
+ stats.dropped_logs = dci_params->dropped_logs;
+ stats.dropped_events =
+ dci_params->dropped_events;
+ stats.received_logs = dci_params->received_logs;
+ stats.received_events =
+ dci_params->received_events;
if (stats.reset_status) {
- params->dropped_logs = 0;
- params->dropped_events = 0;
- params->received_logs = 0;
- params->received_events = 0;
+ dci_params->dropped_logs = 0;
+ dci_params->dropped_events = 0;
+ dci_params->received_logs = 0;
+ dci_params->received_events = 0;
}
break;
}
@@ -551,7 +652,7 @@
for (i = 0; i < driver->num_clients; i++)
if (driver->client_map[i].pid == current->tgid)
break;
- if (i == -1)
+ if (i == driver->num_clients)
return -EINVAL;
driver->data_ready[i] |= DEINIT_TYPE;
wake_up_interruptible(&driver->wait_q);
@@ -583,6 +684,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 +695,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 +796,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;
@@ -1045,7 +1153,7 @@
struct diag_send_desc_type send = { NULL, NULL, DIAG_STATE_START, 0 };
struct diag_hdlc_dest_type enc = { NULL, NULL, 0 };
void *buf_copy = NULL;
- int payload_size;
+ unsigned int payload_size;
#ifdef CONFIG_DIAG_OVER_USB
if (((driver->logging_mode == USB_MODE) && (!driver->usb_connected)) ||
(driver->logging_mode == NO_LOGGING_MODE)) {
@@ -1056,8 +1164,17 @@
/* Get the packet type F3/log/event/Pkt response */
err = copy_from_user((&pkt_type), buf, 4);
/* First 4 bytes indicate the type of payload - ignore these */
+ if (count < 4) {
+ pr_err("diag: Client sending short data\n");
+ return -EBADMSG;
+ }
payload_size = count - 4;
-
+ if (payload_size > USER_SPACE_DATA) {
+ pr_err("diag: Dropping packet, packet payload size crosses 8KB limit. Current payload size %d\n",
+ payload_size);
+ driver->dropped_count++;
+ return -EBADMSG;
+ }
if (pkt_type == DCI_DATA_TYPE) {
err = copy_from_user(driver->user_space_data, buf + 4,
payload_size);
@@ -1206,8 +1323,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 +1352,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 +1378,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/ion/msm/msm_ion.c b/drivers/gpu/ion/msm/msm_ion.c
index 20f84d6..8699178 100644
--- a/drivers/gpu/ion/msm/msm_ion.c
+++ b/drivers/gpu/ion/msm/msm_ion.c
@@ -616,18 +616,15 @@
if (end < start)
goto out;
- down_read(&mm->mmap_sem);
vma = find_vma(mm, start);
if (vma && vma->vm_start < end) {
if (start < vma->vm_start)
- goto out_up;
+ goto out;
if (end > vma->vm_end)
- goto out_up;
+ goto out;
ret = 0;
}
-out_up:
- up_read(&mm->mmap_sem);
out:
return ret;
}
@@ -645,20 +642,12 @@
unsigned long start, end;
struct ion_handle *handle = NULL;
int ret;
+ struct mm_struct *mm = current->active_mm;
if (copy_from_user(&data, (void __user *)arg,
sizeof(struct ion_flush_data)))
return -EFAULT;
- start = (unsigned long) data.vaddr;
- end = (unsigned long) data.vaddr + data.length;
-
- if (check_vaddr_bounds(start, end)) {
- pr_err("%s: virtual address %p is out of bounds\n",
- __func__, data.vaddr);
- return -EINVAL;
- }
-
if (!data.handle) {
handle = ion_import_dma_buf(client, data.fd);
if (IS_ERR(handle)) {
@@ -668,11 +657,27 @@
}
}
+ down_read(&mm->mmap_sem);
+
+ start = (unsigned long) data.vaddr;
+ end = (unsigned long) data.vaddr + data.length;
+
+ if (check_vaddr_bounds(start, end)) {
+ up_read(&mm->mmap_sem);
+ pr_err("%s: virtual address %p is out of bounds\n",
+ __func__, data.vaddr);
+ if (!data.handle)
+ ion_free(client, handle);
+ return -EINVAL;
+ }
+
ret = ion_do_cache_op(client,
data.handle ? data.handle : handle,
data.vaddr, data.offset, data.length,
cmd);
+ up_read(&mm->mmap_sem);
+
if (!data.handle)
ion_free(client, handle);
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..c040bf3 100644
--- a/drivers/gpu/msm/kgsl.c
+++ b/drivers/gpu/msm/kgsl.c
@@ -193,6 +193,52 @@
}
EXPORT_SYMBOL(kgsl_cancel_events);
+int kgsl_memfree_hist_init(void)
+{
+ void *base;
+
+ base = kzalloc(KGSL_MEMFREE_HIST_SIZE, GFP_KERNEL);
+ kgsl_driver.memfree_hist.base_hist_rb = base;
+ if (base == NULL)
+ return -ENOMEM;
+ kgsl_driver.memfree_hist.size = KGSL_MEMFREE_HIST_SIZE;
+ kgsl_driver.memfree_hist.wptr = base;
+ return 0;
+}
+
+void kgsl_memfree_hist_exit(void)
+{
+ kfree(kgsl_driver.memfree_hist.base_hist_rb);
+ kgsl_driver.memfree_hist.base_hist_rb = NULL;
+}
+
+void kgsl_memfree_hist_set_event(unsigned int pid, unsigned int gpuaddr,
+ unsigned int size, int flags)
+{
+ struct kgsl_memfree_hist_elem *p;
+
+ void *base = kgsl_driver.memfree_hist.base_hist_rb;
+ int rbsize = kgsl_driver.memfree_hist.size;
+
+ if (base == NULL)
+ return;
+
+ mutex_lock(&kgsl_driver.memfree_hist_mutex);
+ p = kgsl_driver.memfree_hist.wptr;
+ p->pid = pid;
+ p->gpuaddr = gpuaddr;
+ p->size = size;
+ p->flags = flags;
+
+ kgsl_driver.memfree_hist.wptr++;
+ if ((void *)kgsl_driver.memfree_hist.wptr >= base+rbsize) {
+ kgsl_driver.memfree_hist.wptr =
+ (struct kgsl_memfree_hist_elem *)base;
+ }
+ mutex_unlock(&kgsl_driver.memfree_hist_mutex);
+}
+
+
/* kgsl_get_mem_entry - get the mem_entry structure for the specified object
* @device - Pointer to the device structure
* @ptbase - the pagetable base of the object
@@ -853,13 +899,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 +910,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);
@@ -1349,6 +1405,13 @@
if (entry) {
trace_kgsl_mem_free(entry);
+
+ kgsl_memfree_hist_set_event(
+ entry->priv->pid,
+ entry->memdesc.gpuaddr,
+ entry->memdesc.size,
+ entry->memdesc.flags);
+
kgsl_mem_entry_detach_process(entry);
} else {
KGSL_CORE_ERR("invalid gpuaddr %08x\n", param->gpuaddr);
@@ -2328,6 +2391,8 @@
.process_mutex = __MUTEX_INITIALIZER(kgsl_driver.process_mutex),
.ptlock = __SPIN_LOCK_UNLOCKED(kgsl_driver.ptlock),
.devlock = __MUTEX_INITIALIZER(kgsl_driver.devlock),
+ .memfree_hist_mutex =
+ __MUTEX_INITIALIZER(kgsl_driver.memfree_hist_mutex),
};
EXPORT_SYMBOL(kgsl_driver);
@@ -2658,6 +2723,7 @@
kgsl_driver.class = NULL;
}
+ kgsl_memfree_hist_exit();
unregister_chrdev_region(kgsl_driver.major, KGSL_DEVICE_MAX);
}
@@ -2729,6 +2795,9 @@
goto err;
}
+ if (kgsl_memfree_hist_init())
+ KGSL_CORE_ERR("failed to init memfree_hist");
+
return 0;
err:
diff --git a/drivers/gpu/msm/kgsl.h b/drivers/gpu/msm/kgsl.h
index 17a5b67..d22cb6d 100644
--- a/drivers/gpu/msm/kgsl.h
+++ b/drivers/gpu/msm/kgsl.h
@@ -71,6 +71,23 @@
#define KGSL_STATS_ADD(_size, _stat, _max) \
do { _stat += (_size); if (_stat > _max) _max = _stat; } while (0)
+
+#define KGSL_MEMFREE_HIST_SIZE ((int)(PAGE_SIZE * 2))
+
+struct kgsl_memfree_hist_elem {
+ unsigned int pid;
+ unsigned int gpuaddr;
+ unsigned int size;
+ unsigned int flags;
+};
+
+struct kgsl_memfree_hist {
+ void *base_hist_rb;
+ unsigned int size;
+ struct kgsl_memfree_hist_elem *wptr;
+};
+
+
struct kgsl_device;
struct kgsl_driver {
@@ -98,6 +115,9 @@
void *ptpool;
+ struct mutex memfree_hist_mutex;
+ struct kgsl_memfree_hist memfree_hist;
+
struct {
unsigned int vmalloc;
unsigned int vmalloc_max;
diff --git a/drivers/gpu/msm/kgsl_debugfs.c b/drivers/gpu/msm/kgsl_debugfs.c
index 52097dc..07a5ff4 100644
--- a/drivers/gpu/msm/kgsl_debugfs.c
+++ b/drivers/gpu/msm/kgsl_debugfs.c
@@ -106,6 +106,52 @@
KGSL_DEBUGFS_LOG(mem_log);
KGSL_DEBUGFS_LOG(pwr_log);
+static int memfree_hist_print(struct seq_file *s, void *unused)
+{
+ void *base = kgsl_driver.memfree_hist.base_hist_rb;
+
+ struct kgsl_memfree_hist_elem *wptr = kgsl_driver.memfree_hist.wptr;
+ struct kgsl_memfree_hist_elem *p;
+ char str[16];
+
+ seq_printf(s, "%8s %8s %8s %11s\n",
+ "pid", "gpuaddr", "size", "flags");
+
+ mutex_lock(&kgsl_driver.memfree_hist_mutex);
+ p = wptr;
+ for (;;) {
+ kgsl_get_memory_usage(str, sizeof(str), p->flags);
+ /*
+ * if the ring buffer is not filled up yet
+ * all its empty elems have size==0
+ * just skip them ...
+ */
+ if (p->size)
+ seq_printf(s, "%8d %08x %8d %11s\n",
+ p->pid, p->gpuaddr, p->size, str);
+ p++;
+ if ((void *)p >= base + kgsl_driver.memfree_hist.size)
+ p = (struct kgsl_memfree_hist_elem *) base;
+
+ if (p == kgsl_driver.memfree_hist.wptr)
+ break;
+ }
+ mutex_unlock(&kgsl_driver.memfree_hist_mutex);
+ return 0;
+}
+
+static int memfree_hist_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, memfree_hist_print, inode->i_private);
+}
+
+static const struct file_operations memfree_hist_fops = {
+ .open = memfree_hist_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
void kgsl_device_debugfs_init(struct kgsl_device *device)
{
if (kgsl_debugfs_dir && !IS_ERR(kgsl_debugfs_dir))
@@ -131,6 +177,8 @@
&mem_log_fops);
debugfs_create_file("log_level_pwr", 0644, device->d_debugfs, device,
&pwr_log_fops);
+ debugfs_create_file("memfree_history", 0444, device->d_debugfs, device,
+ &memfree_hist_fops);
/* Create postmortem dump control files */
@@ -190,7 +238,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/msm_vdec.c b/drivers/media/video/msm_vidc/msm_vdec.c
index 22063d4..9e6a593 100644
--- a/drivers/media/video/msm_vidc/msm_vdec.c
+++ b/drivers/media/video/msm_vidc/msm_vdec.c
@@ -518,6 +518,9 @@
fmt->get_frame_size(i,
f->fmt.pix_mp.height,
f->fmt.pix_mp.width);
+ inst->bufq[OUTPUT_PORT].
+ vb2_bufq.plane_sizes[i] =
+ f->fmt.pix_mp.plane_fmt[i].sizeimage;
}
} else {
f->fmt.pix_mp.plane_fmt[0].sizeimage =
@@ -527,6 +530,11 @@
f->fmt.pix_mp.plane_fmt[extra_idx].sizeimage =
inst->buff_req.buffer[HAL_BUFFER_EXTRADATA_OUTPUT].buffer_size;
}
+ for (i = 0; i < fmt->num_planes; ++i)
+ inst->bufq[CAPTURE_PORT].
+ vb2_bufq.plane_sizes[i] =
+ f->fmt.pix_mp.plane_fmt[i].sizeimage;
+
}
} else {
dprintk(VIDC_ERR,
@@ -624,6 +632,10 @@
}
}
f->fmt.pix_mp.num_planes = fmt->num_planes;
+ for (i = 0; i < fmt->num_planes; ++i) {
+ inst->bufq[CAPTURE_PORT].vb2_bufq.plane_sizes[i] =
+ f->fmt.pix_mp.plane_fmt[i].sizeimage;
+ }
} else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
inst->prop.width = f->fmt.pix_mp.width;
inst->prop.height = f->fmt.pix_mp.height;
@@ -652,6 +664,10 @@
fmt->get_frame_size(0, f->fmt.pix_mp.height,
f->fmt.pix_mp.width);
f->fmt.pix_mp.num_planes = fmt->num_planes;
+ for (i = 0; i < fmt->num_planes; ++i) {
+ inst->bufq[OUTPUT_PORT].vb2_bufq.plane_sizes[i] =
+ f->fmt.pix_mp.plane_fmt[i].sizeimage;
+ }
}
err_invalid_fmt:
return rc;
diff --git a/drivers/media/video/msm_vidc/msm_venc.c b/drivers/media/video/msm_vidc/msm_venc.c
index d53da9e..d01841d 100644
--- a/drivers/media/video/msm_vidc/msm_venc.c
+++ b/drivers/media/video/msm_vidc/msm_venc.c
@@ -23,7 +23,6 @@
#define DEFAULT_WIDTH 1280
#define MIN_NUM_OUTPUT_BUFFERS 4
#define MAX_NUM_OUTPUT_BUFFERS 8
-#define MAX_INPUT_BUFFERS 32
#define MIN_BIT_RATE 64000
#define MAX_BIT_RATE 160000000
#define DEFAULT_BIT_RATE 64000
@@ -594,11 +593,11 @@
spin_lock_irqsave(&inst->lock, flags);
*num_buffers = inst->buff_req.buffer[0].buffer_count_actual =
max(*num_buffers, inst->buff_req.buffer[0].
- buffer_count_actual);
+ buffer_count_actual);
spin_unlock_irqrestore(&inst->lock, flags);
property_id = HAL_PARAM_BUFFER_COUNT_ACTUAL;
new_buf_count.buffer_type = HAL_BUFFER_INPUT;
- new_buf_count.buffer_count_actual = MAX_INPUT_BUFFERS;
+ new_buf_count.buffer_count_actual = *num_buffers;
rc = vidc_hal_session_set_property(inst->session,
property_id, &new_buf_count);
dprintk(VIDC_DBG, "size = %d, alignment = %d, count = %d\n",
diff --git a/drivers/media/video/msm_vidc/msm_vidc_common.c b/drivers/media/video/msm_vidc/msm_vidc_common.c
index 9c81d40..6f74d62 100644
--- a/drivers/media/video/msm_vidc/msm_vidc_common.c
+++ b/drivers/media/video/msm_vidc/msm_vidc_common.c
@@ -953,10 +953,10 @@
return;
}
if (core->resources.fw.cookie) {
- subsystem_put(core->resources.fw.cookie);
- core->resources.fw.cookie = NULL;
msm_comm_iommu_detach(core);
msm_comm_disable_clks(core);
+ subsystem_put(core->resources.fw.cookie);
+ core->resources.fw.cookie = NULL;
}
}
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..46015b0 100644
--- a/drivers/misc/qseecom.c
+++ b/drivers/misc/qseecom.c
@@ -35,6 +35,7 @@
#include <linux/elf.h>
#include <linux/firmware.h>
#include <linux/freezer.h>
+#include <linux/scatterlist.h>
#include <mach/board.h>
#include <mach/msm_bus.h>
#include <mach/msm_bus_board.h>
@@ -54,6 +55,8 @@
#define QSEE_CE_CLK_100MHZ 100000000
#define QSEE_CE_CLK_50MHZ 50000000
+#define QSEECOM_MAX_SG_ENTRY 10
+
enum qseecom_command_scm_resp_type {
QSEOS_APP_ID = 0xEE01,
QSEOS_LISTENER_ID
@@ -247,6 +250,11 @@
struct clk *ce_core_src_clk;
struct clk *ce_bus_clk;
+struct qseecom_sg_entry {
+ uint32_t phys_addr;
+ uint32_t len;
+};
+
/* Function proto types */
static int qsee_vote_for_clock(int32_t);
static void qsee_disable_clock_vote(int32_t);
@@ -1093,13 +1101,11 @@
{
struct ion_handle *ihandle;
char *field;
- uint32_t *update;
- ion_phys_addr_t pa;
int ret = 0;
int i = 0;
- uint32_t length;
for (i = 0; i < MAX_ION_FD; i++) {
+ struct sg_table *sg_ptr = NULL;
if (req->ifd_data[i].fd > 0) {
/* Get the handle of the shared fd */
ihandle = ion_import_dma_buf(qseecom.ion_clnt,
@@ -1110,20 +1116,51 @@
}
field = (char *) req->cmd_req_buf +
req->ifd_data[i].cmd_buf_offset;
- update = (uint32_t *) field;
/* Populate the cmd data structure with the phys_addr */
- ret = ion_phys(qseecom.ion_clnt, ihandle, &pa, &length);
- if (ret)
- return -ENOMEM;
-
- *update = (uint32_t)pa;
+ sg_ptr = ion_sg_table(qseecom.ion_clnt, ihandle);
+ if (sg_ptr == NULL) {
+ pr_err("IOn client could not retrieve sg table\n");
+ goto err;
+ }
+ if (sg_ptr->nents == 0) {
+ pr_err("Num of scattered entries is 0\n");
+ goto err;
+ }
+ if (sg_ptr->nents > QSEECOM_MAX_SG_ENTRY) {
+ pr_err("Num of scattered entries");
+ pr_err(" (%d) is greater than max supported %d\n",
+ sg_ptr->nents, QSEECOM_MAX_SG_ENTRY);
+ goto err;
+ }
+ if (sg_ptr->nents == 1) {
+ uint32_t *update;
+ update = (uint32_t *) field;
+ *update = (uint32_t)sg_dma_address(sg_ptr->sgl);
+ } else {
+ struct qseecom_sg_entry *update;
+ struct scatterlist *sg;
+ int j = 0;
+ update = (struct qseecom_sg_entry *) field;
+ sg = sg_ptr->sgl;
+ for (j = 0; j < sg_ptr->nents; j++) {
+ update->phys_addr = (uint32_t)
+ sg_dma_address(sg);
+ update->len = (uint32_t)sg->length;
+ update++;
+ sg = sg_next(sg);
+ }
+ }
/* Deallocate the handle */
if (!IS_ERR_OR_NULL(ihandle))
ion_free(qseecom.ion_clnt, ihandle);
}
}
return ret;
+err:
+ if (!IS_ERR_OR_NULL(ihandle))
+ ion_free(qseecom.ion_clnt, ihandle);
+ return -ENOMEM;
}
static int qseecom_send_modfd_cmd(struct qseecom_dev_handle *data,
@@ -1339,12 +1376,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 +1391,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 +1414,6 @@
break;
}
qsee_disable_clock_vote(CLK_SFPB);
- mutex_unlock(&app_access_lock);
return ret;
}
@@ -1695,6 +1728,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..1955ff4 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,17 @@
#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/rtc.h>
+#include <linux/delay.h>
+#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 */
@@ -49,6 +40,8 @@
#define BMS1_OCV_USE_HIGH_LIMIT_THR0 0x4A
#define BMS1_OCV_USE_HIGH_LIMIT_THR1 0x4B
#define BMS1_OCV_USE_LIMIT_CTL 0x4C
+/* Delay control */
+#define BMS1_S1_DELAY_CTL 0x5A
/* CC interrupt threshold */
#define BMS1_CC_THR0 0x7A
#define BMS1_CC_THR1 0x7B
@@ -60,7 +53,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,19 +64,45 @@
#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 IAVG_SAMPLES 16
+
#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;
+ struct power_supply *batt_psy;
struct spmi_device *spmi;
u16 base;
@@ -93,13 +112,71 @@
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;
+ struct mutex soc_invalidation_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;
+
+ uint16_t ocv_reading_at_100;
+ int64_t cc_reading_at_100;
+ uint16_t prev_last_good_ocv_raw;
+ int last_ocv_uv;
+ int last_cc_uah;
+ unsigned long tm_sec;
+ bool first_time_calc_soc;
+ bool first_time_calc_uuc;
+ int pon_ocv_uv;
+
+ int iavg_samples_ma[IAVG_SAMPLES];
+ int iavg_index;
+ int iavg_num_samples;
+ struct timespec t_soc_queried;
+ int last_soc;
+ int last_soc_est;
+
+ int charge_time_us;
+ int catch_up_time_us;
+ struct single_row_lut *adjusted_fcc_temp_lut;
+
+ 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 +196,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 +235,1420 @@
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)
+{
+ int uv;
+
+ uv = vadc_reading_to_uv(reading);
+ pr_debug("%u raw converted into %d uv\n", reading, uv);
+ uv = adjust_vbatt_reading(chip, uv);
+ pr_debug("adjusted into %d uv\n", uv);
+ return uv;
+}
+
+#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, uint16_t gain)
+{
+ s64 result_uv;
+
+ pr_debug("adjusting_uv = %lld\n", uv);
+ pr_debug("adjusting by factor: %lld/%hu = %lld%%\n",
+ QPNP_ADC_GAIN_NV, gain,
+ div_s64(QPNP_ADC_GAIN_NV * 100LL, (s64)gain));
+
+ result_uv = div_s64(uv * QPNP_ADC_GAIN_NV, (s64)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)
+{
+ struct qpnp_iadc_calib calibration;
+
+ qpnp_iadc_get_gain_and_offset(&calibration);
+ return cc_adjust_for_gain(cc_reading_to_uv(reading),
+ calibration.gain_raw);
+}
+
+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;
+}
+
+#define CC_36_BIT_MASK 0xFFFFFFFFFLL
+
+static int read_cc_raw(struct qpnp_bms_chip *chip, int64_t *reading)
+{
+ int64_t raw_reading;
+ int rc;
+
+ rc = qpnp_read_wrapper(chip, (u8 *)&raw_reading,
+ chip->base + BMS1_CC_DATA0, 5);
+ if (rc) {
+ pr_err("Error reading cc: rc = %d\n", rc);
+ return -ENXIO;
+ }
+
+ raw_reading = raw_reading & CC_36_BIT_MASK;
+ /* convert 36 bit signed value into 64 signed value */
+ *reading = (raw_reading >> 35) == 0LL ?
+ raw_reading : ((-1LL ^ CC_36_BIT_MASK) | raw_reading);
+ pr_debug("before conversion: %llx, after conversion: %llx\n",
+ raw_reading, *reading);
return 0;
}
+static int calib_vadc(struct qpnp_bms_chip *chip)
+{
+ int rc;
+ struct qpnp_vadc_result result;
+
+ rc = qpnp_vadc_read(REF_625MV, &result);
+ if (rc) {
+ pr_debug("vadc read failed with rc = %d\n", rc);
+ return rc;
+ }
+ chip->vadc_v0625 = result.physical;
+
+ rc = qpnp_vadc_read(REF_125V, &result);
+ if (rc) {
+ pr_debug("vadc read failed with rc = %d\n", rc);
+ return rc;
+ }
+ chip->vadc_v1250 = result.physical;
+ pr_debug("vadc calib: 0625 = %d, 1250 = %d\n",
+ chip->vadc_v0625, chip->vadc_v1250);
+ return 0;
+}
+
+static void convert_and_store_ocv(struct qpnp_bms_chip *chip,
+ struct raw_soc_params *raw)
+{
+ int rc;
+
+ pr_debug("prev_last_good_ocv_raw = %d, last_good_ocv_raw = %d\n",
+ chip->prev_last_good_ocv_raw,
+ raw->last_good_ocv_raw);
+ rc = calib_vadc(chip);
+ if (rc)
+ pr_err("Vadc reference voltage read failed, rc = %d\n", rc);
+ chip->prev_last_good_ocv_raw = raw->last_good_ocv_raw;
+ raw->last_good_ocv_uv = convert_vbatt_raw_to_uv(chip,
+ raw->last_good_ocv_raw);
+ chip->last_ocv_uv = raw->last_good_ocv_uv;
+ pr_debug("last_good_ocv_uv = %d\n", raw->last_good_ocv_uv);
+}
+
+static int read_soc_params_raw(struct qpnp_bms_chip *chip,
+ struct raw_soc_params *raw)
+{
+ int rc;
+
+ mutex_lock(&chip->bms_output_lock);
+ lock_output_data(chip);
+
+ rc = qpnp_read_wrapper(chip, (u8 *)&raw->last_good_ocv_raw,
+ chip->base + BMS1_OCV_FOR_SOC_DATA0, 2);
+ if (rc) {
+ pr_err("Error reading ocv: rc = %d\n", rc);
+ return -ENXIO;
+ }
+
+ rc = read_cc_raw(chip, &raw->cc);
+ if (rc) {
+ pr_err("Failed to read raw cc data, rc = %d\n", rc);
+ return rc;
+ }
+
+ unlock_output_data(chip);
+ mutex_unlock(&chip->bms_output_lock);
+
+ if (chip->prev_last_good_ocv_raw == 0) {
+ convert_and_store_ocv(chip, raw);
+ pr_debug("PON_OCV_UV = %d\n", chip->last_ocv_uv);
+ } else if (chip->prev_last_good_ocv_raw != raw->last_good_ocv_raw) {
+ convert_and_store_ocv(chip, raw);
+ /* forget the old cc value upon ocv */
+ chip->last_cc_uah = 0;
+ } else {
+ raw->last_good_ocv_uv = chip->last_ocv_uv;
+ }
+
+ /* fake a high OCV if done charging */
+ if (chip->ocv_reading_at_100 != raw->last_good_ocv_raw) {
+ chip->ocv_reading_at_100 = 0;
+ chip->cc_reading_at_100 = 0;
+ } else {
+ /*
+ * force 100% ocv by selecting the highest voltage the
+ * battery could ever reach
+ */
+ raw->last_good_ocv_uv = chip->max_voltage_uv;
+ chip->last_ocv_uv = chip->max_voltage_uv;
+ }
+ pr_debug("last_good_ocv_raw= 0x%x, last_good_ocv_uv= %duV\n",
+ raw->last_good_ocv_raw, raw->last_good_ocv_uv);
+ pr_debug("cc_raw= 0x%llx\n", raw->cc);
+ return 0;
+}
+
+static int calculate_pc(struct qpnp_bms_chip *chip, int ocv_uv,
+ int batt_temp)
+{
+ int pc;
+
+ pc = interpolate_pc(chip->pc_temp_ocv_lut,
+ batt_temp / 10, ocv_uv / 1000);
+ pr_debug("pc = %u %% for ocv = %d uv batt_temp = %d\n",
+ pc, ocv_uv, batt_temp);
+ /* Multiply the initial FCC value by the scale factor. */
+ return pc;
+}
+
+static int calculate_fcc(struct qpnp_bms_chip *chip, int batt_temp)
+{
+ int fcc_uah;
+
+ if (chip->adjusted_fcc_temp_lut == NULL) {
+ /* interpolate_fcc returns a mv value. */
+ fcc_uah = interpolate_fcc(chip->fcc_temp_lut,
+ batt_temp) * 1000;
+ pr_debug("fcc = %d uAh\n", fcc_uah);
+ return fcc_uah;
+ } else {
+ return 1000 * interpolate_fcc(chip->adjusted_fcc_temp_lut,
+ batt_temp);
+ }
+}
+
+/* calculate remaining charge at the time of ocv */
+static int calculate_ocv_charge(struct qpnp_bms_chip *chip,
+ struct raw_soc_params *raw,
+ int fcc_uah,
+ int batt_temp)
+{
+ int ocv_uv, pc;
+
+ ocv_uv = raw->last_good_ocv_uv;
+ pc = calculate_pc(chip, ocv_uv, batt_temp);
+ pr_debug("ocv_uv = %d pc = %d\n", ocv_uv, pc);
+ return (fcc_uah * pc) / 100;
+}
+
+#define CC_RESOLUTION_N 542535
+#define CC_RESOLUTION_D 100000
+
+static s64 cc_to_uv(s64 cc)
+{
+ return div_s64(cc * CC_RESOLUTION_N, CC_RESOLUTION_D);
+}
+
+#define CC_READING_TICKS 56
+#define SLEEP_CLK_HZ 32764
+#define SECONDS_PER_HOUR 3600
+
+static s64 cc_uv_to_nvh(s64 cc_uv)
+{
+ return div_s64(cc_uv * CC_READING_TICKS * 1000,
+ SLEEP_CLK_HZ * SECONDS_PER_HOUR);
+}
+
+/**
+ * calculate_cc-
+ * @chip: the bms chip pointer
+ * @cc: the cc reading from bms h/w
+ * @val: return value
+ * @coulomb_counter: adjusted coulomb counter for 100%
+ *
+ * RETURNS: in val pointer coulomb counter based charger in uAh
+ * (micro Amp hour)
+ */
+static int calculate_cc(struct qpnp_bms_chip *chip, int64_t cc)
+{
+ int64_t cc_voltage_uv, cc_nvh, cc_uah;
+ struct qpnp_iadc_calib calibration;
+
+ qpnp_iadc_get_gain_and_offset(&calibration);
+ cc_voltage_uv = cc;
+ cc_voltage_uv -= chip->cc_reading_at_100;
+ pr_debug("cc = %lld. after subtracting 0x%llx cc = %lld\n",
+ cc, chip->cc_reading_at_100,
+ cc_voltage_uv);
+ cc_voltage_uv = cc_to_uv(cc_voltage_uv);
+ cc_voltage_uv = cc_adjust_for_gain(cc_voltage_uv, calibration.gain_raw);
+ pr_debug("cc_voltage_uv = %lld uv\n", cc_voltage_uv);
+ cc_nvh = cc_uv_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_mohm);
+ /* cc_raw had 4 bits of extra precision.
+ By now it should be within 32 bit range */
+ return (int)cc_uah;
+}
+
+static int get_rbatt(struct qpnp_bms_chip *chip,
+ int soc_rbatt_mohm, int batt_temp)
+{
+ int rbatt_mohm, scalefactor;
+
+ rbatt_mohm = chip->default_rbatt_mohm;
+ pr_debug("rbatt before scaling = %d\n", rbatt_mohm);
+ if (chip->rbatt_sf_lut == NULL) {
+ pr_debug("RBATT = %d\n", rbatt_mohm);
+ return rbatt_mohm;
+ }
+ /* Convert the batt_temp to DegC from deciDegC */
+ batt_temp = batt_temp / 10;
+ scalefactor = interpolate_scalingfactor(chip->rbatt_sf_lut,
+ batt_temp, soc_rbatt_mohm);
+ pr_debug("rbatt sf = %d for batt_temp = %d, soc_rbatt = %d\n",
+ scalefactor, batt_temp, soc_rbatt_mohm);
+ rbatt_mohm = (rbatt_mohm * scalefactor) / 100;
+
+ rbatt_mohm += chip->r_conn_mohm;
+ pr_debug("adding r_conn_mohm = %d rbatt = %d\n",
+ chip->r_conn_mohm, rbatt_mohm);
+
+ pr_debug("RBATT = %d\n", rbatt_mohm);
+ return rbatt_mohm;
+}
+
+static void calculate_iavg(struct qpnp_bms_chip *chip, int cc_uah,
+ int *iavg_ua)
+{
+ int delta_cc_uah, delta_time_s, rc;
+ struct rtc_time tm;
+ struct rtc_device *rtc;
+ unsigned long now_tm_sec = 0;
+
+ rc = 0;
+ /* if anything fails report the previous iavg_ua */
+ *iavg_ua = chip->prev_iavg_ua;
+
+ rtc = rtc_class_open(CONFIG_RTC_HCTOSYS_DEVICE);
+ if (rtc == NULL) {
+ pr_err("%s: unable to open rtc device (%s)\n",
+ __FILE__, CONFIG_RTC_HCTOSYS_DEVICE);
+ goto out;
+ }
+
+ rc = rtc_read_time(rtc, &tm);
+ if (rc) {
+ pr_err("Error reading rtc device (%s) : %d\n",
+ CONFIG_RTC_HCTOSYS_DEVICE, rc);
+ goto out;
+ }
+
+ rc = rtc_valid_tm(&tm);
+ if (rc) {
+ pr_err("Invalid RTC time (%s): %d\n",
+ CONFIG_RTC_HCTOSYS_DEVICE, rc);
+ goto out;
+ }
+ rtc_tm_to_time(&tm, &now_tm_sec);
+
+ if (chip->tm_sec == 0) {
+ get_battery_current(chip, iavg_ua);
+ goto out;
+ }
+
+ delta_time_s = (now_tm_sec - chip->tm_sec);
+
+ /* use the previous iavg if called within 15 seconds */
+ if (delta_time_s < 15) {
+ *iavg_ua = chip->prev_iavg_ua;
+ goto out;
+ }
+
+ delta_cc_uah = cc_uah - chip->last_cc_uah;
+
+ *iavg_ua = div_s64((s64)delta_cc_uah * 3600, delta_time_s);
+
+ pr_debug("tm_sec = %ld, now_tm_sec = %ld delta_s = %d delta_cc = %d iavg_ua = %d\n",
+ chip->tm_sec, now_tm_sec,
+ delta_time_s, delta_cc_uah, (int)*iavg_ua);
+
+out:
+ /* remember the iavg */
+ chip->prev_iavg_ua = *iavg_ua;
+
+ /* remember cc_uah */
+ chip->last_cc_uah = cc_uah;
+
+ /* remember this time */
+ chip->tm_sec = now_tm_sec;
+}
+
+static int calculate_termination_uuc(struct qpnp_bms_chip *chip,
+ struct soc_params *params,
+ int batt_temp, int uuc_iavg_ma,
+ int *ret_pc_unusable)
+{
+ int unusable_uv, pc_unusable, uuc_uah;
+ int i = 0;
+ int ocv_mv;
+ int batt_temp_degc = batt_temp / 10;
+ int rbatt_mohm;
+ int delta_uv;
+ int prev_delta_uv = 0;
+ int prev_rbatt_mohm = 0;
+ int uuc_rbatt_mohm;
+
+ for (i = 0; i <= 100; i++) {
+ ocv_mv = interpolate_ocv(chip->pc_temp_ocv_lut,
+ batt_temp_degc, i);
+ rbatt_mohm = get_rbatt(chip, i, batt_temp);
+ unusable_uv = (rbatt_mohm * uuc_iavg_ma)
+ + (chip->v_cutoff_uv);
+ delta_uv = ocv_mv * 1000 - unusable_uv;
+
+ pr_debug("soc = %d ocv = %d rbat = %d u_uv = %d delta_v = %d\n",
+ i, ocv_mv, rbatt_mohm, unusable_uv, delta_uv);
+
+ if (delta_uv > 0)
+ break;
+
+ prev_delta_uv = delta_uv;
+ prev_rbatt_mohm = rbatt_mohm;
+ }
+
+ uuc_rbatt_mohm = linear_interpolate(rbatt_mohm, delta_uv,
+ prev_rbatt_mohm, prev_delta_uv,
+ 0);
+
+ unusable_uv = (uuc_rbatt_mohm * uuc_iavg_ma) + (chip->v_cutoff_uv);
+
+ pc_unusable = calculate_pc(chip, unusable_uv, batt_temp);
+ uuc_uah = (params->fcc_uah * pc_unusable) / 100;
+ pr_debug("For uuc_iavg_ma = %d, unusable_rbatt = %d unusable_uv = %d unusable_pc = %d uuc = %d\n",
+ uuc_iavg_ma,
+ uuc_rbatt_mohm, unusable_uv,
+ pc_unusable, uuc_uah);
+ *ret_pc_unusable = pc_unusable;
+ return uuc_uah;
+}
+
+static int adjust_uuc(struct qpnp_bms_chip *chip,
+ struct soc_params *params,
+ int new_pc_unusable,
+ int new_uuc_uah,
+ int batt_temp)
+{
+ int new_unusable_mv, new_iavg_ma;
+ int batt_temp_degc = batt_temp / 10;
+
+ if (chip->prev_pc_unusable == -EINVAL
+ || abs(chip->prev_pc_unusable - new_pc_unusable) <= 1) {
+ chip->prev_pc_unusable = new_pc_unusable;
+ return new_uuc_uah;
+ }
+
+ /* the uuc is trying to change more than 1% restrict it */
+ if (new_pc_unusable > chip->prev_pc_unusable)
+ chip->prev_pc_unusable++;
+ else
+ chip->prev_pc_unusable--;
+
+ new_uuc_uah = (params->fcc_uah * chip->prev_pc_unusable) / 100;
+
+ /* also find update the iavg_ma accordingly */
+ new_unusable_mv = interpolate_ocv(chip->pc_temp_ocv_lut,
+ batt_temp_degc, chip->prev_pc_unusable);
+ if (new_unusable_mv < chip->v_cutoff_uv/1000)
+ new_unusable_mv = chip->v_cutoff_uv/1000;
+
+ new_iavg_ma = (new_unusable_mv * 1000 - chip->v_cutoff_uv)
+ / params->rbatt;
+ if (new_iavg_ma == 0)
+ new_iavg_ma = 1;
+ chip->prev_uuc_iavg_ma = new_iavg_ma;
+ pr_debug("Restricting UUC to %d (%d%%) unusable_mv = %d iavg_ma = %d\n",
+ new_uuc_uah, chip->prev_pc_unusable,
+ new_unusable_mv, new_iavg_ma);
+
+ return new_uuc_uah;
+}
+
+#define CHARGING_IAVG_MA 250
+#define MIN_SECONDS_FOR_VALID_SAMPLE 20
+static int calculate_unusable_charge_uah(struct qpnp_bms_chip *chip,
+ struct soc_params *params,
+ int batt_temp)
+{
+ int uuc_uah_iavg;
+ int i;
+ int uuc_iavg_ma = params->iavg_ua / 1000;
+ int pc_unusable;
+
+ /*
+ * if called first time, fill all the samples with
+ * the shutdown_iavg_ma
+ */
+ if (chip->first_time_calc_uuc && chip->shutdown_iavg_ma != 0) {
+ pr_debug("Using shutdown_iavg_ma = %d in all samples\n",
+ chip->shutdown_iavg_ma);
+ for (i = 0; i < IAVG_SAMPLES; i++)
+ chip->iavg_samples_ma[i] = chip->shutdown_iavg_ma;
+
+ chip->iavg_index = 0;
+ chip->iavg_num_samples = IAVG_SAMPLES;
+ }
+
+ /*
+ * if charging use a nominal avg current to keep
+ * a reasonable UUC while charging
+ */
+ if (uuc_iavg_ma < 0)
+ uuc_iavg_ma = CHARGING_IAVG_MA;
+ chip->iavg_samples_ma[chip->iavg_index] = uuc_iavg_ma;
+ chip->iavg_index = (chip->iavg_index + 1) % IAVG_SAMPLES;
+ chip->iavg_num_samples++;
+ if (chip->iavg_num_samples >= IAVG_SAMPLES)
+ chip->iavg_num_samples = IAVG_SAMPLES;
+
+ /* now that this sample is added calcualte the average */
+ uuc_iavg_ma = 0;
+ if (chip->iavg_num_samples != 0) {
+ for (i = 0; i < chip->iavg_num_samples; i++) {
+ pr_debug("iavg_samples_ma[%d] = %d\n", i,
+ chip->iavg_samples_ma[i]);
+ uuc_iavg_ma += chip->iavg_samples_ma[i];
+ }
+
+ uuc_iavg_ma = DIV_ROUND_CLOSEST(uuc_iavg_ma,
+ chip->iavg_num_samples);
+ }
+
+ uuc_uah_iavg = calculate_termination_uuc(chip, params, uuc_iavg_ma,
+ batt_temp, &pc_unusable);
+ pr_debug("uuc_iavg_ma = %d uuc with iavg = %d\n",
+ uuc_iavg_ma, uuc_uah_iavg);
+
+ chip->prev_uuc_iavg_ma = uuc_iavg_ma;
+ /* restrict the uuc such that it can increase only by one percent */
+ uuc_uah_iavg = adjust_uuc(chip, params, pc_unusable,
+ uuc_uah_iavg, batt_temp);
+
+ chip->first_time_calc_uuc = 0;
+ return uuc_uah_iavg;
+}
+
+static void find_ocv_for_soc(struct qpnp_bms_chip *chip,
+ struct soc_params *params,
+ int batt_temp,
+ int shutdown_soc,
+ int *ret_ocv_uv)
+{
+ s64 ocv_charge_uah;
+ int pc, new_pc;
+ int batt_temp_degc = batt_temp / 10;
+ int ocv_uv;
+
+ ocv_charge_uah = (s64)shutdown_soc
+ * (params->fcc_uah - params->uuc_uah);
+ ocv_charge_uah = div_s64(ocv_charge_uah, 100)
+ + params->cc_uah + params->uuc_uah;
+ pc = DIV_ROUND_CLOSEST((int)ocv_charge_uah * 100, params->fcc_uah);
+ pc = clamp(pc, 0, 100);
+
+ ocv_uv = interpolate_ocv(chip->pc_temp_ocv_lut, batt_temp_degc, pc);
+
+ pr_debug("s_soc = %d, fcc = %d uuc = %d rc = %d, pc = %d, ocv mv = %d\n",
+ shutdown_soc, params->fcc_uah,
+ params->uuc_uah, (int)ocv_charge_uah,
+ pc, ocv_uv);
+ new_pc = interpolate_pc(chip->pc_temp_ocv_lut, batt_temp_degc, ocv_uv);
+ pr_debug("test revlookup pc = %d for ocv = %d\n", new_pc, ocv_uv);
+
+ while (abs(new_pc - pc) > 1) {
+ int delta_mv = 5;
+
+ if (new_pc > pc)
+ delta_mv = -1 * delta_mv;
+
+ ocv_uv = ocv_uv + delta_mv;
+ new_pc = interpolate_pc(chip->pc_temp_ocv_lut,
+ batt_temp_degc, ocv_uv);
+ pr_debug("test revlookup pc = %d for ocv = %d\n",
+ new_pc, ocv_uv);
+ }
+
+ *ret_ocv_uv = ocv_uv * 1000;
+ params->ocv_charge_uah = (int)ocv_charge_uah;
+}
+
+static void calculate_soc_params(struct qpnp_bms_chip *chip,
+ struct raw_soc_params *raw,
+ struct soc_params *params,
+ int batt_temp)
+{
+ int soc_rbatt;
+
+ params->fcc_uah = calculate_fcc(chip, batt_temp);
+ pr_debug("FCC = %uuAh batt_temp = %d\n", params->fcc_uah, batt_temp);
+
+ /* calculate remainging charge */
+ params->ocv_charge_uah = calculate_ocv_charge(
+ chip, raw,
+ params->fcc_uah,
+ batt_temp);
+ pr_debug("ocv_charge_uah = %uuAh\n", params->ocv_charge_uah);
+
+ /* calculate cc micro_volt_hour */
+ params->cc_uah = calculate_cc(chip, raw->cc);
+ pr_debug("cc_uah = %duAh raw->cc = %llx cc = %lld after subtracting %llx\n",
+ params->cc_uah, raw->cc,
+ (int64_t)raw->cc - chip->cc_reading_at_100,
+ chip->cc_reading_at_100);
+
+ soc_rbatt = ((params->ocv_charge_uah - params->cc_uah) * 100)
+ / params->fcc_uah;
+ if (soc_rbatt < 0)
+ soc_rbatt = 0;
+ params->rbatt = get_rbatt(chip, soc_rbatt, batt_temp);
+
+ calculate_iavg(chip, params->cc_uah, ¶ms->iavg_ua);
+
+ params->uuc_uah = calculate_unusable_charge_uah(chip, params,
+ batt_temp);
+ pr_debug("UUC = %uuAh\n", params->uuc_uah);
+}
+
+static bool is_shutdown_soc_within_limits(struct qpnp_bms_chip *chip, int soc)
+{
+ if (chip->shutdown_soc_invalid) {
+ pr_debug("NOT forcing shutdown soc = %d\n", chip->shutdown_soc);
+ return 0;
+ }
+
+ if (abs(chip->shutdown_soc - soc) > chip->shutdown_soc_valid_limit) {
+ pr_debug("rejecting shutdown soc = %d, soc = %d limit = %d\n",
+ chip->shutdown_soc, soc,
+ chip->shutdown_soc_valid_limit);
+ chip->shutdown_soc_invalid = 1;
+ return 0;
+ }
+
+ return 1;
+}
+
+#define BMS_OVERRIDE_MODE_EN_BIT BIT(7)
+#define EN_VBAT_BIT BIT(0)
+#define OVERRIDE_MODE_DELAY_MS 20
+static int override_mode_batt_v_and_i(
+ struct qpnp_bms_chip *chip, int *ibat_ua, int *vbat_uv)
+{
+ int16_t vsense_raw, vbat_raw;
+ int vsense_uv, rc;
+ u8 delay;
+
+ mutex_lock(&chip->bms_output_lock);
+
+ delay = 0x00;
+ rc = qpnp_write_wrapper(chip, &delay,
+ chip->base + BMS1_S1_DELAY_CTL, 1);
+ if (rc)
+ pr_err("unable to write into BMS1_S1_DELAY, rc: %d\n", rc);
+
+ rc = qpnp_masked_write(chip, BMS1_MODE_CTL,
+ BMS_OVERRIDE_MODE_EN_BIT | EN_VBAT_BIT,
+ BMS_OVERRIDE_MODE_EN_BIT | EN_VBAT_BIT);
+ if (rc)
+ pr_err("unable to write into BMS1_MODE_CTL, rc: %d\n", rc);
+
+ msleep(OVERRIDE_MODE_DELAY_MS);
+
+ lock_output_data(chip);
+ qpnp_read_wrapper(chip, (u8 *)&vsense_raw,
+ chip->base + BMS1_VSENSE_AVG_DATA0, 2);
+ qpnp_read_wrapper(chip, (u8 *)&vbat_raw,
+ chip->base + BMS1_VBAT_AVG_DATA0, 2);
+ unlock_output_data(chip);
+
+ rc = qpnp_masked_write(chip, BMS1_MODE_CTL,
+ BMS_OVERRIDE_MODE_EN_BIT | EN_VBAT_BIT, 0);
+
+ delay = 0x0B;
+ rc = qpnp_write_wrapper(chip, &delay,
+ chip->base + BMS1_S1_DELAY_CTL, 1);
+ if (rc)
+ pr_err("unable to write into BMS1_S1_DELAY, rc: %d\n", rc);
+
+ mutex_unlock(&chip->bms_output_lock);
+
+ *vbat_uv = convert_vbatt_raw_to_uv(chip, vbat_raw);
+ vsense_uv = convert_vsense_to_uv(chip, vsense_raw);
+ *ibat_ua = vsense_uv * 1000 / (int)chip->r_sense_mohm;
+
+ pr_debug("vsense_raw = 0x%x vbat_raw = 0x%x ibat_ua = %d vbat_uv = %d\n",
+ (uint16_t)vsense_raw, (uint16_t)vbat_raw,
+ *ibat_ua, *vbat_uv);
+ return 0;
+}
+
+static int get_simultaneous_batt_v_and_i(
+ struct qpnp_bms_chip *chip,
+ int *ibat_ua, int *vbat_uv)
+{
+ int rc;
+ union power_supply_propval ret = {0,};
+
+ if (chip->batt_psy == NULL)
+ chip->batt_psy = power_supply_get_by_name("battery");
+ if (chip->batt_psy) {
+ /* if battery has been registered, use the status property */
+ chip->batt_psy->get_property(chip->batt_psy,
+ POWER_SUPPLY_PROP_STATUS, &ret);
+ } else {
+ /* default to using separate vbat/ibat if unregistered */
+ ret.intval = POWER_SUPPLY_STATUS_FULL;
+ }
+
+ if (ret.intval == POWER_SUPPLY_STATUS_FULL) {
+ pr_debug("batfet is open using separate vbat and ibat meas\n");
+ rc = get_battery_voltage(vbat_uv);
+ if (rc < 0) {
+ pr_err("adc vbat failed err = %d\n", rc);
+ return rc;
+ }
+ rc = get_battery_current(chip, ibat_ua);
+ if (rc < 0) {
+ pr_err("bms ibat failed err = %d\n", rc);
+ return rc;
+ }
+ } else {
+ return override_mode_batt_v_and_i(chip, ibat_ua, vbat_uv);
+ }
+
+ return 0;
+}
+
+static int bound_soc(int soc)
+{
+ soc = max(0, soc);
+ soc = min(100, soc);
+ return soc;
+}
+
+static int charging_adjustments(struct qpnp_bms_chip *chip,
+ struct soc_params *params, int soc,
+ int vbat_uv, int ibat_ua, int batt_temp)
+{
+ int chg_soc;
+
+ if (chip->soc_at_cv == -EINVAL) {
+ /* In constant current charging return the calc soc */
+ if (vbat_uv <= chip->max_voltage_uv)
+ pr_debug("CC CHG SOC %d\n", soc);
+
+ /* Note the CC to CV point */
+ if (vbat_uv >= chip->max_voltage_uv) {
+ chip->soc_at_cv = soc;
+ chip->prev_chg_soc = soc;
+ chip->ibat_at_cv_ua = ibat_ua;
+ pr_debug("CC_TO_CV ibat_ua = %d CHG SOC %d\n",
+ ibat_ua, soc);
+ }
+ return soc;
+ }
+
+ /*
+ * battery is in CV phase - begin liner inerpolation of soc based on
+ * battery charge current
+ */
+
+ /*
+ * if voltage lessened (possibly because of a system load)
+ * keep reporting the prev chg soc
+ */
+ if (vbat_uv <= chip->max_voltage_uv) {
+ pr_debug("vbat %d < max = %d CC CHG SOC %d\n",
+ vbat_uv, chip->max_voltage_uv, chip->prev_chg_soc);
+ return chip->prev_chg_soc;
+ }
+
+ chg_soc = linear_interpolate(chip->soc_at_cv, chip->ibat_at_cv_ua,
+ 100, -100000,
+ ibat_ua);
+
+ /* always report a higher soc */
+ if (chg_soc > chip->prev_chg_soc) {
+ int new_ocv_uv;
+
+ chip->prev_chg_soc = chg_soc;
+
+ find_ocv_for_soc(chip, params, batt_temp, chg_soc, &new_ocv_uv);
+ chip->last_ocv_uv = new_ocv_uv;
+ pr_debug("CC CHG ADJ OCV = %d CHG SOC %d\n",
+ new_ocv_uv,
+ chip->prev_chg_soc);
+ }
+
+ pr_debug("Reporting CHG SOC %d\n", chip->prev_chg_soc);
+ return chip->prev_chg_soc;
+}
+
+static int adjust_soc(struct qpnp_bms_chip *chip, struct soc_params *params,
+ int soc, int batt_temp)
+{
+ int ibat_ua = 0, vbat_uv = 0;
+ int ocv_est_uv = 0, soc_est = 0, pc_est = 0, pc = 0;
+ int delta_ocv_uv = 0;
+ int n = 0;
+ int rc_new_uah = 0;
+ int pc_new = 0;
+ int soc_new = 0;
+ int slope = 0;
+ int rc = 0;
+ int delta_ocv_uv_limit = 0;
+
+ rc = get_simultaneous_batt_v_and_i(chip, &ibat_ua, &vbat_uv);
+ if (rc < 0) {
+ pr_err("simultaneous vbat ibat failed err = %d\n", rc);
+ goto out;
+ }
+
+ delta_ocv_uv_limit = DIV_ROUND_CLOSEST(ibat_ua, 1000);
+
+ ocv_est_uv = vbat_uv + (ibat_ua * params->rbatt)/1000;
+ pc_est = calculate_pc(chip, ocv_est_uv, batt_temp);
+ soc_est = div_s64((s64)params->fcc_uah * pc_est - params->uuc_uah*100,
+ (s64)params->fcc_uah - params->uuc_uah);
+ soc_est = bound_soc(soc_est);
+
+ if (ibat_ua < 0) {
+ soc = charging_adjustments(chip, params, soc, vbat_uv, ibat_ua,
+ batt_temp);
+ goto out;
+ }
+
+ /*
+ * do not adjust
+ * if soc is same as what bms calculated
+ * if soc_est is between 45 and 25, this is the flat portion of the
+ * curve where soc_est is not so accurate. We generally don't want to
+ * adjust when soc_est is inaccurate except for the cases when soc is
+ * way far off (higher than 50 or lesser than 20).
+ * Also don't adjust soc if it is above 90 becuase it might be pulled
+ * low and cause a bad user experience
+ */
+ if (soc_est == soc
+ || (is_between(45, chip->adjust_soc_low_threshold, soc_est)
+ && is_between(50, chip->adjust_soc_low_threshold - 5, soc))
+ || soc >= 90)
+ goto out;
+
+ if (chip->last_soc_est == -EINVAL)
+ chip->last_soc_est = soc;
+
+ n = min(200, max(1 , soc + soc_est + chip->last_soc_est));
+ chip->last_soc_est = soc_est;
+
+ pc = calculate_pc(chip, chip->last_ocv_uv, batt_temp);
+ if (pc > 0) {
+ pc_new = calculate_pc(chip,
+ chip->last_ocv_uv - (++slope * 1000),
+ batt_temp);
+ while (pc_new == pc) {
+ /* start taking 10mV steps */
+ slope = slope + 10;
+ pc_new = calculate_pc(chip,
+ chip->last_ocv_uv - (slope * 1000),
+ batt_temp);
+ }
+ } else {
+ /*
+ * pc is already at the lowest point,
+ * assume 1 millivolt translates to 1% pc
+ */
+ pc = 1;
+ pc_new = 0;
+ slope = 1;
+ }
+
+ delta_ocv_uv = div_s64((soc - soc_est) * (s64)slope * 1000,
+ n * (pc - pc_new));
+
+ if (abs(delta_ocv_uv) > delta_ocv_uv_limit) {
+ pr_debug("limiting delta ocv %d limit = %d\n", delta_ocv_uv,
+ delta_ocv_uv_limit);
+
+ if (delta_ocv_uv > 0)
+ delta_ocv_uv = delta_ocv_uv_limit;
+ else
+ delta_ocv_uv = -1 * delta_ocv_uv_limit;
+ pr_debug("new delta ocv = %d\n", delta_ocv_uv);
+ }
+
+ chip->last_ocv_uv -= delta_ocv_uv;
+
+ if (chip->last_ocv_uv >= chip->max_voltage_uv)
+ chip->last_ocv_uv = chip->max_voltage_uv;
+
+ /* calculate the soc based on this new ocv */
+ pc_new = calculate_pc(chip, chip->last_ocv_uv, batt_temp);
+ rc_new_uah = (params->fcc_uah * pc_new) / 100;
+ soc_new = (rc_new_uah - params->cc_uah - params->uuc_uah)*100
+ / (params->fcc_uah - params->uuc_uah);
+ soc_new = bound_soc(soc_new);
+
+ /*
+ * if soc_new is ZERO force it higher so that phone doesnt report soc=0
+ * soc = 0 should happen only when soc_est == 0
+ */
+ if (soc_new == 0 && soc_est != 0)
+ soc_new = 1;
+
+ soc = soc_new;
+
+out:
+ pr_debug("ibat_ua = %d, vbat_uv = %d, ocv_est_uv = %d, pc_est = %d, soc_est = %d, n = %d, delta_ocv_uv = %d, last_ocv_uv = %d, pc_new = %d, soc_new = %d, rbatt = %d, slope = %d\n",
+ ibat_ua, vbat_uv, ocv_est_uv, pc_est,
+ soc_est, n, delta_ocv_uv, chip->last_ocv_uv,
+ pc_new, soc_new, params->rbatt, slope);
+
+ return soc;
+}
+
+static int calculate_state_of_charge(struct qpnp_bms_chip *chip,
+ struct raw_soc_params *raw,
+ int batt_temp)
+{
+ int soc, new_ocv_uv;
+ int shutdown_soc, new_calculated_soc, remaining_usable_charge_uah;
+ struct soc_params params;
+
+ calculate_soc_params(chip, raw, ¶ms, batt_temp);
+ /* calculate remaining usable charge */
+ remaining_usable_charge_uah = params.ocv_charge_uah
+ - params.cc_uah
+ - params.uuc_uah;
+
+ pr_debug("RUC = %duAh\n", remaining_usable_charge_uah);
+ if (params.fcc_uah - params.uuc_uah <= 0) {
+ pr_warn("FCC = %duAh, UUC = %duAh forcing soc = 0\n",
+ params.fcc_uah,
+ params.uuc_uah);
+ soc = 0;
+ } else {
+ soc = DIV_ROUND_CLOSEST((remaining_usable_charge_uah * 100),
+ (params.fcc_uah
+ - params.uuc_uah));
+ }
+
+ if (chip->first_time_calc_soc && soc < 0) {
+ /*
+ * first time calcualtion and the pon ocv is too low resulting
+ * in a bad soc. Adjust ocv to get 0 soc
+ */
+ pr_debug("soc is %d, adjusting pon ocv to make it 0\n", soc);
+ find_ocv_for_soc(chip, ¶ms, batt_temp, 0, &new_ocv_uv);
+ chip->last_ocv_uv = new_ocv_uv;
+
+ remaining_usable_charge_uah = params.ocv_charge_uah
+ - params.cc_uah
+ - params.uuc_uah;
+
+ soc = DIV_ROUND_CLOSEST((remaining_usable_charge_uah * 100),
+ (params.fcc_uah
+ - params.uuc_uah));
+ pr_debug("DONE for O soc is %d, pon ocv adjusted to %duV\n",
+ soc, chip->last_ocv_uv);
+ }
+
+ if (soc > 100)
+ soc = 100;
+
+ if (soc < 0) {
+ pr_err("bad rem_usb_chg = %d rem_chg %d, cc_uah %d, unusb_chg %d\n",
+ remaining_usable_charge_uah,
+ params.ocv_charge_uah,
+ params.cc_uah, params.uuc_uah);
+
+ pr_err("for bad rem_usb_chg last_ocv_uv = %d batt_temp = %d fcc = %d soc =%d\n",
+ chip->last_ocv_uv, batt_temp,
+ params.fcc_uah, soc);
+ soc = 0;
+ }
+
+ mutex_lock(&chip->soc_invalidation_mutex);
+ shutdown_soc = chip->shutdown_soc;
+
+ if (chip->first_time_calc_soc && soc != shutdown_soc
+ && is_shutdown_soc_within_limits(chip, soc)) {
+ /*
+ * soc for the first time - use shutdown soc
+ * to adjust pon ocv since it is a small percent away from
+ * the real soc
+ */
+ pr_debug("soc = %d before forcing shutdown_soc = %d\n",
+ soc, shutdown_soc);
+ find_ocv_for_soc(chip, ¶ms, batt_temp,
+ shutdown_soc, &new_ocv_uv);
+ chip->pon_ocv_uv = chip->last_ocv_uv;
+ chip->last_ocv_uv = new_ocv_uv;
+
+ remaining_usable_charge_uah = params.ocv_charge_uah
+ - params.cc_uah
+ - params.uuc_uah;
+
+ soc = DIV_ROUND_CLOSEST((remaining_usable_charge_uah * 100),
+ (params.fcc_uah
+ - params.uuc_uah));
+
+ pr_debug("DONE for shutdown_soc = %d soc is %d, adjusted ocv to %duV\n",
+ shutdown_soc, soc, chip->last_ocv_uv);
+ }
+ mutex_unlock(&chip->soc_invalidation_mutex);
+
+ pr_debug("SOC before adjustment = %d\n", soc);
+ new_calculated_soc = adjust_soc(chip, ¶ms, soc, batt_temp);
+
+ if (new_calculated_soc != chip->calculated_soc
+ && chip->bms_psy.name != NULL) {
+ power_supply_changed(&chip->bms_psy);
+ pr_debug("power supply changed\n");
+ }
+
+ chip->calculated_soc = new_calculated_soc;
+ pr_debug("Set calculated SOC = %d\n", chip->calculated_soc);
+ chip->first_time_calc_soc = 0;
+ 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 void backup_soc_and_iavg(struct qpnp_bms_chip *chip, int batt_temp,
+ int soc)
+{
+ u8 temp;
+ int rc;
+ int iavg_ma = chip->prev_uuc_iavg_ma;
+
+ if (iavg_ma > IAVG_START)
+ temp = (iavg_ma - IAVG_START) / IAVG_STEP_SIZE_MA;
+ else
+ temp = 0;
+
+ rc = qpnp_write_wrapper(chip, &temp,
+ chip->base + IAVG_STORAGE_REG, 1);
+
+ if (soc == 0)
+ temp = SOC_ZERO;
+ else
+ temp = soc;
+
+ /* don't store soc if temperature is below 5degC */
+ if (batt_temp > IGNORE_SOC_TEMP_DECIDEG)
+ rc = qpnp_write_wrapper(chip, &temp,
+ chip->base + SOC_STORAGE_REG, 1);
+}
+
+#define SOC_CATCHUP_SEC_MAX 600
+#define SOC_CATCHUP_SEC_PER_PERCENT 60
+#define MAX_CATCHUP_SOC (SOC_CATCHUP_SEC_MAX/SOC_CATCHUP_SEC_PER_PERCENT)
+static int scale_soc_while_chg(struct qpnp_bms_chip *chip,
+ int delta_time_us, int new_soc, int prev_soc)
+{
+ int chg_time_sec;
+ int catch_up_sec;
+ int scaled_soc;
+ int numerator;
+
+ /*
+ * The device must be charging for reporting a higher soc, if
+ * not ignore this soc and continue reporting the prev_soc.
+ * Also don't report a high value immediately slowly scale the
+ * value from prev_soc to the new soc based on a charge time
+ * weighted average
+ */
+
+ /* if not charging, return last soc */
+ if (chip->start_percent == -EINVAL)
+ return prev_soc;
+
+ chg_time_sec = DIV_ROUND_UP(chip->charge_time_us, USEC_PER_SEC);
+ catch_up_sec = DIV_ROUND_UP(chip->catch_up_time_us, USEC_PER_SEC);
+ pr_debug("cts= %d catch_up_sec = %d\n", chg_time_sec, catch_up_sec);
+
+ /*
+ * if charging for more than catch_up time, simply return
+ * new soc
+ */
+ if (chg_time_sec > catch_up_sec)
+ return new_soc;
+
+ numerator = (catch_up_sec - chg_time_sec) * prev_soc
+ + chg_time_sec * new_soc;
+ scaled_soc = numerator / catch_up_sec;
+
+ pr_debug("cts = %d new_soc = %d prev_soc = %d scaled_soc = %d\n",
+ chg_time_sec, new_soc, prev_soc, scaled_soc);
+
+ return scaled_soc;
+}
+
+/*
+ * bms_fake_battery is set in setups where a battery emulator is used instead
+ * of a real battery. This makes the bms driver report a different/fake value
+ * regardless of the calculated state of charge.
+ */
+static int bms_fake_battery = -EINVAL;
+module_param(bms_fake_battery, int, 0644);
+
+static int report_state_of_charge(struct qpnp_bms_chip *chip)
+{
+ int soc;
+ int delta_time_us;
+ struct timespec now;
+ struct qpnp_vadc_result result;
+ int batt_temp;
+ int rc;
+
+ if (bms_fake_battery != -EINVAL) {
+ pr_debug("Returning Fake SOC = %d%%\n", bms_fake_battery);
+ return bms_fake_battery;
+ }
+
+ soc = chip->calculated_soc;
+
+ rc = qpnp_vadc_read(LR_MUX1_BATT_THERM, &result);
+
+ if (rc) {
+ pr_err("error reading adc channel = %d, rc = %d\n",
+ LR_MUX1_BATT_THERM, rc);
+ return rc;
+ }
+ pr_debug("batt_temp phy = %lld meas = 0x%llx\n", result.physical,
+ result.measurement);
+ batt_temp = (int)result.physical;
+
+ do_posix_clock_monotonic_gettime(&now);
+ if (chip->t_soc_queried.tv_sec != 0) {
+ delta_time_us
+ = (now.tv_sec - chip->t_soc_queried.tv_sec) * USEC_PER_SEC
+ + (now.tv_nsec - chip->t_soc_queried.tv_nsec) / 1000;
+ } else {
+ /* calculation for the first time */
+ delta_time_us = 0;
+ }
+
+ /*
+ * account for charge time - limit it to SOC_CATCHUP_SEC to
+ * avoid overflows when charging continues for extended periods
+ */
+ if (chip->start_percent != -EINVAL) {
+ if (chip->charge_time_us == 0) {
+ /*
+ * calculating soc for the first time
+ * after start of chg. Initialize catchup time
+ */
+ if (abs(soc - chip->last_soc) < MAX_CATCHUP_SOC)
+ chip->catch_up_time_us =
+ (soc - chip->last_soc)
+ * SOC_CATCHUP_SEC_PER_PERCENT
+ * USEC_PER_SEC;
+ else
+ chip->catch_up_time_us =
+ SOC_CATCHUP_SEC_MAX * USEC_PER_SEC;
+
+ if (chip->catch_up_time_us < 0)
+ chip->catch_up_time_us = 0;
+ }
+
+ /* add charge time */
+ if (chip->charge_time_us < SOC_CATCHUP_SEC_MAX * USEC_PER_SEC)
+ chip->charge_time_us += delta_time_us;
+
+ /* end catchup if calculated soc and last soc are same */
+ if (chip->last_soc == soc)
+ chip->catch_up_time_us = 0;
+ }
+
+ /* last_soc < soc ... scale and catch up */
+ if (chip->last_soc != -EINVAL && chip->last_soc < soc && soc != 100)
+ soc = scale_soc_while_chg(chip, delta_time_us,
+ soc, chip->last_soc);
+
+ pr_debug("last_soc = %d, calculated_soc = %d, soc = %d\n",
+ chip->last_soc, chip->calculated_soc, soc);
+ chip->last_soc = soc;
+ backup_soc_and_iavg(chip, batt_temp, chip->last_soc);
+ pr_debug("Reported SOC = %d\n", chip->last_soc);
+ chip->t_soc_queried = now;
+
+ return chip->last_soc;
+}
+
+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);
+ else
+ return report_state_of_charge(chip);
}
/* 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 +1682,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 +1714,206 @@
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_soc = -EINVAL;
+ chip->last_vbat_read_uv = -EINVAL;
+ chip->last_soc_est = -EINVAL;
+ chip->first_time_calc_soc = 1;
+ chip->first_time_calc_uuc = 1;
+}
+
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 +1925,10 @@
chip->dev = &(spmi->dev);
chip->spmi = spmi;
+ mutex_init(&chip->bms_output_lock);
+ mutex_init(&chip->last_ocv_uv_mutex);
+ mutex_init(&chip->soc_invalidation_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 +1950,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 +1993,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/mdp.c b/drivers/video/msm/mdp.c
index 0c526fd..fc512c1 100644
--- a/drivers/video/msm/mdp.c
+++ b/drivers/video/msm/mdp.c
@@ -1333,7 +1333,7 @@
}
#endif
-static ssize_t vsync_show_event(struct device *dev,
+ssize_t mdp_dma_show_event(struct device *dev,
struct device_attribute *attr, char *buf)
{
ssize_t ret = 0;
@@ -2244,15 +2244,6 @@
}
#endif
-static DEVICE_ATTR(vsync_event, S_IRUGO, vsync_show_event, NULL);
-static struct attribute *vsync_fs_attrs[] = {
- &dev_attr_vsync_event.attr,
- NULL,
-};
-static struct attribute_group vsync_fs_attr_group = {
- .attrs = vsync_fs_attrs,
-};
-
static int mdp_on(struct platform_device *pdev)
{
int ret = 0;
@@ -2287,21 +2278,7 @@
if (mdp_rev == MDP_REV_303 && mfd->panel.type == MIPI_CMD_PANEL) {
vsync_cntrl.dev = mfd->fbi->dev;
-
- if (!vsync_cntrl.sysfs_created) {
- ret = sysfs_create_group(&vsync_cntrl.dev->kobj,
- &vsync_fs_attr_group);
- if (ret) {
- pr_err("%s: sysfs creation failed, ret=%d\n",
- __func__, ret);
- return ret;
- }
-
- kobject_uevent(&vsync_cntrl.dev->kobj, KOBJ_ADD);
- pr_debug("%s: kobject_uevent(KOBJ_ADD)\n", __func__);
- vsync_cntrl.sysfs_created = 1;
- }
- atomic_set(&vsync_cntrl.suspend, 0);
+ atomic_set(&vsync_cntrl.suspend, 1);
}
mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
@@ -2615,6 +2592,7 @@
/* link to the latest pdev */
mfd->pdev = msm_fb_dev;
mfd->mdp_rev = mdp_rev;
+ mfd->vsync_init = NULL;
mfd->ov0_wb_buf = MDP_ALLOC(sizeof(struct mdp_buf_type));
mfd->ov1_wb_buf = MDP_ALLOC(sizeof(struct mdp_buf_type));
@@ -2780,7 +2758,8 @@
case MIPI_VIDEO_PANEL:
#ifndef CONFIG_FB_MSM_MDP303
mipi = &mfd->panel_info.mipi;
- mdp4_dsi_vsync_init(0);
+ mfd->vsync_init = mdp4_dsi_vsync_init;
+ mfd->vsync_show = mdp4_dsi_video_show_event;
mfd->hw_refresh = TRUE;
mfd->dma_fnc = mdp4_dsi_video_overlay;
mfd->lut_update = mdp_lut_update_lcdc;
@@ -2804,6 +2783,7 @@
mfd->start_histogram = mdp_histogram_start;
mfd->stop_histogram = mdp_histogram_stop;
mfd->vsync_ctrl = mdp_dma_video_vsync_ctrl;
+ mfd->vsync_show = mdp_dma_video_show_event;
if (mfd->panel_info.pdest == DISPLAY_1)
mfd->dma = &dma2_data;
else {
@@ -2824,7 +2804,8 @@
#ifndef CONFIG_FB_MSM_MDP303
mfd->dma_fnc = mdp4_dsi_cmd_overlay;
mipi = &mfd->panel_info.mipi;
- mdp4_dsi_rdptr_init(0);
+ mfd->vsync_init = mdp4_dsi_rdptr_init;
+ mfd->vsync_show = mdp4_dsi_cmd_show_event;
if (mfd->panel_info.pdest == DISPLAY_1) {
if_no = PRIMARY_INTF_SEL;
mfd->dma = &dma2_data;
@@ -2843,6 +2824,7 @@
mfd->start_histogram = mdp_histogram_start;
mfd->stop_histogram = mdp_histogram_stop;
mfd->vsync_ctrl = mdp_dma_vsync_ctrl;
+ mfd->vsync_show = mdp_dma_show_event;
if (mfd->panel_info.pdest == DISPLAY_1)
mfd->dma = &dma2_data;
else {
@@ -2860,7 +2842,8 @@
#ifdef CONFIG_FB_MSM_DTV
case DTV_PANEL:
- mdp4_dtv_vsync_init(0);
+ mfd->vsync_init = mdp4_dtv_vsync_init;
+ mfd->vsync_show = mdp4_dtv_show_event;
pdata->on = mdp4_dtv_on;
pdata->off = mdp4_dtv_off;
mfd->hw_refresh = TRUE;
@@ -2899,7 +2882,8 @@
#endif
#ifdef CONFIG_FB_MSM_MDP40
- mdp4_lcdc_vsync_init(0);
+ mfd->vsync_init = mdp4_lcdc_vsync_init;
+ mfd->vsync_show = mdp4_lcdc_show_event;
if (mfd->panel.type == HDMI_PANEL) {
mfd->dma = &dma_e_data;
mdp4_display_intf_sel(EXTERNAL_INTF_SEL, LCDC_RGB_INTF);
@@ -2910,6 +2894,7 @@
#else
mfd->dma = &dma2_data;
mfd->vsync_ctrl = mdp_dma_lcdc_vsync_ctrl;
+ mfd->vsync_show = mdp_dma_lcdc_show_event;
spin_lock_irqsave(&mdp_spin_lock, flag);
mdp_intr_mask &= ~MDP_DMA_P_DONE;
outp32(MDP_INTR_ENABLE, mdp_intr_mask);
@@ -3010,6 +2995,29 @@
pdev_list[pdev_list_cnt++] = pdev;
mdp4_extn_disp = 0;
+
+ if (mfd->vsync_init != NULL) {
+ mfd->vsync_init(0);
+
+ if (!mfd->vsync_sysfs_created) {
+ mfd->dev_attr.attr.name = "vsync_event";
+ mfd->dev_attr.attr.mode = S_IRUGO;
+ mfd->dev_attr.show = mfd->vsync_show;
+ sysfs_attr_init(&mfd->dev_attr.attr);
+
+ rc = sysfs_create_file(&mfd->fbi->dev->kobj,
+ &mfd->dev_attr.attr);
+ if (rc) {
+ pr_err("%s: sysfs creation failed, ret=%d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ kobject_uevent(&mfd->fbi->dev->kobj, KOBJ_ADD);
+ pr_debug("%s: kobject_uevent(KOBJ_ADD)\n", __func__);
+ mfd->vsync_sysfs_created = 1;
+ }
+ }
return 0;
mdp_probe_err:
diff --git a/drivers/video/msm/mdp.h b/drivers/video/msm/mdp.h
index b4a7f79..0bc2532 100644
--- a/drivers/video/msm/mdp.h
+++ b/drivers/video/msm/mdp.h
@@ -862,6 +862,12 @@
void mdp_dma_vsync_ctrl(int enable);
void mdp_dma_video_vsync_ctrl(int enable);
void mdp_dma_lcdc_vsync_ctrl(int enable);
+ssize_t mdp_dma_show_event(struct device *dev,
+ struct device_attribute *attr, char *buf);
+ssize_t mdp_dma_video_show_event(struct device *dev,
+ struct device_attribute *attr, char *buf);
+ssize_t mdp_dma_lcdc_show_event(struct device *dev,
+ struct device_attribute *attr, char *buf);
#ifdef MDP_HW_VSYNC
void vsync_clk_prepare_enable(void);
diff --git a/drivers/video/msm/mdp4.h b/drivers/video/msm/mdp4.h
index d9da6f9..2f4fac1 100644
--- a/drivers/video/msm/mdp4.h
+++ b/drivers/video/msm/mdp4.h
@@ -557,6 +557,14 @@
void mdp4_dsi_vsync_init(int cndx);
void mdp4_lcdc_vsync_init(int cndx);
void mdp4_dtv_vsync_init(int cndx);
+ssize_t mdp4_dsi_cmd_show_event(struct device *dev,
+ struct device_attribute *attr, char *buf);
+ssize_t mdp4_dsi_video_show_event(struct device *dev,
+ struct device_attribute *attr, char *buf);
+ssize_t mdp4_lcdc_show_event(struct device *dev,
+ struct device_attribute *attr, char *buf);
+ssize_t mdp4_dtv_show_event(struct device *dev,
+ struct device_attribute *attr, char *buf);
void mdp4_overlay_dsi_state_set(int state);
int mdp4_overlay_dsi_state_get(void);
void mdp4_overlay_rgb_setup(struct mdp4_overlay_pipe *pipe);
diff --git a/drivers/video/msm/mdp4_overlay_dsi_cmd.c b/drivers/video/msm/mdp4_overlay_dsi_cmd.c
index 0015403..9cb2b34 100644
--- a/drivers/video/msm/mdp4_overlay_dsi_cmd.c
+++ b/drivers/video/msm/mdp4_overlay_dsi_cmd.c
@@ -662,7 +662,7 @@
mutex_unlock(&vctrl->update_lock);
}
-static ssize_t vsync_show_event(struct device *dev,
+ssize_t mdp4_dsi_cmd_show_event(struct device *dev,
struct device_attribute *attr, char *buf)
{
int cndx;
@@ -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);
@@ -718,6 +721,7 @@
init_completion(&vctrl->dmap_comp);
init_completion(&vctrl->vsync_comp);
spin_lock_init(&vctrl->spin_lock);
+ atomic_set(&vctrl->suspend, 1);
INIT_WORK(&vctrl->clk_work, clk_ctrl_work);
}
@@ -986,14 +990,6 @@
mdp4_dsi_cmd_do_blt(mfd, req->enable);
}
-static DEVICE_ATTR(vsync_event, S_IRUGO, vsync_show_event, NULL);
-static struct attribute *vsync_fs_attrs[] = {
- &dev_attr_vsync_event.attr,
- NULL,
-};
-static struct attribute_group vsync_fs_attr_group = {
- .attrs = vsync_fs_attrs,
-};
int mdp4_dsi_cmd_on(struct platform_device *pdev)
{
int ret = 0;
@@ -1018,22 +1014,7 @@
atomic_set(&vctrl->suspend, 0);
- if (!vctrl->sysfs_created) {
- ret = sysfs_create_group(&vctrl->dev->kobj,
- &vsync_fs_attr_group);
- if (ret) {
- pr_err("%s: sysfs group creation failed, ret=%d\n",
- __func__, ret);
- return ret;
- }
-
- kobject_uevent(&vctrl->dev->kobj, KOBJ_ADD);
- pr_debug("%s: kobject_uevent(KOBJ_ADD)\n", __func__);
- vctrl->sysfs_created = 1;
- }
-
pr_debug("%s-:\n", __func__);
-
return ret;
}
diff --git a/drivers/video/msm/mdp4_overlay_dsi_video.c b/drivers/video/msm/mdp4_overlay_dsi_video.c
index 501c4e6..f5df938 100644
--- a/drivers/video/msm/mdp4_overlay_dsi_video.c
+++ b/drivers/video/msm/mdp4_overlay_dsi_video.c
@@ -374,7 +374,7 @@
wait_for_completion(&vctrl->ov_comp);
}
-static ssize_t vsync_show_event(struct device *dev,
+ssize_t mdp4_dsi_video_show_event(struct device *dev,
struct device_attribute *attr, char *buf)
{
int cndx;
@@ -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);
@@ -417,7 +420,7 @@
return;
}
- pr_info("%s: ndx=%d\n", __func__, cndx);
+ pr_debug("%s: ndx=%d\n", __func__, cndx);
vctrl = &vsync_ctrl_db[cndx];
if (vctrl->inited)
@@ -429,7 +432,7 @@
init_completion(&vctrl->vsync_comp);
init_completion(&vctrl->dmap_comp);
init_completion(&vctrl->ov_comp);
- atomic_set(&vctrl->suspend, 0);
+ atomic_set(&vctrl->suspend, 1);
atomic_set(&vctrl->vsync_resume, 1);
spin_lock_init(&vctrl->spin_lock);
}
@@ -447,16 +450,6 @@
vctrl->base_pipe = pipe;
}
-static DEVICE_ATTR(vsync_event, S_IRUGO, vsync_show_event, NULL);
-
-static struct attribute *vsync_fs_attrs[] = {
- &dev_attr_vsync_event.attr,
- NULL,
-};
-
-static struct attribute_group vsync_fs_attr_group = {
- .attrs = vsync_fs_attrs,
-};
int mdp4_dsi_video_on(struct platform_device *pdev)
{
int dsi_width;
@@ -673,20 +666,6 @@
mdp_histogram_ctrl_all(TRUE);
- if (!vctrl->sysfs_created) {
- ret = sysfs_create_group(&vctrl->dev->kobj,
- &vsync_fs_attr_group);
- if (ret) {
- pr_err("%s: sysfs group creation failed, ret=%d\n",
- __func__, ret);
- return ret;
- }
-
- kobject_uevent(&vctrl->dev->kobj, KOBJ_ADD);
- pr_debug("%s: kobject_uevent(KOBJ_ADD)\n", __func__);
- vctrl->sysfs_created = 1;
- }
-
return ret;
}
diff --git a/drivers/video/msm/mdp4_overlay_dtv.c b/drivers/video/msm/mdp4_overlay_dtv.c
index 2d48781..4db684b 100644
--- a/drivers/video/msm/mdp4_overlay_dtv.c
+++ b/drivers/video/msm/mdp4_overlay_dtv.c
@@ -310,7 +310,7 @@
wait_for_completion(&vctrl->dmae_comp);
}
-static ssize_t vsync_show_event(struct device *dev,
+ssize_t mdp4_dtv_show_event(struct device *dev,
struct device_attribute *attr, char *buf)
{
int cndx;
@@ -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;
@@ -382,7 +385,7 @@
init_completion(&vctrl->vsync_comp);
init_completion(&vctrl->ov_comp);
init_completion(&vctrl->dmae_comp);
- atomic_set(&vctrl->suspend, 0);
+ atomic_set(&vctrl->suspend, 1);
atomic_set(&vctrl->vsync_resume, 1);
spin_lock_init(&vctrl->spin_lock);
}
@@ -561,15 +564,6 @@
return 0;
}
-static DEVICE_ATTR(vsync_event, S_IRUGO, vsync_show_event, NULL);
-static struct attribute *vsync_fs_attrs[] = {
- &dev_attr_vsync_event.attr,
- NULL,
-};
-static struct attribute_group vsync_fs_attr_group = {
- .attrs = vsync_fs_attrs,
-};
-
int mdp4_dtv_on(struct platform_device *pdev)
{
struct msm_fb_data_type *mfd;
@@ -609,21 +603,6 @@
pr_warn("%s: panel_next_on failed", __func__);
atomic_set(&vctrl->suspend, 0);
-
- if (!vctrl->sysfs_created) {
- ret = sysfs_create_group(&vctrl->dev->kobj,
- &vsync_fs_attr_group);
- if (ret) {
- pr_err("%s: sysfs group creation failed, ret=%d\n",
- __func__, ret);
- return ret;
- }
-
- kobject_uevent(&vctrl->dev->kobj, KOBJ_ADD);
- pr_debug("%s: kobject_uevent(KOBJ_ADD)\n", __func__);
- vctrl->sysfs_created = 1;
- }
-
if (mfd->avtimer_phy && (vctrl->avtimer == NULL)) {
vctrl->avtimer = (uint32 *)ioremap(mfd->avtimer_phy, 8);
if (vctrl->avtimer == NULL)
diff --git a/drivers/video/msm/mdp4_overlay_lcdc.c b/drivers/video/msm/mdp4_overlay_lcdc.c
index df5c262..172687a 100644
--- a/drivers/video/msm/mdp4_overlay_lcdc.c
+++ b/drivers/video/msm/mdp4_overlay_lcdc.c
@@ -359,7 +359,7 @@
wait_for_completion(&vctrl->ov_comp);
}
-static ssize_t vsync_show_event(struct device *dev,
+ssize_t mdp4_lcdc_show_event(struct device *dev,
struct device_attribute *attr, char *buf)
{
int cndx;
@@ -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);
@@ -414,7 +417,7 @@
init_completion(&vctrl->vsync_comp);
init_completion(&vctrl->dmap_comp);
init_completion(&vctrl->ov_comp);
- atomic_set(&vctrl->suspend, 0);
+ atomic_set(&vctrl->suspend, 1);
atomic_set(&vctrl->vsync_resume, 1);
spin_lock_init(&vctrl->spin_lock);
}
@@ -432,15 +435,6 @@
vctrl->base_pipe = pipe;
}
-static DEVICE_ATTR(vsync_event, S_IRUGO, vsync_show_event, NULL);
-static struct attribute *vsync_fs_attrs[] = {
- &dev_attr_vsync_event.attr,
- NULL,
-};
-static struct attribute_group vsync_fs_attr_group = {
- .attrs = vsync_fs_attrs,
-};
-
int mdp4_lcdc_on(struct platform_device *pdev)
{
int lcdc_width;
@@ -655,20 +649,6 @@
mdp_histogram_ctrl_all(TRUE);
- if (!vctrl->sysfs_created) {
- ret = sysfs_create_group(&vctrl->dev->kobj,
- &vsync_fs_attr_group);
- if (ret) {
- pr_err("%s: sysfs group creation failed, ret=%d\n",
- __func__, ret);
- return ret;
- }
-
- kobject_uevent(&vctrl->dev->kobj, KOBJ_ADD);
- pr_debug("%s: kobject_uevent(KOBJ_ADD)\n", __func__);
- vctrl->sysfs_created = 1;
- }
-
return ret;
}
diff --git a/drivers/video/msm/mdp4_overlay_writeback.c b/drivers/video/msm/mdp4_overlay_writeback.c
index 18c6635..6c2b1f6 100644
--- a/drivers/video/msm/mdp4_overlay_writeback.c
+++ b/drivers/video/msm/mdp4_overlay_writeback.c
@@ -171,6 +171,8 @@
struct vsycn_ctrl *vctrl;
struct mdp4_overlay_pipe *pipe;
int ret = 0;
+ int undx;
+ struct vsync_update *vp;
pr_debug("%s+:\n", __func__);
@@ -189,6 +191,16 @@
mdp4_overlay_pipe_free(pipe);
vctrl->base_pipe = NULL;
+ undx = vctrl->update_ndx;
+ vp = &vctrl->vlist[undx];
+ if (vp->update_cnt) {
+ /*
+ * pipe's iommu will be freed at next overlay play
+ * and iommu_drop statistic will be increased by one
+ */
+ vp->update_cnt = 0; /* empty queue */
+ }
+
ret = panel_next_off(pdev);
mdp_clk_ctrl(1);
@@ -253,6 +265,12 @@
mdp4_mixer_stage_up(pipe, 0);
mdp4_overlayproc_cfg(pipe);
+
+ if (hdmi_prim_display)
+ outpdw(MDP_BASE + 0x100F4, 0x01);
+ else
+ outpdw(MDP_BASE + 0x100F4, 0x02);
+
/* MDP cmd block disable */
mdp_clk_ctrl(0);
diff --git a/drivers/video/msm/mdp_dma_dsi_video.c b/drivers/video/msm/mdp_dma_dsi_video.c
index e2fb8ba..cfbff9a 100644
--- a/drivers/video/msm/mdp_dma_dsi_video.c
+++ b/drivers/video/msm/mdp_dma_dsi_video.c
@@ -34,7 +34,7 @@
static int first_pixel_start_x;
static int first_pixel_start_y;
-static ssize_t vsync_show_event(struct device *dev,
+ssize_t mdp_dma_video_show_event(struct device *dev,
struct device_attribute *attr, char *buf)
{
ssize_t ret = 0;
@@ -52,15 +52,6 @@
return ret;
}
-static DEVICE_ATTR(vsync_event, S_IRUGO, vsync_show_event, NULL);
-static struct attribute *vsync_fs_attrs[] = {
- &dev_attr_vsync_event.attr,
- NULL,
-};
-static struct attribute_group vsync_fs_attr_group = {
- .attrs = vsync_fs_attrs,
-};
-
int mdp_dsi_video_on(struct platform_device *pdev)
{
int dsi_width;
@@ -254,20 +245,6 @@
/* MDP cmd block disable */
mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
- if (!vsync_cntrl.sysfs_created) {
- ret = sysfs_create_group(&vsync_cntrl.dev->kobj,
- &vsync_fs_attr_group);
- if (ret) {
- pr_err("%s: sysfs creation failed, ret=%d\n",
- __func__, ret);
- return ret;
- }
-
- kobject_uevent(&vsync_cntrl.dev->kobj, KOBJ_ADD);
- pr_debug("%s: kobject_uevent(KOBJ_ADD)\n", __func__);
- vsync_cntrl.sysfs_created = 1;
- }
-
return ret;
}
diff --git a/drivers/video/msm/mdp_dma_lcdc.c b/drivers/video/msm/mdp_dma_lcdc.c
index fbfe35f..c51a99a 100644
--- a/drivers/video/msm/mdp_dma_lcdc.c
+++ b/drivers/video/msm/mdp_dma_lcdc.c
@@ -51,7 +51,7 @@
int first_pixel_start_x;
int first_pixel_start_y;
-static ssize_t vsync_show_event(struct device *dev,
+ssize_t mdp_dma_lcdc_show_event(struct device *dev,
struct device_attribute *attr, char *buf)
{
ssize_t ret = 0;
@@ -69,15 +69,6 @@
return ret;
}
-static DEVICE_ATTR(vsync_event, S_IRUGO, vsync_show_event, NULL);
-static struct attribute *vsync_fs_attrs[] = {
- &dev_attr_vsync_event.attr,
- NULL,
-};
-static struct attribute_group vsync_fs_attr_group = {
- .attrs = vsync_fs_attrs,
-};
-
int mdp_lcdc_on(struct platform_device *pdev)
{
int lcdc_width;
@@ -320,20 +311,6 @@
/* MDP cmd block disable */
mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
- if (!vsync_cntrl.sysfs_created) {
- ret = sysfs_create_group(&vsync_cntrl.dev->kobj,
- &vsync_fs_attr_group);
- if (ret) {
- pr_err("%s: sysfs creation failed, ret=%d\n",
- __func__, ret);
- return ret;
- }
-
- kobject_uevent(&vsync_cntrl.dev->kobj, KOBJ_ADD);
- pr_debug("%s: kobject_uevent(KOBJ_ADD)\n", __func__);
- vsync_cntrl.sysfs_created = 1;
- }
-
return ret;
}
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/msm_fb.h b/drivers/video/msm/msm_fb.h
index 7d8ad73..2fd25cc 100644
--- a/drivers/video/msm/msm_fb.h
+++ b/drivers/video/msm/msm_fb.h
@@ -125,6 +125,7 @@
__u32 channel_irq;
struct mdp_dma_data *dma;
+ struct device_attribute dev_attr;
void (*dma_fnc) (struct msm_fb_data_type *mfd);
int (*cursor_update) (struct fb_info *info,
struct fb_cursor *cursor);
@@ -135,6 +136,8 @@
int (*start_histogram) (struct mdp_histogram_start_req *req);
int (*stop_histogram) (struct fb_info *info, uint32_t block);
void (*vsync_ctrl) (int enable);
+ void (*vsync_init) (int cndx);
+ void *vsync_show;
void *cursor_buf;
void *cursor_buf_phys;
@@ -190,6 +193,7 @@
unsigned char *copy_splash_phys;
void *cpu_pm_hdl;
u32 avtimer_phy;
+ int vsync_sysfs_created;
};
struct dentry *msm_fb_get_debugfs_root(void);
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/mm.h b/include/linux/mm.h
index ddfb7c5..48268f0 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -1561,6 +1561,32 @@
#define in_gate_area(mm, addr) ({(void)mm; in_gate_area_no_mm(addr);})
#endif /* __HAVE_ARCH_GATE_AREA */
+#ifdef CONFIG_USE_USER_ACCESSIBLE_TIMERS
+static inline int use_user_accessible_timers(void) { return 1; }
+extern int in_user_timers_area(struct mm_struct *mm, unsigned long addr);
+extern struct vm_area_struct *get_user_timers_vma(struct mm_struct *mm);
+extern int get_user_timer_page(struct vm_area_struct *vma,
+ struct mm_struct *mm, unsigned long start, unsigned int gup_flags,
+ struct page **pages, int idx, int *goto_next_page);
+#else
+static inline int use_user_accessible_timers(void) { return 0; }
+static inline int in_user_timers_area(struct mm_struct *mm, unsigned long addr)
+{
+ return 0;
+}
+static inline struct vm_area_struct *get_user_timers_vma(struct mm_struct *mm)
+{
+ return NULL;
+}
+static inline int get_user_timer_page(struct vm_area_struct *vma,
+ struct mm_struct *mm, unsigned long start, unsigned int gup_flags,
+ struct page **pages, int idx, int *goto_next_page)
+{
+ *goto_next_page = 0;
+ return 0;
+}
+#endif
+
int drop_caches_sysctl_handler(struct ctl_table *, int,
void __user *, size_t *, loff_t *);
unsigned long shrink_slab(struct shrink_control *shrink,
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/qpnp/qpnp-adc.h b/include/linux/qpnp/qpnp-adc.h
index 9d5de90..077ccfc 100644
--- a/include/linux/qpnp/qpnp-adc.h
+++ b/include/linux/qpnp/qpnp-adc.h
@@ -28,7 +28,7 @@
DCIN,
VCHG_SNS,
SPARE1_03,
- SPARE2_03,
+ USB_ID_MV,
VCOIN,
VBAT_SNS,
VSYS,
@@ -81,44 +81,44 @@
LR_MUX7_HW_ID,
LR_MUX8_AMUX_THM4,
LR_MUX9_AMUX_THM5,
- LR_MUX10_USB_ID,
+ LR_MUX10_USB_ID_LV,
AMUX_PU1,
AMUX_PU2,
LR_MUX3_BUF_XO_THERM_BUF,
- LR_MUX1_PU1_BAT_THERM,
- LR_MUX2_PU1_BAT_ID,
- LR_MUX3_PU1_XO_THERM,
- LR_MUX4_PU1_AMUX_THM1,
- LR_MUX5_PU1_AMUX_THM2,
- LR_MUX6_PU1_AMUX_THM3,
- LR_MUX7_PU1_AMUX_HW_ID,
- LR_MUX8_PU1_AMUX_THM4,
- LR_MUX9_PU1_AMUX_THM5,
- LR_MUX10_PU1_AMUX_USB_ID,
- LR_MUX3_BUF_PU1_XO_THERM_BUF,
- LR_MUX1_PU2_BAT_THERM,
- LR_MUX2_PU2_BAT_ID,
- LR_MUX3_PU2_XO_THERM,
- LR_MUX4_PU2_AMUX_THM1,
- LR_MUX5_PU2_AMUX_THM2,
- LR_MUX6_PU2_AMUX_THM3,
- LR_MUX7_PU2_AMUX_HW_ID,
- LR_MUX8_PU2_AMUX_THM4,
- LR_MUX9_PU2_AMUX_THM5,
- LR_MUX10_PU2_AMUX_USB_ID,
- LR_MUX3_BUF_PU2_XO_THERM_BUF,
- LR_MUX1_PU1_PU2_BAT_THERM,
- LR_MUX2_PU1_PU2_BAT_ID,
- LR_MUX3_PU1_PU2_XO_THERM,
- LR_MUX4_PU1_PU2_AMUX_THM1,
- LR_MUX5_PU1_PU2_AMUX_THM2,
- LR_MUX6_PU1_PU2_AMUX_THM3,
- LR_MUX7_PU1_PU2_AMUX_HW_ID,
- LR_MUX8_PU1_PU2_AMUX_THM4,
- LR_MUX9_PU1_PU2_AMUX_THM5,
- LR_MUX10_PU1_PU2_AMUX_USB_ID,
- LR_MUX3_BUF_PU1_PU2_XO_THERM_BUF,
- ALL_OFF,
+ LR_MUX1_PU1_BAT_THERM = 112,
+ LR_MUX2_PU1_BAT_ID = 113,
+ LR_MUX3_PU1_XO_THERM = 114,
+ LR_MUX4_PU1_AMUX_THM1 = 115,
+ LR_MUX5_PU1_AMUX_THM2 = 116,
+ LR_MUX6_PU1_AMUX_THM3 = 117,
+ LR_MUX7_PU1_AMUX_HW_ID = 118,
+ LR_MUX8_PU1_AMUX_THM4 = 119,
+ LR_MUX9_PU1_AMUX_THM5 = 120,
+ LR_MUX10_PU1_AMUX_USB_ID_LV = 121,
+ LR_MUX3_BUF_PU1_XO_THERM_BUF = 124,
+ LR_MUX1_PU2_BAT_THERM = 176,
+ LR_MUX2_PU2_BAT_ID = 177,
+ LR_MUX3_PU2_XO_THERM = 178,
+ LR_MUX4_PU2_AMUX_THM1 = 179,
+ LR_MUX5_PU2_AMUX_THM2 = 180,
+ LR_MUX6_PU2_AMUX_THM3 = 181,
+ LR_MUX7_PU2_AMUX_HW_ID = 182,
+ LR_MUX8_PU2_AMUX_THM4 = 183,
+ LR_MUX9_PU2_AMUX_THM5 = 184,
+ LR_MUX10_PU2_AMUX_USB_ID_LV = 185,
+ LR_MUX3_BUF_PU2_XO_THERM_BUF = 188,
+ LR_MUX1_PU1_PU2_BAT_THERM = 240,
+ LR_MUX2_PU1_PU2_BAT_ID = 241,
+ LR_MUX3_PU1_PU2_XO_THERM = 242,
+ LR_MUX4_PU1_PU2_AMUX_THM1 = 243,
+ LR_MUX5_PU1_PU2_AMUX_THM2 = 244,
+ LR_MUX6_PU1_PU2_AMUX_THM3 = 245,
+ LR_MUX7_PU1_PU2_AMUX_HW_ID = 246,
+ LR_MUX8_PU1_PU2_AMUX_THM4 = 247,
+ LR_MUX9_PU1_PU2_AMUX_THM5 = 248,
+ LR_MUX10_PU1_PU2_AMUX_USB_ID_LV = 249,
+ LR_MUX3_BUF_PU1_PU2_XO_THERM_BUF = 252,
+ ALL_OFF = 255,
ADC_MAX_NUM,
};
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/mm/Kconfig b/mm/Kconfig
index 4cde97f..bbab5a6 100644
--- a/mm/Kconfig
+++ b/mm/Kconfig
@@ -390,3 +390,14 @@
the memory corresponding to the hole to be removed using memblock-
remove.
+config USE_USER_ACCESSIBLE_TIMERS
+ bool "Enables timers accessible from userspace"
+ depends on MMU
+ help
+ User-accessible timers allow the kernel to map kernel timer
+ registers to a userspace accessible page, to allow faster
+ access to time information. This flag will enable the
+ interface code in the main kernel. However, there are
+ architecture-specific code that will need to be enabled
+ separately.
+
diff --git a/mm/memory.c b/mm/memory.c
index 6105f47..174fcaa 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -1726,6 +1726,19 @@
goto next_page;
}
+ if (use_user_accessible_timers()) {
+ if (!vma && in_user_timers_area(mm, start)) {
+ int goto_next_page = 0;
+ int user_timer_ret = get_user_timer_page(vma,
+ mm, start, gup_flags, pages, i,
+ &goto_next_page);
+ if (goto_next_page)
+ goto next_page;
+ else
+ return user_timer_ret;
+ }
+ }
+
if (!vma ||
(vma->vm_flags & (VM_IO | VM_PFNMAP)) ||
!(vm_flags & vma->vm_flags))
diff --git a/mm/mlock.c b/mm/mlock.c
index ef726e8..38c77ab 100644
--- a/mm/mlock.c
+++ b/mm/mlock.c
@@ -229,7 +229,9 @@
if (!((vma->vm_flags & (VM_DONTEXPAND | VM_RESERVED)) ||
is_vm_hugetlb_page(vma) ||
- vma == get_gate_vma(current->mm))) {
+ vma == get_gate_vma(current->mm) ||
+ ((use_user_accessible_timers() &&
+ (vma == get_user_timers_vma(current->mm)))))) {
__mlock_vma_pages_range(vma, start, end, NULL);
@@ -324,7 +326,9 @@
int lock = !!(newflags & VM_LOCKED);
if (newflags == vma->vm_flags || (vma->vm_flags & VM_SPECIAL) ||
- is_vm_hugetlb_page(vma) || vma == get_gate_vma(current->mm))
+ is_vm_hugetlb_page(vma) || vma == get_gate_vma(current->mm) ||
+ ((use_user_accessible_timers()) &&
+ (vma == get_user_timers_vma(current->mm))))
goto out; /* don't set VM_LOCKED, don't count */
pgoff = vma->vm_pgoff + ((start - vma->vm_start) >> PAGE_SHIFT);
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;