Merge "msm: lpm_resources: Disable L2 PC at CPU_UP_PREPARE"
diff --git a/Documentation/devicetree/bindings/arm/msm/spm-v2.txt b/Documentation/devicetree/bindings/arm/msm/spm-v2.txt
index 1a19dbb..a2d8359 100644
--- a/Documentation/devicetree/bindings/arm/msm/spm-v2.txt
+++ b/Documentation/devicetree/bindings/arm/msm/spm-v2.txt
@@ -33,8 +33,10 @@
between AVS controller requests
- qcom,saw2-pmic-data0..7: Specify the pmic data value and the associated FTS
index to send the PMIC data to
-- qcom,saw2-vctl-port: The FTS port used for changing voltage
-- qcom,saw2-phase-port: The FTS port used for changing the number of phases
+- qcom,saw2-vctl-port: The PVC (PMIC Virtual Channel) port used for changing
+ voltage
+- qcom,saw2-phase-port: The PVC port used for changing the number of phases
+- qcom,saw2-pfm-port: The PVC port used for enabling PWM/PFM modes
- qcom,saw2-spm-cmd-wfi: The WFI command sequence
- qcom,saw2-spm-cmd-ret: The Retention command sequence
- qcom,saw2-spm-cmd-spc: The Standalone PC command sequence
diff --git a/Documentation/devicetree/bindings/fb/mdss-edp.txt b/Documentation/devicetree/bindings/fb/mdss-edp.txt
new file mode 100644
index 0000000..4fedc72
--- /dev/null
+++ b/Documentation/devicetree/bindings/fb/mdss-edp.txt
@@ -0,0 +1,28 @@
+Qualcomm MDSS EDP
+
+MDSS EDP is a edp driver which supports panels that are compatable with
+VESA EDP display interface specification.
+
+Required properties
+- compatible : Must be "qcom,mdss-edp".
+- reg : Offset and length of the register set for the device.
+- reg-names : Names to refer to register sets related to this device
+- vdda-supply : Phandle for vdd regulator device node.
+- gpio-panel-en : GPIO for supplying power to panel and to backlight driver.
+- status : A string that has to be set to "okay/ok" to enable
+ the driver. By default this property will be set to
+ "disable". Will be set to "ok/okay" status for specific
+ platforms.
+
+Example:
+ mdss_edp: qcom,mdss_edp@fd923400 {
+ compatible = "qcom,mdss-edp";
+ reg = <0xfd923400 0x700>,
+ <0xfd8c2000 0x1000>;
+ reg-names = "edp_base", "mmss_cc_base";
+ vdda-supply = <&pm8941_l12>;
+ gpio-panel-en = <&msmgpio 58 0>;
+ status = "disable";
+ };
+
+
diff --git a/Documentation/devicetree/bindings/iommu/msm_iommu.txt b/Documentation/devicetree/bindings/iommu/msm_iommu.txt
index dcf023d..7872280 100644
--- a/Documentation/devicetree/bindings/iommu/msm_iommu.txt
+++ b/Documentation/devicetree/bindings/iommu/msm_iommu.txt
@@ -5,6 +5,9 @@
- "qcom,msm-smmu-v2"
- reg : offset and length of the register set for the device.
+Optional properties:
+- qcom,iommu-secure-id : Secure identifier for the IOMMU block
+
- List of sub nodes, one for each of the translation context banks supported.
Each sub node has the following required properties:
diff --git a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
index 5df0101..9743d0d 100644
--- a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
+++ b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
@@ -54,6 +54,16 @@
- compatible : "qcom,msm-pcm-afe"
+* msm-dai-q6-hdmi
+
+Required properties:
+ - compatible : "msm-dai-q6-hdmi"
+ - qcom,msm-dai-q6-dev-id : The hdmi multi channel port ID.
+ It is passed onto the dsp from the apps to form an audio
+ path to the HDMI device. Currently the only supported value
+ is 8, which indicates the rx path used for audio playback
+ on HDMI device.
+
* msm-dai-q6
[First Level Nodes]
@@ -185,6 +195,11 @@
compatible = "qcom,msm-dai-fe";
};
+ qcom,msm-dai-q6-hdmi {
+ compatible = "qcom,msm-dai-q6-hdmi";
+ qcom,msm-dai-q6-dev-id = <8>;
+ };
+
qcom,msm-dai-q6 {
compatible = "qcom,msm-dai-q6";
qcom,msm-dai-q6-sb-0-rx {
diff --git a/Documentation/devicetree/bindings/thermal/qpnp-temp-alarm.txt b/Documentation/devicetree/bindings/thermal/qpnp-temp-alarm.txt
new file mode 100644
index 0000000..19fbd3a
--- /dev/null
+++ b/Documentation/devicetree/bindings/thermal/qpnp-temp-alarm.txt
@@ -0,0 +1,66 @@
+Qualcomm QPNP Temperature Alarm
+
+QPNP temperature alarm peripherals are found inside of Qualcomm PMIC chips that
+utilize the MSM SPMI implementation. These peripherals provide an interrupt
+signal and status register to identify high PMIC die temperature.
+
+Required properties:
+- compatible: Must be "qcom,qpnp-temp-alarm".
+- reg: Specifies the SPMI address and size for this temperature
+ alarm device.
+- interrupts: PMIC temperature alarm interrupt
+- label: A string used as a descriptive name for this thermal device.
+ This name should be 19 characters or less.
+
+Required structure:
+- A qcom,qpnp-temp-alarm node must be a child of an SPMI node that has specified
+ the spmi-slave-container property
+
+Optional properties:
+- qcom,channel-num: VADC channel number associated PMIC DIE_TEMP thermistor.
+ If no channel is specified, then the die temperature
+ must be estimated based on the over temperature stage.
+- qcom,threshold-set: Integer value which specifies which set of threshold
+ temperatures to use for the over temperature stages.
+ Possible values (x = {stage 1 threshold temperature,
+ stage 2 threshold temperature,
+ stage 3 threshold temperature}):
+ 0 = {105 C, 125 C, 145 C}
+ 1 = {110 C, 130 C, 150 C}
+ 2 = {115 C, 135 C, 155 C}
+ 3 = {120 C, 140 C, 160 C}
+- qcom,allow-override: Boolean which controls the ability of software to
+ override shutdowns. If present, then software is
+ allowed to override automatic PMIC hardware stage 2 and
+ stage 3 over temperature shutdowns. Otherwise, software
+ is not allowed to override automatic shutdown.
+- qcom,default-temp: Specifies the default temperature in millicelcius to use
+ if no ADC channel is present to read the real time
+ temperature.
+
+Note, if a given optional qcom,* binding is not present, then the default
+hardware state for that feature will be maintained.
+
+Example:
+&spmi_bus {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ interrupt-controller;
+ #interrupt-cells = <3>;
+
+ qcom,pm8941@0 {
+ spmi-slave-container;
+ reg = <0x0>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ qcom,temp-alarm@2400 {
+ compatible = "qcom,qpnp-temp-alarm";
+ reg = <0x2400 0x100>;
+ interrupts = <0x0 0x24 0x0>;
+ label = "pm8941_tz";
+ qcom,channel-num = <8>;
+ qcom,threshold-set = <0>;
+ };
+ };
+};
diff --git a/Documentation/devicetree/bindings/thermal/tsens.txt b/Documentation/devicetree/bindings/thermal/tsens.txt
index c683f58..0682cd1 100644
--- a/Documentation/devicetree/bindings/thermal/tsens.txt
+++ b/Documentation/devicetree/bindings/thermal/tsens.txt
@@ -17,10 +17,12 @@
- reg : offset and length of the QFPROM registers used for storing
the calibration data for the individual sensors.
- reg-names : resource names used for the physical address of the TSENS
- registers and the QFPROM efuse calibration address.
- Should be "tsens_physical" for physical address of the TSENS
- and "tsens_eeprom_physical" for physical address where calibration
- data is stored.
+ registers, the QFPROM efuse primary calibration address region,
+ Should be "tsens_physical" for physical address of the TSENS,
+ "tsens_eeprom_physical" for physical address where primary
+ calibration data is stored. This includes the backup
+ calibration address region if TSENS calibration data is stored
+ in the region.
- interrupts : TSENS interrupt for cool/warm temperature threshold.
- qcom,sensors : Total number of available Temperature sensors for TSENS.
- qcom,slope : One point calibration characterized slope data for each
@@ -28,14 +30,20 @@
as ADC code/DegC and the value is multipled by a factor
of 1000.
+Optional properties:
+- qcom,calibration-less-mode : If present the pre-characterized data for offsets
+ are used else it defaults to use calibration data from QFPROM.
+
Example:
tsens@fc4a8000 {
compatible = "qcom,msm-tsens";
reg = <0xfc4a8000 0x2000>,
- <0xfc4b80d0 0x5>;
- reg-names = "tsens_physical", "tsens_eeprom_physical";
+ <0xfc4b8000 0x1000>;
+ reg-names = "tsens_physical",
+ "tsens_eeprom_physical";
interrupts = <0 184 0>;
+ qcom,calibration-less-mode;
qcom,sensors = <11>;
qcom,slope = <1134 1122 1142 1123 1176 1176 1176 1186 1176
1176>;
diff --git a/arch/arm/boot/dts/mpq8092-ion.dtsi b/arch/arm/boot/dts/mpq8092-ion.dtsi
new file mode 100644
index 0000000..2cd2f7b
--- /dev/null
+++ b/arch/arm/boot/dts/mpq8092-ion.dtsi
@@ -0,0 +1,77 @@
+/* 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.
+ */
+
+/ {
+ qcom,ion {
+ compatible = "qcom,msm-ion";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ qcom,ion-heap@30 { /* SYSTEM HEAP */
+ reg = <30>;
+ };
+
+ qcom,ion-heap@8 { /* CP_MM HEAP */
+ compatible = "qcom,msm-ion-reserve";
+ reg = <8>;
+ qcom,heap-align = <0x1000>;
+ qcom,memory-reservation-type = "EBI1"; /* reserve EBI memory */
+ qcom,memory-reservation-size = <0x7800000>;
+ };
+
+ qcom,ion-heap@29 { /* FIRMWARE HEAP */
+ compatible = "qcom,msm-ion-reserve";
+ reg = <29>;
+ qcom,heap-align = <0x20000>;
+ qcom,heap-adjacent = <8>;
+ qcom,memory-reservation-type = "EBI1"; /* reserve EBI memory */
+ qcom,memory-reservation-size = <0xA00000>;
+ };
+
+ qcom,ion-heap@12 { /* MFC HEAP */
+ compatible = "qcom,msm-ion-reserve";
+ reg = <12>;
+ qcom,heap-align = <0x1000>;
+ qcom,memory-reservation-type = "EBI1"; /* reserve EBI memory */
+ qcom,memory-reservation-size = <0x2000>;
+ };
+
+ qcom,ion-heap@24 { /* SF HEAP */
+ compatible = "qcom,msm-ion-reserve";
+ reg = <24>;
+ qcom,heap-align = <0x1000>;
+ qcom,memory-reservation-type = "EBI1"; /* reserve EBI memory */
+ qcom,memory-reservation-size = <0x2800000>;
+ };
+
+ qcom,ion-heap@25 { /* IOMMU HEAP */
+ reg = <25>;
+ };
+
+ qcom,ion-heap@27 { /* QSECOM HEAP */
+ compatible = "qcom,msm-ion-reserve";
+ reg = <27>;
+ qcom,heap-align = <0x1000>;
+ qcom,memory-reservation-type = "EBI1"; /* reserve EBI memory */
+ qcom,memory-reservation-size = <0x600000>;
+ };
+
+ qcom,ion-heap@28 { /* AUDIO HEAP */
+ compatible = "qcom,msm-ion-reserve";
+ reg = <28>;
+ qcom,heap-align = <0x1000>;
+ qcom,memory-reservation-type = "EBI1"; /* reserve EBI memory */
+ qcom,memory-reservation-size = <0x2B4000>;
+ };
+ };
+};
+
diff --git a/arch/arm/boot/dts/mpq8092-regulator.dtsi b/arch/arm/boot/dts/mpq8092-regulator.dtsi
new file mode 100644
index 0000000..fbc9586
--- /dev/null
+++ b/arch/arm/boot/dts/mpq8092-regulator.dtsi
@@ -0,0 +1,290 @@
+/* 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.
+ */
+
+
+/* QPNP controlled regulators: */
+
+&spmi_bus {
+
+ qcom,pm8644@1 {
+
+ pm8644_s3: regulator@1a00 {
+ regulator-min-microvolt = <1350000>;
+ regulator-max-microvolt = <1350000>;
+ qcom,enable-time = <500>;
+ qcom,pull-down-enable = <1>;
+ regulator-always-on;
+ qcom,system-load = <100000>;
+ status = "okay";
+ };
+
+ pm8644_s4: regulator@1d00 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ qcom,enable-time = <500>;
+ qcom,pull-down-enable = <1>;
+ regulator-always-on;
+ qcom,system-load = <100000>;
+ status = "okay";
+ };
+
+ pm8644_s5: regulator@2000 {
+ regulator-min-microvolt = <2150000>;
+ regulator-max-microvolt = <2150000>;
+ qcom,enable-time = <500>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pm8644_s6: regulator@2300 {
+ regulator-min-microvolt = <900000>;
+ regulator-max-microvolt = <900000>;
+ qcom,enable-time = <500>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pm8644_s7: regulator@2600 {
+ regulator-min-microvolt = <900000>;
+ regulator-max-microvolt = <900000>;
+ qcom,enable-time = <500>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pm8644_s8: regulator@2900 {
+ regulator-min-microvolt = <900000>;
+ regulator-max-microvolt = <900000>;
+ qcom,enable-time = <500>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pm8644_l1: regulator@4000 {
+ parent-supply = <&pm8644_s3>;
+ regulator-min-microvolt = <1225000>;
+ regulator-max-microvolt = <1225000>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ regulator-always-on;
+ qcom,system-load = <10000>;
+ status = "okay";
+ };
+
+ pm8644_l2: regulator@4100 {
+ parent-supply = <&pm8644_s3>;
+ regulator-min-microvolt = <900000>;
+ regulator-max-microvolt = <900000>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pm8644_l3: regulator@4200 {
+ parent-supply = <&pm8644_s3>;
+ regulator-min-microvolt = <1200000>;
+ regulator-max-microvolt = <1200000>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pm8644_l4: regulator@4300 {
+ parent-supply = <&pm8644_s3>;
+ regulator-min-microvolt = <1000000>;
+ regulator-max-microvolt = <1000000>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pm8644_l6: regulator@4500 {
+ parent-supply = <&pm8644_s5>;
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pm8644_l8: regulator@4700 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pm8644_l9: regulator@4800 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <2950000>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pm8644_l10: regulator@4900 {
+ regulator-min-microvolt = <2000000>;
+ regulator-max-microvolt = <2000000>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pm8644_l11: regulator@4a00 {
+ parent-supply = <&pm8644_s3>;
+ regulator-min-microvolt = <1300000>;
+ regulator-max-microvolt = <1300000>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pm8644_l12: regulator@4b00 {
+ parent-supply = <&pm8644_s5>;
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pm8644_l13: regulator@4c00 {
+ regulator-min-microvolt = <2950000>;
+ regulator-max-microvolt = <2950000>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pm8644_l14: regulator@4d00 {
+ parent-supply = <&pm8644_s5>;
+ regulator-min-microvolt = <1000000>;
+ regulator-max-microvolt = <1000000>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pm8644_l15: regulator@4e00 {
+ parent-supply = <&pm8644_s5>;
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pm8644_l16: regulator@4f00 {
+ parent-supply = <&pm8644_s4>;
+ regulator-min-microvolt = <750000>;
+ regulator-max-microvolt = <750000>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pm8644_l17: regulator@5000 {
+ regulator-min-microvolt = <3150000>;
+ regulator-max-microvolt = <3150000>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ regulator-always-on;
+ qcom,system-load = <100000>;
+ status = "okay";
+ };
+
+ pm8644_l18: regulator@5100 {
+ regulator-min-microvolt = <2850000>;
+ regulator-max-microvolt = <2850000>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pm8644_l19: regulator@5200 {
+ parent-supply = <&pm8644_s4>;
+ regulator-min-microvolt = <1500000>;
+ regulator-max-microvolt = <1500000>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pm8644_l20: regulator@5300 {
+ regulator-min-microvolt = <2950000>;
+ regulator-max-microvolt = <2950000>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pm8644_l21: regulator@5400 {
+ regulator-min-microvolt = <2950000>;
+ regulator-max-microvolt = <2950000>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pm8644_l22: regulator@5500 {
+ regulator-min-microvolt = <2500000>;
+ regulator-max-microvolt = <2500000>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pm8644_l23: regulator@5600 {
+ regulator-min-microvolt = <3000000>;
+ regulator-max-microvolt = <3000000>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pm8644_l24: regulator@5700 {
+ regulator-min-microvolt = <3075000>;
+ regulator-max-microvolt = <3075000>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pm8644_lvs1: regulator@8000 {
+ parent-supply = <&pm8644_s4>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pm8644_lvs2: regulator@8100 {
+ parent-supply = <&pm8644_s4>;
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pm8644_mvs1: regulator@8200 {
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+
+ pm8644_mvs2: regulator@8300 {
+ qcom,enable-time = <200>;
+ qcom,pull-down-enable = <1>;
+ status = "okay";
+ };
+ };
+};
+
diff --git a/arch/arm/boot/dts/mpq8092-sim.dts b/arch/arm/boot/dts/mpq8092-sim.dts
index ac984a1..0cbfa33 100644
--- a/arch/arm/boot/dts/mpq8092-sim.dts
+++ b/arch/arm/boot/dts/mpq8092-sim.dts
@@ -26,6 +26,155 @@
serial@f995e000 {
status = "ok";
};
-
};
+&pm8644_gpios {
+ gpio@c000 { /* GPIO 1 */
+ };
+
+ gpio@c100 { /* GPIO 2 */
+ };
+
+ gpio@c200 { /* GPIO 3 */
+ };
+
+ gpio@c300 { /* GPIO 4 */
+ };
+
+ gpio@c400 { /* GPIO 5 */
+ };
+
+ gpio@c500 { /* GPIO 6 */
+ };
+
+ gpio@c600 { /* GPIO 7 */
+ };
+
+ gpio@c700 { /* GPIO 8 */
+ };
+
+ gpio@c800 { /* GPIO 9 */
+ };
+
+ gpio@c900 { /* GPIO 10 */
+ };
+
+ gpio@ca00 { /* GPIO 11 */
+ };
+
+ gpio@cb00 { /* GPIO 12 */
+ };
+
+ gpio@cc00 { /* GPIO 13 */
+ };
+
+ gpio@cd00 { /* GPIO 14 */
+ };
+
+ gpio@ce00 { /* GPIO 15 */
+ };
+
+ gpio@cf00 { /* GPIO 16 */
+ };
+
+ gpio@d000 { /* GPIO 17 */
+ };
+
+ gpio@d100 { /* GPIO 18 */
+ };
+
+ gpio@d200 { /* GPIO 19 */
+ };
+
+ gpio@d300 { /* GPIO 20 */
+ };
+
+ gpio@d400 { /* GPIO 21 */
+ };
+
+ gpio@d500 { /* GPIO 22 */
+ };
+
+ gpio@d600 { /* GPIO 23 */
+ };
+
+ gpio@d700 { /* GPIO 24 */
+ };
+
+ gpio@d800 { /* GPIO 25 */
+ };
+
+ gpio@d900 { /* GPIO 26 */
+ };
+
+ gpio@da00 { /* GPIO 27 */
+ };
+
+ gpio@db00 { /* GPIO 28 */
+ };
+
+ gpio@dc00 { /* GPIO 29 */
+ };
+
+ gpio@dd00 { /* GPIO 30 */
+ };
+
+ gpio@de00 { /* GPIO 31 */
+ };
+
+ gpio@df00 { /* GPIO 32 */
+ };
+
+ gpio@e000 { /* GPIO 33 */
+ };
+
+ gpio@e100 { /* GPIO 34 */
+ };
+
+ gpio@e200 { /* GPIO 35 */
+ };
+
+ gpio@e300 { /* GPIO 36 */
+ };
+
+ gpio@e400 { /* GPIO 37 */
+ };
+
+ gpio@e500 { /* GPIO 38 */
+ };
+
+ gpio@e600 { /* GPIO 39 */
+ };
+
+ gpio@e700 { /* GPIO 40 */
+ };
+
+ gpio@e800 { /* GPIO 41 */
+ };
+
+ gpio@e900 { /* GPIO 42 */
+ };
+
+ gpio@ea00 { /* GPIO 43 */
+ };
+};
+
+&pm8644_mpps {
+ mpp@a000 { /* MPP 1 */
+ };
+
+ mpp@a100 { /* MPP 2 */
+ };
+
+ mpp@a200 { /* MPP 3 */
+ };
+
+ mpp@a300 { /* MPP 4 */
+ };
+
+ mpp@a400 { /* MPP5 */
+ };
+
+ mpp@a500 { /* MPP 6 */
+ };
+};
diff --git a/arch/arm/boot/dts/mpq8092.dtsi b/arch/arm/boot/dts/mpq8092.dtsi
index 9b51ceb..252b9f5 100644
--- a/arch/arm/boot/dts/mpq8092.dtsi
+++ b/arch/arm/boot/dts/mpq8092.dtsi
@@ -13,6 +13,7 @@
/include/ "skeleton.dtsi"
/include/ "mpq8092-iommu.dtsi"
/include/ "msm-gdsc.dtsi"
+/include/ "mpq8092-ion.dtsi"
/ {
model = "Qualcomm MPQ8092";
@@ -55,4 +56,179 @@
interrupts = <0 114 0>;
status = "disabled";
};
+
+ spmi_bus: qcom,spmi@fc4c0000 {
+ cell-index = <0>;
+ compatible = "qcom,spmi-pmic-arb";
+ reg = <0xfc4cf000 0x1000>,
+ <0Xfc4cb000 0x1000>;
+ /* 190,ee0_krait_hlos_spmi_periph_irq */
+ /* 187,channel_0_krait_hlos_trans_done_irq */
+ interrupts = <0 190 0 0 187 0>;
+ qcom,pmic-arb-ee = <0>;
+ qcom,pmic-arb-channel = <0>;
+ qcom,pmic-arb-ppid-map = <0x00100000>, /* PM8644_0 */
+ <0x10100001>, /* PM8644_1 */
+ <0x00500002>, /* INTERRUPT */
+ <0x00800003>, /* PON0 */
+ <0x03000004>, /* ADC_1 */
+ <0x03100005>, /* ADC_2 */
+ <0x03200006>, /* ADC_3 */
+ <0x03300007>, /* ADC_4 */
+ <0x03400008>, /* ADC_5 */
+ <0x03500009>, /* ADC_6 */
+ <0x0360000a>, /* ADC_7 */
+ <0x0370000b>, /* ADC_8 */
+ <0x0500000c>, /* SHARED_XO */
+ <0x0510000d>, /* BB_CLK1 */
+ <0x0520000e>, /* BB_CLK2 */
+ <0x05a0000f>, /* SLEEP_CLK */
+ <0x06000010>, /* RTC_RW */
+ <0x06100011>, /* RTC_ALARM */
+ <0x07000012>, /* PBS_CORE */
+ <0x07100013>, /* PBS_CLIENT_1 */
+ <0x07200014>, /* PBS_CLIENT_2 */
+ <0x07300015>, /* PBS_CLIENT_3 */
+ <0x07400016>, /* PBS_CLIENT_4 */
+ <0x07500017>, /* PBS_CLIENT_5 */
+ <0x07600018>, /* PBS_CLIENT_6 */
+ <0x07700019>, /* PBS_CLIENT_7 */
+ <0x0780001a>, /* PBS_CLIENT_8 */
+ <0x0790001b>, /* PBS_CLIENT_9 */
+ <0x07a0001c>, /* PBS_CLIENT_10 */
+ <0x07b0001d>, /* PBS_CLIENT_11 */
+ <0x07c0001e>, /* PBS_CLIENT_12 */
+ <0x07d0001f>, /* PBS_CLIENT_13 */
+ <0x07e00020>, /* PBS_CLIENT_14 */
+ <0x07f00021>, /* PBS_CLIENT_15 */
+ <0x08000022>, /* PBS_CLIENT_16 */
+ <0x0a000023>, /* MPP_1 */
+ <0x0a100024>, /* MPP_2 */
+ <0x0a200025>, /* MPP_3 */
+ <0x0a300026>, /* MPP_4 */
+ <0x0a400027>, /* MPP_5 */
+ <0x0a500028>, /* MPP_6 */
+ <0x0c000029>, /* PM8644_GPIO_1 */
+ <0x0c10002a>, /* PM8644_GPIO_2 */
+ <0x0c20002b>, /* PM8644_GPIO_3 */
+ <0x0c30002c>, /* PM8644_GPIO_4 */
+ <0x0c40002d>, /* PM8644_GPIO_5 */
+ <0x0c50002e>, /* PM8644_GPIO_6 */
+ <0x0c60002f>, /* PM8644_GPIO_7 */
+ <0x0c700030>, /* PM8644_GPIO_8 */
+ <0x0c800031>, /* PM8644_GPIO_9 */
+ <0x0c900032>, /* PM8644_GPIO_10 */
+ <0x0ca00033>, /* PM8644_GPIO_11 */
+ <0x0cb00034>, /* PM8644_GPIO_12 */
+ <0x0cc00035>, /* PM8644_GPIO_13 */
+ <0x0cd00036>, /* PM8644_GPIO_14 */
+ <0x0ce00037>, /* PM8644_GPIO_15 */
+ <0x0cf00038>, /* PM8644_GPIO_16 */
+ <0x0d000039>, /* PM8644_GPIO_17 */
+ <0x0d10003a>, /* PM8644_GPIO_18 */
+ <0x0d20003b>, /* PM8644_GPIO_19 */
+ <0x0d30003c>, /* PM8644_GPIO_20 */
+ <0x0d40003d>, /* PM8644_GPIO_21 */
+ <0x0d50003e>, /* PM8644_GPIO_22 */
+ <0x0d60003f>, /* PM8644_GPIO_23 */
+ <0x0d700040>, /* PM8644_GPIO_24 */
+ <0x0d800041>, /* PM8644_GPIO_25 */
+ <0x0d900042>, /* PM8644_GPIO_26 */
+ <0x0da00043>, /* PM8644_GPIO_27 */
+ <0x0db00044>, /* PM8644_GPIO_28 */
+ <0x0dc00045>, /* PM8644_GPIO_29 */
+ <0x0dd00046>, /* PM8644_GPIO_30 */
+ <0x0de00047>, /* PM8644_GPIO_31 */
+ <0x0df00048>, /* PM8644_GPIO_32 */
+ <0x0e000049>, /* PM8644_GPIO_33 */
+ <0x0e10004a>, /* PM8644_GPIO_34 */
+ <0x0e20004b>, /* PM8644_GPIO_35 */
+ <0x0e30004c>, /* PM8644_GPIO_36 */
+ <0x0e40004d>, /* PM8644_GPIO_37 */
+ <0x0e50004e>, /* PM8644_GPIO_38 */
+ <0x0e60004f>, /* PM8644_GPIO_39 */
+ <0x0e700050>, /* PM8644_GPIO_40 */
+ <0x0e800051>, /* PM8644_GPIO_41 */
+ <0x0e900052>, /* PM8644_GPIO_42 */
+ <0x0ea00053>, /* PM8644_GPIO_43 */
+ <0x11000054>, /* BUCK_CMN_1 */
+ <0x11100055>, /* BUCK_CMN_2 */
+ <0x11200056>, /* BUCK_CMN_3 */
+ <0x11400057>, /* PM8644_SMPS1 */
+ <0x11500058>, /* SMPS_1_PS1 */
+ <0x11600059>, /* BUCK_FREQ_1 */
+ <0x1170005a>, /* PM8644_SMPS2 */
+ <0x1180005b>, /* SMPS_2_PS1 */
+ <0x1190005c>, /* BUCK_FREQ_2 */
+ <0x11a0005d>, /* PM8644_SMPS3 */
+ <0x11b0005e>, /* SMPS_3_PS1 */
+ <0x11c0005f>, /* BUCK_FREQ_3 */
+ <0x11d00060>, /* PM8644_SMPS4 */
+ <0x11e00061>, /* SMPS_4_PS1 */
+ <0x11f00062>, /* PM8644_BUCK_FREQ_4 */
+ <0x12000063>, /* PM8644_SMPS5 */
+ <0x12100064>, /* FTPS1_5 */
+ <0x12200065>, /* PM8644_BUCK_FREQ_5 */
+ <0x12300066>, /* PM8644_SMPS6 */
+ <0x12400067>, /* FTPS1_6 */
+ <0x12500068>, /* PM8644_BUCK_FREQ_6 */
+ <0x12600069>, /* PM8644_SMPS7 */
+ <0x1270006a>, /* FTPS1_7 */
+ <0x1280006b>, /* PM8644_BUCK_FREQ_7 */
+ <0x1290006c>, /* PM8644_SMPS8 */
+ <0x12a0006d>, /* FTPS1_8 */
+ <0x12b0006e>, /* PM8644_BUCK_FREQ_8 */
+ <0x12c0006f>, /* PM8644_SMPS9 */
+ <0x12d00070>, /* FTPS1_9 */
+ <0x12e00071>, /* PM8644_BUCK_FREQ_9 */
+ <0x12f00072>, /* PM8644_SMPS10 */
+ <0x13000073>, /* FTPS1_10 */
+ <0x13100074>, /* PM8644_BUCK_FREQ_10 */
+ <0x13200075>, /* PM8644_SMPS11 */
+ <0x13300076>, /* FTPS1_11 */
+ <0x13400077>, /* BUCK_FREQ_11 */
+ <0x14000078>, /* PM8644_LDO_1 */
+ <0x14100079>, /* PM8644_LDO_2 */
+ <0x1420007a>, /* PM8644_LDO_3 */
+ <0x1430007b>, /* PM8644_LDO_4 */
+ <0x1440007c>, /* PM8644_LDO_5 */
+ <0x1450007d>, /* PM8644_LDO_6 */
+ <0x1460007e>, /* PM8644_LDO_7 */
+ <0x1470007f>, /* PM8644_LDO_8 */
+ <0x14800080>, /* PM8644_LDO_9 */
+ <0x14900081>, /* PM8644_LDO_10 */
+ <0x14a00082>, /* PM8644_LDO_11 */
+ <0x14b00083>, /* PM8644_LDO_12 */
+ <0x14c00084>, /* PM8644_LDO_13 */
+ <0x14d00085>, /* PM8644_LDO_14 */
+ <0x14e00086>, /* PM8644_LDO_15 */
+ <0x14f00087>, /* PM8644_LDO_16 */
+ <0x15000088>, /* PM8644_LDO_17 */
+ <0x15100089>, /* PM8644_LDO_18 */
+ <0x1520008a>, /* PM8644_LDO_19 */
+ <0x1530008b>, /* PM8644_LDO_20 */
+ <0x1540008c>, /* PM8644_LDO_21 */
+ <0x1550008d>, /* PM8644_LDO_22 */
+ <0x1560008e>, /* PM8644_LDO_23 */
+ <0x1570008f>, /* PM8644_LDO_24 */
+ <0x15800090>, /* PM8644_LDO_25 */
+ <0x18000091>, /* PM8644_LVS_1 */
+ <0x18100092>, /* PM8644_LVS_2 */
+ <0x18200093>, /* PM8644_OTG */
+ <0x18300094>, /* PM8644_HDMI */
+ <0x1a800095>, /* KEYPAD */
+ <0x1b000096>, /* LPG_LUT */
+ <0x1b100097>, /* LPG_CHAN_1 */
+ <0x1b200098>, /* LPG_CHAN_2 */
+ <0x1b300099>, /* LPG_CHAN_3 */
+ <0x1b40009a>, /* LPG_CHAN_4 */
+ <0x1b50009b>, /* LPG_CHAN_5 */
+ <0x1b60009c>, /* LPG_CHAN_6 */
+ <0x1b70009d>, /* LPG_CHAN_7 */
+ <0x1b80009e>, /* LPG_CHAN_8 */
+ <0x1bc0009f>; /* LPG_PWM */
+ };
};
+
+/include/ "msm-pm8644.dtsi"
+/include/ "mpq8092-regulator.dtsi"
diff --git a/arch/arm/boot/dts/msm-iommu.dtsi b/arch/arm/boot/dts/msm-iommu.dtsi
index 709f40a..839199a 100755
--- a/arch/arm/boot/dts/msm-iommu.dtsi
+++ b/arch/arm/boot/dts/msm-iommu.dtsi
@@ -20,6 +20,38 @@
vdd-supply = <&gdsc_jpeg>;
status = "disabled";
+ qcom,iommu-bfb-regs = <0x204c
+ 0x2050
+ 0x2514
+ 0x2540
+ 0x256c
+ 0x2314
+ 0x2394
+ 0x2414
+ 0x20ac
+ 0x215c
+ 0x220c
+ 0x2008
+ 0x200c
+ 0x2010
+ 0x2014>;
+
+ qcom,iommu-bfb-data = <0xffffffff
+ 0xffffffff
+ 0x4
+ 0x4
+ 0x0
+ 0x0
+ 0x10
+ 0x50
+ 0x0
+ 0x10
+ 0x20
+ 0x0
+ 0x0
+ 0x0
+ 0x0>;
+
qcom,iommu-ctx@fda6c000 {
reg = <0xfda6c000 0x1000>;
interrupts = <0 70 0>;
@@ -49,8 +81,47 @@
ranges;
reg = <0xfd928000 0x10000>;
vdd-supply = <&gdsc_mdss>;
+ qcom,iommu-secure-id = <1>;
status = "disabled";
+ qcom,iommu-bfb-regs = <0x204c
+ 0x2050
+ 0x2514
+ 0x2540
+ 0x256c
+ 0x20ac
+ 0x215c
+ 0x220c
+ 0x2314
+ 0x2394
+ 0x2414
+ 0x2008
+ 0x200c
+ 0x2010
+ 0x2014
+ 0x2018
+ 0x201c
+ 0x2020>;
+
+ qcom,iommu-bfb-data = <0xffffffff
+ 0xffffffff
+ 0x00000004
+ 0x00000010
+ 0x00000000
+ 0x00000000
+ 0x00000034
+ 0x00000044
+ 0x0
+ 0x34
+ 0x74
+ 0x0
+ 0x0
+ 0x0
+ 0x0
+ 0x0
+ 0x0
+ 0x0>;
+
qcom,iommu-ctx@fd930000 {
reg = <0xfd930000 0x1000>;
interrupts = <0 47 0>;
@@ -73,9 +144,60 @@
ranges;
reg = <0xfdc84000 0x10000>;
vdd-supply = <&gdsc_venus>;
+ qcom,iommu-secure-id = <0>;
qcom,needs-alt-core-clk;
status = "disabled";
+ qcom,iommu-bfb-regs = <0x204c
+ 0x2050
+ 0x2514
+ 0x2540
+ 0x256c
+ 0x20ac
+ 0x215c
+ 0x220c
+ 0x2314
+ 0x2394
+ 0x2414
+ 0x2008
+ 0x200c
+ 0x2010
+ 0x2014
+ 0x2018
+ 0x201c
+ 0x2020
+ 0x2024
+ 0x2028
+ 0x202c
+ 0x2030
+ 0x2034
+ 0x2038>;
+
+ qcom,iommu-bfb-data = <0xffffffff
+ 0xffffffff
+ 0x00000004
+ 0x00000008
+ 0x00000000
+ 0x00000000
+ 0x00000094
+ 0x000000b4
+ 0x0
+ 0x94
+ 0x114
+ 0x0
+ 0x0
+ 0x0
+ 0x0
+ 0x0
+ 0x0
+ 0x0
+ 0x0
+ 0x0
+ 0x0
+ 0x0
+ 0x0
+ 0x0>;
+
qcom,iommu-ctx@fdc8c000 {
reg = <0xfdc8c000 0x1000>;
interrupts = <0 42 0>;
@@ -158,6 +280,44 @@
vdd-supply = <&gdsc_vfe>;
status = "disabled";
+ qcom,iommu-bfb-regs = <0x204c
+ 0x2050
+ 0x2514
+ 0x2540
+ 0x256c
+ 0x2314
+ 0x2394
+ 0x2414
+ 0x20ac
+ 0x215c
+ 0x220c
+ 0x2008
+ 0x200c
+ 0x2010
+ 0x2014
+ 0x2018
+ 0x201c
+ 0x2020>;
+
+ qcom,iommu-bfb-data = <0xffffffff
+ 0xffffffff
+ 0x4
+ 0x8
+ 0x0
+ 0x0
+ 0x20
+ 0x78
+ 0x0
+ 0x20
+ 0x36
+ 0x0
+ 0x0
+ 0x0
+ 0x0
+ 0x0
+ 0x0
+ 0x0>;
+
qcom,iommu-ctx@fda4c000 {
reg = <0xfda4c000 0x1000>;
interrupts = <0 65 0>;
diff --git a/arch/arm/boot/dts/msm-pm8644.dtsi b/arch/arm/boot/dts/msm-pm8644.dtsi
new file mode 100644
index 0000000..17a6b0b
--- /dev/null
+++ b/arch/arm/boot/dts/msm-pm8644.dtsi
@@ -0,0 +1,722 @@
+/* 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.
+ */
+
+&spmi_bus {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ interrupt-controller;
+ #interrupt-cells = <3>;
+
+ qcom,pm8644@0 {
+ spmi-slave-container;
+ reg = <0x0>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ pm8644_gpios: gpios {
+ spmi-dev-container;
+ compatible = "qcom,qpnp-pin";
+ gpio-controller;
+ #gpio-cells = <2>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ label = "pm8644-gpio";
+
+ gpio@c000 {
+ reg = <0xc000 0x100>;
+ qcom,pin-num = <1>;
+ };
+
+ gpio@c100 {
+ reg = <0xc100 0x100>;
+ qcom,pin-num = <2>;
+ };
+
+ gpio@c200 {
+ reg = <0xc200 0x100>;
+ qcom,pin-num = <3>;
+ };
+
+ gpio@c300 {
+ reg = <0xc300 0x100>;
+ qcom,pin-num = <4>;
+ };
+
+ gpio@c400 {
+ reg = <0xc400 0x100>;
+ qcom,pin-num = <5>;
+ };
+
+ gpio@c500 {
+ reg = <0xc500 0x100>;
+ qcom,pin-num = <6>;
+ };
+
+ gpio@c600 {
+ reg = <0xc600 0x100>;
+ qcom,pin-num = <7>;
+ };
+
+ gpio@c700 {
+ reg = <0xc700 0x100>;
+ qcom,pin-num = <8>;
+ };
+
+ gpio@c800 {
+ reg = <0xc800 0x100>;
+ qcom,pin-num = <9>;
+ };
+
+ gpio@c900 {
+ reg = <0xc900 0x100>;
+ qcom,pin-num = <10>;
+ };
+
+ gpio@ca00 {
+ reg = <0xca00 0x100>;
+ qcom,pin-num = <11>;
+ };
+
+ gpio@cb00 {
+ reg = <0xcb00 0x100>;
+ qcom,pin-num = <12>;
+ };
+
+ gpio@cc00 {
+ reg = <0xcc00 0x100>;
+ qcom,pin-num = <13>;
+ };
+
+ gpio@cd00 {
+ reg = <0xcd00 0x100>;
+ qcom,pin-num = <14>;
+ };
+
+ gpio@ce00 {
+ reg = <0xce00 0x100>;
+ qcom,pin-num = <15>;
+ };
+
+ gpio@cf00 {
+ reg = <0xcf00 0x100>;
+ qcom,pin-num = <16>;
+ };
+
+ gpio@d000 {
+ reg = <0xd000 0x100>;
+ qcom,pin-num = <17>;
+ };
+
+ gpio@d100 {
+ reg = <0xd100 0x100>;
+ qcom,pin-num = <18>;
+ };
+
+ gpio@d200 {
+ reg = <0xd200 0x100>;
+ qcom,pin-num = <19>;
+ };
+
+ gpio@d300 {
+ reg = <0xd300 0x100>;
+ qcom,pin-num = <20>;
+ };
+
+ gpio@d400 {
+ reg = <0xd400 0x100>;
+ qcom,pin-num = <21>;
+ };
+
+ gpio@d500 {
+ reg = <0xd500 0x100>;
+ qcom,pin-num = <22>;
+ };
+
+ gpio@d600 {
+ reg = <0xd600 0x100>;
+ qcom,pin-num = <23>;
+ };
+
+ gpio@d700 {
+ reg = <0xd700 0x100>;
+ qcom,pin-num = <24>;
+ };
+
+ gpio@d800 {
+ reg = <0xd800 0x100>;
+ qcom,pin-num = <25>;
+ };
+
+ gpio@d900 {
+ reg = <0xd900 0x100>;
+ qcom,pin-num = <26>;
+ };
+
+ gpio@da00 {
+ reg = <0xda00 0x100>;
+ qcom,pin-num = <27>;
+ };
+
+ gpio@db00 {
+ reg = <0xdb00 0x100>;
+ qcom,pin-num = <28>;
+ };
+
+ gpio@dc00 {
+ reg = <0xdc00 0x100>;
+ qcom,pin-num = <29>;
+ };
+
+ gpio@dd00 {
+ reg = <0xdd00 0x100>;
+ qcom,pin-num = <30>;
+ };
+
+ gpio@de00 {
+ reg = <0xde00 0x100>;
+ qcom,pin-num = <31>;
+ };
+
+ gpio@df00 {
+ reg = <0xdf00 0x100>;
+ qcom,pin-num = <32>;
+ };
+
+ gpio@e000 {
+ reg = <0xe000 0x100>;
+ qcom,pin-num = <33>;
+ };
+
+ gpio@e100 {
+ reg = <0xe100 0x100>;
+ qcom,pin-num = <34>;
+ };
+
+ gpio@e200 {
+ reg = <0xe200 0x100>;
+ qcom,pin-num = <35>;
+ };
+
+ gpio@e300 {
+ reg = <0xe300 0x100>;
+ qcom,pin-num = <36>;
+ };
+
+ gpio@e400 {
+ reg = <0xe400 0x100>;
+ qcom,pin-num = <37>;
+ };
+
+ gpio@e500 {
+ reg = <0xe500 0x100>;
+ qcom,pin-num = <38>;
+ };
+
+ gpio@e600 {
+ reg = <0xe600 0x100>;
+ qcom,pin-num = <39>;
+ };
+
+ gpio@e700 {
+ reg = <0xe700 0x100>;
+ qcom,pin-num = <40>;
+ };
+
+ gpio@e800 {
+ reg = <0xe800 0x100>;
+ qcom,pin-num = <41>;
+ };
+
+ gpio@e900 {
+ reg = <0xe900 0x100>;
+ qcom,pin-num = <42>;
+ };
+
+ gpio@ea00 {
+ reg = <0xea00 0x100>;
+ qcom,pin-num = <43>;
+ };
+ };
+
+ pm8644_mpps: mpps {
+ spmi-dev-container;
+ compatible = "qcom,qpnp-pin";
+ gpio-controller;
+ #gpio-cells = <2>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ label = "pm8644-mpp";
+
+ mpp@a000 {
+ reg = <0xa000 0x100>;
+ qcom,pin-num = <1>;
+ };
+
+ mpp@a100 {
+ reg = <0xa100 0x100>;
+ qcom,pin-num = <2>;
+ };
+
+ mpp@a200 {
+ reg = <0xa200 0x100>;
+ qcom,pin-num = <3>;
+ };
+
+ mpp@a300 {
+ reg = <0xa300 0x100>;
+ qcom,pin-num = <4>;
+ };
+
+ mpp@a400 {
+ reg = <0xa400 0x100>;
+ qcom,pin-num = <5>;
+ };
+
+ mpp@a500 {
+ reg = <0xa500 0x100>;
+ qcom,pin-num = <6>;
+ };
+ };
+ };
+
+ qcom,pm8644@1 {
+ spmi-slave-container;
+ reg = <0x1>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ regulator@1400 {
+ regulator-name = "8644_s1";
+ spmi-dev-container;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "qcom,qpnp-regulator";
+ reg = <0x1400 0x300>;
+ status = "disabled";
+
+ qcom,ctl@1400 {
+ reg = <0x1400 0x100>;
+ };
+ qcom,ps@1500 {
+ reg = <0x1500 0x100>;
+ };
+ qcom,freq@1600 {
+ reg = <0x1600 0x100>;
+ };
+ };
+
+ regulator@1700 {
+ regulator-name = "8644_s2";
+ spmi-dev-container;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "qcom,qpnp-regulator";
+ reg = <0x1700 0x300>;
+ status = "disabled";
+
+ qcom,ctl@1700 {
+ reg = <0x1700 0x100>;
+ };
+ qcom,ps@1800 {
+ reg = <0x1800 0x100>;
+ };
+ qcom,freq@1900 {
+ reg = <0x1900 0x100>;
+ };
+ };
+
+ regulator@1a00 {
+ regulator-name = "8644_s3";
+ spmi-dev-container;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "qcom,qpnp-regulator";
+ reg = <0x1a00 0x300>;
+ status = "disabled";
+
+ qcom,ctl@1a00 {
+ reg = <0x1a00 0x100>;
+ };
+ qcom,ps@1b00 {
+ reg = <0x1b00 0x100>;
+ };
+ qcom,freq@1c00 {
+ reg = <0x1c00 0x100>;
+ };
+ };
+
+ regulator@1d00 {
+ regulator-name = "8644_s4";
+ spmi-dev-container;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "qcom,qpnp-regulator";
+ reg = <0x1d00 0x300>;
+ status = "disabled";
+
+ qcom,ctl@1d00 {
+ reg = <0x1d00 0x100>;
+ };
+ qcom,ps@1e00 {
+ reg = <0x1e00 0x100>;
+ };
+ qcom,freq@1f00 {
+ reg = <0x1f00 0x100>;
+ };
+ };
+
+ regulator@2000 {
+ regulator-name = "8644_s5";
+ spmi-dev-container;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "qcom,qpnp-regulator";
+ reg = <0x2000 0x300>;
+ status = "disabled";
+
+ qcom,ctl@2000 {
+ reg = <0x2000 0x100>;
+ };
+ qcom,ps@2100 {
+ reg = <0x2100 0x100>;
+ };
+ qcom,freq@2200 {
+ reg = <0x2200 0x100>;
+ };
+ };
+
+ regulator@2300 {
+ regulator-name = "8644_s6";
+ spmi-dev-container;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "qcom,qpnp-regulator";
+ reg = <0x2300 0x300>;
+ status = "disabled";
+
+ qcom,ctl@2300 {
+ reg = <0x2300 0x100>;
+ };
+ qcom,ps@2400 {
+ reg = <0x2400 0x100>;
+ };
+ qcom,freq@2500 {
+ reg = <0x2500 0x100>;
+ };
+ };
+
+ regulator@2600 {
+ regulator-name = "8644_s7";
+ spmi-dev-container;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "qcom,qpnp-regulator";
+ reg = <0x2600 0x300>;
+ status = "disabled";
+
+ qcom,ctl@2600 {
+ reg = <0x2600 0x100>;
+ };
+ qcom,ps@2700 {
+ reg = <0x2700 0x100>;
+ };
+ qcom,freq@2800 {
+ reg = <0x2800 0x100>;
+ };
+ };
+
+ regulator@2900 {
+ regulator-name = "8644_s8";
+ spmi-dev-container;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "qcom,qpnp-regulator";
+ reg = <0x2900 0x300>;
+ status = "disabled";
+
+ qcom,ctl@2900 {
+ reg = <0x2900 0x100>;
+ };
+ qcom,ps@2a00 {
+ reg = <0x2a00 0x100>;
+ };
+ qcom,freq@2b00 {
+ reg = <0x2b00 0x100>;
+ };
+ };
+
+ regulator@2c00 {
+ regulator-name = "8644_s9";
+ spmi-dev-container;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "qcom,qpnp-regulator";
+ reg = <0x2c00 0x300>;
+ status = "disabled";
+
+ qcom,ctl@2c00 {
+ reg = <0x2c00 0x100>;
+ };
+ qcom,ps@2d00 {
+ reg = <0x2d00 0x100>;
+ };
+ qcom,freq@2e00 {
+ reg = <0x2e00 0x100>;
+ };
+ };
+
+ regulator@2f00 {
+ regulator-name = "8644_s10";
+ spmi-dev-container;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "qcom,qpnp-regulator";
+ reg = <0x2f00 0x300>;
+ status = "disabled";
+
+ qcom,ctl@2f00 {
+ reg = <0x2f00 0x100>;
+ };
+ qcom,ps@3000 {
+ reg = <0x3000 0x100>;
+ };
+ qcom,freq@3100 {
+ reg = <0x3100 0x100>;
+ };
+ };
+
+ regulator@3200 {
+ regulator-name = "8644_s11";
+ spmi-dev-container;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "qcom,qpnp-regulator";
+ reg = <0x3200 0x300>;
+ status = "disabled";
+
+ qcom,ctl@3200 {
+ reg = <0x3200 0x100>;
+ };
+ qcom,ps@3300 {
+ reg = <0x3300 0x100>;
+ };
+ qcom,freq@3400 {
+ reg = <0x3400 0x100>;
+ };
+ };
+
+ regulator@4000 {
+ regulator-name = "8644_l1";
+ reg = <0x4000 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+
+ regulator@4100 {
+ regulator-name = "8644_l2";
+ reg = <0x4100 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+
+ regulator@4200 {
+ regulator-name = "8644_l3";
+ reg = <0x4200 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+
+ regulator@4300 {
+ regulator-name = "8644_l4";
+ reg = <0x4300 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+
+ regulator@4400 {
+ regulator-name = "8644_l5";
+ reg = <0x4400 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ qcom,force-type = <0x04 0x10>;
+ status = "disabled";
+ };
+
+ regulator@4500 {
+ regulator-name = "8644_l6";
+ reg = <0x4500 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+
+ regulator@4600 {
+ regulator-name = "8644_l7";
+ reg = <0x4600 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ qcom,force-type = <0x04 0x10>;
+ status = "disabled";
+ };
+
+ regulator@4700 {
+ regulator-name = "8644_l8";
+ reg = <0x4700 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+
+ regulator@4800 {
+ regulator-name = "8644_l9";
+ reg = <0x4800 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+
+ regulator@4900 {
+ regulator-name = "8644_l10";
+ reg = <0x4900 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+
+ regulator@4a00 {
+ regulator-name = "8644_l11";
+ reg = <0x4a00 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+
+ regulator@4b00 {
+ regulator-name = "8644_l12";
+ reg = <0x4b00 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+
+ regulator@4c00 {
+ regulator-name = "8644_l13";
+ reg = <0x4c00 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+
+ regulator@4d00 {
+ regulator-name = "8644_l14";
+ reg = <0x4d00 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+
+ regulator@4e00 {
+ regulator-name = "8644_l15";
+ reg = <0x4e00 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+
+ regulator@4f00 {
+ regulator-name = "8644_l16";
+ reg = <0x4f00 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+
+ regulator@5000 {
+ regulator-name = "8644_l17";
+ reg = <0x5000 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+
+ regulator@5100 {
+ regulator-name = "8644_l18";
+ reg = <0x5100 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+
+ regulator@5200 {
+ regulator-name = "8644_l19";
+ reg = <0x5200 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+
+ regulator@5300 {
+ regulator-name = "8644_l20";
+ reg = <0x5300 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+
+ regulator@5400 {
+ regulator-name = "8644_l21";
+ reg = <0x5400 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+
+ regulator@5500 {
+ regulator-name = "8644_l22";
+ reg = <0x5500 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+
+ regulator@5600 {
+ regulator-name = "8644_l23";
+ reg = <0x5600 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+
+ regulator@5700 {
+ regulator-name = "8644_l24";
+ reg = <0x5700 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+
+ regulator@5800 {
+ regulator-name = "8644_l25";
+ reg = <0x5800 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+
+ regulator@8000 {
+ regulator-name = "8644_lvs1";
+ reg = <0x8000 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+
+ regulator@8100 {
+ regulator-name = "8644_lvs2";
+ reg = <0x8100 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+
+ regulator@8200 {
+ regulator-name = "8644_mvs1";
+ reg = <0x8200 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+
+ regulator@8300 {
+ regulator-name = "8644_mvs2";
+ reg = <0x8300 0x100>;
+ compatible = "qcom,qpnp-regulator";
+ status = "disabled";
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/msm-pm8841.dtsi b/arch/arm/boot/dts/msm-pm8841.dtsi
index ea83231..1e0e5dfa 100644
--- a/arch/arm/boot/dts/msm-pm8841.dtsi
+++ b/arch/arm/boot/dts/msm-pm8841.dtsi
@@ -22,6 +22,15 @@
#address-cells = <1>;
#size-cells = <1>;
+ qcom,temp-alarm@2400 {
+ compatible = "qcom,qpnp-temp-alarm";
+ reg = <0x2400 0x100>;
+ interrupts = <0x4 0x24 0x0>;
+ label = "pm8841_tz";
+ qcom,threshold-set = <0>;
+ qcom,default-temp = <37000>;
+ };
+
pm8841_mpps: mpps {
spmi-dev-container;
compatible = "qcom,qpnp-pin";
diff --git a/arch/arm/boot/dts/msm-pm8941.dtsi b/arch/arm/boot/dts/msm-pm8941.dtsi
index bb2417e..1d95407 100644
--- a/arch/arm/boot/dts/msm-pm8941.dtsi
+++ b/arch/arm/boot/dts/msm-pm8941.dtsi
@@ -22,6 +22,15 @@
#address-cells = <1>;
#size-cells = <1>;
+ qcom,temp-alarm@2400 {
+ compatible = "qcom,qpnp-temp-alarm";
+ reg = <0x2400 0x100>;
+ interrupts = <0x0 0x24 0x0>;
+ label = "pm8941_tz";
+ qcom,channel-num = <8>;
+ qcom,threshold-set = <0>;
+ };
+
qcom,power-on@800 {
compatible = "qcom,qpnp-power-on";
reg = <0x800 0x100>;
@@ -180,8 +189,8 @@
<0 0x13 0x1>,
<0x0 0x13 0x2>;
- interrupt-names = "usbin-valid",
- "coarse-det-usb",
+ interrupt-names = "coarse-det-usb",
+ "usbin-valid",
"chg-gone";
};
@@ -1044,5 +1053,68 @@
qcom,label = "wled";
};
+ pwm@b100 {
+ compatible = "qcom,qpnp-pwm";
+ reg = <0xb100 0x100>,
+ <0xb042 0x7e>;
+ reg-names = "qpnp-lpg-channel-base", "qpnp-lpg-lut-base";
+ qcom,channel-id = <0>;
+ };
+
+ pwm@b200 {
+ compatible = "qcom,qpnp-pwm";
+ reg = <0xb200 0x100>,
+ <0xb042 0x7e>;
+ reg-names = "qpnp-lpg-channel-base", "qpnp-lpg-lut-base";
+ qcom,channel-id = <1>;
+ };
+
+ pwm@b300 {
+ compatible = "qcom,qpnp-pwm";
+ reg = <0xb300 0x100>,
+ <0xb042 0x7e>;
+ reg-names = "qpnp-lpg-channel-base", "qpnp-lpg-lut-base";
+ qcom,channel-id = <2>;
+ };
+
+ pwm@b400 {
+ compatible = "qcom,qpnp-pwm";
+ reg = <0xb400 0x100>,
+ <0xb042 0x7e>;
+ reg-names = "qpnp-lpg-channel-base", "qpnp-lpg-lut-base";
+ qcom,channel-id = <3>;
+ };
+
+ pwm@b500 {
+ compatible = "qcom,qpnp-pwm";
+ reg = <0xb500 0x100>,
+ <0xb042 0x7e>;
+ reg-names = "qpnp-lpg-channel-base", "qpnp-lpg-lut-base";
+ qcom,channel-id = <4>;
+ };
+
+ pwm@b600 {
+ compatible = "qcom,qpnp-pwm";
+ reg = <0xb600 0x100>,
+ <0xb042 0x7e>;
+ reg-names = "qpnp-lpg-channel-base", "qpnp-lpg-lut-base";
+ qcom,channel-id = <5>;
+ };
+
+ pwm@b700 {
+ compatible = "qcom,qpnp-pwm";
+ reg = <0xb700 0x100>,
+ <0xb042 0x7e>;
+ reg-names = "qpnp-lpg-channel-base", "qpnp-lpg-lut-base";
+ qcom,channel-id = <6>;
+ };
+
+ pwm@b800 {
+ compatible = "qcom,qpnp-pwm";
+ reg = <0xb800 0x100>,
+ <0xb042 0x7e>;
+ reg-names = "qpnp-lpg-channel-base", "qpnp-lpg-lut-base";
+ qcom,channel-id = <7>;
+ };
};
};
diff --git a/arch/arm/boot/dts/msm8226-sim.dts b/arch/arm/boot/dts/msm8226-sim.dts
index aeda1d8..7c25680 100644
--- a/arch/arm/boot/dts/msm8226-sim.dts
+++ b/arch/arm/boot/dts/msm8226-sim.dts
@@ -11,7 +11,7 @@
*/
/dts-v1/;
-/include/ "skeleton.dtsi"
+/include/ "msm8226.dtsi"
/include/ "msm8226-ion.dtsi"
/include/ "msm8226-camera.dtsi"
@@ -19,60 +19,8 @@
model = "Qualcomm MSM 8226 Simulator";
compatible = "qcom,msm8226-sim", "qcom,msm8226";
qcom,msm-id = <145 1 0>;
- interrupt-parent = <&intc>;
-
- chosen {
- bootargs ="root=/dev/ram rw init=/init console=ttyHSL0,115200n8 initrd=0x00000000,0x00000000 mem=512M@0x00000000";
- };
-
- intc: interrupt-controller@f9000000 {
- compatible = "qcom,msm-qgic2";
- interrupt-controller;
- #interrupt-cells = <3>;
- reg = <0xF9000000 0x1000>,
- <0xF9002000 0x1000>;
- };
-
- msmgpio: gpio@fd510000 {
- compatible = "qcom,msm-gpio";
- interrupt-controller;
- #interrupt-cells = <2>;
- reg = <0xfd510000 0x4000>;
- #gpio-cells = <2>;
- };
-
- timer {
- compatible = "qcom,msm-qtimer", "arm,armv7-timer";
- interrupts = <1 2 0 1 3 0>;
- clock-frequency = <19200000>;
- };
serial@f991f000 {
- compatible = "qcom,msm-lsuart-v14";
- reg = <0xf991f000 0x1000>;
- interrupts = <0 109 0>;
+ status = "ok";
};
-
- serial@f995e000 {
- compatible = "qcom,msm-lsuart-v14";
- reg = <0xf995e000 0x1000>;
- interrupts = <0 114 0>;
- };
-
- usb@f9a55000 {
- compatible = "qcom,hsusb-otg";
- reg = <0xf9a55000 0x400>;
- interrupts = <0 134 0>;
- interrupt-names = "core_irq";
-
- qcom,hsusb-otg-phy-type = <2>;
- qcom,hsusb-otg-mode = <1>;
- qcom,hsusb-otg-otg-control = <1>;
- qcom,hsusb-otg-disable-reset;
- };
-
- android_usb {
- compatible = "qcom,android-usb";
- };
-
};
diff --git a/arch/arm/boot/dts/msm8226.dtsi b/arch/arm/boot/dts/msm8226.dtsi
new file mode 100644
index 0000000..6d2ffec
--- /dev/null
+++ b/arch/arm/boot/dts/msm8226.dtsi
@@ -0,0 +1,72 @@
+/* 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/ "skeleton.dtsi"
+
+/ {
+ model = "Qualcomm MSM 8226";
+ compatible = "qcom,msm8226";
+ interrupt-parent = <&intc>;
+
+ intc: interrupt-controller@f9000000 {
+ compatible = "qcom,msm-qgic2";
+ interrupt-controller;
+ #interrupt-cells = <3>;
+ reg = <0xF9000000 0x1000>,
+ <0xF9002000 0x1000>;
+ };
+
+ msmgpio: gpio@fd510000 {
+ compatible = "qcom,msm-gpio";
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ reg = <0xfd510000 0x4000>;
+ #gpio-cells = <2>;
+ };
+
+ timer {
+ compatible = "qcom,msm-qtimer", "arm,armv7-timer";
+ interrupts = <1 2 0 1 3 0>;
+ clock-frequency = <19200000>;
+ };
+
+ serial@f991f000 {
+ compatible = "qcom,msm-lsuart-v14";
+ reg = <0xf991f000 0x1000>;
+ interrupts = <0 109 0>;
+ status = "disabled";
+ };
+
+ serial@f995e000 {
+ compatible = "qcom,msm-lsuart-v14";
+ reg = <0xf995e000 0x1000>;
+ interrupts = <0 114 0>;
+ status = "disabled";
+ };
+
+ usb@f9a55000 {
+ compatible = "qcom,hsusb-otg";
+ reg = <0xf9a55000 0x400>;
+ interrupts = <0 134 0>;
+ interrupt-names = "core_irq";
+
+ qcom,hsusb-otg-phy-type = <2>;
+ qcom,hsusb-otg-mode = <1>;
+ qcom,hsusb-otg-otg-control = <1>;
+ qcom,hsusb-otg-disable-reset;
+ };
+
+ android_usb {
+ compatible = "qcom,android-usb";
+ };
+
+};
diff --git a/arch/arm/boot/dts/msm8910.dtsi b/arch/arm/boot/dts/msm8910.dtsi
index c8a0f9c..83dabfb 100644
--- a/arch/arm/boot/dts/msm8910.dtsi
+++ b/arch/arm/boot/dts/msm8910.dtsi
@@ -45,4 +45,21 @@
interrupts = <0 109 0>;
status = "disabled";
};
+
+ usb@f9a55000 {
+ compatible = "qcom,hsusb-otg";
+ reg = <0xf9a55000 0x400>;
+ interrupts = <0 134 0>;
+ interrupt-names = "core_irq";
+
+ qcom,hsusb-otg-phy-type = <2>;
+ qcom,hsusb-otg-mode = <1>;
+ qcom,hsusb-otg-otg-control = <1>;
+ qcom,hsusb-otg-disable-reset;
+ };
+
+ android_usb {
+ compatible = "qcom,android-usb";
+ };
+
};
diff --git a/arch/arm/boot/dts/msm8974-fluid.dts b/arch/arm/boot/dts/msm8974-fluid.dts
index 3a7c19d..891379f 100644
--- a/arch/arm/boot/dts/msm8974-fluid.dts
+++ b/arch/arm/boot/dts/msm8974-fluid.dts
@@ -116,6 +116,7 @@
gpio_keys {
compatible = "gpio-keys";
+ input-name = "gpio-keys";
camera_snapshot {
label = "camera_snapshot";
diff --git a/arch/arm/boot/dts/msm8974-gpu.dtsi b/arch/arm/boot/dts/msm8974-gpu.dtsi
index 6d00b01..4fe0eda 100644
--- a/arch/arm/boot/dts/msm8974-gpu.dtsi
+++ b/arch/arm/boot/dts/msm8974-gpu.dtsi
@@ -26,7 +26,7 @@
qcom,idle-timeout = <83>; //<HZ/12>
qcom,nap-allowed = <1>;
qcom,strtstp-sleepwake;
- qcom,clk-map = <0x00000016>; //KGSL_CLK_CORE | KGSL_CLK_IFACE | KGSL_CLK_MEM_IFACE
+ qcom,clk-map = <0x0000006>; //KGSL_CLK_CORE | KGSL_CLK_IFACE
/* Bus Scale Settings */
qcom,grp3d-vectors = <0 0 0 0>, <2 1 0 0>,
diff --git a/arch/arm/boot/dts/msm8974-liquid.dts b/arch/arm/boot/dts/msm8974-liquid.dts
index bc682ea..6ccd933 100644
--- a/arch/arm/boot/dts/msm8974-liquid.dts
+++ b/arch/arm/boot/dts/msm8974-liquid.dts
@@ -23,6 +23,10 @@
status = "ok";
};
+ qcom,mdss_edp@fd923400 {
+ status = "ok";
+ };
+
gpio_keys {
compatible = "gpio-keys";
input-name = "gpio-keys";
@@ -55,9 +59,108 @@
};
};
+ qcom,mdss_mdp@fd900000 {
+ qcom,memory-reservation-size = <0x1000000>; /* size 16MB */
+ };
+
qcom,hdmi_tx@fd922100 {
status = "ok";
};
+
+ i2c@f9924000 {
+ atmel_mxt_ts@4a {
+ compatible = "atmel,mxt-ts";
+ reg = <0x4a>;
+ interrupt-parent = <&msmgpio>;
+ interrupts = <61 0x2>;
+ vdd_ana-supply = <&pm8941_l22>;
+ vcc_i2c-supply = <&pm8941_s3>;
+ atmel,reset-gpio = <&msmgpio 60 0x00>;
+ atmel,irq-gpio = <&msmgpio 61 0x00>;
+ atmel,panel-coords = <0 0 1080 1920>;
+ atmel,display-coords = <0 0 1080 1920>;
+ atmel,i2c-pull-up = <1>;
+ atmel,cfg_1 {
+ atmel,family-id = <0xa2>;
+ atmel,variant-id = <0x00>;
+ atmel,version = <0x11>;
+ atmel,build = <0xaa>;
+ atmel,config = [
+ /* Object 6, Instance = 0 */
+ 00 00 00 00 00 00
+ /* Object 38, Instance = 0 */
+ 16 00 00 14 09 0C 00 00 00 00
+ 00 00 00 00 00 00 00 00 00 00
+ 00 00 00 00 00 00 00 00 00 00
+ 00 00 00 00 00 00 00 00 00 00
+ 00 00 00 00 00 00 00 00 00 00
+ 00 00 00 00 00 00 00 00 00 00
+ 00 00 00 00
+ /* Object 7, Instance = 0 */
+ FF FF 0A 03
+ /* Object 8, Instance = 0 */
+ 5F 00 14 14 00 00 00 01 00 00
+ /* Object 9, Instance = 0 */
+ 8F 00 00 20 34 00 87 3C 08 03
+ 00 05 03 80 0A 14 14 0A 80 07
+ 38 04 00 00 00 00 00 00 00 00
+ 0F 0F 2E 33 02 00
+ /* Object 15, Instance = 0 */
+ 00 00 00 00 00 00 00 00 00 00
+ 00
+ /* Object 18, Instance = 0 */
+ 04 00
+ /* Object 24, Instance = 0 */
+ 00 00 00 00 00 00 00 00 00 00
+ 00 00 00 00 00 00 00 00 00
+ /* Object 25, Instance = 0 */
+ 00 00 54 6F F0 55 00 00 00 00
+ 00 00 00 00 00
+ /* Object 27, Instance = 0 */
+ 00 00 00 00 00 00 00
+ /* Object 40, Instance = 0 */
+ 00 14 14 14 14
+ /* Object 42, Instance = 0 */
+ 20 14 00 00 00 14 11 00 03 00
+ /* Object 43, Instance = 0 */
+ 09 00 01 01 91 00 80 00 00 00
+ 00 00
+ /* Object 46, Instance = 0 */
+ 00 00 10 10 00 00 01 00 00 0F
+ 0A
+ /* Object 47, Instance = 0 */
+ 00 14 23 02 05 1E 01 78 03 10
+ 00 00 0C 00 00 00 00 00 00 00
+ 00 00
+ /* Object 55, Instance = 0 */
+ 00 00 00 00 00 00 00
+ /* Object 56, Instance = 0 */
+ 02 00 01 30 13 14 14 14 15 15
+ 15 15 15 15 15 16 16 16 16 16
+ 16 16 16 16 16 15 14 14 14 14
+ 15 14 14 14 14 13 00 00 01 02
+ 05 05 00 00 00 00 00 00 00 00
+ 00
+ /* Object 57, Instance = 0 */
+ 00 00 00
+ /* Object 61, Instance = 0 */
+ 00 00 00 00 00
+ /* Object 62, Instance = 0 */
+ 00 01 03 01 00 00 00 00 00 0A
+ 0F 14 19 23 05 00 0A 05 05 69
+ 23 23 34 11 64 06 06 04 40 00
+ 00 00 00 00 69 4B 02 00 00 80
+ 0A 14 14 18 18 10 10 80 00 80
+ 00 00 0F 02 00 00 00 00 00 00
+ 00 00 00 00 00 00 00 00 00 00
+ 00 00 00 00
+ /* Object 63, Instance = 0 */
+ 00 00 00 00 00 00 00 00 00 00
+ 00 00
+ ];
+ };
+ };
+ };
};
&pm8941_gpios {
@@ -246,9 +349,21 @@
mpp@a100 { /* MPP 2 */
};
- mpp@a200 { /* MPP 3 */
+ mpp@a200 { /* HDMI_MUX_SEL MPP 3*/
+ status = "ok";
+ qcom,mode = <1>; /* DIG_OUT */
+ qcom,output-type = <0>; /* CMOS */
+ qcom,vin-sel = <2>; /* PM8841_S3A 1.8V */
+ qcom,src-select = <0>; /* CONSTANT */
+ qcom,master-en = <1>; /* ENABLE MPP */
};
- mpp@a300 { /* MPP 4 */
+ mpp@a300 { /* HDMI_MUX_EN MPP 4*/
+ status = "ok";
+ qcom,mode = <1>; /* DIG_OUT */
+ qcom,output-type = <0>; /* CMOS */
+ qcom,vin-sel = <0>; /* PM8841_VPH 3.4V */
+ qcom,src-select = <0>; /* CONSTANT */
+ qcom,master-en = <1>; /* ENABLE MPP */
};
};
diff --git a/arch/arm/boot/dts/msm8974-mdss.dtsi b/arch/arm/boot/dts/msm8974-mdss.dtsi
index ca98706..e791348 100644
--- a/arch/arm/boot/dts/msm8974-mdss.dtsi
+++ b/arch/arm/boot/dts/msm8974-mdss.dtsi
@@ -60,4 +60,14 @@
qcom,mdss_pan_res = <1920 1080>;
qcom,mdss_pan_bpp = <24>;
};
+
+ mdss_edp: qcom,mdss_edp@fd923400 {
+ compatible = "qcom,mdss-edp";
+ reg = <0xfd923400 0x700>,
+ <0xfd8c2000 0x1000>;
+ reg-names = "edp_base", "mmss_cc_base";
+ vdda-supply = <&pm8941_l12>;
+ gpio-panel-en = <&msmgpio 58 0>;
+ status = "disable";
+ };
};
diff --git a/arch/arm/boot/dts/msm8974.dtsi b/arch/arm/boot/dts/msm8974.dtsi
index 07b824d..8d54585 100644
--- a/arch/arm/boot/dts/msm8974.dtsi
+++ b/arch/arm/boot/dts/msm8974.dtsi
@@ -61,7 +61,7 @@
compatible = "qcom,msm-vidc";
reg = <0xfdc00000 0xff000>;
interrupts = <0 44 0>;
- vidc-cp-map = <0x1000000 0x40000000>;
+ vidc-cp-map = <0x1000000 0x3f000000>;
vidc-ns-map = <0x40000000 0x40000000>;
load-freq-tbl = <979200 410000000>,
<783360 410000000>,
@@ -659,7 +659,7 @@
reg = <0xfe200000 0x00100>,
<0xfd485100 0x00010>;
reg-names = "qdsp6_base", "halt_base";
- interrupts = <0 194 1>;
+ interrupts = <0 162 1>;
qcom,firmware-name = "adsp";
};
@@ -704,6 +704,11 @@
compatible = "qcom,msm-pcm-afe";
};
+ qcom,msm-dai-q6-hdmi {
+ compatible = "qcom,msm-dai-q6-hdmi";
+ qcom,msm-dai-q6-dev-id = <8>;
+ };
+
qcom,msm-dai-q6 {
compatible = "qcom,msm-dai-q6";
qcom,msm-dai-q6-sb-0-rx {
@@ -815,7 +820,7 @@
reg = <0xfc820000 0x0020>,
<0x0d1fc000 0x4000>;
reg-names = "rmb_base", "metadata_base";
- interrupts = <0 56 1>;
+ interrupts = <0 24 1>;
qcom,firmware-name = "modem";
qcom,depends-on = "mba";
@@ -827,7 +832,7 @@
<0xfc401700 0x4>,
<0xfd485300 0xc>;
reg-names = "pmu_base", "clk_base", "halt_base";
- interrupts = <0 181 1>;
+ interrupts = <0 149 1>;
vdd_pronto_pll-supply = <&pm8941_l12>;
qcom,firmware-name = "wcnss";
@@ -966,9 +971,10 @@
tsens@fc4a8000 {
compatible = "qcom,msm-tsens";
reg = <0xfc4a8000 0x2000>,
- <0xfc4b80d0 0x5>;
+ <0xfc4b8000 0x1000>;
reg-names = "tsens_physical", "tsens_eeprom_physical";
interrupts = <0 184 0>;
+ qcom,calibration-less-mode;
qcom,sensors = <11>;
qcom,slope = <3200 3200 3200 3200 3200 3200 3200 3200 3200
3200 3200>;
diff --git a/arch/arm/boot/dts/msm8974_pm.dtsi b/arch/arm/boot/dts/msm8974_pm.dtsi
index b2f3fec..c6cbca3 100644
--- a/arch/arm/boot/dts/msm8974_pm.dtsi
+++ b/arch/arm/boot/dts/msm8974_pm.dtsi
@@ -124,6 +124,7 @@
qcom,vctl-timeout-us = <50>;
qcom,vctl-port = <0x0>;
qcom,phase-port = <0x1>;
+ qcom,pfm-port = <0x2>;
qcom,saw2-spm-cmd-ret = [00 20 03 22 00 0f];
qcom,saw2-spm-cmd-gdhs = [00 20 32 42 07 44 22 50 02 32 50 0f];
qcom,saw2-spm-cmd-pc = [00 10 32 b0 11 42 07 01 b0 12 44
diff --git a/arch/arm/boot/dts/msm9625-cdp.dts b/arch/arm/boot/dts/msm9625-cdp.dts
index aa1ec92..89c269e 100644
--- a/arch/arm/boot/dts/msm9625-cdp.dts
+++ b/arch/arm/boot/dts/msm9625-cdp.dts
@@ -17,7 +17,7 @@
/ {
model = "Qualcomm MSM 9625 CDP";
compatible = "qcom,msm9625-cdp", "qcom,msm9625";
- qcom,msm-id = <134 1 0>;
+ qcom,msm-id = <134 1 0>, <152 1 0>;
};
/* PM8019 GPIO and MPP configuration */
@@ -32,6 +32,14 @@
};
gpio@c300 { /* GPIO 4 */
+ /* ext_2p95v regulator enable config */
+ qcom,mode = <1>; /* Digital output */
+ qcom,output-type = <0>; /* CMOS */
+ qcom,invert = <0>; /* Output low */
+ qcom,out-strength = <1>; /* Low */
+ qcom,vin-sel = <2>; /* PM8019 L11 - 1.8V */
+ qcom,src-select = <0>; /* Constant */
+ qcom,master-en = <1>; /* Enable GPIO */
};
gpio@c400 { /* GPIO 5 */
diff --git a/arch/arm/boot/dts/msm9625-mtp.dts b/arch/arm/boot/dts/msm9625-mtp.dts
index 3ec949f..a5673e5 100644
--- a/arch/arm/boot/dts/msm9625-mtp.dts
+++ b/arch/arm/boot/dts/msm9625-mtp.dts
@@ -17,7 +17,7 @@
/ {
model = "Qualcomm MSM 9625 MTP";
compatible = "qcom,msm9625-mtp", "qcom,msm9625";
- qcom,msm-id = <134 8 0>;
+ qcom,msm-id = <134 7 0>, <152 7 0>;
};
/* PM8019 GPIO and MPP configuration */
@@ -32,6 +32,14 @@
};
gpio@c300 { /* GPIO 4 */
+ /* ext_2p95v regulator enable config */
+ qcom,mode = <1>; /* Digital output */
+ qcom,output-type = <0>; /* CMOS */
+ qcom,invert = <0>; /* Output low */
+ qcom,out-strength = <1>; /* Low */
+ qcom,vin-sel = <2>; /* PM8019 L11 - 1.8V */
+ qcom,src-select = <0>; /* Constant */
+ qcom,master-en = <1>; /* Enable GPIO */
};
gpio@c400 { /* GPIO 5 */
diff --git a/arch/arm/boot/dts/msm9625-regulator.dtsi b/arch/arm/boot/dts/msm9625-regulator.dtsi
index 1b5b03b..dccc723 100644
--- a/arch/arm/boot/dts/msm9625-regulator.dtsi
+++ b/arch/arm/boot/dts/msm9625-regulator.dtsi
@@ -250,3 +250,12 @@
};
};
};
+
+/ {
+ ext_2p95v: regulator-isl80101 {
+ compatible = "regulator-fixed";
+ regulator-name = "ext_2p95v";
+ gpio = <&pm8019_gpios 4 0>;
+ enable-active-high;
+ };
+};
diff --git a/arch/arm/boot/dts/msm9625.dtsi b/arch/arm/boot/dts/msm9625.dtsi
index 3d3f563..90e1118 100644
--- a/arch/arm/boot/dts/msm9625.dtsi
+++ b/arch/arm/boot/dts/msm9625.dtsi
@@ -29,8 +29,6 @@
l2: cache-controller@f9040000 {
compatible = "arm,pl310-cache";
reg = <0xf9040000 0x1000>;
- arm,data-latency = <1 1 1>;
- arm,tag-latency = <1 1 1>;
cache-unified;
cache-level = <2>;
};
@@ -173,6 +171,77 @@
qcom,i2c-bus-freq = <100000>;
qcom,i2c-src-freq = <24000000>;
};
+
+ sdcc2: qcom,sdcc@f98a4000 {
+ cell-index = <2>; /* SDC2 SD card slot */
+ compatible = "qcom,msm-sdcc";
+ reg = <0xf98a4000 0x800>,
+ <0xf98a4800 0x100>,
+ <0xf9884000 0x7000>;
+ reg-names = "core_mem", "dml_mem", "bam_mem";
+
+ vdd-supply = <&ext_2p95v>;
+
+ vdd-io-supply = <&pm8019_l13>;
+ qcom,sdcc-vdd-io-always_on;
+ qcom,sdcc-vdd-io-lpm_sup;
+ qcom,sdcc-vdd-io-voltage_level = <1800000 2950000>;
+ qcom,sdcc-vdd-io-current_level = <6 22000>;
+
+ qcom,sdcc-pad-pull-on = <0x0 0x3 0x3>;
+ qcom,sdcc-pad-pull-off = <0x0 0x3 0x3>;
+ qcom,sdcc-pad-drv-on = <0x7 0x4 0x4>;
+ qcom,sdcc-pad-drv-off = <0x0 0x0 0x0>;
+
+ qcom,sdcc-clk-rates = <400000 25000000 50000000 100000000 200000000>;
+ qcom,sdcc-sup-voltages = <2950 2950>;
+ qcom,sdcc-bus-width = <4>;
+ qcom,sdcc-xpc;
+ qcom,sdcc-bus-speed-mode = "SDR12", "SDR25", "SDR50", "DDR50", "SDR104";
+ qcom,sdcc-current-limit = <800>;
+
+ interrupt-parent = <&sdcc2>;
+ #address-cells = <0>;
+ interrupts = <0 1 2>;
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0xffffffff>;
+ interrupt-map = <0 &intc 0 125 0
+ 1 &intc 0 220 0
+ 2 &msmgpio 66 0x3>;
+ interrupt-names = "core_irq", "bam_irq", "status_irq";
+ cd-gpios = <&msmgpio 66 0>;
+ };
+
+ sdcc3: qcom,sdcc@f9864000 {
+ cell-index = <3>; /* SDC3 SDIO slot */
+ compatible = "qcom,msm-sdcc";
+ reg = <0xf9864000 0x800>,
+ <0xf9864800 0x100>,
+ <0xf9844000 0x7000>;
+ reg-names = "core_mem", "dml_mem", "bam_mem";
+ interrupts = <0 127 0>, <0 223 0>;
+ interrupt-names = "core_irq", "bam_irq";
+
+ gpios = <&msmgpio 25 0>,
+ <&msmgpio 24 0>,
+ <&msmgpio 16 0>,
+ <&msmgpio 17 0>,
+ <&msmgpio 18 0>,
+ <&msmgpio 19 0>;
+ qcom,sdcc-gpio-names = "CLK", "CMD", "DAT0", "DAT1", "DAT2", "DAT3";
+
+ qcom,sdcc-clk-rates = <400000 25000000 50000000 100000000>;
+ qcom,sdcc-sup-voltages = <2950 2950>;
+ qcom,sdcc-bus-width = <4>;
+ qcom,sdcc-bus-speed-mode = "SDR12", "SDR25", "SDR50", "DDR50";
+ status = "disable";
+ };
+
+ qcom,bam_dmux@fc834000 {
+ compatible = "qcom,bam_dmux";
+ reg = <0xfc834000 0x7000>;
+ interrupts = <0 29 1>;
+ };
};
/include/ "msm-pm8019-rpm-regulator.dtsi"
diff --git a/arch/arm/common/gic.c b/arch/arm/common/gic.c
index 6b8a374..b4574aa 100644
--- a/arch/arm/common/gic.c
+++ b/arch/arm/common/gic.c
@@ -67,6 +67,7 @@
u32 __percpu *saved_ppi_enable;
u32 __percpu *saved_ppi_conf;
#endif
+ u32 saved_dist_isr[DIV_ROUND_UP(1020, 32)];
struct irq_domain *domain;
unsigned int gic_irqs;
#ifdef CONFIG_GIC_NON_BANKED
@@ -640,6 +641,11 @@
for (i = 0; i < DIV_ROUND_UP(gic_irqs, 32); i++)
gic_data[gic_nr].saved_spi_enable[i] =
readl_relaxed(dist_base + GIC_DIST_ENABLE_SET + i * 4);
+ if (is_cpu_secure()) {
+ for (i = 0; i < DIV_ROUND_UP(gic_irqs, 32); i++)
+ gic_data[gic_nr].saved_dist_isr[i] =
+ readl_relaxed(dist_base + GIC_DIST_ISR + i * 4);
+ }
}
/*
@@ -682,6 +688,12 @@
writel_relaxed(gic_data[gic_nr].saved_spi_enable[i],
dist_base + GIC_DIST_ENABLE_SET + i * 4);
+ if (is_cpu_secure()) {
+ for (i = 0; i < DIV_ROUND_UP(gic_irqs, 32); i++)
+ writel_relaxed(gic_data[gic_nr].saved_dist_isr[i],
+ dist_base + GIC_DIST_ISR + i * 4);
+ }
+
writel_relaxed(saved_dist_ctrl, dist_base + GIC_DIST_CTRL);
}
diff --git a/arch/arm/configs/msm7627a-perf_defconfig b/arch/arm/configs/msm7627a-perf_defconfig
index 9a0bfba..76650e0 100644
--- a/arch/arm/configs/msm7627a-perf_defconfig
+++ b/arch/arm/configs/msm7627a-perf_defconfig
@@ -58,6 +58,7 @@
CONFIG_MSM_RPC_PMIC=y
CONFIG_MSM_RPC_USB=y
CONFIG_MSM_RPC_PMAPP=y
+CONFIG_MSM_FIQ=y
CONFIG_ARM_THUMBEE=y
CONFIG_NO_HZ=y
CONFIG_HIGH_RES_TIMERS=y
@@ -167,10 +168,10 @@
CONFIG_IP_NF_ARPTABLES=y
CONFIG_IP_NF_ARPFILTER=y
CONFIG_IP_NF_ARP_MANGLE=y
-CONFIG_IP6_NF_MANGLE=y
-CONFIG_IP6_NF_RAW=y
CONFIG_IP6_NF_IPTABLES=y
CONFIG_IP6_NF_FILTER=y
+CONFIG_IP6_NF_MANGLE=y
+CONFIG_IP6_NF_RAW=y
CONFIG_ATM=y
CONFIG_L2TP=y
CONFIG_L2TP_DEBUGFS=y
@@ -252,6 +253,7 @@
# CONFIG_HW_RANDOM is not set
CONFIG_I2C=y
CONFIG_I2C_CHARDEV=y
+CONFIG_I2C_GPIO=y
# CONFIG_I2C_MSM is not set
CONFIG_I2C_QUP=y
CONFIG_DEBUG_GPIO=y
@@ -261,6 +263,7 @@
CONFIG_BATTERY_MSM=y
CONFIG_SENSORS_MSM_ADC=y
CONFIG_MARIMBA_CORE=y
+CONFIG_REGULATOR_ONSEMI_NCP6335D=y
CONFIG_REGULATOR_MSM_GPIO=y
CONFIG_MEDIA_SUPPORT=y
CONFIG_MEDIA_CONTROLLER=y
@@ -378,4 +381,3 @@
CONFIG_DYNAMIC_DEBUG=y
CONFIG_DEBUG_USER=y
CONFIG_CRYPTO_TWOFISH=y
-CONFIG_CRC_CCITT=y
diff --git a/arch/arm/configs/msm7627a_defconfig b/arch/arm/configs/msm7627a_defconfig
index 60a2d72..8ab57de 100644
--- a/arch/arm/configs/msm7627a_defconfig
+++ b/arch/arm/configs/msm7627a_defconfig
@@ -60,6 +60,7 @@
CONFIG_MSM_RPC_PMIC=y
CONFIG_MSM_RPC_USB=y
CONFIG_MSM_RPC_PMAPP=y
+CONFIG_MSM_FIQ=y
CONFIG_ARM_THUMBEE=y
CONFIG_NO_HZ=y
CONFIG_HIGH_RES_TIMERS=y
@@ -169,10 +170,10 @@
CONFIG_IP_NF_ARPTABLES=y
CONFIG_IP_NF_ARPFILTER=y
CONFIG_IP_NF_ARP_MANGLE=y
-CONFIG_IP6_NF_MANGLE=y
-CONFIG_IP6_NF_RAW=y
CONFIG_IP6_NF_IPTABLES=y
CONFIG_IP6_NF_FILTER=y
+CONFIG_IP6_NF_MANGLE=y
+CONFIG_IP6_NF_RAW=y
CONFIG_ATM=y
CONFIG_L2TP=y
CONFIG_L2TP_DEBUGFS=y
@@ -254,6 +255,7 @@
# CONFIG_HW_RANDOM is not set
CONFIG_I2C=y
CONFIG_I2C_CHARDEV=y
+CONFIG_I2C_GPIO=y
# CONFIG_I2C_MSM is not set
CONFIG_I2C_QUP=y
CONFIG_DEBUG_GPIO=y
@@ -263,6 +265,7 @@
CONFIG_BATTERY_MSM=y
CONFIG_SENSORS_MSM_ADC=y
CONFIG_MARIMBA_CORE=y
+CONFIG_REGULATOR_ONSEMI_NCP6335D=y
CONFIG_REGULATOR_MSM_GPIO=y
CONFIG_MEDIA_SUPPORT=y
CONFIG_MEDIA_CONTROLLER=y
diff --git a/arch/arm/configs/msm7630-perf_defconfig b/arch/arm/configs/msm7630-perf_defconfig
index 401654d..f2d25ac 100644
--- a/arch/arm/configs/msm7630-perf_defconfig
+++ b/arch/arm/configs/msm7630-perf_defconfig
@@ -280,8 +280,8 @@
CONFIG_FB_MSM_TRIPLE_BUFFER=y
CONFIG_FB_MSM_MDP40=y
CONFIG_FB_MSM_OVERLAY=y
-CONFIG_FB_MSM_OVERLAY0_WRITEBACK=y
CONFIG_FB_MSM_NO_MDP_PIPE_CTRL=y
+CONFIG_FB_MSM_OVERLAY0_WRITEBACK=y
CONFIG_FB_MSM_TRY_MDDI_CATCH_LCDC_PRISM=y
CONFIG_FB_MSM_HDMI_ADV7520_PANEL=y
CONFIG_BACKLIGHT_LCD_SUPPORT=y
diff --git a/arch/arm/configs/msm8660-perf_defconfig b/arch/arm/configs/msm8660-perf_defconfig
index 957dbcf..2ee3f3b 100644
--- a/arch/arm/configs/msm8660-perf_defconfig
+++ b/arch/arm/configs/msm8660-perf_defconfig
@@ -35,8 +35,6 @@
CONFIG_DEFAULT_DEADLINE=y
CONFIG_ARCH_MSM=y
CONFIG_ARCH_MSM8X60=y
-CONFIG_MACH_MSM8X60_RUMI3=y
-CONFIG_MACH_MSM8X60_SIM=y
CONFIG_MACH_MSM8X60_SURF=y
CONFIG_MACH_MSM8X60_FFA=y
CONFIG_MACH_MSM8X60_FLUID=y
@@ -64,13 +62,13 @@
CONFIG_MSM_RMT_STORAGE_CLIENT=y
CONFIG_MSM_SDIO_SMEM=y
# CONFIG_MSM_HW3D is not set
+CONFIG_MSM_SUBSYSTEM_RESTART=y
+CONFIG_MSM_SYSMON_COMM=y
CONFIG_MSM_PIL_MODEM=y
CONFIG_MSM_PIL_QDSP6V3=y
CONFIG_MSM_PIL_TZAPPS=y
CONFIG_MSM_PIL_DSPS=y
CONFIG_MSM_PIL_VIDC=y
-CONFIG_MSM_SUBSYSTEM_RESTART=y
-CONFIG_MSM_SYSMON_COMM=y
CONFIG_MSM_TZ_LOG=y
CONFIG_MSM_RPM_LOG=y
CONFIG_MSM_RPM_STATS_LOG=y
diff --git a/arch/arm/configs/msm8660_defconfig b/arch/arm/configs/msm8660_defconfig
index 4e5479a..25c5207 100644
--- a/arch/arm/configs/msm8660_defconfig
+++ b/arch/arm/configs/msm8660_defconfig
@@ -34,8 +34,6 @@
CONFIG_DEFAULT_DEADLINE=y
CONFIG_ARCH_MSM=y
CONFIG_ARCH_MSM8X60=y
-CONFIG_MACH_MSM8X60_RUMI3=y
-CONFIG_MACH_MSM8X60_SIM=y
CONFIG_MACH_MSM8X60_SURF=y
CONFIG_MACH_MSM8X60_FFA=y
CONFIG_MACH_MSM8X60_FLUID=y
@@ -63,20 +61,19 @@
CONFIG_MSM_RMT_STORAGE_CLIENT=y
CONFIG_MSM_SDIO_SMEM=y
# CONFIG_MSM_HW3D is not set
+CONFIG_MSM_SUBSYSTEM_RESTART=y
+CONFIG_MSM_SYSMON_COMM=y
CONFIG_MSM_PIL_MODEM=y
CONFIG_MSM_PIL_QDSP6V3=y
CONFIG_MSM_PIL_TZAPPS=y
CONFIG_MSM_PIL_DSPS=y
CONFIG_MSM_PIL_VIDC=y
-CONFIG_MSM_SUBSYSTEM_RESTART=y
-CONFIG_MSM_SYSMON_COMM=y
CONFIG_MSM_TZ_LOG=y
CONFIG_MSM_RPM_LOG=y
CONFIG_MSM_RPM_STATS_LOG=y
CONFIG_MSM_WATCHDOG=y
CONFIG_MSM_DLOAD_MODE=y
CONFIG_MSM_ETM=y
-CONFIG_MSM_SLEEP_STATS=y
CONFIG_MSM_GSBI9_UART=y
CONFIG_STRICT_MEMORY_RWX=y
CONFIG_NO_HZ=y
diff --git a/arch/arm/configs/msm8910_defconfig b/arch/arm/configs/msm8910_defconfig
index ebcc6f5..a9dadee 100644
--- a/arch/arm/configs/msm8910_defconfig
+++ b/arch/arm/configs/msm8910_defconfig
@@ -35,6 +35,7 @@
CONFIG_CPU_HAS_L2_PMU=y
# CONFIG_MSM_FIQ_SUPPORT is not set
# CONFIG_MSM_PROC_COMM is not set
+CONFIG_MSM_SMD=y
CONFIG_MSM_DIRECT_SCLK_ACCESS=y
CONFIG_NO_HZ=y
CONFIG_HIGH_RES_TIMERS=y
@@ -46,6 +47,9 @@
CONFIG_USE_OF=y
# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
# CONFIG_SUSPEND is not set
+CONFIG_NET=y
+CONFIG_UNIX=y
+CONFIG_NETFILTER=y
CONFIG_BLK_DEV_LOOP=y
CONFIG_BLK_DEV_RAM=y
# CONFIG_ANDROID_PMEM is not set
@@ -56,15 +60,21 @@
CONFIG_INPUT_UINPUT=y
CONFIG_INPUT_GPIO=m
CONFIG_SERIO_LIBPS2=y
-# CONFIG_LEGACY_PTYS is not set
CONFIG_SERIAL_MSM_HSL=y
CONFIG_SERIAL_MSM_HSL_CONSOLE=y
+CONFIG_DIAG_CHAR=y
CONFIG_HW_RANDOM=y
CONFIG_DEBUG_GPIO=y
CONFIG_GPIO_SYSFS=y
# CONFIG_HWMON is not set
-# CONFIG_HID_SUPPORT is not set
-# CONFIG_USB_SUPPORT is not set
+CONFIG_SOUND=y
+CONFIG_SND=y
+CONFIG_SND_SOC=y
+CONFIG_USB_GADGET=y
+CONFIG_USB_GADGET_DEBUG_FILES=y
+CONFIG_USB_GADGET_DEBUG_FS=y
+CONFIG_USB_CI13XXX_MSM=y
+CONFIG_USB_G_ANDROID=y
CONFIG_VFAT_FS=y
CONFIG_TMPFS=y
# CONFIG_MISC_FILESYSTEMS is not set
diff --git a/arch/arm/configs/msm8960-perf_defconfig b/arch/arm/configs/msm8960-perf_defconfig
index 6a6bfda..53e6260 100644
--- a/arch/arm/configs/msm8960-perf_defconfig
+++ b/arch/arm/configs/msm8960-perf_defconfig
@@ -39,8 +39,6 @@
CONFIG_ARCH_MSM8930=y
CONFIG_ARCH_APQ8064=y
CONFIG_MSM_KRAIT_TBB_ABORT_HANDLER=y
-CONFIG_MACH_MSM8960_SIM=y
-CONFIG_MACH_MSM8960_RUMI3=y
CONFIG_MACH_MSM8960_CDP=y
CONFIG_MACH_MSM8960_MTP=y
CONFIG_MACH_MSM8960_FLUID=y
@@ -50,8 +48,6 @@
CONFIG_MACH_MSM8930_FLUID=y
CONFIG_MACH_MSM8627_CDP=y
CONFIG_MACH_MSM8627_MTP=y
-CONFIG_MACH_APQ8064_SIM=y
-CONFIG_MACH_APQ8064_RUMI3=y
CONFIG_MACH_APQ8064_CDP=y
CONFIG_MACH_APQ8064_MTP=y
CONFIG_MACH_APQ8064_LIQUID=y
@@ -66,11 +62,14 @@
CONFIG_MSM_SMD_PKG4=y
CONFIG_MSM_PCIE=y
CONFIG_MSM_BAM_DMUX=y
+CONFIG_MSM_IPC_LOGGING=y
CONFIG_MSM_DSPS=y
CONFIG_MSM_IPC_ROUTER=y
CONFIG_MSM_IPC_ROUTER_SMD_XPRT=y
CONFIG_MSM_AVS_HW=y
# CONFIG_MSM_HW3D is not set
+CONFIG_MSM_SUBSYSTEM_RESTART=y
+CONFIG_MSM_SYSMON_COMM=y
CONFIG_MSM_PIL_LPASS_QDSP6V4=y
CONFIG_MSM_PIL_MODEM_QDSP6V4=y
CONFIG_MSM_PIL_RIVA=y
@@ -78,27 +77,20 @@
CONFIG_MSM_PIL_DSPS=y
CONFIG_MSM_PIL_VIDC=y
CONFIG_MSM_PIL_GSS=y
-CONFIG_MSM_SUBSYSTEM_RESTART=y
-CONFIG_MSM_SYSMON_COMM=y
-CONFIG_MSM_MODEM_8960=y
-CONFIG_MSM_LPASS_8960=y
-CONFIG_MSM_WCNSS_SSR_8960=y
-CONFIG_MSM_GSS_SSR_8064=y
CONFIG_MSM_TZ_LOG=y
CONFIG_MSM_RPM_LOG=y
-CONFIG_MSM_RPM_RBCPR_STATS_LOG=y
CONFIG_MSM_RPM_STATS_LOG=y
+CONFIG_MSM_RPM_RBCPR_STATS_LOG=y
+CONFIG_MSM_EVENT_TIMER=y
CONFIG_MSM_BUS_SCALING=y
CONFIG_MSM_BUS_RPM_MULTI_TIER_ENABLED=y
CONFIG_MSM_WATCHDOG=y
CONFIG_MSM_DLOAD_MODE=y
-CONFIG_MSM_SLEEP_STATS=y
CONFIG_MSM_EBI_ERP=y
CONFIG_MSM_CACHE_ERP=y
CONFIG_MSM_L1_ERR_PANIC=y
CONFIG_MSM_L1_ERR_LOG=y
CONFIG_MSM_L2_ERP_2BIT_PANIC=y
-CONFIG_MSM_EVENT_TIMER=y
CONFIG_MSM_DCVS=y
CONFIG_MSM_HSIC_SYSMON=y
CONFIG_STRICT_MEMORY_RWX=y
@@ -232,7 +224,6 @@
CONFIG_NET_EMATCH_META=y
CONFIG_NET_EMATCH_TEXT=y
CONFIG_NET_CLS_ACT=y
-CONFIG_MARIMBA_CORE=y
CONFIG_BT=y
CONFIG_BT_RFCOMM=y
CONFIG_BT_RFCOMM_TTY=y
@@ -243,8 +234,8 @@
CONFIG_BT_HCISMD=y
CONFIG_BT_HCIUART=y
CONFIG_BT_HCIUART_H4=y
-CONFIG_BT_HCIUART_IBS=y
CONFIG_BT_HCIUART_ATH3K=y
+CONFIG_BT_HCIUART_IBS=y
CONFIG_MSM_BT_POWER=y
CONFIG_CFG80211=m
# CONFIG_CFG80211_WEXT is not set
@@ -253,6 +244,7 @@
CONFIG_GENLOCK_MISCDEVICE=y
CONFIG_BLK_DEV_LOOP=y
CONFIG_BLK_DEV_RAM=y
+CONFIG_TSPP=m
CONFIG_HAPTIC_ISA1200=y
CONFIG_PMIC8XXX_VIBRATOR=y
CONFIG_QSEECOM=y
@@ -285,6 +277,7 @@
CONFIG_USB_USBNET=y
CONFIG_MSM_RMNET_USB=y
CONFIG_WCNSS_CORE=y
+CONFIG_WCNSS_MEM_PRE_ALLOC=y
CONFIG_INPUT_EVDEV=y
CONFIG_INPUT_EVBUG=m
CONFIG_KEYBOARD_GPIO=y
@@ -297,11 +290,10 @@
CONFIG_TOUCHSCREEN_CYTTSP_I2C_QC=y
CONFIG_INPUT_MISC=y
CONFIG_INPUT_PMIC8XXX_PWRKEY=y
+CONFIG_INPUT_MPU3050=y
CONFIG_INPUT_UINPUT=y
CONFIG_STM_LIS3DH=y
-CONFIG_INPUT_MPU3050=y
# CONFIG_LEGACY_PTYS is not set
-CONFIG_MSM_IPC_LOGGING=y
CONFIG_N_SMUX=y
CONFIG_N_SMUX_LOOPBACK=y
CONFIG_SMUX_CTL=y
@@ -335,6 +327,7 @@
CONFIG_THERMAL_TSENS8960=y
CONFIG_THERMAL_PM8XXX=y
CONFIG_THERMAL_MONITOR=y
+CONFIG_MARIMBA_CORE=y
CONFIG_MFD_PM8921_CORE=y
CONFIG_MFD_PM8821_CORE=y
CONFIG_MFD_PM8038_CORE=y
@@ -348,7 +341,7 @@
CONFIG_MEDIA_CONTROLLER=y
CONFIG_VIDEO_DEV=y
CONFIG_VIDEO_V4L2_SUBDEV_API=y
-CONFIG_MSM_WFD=y
+CONFIG_DVB_CORE=m
CONFIG_USER_RC_INPUT=y
CONFIG_IR_GPIO_CIR=y
# CONFIG_MEDIA_TUNER_CUSTOMISE is not set
@@ -373,8 +366,14 @@
CONFIG_MSM_CSI20_HEADER=y
CONFIG_S5K3L1YX=y
CONFIG_IMX091=y
+CONFIG_MSM_WFD=y
CONFIG_RADIO_IRIS=y
CONFIG_RADIO_IRIS_TRANSPORT=m
+# CONFIG_DVB_FE_CUSTOMISE is not set
+CONFIG_DVB_MPQ=m
+CONFIG_DVB_MPQ_DEMUX=m
+CONFIG_DVB_MPQ_VIDEO=m
+CONFIG_DVB_MPQ_TSPP1=y
CONFIG_ION=y
CONFIG_ION_MSM=y
CONFIG_MSM_KGSL=y
@@ -438,7 +437,6 @@
CONFIG_USB_GADGET_DEBUG_FILES=y
CONFIG_USB_CI13XXX_MSM=y
CONFIG_USB_G_ANDROID=y
-CONFIG_USB_ANDROID_RMNET_CTRL_SMD=y
CONFIG_MMC=y
CONFIG_MMC_PERF_PROFILING=y
CONFIG_MMC_UNSAFE_RESUME=y
@@ -474,6 +472,7 @@
CONFIG_MOBICORE_SUPPORT=m
CONFIG_MOBICORE_API=m
CONFIG_MSM_QDSS=y
+CONFIG_CONTROL_TRACE=m
CONFIG_EXT2_FS=y
CONFIG_EXT2_FS_XATTR=y
CONFIG_EXT3_FS=y
@@ -506,5 +505,3 @@
CONFIG_CRYPTO_DEV_QCE=m
CONFIG_CRYPTO_DEV_QCEDEV=m
CONFIG_CRC_CCITT=y
-CONFIG_WCNSS_MEM_PRE_ALLOC=y
-CONFIG_CONTROL_TRACE=m
diff --git a/arch/arm/configs/msm8960_defconfig b/arch/arm/configs/msm8960_defconfig
index cf2dd23..5770859 100644
--- a/arch/arm/configs/msm8960_defconfig
+++ b/arch/arm/configs/msm8960_defconfig
@@ -38,8 +38,6 @@
CONFIG_ARCH_MSM8930=y
CONFIG_ARCH_APQ8064=y
CONFIG_MSM_KRAIT_TBB_ABORT_HANDLER=y
-CONFIG_MACH_MSM8960_SIM=y
-CONFIG_MACH_MSM8960_RUMI3=y
CONFIG_MACH_MSM8960_CDP=y
CONFIG_MACH_MSM8960_MTP=y
CONFIG_MACH_MSM8960_FLUID=y
@@ -49,8 +47,6 @@
CONFIG_MACH_MSM8930_FLUID=y
CONFIG_MACH_MSM8627_CDP=y
CONFIG_MACH_MSM8627_MTP=y
-CONFIG_MACH_APQ8064_SIM=y
-CONFIG_MACH_APQ8064_RUMI3=y
CONFIG_MACH_APQ8064_CDP=y
CONFIG_MACH_APQ8064_MTP=y
CONFIG_MACH_APQ8064_LIQUID=y
@@ -65,11 +61,14 @@
CONFIG_MSM_SMD_PKG4=y
CONFIG_MSM_PCIE=y
CONFIG_MSM_BAM_DMUX=y
+CONFIG_MSM_IPC_LOGGING=y
CONFIG_MSM_DSPS=y
CONFIG_MSM_IPC_ROUTER=y
CONFIG_MSM_IPC_ROUTER_SMD_XPRT=y
CONFIG_MSM_AVS_HW=y
# CONFIG_MSM_HW3D is not set
+CONFIG_MSM_SUBSYSTEM_RESTART=y
+CONFIG_MSM_SYSMON_COMM=y
CONFIG_MSM_PIL_LPASS_QDSP6V4=y
CONFIG_MSM_PIL_MODEM_QDSP6V4=y
CONFIG_MSM_PIL_RIVA=y
@@ -77,19 +76,13 @@
CONFIG_MSM_PIL_DSPS=y
CONFIG_MSM_PIL_VIDC=y
CONFIG_MSM_PIL_GSS=y
-CONFIG_MSM_SUBSYSTEM_RESTART=y
-CONFIG_MSM_SYSMON_COMM=y
-CONFIG_MSM_MODEM_8960=y
-CONFIG_MSM_LPASS_8960=y
-CONFIG_MSM_WCNSS_SSR_8960=y
-CONFIG_MSM_GSS_SSR_8064=y
CONFIG_MSM_TZ_LOG=y
CONFIG_MSM_RPM_LOG=y
CONFIG_MSM_RPM_STATS_LOG=y
CONFIG_MSM_RPM_RBCPR_STATS_LOG=y
+CONFIG_MSM_EVENT_TIMER=y
CONFIG_MSM_BUS_SCALING=y
CONFIG_MSM_BUS_RPM_MULTI_TIER_ENABLED=y
-CONFIG_MSM_EVENT_TIMER=y
CONFIG_MSM_WATCHDOG=y
CONFIG_MSM_DLOAD_MODE=y
CONFIG_MSM_RTB=y
@@ -236,7 +229,6 @@
CONFIG_NET_EMATCH_META=y
CONFIG_NET_EMATCH_TEXT=y
CONFIG_NET_CLS_ACT=y
-CONFIG_MARIMBA_CORE=y
CONFIG_BT=y
CONFIG_BT_RFCOMM=y
CONFIG_BT_RFCOMM_TTY=y
@@ -246,8 +238,8 @@
CONFIG_BT_HIDP=y
CONFIG_BT_HCISMD=y
CONFIG_BT_HCIUART=y
-CONFIG_BT_HCIUART_ATH3K=y
CONFIG_BT_HCIUART_H4=y
+CONFIG_BT_HCIUART_ATH3K=y
CONFIG_BT_HCIUART_IBS=y
CONFIG_MSM_BT_POWER=y
CONFIG_CFG80211=m
@@ -257,6 +249,7 @@
CONFIG_GENLOCK_MISCDEVICE=y
CONFIG_BLK_DEV_LOOP=y
CONFIG_BLK_DEV_RAM=y
+CONFIG_TSPP=m
CONFIG_HAPTIC_ISA1200=y
CONFIG_PMIC8XXX_VIBRATOR=y
CONFIG_QSEECOM=y
@@ -289,6 +282,7 @@
CONFIG_USB_USBNET=y
CONFIG_MSM_RMNET_USB=y
CONFIG_WCNSS_CORE=y
+CONFIG_WCNSS_MEM_PRE_ALLOC=y
CONFIG_INPUT_EVDEV=y
CONFIG_INPUT_EVBUG=m
CONFIG_KEYBOARD_GPIO=y
@@ -301,11 +295,10 @@
CONFIG_TOUCHSCREEN_CYTTSP_I2C_QC=y
CONFIG_INPUT_MISC=y
CONFIG_INPUT_PMIC8XXX_PWRKEY=y
+CONFIG_INPUT_MPU3050=y
CONFIG_INPUT_UINPUT=y
CONFIG_STM_LIS3DH=y
-CONFIG_INPUT_MPU3050=y
# CONFIG_LEGACY_PTYS is not set
-CONFIG_MSM_IPC_LOGGING=y
CONFIG_N_SMUX=y
CONFIG_N_SMUX_LOOPBACK=y
CONFIG_SMUX_CTL=y
@@ -339,6 +332,7 @@
CONFIG_THERMAL_TSENS8960=y
CONFIG_THERMAL_PM8XXX=y
CONFIG_THERMAL_MONITOR=y
+CONFIG_MARIMBA_CORE=y
CONFIG_MFD_PM8921_CORE=y
CONFIG_MFD_PM8821_CORE=y
CONFIG_MFD_PM8038_CORE=y
@@ -352,7 +346,7 @@
CONFIG_MEDIA_CONTROLLER=y
CONFIG_VIDEO_DEV=y
CONFIG_VIDEO_V4L2_SUBDEV_API=y
-CONFIG_MSM_WFD=y
+CONFIG_DVB_CORE=m
CONFIG_USER_RC_INPUT=y
CONFIG_IR_GPIO_CIR=y
# CONFIG_MEDIA_TUNER_CUSTOMISE is not set
@@ -376,8 +370,14 @@
CONFIG_MSM_CSI20_HEADER=y
CONFIG_S5K3L1YX=y
CONFIG_IMX091=y
+CONFIG_MSM_WFD=y
CONFIG_RADIO_IRIS=y
CONFIG_RADIO_IRIS_TRANSPORT=m
+# CONFIG_DVB_FE_CUSTOMISE is not set
+CONFIG_DVB_MPQ=m
+CONFIG_DVB_MPQ_DEMUX=m
+CONFIG_DVB_MPQ_VIDEO=m
+CONFIG_DVB_MPQ_TSPP1=y
CONFIG_ION=y
CONFIG_ION_MSM=y
CONFIG_MSM_KGSL=y
@@ -440,7 +440,6 @@
CONFIG_USB_GADGET_DEBUG_FILES=y
CONFIG_USB_CI13XXX_MSM=y
CONFIG_USB_G_ANDROID=y
-CONFIG_USB_ANDROID_RMNET_CTRL_SMD=y
CONFIG_MMC=y
CONFIG_MMC_PERF_PROFILING=y
CONFIG_MMC_UNSAFE_RESUME=y
@@ -477,6 +476,7 @@
CONFIG_MOBICORE_API=m
CONFIG_MSM_QDSS=y
CONFIG_MSM_QDSS_ETM_DEFAULT_ENABLE=y
+CONFIG_CONTROL_TRACE=m
CONFIG_EXT2_FS=y
CONFIG_EXT2_FS_XATTR=y
CONFIG_EXT3_FS=y
@@ -514,7 +514,6 @@
CONFIG_FAULT_INJECTION_DEBUG_FS=y
CONFIG_FAULT_INJECTION_STACKTRACE_FILTER=y
CONFIG_DEBUG_PAGEALLOC=y
-CONFIG_ENABLE_DEFAULT_TRACERS=y
CONFIG_CPU_FREQ_SWITCH_PROFILER=y
CONFIG_DYNAMIC_DEBUG=y
CONFIG_DEBUG_USER=y
@@ -524,5 +523,3 @@
CONFIG_CRYPTO_DEV_QCE=m
CONFIG_CRYPTO_DEV_QCEDEV=m
CONFIG_CRC_CCITT=y
-CONFIG_WCNSS_MEM_PRE_ALLOC=y
-CONFIG_CONTROL_TRACE=m
diff --git a/arch/arm/configs/msm8974-perf_defconfig b/arch/arm/configs/msm8974-perf_defconfig
index 2f1833e..b2ee503 100644
--- a/arch/arm/configs/msm8974-perf_defconfig
+++ b/arch/arm/configs/msm8974-perf_defconfig
@@ -55,9 +55,6 @@
CONFIG_MSM_PIL_MBA=y
CONFIG_MSM_PIL_VENUS=y
CONFIG_MSM_PIL_PRONTO=y
-CONFIG_MSM_MODEM_SSR_8974=y
-CONFIG_MSM_ADSP_SSR_8974=y
-CONFIG_MSM_WCNSS_SSR_8974=y
CONFIG_MSM_TZ_LOG=y
CONFIG_MSM_DIRECT_SCLK_ACCESS=y
CONFIG_MSM_BUS_SCALING=y
@@ -73,6 +70,7 @@
CONFIG_MSM_L1_ERR_PANIC=y
CONFIG_MSM_L1_ERR_LOG=y
CONFIG_MSM_L2_ERP_2BIT_PANIC=y
+CONFIG_MSM_ENABLE_WDOG_DEBUG_CONTROL=y
CONFIG_STRICT_MEMORY_RWX=y
CONFIG_NO_HZ=y
CONFIG_HIGH_RES_TIMERS=y
@@ -287,6 +285,7 @@
CONFIG_VIDEOBUF2_MSM_MEM=y
CONFIG_V4L_PLATFORM_DRIVERS=y
CONFIG_MSM_CAMERA_V4L2=y
+CONFIG_MT9M114=y
CONFIG_OV2720=y
CONFIG_MSM_CAMERA_SENSOR=y
CONFIG_MSM_ACTUATOR=y
@@ -298,7 +297,6 @@
CONFIG_MSM_CSI2_REGISTER=y
CONFIG_MSM_ISPIF=y
CONFIG_S5K3L1YX=y
-CONFIG_MT9M114=y
CONFIG_RADIO_IRIS=y
CONFIG_RADIO_IRIS_TRANSPORT=m
CONFIG_ION=y
@@ -402,4 +400,3 @@
CONFIG_CRYPTO_DEV_QCE=m
CONFIG_CRYPTO_DEV_QCEDEV=m
CONFIG_CRC_CCITT=y
-CONFIG_MSM_ENABLE_WDOG_DEBUG_CONTROL=y
diff --git a/arch/arm/configs/msm8974_defconfig b/arch/arm/configs/msm8974_defconfig
index f0d60c5..d8d2eae 100644
--- a/arch/arm/configs/msm8974_defconfig
+++ b/arch/arm/configs/msm8974_defconfig
@@ -55,9 +55,6 @@
CONFIG_MSM_PIL_MBA=y
CONFIG_MSM_PIL_VENUS=y
CONFIG_MSM_PIL_PRONTO=y
-CONFIG_MSM_MODEM_SSR_8974=y
-CONFIG_MSM_ADSP_SSR_8974=y
-CONFIG_MSM_WCNSS_SSR_8974=y
CONFIG_MSM_TZ_LOG=y
CONFIG_MSM_DIRECT_SCLK_ACCESS=y
CONFIG_MSM_BUS_SCALING=y
@@ -76,6 +73,7 @@
CONFIG_MSM_L2_ERP_2BIT_PANIC=y
CONFIG_MSM_CACHE_DUMP=y
CONFIG_MSM_CACHE_DUMP_ON_PANIC=y
+CONFIG_MSM_ENABLE_WDOG_DEBUG_CONTROL=y
CONFIG_STRICT_MEMORY_RWX=y
CONFIG_NO_HZ=y
CONFIG_HIGH_RES_TIMERS=y
@@ -275,16 +273,18 @@
CONFIG_GPIO_QPNP_PIN_DEBUG=y
CONFIG_POWER_SUPPLY=y
# CONFIG_BATTERY_MSM is not set
+CONFIG_QPNP_CHARGER=y
CONFIG_QPNP_BMS=y
CONFIG_SENSORS_QPNP_ADC_VOLTAGE=y
CONFIG_SENSORS_QPNP_ADC_CURRENT=y
-CONFIG_QPNP_CHARGER=y
CONFIG_THERMAL=y
CONFIG_THERMAL_TSENS8974=y
+CONFIG_THERMAL_QPNP=y
CONFIG_WCD9320_CODEC=y
CONFIG_REGULATOR_FIXED_VOLTAGE=y
CONFIG_REGULATOR_STUB=y
CONFIG_REGULATOR_QPNP=y
+CONFIG_QPNP_PWM=y
CONFIG_MEDIA_SUPPORT=y
CONFIG_MEDIA_CONTROLLER=y
CONFIG_VIDEO_DEV=y
@@ -409,7 +409,6 @@
CONFIG_FAULT_INJECTION_DEBUG_FS=y
CONFIG_FAULT_INJECTION_STACKTRACE_FILTER=y
CONFIG_DEBUG_PAGEALLOC=y
-CONFIG_ENABLE_DEFAULT_TRACERS=y
CONFIG_CPU_FREQ_SWITCH_PROFILER=y
CONFIG_DYNAMIC_DEBUG=y
CONFIG_DEBUG_USER=y
@@ -422,4 +421,3 @@
CONFIG_CRYPTO_DEV_QCE=m
CONFIG_CRYPTO_DEV_QCEDEV=m
CONFIG_CRC_CCITT=y
-CONFIG_MSM_ENABLE_WDOG_DEBUG_CONTROL=y
diff --git a/arch/arm/configs/msm9615_defconfig b/arch/arm/configs/msm9615_defconfig
index 7c3d5b0..81b853d 100644
--- a/arch/arm/configs/msm9615_defconfig
+++ b/arch/arm/configs/msm9615_defconfig
@@ -47,11 +47,8 @@
CONFIG_MSM_IPC_ROUTER=y
CONFIG_MSM_IPC_ROUTER_SMD_XPRT=y
CONFIG_MSM_SUBSYSTEM_RESTART=y
-# CONFIG_MSM_SYSMON_COMM is not set
-CONFIG_MSM_MODEM_8960=y
CONFIG_MSM_PIL_LPASS_QDSP6V4=y
CONFIG_MSM_PIL_MODEM_QDSP6V4=y
-CONFIG_MSM_LPASS_8960=y
CONFIG_MSM_RPM_LOG=y
CONFIG_MSM_RPM_STATS_LOG=y
CONFIG_MSM_DIRECT_SCLK_ACCESS=y
diff --git a/arch/arm/configs/msm9625_defconfig b/arch/arm/configs/msm9625_defconfig
index 740a004..4e34ebd 100644
--- a/arch/arm/configs/msm9625_defconfig
+++ b/arch/arm/configs/msm9625_defconfig
@@ -102,6 +102,7 @@
CONFIG_SPMI=y
CONFIG_SPMI_MSM_PMIC_ARB=y
CONFIG_MSM_QPNP_INT=y
+CONFIG_REGULATOR_FIXED_VOLTAGE=y
CONFIG_DEBUG_GPIO=y
CONFIG_GPIO_SYSFS=y
CONFIG_GPIO_QPNP_PIN=y
@@ -117,6 +118,16 @@
CONFIG_USB_GADGET=y
CONFIG_USB_CI13XXX_MSM=y
CONFIG_USB_G_ANDROID=y
+CONFIG_MMC=y
+CONFIG_MMC_PERF_PROFILING=y
+CONFIG_MMC_UNSAFE_RESUME=y
+CONFIG_MMC_CLKGATE=y
+CONFIG_MMC_EMBEDDED_SDIO=y
+CONFIG_MMC_PARANOID_SD_INIT=y
+CONFIG_MMC_BLOCK_MINORS=32
+CONFIG_MMC_TEST=m
+CONFIG_MMC_MSM=y
+CONFIG_MMC_MSM_SPS_SUPPORT=y
CONFIG_RTC_CLASS=y
# CONFIG_RTC_DRV_MSM is not set
CONFIG_RTC_DRV_QPNP=y
@@ -158,13 +169,3 @@
# CONFIG_CRYPTO_HW is not set
CONFIG_CRC_CCITT=y
CONFIG_LIBCRC32C=y
-CONFIG_MMC=y
-CONFIG_MMC_PERF_PROFILING=y
-CONFIG_MMC_CLKGATE=y
-CONFIG_MMC_EMBEDDED_SDIO=y
-CONFIG_MMC_PARANOID_SD_INIT=y
-CONFIG_MMC_BLOCK_MINORS=32
-CONFIG_MMC_TEST=m
-CONFIG_MMC_MSM=y
-CONFIG_MMC_MSM_SPS_SUPPORT=y
-CONFIG_MMC_UNSAFE_RESUME=y
diff --git a/arch/arm/include/asm/fiq.h b/arch/arm/include/asm/fiq.h
index ec4b8b8..006f577 100644
--- a/arch/arm/include/asm/fiq.h
+++ b/arch/arm/include/asm/fiq.h
@@ -39,6 +39,7 @@
extern void set_fiq_handler(void *start, unsigned int length);
extern void enable_fiq(int fiq);
extern void disable_fiq(int fiq);
+extern void fiq_set_type(int fiq, unsigned int type);
#else
static inline int claim_fiq(struct fiq_handler *f)
{
@@ -48,6 +49,7 @@
static inline void set_fiq_handler(void *start, unsigned int length) { }
static inline void enable_fiq(int fiq) { }
static inline void disable_fiq(int fiq) { }
+static inline void fiq_set_type(int fiq, unsigned int type) { }
#endif
/* helpers defined in fiqasm.S: */
diff --git a/arch/arm/kernel/fiq.c b/arch/arm/kernel/fiq.c
index c32f845..ca852c5 100644
--- a/arch/arm/kernel/fiq.c
+++ b/arch/arm/kernel/fiq.c
@@ -39,6 +39,7 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/interrupt.h>
+#include <linux/irq.h>
#include <linux/seq_file.h>
#include <asm/cacheflush.h>
@@ -132,6 +133,11 @@
disable_irq(fiq + FIQ_START);
}
+void fiq_set_type(int fiq, unsigned int type)
+{
+ irq_set_irq_type(fiq + FIQ_START, type);
+}
+
EXPORT_SYMBOL(set_fiq_handler);
EXPORT_SYMBOL(__set_fiq_regs); /* defined in fiqasm.S */
EXPORT_SYMBOL(__get_fiq_regs); /* defined in fiqasm.S */
@@ -139,6 +145,7 @@
EXPORT_SYMBOL(release_fiq);
EXPORT_SYMBOL(enable_fiq);
EXPORT_SYMBOL(disable_fiq);
+EXPORT_SYMBOL(fiq_set_type);
void __init init_FIQ(void)
{
diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig
index 7ee0eb2..10c7089 100644
--- a/arch/arm/mach-msm/Kconfig
+++ b/arch/arm/mach-msm/Kconfig
@@ -22,7 +22,7 @@
config ARCH_MSM7X27
bool "MSM7x27"
select ARCH_MSM_ARM11 if MSM_SOC_REV_NONE
- select ARCH_HAS_BARRIERS if MSM_SOC_REV_NONE
+ select ARCH_HAS_BARRIERS
select ARCH_MSM_CORTEX_A5 if MSM_SOC_REV_A
select MSM_VIC
select CPU_V6 if MSM_SOC_REV_NONE
@@ -176,6 +176,7 @@
select MSM_RUN_QUEUE_STATS
select ARM_HAS_SG_CHAIN
select MSM_KRAIT_WFE_FIXUP
+ select MSM_ULTRASOUND_A
config ARCH_MSM8930
bool "MSM8930"
@@ -202,7 +203,7 @@
select MSM_REMOTE_SPINLOCK_SFPB
select ARCH_SPARSEMEM_ENABLE
select ARCH_HAS_HOLES_MEMORYMODEL
- select MSM_ULTRASOUND
+ select MSM_ULTRASOUND_A
select MULTI_IRQ_HANDLER
select MSM_PM8X60 if PM
select HOLES_IN_ZONE if SPARSEMEM
@@ -235,6 +236,7 @@
select ARCH_SUPPORTS_MSI
select ARM_HAS_SG_CHAIN
select MSM_KRAIT_WFE_FIXUP
+ select MSM_ULTRASOUND_A
config ARCH_MSM8974
bool "MSM8974"
@@ -355,6 +357,7 @@
select SMP
select MSM_SMP
select CPU_V7
+ select MSM_SCM if SMP
select MSM_GPIOMUX
select MSM_RPM_SMD
select MSM_NATIVE_RESTART
@@ -669,6 +672,18 @@
help
Support for the Qualcomm MSM8625 Reference Design.
+config MACH_QRD_SKUD_PRIME
+ depends on ARCH_MSM8625
+ depends on !MSM_STACKED_MEMORY
+ default y
+ bool "MSM8625 SKUD PRIME"
+ help
+ Support for the Qualcomm MSM8625 SKUD prime Reference Design.
+ Add support for a SKUD prime reference design based on MSM8x25
+ chipset. This device is much closer to a phone than regular form
+ factor devices, with new touch, display panel and other hardware
+ configurations.
+
config MACH_MSM7X30_SURF
depends on ARCH_MSM7X30
depends on !MSM_STACKED_MEMORY
@@ -948,7 +963,7 @@
default "0x00000000" if ARCH_MSM8974
default "0x00000000" if ARCH_MPQ8092
default "0x00000000" if ARCH_MSM8226
- default "0x00000000" if ARCH_MSM8910
+ default "0x80200000" if ARCH_MSM8910
default "0x10000000" if ARCH_FSM9XXX
default "0x00200000" if ARCH_MSM9625
default "0x00200000" if !MSM_STACKED_MEMORY
@@ -2325,11 +2340,15 @@
for the platforms that use APRv2.
Say M if you want to enable this module.
-config MSM_ULTRASOUND
- bool "MSM ultrasound support"
- depends on MSM_AUDIO_QDSP6
+config MSM_ULTRASOUND_A
+ bool "QDSP6 HW Ultrasound support"
help
- Enable support for qdsp6/ultrasound.
+ Enable HW ultrasound support in QDSP6.
+ QDSP6 can support HW encoder & decoder and
+ ultrasound processing. It will enable
+ ultrasound data paths between
+ HW and services, calculating input events
+ upon the ultrasound data.
config MSM_RPC_VIBRATOR
bool "RPC based MSM Vibrator Support"
@@ -2629,4 +2648,15 @@
used to control debug image.
This support is currently required for MSM8974 to disable debug image
on PS HOLD reset
+
+config MSM_FIQ
+ bool "Enable FIQ for debugging"
+ depends on ARCH_MSM8625
+ select FIQ
+ select GIC_SECURE
+ help
+ Enable any line to be used as an FIQ. This will help debugging
+ 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.
endif
diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile
index f073e70..6a62f8c 100644
--- a/arch/arm/mach-msm/Makefile
+++ b/arch/arm/mach-msm/Makefile
@@ -6,6 +6,7 @@
endif
obj-y += clock.o clock-voter.o clock-dummy.o
obj-y += modem_notifier.o subsystem_map.o
+obj-$(CONFIG_USE_OF) += board-dt.o
obj-$(CONFIG_CPU_FREQ_MSM) += cpufreq.o
obj-$(CONFIG_DEBUG_FS) += nohlt.o clock-debug.o
obj-$(CONFIG_KEXEC) += msm_kexec.o
@@ -256,6 +257,7 @@
obj-$(CONFIG_MACH_MSM8625_SURF) += board-msm7x27a.o board-7627a-all.o
obj-$(CONFIG_MACH_MSM8625_EVB) += board-qrd7627a.o board-7627a-all.o
obj-$(CONFIG_MACH_MSM8625_QRD7) += board-qrd7627a.o board-7627a-all.o
+obj-$(CONFIG_MACH_QRD_SKUD_PRIME) += board-qrd7627a.o board-7627a-all.o
obj-$(CONFIG_MACH_MSM8625_FFA) += board-msm7x27a.o board-7627a-all.o
obj-$(CONFIG_MACH_MSM8625_EVT) += board-msm7x27a.o board-7627a-all.o
obj-$(CONFIG_ARCH_MSM7X30) += board-msm7x30.o devices-msm7x30.o memory_topology.o
@@ -287,7 +289,7 @@
obj-$(CONFIG_MACH_MPQ8064_DTV) += board-8064-all.o board-8064-regulator.o
obj-$(CONFIG_ARCH_MSM9615) += board-9615.o devices-9615.o board-9615-regulator.o board-9615-gpiomux.o board-9615-storage.o board-9615-display.o
obj-$(CONFIG_ARCH_MSM9615) += clock-local.o clock-9615.o acpuclock-9615.o clock-rpm.o clock-pll.o
-obj-$(CONFIG_ARCH_MSM8974) += board-8974.o board-dt.o board-8974-gpiomux.o
+obj-$(CONFIG_ARCH_MSM8974) += board-8974.o board-8974-gpiomux.o
obj-$(CONFIG_ARCH_MSM8974) += acpuclock-8974.o
obj-$(CONFIG_ARCH_MSM8974) += clock-local2.o clock-pll.o clock-8974.o clock-rpm.o clock-voter.o clock-mdss-8974.o
obj-$(CONFIG_ARCH_MSM8974) += gdsc.o
@@ -397,3 +399,5 @@
ifdef CONFIG_MSM_CPR
obj-$(CONFIG_DEBUG_FS) += msm_cpr-debug.o
endif
+obj-$(CONFIG_MSM_FIQ) += msm7k_fiq.o
+obj-$(CONFIG_MSM_FIQ) += msm7k_fiq_handler.o
diff --git a/arch/arm/mach-msm/Makefile.boot b/arch/arm/mach-msm/Makefile.boot
index cf1f401..fa9ee54 100644
--- a/arch/arm/mach-msm/Makefile.boot
+++ b/arch/arm/mach-msm/Makefile.boot
@@ -66,4 +66,4 @@
zreladdr-$(CONFIG_ARCH_MPQ8092) := 0x00008000
# MSM8910
- zreladdr-$(CONFIG_ARCH_MSM8910) := 0x00008000
+ zreladdr-$(CONFIG_ARCH_MSM8910) := 0x80208000
diff --git a/arch/arm/mach-msm/acpuclock-7627.c b/arch/arm/mach-msm/acpuclock-7627.c
index 5c4a923..dd27123 100644
--- a/arch/arm/mach-msm/acpuclock-7627.c
+++ b/arch/arm/mach-msm/acpuclock-7627.c
@@ -85,10 +85,12 @@
};
static struct pll_config pll4_cfg_tbl[] = {
- { 36, 1, 2 }, /* 700.8 MHz */
- { 52, 1, 2 }, /* 1008 MHz */
- { 63, 0, 1 }, /* 1209.6 MHz */
- { 73, 0, 1 }, /* 1401.6 MHz */
+ [0] = { 36, 1, 2 }, /* 700.8 MHz */
+ [1] = { 52, 1, 2 }, /* 1008 MHz */
+ [2] = { 63, 0, 1 }, /* 1209.6 MHz */
+ [3] = { 73, 0, 1 }, /* 1401.6 MHz */
+ [4] = { 60, 0, 1 }, /* 1152 MHz */
+ [5] = { 57, 1, 2 }, /* 1104 MHz */
};
struct clock_state {
@@ -266,8 +268,8 @@
static struct clkctl_acpu_speed pll0_960_pll1_196_pll2_1200_pll4_1209[] = {
{ 0, 19200, ACPU_PLL_TCXO, 0, 0, 2400, 3, 0, 24576 },
{ 0, 65536, ACPU_PLL_1, 1, 3, 8192, 3, 1, 49152 },
- { 0, 98304, ACPU_PLL_1, 1, 1, 12288, 3, 2, 49152 },
- { 1, 196608, ACPU_PLL_1, 1, 0, 24576, 3, 3, 98304 },
+ { 0, 98304, ACPU_PLL_1, 1, 1, 12288, 3, 1, 49152 },
+ { 1, 196608, ACPU_PLL_1, 1, 0, 24576, 3, 1, 98304 },
{ 1, 320000, ACPU_PLL_0, 4, 2, 40000, 3, 2, 122880 },
{ 1, 480000, ACPU_PLL_0, 4, 1, 60000, 3, 3, 122880 },
{ 0, 600000, ACPU_PLL_2, 2, 1, 75000, 3, 4, 160000 },
@@ -298,8 +300,8 @@
static struct clkctl_acpu_speed pll0_960_pll1_196_pll2_1200_pll4_1401[] = {
{ 0, 19200, ACPU_PLL_TCXO, 0, 0, 2400, 3, 0, 24576 },
{ 0, 65536, ACPU_PLL_1, 1, 3, 8192, 3, 1, 49152 },
- { 0, 98304, ACPU_PLL_1, 1, 1, 12288, 3, 2, 49152 },
- { 1, 196608, ACPU_PLL_1, 1, 0, 24576, 3, 3, 98304 },
+ { 0, 98304, ACPU_PLL_1, 1, 1, 12288, 3, 1, 49152 },
+ { 1, 196608, ACPU_PLL_1, 1, 0, 24576, 3, 1, 98304 },
{ 1, 320000, ACPU_PLL_0, 4, 2, 40000, 3, 2, 122880 },
{ 1, 480000, ACPU_PLL_0, 4, 1, 60000, 3, 3, 122880 },
{ 0, 600000, ACPU_PLL_2, 2, 1, 75000, 3, 4, 160000 },
@@ -310,7 +312,7 @@
{ 0 }
};
-/* 8625v2.0 PLL4 @ 1008MHz with GSM capable modem */
+/* 8625 PLL4 @ 1008MHz with GSM capable modem */
static struct clkctl_acpu_speed pll0_960_pll1_245_pll2_1200_pll4_1008_2p0[] = {
{ 0, 19200, ACPU_PLL_TCXO, 0, 0, 2400, 3, 0, 30720 },
{ 0, 61440, ACPU_PLL_1, 1, 3, 7680, 3, 0, 61440 },
@@ -325,12 +327,12 @@
{ 0 }
};
-/* 8625v2.0 PLL4 @ 1008MHz with CDMA capable modem */
+/* 8625 PLL4 @ 1008MHz with CDMA capable modem */
static struct clkctl_acpu_speed pll0_960_pll1_196_pll2_1200_pll4_1008_2p0[] = {
{ 0, 19200, ACPU_PLL_TCXO, 0, 0, 2400, 3, 0, 24576 },
{ 0, 65536, ACPU_PLL_1, 1, 3, 8192, 3, 1, 49152 },
- { 0, 98304, ACPU_PLL_1, 1, 1, 12288, 3, 2, 49152 },
- { 1, 196608, ACPU_PLL_1, 1, 0, 24576, 3, 3, 98304 },
+ { 0, 98304, ACPU_PLL_1, 1, 1, 12288, 3, 1, 49152 },
+ { 1, 196608, ACPU_PLL_1, 1, 0, 24576, 3, 1, 98304 },
{ 1, 320000, ACPU_PLL_0, 4, 2, 40000, 3, 2, 122880 },
{ 1, 480000, ACPU_PLL_0, 4, 1, 60000, 3, 3, 122880 },
{ 0, 600000, ACPU_PLL_2, 2, 1, 75000, 3, 4, 160000 },
@@ -339,35 +341,58 @@
{ 0 }
};
-/* 8625 PLL4 @ 1152MHz with GSM capable modem */
+/* 8625 PLL4 @ 1104MHz with GSM capable modem with v2.0 plan */
+static struct clkctl_acpu_speed pll0_960_pll1_245_pll2_1200_pll4_1104[] = {
+ { 0, 19200, ACPU_PLL_TCXO, 0, 0, 2400, 3, 0, 30720 },
+ { 1, 245760, ACPU_PLL_1, 1, 0, 30720, 3, 1, 61440 },
+ { 1, 320000, ACPU_PLL_0, 4, 2, 40000, 3, 2, 122880 },
+ { 1, 480000, ACPU_PLL_0, 4, 1, 60000, 3, 3, 122880 },
+ { 0, 600000, ACPU_PLL_2, 2, 1, 75000, 3, 4, 160000 },
+ { 1, 700800, ACPU_PLL_4, 6, 0, 87500, 3, 4, 160000, &pll4_cfg_tbl[0]},
+ { 1, 1008000, ACPU_PLL_4, 6, 0, 126000, 3, 5, 200000, &pll4_cfg_tbl[1]},
+ { 1, 1104000, ACPU_PLL_4, 6, 0, 151200, 3, 6, 200000, &pll4_cfg_tbl[5]},
+ { 0 }
+};
+
+/* 8625 PLL4 @ 1104MHz with CDMA capable modem with v2.0 plan */
+static struct clkctl_acpu_speed pll0_960_pll1_196_pll2_1200_pll4_1104[] = {
+ { 0, 19200, ACPU_PLL_TCXO, 0, 0, 2400, 3, 0, 24576 },
+ { 1, 196608, ACPU_PLL_1, 1, 0, 24576, 3, 1, 98304 },
+ { 1, 320000, ACPU_PLL_0, 4, 2, 40000, 3, 2, 122880 },
+ { 1, 480000, ACPU_PLL_0, 4, 1, 60000, 3, 3, 122880 },
+ { 0, 600000, ACPU_PLL_2, 2, 1, 75000, 3, 4, 160000 },
+ { 1, 700800, ACPU_PLL_4, 6, 0, 87500, 3, 4, 160000, &pll4_cfg_tbl[0]},
+ { 1, 1008000, ACPU_PLL_4, 6, 0, 126000, 3, 5, 200000, &pll4_cfg_tbl[1]},
+ { 1, 1104000, ACPU_PLL_4, 6, 0, 151200, 3, 6, 200000, &pll4_cfg_tbl[5]},
+ { 0 }
+};
+
+/* 8625 PLL4 @ 1152MHz with GSM capable modem with v2.0 plan */
static struct clkctl_acpu_speed pll0_960_pll1_245_pll2_1200_pll4_1152[] = {
{ 0, 19200, ACPU_PLL_TCXO, 0, 0, 2400, 3, 0, 30720 },
- { 0, 61440, ACPU_PLL_1, 1, 3, 7680, 3, 1, 61440 },
- { 1, 122880, ACPU_PLL_1, 1, 1, 15360, 3, 2, 61440 },
- { 1, 245760, ACPU_PLL_1, 1, 0, 30720, 3, 3, 61440 },
- { 1, 320000, ACPU_PLL_0, 4, 2, 40000, 3, 4, 122880 },
- { 1, 480000, ACPU_PLL_0, 4, 1, 60000, 3, 5, 122880 },
- { 0, 576000, ACPU_PLL_4, 6, 1, 72000, 3, 6, 160000 },
- { 1, 600000, ACPU_PLL_2, 2, 1, 75000, 3, 6, 160000 },
- { 1, 1152000, ACPU_PLL_4, 6, 0, 144000, 3, 7, 200000},
+ { 1, 245760, ACPU_PLL_1, 1, 0, 30720, 3, 1, 61440 },
+ { 1, 320000, ACPU_PLL_0, 4, 2, 40000, 3, 2, 122880 },
+ { 1, 480000, ACPU_PLL_0, 4, 1, 60000, 3, 3, 122880 },
+ { 0, 600000, ACPU_PLL_2, 2, 1, 75000, 3, 4, 160000 },
+ { 1, 700800, ACPU_PLL_4, 6, 0, 87500, 3, 4, 160000, &pll4_cfg_tbl[0]},
+ { 1, 1008000, ACPU_PLL_4, 6, 0, 126000, 3, 5, 200000, &pll4_cfg_tbl[1]},
+ { 1, 1152000, ACPU_PLL_4, 6, 0, 151200, 3, 6, 200000, &pll4_cfg_tbl[4]},
{ 0 }
};
-/* 8625 PLL4 @ 1115MHz with CDMA capable modem */
+/* 8625 PLL4 @ 1115MHz with CDMA capable modem with v2.0 plan */
static struct clkctl_acpu_speed pll0_960_pll1_196_pll2_1200_pll4_1152[] = {
{ 0, 19200, ACPU_PLL_TCXO, 0, 0, 2400, 3, 0, 24576 },
- { 0, 65536, ACPU_PLL_1, 1, 3, 8192, 3, 1, 49152 },
- { 1, 98304, ACPU_PLL_1, 1, 1, 12288, 3, 2, 49152 },
- { 1, 196608, ACPU_PLL_1, 1, 0, 24576, 3, 3, 98304 },
- { 1, 320000, ACPU_PLL_0, 4, 2, 40000, 3, 4, 122880 },
- { 1, 480000, ACPU_PLL_0, 4, 1, 60000, 3, 5, 122880 },
- { 0, 576000, ACPU_PLL_4, 6, 1, 72000, 3, 6, 160000 },
- { 1, 600000, ACPU_PLL_2, 2, 1, 75000, 3, 6, 160000 },
- { 1, 1152000, ACPU_PLL_4, 6, 0, 144000, 3, 7, 200000},
+ { 1, 196608, ACPU_PLL_1, 1, 0, 24576, 3, 1, 98304 },
+ { 1, 320000, ACPU_PLL_0, 4, 2, 40000, 3, 2, 122880 },
+ { 1, 480000, ACPU_PLL_0, 4, 1, 60000, 3, 3, 122880 },
+ { 0, 600000, ACPU_PLL_2, 2, 1, 75000, 3, 4, 160000 },
+ { 1, 700800, ACPU_PLL_4, 6, 0, 87500, 3, 4, 160000, &pll4_cfg_tbl[0]},
+ { 1, 1008000, ACPU_PLL_4, 6, 0, 126000, 3, 5, 200000, &pll4_cfg_tbl[1]},
+ { 1, 1152000, ACPU_PLL_4, 6, 0, 151200, 3, 6, 200000, &pll4_cfg_tbl[4]},
{ 0 }
};
-
/* 7625a PLL2 @ 1200MHz with GSM capable modem */
static struct clkctl_acpu_speed pll0_960_pll1_245_pll2_1200_25a[] = {
{ 0, 19200, ACPU_PLL_TCXO, 0, 0, 2400, 3, 0, 30720 },
@@ -486,6 +511,8 @@
PLL_CONFIG(960, 196, 1200, 1209),
PLL_CONFIG(960, 245, 1200, 1152),
PLL_CONFIG(960, 196, 1200, 1152),
+ PLL_CONFIG(960, 245, 1200, 1104),
+ PLL_CONFIG(960, 196, 1200, 1104),
PLL_CONFIG(960, 245, 1200, 1401),
PLL_CONFIG(960, 196, 1200, 1401),
{ 0, 0, 0, 0, 0 }
@@ -754,7 +781,7 @@
acpuclk_set_div(tgt_s);
drv_state.current_speed = tgt_s;
/* Re-adjust lpj for the new clock speed. */
- update_jiffies(cpu, cur_s->lpj);
+ update_jiffies(cpu, tgt_s->lpj);
/* Disable the backup PLL */
if ((delta > drv_state.max_speed_delta_khz)
@@ -987,14 +1014,10 @@
/*
* 1008Mhz table selection based on the Lvalue of the PLL
- * is conflicting with the 7627AA and 8625 v1.0 1GHz parts
- * since v2.0 8625 chips are using different clock plan based
- * reprogramming method.
+ * is conflicting with the 7627AA 1GHz parts since 8625 chips
+ * are using different clock plan based reprogramming method.
*/
- if (cpu_is_msm8625() &&
- (SOCINFO_VERSION_MAJOR(socinfo_get_version()) >= 2) &&
- pll_mhz[ACPU_PLL_4] == 1008) {
-
+ if (cpu_is_msm8625() && pll_mhz[ACPU_PLL_4] == 1008) {
if (pll_mhz[ACPU_PLL_2] == 245)
acpu_freq_tbl =
pll0_960_pll1_245_pll2_1200_pll4_1008_2p0;
diff --git a/arch/arm/mach-msm/acpuclock-8064.c b/arch/arm/mach-msm/acpuclock-8064.c
index d1613d9..cda952f 100644
--- a/arch/arm/mach-msm/acpuclock-8064.c
+++ b/arch/arm/mach-msm/acpuclock-8064.c
@@ -132,8 +132,6 @@
[13] = { { 1080000, HFPLL, 1, 0x28 }, 1150000, 1150000, 5 },
[14] = { { 1134000, HFPLL, 1, 0x2A }, 1150000, 1150000, 5 },
[15] = { { 1188000, HFPLL, 1, 0x2C }, 1150000, 1150000, 5 },
- /* L2 Level 16 is for 8064ab only */
- [16] = { { 1242000, HFPLL, 1, 0x2E }, 1150000, 1150000, 5 },
{ }
};
@@ -215,27 +213,17 @@
{ 0, { 0 } }
};
-static struct acpu_level tbl_slow_1p7[] __initdata = {
+static struct acpu_level tbl_PVS0_1700MHz[] __initdata = {
{ 1, { 384000, PLL_8, 0, 0x00 }, L2(0), 950000 },
- { 0, { 432000, HFPLL, 2, 0x20 }, L2(6), 975000 },
{ 1, { 486000, HFPLL, 2, 0x24 }, L2(6), 975000 },
- { 0, { 540000, HFPLL, 2, 0x28 }, L2(6), 1000000 },
{ 1, { 594000, HFPLL, 1, 0x16 }, L2(6), 1000000 },
- { 0, { 648000, HFPLL, 1, 0x18 }, L2(6), 1025000 },
{ 1, { 702000, HFPLL, 1, 0x1A }, L2(6), 1025000 },
- { 0, { 756000, HFPLL, 1, 0x1C }, L2(6), 1075000 },
{ 1, { 810000, HFPLL, 1, 0x1E }, L2(6), 1075000 },
- { 0, { 864000, HFPLL, 1, 0x20 }, L2(6), 1100000 },
{ 1, { 918000, HFPLL, 1, 0x22 }, L2(6), 1100000 },
- { 0, { 972000, HFPLL, 1, 0x24 }, L2(6), 1125000 },
{ 1, { 1026000, HFPLL, 1, 0x26 }, L2(6), 1125000 },
- { 0, { 1080000, HFPLL, 1, 0x28 }, L2(15), 1175000 },
{ 1, { 1134000, HFPLL, 1, 0x2A }, L2(15), 1175000 },
- { 0, { 1188000, HFPLL, 1, 0x2C }, L2(15), 1200000 },
{ 1, { 1242000, HFPLL, 1, 0x2E }, L2(15), 1200000 },
- { 0, { 1296000, HFPLL, 1, 0x30 }, L2(15), 1225000 },
{ 1, { 1350000, HFPLL, 1, 0x32 }, L2(15), 1225000 },
- { 0, { 1404000, HFPLL, 1, 0x34 }, L2(15), 1237500 },
{ 1, { 1458000, HFPLL, 1, 0x36 }, L2(15), 1237500 },
{ 1, { 1512000, HFPLL, 1, 0x38 }, L2(15), 1250000 },
{ 1, { 1566000, HFPLL, 1, 0x3A }, L2(15), 1250000 },
@@ -244,38 +232,136 @@
{ 0, { 0 } }
};
-static struct acpu_level tbl_slow_2p0[] __initdata = {
- { 1, { 384000, PLL_8, 0, 0x00 }, L2(0), 950000 },
- { 0, { 432000, HFPLL, 2, 0x20 }, L2(6), 975000 },
- { 1, { 486000, HFPLL, 2, 0x24 }, L2(6), 975000 },
- { 0, { 540000, HFPLL, 2, 0x28 }, L2(6), 1000000 },
- { 1, { 594000, HFPLL, 1, 0x16 }, L2(6), 1000000 },
- { 0, { 648000, HFPLL, 1, 0x18 }, L2(6), 1025000 },
- { 1, { 702000, HFPLL, 1, 0x1A }, L2(6), 1025000 },
- { 0, { 756000, HFPLL, 1, 0x1C }, L2(6), 1075000 },
- { 1, { 810000, HFPLL, 1, 0x1E }, L2(6), 1075000 },
- { 0, { 864000, HFPLL, 1, 0x20 }, L2(6), 1100000 },
- { 1, { 918000, HFPLL, 1, 0x22 }, L2(6), 1100000 },
- { 0, { 972000, HFPLL, 1, 0x24 }, L2(6), 1125000 },
- { 1, { 1026000, HFPLL, 1, 0x26 }, L2(6), 1125000 },
- { 0, { 1080000, HFPLL, 1, 0x28 }, L2(15), 1175000 },
- { 1, { 1134000, HFPLL, 1, 0x2A }, L2(15), 1175000 },
- { 0, { 1188000, HFPLL, 1, 0x2C }, L2(15), 1200000 },
- { 1, { 1242000, HFPLL, 1, 0x2E }, L2(15), 1200000 },
- { 0, { 1296000, HFPLL, 1, 0x30 }, L2(15), 1225000 },
- { 1, { 1350000, HFPLL, 1, 0x32 }, L2(15), 1225000 },
- { 0, { 1404000, HFPLL, 1, 0x34 }, L2(15), 1237500 },
- { 1, { 1458000, HFPLL, 1, 0x36 }, L2(15), 1237500 },
- { 1, { 1512000, HFPLL, 1, 0x38 }, L2(15), 1250000 },
- { 1, { 1566000, HFPLL, 1, 0x3A }, L2(15), 1250000 },
- { 1, { 1620000, HFPLL, 1, 0x3C }, L2(15), 1250000 },
- { 1, { 1674000, HFPLL, 1, 0x3E }, L2(15), 1250000 },
- { 1, { 1728000, HFPLL, 1, 0x40 }, L2(15), 1250000 },
- { 1, { 1782000, HFPLL, 1, 0x42 }, L2(15), 1250000 },
- { 1, { 1836000, HFPLL, 1, 0x44 }, L2(15), 1250000 },
+static struct acpu_level tbl_PVS0_2000MHz[] __initdata = {
+ { 1, { 384000, PLL_8, 0, 0x00 }, L2(0), 900000 },
+ { 1, { 486000, HFPLL, 2, 0x24 }, L2(6), 900000 },
+ { 1, { 594000, HFPLL, 1, 0x16 }, L2(6), 900000 },
+ { 1, { 702000, HFPLL, 1, 0x1A }, L2(6), 900000 },
+ { 1, { 810000, HFPLL, 1, 0x1E }, L2(6), 912500 },
+ { 1, { 918000, HFPLL, 1, 0x22 }, L2(6), 962500 },
+ { 1, { 1026000, HFPLL, 1, 0x26 }, L2(6), 987500 },
+ { 1, { 1134000, HFPLL, 1, 0x2A }, L2(15), 1012500 },
+ { 1, { 1242000, HFPLL, 1, 0x2E }, L2(15), 1025000 },
+ { 1, { 1350000, HFPLL, 1, 0x32 }, L2(15), 1075000 },
+ { 1, { 1458000, HFPLL, 1, 0x36 }, L2(15), 1112500 },
+ { 1, { 1566000, HFPLL, 1, 0x3A }, L2(15), 1150000 },
+ { 1, { 1674000, HFPLL, 1, 0x3E }, L2(15), 1200000 },
+ { 1, { 1782000, HFPLL, 1, 0x42 }, L2(15), 1262500 },
+ { 1, { 1890000, HFPLL, 1, 0x46 }, L2(15), 1300000 },
+ { 0, { 0 } }
+};
+
+static struct acpu_level tbl_PVS1_2000MHz[] __initdata = {
+ { 1, { 384000, PLL_8, 0, 0x00 }, L2(0), 900000 },
+ { 1, { 486000, HFPLL, 2, 0x24 }, L2(6), 900000 },
+ { 1, { 594000, HFPLL, 1, 0x16 }, L2(6), 900000 },
+ { 1, { 702000, HFPLL, 1, 0x1A }, L2(6), 900000 },
+ { 1, { 810000, HFPLL, 1, 0x1E }, L2(6), 900000 },
+ { 1, { 918000, HFPLL, 1, 0x22 }, L2(6), 962500 },
+ { 1, { 1026000, HFPLL, 1, 0x26 }, L2(6), 987500 },
+ { 1, { 1134000, HFPLL, 1, 0x2A }, L2(15), 1000000 },
+ { 1, { 1242000, HFPLL, 1, 0x2E }, L2(15), 1012500 },
+ { 1, { 1350000, HFPLL, 1, 0x32 }, L2(15), 1062500 },
+ { 1, { 1458000, HFPLL, 1, 0x36 }, L2(15), 1087500 },
+ { 1, { 1566000, HFPLL, 1, 0x3A }, L2(15), 1125000 },
+ { 1, { 1674000, HFPLL, 1, 0x3E }, L2(15), 1187500 },
+ { 1, { 1782000, HFPLL, 1, 0x42 }, L2(15), 1237500 },
+ { 1, { 1890000, HFPLL, 1, 0x46 }, L2(15), 1275000 },
+ { 0, { 0 } }
+};
+
+static struct acpu_level tbl_PVS2_2000MHz[] __initdata = {
+ { 1, { 384000, PLL_8, 0, 0x00 }, L2(0), 900000 },
+ { 1, { 486000, HFPLL, 2, 0x24 }, L2(6), 900000 },
+ { 1, { 594000, HFPLL, 1, 0x16 }, L2(6), 900000 },
+ { 1, { 702000, HFPLL, 1, 0x1A }, L2(6), 900000 },
+ { 1, { 810000, HFPLL, 1, 0x1E }, L2(6), 900000 },
+ { 1, { 918000, HFPLL, 1, 0x22 }, L2(6), 950000 },
+ { 1, { 1026000, HFPLL, 1, 0x26 }, L2(6), 975000 },
+ { 1, { 1134000, HFPLL, 1, 0x2A }, L2(15), 987500 },
+ { 1, { 1242000, HFPLL, 1, 0x2E }, L2(15), 1000000 },
+ { 1, { 1350000, HFPLL, 1, 0x32 }, L2(15), 1050000 },
+ { 1, { 1458000, HFPLL, 1, 0x36 }, L2(15), 1075000 },
+ { 1, { 1566000, HFPLL, 1, 0x3A }, L2(15), 1112500 },
+ { 1, { 1674000, HFPLL, 1, 0x3E }, L2(15), 1162500 },
+ { 1, { 1782000, HFPLL, 1, 0x42 }, L2(15), 1212500 },
{ 1, { 1890000, HFPLL, 1, 0x46 }, L2(15), 1250000 },
- { 1, { 1944000, HFPLL, 1, 0x48 }, L2(15), 1250000 },
- { 1, { 1998000, HFPLL, 1, 0x4A }, L2(15), 1250000 },
+ { 0, { 0 } }
+};
+
+static struct acpu_level tbl_PVS3_2000MHz[] __initdata = {
+ { 1, { 384000, PLL_8, 0, 0x00 }, L2(0), 900000 },
+ { 1, { 486000, HFPLL, 2, 0x24 }, L2(6), 900000 },
+ { 1, { 594000, HFPLL, 1, 0x16 }, L2(6), 900000 },
+ { 1, { 702000, HFPLL, 1, 0x1A }, L2(6), 900000 },
+ { 1, { 810000, HFPLL, 1, 0x1E }, L2(6), 900000 },
+ { 1, { 918000, HFPLL, 1, 0x22 }, L2(6), 925000 },
+ { 1, { 1026000, HFPLL, 1, 0x26 }, L2(6), 950000 },
+ { 1, { 1134000, HFPLL, 1, 0x2A }, L2(15), 962500 },
+ { 1, { 1242000, HFPLL, 1, 0x2E }, L2(15), 975000 },
+ { 1, { 1350000, HFPLL, 1, 0x32 }, L2(15), 1012500 },
+ { 1, { 1458000, HFPLL, 1, 0x36 }, L2(15), 1037500 },
+ { 1, { 1566000, HFPLL, 1, 0x3A }, L2(15), 1075000 },
+ { 1, { 1674000, HFPLL, 1, 0x3E }, L2(15), 1112500 },
+ { 1, { 1782000, HFPLL, 1, 0x42 }, L2(15), 1162500 },
+ { 1, { 1890000, HFPLL, 1, 0x46 }, L2(15), 1200000 },
+ { 0, { 0 } }
+};
+
+static struct acpu_level tbl_PVS4_2000MHz[] __initdata = {
+ { 1, { 384000, PLL_8, 0, 0x00 }, L2(0), 900000 },
+ { 1, { 486000, HFPLL, 2, 0x24 }, L2(6), 900000 },
+ { 1, { 594000, HFPLL, 1, 0x16 }, L2(6), 900000 },
+ { 1, { 702000, HFPLL, 1, 0x1A }, L2(6), 900000 },
+ { 1, { 810000, HFPLL, 1, 0x1E }, L2(6), 900000 },
+ { 1, { 918000, HFPLL, 1, 0x22 }, L2(6), 900000 },
+ { 1, { 1026000, HFPLL, 1, 0x26 }, L2(6), 925000 },
+ { 1, { 1134000, HFPLL, 1, 0x2A }, L2(15), 937500 },
+ { 1, { 1242000, HFPLL, 1, 0x2E }, L2(15), 950000 },
+ { 1, { 1350000, HFPLL, 1, 0x32 }, L2(15), 975000 },
+ { 1, { 1458000, HFPLL, 1, 0x36 }, L2(15), 1000000 },
+ { 1, { 1566000, HFPLL, 1, 0x3A }, L2(15), 1037500 },
+ { 1, { 1674000, HFPLL, 1, 0x3E }, L2(15), 1062500 },
+ { 1, { 1782000, HFPLL, 1, 0x42 }, L2(15), 1112500 },
+ { 1, { 1890000, HFPLL, 1, 0x46 }, L2(15), 1150000 },
+ { 0, { 0 } }
+};
+
+static struct acpu_level tbl_PVS5_2000MHz[] __initdata = {
+ { 1, { 384000, PLL_8, 0, 0x00 }, L2(0), 900000 },
+ { 1, { 486000, HFPLL, 2, 0x24 }, L2(6), 900000 },
+ { 1, { 594000, HFPLL, 1, 0x16 }, L2(6), 900000 },
+ { 1, { 702000, HFPLL, 1, 0x1A }, L2(6), 900000 },
+ { 1, { 810000, HFPLL, 1, 0x1E }, L2(6), 900000 },
+ { 1, { 918000, HFPLL, 1, 0x22 }, L2(6), 900000 },
+ { 1, { 1026000, HFPLL, 1, 0x26 }, L2(6), 925000 },
+ { 1, { 1134000, HFPLL, 1, 0x2A }, L2(15), 937500 },
+ { 1, { 1242000, HFPLL, 1, 0x2E }, L2(15), 950000 },
+ { 1, { 1350000, HFPLL, 1, 0x32 }, L2(15), 962500 },
+ { 1, { 1458000, HFPLL, 1, 0x36 }, L2(15), 987500 },
+ { 1, { 1566000, HFPLL, 1, 0x3A }, L2(15), 1012500 },
+ { 1, { 1674000, HFPLL, 1, 0x3E }, L2(15), 1037500 },
+ { 1, { 1782000, HFPLL, 1, 0x42 }, L2(15), 1087500 },
+ { 1, { 1890000, HFPLL, 1, 0x46 }, L2(15), 1125000 },
+ { 0, { 0 } }
+};
+
+static struct acpu_level tbl_PVS6_2000MHz[] __initdata = {
+ { 1, { 384000, PLL_8, 0, 0x00 }, L2(0), 900000 },
+ { 1, { 486000, HFPLL, 2, 0x24 }, L2(6), 900000 },
+ { 1, { 594000, HFPLL, 1, 0x16 }, L2(6), 900000 },
+ { 1, { 702000, HFPLL, 1, 0x1A }, L2(6), 900000 },
+ { 1, { 810000, HFPLL, 1, 0x1E }, L2(6), 900000 },
+ { 1, { 918000, HFPLL, 1, 0x22 }, L2(6), 900000 },
+ { 1, { 1026000, HFPLL, 1, 0x26 }, L2(6), 925000 },
+ { 1, { 1134000, HFPLL, 1, 0x2A }, L2(15), 937500 },
+ { 1, { 1242000, HFPLL, 1, 0x2E }, L2(15), 950000 },
+ { 1, { 1350000, HFPLL, 1, 0x32 }, L2(15), 962500 },
+ { 1, { 1458000, HFPLL, 1, 0x36 }, L2(15), 975000 },
+ { 1, { 1566000, HFPLL, 1, 0x3A }, L2(15), 1000000 },
+ { 1, { 1674000, HFPLL, 1, 0x3E }, L2(15), 1025000 },
+ { 1, { 1782000, HFPLL, 1, 0x42 }, L2(15), 1062500 },
+ { 1, { 1890000, HFPLL, 1, 0x46 }, L2(15), 1100000 },
{ 0, { 0 } }
};
@@ -285,21 +371,21 @@
[0][PVS_FAST] = {tbl_fast, sizeof(tbl_fast), 25000 },
[0][PVS_FASTER] = {tbl_fast, sizeof(tbl_fast), 25000 },
- [1][0] = { tbl_slow_1p7, sizeof(tbl_slow_1p7), 0 },
- [1][1] = { tbl_slow_1p7, sizeof(tbl_slow_1p7), 0 },
- [1][2] = { tbl_slow_1p7, sizeof(tbl_slow_1p7), 0 },
- [1][3] = { tbl_slow_1p7, sizeof(tbl_slow_1p7), 0 },
- [1][4] = { tbl_slow_1p7, sizeof(tbl_slow_1p7), 0 },
- [1][5] = { tbl_slow_1p7, sizeof(tbl_slow_1p7), 0 },
- [1][6] = { tbl_slow_1p7, sizeof(tbl_slow_1p7), 0 },
+ [1][0] = { tbl_PVS0_1700MHz, sizeof(tbl_PVS0_1700MHz), 0 },
+ [1][1] = { tbl_PVS0_1700MHz, sizeof(tbl_PVS0_1700MHz), 0 },
+ [1][2] = { tbl_PVS0_1700MHz, sizeof(tbl_PVS0_1700MHz), 0 },
+ [1][3] = { tbl_PVS0_1700MHz, sizeof(tbl_PVS0_1700MHz), 0 },
+ [1][4] = { tbl_PVS0_1700MHz, sizeof(tbl_PVS0_1700MHz), 0 },
+ [1][5] = { tbl_PVS0_1700MHz, sizeof(tbl_PVS0_1700MHz), 0 },
+ [1][6] = { tbl_PVS0_1700MHz, sizeof(tbl_PVS0_1700MHz), 0 },
- [2][0] = { tbl_slow_2p0, sizeof(tbl_slow_2p0), 0 },
- [2][1] = { tbl_slow_2p0, sizeof(tbl_slow_2p0), 0 },
- [2][2] = { tbl_slow_2p0, sizeof(tbl_slow_2p0), 0 },
- [2][3] = { tbl_slow_2p0, sizeof(tbl_slow_2p0), 0 },
- [2][4] = { tbl_slow_2p0, sizeof(tbl_slow_2p0), 0 },
- [2][5] = { tbl_slow_2p0, sizeof(tbl_slow_2p0), 0 },
- [2][6] = { tbl_slow_2p0, sizeof(tbl_slow_2p0), 0 },
+ [2][0] = { tbl_PVS0_2000MHz, sizeof(tbl_PVS0_2000MHz), 0 },
+ [2][1] = { tbl_PVS1_2000MHz, sizeof(tbl_PVS1_2000MHz), 0 },
+ [2][2] = { tbl_PVS2_2000MHz, sizeof(tbl_PVS2_2000MHz), 0 },
+ [2][3] = { tbl_PVS3_2000MHz, sizeof(tbl_PVS3_2000MHz), 0 },
+ [2][4] = { tbl_PVS4_2000MHz, sizeof(tbl_PVS4_2000MHz), 0 },
+ [2][5] = { tbl_PVS5_2000MHz, sizeof(tbl_PVS5_2000MHz), 0 },
+ [2][6] = { tbl_PVS6_2000MHz, sizeof(tbl_PVS6_2000MHz), 0 },
};
static struct acpuclk_krait_params acpuclk_8064_params __initdata = {
diff --git a/arch/arm/mach-msm/acpuclock-krait.c b/arch/arm/mach-msm/acpuclock-krait.c
index d38396d..a386e78 100644
--- a/arch/arm/mach-msm/acpuclock-krait.c
+++ b/arch/arm/mach-msm/acpuclock-krait.c
@@ -528,7 +528,7 @@
};
/* Initialize a HFPLL at a given rate and enable it. */
-static void __init hfpll_init(struct scalable *sc,
+static void __cpuinit hfpll_init(struct scalable *sc,
const struct core_speed *tgt_s)
{
dev_dbg(drv.dev, "Initializing HFPLL%d\n", sc - drv.scalable);
diff --git a/arch/arm/mach-msm/board-8064-display.c b/arch/arm/mach-msm/board-8064-display.c
index 56c3241..b717973 100644
--- a/arch/arm/mach-msm/board-8064-display.c
+++ b/arch/arm/mach-msm/board-8064-display.c
@@ -50,6 +50,7 @@
#define MSM_FB_OVERLAY1_WRITEBACK_SIZE (0)
#endif /* CONFIG_FB_MSM_OVERLAY1_WRITEBACK */
+#define AVTIMER_PHYSICAL_ADDRESS 0x28009008
static struct resource msm_fb_resources[] = {
{
@@ -246,7 +247,7 @@
static struct msm_panel_common_pdata mdp_pdata = {
.gpio = MDP_VSYNC_GPIO,
- .mdp_max_clk = 200000000,
+ .mdp_max_clk = 266667000,
.mdp_bus_scale_table = &mdp_bus_scale_pdata,
.mdp_rev = MDP_REV_44,
#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
@@ -255,6 +256,7 @@
.mem_hid = MEMTYPE_EBI1,
#endif
.mdp_iommu_split_domain = 1,
+ .avtimer_phy = AVTIMER_PHYSICAL_ADDRESS,
};
void __init apq8064_mdp_writeback(struct memtype_reserve* reserve_table)
diff --git a/arch/arm/mach-msm/board-8064-pmic.c b/arch/arm/mach-msm/board-8064-pmic.c
index f6423c8..e64a672 100644
--- a/arch/arm/mach-msm/board-8064-pmic.c
+++ b/arch/arm/mach-msm/board-8064-pmic.c
@@ -401,7 +401,8 @@
.uvd_thresh_voltage = 4050,
.alarm_low_mv = 3400,
.alarm_high_mv = 4000,
- .resume_voltage_delta = 100,
+ .resume_voltage_delta = 60,
+ .resume_charge_percent = 99,
.term_current = CHG_TERM_MA,
.cool_temp = 10,
.warm_temp = 40,
diff --git a/arch/arm/mach-msm/board-8064.c b/arch/arm/mach-msm/board-8064.c
index b3add3b..9e5e4f5 100644
--- a/arch/arm/mach-msm/board-8064.c
+++ b/arch/arm/mach-msm/board-8064.c
@@ -450,59 +450,27 @@
{
#if defined(CONFIG_ION_MSM) && defined(CONFIG_MSM_MULTIMEDIA_USE_ION)
unsigned int i;
- unsigned int reusable_count = 0;
unsigned int fixed_size = 0;
unsigned int fixed_low_size, fixed_middle_size, fixed_high_size;
unsigned long fixed_low_start, fixed_middle_start, fixed_high_start;
- apq8064_fmem_pdata.size = 0;
- apq8064_fmem_pdata.reserved_size_low = 0;
- apq8064_fmem_pdata.reserved_size_high = 0;
- apq8064_fmem_pdata.align = PAGE_SIZE;
fixed_low_size = 0;
fixed_middle_size = 0;
fixed_high_size = 0;
- /* We only support 1 reusable heap. Check if more than one heap
- * is specified as reusable and set as non-reusable if found.
- */
- for (i = 0; i < apq8064_ion_pdata.nr; ++i) {
- const struct ion_platform_heap *heap =
- &(apq8064_ion_pdata.heaps[i]);
-
- if (heap->type == (enum ion_heap_type) ION_HEAP_TYPE_CP
- && heap->extra_data) {
- struct ion_cp_heap_pdata *data = heap->extra_data;
-
- reusable_count += (data->reusable) ? 1 : 0;
-
- if (data->reusable && reusable_count > 1) {
- pr_err("%s: Too many heaps specified as "
- "reusable. Heap %s was not configured "
- "as reusable.\n", __func__, heap->name);
- data->reusable = 0;
- }
- }
- }
-
for (i = 0; i < apq8064_ion_pdata.nr; ++i) {
const struct ion_platform_heap *heap =
&(apq8064_ion_pdata.heaps[i]);
if (heap->extra_data) {
int fixed_position = NOT_FIXED;
- int mem_is_fmem = 0;
switch ((int)heap->type) {
case ION_HEAP_TYPE_CP:
- mem_is_fmem = ((struct ion_cp_heap_pdata *)
- heap->extra_data)->mem_is_fmem;
fixed_position = ((struct ion_cp_heap_pdata *)
heap->extra_data)->fixed_position;
break;
case ION_HEAP_TYPE_CARVEOUT:
- mem_is_fmem = ((struct ion_co_heap_pdata *)
- heap->extra_data)->mem_is_fmem;
fixed_position = ((struct ion_co_heap_pdata *)
heap->extra_data)->fixed_position;
break;
@@ -521,21 +489,12 @@
fixed_middle_size += heap->size;
else if (fixed_position == FIXED_HIGH)
fixed_high_size += heap->size;
-
- if (mem_is_fmem)
- apq8064_fmem_pdata.size += heap->size;
}
}
if (!fixed_size)
return;
- if (apq8064_fmem_pdata.size) {
- apq8064_fmem_pdata.reserved_size_low = fixed_low_size +
- HOLE_SIZE;
- apq8064_fmem_pdata.reserved_size_high = fixed_high_size;
- }
-
/* Since the fixed area may be carved out of lowmem,
* make sure the length is a multiple of 1M.
*/
@@ -709,19 +668,6 @@
apq8064_set_display_params(prim_panel_name, ext_panel_name,
ext_resolution);
msm_reserve();
- if (apq8064_fmem_pdata.size) {
-#if defined(CONFIG_ION_MSM) && defined(CONFIG_MSM_MULTIMEDIA_USE_ION)
- if (reserve_info->fixed_area_size) {
- apq8064_fmem_pdata.phys =
- reserve_info->fixed_area_start + MSM_MM_FW_SIZE;
- pr_info("mm fw at %lx (fixed) size %x\n",
- reserve_info->fixed_area_start, MSM_MM_FW_SIZE);
- pr_info("fmem start %lx (fixed) size %lx\n",
- apq8064_fmem_pdata.phys,
- apq8064_fmem_pdata.size);
- }
-#endif
- }
}
static void __init place_movable_zone(void)
@@ -2436,6 +2382,7 @@
static struct gpio_ir_recv_platform_data gpio_ir_recv_pdata = {
.gpio_nr = 88,
.active_low = 1,
+ .can_wakeup = true,
};
static struct platform_device gpio_ir_recv_pdev = {
@@ -3453,6 +3400,8 @@
apq8064_common_init();
if (machine_is_mpq8064_cdp() || machine_is_mpq8064_hrd() ||
machine_is_mpq8064_dtv()) {
+ gpio_ir_recv_pdata.swfi_latency =
+ msm_rpmrs_levels[0].latency_us;
enable_avc_i2c_bus();
msm_rotator_set_split_iommu_domain();
platform_add_devices(mpq_devices, ARRAY_SIZE(mpq_devices));
diff --git a/arch/arm/mach-msm/board-8092.c b/arch/arm/mach-msm/board-8092.c
index 0471ff4..bd1762d 100644
--- a/arch/arm/mach-msm/board-8092.c
+++ b/arch/arm/mach-msm/board-8092.c
@@ -23,7 +23,8 @@
#include <asm/mach/time.h>
#include <mach/socinfo.h>
#include <mach/board.h>
-
+#include <mach/msm_memtypes.h>
+#include <mach/qpnp-int.h>
#include <linux/io.h>
#include <linux/gpio.h>
#include <linux/irq.h>
@@ -34,6 +35,7 @@
static struct of_device_id irq_match[] __initdata = {
{ .compatible = "qcom,msm-qgic2", .data = gic_of_init, },
{ .compatible = "qcom,msm-gpio", .data = msm_gpio_of_init, },
+ { .compatible = "qcom,spmi-pmic-arb", .data = qpnpint_of_init, },
{}
};
@@ -47,6 +49,38 @@
.size = ARRAY_SIZE(msm_clocks_dummy),
};
+static struct memtype_reserve mpq8092_reserve_table[] __initdata = {
+ [MEMTYPE_SMI] = {
+ },
+ [MEMTYPE_EBI0] = {
+ .flags = MEMTYPE_FLAGS_1M_ALIGN,
+ },
+ [MEMTYPE_EBI1] = {
+ .flags = MEMTYPE_FLAGS_1M_ALIGN,
+ },
+};
+
+static int mpq8092_paddr_to_memtype(unsigned int paddr)
+{
+ return MEMTYPE_EBI1;
+}
+
+static struct reserve_info mpq8092_reserve_info __initdata = {
+ .memtype_reserve_table = mpq8092_reserve_table,
+ .paddr_to_memtype = mpq8092_paddr_to_memtype,
+};
+
+static void __init mpq8092_early_memory(void)
+{
+ reserve_info = &mpq8092_reserve_info;
+ of_scan_flat_dt(dt_scan_for_memory_reserve, mpq8092_reserve_table);
+}
+
+static void __init mpq8092_dt_reserve(void)
+{
+ msm_reserve();
+}
+
void __init mpq8092_init_irq(void)
{
of_irq_init(irq_match);
@@ -77,6 +111,8 @@
static struct of_dev_auxdata mpq8092_auxdata_lookup[] __initdata = {
OF_DEV_AUXDATA("qcom,msm-lsuart-v14", 0xF991F000, \
"msm_serial_hsl.0", NULL),
+ OF_DEV_AUXDATA("qcom,spmi-pmic-arb", 0xFC4C0000, \
+ "spmi-pmic-arb.0", NULL),
{}
};
@@ -107,4 +143,6 @@
.handle_irq = gic_handle_irq,
.timer = &mpq8092_dt_timer,
.dt_compat = mpq8092_dt_match,
+ .reserve = mpq8092_dt_reserve,
+ .init_very_early = mpq8092_early_memory,
MACHINE_END
diff --git a/arch/arm/mach-msm/board-8226.c b/arch/arm/mach-msm/board-8226.c
index 767736f..b27382f 100644
--- a/arch/arm/mach-msm/board-8226.c
+++ b/arch/arm/mach-msm/board-8226.c
@@ -44,10 +44,10 @@
#include "clock.h"
static struct clk_lookup msm_clocks_dummy[] = {
- CLK_DUMMY("core_clk", BLSP1_UART_CLK, "msm_serial_hsl.0", OFF),
- CLK_DUMMY("iface_clk", BLSP1_UART_CLK, "msm_serial_hsl.0", OFF),
- CLK_DUMMY("iface_clk", HSUSB_IFACE_CLK, "msm_otg", OFF),
- CLK_DUMMY("core_clk", HSUSB_CORE_CLK, "msm_otg", OFF),
+ CLK_DUMMY("core_clk", BLSP1_UART_CLK, "f991f000.serial", OFF),
+ CLK_DUMMY("iface_clk", BLSP1_UART_CLK, "f991f000.serial", OFF),
+ CLK_DUMMY("iface_clk", HSUSB_IFACE_CLK, "f9a55000.usb", OFF),
+ CLK_DUMMY("core_clk", HSUSB_CORE_CLK, "f9a55000.usb", OFF),
};
struct clock_init_data msm_dummy_clock_init_data __initdata = {
@@ -61,14 +61,6 @@
{}
};
-static struct of_dev_auxdata msm8226_auxdata_lookup[] __initdata = {
- OF_DEV_AUXDATA("qcom,msm-lsuart-v14", 0xF991F000, \
- "msm_serial_hsl.0", NULL),
- OF_DEV_AUXDATA("qcom,hsusb-otg", 0xF9A55000, \
- "msm_otg", NULL),
- {}
-};
-
static void __init msm8226_dt_timer_init(void)
{
arch_timer_of_register();
@@ -83,24 +75,21 @@
of_irq_init(irq_match);
}
-void __init msm8226_init(struct of_dev_auxdata **adata)
+void __init msm8226_init(void)
{
msm8226_init_gpiomux();
msm_clock_init(&msm_dummy_clock_init_data);
-
- *adata = msm8226_auxdata_lookup;
}
void __init msm8226_dt_init(void)
{
- struct of_dev_auxdata *adata = NULL;
- msm8226_init(&adata);
+ msm8226_init();
if (socinfo_init() < 0)
pr_err("%s: socinfo_init() failed\n", __func__);
- of_platform_populate(NULL, of_default_bus_match_table, adata, NULL);
+ of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
}
diff --git a/arch/arm/mach-msm/board-8910.c b/arch/arm/mach-msm/board-8910.c
index 18463e3..1c92494f 100644
--- a/arch/arm/mach-msm/board-8910.c
+++ b/arch/arm/mach-msm/board-8910.c
@@ -42,6 +42,8 @@
static struct clk_lookup msm_clocks_dummy[] = {
CLK_DUMMY("core_clk", BLSP1_UART_CLK, "f991f000.serial", OFF),
CLK_DUMMY("iface_clk", BLSP1_UART_CLK, "f991f000.serial", OFF),
+ CLK_DUMMY("iface_clk", HSUSB_IFACE_CLK, "f9a55000.usb", OFF),
+ CLK_DUMMY("core_clk", HSUSB_CORE_CLK, "f9a55000.usb", OFF),
};
struct clock_init_data msm_dummy_clock_init_data __initdata = {
diff --git a/arch/arm/mach-msm/board-8930-gpiomux.c b/arch/arm/mach-msm/board-8930-gpiomux.c
index fcb5abd..cf44e08 100644
--- a/arch/arm/mach-msm/board-8930-gpiomux.c
+++ b/arch/arm/mach-msm/board-8930-gpiomux.c
@@ -69,7 +69,7 @@
static struct gpiomux_setting cdc_mclk = {
.func = GPIOMUX_FUNC_1,
- .drv = GPIOMUX_DRV_8MA,
+ .drv = GPIOMUX_DRV_2MA,
.pull = GPIOMUX_PULL_NONE,
};
diff --git a/arch/arm/mach-msm/board-8930-pmic.c b/arch/arm/mach-msm/board-8930-pmic.c
index 402aec4..8687b2a 100644
--- a/arch/arm/mach-msm/board-8930-pmic.c
+++ b/arch/arm/mach-msm/board-8930-pmic.c
@@ -322,7 +322,8 @@
.uvd_thresh_voltage = 4050,
.alarm_low_mv = 3400,
.alarm_high_mv = 4000,
- .resume_voltage_delta = 100,
+ .resume_voltage_delta = 60,
+ .resume_charge_percent = 99,
.term_current = CHG_TERM_MA,
.cool_temp = 10,
.warm_temp = 40,
diff --git a/arch/arm/mach-msm/board-8930.c b/arch/arm/mach-msm/board-8930.c
index f0f59ec..05d2fe1 100644
--- a/arch/arm/mach-msm/board-8930.c
+++ b/arch/arm/mach-msm/board-8930.c
@@ -498,59 +498,27 @@
{
#if defined(CONFIG_ION_MSM) && defined(CONFIG_MSM_MULTIMEDIA_USE_ION)
unsigned int i;
- unsigned int reusable_count = 0;
unsigned int fixed_size = 0;
unsigned int fixed_low_size, fixed_middle_size, fixed_high_size;
unsigned long fixed_low_start, fixed_middle_start, fixed_high_start;
- msm8930_fmem_pdata.size = 0;
- msm8930_fmem_pdata.reserved_size_low = 0;
- msm8930_fmem_pdata.reserved_size_high = 0;
- msm8930_fmem_pdata.align = PAGE_SIZE;
fixed_low_size = 0;
fixed_middle_size = 0;
fixed_high_size = 0;
- /* We only support 1 reusable heap. Check if more than one heap
- * is specified as reusable and set as non-reusable if found.
- */
- for (i = 0; i < msm8930_ion_pdata.nr; ++i) {
- const struct ion_platform_heap *heap =
- &(msm8930_ion_pdata.heaps[i]);
-
- if (heap->type == (enum ion_heap_type) ION_HEAP_TYPE_CP
- && heap->extra_data) {
- struct ion_cp_heap_pdata *data = heap->extra_data;
-
- reusable_count += (data->reusable) ? 1 : 0;
-
- if (data->reusable && reusable_count > 1) {
- pr_err("%s: Too many heaps specified as "
- "reusable. Heap %s was not configured "
- "as reusable.\n", __func__, heap->name);
- data->reusable = 0;
- }
- }
- }
-
for (i = 0; i < msm8930_ion_pdata.nr; ++i) {
const struct ion_platform_heap *heap =
&(msm8930_ion_pdata.heaps[i]);
if (heap->extra_data) {
int fixed_position = NOT_FIXED;
- int mem_is_fmem = 0;
switch ((int) heap->type) {
case ION_HEAP_TYPE_CP:
- mem_is_fmem = ((struct ion_cp_heap_pdata *)
- heap->extra_data)->mem_is_fmem;
fixed_position = ((struct ion_cp_heap_pdata *)
heap->extra_data)->fixed_position;
break;
case ION_HEAP_TYPE_CARVEOUT:
- mem_is_fmem = ((struct ion_co_heap_pdata *)
- heap->extra_data)->mem_is_fmem;
fixed_position = ((struct ion_co_heap_pdata *)
heap->extra_data)->fixed_position;
break;
@@ -570,20 +538,12 @@
else if (fixed_position == FIXED_HIGH)
fixed_high_size += heap->size;
- if (mem_is_fmem)
- msm8930_fmem_pdata.size += heap->size;
}
}
if (!fixed_size)
return;
- if (msm8930_fmem_pdata.size) {
- msm8930_fmem_pdata.reserved_size_low = fixed_low_size +
- HOLE_SIZE;
- msm8930_fmem_pdata.reserved_size_high = fixed_high_size;
- }
-
/* Since the fixed area may be carved out of lowmem,
* make sure the length is a multiple of 1M.
*/
@@ -755,18 +715,6 @@
{
msm8930_set_display_params(prim_panel_name, ext_panel_name);
msm_reserve();
- if (msm8930_fmem_pdata.size) {
-#if defined(CONFIG_ION_MSM) && defined(CONFIG_MSM_MULTIMEDIA_USE_ION)
- if (reserve_info->fixed_area_size) {
- msm8930_fmem_pdata.phys =
- reserve_info->fixed_area_start + MSM_MM_FW_SIZE;
- pr_info("mm fw at %lx (fixed) size %x\n",
- reserve_info->fixed_area_start, MSM_MM_FW_SIZE);
- pr_info("fmem start %lx (fixed) size %lx\n",
- msm8930_fmem_pdata.phys, msm8930_fmem_pdata.size);
- }
-#endif
- }
}
static int msm8930_change_memory_power(u64 start, u64 size,
diff --git a/arch/arm/mach-msm/board-8960-gpiomux.c b/arch/arm/mach-msm/board-8960-gpiomux.c
index 1771bb9..fe37f2a 100644
--- a/arch/arm/mach-msm/board-8960-gpiomux.c
+++ b/arch/arm/mach-msm/board-8960-gpiomux.c
@@ -449,6 +449,13 @@
},
},
{
+ .gpio = 26, /* GSBI6 WLAN_PWD_L for AR6004 */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &gsbi6_suspended_cfg,
+ [GPIOMUX_ACTIVE] = &gsbi6_active_cfg,
+ },
+ },
+ {
.gpio = 27, /* GSBI6 BT_INT2AP_N for AR3002 */
.settings = {
[GPIOMUX_SUSPENDED] = &gsbi6_suspended_cfg,
diff --git a/arch/arm/mach-msm/board-8960-pmic.c b/arch/arm/mach-msm/board-8960-pmic.c
index f6c3653..d8a260b 100644
--- a/arch/arm/mach-msm/board-8960-pmic.c
+++ b/arch/arm/mach-msm/board-8960-pmic.c
@@ -403,7 +403,8 @@
.uvd_thresh_voltage = 4050,
.alarm_low_mv = 3400,
.alarm_high_mv = 4000,
- .resume_voltage_delta = 100,
+ .resume_voltage_delta = 60,
+ .resume_charge_percent = 99,
.term_current = CHG_TERM_MA,
.cool_temp = 10,
.warm_temp = 40,
diff --git a/arch/arm/mach-msm/board-8960-storage.c b/arch/arm/mach-msm/board-8960-storage.c
index 67f44aa..ded5bad 100644
--- a/arch/arm/mach-msm/board-8960-storage.c
+++ b/arch/arm/mach-msm/board-8960-storage.c
@@ -327,9 +327,11 @@
#endif
.vreg_data = &mmc_slot_vreg_data[SDCC3],
.pin_data = &mmc_slot_pin_data[SDCC3],
+#ifndef CONFIG_MMC_MSM_SDC3_POLLING
.status_gpio = PM8921_GPIO_PM_TO_SYS(26),
.status_irq = PM8921_GPIO_IRQ(PM8921_IRQ_BASE, 26),
.irq_flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+#endif
.is_status_gpio_active_low = true,
.xpc_cap = 1,
.uhs_caps = (MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 |
diff --git a/arch/arm/mach-msm/board-8960.c b/arch/arm/mach-msm/board-8960.c
index 7e96edf..4a78e31 100644
--- a/arch/arm/mach-msm/board-8960.c
+++ b/arch/arm/mach-msm/board-8960.c
@@ -103,6 +103,11 @@
#include "pm-boot.h"
#include "msm_watchdog.h"
+#if defined(CONFIG_BT) && defined(CONFIG_BT_HCIUART_ATH3K)
+#include <linux/wlan_plat.h>
+#include <linux/mutex.h>
+#endif
+
static struct platform_device msm_fm_platform_init = {
.name = "iris_fm",
.id = -1,
@@ -541,42 +546,15 @@
{
#if defined(CONFIG_ION_MSM) && defined(CONFIG_MSM_MULTIMEDIA_USE_ION)
unsigned int i;
- unsigned int reusable_count = 0;
unsigned int fixed_size = 0;
unsigned int fixed_low_size, fixed_middle_size, fixed_high_size;
unsigned long fixed_low_start, fixed_middle_start, fixed_high_start;
adjust_mem_for_liquid();
- msm8960_fmem_pdata.size = 0;
- msm8960_fmem_pdata.reserved_size_low = 0;
- msm8960_fmem_pdata.reserved_size_high = 0;
- msm8960_fmem_pdata.align = PAGE_SIZE;
fixed_low_size = 0;
fixed_middle_size = 0;
fixed_high_size = 0;
- /* We only support 1 reusable heap. Check if more than one heap
- * is specified as reusable and set as non-reusable if found.
- */
- for (i = 0; i < msm8960_ion_pdata.nr; ++i) {
- const struct ion_platform_heap *heap =
- &(msm8960_ion_pdata.heaps[i]);
-
- if (heap->type == (enum ion_heap_type) ION_HEAP_TYPE_CP
- && heap->extra_data) {
- struct ion_cp_heap_pdata *data = heap->extra_data;
-
- reusable_count += (data->reusable) ? 1 : 0;
-
- if (data->reusable && reusable_count > 1) {
- pr_err("%s: Too many heaps specified as "
- "reusable. Heap %s was not configured "
- "as reusable.\n", __func__, heap->name);
- data->reusable = 0;
- }
- }
- }
-
for (i = 0; i < msm8960_ion_pdata.nr; ++i) {
struct ion_platform_heap *heap =
&(msm8960_ion_pdata.heaps[i]);
@@ -586,12 +564,9 @@
if (heap->extra_data) {
int fixed_position = NOT_FIXED;
- int mem_is_fmem = 0;
switch ((int) heap->type) {
case ION_HEAP_TYPE_CP:
- mem_is_fmem = ((struct ion_cp_heap_pdata *)
- heap->extra_data)->mem_is_fmem;
fixed_position = ((struct ion_cp_heap_pdata *)
heap->extra_data)->fixed_position;
align = ((struct ion_cp_heap_pdata *)
@@ -601,8 +576,6 @@
heap->extra_data)->iommu_map_all;
break;
case ION_HEAP_TYPE_CARVEOUT:
- mem_is_fmem = ((struct ion_co_heap_pdata *)
- heap->extra_data)->mem_is_fmem;
fixed_position = ((struct ion_co_heap_pdata *)
heap->extra_data)->fixed_position;
adjacent_mem_id = ((struct ion_co_heap_pdata *)
@@ -620,9 +593,6 @@
}
}
- if (mem_is_fmem && adjacent_mem_id != INVALID_HEAP_ID)
- msm8960_fmem_pdata.align = align;
-
if (fixed_position != NOT_FIXED)
fixed_size += heap->size;
else
@@ -634,21 +604,12 @@
fixed_middle_size += heap->size;
else if (fixed_position == FIXED_HIGH)
fixed_high_size += heap->size;
-
- if (mem_is_fmem)
- msm8960_fmem_pdata.size += heap->size;
}
}
if (!fixed_size)
return;
- if (msm8960_fmem_pdata.size) {
- msm8960_fmem_pdata.reserved_size_low = fixed_low_size +
- HOLE_SIZE;
- msm8960_fmem_pdata.reserved_size_high = fixed_high_size;
- }
-
/* Since the fixed area may be carved out of lowmem,
* make sure the length is a multiple of 1M.
*/
@@ -818,19 +779,6 @@
{
msm8960_set_display_params(prim_panel_name, ext_panel_name);
msm_reserve();
- if (msm8960_fmem_pdata.size) {
-#if defined(CONFIG_ION_MSM) && defined(CONFIG_MSM_MULTIMEDIA_USE_ION)
- if (reserve_info->fixed_area_size) {
- msm8960_fmem_pdata.phys =
- reserve_info->fixed_area_start + MSM_MM_FW_SIZE;
- pr_info("mm fw at %lx (fixed) size %x\n",
- reserve_info->fixed_area_start, MSM_MM_FW_SIZE);
- pr_info("fmem start %lx (fixed) size %lx\n",
- msm8960_fmem_pdata.phys,
- msm8960_fmem_pdata.size);
- }
-#endif
- }
}
static int msm8960_change_memory_power(u64 start, u64 size,
@@ -2602,6 +2550,76 @@
#endif
#if defined(CONFIG_BT) && defined(CONFIG_BT_HCIUART_ATH3K)
+enum WLANBT_STATUS {
+ WLANOFF_BTOFF = 1,
+ WLANOFF_BTON,
+ WLANON_BTOFF,
+ WLANON_BTON
+};
+
+static DEFINE_MUTEX(ath_wlanbt_mutex);
+static int gpio_wlan_sys_rest_en = 26;
+static int ath_wlanbt_status = WLANOFF_BTOFF;
+
+static int ath6kl_power_control(int on)
+{
+ int rc;
+
+ if (on) {
+ rc = gpio_request(gpio_wlan_sys_rest_en, "wlan sys_rst_n");
+ if (rc) {
+ pr_err("%s: unable to request gpio %d (%d)\n",
+ __func__, gpio_wlan_sys_rest_en, rc);
+ return rc;
+ }
+ rc = gpio_direction_output(gpio_wlan_sys_rest_en, 0);
+ msleep(200);
+ rc = gpio_direction_output(gpio_wlan_sys_rest_en, 1);
+ msleep(100);
+ } else {
+ gpio_set_value(gpio_wlan_sys_rest_en, 0);
+ rc = gpio_direction_input(gpio_wlan_sys_rest_en);
+ msleep(100);
+ gpio_free(gpio_wlan_sys_rest_en);
+ }
+ return 0;
+};
+
+static int ath6kl_wlan_power(int on)
+{
+ int ret = 0;
+
+ mutex_lock(&ath_wlanbt_mutex);
+ if (on) {
+ if (ath_wlanbt_status == WLANOFF_BTOFF) {
+ ret = ath6kl_power_control(1);
+ ath_wlanbt_status = WLANON_BTOFF;
+ } else if (ath_wlanbt_status == WLANOFF_BTON)
+ ath_wlanbt_status = WLANON_BTON;
+ } else {
+ if (ath_wlanbt_status == WLANON_BTOFF) {
+ ret = ath6kl_power_control(0);
+ ath_wlanbt_status = WLANOFF_BTOFF;
+ } else if (ath_wlanbt_status == WLANON_BTON)
+ ath_wlanbt_status = WLANOFF_BTON;
+ }
+ mutex_unlock(&ath_wlanbt_mutex);
+ pr_debug("%s on= %d, wlan_status= %d\n",
+ __func__, on, ath_wlanbt_status);
+ return ret;
+};
+
+static struct wifi_platform_data ath6kl_wifi_control = {
+ .set_power = ath6kl_wlan_power,
+};
+
+static struct platform_device msm_wlan_power_device = {
+ .name = "ath6kl_power",
+ .dev = {
+ .platform_data = &ath6kl_wifi_control,
+ },
+};
+
static struct resource bluesleep_resources[] = {
{
.name = "gpio_host_wake",
@@ -2634,50 +2652,54 @@
.name = "bt_power",
};
-int gpio_bt_sys_rest_en = 28;
+static int gpio_bt_sys_rest_en = 28;
static int bluetooth_power(int on)
{
int rc;
- pr_debug("%s on= %d\n", __func__, on);
-
+ mutex_lock(&ath_wlanbt_mutex);
if (on) {
+ if (ath_wlanbt_status == WLANOFF_BTOFF) {
+ ath6kl_power_control(1);
+ ath_wlanbt_status = WLANOFF_BTON;
+ } else if (ath_wlanbt_status == WLANON_BTOFF)
+ ath_wlanbt_status = WLANON_BTON;
+
rc = gpio_request(gpio_bt_sys_rest_en, "bt sys_rst_n");
if (rc) {
pr_err("%s: unable to request gpio %d (%d)\n",
__func__, gpio_bt_sys_rest_en, rc);
- goto out;
+ mutex_unlock(&ath_wlanbt_mutex);
+ return rc;
}
rc = gpio_direction_output(gpio_bt_sys_rest_en, 0);
- if (rc) {
- pr_err("%s: Unable to set gpio %d direction\n",
- __func__, gpio_bt_sys_rest_en);
- goto free_gpio;
- }
+ msleep(20);
+ rc = gpio_direction_output(gpio_bt_sys_rest_en, 1);
msleep(100);
- gpio_set_value(gpio_bt_sys_rest_en, 1);
- msleep(100);
- goto out;
} else {
gpio_set_value(gpio_bt_sys_rest_en, 0);
rc = gpio_direction_input(gpio_bt_sys_rest_en);
msleep(100);
- }
+ gpio_free(gpio_bt_sys_rest_en);
-free_gpio:
- gpio_free(gpio_bt_sys_rest_en);
-out:
- return rc;
-}
+ if (ath_wlanbt_status == WLANOFF_BTON) {
+ ath6kl_power_control(0);
+ ath_wlanbt_status = WLANOFF_BTOFF;
+ } else if (ath_wlanbt_status == WLANON_BTON)
+ ath_wlanbt_status = WLANON_BTOFF;
+ }
+ mutex_unlock(&ath_wlanbt_mutex);
+ pr_debug("%s on= %d, wlan_status= %d\n",
+ __func__, on, ath_wlanbt_status);
+ return 0;
+};
static void __init bt_power_init(void)
{
- pr_debug("%s enter\n", __func__);
msm_bt_power_device.dev.platform_data = &bluetooth_power;
-
return;
-}
+};
#else
#define bt_power_init(x) do {} while (0)
#endif
@@ -2703,6 +2725,7 @@
#if defined(CONFIG_BT) && defined(CONFIG_BT_HCIUART_ATH3K)
&msm_bluesleep_device,
&msm_bt_power_device,
+ &msm_wlan_power_device,
#endif
#if defined(CONFIG_QSEECOM)
&qseecom_device,
diff --git a/arch/arm/mach-msm/board-8974.c b/arch/arm/mach-msm/board-8974.c
index 7fe45b8..7480437 100644
--- a/arch/arm/mach-msm/board-8974.c
+++ b/arch/arm/mach-msm/board-8974.c
@@ -13,13 +13,11 @@
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/io.h>
-#include <linux/gpio.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
-#include <linux/of_irq.h>
#include <linux/memory.h>
#ifdef CONFIG_ANDROID_PMEM
#include <linux/android_pmem.h>
@@ -27,9 +25,10 @@
#include <linux/regulator/machine.h>
#include <linux/regulator/krait-regulator.h>
#include <linux/msm_thermal.h>
-#include <linux/mfd/wcd9xxx/core.h>
#include <asm/mach/map.h>
#include <asm/hardware/gic.h>
+#include <asm/mach/map.h>
+#include <asm/mach/arch.h>
#include <mach/board.h>
#include <mach/gpiomux.h>
#include <mach/msm_iomap.h>
@@ -38,12 +37,12 @@
#endif
#include <mach/msm_memtypes.h>
#include <mach/msm_smd.h>
+#include <mach/restart.h>
#include <mach/rpm-smd.h>
#include <mach/rpm-regulator-smd.h>
-#include <mach/qpnp-int.h>
#include <mach/socinfo.h>
#include <mach/msm_bus_board.h>
-#include <mach/mpm.h>
+#include "board-dt.h"
#include "clock.h"
#include "devices.h"
#include "spm.h"
@@ -62,7 +61,7 @@
early_param("kernel_ebi1_mem_size", kernel_ebi1_mem_size_setup);
#endif
-static struct memtype_reserve msm_8974_reserve_table[] __initdata = {
+static struct memtype_reserve msm8974_reserve_table[] __initdata = {
[MEMTYPE_SMI] = {
},
[MEMTYPE_EBI0] = {
@@ -73,7 +72,7 @@
},
};
-static int msm_8974_paddr_to_memtype(unsigned int paddr)
+static int msm8974_paddr_to_memtype(unsigned int paddr)
{
return MEMTYPE_EBI1;
}
@@ -81,7 +80,7 @@
static void __init reserve_ebi_memory(void)
{
#ifdef CONFIG_KERNEL_PMEM_EBI_REGION
- msm_8974_reserve_table[MEMTYPE_EBI1].size += kernel_ebi1_mem_size;
+ msm8974_reserve_table[MEMTYPE_EBI1].size += kernel_ebi1_mem_size;
#endif
}
@@ -247,26 +246,21 @@
}
};
-static void __init msm_8974_calculate_reserve_sizes(void)
+static void __init msm8974_calculate_reserve_sizes(void)
{
reserve_ebi_memory();
}
-static struct reserve_info msm_8974_reserve_info __initdata = {
- .memtype_reserve_table = msm_8974_reserve_table,
- .calculate_reserve_sizes = msm_8974_calculate_reserve_sizes,
- .paddr_to_memtype = msm_8974_paddr_to_memtype,
+static struct reserve_info msm8974_reserve_info __initdata = {
+ .memtype_reserve_table = msm8974_reserve_table,
+ .calculate_reserve_sizes = msm8974_calculate_reserve_sizes,
+ .paddr_to_memtype = msm8974_paddr_to_memtype,
};
-static void __init msm_8974_early_memory(void)
+static void __init msm8974_early_memory(void)
{
- reserve_info = &msm_8974_reserve_info;
- of_scan_flat_dt(dt_scan_for_memory_reserve, msm_8974_reserve_table);
-}
-
-void __init msm_8974_reserve(void)
-{
- msm_reserve();
+ reserve_info = &msm8974_reserve_info;
+ of_scan_flat_dt(dt_scan_for_memory_reserve, msm8974_reserve_table);
}
#define BIMC_BASE 0xfc380000
@@ -456,7 +450,7 @@
ARRAY_SIZE(msm_bus_8974_devices));
};
-void __init msm_8974_add_devices(void)
+void __init msm8974_add_devices(void)
{
platform_device_register(&msm_device_smd_8974);
}
@@ -467,7 +461,7 @@
* into this category, and thus the driver should not be added here. The
* EPROBE_DEFER can satisfy most dependency problems.
*/
-void __init msm_8974_add_drivers(void)
+void __init msm8974_add_drivers(void)
{
msm_init_modem_notifier_list();
msm_smd_init();
@@ -485,32 +479,7 @@
mxt_init_vkeys_8974();
}
-static struct of_device_id irq_match[] __initdata = {
- { .compatible = "qcom,msm-qgic2", .data = gic_of_init, },
- { .compatible = "qcom,msm-gpio", .data = msm_gpio_of_init, },
- { .compatible = "qcom,spmi-pmic-arb", .data = qpnpint_of_init, },
- { .compatible = "qcom,wcd9xxx-irq", .data = wcd9xxx_irq_of_init, },
- {}
-};
-static struct of_device_id mpm_match[] __initdata = {
- {.compatible = "qcom,mpm-v2", },
- {},
-};
-
-void __init msm_8974_init_irq(void)
-{
- struct device_node *node;
-
- of_irq_init(irq_match);
- node = of_find_matching_node(NULL, mpm_match);
-
- WARN_ON(!node);
-
- if (node)
- of_mpm_init(node);
-}
-
-static struct of_dev_auxdata msm_8974_auxdata_lookup[] __initdata = {
+static struct of_dev_auxdata msm8974_auxdata_lookup[] __initdata = {
OF_DEV_AUXDATA("qcom,hsusb-otg", 0xF9A55000, \
"msm_otg", NULL),
OF_DEV_AUXDATA("qcom,dwc-usb3-msm", 0xF9200000, \
@@ -577,16 +546,43 @@
{}
};
-void __init msm_8974_init(struct of_dev_auxdata **adata)
+static void __init msm8974_map_io(void)
{
+ msm_map_8974_io();
+ if (socinfo_init() < 0)
+ pr_err("%s: socinfo_init() failed\n", __func__);
+}
+
+void __init msm8974_init(void)
+{
+ struct of_dev_auxdata *adata = msm8974_auxdata_lookup;
+
msm_8974_init_gpiomux();
-
- *adata = msm_8974_auxdata_lookup;
-
regulator_has_full_constraints();
+ of_platform_populate(NULL, of_default_bus_match_table, adata, NULL);
+
+ msm8974_add_devices();
+ msm8974_add_drivers();
}
-void __init msm_8974_very_early(void)
+void __init msm8974_init_very_early(void)
{
- msm_8974_early_memory();
+ msm8974_early_memory();
}
+
+static const char *msm8974_dt_match[] __initconst = {
+ "qcom,msm8974",
+ NULL
+};
+
+DT_MACHINE_START(MSM8974_DT, "Qualcomm MSM 8974 (Flattened Device Tree)")
+ .map_io = msm8974_map_io,
+ .init_irq = msm_dt_init_irq,
+ .init_machine = msm8974_init,
+ .handle_irq = gic_handle_irq,
+ .timer = &msm_dt_timer,
+ .dt_compat = msm8974_dt_match,
+ .reserve = msm_reserve,
+ .init_very_early = msm8974_init_very_early,
+ .restart = msm_restart,
+MACHINE_END
diff --git a/arch/arm/mach-msm/board-9625-gpiomux.c b/arch/arm/mach-msm/board-9625-gpiomux.c
index fe7670b..c4e174b 100644
--- a/arch/arm/mach-msm/board-9625-gpiomux.c
+++ b/arch/arm/mach-msm/board-9625-gpiomux.c
@@ -76,6 +76,82 @@
};
+static struct gpiomux_setting sdc3_clk_active_cfg = {
+ .func = GPIOMUX_FUNC_1,
+ .drv = GPIOMUX_DRV_8MA,
+ .pull = GPIOMUX_PULL_NONE,
+};
+
+static struct gpiomux_setting sdc3_cmd_active_cfg = {
+ .func = GPIOMUX_FUNC_1,
+ .drv = GPIOMUX_DRV_8MA,
+ .pull = GPIOMUX_PULL_UP,
+};
+
+static struct gpiomux_setting sdc3_data_0_3_active_cfg = {
+ .func = GPIOMUX_FUNC_6,
+ .drv = GPIOMUX_DRV_8MA,
+ .pull = GPIOMUX_PULL_UP,
+};
+
+static struct gpiomux_setting sdc3_suspended_cfg = {
+ .func = GPIOMUX_FUNC_GPIO,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_DOWN,
+};
+
+static struct gpiomux_setting sdc3_data_1_suspended_cfg = {
+ .func = GPIOMUX_FUNC_GPIO,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_UP,
+};
+
+static struct msm_gpiomux_config sdc3_configs[] __initdata = {
+ {
+ .gpio = 25,
+ .settings = {
+ [GPIOMUX_ACTIVE] = &sdc3_clk_active_cfg,
+ [GPIOMUX_SUSPENDED] = &sdc3_suspended_cfg,
+ },
+ },
+ {
+ .gpio = 24,
+ .settings = {
+ [GPIOMUX_ACTIVE] = &sdc3_cmd_active_cfg,
+ [GPIOMUX_SUSPENDED] = &sdc3_suspended_cfg,
+ },
+
+ },
+ {
+ .gpio = 16,
+ .settings = {
+ [GPIOMUX_ACTIVE] = &sdc3_data_0_3_active_cfg,
+ [GPIOMUX_SUSPENDED] = &sdc3_suspended_cfg,
+ },
+ },
+ {
+ .gpio = 17,
+ .settings = {
+ [GPIOMUX_ACTIVE] = &sdc3_data_0_3_active_cfg,
+ [GPIOMUX_SUSPENDED] = &sdc3_data_1_suspended_cfg,
+ },
+ },
+ {
+ .gpio = 18,
+ .settings = {
+ [GPIOMUX_ACTIVE] = &sdc3_data_0_3_active_cfg,
+ [GPIOMUX_SUSPENDED] = &sdc3_suspended_cfg,
+ },
+ },
+ {
+ .gpio = 19,
+ .settings = {
+ [GPIOMUX_ACTIVE] = &sdc3_data_0_3_active_cfg,
+ [GPIOMUX_SUSPENDED] = &sdc3_suspended_cfg,
+ },
+ },
+};
+
void __init msm9625_init_gpiomux(void)
{
int rc;
@@ -87,4 +163,5 @@
}
msm_gpiomux_install(msm_blsp_configs, ARRAY_SIZE(msm_blsp_configs));
+ msm_gpiomux_install(sdc3_configs, ARRAY_SIZE(sdc3_configs));
}
diff --git a/arch/arm/mach-msm/board-9625.c b/arch/arm/mach-msm/board-9625.c
index d2c51ce..49f2561 100644
--- a/arch/arm/mach-msm/board-9625.c
+++ b/arch/arm/mach-msm/board-9625.c
@@ -35,14 +35,19 @@
#include <mach/msm_memtypes.h>
#include <mach/msm_iomap.h>
#include <mach/msm_smd.h>
+#include <mach/scm.h>
#include <mach/rpm-smd.h>
#include <mach/rpm-regulator-smd.h>
+#include <mach/mpm.h>
#include "clock.h"
#include "modem_notifier.h"
#include "lpm_resources.h"
#include "spm.h"
#define MSM_KERNEL_EBI_SIZE 0x51000
+#define SCM_SVC_L2CC_PL310 16
+#define L2CC_PL310_CTRL_ID 1
+#define L2CC_PL310_ON 1
static struct memtype_reserve msm9625_reserve_table[] __initdata = {
[MEMTYPE_SMI] = {
@@ -117,13 +122,30 @@
"spi_qsd.1", NULL),
OF_DEV_AUXDATA("qcom,spmi-pmic-arb", 0xFC4C0000, \
"spmi-pmic-arb.0", NULL),
+ OF_DEV_AUXDATA("qcom,msm-sdcc", 0xF98A4000, \
+ "msm_sdcc.2", NULL),
+ OF_DEV_AUXDATA("qcom,msm-sdcc", 0xF9864000, \
+ "msm_sdcc.3", NULL),
{}
};
+static struct of_device_id mpm_match[] __initdata = {
+ {.compatible = "qcom,mpm-v2", },
+ {},
+};
+
void __init msm9625_init_irq(void)
{
- l2x0_of_init(L2CC_AUX_CTRL, L2X0_AUX_CTRL_MASK);
+ struct device_node *node;
+ scm_call_atomic1(SCM_SVC_L2CC_PL310, L2CC_PL310_CTRL_ID, L2CC_PL310_ON);
+ l2x0_of_init(0, ~0UL);
of_irq_init(irq_match);
+ node = of_find_matching_node(NULL, mpm_match);
+
+ WARN_ON(!node);
+
+ if (node)
+ of_mpm_init(node);
}
static void __init msm_dt_timer_init(void)
@@ -135,9 +157,14 @@
.init = msm_dt_timer_init
};
-static void __init msm9625_reserve(void)
+static void __init msm9625_early_memory(void)
{
reserve_info = &msm9625_reserve_info;
+ of_scan_flat_dt(dt_scan_for_memory_reserve, msm9625_reserve_table);
+}
+
+static void __init msm9625_reserve(void)
+{
msm_reserve();
}
@@ -312,5 +339,6 @@
.timer = &msm_dt_timer,
.dt_compat = msm9625_dt_match,
.reserve = msm9625_reserve,
+ .init_very_early = msm9625_early_memory,
.restart = msm_restart,
MACHINE_END
diff --git a/arch/arm/mach-msm/board-dt.c b/arch/arm/mach-msm/board-dt.c
index 8f9a0ef..15a544a 100644
--- a/arch/arm/mach-msm/board-dt.c
+++ b/arch/arm/mach-msm/board-dt.c
@@ -10,83 +10,50 @@
* GNU General Public License for more details.
*/
+#include <linux/gpio.h>
#include <linux/kernel.h>
-#include <linux/errno.h>
#include <linux/of.h>
-#include <linux/of_address.h>
-#include <linux/of_platform.h>
-#include <linux/of_fdt.h>
#include <linux/of_irq.h>
-#include <asm/hardware/gic.h>
+#include <linux/mfd/wcd9xxx/core.h>
#include <asm/arch_timer.h>
-#include <asm/mach/arch.h>
#include <asm/mach/time.h>
-#include <mach/socinfo.h>
-#include <mach/board.h>
-#include <mach/restart.h>
+#include <asm/hardware/gic.h>
+#include <mach/mpm.h>
+#include <mach/qpnp-int.h>
+
+#include "board-dt.h"
static void __init msm_dt_timer_init(void)
{
arch_timer_of_register();
}
-static struct sys_timer msm_dt_timer = {
+struct sys_timer msm_dt_timer = {
.init = msm_dt_timer_init
};
-static void __init msm_dt_init_irq(void)
-{
- if (machine_is_msm8974())
- msm_8974_init_irq();
-}
-
-static void __init msm_dt_map_io(void)
-{
- if (early_machine_is_msm8974())
- msm_map_8974_io();
- if (socinfo_init() < 0)
- pr_err("%s: socinfo_init() failed\n", __func__);
-}
-
-static void __init msm_dt_init(void)
-{
- struct of_dev_auxdata *adata = NULL;
-
- if (machine_is_msm8974())
- msm_8974_init(&adata);
-
- of_platform_populate(NULL, of_default_bus_match_table, adata, NULL);
- if (machine_is_msm8974()) {
- msm_8974_add_devices();
- msm_8974_add_drivers();
- }
-}
-
-static const char *msm_dt_match[] __initconst = {
- "qcom,msm8974",
- NULL
+static struct of_device_id irq_match[] __initdata = {
+ { .compatible = "qcom,msm-qgic2", .data = gic_of_init, },
+ { .compatible = "qcom,msm-gpio", .data = msm_gpio_of_init, },
+ { .compatible = "qcom,spmi-pmic-arb", .data = qpnpint_of_init, },
+ { .compatible = "qcom,wcd9xxx-irq", .data = wcd9xxx_irq_of_init, },
+ {}
};
-static void __init msm_dt_reserve(void)
-{
- if (early_machine_is_msm8974())
- msm_8974_reserve();
-}
+static struct of_device_id mpm_match[] __initdata = {
+ {.compatible = "qcom,mpm-v2", },
+ {}
+};
-static void __init msm_dt_init_very_early(void)
+void __init msm_dt_init_irq(void)
{
- if (early_machine_is_msm8974())
- msm_8974_very_early();
-}
+ struct device_node *node;
-DT_MACHINE_START(MSM_DT, "Qualcomm MSM (Flattened Device Tree)")
- .map_io = msm_dt_map_io,
- .init_irq = msm_dt_init_irq,
- .init_machine = msm_dt_init,
- .handle_irq = gic_handle_irq,
- .timer = &msm_dt_timer,
- .dt_compat = msm_dt_match,
- .reserve = msm_dt_reserve,
- .init_very_early = msm_dt_init_very_early,
- .restart = msm_restart,
-MACHINE_END
+ of_irq_init(irq_match);
+ node = of_find_matching_node(NULL, mpm_match);
+
+ WARN_ON(!node);
+
+ if (node)
+ of_mpm_init(node);
+}
diff --git a/arch/arm/mach-msm/board-dt.h b/arch/arm/mach-msm/board-dt.h
new file mode 100644
index 0000000..31143a5
--- /dev/null
+++ b/arch/arm/mach-msm/board-dt.h
@@ -0,0 +1,14 @@
+/* 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.
+ */
+
+extern struct sys_timer msm_dt_timer;
+void __init msm_dt_init_irq(void);
diff --git a/arch/arm/mach-msm/board-msm7627a-bt.c b/arch/arm/mach-msm/board-msm7627a-bt.c
index e4edf9b..bcc9645 100644
--- a/arch/arm/mach-msm/board-msm7627a-bt.c
+++ b/arch/arm/mach-msm/board-msm7627a-bt.c
@@ -103,7 +103,8 @@
if (machine_is_msm7627a_qrd1())
gpio_bt_sys_rest_en = 114;
if (machine_is_msm7627a_evb() || machine_is_msm8625_evb()
- || machine_is_msm8625_evt())
+ || machine_is_msm8625_evt()
+ || machine_is_qrd_skud_prime())
gpio_bt_sys_rest_en = 16;
if (machine_is_msm8625_qrd7())
gpio_bt_sys_rest_en = 88;
@@ -975,6 +976,8 @@
int i, rc = 0;
struct device *dev;
+ if (machine_is_qrd_skud_prime())
+ return;
gpio_bt_config();
diff --git a/arch/arm/mach-msm/board-msm7627a-camera.c b/arch/arm/mach-msm/board-msm7627a-camera.c
index b5f214b..79ad996 100644
--- a/arch/arm/mach-msm/board-msm7627a-camera.c
+++ b/arch/arm/mach-msm/board-msm7627a-camera.c
@@ -403,7 +403,8 @@
if (machine_is_msm8625_evb() || machine_is_msm7627a_evb()
|| machine_is_msm8625_evt()
|| machine_is_msm7627a_qrd3()
- || machine_is_msm8625_qrd7()) {
+ || machine_is_msm8625_qrd7()
+ || machine_is_qrd_skud_prime()) {
sensor_board_info_ov7692.cam_vreg =
ov7692_gpio_vreg;
sensor_board_info_ov7692.num_vreg =
@@ -420,7 +421,8 @@
platform_device_register(&msm_camera_server);
if (machine_is_msm8625_surf() || machine_is_msm8625_evb()
|| machine_is_msm8625_evt()
- || machine_is_msm8625_qrd7()) {
+ || machine_is_msm8625_qrd7()
+ || machine_is_qrd_skud_prime()) {
platform_device_register(&msm8625_device_csic0);
platform_device_register(&msm8625_device_csic1);
} else {
@@ -429,7 +431,8 @@
}
if (machine_is_msm8625_evb()
|| machine_is_msm8625_evt()
- || machine_is_msm8625_qrd7())
+ || machine_is_msm8625_qrd7()
+ || machine_is_qrd_skud_prime())
*(int *) msm7x27a_device_clkctl.dev.platform_data = 1;
platform_device_register(&msm7x27a_device_clkctl);
platform_device_register(&msm7x27a_device_vfe);
@@ -1175,7 +1178,6 @@
#ifndef CONFIG_MSM_CAMERA_V4L2
int rc;
#endif
-
pr_debug("msm7627a_camera_init Entered\n");
if (machine_is_msm7627a_qrd3() || machine_is_msm8625_qrd7()) {
@@ -1194,7 +1196,8 @@
if (machine_is_msm7627a_evb() || machine_is_msm8625_evb()
|| machine_is_msm8625_evt()
|| machine_is_msm7627a_qrd3()
- || machine_is_msm8625_qrd7()) {
+ || machine_is_msm8625_qrd7()
+ || machine_is_qrd_skud_prime()) {
#ifndef CONFIG_MSM_CAMERA_V4L2
lcd_camera_power_init();
#endif
@@ -1210,7 +1213,8 @@
|| machine_is_msm8625_evb()
|| machine_is_msm8625_evt()
|| machine_is_msm7627a_qrd3()
- || machine_is_msm8625_qrd7()) {
+ || machine_is_msm8625_qrd7()
+ || machine_is_qrd_skud_prime()) {
platform_add_devices(camera_devices_evb,
ARRAY_SIZE(camera_devices_evb));
} else if (machine_is_msm7627a_qrd3())
@@ -1223,7 +1227,8 @@
|| !machine_is_msm8625_evb()
|| !machine_is_msm8625_evt()
|| !machine_is_msm7627a_qrd3()
- || !machine_is_msm8625_qrd7())
+ || !machine_is_msm8625_qrd7()
+ || !machine_is_qrd_skud_prime())
register_i2c_devices();
#ifndef CONFIG_MSM_CAMERA_V4L2
rc = regulator_bulk_get(NULL, ARRAY_SIZE(regs_camera), regs_camera);
@@ -1253,7 +1258,8 @@
|| machine_is_msm8625_evb()
|| machine_is_msm8625_evt()
|| machine_is_msm7627a_qrd3()
- || machine_is_msm8625_qrd7()) {
+ || machine_is_msm8625_qrd7()
+ || machine_is_qrd_skud_prime()) {
pr_debug("machine_is_msm7627a_evb i2c_register_board_info\n");
i2c_register_board_info(MSM_GSBI0_QUP_I2C_BUS_ID,
i2c_camera_devices_evb,
diff --git a/arch/arm/mach-msm/board-msm7627a-display.c b/arch/arm/mach-msm/board-msm7627a-display.c
index 2db074d..1249c7b 100644
--- a/arch/arm/mach-msm/board-msm7627a-display.c
+++ b/arch/arm/mach-msm/board-msm7627a-display.c
@@ -542,7 +542,8 @@
if (!strncmp(name, "lcdc_truly_hvga_ips3p2335_pt", 28))
ret = 0;
} else if (machine_is_msm7627a_evb() || machine_is_msm8625_evb() ||
- machine_is_msm8625_evt()) {
+ machine_is_msm8625_evt() ||
+ machine_is_qrd_skud_prime()) {
if (!strncmp(name, "mipi_cmd_nt35510_wvga", 21))
ret = 0;
}
@@ -796,7 +797,8 @@
if (machine_is_msm7625a_surf() || machine_is_msm7625a_ffa())
fb_size = MSM7x25A_MSM_FB_SIZE;
else if (machine_is_msm7627a_evb() || machine_is_msm8625_evb()
- || machine_is_msm8625_evt())
+ || machine_is_msm8625_evt()
+ || machine_is_qrd_skud_prime())
fb_size = MSM8x25_MSM_FB_SIZE;
else
fb_size = MSM_FB_SIZE;
@@ -1017,7 +1019,8 @@
if (machine_is_msm7627a_qrd1())
rc = msm_fb_dsi_client_qrd1_reset();
else if (machine_is_msm7627a_evb() || machine_is_msm8625_evb()
- || machine_is_msm8625_evt())
+ || machine_is_msm8625_evt()
+ || machine_is_qrd_skud_prime())
rc = msm_fb_dsi_client_qrd3_reset();
else
rc = msm_fb_dsi_client_msm_reset();
@@ -1327,7 +1330,8 @@
if (machine_is_msm7627a_qrd1())
rc = mipi_dsi_panel_qrd1_power(on);
else if (machine_is_msm7627a_evb() || machine_is_msm8625_evb()
- || machine_is_msm8625_evt())
+ || machine_is_msm8625_evt()
+ || machine_is_qrd_skud_prime())
rc = mipi_dsi_panel_qrd3_power(on);
else
rc = mipi_dsi_panel_msm_power(on);
@@ -1391,7 +1395,8 @@
platform_add_devices(qrd_fb_devices,
ARRAY_SIZE(qrd_fb_devices));
} else if (machine_is_msm7627a_evb() || machine_is_msm8625_evb()
- || machine_is_msm8625_evt()) {
+ || machine_is_msm8625_evt()
+ || machine_is_qrd_skud_prime()) {
mipi_NT35510_pdata.bl_lock = 1;
mipi_NT35516_pdata.bl_lock = 1;
if (disable_splash)
@@ -1423,7 +1428,8 @@
msm_fb_register_device("mipi_dsi", &mipi_dsi_pdata);
#endif
if (machine_is_msm7627a_evb() || machine_is_msm8625_evb()
- || machine_is_msm8625_evt()) {
+ || machine_is_msm8625_evt()
+ || machine_is_qrd_skud_prime()) {
gpio_reg_2p85v = regulator_get(&mipi_dsi_device.dev,
"lcd_vdd");
if (IS_ERR(gpio_reg_2p85v))
diff --git a/arch/arm/mach-msm/board-msm7627a-storage.c b/arch/arm/mach-msm/board-msm7627a-storage.c
index 49ff393..07ff389 100644
--- a/arch/arm/mach-msm/board-msm7627a-storage.c
+++ b/arch/arm/mach-msm/board-msm7627a-storage.c
@@ -378,9 +378,9 @@
if (mmc_regulator_init(1, "mmc", 2850000))
return;
/* 8x25 EVT do not use hw detector */
- if (!(machine_is_msm8625_evt()))
+ if (!((machine_is_msm8625_evt() || machine_is_qrd_skud_prime())))
sdc1_plat_data.status_irq = MSM_GPIO_TO_INT(gpio_sdc1_hw_det);
- if (machine_is_msm8625_evt())
+ if (machine_is_msm8625_evt() || machine_is_qrd_skud_prime())
sdc1_plat_data.status = NULL;
msm_add_sdcc(1, &sdc1_plat_data);
diff --git a/arch/arm/mach-msm/board-msm7627a-wlan.c b/arch/arm/mach-msm/board-msm7627a-wlan.c
index 75395b7..ab29fc5 100644
--- a/arch/arm/mach-msm/board-msm7627a-wlan.c
+++ b/arch/arm/mach-msm/board-msm7627a-wlan.c
@@ -53,7 +53,8 @@
|| machine_is_msm8625_evb()
|| machine_is_msm8625_evt()
|| machine_is_msm7627a_qrd3()
- || machine_is_msm8625_qrd7())
+ || machine_is_msm8625_qrd7()
+ || machine_is_qrd_skud_prime())
gpio_wlan_sys_rest_en = 124;
}
@@ -243,7 +244,8 @@
|| machine_is_msm8625_evb()
|| machine_is_msm8625_evt()
|| machine_is_msm7627a_qrd3()
- || machine_is_msm8625_qrd7()) {
+ || machine_is_msm8625_qrd7()
+ || machine_is_qrd_skud_prime()) {
rc = gpio_tlmm_config(GPIO_CFG(gpio_wlan_sys_rest_en, 0,
GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL,
GPIO_CFG_2MA), GPIO_CFG_ENABLE);
@@ -332,7 +334,8 @@
|| machine_is_msm8625_evb()
|| machine_is_msm8625_evt()
|| machine_is_msm7627a_qrd3()
- || machine_is_msm8625_qrd7()) {
+ || machine_is_msm8625_qrd7()
+ || machine_is_qrd_skud_prime()) {
rc = gpio_tlmm_config(GPIO_CFG(gpio_wlan_sys_rest_en, 0,
GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL,
GPIO_CFG_2MA), GPIO_CFG_ENABLE);
diff --git a/arch/arm/mach-msm/board-qrd7627a.c b/arch/arm/mach-msm/board-qrd7627a.c
index ec8e438..fd322e9 100644
--- a/arch/arm/mach-msm/board-qrd7627a.c
+++ b/arch/arm/mach-msm/board-qrd7627a.c
@@ -32,6 +32,8 @@
#include <linux/msm_adc.h>
#include <linux/regulator/msm-gpio-regulator.h>
#include <linux/msm_ion.h>
+#include <linux/i2c-gpio.h>
+#include <linux/regulator/onsemi-ncp6335d.h>
#include <asm/mach/mmc.h>
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
@@ -128,6 +130,27 @@
.msm_i2c_config_gpio = gsbi_qup_i2c_gpio_config,
};
+static struct msm_gpio i2c_gpio_config[] = {
+ { GPIO_CFG(39, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA),
+ "qup_scl" },
+ { GPIO_CFG(36, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA),
+ "qup_sda" },
+};
+
+static struct i2c_gpio_platform_data i2c_gpio_pdata = {
+ .scl_pin = 39,
+ .sda_pin = 36,
+ .udelay = 5, /* 100 Khz */
+};
+
+static struct platform_device msm_i2c_gpio = {
+ .name = "i2c-gpio",
+ .id = 2,
+ .dev = {
+ .platform_data = &i2c_gpio_pdata,
+ }
+};
+
#ifdef CONFIG_ARCH_MSM7X27A
#define MSM_RESERVE_MDP_SIZE 0x1B00000
@@ -597,6 +620,41 @@
},
};
+/* Regulator configuration for the NCP6335D buck */
+struct regulator_consumer_supply ncp6335d_consumer_supplies[] = {
+ REGULATOR_SUPPLY("ncp6335d", NULL),
+};
+
+static struct regulator_init_data ncp6335d_init_data = {
+ .constraints = {
+ .name = "ncp6335d_sw",
+ .min_uV = 600000,
+ .max_uV = 1400000,
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE |
+ REGULATOR_CHANGE_STATUS |
+ REGULATOR_CHANGE_MODE,
+ .valid_modes_mask = REGULATOR_MODE_NORMAL |
+ REGULATOR_MODE_FAST,
+ .initial_mode = REGULATOR_MODE_NORMAL,
+ .always_on = 1,
+ },
+ .num_consumer_supplies = ARRAY_SIZE(ncp6335d_consumer_supplies),
+ .consumer_supplies = ncp6335d_consumer_supplies,
+};
+
+static struct ncp6335d_platform_data ncp6335d_pdata = {
+ .init_data = &ncp6335d_init_data,
+ .default_vsel = NCP6335D_VSEL0,
+ .slew_rate_ns = 166,
+};
+
+static struct i2c_board_info i2c2_info[] __initdata = {
+ {
+ I2C_BOARD_INFO("ncp6335d", 0x38 >> 1),
+ .platform_data = &ncp6335d_pdata,
+ },
+};
+
static struct platform_device *common_devices[] __initdata = {
&android_usb_device,
&msm_batt_device,
@@ -651,6 +709,7 @@
&msm8625_device_smd,
&msm8625_gsbi0_qup_i2c_device,
&msm8625_gsbi1_qup_i2c_device,
+ &msm_i2c_gpio, /* TODO: Make this specific to 8625q */
&msm8625_device_uart1,
&msm8625_device_uart_dm1,
&msm8625_device_otg,
@@ -847,10 +906,20 @@
static void __init msm8625_device_i2c_init(void)
{
+ int i, rc;
+
msm8625_gsbi0_qup_i2c_device.dev.platform_data
= &msm_gsbi0_qup_i2c_pdata;
msm8625_gsbi1_qup_i2c_device.dev.platform_data
= &msm_gsbi1_qup_i2c_pdata;
+ if (machine_is_qrd_skud_prime()) {
+ for (i = 0 ; i < ARRAY_SIZE(i2c_gpio_config); i++) {
+ rc = gpio_tlmm_config(i2c_gpio_config[i].gpio_cfg,
+ GPIO_CFG_ENABLE);
+ if (rc)
+ pr_err("I2C-gpio tlmm config failed\n");
+ }
+ }
}
static struct platform_device msm_proccomm_regulator_dev = {
@@ -884,7 +953,8 @@
static void __init add_platform_devices(void)
{
if (machine_is_msm8625_evb() || machine_is_msm8625_qrd7()
- || machine_is_msm8625_evt()) {
+ || machine_is_msm8625_evt()
+ || machine_is_qrd_skud_prime()) {
platform_add_devices(msm8625_evb_devices,
ARRAY_SIZE(msm8625_evb_devices));
platform_add_devices(qrd3_devices,
@@ -899,7 +969,8 @@
ARRAY_SIZE(qrd3_devices));
if (machine_is_msm7627a_evb() || machine_is_msm8625_evb()
- || machine_is_msm8625_evt())
+ || machine_is_msm8625_evt()
+ || machine_is_qrd_skud_prime())
platform_add_devices(msm8625_lcd_camera_devices,
ARRAY_SIZE(msm8625_lcd_camera_devices));
else if (machine_is_msm8625_qrd7())
@@ -995,6 +1066,11 @@
msm_pm_register_irqs();
msm_fb_add_devices();
+ if (machine_is_qrd_skud_prime())
+ i2c_register_board_info(2, i2c2_info,
+ ARRAY_SIZE(i2c2_info));
+
+
#if defined(CONFIG_BT) && defined(CONFIG_MARIMBA_CORE)
msm7627a_bt_power_init();
#endif
@@ -1070,3 +1146,13 @@
.init_early = qrd7627a_init_early,
.handle_irq = gic_handle_irq,
MACHINE_END
+MACHINE_START(QRD_SKUD_PRIME, "QRD MSM8625 SKUD PRIME")
+ .atag_offset = 0x100,
+ .map_io = msm8625_map_io,
+ .reserve = msm8625_reserve,
+ .init_irq = msm8625_init_irq,
+ .init_machine = msm_qrd_init,
+ .timer = &msm_timer,
+ .init_early = qrd7627a_init_early,
+ .handle_irq = gic_handle_irq,
+MACHINE_END
diff --git a/arch/arm/mach-msm/clock-8974.c b/arch/arm/mach-msm/clock-8974.c
index b21e60e..246ceaf 100644
--- a/arch/arm/mach-msm/clock-8974.c
+++ b/arch/arm/mach-msm/clock-8974.c
@@ -4677,6 +4677,10 @@
{&gcc_ce1_clk.c, GCC_BASE, 0x0138},
{&gcc_lpass_q6_axi_clk.c, GCC_BASE, 0x0160},
{&gcc_mss_q6_bimc_axi_clk.c, GCC_BASE, 0x0031},
+ {&cnoc_clk.c, GCC_BASE, 0x0008},
+ {&pnoc_clk.c, GCC_BASE, 0x0010},
+ {&snoc_clk.c, GCC_BASE, 0x0000},
+ {&bimc_clk.c, GCC_BASE, 0x0155},
{&mmss_mmssnoc_axi_clk.c, MMSS_BASE, 0x0004},
{&ocmemnoc_clk.c, MMSS_BASE, 0x0007},
{&ocmemcx_ocmemnoc_clk.c, MMSS_BASE, 0x0009},
diff --git a/arch/arm/mach-msm/clock-9615.c b/arch/arm/mach-msm/clock-9615.c
index fee8445..035ef5c 100644
--- a/arch/arm/mach-msm/clock-9615.c
+++ b/arch/arm/mach-msm/clock-9615.c
@@ -214,49 +214,6 @@
DEFINE_CLK_RPM_BRANCH(cxo_clk, cxo_a_clk, CXO, 19200000);
-static DEFINE_SPINLOCK(soft_vote_lock);
-
-static int pll_acpu_vote_clk_enable(struct clk *c)
-{
- int ret = 0;
- unsigned long flags;
- struct pll_vote_clk *pllv = to_pll_vote_clk(c);
-
- spin_lock_irqsave(&soft_vote_lock, flags);
-
- if (!*pllv->soft_vote)
- ret = pll_vote_clk_enable(c);
- if (ret == 0)
- *pllv->soft_vote |= (pllv->soft_vote_mask);
-
- spin_unlock_irqrestore(&soft_vote_lock, flags);
- return ret;
-}
-
-static void pll_acpu_vote_clk_disable(struct clk *c)
-{
- unsigned long flags;
- struct pll_vote_clk *pllv = to_pll_vote_clk(c);
-
- spin_lock_irqsave(&soft_vote_lock, flags);
-
- *pllv->soft_vote &= ~(pllv->soft_vote_mask);
- if (!*pllv->soft_vote)
- pll_vote_clk_disable(c);
-
- spin_unlock_irqrestore(&soft_vote_lock, flags);
-}
-
-static struct clk_ops clk_ops_pll_acpu_vote = {
- .enable = pll_acpu_vote_clk_enable,
- .disable = pll_acpu_vote_clk_disable,
- .is_enabled = pll_vote_clk_is_enabled,
- .get_parent = pll_vote_clk_get_parent,
-};
-
-#define PLL_SOFT_VOTE_PRIMARY BIT(0)
-#define PLL_SOFT_VOTE_ACPU BIT(1)
-
static unsigned int soft_vote_pll0;
static struct pll_vote_clk pll0_clk = {
diff --git a/arch/arm/mach-msm/clock-9625.c b/arch/arm/mach-msm/clock-9625.c
index 02dd562..fb4f32a 100644
--- a/arch/arm/mach-msm/clock-9625.c
+++ b/arch/arm/mach-msm/clock-9625.c
@@ -279,6 +279,16 @@
| BVAL(10, 8, s##_lpass_source_val), \
}
+#define F_APCS_PLL(f, l, m, n, pre_div, post_div, vco) \
+ { \
+ .freq_hz = (f), \
+ .l_val = (l), \
+ .m_val = (m), \
+ .n_val = (n), \
+ .pre_div_val = BVAL(14, 12, (pre_div)), \
+ .post_div_val = BVAL(9, 8, (post_div)), \
+ .vco_val = BVAL(21, 20, (vco)), \
+ }
#define VDD_DIG_FMAX_MAP1(l1, f1) \
.vdd_class = &vdd_dig, \
@@ -362,20 +372,39 @@
DEFINE_CLK_RPM_SMD_XO_BUFFER_PINCTRL(cxo_a1_pin, cxo_a1_a_pin, A1_ID);
DEFINE_CLK_RPM_SMD_XO_BUFFER_PINCTRL(cxo_a2_pin, cxo_a2_a_pin, A2_ID);
+static unsigned int soft_vote_gpll0;
+
static struct pll_vote_clk gpll0_clk_src = {
.en_reg = (void __iomem *)APCS_GPLL_ENA_VOTE_REG,
.status_reg = (void __iomem *)GPLL0_STATUS_REG,
.status_mask = BIT(17),
.parent = &cxo_clk_src.c,
+ .soft_vote = &soft_vote_gpll0,
+ .soft_vote_mask = PLL_SOFT_VOTE_PRIMARY,
.base = &virt_bases[GCC_BASE],
.c = {
.rate = 600000000,
.dbg_name = "gpll0_clk_src",
- .ops = &clk_ops_pll_vote,
+ .ops = &clk_ops_pll_acpu_vote,
CLK_INIT(gpll0_clk_src.c),
},
};
+static struct pll_vote_clk gpll0_activeonly_clk_src = {
+ .en_reg = (void __iomem *)APCS_GPLL_ENA_VOTE_REG,
+ .status_reg = (void __iomem *)GPLL0_STATUS_REG,
+ .status_mask = BIT(17),
+ .soft_vote = &soft_vote_gpll0,
+ .soft_vote_mask = PLL_SOFT_VOTE_ACPU,
+ .base = &virt_bases[GCC_BASE],
+ .c = {
+ .rate = 600000000,
+ .dbg_name = "gpll0_activeonly_clk_src",
+ .ops = &clk_ops_pll_acpu_vote,
+ CLK_INIT(gpll0_activeonly_clk_src.c),
+ },
+};
+
static struct pll_vote_clk lpapll0_clk_src = {
.en_reg = (void __iomem *)LPASS_LPA_PLL_VOTE_APPS_REG,
.en_mask = BIT(0),
@@ -406,17 +435,33 @@
},
};
+static struct pll_freq_tbl apcs_pll_freq[] = {
+ F_APCS_PLL(748800000, 0x27, 0x0, 0x1, 0x0, 0x0, 0x0),
+ F_APCS_PLL(998400000, 0x34, 0x0, 0x1, 0x0, 0x0, 0x0),
+ PLL_F_END
+};
+
/*
* Need to skip handoff of the acpu pll to avoid handoff code
* to turn off the pll when the acpu is running off this pll.
*/
static struct pll_clk apcspll_clk_src = {
.mode_reg = (void __iomem *)APCS_CPU_PLL_MODE_REG,
+ .l_reg = (void __iomem *)APCS_CPU_PLL_L_REG,
+ .m_reg = (void __iomem *)APCS_CPU_PLL_M_REG,
+ .n_reg = (void __iomem *)APCS_CPU_PLL_N_REG,
+ .config_reg = (void __iomem *)APCS_CPU_PLL_USER_CTL_REG,
.status_reg = (void __iomem *)APCS_CPU_PLL_STATUS_REG,
- .parent = &cxo_clk_src.c,
+ .freq_tbl = apcs_pll_freq,
+ .masks = {
+ .vco_mask = BM(21, 20),
+ .pre_div_mask = BM(14, 12),
+ .post_div_mask = BM(9, 8),
+ .mn_en_mask = BIT(24),
+ .main_output_mask = BIT(0),
+ },
.base = &virt_bases[APCS_PLL_BASE],
.c = {
- .rate = 998400000,
.dbg_name = "apcspll_clk_src",
.ops = &clk_ops_local_pll,
CLK_INIT(apcspll_clk_src.c),
@@ -1967,8 +2012,8 @@
CLK_LOOKUP("xo", cxo_clk_src.c, ""),
CLK_LOOKUP("measure", measure_clk.c, "debug"),
- CLK_LOOKUP("pll0", gpll0_clk_src.c, "f9010008.qcom,acpuclk"),
- CLK_LOOKUP("pll14", apcspll_clk_src.c, "f9010008.qcom,acpuclk"),
+ CLK_LOOKUP("pll0", gpll0_activeonly_clk_src.c, "f9010008.qcom,acpuclk"),
+ CLK_LOOKUP("pll14", apcspll_clk_src.c, "f9010008.qcom,acpuclk"),
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"),
@@ -2011,12 +2056,12 @@
CLK_LOOKUP("core_clk", gcc_pdm2_clk.c, ""),
CLK_LOOKUP("iface_clk", gcc_pdm_ahb_clk.c, ""),
- CLK_LOOKUP("iface_clk", gcc_sdcc2_ahb_clk.c, "f98a4000.qcom,sdcc"),
- CLK_LOOKUP("core_clk", gcc_sdcc2_apps_clk.c, "f98a4000.qcom,sdcc"),
- CLK_LOOKUP("bus_clk", pnoc_sdcc2_clk.c, "f98a4000.qcom,sdcc"),
- CLK_LOOKUP("iface_clk", gcc_sdcc3_ahb_clk.c, ""),
- CLK_LOOKUP("core_clk", gcc_sdcc3_apps_clk.c, ""),
- CLK_LOOKUP("bus_clk", pnoc_sdcc3_clk.c, ""),
+ CLK_LOOKUP("iface_clk", gcc_sdcc2_ahb_clk.c, "msm_sdcc.2"),
+ CLK_LOOKUP("core_clk", gcc_sdcc2_apps_clk.c, "msm_sdcc.2"),
+ CLK_LOOKUP("bus_clk", pnoc_sdcc2_clk.c, "msm_sdcc.2"),
+ CLK_LOOKUP("iface_clk", gcc_sdcc3_ahb_clk.c, "msm_sdcc.3"),
+ CLK_LOOKUP("core_clk", gcc_sdcc3_apps_clk.c, "msm_sdcc.3"),
+ CLK_LOOKUP("bus_clk", pnoc_sdcc3_clk.c, "msm_sdcc.3"),
CLK_LOOKUP("iface_clk", gcc_usb_hs_ahb_clk.c, "f9a55000.usb"),
CLK_LOOKUP("core_clk", gcc_usb_hs_system_clk.c, "f9a55000.usb"),
@@ -2147,32 +2192,6 @@
.main_output_mask = BIT(0),
};
-static struct pll_config_regs apcspll_regs __initdata = {
- .l_reg = (void __iomem *)APCS_CPU_PLL_L_REG,
- .m_reg = (void __iomem *)APCS_CPU_PLL_M_REG,
- .n_reg = (void __iomem *)APCS_CPU_PLL_N_REG,
- .config_reg = (void __iomem *)APCS_CPU_PLL_USER_CTL_REG,
- .mode_reg = (void __iomem *)APCS_CPU_PLL_MODE_REG,
- .base = &virt_bases[APCS_PLL_BASE],
-};
-
-/* A5PLL with 998.4MHz */
-static struct pll_config apcspll_config __initdata = {
- .l = 0x34,
- .m = 0x0,
- .n = 0x1,
- .vco_val = 0x0,
- .vco_mask = BM(21, 20),
- .pre_div_val = 0x0,
- .pre_div_mask = BM(14, 12),
- .post_div_val = BVAL(9, 8, 0x0),
- .post_div_mask = BM(9, 8),
- .mn_ena_val = BIT(24),
- .mn_ena_mask = BIT(24),
- .main_output_val = BIT(0),
- .main_output_mask = BIT(0),
-};
-
#define PLL_AUX_OUTPUT_BIT 1
#define PLL_AUX2_OUTPUT_BIT 2
@@ -2218,9 +2237,12 @@
{
u32 regval;
- configure_sr_hpm_lp_pll(&apcspll_config, &apcspll_regs, 0);
+ clk_set_rate(&apcspll_clk_src.c, 998400000);
+
writel_relaxed(0x00141200,
APCS_PLL_REG_BASE(APCS_CPU_PLL_CONFIG_CTL_REG));
+
+ /* Enable AUX and AUX2 output */
regval = readl_relaxed(APCS_PLL_REG_BASE(APCS_CPU_PLL_USER_CTL_REG));
regval |= BIT(PLL_AUX_OUTPUT_BIT) | BIT(PLL_AUX2_OUTPUT_BIT);
writel_relaxed(regval, APCS_PLL_REG_BASE(APCS_CPU_PLL_USER_CTL_REG));
diff --git a/arch/arm/mach-msm/clock-local.c b/arch/arm/mach-msm/clock-local.c
index ca031ad..d2260cb 100644
--- a/arch/arm/mach-msm/clock-local.c
+++ b/arch/arm/mach-msm/clock-local.c
@@ -352,7 +352,7 @@
u32 reg_val;
reg_val = b->ctl_reg ? readl_relaxed(b->ctl_reg) : 0;
- if (b->en_mask) {
+ if (b->ctl_reg && b->en_mask) {
reg_val &= ~(b->en_mask);
writel_relaxed(reg_val, b->ctl_reg);
}
diff --git a/arch/arm/mach-msm/clock-pll.c b/arch/arm/mach-msm/clock-pll.c
index 23941d7..240f4e4 100644
--- a/arch/arm/mach-msm/clock-pll.c
+++ b/arch/arm/mach-msm/clock-pll.c
@@ -55,7 +55,7 @@
#define ENABLE_WAIT_MAX_LOOPS 200
-int pll_vote_clk_enable(struct clk *c)
+static int pll_vote_clk_enable(struct clk *c)
{
u32 ena, count;
unsigned long flags;
@@ -85,7 +85,7 @@
return -ETIMEDOUT;
}
-void pll_vote_clk_disable(struct clk *c)
+static void pll_vote_clk_disable(struct clk *c)
{
u32 ena;
unsigned long flags;
@@ -98,12 +98,12 @@
spin_unlock_irqrestore(&pll_reg_lock, flags);
}
-struct clk *pll_vote_clk_get_parent(struct clk *c)
+static struct clk *pll_vote_clk_get_parent(struct clk *c)
{
return to_pll_vote_clk(c)->parent;
}
-int pll_vote_clk_is_enabled(struct clk *c)
+static int pll_vote_clk_is_enabled(struct clk *c)
{
struct pll_vote_clk *pllv = to_pll_vote_clk(c);
return !!(readl_relaxed(PLL_STATUS_REG(pllv)) & pllv->status_mask);
@@ -126,6 +126,34 @@
.handoff = pll_vote_clk_handoff,
};
+static void __pll_config_reg(void __iomem *pll_config, struct pll_freq_tbl *f,
+ struct pll_config_masks *masks)
+{
+ u32 regval;
+
+ regval = readl_relaxed(pll_config);
+
+ /* Enable the MN counter if used */
+ if (f->m_val)
+ regval |= masks->mn_en_mask;
+
+ /* Set pre-divider and post-divider values */
+ regval &= ~masks->pre_div_mask;
+ regval |= f->pre_div_val;
+ regval &= ~masks->post_div_mask;
+ regval |= f->post_div_val;
+
+ /* Select VCO setting */
+ regval &= ~masks->vco_mask;
+ regval |= f->vco_val;
+
+ /* Enable main output if it has not been enabled */
+ if (masks->main_output_mask && !(regval & masks->main_output_mask))
+ regval |= masks->main_output_mask;
+
+ writel_relaxed(regval, pll_config);
+}
+
static void __pll_clk_enable_reg(void __iomem *mode_reg)
{
u32 mode = readl_relaxed(mode_reg);
@@ -206,6 +234,34 @@
return to_pll_clk(c)->parent;
}
+static int local_pll_clk_set_rate(struct clk *c, unsigned long rate)
+{
+ struct pll_freq_tbl *nf;
+ struct pll_clk *pll = to_pll_clk(c);
+ u32 mode;
+
+ mode = readl_relaxed(PLL_MODE_REG(pll));
+
+ /* Don't change PLL's rate if it is enabled */
+ if ((mode & PLL_MODE_MASK) == PLL_MODE_MASK)
+ return -EBUSY;
+
+ for (nf = pll->freq_tbl; nf->freq_hz != PLL_FREQ_END
+ && nf->freq_hz != rate; nf++)
+ ;
+
+ if (nf->freq_hz == PLL_FREQ_END)
+ return -EINVAL;
+
+ writel_relaxed(nf->l_val, PLL_L_REG(pll));
+ writel_relaxed(nf->m_val, PLL_M_REG(pll));
+ writel_relaxed(nf->n_val, PLL_N_REG(pll));
+
+ __pll_config_reg(PLL_CONFIG_REG(pll), nf, &pll->masks);
+
+ return 0;
+}
+
int sr_pll_clk_enable(struct clk *c)
{
u32 mode;
@@ -288,6 +344,7 @@
struct clk_ops clk_ops_local_pll = {
.enable = local_pll_clk_enable,
.disable = local_pll_clk_disable,
+ .set_rate = local_pll_clk_set_rate,
.handoff = local_pll_clk_handoff,
.get_parent = local_pll_clk_get_parent,
};
@@ -305,6 +362,7 @@
{41, 800000000},
{50, 960000000},
{52, 1008000000},
+ {57, 1104000000},
{60, 1152000000},
{62, 1200000000},
{63, 1209600000},
@@ -442,6 +500,46 @@
.is_enabled = pll_clk_is_enabled,
};
+static DEFINE_SPINLOCK(soft_vote_lock);
+
+static int pll_acpu_vote_clk_enable(struct clk *c)
+{
+ int ret = 0;
+ unsigned long flags;
+ struct pll_vote_clk *pllv = to_pll_vote_clk(c);
+
+ spin_lock_irqsave(&soft_vote_lock, flags);
+
+ if (!*pllv->soft_vote)
+ ret = pll_vote_clk_enable(c);
+ if (ret == 0)
+ *pllv->soft_vote |= (pllv->soft_vote_mask);
+
+ spin_unlock_irqrestore(&soft_vote_lock, flags);
+ return ret;
+}
+
+static void pll_acpu_vote_clk_disable(struct clk *c)
+{
+ unsigned long flags;
+ struct pll_vote_clk *pllv = to_pll_vote_clk(c);
+
+ spin_lock_irqsave(&soft_vote_lock, flags);
+
+ *pllv->soft_vote &= ~(pllv->soft_vote_mask);
+ if (!*pllv->soft_vote)
+ pll_vote_clk_disable(c);
+
+ spin_unlock_irqrestore(&soft_vote_lock, flags);
+}
+
+struct clk_ops clk_ops_pll_acpu_vote = {
+ .enable = pll_acpu_vote_clk_enable,
+ .disable = pll_acpu_vote_clk_disable,
+ .is_enabled = pll_vote_clk_is_enabled,
+ .get_parent = pll_vote_clk_get_parent,
+};
+
static void __init __set_fsm_mode(void __iomem *mode_reg,
u32 bias_count, u32 lock_count)
{
diff --git a/arch/arm/mach-msm/clock-pll.h b/arch/arm/mach-msm/clock-pll.h
index 5c7c304..33b35a8 100644
--- a/arch/arm/mach-msm/clock-pll.h
+++ b/arch/arm/mach-msm/clock-pll.h
@@ -58,6 +58,45 @@
void msm_shared_pll_control_init(void);
/**
+ * struct pll_freq_tbl - generic PLL frequency definition
+ * @freq_hz: pll frequency in hz
+ * @l_val: pll l value
+ * @m_val: pll m value
+ * @n_val: pll n value
+ * @post_div_val: pll post divider value
+ * @pre_div_val: pll pre-divider value
+ * @vco_val: pll vco value
+ */
+struct pll_freq_tbl {
+ const u32 freq_hz;
+ const u32 l_val;
+ const u32 m_val;
+ const u32 n_val;
+ const u32 post_div_val;
+ const u32 pre_div_val;
+ const u32 vco_val;
+};
+
+/**
+ * struct pll_config_masks - PLL config masks struct
+ * @post_div_mask: mask for post divider bits location
+ * @pre_div_mask: mask for pre-divider bits location
+ * @vco_mask: mask for vco bits location
+ * @mn_en_mask: ORed with pll config register to enable the mn counter
+ * @main_output_mask: ORed with pll config register to enable the main output
+ */
+struct pll_config_masks {
+ u32 post_div_mask;
+ u32 pre_div_mask;
+ u32 vco_mask;
+ u32 mn_en_mask;
+ u32 main_output_mask;
+};
+
+#define PLL_FREQ_END (UINT_MAX-1)
+#define PLL_F_END { .freq_hz = PLL_FREQ_END }
+
+/**
* struct pll_vote_clk - phase locked loop (HW voteable)
* @soft_vote: soft voting variable for multiple PLL software instances
* @soft_vote_mask: soft voting mask for multiple PLL software instances
@@ -82,6 +121,11 @@
};
extern struct clk_ops clk_ops_pll_vote;
+extern struct clk_ops clk_ops_pll_acpu_vote;
+
+/* Soft voting values */
+#define PLL_SOFT_VOTE_PRIMARY BIT(0)
+#define PLL_SOFT_VOTE_ACPU BIT(1)
static inline struct pll_vote_clk *to_pll_vote_clk(struct clk *c)
{
@@ -91,15 +135,30 @@
/**
* struct pll_clk - phase locked loop
* @mode_reg: enable register
+ * @l_reg: l value register
+ * @m_reg: m value register
+ * @n_reg: n value register
+ * @config_reg: configuration register, contains mn divider enable, pre divider,
+ * post divider and vco configuration. register name can be configure register
+ * or user_ctl register depending on targets
* @status_reg: status register, contains the lock detection bit
+ * @masks: masks used for settings in config_reg
+ * @freq_tbl: pll freq table
* @parent: clock source
* @c: clk
* @base: pointer to base address of ioremapped registers.
*/
struct pll_clk {
void __iomem *const mode_reg;
+ void __iomem *const l_reg;
+ void __iomem *const m_reg;
+ void __iomem *const n_reg;
+ void __iomem *const config_reg;
void __iomem *const status_reg;
+ struct pll_config_masks masks;
+ struct pll_freq_tbl *freq_tbl;
+
struct clk *parent;
struct clk c;
void *const __iomem *base;
@@ -115,14 +174,6 @@
int sr_pll_clk_enable(struct clk *c);
int sr_hpm_lp_pll_clk_enable(struct clk *c);
-/*
- * PLL vote clock APIs
- */
-int pll_vote_clk_enable(struct clk *c);
-void pll_vote_clk_disable(struct clk *c);
-struct clk *pll_vote_clk_get_parent(struct clk *c);
-int pll_vote_clk_is_enabled(struct clk *c);
-
struct pll_config {
u32 l;
u32 m;
diff --git a/arch/arm/mach-msm/clock.c b/arch/arm/mach-msm/clock.c
index c30bd79..5100980 100644
--- a/arch/arm/mach-msm/clock.c
+++ b/arch/arm/mach-msm/clock.c
@@ -425,6 +425,19 @@
static struct clock_init_data *clk_init_data;
+static void init_sibling_lists(struct clk_lookup *clock_tbl, size_t num_clocks)
+{
+ struct clk *clk, *parent;
+ unsigned n;
+
+ for (n = 0; n < num_clocks; n++) {
+ clk = clock_tbl[n].clk;
+ parent = clk_get_parent(clk);
+ if (parent && list_empty(&clk->siblings))
+ list_add(&clk->siblings, &parent->children);
+ }
+}
+
/**
* msm_clock_register() - Register additional clock tables
* @table: Table of clocks
@@ -443,6 +456,7 @@
if (!table)
return -EINVAL;
+ init_sibling_lists(table, size);
clkdev_add_table(table, size);
clock_debug_register(table, size);
@@ -514,7 +528,6 @@
unsigned n;
struct clk_lookup *clock_tbl;
size_t num_clocks;
- struct clk *clk;
if (!data)
return -EINVAL;
@@ -526,13 +539,7 @@
clock_tbl = data->table;
num_clocks = data->size;
- for (n = 0; n < num_clocks; n++) {
- struct clk *parent;
- clk = clock_tbl[n].clk;
- parent = clk_get_parent(clk);
- if (parent && list_empty(&clk->siblings))
- list_add(&clk->siblings, &parent->children);
- }
+ init_sibling_lists(clock_tbl, num_clocks);
/*
* Detect and preserve initial clock state until clock_late_init() or
diff --git a/arch/arm/mach-msm/devices-iommu.c b/arch/arm/mach-msm/devices-iommu.c
index 6434a63..63c1dbd 100644
--- a/arch/arm/mach-msm/devices-iommu.c
+++ b/arch/arm/mach-msm/devices-iommu.c
@@ -1017,13 +1017,13 @@
ARRAY_SIZE(msm_iommu_gfx2d_devs));
}
- if (cpu_is_apq8064() || cpu_is_msm8960ab() || cpu_is_apq8064ab()) {
+ if (soc_class_is_apq8064() || cpu_is_msm8960ab()) {
platform_add_devices(msm_iommu_jpegd_devs,
ARRAY_SIZE(msm_iommu_jpegd_devs));
platform_add_devices(msm_iommu_adreno3xx_gfx_devs,
ARRAY_SIZE(msm_iommu_adreno3xx_gfx_devs));
}
- if (cpu_is_apq8064() || cpu_is_apq8064ab())
+ if (soc_class_is_apq8064())
platform_add_devices(msm_iommu_vcap_devs,
ARRAY_SIZE(msm_iommu_vcap_devs));
@@ -1039,14 +1039,14 @@
ARRAY_SIZE(msm_iommu_gfx2d_ctx_devs));
}
- if (cpu_is_apq8064() || cpu_is_msm8960ab() || cpu_is_apq8064ab()) {
+ if (soc_class_is_apq8064() || cpu_is_msm8960ab()) {
platform_add_devices(msm_iommu_jpegd_ctx_devs,
ARRAY_SIZE(msm_iommu_jpegd_ctx_devs));
platform_add_devices(msm_iommu_adreno3xx_ctx_devs,
ARRAY_SIZE(msm_iommu_adreno3xx_ctx_devs));
}
- if (cpu_is_apq8064() || cpu_is_apq8064ab())
+ if (soc_class_is_apq8064())
platform_add_devices(msm_iommu_vcap_ctx_devs,
ARRAY_SIZE(msm_iommu_vcap_ctx_devs));
@@ -1081,12 +1081,12 @@
for (i = 0; i < ARRAY_SIZE(msm_iommu_jpegd_devs); i++)
platform_device_unregister(msm_iommu_jpegd_devs[i]);
}
- if (cpu_is_apq8064() || cpu_is_apq8064ab()) {
+ if (soc_class_is_apq8064()) {
for (i = 0; i < ARRAY_SIZE(msm_iommu_vcap_ctx_devs); i++)
platform_device_unregister(msm_iommu_vcap_ctx_devs[i]);
}
- if (cpu_is_apq8064() || cpu_is_msm8960ab() || cpu_is_apq8064ab()) {
+ if (soc_class_is_apq8064() || cpu_is_msm8960ab()) {
for (i = 0; i < ARRAY_SIZE(msm_iommu_adreno3xx_ctx_devs);
i++)
platform_device_unregister(
@@ -1097,7 +1097,7 @@
platform_device_unregister(
msm_iommu_jpegd_ctx_devs[i]);
- if (cpu_is_apq8064() || cpu_is_apq8064ab()) {
+ if (soc_class_is_apq8064()) {
for (i = 0; i < ARRAY_SIZE(msm_iommu_vcap_devs);
i++)
platform_device_unregister(
diff --git a/arch/arm/mach-msm/devices-msm7x27a.c b/arch/arm/mach-msm/devices-msm7x27a.c
index c0ad39b..9943812 100644
--- a/arch/arm/mach-msm/devices-msm7x27a.c
+++ b/arch/arm/mach-msm/devices-msm7x27a.c
@@ -1730,9 +1730,9 @@
.step_quot = ~0,
.tgt_volt_offset = 0,
.turbo_Vmax = 1350000,
- .turbo_Vmin = 1100000,
+ .turbo_Vmin = 1150000,
.nom_Vmax = 1350000,
- .nom_Vmin = 1100000,
+ .nom_Vmin = 1150000,
.calibrated_uV = 1300000,
},
};
@@ -1864,10 +1864,25 @@
msm_cpr_mode_data[TURBO_MODE].calibrated_uV =
msm_c2_pmic_mv[cpr_info->pvs_fuse & 0x1F];
+ if ((cpr_info->floor_fuse & 0x3) == 0x0) {
+ msm_cpr_mode_data[TURBO_MODE].nom_Vmin = 1000000;
+ msm_cpr_mode_data[TURBO_MODE].turbo_Vmin = 1100000;
+ } else if ((cpr_info->floor_fuse & 0x3) == 0x1) {
+ msm_cpr_mode_data[TURBO_MODE].nom_Vmin = 1050000;
+ msm_cpr_mode_data[TURBO_MODE].turbo_Vmin = 1100000;
+ } else if ((cpr_info->floor_fuse & 0x3) == 0x2) {
+ msm_cpr_mode_data[TURBO_MODE].nom_Vmin = 1100000;
+ msm_cpr_mode_data[TURBO_MODE].turbo_Vmin = 1100000;
+ }
+
pr_debug("%s: cpr: ring_osc: 0x%x\n", __func__,
msm_cpr_mode_data[TURBO_MODE].ring_osc);
pr_debug("%s: cpr: turbo_quot: 0x%x\n", __func__, cpr_info->turbo_quot);
pr_debug("%s: cpr: pvs_fuse: 0x%x\n", __func__, cpr_info->pvs_fuse);
+ pr_debug("%s: cpr: floor_fuse: 0x%x\n", __func__, cpr_info->floor_fuse);
+ pr_debug("%s: cpr: nom_Vmin: %d, turbo_Vmin: %d\n", __func__,
+ msm_cpr_mode_data[TURBO_MODE].nom_Vmin,
+ msm_cpr_mode_data[TURBO_MODE].turbo_Vmin);
kfree(cpr_info);
if (msm8625_cpu_id() == MSM8625A)
diff --git a/arch/arm/mach-msm/include/mach/board.h b/arch/arm/mach-msm/include/mach/board.h
index 0b53bad..ff4776a 100644
--- a/arch/arm/mach-msm/include/mach/board.h
+++ b/arch/arm/mach-msm/include/mach/board.h
@@ -398,6 +398,7 @@
u32 splash_screen_addr;
u32 splash_screen_size;
char mdp_iommu_split_domain;
+ u32 avtimer_phy;
};
diff --git a/arch/arm/mach-msm/include/mach/iommu.h b/arch/arm/mach-msm/include/mach/iommu.h
index 41a59af6..69ddeb9 100644
--- a/arch/arm/mach-msm/include/mach/iommu.h
+++ b/arch/arm/mach-msm/include/mach/iommu.h
@@ -20,9 +20,11 @@
extern pgprot_t pgprot_kernel;
extern struct platform_device *msm_iommu_root_dev;
+extern struct bus_type msm_iommu_sec_bus_type;
/* Domain attributes */
#define MSM_IOMMU_DOMAIN_PT_CACHEABLE 0x1
+#define MSM_IOMMU_DOMAIN_PT_SECURE 0x2
/* Mask for the cache policy attribute */
#define MSM_IOMMU_CP_MASK 0x03
@@ -103,6 +105,7 @@
const char *name;
struct regulator *gdsc;
struct msm_iommu_bfb_settings *bfb_settings;
+ int sec_id;
};
/**
diff --git a/arch/arm/mach-msm/include/mach/iommu_domains.h b/arch/arm/mach-msm/include/mach/iommu_domains.h
index 1d538f2..01bc479 100644
--- a/arch/arm/mach-msm/include/mach/iommu_domains.h
+++ b/arch/arm/mach-msm/include/mach/iommu_domains.h
@@ -15,6 +15,8 @@
#include <linux/memory_alloc.h>
+#define MSM_IOMMU_DOMAIN_SECURE 0x1
+
enum {
VIDEO_DOMAIN,
CAMERA_DOMAIN,
@@ -69,6 +71,7 @@
int npartitions;
const char *client_name;
unsigned int domain_flags;
+ unsigned int is_secure;
};
#if defined(CONFIG_MSM_IOMMU)
diff --git a/arch/arm/mach-msm/include/mach/irqs-8625.h b/arch/arm/mach-msm/include/mach/irqs-8625.h
index 2ec0e21..2a61118 100644
--- a/arch/arm/mach-msm/include/mach/irqs-8625.h
+++ b/arch/arm/mach-msm/include/mach/irqs-8625.h
@@ -16,6 +16,10 @@
#define GIC_PPI_START 16
#define GIC_SPI_START 32
+#ifdef CONFIG_MSM_FIQ
+#define FIQ_START 0
+#endif
+
/* As per QGIC2 PPI 16 aka 0 is reserved */
#define MSM8625_INT_A5_PMU_IRQ (GIC_PPI_START + 1)
#define MSM8625_INT_DEBUG_TIMER_EXP (GIC_PPI_START + 2)
diff --git a/arch/arm/mach-msm/include/mach/msm-krait-l2-accessors.h b/arch/arm/mach-msm/include/mach/msm-krait-l2-accessors.h
index f835e82..a35ff4d 100644
--- a/arch/arm/mach-msm/include/mach/msm-krait-l2-accessors.h
+++ b/arch/arm/mach-msm/include/mach/msm-krait-l2-accessors.h
@@ -2,7 +2,7 @@
#define __ASM_ARCH_MSM_MSM_KRAIT_L2_ACCESSORS_H
/*
- * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2011,2012, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -13,8 +13,21 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
+
+#ifdef CONFIG_ARCH_MSM_KRAIT
extern void set_l2_indirect_reg(u32 reg_addr, u32 val);
extern u32 get_l2_indirect_reg(u32 reg_addr);
extern u32 set_get_l2_indirect_reg(u32 reg_addr, u32 val);
+#else
+static inline void set_l2_indirect_reg(u32 reg_addr, u32 val) {}
+static inline u32 get_l2_indirect_reg(u32 reg_addr)
+{
+ return 0;
+}
+static inline u32 set_get_l2_indirect_reg(u32 reg_addr, u32 val)
+{
+ return 0;
+}
+#endif
#endif
diff --git a/arch/arm/mach-msm/include/mach/msm_iomap-9625.h b/arch/arm/mach-msm/include/mach/msm_iomap-9625.h
index 652563c..89252a5 100644
--- a/arch/arm/mach-msm/include/mach/msm_iomap-9625.h
+++ b/arch/arm/mach-msm/include/mach/msm_iomap-9625.h
@@ -25,6 +25,12 @@
#define MSM9625_SHARED_RAM_PHYS 0x00000000
+#define MSM9625_QGIC_DIST_PHYS 0xF9000000
+#define MSM9625_QGIC_DIST_SIZE SZ_4K
+
+#define MSM9625_QGIC_CPU_PHYS 0xF9002000
+#define MSM9625_QGIC_CPU_SIZE SZ_4K
+
#define MSM9625_APCS_GCC_PHYS 0xF9011000
#define MSM9625_APCS_GCC_SIZE SZ_4K
diff --git a/arch/arm/mach-msm/include/mach/ocmem.h b/arch/arm/mach-msm/include/mach/ocmem.h
index 904de5e..cb8aae0 100644
--- a/arch/arm/mach-msm/include/mach/ocmem.h
+++ b/arch/arm/mach-msm/include/mach/ocmem.h
@@ -134,6 +134,9 @@
int ocmem_unmap(int client_id, struct ocmem_buf *buffer,
struct ocmem_map_list *list);
+int ocmem_dump(int client_id, struct ocmem_buf *buffer,
+ unsigned long dst_phys_addr);
+
/* Priority Enforcement APIs */
int ocmem_evict(int client_id);
diff --git a/arch/arm/mach-msm/include/mach/ocmem_priv.h b/arch/arm/mach-msm/include/mach/ocmem_priv.h
index 09dfac0..0b30c26 100644
--- a/arch/arm/mach-msm/include/mach/ocmem_priv.h
+++ b/arch/arm/mach-msm/include/mach/ocmem_priv.h
@@ -56,6 +56,8 @@
NR_TRANSFER_FAILS,
NR_EVICTIONS,
NR_RESTORES,
+ NR_DUMP_REQUESTS,
+ NR_DUMP_COMPLETE,
NR_OCMEM_ZSTAT_ITEMS,
};
@@ -198,6 +200,7 @@
int process_evict(int);
int process_restore(int);
int process_shrink(int, struct ocmem_handle *, unsigned long);
+int process_dump(int, struct ocmem_handle *, unsigned long);
int ocmem_rdm_transfer(int, struct ocmem_map_list *,
unsigned long, int);
int ocmem_clear(unsigned long, unsigned long);
diff --git a/arch/arm/mach-msm/include/mach/qdsp6v2/apr_us.h b/arch/arm/mach-msm/include/mach/qdsp6v2/apr_us.h
index da639ce..22f343c 100644
--- a/arch/arm/mach-msm/include/mach/qdsp6v2/apr_us.h
+++ b/arch/arm/mach-msm/include/mach/qdsp6v2/apr_us.h
@@ -17,20 +17,6 @@
/* ======================================================================= */
/* Session Level commands */
-#define USM_SESSION_CMD_MEMORY_MAP 0x00012304
-struct usm_stream_cmd_memory_map {
- struct apr_hdr hdr;
- u32 buf_add;
- u32 buf_size;
- u16 mempool_id;
- u16 reserved;
-} __packed;
-
-#define USM_SESSION_CMD_MEMORY_UNMAP 0x00012305
-struct usm_stream_cmd_memory_unmap {
- struct apr_hdr hdr;
- u32 buf_add;
-} __packed;
#define USM_SESSION_CMD_RUN 0x00012306
struct usm_stream_cmd_run {
@@ -113,31 +99,6 @@
u8 transp_data[USM_MAX_CFG_DATA_SIZE];
} __packed;
-
-#define USM_DATA_CMD_READ 0x0001230E
-struct usm_stream_cmd_read {
- struct apr_hdr hdr;
- u32 buf_add;
- u32 buf_size;
- u32 uid;
- u32 counter;
-} __packed;
-
-#define USM_DATA_EVENT_READ_DONE 0x0001230F
-
-#define USM_DATA_CMD_WRITE 0x00011273
-struct usm_stream_cmd_write {
- struct apr_hdr hdr;
- u32 buf_add;
- u32 buf_size;
- u32 uid;
- u32 msw_ts;
- u32 lsw_ts;
- u32 flags;
-} __packed;
-
-#define USM_DATA_EVENT_WRITE_DONE 0x00011274
-
/* Start/stop US signal detection */
#define USM_SESSION_CMD_SIGNAL_DETECT_MODE 0x00012719
diff --git a/arch/arm/mach-msm/include/mach/qdsp6v2/apr_us_a.h b/arch/arm/mach-msm/include/mach/qdsp6v2/apr_us_a.h
new file mode 100644
index 0000000..4008698
--- /dev/null
+++ b/arch/arm/mach-msm/include/mach/qdsp6v2/apr_us_a.h
@@ -0,0 +1,59 @@
+/* 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 __APR_US_A_H__
+#define __APR_US_A_H__
+
+#include "apr_us.h"
+
+/* ======================================================================= */
+/* Session Level commands */
+#define USM_SESSION_CMD_MEMORY_MAP 0x00012304
+struct usm_stream_cmd_memory_map {
+ struct apr_hdr hdr;
+ u32 buf_add;
+ u32 buf_size;
+ u16 mempool_id;
+ u16 reserved;
+} __packed;
+
+#define USM_SESSION_CMD_MEMORY_UNMAP 0x00012305
+struct usm_stream_cmd_memory_unmap {
+ struct apr_hdr hdr;
+ u32 buf_add;
+} __packed;
+
+#define USM_DATA_CMD_READ 0x0001230E
+struct usm_stream_cmd_read {
+ struct apr_hdr hdr;
+ u32 buf_add;
+ u32 buf_size;
+ u32 uid;
+ u32 counter;
+} __packed;
+
+#define USM_DATA_EVENT_READ_DONE 0x0001230F
+
+#define USM_DATA_CMD_WRITE 0x00011273
+struct usm_stream_cmd_write {
+ struct apr_hdr hdr;
+ u32 buf_add;
+ u32 buf_size;
+ u32 uid;
+ u32 msw_ts;
+ u32 lsw_ts;
+ u32 flags;
+} __packed;
+
+#define USM_DATA_EVENT_WRITE_DONE 0x00011274
+
+#endif /* __APR_US_A_H__ */
diff --git a/arch/arm/mach-msm/include/mach/qdsp6v2/audio_acdb.h b/arch/arm/mach-msm/include/mach/qdsp6v2/audio_acdb.h
index 3d33350..d34536d 100644
--- a/arch/arm/mach-msm/include/mach/qdsp6v2/audio_acdb.h
+++ b/arch/arm/mach-msm/include/mach/qdsp6v2/audio_acdb.h
@@ -25,6 +25,13 @@
MAX_AUDPROC_TYPES
};
+enum {
+ VOCPROC_CAL,
+ VOCSTRM_CAL,
+ VOCVOL_CAL,
+ MAX_VOCPROC_TYPES
+};
+
struct acdb_cal_block {
uint32_t cal_size;
uint32_t cal_kvaddr;
@@ -47,16 +54,20 @@
uint32_t get_adm_rx_topology(void);
uint32_t get_adm_tx_topology(void);
uint32_t get_asm_topology(void);
+void get_voice_cal_allocation(struct acdb_cal_block *cal_block);
void get_all_voice_cal(struct acdb_cal_block *cal_block);
void get_all_cvp_cal(struct acdb_cal_block *cal_block);
void get_all_vocproc_cal(struct acdb_cal_block *cal_block);
void get_all_vocstrm_cal(struct acdb_cal_block *cal_block);
void get_all_vocvol_cal(struct acdb_cal_block *cal_block);
+void get_voice_col_data(uint32_t vocproc_type,
+ struct acdb_cal_block *cal_block);
void get_anc_cal(struct acdb_cal_block *cal_block);
void get_afe_cal(int32_t path, struct acdb_cal_block *cal_block);
void get_audproc_cal(int32_t path, struct acdb_cal_block *cal_block);
void get_audstrm_cal(int32_t path, struct acdb_cal_block *cal_block);
void get_audvol_cal(int32_t path, struct acdb_cal_block *cal_block);
+void get_vocproc_dev_cfg_cal(struct acdb_cal_block *cal_block);
void get_vocproc_cal(struct acdb_cal_data *cal_data);
void get_vocstrm_cal(struct acdb_cal_data *cal_data);
void get_vocvol_cal(struct acdb_cal_data *cal_data);
diff --git a/arch/arm/mach-msm/include/mach/socinfo.h b/arch/arm/mach-msm/include/mach/socinfo.h
index 86045b9..5c88101 100644
--- a/arch/arm/mach-msm/include/mach/socinfo.h
+++ b/arch/arm/mach-msm/include/mach/socinfo.h
@@ -436,4 +436,20 @@
return 0;
#endif
}
+
+static inline int soc_class_is_msm8960(void)
+{
+ return cpu_is_msm8960() || cpu_is_msm8960ab();
+}
+
+static inline int soc_class_is_apq8064(void)
+{
+ return cpu_is_apq8064() || cpu_is_apq8064ab();
+}
+
+static inline int soc_class_is_msm8930(void)
+{
+ return cpu_is_msm8930() || cpu_is_msm8930aa() || cpu_is_msm8627();
+}
+
#endif
diff --git a/arch/arm/mach-msm/io.c b/arch/arm/mach-msm/io.c
index 4b81ce7..c2c9233 100644
--- a/arch/arm/mach-msm/io.c
+++ b/arch/arm/mach-msm/io.c
@@ -25,6 +25,7 @@
#include <mach/hardware.h>
#include <asm/page.h>
#include <mach/msm_iomap.h>
+#include <mach/memory.h>
#include <asm/mach/map.h>
#include <linux/dma-mapping.h>
@@ -99,6 +100,7 @@
asm("mcr p15, 0, %0, c15, c2, 4" : : "r" (0));
#endif
msm_map_io(msm_io_desc, ARRAY_SIZE(msm_io_desc));
+ map_page_strongly_ordered();
}
#endif
@@ -452,6 +454,7 @@
void __init msm_map_msm8625_io(void)
{
msm_map_io(msm8625_io_desc, ARRAY_SIZE(msm8625_io_desc));
+ map_page_strongly_ordered();
}
#else
void __init msm_map_msm8625_io(void) { return; }
@@ -460,6 +463,8 @@
#ifdef CONFIG_ARCH_MSM9625
static struct map_desc msm9625_io_desc[] __initdata = {
MSM_CHIP_DEVICE(APCS_GCC, MSM9625),
+ MSM_CHIP_DEVICE(QGIC_DIST, MSM9625),
+ MSM_CHIP_DEVICE(QGIC_CPU, MSM9625),
MSM_CHIP_DEVICE(TLMM, MSM9625),
MSM_CHIP_DEVICE(MPM2_PSHOLD, MSM9625),
MSM_CHIP_DEVICE(TMR, MSM9625),
diff --git a/arch/arm/mach-msm/iommu_domains.c b/arch/arm/mach-msm/iommu_domains.c
index 3acb6d8..75e56fe 100644
--- a/arch/arm/mach-msm/iommu_domains.c
+++ b/arch/arm/mach-msm/iommu_domains.c
@@ -267,6 +267,7 @@
else
return NULL;
}
+EXPORT_SYMBOL(msm_get_iommu_domain);
int msm_allocate_iova_address(unsigned int iommu_domain,
unsigned int partition_no,
@@ -344,6 +345,7 @@
int i;
struct msm_iova_data *data;
struct mem_pool *pools;
+ struct bus_type *bus;
if (!layout)
return -EINVAL;
@@ -389,11 +391,14 @@
}
}
+ bus = layout->is_secure == MSM_IOMMU_DOMAIN_SECURE ?
+ &msm_iommu_sec_bus_type :
+ &platform_bus_type;
+
data->pools = pools;
data->npools = layout->npartitions;
data->domain_num = atomic_inc_return(&domain_nums);
- data->domain = iommu_domain_alloc(&platform_bus_type,
- layout->domain_flags);
+ data->domain = iommu_domain_alloc(bus, layout->domain_flags);
add_domain(data);
diff --git a/arch/arm/mach-msm/memory.c b/arch/arm/mach-msm/memory.c
index a785389..7d7380b 100644
--- a/arch/arm/mach-msm/memory.c
+++ b/arch/arm/mach-msm/memory.c
@@ -44,14 +44,14 @@
#include <../../mm/mm.h>
#include <linux/fmem.h>
-void *strongly_ordered_page;
-char strongly_ordered_mem[PAGE_SIZE*2-4];
+#if defined(CONFIG_ARCH_MSM7X27)
+static void *strongly_ordered_page;
+static char strongly_ordered_mem[PAGE_SIZE*2-4];
-void map_page_strongly_ordered(void)
+void __init map_page_strongly_ordered(void)
{
-#if defined(CONFIG_ARCH_MSM7X27) && !defined(CONFIG_ARCH_MSM7X27A)
long unsigned int phys;
- struct map_desc map;
+ struct map_desc map[1];
if (strongly_ordered_page)
return;
@@ -59,33 +59,26 @@
strongly_ordered_page = (void*)PFN_ALIGN((int)&strongly_ordered_mem);
phys = __pa(strongly_ordered_page);
- map.pfn = __phys_to_pfn(phys);
- map.virtual = MSM_STRONGLY_ORDERED_PAGE;
- map.length = PAGE_SIZE;
- map.type = MT_DEVICE_STRONGLY_ORDERED;
- create_mapping(&map);
+ map[0].pfn = __phys_to_pfn(phys);
+ map[0].virtual = MSM_STRONGLY_ORDERED_PAGE;
+ map[0].length = PAGE_SIZE;
+ map[0].type = MT_MEMORY_SO;
+ iotable_init(map, ARRAY_SIZE(map));
printk(KERN_ALERT "Initialized strongly ordered page successfully\n");
-#endif
}
-EXPORT_SYMBOL(map_page_strongly_ordered);
+#else
+void map_page_strongly_ordered(void) { }
+#endif
+#if defined(CONFIG_ARCH_MSM7X27)
void write_to_strongly_ordered_memory(void)
{
-#if defined(CONFIG_ARCH_MSM7X27) && !defined(CONFIG_ARCH_MSM7X27A)
- if (!strongly_ordered_page) {
- if (!in_interrupt())
- map_page_strongly_ordered();
- else {
- printk(KERN_ALERT "Cannot map strongly ordered page in "
- "Interrupt Context\n");
- /* capture it here before the allocation fails later */
- BUG();
- }
- }
*(int *)MSM_STRONGLY_ORDERED_PAGE = 0;
-#endif
}
+#else
+void write_to_strongly_ordered_memory(void) { }
+#endif
EXPORT_SYMBOL(write_to_strongly_ordered_memory);
/* These cache related routines make the assumption (if outer cache is
@@ -278,7 +271,8 @@
unsigned long msm_fixed_area_start;
memory_pool_init();
- reserve_info->calculate_reserve_sizes();
+ if (reserve_info->calculate_reserve_sizes)
+ reserve_info->calculate_reserve_sizes();
msm_fixed_area_size = reserve_info->fixed_area_size;
msm_fixed_area_start = reserve_info->fixed_area_start;
diff --git a/arch/arm/mach-msm/mpm-8625.c b/arch/arm/mach-msm/mpm-8625.c
index fe7ffff..c70ff5c 100644
--- a/arch/arm/mach-msm/mpm-8625.c
+++ b/arch/arm/mach-msm/mpm-8625.c
@@ -92,6 +92,7 @@
[MSM8625_INT_GPIO_GROUP2] = SMSM_FAKE_IRQ,
[MSM8625_INT_A9_M2A_0] = SMSM_FAKE_IRQ,
[MSM8625_INT_A9_M2A_1] = SMSM_FAKE_IRQ,
+ [MSM8625_INT_A9_M2A_2] = SMSM_FAKE_IRQ,
[MSM8625_INT_A9_M2A_5] = SMSM_FAKE_IRQ,
[MSM8625_INT_GP_TIMER_EXP] = SMSM_FAKE_IRQ,
[MSM8625_INT_DEBUG_TIMER_EXP] = SMSM_FAKE_IRQ,
diff --git a/arch/arm/mach-msm/msm7k_fiq.c b/arch/arm/mach-msm/msm7k_fiq.c
new file mode 100644
index 0000000..421b4f9
--- /dev/null
+++ b/arch/arm/mach-msm/msm7k_fiq.c
@@ -0,0 +1,86 @@
+/* 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/module.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/irq.h>
+#include <asm/fiq.h>
+#include <asm/hardware/gic.h>
+#include <asm/cacheflush.h>
+#include <mach/irqs-8625.h>
+#include <mach/socinfo.h>
+
+#include "msm_watchdog.h"
+
+#define MODULE_NAME "MSM7K_FIQ"
+
+struct msm_watchdog_dump msm_dump_cpu_ctx;
+static int fiq_counter;
+void *msm7k_fiq_stack;
+
+/* Called from the FIQ asm handler */
+void msm7k_fiq_handler(void)
+{
+ struct irq_data *d;
+ struct irq_chip *c;
+
+ pr_info("Fiq is received %s\n", __func__);
+ fiq_counter++;
+ d = irq_get_irq_data(MSM8625_INT_A9_M2A_2);
+ c = irq_data_get_irq_chip(d);
+ c->irq_mask(d);
+ local_irq_disable();
+
+ /* Clear the IRQ from the ENABLE_SET */
+ gic_clear_irq_pending(MSM8625_INT_A9_M2A_2);
+ local_irq_enable();
+ flush_cache_all();
+ outer_flush_all();
+ return;
+}
+
+struct fiq_handler msm7k_fh = {
+ .name = MODULE_NAME,
+};
+
+static int __init msm_setup_fiq_handler(void)
+{
+ int ret = 0;
+
+ claim_fiq(&msm7k_fh);
+ set_fiq_handler(&msm7k_fiq_start, msm7k_fiq_length);
+ msm7k_fiq_stack = (void *)__get_free_pages(GFP_KERNEL,
+ THREAD_SIZE_ORDER);
+ if (msm7k_fiq_stack == NULL) {
+ pr_err("FIQ STACK SETUP IS NOT SUCCESSFUL\n");
+ return -ENOMEM;
+ }
+
+ fiq_set_type(MSM8625_INT_A9_M2A_2, IRQF_TRIGGER_RISING);
+ gic_set_irq_secure(MSM8625_INT_A9_M2A_2);
+ enable_irq(MSM8625_INT_A9_M2A_2);
+ pr_info("%s : msm7k fiq setup--done\n", __func__);
+ return ret;
+}
+
+static int __init init7k_fiq(void)
+{
+ if (!cpu_is_msm8625())
+ return 0;
+
+ if (msm_setup_fiq_handler())
+ pr_err("MSM7K FIQ INIT FAILED\n");
+
+ return 0;
+}
+late_initcall(init7k_fiq);
diff --git a/arch/arm/mach-msm/msm7k_fiq_handler.S b/arch/arm/mach-msm/msm7k_fiq_handler.S
new file mode 100644
index 0000000..e2da9cf
--- /dev/null
+++ b/arch/arm/mach-msm/msm7k_fiq_handler.S
@@ -0,0 +1,94 @@
+/* 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/linkage.h>
+#include <asm/assembler.h>
+
+#define VERSION_ID 0x1
+#define MAGIC 0xDEAD0000 | VERSION_ID
+ .text
+ .align 3
+
+ENTRY(msm7k_fiq_start)
+ sub r14, r14, #4 @return address
+ ldr r8, Lmsm_fiq_stack
+ ldr sp, [r8] @get stack
+ stmfa sp!, {r0-r7, lr}
+ stmfa sp!, {r8-r9}
+ ldr r8, Ldump_cpu_ctx
+ @ store magic to indicate a valid dump
+ ldr r9, Lmagic
+ str r9, [r8], #4
+ @ get the current cpsr
+ mrs r9, cpsr
+ str r9, [r8],#4
+ stmia r8!, {r0-r7} @ get the USR r0-r7
+ mov r4, r8
+ mov r5, #PSR_I_BIT | PSR_F_BIT | SYSTEM_MODE
+ msr cpsr_c, r5 @ select SYSTEM mode
+ stmia r4!, {r8-r14}
+ mov r5, #PSR_I_BIT | PSR_F_BIT | IRQ_MODE
+ msr cpsr_c, r5 @ select IRQ mode
+ mrs r5, spsr
+ str r5, [r4], #4
+ stmia r4!, {r13-r14}
+ mov r5, #PSR_I_BIT | PSR_F_BIT | SVC_MODE
+ msr cpsr_c, r5 @ select SVC mode
+ mrs r5, spsr
+ str r5, [r4], #4
+ stmia r4!, {r13-r14}
+ mov r0, r13
+ mov r1, r14
+ mov r5, #PSR_I_BIT | PSR_F_BIT | FIQ_MODE
+ msr cpsr_c, r5 @ select FIQ mode
+ stmfa sp!, {r0-r1}
+ mov r5, #PSR_I_BIT | PSR_F_BIT | ABT_MODE
+ msr cpsr_c, r5 @ select ABT mode
+ mrs r5, spsr
+ str r5, [r4], #4
+ stmia r4!, {r13-r14}
+ mov r5, #PSR_I_BIT | PSR_F_BIT | UND_MODE
+ msr cpsr_c, r5 @ select UND mode
+ mrs r5, spsr
+ str r5, [r4], #4
+ stmia r4!, {r13-r14}
+ mov r5, #PSR_I_BIT | PSR_F_BIT | FIQ_MODE
+ msr cpsr_c, r5 @ select FIQ mode
+ mrs r5, spsr
+ str r5, [r4], #4
+ stmia r4!, {r8-r14}
+ dsb
+ mov r5, #PSR_I_BIT | PSR_F_BIT | SVC_MODE
+ msr cpsr_c, r5 @ select SVC mode
+ ldr r2, Lmsm_fiq_handler
+ blx r2
+ mov r5, #PSR_I_BIT | PSR_F_BIT | FIQ_MODE
+ msr cpsr_c, r5 @ select FIQ mode
+ ldmfa sp!, {r0, r1}
+ mov r5, #PSR_I_BIT | PSR_F_BIT | SVC_MODE
+ msr cpsr_c, r5 @ select SVC mode
+ mov r13, r0
+ mov r14, r1
+ mov r5, #PSR_I_BIT | PSR_F_BIT | FIQ_MODE
+ msr cpsr_c, r5 @ select SVC mode
+ ldmfa sp!, {r8-r9}
+ ldmfa sp!, {r0-r7, pc}^
+Ldump_cpu_ctx:
+ .word msm_dump_cpu_ctx
+Lmsm_fiq_stack:
+ .word msm7k_fiq_stack
+Lmagic:
+ .word MAGIC
+Lmsm_fiq_handler:
+ .word msm7k_fiq_handler
+ENTRY(msm7k_fiq_length)
+ .word . - msm7k_fiq_start
diff --git a/arch/arm/mach-msm/msm_smem_iface.c b/arch/arm/mach-msm/msm_smem_iface.c
index b09fda5..b35467b 100644
--- a/arch/arm/mach-msm/msm_smem_iface.c
+++ b/arch/arm/mach-msm/msm_smem_iface.c
@@ -41,4 +41,5 @@
cpr_info->ring_osc = temp_cpr_info->ring_osc;
cpr_info->turbo_quot = temp_cpr_info->turbo_quot;
cpr_info->pvs_fuse = temp_cpr_info->pvs_fuse;
+ cpr_info->floor_fuse = temp_cpr_info->floor_fuse;
}
diff --git a/arch/arm/mach-msm/msm_smem_iface.h b/arch/arm/mach-msm/msm_smem_iface.h
index 2da0232..a6d6714 100644
--- a/arch/arm/mach-msm/msm_smem_iface.h
+++ b/arch/arm/mach-msm/msm_smem_iface.h
@@ -33,10 +33,12 @@
uint8_t iv[MAX_SEC_KEY_PAYLOAD]; /* Initialization Vector */
};
+/* floor_fuse to re-use the fuse bit earlier used by ring_osc */
struct cpr_info_type {
- uint8_t ring_osc; /* CPR FUSE [0]: TURBO RO SEL BIT */
- uint8_t turbo_quot; /* CPRFUSE[1:7] : TURBO QUOT*/
- uint8_t pvs_fuse; /* TURBO PVS FUSE */
+ uint8_t ring_osc; /* CPR FUSE [0]: TURBO RO SEL BIT */
+ uint8_t turbo_quot; /* CPRFUSE[1:7] : TURBO QUOT*/
+ uint8_t pvs_fuse; /* TURBO PVS FUSE */
+ uint8_t floor_fuse; /* Vmin Selection. b1: FAB_ID(2), b0: CPR_fuse[0] */
};
struct boot_info_for_apps {
@@ -47,7 +49,7 @@
uint16_t boot_keys_pressed[MAX_KEY_EVENTS]; /* Log of key presses */
uint32_t timetick; /* Modem tick timer value before apps out of reset */
struct cpr_info_type cpr_info;
- uint8_t PAD[25];
+ uint8_t PAD[24];
};
void msm_smem_get_cpr_info(struct cpr_info_type *cpr_info);
diff --git a/arch/arm/mach-msm/msm_watchdog.h b/arch/arm/mach-msm/msm_watchdog.h
index 5fb82ee..7bf97d9 100644
--- a/arch/arm/mach-msm/msm_watchdog.h
+++ b/arch/arm/mach-msm/msm_watchdog.h
@@ -72,6 +72,7 @@
void msm_wdog_fiq_setup(void *stack);
extern unsigned int msm_wdog_fiq_length, msm_wdog_fiq_start;
+extern unsigned int msm7k_fiq_start, msm7k_fiq_length;
#ifdef CONFIG_MSM_WATCHDOG
void pet_watchdog(void);
diff --git a/arch/arm/mach-msm/msm_xo.c b/arch/arm/mach-msm/msm_xo.c
index 404b350..46d4a12 100644
--- a/arch/arm/mach-msm/msm_xo.c
+++ b/arch/arm/mach-msm/msm_xo.c
@@ -233,10 +233,9 @@
int ret;
struct msm_xo *xo = xo_voter->xo;
int is_d0 = xo == &msm_xo_sources[MSM_XO_TCXO_D0];
- int needs_workaround = cpu_is_msm8960() || cpu_is_apq8064() ||
- cpu_is_msm8930() || cpu_is_msm8930aa() ||
- cpu_is_msm9615() || cpu_is_msm8627() ||
- cpu_is_msm8960ab() || cpu_is_apq8064ab();
+ int needs_workaround = soc_class_is_msm8960() ||
+ soc_class_is_apq8064() ||
+ soc_class_is_msm8930() || cpu_is_msm9615();
if (xo_voter->mode == mode)
return 0;
diff --git a/arch/arm/mach-msm/ocmem.c b/arch/arm/mach-msm/ocmem.c
index 793fcc5..7829d8d 100644
--- a/arch/arm/mach-msm/ocmem.c
+++ b/arch/arm/mach-msm/ocmem.c
@@ -82,6 +82,8 @@
"Transfer failures",
"Evictions",
"Restorations",
+ "Dump requests",
+ "Dump completed",
};
struct ocmem_quota_table {
diff --git a/arch/arm/mach-msm/ocmem_api.c b/arch/arm/mach-msm/ocmem_api.c
index 6e094fd..689e015 100644
--- a/arch/arm/mach-msm/ocmem_api.c
+++ b/arch/arm/mach-msm/ocmem_api.c
@@ -399,6 +399,36 @@
}
EXPORT_SYMBOL(ocmem_unmap);
+int ocmem_dump(int client_id, struct ocmem_buf *buffer,
+ unsigned long dst_phys_addr)
+{
+ int ret = 0;
+ struct ocmem_handle *handle = NULL;
+
+ if (!check_id(client_id)) {
+ pr_err("ocmem: Invalid client id: %d\n", client_id);
+ return -EINVAL;
+ }
+
+ if (!zone_active(client_id)) {
+ pr_err("ocmem: Client id: %s (id: %d) not allowed to use OCMEM\n",
+ get_name(client_id), client_id);
+ return -EINVAL;
+ }
+
+ if (!buffer) {
+ pr_err("ocmem: Invalid buffer\n");
+ return -EINVAL;
+ }
+
+ handle = buffer_to_handle(buffer);
+ mutex_lock(&handle->handle_mutex);
+ ret = process_dump(client_id, handle, dst_phys_addr);
+ mutex_unlock(&handle->handle_mutex);
+ return ret;
+}
+EXPORT_SYMBOL(ocmem_dump);
+
unsigned long get_max_quota(int client_id)
{
if (!check_id(client_id)) {
diff --git a/arch/arm/mach-msm/ocmem_sched.c b/arch/arm/mach-msm/ocmem_sched.c
index e8854d5..c380c54 100644
--- a/arch/arm/mach-msm/ocmem_sched.c
+++ b/arch/arm/mach-msm/ocmem_sched.c
@@ -65,6 +65,7 @@
MAX_OCMEM_PRIO = PRIO_OCMEM + 1,
};
+static void __iomem *ocmem_vaddr;
static struct list_head sched_queue[MAX_OCMEM_PRIO];
static struct mutex sched_queue_mutex;
@@ -1670,6 +1671,34 @@
return -EINVAL;
}
+static int do_dump(struct ocmem_req *req, unsigned long addr)
+{
+
+ void __iomem *req_vaddr;
+ unsigned long offset = 0x0;
+
+ down_write(&req->rw_sem);
+
+ offset = phys_to_offset(req->req_start);
+
+ req_vaddr = ocmem_vaddr + offset;
+
+ if (!req_vaddr)
+ goto err_do_dump;
+
+ pr_debug("Dumping client %s buffer ocmem p: %lx (v: %p) to ddr %lx\n",
+ get_name(req->owner), req->req_start,
+ req_vaddr, addr);
+
+ memcpy((void *)addr, req_vaddr, req->req_sz);
+
+ up_write(&req->rw_sem);
+ return 0;
+err_do_dump:
+ up_write(&req->rw_sem);
+ return -EINVAL;
+}
+
int process_restore(int id)
{
struct ocmem_req *req = NULL;
@@ -1828,6 +1857,38 @@
return -EINVAL;
}
+int process_dump(int id, struct ocmem_handle *handle, unsigned long addr)
+{
+ struct ocmem_req *req = NULL;
+ int rc = 0;
+
+ req = handle_to_req(handle);
+
+ if (!req)
+ return -EINVAL;
+
+ if (!is_mapped(req)) {
+ pr_err("Buffer is not mapped\n");
+ goto dump_error;
+ }
+
+ inc_ocmem_stat(zone_of(req), NR_DUMP_REQUESTS);
+
+ mutex_lock(&sched_mutex);
+ rc = do_dump(req, addr);
+ mutex_unlock(&sched_mutex);
+
+ if (rc < 0)
+ goto dump_error;
+
+ inc_ocmem_stat(zone_of(req), NR_DUMP_COMPLETE);
+ return 0;
+
+dump_error:
+ pr_err("Dumping OCMEM memory failed for client %d\n", id);
+ return -EINVAL;
+}
+
static void ocmem_sched_wk_func(struct work_struct *work)
{
@@ -1906,6 +1967,7 @@
pdata = platform_get_drvdata(pdev);
mutex_init(&sched_mutex);
mutex_init(&sched_queue_mutex);
+ ocmem_vaddr = pdata->vbase;
for (i = MIN_PRIO; i < MAX_OCMEM_PRIO; i++)
INIT_LIST_HEAD(&sched_queue[i]);
diff --git a/arch/arm/mach-msm/platsmp.c b/arch/arm/mach-msm/platsmp.c
index 89003cf..5f05f98 100644
--- a/arch/arm/mach-msm/platsmp.c
+++ b/arch/arm/mach-msm/platsmp.c
@@ -183,9 +183,8 @@
machine_is_msm8226_sim())
return krait_release_secondary_sim(0xf9088000, cpu);
- if (cpu_is_msm8960() || cpu_is_msm8930() || cpu_is_msm8930aa() ||
- cpu_is_apq8064() || cpu_is_msm8627() || cpu_is_msm8960ab() ||
- cpu_is_apq8064ab())
+ if (soc_class_is_msm8960() || soc_class_is_msm8930() ||
+ soc_class_is_apq8064())
return krait_release_secondary(0x02088000, cpu);
if (cpu_is_msm8974())
diff --git a/arch/arm/mach-msm/qdsp6v2/Makefile b/arch/arm/mach-msm/qdsp6v2/Makefile
index ed8cb345..3731722 100644
--- a/arch/arm/mach-msm/qdsp6v2/Makefile
+++ b/arch/arm/mach-msm/qdsp6v2/Makefile
@@ -25,5 +25,5 @@
obj-$(CONFIG_MSM_QDSP6V2_CODECS) += rtac_v2.o q6audio_v2.o q6audio_v2_aio.o
obj-$(CONFIG_MSM_QDSP6V2_CODECS) += audio_mp3.o audio_amrnb.o audio_amrwb.o audio_evrc.o audio_qcelp.o amrwb_in.o
obj-$(CONFIG_MSM_ADSP_LOADER) += adsp-loader.o
-obj-$(CONFIG_MSM_ULTRASOUND) += ultrasound/
+obj-$(CONFIG_MSM_ULTRASOUND_A) += ultrasound/version_a/
obj-m += adsprpc.o
diff --git a/arch/arm/mach-msm/qdsp6v2/audio_acdb.c b/arch/arm/mach-msm/qdsp6v2/audio_acdb.c
index 7272f97..a24b9ec 100644
--- a/arch/arm/mach-msm/qdsp6v2/audio_acdb.c
+++ b/arch/arm/mach-msm/qdsp6v2/audio_acdb.c
@@ -10,6 +10,7 @@
* GNU General Public License for more details.
*
*/
+#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
@@ -20,7 +21,14 @@
#include <mach/qdsp6v2/audio_acdb.h>
-#define MAX_NETWORKS 15
+#define MAX_NETWORKS 15
+#define MAX_IOCTL_DATA (MAX_NETWORKS * 2)
+#define MAX_COL_SIZE 324
+
+#define ACDB_BLOCK_SIZE 4096
+#define NUM_VOCPROC_BLOCKS (6 * MAX_NETWORKS)
+#define ACDB_TOTAL_VOICE_ALLOCATION (ACDB_BLOCK_SIZE * NUM_VOCPROC_BLOCKS)
+
struct sidetone_atomic_cal {
atomic_t enable;
@@ -56,6 +64,15 @@
atomic_t vocstrm_total_cal_size;
atomic_t vocvol_total_cal_size;
+ /* Voice Column data */
+ struct acdb_atomic_cal_block vocproc_col_cal[MAX_VOCPROC_TYPES];
+ uint32_t *col_data[MAX_VOCPROC_TYPES];
+
+ /* VocProc dev cfg cal*/
+ struct acdb_atomic_cal_block vocproc_dev_cal[MAX_NETWORKS];
+ atomic_t vocproc_dev_cal_size;
+ atomic_t vocproc_dev_total_cal_size;
+
/* AFE cal */
struct acdb_atomic_cal_block afe_cal[MAX_AUDPROC_TYPES];
@@ -124,6 +141,15 @@
atomic_set(&acdb_data.asm_topology, topology);
}
+void get_voice_cal_allocation(struct acdb_cal_block *cal_block)
+{
+ cal_block->cal_kvaddr =
+ atomic_read(&acdb_data.vocproc_cal[0].cal_kvaddr);
+ cal_block->cal_paddr =
+ atomic_read(&acdb_data.vocproc_cal[0].cal_paddr);
+ cal_block->cal_size = ACDB_TOTAL_VOICE_ALLOCATION;
+}
+
void get_all_voice_cal(struct acdb_cal_block *cal_block)
{
cal_block->cal_kvaddr =
@@ -177,6 +203,45 @@
atomic_read(&acdb_data.vocvol_total_cal_size);
}
+void get_voice_col_data(uint32_t vocproc_type,
+ struct acdb_cal_block *cal_block)
+{
+ if (cal_block == NULL) {
+ pr_err("ACDB=> NULL pointer sent to %s\n", __func__);
+ goto done;
+ }
+
+ cal_block->cal_kvaddr = atomic_read(&acdb_data.
+ vocproc_col_cal[vocproc_type].cal_kvaddr);
+ cal_block->cal_paddr = atomic_read(&acdb_data.
+ vocproc_col_cal[vocproc_type].cal_paddr);
+ cal_block->cal_size = atomic_read(&acdb_data.
+ vocproc_col_cal[vocproc_type].cal_size);
+done:
+ return;
+}
+
+void store_voice_col_data(uint32_t vocproc_type, uint32_t cal_size,
+ uint32_t *cal_data)
+{
+ if (cal_size > MAX_COL_SIZE) {
+ pr_err("%s: col size is to big %d\n", __func__,
+ cal_size);
+ goto done;
+ }
+ if (copy_from_user(acdb_data.col_data[vocproc_type],
+ (void *)((uint8_t *)cal_data + sizeof(cal_size)),
+ cal_size)) {
+ pr_err("%s: fail to copy col size %d\n",
+ __func__, cal_size);
+ goto done;
+ }
+ atomic_set(&acdb_data.vocproc_col_cal[vocproc_type].cal_size,
+ cal_size);
+done:
+ return;
+}
+
void get_anc_cal(struct acdb_cal_block *cal_block)
{
pr_debug("%s\n", __func__);
@@ -417,6 +482,56 @@
return;
}
+void store_vocproc_dev_cfg_cal(int32_t len, struct cal_block *cal_blocks)
+{
+ int i;
+ pr_debug("%s\n", __func__);
+
+ if (len > MAX_NETWORKS) {
+ pr_err("%s: Calibration sent for %d networks, only %d are supported!\n",
+ __func__, len, MAX_NETWORKS);
+ goto done;
+ }
+
+ atomic_set(&acdb_data.vocproc_dev_total_cal_size, 0);
+ for (i = 0; i < len; i++) {
+ if (cal_blocks[i].cal_offset >
+ atomic64_read(&acdb_data.mem_len)) {
+ pr_err("%s: offset %d is > mem_len %ld\n",
+ __func__, cal_blocks[i].cal_offset,
+ (long)atomic64_read(&acdb_data.mem_len));
+ atomic_set(&acdb_data.vocproc_dev_cal[i].cal_size, 0);
+ } else {
+ atomic_add(cal_blocks[i].cal_size,
+ &acdb_data.vocproc_dev_total_cal_size);
+ atomic_set(&acdb_data.vocproc_dev_cal[i].cal_size,
+ cal_blocks[i].cal_size);
+ atomic_set(&acdb_data.vocproc_dev_cal[i].cal_paddr,
+ cal_blocks[i].cal_offset +
+ atomic64_read(&acdb_data.paddr));
+ atomic_set(&acdb_data.vocproc_dev_cal[i].cal_kvaddr,
+ cal_blocks[i].cal_offset +
+ atomic64_read(&acdb_data.kvaddr));
+ }
+ }
+ atomic_set(&acdb_data.vocproc_dev_cal_size, len);
+done:
+ return;
+}
+
+void get_vocproc_dev_cfg_cal(struct acdb_cal_block *cal_block)
+{
+ pr_debug("%s\n", __func__);
+
+ cal_block->cal_kvaddr =
+ atomic_read(&acdb_data.vocproc_dev_cal[0].cal_kvaddr);
+ cal_block->cal_paddr =
+ atomic_read(&acdb_data.vocproc_dev_cal[0].cal_paddr);
+ cal_block->cal_size =
+ atomic_read(&acdb_data.vocproc_dev_total_cal_size);
+}
+
+
void store_vocproc_cal(int32_t len, struct cal_block *cal_blocks)
{
@@ -698,7 +813,7 @@
int32_t size;
int32_t map_fd;
uint32_t topology;
- struct cal_block data[MAX_NETWORKS];
+ uint32_t data[MAX_IOCTL_DATA];
pr_debug("%s\n", __func__);
switch (cmd) {
@@ -777,6 +892,18 @@
goto done;
}
+ switch (cmd) {
+ case AUDIO_SET_VOCPROC_COL_CAL:
+ store_voice_col_data(VOCPROC_CAL, size, (uint32_t *)arg);
+ goto done;
+ case AUDIO_SET_VOCSTRM_COL_CAL:
+ store_voice_col_data(VOCSTRM_CAL, size, (uint32_t *)arg);
+ goto done;
+ case AUDIO_SET_VOCVOL_COL_CAL:
+ store_voice_col_data(VOCVOL_CAL, size, (uint32_t *)arg);
+ goto done;
+ }
+
if (copy_from_user(data, (void *)(arg + sizeof(size)), size)) {
pr_err("%s: fail to copy table size %d\n", __func__, size);
@@ -795,58 +922,65 @@
if (size > sizeof(struct cal_block))
pr_err("%s: More Audproc Cal then expected, "
"size received: %d\n", __func__, size);
- store_audproc_cal(TX_CAL, data);
+ store_audproc_cal(TX_CAL, (struct cal_block *)data);
break;
case AUDIO_SET_AUDPROC_RX_CAL:
if (size > sizeof(struct cal_block))
pr_err("%s: More Audproc Cal then expected, "
"size received: %d\n", __func__, size);
- store_audproc_cal(RX_CAL, data);
+ store_audproc_cal(RX_CAL, (struct cal_block *)data);
break;
case AUDIO_SET_AUDPROC_TX_STREAM_CAL:
if (size > sizeof(struct cal_block))
pr_err("%s: More Audproc Cal then expected, "
"size received: %d\n", __func__, size);
- store_audstrm_cal(TX_CAL, data);
+ store_audstrm_cal(TX_CAL, (struct cal_block *)data);
break;
case AUDIO_SET_AUDPROC_RX_STREAM_CAL:
if (size > sizeof(struct cal_block))
pr_err("%s: More Audproc Cal then expected, "
"size received: %d\n", __func__, size);
- store_audstrm_cal(RX_CAL, data);
+ store_audstrm_cal(RX_CAL, (struct cal_block *)data);
break;
case AUDIO_SET_AUDPROC_TX_VOL_CAL:
if (size > sizeof(struct cal_block))
pr_err("%s: More Audproc Cal then expected, "
"size received: %d\n", __func__, size);
- store_audvol_cal(TX_CAL, data);
+ store_audvol_cal(TX_CAL, (struct cal_block *)data);
break;
case AUDIO_SET_AUDPROC_RX_VOL_CAL:
if (size > sizeof(struct cal_block))
pr_err("%s: More Audproc Cal then expected, "
"size received: %d\n", __func__, size);
- store_audvol_cal(RX_CAL, data);
+ store_audvol_cal(RX_CAL, (struct cal_block *)data);
break;
case AUDIO_SET_AFE_TX_CAL:
if (size > sizeof(struct cal_block))
pr_err("%s: More AFE Cal then expected, "
"size received: %d\n", __func__, size);
- store_afe_cal(TX_CAL, data);
+ store_afe_cal(TX_CAL, (struct cal_block *)data);
break;
case AUDIO_SET_AFE_RX_CAL:
if (size > sizeof(struct cal_block))
pr_err("%s: More AFE Cal then expected, "
"size received: %d\n", __func__, size);
- store_afe_cal(RX_CAL, data);
+ store_afe_cal(RX_CAL, (struct cal_block *)data);
break;
case AUDIO_SET_VOCPROC_CAL:
- store_vocproc_cal(size / sizeof(struct cal_block), data);
+ store_vocproc_cal(size / sizeof(struct cal_block),
+ (struct cal_block *)data);
break;
case AUDIO_SET_VOCPROC_STREAM_CAL:
- store_vocstrm_cal(size / sizeof(struct cal_block), data);
+ store_vocstrm_cal(size / sizeof(struct cal_block),
+ (struct cal_block *)data);
break;
case AUDIO_SET_VOCPROC_VOL_CAL:
- store_vocvol_cal(size / sizeof(struct cal_block), data);
+ store_vocvol_cal(size / sizeof(struct cal_block),
+ (struct cal_block *)data);
+ break;
+ case AUDIO_SET_VOCPROC_DEV_CFG_CAL:
+ store_vocproc_dev_cfg_cal(size / sizeof(struct cal_block),
+ (struct cal_block *)data);
break;
case AUDIO_SET_SIDETONE_CAL:
if (size > sizeof(struct sidetone_cal))
@@ -855,7 +989,7 @@
store_sidetone_cal((struct sidetone_cal *)data);
break;
case AUDIO_SET_ANC_CAL:
- store_anc_cal(data);
+ store_anc_cal((struct cal_block *)data);
break;
default:
pr_err("ACDB=> ACDB ioctl not found!\n");
@@ -895,6 +1029,7 @@
static int acdb_release(struct inode *inode, struct file *f)
{
+ int i;
s32 result = 0;
atomic_dec(&usage_count);
@@ -903,6 +1038,11 @@
pr_debug("%s: ref count %d!\n", __func__,
atomic_read(&usage_count));
+ for (i = 0; i < MAX_VOCPROC_TYPES; i++) {
+ kfree(acdb_data.col_data[i]);
+ acdb_data.col_data[i] = NULL;
+ }
+
if (atomic_read(&usage_count) >= 1)
result = -EBUSY;
else
@@ -927,9 +1067,16 @@
static int __init acdb_init(void)
{
+ int i;
memset(&acdb_data, 0, sizeof(acdb_data));
mutex_init(&acdb_data.acdb_mutex);
atomic_set(&usage_count, 0);
+
+ for (i = 0; i < MAX_VOCPROC_TYPES; i++) {
+ acdb_data.col_data[i] = kmalloc(MAX_COL_SIZE, GFP_KERNEL);
+ atomic_set(&acdb_data.vocproc_col_cal[i].cal_kvaddr,
+ (uint32_t)acdb_data.col_data[i]);
+ }
return misc_register(&acdb_misc);
}
diff --git a/arch/arm/mach-msm/qdsp6v2/ultrasound/Makefile b/arch/arm/mach-msm/qdsp6v2/ultrasound/Makefile
deleted file mode 100644
index 0be1303..0000000
--- a/arch/arm/mach-msm/qdsp6v2/ultrasound/Makefile
+++ /dev/null
@@ -1,2 +0,0 @@
-obj-y += q6usm.o usf.o usfcdev.o
-EXTRA_CFLAGS += -I$(src)/..
diff --git a/arch/arm/mach-msm/qdsp6v2/ultrasound/q6usm.h b/arch/arm/mach-msm/qdsp6v2/ultrasound/q6usm.h
index 1fe71bf..c68ad68 100644
--- a/arch/arm/mach-msm/qdsp6v2/ultrasound/q6usm.h
+++ b/arch/arm/mach-msm/qdsp6v2/ultrasound/q6usm.h
@@ -15,6 +15,11 @@
#include <mach/qdsp6v2/apr_us.h>
+#define Q6USM_EVENT_UNDEF 0
+#define Q6USM_EVENT_READ_DONE 1
+#define Q6USM_EVENT_WRITE_DONE 2
+#define Q6USM_EVENT_SIGNAL_DETECT_RESULT 3
+
/* cyclic buffer with 1 gap support */
#define USM_MIN_BUF_CNT 3
@@ -39,16 +44,6 @@
/* bit 4 represents META enable of encoded data buffer */
#define BUFFER_META_ENABLE 0x0010
-struct us_region {
- dma_addr_t phys;
- /* If == NULL, the region isn't allocated */
- void *data;
- /* number of buffers in the region */
- uint32_t buf_cnt;
- /* size of buffer */
- uint32_t buf_size;
-};
-
struct us_port_data {
dma_addr_t phys;
/* cyclic region of buffers with 1 gap */
@@ -57,15 +52,17 @@
uint32_t buf_cnt;
/* size of buffer */
uint32_t buf_size;
- /* TX: write index */
+ /* write index */
uint32_t dsp_buf;
- /* TX: read index */
+ /* read index */
uint32_t cpu_buf;
/* expected token from dsp */
uint32_t expected_token;
/* read or write locks */
struct mutex lock;
spinlock_t dsp_lock;
+ /* extended parameters, related to q6 variants */
+ void *ext;
};
struct us_client {
@@ -97,19 +94,11 @@
void *priv);
int q6usm_open_read(struct us_client *usc, uint32_t format);
void q6usm_us_client_free(struct us_client *usc);
-int q6usm_memory_map(struct us_client *usc, uint32_t buf_add,
- int dir, uint32_t bufsz, uint32_t bufcnt);
-int q6usm_memory_unmap(struct us_client *usc, uint32_t buf_add,
- int dir);
-
-uint32_t q6usm_get_ready_data(int dir, struct us_client *usc);
uint32_t q6usm_get_virtual_address(int dir, struct us_client *usc,
struct vm_area_struct *vms);
-
int q6usm_open_write(struct us_client *usc, uint32_t format);
int q6usm_write(struct us_client *usc, uint32_t write_ind);
-bool q6usm_is_write_buf_full(struct us_client *usc, uint32_t* free_region);
-
+bool q6usm_is_write_buf_full(struct us_client *usc, uint32_t *free_region);
int q6usm_set_us_detection(struct us_client *usc,
struct usm_session_cmd_detect_info *detect_info,
uint16_t detect_info_size);
diff --git a/arch/arm/mach-msm/qdsp6v2/ultrasound/usf.c b/arch/arm/mach-msm/qdsp6v2/ultrasound/usf.c
index a973b92..d00eae8 100644
--- a/arch/arm/mach-msm/qdsp6v2/ultrasound/usf.c
+++ b/arch/arm/mach-msm/qdsp6v2/ultrasound/usf.c
@@ -27,8 +27,8 @@
#include "usfcdev.h"
/* The driver version*/
-#define DRV_VERSION "1.4.0"
-#define USF_VERSION_ID 0x0140
+#define DRV_VERSION "1.4.1"
+#define USF_VERSION_ID 0x0141
/* Standard timeout in the asynchronous ops */
#define USF_TIMEOUT_JIFFIES (1*HZ) /* 1 sec */
@@ -351,7 +351,7 @@
}
switch (opcode) {
- case USM_DATA_EVENT_WRITE_DONE:
+ case Q6USM_EVENT_WRITE_DONE:
wake_up(&usf_xx->wait);
break;
default:
@@ -370,14 +370,14 @@
}
switch (opcode) {
- case USM_DATA_EVENT_READ_DONE:
+ case Q6USM_EVENT_READ_DONE:
if (token == USM_WRONG_TOKEN)
usf_xx->usf_state = USF_ERROR_STATE;
usf_xx->new_region = token;
wake_up(&usf_xx->wait);
break;
- case USM_SESSION_EVENT_SIGNAL_DETECT_RESULT:
+ case Q6USM_EVENT_SIGNAL_DETECT_RESULT:
usf_xx->us_detect_type = (payload[APR_US_DETECT_RESULT_IND]) ?
USF_US_DETECT_YES :
USF_US_DETECT_NO;
@@ -1043,8 +1043,7 @@
if ((usf_xx->usf_state != USF_WORK_STATE) ||
(rc == -ERESTARTSYS)) {
- pr_err("%s: Getting ready region failed "
- "work state[%d]; rc[%d]\n",
+ pr_err("%s: Get ready region failure; state[%d]; rc[%d]\n",
__func__, usf_xx->usf_state, rc);
return -EINTR;
}
@@ -1459,4 +1458,3 @@
device_initcall(usf_init);
MODULE_DESCRIPTION("Ultrasound framework driver");
-MODULE_VERSION(DRV_VERSION);
diff --git a/arch/arm/mach-msm/qdsp6v2/ultrasound/version_a/Makefile b/arch/arm/mach-msm/qdsp6v2/ultrasound/version_a/Makefile
new file mode 100644
index 0000000..38d7d51
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp6v2/ultrasound/version_a/Makefile
@@ -0,0 +1,2 @@
+obj-y += q6usm_a.o ../usf.o ../usfcdev.o
+ccflags-y := -I$(src)/.. -I$(src)/../..
diff --git a/arch/arm/mach-msm/qdsp6v2/ultrasound/q6usm.c b/arch/arm/mach-msm/qdsp6v2/ultrasound/version_a/q6usm_a.c
similarity index 96%
rename from arch/arm/mach-msm/qdsp6v2/ultrasound/q6usm.c
rename to arch/arm/mach-msm/qdsp6v2/ultrasound/version_a/q6usm_a.c
index dce3812..5d30eb1 100644
--- a/arch/arm/mach-msm/qdsp6v2/ultrasound/q6usm.c
+++ b/arch/arm/mach-msm/qdsp6v2/ultrasound/version_a/q6usm_a.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -19,11 +19,9 @@
#include <linux/slab.h>
#include <linux/msm_audio.h>
#include <sound/apr_audio.h>
+#include <mach/qdsp6v2/apr_us_a.h>
#include "q6usm.h"
-/* The driver version*/
-#define DRV_VERSION "1.2"
-
#define SESSION_MAX 0x02 /* aDSP:USM limit */
#define READDONE_IDX_STATUS 0
@@ -58,6 +56,96 @@
static struct usm_mmap this_mmap;
+static void q6usm_add_mmaphdr(struct us_client *usc, struct apr_hdr *hdr,
+ uint32_t pkt_size, bool cmd_flg)
+{
+ hdr->hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, \
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ hdr->src_port = 0;
+ hdr->dest_port = 0;
+ if (cmd_flg) {
+ hdr->token = 0;
+ atomic_set(&this_mmap.cmd_state, 1);
+ }
+ hdr->pkt_size = pkt_size;
+ return;
+}
+
+static int q6usm_memory_map(struct us_client *usc, uint32_t buf_add, int dir,
+ uint32_t bufsz, uint32_t bufcnt)
+{
+ struct usm_stream_cmd_memory_map mem_map;
+ int rc = 0;
+
+ if ((usc == NULL) || (usc->apr == NULL) || (this_mmap.apr == NULL)) {
+ pr_err("%s: APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ q6usm_add_mmaphdr(usc, &mem_map.hdr,
+ sizeof(struct usm_stream_cmd_memory_map), true);
+ mem_map.hdr.opcode = USM_SESSION_CMD_MEMORY_MAP;
+
+ mem_map.buf_add = buf_add;
+ mem_map.buf_size = bufsz * bufcnt;
+ mem_map.mempool_id = 0;
+
+ pr_debug("%s: buf add[%x] buf_add_parameter[%x]\n",
+ __func__, mem_map.buf_add, buf_add);
+
+ rc = apr_send_pkt(this_mmap.apr, (uint32_t *) &mem_map);
+ if (rc < 0) {
+ pr_err("%s: mem_map op[0x%x]rc[%d]\n",
+ __func__, mem_map.hdr.opcode, rc);
+ goto fail_cmd;
+ }
+
+ rc = wait_event_timeout(this_mmap.cmd_wait,
+ (atomic_read(&this_mmap.cmd_state) == 0),
+ Q6USM_TIMEOUT_JIFFIES);
+ if (!rc) {
+ rc = -ETIME;
+ pr_err("%s: timeout. waited for memory_map\n", __func__);
+ } else
+ rc = 0;
+fail_cmd:
+ return rc;
+}
+
+int q6usm_memory_unmap(struct us_client *usc, uint32_t buf_add, int dir)
+{
+ struct usm_stream_cmd_memory_unmap mem_unmap;
+ int rc = 0;
+
+ if ((usc == NULL) || (usc->apr == NULL) || (this_mmap.apr == NULL)) {
+ pr_err("%s: APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ q6usm_add_mmaphdr(usc, &mem_unmap.hdr,
+ sizeof(struct usm_stream_cmd_memory_unmap), true);
+ mem_unmap.hdr.opcode = USM_SESSION_CMD_MEMORY_UNMAP;
+ mem_unmap.buf_add = buf_add;
+
+ rc = apr_send_pkt(this_mmap.apr, (uint32_t *) &mem_unmap);
+ if (rc < 0) {
+ pr_err("%s:mem_unmap op[0x%x]rc[%d]\n",
+ __func__, mem_unmap.hdr.opcode, rc);
+ goto fail_cmd;
+ }
+
+ rc = wait_event_timeout(this_mmap.cmd_wait,
+ (atomic_read(&this_mmap.cmd_state) == 0),
+ Q6USM_TIMEOUT_JIFFIES);
+ if (!rc) {
+ rc = -ETIME;
+ pr_err("%s: timeout. waited for memory_map\n", __func__);
+ } else
+ rc = 0;
+fail_cmd:
+ return rc;
+}
+
static int q6usm_session_alloc(struct us_client *usc)
{
int ind = 0;
@@ -276,10 +364,11 @@
uint32_t token;
uint32_t *payload = data->payload;
- pr_debug("%s: ptr0[0x%x]; ptr1[0x%x]; opcode[0x%x];"
- "token[0x%x]; payload_s[%d]; src[%d]; dest[%d];\n",
- __func__, payload[0], payload[1], data->opcode, data->token,
- data->payload_size, data->src_port, data->dest_port);
+ pr_debug("%s: ptr0[0x%x]; ptr1[0x%x]; opcode[0x%x]\n",
+ __func__, payload[0], payload[1], data->opcode);
+ pr_debug("%s: token[0x%x]; payload_size[%d]; src[%d]; dest[%d];\n",
+ __func__, data->token, data->payload_size,
+ data->src_port, data->dest_port);
if (data->opcode == APR_BASIC_RSP_RESULT) {
/* status field check */
@@ -315,6 +404,7 @@
unsigned long dsp_flags;
uint32_t *payload = data->payload;
uint32_t token = data->token;
+ uint32_t opcode = Q6USM_EVENT_UNDEF;
if (usc == NULL) {
pr_err("%s: client info is NULL\n", __func__);
@@ -363,6 +453,7 @@
case USM_DATA_EVENT_READ_DONE: {
struct us_port_data *port = &usc->port[OUT];
+ opcode = Q6USM_EVENT_READ_DONE;
spin_lock_irqsave(&port->dsp_lock, dsp_flags);
if (payload[READDONE_IDX_STATUS]) {
pr_err("%s: wrong READDONE[%d]; token[%d]\n",
@@ -408,6 +499,7 @@
case USM_DATA_EVENT_WRITE_DONE: {
struct us_port_data *port = &usc->port[IN];
+ opcode = Q6USM_EVENT_WRITE_DONE;
if (payload[WRITEDONE_IDX_STATUS]) {
pr_err("%s: wrong WRITEDONE_IDX_STATUS[%d]\n",
__func__,
@@ -428,6 +520,7 @@
pr_debug("%s: US detect result: result=%d",
__func__,
payload[0]);
+ opcode = Q6USM_EVENT_SIGNAL_DETECT_RESULT;
break;
} /* case USM_SESSION_EVENT_SIGNAL_DETECT_RESULT */
@@ -438,21 +531,12 @@
} /* switch */
if (usc->cb)
- usc->cb(data->opcode, token,
+ usc->cb(opcode, token,
data->payload, usc->priv);
return 0;
}
-uint32_t q6usm_get_ready_data(int dir, struct us_client *usc)
-{
- uint32_t ret = 0xffffffff;
-
- if ((usc != NULL) && ((dir == IN) || (dir == OUT)))
- ret = usc->port[dir].dsp_buf;
- return ret;
-}
-
uint32_t q6usm_get_virtual_address(int dir,
struct us_client *usc,
struct vm_area_struct *vms)
@@ -490,21 +574,6 @@
return;
}
-static void q6usm_add_mmaphdr(struct us_client *usc, struct apr_hdr *hdr,
- uint32_t pkt_size, bool cmd_flg)
-{
- hdr->hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, \
- APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
- hdr->src_port = 0;
- hdr->dest_port = 0;
- if (cmd_flg) {
- hdr->token = 0;
- atomic_set(&this_mmap.cmd_state, 1);
- }
- hdr->pkt_size = pkt_size;
- return;
-}
-
static uint32_t q6usm_ext2int_format(uint32_t ext_format)
{
uint32_t int_format = INVALID_FORMAT;
@@ -573,7 +642,7 @@
}
-int q6usm_enc_cfg_blk(struct us_client *usc, struct us_encdec_cfg* us_cfg)
+int q6usm_enc_cfg_blk(struct us_client *usc, struct us_encdec_cfg *us_cfg)
{
uint32_t int_format = INVALID_FORMAT;
struct usm_stream_cmd_encdec_cfg_blk enc_cfg_obj;
@@ -849,80 +918,6 @@
}
-int q6usm_memory_map(struct us_client *usc, uint32_t buf_add, int dir,
- uint32_t bufsz, uint32_t bufcnt)
-{
- struct usm_stream_cmd_memory_map mem_map;
- int rc = 0;
-
- if ((usc == NULL) || (usc->apr == NULL) || (this_mmap.apr == NULL)) {
- pr_err("%s: APR handle NULL\n", __func__);
- return -EINVAL;
- }
-
- q6usm_add_mmaphdr(usc, &mem_map.hdr,
- sizeof(struct usm_stream_cmd_memory_map), true);
- mem_map.hdr.opcode = USM_SESSION_CMD_MEMORY_MAP;
-
- mem_map.buf_add = buf_add;
- mem_map.buf_size = bufsz * bufcnt;
- mem_map.mempool_id = 0;
-
- pr_debug("%s: buf add[%x] buf_add_parameter[%x]\n",
- __func__, mem_map.buf_add, buf_add);
-
- rc = apr_send_pkt(this_mmap.apr, (uint32_t *) &mem_map);
- if (rc < 0) {
- pr_err("%s: mem_map op[0x%x]rc[%d]\n",
- __func__, mem_map.hdr.opcode, rc);
- goto fail_cmd;
- }
-
- rc = wait_event_timeout(this_mmap.cmd_wait,
- (atomic_read(&this_mmap.cmd_state) == 0),
- Q6USM_TIMEOUT_JIFFIES);
- if (!rc) {
- rc = -ETIME;
- pr_err("%s: timeout. waited for memory_map\n", __func__);
- } else
- rc = 0;
-fail_cmd:
- return rc;
-}
-
-int q6usm_memory_unmap(struct us_client *usc, uint32_t buf_add, int dir)
-{
- struct usm_stream_cmd_memory_unmap mem_unmap;
- int rc = 0;
-
- if ((usc == NULL) || (usc->apr == NULL) || (this_mmap.apr == NULL)) {
- pr_err("%s: APR handle NULL\n", __func__);
- return -EINVAL;
- }
-
- q6usm_add_mmaphdr(usc, &mem_unmap.hdr,
- sizeof(struct usm_stream_cmd_memory_unmap), true);
- mem_unmap.hdr.opcode = USM_SESSION_CMD_MEMORY_UNMAP;
- mem_unmap.buf_add = buf_add;
-
- rc = apr_send_pkt(this_mmap.apr, (uint32_t *) &mem_unmap);
- if (rc < 0) {
- pr_err("%s:mem_unmap op[0x%x]rc[%d]\n",
- __func__, mem_unmap.hdr.opcode, rc);
- goto fail_cmd;
- }
-
- rc = wait_event_timeout(this_mmap.cmd_wait,
- (atomic_read(&this_mmap.cmd_state) == 0),
- Q6USM_TIMEOUT_JIFFIES);
- if (!rc) {
- rc = -ETIME;
- pr_err("%s: timeout. waited for memory_map\n", __func__);
- } else
- rc = 0;
-fail_cmd:
- return rc;
-}
int q6usm_read(struct us_client *usc, uint32_t read_ind)
{
@@ -1057,7 +1052,7 @@
return rc;
}
-bool q6usm_is_write_buf_full(struct us_client *usc, uint32_t* free_region)
+bool q6usm_is_write_buf_full(struct us_client *usc, uint32_t *free_region)
{
struct us_port_data *port = NULL;
u32 cpu_buf = 0;
diff --git a/arch/arm/mach-msm/restart_7k.c b/arch/arm/mach-msm/restart_7k.c
index dc9edf4..9675b61 100644
--- a/arch/arm/mach-msm/restart_7k.c
+++ b/arch/arm/mach-msm/restart_7k.c
@@ -21,13 +21,11 @@
#include <mach/proc_comm.h>
#include "devices-msm7x2xa.h"
-#include "smd_rpcrouter.h"
static uint32_t restart_reason = 0x776655AA;
static void msm_pm_power_off(void)
{
- msm_rpcrouter_close();
msm_proc_comm(PCOM_POWER_DOWN, 0, 0);
for (;;)
;
@@ -35,7 +33,6 @@
static void msm_pm_restart(char str, const char *cmd)
{
- msm_rpcrouter_close();
pr_debug("The reset reason is %x\n", restart_reason);
/* Disable interrupts */
diff --git a/arch/arm/mach-msm/rpm_resources.c b/arch/arm/mach-msm/rpm_resources.c
index dfed3aa..43073d3 100644
--- a/arch/arm/mach-msm/rpm_resources.c
+++ b/arch/arm/mach-msm/rpm_resources.c
@@ -1131,9 +1131,8 @@
static int __init msm_rpmrs_l2_init(void)
{
- if (cpu_is_msm8960() || cpu_is_msm8930() || cpu_is_msm8930aa() ||
- cpu_is_apq8064() || cpu_is_msm8627() || cpu_is_msm8960ab() ||
- cpu_is_apq8064ab()) {
+ if (soc_class_is_msm8960() || soc_class_is_msm8930() ||
+ soc_class_is_apq8064()) {
msm_pm_set_l2_flush_flag(0);
diff --git a/arch/arm/mach-msm/smd.c b/arch/arm/mach-msm/smd.c
index c1e2421..71fb87c 100644
--- a/arch/arm/mach-msm/smd.c
+++ b/arch/arm/mach-msm/smd.c
@@ -3191,6 +3191,7 @@
flags, "smd_dev", 0);
if (r < 0)
return r;
+ interrupt_stats[SMD_MODEM].smd_interrupt_id = INT_A9_M2A_0;
r = enable_irq_wake(INT_A9_M2A_0);
if (r < 0)
pr_err("smd_core_init: "
@@ -3202,6 +3203,7 @@
free_irq(INT_A9_M2A_0, 0);
return r;
}
+ interrupt_stats[SMD_MODEM].smsm_interrupt_id = INT_A9_M2A_5;
r = enable_irq_wake(INT_A9_M2A_5);
if (r < 0)
pr_err("smd_core_init: "
@@ -3219,6 +3221,7 @@
return r;
}
+ interrupt_stats[SMD_Q6].smd_interrupt_id = INT_ADSP_A11;
r = request_irq(INT_ADSP_A11_SMSM, smsm_dsp_irq_handler,
flags, "smsm_dev", smsm_dsp_irq_handler);
if (r < 0) {
@@ -3228,6 +3231,7 @@
return r;
}
+ interrupt_stats[SMD_Q6].smsm_interrupt_id = INT_ADSP_A11_SMSM;
r = enable_irq_wake(INT_ADSP_A11);
if (r < 0)
pr_err("smd_core_init: "
@@ -3253,6 +3257,7 @@
return r;
}
+ interrupt_stats[SMD_DSPS].smd_interrupt_id = INT_DSPS_A11;
r = enable_irq_wake(INT_DSPS_A11);
if (r < 0)
pr_err("smd_core_init: "
@@ -3271,6 +3276,7 @@
return r;
}
+ interrupt_stats[SMD_WCNSS].smd_interrupt_id = INT_WCNSS_A11;
r = enable_irq_wake(INT_WCNSS_A11);
if (r < 0)
pr_err("smd_core_init: "
@@ -3288,6 +3294,7 @@
return r;
}
+ interrupt_stats[SMD_WCNSS].smsm_interrupt_id = INT_WCNSS_A11_SMSM;
r = enable_irq_wake(INT_WCNSS_A11_SMSM);
if (r < 0)
pr_err("smd_core_init: "
@@ -3308,6 +3315,7 @@
return r;
}
+ interrupt_stats[SMD_DSPS].smsm_interrupt_id = INT_DSPS_A11_SMSM;
r = enable_irq_wake(INT_DSPS_A11_SMSM);
if (r < 0)
pr_err("smd_core_init: "
@@ -3437,6 +3445,8 @@
goto intr_failed;
}
+ interrupt_stats[cfg->irq_config_id].smd_interrupt_id
+ = cfg->smd_int.irq_id;
/* only init smsm structs if this edge supports smsm */
if (cfg->smsm_int.irq_id)
ret = intr_init(
@@ -3452,6 +3462,9 @@
goto intr_failed;
}
+ if (cfg->smsm_int.irq_id)
+ interrupt_stats[cfg->irq_config_id].smsm_interrupt_id
+ = cfg->smsm_int.irq_id;
if (cfg->subsys_name)
strlcpy(edge_to_pids[cfg->edge].subsys_name,
cfg->subsys_name, SMD_MAX_CH_NAME_LEN);
diff --git a/arch/arm/mach-msm/smd_debug.c b/arch/arm/mach-msm/smd_debug.c
index d64bcf2..cc82a01 100644
--- a/arch/arm/mach-msm/smd_debug.c
+++ b/arch/arm/mach-msm/smd_debug.c
@@ -103,22 +103,24 @@
const char *subsys_name;
i += scnprintf(buf + i, max - i,
- " Subsystem | In | Out (Hardcoded) |"
+ " Subsystem | Interrupt ID | In | Out (Hardcoded) |"
" Out (Configured) |\n");
for (subsys = 0; subsys < NUM_SMD_SUBSYSTEMS; ++subsys) {
subsys_name = smd_pid_to_subsystem(subsys);
if (subsys_name) {
i += scnprintf(buf + i, max - i,
- "%-10s %4s | %9u | %9u | %9u |\n",
+ "%-10s %4s | %9d | %9u | %9u | %9u |\n",
smd_pid_to_subsystem(subsys), "smd",
+ stats->smd_interrupt_id,
stats->smd_in_count,
stats->smd_out_hardcode_count,
stats->smd_out_config_count);
i += scnprintf(buf + i, max - i,
- "%-10s %4s | %9u | %9u | %9u |\n",
+ "%-10s %4s | %9d | %9u | %9u | %9u |\n",
smd_pid_to_subsystem(subsys), "smsm",
+ stats->smsm_interrupt_id,
stats->smsm_in_count,
stats->smsm_out_hardcode_count,
stats->smsm_out_config_count);
diff --git a/arch/arm/mach-msm/smd_private.h b/arch/arm/mach-msm/smd_private.h
index 9117280..7f39a24 100644
--- a/arch/arm/mach-msm/smd_private.h
+++ b/arch/arm/mach-msm/smd_private.h
@@ -255,10 +255,12 @@
uint32_t smd_in_count;
uint32_t smd_out_hardcode_count;
uint32_t smd_out_config_count;
+ uint32_t smd_interrupt_id;
uint32_t smsm_in_count;
uint32_t smsm_out_hardcode_count;
uint32_t smsm_out_config_count;
+ uint32_t smsm_interrupt_id;
};
extern struct interrupt_stat interrupt_stats[NUM_SMD_SUBSYSTEMS];
diff --git a/arch/arm/mach-msm/smd_rpcrouter.c b/arch/arm/mach-msm/smd_rpcrouter.c
index 68c3dd3..80a639c 100644
--- a/arch/arm/mach-msm/smd_rpcrouter.c
+++ b/arch/arm/mach-msm/smd_rpcrouter.c
@@ -1,7 +1,7 @@
/* arch/arm/mach-msm/smd_rpcrouter.c
*
* Copyright (C) 2007 Google, Inc.
- * Copyright (c) 2007-2011, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2007-2012, The Linux Foundation. All rights reserved.
* Author: San Mehat <san@android.com>
*
* This software is licensed under the terms of the GNU General Public
@@ -40,6 +40,7 @@
#include <linux/platform_device.h>
#include <linux/uaccess.h>
#include <linux/debugfs.h>
+#include <linux/reboot.h>
#include <asm/byteorder.h>
@@ -158,6 +159,7 @@
static void do_read_data(struct work_struct *work);
static void do_create_pdevs(struct work_struct *work);
static void do_create_rpcrouter_pdev(struct work_struct *work);
+static int msm_rpcrouter_close(void);
static DECLARE_WORK(work_create_pdevs, do_create_pdevs);
static DECLARE_WORK(work_create_rpcrouter_pdev, do_create_rpcrouter_pdev);
@@ -213,6 +215,24 @@
DECLARE_COMPLETION(rpc_remote_router_up);
static atomic_t pending_close_count = ATOMIC_INIT(0);
+static int msm_rpc_reboot_call(struct notifier_block *this,
+ unsigned long code, void *_cmd)
+{
+ switch (code) {
+ case SYS_RESTART:
+ case SYS_HALT:
+ case SYS_POWER_OFF:
+ msm_rpcrouter_close();
+ break;
+ }
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block msm_rpc_reboot_notifier = {
+ .notifier_call = msm_rpc_reboot_call,
+ .priority = 100
+};
+
/*
* Search for transport (xprt) that matches the provided PID.
*
@@ -2124,7 +2144,7 @@
return rc;
}
-int msm_rpcrouter_close(void)
+static int msm_rpcrouter_close(void)
{
struct rpcrouter_xprt_info *xprt_info;
union rr_control_msg ctl;
@@ -2508,7 +2528,9 @@
msm_rpc_connect_timeout_ms = 0;
smd_rpcrouter_debug_mask |= SMEM_LOG;
debugfs_init();
-
+ ret = register_reboot_notifier(&msm_rpc_reboot_notifier);
+ if (ret)
+ pr_err("%s: Failed to register reboot notifier", __func__);
/* Initialize what we need to start processing */
rpcrouter_workqueue =
diff --git a/arch/arm/mach-msm/smd_rpcrouter.h b/arch/arm/mach-msm/smd_rpcrouter.h
index 1da7579..2ad85a4 100644
--- a/arch/arm/mach-msm/smd_rpcrouter.h
+++ b/arch/arm/mach-msm/smd_rpcrouter.h
@@ -1,7 +1,7 @@
/** arch/arm/mach-msm/smd_rpcrouter.h
*
* Copyright (C) 2007 Google, Inc.
- * Copyright (c) 2007-2011, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2007-2012, The Linux Foundation. All rights reserved.
* Author: San Mehat <san@android.com>
*
* This software is licensed under the terms of the GNU General Public
@@ -240,7 +240,6 @@
struct rr_fragment **frag,
unsigned len, long timeout);
-int msm_rpcrouter_close(void);
struct msm_rpc_endpoint *msm_rpcrouter_create_local_endpoint(dev_t dev);
int msm_rpcrouter_destroy_local_endpoint(struct msm_rpc_endpoint *ept);
diff --git a/arch/arm/mach-msm/spm-v2.c b/arch/arm/mach-msm/spm-v2.c
index f0d3d06..17997d2 100644
--- a/arch/arm/mach-msm/spm-v2.c
+++ b/arch/arm/mach-msm/spm-v2.c
@@ -84,10 +84,6 @@
[MSM_SPM_REG_SAW2_VERSION] = 0xFD0,
};
-/******************************************************************************
- * Internal helper functions
- *****************************************************************************/
-
static inline uint32_t msm_spm_drv_get_num_spm_entry(
struct msm_spm_driver_data *dev)
{
@@ -150,6 +146,12 @@
{
unsigned int pmic_data = 0;
+ /**
+ * VCTL_PORT has to be 0, for PMIC_STS register to be updated.
+ * Ensure that vctl_port is always set to 0.
+ */
+ WARN_ON(dev->vctl_port);
+
pmic_data |= vlevel;
pmic_data |= (dev->vctl_port & 0x7) << 16;
@@ -213,10 +215,6 @@
return ret;
}
-/******************************************************************************
- * Public functions
- *****************************************************************************/
-
inline int msm_spm_drv_set_spm_enable(
struct msm_spm_driver_data *dev, bool enable)
{
@@ -381,69 +379,92 @@
goto set_vdd_bail;
}
- /* Set AVS min/max */
- msm_spm_drv_set_avs_vlevel(dev, vlevel);
-
if (msm_spm_debug_mask & MSM_SPM_DEBUG_VCTL)
- pr_info("%s: done, remaining timeout %uus\n",
+ pr_info("%s: done, remaining timeout %u us\n",
__func__, timeout_us);
+ /* Set AVS min/max */
+ msm_spm_drv_set_avs_vlevel(dev, vlevel);
msm_spm_drv_enable_avs(dev);
+
return 0;
set_vdd_bail:
msm_spm_drv_enable_avs(dev);
- pr_err("%s: failed %#x, remaining timeout %uus, vlevel %#x\n",
+ pr_err("%s: failed %#x, remaining timeout %u us, vlevel %#x\n",
__func__, vlevel, timeout_us, new_level);
return -EIO;
}
-int msm_spm_drv_set_phase(struct msm_spm_driver_data *dev,
- unsigned int phase_cnt)
+static int msm_spm_drv_get_pmic_port(struct msm_spm_driver_data *dev,
+ enum msm_spm_pmic_port port)
+{
+ int index = -1;
+
+ switch (port) {
+ case MSM_SPM_PMIC_VCTL_PORT:
+ index = dev->vctl_port;
+ break;
+ case MSM_SPM_PMIC_PHASE_PORT:
+ index = dev->phase_port;
+ break;
+ case MSM_SPM_PMIC_PFM_PORT:
+ index = dev->pfm_port;
+ break;
+ default:
+ break;
+ }
+
+ return index;
+}
+
+int msm_spm_drv_set_pmic_data(struct msm_spm_driver_data *dev,
+ enum msm_spm_pmic_port port, unsigned int data)
{
unsigned int pmic_data = 0;
unsigned int timeout_us = 0;
+ int index = 0;
if (dev->major != SAW2_MAJOR_2)
return -ENODEV;
- pmic_data |= phase_cnt & 0xFF;
- pmic_data |= (dev->phase_port & 0x7) << 16;
+ if (!msm_spm_pmic_arb_present(dev))
+ return -ENOSYS;
+
+ index = msm_spm_drv_get_pmic_port(dev, port);
+ if (index < 0)
+ return -ENODEV;
+
+ pmic_data |= data & 0xFF;
+ pmic_data |= (index & 0x7) << 16;
dev->reg_shadow[MSM_SPM_REG_SAW2_VCTL] &= ~0x700FF;
dev->reg_shadow[MSM_SPM_REG_SAW2_VCTL] |= pmic_data;
msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW2_VCTL);
mb();
- /* Wait for PMIC state to return to idle or until timeout */
timeout_us = dev->vctl_timeout_us;
- while (msm_spm_drv_get_sts_pmic_state(dev) != MSM_SPM_PMIC_STATE_IDLE) {
- if (!timeout_us)
- goto set_phase_bail;
+ /**
+ * Confirm the pmic data set was what hardware sent by
+ * checking the PMIC FSM state.
+ * We cannot use the sts_pmic_data and check it against
+ * the value like we do fot set_vdd, since the PMIC_STS
+ * is only updated for SAW_VCTL sent with port index 0.
+ */
+ do {
+ if (msm_spm_drv_get_sts_pmic_state(dev) ==
+ MSM_SPM_PMIC_STATE_IDLE)
+ break;
+ udelay(1);
+ } while (--timeout_us);
- if (timeout_us > 10) {
- udelay(10);
- timeout_us -= 10;
- } else {
- udelay(timeout_us);
- timeout_us = 0;
- }
+ if (!timeout_us) {
+ pr_err("%s: failed, remaining timeout %u us, data %d\n",
+ __func__, timeout_us, data);
+ return -EIO;
}
- if (msm_spm_drv_get_sts_curr_pmic_data(dev) != phase_cnt)
- goto set_phase_bail;
-
- if (msm_spm_debug_mask & MSM_SPM_DEBUG_VCTL)
- pr_info("%s: done, remaining timeout %uus\n",
- __func__, timeout_us);
-
return 0;
-
-set_phase_bail:
- pr_err("%s: failed, remaining timeout %uus, phase count %d\n",
- __func__, timeout_us, msm_spm_drv_get_sts_curr_pmic_data(dev));
- return -EIO;
-
}
void msm_spm_drv_reinit(struct msm_spm_driver_data *dev)
@@ -472,6 +493,7 @@
dev->vctl_port = data->vctl_port;
dev->phase_port = data->phase_port;
+ dev->pfm_port = data->pfm_port;
dev->reg_base_addr = data->reg_base_addr;
memcpy(dev->reg_shadow, data->reg_init_values,
sizeof(data->reg_init_values));
diff --git a/arch/arm/mach-msm/spm.h b/arch/arm/mach-msm/spm.h
index 09ee26a..4cdfcf8 100644
--- a/arch/arm/mach-msm/spm.h
+++ b/arch/arm/mach-msm/spm.h
@@ -112,6 +112,7 @@
uint32_t ver_reg;
uint32_t vctl_port;
uint32_t phase_port;
+ uint32_t pfm_port;
uint8_t awake_vlevel;
uint32_t vctl_timeout_us;
@@ -196,6 +197,12 @@
*/
int msm_spm_apcs_set_phase(unsigned int phase_cnt);
+/** msm_spm_enable_fts_lpm() : Enable FTS to switch to low power
+ * when the cores are in low power modes
+ * @mode: The mode configuration for FTS
+ */
+int msm_spm_enable_fts_lpm(uint32_t mode);
+
/* Internal low power management specific functions */
/**
@@ -234,6 +241,11 @@
{
return -ENOSYS;
}
+
+static inline int msm_spm_enable_fts_lpm(uint32_t mode)
+{
+ return -ENOSYS;
+}
#endif /* defined(CONFIG_MSM_L2_SPM) */
#else /* defined(CONFIG_MSM_SPM_V1) || defined(CONFIG_MSM_SPM_V2) */
static inline int msm_spm_set_low_power_mode(unsigned int mode, bool notify_rpm)
diff --git a/arch/arm/mach-msm/spm_devices.c b/arch/arm/mach-msm/spm_devices.c
index 12a6f08..3fe3bd7 100644
--- a/arch/arm/mach-msm/spm_devices.c
+++ b/arch/arm/mach-msm/spm_devices.c
@@ -183,9 +183,8 @@
reg = saw_bases[cpu];
- if (cpu_is_msm8960() || cpu_is_msm8930() || cpu_is_msm8930aa() ||
- cpu_is_apq8064() || cpu_is_msm8627() || cpu_is_msm8960ab() ||
- cpu_is_apq8064ab()) {
+ if (soc_class_is_msm8960() || soc_class_is_msm8930() ||
+ soc_class_is_apq8064()) {
val = 0xA4;
reg += 0x14;
timeout = 512;
@@ -260,10 +259,18 @@
int msm_spm_apcs_set_phase(unsigned int phase_cnt)
{
- return msm_spm_drv_set_phase(&msm_spm_l2_device.reg_data, phase_cnt);
+ return msm_spm_drv_set_pmic_data(&msm_spm_l2_device.reg_data,
+ MSM_SPM_PMIC_PHASE_PORT, phase_cnt);
}
EXPORT_SYMBOL(msm_spm_apcs_set_phase);
+int msm_spm_enable_fts_lpm(uint32_t mode)
+{
+ return msm_spm_drv_set_pmic_data(&msm_spm_l2_device.reg_data,
+ MSM_SPM_PMIC_PFM_PORT, mode);
+}
+EXPORT_SYMBOL(msm_spm_enable_fts_lpm);
+
/* Board file init function */
int __init msm_spm_l2_init(struct msm_spm_platform_data *data)
{
@@ -361,25 +368,6 @@
if (!ret)
spm_data.vctl_timeout_us = val;
- /* optional */
- key = "qcom,vctl-port";
- ret = of_property_read_u32(node, key, &val);
- if (!ret)
- spm_data.vctl_port = val;
-
- /* optional */
- key = "qcom,phase-port";
- ret = of_property_read_u32(node, key, &val);
- if (!ret)
- spm_data.phase_port = val;
-
- for (i = 0; i < ARRAY_SIZE(spm_of_data); i++) {
- ret = of_property_read_u32(node, spm_of_data[i].key, &val);
- if (ret)
- continue;
- spm_data.reg_init_values[spm_of_data[i].id] = val;
- }
-
/*
* Device with id 0..NR_CPUS are SPM for apps cores
* Device with id 0xFFFF is for L2 SPM.
@@ -395,6 +383,35 @@
dev = &msm_spm_l2_device;
}
+ spm_data.vctl_port = -1;
+ spm_data.phase_port = -1;
+ spm_data.pfm_port = -1;
+
+ /* optional */
+ if (dev == &msm_spm_l2_device) {
+ key = "qcom,vctl-port";
+ ret = of_property_read_u32(node, key, &val);
+ if (!ret)
+ spm_data.vctl_port = val;
+
+ key = "qcom,phase-port";
+ ret = of_property_read_u32(node, key, &val);
+ if (!ret)
+ spm_data.phase_port = val;
+
+ key = "qcom,pfm-port";
+ ret = of_property_read_u32(node, key, &val);
+ if (!ret)
+ spm_data.pfm_port = val;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(spm_of_data); i++) {
+ ret = of_property_read_u32(node, spm_of_data[i].key, &val);
+ if (ret)
+ continue;
+ spm_data.reg_init_values[spm_of_data[i].id] = val;
+ }
+
for (i = 0; i < num_modes; i++) {
key = mode_of_data[i].key;
modes[mode_count].cmd =
diff --git a/arch/arm/mach-msm/spm_driver.h b/arch/arm/mach-msm/spm_driver.h
index 4cdfd33..1beaffb 100644
--- a/arch/arm/mach-msm/spm_driver.h
+++ b/arch/arm/mach-msm/spm_driver.h
@@ -14,12 +14,19 @@
#include "spm.h"
+enum msm_spm_pmic_port {
+ MSM_SPM_PMIC_VCTL_PORT,
+ MSM_SPM_PMIC_PHASE_PORT,
+ MSM_SPM_PMIC_PFM_PORT,
+};
+
struct msm_spm_driver_data {
uint32_t major;
uint32_t minor;
uint32_t ver_reg;
uint32_t vctl_port;
uint32_t phase_port;
+ uint32_t pfm_port;
void __iomem *reg_base_addr;
uint32_t vctl_timeout_us;
uint32_t avs_timeout_us;
@@ -42,6 +49,6 @@
void msm_spm_drv_flush_seq_entry(struct msm_spm_driver_data *dev);
int msm_spm_drv_set_spm_enable(struct msm_spm_driver_data *dev,
bool enable);
-int msm_spm_drv_set_phase(struct msm_spm_driver_data *dev,
- unsigned int phase_cnt);
+int msm_spm_drv_set_pmic_data(struct msm_spm_driver_data *dev,
+ enum msm_spm_pmic_port port, unsigned int data);
#endif
diff --git a/arch/arm/mach-msm/subsystem_restart.c b/arch/arm/mach-msm/subsystem_restart.c
index bae1ab0..a58f2ab 100644
--- a/arch/arm/mach-msm/subsystem_restart.c
+++ b/arch/arm/mach-msm/subsystem_restart.c
@@ -40,12 +40,68 @@
#include "smd_private.h"
+/**
+ * enum p_subsys_state - state of a subsystem (private)
+ * @SUBSYS_NORMAL: subsystem is operating normally
+ * @SUBSYS_CRASHED: subsystem has crashed and hasn't been shutdown
+ * @SUBSYS_RESTARTING: subsystem has been shutdown and is now restarting
+ *
+ * The 'private' side of the subsytem state used to determine where in the
+ * restart process the subsystem is.
+ */
+enum p_subsys_state {
+ SUBSYS_NORMAL,
+ SUBSYS_CRASHED,
+ SUBSYS_RESTARTING,
+};
+
+/**
+ * enum subsys_state - state of a subsystem (public)
+ * @SUBSYS_OFFLINE: subsystem is offline
+ * @SUBSYS_ONLINE: subsystem is online
+ *
+ * The 'public' side of the subsytem state, exposed to userspace.
+ */
+enum subsys_state {
+ SUBSYS_OFFLINE,
+ SUBSYS_ONLINE,
+};
+
+static const char * const subsys_states[] = {
+ [SUBSYS_OFFLINE] = "OFFLINE",
+ [SUBSYS_ONLINE] = "ONLINE",
+};
+
+/**
+ * struct subsys_tracking - track state of a subsystem or restart order
+ * @p_state: private state of subsystem/order
+ * @state: public state of subsystem/order
+ * @s_lock: protects p_state
+ * @lock: protects subsystem/order callbacks and state
+ *
+ * Tracks the state of a subsystem or a set of subsystems (restart order).
+ * Doing this avoids the need to grab each subsystem's lock and update
+ * each subsystems state when restarting an order.
+ */
+struct subsys_tracking {
+ enum p_subsys_state p_state;
+ spinlock_t s_lock;
+ enum subsys_state state;
+ struct mutex lock;
+};
+
+/**
+ * struct subsys_soc_restart_order - subsystem restart order
+ * @subsystem_list: names of subsystems in this restart order
+ * @count: number of subsystems in order
+ * @track: state tracking and locking
+ * @subsys_ptrs: pointers to subsystems in this restart order
+ */
struct subsys_soc_restart_order {
const char * const *subsystem_list;
int count;
- struct mutex shutdown_lock;
- struct mutex powerup_lock;
+ struct subsys_tracking track;
struct subsys_device *subsys_ptrs[];
};
@@ -55,36 +111,34 @@
struct list_head list;
};
-enum subsys_state {
- SUBSYS_OFFLINE,
- SUBSYS_ONLINE,
- SUBSYS_CRASHED,
-};
-
-static const char * const subsys_states[] = {
- [SUBSYS_OFFLINE] = "OFFLINE",
- [SUBSYS_ONLINE] = "ONLINE",
- [SUBSYS_CRASHED] = "CRASHED",
-};
-
+/**
+ * struct subsys_device - subsystem device
+ * @desc: subsystem descriptor
+ * @wake_lock: prevents suspend during subsystem_restart()
+ * @wlname: name of @wake_lock
+ * @work: context for subsystem_restart_wq_func() for this device
+ * @track: state tracking and locking
+ * @notify: subsys notify handle
+ * @dev: device
+ * @owner: module that provides @desc
+ * @count: reference count of subsystem_get()/subsystem_put()
+ * @id: ida
+ * @restart_order: order of other devices this devices restarts with
+ * @dentry: debugfs directory for this device
+ */
struct subsys_device {
struct subsys_desc *desc;
struct wake_lock wake_lock;
char wlname[64];
struct work_struct work;
- spinlock_t restart_lock;
- bool restarting;
+
+ struct subsys_tracking track;
void *notify;
struct device dev;
struct module *owner;
int count;
- enum subsys_state state;
int id;
-
- struct mutex shutdown_lock;
- struct mutex powerup_lock;
-
void *restart_order;
#ifdef CONFIG_DEBUG_FS
struct dentry *dentry;
@@ -105,7 +159,7 @@
static ssize_t state_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
- enum subsys_state state = to_subsys(dev)->state;
+ enum subsys_state state = to_subsys(dev)->track.state;
return snprintf(buf, PAGE_SIZE, "%s\n", subsys_states[state]);
}
@@ -114,14 +168,14 @@
{
unsigned long flags;
- spin_lock_irqsave(&subsys->restart_lock, flags);
- if (subsys->state != state) {
- subsys->state = state;
- spin_unlock_irqrestore(&subsys->restart_lock, flags);
+ spin_lock_irqsave(&subsys->track.s_lock, flags);
+ if (subsys->track.state != state) {
+ subsys->track.state = state;
+ spin_unlock_irqrestore(&subsys->track.s_lock, flags);
sysfs_notify(&subsys->dev.kobj, NULL, "state");
return;
}
- spin_unlock_irqrestore(&subsys->restart_lock, flags);
+ spin_unlock_irqrestore(&subsys->track.s_lock, flags);
}
static struct device_attribute subsys_attrs[] = {
@@ -396,56 +450,27 @@
struct subsys_device, work);
struct subsys_device **list;
struct subsys_desc *desc = dev->desc;
- struct subsys_soc_restart_order *soc_restart_order = NULL;
- struct mutex *powerup_lock;
- struct mutex *shutdown_lock;
+ struct subsys_soc_restart_order *order = dev->restart_order;
+ struct subsys_tracking *track;
unsigned count;
unsigned long flags;
- if (restart_level != RESET_SUBSYS_INDEPENDENT)
- soc_restart_order = dev->restart_order;
-
/*
* It's OK to not take the registration lock at this point.
* This is because the subsystem list inside the relevant
* restart order is not being traversed.
*/
- if (!soc_restart_order) {
+ if (restart_level != RESET_SUBSYS_INDEPENDENT && order) {
+ list = order->subsys_ptrs;
+ count = order->count;
+ track = &order->track;
+ } else {
list = &dev;
count = 1;
- powerup_lock = &dev->powerup_lock;
- shutdown_lock = &dev->shutdown_lock;
- } else {
- list = soc_restart_order->subsys_ptrs;
- count = soc_restart_order->count;
- powerup_lock = &soc_restart_order->powerup_lock;
- shutdown_lock = &soc_restart_order->shutdown_lock;
+ track = &dev->track;
}
- pr_debug("[%p]: Attempting to get shutdown lock!\n", current);
-
- /*
- * Try to acquire shutdown_lock. If this fails, these subsystems are
- * already being restarted - return.
- */
- if (!mutex_trylock(shutdown_lock))
- goto out;
-
- pr_debug("[%p]: Attempting to get powerup lock!\n", current);
-
- /*
- * Now that we've acquired the shutdown lock, either we're the first to
- * restart these subsystems or some other thread is doing the powerup
- * sequence for these subsystems. In the latter case, panic and bail
- * out, since a subsystem died in its powerup sequence. This catches
- * the case where a subsystem in a restart order isn't the one
- * who initiated the original restart but has crashed while the restart
- * order is being rebooted.
- */
- if (!mutex_trylock(powerup_lock))
- panic("%s[%p]: Subsystem died during powerup!",
- __func__, current);
-
+ mutex_lock(&track->lock);
do_epoch_check(dev);
/*
@@ -461,13 +486,9 @@
for_each_subsys_device(list, count, NULL, subsystem_shutdown);
send_notification_to_order(list, count, SUBSYS_AFTER_SHUTDOWN);
- /*
- * Now that we've finished shutting down these subsystems, release the
- * shutdown lock. If a subsystem restart request comes in for a
- * subsystem in _this_ restart order after the unlock below, and
- * before the powerup lock is released, panic and bail out.
- */
- mutex_unlock(shutdown_lock);
+ spin_lock_irqsave(&track->s_lock, flags);
+ track->p_state = SUBSYS_RESTARTING;
+ spin_unlock_irqrestore(&track->s_lock, flags);
/* Collect ram dumps for all subsystems in order here */
for_each_subsys_device(list, count, NULL, subsystem_ramdump);
@@ -479,44 +500,46 @@
pr_info("[%p]: Restart sequence for %s completed.\n",
current, desc->name);
- mutex_unlock(powerup_lock);
-
mutex_unlock(&soc_order_reg_lock);
+ mutex_unlock(&track->lock);
- pr_debug("[%p]: Released powerup lock!\n", current);
-
-out:
- spin_lock_irqsave(&dev->restart_lock, flags);
- dev->restarting = false;
+ spin_lock_irqsave(&track->s_lock, flags);
+ track->p_state = SUBSYS_NORMAL;
wake_unlock(&dev->wake_lock);
- spin_unlock_irqrestore(&dev->restart_lock, flags);
+ spin_unlock_irqrestore(&track->s_lock, flags);
}
static void __subsystem_restart_dev(struct subsys_device *dev)
{
struct subsys_desc *desc = dev->desc;
const char *name = dev->desc->name;
+ struct subsys_soc_restart_order *order = dev->restart_order;
+ struct subsys_tracking *track;
unsigned long flags;
+ if (restart_level != RESET_SUBSYS_INDEPENDENT && order)
+ track = &order->track;
+ else
+ track = &dev->track;
+
pr_debug("Restarting %s [level=%d]!\n", desc->name, restart_level);
/*
- * We want to allow drivers to call subsystem_restart{_dev}() as many
- * times as they want up until the point where the subsystem is
- * shutdown.
+ * Allow drivers to call subsystem_restart{_dev}() as many times as
+ * they want up until the point where the subsystem is shutdown.
*/
- spin_lock_irqsave(&dev->restart_lock, flags);
- if (dev->state != SUBSYS_CRASHED) {
- if (dev->state == SUBSYS_ONLINE && !dev->restarting) {
- dev->restarting = true;
- dev->state = SUBSYS_CRASHED;
+ spin_lock_irqsave(&track->s_lock, flags);
+ if (track->p_state != SUBSYS_CRASHED) {
+ if (dev->track.state == SUBSYS_ONLINE &&
+ track->p_state != SUBSYS_RESTARTING) {
+ track->p_state = SUBSYS_CRASHED;
wake_lock(&dev->wake_lock);
queue_work(ssr_wq, &dev->work);
} else {
panic("Subsystem %s crashed during SSR!", name);
}
}
- spin_unlock_irqrestore(&dev->restart_lock, flags);
+ spin_unlock_irqrestore(&track->s_lock, flags);
}
int subsystem_restart_dev(struct subsys_device *dev)
@@ -650,8 +673,7 @@
struct subsys_device *subsys = to_subsys(dev);
wake_lock_destroy(&subsys->wake_lock);
- mutex_destroy(&subsys->shutdown_lock);
- mutex_destroy(&subsys->powerup_lock);
+ mutex_destroy(&subsys->track.lock);
ida_simple_remove(&subsys_ida, subsys->id);
kfree(subsys);
}
@@ -670,7 +692,7 @@
subsys->dev.parent = desc->dev;
subsys->dev.bus = &subsys_bus_type;
subsys->dev.release = subsys_device_release;
- subsys->state = SUBSYS_ONLINE; /* Until proper refcounting appears */
+ subsys->track.state = SUBSYS_ONLINE; /* Until proper refcounting */
subsys->notify = subsys_notif_add_subsys(desc->name);
subsys->restart_order = update_restart_order(subsys);
@@ -678,7 +700,7 @@
snprintf(subsys->wlname, sizeof(subsys->wlname), "ssr(%s)", desc->name);
wake_lock_init(&subsys->wake_lock, WAKE_LOCK_SUSPEND, subsys->wlname);
INIT_WORK(&subsys->work, subsystem_restart_wq_func);
- spin_lock_init(&subsys->restart_lock);
+ spin_lock_init(&subsys->track.s_lock);
subsys->id = ida_simple_get(&subsys_ida, 0, 0, GFP_KERNEL);
if (subsys->id < 0) {
@@ -687,8 +709,7 @@
}
dev_set_name(&subsys->dev, "subsys%d", subsys->id);
- mutex_init(&subsys->shutdown_lock);
- mutex_init(&subsys->powerup_lock);
+ mutex_init(&subsys->track.lock);
ret = subsys_debugfs_add(subsys);
if (ret)
@@ -705,8 +726,7 @@
err_register:
subsys_debugfs_remove(subsys);
err_debugfs:
- mutex_destroy(&subsys->shutdown_lock);
- mutex_destroy(&subsys->powerup_lock);
+ mutex_destroy(&subsys->track.lock);
ida_simple_remove(&subsys_ida, subsys->id);
err_ida:
wake_lock_destroy(&subsys->wake_lock);
@@ -721,10 +741,10 @@
return;
if (get_device(&subsys->dev)) {
- mutex_lock(&subsys->powerup_lock);
+ mutex_lock(&subsys->track.lock);
WARN_ON(subsys->count);
device_unregister(&subsys->dev);
- mutex_unlock(&subsys->powerup_lock);
+ mutex_unlock(&subsys->track.lock);
subsys_debugfs_remove(subsys);
put_device(&subsys->dev);
}
@@ -760,13 +780,13 @@
if (cpu_is_msm8x60()) {
for (i = 0; i < ARRAY_SIZE(orders_8x60_all); i++) {
- mutex_init(&orders_8x60_all[i]->powerup_lock);
- mutex_init(&orders_8x60_all[i]->shutdown_lock);
+ mutex_init(&orders_8x60_all[i]->track.lock);
+ spin_lock_init(&orders_8x60_all[i]->track.s_lock);
}
for (i = 0; i < ARRAY_SIZE(orders_8x60_modems); i++) {
- mutex_init(&orders_8x60_modems[i]->powerup_lock);
- mutex_init(&orders_8x60_modems[i]->shutdown_lock);
+ mutex_init(&orders_8x60_modems[i]->track.lock);
+ spin_lock_init(&orders_8x60_modems[i]->track.s_lock);
}
restart_orders = orders_8x60_all;
@@ -779,13 +799,12 @@
}
for (i = 0; i < n_restart_orders; i++) {
- mutex_init(&restart_orders[i]->powerup_lock);
- mutex_init(&restart_orders[i]->shutdown_lock);
+ mutex_init(&restart_orders[i]->track.lock);
+ spin_lock_init(&restart_orders[i]->track.s_lock);
}
- if (restart_orders == NULL || n_restart_orders < 1) {
+ if (restart_orders == NULL || n_restart_orders < 1)
WARN_ON(1);
- }
return 0;
}
diff --git a/arch/arm/mach-msm/timer.c b/arch/arm/mach-msm/timer.c
index b361d9d..b61604a 100644
--- a/arch/arm/mach-msm/timer.c
+++ b/arch/arm/mach-msm/timer.c
@@ -964,9 +964,8 @@
if (!smp_processor_id())
return 0;
- if (cpu_is_msm8x60() || cpu_is_msm8960() || cpu_is_apq8064() ||
- cpu_is_msm8930() || cpu_is_msm8930aa() || cpu_is_msm8627() ||
- cpu_is_msm8960ab() || cpu_is_apq8064ab())
+ if (cpu_is_msm8x60() || soc_class_is_msm8960() ||
+ soc_class_is_apq8064() || soc_class_is_msm8930())
__raw_writel(DGT_CLK_CTL_DIV_4, MSM_TMR_BASE + DGT_CLK_CTL);
if (__get_cpu_var(first_boot)) {
@@ -1062,9 +1061,8 @@
sclk_hz = 32765;
gpt->flags |= MSM_CLOCK_FLAGS_UNSTABLE_COUNT;
dgt->flags |= MSM_CLOCK_FLAGS_UNSTABLE_COUNT;
- } else if (cpu_is_msm8960() || cpu_is_apq8064() || cpu_is_msm8930() ||
- cpu_is_msm8930aa() || cpu_is_msm8627() ||
- cpu_is_msm8960ab() || cpu_is_apq8064ab()) {
+ } else if (soc_class_is_msm8960() || soc_class_is_apq8064() ||
+ soc_class_is_msm8930()) {
global_timer_offset = MSM_TMR0_BASE - MSM_TMR_BASE;
dgt->freq = 6750000;
__raw_writel(DGT_CLK_CTL_DIV_4, MSM_TMR_BASE + DGT_CLK_CTL);
@@ -1073,8 +1071,7 @@
gpt->freq = 32765;
gpt_hz = 32765;
sclk_hz = 32765;
- if (!cpu_is_msm8930() && !cpu_is_msm8930aa() &&
- !cpu_is_msm8627() && !cpu_is_msm8960ab()) {
+ if (!soc_class_is_msm8930() && !cpu_is_msm8960ab()) {
gpt->flags |= MSM_CLOCK_FLAGS_UNSTABLE_COUNT;
dgt->flags |= MSM_CLOCK_FLAGS_UNSTABLE_COUNT;
}
@@ -1124,10 +1121,9 @@
"failed for %s\n", cs->name);
ce->irq = clock->irq;
- if (cpu_is_msm8x60() || cpu_is_msm8960() || cpu_is_apq8064() ||
- cpu_is_msm8930() || cpu_is_msm9615() || cpu_is_msm8625() ||
- cpu_is_msm8627() || cpu_is_msm8930aa() ||
- cpu_is_msm8960ab() || cpu_is_apq8064ab()) {
+ if (cpu_is_msm8x60() || cpu_is_msm9615() || cpu_is_msm8625() ||
+ soc_class_is_msm8960() || soc_class_is_apq8064() ||
+ soc_class_is_msm8930()) {
clock->percpu_evt = alloc_percpu(struct clock_event_device *);
if (!clock->percpu_evt) {
pr_err("msm_timer_init: memory allocation "
diff --git a/arch/arm/tools/mach-types b/arch/arm/tools/mach-types
index d283705..2b31f47 100644
--- a/arch/arm/tools/mach-types
+++ b/arch/arm/tools/mach-types
@@ -1196,3 +1196,4 @@
msm8625_qrd7 MACH_MSM8625_QRD7 MSM8625_QRD7 4095
msm8625_ffa MACH_MSM8625_FFA MSM8625_FFA 4166
msm8625_evt MACH_MSM8625_EVT MSM8625_EVT 4193
+qrd_skud_prime MACH_QRD_SKUD_PRIME QRD_SKUD_PRIME 4393
diff --git a/block/row-iosched.c b/block/row-iosched.c
index f24437c..b7965c6 100644
--- a/block/row-iosched.c
+++ b/block/row-iosched.c
@@ -387,12 +387,19 @@
if (list_empty(&rd->row_queues[currq].rqueue.fifo)) {
/* check idling */
if (delayed_work_pending(&rd->read_idle.idle_work)) {
- row_log_rowq(rd, currq,
- "Delayed work pending. Exiting");
- goto done;
+ if (force) {
+ (void)cancel_delayed_work(
+ &rd->read_idle.idle_work);
+ row_log_rowq(rd, currq,
+ "Canceled delayed work - forced dispatch");
+ } else {
+ row_log_rowq(rd, currq,
+ "Delayed work pending. Exiting");
+ goto done;
+ }
}
- if (queue_idling_enabled[currq] &&
+ if (!force && queue_idling_enabled[currq] &&
rd->row_queues[currq].rqueue.idle_data.begin_idling) {
if (!queue_delayed_work(rd->read_idle.idle_workqueue,
&rd->read_idle.idle_work,
@@ -603,29 +610,27 @@
return ret; \
}
STORE_FUNCTION(row_hp_read_quantum_store,
- &rowd->row_queues[ROWQ_PRIO_HIGH_READ].disp_quantum, 0,
- INT_MAX, 0);
+&rowd->row_queues[ROWQ_PRIO_HIGH_READ].disp_quantum, 1, INT_MAX, 0);
STORE_FUNCTION(row_rp_read_quantum_store,
- &rowd->row_queues[ROWQ_PRIO_REG_READ].disp_quantum, 0,
- INT_MAX, 0);
+ &rowd->row_queues[ROWQ_PRIO_REG_READ].disp_quantum,
+ 1, INT_MAX, 0);
STORE_FUNCTION(row_hp_swrite_quantum_store,
- &rowd->row_queues[ROWQ_PRIO_HIGH_SWRITE].disp_quantum, 0,
- INT_MAX, 0);
+ &rowd->row_queues[ROWQ_PRIO_HIGH_SWRITE].disp_quantum,
+ 1, INT_MAX, 0);
STORE_FUNCTION(row_rp_swrite_quantum_store,
- &rowd->row_queues[ROWQ_PRIO_REG_SWRITE].disp_quantum, 0,
- INT_MAX, 0);
+ &rowd->row_queues[ROWQ_PRIO_REG_SWRITE].disp_quantum,
+ 1, INT_MAX, 0);
STORE_FUNCTION(row_rp_write_quantum_store,
- &rowd->row_queues[ROWQ_PRIO_REG_WRITE].disp_quantum, 0,
- INT_MAX, 0);
+ &rowd->row_queues[ROWQ_PRIO_REG_WRITE].disp_quantum,
+ 1, INT_MAX, 0);
STORE_FUNCTION(row_lp_read_quantum_store,
- &rowd->row_queues[ROWQ_PRIO_LOW_READ].disp_quantum, 0,
- INT_MAX, 0);
+ &rowd->row_queues[ROWQ_PRIO_LOW_READ].disp_quantum,
+ 1, INT_MAX, 0);
STORE_FUNCTION(row_lp_swrite_quantum_store,
- &rowd->row_queues[ROWQ_PRIO_LOW_SWRITE].disp_quantum, 0,
- INT_MAX, 1);
+ &rowd->row_queues[ROWQ_PRIO_LOW_SWRITE].disp_quantum,
+ 1, INT_MAX, 1);
STORE_FUNCTION(row_read_idle_store, &rowd->read_idle.idle_time, 1, INT_MAX, 1);
-STORE_FUNCTION(row_read_idle_freq_store, &rowd->read_idle.freq,
- 1, INT_MAX, 1);
+STORE_FUNCTION(row_read_idle_freq_store, &rowd->read_idle.freq, 1, INT_MAX, 1);
#undef STORE_FUNCTION
diff --git a/drivers/char/diag/diag_dci.c b/drivers/char/diag/diag_dci.c
index 2860055..7c0c0b9 100644
--- a/drivers/char/diag/diag_dci.c
+++ b/drivers/char/diag/diag_dci.c
@@ -28,17 +28,23 @@
#include "diagmem.h"
#include "diagchar.h"
#include "diagfwd.h"
+#include "diagfwd_cntl.h"
#include "diag_dci.h"
unsigned int dci_max_reg = 100;
unsigned int dci_max_clients = 10;
+unsigned char dci_cumulative_log_mask[DCI_LOG_MASK_SIZE];
+unsigned char dci_cumulative_event_mask[DCI_EVENT_MASK_SIZE];
+
+#define DCI_CHK_CAPACITY(entry, new_data_len) \
+((entry->data_len + new_data_len > entry->total_capacity) ? 1 : 0) \
static void diag_smd_dci_send_req(int proc_num)
{
void *buf = NULL;
smd_channel_t *smd_ch = NULL;
- int i, r, found = 1;
- int cmd_code_len = 1;
+ int recd_bytes, read_bytes, dci_pkt_len, i;
+ uint8_t recv_pkt_cmd_code;
if (driver->in_busy_dci)
return;
@@ -51,58 +57,237 @@
if (!smd_ch || !buf)
return;
- r = smd_read_avail(smd_ch);
- if (r > IN_BUF_SIZE) {
- if (r < MAX_IN_BUF_SIZE) {
- pr_err("diag: SMD DCI sending pkt upto %d bytes", r);
- buf = krealloc(buf, r, GFP_KERNEL);
+ recd_bytes = smd_read_avail(smd_ch);
+ if (recd_bytes > IN_BUF_SIZE) {
+ if (recd_bytes < MAX_IN_BUF_SIZE) {
+ pr_err("diag: SMD DCI sending pkt upto %d bytes",
+ recd_bytes);
+ buf = krealloc(buf, recd_bytes, GFP_KERNEL);
} else {
pr_err("diag: DCI pkt > %d bytes", MAX_IN_BUF_SIZE);
return;
}
}
- if (buf && r > 0) {
- smd_read(smd_ch, buf, r);
- pr_debug("diag: data received ---\n");
- for (i = 0; i < r; i++)
- pr_debug("\t %x \t", *(((unsigned char *)buf)+i));
+ if (buf && recd_bytes > 0) {
+ smd_read(smd_ch, buf, recd_bytes);
+ pr_debug("diag: data received %d bytes\n", recd_bytes);
+ /* Each SMD read can have multiple DCI packets */
+ read_bytes = 0;
+ while (read_bytes < recd_bytes) {
+ /* read actual length of dci pkt */
+ dci_pkt_len = *(uint16_t *)(buf+2);
+ /* process one dci packet */
+ pr_debug("diag: bytes read = %d, single dci pkt len = %d\n",
+ read_bytes, dci_pkt_len);
+ /* print_hex_dump(KERN_DEBUG, "Single DCI packet :",
+ DUMP_PREFIX_ADDRESS, 16, 1, buf, 5 + dci_pkt_len, 1);*/
+ recv_pkt_cmd_code = *(uint8_t *)(buf+4);
+ if (recv_pkt_cmd_code == LOG_CMD_CODE)
+ extract_dci_log(buf+4);
+ else if (recv_pkt_cmd_code == EVENT_CMD_CODE)
+ extract_dci_events(buf+4);
+ else
+ extract_dci_pkt_rsp(buf); /* pkt response */
+ read_bytes += 5 + dci_pkt_len;
+ buf += 5 + dci_pkt_len; /* advance to next DCI pkt */
+ }
+ driver->in_busy_dci = 1;
+ /* wake up all sleeping DCI clients which have some data */
+ for (i = 0; i < MAX_DCI_CLIENTS; i++)
+ if (driver->dci_client_tbl[i].client &&
+ driver->dci_client_tbl[i].data_len)
+ diag_update_sleeping_process(
+ driver->dci_client_tbl[i].client->tgid,
+ DCI_DATA_TYPE);
+ }
+}
- if (*(uint8_t *)(buf+4) != DCI_CMD_CODE)
- cmd_code_len = 4; /* delayed response */
- driver->write_ptr_dci->length =
- (int)(*(uint16_t *)(buf+2)) - (4+cmd_code_len);
- pr_debug("diag: len = %d\n", (int)(*(uint16_t *)(buf+2))
- - (4+cmd_code_len));
- /* look up DCI client with tag */
- for (i = 0; i < dci_max_reg; i++) {
- if (driver->dci_tbl[i].tag ==
- *(int *)(buf+(4+cmd_code_len))) {
- found = 0;
- break;
+void extract_dci_pkt_rsp(unsigned char *buf)
+{
+ int i = 0, index = -1, cmd_code_len = 1;
+ int curr_client_pid = 0, write_len;
+ struct diag_dci_client_tbl *entry;
+ void *temp_buf = NULL;
+ uint8_t recv_pkt_cmd_code;
+
+ recv_pkt_cmd_code = *(uint8_t *)(buf+4);
+ if (recv_pkt_cmd_code != DCI_PKT_RSP_CODE)
+ cmd_code_len = 4; /* delayed response */
+ write_len = (int)(*(uint16_t *)(buf+2)) - cmd_code_len;
+ pr_debug("diag: len = %d\n", write_len);
+ /* look up DCI client with tag */
+ for (i = 0; i < dci_max_reg; i++) {
+ if (driver->req_tracking_tbl[i].tag ==
+ *(int *)(buf+(4+cmd_code_len))) {
+ *(int *)(buf+4+cmd_code_len) =
+ driver->req_tracking_tbl[i].uid;
+ curr_client_pid =
+ driver->req_tracking_tbl[i].pid;
+ index = i;
+ break;
+ }
+ }
+ if (index == -1)
+ pr_alert("diag: No matching PID for DCI data\n");
+ /* Using PID of client process, find client buffer */
+ for (i = 0; i < MAX_DCI_CLIENTS; i++) {
+ if (curr_client_pid == driver->dci_client_tbl[i].client->tgid) {
+ /* copy pkt rsp in client buf */
+ entry = &(driver->dci_client_tbl[i]);
+ if (DCI_CHK_CAPACITY(entry, 8+write_len)) {
+ pr_alert("diag: create capacity for pkt rsp\n");
+ entry->total_capacity += 8+write_len;
+ temp_buf = krealloc(entry->dci_data,
+ entry->total_capacity, GFP_KERNEL);
+ if (!temp_buf) {
+ pr_err("diag: DCI realloc failed\n");
+ break;
+ } else {
+ entry->dci_data = temp_buf;
+ }
+ }
+ *(int *)(entry->dci_data+entry->data_len) =
+ DCI_PKT_RSP_TYPE;
+ entry->data_len += 4;
+ *(int *)(entry->dci_data+entry->data_len) = write_len;
+ entry->data_len += 4;
+ memcpy(entry->dci_data+entry->data_len,
+ buf+4+cmd_code_len, write_len);
+ entry->data_len += write_len;
+ /* delete immediate response entry */
+ if (driver->buf_in_dci[8+cmd_code_len] != 0x80)
+ driver->req_tracking_tbl[index].pid = 0;
+ break;
+ }
+ }
+}
+
+void extract_dci_events(unsigned char *buf)
+{
+ uint16_t event_id, event_id_packet;
+ uint8_t *event_mask_ptr, byte_mask, payload_len;
+ uint8_t event_data[MAX_EVENT_SIZE], timestamp[8];
+ int i, byte_index, bit_index, length, temp_len;
+ int total_event_len, payload_len_field, timestamp_len;
+ struct diag_dci_client_tbl *entry;
+
+ length = *(uint16_t *)(buf+1); /* total length of event series */
+ temp_len = 0;
+ buf = buf + 3; /* start of event series */
+ while (temp_len < length-1) {
+ *event_data = EVENT_CMD_CODE;
+ event_id_packet = *(uint16_t *)(buf+temp_len);
+ event_id = event_id_packet & 0x0FFF; /* extract 12 bits */
+ if (event_id_packet & 0x8000) {
+ timestamp_len = 2;
+ } else {
+ timestamp_len = 8;
+ memcpy(timestamp, buf+temp_len+2, 8);
+ }
+ if (((event_id_packet & 0x6000) >> 13) == 3) {
+ payload_len_field = 1;
+ payload_len = *(uint8_t *)
+ (buf+temp_len+2+timestamp_len);
+ memcpy(event_data+13, buf+temp_len+2+timestamp_len, 1);
+ memcpy(event_data+14, buf+temp_len+2+timestamp_len+1,
+ payload_len);
+ } else {
+ payload_len_field = 0;
+ payload_len = (event_id_packet & 0x6000) >> 13;
+ if (payload_len < MAX_EVENT_SIZE)
+ memcpy(event_data+13,
+ buf+temp_len+2+timestamp_len, payload_len);
+ else
+ pr_alert("diag: event > %d\n", MAX_EVENT_SIZE);
+ }
+ /* 2 bytes for the event id & timestamp len is hard coded to 8,
+ as individual events have full timestamp */
+ *(uint16_t *)(event_data+1) = 10+payload_len_field+payload_len;
+ *(uint16_t *)(event_data+3) = event_id_packet & 0x7FFF;
+ memcpy(event_data+5, timestamp, 8);
+ total_event_len = 3 + 10 + payload_len_field + payload_len;
+ byte_index = event_id/8;
+ bit_index = event_id % 8;
+ byte_mask = 0x1 << bit_index;
+ /* parse through event mask tbl of each client and check mask */
+ for (i = 0; i < MAX_DCI_CLIENTS; i++) {
+ if (driver->dci_client_tbl[i].client) {
+ entry = &(driver->dci_client_tbl[i]);
+ event_mask_ptr = entry->dci_event_mask +
+ byte_index;
+ if (*event_mask_ptr & byte_mask) {
+ /* copy to client buffer */
+ if (DCI_CHK_CAPACITY(entry,
+ 4 + total_event_len)) {
+ pr_err("diag:DCI event drop\n");
+ driver->dci_client_tbl[i].
+ dropped_events++;
+ return;
+ }
+ *(int *)(entry->dci_data+
+ entry->data_len) = DCI_EVENT_TYPE;
+ memcpy(entry->dci_data+
+ entry->data_len+4, event_data, total_event_len);
+ entry->data_len += 4 + total_event_len;
+ }
}
}
- if (found)
- pr_alert("diag: No matching PID for DCI data\n");
- pr_debug("\n diag PID = %d", driver->dci_tbl[i].pid);
- if (driver->dci_tbl[i].pid == 0)
- pr_alert("diag: Receiving DCI process deleted\n");
- *(int *)(buf+4+cmd_code_len) = driver->dci_tbl[i].uid;
- /* update len after adding UID */
- driver->write_ptr_dci->length =
- driver->write_ptr_dci->length + 4;
- pr_debug("diag: data receivd, wake process\n");
- driver->in_busy_dci = 1;
- diag_update_sleeping_process(driver->dci_tbl[i].pid,
- DCI_DATA_TYPE);
- /* delete immediate response entry */
- if (driver->buf_in_dci[8+cmd_code_len] != 0x80)
- driver->dci_tbl[i].pid = 0;
- for (i = 0; i < dci_max_reg; i++)
- if (driver->dci_tbl[i].pid != 0)
- pr_debug("diag: PID = %d, UID = %d, tag = %d\n",
- driver->dci_tbl[i].pid, driver->dci_tbl[i].uid,
- driver->dci_tbl[i].tag);
- pr_debug("diag: completed clearing table\n");
+ temp_len += 2 + timestamp_len + payload_len_field + payload_len;
+ }
+}
+
+void extract_dci_log(unsigned char *buf)
+{
+ uint16_t log_code, item_num;
+ uint8_t equip_id, *log_mask_ptr, byte_mask;
+ int i, byte_index, found = 0;
+ struct diag_dci_client_tbl *entry;
+
+ log_code = *(uint16_t *)(buf+6);
+ equip_id = LOG_GET_EQUIP_ID(log_code);
+ item_num = LOG_GET_ITEM_NUM(log_code);
+ byte_index = item_num/8 + 2;
+ byte_mask = 0x01 << (item_num % 8);
+
+ /* parse through log mask table of each client and check mask */
+ for (i = 0; i < MAX_DCI_CLIENTS; i++) {
+ if (driver->dci_client_tbl[i].client) {
+ entry = &(driver->dci_client_tbl[i]);
+ log_mask_ptr = entry->dci_log_mask;
+ found = 0;
+ while (log_mask_ptr) {
+ if (*log_mask_ptr == equip_id) {
+ found = 1;
+ pr_debug("diag: find equip id = %x at %p\n",
+ equip_id, log_mask_ptr);
+ break;
+ } else {
+ pr_debug("diag: did not find equip id = %x at %p\n",
+ equip_id, log_mask_ptr);
+ log_mask_ptr += 514;
+ }
+ }
+ if (!found)
+ pr_err("diag: dci equip id not found\n");
+ log_mask_ptr = log_mask_ptr + byte_index;
+ if (*log_mask_ptr & byte_mask) {
+ pr_debug("\t log code %x needed by client %d",
+ log_code, entry->client->tgid);
+ /* copy to client buffer */
+ if (DCI_CHK_CAPACITY(entry,
+ 4 + *(uint16_t *)(buf+2))) {
+ pr_err("diag:DCI log drop\n");
+ driver->dci_client_tbl[i].
+ dropped_logs++;
+ return;
+ }
+ *(int *)(entry->dci_data+entry->data_len) =
+ DCI_LOG_TYPE;
+ memcpy(entry->dci_data+entry->data_len+4, buf+4,
+ *(uint16_t *)(buf+2));
+ entry->data_len += 4 + *(uint16_t *)(buf+2);
+ }
+ }
}
}
@@ -113,7 +298,7 @@
static void diag_smd_dci_notify(void *ctxt, unsigned event)
{
- queue_work(driver->diag_wq, &(driver->diag_read_smd_dci_work));
+ queue_work(driver->diag_dci_wq, &(driver->diag_read_smd_dci_work));
}
void diag_dci_notify_client(int peripheral_mask)
@@ -121,11 +306,11 @@
int i, stat;
/* Notify the DCI process that the peripheral DCI Channel is up */
- for (i = 0; i < MAX_DCI_CLIENT; i++) {
- if (driver->dci_notify_tbl[i].list & peripheral_mask) {
- pr_debug("diag: sending signal now\n");
- stat = send_sig(driver->dci_notify_tbl[i].signal_type,
- driver->dci_notify_tbl[i].client, 0);
+ for (i = 0; i < MAX_DCI_CLIENTS; i++) {
+ if (driver->dci_client_tbl[i].list & peripheral_mask) {
+ pr_info("diag: sending signal now\n");
+ stat = send_sig(driver->dci_client_tbl[i].signal_type,
+ driver->dci_client_tbl[i].client, 0);
if (stat)
pr_err("diag: Err send sig stat: %d\n", stat);
break;
@@ -139,7 +324,7 @@
if (pdev->id == SMD_APPS_MODEM) {
err = smd_open("DIAG_2", &driver->ch_dci, driver,
- diag_smd_dci_notify);
+ diag_smd_dci_notify);
if (err)
pr_err("diag: cannot open DCI port, Id = %d, err ="
" %d\n", pdev->id, err);
@@ -163,10 +348,12 @@
driver->apps_dci_buf[0] = CONTROL_CHAR; /* start */
driver->apps_dci_buf[1] = 1; /* version */
*(uint16_t *)(driver->apps_dci_buf + 2) = len + 4 + 1; /* length */
- driver->apps_dci_buf[4] = DCI_CMD_CODE; /* DCI ID */
- *(int *)(driver->apps_dci_buf + 5) = driver->dci_tbl[index].tag;
+ driver->apps_dci_buf[4] = DCI_PKT_RSP_CODE;
+ *(int *)(driver->apps_dci_buf + 5) =
+ driver->req_tracking_tbl[index].tag;
for (i = 0; i < len; i++)
driver->apps_dci_buf[i+9] = *(buf+i);
+
driver->apps_dci_buf[9+len] = CONTROL_CHAR; /* end */
if (entry.client_id == MODEM_PROC && driver->ch_dci) {
@@ -185,7 +372,7 @@
int i, new_dci_client = 1, ret = -1;
for (i = 0; i < dci_max_reg; i++) {
- if (driver->dci_tbl[i].pid == current->tgid) {
+ if (driver->req_tracking_tbl[i].pid == current->tgid) {
new_dci_client = 0;
break;
}
@@ -193,7 +380,7 @@
mutex_lock(&driver->dci_mutex);
if (new_dci_client)
driver->num_dci_client++;
- if (driver->num_dci_client > MAX_DCI_CLIENT) {
+ if (driver->num_dci_client > MAX_DCI_CLIENTS) {
pr_info("diag: Max DCI Client limit reached\n");
driver->num_dci_client--;
mutex_unlock(&driver->dci_mutex);
@@ -202,10 +389,10 @@
/* Make an entry in kernel DCI table */
driver->dci_tag++;
for (i = 0; i < dci_max_reg; i++) {
- if (driver->dci_tbl[i].pid == 0) {
- driver->dci_tbl[i].pid = current->tgid;
- driver->dci_tbl[i].uid = uid;
- driver->dci_tbl[i].tag = driver->dci_tag;
+ if (driver->req_tracking_tbl[i].pid == 0) {
+ driver->req_tracking_tbl[i].pid = current->tgid;
+ driver->req_tracking_tbl[i].uid = uid;
+ driver->req_tracking_tbl[i].tag = driver->dci_tag;
ret = i;
break;
}
@@ -214,62 +401,313 @@
return ret;
}
-int diag_process_dci_client(unsigned char *buf, int len)
+int diag_process_dci_transaction(unsigned char *buf, int len)
{
unsigned char *temp = buf;
- uint16_t subsys_cmd_code;
- int subsys_id, cmd_code, i, ret = -1, index = -1;
+ uint16_t subsys_cmd_code, log_code, item_num;
+ int subsys_id, cmd_code, i, ret = -1, index = -1, found = 0;
struct diag_master_table entry;
+ int count, set_mask, num_codes, byte_index, bit_index, event_id;
+ uint8_t equip_id, *log_mask_ptr, *head_log_mask_ptr, byte_mask;
+ uint8_t *event_mask_ptr;
- /* enter this UID into kernel table and return index */
- index = diag_register_dci_transaction(*(int *)temp);
- if (index < 0) {
- pr_alert("diag: registering new DCI transaction failed\n");
- return DIAG_DCI_NO_REG;
- }
- temp += 4;
- /* Check for registered peripheral and fwd pkt to apropriate proc */
- cmd_code = (int)(*(char *)buf);
- temp++;
- subsys_id = (int)(*(char *)temp);
- temp++;
- subsys_cmd_code = *(uint16_t *)temp;
- temp += 2;
- pr_debug("diag: %d %d %d", cmd_code, subsys_id, subsys_cmd_code);
- for (i = 0; i < diag_max_reg; i++) {
- entry = driver->table[i];
- if (entry.process_id != NO_PROCESS) {
- if (entry.cmd_code == cmd_code && entry.subsys_id ==
- subsys_id && entry.cmd_code_lo <=
- subsys_cmd_code &&
- entry.cmd_code_hi >= subsys_cmd_code) {
- ret = diag_send_dci_pkt(entry, buf, len, index);
- } else if (entry.cmd_code == 255
- && cmd_code == 75) {
- if (entry.subsys_id ==
- subsys_id &&
- entry.cmd_code_lo <=
- subsys_cmd_code &&
- entry.cmd_code_hi >=
- subsys_cmd_code) {
- ret = diag_send_dci_pkt(entry, buf, len,
- index);
- }
- } else if (entry.cmd_code == 255 &&
- entry.subsys_id == 255) {
- if (entry.cmd_code_lo <=
- cmd_code &&
- entry.
- cmd_code_hi >= cmd_code) {
- ret = diag_send_dci_pkt(entry, buf, len,
- index);
+ /* This is Pkt request/response transaction */
+ if (*(int *)temp > 0) {
+ /* enter this UID into kernel table and return index */
+ index = diag_register_dci_transaction(*(int *)temp);
+ if (index < 0) {
+ pr_alert("diag: registering new DCI transaction failed\n");
+ return DIAG_DCI_NO_REG;
+ }
+ temp += 4;
+ /*
+ * Check for registered peripheral and fwd pkt to
+ * appropriate proc
+ */
+ cmd_code = (int)(*(char *)temp);
+ temp++;
+ subsys_id = (int)(*(char *)temp);
+ temp++;
+ subsys_cmd_code = *(uint16_t *)temp;
+ temp += 2;
+ pr_debug("diag: %d %d %d", cmd_code, subsys_id,
+ subsys_cmd_code);
+ for (i = 0; i < diag_max_reg; i++) {
+ entry = driver->table[i];
+ if (entry.process_id != NO_PROCESS) {
+ if (entry.cmd_code == cmd_code &&
+ entry.subsys_id == subsys_id &&
+ entry.cmd_code_lo <= subsys_cmd_code &&
+ entry.cmd_code_hi >= subsys_cmd_code) {
+ ret = diag_send_dci_pkt(entry, buf,
+ len, index);
+ } else if (entry.cmd_code == 255
+ && cmd_code == 75) {
+ if (entry.subsys_id == subsys_id &&
+ entry.cmd_code_lo <=
+ subsys_cmd_code &&
+ entry.cmd_code_hi >=
+ subsys_cmd_code) {
+ ret = diag_send_dci_pkt(entry,
+ buf, len, index);
+ }
+ } else if (entry.cmd_code == 255 &&
+ entry.subsys_id == 255) {
+ if (entry.cmd_code_lo <= cmd_code &&
+ entry.cmd_code_hi >=
+ cmd_code) {
+ ret = diag_send_dci_pkt(entry,
+ buf, len, index);
+ }
}
}
}
+ } else if (*(int *)temp == DCI_LOG_TYPE) {
+ /* find client id and table */
+ for (i = 0; i < MAX_DCI_CLIENTS; i++) {
+ if (driver->dci_client_tbl[i].client->tgid ==
+ current->tgid) {
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ pr_err("diag: dci client not registered/found\n");
+ return ret;
+ }
+ /* Extract each log code and put in client table */
+ temp += 4;
+ set_mask = *(int *)temp;
+ temp += 4;
+ num_codes = *(int *)temp;
+ temp += 4;
+
+ head_log_mask_ptr = driver->dci_client_tbl[i].dci_log_mask;
+ pr_info("diag: head of dci log mask %p\n", head_log_mask_ptr);
+ count = 0; /* iterator for extracting log codes */
+ while (count < num_codes) {
+ log_code = *(uint16_t *)temp;
+ equip_id = LOG_GET_EQUIP_ID(log_code);
+ item_num = LOG_GET_ITEM_NUM(log_code);
+ byte_index = item_num/8 + 2;
+ byte_mask = 0x01 << (item_num % 8);
+ /*
+ * Parse through log mask table and find
+ * relevant range
+ */
+ log_mask_ptr = head_log_mask_ptr;
+ found = 0;
+ while (log_mask_ptr) {
+ if (*log_mask_ptr == equip_id) {
+ found = 1;
+ pr_info("diag: find equip id = %x at %p\n",
+ equip_id, log_mask_ptr);
+ break;
+ } else {
+ pr_info("diag: did not find equip id = %x at %p\n",
+ equip_id, log_mask_ptr);
+ log_mask_ptr += 514;
+ }
+ }
+ if (!found) {
+ pr_err("diag: dci equip id not found\n");
+ return ret;
+ }
+ *(log_mask_ptr+1) = 1; /* set the dirty byte */
+ log_mask_ptr = log_mask_ptr + byte_index;
+ if (set_mask)
+ *log_mask_ptr |= byte_mask;
+ else
+ *log_mask_ptr &= ~byte_mask;
+ temp += 2;
+ count++;
+ ret = DIAG_DCI_NO_ERROR;
+ }
+ /* add to cumulative mask */
+ update_dci_cumulative_log_mask(i);
+ /* send updated mask to peripherals */
+ diag_send_dci_log_mask(driver->ch_cntl);
+ } else if (*(int *)temp == DCI_EVENT_TYPE) {
+ /* find client id and table */
+ for (i = 0; i < MAX_DCI_CLIENTS; i++) {
+ if (driver->dci_client_tbl[i].client->tgid ==
+ current->tgid) {
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ pr_err("diag: dci client not registered/found\n");
+ return ret;
+ }
+ /* Extract each log code and put in client table */
+ temp += 4;
+ set_mask = *(int *)temp;
+ temp += 4;
+ num_codes = *(int *)temp;
+ temp += 4;
+
+ event_mask_ptr = driver->dci_client_tbl[i].dci_event_mask;
+ pr_debug("diag: head of dci event mask %p\n", event_mask_ptr);
+ count = 0; /* iterator for extracting log codes */
+ while (count < num_codes) {
+ event_id = *(int *)temp;
+ byte_index = event_id/8;
+ bit_index = event_id % 8;
+ byte_mask = 0x1 << bit_index;
+ /*
+ * Parse through event mask table and set
+ * relevant byte & bit combination
+ */
+ if (set_mask)
+ *(event_mask_ptr + byte_index) |= byte_mask;
+ else
+ *(event_mask_ptr + byte_index) &= ~byte_mask;
+ temp += sizeof(int);
+ count++;
+ ret = DIAG_DCI_NO_ERROR;
+ }
+ /* add to cumulative mask */
+ update_dci_cumulative_event_mask(i);
+ /* send updated mask to peripherals */
+ diag_send_dci_event_mask(driver->ch_cntl);
+ } else {
+ pr_alert("diag: Incorrect DCI transaction\n");
}
return ret;
}
+void update_dci_cumulative_event_mask(int client_index)
+{
+ int i;
+ uint8_t *update_ptr = dci_cumulative_event_mask;
+ uint8_t *event_mask_ptr;
+
+ event_mask_ptr = driver->dci_client_tbl[client_index].dci_event_mask;
+ for (i = 0; i < DCI_EVENT_MASK_SIZE; i++)
+ *(update_ptr+i) |= *(event_mask_ptr+i);
+}
+
+void diag_send_dci_event_mask(smd_channel_t *ch)
+{
+ void *buf = driver->buf_event_mask_update;
+ int header_size = sizeof(struct diag_ctrl_event_mask);
+ int wr_size = -ENOMEM, retry_count = 0, timer;
+
+ mutex_lock(&driver->diag_cntl_mutex);
+ /* send event mask update */
+ driver->event_mask->cmd_type = DIAG_CTRL_MSG_EVENT_MASK;
+ driver->event_mask->data_len = 7 + DCI_EVENT_MASK_SIZE;
+ driver->event_mask->stream_id = DCI_MASK_STREAM;
+ driver->event_mask->status = 3; /* status for valid mask */
+ driver->event_mask->event_config = 1; /* event config */
+ driver->event_mask->event_mask_size = DCI_EVENT_MASK_SIZE;
+ memcpy(buf, driver->event_mask, header_size);
+ memcpy(buf+header_size, dci_cumulative_event_mask, DCI_EVENT_MASK_SIZE);
+ if (ch) {
+ while (retry_count < 3) {
+ wr_size = smd_write(ch, buf,
+ header_size + DCI_EVENT_MASK_SIZE);
+ if (wr_size == -ENOMEM) {
+ retry_count++;
+ for (timer = 0; timer < 5; timer++)
+ udelay(2000);
+ } else {
+ break;
+ }
+ }
+ if (wr_size != header_size + DCI_EVENT_MASK_SIZE)
+ pr_err("diag: error writing dci event mask %d, tried %d\n",
+ wr_size, header_size + DCI_EVENT_MASK_SIZE);
+ } else
+ pr_err("diag: ch not valid for dci event mask update\n");
+ mutex_unlock(&driver->diag_cntl_mutex);
+}
+
+void update_dci_cumulative_log_mask(int client_index)
+{
+ int i, j;
+ uint8_t *update_ptr = dci_cumulative_log_mask;
+ uint8_t *log_mask_ptr =
+ driver->dci_client_tbl[client_index].dci_log_mask;
+
+ *update_ptr = 0; /* add first equip id */
+ /* skip the first equip id */
+ update_ptr++; log_mask_ptr++;
+ for (i = 0; i < 16; i++) {
+ for (j = 0; j < 513; j++) {
+ *update_ptr |= *log_mask_ptr;
+ update_ptr++;
+ log_mask_ptr++;
+ }
+ *update_ptr = i+1;
+ update_ptr++;
+ log_mask_ptr++;
+ }
+}
+
+void diag_send_dci_log_mask(smd_channel_t *ch)
+{
+ void *buf = driver->buf_log_mask_update;
+ int header_size = sizeof(struct diag_ctrl_log_mask);
+ uint8_t *log_mask_ptr = dci_cumulative_log_mask;
+ int i, wr_size = -ENOMEM, retry_count = 0, timer;
+
+ mutex_lock(&driver->diag_cntl_mutex);
+ for (i = 0; i < 16; i++) {
+ driver->log_mask->cmd_type = DIAG_CTRL_MSG_LOG_MASK;
+ driver->log_mask->num_items = 512;
+ driver->log_mask->data_len = 11 + 512;
+ driver->log_mask->stream_id = DCI_MASK_STREAM;
+ driver->log_mask->status = 3; /* status for valid mask */
+ driver->log_mask->equip_id = *log_mask_ptr;
+ driver->log_mask->log_mask_size = 512;
+ memcpy(buf, driver->log_mask, header_size);
+ memcpy(buf+header_size, log_mask_ptr+2, 512);
+ /* if dirty byte is set and channel is valid */
+ if (ch && *(log_mask_ptr+1)) {
+ while (retry_count < 3) {
+ wr_size = smd_write(ch, buf, header_size + 512);
+ if (wr_size == -ENOMEM) {
+ retry_count++;
+ for (timer = 0; timer < 5; timer++)
+ udelay(2000);
+ } else
+ break;
+ }
+ if (wr_size != header_size + 512)
+ pr_err("diag: dci log mask update failed %d, tried %d",
+ wr_size, header_size + 512);
+ else {
+ *(log_mask_ptr+1) = 0; /* clear dirty byte */
+ pr_debug("diag: updated dci log equip ID %d\n",
+ *log_mask_ptr);
+ }
+ }
+ log_mask_ptr += 514;
+ }
+ mutex_unlock(&driver->diag_cntl_mutex);
+}
+
+void create_dci_log_mask_tbl(unsigned char *tbl_buf)
+{
+ uint8_t i; int count = 0;
+
+ /* create hard coded table for log mask with 16 categories */
+ for (i = 0; i < 16; i++) {
+ *(uint8_t *)tbl_buf = i;
+ pr_debug("diag: put value %x at %p\n", i, tbl_buf);
+ memset(tbl_buf+1, 0, 513); /* set dirty bit as 0 */
+ tbl_buf += 514;
+ count += 514;
+ }
+}
+
+void create_dci_event_mask_tbl(unsigned char *tbl_buf)
+{
+ memset(tbl_buf, 0, 512);
+}
+
static int diag_dci_runtime_suspend(struct device *dev)
{
dev_dbg(dev, "pm_runtime: suspending...\n");
@@ -290,10 +728,10 @@
struct platform_driver msm_diag_dci_driver = {
.probe = diag_dci_probe,
.driver = {
- .name = "DIAG_2",
- .owner = THIS_MODULE,
- .pm = &diag_dci_dev_pm_ops,
- },
+ .name = "DIAG_2",
+ .owner = THIS_MODULE,
+ .pm = &diag_dci_dev_pm_ops,
+ },
};
int diag_dci_init(void)
@@ -316,16 +754,10 @@
if (driver->write_ptr_dci == NULL)
goto err;
}
- if (driver->dci_tbl == NULL) {
- driver->dci_tbl = kzalloc(dci_max_reg *
- sizeof(struct diag_dci_tbl), GFP_KERNEL);
- if (driver->dci_tbl == NULL)
- goto err;
- }
- if (driver->dci_notify_tbl == NULL) {
- driver->dci_notify_tbl = kzalloc(MAX_DCI_CLIENT *
- sizeof(struct dci_notification_tbl), GFP_KERNEL);
- if (driver->dci_notify_tbl == NULL)
+ if (driver->req_tracking_tbl == NULL) {
+ driver->req_tracking_tbl = kzalloc(dci_max_reg *
+ sizeof(struct dci_pkt_req_tracking_tbl), GFP_KERNEL);
+ if (driver->req_tracking_tbl == NULL)
goto err;
}
if (driver->apps_dci_buf == NULL) {
@@ -333,6 +765,13 @@
if (driver->apps_dci_buf == NULL)
goto err;
}
+ if (driver->dci_client_tbl == NULL) {
+ driver->dci_client_tbl = kzalloc(MAX_DCI_CLIENTS *
+ sizeof(struct diag_dci_client_tbl), GFP_KERNEL);
+ if (driver->dci_client_tbl == NULL)
+ goto err;
+ }
+ driver->diag_dci_wq = create_singlethread_workqueue("diag_dci_wq");
success = platform_driver_register(&msm_diag_dci_driver);
if (success) {
pr_err("diag: Could not register DCI driver\n");
@@ -341,11 +780,13 @@
return DIAG_DCI_NO_ERROR;
err:
pr_err("diag: Could not initialize diag DCI buffers");
- kfree(driver->dci_tbl);
- kfree(driver->dci_notify_tbl);
+ kfree(driver->req_tracking_tbl);
+ kfree(driver->dci_client_tbl);
kfree(driver->apps_dci_buf);
kfree(driver->buf_in_dci);
kfree(driver->write_ptr_dci);
+ if (driver->diag_dci_wq)
+ destroy_workqueue(driver->diag_dci_wq);
return DIAG_DCI_NO_REG;
}
@@ -354,10 +795,10 @@
smd_close(driver->ch_dci);
driver->ch_dci = 0;
platform_driver_unregister(&msm_diag_dci_driver);
- kfree(driver->dci_tbl);
- kfree(driver->dci_notify_tbl);
+ kfree(driver->req_tracking_tbl);
+ kfree(driver->dci_client_tbl);
kfree(driver->apps_dci_buf);
kfree(driver->buf_in_dci);
kfree(driver->write_ptr_dci);
+ destroy_workqueue(driver->diag_dci_wq);
}
-
diff --git a/drivers/char/diag/diag_dci.h b/drivers/char/diag/diag_dci.h
index b70efe3..97a285c 100644
--- a/drivers/char/diag/diag_dci.h
+++ b/drivers/char/diag/diag_dci.h
@@ -11,21 +11,48 @@
*/
#ifndef DIAG_DCI_H
#define DIAG_DCI_H
-#define MAX_DCI_CLIENT 10
-#define DCI_CMD_CODE 0x93
+
+#define MAX_DCI_CLIENTS 10
+#define DCI_PKT_RSP_CODE 0x93
+#define DCI_DELAYED_RSP_CODE 0x94
+#define LOG_CMD_CODE 0x10
+#define EVENT_CMD_CODE 0x60
+#define DCI_PKT_RSP_TYPE 0
+#define DCI_LOG_TYPE -1
+#define DCI_EVENT_TYPE -2
+#define SET_LOG_MASK 1
+#define DISABLE_LOG_MASK 0
+#define MAX_EVENT_SIZE 100
+
+/* 16 log code categories, each has:
+ * 1 bytes equip id + 1 dirty byte + 512 byte max log mask
+ */
+#define DCI_LOG_MASK_SIZE (16*514)
+#define DCI_EVENT_MASK_SIZE 512
+#define DCI_MASK_STREAM 2
+#define DCI_MAX_LOG_CODES 16
+#define DCI_MAX_ITEMS_PER_LOG_CODE 512
extern unsigned int dci_max_reg;
extern unsigned int dci_max_clients;
-struct diag_dci_tbl {
+
+struct dci_pkt_req_tracking_tbl {
int pid;
int uid;
int tag;
};
-struct dci_notification_tbl {
+struct diag_dci_client_tbl {
struct task_struct *client;
uint16_t list; /* bit mask */
int signal_type;
+ unsigned char dci_log_mask[DCI_LOG_MASK_SIZE];
+ unsigned char dci_event_mask[DCI_EVENT_MASK_SIZE];
+ unsigned char *dci_data;
+ int data_len;
+ int total_capacity;
+ int dropped_logs;
+ int dropped_events;
};
enum {
@@ -41,7 +68,18 @@
int diag_dci_init(void);
void diag_dci_exit(void);
void diag_read_smd_dci_work_fn(struct work_struct *);
-int diag_process_dci_client(unsigned char *buf, int len);
+int diag_process_dci_transaction(unsigned char *buf, int len);
int diag_send_dci_pkt(struct diag_master_table entry, unsigned char *buf,
int len, int index);
+void extract_dci_pkt_rsp(unsigned char *buf);
+/* DCI Log streaming functions */
+void create_dci_log_mask_tbl(unsigned char *tbl_buf);
+void update_dci_cumulative_log_mask(int client_index);
+void diag_send_dci_log_mask(smd_channel_t *ch);
+void extract_dci_log(unsigned char *buf);
+/* DCI event streaming functions */
+void update_dci_cumulative_event_mask(int client_index);
+void diag_send_dci_event_mask(smd_channel_t *ch);
+void extract_dci_events(unsigned char *buf);
+void create_dci_event_mask_tbl(unsigned char *tbl_buf);
#endif
diff --git a/drivers/char/diag/diagchar.h b/drivers/char/diag/diagchar.h
index a8e33b5..a37260b 100644
--- a/drivers/char/diag/diagchar.h
+++ b/drivers/char/diag/diagchar.h
@@ -140,6 +140,7 @@
int ref_count;
struct mutex diagchar_mutex;
wait_queue_head_t wait_q;
+ wait_queue_head_t smd_wait_q;
struct diag_client_map *client_map;
int *data_ready;
int num_clients;
@@ -147,14 +148,15 @@
struct diag_write_device *buf_tbl;
int use_device_tree;
/* DCI related variables */
- struct diag_dci_tbl *dci_tbl;
- struct dci_notification_tbl *dci_notify_tbl;
+ struct dci_pkt_req_tracking_tbl *req_tracking_tbl;
+ struct diag_dci_client_tbl *dci_client_tbl;
int dci_tag;
int dci_client_id;
struct mutex dci_mutex;
int num_dci_client;
unsigned char *apps_dci_buf;
int dci_state;
+ struct workqueue_struct *diag_dci_wq;
/* Memory pool parameters */
unsigned int itemsize;
unsigned int poolsize;
diff --git a/drivers/char/diag/diagchar_core.c b/drivers/char/diag/diagchar_core.c
index 5c6cdc6..c29a1d3f 100644
--- a/drivers/char/diag/diagchar_core.c
+++ b/drivers/char/diag/diagchar_core.c
@@ -377,7 +377,7 @@
int success = -1;
void *temp_buf;
uint16_t support_list = 0;
- struct dci_notification_tbl *notify_params;
+ struct diag_dci_client_tbl *notify_params;
int status;
if (iocmd == DIAG_IOCTL_COMMAND_REG) {
@@ -449,20 +449,31 @@
} else if (iocmd == DIAG_IOCTL_DCI_REG) {
if (driver->dci_state == DIAG_DCI_NO_REG)
return DIAG_DCI_NO_REG;
- if (driver->num_dci_client >= MAX_DCI_CLIENT)
+ if (driver->num_dci_client >= MAX_DCI_CLIENTS)
return DIAG_DCI_NO_REG;
- notify_params = (struct dci_notification_tbl *) ioarg;
+ notify_params = (struct diag_dci_client_tbl *) ioarg;
mutex_lock(&driver->dci_mutex);
driver->num_dci_client++;
pr_debug("diag: id = %d\n", driver->dci_client_id);
driver->dci_client_id++;
- for (i = 0; i < MAX_DCI_CLIENT; i++) {
- if (driver->dci_notify_tbl[i].client == NULL) {
- driver->dci_notify_tbl[i].client = current;
- driver->dci_notify_tbl[i].list =
+ for (i = 0; i < MAX_DCI_CLIENTS; i++) {
+ if (driver->dci_client_tbl[i].client == NULL) {
+ driver->dci_client_tbl[i].client = current;
+ driver->dci_client_tbl[i].list =
notify_params->list;
- driver->dci_notify_tbl[i].signal_type =
+ driver->dci_client_tbl[i].signal_type =
notify_params->signal_type;
+ create_dci_log_mask_tbl(driver->
+ dci_client_tbl[i].dci_log_mask);
+ create_dci_event_mask_tbl(driver->
+ dci_client_tbl[i].dci_event_mask);
+ driver->dci_client_tbl[i].data_len = 0;
+ driver->dci_client_tbl[i].dci_data =
+ kzalloc(IN_BUF_SIZE, GFP_KERNEL);
+ driver->dci_client_tbl[i].total_capacity =
+ IN_BUF_SIZE;
+ driver->dci_client_tbl[i].dropped_logs = 0;
+ driver->dci_client_tbl[i].dropped_events = 0;
break;
}
}
@@ -473,15 +484,15 @@
/* Delete this process from DCI table */
mutex_lock(&driver->dci_mutex);
for (i = 0; i < dci_max_reg; i++) {
- if (driver->dci_tbl[i].pid == current->tgid) {
+ if (driver->req_tracking_tbl[i].pid == current->tgid) {
pr_debug("diag: delete %d\n", current->tgid);
- driver->dci_tbl[i].pid = 0;
+ driver->req_tracking_tbl[i].pid = 0;
success = i;
}
}
- for (i = 0; i < MAX_DCI_CLIENT; i++) {
- if (driver->dci_notify_tbl[i].client == current) {
- driver->dci_notify_tbl[i].client = NULL;
+ for (i = 0; i < MAX_DCI_CLIENTS; i++) {
+ if (driver->dci_client_tbl[i].client == current) {
+ driver->dci_client_tbl[i].client = NULL;
break;
}
}
@@ -491,10 +502,6 @@
driver->num_dci_client--;
driver->num_dci_client--;
mutex_unlock(&driver->dci_mutex);
- for (i = 0; i < dci_max_reg; i++)
- if (driver->dci_tbl[i].pid != 0)
- pr_debug("diag: PID = %d, UID = %d, tag = %d\n",
- driver->dci_tbl[i].pid, driver->dci_tbl[i].uid, driver->dci_tbl[i].tag);
pr_debug("diag: complete deleting registrations\n");
return success;
} else if (iocmd == DIAG_IOCTL_DCI_SUPPORT) {
@@ -653,6 +660,7 @@
static int diagchar_read(struct file *file, char __user *buf, size_t count,
loff_t *ppos)
{
+ struct diag_dci_client_tbl *entry;
int index = -1, i = 0, ret = 0;
int num_data = 0, data_type;
#if defined(CONFIG_DIAG_SDIO_PIPE) || defined(CONFIG_DIAG_BRIDGE_CODE)
@@ -956,23 +964,26 @@
}
if (driver->data_ready[index] & DCI_DATA_TYPE) {
- /*Copy the type of data being passed*/
+ /* Copy the type of data being passed */
data_type = driver->data_ready[index] & DCI_DATA_TYPE;
COPY_USER_SPACE_OR_EXIT(buf, data_type, 4);
- COPY_USER_SPACE_OR_EXIT(buf+4,
- driver->write_ptr_dci->length, 4);
- /* check delayed vs immediate response */
- if (*(uint8_t *)(driver->buf_in_dci+4) == DCI_CMD_CODE)
- COPY_USER_SPACE_OR_EXIT(buf+8,
- *(driver->buf_in_dci + 5), driver->write_ptr_dci->length);
- else
- COPY_USER_SPACE_OR_EXIT(buf+8,
- *(driver->buf_in_dci + 8), driver->write_ptr_dci->length);
- driver->in_busy_dci = 0;
+ /* check the current client and copy its data */
+ for (i = 0; i < MAX_DCI_CLIENTS; i++) {
+ entry = &(driver->dci_client_tbl[i]);
+ if (entry && (current->tgid == entry->client->tgid)) {
+ COPY_USER_SPACE_OR_EXIT(buf+4,
+ entry->data_len, 4);
+ COPY_USER_SPACE_OR_EXIT(buf+8,
+ *(entry->dci_data), entry->data_len);
+ entry->data_len = 0;
+ break;
+ }
+ }
driver->data_ready[index] ^= DCI_DATA_TYPE;
+ driver->in_busy_dci = 0;
if (driver->ch_dci)
- queue_work(driver->diag_wq,
- &(driver->diag_read_smd_dci_work));
+ queue_work(driver->diag_dci_wq,
+ &(driver->diag_read_smd_dci_work));
goto exit;
}
exit:
@@ -981,7 +992,7 @@
}
static int diagchar_write(struct file *file, const char __user *buf,
- size_t count, loff_t *ppos)
+ size_t count, loff_t *ppos)
{
int err, ret = 0, pkt_type;
bool mdm_mask = false;
@@ -1011,7 +1022,7 @@
pr_alert("diag: copy failed for DCI data\n");
return DIAG_DCI_SEND_DATA_FAIL;
}
- err = diag_process_dci_client(driver->user_space_data,
+ err = diag_process_dci_transaction(driver->user_space_data,
payload_size);
return err;
}
@@ -1401,6 +1412,7 @@
driver->mask_check = 0;
mutex_init(&driver->diagchar_mutex);
init_waitqueue_head(&driver->wait_q);
+ init_waitqueue_head(&driver->smd_wait_q);
INIT_WORK(&(driver->diag_drain_work), diag_drain_work_fn);
INIT_WORK(&(driver->diag_read_smd_work), diag_read_smd_work_fn);
INIT_WORK(&(driver->diag_read_smd_cntl_work),
diff --git a/drivers/char/diag/diagfwd.c b/drivers/char/diag/diagfwd.c
index a537bb3..0bce4e9 100644
--- a/drivers/char/diag/diagfwd.c
+++ b/drivers/char/diag/diagfwd.c
@@ -160,9 +160,8 @@
{
if (driver->use_device_tree)
return 1;
- else if (cpu_is_msm8960() || cpu_is_msm8930() || cpu_is_msm8930aa() ||
- cpu_is_msm9615() || cpu_is_apq8064() || cpu_is_msm8627() ||
- cpu_is_msm8960ab() || cpu_is_apq8064ab())
+ else if (soc_class_is_msm8960() || soc_class_is_msm8930() ||
+ soc_class_is_apq8064() || cpu_is_msm9615())
return 1;
else
return 0;
@@ -221,8 +220,9 @@
void __diag_smd_send_req(void)
{
- void *buf = NULL;
- int *in_busy_ptr = NULL;
+ void *buf = NULL, *temp_buf = NULL;
+ int total_recd = 0, r = 0, pkt_len, *in_busy_ptr = NULL;
+ int loop_count = 0;
struct diag_request *write_ptr_modem = NULL;
if (!driver->in_busy_1) {
@@ -236,27 +236,63 @@
}
if (driver->ch && buf) {
- int r = smd_read_avail(driver->ch);
+ temp_buf = buf;
+ pkt_len = smd_cur_packet_size(driver->ch);
- if (r > IN_BUF_SIZE) {
- if (r < MAX_IN_BUF_SIZE) {
- pr_err("diag: SMD sending in "
- "packets upto %d bytes", r);
- buf = krealloc(buf, r, GFP_KERNEL);
- } else {
- pr_err("diag: SMD sending in "
- "packets more than %d bytes", MAX_IN_BUF_SIZE);
+ while (pkt_len && (pkt_len != total_recd)) {
+ loop_count++;
+ r = smd_read_avail(driver->ch);
+ pr_debug("diag: In %s, received pkt %d %d\n",
+ __func__, r, total_recd);
+ if (!r) {
+ /* Nothing to read from SMD */
+ wait_event(driver->smd_wait_q,
+ ((driver->ch == 0) ||
+ smd_read_avail(driver->ch)));
+ /* If the smd channel is open */
+ if (driver->ch) {
+ pr_debug("diag: In %s, return from wait_event\n",
+ __func__);
+ continue;
+ } else {
+ pr_debug("diag: In %s, return from wait_event ch closed\n",
+ __func__);
+ return;
+ }
+ }
+ total_recd += r;
+ if (total_recd > IN_BUF_SIZE) {
+ if (total_recd < MAX_IN_BUF_SIZE) {
+ pr_err("diag: In %s, SMD sending in packets up to %d bytes\n",
+ __func__, total_recd);
+ buf = krealloc(buf, total_recd,
+ GFP_KERNEL);
+ } else {
+ pr_err("diag: In %s, SMD sending in packets more than %d bytes\n",
+ __func__, MAX_IN_BUF_SIZE);
+ return;
+ }
+ }
+ if (pkt_len < r) {
+ pr_err("diag: In %s, SMD sending incorrect pkt\n",
+ __func__);
return;
}
+ if (pkt_len > r)
+ pr_debug("diag: In %s, SMD sending partial pkt %d %d %d %d\n",
+ __func__, pkt_len, r, total_recd,
+ loop_count);
+ /* keep reading for complete packet */
+ smd_read(driver->ch, temp_buf, r);
+ temp_buf += r;
}
- if (r > 0) {
+
+ if (total_recd > 0) {
if (!buf)
- pr_info("Out of diagmem for Modem\n");
+ pr_err("diag: Out of diagmem for Modem\n");
else {
- APPEND_DEBUG('i');
- smd_read(driver->ch, buf, r);
APPEND_DEBUG('j');
- write_ptr_modem->length = r;
+ write_ptr_modem->length = total_recd;
*in_busy_ptr = 1;
diag_device_write(buf, MODEM_DATA,
write_ptr_modem);
@@ -443,9 +479,10 @@
void __diag_smd_wcnss_send_req(void)
{
- void *buf = NULL;
- int *in_busy_wcnss_ptr = NULL;
+ void *buf = NULL, *temp_buf = NULL;
+ int total_recd = 0, r = 0, pkt_len, *in_busy_wcnss_ptr = NULL;
struct diag_request *write_ptr_wcnss = NULL;
+ int loop_count = 0;
if (!driver->in_busy_wcnss_1) {
buf = driver->buf_in_wcnss_1;
@@ -458,24 +495,64 @@
}
if (driver->ch_wcnss && buf) {
- int r = smd_read_avail(driver->ch_wcnss);
- if (r > IN_BUF_SIZE) {
- if (r < MAX_IN_BUF_SIZE) {
- pr_err("diag: wcnss packets > %d bytes", r);
- buf = krealloc(buf, r, GFP_KERNEL);
- } else {
- pr_err("diag: wcnss pkt > %d", MAX_IN_BUF_SIZE);
+ temp_buf = buf;
+ pkt_len = smd_cur_packet_size(driver->ch_wcnss);
+
+ while (pkt_len && (pkt_len != total_recd)) {
+ loop_count++;
+ r = smd_read_avail(driver->ch_wcnss);
+ pr_debug("diag: In %s, received pkt %d %d\n",
+ __func__, r, total_recd);
+ if (!r) {
+ /* Nothing to read from SMD */
+ wait_event(driver->smd_wait_q,
+ ((driver->ch_wcnss == 0) ||
+ smd_read_avail(driver->ch_wcnss)));
+ /* If the smd channel is open */
+ if (driver->ch_wcnss) {
+ pr_debug("diag: In %s, return from wait_event\n",
+ __func__);
+ continue;
+ } else {
+ pr_debug("diag: In %s, return from wait_event ch_wcnss closed\n",
+ __func__);
+ return;
+ }
+ }
+ total_recd += r;
+ if (total_recd > IN_BUF_SIZE) {
+ if (total_recd < MAX_IN_BUF_SIZE) {
+ pr_err("diag: In %s, SMD sending in packets up to %d bytes\n",
+ __func__, total_recd);
+ buf = krealloc(buf, total_recd,
+ GFP_KERNEL);
+ } else {
+ pr_err("diag: In %s, SMD sending in packets more than %d bytes\n",
+ __func__, MAX_IN_BUF_SIZE);
+ return;
+ }
+ }
+ if (pkt_len < r) {
+ pr_err("diag: In %s, SMD sending incorrect pkt\n",
+ __func__);
return;
}
+ if (pkt_len > r) {
+ pr_debug("diag: In %s, SMD sending partial pkt %d %d %d %d\n",
+ __func__, pkt_len, r, total_recd,
+ loop_count);
+ }
+ /* keep reading for complete packet */
+ smd_read(driver->ch_wcnss, temp_buf, r);
+ temp_buf += r;
}
- if (r > 0) {
+
+ if (total_recd > 0) {
if (!buf) {
- pr_err("Out of diagmem for wcnss\n");
+ pr_err("diag: Out of diagmem for wcnss\n");
} else {
- APPEND_DEBUG('i');
- smd_read(driver->ch_wcnss, buf, r);
APPEND_DEBUG('j');
- write_ptr_wcnss->length = r;
+ write_ptr_wcnss->length = total_recd;
*in_busy_wcnss_ptr = 1;
diag_device_write(buf, WCNSS_DATA,
write_ptr_wcnss);
@@ -489,9 +566,10 @@
void __diag_smd_lpass_send_req(void)
{
- void *buf = NULL;
- int *in_busy_lpass_ptr = NULL;
+ void *buf = NULL, *temp_buf = NULL;
+ int total_recd = 0, r = 0, pkt_len, *in_busy_lpass_ptr = NULL;
struct diag_request *write_ptr_lpass = NULL;
+ int loop_count = 0;
if (!driver->in_busy_lpass_1) {
buf = driver->buf_in_lpass_1;
@@ -504,27 +582,63 @@
}
if (driver->chlpass && buf) {
- int r = smd_read_avail(driver->chlpass);
+ temp_buf = buf;
+ pkt_len = smd_cur_packet_size(driver->chlpass);
- if (r > IN_BUF_SIZE) {
- if (r < MAX_IN_BUF_SIZE) {
- pr_err("diag: SMD sending in "
- "packets upto %d bytes", r);
- buf = krealloc(buf, r, GFP_KERNEL);
- } else {
- pr_err("diag: SMD sending in "
- "packets more than %d bytes", MAX_IN_BUF_SIZE);
+ while (pkt_len && (pkt_len != total_recd)) {
+ loop_count++;
+ r = smd_read_avail(driver->chlpass);
+ pr_debug("diag: In %s, received pkt %d %d\n",
+ __func__, r, total_recd);
+ if (!r) {
+ /* Nothing to read from SMD */
+ wait_event(driver->smd_wait_q,
+ ((driver->chlpass == 0) ||
+ smd_read_avail(driver->chlpass)));
+ /* If the smd channel is open */
+ if (driver->chlpass) {
+ pr_debug("diag: In %s, return from wait_event\n",
+ __func__);
+ continue;
+ } else {
+ pr_debug("diag: In %s, return from wait_event chlpass closed\n",
+ __func__);
+ return;
+ }
+ }
+ total_recd += r;
+ if (total_recd > IN_BUF_SIZE) {
+ if (total_recd < MAX_IN_BUF_SIZE) {
+ pr_err("diag: In %s, SMD sending in packets up to %d bytes\n",
+ __func__, total_recd);
+ buf = krealloc(buf, total_recd,
+ GFP_KERNEL);
+ } else {
+ pr_err("diag: In %s, SMD sending in packets more than %d bytes\n",
+ __func__, MAX_IN_BUF_SIZE);
+ return;
+ }
+ }
+ if (pkt_len < r) {
+ pr_err("diag: In %s, SMD sending incorrect pkt\n",
+ __func__);
return;
}
+ if (pkt_len > r)
+ pr_debug("diag: In %s, SMD sending partial pkt %d %d %d %d\n",
+ __func__, pkt_len, r, total_recd,
+ loop_count);
+ /* keep reading for complete packet */
+ smd_read(driver->chlpass, temp_buf, r);
+ temp_buf += r;
}
- if (r > 0) {
+
+ if (total_recd > 0) {
if (!buf)
- printk(KERN_INFO "Out of diagmem for LPASS\n");
+ pr_err("diag: Out of diagmem for LPASS\n");
else {
- APPEND_DEBUG('i');
- smd_read(driver->chlpass, buf, r);
APPEND_DEBUG('j');
- write_ptr_lpass->length = r;
+ write_ptr_lpass->length = total_recd;
*in_busy_lpass_ptr = 1;
diag_device_write(buf, LPASS_DATA,
write_ptr_lpass);
@@ -1223,14 +1337,16 @@
static void diag_smd_notify(void *ctxt, unsigned event)
{
if (event == SMD_EVENT_CLOSE) {
+ driver->ch = 0;
+ wake_up(&driver->smd_wait_q);
queue_work(driver->diag_cntl_wq,
&(driver->diag_clean_modem_reg_work));
- driver->ch = 0;
return;
} else if (event == SMD_EVENT_OPEN) {
if (ch_temp)
driver->ch = ch_temp;
}
+ wake_up(&driver->smd_wait_q);
queue_work(driver->diag_wq, &(driver->diag_read_smd_work));
}
@@ -1238,14 +1354,16 @@
static void diag_smd_lpass_notify(void *ctxt, unsigned event)
{
if (event == SMD_EVENT_CLOSE) {
+ driver->chlpass = 0;
+ wake_up(&driver->smd_wait_q);
queue_work(driver->diag_cntl_wq,
&(driver->diag_clean_lpass_reg_work));
- driver->chlpass = 0;
return;
} else if (event == SMD_EVENT_OPEN) {
if (chlpass_temp)
driver->chlpass = chlpass_temp;
}
+ wake_up(&driver->smd_wait_q);
queue_work(driver->diag_wq, &(driver->diag_read_smd_lpass_work));
}
#endif
@@ -1253,14 +1371,16 @@
static void diag_smd_wcnss_notify(void *ctxt, unsigned event)
{
if (event == SMD_EVENT_CLOSE) {
+ driver->ch_wcnss = 0;
+ wake_up(&driver->smd_wait_q);
queue_work(driver->diag_cntl_wq,
&(driver->diag_clean_wcnss_reg_work));
- driver->ch_wcnss = 0;
return;
} else if (event == SMD_EVENT_OPEN) {
if (ch_wcnss_temp)
driver->ch_wcnss = ch_wcnss_temp;
}
+ wake_up(&driver->smd_wait_q);
queue_work(driver->diag_wq, &(driver->diag_read_smd_wcnss_work));
}
diff --git a/drivers/crypto/msm/qce50.c b/drivers/crypto/msm/qce50.c
index de5f10f..8a9567b 100644
--- a/drivers/crypto/msm/qce50.c
+++ b/drivers/crypto/msm/qce50.c
@@ -77,7 +77,8 @@
int dst_nents;
dma_addr_t phy_iv_in;
-
+ unsigned char dec_iv[16];
+ int dir;
void *areq;
enum qce_cipher_mode_enum mode;
struct ce_sps_data ce_sps;
@@ -713,16 +714,19 @@
{
struct ahash_request *areq;
unsigned char digest[SHA256_DIGEST_SIZE];
+ uint32_t bytecount32[2];
areq = (struct ahash_request *) pce_dev->areq;
dma_unmap_sg(pce_dev->pdev, areq->src, pce_dev->src_nents,
DMA_TO_DEVICE);
memcpy(digest, (char *)(&pce_dev->ce_sps.result->auth_iv[0]),
SHA256_DIGEST_SIZE);
+ _byte_stream_to_net_words(bytecount32,
+ (unsigned char *)pce_dev->ce_sps.result->auth_byte_count,
+ 2 * CRYPTO_REG_SIZE);
if (_qce_unlock_other_pipes(pce_dev))
return -EINVAL;
- pce_dev->qce_cb(areq, digest,
- (char *)pce_dev->ce_sps.result->auth_byte_count,
+ pce_dev->qce_cb(areq, digest, (char *)bytecount32,
pce_dev->ce_sps.consumer_status);
return 0;
};
@@ -750,10 +754,16 @@
pce_dev->ce_sps.producer_status);
} else {
if (pce_dev->ce_sps.minor_version == 0) {
- if (pce_dev->mode == QCE_MODE_CBC)
- memcpy(iv, (char *)sg_virt(areq->src),
- sizeof(iv));
-
+ if (pce_dev->mode == QCE_MODE_CBC) {
+ if (pce_dev->dir == QCE_DECRYPT)
+ memcpy(iv, (char *)pce_dev->dec_iv,
+ sizeof(iv));
+ else
+ memcpy(iv, (unsigned char *)
+ (sg_virt(areq->src) +
+ areq->src->length - 16),
+ sizeof(iv));
+ }
if ((pce_dev->mode == QCE_MODE_CTR) ||
(pce_dev->mode == QCE_MODE_XTS)) {
uint32_t num_blk = 0;
@@ -2326,6 +2336,16 @@
} else {
pce_dev->dst_nents = pce_dev->src_nents;
}
+ pce_dev->dir = c_req->dir;
+ if ((pce_dev->ce_sps.minor_version == 0) && (c_req->dir == QCE_DECRYPT)
+ && (c_req->mode == QCE_MODE_CBC)) {
+ struct ablkcipher_request *areq =
+ (struct ablkcipher_request *)pce_dev->areq;
+ memcpy(pce_dev->dec_iv, (unsigned char *)sg_virt(areq->src) +
+ areq->src->length - 16,
+ NUM_OF_CRYPTO_CNTR_IV_REG * CRYPTO_REG_SIZE);
+ }
+
/* set up crypto device */
rc = _ce_setup_cipher(pce_dev, c_req, areq->nbytes, 0, cmdlistinfo);
if (rc < 0)
diff --git a/drivers/crypto/msm/qce50.h b/drivers/crypto/msm/qce50.h
index c9eba82..8533636 100644
--- a/drivers/crypto/msm/qce50.h
+++ b/drivers/crypto/msm/qce50.h
@@ -36,7 +36,7 @@
/* QCE max number of descriptor in a descriptor list */
#define QCE_MAX_NUM_DESC 128
-#define SPS_MAX_PKT_SIZE (64 * 1024 - 1)
+#define SPS_MAX_PKT_SIZE (32 * 1024 - 64)
/* State of consumer/producer Pipe */
enum qce_pipe_st_enum {
diff --git a/drivers/gpu/ion/ion.c b/drivers/gpu/ion/ion.c
index a48e6b2..b3df752 100644
--- a/drivers/gpu/ion/ion.c
+++ b/drivers/gpu/ion/ion.c
@@ -261,6 +261,12 @@
mutex_unlock(&buffer->lock);
}
+static void ion_delayed_unsecure(struct ion_buffer *buffer)
+{
+ if (buffer->heap->ops->unsecure_buffer)
+ buffer->heap->ops->unsecure_buffer(buffer, 1);
+}
+
static void ion_buffer_destroy(struct kref *kref)
{
struct ion_buffer *buffer = container_of(kref, struct ion_buffer, ref);
@@ -271,6 +277,7 @@
buffer->heap->ops->unmap_dma(buffer->heap, buffer);
+ ion_delayed_unsecure(buffer);
ion_iommu_delayed_unmap(buffer);
buffer->heap->ops->free(buffer);
mutex_lock(&dev->lock);
@@ -1644,6 +1651,73 @@
mutex_unlock(&dev->lock);
}
+int ion_secure_handle(struct ion_client *client, struct ion_handle *handle,
+ int version, void *data, int flags)
+{
+ int ret = -EINVAL;
+ struct ion_heap *heap;
+ struct ion_buffer *buffer;
+
+ mutex_lock(&client->lock);
+ if (!ion_handle_validate(client, handle)) {
+ WARN(1, "%s: invalid handle passed to secure.\n", __func__);
+ goto out_unlock;
+ }
+
+ buffer = handle->buffer;
+ heap = buffer->heap;
+
+ if (heap->type != (enum ion_heap_type) ION_HEAP_TYPE_CP) {
+ pr_err("%s: cannot secure buffer from non secure heap\n",
+ __func__);
+ goto out_unlock;
+ }
+
+ BUG_ON(!buffer->heap->ops->secure_buffer);
+ /*
+ * Protect the handle via the client lock to ensure we aren't
+ * racing with free
+ */
+ ret = buffer->heap->ops->secure_buffer(buffer, version, data, flags);
+
+out_unlock:
+ mutex_unlock(&client->lock);
+ return ret;
+}
+
+int ion_unsecure_handle(struct ion_client *client, struct ion_handle *handle)
+{
+ int ret = -EINVAL;
+ struct ion_heap *heap;
+ struct ion_buffer *buffer;
+
+ mutex_lock(&client->lock);
+ if (!ion_handle_validate(client, handle)) {
+ WARN(1, "%s: invalid handle passed to secure.\n", __func__);
+ goto out_unlock;
+ }
+
+ buffer = handle->buffer;
+ heap = buffer->heap;
+
+ if (heap->type != (enum ion_heap_type) ION_HEAP_TYPE_CP) {
+ pr_err("%s: cannot secure buffer from non secure heap\n",
+ __func__);
+ goto out_unlock;
+ }
+
+ BUG_ON(!buffer->heap->ops->unsecure_buffer);
+ /*
+ * Protect the handle via the client lock to ensure we aren't
+ * racing with free
+ */
+ ret = buffer->heap->ops->unsecure_buffer(buffer, 0);
+
+out_unlock:
+ mutex_unlock(&client->lock);
+ return ret;
+}
+
int ion_secure_heap(struct ion_device *dev, int heap_id, int version,
void *data)
{
diff --git a/drivers/gpu/ion/ion_cp_heap.c b/drivers/gpu/ion/ion_cp_heap.c
index 2070abf..f9a9212 100644
--- a/drivers/gpu/ion/ion_cp_heap.c
+++ b/drivers/gpu/ion/ion_cp_heap.c
@@ -85,8 +85,8 @@
unsigned int heap_protected;
unsigned long allocated_bytes;
unsigned long total_size;
- int (*request_region)(void *);
- int (*release_region)(void *);
+ int (*heap_request_region)(void *);
+ int (*heap_release_region)(void *);
void *bus_id;
unsigned long kmap_cached_count;
unsigned long kmap_uncached_count;
@@ -106,6 +106,26 @@
HEAP_PROTECTED = 1,
};
+struct ion_cp_buffer {
+ phys_addr_t buffer;
+ atomic_t secure_cnt;
+ int is_secure;
+ int want_delayed_unsecure;
+ /*
+ * Currently all user/kernel mapping is protected by the heap lock.
+ * This is sufficient to protect the map count as well. The lock
+ * should be used to protect map_cnt if the whole heap lock is
+ * ever removed.
+ */
+ atomic_t map_cnt;
+ /*
+ * protects secure_cnt for securing.
+ */
+ struct mutex lock;
+ int version;
+ void *data;
+};
+
static int ion_cp_protect_mem(unsigned int phy_base, unsigned int size,
unsigned int permission_type, int version,
void *data);
@@ -124,6 +144,170 @@
return cp_heap->kmap_cached_count + cp_heap->kmap_uncached_count;
}
+static int ion_on_first_alloc(struct ion_heap *heap)
+{
+ struct ion_cp_heap *cp_heap =
+ container_of(heap, struct ion_cp_heap, heap);
+ int ret_value;
+
+ if (cp_heap->reusable) {
+ ret_value = fmem_set_state(FMEM_C_STATE);
+ if (ret_value)
+ return 1;
+ }
+ return 0;
+}
+
+static void ion_on_last_free(struct ion_heap *heap)
+{
+ struct ion_cp_heap *cp_heap =
+ container_of(heap, struct ion_cp_heap, heap);
+
+ if (cp_heap->reusable)
+ if (fmem_set_state(FMEM_T_STATE) != 0)
+ pr_err("%s: unable to transition heap to T-state\n",
+ __func__);
+}
+
+/* Must be protected by ion_cp_buffer lock */
+static int __ion_cp_protect_buffer(struct ion_buffer *buffer, int version,
+ void *data, int flags)
+{
+ struct ion_cp_buffer *buf = buffer->priv_virt;
+ int ret_value = 0;
+
+ if (atomic_inc_return(&buf->secure_cnt) == 1) {
+ ret_value = ion_cp_protect_mem(buf->buffer,
+ buffer->size, 0,
+ version, data);
+
+ if (ret_value) {
+ pr_err("Failed to secure buffer %p, error %d\n",
+ buffer, ret_value);
+ atomic_dec(&buf->secure_cnt);
+ } else {
+ pr_debug("Protected buffer %p from %x-%x\n",
+ buffer, buf->buffer,
+ buf->buffer + buffer->size);
+ buf->want_delayed_unsecure |=
+ flags & ION_UNSECURE_DELAYED ? 1 : 0;
+ buf->data = data;
+ buf->version = version;
+ }
+ }
+ pr_debug("buffer %p protect count %d\n", buffer,
+ atomic_read(&buf->secure_cnt));
+ BUG_ON(atomic_read(&buf->secure_cnt) < 0);
+ return ret_value;
+}
+
+/* Must be protected by ion_cp_buffer lock */
+static int __ion_cp_unprotect_buffer(struct ion_buffer *buffer, int version,
+ void *data, int force_unsecure)
+{
+ struct ion_cp_buffer *buf = buffer->priv_virt;
+ int ret_value = 0;
+
+ if (force_unsecure) {
+ if (!buf->is_secure || atomic_read(&buf->secure_cnt) == 0)
+ return 0;
+
+ if (atomic_read(&buf->secure_cnt) != 1) {
+ WARN(1, "Forcing unsecure of buffer with outstanding secure count %d!\n",
+ atomic_read(&buf->secure_cnt));
+ atomic_set(&buf->secure_cnt, 1);
+ }
+ }
+
+ if (atomic_dec_and_test(&buf->secure_cnt)) {
+ ret_value = ion_cp_unprotect_mem(
+ buf->buffer, buffer->size,
+ 0, version, data);
+
+ if (ret_value) {
+ pr_err("Failed to unsecure buffer %p, error %d\n",
+ buffer, ret_value);
+ /*
+ * If the force unsecure is happening, the buffer
+ * is being destroyed. We failed to unsecure the
+ * buffer even though the memory is given back.
+ * Just die now rather than discovering later what
+ * happens when trying to use the secured memory as
+ * unsecured...
+ */
+ BUG_ON(force_unsecure);
+ /* Bump the count back up one to try again later */
+ atomic_inc(&buf->secure_cnt);
+ } else {
+ buf->version = -1;
+ buf->data = NULL;
+ }
+ }
+ pr_debug("buffer %p unprotect count %d\n", buffer,
+ atomic_read(&buf->secure_cnt));
+ BUG_ON(atomic_read(&buf->secure_cnt) < 0);
+ return ret_value;
+}
+
+int ion_cp_secure_buffer(struct ion_buffer *buffer, int version, void *data,
+ int flags)
+{
+ int ret_value;
+ struct ion_cp_buffer *buf = buffer->priv_virt;
+
+ mutex_lock(&buf->lock);
+ if (!buf->is_secure) {
+ pr_err("%s: buffer %p was not allocated as secure\n",
+ __func__, buffer);
+ ret_value = -EINVAL;
+ goto out_unlock;
+ }
+
+ if (ION_IS_CACHED(buffer->flags)) {
+ pr_err("%s: buffer %p was allocated as cached\n",
+ __func__, buffer);
+ ret_value = -EINVAL;
+ goto out_unlock;
+ }
+
+ if (atomic_read(&buf->map_cnt)) {
+ pr_err("%s: cannot secure buffer %p with outstanding mappings. Total count: %d",
+ __func__, buffer, atomic_read(&buf->map_cnt));
+ ret_value = -EINVAL;
+ goto out_unlock;
+ }
+
+ if (atomic_read(&buf->secure_cnt)) {
+ if (buf->version != version || buf->data != data) {
+ pr_err("%s: Trying to re-secure buffer with different values",
+ __func__);
+ pr_err("Last secured version: %d Currrent %d\n",
+ buf->version, version);
+ pr_err("Last secured data: %p current %p\n",
+ buf->data, data);
+ ret_value = -EINVAL;
+ goto out_unlock;
+ }
+ }
+ ret_value = __ion_cp_protect_buffer(buffer, version, data, flags);
+
+out_unlock:
+ mutex_unlock(&buf->lock);
+ return ret_value;
+}
+
+int ion_cp_unsecure_buffer(struct ion_buffer *buffer, int force_unsecure)
+{
+ int ret_value = 0;
+ struct ion_cp_buffer *buf = buffer->priv_virt;
+
+ mutex_lock(&buf->lock);
+ ret_value = __ion_cp_unprotect_buffer(buffer, buf->version, buf->data,
+ force_unsecure);
+ mutex_unlock(&buf->lock);
+ return ret_value;
+}
+
/**
* Protects memory if heap is unsecured heap. Also ensures that we are in
* the correct FMEM state if this heap is a reusable heap.
@@ -137,11 +321,9 @@
if (atomic_inc_return(&cp_heap->protect_cnt) == 1) {
/* Make sure we are in C state when the heap is protected. */
- if (cp_heap->reusable && !cp_heap->allocated_bytes) {
- ret_value = fmem_set_state(FMEM_C_STATE);
- if (ret_value)
+ if (!cp_heap->allocated_bytes)
+ if (ion_on_first_alloc(heap))
goto out;
- }
ret_value = ion_cp_protect_mem(cp_heap->secure_base,
cp_heap->secure_size, cp_heap->permission_type,
@@ -150,11 +332,9 @@
pr_err("Failed to protect memory for heap %s - "
"error code: %d\n", heap->name, ret_value);
- if (cp_heap->reusable && !cp_heap->allocated_bytes) {
- if (fmem_set_state(FMEM_T_STATE) != 0)
- pr_err("%s: unable to transition heap to T-state\n",
- __func__);
- }
+ if (!cp_heap->allocated_bytes)
+ ion_on_last_free(heap);
+
atomic_dec(&cp_heap->protect_cnt);
} else {
cp_heap->heap_protected = HEAP_PROTECTED;
@@ -191,11 +371,8 @@
pr_debug("Un-protected heap %s @ 0x%x\n", heap->name,
(unsigned int) cp_heap->base);
- if (cp_heap->reusable && !cp_heap->allocated_bytes) {
- if (fmem_set_state(FMEM_T_STATE) != 0)
- pr_err("%s: unable to transition heap to T-state",
- __func__);
- }
+ if (!cp_heap->allocated_bytes)
+ ion_on_last_free(heap);
}
}
pr_debug("%s: protect count is %d\n", __func__,
@@ -236,12 +413,11 @@
* if this is the first reusable allocation, transition
* the heap
*/
- if (cp_heap->reusable && !cp_heap->allocated_bytes) {
- if (fmem_set_state(FMEM_C_STATE) != 0) {
+ if (!cp_heap->allocated_bytes)
+ if (ion_on_first_alloc(heap)) {
mutex_unlock(&cp_heap->lock);
return ION_RESERVED_ALLOCATE_FAIL;
}
- }
cp_heap->allocated_bytes += size;
mutex_unlock(&cp_heap->lock);
@@ -260,13 +436,9 @@
__func__, heap->name,
cp_heap->total_size -
cp_heap->allocated_bytes, size);
-
- if (cp_heap->reusable && !cp_heap->allocated_bytes &&
- cp_heap->heap_protected == HEAP_NOT_PROTECTED) {
- if (fmem_set_state(FMEM_T_STATE) != 0)
- pr_err("%s: unable to transition heap to T-state\n",
- __func__);
- }
+ if (!cp_heap->allocated_bytes &&
+ cp_heap->heap_protected == HEAP_NOT_PROTECTED)
+ ion_on_last_free(heap);
mutex_unlock(&cp_heap->lock);
return ION_CP_ALLOCATE_FAIL;
@@ -311,12 +483,9 @@
mutex_lock(&cp_heap->lock);
cp_heap->allocated_bytes -= size;
- if (cp_heap->reusable && !cp_heap->allocated_bytes &&
- cp_heap->heap_protected == HEAP_NOT_PROTECTED) {
- if (fmem_set_state(FMEM_T_STATE) != 0)
- pr_err("%s: unable to transition heap to T-state\n",
- __func__);
- }
+ if (!cp_heap->allocated_bytes &&
+ cp_heap->heap_protected == HEAP_NOT_PROTECTED)
+ ion_on_last_free(heap);
/* Unmap everything if we previously mapped the whole heap at once. */
if (!cp_heap->allocated_bytes) {
@@ -344,7 +513,9 @@
struct ion_buffer *buffer,
ion_phys_addr_t *addr, size_t *len)
{
- *addr = buffer->priv_phys;
+ struct ion_cp_buffer *buf = buffer->priv_virt;
+
+ *addr = buf->buffer;
*len = buffer->size;
return 0;
}
@@ -354,34 +525,83 @@
unsigned long size, unsigned long align,
unsigned long flags)
{
- buffer->priv_phys = ion_cp_allocate(heap, size, align, flags);
- return buffer->priv_phys == ION_CP_ALLOCATE_FAIL ? -ENOMEM : 0;
+ struct ion_cp_buffer *buf;
+ phys_addr_t addr;
+
+ buf = kzalloc(sizeof(*buf), GFP_KERNEL);
+ if (!buf)
+ return ION_CP_ALLOCATE_FAIL;
+
+ addr = ion_cp_allocate(heap, size, align, flags);
+ if (addr == ION_CP_ALLOCATE_FAIL)
+ return -ENOMEM;
+
+ buf->buffer = addr;
+ buf->want_delayed_unsecure = 0;
+ atomic_set(&buf->secure_cnt, 0);
+ mutex_init(&buf->lock);
+ buf->is_secure = flags & ION_SECURE ? 1 : 0;
+ buffer->priv_virt = buf;
+
+ return 0;
}
static void ion_cp_heap_free(struct ion_buffer *buffer)
{
struct ion_heap *heap = buffer->heap;
+ struct ion_cp_buffer *buf = buffer->priv_virt;
- ion_cp_free(heap, buffer->priv_phys, buffer->size);
- buffer->priv_phys = ION_CP_ALLOCATE_FAIL;
+ ion_cp_free(heap, buf->buffer, buffer->size);
+ WARN_ON(atomic_read(&buf->secure_cnt));
+ WARN_ON(atomic_read(&buf->map_cnt));
+ kfree(buf);
+
+ buffer->priv_virt = NULL;
}
struct sg_table *ion_cp_heap_create_sg_table(struct ion_buffer *buffer)
{
struct sg_table *table;
int ret;
+ struct ion_cp_buffer *buf = buffer->priv_virt;
table = kzalloc(sizeof(struct sg_table), GFP_KERNEL);
if (!table)
return ERR_PTR(-ENOMEM);
- ret = sg_alloc_table(table, 1, GFP_KERNEL);
- if (ret)
- goto err0;
+ if (buf->is_secure) {
+ int n_chunks;
+ int i;
+ struct scatterlist *sg;
- table->sgl->length = buffer->size;
- table->sgl->offset = 0;
- table->sgl->dma_address = buffer->priv_phys;
+ if (!IS_ALIGNED(buffer->size, SZ_1M)) {
+ pr_err("%s: buffer is marked as secure but buffer size %x is not aligned to 1MB\n",
+ __func__, buffer->size);
+
+ return ERR_PTR(-EINVAL);
+ }
+
+ /* Count number of 1MB chunks. Alignment is already checked. */
+ n_chunks = buffer->size >> 20;
+
+ ret = sg_alloc_table(table, n_chunks, GFP_KERNEL);
+ if (ret)
+ goto err0;
+
+ for_each_sg(table->sgl, sg, table->nents, i) {
+ sg_dma_address(sg) = buf->buffer + i * SZ_1M;
+ sg->length = SZ_1M;
+ sg->offset = 0;
+ }
+ } else {
+ ret = sg_alloc_table(table, 1, GFP_KERNEL);
+ if (ret)
+ goto err0;
+
+ table->sgl->length = buffer->size;
+ table->sgl->offset = 0;
+ table->sgl->dma_address = buf->buffer;
+ }
return table;
err0:
@@ -411,8 +631,9 @@
{
int ret_value = 0;
if ((cp_heap->umap_count + ion_cp_get_total_kmap_count(cp_heap)) == 0)
- if (cp_heap->request_region)
- ret_value = cp_heap->request_region(cp_heap->bus_id);
+ if (cp_heap->heap_request_region)
+ ret_value = cp_heap->heap_request_region(
+ cp_heap->bus_id);
return ret_value;
}
@@ -423,8 +644,9 @@
{
int ret_value = 0;
if ((cp_heap->umap_count + ion_cp_get_total_kmap_count(cp_heap)) == 0)
- if (cp_heap->release_region)
- ret_value = cp_heap->release_region(cp_heap->bus_id);
+ if (cp_heap->heap_release_region)
+ ret_value = cp_heap->heap_release_region(
+ cp_heap->bus_id);
return ret_value;
}
@@ -432,17 +654,18 @@
void *virt_base, unsigned long flags)
{
int ret;
- unsigned int offset = buffer->priv_phys - phys_base;
+ struct ion_cp_buffer *buf = buffer->priv_virt;
+ unsigned int offset = buf->buffer - phys_base;
unsigned long start = ((unsigned long)virt_base) + offset;
const struct mem_type *type = ION_IS_CACHED(flags) ?
get_mem_type(MT_DEVICE_CACHED) :
get_mem_type(MT_DEVICE);
- if (phys_base > buffer->priv_phys)
+ if (phys_base > buf->buffer)
return NULL;
- ret = ioremap_pages(start, buffer->priv_phys, buffer->size, type);
+ ret = ioremap_pages(start, buf->buffer, buffer->size, type);
if (!ret)
return (void *)start;
@@ -455,6 +678,7 @@
struct ion_cp_heap *cp_heap =
container_of(heap, struct ion_cp_heap, heap);
void *ret_value = NULL;
+ struct ion_cp_buffer *buf = buffer->priv_virt;
mutex_lock(&cp_heap->lock);
if ((cp_heap->heap_protected == HEAP_NOT_PROTECTED) ||
@@ -472,10 +696,10 @@
} else {
if (ION_IS_CACHED(buffer->flags))
- ret_value = ioremap_cached(buffer->priv_phys,
+ ret_value = ioremap_cached(buf->buffer,
buffer->size);
else
- ret_value = ioremap(buffer->priv_phys,
+ ret_value = ioremap(buf->buffer,
buffer->size);
}
@@ -486,6 +710,7 @@
++cp_heap->kmap_cached_count;
else
++cp_heap->kmap_uncached_count;
+ atomic_inc(&buf->map_cnt);
}
}
mutex_unlock(&cp_heap->lock);
@@ -497,6 +722,7 @@
{
struct ion_cp_heap *cp_heap =
container_of(heap, struct ion_cp_heap, heap);
+ struct ion_cp_buffer *buf = buffer->priv_virt;
if (cp_heap->reusable)
unmap_kernel_range((unsigned long)buffer->vaddr, buffer->size);
@@ -510,6 +736,8 @@
--cp_heap->kmap_cached_count;
else
--cp_heap->kmap_uncached_count;
+
+ atomic_dec(&buf->map_cnt);
ion_cp_release_region(cp_heap);
mutex_unlock(&cp_heap->lock);
@@ -522,6 +750,7 @@
int ret_value = -EAGAIN;
struct ion_cp_heap *cp_heap =
container_of(heap, struct ion_cp_heap, heap);
+ struct ion_cp_buffer *buf = buffer->priv_virt;
mutex_lock(&cp_heap->lock);
if (cp_heap->heap_protected == HEAP_NOT_PROTECTED) {
@@ -535,14 +764,17 @@
vma->vm_page_prot);
ret_value = remap_pfn_range(vma, vma->vm_start,
- __phys_to_pfn(buffer->priv_phys) + vma->vm_pgoff,
+ __phys_to_pfn(buf->buffer) + vma->vm_pgoff,
vma->vm_end - vma->vm_start,
vma->vm_page_prot);
- if (ret_value)
+ if (ret_value) {
ion_cp_release_region(cp_heap);
- else
+ } else {
+ atomic_inc(&buf->map_cnt);
++cp_heap->umap_count;
+ }
+
}
mutex_unlock(&cp_heap->lock);
return ret_value;
@@ -553,9 +785,11 @@
{
struct ion_cp_heap *cp_heap =
container_of(heap, struct ion_cp_heap, heap);
+ struct ion_cp_buffer *buf = buffer->priv_virt;
mutex_lock(&cp_heap->lock);
--cp_heap->umap_count;
+ atomic_dec(&buf->map_cnt);
ion_cp_release_region(cp_heap);
mutex_unlock(&cp_heap->lock);
}
@@ -567,6 +801,7 @@
void (*outer_cache_op)(phys_addr_t, phys_addr_t);
struct ion_cp_heap *cp_heap =
container_of(heap, struct ion_cp_heap, heap);
+ struct ion_cp_buffer *buf = buffer->priv_virt;
switch (cmd) {
case ION_IOC_CLEAN_CACHES:
@@ -586,7 +821,7 @@
}
if (cp_heap->has_outer_cache) {
- unsigned long pstart = buffer->priv_phys + offset;
+ unsigned long pstart = buf->buffer + offset;
outer_cache_op(pstart, pstart + length);
}
return 0;
@@ -774,25 +1009,26 @@
struct ion_cp_heap *cp_heap =
container_of(buffer->heap, struct ion_cp_heap, heap);
int prot = IOMMU_WRITE | IOMMU_READ;
+ struct ion_cp_buffer *buf = buffer->priv_virt;
prot |= ION_IS_CACHED(flags) ? IOMMU_CACHE : 0;
data->mapped_size = iova_length;
if (!msm_use_iommu()) {
- data->iova_addr = buffer->priv_phys;
+ data->iova_addr = buf->buffer;
return 0;
}
if (cp_heap->iommu_iova[domain_num]) {
/* Already mapped. */
- unsigned long offset = buffer->priv_phys - cp_heap->base;
+ unsigned long offset = buf->buffer - cp_heap->base;
data->iova_addr = cp_heap->iommu_iova[domain_num] + offset;
return 0;
} else if (cp_heap->iommu_map_all) {
ret = iommu_map_all(domain_num, cp_heap, partition_num, prot);
if (!ret) {
unsigned long offset =
- buffer->priv_phys - cp_heap->base;
+ buf->buffer - cp_heap->base;
data->iova_addr =
cp_heap->iommu_iova[domain_num] + offset;
cp_heap->iommu_partition[domain_num] = partition_num;
@@ -902,6 +1138,8 @@
.unsecure_heap = ion_cp_unsecure_heap,
.map_iommu = ion_cp_heap_map_iommu,
.unmap_iommu = ion_cp_heap_unmap_iommu,
+ .secure_buffer = ion_cp_secure_buffer,
+ .unsecure_buffer = ion_cp_unsecure_buffer,
};
struct ion_heap *ion_cp_heap_create(struct ion_platform_heap *heap_data)
@@ -949,9 +1187,11 @@
if (extra_data->setup_region)
cp_heap->bus_id = extra_data->setup_region();
if (extra_data->request_region)
- cp_heap->request_region = extra_data->request_region;
+ cp_heap->heap_request_region =
+ extra_data->request_region;
if (extra_data->release_region)
- cp_heap->release_region = extra_data->release_region;
+ cp_heap->heap_release_region =
+ extra_data->release_region;
cp_heap->iommu_map_all =
extra_data->iommu_map_all;
cp_heap->iommu_2x_map_domain =
diff --git a/drivers/gpu/ion/ion_priv.h b/drivers/gpu/ion/ion_priv.h
index 273e57e..991a310 100644
--- a/drivers/gpu/ion/ion_priv.h
+++ b/drivers/gpu/ion/ion_priv.h
@@ -147,6 +147,9 @@
const struct rb_root *mem_map);
int (*secure_heap)(struct ion_heap *heap, int version, void *data);
int (*unsecure_heap)(struct ion_heap *heap, int version, void *data);
+ int (*secure_buffer)(struct ion_buffer *buffer, int version,
+ void *data, int flags);
+ int (*unsecure_buffer)(struct ion_buffer *buffer, int force_unsecure);
};
/**
@@ -307,4 +310,10 @@
void ion_mem_map_show(struct ion_heap *heap);
+
+
+int ion_secure_handle(struct ion_client *client, struct ion_handle *handle,
+ int version, void *data, int flags);
+
+int ion_unsecure_handle(struct ion_client *client, struct ion_handle *handle);
#endif /* _ION_PRIV_H */
diff --git a/drivers/gpu/ion/msm/msm_ion.c b/drivers/gpu/ion/msm/msm_ion.c
index deff514..7fe47ee 100644
--- a/drivers/gpu/ion/msm/msm_ion.c
+++ b/drivers/gpu/ion/msm/msm_ion.c
@@ -130,6 +130,22 @@
}
EXPORT_SYMBOL(msm_ion_unsecure_heap_2_0);
+int msm_ion_secure_buffer(struct ion_client *client, struct ion_handle *handle,
+ enum cp_mem_usage usage,
+ int flags)
+{
+ return ion_secure_handle(client, handle, ION_CP_V2,
+ (void *)usage, flags);
+}
+EXPORT_SYMBOL(msm_ion_secure_buffer);
+
+int msm_ion_unsecure_buffer(struct ion_client *client,
+ struct ion_handle *handle)
+{
+ return ion_unsecure_handle(client, handle);
+}
+EXPORT_SYMBOL(msm_ion_unsecure_buffer);
+
int msm_ion_do_cache_op(struct ion_client *client, struct ion_handle *handle,
void *vaddr, unsigned long len, unsigned int cmd)
{
diff --git a/drivers/gpu/msm/a3xx_reg.h b/drivers/gpu/msm/a3xx_reg.h
index 2681836..90f14e6 100644
--- a/drivers/gpu/msm/a3xx_reg.h
+++ b/drivers/gpu/msm/a3xx_reg.h
@@ -533,9 +533,9 @@
/* RBBM_CLOCK_CTL default value */
#define A305_RBBM_CLOCK_CTL_DEFAULT 0xAAAAAAAA
#define A320_RBBM_CLOCK_CTL_DEFAULT 0xBFFFFFFF
-#define A330_RBBM_CLOCK_CTL_DEFAULT 0xAAAAAAAE
+#define A330_RBBM_CLOCK_CTL_DEFAULT 0xBFFCFFFF
-#define A330_RBBM_GPR0_CTL_DEFAULT 0x0AE2B8AE
+#define A330_RBBM_GPR0_CTL_DEFAULT 0x00000000
/* COUNTABLE FOR SP PERFCOUNTER */
#define SP_FS_FULL_ALU_INSTRUCTIONS 0x0E
diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c
index 41fd7aa..930d233 100644
--- a/drivers/gpu/msm/adreno.c
+++ b/drivers/gpu/msm/adreno.c
@@ -1467,6 +1467,7 @@
ret = -ENOMEM;
goto done;
}
+ rec_data->fault = device->mmu.fault;
done:
if (ret) {
@@ -1529,9 +1530,9 @@
goto done;
}
- /* Do not try the bad caommands if recovery has failed bad commands
- * once already */
- if (!try_bad_commands)
+ /* Do not try the bad commands if recovery has failed bad commands
+ * once already or if hang is due to a fault */
+ if (!try_bad_commands || rec_data->fault)
rec_data->bad_rb_size = 0;
if (rec_data->bad_rb_size) {
diff --git a/drivers/gpu/msm/adreno.h b/drivers/gpu/msm/adreno.h
index fd9a0c3..bec19e2 100644
--- a/drivers/gpu/msm/adreno.h
+++ b/drivers/gpu/msm/adreno.h
@@ -138,6 +138,7 @@
* bad_rb_size - Number of valid dwords in bad_rb_buffer
* @last_valid_ctx_id - The last context from which commands were placed in
* ringbuffer before the GPU hung
+ * @fault - Indicates whether the hang was caused due to a pagefault
*/
struct adreno_recovery_data {
unsigned int ib1;
@@ -148,6 +149,7 @@
unsigned int *bad_rb_buffer;
unsigned int bad_rb_size;
unsigned int last_valid_ctx_id;
+ int fault;
};
extern struct adreno_gpudev adreno_a2xx_gpudev;
diff --git a/drivers/gpu/msm/kgsl_iommu.c b/drivers/gpu/msm/kgsl_iommu.c
index 87e8746..62108f2 100644
--- a/drivers/gpu/msm/kgsl_iommu.c
+++ b/drivers/gpu/msm/kgsl_iommu.c
@@ -34,6 +34,7 @@
{ 0x14, 0x0003FFFF, 14 }, /* TTBR1 */
{ 0x20, 0, 0 }, /* FSR */
{ 0x800, 0, 0 }, /* TLBIALL */
+ { 0x820, 0, 0 }, /* RESUME */
};
static struct kgsl_iommu_register_list kgsl_iommuv2_reg[KGSL_IOMMU_REG_MAX] = {
@@ -41,7 +42,8 @@
{ 0x20, 0x00FFFFFF, 14 }, /* TTBR0 */
{ 0x28, 0x00FFFFFF, 14 }, /* TTBR1 */
{ 0x58, 0, 0 }, /* FSR */
- { 0x618, 0, 0 } /* TLBIALL */
+ { 0x618, 0, 0 }, /* TLBIALL */
+ { 0x008, 0, 0 } /* RESUME */
};
static int get_iommu_unit(struct device *dev, struct kgsl_mmu **mmu_out,
@@ -124,9 +126,19 @@
KGSL_MEM_CRIT(iommu_dev->kgsldev, "context = %d FSR = %X\n",
iommu_dev->ctx_id, fsr);
+ mmu->fault = 1;
+ iommu_dev->fault = 1;
+
trace_kgsl_mmu_pagefault(iommu_dev->kgsldev, addr,
kgsl_mmu_get_ptname_from_ptbase(mmu, ptbase), 0);
+ /*
+ * We do not want the h/w to resume fetching data from an iommu unit
+ * that has faulted, this is better for debugging as it will stall
+ * the GPU and trigger a snapshot. To stall the transaction return
+ * EBUSY error.
+ */
+ ret = -EBUSY;
done:
return ret;
}
@@ -859,12 +871,13 @@
*/
for (i = 0; i < iommu->unit_count; i++) {
struct kgsl_iommu_unit *iommu_unit = &iommu->iommu_units[i];
- for (j = 0; j < iommu_unit->dev_count; j++)
+ for (j = 0; j < iommu_unit->dev_count; j++) {
iommu_unit->dev[j].pt_lsb = KGSL_IOMMMU_PT_LSB(iommu,
KGSL_IOMMU_GET_CTX_REG(iommu,
iommu_unit,
iommu_unit->dev[j].ctx_id,
TTBR0));
+ }
}
kgsl_iommu_disable_clk_on_ts(mmu, 0, false);
@@ -923,19 +936,22 @@
unsigned int iommu_virt_addr;
struct kgsl_iommu_pt *iommu_pt = mmu_specific_pt;
int size = kgsl_sg_size(memdesc->sg, memdesc->sglen);
+ unsigned int iommu_flags = IOMMU_READ;
BUG_ON(NULL == iommu_pt);
+ if (protflags & GSL_PT_PAGE_WV)
+ iommu_flags |= IOMMU_WRITE;
iommu_virt_addr = memdesc->gpuaddr;
ret = iommu_map_range(iommu_pt->domain, iommu_virt_addr, memdesc->sg,
- size, (IOMMU_READ | IOMMU_WRITE));
+ size, iommu_flags);
if (ret) {
KGSL_CORE_ERR("iommu_map_range(%p, %x, %p, %d, %d) "
"failed with err: %d\n", iommu_pt->domain,
iommu_virt_addr, memdesc->sg, size,
- (IOMMU_READ | IOMMU_WRITE), ret);
+ iommu_flags, ret);
return ret;
}
@@ -945,6 +961,7 @@
static void kgsl_iommu_stop(struct kgsl_mmu *mmu)
{
struct kgsl_iommu *iommu = mmu->priv;
+ int i, j;
/*
* stop device mmu
*
@@ -957,8 +974,25 @@
mmu->hwpagetable = NULL;
mmu->flags &= ~KGSL_FLAGS_STARTED;
- }
+ if (mmu->fault) {
+ for (i = 0; i < iommu->unit_count; i++) {
+ struct kgsl_iommu_unit *iommu_unit =
+ &iommu->iommu_units[i];
+ for (j = 0; j < iommu_unit->dev_count; j++) {
+ if (iommu_unit->dev[j].fault) {
+ kgsl_iommu_enable_clk(mmu, j);
+ KGSL_IOMMU_SET_CTX_REG(iommu,
+ iommu_unit,
+ iommu_unit->dev[j].ctx_id,
+ RESUME, 1);
+ iommu_unit->dev[j].fault = 0;
+ }
+ }
+ }
+ mmu->fault = 0;
+ }
+ }
/* switch off MMU clocks and cancel any events it has queued */
iommu->clk_event_queued = false;
kgsl_cancel_events(mmu->device, mmu);
diff --git a/drivers/gpu/msm/kgsl_iommu.h b/drivers/gpu/msm/kgsl_iommu.h
index eafba7b..661b4f0 100644
--- a/drivers/gpu/msm/kgsl_iommu.h
+++ b/drivers/gpu/msm/kgsl_iommu.h
@@ -25,6 +25,7 @@
KGSL_IOMMU_CTX_TTBR1,
KGSL_IOMMU_CTX_FSR,
KGSL_IOMMU_CTX_TLBIALL,
+ KGSL_IOMMU_CTX_RESUME,
KGSL_IOMMU_REG_MAX
};
@@ -77,6 +78,8 @@
* @ctx_id: This iommu units context id. It can be either 0 or 1
* @clk_enabled: If set indicates that iommu clocks of this iommu context
* are on, else the clocks are off
+ * fault: Flag when set indicates that this iommu device has caused a page
+ * fault
*/
struct kgsl_iommu_device {
struct device *dev;
@@ -85,6 +88,7 @@
enum kgsl_iommu_context_id ctx_id;
bool clk_enabled;
struct kgsl_device *kgsldev;
+ int fault;
};
/*
diff --git a/drivers/gpu/msm/kgsl_mmu.h b/drivers/gpu/msm/kgsl_mmu.h
index 234629b..b8b9149 100644
--- a/drivers/gpu/msm/kgsl_mmu.h
+++ b/drivers/gpu/msm/kgsl_mmu.h
@@ -175,6 +175,7 @@
struct kgsl_pagetable *hwpagetable;
const struct kgsl_mmu_ops *mmu_ops;
void *priv;
+ int fault;
};
#include "kgsl_gpummu.h"
diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile
index 066bc3e..11d50c1 100644
--- a/drivers/iommu/Makefile
+++ b/drivers/iommu/Makefile
@@ -1,7 +1,7 @@
obj-$(CONFIG_IOMMU_API) += iommu.o
obj-$(CONFIG_MSM_IOMMU) += msm_iommu.o msm_iommu_dev.o
ifdef CONFIG_OF
-obj-$(CONFIG_MSM_IOMMU) += msm_iommu-v2.o msm_iommu_dev-v2.o msm_iommu_pagetable.o
+obj-$(CONFIG_MSM_IOMMU) += msm_iommu-v2.o msm_iommu_dev-v2.o msm_iommu_pagetable.o msm_iommu_sec.o
endif
obj-$(CONFIG_AMD_IOMMU) += amd_iommu.o amd_iommu_init.o
obj-$(CONFIG_AMD_IOMMU_V2) += amd_iommu_v2.o
diff --git a/drivers/iommu/msm_iommu.c b/drivers/iommu/msm_iommu.c
index 63a027b..bf173b3 100644
--- a/drivers/iommu/msm_iommu.c
+++ b/drivers/iommu/msm_iommu.c
@@ -48,6 +48,9 @@
#define MSM_IOMMU_ATTR_CACHED_WB_NWA 0x2
#define MSM_IOMMU_ATTR_CACHED_WT 0x3
+struct bus_type msm_iommu_sec_bus_type = {
+ .name = "msm_iommu_sec_bus",
+};
static inline void clean_pte(unsigned long *start, unsigned long *end,
int redirect)
diff --git a/drivers/iommu/msm_iommu_dev-v2.c b/drivers/iommu/msm_iommu_dev-v2.c
index 68612ba..ea6c87c 100644
--- a/drivers/iommu/msm_iommu_dev-v2.c
+++ b/drivers/iommu/msm_iommu_dev-v2.c
@@ -110,6 +110,10 @@
}
drvdata->name = dev_name(&pdev->dev);
+ drvdata->sec_id = -1;
+ of_property_read_u32(pdev->dev.of_node, "qcom,iommu-secure-id",
+ &drvdata->sec_id);
+ return 0;
fail:
return ret;
}
diff --git a/drivers/iommu/msm_iommu_sec.c b/drivers/iommu/msm_iommu_sec.c
new file mode 100644
index 0000000..a89c4a8
--- /dev/null
+++ b/drivers/iommu/msm_iommu_sec.c
@@ -0,0 +1,556 @@
+/* Copyright (c) 2012 Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/errno.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/iommu.h>
+#include <linux/clk.h>
+#include <linux/scatterlist.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+
+#include <asm/sizes.h>
+
+#include <mach/iommu_hw-v2.h>
+#include <mach/iommu.h>
+#include <mach/scm.h>
+
+/* bitmap of the page sizes currently supported */
+#define MSM_IOMMU_PGSIZES (SZ_4K | SZ_64K | SZ_1M | SZ_16M)
+
+#define IOMMU_SECURE_CFG 2
+#define IOMMU_SECURE_PTBL_SIZE 3
+#define IOMMU_SECURE_PTBL_INIT 4
+#define IOMMU_SECURE_MAP 6
+#define IOMMU_SECURE_UNMAP 7
+
+static DEFINE_MUTEX(msm_iommu_lock);
+
+struct msm_priv {
+ struct list_head list_attached;
+};
+
+struct msm_scm_paddr_list {
+ unsigned int list;
+ unsigned int list_size;
+ unsigned int size;
+};
+
+struct msm_scm_mapping_info {
+ unsigned int id;
+ unsigned int ctx_id;
+ unsigned int va;
+ unsigned int size;
+};
+
+struct msm_scm_map_req {
+ struct msm_scm_paddr_list plist;
+ struct msm_scm_mapping_info info;
+};
+
+static int msm_iommu_sec_ptbl_init(void)
+{
+ struct device_node *np;
+ struct msm_scm_ptbl_init {
+ unsigned int paddr;
+ unsigned int size;
+ unsigned int spare;
+ } pinit;
+ unsigned int *buf;
+ int psize[2] = {0};
+ unsigned int spare;
+ int ret, ptbl_ret;
+
+ for_each_compatible_node(np, NULL, "qcom,msm-smmu-v2")
+ if (of_find_property(np, "qcom,iommu-secure-id", NULL))
+ break;
+
+ if (!np)
+ return 0;
+
+ of_node_put(np);
+ ret = scm_call(SCM_SVC_CP, IOMMU_SECURE_PTBL_SIZE, &spare,
+ sizeof(spare), psize, sizeof(psize));
+ if (ret) {
+ pr_err("scm call IOMMU_SECURE_PTBL_SIZE failed\n");
+ goto fail;
+ }
+
+ if (psize[1]) {
+ pr_err("scm call IOMMU_SECURE_PTBL_SIZE failed\n");
+ goto fail;
+ }
+
+ buf = kmalloc(psize[0], GFP_KERNEL);
+ if (!buf) {
+ pr_err("%s: Failed to allocate %d bytes for PTBL\n",
+ __func__, psize[0]);
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ pinit.paddr = virt_to_phys(buf);
+ pinit.size = psize[0];
+
+ ret = scm_call(SCM_SVC_CP, IOMMU_SECURE_PTBL_INIT, &pinit,
+ sizeof(pinit), &ptbl_ret, sizeof(ptbl_ret));
+ if (ret) {
+ pr_err("scm call IOMMU_SECURE_PTBL_INIT failed\n");
+ goto fail_mem;
+ }
+ if (ptbl_ret) {
+ pr_err("scm call IOMMU_SECURE_PTBL_INIT extended ret fail\n");
+ goto fail_mem;
+ }
+
+ return 0;
+
+fail_mem:
+ kfree(buf);
+fail:
+ return ret;
+}
+
+static int msm_iommu_sec_program_iommu(int sec_id)
+{
+ struct msm_scm_sec_cfg {
+ unsigned int id;
+ unsigned int spare;
+ } cfg;
+ int ret, scm_ret;
+
+ cfg.id = sec_id;
+
+ ret = scm_call(SCM_SVC_CP, IOMMU_SECURE_CFG, &cfg, sizeof(cfg),
+ &scm_ret, sizeof(scm_ret));
+ if (ret || scm_ret) {
+ pr_err("scm call IOMMU_SECURE_CFG failed\n");
+ return ret ? ret : -EINVAL;
+ }
+
+ return ret;
+}
+
+static int msm_iommu_sec_ptbl_map(struct msm_iommu_drvdata *iommu_drvdata,
+ struct msm_iommu_ctx_drvdata *ctx_drvdata,
+ unsigned long va, phys_addr_t pa, size_t len)
+{
+ struct msm_scm_map_req map;
+ int ret = 0;
+
+ map.plist.list = virt_to_phys(&pa);
+ map.plist.list_size = 1;
+ map.plist.size = len;
+ map.info.id = iommu_drvdata->sec_id;
+ map.info.ctx_id = ctx_drvdata->num;
+ map.info.va = va;
+ map.info.size = len;
+
+ if (scm_call(SCM_SVC_CP, IOMMU_SECURE_MAP, &map, sizeof(map), &ret,
+ sizeof(ret)))
+ return -EINVAL;
+ if (ret)
+ return -EINVAL;
+
+ return 0;
+}
+
+static unsigned int get_phys_addr(struct scatterlist *sg)
+{
+ /*
+ * Try sg_dma_address first so that we can
+ * map carveout regions that do not have a
+ * struct page associated with them.
+ */
+ unsigned int pa = sg_dma_address(sg);
+ if (pa == 0)
+ pa = sg_phys(sg);
+ return pa;
+}
+
+static int msm_iommu_sec_ptbl_map_range(struct msm_iommu_drvdata *iommu_drvdata,
+ struct msm_iommu_ctx_drvdata *ctx_drvdata,
+ unsigned long va, struct scatterlist *sg, size_t len)
+{
+ struct scatterlist *sgiter;
+ struct msm_scm_map_req map;
+ unsigned int *pa_list = 0;
+ unsigned int pa, cnt;
+ unsigned int offset = 0, chunk_offset = 0;
+ int ret, scm_ret;
+
+ map.info.id = iommu_drvdata->sec_id;
+ map.info.ctx_id = ctx_drvdata->num;
+ map.info.va = va;
+ map.info.size = len;
+
+ if (sg->length == len) {
+ pa = get_phys_addr(sg);
+ map.plist.list = virt_to_phys(&pa);
+ map.plist.list_size = 1;
+ map.plist.size = len;
+ } else {
+ sgiter = sg;
+ cnt = sg->length / SZ_1M;
+ while ((sgiter = sg_next(sgiter)))
+ cnt += sgiter->length / SZ_1M;
+
+ pa_list = kmalloc(cnt * sizeof(*pa_list), GFP_KERNEL);
+ if (!pa_list)
+ return -ENOMEM;
+
+ sgiter = sg;
+ cnt = 0;
+ pa = get_phys_addr(sgiter);
+ while (offset < len) {
+ pa += chunk_offset;
+ pa_list[cnt] = pa;
+ chunk_offset += SZ_1M;
+ offset += SZ_1M;
+ cnt++;
+
+ if (chunk_offset >= sgiter->length && offset < len) {
+ chunk_offset = 0;
+ sgiter = sg_next(sgiter);
+ pa = get_phys_addr(sgiter);
+ }
+ }
+
+ map.plist.list = virt_to_phys(pa_list);
+ map.plist.list_size = cnt;
+ map.plist.size = SZ_1M;
+ }
+
+ ret = scm_call(SCM_SVC_CP, IOMMU_SECURE_MAP, &map, sizeof(map),
+ &scm_ret, sizeof(scm_ret));
+ kfree(pa_list);
+ return ret;
+}
+
+static int msm_iommu_sec_ptbl_unmap(struct msm_iommu_drvdata *iommu_drvdata,
+ struct msm_iommu_ctx_drvdata *ctx_drvdata,
+ unsigned long va, size_t len)
+{
+ struct msm_scm_mapping_info mi;
+ int ret, scm_ret;
+
+ mi.id = iommu_drvdata->sec_id;
+ mi.ctx_id = ctx_drvdata->num;
+ mi.va = va;
+ mi.size = len;
+
+ ret = scm_call(SCM_SVC_CP, IOMMU_SECURE_UNMAP, &mi, sizeof(mi),
+ &scm_ret, sizeof(scm_ret));
+ return ret;
+}
+
+static int __enable_clocks(struct msm_iommu_drvdata *drvdata)
+{
+ int ret;
+
+ ret = clk_prepare_enable(drvdata->pclk);
+ if (ret)
+ goto fail;
+
+ ret = clk_prepare_enable(drvdata->clk);
+ if (ret)
+ clk_disable_unprepare(drvdata->pclk);
+
+ if (drvdata->aclk) {
+ ret = clk_prepare_enable(drvdata->aclk);
+ if (ret) {
+ clk_disable_unprepare(drvdata->clk);
+ clk_disable_unprepare(drvdata->pclk);
+ }
+ }
+fail:
+ return ret;
+}
+
+static void __disable_clocks(struct msm_iommu_drvdata *drvdata)
+{
+ if (drvdata->aclk)
+ clk_disable_unprepare(drvdata->aclk);
+ clk_disable_unprepare(drvdata->clk);
+ clk_disable_unprepare(drvdata->pclk);
+}
+
+static int msm_iommu_domain_init(struct iommu_domain *domain, int flags)
+{
+ struct msm_priv *priv;
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&priv->list_attached);
+ domain->priv = priv;
+ return 0;
+}
+
+static void msm_iommu_domain_destroy(struct iommu_domain *domain)
+{
+ struct msm_priv *priv;
+
+ mutex_lock(&msm_iommu_lock);
+ priv = domain->priv;
+ domain->priv = NULL;
+
+ kfree(priv);
+ mutex_unlock(&msm_iommu_lock);
+}
+
+static int msm_iommu_attach_dev(struct iommu_domain *domain, struct device *dev)
+{
+ struct msm_priv *priv;
+ struct msm_iommu_drvdata *iommu_drvdata;
+ struct msm_iommu_ctx_drvdata *ctx_drvdata;
+ struct msm_iommu_ctx_drvdata *tmp_drvdata;
+ int ret = 0;
+
+ mutex_lock(&msm_iommu_lock);
+
+ priv = domain->priv;
+ if (!priv || !dev) {
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ iommu_drvdata = dev_get_drvdata(dev->parent);
+ ctx_drvdata = dev_get_drvdata(dev);
+ if (!iommu_drvdata || !ctx_drvdata) {
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ if (!list_empty(&ctx_drvdata->attached_elm)) {
+ ret = -EBUSY;
+ goto fail;
+ }
+
+ list_for_each_entry(tmp_drvdata, &priv->list_attached, attached_elm)
+ if (tmp_drvdata == ctx_drvdata) {
+ ret = -EBUSY;
+ goto fail;
+ }
+
+ ret = regulator_enable(iommu_drvdata->gdsc);
+ if (ret)
+ goto fail;
+
+ ret = __enable_clocks(iommu_drvdata);
+ if (ret) {
+ regulator_disable(iommu_drvdata->gdsc);
+ goto fail;
+ }
+
+ ret = msm_iommu_sec_program_iommu(iommu_drvdata->sec_id);
+ __disable_clocks(iommu_drvdata);
+ if (ret) {
+ regulator_disable(iommu_drvdata->gdsc);
+ goto fail;
+ }
+
+ list_add(&(ctx_drvdata->attached_elm), &priv->list_attached);
+ ctx_drvdata->attached_domain = domain;
+
+fail:
+ mutex_unlock(&msm_iommu_lock);
+ return ret;
+}
+
+static void msm_iommu_detach_dev(struct iommu_domain *domain,
+ struct device *dev)
+{
+ struct msm_iommu_drvdata *iommu_drvdata;
+ struct msm_iommu_ctx_drvdata *ctx_drvdata;
+
+ mutex_lock(&msm_iommu_lock);
+ if (!dev)
+ goto fail;
+
+ iommu_drvdata = dev_get_drvdata(dev->parent);
+ ctx_drvdata = dev_get_drvdata(dev);
+ if (!iommu_drvdata || !ctx_drvdata || !ctx_drvdata->attached_domain)
+ goto fail;
+
+ list_del_init(&ctx_drvdata->attached_elm);
+ ctx_drvdata->attached_domain = NULL;
+
+ regulator_disable(iommu_drvdata->gdsc);
+
+fail:
+ mutex_unlock(&msm_iommu_lock);
+}
+
+static int get_drvdata(struct iommu_domain *domain,
+ struct msm_iommu_drvdata **iommu_drvdata,
+ struct msm_iommu_ctx_drvdata **ctx_drvdata)
+{
+ struct msm_priv *priv = domain->priv;
+ struct msm_iommu_ctx_drvdata *ctx;
+
+ list_for_each_entry(ctx, &priv->list_attached, attached_elm) {
+ if (ctx->attached_domain == domain)
+ break;
+ }
+
+ if (ctx->attached_domain != domain)
+ return -EINVAL;
+
+ *ctx_drvdata = ctx;
+ *iommu_drvdata = dev_get_drvdata(ctx->pdev->dev.parent);
+ return 0;
+}
+
+static int msm_iommu_map(struct iommu_domain *domain, unsigned long va,
+ phys_addr_t pa, size_t len, int prot)
+{
+ struct msm_iommu_drvdata *iommu_drvdata;
+ struct msm_iommu_ctx_drvdata *ctx_drvdata;
+ int ret = 0;
+
+ mutex_lock(&msm_iommu_lock);
+
+ ret = get_drvdata(domain, &iommu_drvdata, &ctx_drvdata);
+ if (ret)
+ goto fail;
+
+ ret = msm_iommu_sec_ptbl_map(iommu_drvdata, ctx_drvdata,
+ va, pa, len);
+fail:
+ mutex_unlock(&msm_iommu_lock);
+ return ret;
+}
+
+static size_t msm_iommu_unmap(struct iommu_domain *domain, unsigned long va,
+ size_t len)
+{
+ struct msm_iommu_drvdata *iommu_drvdata;
+ struct msm_iommu_ctx_drvdata *ctx_drvdata;
+ int ret = -ENODEV;
+
+ mutex_lock(&msm_iommu_lock);
+
+ ret = get_drvdata(domain, &iommu_drvdata, &ctx_drvdata);
+ if (ret)
+ goto fail;
+
+ ret = msm_iommu_sec_ptbl_unmap(iommu_drvdata, ctx_drvdata,
+ va, len);
+fail:
+ mutex_unlock(&msm_iommu_lock);
+
+ /* the IOMMU API requires us to return how many bytes were unmapped */
+ len = ret ? 0 : len;
+ return len;
+}
+
+static int msm_iommu_map_range(struct iommu_domain *domain, unsigned int va,
+ struct scatterlist *sg, unsigned int len,
+ int prot)
+{
+ int ret;
+ struct msm_iommu_drvdata *iommu_drvdata;
+ struct msm_iommu_ctx_drvdata *ctx_drvdata;
+
+ mutex_lock(&msm_iommu_lock);
+
+ ret = get_drvdata(domain, &iommu_drvdata, &ctx_drvdata);
+ if (ret)
+ goto fail;
+ ret = msm_iommu_sec_ptbl_map_range(iommu_drvdata, ctx_drvdata,
+ va, sg, len);
+fail:
+ mutex_unlock(&msm_iommu_lock);
+ return ret;
+}
+
+
+static int msm_iommu_unmap_range(struct iommu_domain *domain, unsigned int va,
+ unsigned int len)
+{
+ struct msm_iommu_drvdata *iommu_drvdata;
+ struct msm_iommu_ctx_drvdata *ctx_drvdata;
+ int ret;
+
+ mutex_lock(&msm_iommu_lock);
+
+ ret = get_drvdata(domain, &iommu_drvdata, &ctx_drvdata);
+ if (ret)
+ goto fail;
+
+ ret = msm_iommu_sec_ptbl_unmap(iommu_drvdata, ctx_drvdata, va, len);
+
+fail:
+ mutex_unlock(&msm_iommu_lock);
+ return 0;
+}
+
+static phys_addr_t msm_iommu_iova_to_phys(struct iommu_domain *domain,
+ unsigned long va)
+{
+ return 0;
+}
+
+static int msm_iommu_domain_has_cap(struct iommu_domain *domain,
+ unsigned long cap)
+{
+ return 0;
+}
+
+static phys_addr_t msm_iommu_get_pt_base_addr(struct iommu_domain *domain)
+{
+ return 0;
+}
+
+static struct iommu_ops msm_iommu_ops = {
+ .domain_init = msm_iommu_domain_init,
+ .domain_destroy = msm_iommu_domain_destroy,
+ .attach_dev = msm_iommu_attach_dev,
+ .detach_dev = msm_iommu_detach_dev,
+ .map = msm_iommu_map,
+ .unmap = msm_iommu_unmap,
+ .map_range = msm_iommu_map_range,
+ .unmap_range = msm_iommu_unmap_range,
+ .iova_to_phys = msm_iommu_iova_to_phys,
+ .domain_has_cap = msm_iommu_domain_has_cap,
+ .get_pt_base_addr = msm_iommu_get_pt_base_addr,
+ .pgsize_bitmap = MSM_IOMMU_PGSIZES,
+};
+
+static int __init msm_iommu_sec_init(void)
+{
+ int ret;
+
+ ret = bus_register(&msm_iommu_sec_bus_type);
+ if (ret)
+ goto fail;
+
+ bus_set_iommu(&msm_iommu_sec_bus_type, &msm_iommu_ops);
+ ret = msm_iommu_sec_ptbl_init();
+fail:
+ return ret;
+}
+
+subsys_initcall(msm_iommu_sec_init);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("MSM SMMU Secure Driver");
diff --git a/drivers/media/dvb/mpq/video/mpq_dvb_video.c b/drivers/media/dvb/mpq/video/mpq_dvb_video.c
index bd8c4a4..4628ba7 100644
--- a/drivers/media/dvb/mpq/video/mpq_dvb_video.c
+++ b/drivers/media/dvb/mpq/video/mpq_dvb_video.c
@@ -846,7 +846,6 @@
{
struct vdec_picsize pic_res;
int rc;
- struct video_buffer_req vdec_buf_req;
pic_res.frame_height = 1080;
pic_res.frame_width = 1920;
@@ -864,15 +863,6 @@
DBG("Failed in mpq_int_vid_dec_set_cont_on_reconfig : %d\n",\
rc);
- rc = mpq_int_vid_dec_get_buffer_req(client_ctx, &vdec_buf_req);
- if (rc)
- DBG("Failed in mpq_int_vid_dec_get_buffer_req : %d\n", rc);
-
- vdec_buf_req.num_output_buffers = 12;
- rc = mpq_int_set_out_buffer_req(client_ctx, &vdec_buf_req);
- if (rc)
- DBG("Failed in mpq_int_set_out_buffer_req (15) : %d\n", rc);
-
return rc;
}
@@ -1285,6 +1275,27 @@
return 0;
}
+static int mpq_int_vid_dec_set_buffer_req(struct video_client_ctx *client_ctx,
+ struct video_buffer_req vdec_buf_req)
+{
+ int rc = 0;
+ struct video_buffer_req vdec_req;
+
+ rc = mpq_int_vid_dec_get_buffer_req(client_ctx, &vdec_req);
+ if (rc)
+ DBG("Failed in mpq_int_vid_dec_get_buffer_req : %d\n", rc);
+
+ vdec_req.num_output_buffers = vdec_buf_req.num_output_buffers;
+ DBG(" num_output_buffers Set to %u\n", vdec_buf_req.num_output_buffers);
+ if (!vdec_buf_req.num_output_buffers)
+ return -EINVAL;
+ rc = mpq_int_set_out_buffer_req(client_ctx, &vdec_req);
+ if (rc)
+ DBG("Failed in mpq_int_set_out_buffer_req %d\n", rc);
+
+ return 0;
+}
+
static int mpq_int_vid_dec_set_buffer(struct mpq_dvb_video_inst *dev_inst,
struct video_data_buffer *data_buffer,
enum buffer_dir dir_buffer)
@@ -2006,6 +2017,10 @@
DBG("cmd : VIDEO_CMD_GET_BUFFER_REQ\n");
rc = mpq_int_vid_dec_get_buffer_req(client_ctx, &cmd->buf_req);
break;
+ case VIDEO_CMD_SET_BUFFER_COUNT:
+ DBG("cmd : VIDEO_CMD_SET_BUFFER_COUNT\n");
+ rc = mpq_int_vid_dec_set_buffer_req(client_ctx, cmd->buf_req);
+ break;
case VIDEO_CMD_READ_RAW_OUTPUT:
DBG("cmd : VIDEO_CMD_READ_RAW_OUTPUT\n");
rc = mpq_int_vid_dec_fill_output_buffer(client_ctx,
diff --git a/drivers/media/rc/gpio-ir-recv.c b/drivers/media/rc/gpio-ir-recv.c
index d7dc67d..8a99968 100644
--- a/drivers/media/rc/gpio-ir-recv.c
+++ b/drivers/media/rc/gpio-ir-recv.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+/* 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
@@ -18,17 +18,35 @@
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/irq.h>
+#include <linux/pm_qos.h>
+#include <linux/timer.h>
#include <media/rc-core.h>
#include <media/gpio-ir-recv.h>
#define GPIO_IR_DRIVER_NAME "gpio-rc-recv"
#define GPIO_IR_DEVICE_NAME "gpio_ir_recv"
+static int gpio_ir_timeout = 200;
+module_param_named(gpio_ir_timeout, gpio_ir_timeout, int, 0664);
+
+static int __init gpio_ir_timeout_setup(char *p)
+{
+ gpio_ir_timeout = memparse(p, NULL);
+ return 0;
+}
+
+early_param("gpio_ir_timeout", gpio_ir_timeout_setup);
+
struct gpio_rc_dev {
struct rc_dev *rcdev;
+ struct pm_qos_request pm_qos_req;
+ struct timer_list gpio_ir_timer;
unsigned int gpio_nr;
bool active_low;
int can_sleep;
+ bool can_wakeup;
+ bool pm_qos_vote;
+ int gpio_irq_latency;
};
static irqreturn_t gpio_ir_recv_irq(int irq, void *dev_id)
@@ -38,6 +56,12 @@
int rc = 0;
enum raw_event_type type = IR_SPACE;
+ if (!gpio_dev->pm_qos_vote && gpio_dev->can_wakeup) {
+ gpio_dev->pm_qos_vote = 1;
+ pm_qos_update_request(&gpio_dev->pm_qos_req,
+ gpio_dev->gpio_irq_latency);
+ }
+
if (gpio_dev->can_sleep)
gval = gpio_get_value_cansleep(gpio_dev->gpio_nr);
else
@@ -58,10 +82,22 @@
ir_raw_event_handle(gpio_dev->rcdev);
+ if (gpio_dev->can_wakeup)
+ mod_timer(&gpio_dev->gpio_ir_timer,
+ jiffies + msecs_to_jiffies(gpio_ir_timeout));
err_get_value:
return IRQ_HANDLED;
}
+static void gpio_ir_timer(unsigned long data)
+{
+ struct gpio_rc_dev *gpio_dev = (struct gpio_rc_dev *)data;
+
+ pm_qos_update_request(&gpio_dev->pm_qos_req, PM_QOS_DEFAULT_VALUE);
+ pm_qos_request_active(&gpio_dev->pm_qos_req);
+ gpio_dev->pm_qos_vote = 0;
+}
+
static int __devinit gpio_ir_recv_probe(struct platform_device *pdev)
{
struct gpio_rc_dev *gpio_dev;
@@ -96,6 +132,9 @@
gpio_dev->rcdev = rcdev;
gpio_dev->gpio_nr = pdata->gpio_nr;
gpio_dev->active_low = pdata->active_low;
+ gpio_dev->can_wakeup = pdata->can_wakeup;
+ gpio_dev->gpio_irq_latency = pdata->swfi_latency + 1;
+ gpio_dev->pm_qos_vote = 0;
rc = gpio_request(pdata->gpio_nr, "gpio-ir-recv");
if (rc < 0)
@@ -122,7 +161,14 @@
if (rc < 0)
goto err_request_irq;
- device_init_wakeup(&pdev->dev, pdata->can_wakeup);
+ if (gpio_dev->can_wakeup) {
+ pm_qos_add_request(&gpio_dev->pm_qos_req,
+ PM_QOS_CPU_DMA_LATENCY,
+ PM_QOS_DEFAULT_VALUE);
+ device_init_wakeup(&pdev->dev, pdata->can_wakeup);
+ setup_timer(&gpio_dev->gpio_ir_timer, gpio_ir_timer,
+ (unsigned long)gpio_dev);
+ }
return 0;
@@ -144,6 +190,10 @@
{
struct gpio_rc_dev *gpio_dev = platform_get_drvdata(pdev);
+ if (gpio_dev->can_wakeup) {
+ del_timer_sync(&gpio_dev->gpio_ir_timer);
+ pm_qos_remove_request(&gpio_dev->pm_qos_req);
+ }
free_irq(gpio_to_irq(gpio_dev->gpio_nr), gpio_dev);
platform_set_drvdata(pdev, NULL);
rc_unregister_device(gpio_dev->rcdev);
diff --git a/drivers/media/video/msm/jpeg_10/msm_jpeg_core.c b/drivers/media/video/msm/jpeg_10/msm_jpeg_core.c
index b67245c..a2fc813 100644
--- a/drivers/media/video/msm/jpeg_10/msm_jpeg_core.c
+++ b/drivers/media/video/msm/jpeg_10/msm_jpeg_core.c
@@ -159,7 +159,7 @@
void *msm_jpeg_core_err_irq(int jpeg_irq_status,
struct msm_jpeg_device *pgmn_dev)
{
- JPEG_PR_ERR("%s:%d]\n", __func__, jpeg_irq_status);
+ JPEG_PR_ERR("%s: Error %x\n", __func__, jpeg_irq_status);
return NULL;
}
@@ -211,6 +211,7 @@
if (msm_jpeg_hw_irq_is_frame_done(jpeg_irq_status)) {
/* send fe ping pong irq */
+ JPEG_DBG_HIGH("%s:%d] Session done\n", __func__, __LINE__);
data = msm_jpeg_core_fe_pingpong_irq(jpeg_irq_status,
pgmn_dev);
if (msm_jpeg_irq_handler)
diff --git a/drivers/media/video/msm/jpeg_10/msm_jpeg_hw.c b/drivers/media/video/msm/jpeg_10/msm_jpeg_hw.c
index e311e4c..c38771b 100644
--- a/drivers/media/video/msm/jpeg_10/msm_jpeg_hw.c
+++ b/drivers/media/video/msm/jpeg_10/msm_jpeg_hw.c
@@ -132,6 +132,10 @@
JPEG_PLN1_RD_OFFSET_BMSK, {0} },
{MSM_JPEG_HW_CMD_TYPE_WRITE, 1, JPEG_PLN1_RD_PNTR_ADDR,
JPEG_PLN1_RD_PNTR_BMSK, {0} },
+ {MSM_JPEG_HW_CMD_TYPE_WRITE, 1, JPEG_PLN2_RD_OFFSET_ADDR,
+ JPEG_PLN1_RD_OFFSET_BMSK, {0} },
+ {MSM_JPEG_HW_CMD_TYPE_WRITE, 1, JPEG_PLN2_RD_PNTR_ADDR,
+ JPEG_PLN2_RD_PNTR_BMSK, {0} },
};
void msm_jpeg_hw_fe_buffer_update(struct msm_jpeg_hw_buf *p_input,
@@ -156,7 +160,11 @@
hw_cmd_p->data = p_input->cbcr_buffer_addr;
msm_jpeg_hw_write(hw_cmd_p++, base);
wmb();
-
+ msm_jpeg_hw_write(hw_cmd_p++, base);
+ wmb();
+ hw_cmd_p->data = p_input->pln2_addr;
+ msm_jpeg_hw_write(hw_cmd_p++, base);
+ wmb();
}
return;
}
@@ -215,6 +223,7 @@
JPEG_PR_ERR("%s Output pln1 buffer address is %x\n", __func__,
p_input->cbcr_buffer_addr);
msm_jpeg_hw_write(hw_cmd_p++, base);
+ hw_cmd_p->data = p_input->pln2_addr;
JPEG_PR_ERR("%s Output pln2 buffer address is %x\n", __func__,
p_input->pln2_addr);
msm_jpeg_hw_write(hw_cmd_p++, base);
diff --git a/drivers/media/video/msm/jpeg_10/msm_jpeg_hw.h b/drivers/media/video/msm/jpeg_10/msm_jpeg_hw.h
index e90b941..084e36b 100644
--- a/drivers/media/video/msm/jpeg_10/msm_jpeg_hw.h
+++ b/drivers/media/video/msm/jpeg_10/msm_jpeg_hw.h
@@ -29,7 +29,7 @@
uint32_t num_of_mcu_rows;
struct ion_handle *handle;
uint32_t pln2_addr;
- uint32_t pln2_offset;
+ uint32_t pln2_len;
};
struct msm_jpeg_hw_pingpong {
diff --git a/drivers/media/video/msm/jpeg_10/msm_jpeg_hw_reg.h b/drivers/media/video/msm/jpeg_10/msm_jpeg_hw_reg.h
index 928d59e..ff99aa3 100644
--- a/drivers/media/video/msm/jpeg_10/msm_jpeg_hw_reg.h
+++ b/drivers/media/video/msm/jpeg_10/msm_jpeg_hw_reg.h
@@ -72,6 +72,12 @@
#define JPEG_PLN1_RD_OFFSET_ADDR 0x00000048
#define JPEG_PLN1_RD_OFFSET_BMSK 0x1FFFFFFF
+#define JPEG_PLN2_RD_PNTR_ADDR (JPEG_REG_BASE + 0x00000050)
+#define JPEG_PLN2_RD_PNTR_BMSK 0xFFFFFFFF
+
+#define JPEG_PLN2_RD_OFFSET_ADDR 0x00000054
+#define JPEG_PLN2_RD_OFFSET_BMSK 0x1FFFFFFF
+
#define JPEG_CMD_ADDR (JPEG_REG_BASE + 0x00000010)
#define JPEG_CMD_BMSK 0x00000FFF
#define JPEG_CMD_CLEAR_WRITE_PLN_QUEUES 0x700
diff --git a/drivers/media/video/msm/jpeg_10/msm_jpeg_sync.c b/drivers/media/video/msm/jpeg_10/msm_jpeg_sync.c
index a0aaf03..a7a9d70 100644
--- a/drivers/media/video/msm/jpeg_10/msm_jpeg_sync.c
+++ b/drivers/media/video/msm/jpeg_10/msm_jpeg_sync.c
@@ -368,8 +368,8 @@
buf_cmd.fd);
buf_p->y_buffer_addr = msm_jpeg_platform_v2p(pgmn_dev, buf_cmd.fd,
- buf_cmd.y_len, &buf_p->file, &buf_p->handle,
- pgmn_dev->domain_num);
+ buf_cmd.y_len + buf_cmd.cbcr_len + buf_cmd.pln2_len,
+ &buf_p->file, &buf_p->handle, pgmn_dev->domain_num);
if (!buf_p->y_buffer_addr) {
JPEG_PR_ERR("%s:%d] v2p wrong\n", __func__, __LINE__);
kfree(buf_p);
@@ -382,11 +382,23 @@
else
buf_p->cbcr_buffer_addr = 0x0;
- JPEG_DBG("%s:%d] After v2p pln0_addr =0x%x,pln0_len %d pl1_len %d",
+ if (buf_cmd.pln2_len)
+ buf_p->pln2_addr = buf_p->cbcr_buffer_addr +
+ buf_cmd.cbcr_len;
+ else
+ buf_p->pln2_addr = 0x0;
+
+ JPEG_DBG("%s:%d]After v2p pln0_addr %x pln0_len %d",
__func__, __LINE__, buf_p->y_buffer_addr,
- buf_cmd.y_len, buf_cmd.cbcr_len);
+ buf_cmd.y_len);
+
+ JPEG_DBG("pl1_len %d, pln1_addr %x, pln2_adrr %x,pln2_len %d",
+ buf_cmd.cbcr_len, buf_p->cbcr_buffer_addr,
+ buf_p->pln2_addr, buf_cmd.pln2_len);
+
buf_p->y_len = buf_cmd.y_len;
buf_p->cbcr_len = buf_cmd.cbcr_len;
+ buf_p->pln2_len = buf_cmd.pln2_len;
buf_p->vbuf = buf_cmd;
msm_jpeg_q_in(&pgmn_dev->output_buf_q, buf_p);
@@ -489,23 +501,31 @@
(int) buf_cmd.vaddr, buf_cmd.y_len);
buf_p->y_buffer_addr = msm_jpeg_platform_v2p(pgmn_dev, buf_cmd.fd,
- buf_cmd.y_len + buf_cmd.cbcr_len, &buf_p->file,
- &buf_p->handle, pgmn_dev->domain_num) + buf_cmd.offset
- + buf_cmd.y_off;
+ buf_cmd.y_len + buf_cmd.cbcr_len + buf_cmd.pln2_len,
+ &buf_p->file, &buf_p->handle, pgmn_dev->domain_num) +
+ buf_cmd.offset + buf_cmd.y_off;
buf_p->y_len = buf_cmd.y_len;
buf_p->cbcr_len = buf_cmd.cbcr_len;
+ buf_p->pln2_len = buf_cmd.pln2_len;
buf_p->num_of_mcu_rows = buf_cmd.num_of_mcu_rows;
- buf_p->y_len = buf_cmd.y_len;
- buf_p->cbcr_len = buf_cmd.cbcr_len;
+
if (buf_cmd.cbcr_len)
- buf_p->cbcr_buffer_addr = buf_p->y_buffer_addr + buf_cmd.y_len
- + buf_cmd.cbcr_off;
+ buf_p->cbcr_buffer_addr = buf_p->y_buffer_addr +
+ buf_cmd.y_len + buf_cmd.cbcr_off;
else
buf_p->cbcr_buffer_addr = 0x0;
- JPEG_DBG("%s: y_addr=%x, y_len=%x, cbcr_addr=%x, cbcr_len=%x, fd =%d\n",
+ if (buf_cmd.pln2_len)
+ buf_p->pln2_addr = buf_p->cbcr_buffer_addr +
+ buf_cmd.cbcr_len + buf_cmd.pln2_off;
+ else
+ buf_p->pln2_addr = 0x0;
+
+ JPEG_DBG("%s: y_addr=%x, y_len=%x, cbcr_addr=%x, cbcr_len=%d",
__func__, buf_p->y_buffer_addr, buf_p->y_len,
- buf_p->cbcr_buffer_addr, buf_p->cbcr_len, buf_cmd.fd);
+ buf_p->cbcr_buffer_addr, buf_p->cbcr_len);
+ JPEG_DBG("pln2_addr = %x, pln2_len = %d, fd =%d\n",
+ buf_p->pln2_addr, buf_p->pln2_len, buf_cmd.fd);
if (!buf_p->y_buffer_addr) {
JPEG_PR_ERR("%s:%d] v2p wrong\n", __func__, __LINE__);
@@ -733,9 +753,11 @@
for (i = 0; i < 2; i++)
kfree(buf_out_free[i]);
- msm_jpeg_io_dump(pgmn_dev->base, JPEG_REG_SIZE);
+ JPEG_DBG_HIGH("%s:%d] START\n", __func__, __LINE__);
+ wmb();
rc = msm_jpeg_ioctl_hw_cmds(pgmn_dev, arg);
- JPEG_DBG("%s:%d]\n", __func__, __LINE__);
+ wmb();
+ JPEG_DBG("%s:%d]", __func__, __LINE__);
return rc;
}
diff --git a/drivers/media/video/msm/msm_mctl.c b/drivers/media/video/msm/msm_mctl.c
index 3b678c4..36fb849 100644
--- a/drivers/media/video/msm/msm_mctl.c
+++ b/drivers/media/video/msm/msm_mctl.c
@@ -222,7 +222,7 @@
pr_err("%s Copy from user failed ", __func__);
rc = -EFAULT;
} else {
- pr_info("%s: mctl=0x%p, vfe output mode =0x%x",
+ pr_debug("%s: mctl=0x%p, vfe output mode =0x%x\n",
__func__, p_mctl, p_mctl->vfe_output_mode);
}
return rc;
diff --git a/drivers/media/video/msm/server/msm_cam_server.c b/drivers/media/video/msm/server/msm_cam_server.c
index 84aaa69..b8b1d51 100644
--- a/drivers/media/video/msm/server/msm_cam_server.c
+++ b/drivers/media/video/msm/server/msm_cam_server.c
@@ -152,6 +152,27 @@
mctl_handle = 0;
}
}
+
+ if (!is_bayer_sensor && interface == PIX_0) {
+ if (g_server_dev.
+ interface_map_table[i].mctl_handle &&
+ g_server_dev.interface_map_table[i].
+ is_bayer_sensor) {
+ /* In case of simultaneous camera,
+ * the YUV sensor could use PIX
+ * interface to only queue the preview
+ * or video buffers, but does not
+ * expect any notifications directly.
+ * (preview/video data is updated from
+ * postprocessing in such scenario).
+ * In such case, there is no need to
+ * update the mctl_handle in the intf
+ * map table, since the notification
+ * will not be sent directly. */
+ break;
+ }
+ }
+
old_handle =
g_server_dev.interface_map_table[i].mctl_handle;
if (old_handle == 0) {
diff --git a/drivers/media/video/msm/vfe/msm_vfe40.c b/drivers/media/video/msm/vfe/msm_vfe40.c
index 9ffa874..a084a6d 100644
--- a/drivers/media/video/msm/vfe/msm_vfe40.c
+++ b/drivers/media/video/msm/vfe/msm_vfe40.c
@@ -821,24 +821,76 @@
/* stats UB config */
CDBG("%s: Use bayer stats = %d\n", __func__,
vfe40_use_bayer_stats(vfe40_ctrl));
- msm_camera_io_w(0x350001F,
+
+ msm_camera_io_w(0x8350001F,
vfe40_ctrl->share_ctrl->vfebase +
VFE_BUS_STATS_HIST_WR_UB_CFG);
- msm_camera_io_w(0x370002F,
+ msm_camera_io_w(0x8370002F,
vfe40_ctrl->share_ctrl->vfebase +
VFE_BUS_STATS_BG_WR_UB_CFG);
- msm_camera_io_w(0x3A0002F,
+ msm_camera_io_w(0x83A0002F,
vfe40_ctrl->share_ctrl->vfebase +
VFE_BUS_STATS_BF_WR_UB_CFG);
- msm_camera_io_w(0x3D00007,
+ msm_camera_io_w(0x83D0000F,
vfe40_ctrl->share_ctrl->vfebase +
VFE_BUS_STATS_RS_WR_UB_CFG);
- msm_camera_io_w(0x3D8001F,
+ msm_camera_io_w(0x83E00007,
vfe40_ctrl->share_ctrl->vfebase +
VFE_BUS_STATS_CS_WR_UB_CFG);
- msm_camera_io_w(0x3F80007,
+ msm_camera_io_w(0x83E80007,
vfe40_ctrl->share_ctrl->vfebase +
VFE_BUS_STATS_SKIN_WR_UB_CFG);
+ msm_camera_io_w(0x83F0000F,
+ vfe40_ctrl->share_ctrl->vfebase +
+ VFE_BUS_STATS_AWB_WR_UB_CFG);
+
+
+ /* stats frame subsample config*/
+ msm_camera_io_w(0xFFFFFFFF,
+ vfe40_ctrl->share_ctrl->vfebase +
+ VFE_BUS_STATS_HIST_WR_FRAMEDROP_PATTERN);
+ msm_camera_io_w(0xFFFFFFFF,
+ vfe40_ctrl->share_ctrl->vfebase +
+ VFE_BUS_STATS_BG_WR_FRAMEDROP_PATTERN);
+ msm_camera_io_w(0xFFFFFFFF,
+ vfe40_ctrl->share_ctrl->vfebase +
+ VFE_BUS_STATS_BF_WR_FRAMEDROP_PATTERN);
+ msm_camera_io_w(0xFFFFFFFF,
+ vfe40_ctrl->share_ctrl->vfebase +
+ VFE_BUS_STATS_RS_WR_FRAMEDROP_PATTERN);
+ msm_camera_io_w(0xFFFFFFFF,
+ vfe40_ctrl->share_ctrl->vfebase +
+ VFE_BUS_STATS_CS_WR_FRAMEDROP_PATTERN);
+ msm_camera_io_w(0xFFFFFFFF,
+ vfe40_ctrl->share_ctrl->vfebase +
+ VFE_BUS_STATS_SKIN_WR_FRAMEDROP_PATTERN);
+ msm_camera_io_w(0xFFFFFFFF,
+ vfe40_ctrl->share_ctrl->vfebase +
+ VFE_BUS_STATS_AWB_WR_FRAMEDROP_PATTERN);
+
+ /* stats irq subsample config*/
+ msm_camera_io_w(0xFFFFFFFF,
+ vfe40_ctrl->share_ctrl->vfebase +
+ VFE_BUS_STATS_HIST_WR_IRQ_SUBSAMPLE_PATTERN);
+ msm_camera_io_w(0xFFFFFFFF,
+ vfe40_ctrl->share_ctrl->vfebase +
+ VFE_BUS_STATS_BG_WR_IRQ_SUBSAMPLE_PATTERN);
+ msm_camera_io_w(0xFFFFFFFF,
+ vfe40_ctrl->share_ctrl->vfebase +
+ VFE_BUS_STATS_BF_WR_IRQ_SUBSAMPLE_PATTERN);
+ msm_camera_io_w(0xFFFFFFFF,
+ vfe40_ctrl->share_ctrl->vfebase +
+ VFE_BUS_STATS_RS_WR_IRQ_SUBSAMPLE_PATTERN);
+ msm_camera_io_w(0xFFFFFFFF,
+ vfe40_ctrl->share_ctrl->vfebase +
+ VFE_BUS_STATS_CS_WR_IRQ_SUBSAMPLE_PATTERN);
+ msm_camera_io_w(0xFFFFFFFF,
+ vfe40_ctrl->share_ctrl->vfebase +
+ VFE_BUS_STATS_SKIN_WR_IRQ_SUBSAMPLE_PATTERN);
+ msm_camera_io_w(0xFFFFFFFF,
+ vfe40_ctrl->share_ctrl->vfebase +
+ VFE_BUS_STATS_AWB_WR_IRQ_SUBSAMPLE_PATTERN);
+
vfe40_reset_dmi_tables(vfe40_ctrl);
}
@@ -967,7 +1019,7 @@
rc = vfe40_ctrl->stats_ops.dqbuf(
vfe40_ctrl->stats_ops.stats_ctrl, stats_type, &buf);
if (rc < 0) {
- pr_err("%s: dq stats buf (type = %d) err = %d",
+ pr_err("%s: dq stats buf (type = %d) err = %d\n",
__func__, stats_type, rc);
return 0L;
}
@@ -1043,7 +1095,7 @@
addr = (uint32_t)vfe40_stats_dqbuf(vfe40_ctrl, MSM_STATS_TYPE_AWB);
spin_unlock_irqrestore(&vfe40_ctrl->stats_bufq_lock, flags);
if (!addr) {
- pr_err("%s: dq awb ping buf from free buf queue", __func__);
+ pr_err("%s: dq awb ping buf from free buf queue\n", __func__);
return -ENOMEM;
}
msm_camera_io_w(addr,
@@ -1053,7 +1105,7 @@
addr = (uint32_t)vfe40_stats_dqbuf(vfe40_ctrl, MSM_STATS_TYPE_AWB);
spin_unlock_irqrestore(&vfe40_ctrl->stats_bufq_lock, flags);
if (!addr) {
- pr_err("%s: dq awb ping buf from free buf queue",
+ pr_err("%s: dq awb ping buf from free buf queue\n",
__func__);
return -ENOMEM;
}
@@ -1070,14 +1122,12 @@
unsigned long flags;
uint32_t stats_type;
- stats_type =
- (!vfe40_use_bayer_stats(vfe40_ctrl)) ? MSM_STATS_TYPE_AEC
- : MSM_STATS_TYPE_BG;
+ stats_type = MSM_STATS_TYPE_BG;
spin_lock_irqsave(&vfe40_ctrl->stats_bufq_lock, flags);
addr = (uint32_t)vfe40_stats_dqbuf(vfe40_ctrl, stats_type);
spin_unlock_irqrestore(&vfe40_ctrl->stats_bufq_lock, flags);
if (!addr) {
- pr_err("%s: dq aec ping buf from free buf queue",
+ pr_err("%s: dq aec/Bg ping buf from free buf queue\n",
__func__);
return -ENOMEM;
}
@@ -1088,7 +1138,7 @@
addr = (uint32_t)vfe40_stats_dqbuf(vfe40_ctrl, stats_type);
spin_unlock_irqrestore(&vfe40_ctrl->stats_bufq_lock, flags);
if (!addr) {
- pr_err("%s: dq aec pong buf from free buf queue",
+ pr_err("%s: dq aec/Bg pong buf from free buf queue\n",
__func__);
return -ENOMEM;
}
@@ -1106,9 +1156,7 @@
int rc = 0;
uint32_t stats_type;
- stats_type =
- (!vfe40_use_bayer_stats(vfe40_ctrl)) ? MSM_STATS_TYPE_AF
- : MSM_STATS_TYPE_BF;
+ stats_type = MSM_STATS_TYPE_BF;
spin_lock_irqsave(&vfe40_ctrl->stats_bufq_lock, flags);
rc = vfe40_stats_flush_enqueue(vfe40_ctrl, stats_type);
@@ -1193,7 +1241,7 @@
addr = (uint32_t)vfe40_stats_dqbuf(vfe40_ctrl, MSM_STATS_TYPE_IHIST);
spin_unlock_irqrestore(&vfe40_ctrl->stats_bufq_lock, flags);
if (!addr) {
- pr_err("%s: dq ihist pong buf from free buf queue",
+ pr_err("%s: dq Ihist pong buf from free buf queue",
__func__);
return -ENOMEM;
}
@@ -1299,6 +1347,7 @@
msm_camera_io_w_mb(1, vfe40_ctrl->share_ctrl->vfebase +
VFE_CAMIF_COMMAND);
}
+
}
static int vfe40_start_recording(
@@ -1874,11 +1923,6 @@
}
break;
case VFE_CMD_STATS_AWB_START: {
- if (vfe40_use_bayer_stats(vfe40_ctrl)) {
- /* Error */
- rc = -EFAULT;
- goto proc_general_done;
- }
rc = vfe_stats_awb_buf_init(vfe40_ctrl, NULL);
if (rc < 0) {
pr_err("%s: cannot config ping/pong address of AWB",
@@ -1992,11 +2036,6 @@
case VFE_CMD_STATS_BG_START:
case VFE_CMD_STATS_BF_START:
case VFE_CMD_STATS_BHIST_START: {
- if (!vfe40_use_bayer_stats(vfe40_ctrl)) {
- /* Error */
- rc = -EFAULT;
- goto proc_general_done;
- }
old_val = msm_camera_io_r(
vfe40_ctrl->share_ctrl->vfebase + VFE_STATS_CFG);
module_val = msm_camera_io_r(
@@ -2005,7 +2044,7 @@
module_val |= BG_ENABLE_MASK;
rc = vfe_stats_aec_bg_buf_init(vfe40_ctrl);
if (rc < 0) {
- pr_err("%s: cannot config ping/pong address of CS",
+ pr_err("%s: cannot config ping/pong address of BG",
__func__);
goto proc_general_done;
}
@@ -2013,7 +2052,7 @@
module_val |= BF_ENABLE_MASK;
rc = vfe_stats_af_bf_buf_init(vfe40_ctrl);
if (rc < 0) {
- pr_err("%s: cannot config ping/pong address of CS",
+ pr_err("%s: cannot config ping/pong address of BF",
__func__);
goto proc_general_done;
}
@@ -2022,7 +2061,7 @@
old_val |= STATS_BHIST_ENABLE_MASK;
rc = vfe_stats_bhist_buf_init(vfe40_ctrl);
if (rc < 0) {
- pr_err("%s: cannot config ping/pong address of CS",
+ pr_err("%s: cannot config ping/pong address of BHist",
__func__);
goto proc_general_done;
}
@@ -3933,11 +3972,13 @@
VFE_BUS_PING_PONG_STATUS))
& ((uint32_t)(1<<(statsNum + 7)))) >> (statsNum + 7);
/* stats bits starts at 7 */
- CDBG("statsNum %d, pingpongStatus %d\n", statsNum, pingpongStatus);
+ CDBG("%s:statsNum %d, pingpongStatus %d\n", __func__,
+ statsNum, pingpongStatus);
pingpongAddr =
((uint32_t)(vfe40_ctrl->share_ctrl->vfebase +
VFE_BUS_STATS_PING_PONG_BASE)) +
- (3*statsNum)*4 + (1-pingpongStatus)*4;
+ (VFE_STATS_BUS_REG_NUM*statsNum)*4 +
+ (1-pingpongStatus)*4;
returnAddr = msm_camera_io_r((uint32_t *)pingpongAddr);
msm_camera_io_w(newAddr, (uint32_t *)pingpongAddr);
return returnAddr;
@@ -3959,13 +4000,9 @@
msgStats.frameCounter--;
msgStats.buffer = bufAddress;
switch (statsNum) {
- case statsAeNum:{
- msgStats.id =
- (!vfe40_use_bayer_stats(vfe40_ctrl)) ? MSG_ID_STATS_AEC
- : MSG_ID_STATS_BG;
- stats_type =
- (!vfe40_use_bayer_stats(vfe40_ctrl)) ?
- MSM_STATS_TYPE_AEC : MSM_STATS_TYPE_BG;
+ case statsBgNum:{
+ msgStats.id = MSG_ID_STATS_BG;
+ stats_type = MSM_STATS_TYPE_BG;
rc = vfe40_ctrl->stats_ops.dispatch(
vfe40_ctrl->stats_ops.stats_ctrl,
stats_type, bufAddress,
@@ -3973,13 +4010,9 @@
vfe40_ctrl->stats_ops.client);
}
break;
- case statsAfNum:{
- msgStats.id =
- (!vfe40_use_bayer_stats(vfe40_ctrl)) ? MSG_ID_STATS_AF
- : MSG_ID_STATS_BF;
- stats_type =
- (!vfe40_use_bayer_stats(vfe40_ctrl)) ? MSM_STATS_TYPE_AF
- : MSM_STATS_TYPE_BF;
+ case statsBfNum:{
+ msgStats.id = MSG_ID_STATS_BF;
+ stats_type = MSM_STATS_TYPE_BF;
rc = vfe40_ctrl->stats_ops.dispatch(
vfe40_ctrl->stats_ops.stats_ctrl,
stats_type, bufAddress,
@@ -4085,19 +4118,17 @@
unsigned long flags;
uint32_t addr;
uint32_t stats_type;
- stats_type =
- (!vfe40_use_bayer_stats(vfe40_ctrl)) ? MSM_STATS_TYPE_AEC
- : MSM_STATS_TYPE_BG;
+ stats_type = MSM_STATS_TYPE_BG;
spin_lock_irqsave(&vfe40_ctrl->stats_bufq_lock, flags);
addr = (uint32_t)vfe40_stats_dqbuf(vfe40_ctrl, stats_type);
spin_unlock_irqrestore(&vfe40_ctrl->stats_bufq_lock, flags);
if (addr) {
vfe40_ctrl->aecbgStatsControl.bufToRender =
- vfe40_process_stats_irq_common(vfe40_ctrl, statsAeNum,
+ vfe40_process_stats_irq_common(vfe40_ctrl, statsBgNum,
addr);
vfe_send_stats_msg(vfe40_ctrl,
- vfe40_ctrl->aecbgStatsControl.bufToRender, statsAeNum);
+ vfe40_ctrl->aecbgStatsControl.bufToRender, statsBgNum);
} else{
vfe40_ctrl->aecbgStatsControl.droppedStatsFrameCount++;
CDBG("%s: droppedStatsFrameCount = %d", __func__,
@@ -4131,19 +4162,17 @@
unsigned long flags;
uint32_t addr;
uint32_t stats_type;
- stats_type =
- (!vfe40_use_bayer_stats(vfe40_ctrl)) ? MSM_STATS_TYPE_AF
- : MSM_STATS_TYPE_BF;
+ stats_type = MSM_STATS_TYPE_BF;
spin_lock_irqsave(&vfe40_ctrl->stats_bufq_lock, flags);
addr = (uint32_t)vfe40_stats_dqbuf(vfe40_ctrl, stats_type);
spin_unlock_irqrestore(&vfe40_ctrl->stats_bufq_lock, flags);
if (addr) {
vfe40_ctrl->afbfStatsControl.bufToRender =
- vfe40_process_stats_irq_common(vfe40_ctrl, statsAfNum,
+ vfe40_process_stats_irq_common(vfe40_ctrl, statsBfNum,
addr);
vfe_send_stats_msg(vfe40_ctrl,
- vfe40_ctrl->afbfStatsControl.bufToRender, statsAfNum);
+ vfe40_ctrl->afbfStatsControl.bufToRender, statsBfNum);
} else{
vfe40_ctrl->afbfStatsControl.droppedStatsFrameCount++;
CDBG("%s: droppedStatsFrameCount = %d", __func__,
@@ -4248,9 +4277,7 @@
CDBG("%s, stats = 0x%x\n", __func__, status_bits);
spin_lock_irqsave(&vfe40_ctrl->stats_bufq_lock, flags);
- stats_type =
- (!vfe40_use_bayer_stats(vfe40_ctrl)) ? MSM_STATS_TYPE_AEC
- : MSM_STATS_TYPE_BG;
+ stats_type = MSM_STATS_TYPE_BG;
if (status_bits & VFE_IRQ_STATUS0_STATS_BG) {
addr = (uint32_t)vfe40_stats_dqbuf(vfe40_ctrl,
@@ -4258,7 +4285,7 @@
if (addr) {
vfe40_ctrl->aecbgStatsControl.bufToRender =
vfe40_process_stats_irq_common(
- vfe40_ctrl, statsAeNum, addr);
+ vfe40_ctrl, statsBgNum, addr);
process_stats = true;
} else{
vfe40_ctrl->aecbgStatsControl.bufToRender = 0;
@@ -4285,16 +4312,14 @@
vfe40_ctrl->awbStatsControl.bufToRender = 0;
}
- stats_type =
- (!vfe40_use_bayer_stats(vfe40_ctrl)) ? MSM_STATS_TYPE_AF
- : MSM_STATS_TYPE_BF;
+ stats_type = MSM_STATS_TYPE_BF;
if (status_bits & VFE_IRQ_STATUS0_STATS_BF) {
addr = (uint32_t)vfe40_stats_dqbuf(vfe40_ctrl,
stats_type);
if (addr) {
vfe40_ctrl->afbfStatsControl.bufToRender =
vfe40_process_stats_irq_common(
- vfe40_ctrl, statsAfNum,
+ vfe40_ctrl, statsBfNum,
addr);
process_stats = true;
} else {
@@ -6288,8 +6313,8 @@
axi40_do_tasklet, (unsigned long)axi_ctrl);
vfe40_ctrl->pdev = pdev;
- /*disable bayer stats by default*/
- vfe40_ctrl->ver_num.main = 0;
+ /*enable bayer stats by default*/
+ vfe40_ctrl->ver_num.main = 4;
return 0;
diff --git a/drivers/media/video/msm/vfe/msm_vfe40.h b/drivers/media/video/msm/vfe/msm_vfe40.h
index 8201d18..6363bfb 100644
--- a/drivers/media/video/msm/vfe/msm_vfe40.h
+++ b/drivers/media/video/msm/vfe/msm_vfe40.h
@@ -1035,13 +1035,16 @@
uint32_t simultaneous_sof_stat;
};
-#define statsAeNum 0
-#define statsAfNum 1
-#define statsAwbNum 2
-#define statsRsNum 3
-#define statsCsNum 4
-#define statsIhistNum 5
-#define statsSkinNum 6
+#define statsBeNum 0
+#define statsBgNum 1
+#define statsBfNum 2
+#define statsAwbNum 3
+#define statsRsNum 4
+#define statsCsNum 5
+#define statsIhistNum 6
+#define statsSkinNum 7
+
+#define VFE_STATS_BUS_REG_NUM 6
struct vfe_cmd_stats_ack {
uint32_t nextStatsBuf;
diff --git a/drivers/media/video/msm_vidc/msm_vidc_common.c b/drivers/media/video/msm_vidc/msm_vidc_common.c
index d89ab9f..4ff28d62 100644
--- a/drivers/media/video/msm_vidc/msm_vidc_common.c
+++ b/drivers/media/video/msm_vidc/msm_vidc_common.c
@@ -65,8 +65,8 @@
int i;
if (!load)
return 0;
- for (i = num_rows - 1; i > 1; i--) {
- if (load >= bus_table[i])
+ for (i = 0; i < num_rows; i++) {
+ if (load <= bus_table[i])
break;
}
dprintk(VIDC_DBG, "Required bus = %d\n", i);
diff --git a/drivers/media/video/msm_vidc/msm_vidc_debug.c b/drivers/media/video/msm_vidc/msm_vidc_debug.c
index 7368136..914c422 100644
--- a/drivers/media/video/msm_vidc/msm_vidc_debug.c
+++ b/drivers/media/video/msm_vidc/msm_vidc_debug.c
@@ -15,6 +15,7 @@
#define MAX_DBG_BUF_SIZE 4096
int msm_vidc_debug;
+int msm_fw_debug;
struct debug_buffer {
char ptr[MAX_DBG_BUF_SIZE];
@@ -89,6 +90,7 @@
goto failed_create_dir;
}
msm_vidc_debug = 0;
+ msm_fw_debug = 0;
snprintf(debugfs_name, MAX_DEBUGFS_NAME, "core%d", core->id);
dir = debugfs_create_dir(debugfs_name, parent);
if (!dir) {
@@ -105,6 +107,12 @@
goto failed_create_dir;
}
msm_vidc_debug = 0x3;
+ if (!debugfs_create_u32("fw_level", S_IRUGO | S_IWUSR,
+ parent, &msm_fw_debug)) {
+ dprintk(VIDC_ERR, "debugfs_create_file: fail\n");
+ goto failed_create_dir;
+ }
+ msm_fw_debug = 0x18;
failed_create_dir:
return dir;
}
diff --git a/drivers/media/video/msm_vidc/msm_vidc_debug.h b/drivers/media/video/msm_vidc/msm_vidc_debug.h
index b641953..1a51173 100644
--- a/drivers/media/video/msm_vidc/msm_vidc_debug.h
+++ b/drivers/media/video/msm_vidc/msm_vidc_debug.h
@@ -42,6 +42,8 @@
};
extern int msm_vidc_debug;
+extern int msm_fw_debug;
+
#define dprintk(__level, __fmt, arg...) \
do { \
if (msm_vidc_debug & __level) \
diff --git a/drivers/media/video/msm_vidc/vidc_hal.c b/drivers/media/video/msm_vidc/vidc_hal.c
index aa30644..89f0273 100644
--- a/drivers/media/video/msm_vidc/vidc_hal.c
+++ b/drivers/media/video/msm_vidc/vidc_hal.c
@@ -592,7 +592,7 @@
u32 ctrl_status = 0, count = 0, rc = 0;
int max_tries = 100;
write_register(device->hal_data->register_base_addr,
- VIDC_WRAPPER_INTR_MASK, 0, 0);
+ VIDC_WRAPPER_INTR_MASK, 0x8, 0);
write_register(device->hal_data->register_base_addr,
VIDC_CPU_CS_SCIACMDARG3, 1, 0);
while (!ctrl_status && count < max_tries) {
@@ -644,6 +644,24 @@
VIDC_VENUS0_WRAPPER_VBIF_REQ_PRIORITY, 0x5555556, 0);
}
+static int vidc_hal_sys_set_debug(struct hal_device *device, int debug)
+{
+ struct hfi_debug_config *hfi;
+ u8 packet[VIDC_IFACEQ_VAR_SMALL_PKT_SIZE];
+ struct hfi_cmd_sys_set_property_packet *pkt =
+ (struct hfi_cmd_sys_set_property_packet *) &packet;
+ pkt->size = sizeof(struct hfi_cmd_sys_set_property_packet) +
+ sizeof(struct hfi_debug_config) + sizeof(u32);
+ pkt->packet_type = HFI_CMD_SYS_SET_PROPERTY;
+ pkt->num_properties = 1;
+ pkt->rg_property_data[0] = HFI_PROPERTY_SYS_DEBUG_CONFIG;
+ hfi = (struct hfi_debug_config *) &pkt->rg_property_data[1];
+ hfi->debug_config = debug;
+ if (vidc_hal_iface_cmdq_write(device, pkt))
+ return -ENOTEMPTY;
+ return 0;
+}
+
int vidc_hal_core_init(void *device, int domain)
{
struct hfi_cmd_sys_init_packet pkt;
@@ -1444,17 +1462,6 @@
}
case HAL_CONFIG_VPE_DEINTERLACE:
break;
- case HAL_SYS_DEBUG_CONFIG:
- {
- struct hfi_debug_config *hfi;
- pkt->rg_property_data[0] = HFI_PROPERTY_SYS_DEBUG_CONFIG;
- hfi = (struct hfi_debug_config *) &pkt->rg_property_data[1];
- hfi->debug_config = ((struct hal_debug_config *)
- pdata)->debug_config;
- pkt->size = sizeof(struct hfi_cmd_sys_set_property_packet) +
- sizeof(struct hfi_debug_config);
- break;
- }
/* FOLLOWING PROPERTIES ARE NOT IMPLEMENTED IN CORE YET */
case HAL_CONFIG_BUFFER_REQUIREMENTS:
case HAL_CONFIG_PRIORITY:
@@ -1645,6 +1652,8 @@
pkt.session_codec = codec_type;
if (vidc_hal_iface_cmdq_write(dev, &pkt))
return NULL;
+ if (vidc_hal_sys_set_debug(dev, msm_fw_debug))
+ dprintk(VIDC_ERR, "Setting fw_debug msg ON failed");
return (void *) new_session;
}
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 a0dd93c..043ed73 100644
--- a/drivers/media/video/msm_vidc/vidc_hal_interrupt_handler.c
+++ b/drivers/media/video/msm_vidc/vidc_hal_interrupt_handler.c
@@ -703,7 +703,6 @@
struct hfi_msg_sys_session_end_done_packet *pkt)
{
struct msm_vidc_cb_cmd_done cmd_done;
- struct list_head *curr, *next;
struct hal_session *sess_close;
dprintk(VIDC_DBG, "RECEIVED:SESSION_END_DONE");
@@ -715,13 +714,11 @@
return;
}
- list_for_each_safe(curr, next, &device->sess_head) {
- sess_close = list_entry(curr, struct hal_session, list);
- dprintk(VIDC_INFO, "deleted the session: 0x%x",
- sess_close->session_id);
- list_del(&sess_close->list);
- kfree(sess_close);
- }
+ sess_close = (struct hal_session *)pkt->session_id;
+ dprintk(VIDC_INFO, "deleted the session: 0x%x",
+ sess_close->session_id);
+ list_del(&sess_close->list);
+ kfree(sess_close);
memset(&cmd_done, 0, sizeof(struct msm_vidc_cb_cmd_done));
cmd_done.device_id = device->device_id;
@@ -849,7 +846,12 @@
if (device) {
while (!vidc_hal_iface_msgq_read(device, packet)) {
hal_process_msg_packet(device,
- (struct vidc_hal_msg_pkt_hdr *) packet);
+ (struct vidc_hal_msg_pkt_hdr *) packet);
+ }
+ while (!vidc_hal_iface_dbgq_read(device, packet)) {
+ struct hfi_msg_sys_debug_packet *pkt =
+ (struct hfi_msg_sys_debug_packet *) packet;
+ dprintk(VIDC_FW, "FW-SAYS: %s", pkt->rg_msg_data);
}
} else {
dprintk(VIDC_ERR, "SPURIOUS_INTERRUPT");
diff --git a/drivers/media/video/vcap_v4l2.c b/drivers/media/video/vcap_v4l2.c
index c552e2d..7757b5c 100644
--- a/drivers/media/video/vcap_v4l2.c
+++ b/drivers/media/video/vcap_v4l2.c
@@ -21,13 +21,21 @@
#include <linux/memory_alloc.h>
#include <linux/ctype.h>
#include <linux/debugfs.h>
+#include <linux/regulator/consumer.h>
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/iommu.h>
#include <mach/board.h>
#include <mach/gpio.h>
#include <mach/irqs.h>
+#include <mach/clk.h>
+#include <mach/msm_bus.h>
+#include <mach/msm_bus_board.h>
+#include <mach/iommu.h>
+#include <mach/iommu_domains.h>
#include <media/videobuf2-msm-mem.h>
-
#include <media/videobuf2-vmalloc.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
@@ -35,16 +43,9 @@
#include <media/v4l2-fh.h>
#include <media/v4l2-common.h>
#include <media/v4l2-event.h>
-#include <linux/regulator/consumer.h>
-#include <mach/clk.h>
-#include <linux/clk.h>
-#include <linux/interrupt.h>
-#include <mach/msm_bus.h>
-#include <mach/msm_bus_board.h>
-#include <mach/iommu_domains.h>
-
#include <media/vcap_v4l2.h>
#include <media/vcap_fmt.h>
+
#include "vcap_vc.h"
#include "vcap_vp.h"
@@ -79,7 +80,7 @@
};
#endif
-int vcap_reg_powerup(struct vcap_dev *dev)
+static int vcap_reg_powerup(struct vcap_dev *dev)
{
dev->fs_vcap = regulator_get(NULL, "fs_vcap");
if (IS_ERR(dev->fs_vcap)) {
@@ -95,7 +96,7 @@
return 0;
}
-void vcap_reg_powerdown(struct vcap_dev *dev)
+static void vcap_reg_powerdown(struct vcap_dev *dev)
{
if (dev->fs_vcap == NULL)
return;
@@ -105,7 +106,7 @@
return;
}
-int config_gpios(int on, struct vcap_platform_data *pdata)
+static int vcap_config_gpios(int on, struct vcap_platform_data *pdata)
{
int i, ret;
int num_gpios = pdata->num_gpios;
@@ -140,7 +141,7 @@
return -EINVAL;
}
-int vcap_clk_powerup(struct vcap_dev *dev, struct device *ddev,
+static int vcap_clk_powerup(struct vcap_dev *dev, struct device *ddev,
unsigned long rate)
{
int ret = 0;
@@ -228,7 +229,7 @@
return -EINVAL;
}
-void vcap_clk_powerdown(struct vcap_dev *dev)
+static void vcap_clk_powerdown(struct vcap_dev *dev)
{
if (dev->vcap_p_clk != NULL) {
clk_disable(dev->vcap_p_clk);
@@ -254,7 +255,7 @@
dev->dbg_p.clk_rate = 0;
}
-int vcap_get_bus_client_handle(struct vcap_dev *dev)
+static int vcap_get_bus_client_handle(struct vcap_dev *dev)
{
struct msm_bus_scale_pdata *vcap_axi_client_pdata =
dev->vcap_pdata->bus_client_pdata;
@@ -264,7 +265,7 @@
return 0;
}
-int vcap_enable(struct vcap_dev *dev, struct device *ddev,
+static int vcap_enable(struct vcap_dev *dev, struct device *ddev,
unsigned long rate)
{
int rc;
@@ -279,14 +280,24 @@
rc = vcap_get_bus_client_handle(dev);
if (rc < 0)
goto bus_r_failed;
- rc = config_gpios(1, dev->vcap_pdata);
+ rc = vcap_config_gpios(1, dev->vcap_pdata);
if (rc < 0)
goto gpio_failed;
+ rc = iommu_attach_device(dev->iommu_vcap_domain, dev->vc_iommu_ctx);
+ if (rc < 0)
+ goto vc_iommu_attach_failed;
+ rc = iommu_attach_device(dev->iommu_vcap_domain, dev->vp_iommu_ctx);
+ if (rc < 0)
+ goto vp_iommu_attach_failed;
writel_relaxed(0x00030003, VCAP_OFFSET(0xD78));
writel_relaxed(0x00030003, VCAP_OFFSET(0xD7C));
pr_debug("Success Exit %s", __func__);
return 0;
+vp_iommu_attach_failed:
+ iommu_detach_device(dev->iommu_vcap_domain, dev->vc_iommu_ctx);
+vc_iommu_attach_failed:
+ vcap_config_gpios(0, dev->vcap_pdata);
gpio_failed:
msm_bus_scale_unregister_client(dev->bus_client_handle);
dev->bus_client_handle = 0;
@@ -298,10 +309,13 @@
return rc;
}
-int vcap_disable(struct vcap_dev *dev)
+static int vcap_disable(struct vcap_dev *dev)
{
pr_debug("Enter %s", __func__);
- config_gpios(0, dev->vcap_pdata);
+ iommu_detach_device(dev->iommu_vcap_domain, dev->vp_iommu_ctx);
+ iommu_detach_device(dev->iommu_vcap_domain, dev->vc_iommu_ctx);
+
+ vcap_config_gpios(0, dev->vcap_pdata);
msm_bus_scale_unregister_client(dev->bus_client_handle);
dev->bus_client_handle = 0;
@@ -311,6 +325,22 @@
return 0;
}
+static int vcap_register_domain(void)
+{
+ struct msm_iova_partition vcap_partition = {
+ .start = 0,
+ .size = SZ_2G,
+ };
+ struct msm_iova_layout vcap_layout = {
+ .partitions = &vcap_partition,
+ .npartitions = 1,
+ .client_name = "vcap",
+ .domain_flags = 0,
+ };
+
+ return msm_register_domain(&vcap_layout);
+}
+
enum vcap_op_mode determine_mode(struct vcap_client_data *cd)
{
if (cd->set_cap == 1 && cd->set_vp_o == 0 &&
@@ -476,8 +506,9 @@
buf->ion_handle = NULL;
return -ENOMEM;
}
- rc = ion_phys(dev->ion_client, buf->ion_handle,
- &buf->paddr, (size_t *)&len);
+ rc = ion_map_iommu(dev->ion_client, buf->ion_handle,
+ dev->domain_num, 0, SZ_4K, 0, &buf->paddr, &len,
+ 0, 0);
if (rc < 0) {
pr_err("%s: Could not get phys addr\n", __func__);
ion_free(dev->ion_client, buf->ion_handle);
@@ -500,6 +531,7 @@
return;
}
buf->paddr = 0;
+ ion_unmap_iommu(dev->ion_client, buf->ion_handle, dev->domain_num, 0);
ion_free(dev->ion_client, buf->ion_handle);
buf->ion_handle = NULL;
return;
@@ -681,6 +713,7 @@
struct vb2_buffer *vb;
pr_debug("VP IN stop streaming\n");
+ vp_stop_capture(c_data);
while (!list_empty(&c_data->vp_action.in_active)) {
struct vcap_buffer *buf;
@@ -2244,6 +2277,34 @@
if (ret)
goto free_resource;
+ dev->vc_iommu_ctx = msm_iommu_get_ctx("vcap_vc");
+ if (!dev->vc_iommu_ctx) {
+ pr_err("%s: No iommu vc context found\n", __func__);
+ ret = -ENODEV;
+ goto free_resource;
+ }
+
+ dev->vp_iommu_ctx = msm_iommu_get_ctx("vcap_vp");
+ if (!dev->vp_iommu_ctx) {
+ pr_err("%s: No iommu vp context found\n", __func__);
+ ret = -ENODEV;
+ goto free_resource;
+ }
+
+ dev->domain_num = vcap_register_domain();
+ if (dev->domain_num < 0) {
+ pr_err("%s: VCAP iommu domain register failed\n", __func__);
+ ret = -ENODEV;
+ goto free_resource;
+ }
+
+ dev->iommu_vcap_domain = msm_get_iommu_domain(dev->domain_num);
+ if (!dev->iommu_vcap_domain) {
+ pr_err("%s: No iommu vcap domain found\n", __func__);
+ ret = -ENODEV;
+ goto free_resource;
+ }
+
ret = vcap_enable(dev, &pdev->dev, 54860000);
if (ret)
goto unreg_dev;
diff --git a/drivers/media/video/vcap_vp.c b/drivers/media/video/vcap_vp.c
index c7de465..57813f5 100644
--- a/drivers/media/video/vcap_vp.c
+++ b/drivers/media/video/vcap_vp.c
@@ -15,14 +15,16 @@
#include <linux/sched.h>
#include <linux/kthread.h>
#include <linux/freezer.h>
-#include <mach/camera.h>
-#include <linux/io.h>
-#include <mach/clk.h>
#include <linux/clk.h>
+#include <linux/io.h>
+
+#include <mach/camera.h>
+#include <mach/clk.h>
#include <media/v4l2-event.h>
#include <media/vcap_v4l2.h>
#include <media/vcap_fmt.h>
+
#include "vcap_vp.h"
void config_nr_buffer(struct vcap_client_data *c_data,
@@ -393,6 +395,7 @@
msecs_to_jiffies(50));
if (rc == 0 && atomic_read(&dev->vp_enabled) == 1) {
/* This should not happen, if it does hw is stuck */
+ disable_irq_nosync(dev->vpirq->start);
pr_err("%s: VP Timeout and VP still running\n",
__func__);
}
@@ -463,9 +466,8 @@
int rc;
struct vcap_dev *dev = c_data->dev;
struct ion_handle *handle = NULL;
- unsigned long paddr, ionflag = 0;
+ unsigned long paddr, len, ionflag = 0;
void *vaddr;
- size_t len;
size_t size = ((c_data->vp_out_fmt.width + 63) >> 6) *
((c_data->vp_out_fmt.height + 7) >> 3) * 16;
@@ -480,12 +482,6 @@
pr_err("%s: ion_alloc failed\n", __func__);
return -ENOMEM;
}
- rc = ion_phys(dev->ion_client, handle, &paddr, &len);
- if (rc < 0) {
- pr_err("%s: ion_phys failed\n", __func__);
- ion_free(dev->ion_client, handle);
- return rc;
- }
rc = ion_handle_get_flags(dev->ion_client, handle, &ionflag);
if (rc) {
@@ -503,10 +499,20 @@
}
memset(vaddr, 0, size);
+ ion_unmap_kernel(dev->ion_client, handle);
+
+ rc = ion_map_iommu(dev->ion_client, handle,
+ dev->domain_num, 0, SZ_4K, 0, &paddr, &len,
+ 0, 0);
+ if (rc < 0) {
+ pr_err("%s: map_iommu failed\n", __func__);
+ ion_free(dev->ion_client, handle);
+ return rc;
+ }
+
c_data->vp_action.motionHandle = handle;
vaddr = NULL;
- ion_unmap_kernel(dev->ion_client, handle);
writel_iowmb(paddr, VCAP_VP_MOTION_EST_ADDR);
return 0;
@@ -521,6 +527,8 @@
}
writel_iowmb(0x00000000, VCAP_VP_MOTION_EST_ADDR);
+ ion_unmap_iommu(dev->ion_client, c_data->vp_action.motionHandle,
+ dev->domain_num, 0);
ion_free(dev->ion_client, c_data->vp_action.motionHandle);
c_data->vp_action.motionHandle = NULL;
return;
@@ -530,8 +538,8 @@
{
struct vcap_dev *dev = c_data->dev;
struct ion_handle *handle = NULL;
- size_t frame_size, tot_size, len;
- unsigned long paddr;
+ size_t frame_size, tot_size;
+ unsigned long paddr, len;
int rc;
if (c_data->vp_action.bufNR.nr_handle) {
@@ -552,9 +560,11 @@
return -ENOMEM;
}
- rc = ion_phys(dev->ion_client, handle, &paddr, &len);
+ rc = ion_map_iommu(dev->ion_client, handle,
+ dev->domain_num, 0, SZ_4K, 0, &paddr, &len,
+ 0, 0);
if (rc < 0) {
- pr_err("%s: ion_phys failed\n", __func__);
+ pr_err("%s: map_iommu failed\n", __func__);
ion_free(dev->ion_client, handle);
return rc;
}
@@ -588,6 +598,7 @@
rc &= !(0x0FF00001);
writel_relaxed(rc, VCAP_VP_NR_CONFIG2);
+ ion_unmap_iommu(dev->ion_client, buf->nr_handle, dev->domain_num, 0);
ion_free(dev->ion_client, buf->nr_handle);
buf->nr_handle = NULL;
buf->paddr = 0;
@@ -669,8 +680,7 @@
struct vcap_dev *dev = c_data->dev;
unsigned int width, height;
struct ion_handle *handle = NULL;
- unsigned long paddr;
- size_t len;
+ unsigned long paddr, len;
uint32_t reg;
int rc = 0;
@@ -682,9 +692,11 @@
return -ENOMEM;
}
- rc = ion_phys(dev->ion_client, handle, &paddr, &len);
+ rc = ion_map_iommu(dev->ion_client, handle,
+ dev->domain_num, 0, SZ_4K, 0, &paddr, &len,
+ 0, 0);
if (rc < 0) {
- pr_err("%s: ion_phys failed\n", __func__);
+ pr_err("%s: map_iommu failed\n", __func__);
ion_free(dev->ion_client, handle);
return rc;
}
@@ -732,6 +744,7 @@
c_data->vp_out_fmt.width = width;
c_data->vp_out_fmt.height = height;
+ ion_unmap_iommu(dev->ion_client, handle, dev->domain_num, 0);
ion_free(dev->ion_client, handle);
pr_debug("%s: Exit VP dummy event\n", __func__);
diff --git a/drivers/misc/tsif.c b/drivers/misc/tsif.c
index 1ff4468..b7b1203 100644
--- a/drivers/misc/tsif.c
+++ b/drivers/misc/tsif.c
@@ -140,6 +140,10 @@
unsigned int irq;
int mode;
u32 time_limit;
+ int clock_inverse;
+ int data_inverse;
+ int sync_inverse;
+ int enable_inverse;
enum tsif_state state;
struct wake_lock wake_lock;
/* clocks */
@@ -358,6 +362,19 @@
TSIF_STS_CTL_EN_TIME_LIM |
TSIF_STS_CTL_EN_TCR |
TSIF_STS_CTL_EN_DM;
+
+ if (tsif_device->clock_inverse)
+ ctl |= TSIF_STS_CTL_INV_CLOCK;
+
+ if (tsif_device->data_inverse)
+ ctl |= TSIF_STS_CTL_INV_DATA;
+
+ if (tsif_device->sync_inverse)
+ ctl |= TSIF_STS_CTL_INV_SYNC;
+
+ if (tsif_device->enable_inverse)
+ ctl |= TSIF_STS_CTL_INV_ENABLE;
+
dev_info(&tsif_device->pdev->dev, "%s\n", __func__);
switch (tsif_device->mode) {
case 1: /* mode 1 */
@@ -805,6 +822,10 @@
"Client = %p\n"
"Pkt/Buf = %d\n"
"Pkt/chunk = %d\n"
+ "Clock inv = %d\n"
+ "Data inv = %d\n"
+ "Sync inv = %d\n"
+ "Enable inv = %d\n"
"--statistics--\n"
"Rx chunks = %d\n"
"Overflow = %d\n"
@@ -827,6 +848,10 @@
tsif_device->client_data,
TSIF_PKTS_IN_BUF,
TSIF_PKTS_IN_CHUNK,
+ tsif_device->clock_inverse,
+ tsif_device->data_inverse,
+ tsif_device->sync_inverse,
+ tsif_device->enable_inverse,
tsif_device->stat_rx,
tsif_device->stat_overflow,
tsif_device->stat_lost_sync,
@@ -950,11 +975,120 @@
static DEVICE_ATTR(buf_config, S_IRUGO | S_IWUSR,
show_buf_config, set_buf_config);
+static ssize_t show_clk_inverse(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct msm_tsif_device *tsif_device = dev_get_drvdata(dev);
+ return snprintf(buf, PAGE_SIZE, "%d\n", tsif_device->clock_inverse);
+}
+
+static ssize_t set_clk_inverse(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct msm_tsif_device *tsif_device = dev_get_drvdata(dev);
+ int value;
+ int rc;
+ if (1 != sscanf(buf, "%d", &value)) {
+ dev_err(&tsif_device->pdev->dev,
+ "Failed to parse integer: <%s>\n", buf);
+ return -EINVAL;
+ }
+ rc = tsif_set_clk_inverse(tsif_device, value);
+ if (!rc)
+ rc = count;
+ return rc;
+}
+static DEVICE_ATTR(clk_inverse, S_IRUGO | S_IWUSR,
+ show_clk_inverse, set_clk_inverse);
+
+static ssize_t show_data_inverse(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct msm_tsif_device *tsif_device = dev_get_drvdata(dev);
+ return snprintf(buf, PAGE_SIZE, "%d\n", tsif_device->data_inverse);
+}
+
+static ssize_t set_data_inverse(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct msm_tsif_device *tsif_device = dev_get_drvdata(dev);
+ int value;
+ int rc;
+ if (1 != sscanf(buf, "%d", &value)) {
+ dev_err(&tsif_device->pdev->dev,
+ "Failed to parse integer: <%s>\n", buf);
+ return -EINVAL;
+ }
+ rc = tsif_set_data_inverse(tsif_device, value);
+ if (!rc)
+ rc = count;
+ return rc;
+}
+static DEVICE_ATTR(data_inverse, S_IRUGO | S_IWUSR,
+ show_data_inverse, set_data_inverse);
+
+static ssize_t show_sync_inverse(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct msm_tsif_device *tsif_device = dev_get_drvdata(dev);
+ return snprintf(buf, PAGE_SIZE, "%d\n", tsif_device->sync_inverse);
+}
+
+static ssize_t set_sync_inverse(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct msm_tsif_device *tsif_device = dev_get_drvdata(dev);
+ int value;
+ int rc;
+ if (1 != sscanf(buf, "%d", &value)) {
+ dev_err(&tsif_device->pdev->dev,
+ "Failed to parse integer: <%s>\n", buf);
+ return -EINVAL;
+ }
+ rc = tsif_set_sync_inverse(tsif_device, value);
+ if (!rc)
+ rc = count;
+ return rc;
+}
+static DEVICE_ATTR(sync_inverse, S_IRUGO | S_IWUSR,
+ show_sync_inverse, set_sync_inverse);
+
+static ssize_t show_enable_inverse(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct msm_tsif_device *tsif_device = dev_get_drvdata(dev);
+ return snprintf(buf, PAGE_SIZE, "%d\n", tsif_device->enable_inverse);
+}
+
+static ssize_t set_enable_inverse(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct msm_tsif_device *tsif_device = dev_get_drvdata(dev);
+ int value;
+ int rc;
+ if (1 != sscanf(buf, "%d", &value)) {
+ dev_err(&tsif_device->pdev->dev,
+ "Failed to parse integer: <%s>\n", buf);
+ return -EINVAL;
+ }
+ rc = tsif_set_enable_inverse(tsif_device, value);
+ if (!rc)
+ rc = count;
+ return rc;
+}
+static DEVICE_ATTR(enable_inverse, S_IRUGO | S_IWUSR,
+ show_enable_inverse, set_enable_inverse);
+
+
static struct attribute *dev_attrs[] = {
&dev_attr_stats.attr,
&dev_attr_mode.attr,
&dev_attr_time_limit.attr,
&dev_attr_buf_config.attr,
+ &dev_attr_clk_inverse.attr,
+ &dev_attr_data_inverse.attr,
+ &dev_attr_sync_inverse.attr,
+ &dev_attr_enable_inverse.attr,
NULL,
};
static struct attribute_group dev_attr_grp = {
@@ -1287,6 +1421,10 @@
tsif_device->pdev = pdev;
platform_set_drvdata(pdev, tsif_device);
tsif_device->mode = 1;
+ tsif_device->clock_inverse = 0;
+ tsif_device->data_inverse = 0;
+ tsif_device->sync_inverse = 0;
+ tsif_device->enable_inverse = 0;
tsif_device->pkts_per_chunk = TSIF_PKTS_IN_CHUNK_DEFAULT;
tsif_device->chunks_per_buf = TSIF_CHUNKS_IN_BUF_DEFAULT;
tasklet_init(&tsif_device->dma_refill, tsif_dma_refill,
@@ -1534,6 +1672,78 @@
}
EXPORT_SYMBOL(tsif_set_buf_config);
+int tsif_set_clk_inverse(void *cookie, int value)
+{
+ struct msm_tsif_device *tsif_device = cookie;
+ if (tsif_device->state != tsif_state_stopped) {
+ dev_err(&tsif_device->pdev->dev,
+ "Can't change clock inverse while device is active\n");
+ return -EBUSY;
+ }
+ if ((value != 0) && (value != 1)) {
+ dev_err(&tsif_device->pdev->dev,
+ "Invalid parameter, either 0 or 1: %#x\n", value);
+ return -EINVAL;
+ }
+ tsif_device->clock_inverse = value;
+ return 0;
+}
+EXPORT_SYMBOL(tsif_set_clk_inverse);
+
+int tsif_set_data_inverse(void *cookie, int value)
+{
+ struct msm_tsif_device *tsif_device = cookie;
+ if (tsif_device->state != tsif_state_stopped) {
+ dev_err(&tsif_device->pdev->dev,
+ "Can't change data inverse while device is active\n");
+ return -EBUSY;
+ }
+ if ((value != 0) && (value != 1)) {
+ dev_err(&tsif_device->pdev->dev,
+ "Invalid parameter, either 0 or 1: %#x\n", value);
+ return -EINVAL;
+ }
+ tsif_device->data_inverse = value;
+ return 0;
+}
+EXPORT_SYMBOL(tsif_set_data_inverse);
+
+int tsif_set_sync_inverse(void *cookie, int value)
+{
+ struct msm_tsif_device *tsif_device = cookie;
+ if (tsif_device->state != tsif_state_stopped) {
+ dev_err(&tsif_device->pdev->dev,
+ "Can't change sync inverse while device is active\n");
+ return -EBUSY;
+ }
+ if ((value != 0) && (value != 1)) {
+ dev_err(&tsif_device->pdev->dev,
+ "Invalid parameter, either 0 or 1: %#x\n", value);
+ return -EINVAL;
+ }
+ tsif_device->sync_inverse = value;
+ return 0;
+}
+EXPORT_SYMBOL(tsif_set_sync_inverse);
+
+int tsif_set_enable_inverse(void *cookie, int value)
+{
+ struct msm_tsif_device *tsif_device = cookie;
+ if (tsif_device->state != tsif_state_stopped) {
+ dev_err(&tsif_device->pdev->dev,
+ "Can't change enable inverse while device is active\n");
+ return -EBUSY;
+ }
+ if ((value != 0) && (value != 1)) {
+ dev_err(&tsif_device->pdev->dev,
+ "Invalid parameter, either 0 or 1: %#x\n", value);
+ return -EINVAL;
+ }
+ tsif_device->enable_inverse = value;
+ return 0;
+}
+EXPORT_SYMBOL(tsif_set_enable_inverse);
+
void tsif_get_state(void *cookie, int *ri, int *wi, enum tsif_state *state)
{
struct msm_tsif_device *tsif_device = cookie;
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index 7387d9a..254672f 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -1392,7 +1392,10 @@
}
mqrq->mmc_active.mrq = &brq->mrq;
- mqrq->mmc_active.err_check = mmc_blk_err_check;
+ if (mq->err_check_fn)
+ mqrq->mmc_active.err_check = mq->err_check_fn;
+ else
+ mqrq->mmc_active.err_check = mmc_blk_err_check;
mmc_queue_bounce_pre(mqrq);
}
diff --git a/drivers/mmc/card/mmc_block_test.c b/drivers/mmc/card/mmc_block_test.c
index bdda8d5..7d3ac83 100644
--- a/drivers/mmc/card/mmc_block_test.c
+++ b/drivers/mmc/card/mmc_block_test.c
@@ -21,6 +21,7 @@
#include <linux/delay.h>
#include <linux/test-iosched.h>
#include "queue.h"
+#include <linux/mmc/mmc.h>
#define MODULE_NAME "mmc_block_test"
#define TEST_MAX_SECTOR_RANGE (600*1024*1024) /* 600 MB */
@@ -32,11 +33,24 @@
#define PACKED_HDR_RW_MASK 0x0000FF00
#define PACKED_HDR_NUM_REQS_MASK 0x00FF0000
#define PACKED_HDR_BITS_16_TO_29_SET 0x3FFF0000
+#define SECTOR_SIZE 512
+#define NUM_OF_SECTORS_PER_BIO ((BIO_U32_SIZE * 4) / SECTOR_SIZE)
+#define BIO_TO_SECTOR(x) (x * NUM_OF_SECTORS_PER_BIO)
#define test_pr_debug(fmt, args...) pr_debug("%s: "fmt"\n", MODULE_NAME, args)
#define test_pr_info(fmt, args...) pr_info("%s: "fmt"\n", MODULE_NAME, args)
#define test_pr_err(fmt, args...) pr_err("%s: "fmt"\n", MODULE_NAME, args)
+#define SANITIZE_TEST_TIMEOUT 240000
+#define TEST_REQUEST_NUM_OF_BIOS 3
+
+
+#define CHECK_BKOPS_STATS(stats, exp_bkops, exp_hpi, exp_suspend) \
+ ((stats.bkops != exp_bkops) || \
+ (stats.hpi != exp_hpi) || \
+ (stats.suspend != exp_suspend))
+#define BKOPS_TEST_TIMEOUT 60000
+
enum is_random {
NON_RANDOM_TEST,
RANDOM_TEST,
@@ -101,6 +115,18 @@
TEST_PACK_MIX_PACKED_NO_PACKED_PACKED,
TEST_PACK_MIX_NO_PACKED_PACKED_NO_PACKED,
PACKING_CONTROL_MAX_TESTCASE = TEST_PACK_MIX_NO_PACKED_PACKED_NO_PACKED,
+
+ TEST_WRITE_DISCARD_SANITIZE_READ,
+
+ /* Start of bkops test group */
+ BKOPS_MIN_TESTCASE,
+ BKOPS_DELAYED_WORK_LEVEL_1 = BKOPS_MIN_TESTCASE,
+ BKOPS_DELAYED_WORK_LEVEL_1_HPI,
+ BKOPS_CANCEL_DELAYED_WORK,
+ BKOPS_URGENT_LEVEL_2,
+ BKOPS_URGENT_LEVEL_2_TWO_REQS,
+ BKOPS_URGENT_LEVEL_3,
+ BKOPS_MAX_TESTCASE = BKOPS_URGENT_LEVEL_3,
};
enum mmc_block_test_group {
@@ -110,6 +136,14 @@
TEST_ERR_CHECK_GROUP,
TEST_SEND_INVALID_GROUP,
TEST_PACKING_CONTROL_GROUP,
+ TEST_BKOPS_GROUP,
+};
+
+enum bkops_test_stages {
+ BKOPS_STAGE_1,
+ BKOPS_STAGE_2,
+ BKOPS_STAGE_3,
+ BKOPS_STAGE_4,
};
struct mmc_block_test_debug {
@@ -118,6 +152,8 @@
struct dentry *send_invalid_packed_test;
struct dentry *random_test_seed;
struct dentry *packing_control_test;
+ struct dentry *discard_sanitize_test;
+ struct dentry *bkops_test;
};
struct mmc_block_test_data {
@@ -149,6 +185,10 @@
struct test_info test_info;
/* mmc block device test */
struct blk_dev_test_type bdt;
+ /* Current BKOPs test stage */
+ enum bkops_test_stages bkops_stage;
+ /* A wait queue for BKOPs tests */
+ wait_queue_head_t bkops_wait_q;
};
static struct mmc_block_test_data *mbtd;
@@ -308,6 +348,7 @@
struct mmc_queue *mq;
int max_packed_reqs;
int ret = 0;
+ struct mmc_blk_request *brq;
if (req_q)
mq = req_q->queuedata;
@@ -329,6 +370,7 @@
mmc_hostname(card->host));
return 0;
}
+ brq = &mq_rq->brq;
switch (mbtd->test_info.testcase) {
case TEST_RET_ABORT:
@@ -397,6 +439,16 @@
test_pr_info("%s: return data err", __func__);
ret = MMC_BLK_DATA_ERR;
break;
+ case BKOPS_URGENT_LEVEL_2:
+ case BKOPS_URGENT_LEVEL_3:
+ case BKOPS_URGENT_LEVEL_2_TWO_REQS:
+ if (mbtd->err_check_counter++ == 0) {
+ test_pr_info("%s: simulate an exception from the card",
+ __func__);
+ brq->cmd.resp[0] |= R1_EXCEPTION_EVENT;
+ }
+ mq->err_check_fn = NULL;
+ break;
default:
test_pr_err("%s: unexpected testcase %d",
__func__, mbtd->test_info.testcase);
@@ -496,6 +548,20 @@
return "\nTest packing control - mix: pack -> no pack -> pack";
case TEST_PACK_MIX_NO_PACKED_PACKED_NO_PACKED:
return "\nTest packing control - mix: no pack->pack->no pack";
+ case TEST_WRITE_DISCARD_SANITIZE_READ:
+ return "\nTest write, discard, sanitize";
+ case BKOPS_DELAYED_WORK_LEVEL_1:
+ return "\nTest delayed work BKOPS level 1";
+ case BKOPS_DELAYED_WORK_LEVEL_1_HPI:
+ return "\nTest delayed work BKOPS level 1 with HPI";
+ case BKOPS_CANCEL_DELAYED_WORK:
+ return "\nTest cancel delayed BKOPS work";
+ case BKOPS_URGENT_LEVEL_2:
+ return "\nTest urgent BKOPS level 2";
+ case BKOPS_URGENT_LEVEL_2_TWO_REQS:
+ return "\nTest urgent BKOPS level 2, followed by a request";
+ case BKOPS_URGENT_LEVEL_3:
+ return "\nTest urgent BKOPS level 3";
default:
return "Unknown testcase";
}
@@ -1382,6 +1448,446 @@
return 0;
}
+static void pseudo_rnd_sector_and_size(unsigned int *seed,
+ unsigned int min_start_sector,
+ unsigned int *start_sector,
+ unsigned int *num_of_bios)
+{
+ unsigned int max_sec = min_start_sector + TEST_MAX_SECTOR_RANGE;
+ do {
+ *start_sector = pseudo_random_seed(seed,
+ 1, max_sec);
+ *num_of_bios = pseudo_random_seed(seed,
+ 1, TEST_MAX_BIOS_PER_REQ);
+ if (!(*num_of_bios))
+ *num_of_bios = 1;
+ } while ((*start_sector < min_start_sector) ||
+ (*start_sector + (*num_of_bios * BIO_U32_SIZE * 4)) > max_sec);
+}
+
+/* sanitize test functions */
+static int prepare_write_discard_sanitize_read(struct test_data *td)
+{
+ unsigned int start_sector;
+ unsigned int num_of_bios = 0;
+ static unsigned int total_bios;
+ unsigned int *num_bios_seed;
+ int i = 0;
+
+ if (mbtd->random_test_seed == 0) {
+ mbtd->random_test_seed =
+ (unsigned int)(get_jiffies_64() & 0xFFFF);
+ test_pr_info("%s: got seed from jiffies %d",
+ __func__, mbtd->random_test_seed);
+ }
+ num_bios_seed = &mbtd->random_test_seed;
+
+ do {
+ pseudo_rnd_sector_and_size(num_bios_seed, td->start_sector,
+ &start_sector, &num_of_bios);
+
+ /* DISCARD */
+ total_bios += num_of_bios;
+ test_pr_info("%s: discard req: id=%d, startSec=%d, NumBios=%d",
+ __func__, td->unique_next_req_id, start_sector,
+ num_of_bios);
+ test_iosched_add_unique_test_req(0, REQ_UNIQUE_DISCARD,
+ start_sector, BIO_TO_SECTOR(num_of_bios),
+ NULL);
+
+ } while (++i < (BLKDEV_MAX_RQ-10));
+
+ test_pr_info("%s: total discard bios = %d", __func__, total_bios);
+
+ test_pr_info("%s: add sanitize req", __func__);
+ test_iosched_add_unique_test_req(0, REQ_UNIQUE_SANITIZE, 0, 0, NULL);
+
+ return 0;
+}
+
+/*
+ * Post test operations for BKOPs test
+ * Disable the BKOPs statistics and clear the feature flags
+ */
+static int bkops_post_test(struct test_data *td)
+{
+ struct request_queue *q = td->req_q;
+ struct mmc_queue *mq = (struct mmc_queue *)q->queuedata;
+ struct mmc_card *card = mq->card;
+
+ mmc_card_clr_doing_bkops(mq->card);
+ card->ext_csd.raw_bkops_status = 0;
+
+ spin_lock(&card->bkops_info.bkops_stats.lock);
+ card->bkops_info.bkops_stats.enabled = false;
+ spin_unlock(&card->bkops_info.bkops_stats.lock);
+
+ return 0;
+}
+
+/*
+ * Verify the BKOPs statsistics
+ */
+static int check_bkops_result(struct test_data *td)
+{
+ struct request_queue *q = td->req_q;
+ struct mmc_queue *mq = (struct mmc_queue *)q->queuedata;
+ struct mmc_card *card = mq->card;
+ struct mmc_bkops_stats *bkops_stat;
+
+ if (!card)
+ goto fail;
+
+ bkops_stat = &card->bkops_info.bkops_stats;
+
+ test_pr_info("%s: Test results: bkops:(%d,%d,%d) hpi:%d, suspend:%d",
+ __func__,
+ bkops_stat->bkops_level[BKOPS_SEVERITY_1_INDEX],
+ bkops_stat->bkops_level[BKOPS_SEVERITY_2_INDEX],
+ bkops_stat->bkops_level[BKOPS_SEVERITY_3_INDEX],
+ bkops_stat->hpi,
+ bkops_stat->suspend);
+
+ switch (mbtd->test_info.testcase) {
+ case BKOPS_DELAYED_WORK_LEVEL_1:
+ if ((bkops_stat->bkops_level[BKOPS_SEVERITY_1_INDEX] == 1) &&
+ (bkops_stat->suspend == 1) &&
+ (bkops_stat->hpi == 0))
+ goto exit;
+ else
+ goto fail;
+ break;
+ case BKOPS_DELAYED_WORK_LEVEL_1_HPI:
+ if ((bkops_stat->bkops_level[BKOPS_SEVERITY_1_INDEX] == 1) &&
+ (bkops_stat->suspend == 0) &&
+ (bkops_stat->hpi == 1))
+ goto exit;
+ else
+ goto fail;
+ break;
+ case BKOPS_CANCEL_DELAYED_WORK:
+ if ((bkops_stat->bkops_level[BKOPS_SEVERITY_1_INDEX] == 0) &&
+ (bkops_stat->bkops_level[BKOPS_SEVERITY_2_INDEX] == 0) &&
+ (bkops_stat->bkops_level[BKOPS_SEVERITY_3_INDEX] == 0) &&
+ (bkops_stat->suspend == 0) &&
+ (bkops_stat->hpi == 0))
+ goto exit;
+ else
+ goto fail;
+ case BKOPS_URGENT_LEVEL_2:
+ case BKOPS_URGENT_LEVEL_2_TWO_REQS:
+ if ((bkops_stat->bkops_level[BKOPS_SEVERITY_2_INDEX] == 1) &&
+ (bkops_stat->suspend == 0) &&
+ (bkops_stat->hpi == 0))
+ goto exit;
+ else
+ goto fail;
+ case BKOPS_URGENT_LEVEL_3:
+ if ((bkops_stat->bkops_level[BKOPS_SEVERITY_3_INDEX] == 1) &&
+ (bkops_stat->suspend == 0) &&
+ (bkops_stat->hpi == 0))
+ goto exit;
+ else
+ goto fail;
+ default:
+ return -EINVAL;
+ }
+
+exit:
+ return 0;
+fail:
+ if (td->fs_wr_reqs_during_test) {
+ test_pr_info("%s: wr reqs during test, cancel the round",
+ __func__);
+ test_iosched_set_ignore_round(true);
+ return 0;
+ }
+
+ test_pr_info("%s: BKOPs statistics are not as expected, test failed",
+ __func__);
+ return -EINVAL;
+}
+
+static void bkops_end_io_final_fn(struct request *rq, int err)
+{
+ struct test_request *test_rq =
+ (struct test_request *)rq->elv.priv[0];
+ BUG_ON(!test_rq);
+
+ test_rq->req_completed = 1;
+ test_rq->req_result = err;
+
+ test_pr_info("%s: request %d completed, err=%d",
+ __func__, test_rq->req_id, err);
+
+ mbtd->bkops_stage = BKOPS_STAGE_4;
+ wake_up(&mbtd->bkops_wait_q);
+}
+
+static void bkops_end_io_fn(struct request *rq, int err)
+{
+ struct test_request *test_rq =
+ (struct test_request *)rq->elv.priv[0];
+ BUG_ON(!test_rq);
+
+ test_rq->req_completed = 1;
+ test_rq->req_result = err;
+
+ test_pr_info("%s: request %d completed, err=%d",
+ __func__, test_rq->req_id, err);
+ mbtd->bkops_stage = BKOPS_STAGE_2;
+ wake_up(&mbtd->bkops_wait_q);
+
+}
+
+static int prepare_bkops(struct test_data *td)
+{
+ int ret = 0;
+ struct request_queue *q = td->req_q;
+ struct mmc_queue *mq = (struct mmc_queue *)q->queuedata;
+ struct mmc_card *card = mq->card;
+ struct mmc_bkops_stats *bkops_stat;
+
+ if (!card)
+ return -EINVAL;
+
+ bkops_stat = &card->bkops_info.bkops_stats;
+
+ if (!card->ext_csd.bkops_en) {
+ test_pr_err("%s: BKOPS is not enabled by card or host)",
+ __func__);
+ return -ENOTSUPP;
+ }
+ if (mmc_card_doing_bkops(card)) {
+ test_pr_err("%s: BKOPS in progress, try later", __func__);
+ return -EAGAIN;
+ }
+
+ mmc_blk_init_bkops_statistics(card);
+
+ if ((mbtd->test_info.testcase == BKOPS_URGENT_LEVEL_2) ||
+ (mbtd->test_info.testcase == BKOPS_URGENT_LEVEL_2_TWO_REQS) ||
+ (mbtd->test_info.testcase == BKOPS_URGENT_LEVEL_3))
+ mq->err_check_fn = test_err_check;
+ mbtd->err_check_counter = 0;
+
+ return ret;
+}
+
+static int run_bkops(struct test_data *td)
+{
+ int ret = 0;
+ struct request_queue *q = td->req_q;
+ struct mmc_queue *mq = (struct mmc_queue *)q->queuedata;
+ struct mmc_card *card = mq->card;
+ struct mmc_bkops_stats *bkops_stat;
+
+ if (!card)
+ return -EINVAL;
+
+ bkops_stat = &card->bkops_info.bkops_stats;
+
+ switch (mbtd->test_info.testcase) {
+ case BKOPS_DELAYED_WORK_LEVEL_1:
+ bkops_stat->ignore_card_bkops_status = true;
+ card->ext_csd.raw_bkops_status = 1;
+ card->bkops_info.sectors_changed =
+ card->bkops_info.min_sectors_to_queue_delayed_work + 1;
+ mbtd->bkops_stage = BKOPS_STAGE_1;
+
+ __blk_run_queue(q);
+ /* this long sleep makes sure the host starts bkops and
+ also, gets into suspend */
+ msleep(10000);
+
+ bkops_stat->ignore_card_bkops_status = false;
+ card->ext_csd.raw_bkops_status = 0;
+
+ test_iosched_mark_test_completion();
+ break;
+
+ case BKOPS_DELAYED_WORK_LEVEL_1_HPI:
+ bkops_stat->ignore_card_bkops_status = true;
+ card->ext_csd.raw_bkops_status = 1;
+ card->bkops_info.sectors_changed =
+ card->bkops_info.min_sectors_to_queue_delayed_work + 1;
+ mbtd->bkops_stage = BKOPS_STAGE_1;
+
+ __blk_run_queue(q);
+ msleep(card->bkops_info.delay_ms);
+
+ ret = test_iosched_add_wr_rd_test_req(0, WRITE,
+ td->start_sector,
+ TEST_REQUEST_NUM_OF_BIOS,
+ TEST_PATTERN_5A,
+ bkops_end_io_final_fn);
+ if (ret) {
+ test_pr_err("%s: failed to add a write request",
+ __func__);
+ ret = -EINVAL;
+ break;
+ }
+
+ td->next_req = list_entry(td->test_queue.prev,
+ struct test_request, queuelist);
+ __blk_run_queue(q);
+ wait_event(mbtd->bkops_wait_q,
+ mbtd->bkops_stage == BKOPS_STAGE_4);
+ bkops_stat->ignore_card_bkops_status = false;
+
+ test_iosched_mark_test_completion();
+ break;
+
+ case BKOPS_CANCEL_DELAYED_WORK:
+ bkops_stat->ignore_card_bkops_status = true;
+ card->ext_csd.raw_bkops_status = 1;
+ card->bkops_info.sectors_changed =
+ card->bkops_info.min_sectors_to_queue_delayed_work + 1;
+ mbtd->bkops_stage = BKOPS_STAGE_1;
+
+ __blk_run_queue(q);
+
+ ret = test_iosched_add_wr_rd_test_req(0, WRITE,
+ td->start_sector,
+ TEST_REQUEST_NUM_OF_BIOS,
+ TEST_PATTERN_5A,
+ bkops_end_io_final_fn);
+ if (ret) {
+ test_pr_err("%s: failed to add a write request",
+ __func__);
+ ret = -EINVAL;
+ break;
+ }
+
+ td->next_req = list_entry(td->test_queue.prev,
+ struct test_request, queuelist);
+ __blk_run_queue(q);
+ wait_event(mbtd->bkops_wait_q,
+ mbtd->bkops_stage == BKOPS_STAGE_4);
+ bkops_stat->ignore_card_bkops_status = false;
+
+ test_iosched_mark_test_completion();
+ break;
+
+ case BKOPS_URGENT_LEVEL_2:
+ case BKOPS_URGENT_LEVEL_3:
+ bkops_stat->ignore_card_bkops_status = true;
+ if (mbtd->test_info.testcase == BKOPS_URGENT_LEVEL_2)
+ card->ext_csd.raw_bkops_status = 2;
+ else
+ card->ext_csd.raw_bkops_status = 3;
+ mbtd->bkops_stage = BKOPS_STAGE_1;
+
+ ret = test_iosched_add_wr_rd_test_req(0, WRITE,
+ td->start_sector,
+ TEST_REQUEST_NUM_OF_BIOS,
+ TEST_PATTERN_5A,
+ bkops_end_io_fn);
+ if (ret) {
+ test_pr_err("%s: failed to add a write request",
+ __func__);
+ ret = -EINVAL;
+ break;
+ }
+
+ td->next_req = list_entry(td->test_queue.prev,
+ struct test_request, queuelist);
+ __blk_run_queue(q);
+ wait_event(mbtd->bkops_wait_q,
+ mbtd->bkops_stage == BKOPS_STAGE_2);
+ card->ext_csd.raw_bkops_status = 0;
+
+ ret = test_iosched_add_wr_rd_test_req(0, WRITE,
+ td->start_sector,
+ TEST_REQUEST_NUM_OF_BIOS,
+ TEST_PATTERN_5A,
+ bkops_end_io_final_fn);
+ if (ret) {
+ test_pr_err("%s: failed to add a write request",
+ __func__);
+ ret = -EINVAL;
+ break;
+ }
+
+ td->next_req = list_entry(td->test_queue.prev,
+ struct test_request, queuelist);
+ __blk_run_queue(q);
+
+ wait_event(mbtd->bkops_wait_q,
+ mbtd->bkops_stage == BKOPS_STAGE_4);
+
+ bkops_stat->ignore_card_bkops_status = false;
+ test_iosched_mark_test_completion();
+ break;
+
+ case BKOPS_URGENT_LEVEL_2_TWO_REQS:
+ mq->wr_packing_enabled = false;
+ bkops_stat->ignore_card_bkops_status = true;
+ card->ext_csd.raw_bkops_status = 2;
+ mbtd->bkops_stage = BKOPS_STAGE_1;
+
+ ret = test_iosched_add_wr_rd_test_req(0, WRITE,
+ td->start_sector,
+ TEST_REQUEST_NUM_OF_BIOS,
+ TEST_PATTERN_5A,
+ NULL);
+ if (ret) {
+ test_pr_err("%s: failed to add a write request",
+ __func__);
+ ret = -EINVAL;
+ break;
+ }
+
+ ret = test_iosched_add_wr_rd_test_req(0, WRITE,
+ td->start_sector,
+ TEST_REQUEST_NUM_OF_BIOS,
+ TEST_PATTERN_5A,
+ bkops_end_io_fn);
+ if (ret) {
+ test_pr_err("%s: failed to add a write request",
+ __func__);
+ ret = -EINVAL;
+ break;
+ }
+
+ td->next_req = list_entry(td->test_queue.next,
+ struct test_request, queuelist);
+ __blk_run_queue(q);
+ wait_event(mbtd->bkops_wait_q,
+ mbtd->bkops_stage == BKOPS_STAGE_2);
+ card->ext_csd.raw_bkops_status = 0;
+
+ ret = test_iosched_add_wr_rd_test_req(0, WRITE,
+ td->start_sector,
+ TEST_REQUEST_NUM_OF_BIOS,
+ TEST_PATTERN_5A,
+ bkops_end_io_final_fn);
+ if (ret) {
+ test_pr_err("%s: failed to add a write request",
+ __func__);
+ ret = -EINVAL;
+ break;
+ }
+
+ td->next_req = list_entry(td->test_queue.prev,
+ struct test_request, queuelist);
+ __blk_run_queue(q);
+
+ wait_event(mbtd->bkops_wait_q,
+ mbtd->bkops_stage == BKOPS_STAGE_4);
+
+ bkops_stat->ignore_card_bkops_status = false;
+ test_iosched_mark_test_completion();
+
+ break;
+ default:
+ test_pr_err("%s: wrong testcase: %d", __func__,
+ mbtd->test_info.testcase);
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
static bool message_repeat;
static int test_open(struct inode *inode, struct file *file)
{
@@ -1809,6 +2315,121 @@
.read = write_packing_control_test_read,
};
+static ssize_t write_discard_sanitize_test_write(struct file *file,
+ const char __user *buf,
+ size_t count,
+ loff_t *ppos)
+{
+ int ret = 0;
+ int i = 0;
+ int number = -1;
+
+ sscanf(buf, "%d", &number);
+ if (number <= 0)
+ number = 1;
+
+ test_pr_info("%s: -- write_discard_sanitize TEST --\n", __func__);
+
+ memset(&mbtd->test_info, 0, sizeof(struct test_info));
+
+ mbtd->test_group = TEST_GENERAL_GROUP;
+
+ mbtd->test_info.data = mbtd;
+ mbtd->test_info.prepare_test_fn = prepare_write_discard_sanitize_read;
+ mbtd->test_info.get_test_case_str_fn = get_test_case_str;
+ mbtd->test_info.timeout_msec = SANITIZE_TEST_TIMEOUT;
+
+ for (i = 0 ; i < number ; ++i) {
+ test_pr_info("%s: Cycle # %d / %d\n", __func__, i+1, number);
+ test_pr_info("%s: ===================", __func__);
+
+ mbtd->test_info.testcase = TEST_WRITE_DISCARD_SANITIZE_READ;
+ ret = test_iosched_start_test(&mbtd->test_info);
+
+ if (ret)
+ break;
+ }
+
+ return count;
+}
+
+const struct file_operations write_discard_sanitize_test_ops = {
+ .open = test_open,
+ .write = write_discard_sanitize_test_write,
+};
+
+static ssize_t bkops_test_write(struct file *file,
+ const char __user *buf,
+ size_t count,
+ loff_t *ppos)
+{
+ int ret = 0;
+ int i = 0, j;
+ int number = -1;
+
+ test_pr_info("%s: -- bkops_test TEST --", __func__);
+
+ sscanf(buf, "%d", &number);
+
+ if (number <= 0)
+ number = 1;
+
+ mbtd->test_group = TEST_BKOPS_GROUP;
+
+ memset(&mbtd->test_info, 0, sizeof(struct test_info));
+
+ mbtd->test_info.data = mbtd;
+ mbtd->test_info.prepare_test_fn = prepare_bkops;
+ mbtd->test_info.check_test_result_fn = check_bkops_result;
+ mbtd->test_info.get_test_case_str_fn = get_test_case_str;
+ mbtd->test_info.run_test_fn = run_bkops;
+ mbtd->test_info.timeout_msec = BKOPS_TEST_TIMEOUT;
+ mbtd->test_info.post_test_fn = bkops_post_test;
+
+ for (i = 0 ; i < number ; ++i) {
+ test_pr_info("%s: Cycle # %d / %d", __func__, i+1, number);
+ test_pr_info("%s: ===================", __func__);
+ for (j = BKOPS_MIN_TESTCASE ;
+ j <= BKOPS_MAX_TESTCASE ; j++) {
+ mbtd->test_info.testcase = j;
+ ret = test_iosched_start_test(&mbtd->test_info);
+ if (ret)
+ break;
+ }
+ }
+
+ test_pr_info("%s: Completed all the test cases.", __func__);
+
+ return count;
+}
+
+static ssize_t bkops_test_read(struct file *file,
+ char __user *buffer,
+ size_t count,
+ loff_t *offset)
+{
+ memset((void *)buffer, 0, count);
+
+ snprintf(buffer, count,
+ "\nbkops_test\n========================\n"
+ "Description:\n"
+ "This test simulates BKOPS status from card\n"
+ "and verifies that:\n"
+ " - Starting BKOPS delayed work, level 1\n"
+ " - Starting BKOPS delayed work, level 1, with HPI\n"
+ " - Cancel starting BKOPS delayed work, "
+ " when a request is received\n"
+ " - Starting BKOPS urgent, level 2,3\n"
+ " - Starting BKOPS urgent with 2 requests\n");
+ return strnlen(buffer, count);
+}
+
+const struct file_operations bkops_test_ops = {
+ .open = test_open,
+ .write = bkops_test_write,
+ .read = bkops_test_read,
+};
+
static void mmc_block_test_debugfs_cleanup(void)
{
debugfs_remove(mbtd->debug.random_test_seed);
@@ -1816,6 +2437,8 @@
debugfs_remove(mbtd->debug.err_check_test);
debugfs_remove(mbtd->debug.send_invalid_packed_test);
debugfs_remove(mbtd->debug.packing_control_test);
+ debugfs_remove(mbtd->debug.discard_sanitize_test);
+ debugfs_remove(mbtd->debug.bkops_test);
}
static int mmc_block_test_debugfs_init(void)
@@ -1877,6 +2500,27 @@
if (!mbtd->debug.packing_control_test)
goto err_nomem;
+ mbtd->debug.discard_sanitize_test =
+ debugfs_create_file("write_discard_sanitize_test",
+ S_IRUGO | S_IWUGO,
+ tests_root,
+ NULL,
+ &write_discard_sanitize_test_ops);
+ if (!mbtd->debug.discard_sanitize_test) {
+ mmc_block_test_debugfs_cleanup();
+ return -ENOMEM;
+ }
+
+ mbtd->debug.bkops_test =
+ debugfs_create_file("bkops_test",
+ S_IRUGO | S_IWUGO,
+ tests_root,
+ NULL,
+ &bkops_test_ops);
+
+ if (!mbtd->debug.bkops_test)
+ goto err_nomem;
+
return 0;
err_nomem:
@@ -1924,6 +2568,7 @@
return -ENODEV;
}
+ init_waitqueue_head(&mbtd->bkops_wait_q);
mbtd->bdt.init_fn = mmc_block_test_probe;
mbtd->bdt.exit_fn = mmc_block_test_remove;
INIT_LIST_HEAD(&mbtd->bdt.list);
diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c
index 0592f9d..b24620b 100644
--- a/drivers/mmc/core/bus.c
+++ b/drivers/mmc/core/bus.c
@@ -254,6 +254,7 @@
card->dev.release = mmc_release_card;
card->dev.type = type;
+ spin_lock_init(&card->bkops_info.bkops_stats.lock);
spin_lock_init(&card->wr_pack_stats.lock);
return card;
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 7e03e5a..3aa38d2 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -77,6 +77,30 @@
removable,
"MMC/SD cards are removable and may be removed during suspend");
+#define MMC_UPDATE_BKOPS_STATS_HPI(stats) \
+ do { \
+ spin_lock(&stats.lock); \
+ if (stats.enabled) \
+ stats.hpi++; \
+ spin_unlock(&stats.lock); \
+ } while (0);
+#define MMC_UPDATE_BKOPS_STATS_SUSPEND(stats) \
+ do { \
+ spin_lock(&stats.lock); \
+ if (stats.enabled) \
+ stats.suspend++; \
+ spin_unlock(&stats.lock); \
+ } while (0);
+#define MMC_UPDATE_STATS_BKOPS_SEVERITY_LEVEL(stats, level) \
+ do { \
+ if (level <= 0 || level > BKOPS_NUM_OF_SEVERITY_LEVELS) \
+ break; \
+ spin_lock(&stats.lock); \
+ if (stats.enabled) \
+ stats.bkops_level[level-1]++; \
+ spin_unlock(&stats.lock); \
+ } while (0);
+
/*
* Internal function. Schedule delayed work in the MMC work queue.
*/
@@ -279,6 +303,29 @@
host->ops->request(host, mrq);
}
+void mmc_blk_init_bkops_statistics(struct mmc_card *card)
+{
+ int i;
+ struct mmc_bkops_stats *bkops_stats;
+
+ if (!card)
+ return;
+
+ bkops_stats = &card->bkops_info.bkops_stats;
+
+ spin_lock(&bkops_stats->lock);
+
+ for (i = 0 ; i < BKOPS_NUM_OF_SEVERITY_LEVELS ; ++i)
+ bkops_stats->bkops_level[i] = 0;
+
+ bkops_stats->suspend = 0;
+ bkops_stats->hpi = 0;
+ bkops_stats->enabled = true;
+
+ spin_unlock(&bkops_stats->lock);
+}
+EXPORT_SYMBOL(mmc_blk_init_bkops_statistics);
+
/**
* mmc_start_delayed_bkops() - Start a delayed work to check for
* the need of non urgent BKOPS
@@ -349,8 +396,8 @@
err = mmc_read_bkops_status(card);
if (err) {
- pr_err("%s: Failed to read bkops status: %d\n",
- mmc_hostname(card->host), err);
+ pr_err("%s: %s: Failed to read bkops status: %d\n",
+ mmc_hostname(card->host), __func__, err);
goto out;
}
@@ -367,8 +414,11 @@
* work, before going to suspend
*/
if (card->ext_csd.raw_bkops_status < EXT_CSD_BKOPS_LEVEL_2 &&
- from_exception)
+ from_exception) {
+ pr_debug("%s: %s: Level 1 from exception, exit",
+ mmc_hostname(card->host), __func__);
goto out;
+ }
if (card->ext_csd.raw_bkops_status >= EXT_CSD_BKOPS_LEVEL_2) {
timeout = MMC_BKOPS_MAX_TIMEOUT;
@@ -381,10 +431,12 @@
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_BKOPS_START, 1, timeout, use_busy_signal);
if (err) {
- pr_warn("%s: Error %d starting bkops\n",
- mmc_hostname(card->host), err);
+ pr_warn("%s: %s: Error %d when starting bkops\n",
+ mmc_hostname(card->host), __func__, err);
goto out;
}
+ MMC_UPDATE_STATS_BKOPS_SEVERITY_LEVEL(card->bkops_info.bkops_stats,
+ card->ext_csd.raw_bkops_status);
/*
* For urgent bkops status (LEVEL_2 and more)
@@ -608,8 +660,11 @@
if (host->card && mmc_card_mmc(host->card) &&
((mmc_resp_type(host->areq->mrq->cmd) == MMC_RSP_R1) ||
(mmc_resp_type(host->areq->mrq->cmd) == MMC_RSP_R1B)) &&
- (host->areq->mrq->cmd->resp[0] & R1_EXCEPTION_EVENT))
+ (host->areq->mrq->cmd->resp[0] & R1_EXCEPTION_EVENT)) {
mmc_start_bkops(host->card, true);
+ pr_debug("%s: %s: completed BKOPs due to exception",
+ mmc_hostname(host), __func__);
+ }
}
if (!err && areq)
@@ -620,7 +675,7 @@
/* Cancel a prepared request if it was not started. */
if ((err || start_err) && areq)
- mmc_post_req(host, areq->mrq, -EINVAL);
+ mmc_post_req(host, areq->mrq, -EINVAL);
if (err)
host->areq = NULL;
@@ -773,6 +828,8 @@
err = 0;
}
+ MMC_UPDATE_BKOPS_STATS_HPI(card->bkops_info.bkops_stats);
+
out:
mmc_release_host(card->host);
return err;
@@ -794,6 +851,12 @@
return -ENOMEM;
}
+ if (card->bkops_info.bkops_stats.ignore_card_bkops_status) {
+ pr_debug("%s: skipping read raw_bkops_status in unittest mode",
+ __func__);
+ return 0;
+ }
+
mmc_claim_host(card->host);
err = mmc_send_ext_csd(card, ext_csd);
mmc_release_host(card->host);
@@ -1438,6 +1501,7 @@
mmc_set_ios(host);
mmc_host_clk_release(host);
}
+
/*
* Apply power to the MMC stack. This is a two-stage process.
* First, we enable power to the card without the clock running.
@@ -1500,7 +1564,6 @@
host->ios.clock = 0;
host->ios.vdd = 0;
-
/*
* Reset ocr mask to be the highest possible voltage supported for
* this mmc host. This value will be used at next power up.
@@ -2030,15 +2093,6 @@
}
EXPORT_SYMBOL(mmc_can_secure_erase_trim);
-int mmc_can_poweroff_notify(const struct mmc_card *card)
-{
- return card &&
- mmc_card_mmc(card) &&
- card->host->bus_ops->poweroff_notify &&
- (card->poweroff_notify_state == MMC_POWERED_ON);
-}
-EXPORT_SYMBOL(mmc_can_poweroff_notify);
-
int mmc_erase_group_aligned(struct mmc_card *card, unsigned int from,
unsigned int nr)
{
@@ -2431,15 +2485,6 @@
mmc_bus_get(host);
if (host->bus_ops && !host->bus_dead) {
- mmc_claim_host(host);
- if (mmc_can_poweroff_notify(host->card)) {
- int err = host->bus_ops->poweroff_notify(host,
- MMC_PW_OFF_NOTIFY_LONG);
- if (err)
- pr_info("%s: error [%d] in poweroff notify\n",
- mmc_hostname(host), err);
- }
- mmc_release_host(host);
/* Calling bus_ops->remove() with a claimed host can deadlock */
if (host->bus_ops->remove)
host->bus_ops->remove(host);
@@ -2475,15 +2520,6 @@
if (host->bus_ops->power_save)
ret = host->bus_ops->power_save(host);
- mmc_claim_host(host);
- if (mmc_can_poweroff_notify(host->card)) {
- int err = host->bus_ops->poweroff_notify(host,
- MMC_PW_OFF_NOTIFY_SHORT);
- if (err)
- pr_info("%s: error [%d] in poweroff notify\n",
- mmc_hostname(host), err);
- }
- mmc_release_host(host);
mmc_bus_put(host);
@@ -2526,11 +2562,8 @@
mmc_bus_get(host);
- if (host->bus_ops && !host->bus_dead && host->bus_ops->awake) {
+ if (host->bus_ops && !host->bus_dead && host->bus_ops->awake)
err = host->bus_ops->awake(host);
- if (!err)
- mmc_card_clr_sleep(host->card);
- }
mmc_bus_put(host);
@@ -2547,11 +2580,8 @@
mmc_bus_get(host);
- if (host->bus_ops && !host->bus_dead && host->bus_ops->sleep) {
+ if (host->bus_ops && !host->bus_dead && host->bus_ops->sleep)
err = host->bus_ops->sleep(host);
- if (!err)
- mmc_card_set_sleep(host->card);
- }
mmc_bus_put(host);
@@ -2679,6 +2709,8 @@
goto stop_bkops_err;
}
err = host->bus_ops->suspend(host);
+ MMC_UPDATE_BKOPS_STATS_SUSPEND(host->
+ card->bkops_info.bkops_stats);
}
if (!(host->card && mmc_card_sdio(host->card)))
mmc_release_host(host);
@@ -2801,15 +2833,6 @@
if (!host->bus_ops || host->bus_ops->suspend)
break;
- mmc_claim_host(host);
- if (mmc_can_poweroff_notify(host->card)) {
- int err = host->bus_ops->poweroff_notify(host,
- MMC_PW_OFF_NOTIFY_SHORT);
- if (err)
- pr_info("%s: error [%d] in poweroff notify\n",
- mmc_hostname(host), err);
- }
- mmc_release_host(host);
/* Calling bus_ops->remove() with a claimed host can deadlock */
if (host->bus_ops->remove)
diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h
index 22f6043..85d2737 100644
--- a/drivers/mmc/core/core.h
+++ b/drivers/mmc/core/core.h
@@ -25,7 +25,6 @@
int (*power_save)(struct mmc_host *);
int (*power_restore)(struct mmc_host *);
int (*alive)(struct mmc_host *);
- int (*poweroff_notify)(struct mmc_host *, int notify);
};
void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops);
diff --git a/drivers/mmc/core/debugfs.c b/drivers/mmc/core/debugfs.c
index 4022ccc..ddb562e 100644
--- a/drivers/mmc/core/debugfs.c
+++ b/drivers/mmc/core/debugfs.c
@@ -484,6 +484,113 @@
.write = mmc_wr_pack_stats_write,
};
+static int mmc_bkops_stats_open(struct inode *inode, struct file *filp)
+{
+ struct mmc_card *card = inode->i_private;
+
+ filp->private_data = card;
+
+ card->bkops_info.bkops_stats.print_stats = 1;
+ return 0;
+}
+
+static ssize_t mmc_bkops_stats_read(struct file *filp, char __user *ubuf,
+ size_t cnt, loff_t *ppos)
+{
+ struct mmc_card *card = filp->private_data;
+ struct mmc_bkops_stats *bkops_stats;
+ int i;
+ char *temp_buf;
+
+ if (!card)
+ return cnt;
+
+ bkops_stats = &card->bkops_info.bkops_stats;
+
+ if (!bkops_stats->print_stats)
+ return 0;
+
+ if (!bkops_stats->enabled) {
+ pr_info("%s: bkops statistics are disabled\n",
+ mmc_hostname(card->host));
+ goto exit;
+ }
+
+ temp_buf = kmalloc(TEMP_BUF_SIZE, GFP_KERNEL);
+ if (!temp_buf)
+ goto exit;
+
+ spin_lock(&bkops_stats->lock);
+
+ memset(ubuf, 0, cnt);
+
+ snprintf(temp_buf, TEMP_BUF_SIZE, "%s: bkops statistics:\n",
+ mmc_hostname(card->host));
+ strlcat(ubuf, temp_buf, cnt);
+
+ for (i = 0 ; i < BKOPS_NUM_OF_SEVERITY_LEVELS ; ++i) {
+ snprintf(temp_buf, TEMP_BUF_SIZE,
+ "%s: BKOPS: due to level %d: %u\n",
+ mmc_hostname(card->host), i, bkops_stats->bkops_level[i]);
+ strlcat(ubuf, temp_buf, cnt);
+ }
+
+ snprintf(temp_buf, TEMP_BUF_SIZE,
+ "%s: BKOPS: stopped due to HPI: %u\n",
+ mmc_hostname(card->host), bkops_stats->hpi);
+ strlcat(ubuf, temp_buf, cnt);
+
+ snprintf(temp_buf, TEMP_BUF_SIZE,
+ "%s: BKOPS: how many time host was suspended: %u\n",
+ mmc_hostname(card->host), bkops_stats->suspend);
+ strlcat(ubuf, temp_buf, cnt);
+
+ spin_unlock(&bkops_stats->lock);
+
+ kfree(temp_buf);
+
+ pr_info("%s", ubuf);
+
+exit:
+ if (bkops_stats->print_stats == 1) {
+ bkops_stats->print_stats = 0;
+ return strnlen(ubuf, cnt);
+ }
+
+ return 0;
+}
+
+static ssize_t mmc_bkops_stats_write(struct file *filp,
+ const char __user *ubuf, size_t cnt,
+ loff_t *ppos)
+{
+ struct mmc_card *card = filp->private_data;
+ int value;
+ struct mmc_bkops_stats *bkops_stats;
+
+ if (!card)
+ return cnt;
+
+ bkops_stats = &card->bkops_info.bkops_stats;
+
+ sscanf(ubuf, "%d", &value);
+ if (value) {
+ mmc_blk_init_bkops_statistics(card);
+ } else {
+ spin_lock(&bkops_stats->lock);
+ bkops_stats->enabled = false;
+ spin_unlock(&bkops_stats->lock);
+ }
+
+ return cnt;
+}
+
+static const struct file_operations mmc_dbg_bkops_stats_fops = {
+ .open = mmc_bkops_stats_open,
+ .read = mmc_bkops_stats_read,
+ .write = mmc_bkops_stats_write,
+};
+
void mmc_add_card_debugfs(struct mmc_card *card)
{
struct mmc_host *host = card->host;
@@ -522,6 +629,12 @@
&mmc_dbg_wr_pack_stats_fops))
goto err;
+ if (mmc_card_mmc(card) && (card->ext_csd.rev >= 6) &&
+ card->ext_csd.bkops_en)
+ if (!debugfs_create_file("bkops_stats", S_IRUSR, root, card,
+ &mmc_dbg_bkops_stats_fops))
+ goto err;
+
return;
err:
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index ca8b01c..47fd9b9 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -1028,7 +1028,7 @@
* so check for success and update the flag
*/
if (!err)
- card->poweroff_notify_state = MMC_POWERED_ON;
+ card->ext_csd.power_off_notification = EXT_CSD_POWER_ON;
}
/*
@@ -1356,40 +1356,35 @@
return err;
}
-static int mmc_poweroff_notify(struct mmc_host *host, int notify)
+static int mmc_can_poweroff_notify(const struct mmc_card *card)
{
- struct mmc_card *card;
- unsigned int timeout;
- unsigned int notify_type = EXT_CSD_NO_POWER_NOTIFICATION;
+ return card &&
+ mmc_card_mmc(card) &&
+ (card->ext_csd.power_off_notification == EXT_CSD_POWER_ON);
+}
+
+static int mmc_poweroff_notify(struct mmc_card *card, unsigned int notify_type)
+{
+ unsigned int timeout = card->ext_csd.generic_cmd6_time;
int err;
- card = host->card;
-
- if (notify == MMC_PW_OFF_NOTIFY_SHORT) {
- notify_type = EXT_CSD_POWER_OFF_SHORT;
- timeout = card->ext_csd.generic_cmd6_time;
- } else if (notify == MMC_PW_OFF_NOTIFY_LONG) {
- notify_type = EXT_CSD_POWER_OFF_LONG;
+ /* Use EXT_CSD_POWER_OFF_SHORT as default notification type. */
+ if (notify_type == EXT_CSD_POWER_OFF_LONG)
timeout = card->ext_csd.power_off_longtime;
- } else {
- pr_info("%s: mmc_poweroff_notify called "
- "with notify type %d\n", mmc_hostname(host), notify);
- return -EINVAL;
- }
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_POWER_OFF_NOTIFICATION,
notify_type, timeout);
-
if (err)
- pr_err("%s: Device failed to respond within %d "
- "poweroff timeout.\n", mmc_hostname(host), timeout);
- else
- card->poweroff_notify_state =
- MMC_NO_POWER_NOTIFICATION;
+ pr_err("%s: Power Off Notification timed out, %u\n",
+ mmc_hostname(card->host), timeout);
+
+ /* Disable the power off notification after the switch operation. */
+ card->ext_csd.power_off_notification = EXT_CSD_NO_POWER_NOTIFICATION;
return err;
}
+
/*
* Host is being removed. Free up the current card.
*/
@@ -1453,26 +1448,13 @@
BUG_ON(!host->card);
mmc_claim_host(host);
- if (mmc_can_poweroff_notify(host->card) &&
- (host->caps2 & MMC_CAP2_POWER_OFF_VCCQ_DURING_SUSPEND)) {
- err = mmc_poweroff_notify(host, MMC_PW_OFF_NOTIFY_SHORT);
- } else {
- if (mmc_card_can_sleep(host))
- /*
- * If sleep command has error it doesn't mean host
- * cannot suspend, but a deeper low power state
- * transition for the card has failed. Ignore
- * sleep errors so that the suspend is not aborted.
- * In error case, mmc_resume() takes care of
- * complete intialization of the card.
- */
- mmc_card_sleep(host);
- else if (!mmc_host_is_spi(host))
- mmc_deselect_cards(host);
- }
- if (!err)
- host->card->state &=
- ~(MMC_STATE_HIGHSPEED | MMC_STATE_HIGHSPEED_200);
+ if (mmc_can_poweroff_notify(host->card))
+ err = mmc_poweroff_notify(host->card, EXT_CSD_POWER_OFF_SHORT);
+ else if (mmc_card_can_sleep(host))
+ err = mmc_card_sleep(host);
+ else if (!mmc_host_is_spi(host))
+ mmc_deselect_cards(host);
+ host->card->state &= ~(MMC_STATE_HIGHSPEED | MMC_STATE_HIGHSPEED_200);
mmc_release_host(host);
return err;
@@ -1503,7 +1485,6 @@
int ret;
host->card->state &= ~(MMC_STATE_HIGHSPEED | MMC_STATE_HIGHSPEED_200);
- mmc_card_clr_sleep(host->card);
mmc_claim_host(host);
ret = mmc_init_card(host, host->ocr, host->card);
mmc_release_host(host);
@@ -1550,7 +1531,6 @@
.resume = NULL,
.power_restore = mmc_power_restore,
.alive = mmc_alive,
- .poweroff_notify = mmc_poweroff_notify,
};
static const struct mmc_bus_ops mmc_ops_unsafe = {
@@ -1562,7 +1542,6 @@
.resume = mmc_resume,
.power_restore = mmc_power_restore,
.alive = mmc_alive,
- .poweroff_notify = mmc_poweroff_notify,
};
static void mmc_attach_bus_ops(struct mmc_host *host)
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 0c3f994..da38122 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -500,6 +500,10 @@
help
Select Y to enable Slot 3.
+config MMC_MSM_SDC3_POLLING
+ boolean "Qualcomm SDC3 support"
+ depends on MMC_MSM
+
config MMC_MSM_SDC3_8_BIT_SUPPORT
boolean "Qualcomm SDC3 8bit support"
depends on MMC_MSM_SDC3_SUPPORT
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 2f1a0dc..43f7e77 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -2581,6 +2581,10 @@
if (caps[1] & SDHCI_DRIVER_TYPE_D)
mmc->caps |= MMC_CAP_DRIVER_TYPE_D;
+ /* Initial value for re-tuning timer count */
+ host->tuning_count = (caps[1] & SDHCI_RETUNING_TIMER_COUNT_MASK) >>
+ SDHCI_RETUNING_TIMER_COUNT_SHIFT;
+
/*
* In case Re-tuning Timer is not disabled, the actual value of
* re-tuning timer will be 2 ^ (n - 1).
diff --git a/drivers/net/usb/rmnet_usb_data.c b/drivers/net/usb/rmnet_usb_data.c
index 17ff067..fdfe468 100644
--- a/drivers/net/usb/rmnet_usb_data.c
+++ b/drivers/net/usb/rmnet_usb_data.c
@@ -556,10 +556,6 @@
/* allow modem and roothub to wake up suspended system */
device_set_wakeup_enable(&udev->dev, 1);
device_set_wakeup_enable(&udev->parent->dev, 1);
-
- /* set default autosuspend timeout for modem and roothub */
- pm_runtime_set_autosuspend_delay(&udev->dev, 1000);
- pm_runtime_set_autosuspend_delay(&udev->parent->dev, 200);
}
out:
diff --git a/drivers/platform/msm/qpnp-pwm.c b/drivers/platform/msm/qpnp-pwm.c
index ebe3ad06..1729b49 100644
--- a/drivers/platform/msm/qpnp-pwm.c
+++ b/drivers/platform/msm/qpnp-pwm.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -107,8 +107,10 @@
/* LPG Control for RAMP_CONTROL */
#define QPNP_RAMP_START_MASK 0x01
-#define QPNP_ENABLE_LUT(value) (value |= QPNP_RAMP_START_MASK)
-#define QPNP_DISABLE_LUT(value) (value &= ~QPNP_RAMP_START_MASK)
+#define QPNP_ENABLE_LUT_V0(value) (value |= QPNP_RAMP_START_MASK)
+#define QPNP_DISABLE_LUT_V0(value) (value &= ~QPNP_RAMP_START_MASK)
+#define QPNP_ENABLE_LUT_V1(value, id) (value |= BIT(id))
+#define QPNP_DISABLE_LUT_V1(value, id) (value &= ~BIT(id))
/* LPG Control for RAMP_STEP_DURATION_LSB */
#define QPNP_RAMP_STEP_DURATION_LSB_MASK 0xFF
@@ -154,10 +156,30 @@
#define PRE_DIVIDE_5 5
#define PRE_DIVIDE_6 6
-#define SPMI_LPG_REG_ADDR_BASE 0x40
-#define SPMI_LPG_REG_ADDR(b, n) (b + SPMI_LPG_REG_ADDR_BASE + (n))
+#define SPMI_LPG_REG_BASE_OFFSET 0x40
+#define SPMI_LPG_REVISION2_OFFSET 0x1
+#define SPMI_LPG_REV1_RAMP_CONTROL_OFFSET 0x86
+#define SPMI_LPG_REG_ADDR(b, n) (b + SPMI_LPG_REG_BASE_OFFSET + (n))
#define SPMI_MAX_BUF_LEN 8
+/* LPG revisions */
+enum qpnp_lpg_revision {
+ QPNP_LPG_REVISION_0 = 0x0,
+ QPNP_LPG_REVISION_1 = 0x1,
+};
+
+/* LPG LUT MODE STATE */
+enum qpnp_lut_state {
+ QPNP_LUT_ENABLE = 0x0,
+ QPNP_LUT_DISABLE = 0x1,
+};
+
+/* PWM MODE STATE */
+enum qpnp_pwm_state {
+ QPNP_PWM_ENABLE = 0x0,
+ QPNP_PWM_DISABLE = 0x1,
+};
+
/* SPMI LPG registers */
enum qpnp_lpg_registers_list {
QPNP_LPG_PATTERN_CONFIG,
@@ -251,9 +273,10 @@
struct qpnp_lpg_chip {
struct spmi_device *spmi_dev;
struct pwm_device pwm_dev;
- struct mutex lpg_mutex;
+ spinlock_t lpg_lock;
struct qpnp_lpg_config lpg_config;
u8 qpnp_lpg_registers[QPNP_TOTAL_LPG_SPMI_REGISTERS];
+ enum qpnp_lpg_revision revision;
};
/* Internal functions */
@@ -283,21 +306,22 @@
QPNP_EN_GLITCH_REMOVAL_MASK;
}
-static inline void qpnp_set_control(u8 *val, bool pwm_hi, bool pwm_lo,
- bool pwm_out, bool pwm_src, bool ramp_gen)
+static int qpnp_set_control(bool pwm_hi, bool pwm_lo, bool pwm_out,
+ bool pwm_src, bool ramp_gen)
{
- *val = (ramp_gen << QPNP_PWM_EN_RAMP_GEN_SHIFT) &
- QPNP_PWM_EN_RAMP_GEN_MASK;
- *val |= (pwm_src << QPNP_PWM_SRC_SELECT_SHIFT) &
- QPNP_PWM_SRC_SELECT_MASK;
- *val |= (pwm_out << QPNP_EN_PWM_OUTPUT_SHIFT) &
- QPNP_EN_PWM_OUTPUT_MASK;
- *val |= (pwm_lo << QPNP_EN_PWM_LO_SHIFT) & QPNP_EN_PWM_LO_MASK;
- *val |= (pwm_hi << QPNP_EN_PWM_HIGH_SHIFT) & QPNP_EN_PWM_HIGH_MASK;
+ return (ramp_gen << QPNP_PWM_EN_RAMP_GEN_SHIFT)
+ | (pwm_src << QPNP_PWM_SRC_SELECT_SHIFT)
+ | (pwm_out << QPNP_EN_PWM_OUTPUT_SHIFT)
+ | (pwm_lo << QPNP_EN_PWM_LO_SHIFT)
+ | (pwm_hi << QPNP_EN_PWM_HIGH_SHIFT);
}
-#define QPNP_ENABLE_LUT_CONTROL(p_val) qpnp_set_control(p_val, 1, 1, 1, 0, 1)
-#define QPNP_ENABLE_PWM_CONTROL(p_val) qpnp_set_control(p_val, 1, 1, 0, 1, 0)
+#define QPNP_ENABLE_LUT_CONTROL qpnp_set_control(0, 0, 0, 0, 1)
+#define QPNP_ENABLE_PWM_CONTROL qpnp_set_control(0, 0, 0, 1, 0)
+#define QPNP_ENABLE_PWM_MODE qpnp_set_control(1, 1, 1, 1, 0)
+#define QPNP_ENABLE_LPG_MODE qpnp_set_control(1, 1, 1, 0, 1)
+#define QPNP_DISABLE_PWM_MODE qpnp_set_control(0, 0, 0, 1, 0)
+#define QPNP_DISABLE_LPG_MODE qpnp_set_control(0, 0, 0, 0, 1)
#define QPNP_IS_PWM_CONFIG_SELECTED(val) (val & QPNP_PWM_SRC_SELECT_MASK)
@@ -328,13 +352,13 @@
*u8p |= val & mask;
}
-static int qpnp_lpg_save_and_write(u8 value, u8 mask, u8 *reg, u16 base_addr,
- u16 offset, u16 size, struct qpnp_lpg_chip *chip)
+static int qpnp_lpg_save_and_write(u8 value, u8 mask, u8 *reg, u16 addr,
+ u16 size, struct qpnp_lpg_chip *chip)
{
qpnp_lpg_save(reg, mask, value);
return spmi_ext_register_writel(chip->spmi_dev->ctrl,
- chip->spmi_dev->sid, SPMI_LPG_REG_ADDR(base_addr, offset), reg, size);
+ chip->spmi_dev->sid, addr, reg, size);
}
/*
@@ -455,7 +479,7 @@
return -EINVAL;
}
- for (i = 0; i <= lut->list_len; i++) {
+ for (i = 0; i < lut->list_len; i++) {
if (raw_value)
pwm_value = duty_pct[i];
else
@@ -530,7 +554,8 @@
rc = qpnp_lpg_save_and_write(value, mask,
&pwm->chip->qpnp_lpg_registers[QPNP_PWM_VALUE_LSB],
- lpg_config->base_addr, QPNP_PWM_VALUE_LSB, 1, chip);
+ SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+ QPNP_PWM_VALUE_LSB), 1, chip);
if (rc)
return rc;
@@ -541,7 +566,8 @@
return qpnp_lpg_save_and_write(value, mask,
&pwm->chip->qpnp_lpg_registers[QPNP_PWM_VALUE_MSB],
- lpg_config->base_addr, QPNP_PWM_VALUE_MSB, 1, chip);
+ SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+ QPNP_PWM_VALUE_MSB), 1, chip);
}
static int qpnp_lpg_configure_pattern(struct pwm_device *pwm)
@@ -559,7 +585,8 @@
return qpnp_lpg_save_and_write(value, mask,
&pwm->chip->qpnp_lpg_registers[QPNP_LPG_PATTERN_CONFIG],
- lpg_config->base_addr, QPNP_LPG_PATTERN_CONFIG, 1, chip);
+ SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+ QPNP_LPG_PATTERN_CONFIG), 1, chip);
}
static int qpnp_lpg_configure_pwm(struct pwm_device *pwm)
@@ -590,7 +617,8 @@
return qpnp_lpg_save_and_write(value, mask,
&pwm->chip->qpnp_lpg_registers[QPNP_LPG_PWM_TYPE_CONFIG],
- lpg_config->base_addr, QPNP_LPG_PWM_TYPE_CONFIG, 1, chip);
+ SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+ QPNP_LPG_PWM_TYPE_CONFIG), 1, chip);
}
static int qpnp_configure_pwm_control(struct pwm_device *pwm)
@@ -599,7 +627,7 @@
struct qpnp_lpg_chip *chip = pwm->chip;
u8 value, mask;
- QPNP_ENABLE_PWM_CONTROL(&value);
+ value = QPNP_ENABLE_PWM_CONTROL;
mask = QPNP_EN_PWM_HIGH_MASK | QPNP_EN_PWM_LO_MASK |
QPNP_EN_PWM_OUTPUT_MASK | QPNP_PWM_SRC_SELECT_MASK |
@@ -607,7 +635,8 @@
return qpnp_lpg_save_and_write(value, mask,
&pwm->chip->qpnp_lpg_registers[QPNP_ENABLE_CONTROL],
- lpg_config->base_addr, QPNP_ENABLE_CONTROL, 1, chip);
+ SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+ QPNP_ENABLE_CONTROL), 1, chip);
}
@@ -617,7 +646,7 @@
struct qpnp_lpg_chip *chip = pwm->chip;
u8 value, mask;
- QPNP_ENABLE_LUT_CONTROL(&value);
+ value = QPNP_ENABLE_LUT_CONTROL;
mask = QPNP_EN_PWM_HIGH_MASK | QPNP_EN_PWM_LO_MASK |
QPNP_EN_PWM_OUTPUT_MASK | QPNP_PWM_SRC_SELECT_MASK |
@@ -625,7 +654,8 @@
return qpnp_lpg_save_and_write(value, mask,
&pwm->chip->qpnp_lpg_registers[QPNP_ENABLE_CONTROL],
- lpg_config->base_addr, QPNP_ENABLE_CONTROL, 1, chip);
+ SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+ QPNP_ENABLE_CONTROL), 1, chip);
}
@@ -643,7 +673,8 @@
rc = qpnp_lpg_save_and_write(val, mask,
&pwm->chip->qpnp_lpg_registers[QPNP_RAMP_STEP_DURATION_LSB],
- lpg_config->base_addr, QPNP_RAMP_STEP_DURATION_LSB, 1, chip);
+ SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+ QPNP_RAMP_STEP_DURATION_LSB), 1, chip);
if (rc)
return rc;
@@ -654,7 +685,8 @@
return qpnp_lpg_save_and_write(val, mask,
&pwm->chip->qpnp_lpg_registers[QPNP_RAMP_STEP_DURATION_MSB],
- lpg_config->base_addr, QPNP_RAMP_STEP_DURATION_MSB, 1, chip);
+ SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+ QPNP_RAMP_STEP_DURATION_MSB), 1, chip);
}
static int qpnp_lpg_configure_pause(struct pwm_device *pwm)
@@ -671,7 +703,8 @@
rc = qpnp_lpg_save_and_write(value, mask,
&pwm->chip->qpnp_lpg_registers[QPNP_PAUSE_HI_MULTIPLIER_LSB],
- lpg_config->base_addr, QPNP_PAUSE_HI_MULTIPLIER_LSB, 1, chip);
+ SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+ QPNP_PAUSE_HI_MULTIPLIER_LSB), 1, chip);
if (rc)
return rc;
@@ -683,14 +716,16 @@
rc = qpnp_lpg_save_and_write(value, mask,
&pwm->chip->qpnp_lpg_registers[QPNP_PAUSE_HI_MULTIPLIER_MSB],
- lpg_config->base_addr, QPNP_PAUSE_HI_MULTIPLIER_MSB, 1, chip);
+ SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+ QPNP_PAUSE_HI_MULTIPLIER_MSB), 1, chip);
} else {
value = 0;
mask = QPNP_PAUSE_HI_MULTIPLIER_LSB_MASK;
rc = qpnp_lpg_save_and_write(value, mask,
&pwm->chip->qpnp_lpg_registers[QPNP_PAUSE_HI_MULTIPLIER_LSB],
- lpg_config->base_addr, QPNP_PAUSE_HI_MULTIPLIER_LSB, 1, chip);
+ SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+ QPNP_PAUSE_HI_MULTIPLIER_LSB), 1, chip);
if (rc)
return rc;
@@ -698,7 +733,8 @@
rc = qpnp_lpg_save_and_write(value, mask,
&pwm->chip->qpnp_lpg_registers[QPNP_PAUSE_HI_MULTIPLIER_MSB],
- lpg_config->base_addr, QPNP_PAUSE_HI_MULTIPLIER_MSB, 1, chip);
+ SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+ QPNP_PAUSE_HI_MULTIPLIER_MSB), 1, chip);
if (rc)
return rc;
@@ -710,7 +746,8 @@
rc = qpnp_lpg_save_and_write(value, mask,
&pwm->chip->qpnp_lpg_registers[QPNP_PAUSE_LO_MULTIPLIER_LSB],
- lpg_config->base_addr, QPNP_PAUSE_LO_MULTIPLIER_LSB, 1, chip);
+ SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+ QPNP_PAUSE_LO_MULTIPLIER_LSB), 1, chip);
if (rc)
return rc;
@@ -722,14 +759,16 @@
rc = qpnp_lpg_save_and_write(value, mask,
&pwm->chip->qpnp_lpg_registers[QPNP_PAUSE_LO_MULTIPLIER_MSB],
- lpg_config->base_addr, QPNP_PAUSE_LO_MULTIPLIER_MSB, 1, chip);
+ SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+ QPNP_PAUSE_LO_MULTIPLIER_MSB), 1, chip);
} else {
value = 0;
mask = QPNP_PAUSE_LO_MULTIPLIER_LSB_MASK;
rc = qpnp_lpg_save_and_write(value, mask,
&pwm->chip->qpnp_lpg_registers[QPNP_PAUSE_LO_MULTIPLIER_LSB],
- lpg_config->base_addr, QPNP_PAUSE_LO_MULTIPLIER_LSB, 1, chip);
+ SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+ QPNP_PAUSE_LO_MULTIPLIER_LSB), 1, chip);
if (rc)
return rc;
@@ -737,7 +776,8 @@
rc = qpnp_lpg_save_and_write(value, mask,
&pwm->chip->qpnp_lpg_registers[QPNP_PAUSE_LO_MULTIPLIER_MSB],
- lpg_config->base_addr, QPNP_PAUSE_LO_MULTIPLIER_MSB, 1, chip);
+ SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+ QPNP_PAUSE_LO_MULTIPLIER_MSB), 1, chip);
return rc;
}
@@ -757,7 +797,8 @@
rc = qpnp_lpg_save_and_write(value, mask,
&pwm->chip->qpnp_lpg_registers[QPNP_HI_INDEX],
- lpg_config->base_addr, QPNP_HI_INDEX, 1, chip);
+ SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+ QPNP_HI_INDEX), 1, chip);
if (rc)
return rc;
@@ -766,7 +807,8 @@
rc = qpnp_lpg_save_and_write(value, mask,
&pwm->chip->qpnp_lpg_registers[QPNP_LO_INDEX],
- lpg_config->base_addr, QPNP_LO_INDEX, 1, chip);
+ SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+ QPNP_LO_INDEX), 1, chip);
return rc;
}
@@ -808,72 +850,97 @@
return rc;
}
-static int qpnp_lpg_enable_lut(struct pwm_device *pwm)
+static int qpnp_lpg_configure_lut_state(struct pwm_device *pwm,
+ enum qpnp_lut_state state)
{
struct qpnp_lpg_config *lpg_config = &pwm->chip->lpg_config;
struct qpnp_lpg_chip *chip = pwm->chip;
- u8 value, mask;
+ u8 value1, value2, mask1, mask2;
+ u8 *reg1, *reg2;
+ u16 addr;
+ int rc;
- value = pwm->chip->qpnp_lpg_registers[QPNP_RAMP_CONTROL];
+ value1 = pwm->chip->qpnp_lpg_registers[QPNP_RAMP_CONTROL];
+ reg1 = &pwm->chip->qpnp_lpg_registers[QPNP_RAMP_CONTROL];
+ reg2 = &pwm->chip->qpnp_lpg_registers[QPNP_ENABLE_CONTROL];
+ mask2 = QPNP_EN_PWM_HIGH_MASK | QPNP_EN_PWM_LO_MASK |
+ QPNP_EN_PWM_OUTPUT_MASK | QPNP_PWM_SRC_SELECT_MASK |
+ QPNP_PWM_EN_RAMP_GEN_MASK;
- QPNP_ENABLE_LUT(value);
+ switch (chip->revision) {
+ case QPNP_LPG_REVISION_0:
+ if (state == QPNP_LUT_ENABLE) {
+ QPNP_ENABLE_LUT_V0(value1);
+ value2 = QPNP_ENABLE_LPG_MODE;
+ } else {
+ QPNP_DISABLE_LUT_V0(value1);
+ value2 = QPNP_DISABLE_LPG_MODE;
+ }
+ mask1 = QPNP_RAMP_START_MASK;
+ addr = SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+ QPNP_RAMP_CONTROL);
+ break;
+ case QPNP_LPG_REVISION_1:
+ if (state == QPNP_LUT_ENABLE) {
+ QPNP_ENABLE_LUT_V1(value1, pwm->pwm_config.channel_id);
+ value2 = QPNP_ENABLE_LPG_MODE;
+ } else {
+ QPNP_DISABLE_LUT_V1(value1, pwm->pwm_config.channel_id);
+ value2 = QPNP_DISABLE_LPG_MODE;
+ }
+ mask1 = BIT(pwm->pwm_config.channel_id);
+ addr = lpg_config->lut_base_addr +
+ SPMI_LPG_REV1_RAMP_CONTROL_OFFSET;
+ break;
+ default:
+ pr_err("Invalid LPG revision\n");
+ return -EINVAL;
+ }
- mask = QPNP_RAMP_START_MASK;
+ rc = qpnp_lpg_save_and_write(value1, mask1, reg1,
+ addr, 1, chip);
+ if (rc)
+ return rc;
+ addr = SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+ QPNP_ENABLE_CONTROL);
+ return qpnp_lpg_save_and_write(value2, mask2, reg2,
+ addr, 1, chip);
- return qpnp_lpg_save_and_write(value, mask,
- &pwm->chip->qpnp_lpg_registers[QPNP_RAMP_CONTROL],
- lpg_config->base_addr, QPNP_RAMP_CONTROL, 1, chip);
}
-static int qpnp_disable_lut(struct pwm_device *pwm)
+static int qpnp_lpg_configure_pwm_state(struct pwm_device *pwm,
+ enum qpnp_pwm_state state)
{
struct qpnp_lpg_config *lpg_config = &pwm->chip->lpg_config;
struct qpnp_lpg_chip *chip = pwm->chip;
u8 value, mask;
+ int rc;
- value = pwm->chip->qpnp_lpg_registers[QPNP_RAMP_CONTROL];
+ if (state == QPNP_PWM_ENABLE)
+ value = QPNP_ENABLE_PWM_MODE;
+ else
+ value = QPNP_DISABLE_PWM_MODE;
- QPNP_DISABLE_LUT(value);
+ mask = QPNP_EN_PWM_HIGH_MASK | QPNP_EN_PWM_LO_MASK |
+ QPNP_EN_PWM_OUTPUT_MASK | QPNP_PWM_SRC_SELECT_MASK |
+ QPNP_PWM_EN_RAMP_GEN_MASK;
- mask = QPNP_RAMP_START_MASK;
-
- return qpnp_lpg_save_and_write(value, mask,
- &pwm->chip->qpnp_lpg_registers[QPNP_RAMP_CONTROL],
- lpg_config->base_addr, QPNP_RAMP_CONTROL, 1, chip);
-}
-
-static int qpnp_lpg_enable_pwm(struct pwm_device *pwm)
-{
- struct qpnp_lpg_config *lpg_config = &pwm->chip->lpg_config;
- struct qpnp_lpg_chip *chip = pwm->chip;
- u8 value, mask;
-
- value = pwm->chip->qpnp_lpg_registers[QPNP_ENABLE_CONTROL];
-
- QPNP_ENABLE_PWM(value);
-
- mask = QPNP_EN_PWM_OUTPUT_MASK;
-
- return qpnp_lpg_save_and_write(value, mask,
+ rc = qpnp_lpg_save_and_write(value, mask,
&pwm->chip->qpnp_lpg_registers[QPNP_ENABLE_CONTROL],
- lpg_config->base_addr, QPNP_ENABLE_CONTROL, 1, chip);
-}
+ SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+ QPNP_ENABLE_CONTROL), 1, chip);
+ if (rc)
+ goto out;
-static int qpnp_disable_pwm(struct pwm_device *pwm)
-{
- struct qpnp_lpg_config *lpg_config = &pwm->chip->lpg_config;
- struct qpnp_lpg_chip *chip = pwm->chip;
- u8 value, mask;
+ /*
+ * Due to LPG hardware bug, in the PWM mode, having enabled PWM,
+ * We have to write PWM values one more time.
+ */
+ if (state == QPNP_PWM_ENABLE)
+ return qpnp_lpg_save_pwm_value(pwm);
- value = pwm->chip->qpnp_lpg_registers[QPNP_ENABLE_CONTROL];
-
- QPNP_DISABLE_PWM(value);
-
- mask = QPNP_EN_PWM_OUTPUT_MASK;
-
- return qpnp_lpg_save_and_write(value, mask,
- &pwm->chip->qpnp_lpg_registers[QPNP_ENABLE_CONTROL],
- lpg_config->base_addr, QPNP_ENABLE_CONTROL, 1, chip);
+out:
+ return rc;
}
static int _pwm_config(struct pwm_device *pwm, int duty_us, int period_us)
@@ -999,18 +1066,23 @@
{
int rc;
struct qpnp_lpg_chip *chip;
+ unsigned long flags;
chip = pwm->chip;
- mutex_lock(&pwm->chip->lpg_mutex);
+ spin_lock_irqsave(&pwm->chip->lpg_lock, flags);
if (QPNP_IS_PWM_CONFIG_SELECTED(
chip->qpnp_lpg_registers[QPNP_ENABLE_CONTROL]))
- rc = qpnp_lpg_enable_pwm(pwm);
+ rc = qpnp_lpg_configure_pwm_state(pwm, QPNP_PWM_ENABLE);
else
- rc = qpnp_lpg_enable_lut(pwm);
+ rc = qpnp_lpg_configure_lut_state(pwm, QPNP_LUT_ENABLE);
- mutex_unlock(&pwm->chip->lpg_mutex);
+ spin_unlock_irqrestore(&pwm->chip->lpg_lock, flags);
+
+ if (rc)
+ pr_err("Failed to enable PWM channel: %d\n",
+ pwm->pwm_config.channel_id);
return rc;
}
@@ -1025,6 +1097,7 @@
{
struct qpnp_lpg_chip *chip;
struct pwm_device *pwm;
+ unsigned long flags;
chip = radix_tree_lookup(&lpg_dev_tree, pwm_id);
@@ -1034,7 +1107,7 @@
return ERR_PTR(-EINVAL);
}
- mutex_lock(&chip->lpg_mutex);
+ spin_lock_irqsave(&chip->lpg_lock, flags);
pwm = &chip->pwm_dev;
@@ -1048,7 +1121,7 @@
pwm->pwm_config.lable = lable;
}
- mutex_unlock(&chip->lpg_mutex);
+ spin_unlock_irqrestore(&chip->lpg_lock, flags);
return pwm;
}
@@ -1061,24 +1134,25 @@
void pwm_free(struct pwm_device *pwm)
{
struct qpnp_pwm_config *pwm_config;
+ unsigned long flags;
if (pwm == NULL || IS_ERR(pwm) || pwm->chip == NULL) {
pr_err("Invalid pwm handle or no pwm_chip\n");
return;
}
- mutex_lock(&pwm->chip->lpg_mutex);
+ spin_lock_irqsave(&pwm->chip->lpg_lock, flags);
pwm_config = &pwm->pwm_config;
if (pwm_config->in_use) {
- qpnp_disable_pwm(pwm);
- qpnp_disable_lut(pwm);
+ qpnp_lpg_configure_pwm_state(pwm, QPNP_PWM_DISABLE);
+ qpnp_lpg_configure_lut_state(pwm, QPNP_LUT_DISABLE);
pwm_config->in_use = 0;
pwm_config->lable = NULL;
}
- mutex_unlock(&pwm->chip->lpg_mutex);
+ spin_unlock_irqrestore(&pwm->chip->lpg_lock, flags);
}
EXPORT_SYMBOL_GPL(pwm_free);
@@ -1091,6 +1165,7 @@
int pwm_config(struct pwm_device *pwm, int duty_us, int period_us)
{
int rc;
+ unsigned long flags;
if (pwm == NULL || IS_ERR(pwm) ||
duty_us > period_us ||
@@ -1103,9 +1178,12 @@
if (!pwm->pwm_config.in_use)
return -EINVAL;
- mutex_lock(&pwm->chip->lpg_mutex);
+ spin_lock_irqsave(&pwm->chip->lpg_lock, flags);
rc = _pwm_config(pwm, duty_us, period_us);
- mutex_unlock(&pwm->chip->lpg_mutex);
+ spin_unlock_irqrestore(&pwm->chip->lpg_lock, flags);
+
+ if (rc)
+ pr_err("Failed to configure PWM mode\n");
return rc;
}
@@ -1143,13 +1221,15 @@
{
struct qpnp_pwm_config *pwm_config;
struct qpnp_lpg_chip *chip;
+ unsigned long flags;
+ int rc = 0;
if (pwm == NULL || IS_ERR(pwm) || pwm->chip == NULL) {
pr_err("Invalid pwm handle or no pwm_chip\n");
return;
}
- mutex_lock(&pwm->chip->lpg_mutex);
+ spin_lock_irqsave(&pwm->chip->lpg_lock, flags);
chip = pwm->chip;
pwm_config = &pwm->pwm_config;
@@ -1157,12 +1237,18 @@
if (pwm_config->in_use) {
if (QPNP_IS_PWM_CONFIG_SELECTED(
chip->qpnp_lpg_registers[QPNP_ENABLE_CONTROL]))
- qpnp_disable_pwm(pwm);
+ rc = qpnp_lpg_configure_pwm_state(pwm,
+ QPNP_PWM_DISABLE);
else
- qpnp_disable_lut(pwm);
+ rc = qpnp_lpg_configure_lut_state(pwm,
+ QPNP_LUT_DISABLE);
}
- mutex_unlock(&pwm->chip->lpg_mutex);
+ spin_unlock_irqrestore(&pwm->chip->lpg_lock, flags);
+
+ if (rc)
+ pr_err("Failed to disable PWM channel: %d\n",
+ pwm_config->channel_id);
}
EXPORT_SYMBOL_GPL(pwm_disable);
@@ -1174,6 +1260,7 @@
int pwm_change_mode(struct pwm_device *pwm, enum pm_pwm_mode mode)
{
int rc;
+ unsigned long flags;
if (pwm == NULL || IS_ERR(pwm) || pwm->chip == NULL) {
pr_err("Invalid pwm handle or no pwm_chip\n");
@@ -1185,15 +1272,17 @@
return -EINVAL;
}
- mutex_lock(&pwm->chip->lpg_mutex);
+ spin_lock_irqsave(&pwm->chip->lpg_lock, flags);
if (mode)
rc = qpnp_configure_lpg_control(pwm);
else
rc = qpnp_configure_pwm_control(pwm);
- mutex_unlock(&pwm->chip->lpg_mutex);
+ spin_unlock_irqrestore(&pwm->chip->lpg_lock, flags);
+ if (rc)
+ pr_err("Failed to change the mode\n");
return rc;
}
EXPORT_SYMBOL_GPL(pwm_change_mode);
@@ -1210,6 +1299,7 @@
struct qpnp_pwm_config *pwm_config;
struct qpnp_lpg_config *lpg_config;
struct qpnp_lpg_chip *chip;
+ unsigned long flags;
int rc = 0;
if (pwm == NULL || IS_ERR(pwm) || period == NULL)
@@ -1217,7 +1307,7 @@
if (pwm->chip == NULL)
return -ENODEV;
- mutex_lock(&pwm->chip->lpg_mutex);
+ spin_lock_irqsave(&pwm->chip->lpg_lock, flags);
chip = pwm->chip;
pwm_config = &pwm->pwm_config;
@@ -1256,7 +1346,7 @@
}
out_unlock:
- mutex_unlock(&pwm->chip->lpg_mutex);
+ spin_unlock_irqrestore(&pwm->chip->lpg_lock, flags);
return rc;
}
EXPORT_SYMBOL(pwm_config_period);
@@ -1270,21 +1360,27 @@
{
struct qpnp_lpg_config *lpg_config;
struct qpnp_pwm_config *pwm_config;
+ unsigned long flags;
int rc = 0;
- if (pwm == NULL || IS_ERR(pwm))
+ if (pwm == NULL || IS_ERR(pwm)) {
+ pr_err("Invalid parameter passed\n");
return -EINVAL;
+ }
- if (pwm->chip == NULL)
+ if (pwm->chip == NULL) {
+ pr_err("Invalid device handle\n");
return -ENODEV;
+ }
lpg_config = &pwm->chip->lpg_config;
pwm_config = &pwm->pwm_config;
- mutex_lock(&pwm->chip->lpg_mutex);
+ spin_lock_irqsave(&pwm->chip->lpg_lock, flags);
if (!pwm_config->in_use || !pwm_config->pwm_period) {
rc = -EINVAL;
+ pr_err("PWM channel isn't in use or period value missing\n");
goto out_unlock;
}
@@ -1300,7 +1396,7 @@
pwm_config->channel_id, rc);
out_unlock:
- mutex_unlock(&pwm->chip->lpg_mutex);
+ spin_unlock_irqrestore(&pwm->chip->lpg_lock, flags);
return rc;
}
EXPORT_SYMBOL_GPL(pwm_config_pwm_value);
@@ -1315,6 +1411,7 @@
int pwm_lut_config(struct pwm_device *pwm, int period_us,
int duty_pct[], struct lut_params lut_params)
{
+ unsigned long flags;
int rc = 0;
if (pwm == NULL || IS_ERR(pwm) || !lut_params.idx_len) {
@@ -1348,11 +1445,14 @@
return -EINVAL;
}
- mutex_lock(&pwm->chip->lpg_mutex);
+ spin_lock_irqsave(&pwm->chip->lpg_lock, flags);
rc = _pwm_lut_config(pwm, period_us, duty_pct, lut_params);
- mutex_unlock(&pwm->chip->lpg_mutex);
+ spin_unlock_irqrestore(&pwm->chip->lpg_lock, flags);
+
+ if (rc)
+ pr_err("Failed to configure LUT\n");
return rc;
}
@@ -1583,7 +1683,7 @@
return -ENOMEM;
}
- mutex_init(&chip->lpg_mutex);
+ spin_lock_init(&chip->lpg_lock);
chip->spmi_dev = spmi;
chip->pwm_dev.chip = chip;
@@ -1596,6 +1696,19 @@
id = chip->pwm_dev.pwm_config.channel_id;
+ spmi_ext_register_readl(chip->spmi_dev->ctrl,
+ chip->spmi_dev->sid,
+ chip->lpg_config.base_addr + SPMI_LPG_REVISION2_OFFSET,
+ (u8 *) &chip->revision, 1);
+
+ if (chip->revision < QPNP_LPG_REVISION_0 ||
+ chip->revision > QPNP_LPG_REVISION_1) {
+ pr_err("Unknown LPG revision detected, rev:%d\n",
+ chip->revision);
+ rc = -EINVAL;
+ goto failed_insert;
+ }
+
rc = radix_tree_insert(&lpg_dev_tree, id, chip);
if (rc) {
@@ -1610,7 +1723,6 @@
kfree(chip->lpg_config.lut_config.duty_pct_list);
failed_config:
dev_set_drvdata(&spmi->dev, NULL);
- mutex_destroy(&chip->lpg_mutex);
kfree(chip);
return rc;
}
@@ -1627,7 +1739,6 @@
if (chip) {
lpg_config = &chip->lpg_config;
kfree(lpg_config->lut_config.duty_pct_list);
- mutex_destroy(&chip->lpg_mutex);
kfree(chip);
}
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index c474e36..376750f 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -290,8 +290,7 @@
config BATTERY_MSM
tristate "MSM battery"
- depends on ARCH_MSM
- default m
+ depends on ARCH_MSM && MSM_ONCRPCROUTER
help
Say Y to enable support for the battery in Qualcomm MSM.
diff --git a/drivers/power/msm_battery.c b/drivers/power/msm_battery.c
index 5abc032..f8186b1 100644
--- a/drivers/power/msm_battery.c
+++ b/drivers/power/msm_battery.c
@@ -1405,8 +1405,10 @@
msm_batt_info.msm_psy_batt = &msm_psy_batt;
#ifndef CONFIG_BATTERY_MSM_FAKE
- rc = msm_batt_register(BATTERY_LOW, BATTERY_ALL_ACTIVITY,
- BATTERY_CB_ID_ALL_ACTIV, BATTERY_ALL_ACTIVITY);
+ rc = msm_batt_register(msm_batt_info.voltage_fail_safe,
+ BATTERY_ALL_ACTIVITY,
+ BATTERY_CB_ID_ALL_ACTIV,
+ BATTERY_ALL_ACTIVITY);
if (rc < 0) {
dev_err(&pdev->dev,
"%s: msm_batt_register failed rc = %d\n", __func__, rc);
diff --git a/drivers/power/pm8921-charger.c b/drivers/power/pm8921-charger.c
index b4080df..3977f17 100644
--- a/drivers/power/pm8921-charger.c
+++ b/drivers/power/pm8921-charger.c
@@ -28,6 +28,7 @@
#include <linux/debugfs.h>
#include <linux/slab.h>
#include <linux/mfd/pm8xxx/batt-alarm.h>
+#include <linux/ratelimit.h>
#include <mach/msm_xo.h>
#include <mach/msm_hsusb.h>
@@ -247,6 +248,7 @@
unsigned int is_bat_cool;
unsigned int is_bat_warm;
unsigned int resume_voltage_delta;
+ int resume_charge_percent;
unsigned int term_current;
unsigned int vbat_channel;
unsigned int batt_temp_channel;
@@ -1427,7 +1429,24 @@
percent_soc = voltage_based_capacity(chip);
if (percent_soc <= 10)
- pr_warn("low battery charge = %d%%\n", percent_soc);
+ pr_warn_ratelimited("low battery charge = %d%%\n",
+ percent_soc);
+
+ if (chip->recent_reported_soc == (chip->resume_charge_percent + 1)
+ && percent_soc == chip->resume_charge_percent) {
+ pr_debug("soc fell below %d. charging enabled.\n",
+ chip->resume_charge_percent);
+ if (chip->is_bat_warm)
+ pr_warn_ratelimited("battery is warm = %d, do not resume charging at %d%%.\n",
+ chip->is_bat_warm,
+ chip->resume_charge_percent);
+ else if (chip->is_bat_cool)
+ pr_warn_ratelimited("battery is cool = %d, do not resume charging at %d%%.\n",
+ chip->is_bat_cool,
+ chip->resume_charge_percent);
+ else
+ pm_chg_vbatdet_set(the_chip, PM8921_CHG_VBATDET_MAX);
+ }
chip->recent_reported_soc = percent_soc;
return percent_soc;
@@ -2903,6 +2922,12 @@
handle_start_ext_chg(chip);
else
handle_stop_ext_chg(chip);
+
+ if (!chip->ext_psy) {
+ power_supply_changed(&chip->dc_psy);
+ power_supply_changed(&chip->batt_psy);
+ }
+
return IRQ_HANDLED;
}
@@ -2985,12 +3010,10 @@
module_param(ichg_threshold_ua, int, 0644);
#define PM8921_CHG_VDDMAX_RES_MV 10
-static void adjust_vdd_max_for_fastchg(struct pm8921_chg_chip *chip)
+static void adjust_vdd_max_for_fastchg(struct pm8921_chg_chip *chip,
+ int vbat_batt_terminal_uv)
{
- int ichg_meas_ua, vbat_uv;
- int ichg_meas_ma;
int adj_vdd_max_mv, programmed_vdd_max;
- int vbat_batt_terminal_uv;
int vbat_batt_terminal_mv;
int reg_loop;
int delta_mv = 0;
@@ -3012,18 +3035,6 @@
reg_loop);
return;
}
-
- pm8921_bms_get_simultaneous_battery_voltage_and_current(&ichg_meas_ua,
- &vbat_uv);
- if (ichg_meas_ua >= 0) {
- pr_debug("Exiting ichg_meas_ua = %d > 0\n", ichg_meas_ua);
- return;
- }
-
- ichg_meas_ma = ichg_meas_ua / 1000;
-
- /* rconn_mohm is in milliOhms */
- vbat_batt_terminal_uv = vbat_uv + ichg_meas_ma * the_chip->rconn_mohm;
vbat_batt_terminal_mv = vbat_batt_terminal_uv/1000;
pm_chg_vddmax_get(the_chip, &programmed_vdd_max);
@@ -3059,10 +3070,10 @@
#define VBAT_TOLERANCE_MV 70
#define CHG_DISABLE_MSLEEP 100
-static int is_charging_finished(struct pm8921_chg_chip *chip)
+static int is_charging_finished(struct pm8921_chg_chip *chip,
+ int vbat_batt_terminal_uv, int ichg_meas_ma)
{
- int vbat_meas_uv, vbat_meas_mv, vbat_programmed, vbatdet_low;
- int ichg_meas_ma, iterm_programmed;
+ int vbat_programmed, iterm_programmed, vbat_intended;
int regulation_loop, fast_chg, vcp;
int rc;
static int last_vbat_programmed = -EINVAL;
@@ -3079,30 +3090,19 @@
if (vcp == 1)
return CHG_IN_PROGRESS;
- vbatdet_low = pm_chg_get_rt_status(chip, VBATDET_LOW_IRQ);
- pr_debug("vbatdet_low = %d\n", vbatdet_low);
- if (vbatdet_low == 1)
- return CHG_IN_PROGRESS;
-
/* reset count if battery is hot/cold */
rc = pm_chg_get_rt_status(chip, BAT_TEMP_OK_IRQ);
pr_debug("batt_temp_ok = %d\n", rc);
if (rc == 0)
return CHG_IN_PROGRESS;
- /* reset count if battery voltage is less than vddmax */
- vbat_meas_uv = get_prop_battery_uvolts(chip);
- if (vbat_meas_uv < 0)
- return CHG_IN_PROGRESS;
- vbat_meas_mv = vbat_meas_uv / 1000;
-
rc = pm_chg_vddmax_get(chip, &vbat_programmed);
if (rc) {
pr_err("couldnt read vddmax rc = %d\n", rc);
return CHG_IN_PROGRESS;
}
- pr_debug("vddmax = %d vbat_meas_mv=%d\n",
- vbat_programmed, vbat_meas_mv);
+ pr_debug("vddmax = %d vbat_batt_terminal_uv=%d\n",
+ vbat_programmed, vbat_batt_terminal_uv);
if (last_vbat_programmed == -EINVAL)
last_vbat_programmed = vbat_programmed;
@@ -3114,6 +3114,20 @@
return CHG_IN_PROGRESS;
}
+ if (chip->is_bat_cool)
+ vbat_intended = chip->cool_bat_voltage;
+ else if (chip->is_bat_warm)
+ vbat_intended = chip->warm_bat_voltage;
+ else
+ vbat_intended = chip->max_voltage_mv;
+
+ if (vbat_batt_terminal_uv / 1000 < vbat_intended) {
+ pr_debug("terminal_uv:%d < vbat_intended:%d.\n",
+ vbat_batt_terminal_uv,
+ vbat_intended);
+ return CHG_IN_PROGRESS;
+ }
+
regulation_loop = pm_chg_get_regulation_loop(chip);
if (regulation_loop < 0) {
pr_err("couldnt read the regulation loop err=%d\n",
@@ -3133,7 +3147,6 @@
return CHG_IN_PROGRESS;
}
- ichg_meas_ma = (get_prop_batt_current(chip)) / 1000;
pr_debug("iterm_programmed = %d ichg_meas_ma=%d\n",
iterm_programmed, ichg_meas_ma);
/*
@@ -3168,9 +3181,22 @@
struct pm8921_chg_chip, eoc_work);
static int count;
int end;
+ int vbat_meas_uv, vbat_meas_mv;
+ int ichg_meas_ua, ichg_meas_ma;
+ int vbat_batt_terminal_uv;
pm_chg_failed_clear(chip, 1);
- end = is_charging_finished(chip);
+
+ pm8921_bms_get_simultaneous_battery_voltage_and_current(
+ &ichg_meas_ua, &vbat_meas_uv);
+ vbat_meas_mv = vbat_meas_uv / 1000;
+ /* rconn_mohm is in milliOhms */
+ ichg_meas_ma = ichg_meas_ua / 1000;
+ vbat_batt_terminal_uv = vbat_meas_uv
+ + ichg_meas_ma
+ * the_chip->rconn_mohm;
+
+ end = is_charging_finished(chip, vbat_batt_terminal_uv, ichg_meas_ma);
if (end == CHG_NOT_IN_PROGRESS) {
count = 0;
@@ -3199,6 +3225,21 @@
if (count == CONSECUTIVE_COUNT) {
count = 0;
pr_info("End of Charging\n");
+ /* set the vbatdet back, in case it was changed
+ * to trigger charging */
+ if (chip->is_bat_cool) {
+ pm_chg_vbatdet_set(the_chip,
+ the_chip->cool_bat_voltage
+ - the_chip->resume_voltage_delta);
+ } else if (chip->is_bat_warm) {
+ pm_chg_vbatdet_set(the_chip,
+ the_chip->warm_bat_voltage
+ - the_chip->resume_voltage_delta);
+ } else {
+ pm_chg_vbatdet_set(the_chip,
+ the_chip->max_voltage_mv
+ - the_chip->resume_voltage_delta);
+ }
pm_chg_auto_enable(chip, 0);
@@ -3213,7 +3254,7 @@
chgdone_irq_handler(chip->pmic_chg_irq[CHGDONE_IRQ], chip);
wake_unlock(&chip->eoc_wake_lock);
} else {
- adjust_vdd_max_for_fastchg(chip);
+ adjust_vdd_max_for_fastchg(chip, vbat_batt_terminal_uv);
pr_debug("EOC count = %d\n", count);
schedule_delayed_work(&chip->eoc_work,
round_jiffies_relative(msecs_to_jiffies
@@ -3707,10 +3748,12 @@
#define CHG_VCP_EN BIT(0)
#define CHG_BAT_TEMP_DIS_BIT BIT(2)
#define SAFE_CURRENT_MA 1500
+#define PM_SUB_REV 0x001
static int __devinit pm8921_chg_hw_init(struct pm8921_chg_chip *chip)
{
int rc;
int vdd_safe;
+ u8 subrev;
/* forcing 19p2mhz before accessing any charger registers */
pm8921_chg_force_19p2mhz_clk(chip);
@@ -3889,8 +3932,21 @@
/* Workarounds for die 3.0 */
if (pm8xxx_get_revision(chip->dev->parent) == PM8XXX_REVISION_8921_3p0
- && pm8xxx_get_version(chip->dev->parent) == PM8XXX_VERSION_8921)
- pm8xxx_writeb(chip->dev->parent, CHG_BUCK_CTRL_TEST3, 0xAC);
+ && pm8xxx_get_version(chip->dev->parent) == PM8XXX_VERSION_8921) {
+ rc = pm8xxx_readb(chip->dev->parent, PM_SUB_REV, &subrev);
+ if (rc) {
+ pr_err("read failed: addr=%03X, rc=%d\n",
+ PM_SUB_REV, rc);
+ return rc;
+ }
+ /* Check if die 3.0.1 is present */
+ if (subrev == 0x1)
+ pm8xxx_writeb(chip->dev->parent,
+ CHG_BUCK_CTRL_TEST3, 0xA4);
+ else
+ pm8xxx_writeb(chip->dev->parent,
+ CHG_BUCK_CTRL_TEST3, 0xAC);
+ }
/* Enable isub_fine resolution AICL for PM8917 */
if (pm8xxx_get_version(chip->dev->parent) == PM8XXX_VERSION_8917) {
@@ -4194,6 +4250,7 @@
chip->min_voltage_mv = pdata->min_voltage;
chip->uvd_voltage_mv = pdata->uvd_thresh_voltage;
chip->resume_voltage_delta = pdata->resume_voltage_delta;
+ chip->resume_charge_percent = pdata->resume_charge_percent;
chip->term_current = pdata->term_current;
chip->vbat_channel = pdata->charger_cdata.vbat_channel;
chip->batt_temp_channel = pdata->charger_cdata.batt_temp_channel;
diff --git a/drivers/power/pm8xxx-ccadc.c b/drivers/power/pm8xxx-ccadc.c
index e48257a..1b9426a 100644
--- a/drivers/power/pm8xxx-ccadc.c
+++ b/drivers/power/pm8xxx-ccadc.c
@@ -480,6 +480,9 @@
struct pm8xxx_ccadc_chip *chip = data;
int rc;
+ if (!the_chip)
+ goto out;
+
pr_debug("irq = %d triggered\n", irq);
data_msb = chip->ccadc_offset >> 8;
data_lsb = chip->ccadc_offset;
@@ -488,6 +491,7 @@
data_msb, data_lsb, 0);
disable_irq_nosync(chip->eoc_irq);
+out:
return IRQ_HANDLED;
}
@@ -685,7 +689,6 @@
goto free_chip;
}
-
disable_irq_nosync(chip->eoc_irq);
platform_set_drvdata(pdev, chip);
diff --git a/drivers/power/power_supply_core.c b/drivers/power/power_supply_core.c
index 24918ca..61f4946 100644
--- a/drivers/power/power_supply_core.c
+++ b/drivers/power/power_supply_core.c
@@ -47,9 +47,27 @@
EXPORT_SYMBOL_GPL(power_supply_set_current_limit);
/**
- * power_supply_set_online - set online state of the power supply
+ * power_supply_set_charging_enabled - enable or disable charging
* @psy: the power supply to control
- * @enable: sets online property of power supply
+ * @enable: sets enable property of power supply
+ */
+int power_supply_set_charging_enabled(struct power_supply *psy, bool enable)
+{
+ const union power_supply_propval ret = {enable,};
+
+ if (psy->set_property)
+ return psy->set_property(psy,
+ POWER_SUPPLY_PROP_CHARGING_ENABLED,
+ &ret);
+
+ return -ENXIO;
+}
+EXPORT_SYMBOL_GPL(power_supply_set_charging_enabled);
+
+/**
+ * power_supply_set_present - set present state of the power supply
+ * @psy: the power supply to control
+ * @enable: sets present property of power supply
*/
int power_supply_set_present(struct power_supply *psy, bool enable)
{
diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c
index 4368e7d..7eb285b 100644
--- a/drivers/power/power_supply_sysfs.c
+++ b/drivers/power/power_supply_sysfs.c
@@ -138,6 +138,7 @@
POWER_SUPPLY_ATTR(health),
POWER_SUPPLY_ATTR(present),
POWER_SUPPLY_ATTR(online),
+ POWER_SUPPLY_ATTR(charging_enabled),
POWER_SUPPLY_ATTR(technology),
POWER_SUPPLY_ATTR(cycle_count),
POWER_SUPPLY_ATTR(voltage_max),
diff --git a/drivers/power/qpnp-charger.c b/drivers/power/qpnp-charger.c
index 0185a3e..8d5244b 100644
--- a/drivers/power/qpnp-charger.c
+++ b/drivers/power/qpnp-charger.c
@@ -94,6 +94,11 @@
#define QPNP_CHARGER_DEV_NAME "qcom,qpnp-charger"
+/* Status bits and masks */
+#define CHGR_BOOT_DONE BIT(7)
+#define CHGR_CHG_EN BIT(7)
+#define CHGR_ON_BAT_FORCE_BIT BIT(0)
+
/* Interrupt definitions */
/* smbb_chg_interrupts */
#define CHG_DONE_IRQ BIT(7)
@@ -198,7 +203,7 @@
};
static struct qpnp_chg_chip *the_chip;
-static int charging_disabled;
+static bool charging_disabled;
static struct of_device_id qpnp_charger_match_table[] = {
{ .compatible = QPNP_CHARGER_DEV_NAME, },
@@ -396,7 +401,7 @@
qpnp_chg_chgr_chg_failed_irq_handler(int irq, void *_chip)
{
struct qpnp_chg_chip *chip = _chip;
- int rc, usb_present;
+ int rc;
rc = qpnp_chg_masked_write(chip,
chip->chgr_base + CHGR_CHG_FAILED,
@@ -405,14 +410,6 @@
if (rc)
pr_err("Failed to write chg_fail clear bit!\n");
- /* Hack: recheck usbin_valid status after chg_fail triggered */
- usb_present = qpnp_chg_is_usb_chg_plugged_in(chip);
- pr_debug("usb_status: %d\n", usb_present);
- if (usb_present)
- qpnp_chg_usb_usbin_valid_irq_handler(chip->usbin_valid_irq,
- _chip);
-
-
return IRQ_HANDLED;
}
@@ -433,6 +430,7 @@
};
static enum power_supply_property msm_batt_power_props[] = {
+ POWER_SUPPLY_PROP_CHARGING_ENABLED,
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_CHARGE_TYPE,
POWER_SUPPLY_PROP_HEALTH,
@@ -692,6 +690,24 @@
}
static int
+qpnp_chg_charge_dis(struct qpnp_chg_chip *chip, int disable)
+{
+ /* This bit forces the charger to run off of the battery rather
+ * than a connected charger */
+ return qpnp_chg_masked_write(chip, chip->chgr_base + CHGR_CHG_CTRL,
+ CHGR_ON_BAT_FORCE_BIT,
+ disable ? CHGR_ON_BAT_FORCE_BIT : 0, 1);
+}
+
+static int
+qpnp_chg_charge_en(struct qpnp_chg_chip *chip, int enable)
+{
+ return qpnp_chg_masked_write(chip, chip->chgr_base + CHGR_CHG_CTRL,
+ CHGR_CHG_EN,
+ enable ? CHGR_CHG_EN : 0, 1);
+}
+
+static int
qpnp_batt_power_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
@@ -736,6 +752,9 @@
case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
val->intval = get_prop_full_design(chip);
break;
+ case POWER_SUPPLY_PROP_CHARGING_ENABLED:
+ val->intval = !charging_disabled;
+ break;
default:
return -EINVAL;
}
@@ -743,27 +762,28 @@
return 0;
}
-#define CHGR_BOOT_DONE BIT(7)
-#define CHGR_CHG_EN BIT(7)
-#define CHGR_ON_BAT_FORCE_BIT BIT(0)
-#define CHGR_BAT_IF_CONST_RDS_EN BIT(7)
-#define CHGR_BAT_IF_VCP_EN BIT(0)
static int
-qpnp_chg_charge_dis(struct qpnp_chg_chip *chip, int disable)
+qpnp_batt_power_set_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ const union power_supply_propval *val)
{
- /* This bit forces the charger to run off of the battery rather
- * than a connected charger */
- return qpnp_chg_masked_write(chip, chip->chgr_base + CHGR_CHG_CTRL,
- CHGR_ON_BAT_FORCE_BIT,
- disable ? CHGR_ON_BAT_FORCE_BIT : 0, 1);
-}
+ struct qpnp_chg_chip *chip = container_of(psy, struct qpnp_chg_chip,
+ batt_psy);
-static int
-qpnp_chg_charge_en(struct qpnp_chg_chip *chip, int enable)
-{
- return qpnp_chg_masked_write(chip, chip->chgr_base + CHGR_CHG_CTRL,
- CHGR_CHG_EN,
- enable ? CHGR_CHG_EN : 0, 1);
+ switch (psp) {
+ case POWER_SUPPLY_PROP_CHARGING_ENABLED:
+ if (val->intval)
+ qpnp_chg_charge_en(chip, val->intval);
+ else
+ qpnp_chg_charge_dis(chip, val->intval);
+ charging_disabled = !(val->intval);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ power_supply_changed(&chip->batt_psy);
+ return 0;
}
static int
@@ -782,7 +802,7 @@
qpnp_chg_charge_dis(chip, charging_disabled);
return 0;
}
-module_param_call(disabled, qpnp_chg_set_disable_status_param, param_get_uint,
+module_param_call(disabled, qpnp_chg_set_disable_status_param, param_get_bool,
&charging_disabled, 0644);
#define QPNP_CHG_VINMIN_MIN_MV 3400
@@ -1234,6 +1254,7 @@
chip->batt_psy.properties = msm_batt_power_props;
chip->batt_psy.num_properties = ARRAY_SIZE(msm_batt_power_props);
chip->batt_psy.get_property = qpnp_batt_power_get_property;
+ chip->batt_psy.set_property = qpnp_batt_power_set_property;
chip->batt_psy.external_power_changed =
qpnp_batt_external_power_changed;
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index 6b0916e..8f924d6 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -195,6 +195,15 @@
via I2C bus. The provided regulator is suitable for S3C6410
and S5PC1XX chips to control VCC_CORE and VCC_USIM voltages.
+config REGULATOR_ONSEMI_NCP6335D
+ tristate "OnSemi NCP6335D regulator support"
+ depends on I2C
+ help
+ This driver supports the OnSemi NCP6335D switching voltage regulator
+ (buck convertor). The regulator is controlled using an I2C interface
+ and supports a programmable voltage range from 0.6V to 1.4V in steps
+ of 6.25mV.
+
config REGULATOR_PCAP
tristate "Motorola PCAP2 regulator driver"
depends on EZX_PCAP
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index 7fa396f..054ce42 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -33,6 +33,7 @@
obj-$(CONFIG_REGULATOR_MC13783) += mc13783-regulator.o
obj-$(CONFIG_REGULATOR_MC13892) += mc13892-regulator.o
obj-$(CONFIG_REGULATOR_MC13XXX_CORE) += mc13xxx-regulator-core.o
+obj-$(CONFIG_REGULATOR_ONSEMI_NCP6335D) += onsemi-ncp6335d.o
obj-$(CONFIG_REGULATOR_PCAP) += pcap-regulator.o
obj-$(CONFIG_REGULATOR_PCF50633) += pcf50633-regulator.o
obj-$(CONFIG_REGULATOR_S5M8767) += s5m8767.o
diff --git a/drivers/regulator/onsemi-ncp6335d.c b/drivers/regulator/onsemi-ncp6335d.c
new file mode 100644
index 0000000..a0c90f0
--- /dev/null
+++ b/drivers/regulator/onsemi-ncp6335d.c
@@ -0,0 +1,376 @@
+/* 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/module.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/regulator/driver.h>
+#include <linux/regmap.h>
+#include <linux/regulator/onsemi-ncp6335d.h>
+
+/* registers */
+#define REG_NCP6335D_PID 0x03
+#define REG_NCP6335D_PROGVSEL1 0x10
+#define REG_NCP6335D_PROGVSEL0 0x11
+#define REG_NCP6335D_PGOOD 0x12
+#define REG_NCP6335D_TIMING 0x13
+#define REG_NCP6335D_COMMAND 0x14
+
+/* constraints */
+#define NCP6335D_MIN_VOLTAGE_UV 600000
+#define NCP6335D_STEP_VOLTAGE_UV 6250
+#define NCP6335D_MIN_SLEW_NS 166
+#define NCP6335D_MAX_SLEW_NS 1333
+
+/* bits */
+#define NCP6335D_ENABLE BIT(7)
+#define NCP6335D_DVS_PWM_MODE BIT(5)
+#define NCP6335D_PWM_MODE1 BIT(6)
+#define NCP6335D_PWM_MODE0 BIT(7)
+#define NCP6335D_PGOOD_DISCHG BIT(4)
+
+#define NCP6335D_VOUT_SEL_MASK 0x7F
+#define NCP6335D_SLEW_MASK 0x18
+#define NCP6335D_SLEW_SHIFT 0x3
+
+struct ncp6335d_info {
+ struct regulator_dev *regulator;
+ struct regulator_init_data *init_data;
+ struct regmap *regmap;
+ struct device *dev;
+ unsigned int vsel_reg;
+ unsigned int mode_bit;
+ int curr_voltage;
+ int slew_rate;
+};
+
+static void dump_registers(struct ncp6335d_info *dd,
+ unsigned int reg, const char *func)
+{
+ unsigned int val = 0;
+
+ regmap_read(dd->regmap, reg, &val);
+ dev_dbg(dd->dev, "%s: NCP6335D: Reg = %x, Val = %x\n", func, reg, val);
+}
+
+static void ncp633d_slew_delay(struct ncp6335d_info *dd,
+ int prev_uV, int new_uV)
+{
+ u8 val;
+ int delay;
+
+ val = abs(prev_uV - new_uV) / NCP6335D_STEP_VOLTAGE_UV;
+ delay = (val * dd->slew_rate / 1000) + 1;
+
+ dev_dbg(dd->dev, "Slew Delay = %d\n", delay);
+
+ udelay(delay);
+}
+
+static int ncp6335d_enable(struct regulator_dev *rdev)
+{
+ int rc;
+ struct ncp6335d_info *dd = rdev_get_drvdata(rdev);
+
+ rc = regmap_update_bits(dd->regmap, dd->vsel_reg,
+ NCP6335D_ENABLE, NCP6335D_ENABLE);
+ if (rc)
+ dev_err(dd->dev, "Unable to enable regualtor rc(%d)", rc);
+
+ dump_registers(dd, dd->vsel_reg, __func__);
+
+ return rc;
+}
+
+static int ncp6335d_disable(struct regulator_dev *rdev)
+{
+ int rc;
+ struct ncp6335d_info *dd = rdev_get_drvdata(rdev);
+
+ rc = regmap_update_bits(dd->regmap, dd->vsel_reg,
+ NCP6335D_ENABLE, 0);
+ if (rc)
+ dev_err(dd->dev, "Unable to disable regualtor rc(%d)", rc);
+
+ dump_registers(dd, dd->vsel_reg, __func__);
+
+ return rc;
+}
+
+static int ncp6335d_get_voltage(struct regulator_dev *rdev)
+{
+ unsigned int val;
+ int rc;
+ struct ncp6335d_info *dd = rdev_get_drvdata(rdev);
+
+ rc = regmap_read(dd->regmap, dd->vsel_reg, &val);
+ if (rc) {
+ dev_err(dd->dev, "Unable to get volatge rc(%d)", rc);
+ return rc;
+ }
+ dd->curr_voltage = ((val & NCP6335D_VOUT_SEL_MASK) *
+ NCP6335D_STEP_VOLTAGE_UV) + NCP6335D_MIN_VOLTAGE_UV;
+
+ dump_registers(dd, dd->vsel_reg, __func__);
+
+ return dd->curr_voltage;
+}
+
+static int ncp6335d_set_voltage(struct regulator_dev *rdev,
+ int min_uV, int max_uV, unsigned *selector)
+{
+ int rc, set_val, new_uV;
+ struct ncp6335d_info *dd = rdev_get_drvdata(rdev);
+
+ set_val = DIV_ROUND_UP(min_uV - NCP6335D_MIN_VOLTAGE_UV,
+ NCP6335D_STEP_VOLTAGE_UV);
+ new_uV = (set_val * NCP6335D_STEP_VOLTAGE_UV) +
+ NCP6335D_MIN_VOLTAGE_UV;
+ if (new_uV > max_uV) {
+ dev_err(dd->dev, "Unable to set volatge (%d %d)\n",
+ min_uV, max_uV);
+ return -EINVAL;
+ }
+
+ rc = regmap_update_bits(dd->regmap, dd->vsel_reg,
+ NCP6335D_VOUT_SEL_MASK, (set_val & NCP6335D_VOUT_SEL_MASK));
+ if (rc) {
+ dev_err(dd->dev, "Unable to set volatge (%d %d)\n",
+ min_uV, max_uV);
+ } else {
+ ncp633d_slew_delay(dd, dd->curr_voltage, new_uV);
+ dd->curr_voltage = new_uV;
+ }
+
+ dump_registers(dd, dd->vsel_reg, __func__);
+
+ return rc;
+}
+
+static int ncp6335d_set_mode(struct regulator_dev *rdev,
+ unsigned int mode)
+{
+ int rc;
+ struct ncp6335d_info *dd = rdev_get_drvdata(rdev);
+
+ /* only FAST and NORMAL mode types are supported */
+ if (mode != REGULATOR_MODE_FAST && mode != REGULATOR_MODE_NORMAL) {
+ dev_err(dd->dev, "Mode %d not supported\n", mode);
+ return -EINVAL;
+ }
+
+ rc = regmap_update_bits(dd->regmap, REG_NCP6335D_COMMAND, dd->mode_bit,
+ (mode == REGULATOR_MODE_FAST) ? dd->mode_bit : 0);
+ if (rc) {
+ dev_err(dd->dev, "Unable to set operating mode rc(%d)", rc);
+ return rc;
+ }
+
+ rc = regmap_update_bits(dd->regmap, REG_NCP6335D_COMMAND,
+ NCP6335D_DVS_PWM_MODE,
+ (mode == REGULATOR_MODE_FAST) ?
+ NCP6335D_DVS_PWM_MODE : 0);
+ if (rc)
+ dev_err(dd->dev, "Unable to set DVS trans. mode rc(%d)", rc);
+
+ dump_registers(dd, REG_NCP6335D_COMMAND, __func__);
+
+ return rc;
+}
+
+static unsigned int ncp6335d_get_mode(struct regulator_dev *rdev)
+{
+ unsigned int val;
+ int rc;
+ struct ncp6335d_info *dd = rdev_get_drvdata(rdev);
+
+ rc = regmap_read(dd->regmap, REG_NCP6335D_COMMAND, &val);
+ if (rc) {
+ dev_err(dd->dev, "Unable to get regulator mode rc(%d)\n", rc);
+ return rc;
+ }
+
+ dump_registers(dd, REG_NCP6335D_COMMAND, __func__);
+
+ if (val & dd->mode_bit)
+ return REGULATOR_MODE_FAST;
+
+ return REGULATOR_MODE_NORMAL;
+}
+
+static struct regulator_ops ncp6335d_ops = {
+ .set_voltage = ncp6335d_set_voltage,
+ .get_voltage = ncp6335d_get_voltage,
+ .enable = ncp6335d_enable,
+ .disable = ncp6335d_disable,
+ .set_mode = ncp6335d_set_mode,
+ .get_mode = ncp6335d_get_mode,
+};
+
+static struct regulator_desc rdesc = {
+ .name = "ncp6335d",
+ .owner = THIS_MODULE,
+ .n_voltages = 128,
+ .ops = &ncp6335d_ops,
+};
+
+static int __devinit ncp6335d_init(struct ncp6335d_info *dd,
+ const struct ncp6335d_platform_data *pdata)
+{
+ int rc;
+ unsigned int val;
+
+ switch (pdata->default_vsel) {
+ case NCP6335D_VSEL0:
+ dd->vsel_reg = REG_NCP6335D_PROGVSEL0;
+ dd->mode_bit = NCP6335D_PWM_MODE0;
+ break;
+ case NCP6335D_VSEL1:
+ dd->vsel_reg = REG_NCP6335D_PROGVSEL1;
+ dd->mode_bit = NCP6335D_PWM_MODE1;
+ break;
+ default:
+ dev_err(dd->dev, "Invalid VSEL ID %d\n", pdata->default_vsel);
+ return -EINVAL;
+ }
+
+ /* get the current programmed voltage */
+ rc = regmap_read(dd->regmap, dd->vsel_reg, &val);
+ if (rc) {
+ dev_err(dd->dev, "Unable to get volatge rc(%d)", rc);
+ return rc;
+ }
+ dd->curr_voltage = ((val & NCP6335D_VOUT_SEL_MASK) *
+ NCP6335D_STEP_VOLTAGE_UV) + NCP6335D_MIN_VOLTAGE_UV;
+
+ /* set discharge */
+ rc = regmap_update_bits(dd->regmap, REG_NCP6335D_PGOOD,
+ NCP6335D_PGOOD_DISCHG,
+ (pdata->discharge_enable ?
+ NCP6335D_PGOOD_DISCHG : 0));
+ if (rc) {
+ dev_err(dd->dev, "Unable to set Active Discharge rc(%d)\n", rc);
+ return -EINVAL;
+ }
+
+ /* set slew rate */
+ if (pdata->slew_rate_ns < NCP6335D_MIN_SLEW_NS ||
+ pdata->slew_rate_ns > NCP6335D_MAX_SLEW_NS) {
+ dev_err(dd->dev, "Invalid slew rate %d\n", pdata->slew_rate_ns);
+ return -EINVAL;
+ }
+ val = DIV_ROUND_UP(pdata->slew_rate_ns - NCP6335D_MIN_SLEW_NS,
+ NCP6335D_MIN_SLEW_NS);
+ val >>= 1;
+ dd->slew_rate = val * NCP6335D_MIN_SLEW_NS;
+
+ rc = regmap_update_bits(dd->regmap, REG_NCP6335D_TIMING,
+ NCP6335D_SLEW_MASK, val << NCP6335D_SLEW_SHIFT);
+ if (rc)
+ dev_err(dd->dev, "Unable to set slew rate rc(%d)\n", rc);
+
+ dump_registers(dd, REG_NCP6335D_PROGVSEL0, __func__);
+ dump_registers(dd, REG_NCP6335D_TIMING, __func__);
+ dump_registers(dd, REG_NCP6335D_PGOOD, __func__);
+
+ return rc;
+}
+
+static struct regmap_config ncp6335d_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+};
+
+static int __devinit ncp6335d_regulator_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int rc;
+ unsigned int val = 0;
+ struct ncp6335d_info *dd;
+ const struct ncp6335d_platform_data *pdata;
+
+ pdata = client->dev.platform_data;
+ if (!pdata) {
+ dev_err(&client->dev, "Platform data not specified\n");
+ return -EINVAL;
+ }
+
+ dd = devm_kzalloc(&client->dev, sizeof(*dd), GFP_KERNEL);
+ if (!dd) {
+ dev_err(&client->dev, "Unable to allocate memory\n");
+ return -ENOMEM;
+ }
+
+ dd->regmap = devm_regmap_init_i2c(client, &ncp6335d_regmap_config);
+ if (IS_ERR(dd->regmap)) {
+ dev_err(&client->dev, "Error allocating regmap\n");
+ return PTR_ERR(dd->regmap);
+ }
+
+ rc = regmap_read(dd->regmap, REG_NCP6335D_PID, &val);
+ if (rc) {
+ dev_err(&client->dev, "Unable to identify NCP6335D, rc(%d)\n",
+ rc);
+ return rc;
+ }
+ dev_info(&client->dev, "Detected Regulator NCP6335D PID = %d\n", val);
+
+ dd->init_data = pdata->init_data;
+ dd->dev = &client->dev;
+ i2c_set_clientdata(client, dd);
+
+ rc = ncp6335d_init(dd, pdata);
+ if (rc) {
+ dev_err(&client->dev, "Unable to intialize the regulator\n");
+ return -EINVAL;
+ }
+
+ dd->regulator = regulator_register(&rdesc, &client->dev,
+ dd->init_data, dd, NULL);
+ if (IS_ERR(dd->regulator)) {
+ dev_err(&client->dev, "Unable to register regulator rc(%ld)",
+ PTR_ERR(dd->regulator));
+ return PTR_ERR(dd->regulator);
+ }
+
+ return 0;
+}
+
+static int __devexit ncp6335d_regulator_remove(struct i2c_client *client)
+{
+ struct ncp6335d_info *dd = i2c_get_clientdata(client);
+
+ regulator_unregister(dd->regulator);
+
+ return 0;
+}
+
+static const struct i2c_device_id ncp6335d_id[] = {
+ {"ncp6335d", -1},
+ { },
+};
+
+static struct i2c_driver ncp6335d_regulator_driver = {
+ .driver = {
+ .name = "ncp6335d-regulator",
+ },
+ .probe = ncp6335d_regulator_probe,
+ .remove = __devexit_p(ncp6335d_regulator_remove),
+ .id_table = ncp6335d_id,
+};
+
+module_i2c_driver(ncp6335d_regulator_driver);
+
+MODULE_DESCRIPTION("OnSemi-NCP6335D regulator driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/slimbus/slim-msm-ctrl.c b/drivers/slimbus/slim-msm-ctrl.c
index e2f37cc..58a1d66 100644
--- a/drivers/slimbus/slim-msm-ctrl.c
+++ b/drivers/slimbus/slim-msm-ctrl.c
@@ -1257,7 +1257,7 @@
else
clk_prepare_enable(dev->hclk);
- ret = msm_slim_sps_init(dev, bam_mem, MGR_STATUS);
+ ret = msm_slim_sps_init(dev, bam_mem, MGR_STATUS, false);
if (ret != 0) {
dev_err(dev->dev, "error SPS init\n");
goto err_sps_init_failed;
diff --git a/drivers/slimbus/slim-msm.c b/drivers/slimbus/slim-msm.c
index 8a1ea84..7cd34d3 100644
--- a/drivers/slimbus/slim-msm.c
+++ b/drivers/slimbus/slim-msm.c
@@ -494,7 +494,7 @@
/* Registers BAM h/w resource with SPS driver and initializes msgq endpoints */
int msm_slim_sps_init(struct msm_slim_ctrl *dev, struct resource *bam_mem,
- u32 pipe_reg)
+ u32 pipe_reg, bool remote)
{
int i, ret;
u32 bam_handle;
@@ -521,10 +521,16 @@
bam_props.virt_addr = dev->bam.base;
bam_props.phys_addr = bam_mem->start;
bam_props.irq = dev->bam.irq;
- bam_props.manage = SPS_BAM_MGR_LOCAL;
+ if (!remote) {
+ bam_props.manage = SPS_BAM_MGR_LOCAL;
+ bam_props.sec_config = SPS_BAM_SEC_DO_CONFIG;
+ } else {
+ bam_props.manage = SPS_BAM_MGR_DEVICE_REMOTE |
+ SPS_BAM_MGR_MULTI_EE;
+ bam_props.sec_config = SPS_BAM_SEC_DO_NOT_CONFIG;
+ }
bam_props.summing_threshold = MSM_SLIM_PERF_SUMM_THRESHOLD;
- bam_props.sec_config = SPS_BAM_SEC_DO_CONFIG;
bam_props.p_sec_config_props = &sec_props;
bam_props.options = SPS_O_DESC_DONE | SPS_O_ERROR |
diff --git a/drivers/slimbus/slim-msm.h b/drivers/slimbus/slim-msm.h
index f68475a..35bb040 100644
--- a/drivers/slimbus/slim-msm.h
+++ b/drivers/slimbus/slim-msm.h
@@ -246,6 +246,6 @@
u32 *msm_get_msg_buf(struct msm_slim_ctrl *dev, int len);
int msm_slim_rx_msgq_get(struct msm_slim_ctrl *dev, u32 *data, int offset);
int msm_slim_sps_init(struct msm_slim_ctrl *dev, struct resource *bam_mem,
- u32 pipe_reg);
+ u32 pipe_reg, bool remote);
void msm_slim_sps_exit(struct msm_slim_ctrl *dev);
#endif
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index d082273..e70924c 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -91,3 +91,19 @@
help
Enable this to plug the SPEAr thermal sensor driver into the Linux
thermal framework
+
+config THERMAL_QPNP
+ tristate "Qualcomm Plug-and-Play PMIC Temperature Alarm"
+ depends on THERMAL
+ depends on OF
+ depends on SPMI
+ depends on OF_SPMI
+ help
+ This enables a thermal Sysfs driver for Qualcomm plug-and-play (QPNP)
+ PMIC devices. It shows up in Sysfs as a thermal zone with multiple
+ trip points. The temperature reported by the thermal zone reflects the
+ real time die temperature if an ADC is present or an estimate of the
+ temperature based upon the over temperature stage value if no ADC is
+ available. If allowed via compile time configuration; enabling the
+ thermal zone device via the mode file results in shifting PMIC over
+ temperature shutdown control from hardware to software.
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
index f7e7cc6..3b2b3a8 100644
--- a/drivers/thermal/Makefile
+++ b/drivers/thermal/Makefile
@@ -10,3 +10,4 @@
obj-$(CONFIG_THERMAL_MONITOR) += msm_thermal.o
obj-$(CONFIG_SPEAR_THERMAL) += spear_thermal.o
obj-$(CONFIG_THERMAL_TSENS8974) += msm8974-tsens.o
+obj-$(CONFIG_THERMAL_QPNP) += qpnp-temp-alarm.o
diff --git a/drivers/thermal/msm8974-tsens.c b/drivers/thermal/msm8974-tsens.c
index 77cc1f9..f3387d9 100644
--- a/drivers/thermal/msm8974-tsens.c
+++ b/drivers/thermal/msm8974-tsens.c
@@ -56,6 +56,13 @@
#define TSENS_S0_MAIN_CONFIG(n) ((n) + 0x38)
#define TSENS_SN_REMOTE_CONFIG(n) ((n) + 0x3c)
+#define TSENS_EEPROM(n) ((n) + 0xd0)
+#define TSENS_EEPROM_REDUNDANCY_SEL(n) ((n) + 0x1cc)
+#define TSENS_EEPROM_BACKUP_REGION(n) ((n) + 0x440)
+
+#define TSENS_MAIN_CALIB_ADDR_RANGE 6
+#define TSENS_BACKUP_CALIB_ADDR_RANGE 4
+
/* TSENS calibration Mask data */
#define TSENS_BASE1_MASK 0xff
#define TSENS0_POINT1_MASK 0x3f00
@@ -67,8 +74,11 @@
#define TSENS6_POINT1_MASK 0x3f000
#define TSENS7_POINT1_MASK 0xfc0000
#define TSENS8_POINT1_MASK 0x3f000000
+#define TSENS8_POINT1_MASK_BACKUP 0x3f
#define TSENS9_POINT1_MASK 0x3f
+#define TSENS9_POINT1_MASK_BACKUP 0xfc0
#define TSENS10_POINT1_MASK 0xfc00
+#define TSENS10_POINT1_MASK_BACKUP 0x3f000
#define TSENS_CAL_SEL_0_1 0xc0000000
#define TSENS_CAL_SEL_2 0x40000000
#define TSENS_CAL_SEL_SHIFT 30
@@ -85,31 +95,55 @@
#define TSENS6_POINT1_SHIFT 12
#define TSENS7_POINT1_SHIFT 18
#define TSENS8_POINT1_SHIFT 24
+#define TSENS9_POINT1_BACKUP_SHIFT 6
#define TSENS10_POINT1_SHIFT 6
+#define TSENS10_POINT1_BACKUP_SHIFT 12
#define TSENS_POINT2_BASE_SHIFT 12
+#define TSENS_POINT2_BASE_BACKUP_SHIFT 18
#define TSENS0_POINT2_SHIFT 20
+#define TSENS0_POINT2_BACKUP_SHIFT 26
#define TSENS1_POINT2_SHIFT 26
+#define TSENS2_POINT2_BACKUP_SHIFT 6
#define TSENS3_POINT2_SHIFT 6
+#define TSENS3_POINT2_BACKUP_SHIFT 12
#define TSENS4_POINT2_SHIFT 12
+#define TSENS4_POINT2_BACKUP_SHIFT 18
#define TSENS5_POINT2_SHIFT 18
+#define TSENS5_POINT2_BACKUP_SHIFT 24
#define TSENS6_POINT2_SHIFT 24
+#define TSENS7_POINT2_BACKUP_SHIFT 6
#define TSENS8_POINT2_SHIFT 6
+#define TSENS8_POINT2_BACKUP_SHIFT 12
#define TSENS9_POINT2_SHIFT 12
+#define TSENS9_POINT2_BACKUP_SHIFT 18
#define TSENS10_POINT2_SHIFT 18
+#define TSENS10_POINT2_BACKUP_SHIFT 24
#define TSENS_BASE2_MASK 0xff000
+#define TSENS_BASE2_BACKUP_MASK 0xfc0000
#define TSENS0_POINT2_MASK 0x3f00000
+#define TSENS0_POINT2_BACKUP_MASK 0xfc000000
#define TSENS1_POINT2_MASK 0xfc000000
+#define TSENS1_POINT2_BACKUP_MASK 0x3f
#define TSENS2_POINT2_MASK 0x3f
+#define TSENS2_POINT2_BACKUP_MASK 0xfc0
#define TSENS3_POINT2_MASK 0xfc00
+#define TSENS3_POINT2_BACKUP_MASK 0x3f000
#define TSENS4_POINT2_MASK 0x3f000
+#define TSENS4_POINT2_BACKUP_MASK 0xfc0000
#define TSENS5_POINT2_MASK 0xfc0000
+#define TSENS5_POINT2_BACKUP_MASK 0x3f000000
#define TSENS6_POINT2_MASK 0x3f000000
+#define TSENS6_POINT2_BACKUP_MASK 0x3f
#define TSENS7_POINT2_MASK 0x3f
+#define TSENS7_POINT2_BACKUP_MASK 0xfc00
#define TSENS8_POINT2_MASK 0xfc00
+#define TSENS8_POINT2_BACKUP_MASK 0x3f000
#define TSENS9_POINT2_MASK 0x3f000
+#define TSENS9_POINT2_BACKUP_MASK 0xfc0000
#define TSENS10_POINT2_MASK 0xfc0000
+#define TSENS10_POINT2_BACKUP_MASK 0x3f000000
#define TSENS_BIT_APPEND 0x3
#define TSENS_CAL_DEGC_POINT1 30
@@ -128,6 +162,10 @@
#define TSENS_SN_MIN_MAX_STATUS_CTRL_DATA 0x3ffc00
#define TSENS_SN_REMOTE_CFG_DATA 0x11c3
+#define TSENS_QFPROM_BACKUP_SEL 0x3
+#define TSENS_QFPROM_BACKUP_REDUN_SEL 0xe0000000
+#define TSENS_QFPROM_BACKUP_REDUN_SHIFT 29
+
/* Trips: warm and cool */
enum tsens_trip_type {
TSENS_TRIP_WARM = 0,
@@ -149,6 +187,7 @@
struct tsens_tm_device {
struct platform_device *pdev;
bool prev_reading_avail;
+ bool calibration_less_mode;
int tsens_factor;
uint32_t tsens_num_sensor;
int tsens_irq;
@@ -515,78 +554,185 @@
int tsens6_point2 = 0, tsens7_point2 = 0, tsens8_point2 = 0;
int tsens9_point2 = 0, tsens10_point2 = 0;
int tsens_base2_data = 0, tsens_calibration_mode = 0, temp = 0;
- uint32_t calib_data[5];
+ uint32_t calib_data[6], calib_redun_sel, calib_data_backup[4];
- for (i = 0; i < 5; i++)
- calib_data[i] = readl_relaxed(tmdev->tsens_calib_addr
+ if (tmdev->calibration_less_mode)
+ goto calibration_less_mode;
+
+ calib_redun_sel = readl_relaxed(
+ TSENS_EEPROM_REDUNDANCY_SEL(tmdev->tsens_calib_addr));
+ calib_redun_sel = calib_redun_sel & TSENS_QFPROM_BACKUP_REDUN_SEL;
+ calib_redun_sel >>= TSENS_QFPROM_BACKUP_REDUN_SHIFT;
+
+ for (i = 0; i < TSENS_MAIN_CALIB_ADDR_RANGE; i++)
+ calib_data[i] = readl_relaxed(
+ (TSENS_EEPROM(tmdev->tsens_calib_addr))
+ (i * TSENS_SN_ADDR_OFFSET));
- tsens_calibration_mode = (calib_data[1] & TSENS_CAL_SEL_0_1)
- >> TSENS_CAL_SEL_SHIFT;
- temp = (calib_data[3] & TSENS_CAL_SEL_2)
- >> TSENS_CAL_SEL_SHIFT_2;
- tsens_calibration_mode |= temp;
+ if (calib_redun_sel == TSENS_QFPROM_BACKUP_SEL) {
+ tsens_calibration_mode = (calib_data[4] & TSENS_CAL_SEL_0_1)
+ >> TSENS_CAL_SEL_SHIFT;
+ temp = (calib_data[5] & TSENS_CAL_SEL_2)
+ >> TSENS_CAL_SEL_SHIFT_2;
+ tsens_calibration_mode |= temp;
- if (tsens_calibration_mode == 0) {
- pr_debug("TSENS is calibrationless mode\n");
+ for (i = 0; i < TSENS_BACKUP_CALIB_ADDR_RANGE; i++)
+ calib_data_backup[i] = readl_relaxed(
+ (TSENS_EEPROM_BACKUP_REGION(
+ tmdev->tsens_calib_addr))
+ + (i * TSENS_SN_ADDR_OFFSET));
+
+ if ((tsens_calibration_mode == TSENS_ONE_POINT_CALIB)
+ || (tsens_calibration_mode ==
+ TSENS_TWO_POINT_CALIB) ||
+ (tsens_calibration_mode ==
+ TSENS_ONE_POINT_CALIB_OPTION_2)) {
+ pr_debug("backup one point calibrationless mode\n");
+ tsens_base1_data = (calib_data_backup[0] &
+ TSENS_BASE1_MASK);
+ tsens0_point1 = (calib_data_backup[0] &
+ TSENS0_POINT1_MASK) >>
+ TSENS0_POINT1_SHIFT;
+ tsens1_point1 = (calib_data_backup[0] &
+ TSENS1_POINT1_MASK) >> TSENS1_POINT1_SHIFT;
+ tsens2_point1 = (calib_data_backup[0] &
+ TSENS2_POINT1_MASK) >> TSENS2_POINT1_SHIFT;
+ tsens3_point1 = (calib_data_backup[0] &
+ TSENS3_POINT1_MASK) >> TSENS3_POINT1_SHIFT;
+ tsens4_point1 = (calib_data_backup[1] &
+ TSENS4_POINT1_MASK);
+ tsens5_point1 = (calib_data_backup[1] &
+ TSENS5_POINT1_MASK) >> TSENS5_POINT1_SHIFT;
+ tsens6_point1 = (calib_data_backup[1] &
+ TSENS6_POINT1_MASK) >> TSENS6_POINT1_SHIFT;
+ tsens7_point1 = (calib_data_backup[1] &
+ TSENS7_POINT1_MASK) >> TSENS7_POINT1_SHIFT;
+ tsens8_point1 = (calib_data_backup[2] &
+ TSENS8_POINT1_MASK_BACKUP) >>
+ TSENS8_POINT1_SHIFT;
+ tsens9_point1 = (calib_data_backup[2] &
+ TSENS9_POINT1_MASK_BACKUP) >>
+ TSENS9_POINT1_BACKUP_SHIFT;
+ tsens10_point1 = (calib_data_backup[2] &
+ TSENS10_POINT1_MASK_BACKUP) >>
+ TSENS10_POINT1_BACKUP_SHIFT;
+ } else if (tsens_calibration_mode == TSENS_TWO_POINT_CALIB) {
+ pr_debug("backup two point calibrationless mode\n");
+ tsens_base2_data = (calib_data_backup[2] &
+ TSENS_BASE2_BACKUP_MASK) >>
+ TSENS_POINT2_BASE_BACKUP_SHIFT;
+ tsens0_point2 = (calib_data_backup[2] &
+ TSENS0_POINT2_BACKUP_MASK) >>
+ TSENS0_POINT2_BACKUP_SHIFT;
+ tsens1_point2 = (calib_data_backup[3] &
+ TSENS1_POINT2_BACKUP_MASK);
+ tsens2_point2 = (calib_data_backup[3] &
+ TSENS2_POINT2_BACKUP_MASK) >>
+ TSENS2_POINT2_BACKUP_SHIFT;
+ tsens3_point2 = (calib_data_backup[3] &
+ TSENS3_POINT2_BACKUP_MASK) >>
+ TSENS3_POINT2_BACKUP_SHIFT;
+ tsens4_point2 = (calib_data_backup[3] &
+ TSENS4_POINT2_BACKUP_MASK) >>
+ TSENS4_POINT2_BACKUP_SHIFT;
+ tsens5_point2 = (calib_data[4] & TSENS5_POINT2_BACKUP_MASK) >>
+ TSENS5_POINT2_BACKUP_SHIFT;
+ tsens6_point2 = (calib_data[5] & TSENS6_POINT2_BACKUP_MASK);
+ tsens7_point2 = (calib_data[5] & TSENS7_POINT2_BACKUP_MASK) >>
+ TSENS7_POINT2_BACKUP_SHIFT;
+ tsens8_point2 = (calib_data[5] & TSENS8_POINT2_BACKUP_MASK) >>
+ TSENS8_POINT2_BACKUP_SHIFT;
+ tsens9_point2 = (calib_data[5] & TSENS9_POINT2_BACKUP_MASK) >>
+ TSENS9_POINT2_BACKUP_SHIFT;
+ tsens10_point2 = (calib_data[5] & TSENS10_POINT2_BACKUP_MASK)
+ >> TSENS10_POINT2_BACKUP_SHIFT;
+ } else {
+ pr_debug("TSENS:backup is calibrationless mode\n");
for (i = 0; i < tmdev->tsens_num_sensor; i++) {
tmdev->sensor[i].calib_data_point2 = 780;
tmdev->sensor[i].calib_data_point1 = 492;
}
+ tsens_calibration_mode = 0;
goto compute_intercept_slope;
- } else if ((tsens_calibration_mode == TSENS_ONE_POINT_CALIB) ||
- (tsens_calibration_mode == TSENS_TWO_POINT_CALIB)) {
- tsens_base1_data = (calib_data[0] & TSENS_BASE1_MASK);
- tsens0_point1 = (calib_data[0] & TSENS0_POINT1_MASK) >>
- TSENS0_POINT1_SHIFT;
- tsens1_point1 = (calib_data[0] & TSENS1_POINT1_MASK) >>
- TSENS1_POINT1_SHIFT;
- tsens2_point1 = (calib_data[0] & TSENS2_POINT1_MASK) >>
- TSENS2_POINT1_SHIFT;
- tsens3_point1 = (calib_data[0] & TSENS3_POINT1_MASK) >>
- TSENS3_POINT1_SHIFT;
- tsens4_point1 = (calib_data[1] & TSENS4_POINT1_MASK);
- tsens5_point1 = (calib_data[1] & TSENS5_POINT1_MASK) >>
- TSENS5_POINT1_SHIFT;
- tsens6_point1 = (calib_data[1] & TSENS6_POINT1_MASK) >>
- TSENS6_POINT1_SHIFT;
- tsens7_point1 = (calib_data[1] & TSENS7_POINT1_MASK) >>
- TSENS7_POINT1_SHIFT;
- tsens8_point1 = (calib_data[1] & TSENS8_POINT1_MASK) >>
- TSENS8_POINT1_SHIFT;
- tsens9_point1 = (calib_data[2] & TSENS9_POINT1_MASK);
- tsens10_point1 = (calib_data[2] & TSENS10_POINT1_MASK) >>
- TSENS10_POINT1_SHIFT;
- } else if (tsens_calibration_mode == TSENS_TWO_POINT_CALIB) {
- tsens_base2_data = (calib_data[2] & TSENS_BASE2_MASK) >>
- TSENS_POINT2_BASE_SHIFT;
- tsens0_point2 = (calib_data[2] & TSENS0_POINT2_MASK) >>
- TSENS0_POINT2_SHIFT;
- tsens1_point2 = (calib_data[2] & TSENS1_POINT2_MASK) >>
- TSENS1_POINT2_SHIFT;
- tsens2_point2 = (calib_data[3] & TSENS2_POINT2_MASK);
- tsens3_point2 = (calib_data[3] & TSENS3_POINT2_MASK) >>
- TSENS3_POINT2_SHIFT;
- tsens4_point2 = (calib_data[3] & TSENS4_POINT2_MASK) >>
- TSENS4_POINT2_SHIFT;
- tsens5_point2 = (calib_data[3] & TSENS5_POINT2_MASK) >>
- TSENS5_POINT2_SHIFT;
- tsens6_point2 = (calib_data[3] & TSENS6_POINT2_MASK) >>
- TSENS6_POINT2_SHIFT;
- tsens7_point2 = (calib_data[4] & TSENS7_POINT2_MASK);
- tsens8_point2 = (calib_data[4] & TSENS8_POINT2_MASK) >>
- TSENS8_POINT2_SHIFT;
- tsens9_point2 = (calib_data[4] & TSENS9_POINT2_MASK) >>
- TSENS9_POINT2_SHIFT;
- tsens10_point2 = (calib_data[4] & TSENS10_POINT2_MASK) >>
- TSENS10_POINT2_SHIFT;
+ }
} else {
- pr_debug("Calibration mode is unknown: %d\n",
- tsens_calibration_mode);
- return -ENODEV;
+ tsens_calibration_mode = (calib_data[1] & TSENS_CAL_SEL_0_1)
+ >> TSENS_CAL_SEL_SHIFT;
+ temp = (calib_data[3] & TSENS_CAL_SEL_2)
+ >> TSENS_CAL_SEL_SHIFT_2;
+ tsens_calibration_mode |= temp;
+ if ((tsens_calibration_mode == TSENS_ONE_POINT_CALIB) ||
+ (tsens_calibration_mode ==
+ TSENS_ONE_POINT_CALIB_OPTION_2) ||
+ (tsens_calibration_mode == TSENS_TWO_POINT_CALIB)) {
+ pr_debug("TSENS is one point calibrationless mode\n");
+ tsens_base1_data = (calib_data[0] & TSENS_BASE1_MASK);
+ tsens0_point1 = (calib_data[0] & TSENS0_POINT1_MASK) >>
+ TSENS0_POINT1_SHIFT;
+ tsens1_point1 = (calib_data[0] & TSENS1_POINT1_MASK) >>
+ TSENS1_POINT1_SHIFT;
+ tsens2_point1 = (calib_data[0] & TSENS2_POINT1_MASK) >>
+ TSENS2_POINT1_SHIFT;
+ tsens3_point1 = (calib_data[0] & TSENS3_POINT1_MASK) >>
+ TSENS3_POINT1_SHIFT;
+ tsens4_point1 = (calib_data[1] & TSENS4_POINT1_MASK);
+ tsens5_point1 = (calib_data[1] & TSENS5_POINT1_MASK) >>
+ TSENS5_POINT1_SHIFT;
+ tsens6_point1 = (calib_data[1] & TSENS6_POINT1_MASK) >>
+ TSENS6_POINT1_SHIFT;
+ tsens7_point1 = (calib_data[1] & TSENS7_POINT1_MASK) >>
+ TSENS7_POINT1_SHIFT;
+ tsens8_point1 = (calib_data[1] & TSENS8_POINT1_MASK) >>
+ TSENS8_POINT1_SHIFT;
+ tsens9_point1 = (calib_data[2] & TSENS9_POINT1_MASK);
+ tsens10_point1 = (calib_data[2] & TSENS10_POINT1_MASK)
+ >> TSENS10_POINT1_SHIFT;
+ } else if (tsens_calibration_mode == TSENS_TWO_POINT_CALIB) {
+ pr_debug("TSENS is two point calibrationless mode\n");
+ tsens_base2_data = (calib_data[2] & TSENS_BASE2_MASK) >>
+ TSENS_POINT2_BASE_SHIFT;
+ tsens0_point2 = (calib_data[2] & TSENS0_POINT2_MASK) >>
+ TSENS0_POINT2_SHIFT;
+ tsens1_point2 = (calib_data[2] & TSENS1_POINT2_MASK) >>
+ TSENS1_POINT2_SHIFT;
+ tsens2_point2 = (calib_data[3] & TSENS2_POINT2_MASK);
+ tsens3_point2 = (calib_data[3] & TSENS3_POINT2_MASK) >>
+ TSENS3_POINT2_SHIFT;
+ tsens4_point2 = (calib_data[3] & TSENS4_POINT2_MASK) >>
+ TSENS4_POINT2_SHIFT;
+ tsens5_point2 = (calib_data[3] & TSENS5_POINT2_MASK) >>
+ TSENS5_POINT2_SHIFT;
+ tsens6_point2 = (calib_data[3] & TSENS6_POINT2_MASK) >>
+ TSENS6_POINT2_SHIFT;
+ tsens7_point2 = (calib_data[4] & TSENS7_POINT2_MASK);
+ tsens8_point2 = (calib_data[4] & TSENS8_POINT2_MASK) >>
+ TSENS8_POINT2_SHIFT;
+ tsens9_point2 = (calib_data[4] & TSENS9_POINT2_MASK) >>
+ TSENS9_POINT2_SHIFT;
+ tsens10_point2 = (calib_data[4] & TSENS10_POINT2_MASK)
+ >> TSENS10_POINT2_SHIFT;
+ } else {
+calibration_less_mode:
+ pr_debug("TSENS is calibrationless mode\n");
+ for (i = 0; i < tmdev->tsens_num_sensor; i++)
+ tmdev->sensor[i].calib_data_point2 = 780;
+ tmdev->sensor[0].calib_data_point1 = 502;
+ tmdev->sensor[1].calib_data_point1 = 509;
+ tmdev->sensor[2].calib_data_point1 = 503;
+ tmdev->sensor[3].calib_data_point1 = 509;
+ tmdev->sensor[4].calib_data_point1 = 505;
+ tmdev->sensor[5].calib_data_point1 = 509;
+ tmdev->sensor[6].calib_data_point1 = 507;
+ tmdev->sensor[7].calib_data_point1 = 510;
+ tmdev->sensor[8].calib_data_point1 = 508;
+ tmdev->sensor[9].calib_data_point1 = 509;
+ tmdev->sensor[10].calib_data_point1 = 508;
+ goto compute_intercept_slope;
+ }
}
if (tsens_calibration_mode == TSENS_ONE_POINT_CALIB) {
+ pr_debug("old one point calibration calculation\n");
tmdev->sensor[0].calib_data_point1 =
(((tsens_base1_data) << 2) | TSENS_BIT_APPEND) + tsens0_point1;
tmdev->sensor[1].calib_data_point1 =
@@ -613,6 +759,8 @@
if ((tsens_calibration_mode == TSENS_ONE_POINT_CALIB_OPTION_2) ||
(tsens_calibration_mode == TSENS_TWO_POINT_CALIB)) {
+ pr_debug("one and two point calibration calculation\n");
+
tmdev->sensor[0].calib_data_point1 =
((((tsens_base1_data) + tsens0_point1) << 2) |
TSENS_BIT_APPEND);
@@ -649,6 +797,7 @@
}
if (tsens_calibration_mode == TSENS_TWO_POINT_CALIB) {
+ pr_debug("two point calibration calculation\n");
tmdev->sensor[0].calib_data_point2 =
(((tsens_base2_data + tsens0_point2) << 2) | TSENS_BIT_APPEND);
tmdev->sensor[1].calib_data_point2 =
@@ -708,7 +857,7 @@
}
tsens_slope_data = devm_kzalloc(&pdev->dev,
- tsens_num_sensors, GFP_KERNEL);
+ tsens_num_sensors * sizeof(u32), GFP_KERNEL);
if (!tsens_slope_data) {
dev_err(&pdev->dev, "can not allocate slope data\n");
return -ENOMEM;
@@ -735,18 +884,23 @@
tmdev->sensor[i].slope_mul_tsens_factor = tsens_slope_data[i];
tmdev->tsens_factor = TSENS_SLOPE_FACTOR;
tmdev->tsens_num_sensor = tsens_num_sensors;
+ tmdev->calibration_less_mode = of_property_read_bool(of_node,
+ "qcom,calibration-less-mode");
tmdev->tsens_irq = platform_get_irq(pdev, 0);
if (tmdev->tsens_irq < 0) {
pr_err("Invalid get irq\n");
- return tmdev->tsens_irq;
+ rc = tmdev->tsens_irq;
+ goto fail_tmdev;
}
+ /* TSENS register region */
tmdev->res_tsens_mem = platform_get_resource_byname(pdev,
IORESOURCE_MEM, "tsens_physical");
if (!tmdev->res_tsens_mem) {
pr_err("Could not get tsens physical address resource\n");
- return -EINVAL;
+ rc = -EINVAL;
+ goto fail_tmdev;
}
tmdev->tsens_len = tmdev->res_tsens_mem->end -
@@ -756,7 +910,8 @@
tmdev->tsens_len, tmdev->res_tsens_mem->name);
if (!res_mem) {
pr_err("Request tsens physical memory region failed\n");
- return -EINVAL;
+ rc = -EINVAL;
+ goto fail_tmdev;
}
tmdev->tsens_addr = ioremap(res_mem->start, tmdev->tsens_len);
@@ -766,6 +921,7 @@
goto fail_unmap_tsens_region;
}
+ /* TSENS calibration region */
tmdev->res_calib_mem = platform_get_resource_byname(pdev,
IORESOURCE_MEM, "tsens_eeprom_physical");
if (!tmdev->res_calib_mem) {
@@ -806,6 +962,8 @@
if (tmdev->res_tsens_mem)
release_mem_region(tmdev->res_tsens_mem->start,
tmdev->tsens_len);
+fail_tmdev:
+ tmdev = NULL;
return rc;
}
@@ -818,9 +976,13 @@
return -EBUSY;
}
- if (pdev->dev.of_node)
+ if (pdev->dev.of_node) {
rc = get_device_tree_data(pdev);
- else
+ if (rc) {
+ pr_err("Error reading TSENS DT\n");
+ return rc;
+ }
+ } else
return -ENODEV;
tmdev->pdev = pdev;
@@ -899,14 +1061,12 @@
iounmap(tmdev->tsens_calib_addr);
if (tmdev->res_calib_mem)
release_mem_region(tmdev->res_calib_mem->start,
- tmdev->calib_len);
+ tmdev->calib_len);
if (tmdev->tsens_addr)
iounmap(tmdev->tsens_addr);
if (tmdev->res_tsens_mem)
release_mem_region(tmdev->res_tsens_mem->start,
- tmdev->tsens_len);
- kfree(tmdev);
-
+ tmdev->tsens_len);
return rc;
}
@@ -921,12 +1081,12 @@
iounmap(tmdev->tsens_calib_addr);
if (tmdev->res_calib_mem)
release_mem_region(tmdev->res_calib_mem->start,
- tmdev->calib_len);
+ tmdev->calib_len);
if (tmdev->tsens_addr)
iounmap(tmdev->tsens_addr);
if (tmdev->res_tsens_mem)
release_mem_region(tmdev->res_tsens_mem->start,
- tmdev->tsens_len);
+ tmdev->tsens_len);
free_irq(tmdev->tsens_irq, tmdev);
platform_set_drvdata(pdev, NULL);
diff --git a/drivers/thermal/qpnp-temp-alarm.c b/drivers/thermal/qpnp-temp-alarm.c
new file mode 100644
index 0000000..499d67e
--- /dev/null
+++ b/drivers/thermal/qpnp-temp-alarm.c
@@ -0,0 +1,721 @@
+/*
+ * Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/string.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/spmi.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/thermal.h>
+#include <linux/qpnp/qpnp-adc.h>
+
+#define QPNP_TM_DRIVER_NAME "qcom,qpnp-temp-alarm"
+
+enum qpnp_tm_registers {
+ QPNP_TM_REG_TYPE = 0x04,
+ QPNP_TM_REG_SUBTYPE = 0x05,
+ QPNP_TM_REG_STATUS = 0x08,
+ QPNP_TM_REG_SHUTDOWN_CTRL1 = 0x40,
+ QPNP_TM_REG_SHUTDOWN_CTRL2 = 0x42,
+ QPNP_TM_REG_ALARM_CTRL = 0x46,
+};
+
+#define QPNP_TM_TYPE 0x09
+#define QPNP_TM_SUBTYPE 0x08
+
+#define STATUS_STAGE_MASK 0x03
+
+#define SHUTDOWN_CTRL1_OVERRIDE_STAGE3 0x80
+#define SHUTDOWN_CTRL1_OVERRIDE_STAGE2 0x40
+#define SHUTDOWN_CTRL1_THRESHOLD_MASK 0x03
+
+#define SHUTDOWN_CTRL2_CLEAR_STAGE3 0x80
+#define SHUTDOWN_CTRL2_CLEAR_STAGE2 0x40
+
+#define ALARM_CTRL_FORCE_ENABLE 0x80
+#define ALARM_CTRL_FOLLOW_HW_ENABLE 0x01
+
+#define TEMP_STAGE_STEP 20000 /* Stage step: 20.000 C */
+#define TEMP_STAGE_HYSTERESIS 2000
+
+#define TEMP_THRESH_MIN 105000 /* Threshold Min: 105 C */
+#define TEMP_THRESH_STEP 5000 /* Threshold step: 5 C */
+
+#define THRESH_MIN 0
+#define THRESH_MAX 3
+
+/* Trip points from most critical to least critical */
+#define TRIP_STAGE3 0
+#define TRIP_STAGE2 1
+#define TRIP_STAGE1 2
+#define TRIP_NUM 3
+
+enum qpnp_tm_adc_type {
+ QPNP_TM_ADC_NONE, /* Estimates temp based on overload level. */
+ QPNP_TM_ADC_QPNP_ADC,
+};
+
+/*
+ * Temperature in millicelcius reported during stage 0 if no ADC is present and
+ * no value has been specified via device tree.
+ */
+#define DEFAULT_NO_ADC_TEMP 37000
+
+struct qpnp_tm_chip {
+ struct delayed_work irq_work;
+ struct spmi_device *spmi_dev;
+ struct thermal_zone_device *tz_dev;
+ const char *tm_name;
+ enum qpnp_tm_adc_type adc_type;
+ unsigned long temperature;
+ enum thermal_device_mode mode;
+ unsigned int thresh;
+ unsigned int stage;
+ unsigned int prev_stage;
+ int irq;
+ enum qpnp_vadc_channels adc_channel;
+ u16 base_addr;
+ bool allow_software_override;
+};
+
+/* Delay between TEMP_STAT IRQ going high and status value changing in ms. */
+#define STATUS_REGISTER_DELAY_MS 40
+
+enum pmic_thermal_override_mode {
+ SOFTWARE_OVERRIDE_DISABLED = 0,
+ SOFTWARE_OVERRIDE_ENABLED,
+};
+
+static inline int qpnp_tm_read(struct qpnp_tm_chip *chip, u16 addr, u8 *buf,
+ int len)
+{
+ int rc;
+
+ rc = spmi_ext_register_readl(chip->spmi_dev->ctrl,
+ chip->spmi_dev->sid, chip->base_addr + addr, buf, len);
+
+ if (rc)
+ dev_err(&chip->spmi_dev->dev, "%s: spmi_ext_register_readl() failed. sid=%d, addr=%04X, len=%d, rc=%d\n",
+ __func__, chip->spmi_dev->sid, chip->base_addr + addr,
+ len, rc);
+
+ return rc;
+}
+
+static inline int qpnp_tm_write(struct qpnp_tm_chip *chip, u16 addr, u8 *buf,
+ int len)
+{
+ int rc;
+
+ rc = spmi_ext_register_writel(chip->spmi_dev->ctrl,
+ chip->spmi_dev->sid, chip->base_addr + addr, buf, len);
+
+ if (rc)
+ dev_err(&chip->spmi_dev->dev, "%s: spmi_ext_register_writel() failed. sid=%d, addr=%04X, len=%d, rc=%d\n",
+ __func__, chip->spmi_dev->sid, chip->base_addr + addr,
+ len, rc);
+
+ return rc;
+}
+
+
+static inline int qpnp_tm_shutdown_override(struct qpnp_tm_chip *chip,
+ enum pmic_thermal_override_mode mode)
+{
+ int rc = 0;
+ u8 reg;
+
+ if (chip->allow_software_override) {
+ reg = chip->thresh & SHUTDOWN_CTRL1_THRESHOLD_MASK;
+
+ if (mode == SOFTWARE_OVERRIDE_ENABLED)
+ reg |= SHUTDOWN_CTRL1_OVERRIDE_STAGE2
+ | SHUTDOWN_CTRL1_OVERRIDE_STAGE3;
+
+ rc = qpnp_tm_write(chip, QPNP_TM_REG_SHUTDOWN_CTRL1, ®, 1);
+ }
+
+ return rc;
+}
+
+static int qpnp_tm_update_temp(struct qpnp_tm_chip *chip)
+{
+ struct qpnp_vadc_result adc_result;
+ int rc;
+
+ rc = qpnp_vadc_read(chip->adc_channel, &adc_result);
+ if (!rc)
+ chip->temperature = adc_result.physical;
+ else
+ dev_err(&chip->spmi_dev->dev, "%s: qpnp_vadc_read(%d) failed, rc=%d\n",
+ __func__, chip->adc_channel, rc);
+
+ return rc;
+}
+
+/*
+ * This function initializes the internal temperature value based on only the
+ * current thermal stage and threshold.
+ */
+static int qpnp_tm_init_temp_no_adc(struct qpnp_tm_chip *chip)
+{
+ int rc;
+ u8 reg;
+
+ rc = qpnp_tm_read(chip, QPNP_TM_REG_STATUS, ®, 1);
+ if (rc < 0)
+ return rc;
+
+ chip->stage = reg & STATUS_STAGE_MASK;
+
+ if (chip->stage)
+ chip->temperature = chip->thresh * TEMP_THRESH_STEP +
+ (chip->stage - 1) * TEMP_STAGE_STEP +
+ TEMP_THRESH_MIN;
+
+ return 0;
+}
+
+/*
+ * This function updates the internal temperature value based on the
+ * current thermal stage and threshold as well as the previous stage
+ */
+static int qpnp_tm_update_temp_no_adc(struct qpnp_tm_chip *chip)
+{
+ unsigned int stage;
+ int rc;
+ u8 reg;
+
+ rc = qpnp_tm_read(chip, QPNP_TM_REG_STATUS, ®, 1);
+ if (rc < 0)
+ return rc;
+
+ stage = reg & STATUS_STAGE_MASK;
+
+ if (stage > chip->stage) {
+ /* increasing stage, use lower bound */
+ chip->temperature = (stage - 1) * TEMP_STAGE_STEP
+ + chip->thresh * TEMP_THRESH_STEP
+ + TEMP_STAGE_HYSTERESIS + TEMP_THRESH_MIN;
+ } else if (stage < chip->stage) {
+ /* decreasing stage, use upper bound */
+ chip->temperature = stage * TEMP_STAGE_STEP
+ + chip->thresh * TEMP_THRESH_STEP
+ - TEMP_STAGE_HYSTERESIS + TEMP_THRESH_MIN;
+ }
+
+ chip->stage = stage;
+
+ return 0;
+}
+
+static int qpnp_tz_get_temp_no_adc(struct thermal_zone_device *thermal,
+ unsigned long *temperature)
+{
+ struct qpnp_tm_chip *chip = thermal->devdata;
+ int rc;
+
+ if (!temperature)
+ return -EINVAL;
+
+ rc = qpnp_tm_update_temp_no_adc(chip);
+ if (rc < 0)
+ return rc;
+
+ *temperature = chip->temperature;
+
+ return 0;
+}
+
+static int qpnp_tz_get_temp_qpnp_adc(struct thermal_zone_device *thermal,
+ unsigned long *temperature)
+{
+ struct qpnp_tm_chip *chip = thermal->devdata;
+ int rc;
+
+ if (!temperature)
+ return -EINVAL;
+
+ rc = qpnp_tm_update_temp(chip);
+ if (rc < 0) {
+ dev_err(&chip->spmi_dev->dev, "%s: %s: adc read failed, rc = %d\n",
+ __func__, chip->tm_name, rc);
+ return rc;
+ }
+
+ *temperature = chip->temperature;
+
+ return 0;
+}
+
+static int qpnp_tz_get_mode(struct thermal_zone_device *thermal,
+ enum thermal_device_mode *mode)
+{
+ struct qpnp_tm_chip *chip = thermal->devdata;
+
+ if (!mode)
+ return -EINVAL;
+
+ *mode = chip->mode;
+
+ return 0;
+}
+
+static int qpnp_tz_set_mode(struct thermal_zone_device *thermal,
+ enum thermal_device_mode mode)
+{
+ struct qpnp_tm_chip *chip = thermal->devdata;
+ int rc = 0;
+
+ if (mode != chip->mode) {
+ if (mode == THERMAL_DEVICE_ENABLED)
+ rc = qpnp_tm_shutdown_override(chip,
+ SOFTWARE_OVERRIDE_ENABLED);
+ else
+ rc = qpnp_tm_shutdown_override(chip,
+ SOFTWARE_OVERRIDE_DISABLED);
+
+ chip->mode = mode;
+ }
+
+ return rc;
+}
+
+static int qpnp_tz_get_trip_type(struct thermal_zone_device *thermal,
+ int trip, enum thermal_trip_type *type)
+{
+ if (trip < 0 || !type)
+ return -EINVAL;
+
+ switch (trip) {
+ case TRIP_STAGE3:
+ *type = THERMAL_TRIP_CRITICAL;
+ break;
+ case TRIP_STAGE2:
+ *type = THERMAL_TRIP_HOT;
+ break;
+ case TRIP_STAGE1:
+ *type = THERMAL_TRIP_HOT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int qpnp_tz_get_trip_temp(struct thermal_zone_device *thermal,
+ int trip, unsigned long *temperature)
+{
+ struct qpnp_tm_chip *chip = thermal->devdata;
+ int thresh_temperature;
+
+ if (trip < 0 || !temperature)
+ return -EINVAL;
+
+ thresh_temperature = chip->thresh * TEMP_THRESH_STEP + TEMP_THRESH_MIN;
+
+ switch (trip) {
+ case TRIP_STAGE3:
+ thresh_temperature += 2 * TEMP_STAGE_STEP;
+ break;
+ case TRIP_STAGE2:
+ thresh_temperature += TEMP_STAGE_STEP;
+ break;
+ case TRIP_STAGE1:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ *temperature = thresh_temperature;
+
+ return 0;
+}
+
+static int qpnp_tz_get_crit_temp(struct thermal_zone_device *thermal,
+ unsigned long *temperature)
+{
+ struct qpnp_tm_chip *chip = thermal->devdata;
+
+ if (!temperature)
+ return -EINVAL;
+
+ *temperature = chip->thresh * TEMP_THRESH_STEP + TEMP_THRESH_MIN +
+ 2 * TEMP_STAGE_STEP;
+
+ return 0;
+}
+
+static struct thermal_zone_device_ops qpnp_thermal_zone_ops_no_adc = {
+ .get_temp = qpnp_tz_get_temp_no_adc,
+ .get_mode = qpnp_tz_get_mode,
+ .set_mode = qpnp_tz_set_mode,
+ .get_trip_type = qpnp_tz_get_trip_type,
+ .get_trip_temp = qpnp_tz_get_trip_temp,
+ .get_crit_temp = qpnp_tz_get_crit_temp,
+};
+
+static struct thermal_zone_device_ops qpnp_thermal_zone_ops_qpnp_adc = {
+ .get_temp = qpnp_tz_get_temp_qpnp_adc,
+ .get_mode = qpnp_tz_get_mode,
+ .set_mode = qpnp_tz_set_mode,
+ .get_trip_type = qpnp_tz_get_trip_type,
+ .get_trip_temp = qpnp_tz_get_trip_temp,
+ .get_crit_temp = qpnp_tz_get_crit_temp,
+};
+
+static void qpnp_tm_work(struct work_struct *work)
+{
+ struct delayed_work *dwork
+ = container_of(work, struct delayed_work, work);
+ struct qpnp_tm_chip *chip
+ = container_of(dwork, struct qpnp_tm_chip, irq_work);
+ int rc;
+ u8 reg;
+
+ if (chip->adc_type == QPNP_TM_ADC_NONE) {
+ rc = qpnp_tm_update_temp_no_adc(chip);
+ if (rc < 0)
+ goto bail;
+ } else {
+ rc = qpnp_tm_read(chip, QPNP_TM_REG_STATUS, ®, 1);
+ if (rc < 0)
+ goto bail;
+
+ chip->stage = reg & STATUS_STAGE_MASK;
+
+ rc = qpnp_tm_update_temp(chip);
+ if (rc < 0)
+ goto bail;
+ }
+
+ if (chip->stage != chip->prev_stage) {
+ chip->prev_stage = chip->stage;
+
+ pr_crit("%s: PMIC Temp Alarm - stage=%u, threshold=%u, temperature=%lu mC\n",
+ chip->tm_name, chip->stage, chip->thresh,
+ chip->temperature);
+
+ thermal_zone_device_update(chip->tz_dev);
+
+ /* Notify user space */
+ sysfs_notify(&chip->tz_dev->device.kobj, NULL, "type");
+ }
+
+bail:
+ return;
+}
+
+static irqreturn_t qpnp_tm_isr(int irq, void *data)
+{
+ struct qpnp_tm_chip *chip = data;
+
+ schedule_delayed_work(&chip->irq_work,
+ msecs_to_jiffies(STATUS_REGISTER_DELAY_MS) + 1);
+
+ return IRQ_HANDLED;
+}
+
+static int qpnp_tm_init_reg(struct qpnp_tm_chip *chip)
+{
+ int rc = 0;
+ u8 reg;
+
+ if (chip->thresh < THRESH_MIN || chip->thresh > THRESH_MAX) {
+ /* Read hardware threshold value if configuration is invalid. */
+ rc = qpnp_tm_read(chip, QPNP_TM_REG_SHUTDOWN_CTRL1, ®, 1);
+ if (rc < 0)
+ return rc;
+ chip->thresh = reg & SHUTDOWN_CTRL1_THRESHOLD_MASK;
+ }
+
+ /*
+ * Set threshold and disable software override of stage 2 and 3
+ * shutdowns.
+ */
+ reg = chip->thresh & SHUTDOWN_CTRL1_THRESHOLD_MASK;
+ rc = qpnp_tm_write(chip, QPNP_TM_REG_SHUTDOWN_CTRL1, ®, 1);
+ if (rc < 0)
+ return rc;
+
+ /* Enable the thermal alarm PMIC module in always-on mode. */
+ reg = ALARM_CTRL_FORCE_ENABLE;
+ rc = qpnp_tm_write(chip, QPNP_TM_REG_ALARM_CTRL, ®, 1);
+
+ return rc;
+}
+
+static int __devinit qpnp_tm_probe(struct spmi_device *spmi)
+{
+ struct device_node *node;
+ struct resource *res;
+ struct qpnp_tm_chip *chip;
+ struct thermal_zone_device_ops *tz_ops;
+ char *tm_name;
+ u32 default_temperature;
+ int rc = 0;
+ u8 raw_type[2], type, subtype;
+
+ if (!spmi || !(&spmi->dev) || !spmi->dev.of_node) {
+ dev_err(&spmi->dev, "%s: device tree node not found\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ node = spmi->dev.of_node;
+
+ chip = kzalloc(sizeof(struct qpnp_tm_chip), GFP_KERNEL);
+ if (!chip) {
+ dev_err(&spmi->dev, "%s: Can't allocate qpnp_tm_chip\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ dev_set_drvdata(&spmi->dev, chip);
+
+ res = spmi_get_resource(spmi, NULL, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&spmi->dev, "%s: node is missing base address\n",
+ __func__);
+ rc = -EINVAL;
+ goto free_chip;
+ }
+ chip->base_addr = res->start;
+ chip->spmi_dev = spmi;
+
+ chip->irq = spmi_get_irq(spmi, NULL, 0);
+ if (chip->irq < 0) {
+ rc = chip->irq;
+ dev_err(&spmi->dev, "%s: node is missing irq, rc=%d\n",
+ __func__, rc);
+ goto free_chip;
+ }
+
+ chip->tm_name = of_get_property(node, "label", NULL);
+ if (chip->tm_name == NULL) {
+ dev_err(&spmi->dev, "%s: node is missing label\n",
+ __func__);
+ rc = -EINVAL;
+ goto free_chip;
+ }
+
+ tm_name = kstrdup(chip->tm_name, GFP_KERNEL);
+ if (tm_name == NULL) {
+ dev_err(&spmi->dev, "%s: could not allocate memory for label\n",
+ __func__);
+ rc = -ENOMEM;
+ goto free_chip;
+ }
+ chip->tm_name = tm_name;
+
+ INIT_DELAYED_WORK(&chip->irq_work, qpnp_tm_work);
+
+ /* These bindings are optional, so it is okay if they are not found. */
+ chip->thresh = THRESH_MAX + 1;
+ rc = of_property_read_u32(node, "qcom,threshold-set", &chip->thresh);
+ if (!rc && (chip->thresh < THRESH_MIN || chip->thresh > THRESH_MAX))
+ dev_err(&spmi->dev, "%s: invalid qcom,threshold-set=%u specified\n",
+ __func__, chip->thresh);
+
+ chip->adc_type = QPNP_TM_ADC_NONE;
+ rc = of_property_read_u32(node, "qcom,channel-num", &chip->adc_channel);
+ if (!rc) {
+ if (chip->adc_channel < 0 || chip->adc_channel >= ADC_MAX_NUM) {
+ dev_err(&spmi->dev, "%s: invalid qcom,channel-num=%d specified\n",
+ __func__, chip->adc_channel);
+ } else {
+ chip->adc_type = QPNP_TM_ADC_QPNP_ADC;
+ rc = qpnp_vadc_is_ready();
+ if (rc) {
+ /* Probe retry, do not print an error message */
+ goto err_cancel_work;
+ }
+ }
+ }
+
+ if (chip->adc_type == QPNP_TM_ADC_QPNP_ADC)
+ tz_ops = &qpnp_thermal_zone_ops_qpnp_adc;
+ else
+ tz_ops = &qpnp_thermal_zone_ops_no_adc;
+
+ chip->allow_software_override
+ = of_property_read_bool(node, "qcom,allow-override");
+
+ default_temperature = DEFAULT_NO_ADC_TEMP;
+ rc = of_property_read_u32(node, "qcom,default-temp",
+ &default_temperature);
+ chip->temperature = default_temperature;
+
+ rc = qpnp_tm_read(chip, QPNP_TM_REG_TYPE, raw_type, 2);
+ if (rc) {
+ dev_err(&spmi->dev, "%s: could not read type register, rc=%d\n",
+ __func__, rc);
+ goto err_cancel_work;
+ }
+ type = raw_type[0];
+ subtype = raw_type[1];
+
+ if (type != QPNP_TM_TYPE || subtype != QPNP_TM_SUBTYPE) {
+ dev_err(&spmi->dev, "%s: invalid type=%02X or subtype=%02X register value\n",
+ __func__, type, subtype);
+ rc = -ENODEV;
+ goto err_cancel_work;
+ }
+
+ rc = qpnp_tm_init_reg(chip);
+ if (rc) {
+ dev_err(&spmi->dev, "%s: qpnp_tm_init_reg() failed, rc=%d\n",
+ __func__, rc);
+ goto err_cancel_work;
+ }
+
+ if (chip->adc_type == QPNP_TM_ADC_NONE) {
+ rc = qpnp_tm_init_temp_no_adc(chip);
+ if (rc) {
+ dev_err(&spmi->dev, "%s: qpnp_tm_init_temp_no_adc() failed, rc=%d\n",
+ __func__, rc);
+ goto err_cancel_work;
+ }
+ }
+
+ /* Start in HW control; switch to SW control when user changes mode. */
+ chip->mode = THERMAL_DEVICE_DISABLED;
+ rc = qpnp_tm_shutdown_override(chip, SOFTWARE_OVERRIDE_DISABLED);
+ if (rc) {
+ dev_err(&spmi->dev, "%s: qpnp_tm_shutdown_override() failed, rc=%d\n",
+ __func__, rc);
+ goto err_cancel_work;
+ }
+
+ chip->tz_dev = thermal_zone_device_register(tm_name, TRIP_NUM, chip,
+ tz_ops, 0, 0, 0, 0);
+ if (chip->tz_dev == NULL) {
+ dev_err(&spmi->dev, "%s: thermal_zone_device_register() failed.\n",
+ __func__);
+ rc = -ENODEV;
+ goto err_cancel_work;
+ }
+
+ rc = request_irq(chip->irq, qpnp_tm_isr, IRQF_TRIGGER_RISING, tm_name,
+ chip);
+ if (rc < 0) {
+ dev_err(&spmi->dev, "%s: request_irq(%d) failed: %d\n",
+ __func__, chip->irq, rc);
+ goto err_free_tz;
+ }
+
+ return 0;
+
+err_free_tz:
+ thermal_zone_device_unregister(chip->tz_dev);
+err_cancel_work:
+ cancel_delayed_work_sync(&chip->irq_work);
+ kfree(chip->tm_name);
+free_chip:
+ dev_set_drvdata(&spmi->dev, NULL);
+ kfree(chip);
+ return rc;
+}
+
+static int __devexit qpnp_tm_remove(struct spmi_device *spmi)
+{
+ struct qpnp_tm_chip *chip = dev_get_drvdata(&spmi->dev);
+
+ dev_set_drvdata(&spmi->dev, NULL);
+ thermal_zone_device_unregister(chip->tz_dev);
+ kfree(chip->tm_name);
+ qpnp_tm_shutdown_override(chip, SOFTWARE_OVERRIDE_DISABLED);
+ free_irq(chip->irq, chip);
+ cancel_delayed_work_sync(&chip->irq_work);
+ kfree(chip);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int qpnp_tm_suspend(struct device *dev)
+{
+ struct qpnp_tm_chip *chip = dev_get_drvdata(dev);
+
+ /* Clear override bits in suspend to allow hardware control */
+ qpnp_tm_shutdown_override(chip, SOFTWARE_OVERRIDE_DISABLED);
+
+ return 0;
+}
+
+static int qpnp_tm_resume(struct device *dev)
+{
+ struct qpnp_tm_chip *chip = dev_get_drvdata(dev);
+
+ /* Override hardware actions so software can control */
+ if (chip->mode == THERMAL_DEVICE_ENABLED)
+ qpnp_tm_shutdown_override(chip, SOFTWARE_OVERRIDE_ENABLED);
+
+ return 0;
+}
+
+static const struct dev_pm_ops qpnp_tm_pm_ops = {
+ .suspend = qpnp_tm_suspend,
+ .resume = qpnp_tm_resume,
+};
+
+#define QPNP_TM_PM_OPS (&qpnp_tm_pm_ops)
+#else
+#define QPNP_TM_PM_OPS NULL
+#endif
+
+static struct of_device_id qpnp_tm_match_table[] = {
+ { .compatible = QPNP_TM_DRIVER_NAME, },
+ {}
+};
+
+static const struct spmi_device_id qpnp_tm_id[] = {
+ { QPNP_TM_DRIVER_NAME, 0 },
+ {}
+};
+
+static struct spmi_driver qpnp_tm_driver = {
+ .driver = {
+ .name = QPNP_TM_DRIVER_NAME,
+ .of_match_table = qpnp_tm_match_table,
+ .owner = THIS_MODULE,
+ .pm = QPNP_TM_PM_OPS,
+ },
+ .probe = qpnp_tm_probe,
+ .remove = __devexit_p(qpnp_tm_remove),
+ .id_table = qpnp_tm_id,
+};
+
+int __init qpnp_tm_init(void)
+{
+ return spmi_driver_register(&qpnp_tm_driver);
+}
+
+static void __exit qpnp_tm_exit(void)
+{
+ spmi_driver_unregister(&qpnp_tm_driver);
+}
+
+module_init(qpnp_tm_init);
+module_exit(qpnp_tm_exit);
+
+MODULE_DESCRIPTION("QPNP PMIC Temperature Alarm driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/tty/serial/msm_serial_hs_hwreg.h b/drivers/tty/serial/msm_serial_hs_hwreg.h
index 81f3d54..8debc36 100644
--- a/drivers/tty/serial/msm_serial_hs_hwreg.h
+++ b/drivers/tty/serial/msm_serial_hs_hwreg.h
@@ -183,8 +183,8 @@
/* Parity configuration */
#define NO_PARITY 0x0
-#define EVEN_PARITY 0x1
-#define ODD_PARITY 0x2
+#define EVEN_PARITY 0x2
+#define ODD_PARITY 0x1
#define SPACE_PARITY 0x3
#define UARTDM_IPR_STALE_TIMEOUT_MSB_BMSK 0xffffff80
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index c74ba7b..056ce18 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -451,8 +451,11 @@
INIT_LIST_HEAD(&dev->filelist);
#ifdef CONFIG_PM
- pm_runtime_set_autosuspend_delay(&dev->dev,
- usb_autosuspend_delay * 1000);
+ if (usb_hcd->driver->set_autosuspend_delay)
+ usb_hcd->driver->set_autosuspend_delay(dev);
+ else
+ pm_runtime_set_autosuspend_delay(&dev->dev,
+ usb_autosuspend_delay * 1000);
dev->connect_time = jiffies;
dev->active_duration = -jiffies;
#endif
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index b2709d2..f431e9d 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -2366,7 +2366,7 @@
dev_set_name(&dwc->gadget.dev, "gadget");
dwc->gadget.ops = &dwc3_gadget_ops;
- dwc->gadget.max_speed = USB_SPEED_SUPER;
+ dwc->gadget.max_speed = USB_SPEED_HIGH;
dwc->gadget.speed = USB_SPEED_UNKNOWN;
dwc->gadget.dev.parent = dwc->dev;
dwc->gadget.sg_supported = true;
diff --git a/drivers/usb/gadget/android.c b/drivers/usb/gadget/android.c
index f596411..52c19cf 100644
--- a/drivers/usb/gadget/android.c
+++ b/drivers/usb/gadget/android.c
@@ -636,7 +636,7 @@
static void ecm_qc_function_unbind_config(struct android_usb_function *f,
struct usb_configuration *c)
{
- gether_qc_cleanup();
+ gether_qc_cleanup_name("ecm0");
}
static ssize_t ecm_ethaddr_show(struct device *dev,
@@ -1181,7 +1181,7 @@
static void rndis_qc_function_unbind_config(struct android_usb_function *f,
struct usb_configuration *c)
{
- gether_qc_cleanup();
+ gether_qc_cleanup_name("rndis0");
}
static ssize_t rndis_manufacturer_show(struct device *dev,
diff --git a/drivers/usb/gadget/f_qc_ecm.c b/drivers/usb/gadget/f_qc_ecm.c
index 1c64955..0b41197 100644
--- a/drivers/usb/gadget/f_qc_ecm.c
+++ b/drivers/usb/gadget/f_qc_ecm.c
@@ -518,7 +518,7 @@
if (ecm->port.in_ep->driver_data) {
DBG(cdev, "reset ecm\n");
- gether_qc_disconnect(&ecm->port);
+ gether_qc_disconnect_name(&ecm->port, "ecm0");
ecm_qc_bam_disconnect(ecm);
}
@@ -548,7 +548,7 @@
);
ecm->port.cdc_filter = DEFAULT_FILTER;
DBG(cdev, "activate ecm\n");
- net = gether_qc_connect(&ecm->port);
+ net = gether_qc_connect_name(&ecm->port, "ecm0");
if (IS_ERR(net))
return PTR_ERR(net);
@@ -591,7 +591,7 @@
DBG(cdev, "ecm deactivated\n");
if (ecm->port.in_ep->driver_data) {
- gether_qc_disconnect(&ecm->port);
+ gether_qc_disconnect_name(&ecm->port, "ecm0");
ecm_qc_bam_disconnect(ecm);
}
diff --git a/drivers/usb/gadget/f_qc_rndis.c b/drivers/usb/gadget/f_qc_rndis.c
index 7a181eb..f86bf12 100644
--- a/drivers/usb/gadget/f_qc_rndis.c
+++ b/drivers/usb/gadget/f_qc_rndis.c
@@ -661,7 +661,7 @@
if (rndis->port.in_ep->driver_data) {
DBG(cdev, "reset rndis\n");
- gether_qc_disconnect(&rndis->port);
+ gether_qc_disconnect_name(&rndis->port, "rndis0");
rndis_qc_bam_disconnect(rndis);
}
@@ -695,7 +695,7 @@
rndis->port.cdc_filter = 0;
DBG(cdev, "RNDIS RX/TX early activation ...\n");
- net = gether_qc_connect(&rndis->port);
+ net = gether_qc_connect_name(&rndis->port, "rndis0");
if (IS_ERR(net))
return PTR_ERR(net);
@@ -722,7 +722,7 @@
pr_info("rndis deactivated\n");
rndis_uninit(rndis->config);
- gether_qc_disconnect(&rndis->port);
+ gether_qc_disconnect_name(&rndis->port, "rndis0");
rndis_qc_bam_disconnect(rndis);
usb_ep_disable(rndis->notify);
diff --git a/drivers/usb/gadget/f_qdss.c b/drivers/usb/gadget/f_qdss.c
index 0c81904..fd4f352 100644
--- a/drivers/usb/gadget/f_qdss.c
+++ b/drivers/usb/gadget/f_qdss.c
@@ -436,11 +436,30 @@
}
}
+static void usb_qdss_disconnect_work(struct work_struct *work)
+{
+ struct f_qdss *qdss = container_of(work, struct f_qdss, disconnect_w);
+ int status;
+
+ pr_debug("usb_qdss_disconnect_work\n");
+
+ /* notify qdss to cancell all active transfers*/
+ if (qdss->ch.notify) {
+ qdss->ch.notify(qdss->ch.priv, USB_QDSS_DISCONNECT, NULL,
+ NULL);
+ /* If the app was never started, we can skip USB BAM reset */
+ status = set_qdss_data_connection(qdss->data,
+ qdss->data->address, 0);
+ if (status)
+ pr_err("qdss_disconnect error");
+ }
+
+}
+
static void qdss_disable(struct usb_function *f)
{
struct f_qdss *qdss = func_to_qdss(f);
unsigned long flags;
- int status;
pr_debug("qdss_disable\n");
@@ -451,24 +470,15 @@
/*cancell all active xfers*/
qdss_eps_disable(f);
- /* notify qdss to cancell all active transfers*/
- if (qdss->ch.notify) {
- qdss->ch.notify(qdss->ch.priv, USB_QDSS_DISCONNECT, NULL,
- NULL);
- /* If the app was never started, we can skip USB BAM reset */
- status = set_qdss_data_connection(qdss->data,
- qdss->data->address, 0);
- if (status)
- pr_err("qdss_disable error");
- }
+ schedule_work(&qdss->disconnect_w);
}
-static void usb_qdss_work_func(struct work_struct *work)
+static void usb_qdss_connect_work(struct work_struct *work)
{
- struct f_qdss *qdss = container_of(work, struct f_qdss, qdss_work);
+ struct f_qdss *qdss = container_of(work, struct f_qdss, connect_w);
int status;
- pr_debug("usb_qdss_work_func\n");
+ pr_debug("usb_qdss_connect_work\n");
status = init_data(qdss->data);
if (status) {
@@ -549,7 +559,7 @@
qdss->usb_connected = 1;
if (qdss->usb_connected && ch->app_conn)
- schedule_work(&qdss->qdss_work);
+ schedule_work(&qdss->connect_w);
return 0;
fail:
@@ -615,9 +625,11 @@
qdss->function.unbind = qdss_unbind;
qdss->function.set_alt = qdss_set_alt;
qdss->function.disable = qdss_disable;
+ spin_lock_init(&qdss->lock);
INIT_LIST_HEAD(&qdss->ctrl_read_pool);
INIT_LIST_HEAD(&qdss->ctrl_write_pool);
- INIT_WORK(&qdss->qdss_work, usb_qdss_work_func);
+ INIT_WORK(&qdss->connect_w, usb_qdss_connect_work);
+ INIT_WORK(&qdss->disconnect_w, usb_qdss_disconnect_work);
status = usb_add_function(c, &qdss->function);
if (status) {
@@ -766,7 +778,7 @@
/* the case USB cabel was connected befor qdss called qdss_open*/
if (qdss->usb_connected == 1)
- schedule_work(&qdss->qdss_work);
+ schedule_work(&qdss->connect_w);
return ch;
}
diff --git a/drivers/usb/gadget/f_qdss.h b/drivers/usb/gadget/f_qdss.h
index b61244b..d6be8b7 100644
--- a/drivers/usb/gadget/f_qdss.h
+++ b/drivers/usb/gadget/f_qdss.h
@@ -32,7 +32,8 @@
struct usb_qdss_ch ch;
struct list_head ctrl_read_pool;
struct list_head ctrl_write_pool;
- struct work_struct qdss_work;
+ struct work_struct connect_w;
+ struct work_struct disconnect_w;
spinlock_t lock;
unsigned int data_enabled:1;
unsigned int ctrl_in_enabled:1;
diff --git a/drivers/usb/gadget/f_serial.c b/drivers/usb/gadget/f_serial.c
index 3d6ceaa..649fe14 100644
--- a/drivers/usb/gadget/f_serial.c
+++ b/drivers/usb/gadget/f_serial.c
@@ -225,26 +225,26 @@
NULL,
};
-static struct usb_endpoint_descriptor gser_ss_in_desc __initdata = {
+static struct usb_endpoint_descriptor gser_ss_in_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = cpu_to_le16(1024),
};
-static struct usb_endpoint_descriptor gser_ss_out_desc __initdata = {
+static struct usb_endpoint_descriptor gser_ss_out_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = cpu_to_le16(1024),
};
-static struct usb_ss_ep_comp_descriptor gser_ss_bulk_comp_desc __initdata = {
+static struct usb_ss_ep_comp_descriptor gser_ss_bulk_comp_desc = {
.bLength = sizeof gser_ss_bulk_comp_desc,
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
};
-static struct usb_descriptor_header *gser_ss_function[] __initdata = {
+static struct usb_descriptor_header *gser_ss_function[] = {
(struct usb_descriptor_header *) &gser_interface_desc,
(struct usb_descriptor_header *) &gser_ss_in_desc,
(struct usb_descriptor_header *) &gser_ss_bulk_comp_desc,
diff --git a/drivers/usb/gadget/storage_common.c b/drivers/usb/gadget/storage_common.c
index 3c57df4..7e62c19 100644
--- a/drivers/usb/gadget/storage_common.c
+++ b/drivers/usb/gadget/storage_common.c
@@ -925,7 +925,7 @@
int rc = 0;
-#ifndef CONFIG_USB_ANDROID_MASS_STORAGE
+#if !defined(CONFIG_USB_G_ANDROID)
/* disabled in android because we need to allow closing the backing file
* if the media was removed
*/
diff --git a/drivers/usb/gadget/u_qc_ether.c b/drivers/usb/gadget/u_qc_ether.c
index 20933b6..4931c1e 100644
--- a/drivers/usb/gadget/u_qc_ether.c
+++ b/drivers/usb/gadget/u_qc_ether.c
@@ -222,8 +222,6 @@
return 1;
}
-static struct eth_qc_dev *qc_dev;
-
static const struct net_device_ops eth_qc_netdev_ops = {
.ndo_open = eth_qc_open,
.ndo_stop = eth_qc_stop,
@@ -276,9 +274,6 @@
struct net_device *net;
int status;
- if (qc_dev)
- return -EBUSY;
-
net = alloc_etherdev(sizeof *dev);
if (!net)
return -ENOMEM;
@@ -318,32 +313,33 @@
INFO(dev, "MAC %pM\n", net->dev_addr);
INFO(dev, "HOST MAC %pM\n", dev->host_mac);
- qc_dev = dev;
}
return status;
}
/**
- * gether_qc_cleanup - remove Ethernet-over-USB device
+ * gether_qc_cleanup_name - remove Ethernet-over-USB device
* Context: may sleep
*
* This is called to free all resources allocated by @gether_qc_setup().
*/
-void gether_qc_cleanup(void)
+void gether_qc_cleanup_name(const char *netname)
{
- if (!qc_dev)
- return;
+ struct net_device *net_dev;
- unregister_netdev(qc_dev->net);
- free_netdev(qc_dev->net);
+ /* Extract the eth_qc_dev from the net device */
+ net_dev = dev_get_by_name(&init_net, netname);
- qc_dev = NULL;
+ if (net_dev) {
+ unregister_netdev(net_dev);
+ free_netdev(net_dev);
+ }
}
-
/**
- * gether_qc_connect - notify network layer that USB link is active
+ * gether_qc_connect_name - notify network layer that USB link
+ * is active
* @link: the USB link, set up with endpoints, descriptors matching
* current device speed, and any framing wrapper(s) set up.
* Context: irqs blocked
@@ -351,9 +347,15 @@
* This is called to let the network layer know the connection
* is active ("carrier detect").
*/
-struct net_device *gether_qc_connect(struct qc_gether *link)
+struct net_device *gether_qc_connect_name(struct qc_gether *link,
+ const char *netname)
{
- struct eth_qc_dev *dev = qc_dev;
+ struct net_device *net_dev;
+ struct eth_qc_dev *dev;
+
+ /* Extract the eth_qc_dev from the net device */
+ net_dev = dev_get_by_name(&init_net, netname);
+ dev = netdev_priv(net_dev);
if (!dev)
return ERR_PTR(-EINVAL);
@@ -381,7 +383,8 @@
}
/**
- * gether_qc_disconnect - notify network layer that USB link is inactive
+ * gether_qc_disconnect_name - notify network layer that USB
+ * link is inactive
* @link: the USB link, on which gether_connect() was called
* Context: irqs blocked
*
@@ -390,9 +393,14 @@
*
* On return, the state is as if gether_connect() had never been called.
*/
-void gether_qc_disconnect(struct qc_gether *link)
+void gether_qc_disconnect_name(struct qc_gether *link, const char *netname)
{
- struct eth_qc_dev *dev = link->ioport;
+ struct net_device *net_dev;
+ struct eth_qc_dev *dev;
+
+ /* Extract the eth_qc_dev from the net device */
+ net_dev = dev_get_by_name(&init_net, netname);
+ dev = netdev_priv(net_dev);
if (!dev)
return;
diff --git a/drivers/usb/gadget/u_qc_ether.h b/drivers/usb/gadget/u_qc_ether.h
index b3c281b..d91e805 100644
--- a/drivers/usb/gadget/u_qc_ether.h
+++ b/drivers/usb/gadget/u_qc_ether.h
@@ -78,14 +78,15 @@
/* netdev setup/teardown as directed by the gadget driver */
int gether_qc_setup(struct usb_gadget *g, u8 ethaddr[ETH_ALEN]);
-void gether_qc_cleanup(void);
+void gether_qc_cleanup_name(const char *netname);
/* variant of gether_setup that allows customizing network device name */
int gether_qc_setup_name(struct usb_gadget *g, u8 ethaddr[ETH_ALEN],
const char *netname);
/* connect/disconnect is handled by individual functions */
-struct net_device *gether_qc_connect(struct qc_gether *);
-void gether_qc_disconnect(struct qc_gether *);
+struct net_device *gether_qc_connect_name(struct qc_gether *link,
+ const char *netname);
+void gether_qc_disconnect_name(struct qc_gether *link, const char *netname);
/* each configuration may bind one instance of an ethernet link */
int ecm_qc_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]);
diff --git a/drivers/usb/host/ehci-msm-hsic.c b/drivers/usb/host/ehci-msm-hsic.c
index 6fe9e58..cd02489 100644
--- a/drivers/usb/host/ehci-msm-hsic.c
+++ b/drivers/usb/host/ehci-msm-hsic.c
@@ -1144,6 +1144,14 @@
#endif /* CONFIG_PM */
+static void ehci_msm_set_autosuspend_delay(struct usb_device *dev)
+{
+ if (!dev->parent) /*for root hub no delay*/
+ pm_runtime_set_autosuspend_delay(&dev->dev, 0);
+ else
+ pm_runtime_set_autosuspend_delay(&dev->dev, 200);
+}
+
static struct hc_driver msm_hsic_driver = {
.description = hcd_name,
.product_desc = "Qualcomm EHCI Host Controller using HSIC",
@@ -1194,6 +1202,8 @@
.enable_ulpi_control = ehci_msm_enable_ulpi_control,
.disable_ulpi_control = ehci_msm_disable_ulpi_control,
+
+ .set_autosuspend_delay = ehci_msm_set_autosuspend_delay,
};
static int msm_hsic_init_clocks(struct msm_hsic_hcd *mehci, u32 init)
diff --git a/drivers/usb/misc/diag_bridge.c b/drivers/usb/misc/diag_bridge.c
index b1b7763..2d95945 100644
--- a/drivers/usb/misc/diag_bridge.c
+++ b/drivers/usb/misc/diag_bridge.c
@@ -31,6 +31,8 @@
#define DRIVER_DESC "USB host diag bridge driver"
#define DRIVER_VERSION "1.0"
+#define AUTOSUSP_DELAY_WITH_USB 1000
+
struct diag_bridge {
struct usb_device *udev;
struct usb_interface *ifc;
@@ -42,6 +44,7 @@
struct mutex ifc_mutex;
struct diag_bridge_ops *ops;
struct platform_device *pdev;
+ unsigned default_autosusp_delay;
/* debugging counters */
unsigned long bytes_to_host;
@@ -68,6 +71,12 @@
dev->ops = ops;
dev->err = 0;
+#ifdef CONFIG_PM_RUNTIME
+ dev->default_autosusp_delay = dev->udev->dev.power.autosuspend_delay;
+#endif
+ pm_runtime_set_autosuspend_delay(&dev->udev->dev,
+ AUTOSUSP_DELAY_WITH_USB);
+
kref_get(&dev->kref);
return 0;
@@ -101,6 +110,10 @@
usb_kill_anchored_urbs(&dev->submitted);
dev->ops = 0;
+
+ pm_runtime_set_autosuspend_delay(&dev->udev->dev,
+ dev->default_autosusp_delay);
+
kref_put(&dev->kref, diag_bridge_delete);
}
EXPORT_SYMBOL(diag_bridge_close);
diff --git a/drivers/usb/misc/mdm_data_bridge.c b/drivers/usb/misc/mdm_data_bridge.c
index c78fd0c..e821fda 100644
--- a/drivers/usb/misc/mdm_data_bridge.c
+++ b/drivers/usb/misc/mdm_data_bridge.c
@@ -21,7 +21,7 @@
#include <linux/ratelimit.h>
#include <mach/usb_bridge.h>
-#define MAX_RX_URBS 50
+#define MAX_RX_URBS 100
#define RMNET_RX_BUFSIZE 2048
#define STOP_SUBMIT_URB_LIMIT 500
diff --git a/drivers/usb/otg/msm_otg.c b/drivers/usb/otg/msm_otg.c
index 26f1b84..18f8729 100644
--- a/drivers/usb/otg/msm_otg.c
+++ b/drivers/usb/otg/msm_otg.c
@@ -1015,6 +1015,7 @@
if (!atomic_read(&motg->in_lpm))
return 0;
+ disable_irq(motg->irq);
wake_lock(&motg->wlock);
/* Vote for TCXO when waking up the phy */
@@ -1105,6 +1106,7 @@
enable_irq(motg->async_int);
motg->async_int = 0;
}
+ enable_irq(motg->irq);
/* If ASYNC IRQ is present then keep it enabled only during LPM */
if (motg->async_irq)
@@ -4020,10 +4022,6 @@
if (ret)
goto remove_phy;
}
- } else {
- ret = msm_otg_register_power_supply(pdev, motg);
- if (ret)
- goto remove_phy;
}
return 0;
diff --git a/drivers/video/msm/hdmi_msm.c b/drivers/video/msm/hdmi_msm.c
index deef4ab..9f30041 100644
--- a/drivers/video/msm/hdmi_msm.c
+++ b/drivers/video/msm/hdmi_msm.c
@@ -3225,6 +3225,9 @@
/* HDMI_ACR_PKT_CTRL[0x0024] */
uint32 acr_pck_ctrl_reg = HDMI_INP(0x0024);
+ /* Clear N/CTS selection bits */
+ acr_pck_ctrl_reg &= ~(3 << 4);
+
if (enabled) {
const struct hdmi_disp_mode_timing_type *timing =
hdmi_common_get_supported_mode(video_format);
@@ -3629,6 +3632,9 @@
void hdmi_msm_audio_sample_rate_reset(int rate)
{
+ if (msm_hdmi_sample_rate == rate)
+ return;
+
msm_hdmi_sample_rate = rate;
if (hdmi_msm_state->hdcp_enable)
diff --git a/drivers/video/msm/mdp.c b/drivers/video/msm/mdp.c
index 946a9d7..712d41b 100644
--- a/drivers/video/msm/mdp.c
+++ b/drivers/video/msm/mdp.c
@@ -2673,10 +2673,12 @@
mfd->ov0_wb_buf->size = mdp_pdata->ov0_wb_size;
mfd->ov1_wb_buf->size = mdp_pdata->ov1_wb_size;
mfd->mem_hid = mdp_pdata->mem_hid;
+ mfd->avtimer_phy = mdp_pdata->avtimer_phy;
} else {
mfd->ov0_wb_buf->size = 0;
mfd->ov1_wb_buf->size = 0;
mfd->mem_hid = 0;
+ mfd->avtimer_phy = 0;
}
/* initialize Post Processing data*/
diff --git a/drivers/video/msm/mdp4.h b/drivers/video/msm/mdp4.h
index a73b3ad..3ea196a 100644
--- a/drivers/video/msm/mdp4.h
+++ b/drivers/video/msm/mdp4.h
@@ -594,7 +594,7 @@
void mdp4_overlay_dmap_xy(struct mdp4_overlay_pipe *pipe);
void mdp4_overlay_dmae_cfg(struct msm_fb_data_type *mfd, int atv);
void mdp4_overlay_dmae_xy(struct mdp4_overlay_pipe *pipe);
-int mdp4_overlay_pipe_staged(int mixer);
+int mdp4_overlay_pipe_staged(struct mdp4_overlay_pipe *pipe);
void mdp4_lcdc_primary_vsyn(void);
void mdp4_overlay0_done_lcdc(int cndx);
void mdp4_overlay0_done_mddi(int cndx);
@@ -611,6 +611,7 @@
void mdp4_overlay_lcdc_vsync_push(struct msm_fb_data_type *mfd,
struct mdp4_overlay_pipe *pipe);
void mdp4_mddi_overlay_dmas_restore(void);
+void mdp4_dtv_set_avparams(struct mdp4_overlay_pipe *pipe, int id);
#ifndef CONFIG_FB_MSM_MIPI_DSI
void mdp4_mddi_dma_busy_wait(struct msm_fb_data_type *mfd);
diff --git a/drivers/video/msm/mdp4_overlay.c b/drivers/video/msm/mdp4_overlay.c
index ab27267..7c87c44 100644
--- a/drivers/video/msm/mdp4_overlay.c
+++ b/drivers/video/msm/mdp4_overlay.c
@@ -1568,36 +1568,19 @@
mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
}
-int mdp4_overlay_pipe_staged(int mixer)
+int mdp4_overlay_pipe_staged(struct mdp4_overlay_pipe *pipe)
{
- uint32 data, mask, i, off;
- int p1, p2;
+ uint32 data, mask;
+ int mixer;
- if (mixer == MDP4_MIXER2)
- off = 0x100F0;
- else
- off = 0x10100;
+ mixer = pipe->mixer_num;
+ data = ctrl->mixer_cfg[mixer];
- mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
- data = inpdw(MDP_BASE + off);
- mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
- p1 = 0;
- p2 = 0;
- for (i = 0; i < 8; i++) {
- mask = data & 0x0f;
- if (mask) {
- if (mask <= 4)
- p1++;
- else
- p2++;
- }
- data >>= 4;
- }
+ mask = 0x0f;
+ mask <<= (4 * pipe->pipe_num);
+ data &= mask;
- if (mixer)
- return p2;
- else
- return p1;
+ return data;
}
int mdp4_mixer_info(int mixer_num, struct mdp_mixer_info *info)
@@ -2434,6 +2417,11 @@
* zorder 2 == stage 2 == 4
*/
if (req->id == MSMFB_NEW_REQUEST) { /* new request */
+ if (mdp4_overlay_pipe_staged(pipe)) {
+ pr_err("%s: ndx=%d still staged\n", __func__,
+ pipe->pipe_ndx);
+ return -EPERM;
+ }
pipe->pipe_used++;
pipe->mixer_num = mixer;
pr_debug("%s: zorder=%d pipe ndx=%d num=%d\n", __func__,
@@ -3479,8 +3467,10 @@
mdp4_mddi_pipe_queue(0, pipe);
}
} else if (pipe->mixer_num == MDP4_MIXER1) {
- if (ctrl->panel_mode & MDP4_PANEL_DTV)
+ if (ctrl->panel_mode & MDP4_PANEL_DTV) {
mdp4_dtv_pipe_queue(0, pipe);/* cndx = 0 */
+ mdp4_dtv_set_avparams(pipe, img->memory_id);
+ }
} else if (pipe->mixer_num == MDP4_MIXER2) {
ctrl->mixer2_played++;
if (ctrl->panel_mode & MDP4_PANEL_WRITEBACK)
diff --git a/drivers/video/msm/mdp4_overlay_dsi_cmd.c b/drivers/video/msm/mdp4_overlay_dsi_cmd.c
index ecdd567..c5442a7 100644
--- a/drivers/video/msm/mdp4_overlay_dsi_cmd.c
+++ b/drivers/video/msm/mdp4_overlay_dsi_cmd.c
@@ -516,9 +516,9 @@
vctrl = &vsync_ctrl_db[cndx];
pr_debug("%s: ISR, cpu=%d\n", __func__, smp_processor_id());
vctrl->rdptr_intr_tot++;
- vctrl->vsync_time = ktime_get();
spin_lock(&vctrl->spin_lock);
+ vctrl->vsync_time = ktime_get();
complete_all(&vctrl->vsync_comp);
vctrl->wait_vsync_cnt = 0;
@@ -647,6 +647,7 @@
struct vsycn_ctrl *vctrl;
ssize_t ret = 0;
unsigned long flags;
+ u64 vsync_tick;
cndx = 0;
vctrl = &vsync_ctrl_db[0];
@@ -661,10 +662,15 @@
vctrl->wait_vsync_cnt++;
spin_unlock_irqrestore(&vctrl->spin_lock, flags);
- wait_for_completion(&vctrl->vsync_comp);
+ ret = wait_for_completion_interruptible(&vctrl->vsync_comp);
+ if (ret)
+ return ret;
- ret = snprintf(buf, PAGE_SIZE, "VSYNC=%llu",
- ktime_to_ns(vctrl->vsync_time));
+ spin_lock_irqsave(&vctrl->spin_lock, flags);
+ vsync_tick = ktime_to_ns(vctrl->vsync_time);
+ spin_unlock_irqrestore(&vctrl->spin_lock, flags);
+
+ ret = snprintf(buf, PAGE_SIZE, "VSYNC=%llu", vsync_tick);
buf[strlen(buf) + 1] = '\0';
return ret;
}
@@ -1123,14 +1129,18 @@
unsigned long flags;
long long tick;
+ mutex_lock(&mfd->dma->ov_mutex);
vctrl = &vsync_ctrl_db[cndx];
- if (!mfd->panel_power_on)
+ if (!mfd->panel_power_on) {
+ mutex_unlock(&mfd->dma->ov_mutex);
return;
+ }
pipe = vctrl->base_pipe;
if (pipe == NULL) {
pr_err("%s: NO base pipe\n", __func__);
+ mutex_unlock(&mfd->dma->ov_mutex);
return;
}
@@ -1138,6 +1148,7 @@
if (!vctrl->clk_enabled) {
pr_err("%s: mdp clocks disabled\n", __func__);
mutex_unlock(&vctrl->update_lock);
+ mutex_unlock(&mfd->dma->ov_mutex);
return;
}
@@ -1160,12 +1171,9 @@
}
mdp4_overlay_mdp_perf_upd(mfd, 1);
-
- mutex_lock(&mfd->dma->ov_mutex);
mdp4_dsi_cmd_pipe_commit(cndx, 0);
+ mdp4_dsi_cmd_wait4vsync(cndx, &tick);
+ mdp4_overlay_mdp_perf_upd(mfd, 0);
mutex_unlock(&mfd->dma->ov_mutex);
- mdp4_dsi_cmd_wait4vsync(cndx, &tick);
-
- mdp4_overlay_mdp_perf_upd(mfd, 0);
}
diff --git a/drivers/video/msm/mdp4_overlay_dsi_video.c b/drivers/video/msm/mdp4_overlay_dsi_video.c
index 6aa101f..a83c340 100644
--- a/drivers/video/msm/mdp4_overlay_dsi_video.c
+++ b/drivers/video/msm/mdp4_overlay_dsi_video.c
@@ -194,8 +194,6 @@
}
spin_unlock_irqrestore(&vctrl->spin_lock, flags);
- mdp4_overlay_mdp_perf_upd(vctrl->mfd, 1);
-
if (vctrl->blt_change) {
pipe = vctrl->base_pipe;
spin_lock_irqsave(&vctrl->spin_lock, flags);
@@ -382,6 +380,7 @@
struct vsycn_ctrl *vctrl;
ssize_t ret = 0;
unsigned long flags;
+ u64 vsync_tick;
cndx = 0;
vctrl = &vsync_ctrl_db[0];
@@ -395,10 +394,15 @@
INIT_COMPLETION(vctrl->vsync_comp);
vctrl->wait_vsync_cnt++;
spin_unlock_irqrestore(&vctrl->spin_lock, flags);
- wait_for_completion(&vctrl->vsync_comp);
+ ret = wait_for_completion_interruptible(&vctrl->vsync_comp);
+ if (ret)
+ return ret;
- ret = snprintf(buf, PAGE_SIZE, "VSYNC=%llu",
- ktime_to_ns(vctrl->vsync_time));
+ spin_lock_irqsave(&vctrl->spin_lock, flags);
+ vsync_tick = ktime_to_ns(vctrl->vsync_time);
+ spin_unlock_irqrestore(&vctrl->spin_lock, flags);
+
+ ret = snprintf(buf, PAGE_SIZE, "VSYNC=%llu", vsync_tick);
buf[strlen(buf) + 1] = '\0';
return ret;
}
@@ -918,9 +922,10 @@
cndx = 0;
vctrl = &vsync_ctrl_db[cndx];
pr_debug("%s: cpu=%d\n", __func__, smp_processor_id());
- vctrl->vsync_time = ktime_get();
spin_lock(&vctrl->spin_lock);
+ vctrl->vsync_time = ktime_get();
+
if (vctrl->wait_vsync_cnt) {
complete_all(&vctrl->vsync_comp);
vctrl->wait_vsync_cnt = 0;
@@ -1072,11 +1077,15 @@
struct vsycn_ctrl *vctrl;
struct mdp4_overlay_pipe *pipe;
+ mutex_lock(&mfd->dma->ov_mutex);
+
vctrl = &vsync_ctrl_db[cndx];
pipe = vctrl->base_pipe;
- if (!pipe || !mfd->panel_power_on)
+ if (!pipe || !mfd->panel_power_on) {
+ mutex_unlock(&mfd->dma->ov_mutex);
return;
+ }
pr_debug("%s: cpu=%d pid=%d\n", __func__,
smp_processor_id(), current->pid);
@@ -1095,10 +1104,7 @@
mdp_update_pm(mfd, vsync_ctrl_db[0].vsync_time);
mdp4_overlay_mdp_perf_upd(mfd, 1);
-
- mutex_lock(&mfd->dma->ov_mutex);
mdp4_dsi_video_pipe_commit(cndx, 0);
- mutex_unlock(&mfd->dma->ov_mutex);
if (pipe->ov_blt_addr)
mdp4_dsi_video_wait4ov(cndx);
@@ -1106,5 +1112,6 @@
mdp4_dsi_video_wait4dmap(cndx);
mdp4_overlay_mdp_perf_upd(mfd, 0);
+ mutex_unlock(&mfd->dma->ov_mutex);
}
diff --git a/drivers/video/msm/mdp4_overlay_dtv.c b/drivers/video/msm/mdp4_overlay_dtv.c
index 21e5d1d..67690cf 100644
--- a/drivers/video/msm/mdp4_overlay_dtv.c
+++ b/drivers/video/msm/mdp4_overlay_dtv.c
@@ -33,7 +33,6 @@
#include "mdp4.h"
#define DTV_BASE 0xD0000
-
static int dtv_enabled;
/*#define DEBUG*/
@@ -55,14 +54,6 @@
static int first_pixel_start_x;
static int first_pixel_start_y;
-void mdp4_dtv_base_swap(int cndx, struct mdp4_overlay_pipe *pipe)
-{
-#ifdef BYPASS4
- if (hdmi_prim_display)
- dtv_pipe = pipe;
-#endif
-}
-
#define MAX_CONTROLLER 1
static struct vsycn_ctrl {
@@ -85,6 +76,10 @@
struct vsync_update vlist[2];
int vsync_irq_enabled;
ktime_t vsync_time;
+ uint32 *avtimer;
+ int vg1fd;
+ int vg2fd;
+ unsigned long long avtimer_tick;
} vsync_ctrl_db[MAX_CONTROLLER];
static void vsync_irq_enable(int intr, int term)
@@ -322,9 +317,14 @@
struct vsycn_ctrl *vctrl;
ssize_t ret = 0;
unsigned long flags;
+ char ch = '\0';
+ int vg1fd = -1, vg2fd = -1;
+ unsigned long long avtimer_tick = 0;
+ u64 vsync_tick = 0;
cndx = 0;
vctrl = &vsync_ctrl_db[0];
+ memset(buf, 0, 64);
if (atomic_read(&vctrl->suspend) > 0 ||
!external_common_state->hpd_state ||
@@ -336,13 +336,31 @@
INIT_COMPLETION(vctrl->vsync_comp);
vctrl->wait_vsync_cnt++;
spin_unlock_irqrestore(&vctrl->spin_lock, flags);
- wait_for_completion(&vctrl->vsync_comp);
- ret = snprintf(buf, PAGE_SIZE, "VSYNC=%llu",
- ktime_to_ns(vctrl->vsync_time));
- buf[strlen(buf) + 1] = '\0';
+ ret = wait_for_completion_interruptible(&vctrl->vsync_comp);
+ if (ret)
+ return ret;
+
+ spin_lock_irqsave(&vctrl->spin_lock, flags);
+ vg1fd = vctrl->vg1fd;
+ vg2fd = vctrl->vg2fd;
+ avtimer_tick = vctrl->avtimer_tick;
+ vsync_tick = ktime_to_ns(vctrl->vsync_time);
+ spin_unlock_irqrestore(&vctrl->spin_lock, flags);
+
+ ret = snprintf(buf, PAGE_SIZE,
+ "VSYNC=%llu%c"
+ "AVSYNCTP=%llu%c"
+ "VG1MEMID=%d%c"
+ "VG2MEMID=%d",
+ vsync_tick,
+ ch, avtimer_tick,
+ ch, vg1fd,
+ ch, vg2fd);
+
return ret;
}
+
void mdp4_dtv_vsync_init(int cndx)
{
struct vsycn_ctrl *vctrl;
@@ -369,6 +387,24 @@
spin_lock_init(&vctrl->spin_lock);
}
+void mdp4_dtv_base_swap(int cndx, struct mdp4_overlay_pipe *pipe)
+{
+ struct vsycn_ctrl *vctrl;
+
+ if (!hdmi_prim_display) {
+ pr_err("%s: failed, hdmi is not primary\n", __func__);
+ return;
+ }
+
+ if (cndx >= MAX_CONTROLLER) {
+ pr_err("%s: out or range: cndx=%d\n", __func__, cndx);
+ return;
+ }
+
+ vctrl = &vsync_ctrl_db[cndx];
+ vctrl->base_pipe = pipe;
+}
+
static int mdp4_dtv_start(struct msm_fb_data_type *mfd)
{
int dtv_width;
@@ -587,6 +623,13 @@
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)
+ pr_err(" avtimer ioremap fail\n");
+ }
+
pr_info("%s:\n", __func__);
return ret;
@@ -656,6 +699,11 @@
vp->update_cnt = 0; /* empty queue */
}
+ if (vctrl->avtimer != NULL) {
+ iounmap(vctrl->avtimer);
+ vctrl->avtimer = NULL;
+ }
+
ret = panel_next_off(pdev);
mdp_footswitch_ctrl(FALSE);
@@ -834,7 +882,7 @@
struct vsycn_ctrl *vctrl;
vctrl = &vsync_ctrl_db[cndx];
- if (vctrl->base_pipe != NULL)
+ if (vctrl->base_pipe == NULL)
return 0;
if (pipe->mixer_stage == MDP4_MIXER_STAGE_BASE &&
@@ -842,6 +890,12 @@
result = mdp4_dtv_stop(mfd);
vctrl->base_pipe = NULL;
}
+
+ if (pipe->pipe_num == OVERLAY_PIPE_VG1)
+ vctrl->vg1fd = -1;
+ else if (pipe->pipe_num == OVERLAY_PIPE_VG2)
+ vctrl->vg2fd = -1;
+
return result;
}
@@ -851,13 +905,24 @@
{
int cndx;
struct vsycn_ctrl *vctrl;
+ uint32 *tp, LSW;
cndx = 0;
vctrl = &vsync_ctrl_db[cndx];
pr_debug("%s: cpu=%d\n", __func__, smp_processor_id());
- vctrl->vsync_time = ktime_get();
spin_lock(&vctrl->spin_lock);
+ vctrl->vsync_time = ktime_get();
+ vctrl->avtimer_tick = 0;
+
+ if (vctrl->avtimer && ((vctrl->vg1fd > 0) || (vctrl->vg2fd > 0))) {
+ tp = vctrl->avtimer;
+ LSW = inpdw(tp);
+ tp++;
+ vctrl->avtimer_tick = (unsigned long long) inpdw(tp);
+ vctrl->avtimer_tick = ((vctrl->avtimer_tick << 32) | LSW);
+ }
+
if (vctrl->wait_vsync_cnt) {
complete_all(&vctrl->vsync_comp);
vctrl->wait_vsync_cnt = 0;
@@ -1050,8 +1115,11 @@
struct vsycn_ctrl *vctrl;
struct mdp4_overlay_pipe *pipe;
- if (!mfd->panel_power_on)
+ mutex_lock(&mfd->dma->ov_mutex);
+ if (!mfd->panel_power_on) {
+ mutex_unlock(&mfd->dma->ov_mutex);
return;
+ }
vctrl = &vsync_ctrl_db[cndx];
if (vctrl->base_pipe == NULL)
@@ -1061,6 +1129,7 @@
if (pipe == NULL) {
pr_warn("%s: dtv_pipe == NULL\n", __func__);
+ mutex_unlock(&mfd->dma->ov_mutex);
return;
}
@@ -1076,10 +1145,24 @@
mdp4_dtv_pipe_queue(0, pipe);
}
mdp_update_pm(mfd, vsync_ctrl_db[0].vsync_time);
-
- mutex_lock(&mfd->dma->ov_mutex);
mdp4_overlay_mdp_perf_upd(mfd, 1);
mdp4_dtv_pipe_commit(cndx, 0);
mdp4_overlay_mdp_perf_upd(mfd, 0);
mutex_unlock(&mfd->dma->ov_mutex);
}
+
+void mdp4_dtv_set_avparams(struct mdp4_overlay_pipe *pipe, int id)
+{
+ struct vsycn_ctrl *vctrl;
+
+ if (pipe == NULL) {
+ pr_warn("%s: dtv_pipe == NULL\n", __func__);
+ return;
+ }
+ vctrl = &vsync_ctrl_db[0];
+ if (pipe->pipe_num == OVERLAY_PIPE_VG1)
+ vctrl->vg1fd = id;
+ else if (pipe->pipe_num == OVERLAY_PIPE_VG2)
+ vctrl->vg2fd = id;
+}
+
diff --git a/drivers/video/msm/mdp4_overlay_lcdc.c b/drivers/video/msm/mdp4_overlay_lcdc.c
index 1f5136f..9e0c411 100644
--- a/drivers/video/msm/mdp4_overlay_lcdc.c
+++ b/drivers/video/msm/mdp4_overlay_lcdc.c
@@ -366,6 +366,7 @@
struct vsycn_ctrl *vctrl;
ssize_t ret = 0;
unsigned long flags;
+ u64 vsync_tick;
cndx = 0;
vctrl = &vsync_ctrl_db[0];
@@ -379,10 +380,15 @@
INIT_COMPLETION(vctrl->vsync_comp);
vctrl->wait_vsync_cnt++;
spin_unlock_irqrestore(&vctrl->spin_lock, flags);
- wait_for_completion(&vctrl->vsync_comp);
+ ret = wait_for_completion_interruptible(&vctrl->vsync_comp);
+ if (ret)
+ return ret;
- ret = snprintf(buf, PAGE_SIZE, "VSYNC=%llu",
- ktime_to_ns(vctrl->vsync_time));
+ spin_lock_irqsave(&vctrl->spin_lock, flags);
+ vsync_tick = ktime_to_ns(vctrl->vsync_time);
+ spin_unlock_irqrestore(&vctrl->spin_lock, flags);
+
+ ret = snprintf(buf, PAGE_SIZE, "VSYNC=%llu", vsync_tick);
buf[strlen(buf) + 1] = '\0';
return ret;
}
@@ -804,9 +810,10 @@
cndx = 0;
vctrl = &vsync_ctrl_db[cndx];
pr_debug("%s: cpu=%d\n", __func__, smp_processor_id());
- vctrl->vsync_time = ktime_get();
spin_lock(&vctrl->spin_lock);
+ vctrl->vsync_time = ktime_get();
+
if (vctrl->wait_vsync_cnt) {
complete_all(&vctrl->vsync_comp);
vctrl->wait_vsync_cnt = 0;
@@ -958,12 +965,15 @@
struct vsycn_ctrl *vctrl;
struct mdp4_overlay_pipe *pipe;
+ mutex_lock(&mfd->dma->ov_mutex);
vctrl = &vsync_ctrl_db[cndx];
pipe = vctrl->base_pipe;
- if (!pipe || !mfd->panel_power_on)
+ if (!pipe || !mfd->panel_power_on) {
+ mutex_unlock(&mfd->dma->ov_mutex);
return;
+ }
pr_debug("%s: cpu=%d pid=%d\n", __func__,
smp_processor_id(), current->pid);
@@ -983,9 +993,8 @@
mdp4_overlay_mdp_perf_upd(mfd, 1);
- mutex_lock(&mfd->dma->ov_mutex);
+
mdp4_lcdc_pipe_commit(cndx, 0);
- mutex_unlock(&mfd->dma->ov_mutex);
if (pipe->ov_blt_addr)
mdp4_lcdc_wait4ov(cndx);
@@ -993,4 +1002,5 @@
mdp4_lcdc_wait4dmap(cndx);
mdp4_overlay_mdp_perf_upd(mfd, 0);
+ mutex_unlock(&mfd->dma->ov_mutex);
}
diff --git a/drivers/video/msm/mdss/Makefile b/drivers/video/msm/mdss/Makefile
index ddb6dd9..b4bd31e 100644
--- a/drivers/video/msm/mdss/Makefile
+++ b/drivers/video/msm/mdss/Makefile
@@ -7,14 +7,16 @@
mdss-mdp-objs += mdss_mdp_wb.o
obj-$(CONFIG_FB_MSM_MDSS) += mdss-mdp.o
obj-$(CONFIG_FB_MSM_MDSS) += mdss_fb.o
-obj-$(CONFIG_FB_MSM_MDSS) += mdss_io_util.o
mdss-dsi-objs := mdss_dsi.o mdss_dsi_host.o
mdss-dsi-objs += mdss_dsi_panel.o
mdss-dsi-objs += msm_mdss_io_8974.o
obj-$(CONFIG_FB_MSM_MDSS) += mdss-dsi.o
+obj-$(CONFIG_FB_MSM_MDSS) += mdss_edp.o
+obj-$(CONFIG_FB_MSM_MDSS) += mdss_io_util.o
obj-$(CONFIG_FB_MSM_MDSS_HDMI_PANEL) += mdss_hdmi_tx.o
obj-$(CONFIG_FB_MSM_MDSS_HDMI_PANEL) += mdss_hdmi_util.o
obj-$(CONFIG_FB_MSM_MDSS_HDMI_PANEL) += mdss_hdmi_edid.o
+
obj-$(CONFIG_FB_MSM_MDSS_WRITEBACK) += mdss_wb.o
diff --git a/drivers/video/msm/mdss/mdss_edp.c b/drivers/video/msm/mdss/mdss_edp.c
new file mode 100644
index 0000000..9460d71
--- /dev/null
+++ b/drivers/video/msm/mdss/mdss_edp.c
@@ -0,0 +1,526 @@
+/* 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/module.h>
+#include <linux/kernel.h>
+#include <linux/time.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/of_platform.h>
+#include <linux/of_gpio.h>
+#include <linux/gpio.h>
+#include <linux/err.h>
+
+#include <asm/system.h>
+#include <asm/mach-types.h>
+
+#include <mach/hardware.h>
+#include <mach/dma.h>
+
+#include "mdss_edp.h"
+
+#define RGB_COMPONENTS 3
+#define VDDA_MIN_UV 1800000 /* uV units */
+#define VDDA_MAX_UV 1800000 /* uV units */
+#define VDDA_UA_ON_LOAD 100000 /* uA units */
+#define VDDA_UA_OFF_LOAD 100 /* uA units */
+
+
+static int mdss_edp_get_base_address(struct mdss_edp_drv_pdata *edp_drv);
+static int mdss_edp_get_mmss_cc_base_address(struct mdss_edp_drv_pdata
+ *edp_drv);
+static int mdss_edp_regulator_init(struct mdss_edp_drv_pdata *edp_drv);
+static int mdss_edp_regulator_on(struct mdss_edp_drv_pdata *edp_drv);
+static int mdss_edp_regulator_off(struct mdss_edp_drv_pdata *edp_drv);
+
+static int mdss_edp_gpio_panel_en(struct mdss_edp_drv_pdata *edp_drv);
+
+static void mdss_edp_edid2pinfo(struct mdss_edp_drv_pdata *edp_drv);
+static void mdss_edp_fill_edid_data(struct mdss_edp_drv_pdata *edp_drv);
+static void mdss_edp_fill_dpcd_data(struct mdss_edp_drv_pdata *edp_drv);
+
+static int mdss_edp_device_register(struct mdss_edp_drv_pdata *edp_drv);
+
+static void mdss_edp_config_sync(unsigned char *edp_base);
+static void mdss_edp_config_sw_div(unsigned char *edp_base);
+static void mdss_edp_config_static_mdiv(unsigned char *edp_base);
+static void mdss_edp_enable(unsigned char *edp_base, int enable);
+
+/*
+ * Init regulator needed for edp, 8974_l12
+ */
+static int mdss_edp_regulator_init(struct mdss_edp_drv_pdata *edp_drv)
+{
+ int ret;
+
+ edp_drv->vdda_vreg = devm_regulator_get(&(edp_drv->pdev->dev), "vdda");
+ if (IS_ERR(edp_drv->vdda_vreg)) {
+ pr_err("%s: Could not get 8941_l12, ret = %ld\n", __func__,
+ PTR_ERR(edp_drv->vdda_vreg));
+ return -ENODEV;
+ }
+
+ ret = regulator_set_voltage(edp_drv->vdda_vreg,
+ VDDA_MIN_UV, VDDA_MAX_UV);
+ if (ret) {
+ pr_err("%s: vdda_vreg set_voltage failed, ret=%d\n", __func__,
+ ret);
+ return -EINVAL;
+ }
+
+ ret = mdss_edp_regulator_on(edp_drv);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+/*
+ * Set uA and enable vdda
+ */
+static int mdss_edp_regulator_on(struct mdss_edp_drv_pdata *edp_drv)
+{
+ int ret;
+
+ ret = regulator_set_optimum_mode(edp_drv->vdda_vreg, VDDA_UA_ON_LOAD);
+ if (ret < 0) {
+ pr_err("%s: vdda_vreg set regulator mode failed.\n", __func__);
+ return ret;
+ }
+
+ ret = regulator_enable(edp_drv->vdda_vreg);
+ if (ret) {
+ pr_err("%s: Failed to enable vdda_vreg regulator.\n", __func__);
+ return ret;
+ }
+
+ return 0;
+}
+
+/*
+ * Disable vdda and set uA
+ */
+static int mdss_edp_regulator_off(struct mdss_edp_drv_pdata *edp_drv)
+{
+ int ret;
+
+ ret = regulator_disable(edp_drv->vdda_vreg);
+ if (ret) {
+ pr_err("%s: Failed to disable vdda_vreg regulator.\n",
+ __func__);
+ return ret;
+ }
+
+ ret = regulator_set_optimum_mode(edp_drv->vdda_vreg, VDDA_UA_OFF_LOAD);
+ if (ret < 0) {
+ pr_err("%s: vdda_vreg set regulator mode failed.\n",
+ __func__);
+ return ret;
+ }
+
+ return 0;
+}
+
+/*
+ * Enables the gpio that supply power to the panel
+ */
+static int mdss_edp_gpio_panel_en(struct mdss_edp_drv_pdata *edp_drv)
+{
+ int ret = 0;
+
+ edp_drv->gpio_panel_en = of_get_named_gpio(edp_drv->pdev->dev.of_node,
+ "gpio-panel-en", 0);
+ if (!gpio_is_valid(edp_drv->gpio_panel_en)) {
+ pr_err("%s: gpio_panel_en not specified\n", __func__);
+ goto gpio_err;
+ }
+
+ ret = gpio_request(edp_drv->gpio_panel_en, "disp_enable");
+ if (ret) {
+ pr_err("%s: Request reset gpio_panel_en failed, ret=%d\n",
+ __func__, ret);
+ goto gpio_free;
+ }
+
+ ret = gpio_direction_output(edp_drv->gpio_panel_en, 1);
+ if (ret) {
+ pr_err("%s: Set direction for gpio_panel_en failed, ret=%d\n",
+ __func__, ret);
+ goto gpio_free;
+ }
+
+ gpio_set_value(edp_drv->gpio_panel_en, 1);
+
+ return 0;
+
+gpio_free:
+ gpio_free(edp_drv->gpio_panel_en);
+gpio_err:
+ return -ENODEV;
+}
+
+static void mdss_edp_config_sync(unsigned char *edp_base)
+{
+ int ret = 0;
+
+ ret = edp_read(edp_base + 0xc); /* EDP_CONFIGURATION_CTRL */
+ ret &= ~0x733;
+ ret |= (0x55 & 0x733);
+ edp_write(edp_base + 0xc, ret);
+ edp_write(edp_base + 0xc, 0x55); /* EDP_CONFIGURATION_CTRL */
+}
+
+static void mdss_edp_config_sw_div(unsigned char *edp_base)
+{
+ edp_write(edp_base + 0x14, 0x13b); /* EDP_SOFTWARE_MVID */
+ edp_write(edp_base + 0x18, 0x266); /* EDP_SOFTWARE_NVID */
+}
+
+static void mdss_edp_config_static_mdiv(unsigned char *edp_base)
+{
+ int ret = 0;
+
+ ret = edp_read(edp_base + 0xc); /* EDP_CONFIGURATION_CTRL */
+ edp_write(edp_base + 0xc, ret | 0x2); /* EDP_CONFIGURATION_CTRL */
+ edp_write(edp_base + 0xc, 0x57); /* EDP_CONFIGURATION_CTRL */
+}
+
+static void mdss_edp_enable(unsigned char *edp_base, int enable)
+{
+ edp_write(edp_base + 0x8, 0x0); /* EDP_STATE_CTRL */
+ edp_write(edp_base + 0x8, 0x40); /* EDP_STATE_CTRL */
+ edp_write(edp_base + 0x94, enable); /* EDP_TIMING_ENGINE_EN */
+ edp_write(edp_base + 0x4, enable); /* EDP_MAINLINK_CTRL */
+}
+
+int mdss_edp_on(struct mdss_panel_data *pdata)
+{
+ struct mdss_edp_drv_pdata *edp_drv = NULL;
+ int i;
+
+ edp_drv = container_of(pdata, struct mdss_edp_drv_pdata,
+ panel_data);
+ if (!edp_drv) {
+ pr_err("%s: Invalid input data\n", __func__);
+ return -EINVAL;
+ }
+
+ mdss_edp_prepare_clocks(edp_drv);
+ mdss_edp_clk_enable(edp_drv);
+ mdss_edp_phy_sw_reset(edp_drv->edp_base);
+ mdss_edp_hw_powerup(edp_drv->edp_base, 1);
+ mdss_edp_pll_configure(edp_drv->edp_base, edp_drv->edid.timing[0].pclk);
+
+ for (i = 0; i < edp_drv->dpcd.max_lane_count; ++i)
+ mdss_edp_enable_lane_bist(edp_drv->edp_base, i, 1);
+
+ mdss_edp_enable_mainlink(edp_drv->edp_base, 1);
+ mdss_edp_config_clk(edp_drv->edp_base, edp_drv->mmss_cc_base);
+
+ mdss_edp_phy_misc_cfg(edp_drv->edp_base);
+ mdss_edp_config_sync(edp_drv->edp_base);
+ mdss_edp_config_sw_div(edp_drv->edp_base);
+ mdss_edp_config_static_mdiv(edp_drv->edp_base);
+ mdss_edp_enable(edp_drv->edp_base, 1);
+
+ return 0;
+}
+
+int mdss_edp_off(struct mdss_panel_data *pdata)
+{
+ struct mdss_edp_drv_pdata *edp_drv = NULL;
+ int ret = 0;
+ int i;
+
+ edp_drv = container_of(pdata, struct mdss_edp_drv_pdata,
+ panel_data);
+ if (!edp_drv) {
+ pr_err("%s: Invalid input data\n", __func__);
+ return -EINVAL;
+ }
+
+ mdss_edp_enable(edp_drv->edp_base, 0);
+ mdss_edp_unconfig_clk(edp_drv->edp_base, edp_drv->mmss_cc_base);
+ mdss_edp_enable_mainlink(edp_drv->edp_base, 0);
+
+ for (i = 0; i < edp_drv->dpcd.max_lane_count; ++i)
+ mdss_edp_enable_lane_bist(edp_drv->edp_base, i, 0);
+
+ mdss_edp_hw_powerup(edp_drv->edp_base, 0);
+ mdss_edp_clk_disable(edp_drv);
+ mdss_edp_unprepare_clocks(edp_drv);
+
+ return ret;
+}
+
+/*
+ * Converts from EDID struct to mdss_panel_info
+ */
+static void mdss_edp_edid2pinfo(struct mdss_edp_drv_pdata *edp_drv)
+{
+ struct display_timing_desc *dp;
+ struct mdss_panel_info *pinfo;
+
+ dp = &edp_drv->edid.timing[0];
+ pinfo = &edp_drv->panel_data.panel_info;
+
+ pinfo->clk_rate = dp->pclk;
+
+ pinfo->xres = dp->h_addressable + dp->h_border * 2;
+ pinfo->yres = dp->v_addressable + dp->v_border * 2;
+
+ pinfo->lcdc.h_back_porch = dp->h_blank - dp->h_fporch \
+ - dp->h_sync_pulse;
+ pinfo->lcdc.h_front_porch = dp->h_fporch;
+ pinfo->lcdc.h_pulse_width = dp->h_sync_pulse;
+
+ pinfo->lcdc.v_back_porch = dp->v_blank - dp->v_fporch \
+ - dp->v_sync_pulse;
+ pinfo->lcdc.v_front_porch = dp->v_fporch;
+ pinfo->lcdc.v_pulse_width = dp->v_sync_pulse;
+
+ pinfo->type = EDP_PANEL;
+ pinfo->pdest = DISPLAY_1;
+ pinfo->wait_cycle = 0;
+ pinfo->bpp = edp_drv->edid.color_depth * RGB_COMPONENTS;
+ pinfo->fb_num = 2;
+
+ pinfo->lcdc.border_clr = 0; /* black */
+ pinfo->lcdc.underflow_clr = 0xff; /* blue */
+ pinfo->lcdc.hsync_skew = 0;
+}
+
+static int __devexit mdss_edp_remove(struct platform_device *pdev)
+{
+ struct mdss_edp_drv_pdata *edp_drv = NULL;
+
+ edp_drv = platform_get_drvdata(pdev);
+
+ gpio_free(edp_drv->gpio_panel_en);
+ mdss_edp_regulator_off(edp_drv);
+ iounmap(edp_drv->edp_base);
+ iounmap(edp_drv->mmss_cc_base);
+ edp_drv->edp_base = NULL;
+
+ return 0;
+}
+
+static int mdss_edp_device_register(struct mdss_edp_drv_pdata *edp_drv)
+{
+ int ret;
+
+ mdss_edp_edid2pinfo(edp_drv);
+
+ edp_drv->panel_data.on = mdss_edp_on;
+ edp_drv->panel_data.off = mdss_edp_off;
+
+ ret = mdss_register_panel(&edp_drv->panel_data);
+ if (ret) {
+ dev_err(&(edp_drv->pdev->dev), "unable to register eDP\n");
+ return ret;
+ }
+
+ pr_debug("%s: eDP initialized\n", __func__);
+
+ return 0;
+}
+
+/*
+ * Retrieve edp base address
+ */
+static int mdss_edp_get_base_address(struct mdss_edp_drv_pdata *edp_drv)
+{
+ struct resource *res;
+
+ res = platform_get_resource_byname(edp_drv->pdev, IORESOURCE_MEM,
+ "edp_base");
+ if (!res) {
+ pr_err("%s: Unable to get the MDSS EDP resources", __func__);
+ return -ENOMEM;
+ }
+
+ edp_drv->edp_base = ioremap(res->start, resource_size(res));
+ if (!edp_drv->edp_base) {
+ pr_err("%s: Unable to remap EDP resources", __func__);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static int mdss_edp_get_mmss_cc_base_address(struct mdss_edp_drv_pdata
+ *edp_drv)
+{
+ struct resource *res;
+
+ res = platform_get_resource_byname(edp_drv->pdev, IORESOURCE_MEM,
+ "mmss_cc_base");
+ if (!res) {
+ pr_err("%s: Unable to get the MMSS_CC resources", __func__);
+ return -ENOMEM;
+ }
+
+ edp_drv->mmss_cc_base = ioremap(res->start, resource_size(res));
+ if (!edp_drv->mmss_cc_base) {
+ pr_err("%s: Unable to remap MMSS_CC resources", __func__);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static void mdss_edp_fill_edid_data(struct mdss_edp_drv_pdata *edp_drv)
+{
+ struct edp_edid *edid = &edp_drv->edid;
+
+ edid->id_name[0] = 'A';
+ edid->id_name[0] = 'U';
+ edid->id_name[0] = 'O';
+ edid->id_name[0] = 0;
+ edid->id_product = 0x305D;
+ edid->version = 1;
+ edid->revision = 4;
+ edid->ext_block_cnt = 0;
+ edid->video_digital = 0x5;
+ edid->color_depth = 6;
+ edid->dpm = 0;
+ edid->color_format = 0;
+ edid->timing[0].pclk = 138500000;
+ edid->timing[0].h_addressable = 1920;
+ edid->timing[0].h_blank = 160;
+ edid->timing[0].v_addressable = 1080;
+ edid->timing[0].v_blank = 30;
+ edid->timing[0].h_fporch = 48;
+ edid->timing[0].h_sync_pulse = 32;
+ edid->timing[0].v_sync_pulse = 14;
+ edid->timing[0].v_fporch = 8;
+ edid->timing[0].width_mm = 256;
+ edid->timing[0].height_mm = 144;
+ edid->timing[0].h_border = 0;
+ edid->timing[0].v_border = 0;
+ edid->timing[0].interlaced = 0;
+ edid->timing[0].stereo = 0;
+ edid->timing[0].sync_type = 1;
+ edid->timing[0].sync_separate = 1;
+ edid->timing[0].vsync_pol = 0;
+ edid->timing[0].hsync_pol = 0;
+
+}
+
+static void mdss_edp_fill_dpcd_data(struct mdss_edp_drv_pdata *edp_drv)
+{
+ struct dpcd_cap *cap = &edp_drv->dpcd;
+
+ cap->max_lane_count = 2;
+ cap->max_link_clk = 270;
+}
+
+
+static int __devinit mdss_edp_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct mdss_edp_drv_pdata *edp_drv;
+
+ if (!pdev->dev.of_node) {
+ pr_err("%s: Failed\n", __func__);
+ return -EPERM;
+ }
+
+ edp_drv = devm_kzalloc(&pdev->dev, sizeof(*edp_drv), GFP_KERNEL);
+ if (edp_drv == NULL) {
+ pr_err("%s: Failed, could not allocate edp_drv", __func__);
+ return -ENOMEM;
+ }
+
+ edp_drv->pdev = pdev;
+ edp_drv->pdev->id = 1;
+ edp_drv->clk_on = 0;
+
+ ret = mdss_edp_get_base_address(edp_drv);
+ if (ret)
+ goto probe_err;
+
+ ret = mdss_edp_get_mmss_cc_base_address(edp_drv);
+ if (ret)
+ goto edp_base_unmap;
+
+ ret = mdss_edp_regulator_init(edp_drv);
+ if (ret)
+ goto mmss_cc_base_unmap;
+
+ ret = mdss_edp_clk_init(edp_drv);
+ if (ret)
+ goto edp_clk_deinit;
+
+ ret = mdss_edp_gpio_panel_en(edp_drv);
+ if (ret)
+ goto edp_clk_deinit;
+
+ mdss_edp_fill_edid_data(edp_drv);
+ mdss_edp_fill_dpcd_data(edp_drv);
+ mdss_edp_device_register(edp_drv);
+
+ return 0;
+
+edp_clk_deinit:
+ mdss_edp_clk_deinit(edp_drv);
+ mdss_edp_regulator_off(edp_drv);
+mmss_cc_base_unmap:
+ iounmap(edp_drv->mmss_cc_base);
+edp_base_unmap:
+ iounmap(edp_drv->edp_base);
+probe_err:
+ return ret;
+
+}
+
+static const struct of_device_id msm_mdss_edp_dt_match[] = {
+ {.compatible = "qcom,mdss-edp"},
+ {}
+};
+MODULE_DEVICE_TABLE(of, msm_mdss_edp_dt_match);
+
+static struct platform_driver mdss_edp_driver = {
+ .probe = mdss_edp_probe,
+ .remove = __devexit_p(mdss_edp_remove),
+ .shutdown = NULL,
+ .driver = {
+ .name = "mdss_edp",
+ .of_match_table = msm_mdss_edp_dt_match,
+ },
+};
+
+static int __init mdss_edp_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&mdss_edp_driver);
+ if (ret) {
+ pr_err("%s driver register failed", __func__);
+ return ret;
+ }
+
+ return ret;
+}
+module_init(mdss_edp_init);
+
+static void __exit mdss_edp_driver_cleanup(void)
+{
+ platform_driver_unregister(&mdss_edp_driver);
+}
+module_exit(mdss_edp_driver_cleanup);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("eDP controller driver");
diff --git a/drivers/video/msm/mdss/mdss_edp.h b/drivers/video/msm/mdss/mdss_edp.h
new file mode 100644
index 0000000..72c061f
--- /dev/null
+++ b/drivers/video/msm/mdss/mdss_edp.h
@@ -0,0 +1,113 @@
+/* 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 MDSS_EDP_H
+#define MDSS_EDP_H
+
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/clk.h>
+
+#include "mdss_panel.h"
+
+#define edp_read(offset) readl_relaxed((offset))
+#define edp_write(offset, data) writel_relaxed((data), (offset))
+
+struct display_timing_desc {
+ u32 pclk;
+ u32 h_addressable; /* addressable + boder = active */
+ u32 h_border;
+ u32 h_blank; /* fporch + bporch + sync_pulse = blank */
+ u32 h_fporch;
+ u32 h_sync_pulse;
+ u32 v_addressable; /* addressable + boder = active */
+ u32 v_border;
+ u32 v_blank; /* fporch + bporch + sync_pulse = blank */
+ u32 v_fporch;
+ u32 v_sync_pulse;
+ u32 width_mm;
+ u32 height_mm;
+ u32 interlaced;
+ u32 stereo;
+ u32 sync_type;
+ u32 sync_separate;
+ u32 vsync_pol;
+ u32 hsync_pol;
+};
+
+struct edp_edid {
+ char id_name[4];
+ short id_product;
+ char version;
+ char revision;
+ char video_digital;
+ char color_depth; /* 6, 8, 10, 12 and 14 bits */
+ char color_format; /* RGB 4:4:4, YCrCb 4:4:4, Ycrcb 4:2:2 */
+ char dpm; /* display power management */
+ char sync_digital; /* 1 = digital */
+ char sync_separate; /* 1 = separate */
+ char vsync_pol; /* 0 = negative, 1 = positive */
+ char hsync_pol; /* 0 = negative, 1 = positive */
+ char ext_block_cnt;
+ struct display_timing_desc timing[4];
+};
+
+struct dpcd_cap {
+ char max_lane_count;
+ u32 max_link_clk; /* 162, 270 and 540 Mb, divided by 10 */
+};
+
+struct mdss_edp_drv_pdata {
+ /* device driver */
+ int (*on) (struct mdss_panel_data *pdata);
+ int (*off) (struct mdss_panel_data *pdata);
+ struct platform_device *pdev;
+
+ /* edp specific */
+ struct mdss_panel_data panel_data;
+ unsigned char *edp_base;
+ unsigned char *mmss_cc_base;
+ struct edp_edid edid;
+ struct dpcd_cap dpcd;
+
+ /* regulators */
+ struct regulator *vdda_vreg;
+
+ /* clocks */
+ struct clk *aux_clk;
+ struct clk *pixel_clk;
+ struct clk *ahb_clk;
+ struct clk *link_clk;
+ int clk_on;
+
+ /* gpios */
+ int gpio_panel_en;
+};
+
+void mdss_edp_phy_sw_reset(unsigned char *edp_base);
+void mdss_edp_pll_configure(unsigned char *edp_base, int rate);
+void mdss_edp_enable_lane_bist(unsigned char *edp_base, int lane, int enable);
+void mdss_edp_enable_mainlink(unsigned char *edp_base, int enable);
+void mdss_edp_hw_powerup(unsigned char *edp_base, int enable);
+void mdss_edp_clk_enable(struct mdss_edp_drv_pdata *edp_drv);
+void mdss_edp_clk_disable(struct mdss_edp_drv_pdata *edp_drv);
+int mdss_edp_clk_init(struct mdss_edp_drv_pdata *edp_drv);
+void mdss_edp_clk_deinit(struct mdss_edp_drv_pdata *edp_drv);
+void mdss_edp_prepare_clocks(struct mdss_edp_drv_pdata *edp_drv);
+void mdss_edp_unprepare_clocks(struct mdss_edp_drv_pdata *edp_drv);
+void mdss_edp_config_clk(unsigned char *edp_base, unsigned char *mmss_cc_base);
+void mdss_edp_unconfig_clk(unsigned char *edp_base,
+ unsigned char *mmss_cc_base);
+void mdss_edp_phy_misc_cfg(unsigned char *edp_base);
+
+#endif /* MDSS_EDP_H */
diff --git a/drivers/video/msm/mdss/mdss_fb.c b/drivers/video/msm/mdss/mdss_fb.c
index 9f29887..ca5f890 100644
--- a/drivers/video/msm/mdss/mdss_fb.c
+++ b/drivers/video/msm/mdss/mdss_fb.c
@@ -139,6 +139,9 @@
case WRITEBACK_PANEL:
ret = snprintf(buf, PAGE_SIZE, "writeback panel\n");
break;
+ case EDP_PANEL:
+ ret = snprintf(buf, PAGE_SIZE, "edp panel\n");
+ break;
default:
ret = snprintf(buf, PAGE_SIZE, "unknown panel\n");
break;
diff --git a/drivers/video/msm/mdss/mdss_mdp_ctl.c b/drivers/video/msm/mdss/mdss_mdp_ctl.c
index d21a095..73b8c60 100644
--- a/drivers/video/msm/mdss/mdss_mdp_ctl.c
+++ b/drivers/video/msm/mdss/mdss_mdp_ctl.c
@@ -396,7 +396,6 @@
ctl->width = width;
ctl->height = height;
- ctl->dst_format = mfd->panel_info.out_format;
if (!ctl->mixer_left) {
ctl->mixer_left =
@@ -467,6 +466,20 @@
ctl->opmode |= (ctl->intf_num << 4);
+ if (ctl->intf_num == MDSS_MDP_NO_INTF) {
+ ctl->dst_format = mfd->panel_info.out_format;
+ } else {
+ switch (mfd->panel_info.bpp) {
+ case 18:
+ ctl->dst_format = MDSS_MDP_PANEL_FORMAT_RGB666;
+ break;
+ case 24:
+ default:
+ ctl->dst_format = MDSS_MDP_PANEL_FORMAT_RGB888;
+ break;
+ }
+ }
+
if (ctl->mixer_right) {
ctl->opmode |= MDSS_MDP_CTL_OP_PACK_3D_ENABLE |
MDSS_MDP_CTL_OP_PACK_3D_H_ROW_INT;
@@ -560,6 +573,12 @@
temp |= (ctl->intf_type << ((ctl->intf_num - MDSS_MDP_INTF0) * 8));
MDSS_MDP_REG_WRITE(MDSS_MDP_REG_DISP_INTF_SEL, temp);
+ if (ctl->intf_num != MDSS_MDP_NO_INTF) {
+ off = MDSS_MDP_REG_INTF_OFFSET(ctl->intf_num);
+ MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_INTF_PANEL_FORMAT,
+ ctl->dst_format);
+ }
+
outsize = (mixer->height << 16) | mixer->width;
off = MDSS_MDP_REG_LM_OFFSET(mixer->num);
MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_LM_OUT_SIZE, outsize);
diff --git a/drivers/video/msm/mdss/mdss_mdp_hwio.h b/drivers/video/msm/mdss/mdss_mdp_hwio.h
index 651d595..1da30b8 100644
--- a/drivers/video/msm/mdss/mdss_mdp_hwio.h
+++ b/drivers/video/msm/mdss/mdss_mdp_hwio.h
@@ -395,6 +395,7 @@
#define MDSS_MDP_REG_INTF_FRAME_COUNT 0x0AC
#define MDSS_MDP_REG_INTF_LINE_COUNT 0x0B0
#define MDSS_MDP_PANEL_FORMAT_RGB888 0x213F
+#define MDSS_MDP_PANEL_FORMAT_RGB666 0x212A
enum mdss_mdp_pingpong_index {
MDSS_MDP_PINGPONG0,
diff --git a/drivers/video/msm/mdss/mdss_mdp_intf_video.c b/drivers/video/msm/mdss/mdss_mdp_intf_video.c
index 3c7c362..4d3fbf0 100644
--- a/drivers/video/msm/mdss/mdss_mdp_intf_video.c
+++ b/drivers/video/msm/mdss/mdss_mdp_intf_video.c
@@ -153,8 +153,6 @@
p->hsync_skew);
MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_INTF_POLARITY_CTL,
polarity_ctl);
- MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_INTF_PANEL_FORMAT,
- MDSS_MDP_PANEL_FORMAT_RGB888);
return 0;
}
diff --git a/drivers/video/msm/mdss/mdss_wb.c b/drivers/video/msm/mdss/mdss_wb.c
index a26d339..d4c924f 100644
--- a/drivers/video/msm/mdss/mdss_wb.c
+++ b/drivers/video/msm/mdss/mdss_wb.c
@@ -73,7 +73,7 @@
pdata->panel_info.type = WRITEBACK_PANEL;
pdata->panel_info.clk_rate = 74250000;
pdata->panel_info.pdest = DISPLAY_3;
- pdata->panel_info.out_format = MDP_Y_CBCR_H2V2;
+ pdata->panel_info.out_format = MDP_Y_CBCR_H2V2_VENUS;
pdata->on = mdss_wb_on;
pdata->off = mdss_wb_off;
diff --git a/drivers/video/msm/mdss/msm_mdss_io_8974.c b/drivers/video/msm/mdss/msm_mdss_io_8974.c
index 1232ec6..f594b17 100644
--- a/drivers/video/msm/mdss/msm_mdss_io_8974.c
+++ b/drivers/video/msm/mdss/msm_mdss_io_8974.c
@@ -20,6 +20,7 @@
#include <mach/msm_iomap.h>
#include "mdss_dsi.h"
+#include "mdss_edp.h"
#define SW_RESET BIT(2)
#define SW_RESET_PLL BIT(0)
@@ -341,3 +342,333 @@
}
}
+
+/* EDP phy configuration settings */
+void mdss_edp_phy_sw_reset(unsigned char *edp_base)
+{
+ /* phy sw reset */
+ edp_write(edp_base + 0x74, 0x100); /* EDP_PHY_CTRL */
+ wmb();
+ usleep(1);
+ edp_write(edp_base + 0x74, 0x000); /* EDP_PHY_CTRL */
+ wmb();
+ usleep(1);
+
+ /* phy PLL sw reset */
+ edp_write(edp_base + 0x74, 0x001); /* EDP_PHY_CTRL */
+ wmb();
+ usleep(1);
+ edp_write(edp_base + 0x74, 0x000); /* EDP_PHY_CTRL */
+ wmb();
+ usleep(1);
+}
+
+void mdss_edp_hw_powerup(unsigned char *edp_base, int enable)
+{
+ int ret = 0;
+
+ if (enable) {
+ /* EDP_PHY_EDPPHY_GLB_PD_CTL */
+ edp_write(edp_base + 0x52c, 0x3f);
+ /* EDP_PHY_EDPPHY_GLB_CFG */
+ edp_write(edp_base + 0x528, 0x1);
+ /* EDP_PHY_PLL_UNIPHY_PLL_GLB_CFG */
+ edp_write(edp_base + 0x620, 0xf);
+ /* EDP_AUX_CTRL */
+ ret = edp_read(edp_base + 0x300);
+ edp_write(edp_base + 0x300, ret | 0x1);
+ } else {
+ /* EDP_PHY_EDPPHY_GLB_PD_CTL */
+ edp_write(edp_base + 0x52c, 0xc0);
+ }
+}
+
+void mdss_edp_pll_configure(unsigned char *edp_base, int rate)
+{
+ if (rate == 810000000) {
+ edp_write(edp_base + 0x60c, 0x18);
+ edp_write(edp_base + 0x664, 0x5);
+ edp_write(edp_base + 0x600, 0x0);
+ edp_write(edp_base + 0x638, 0x36);
+ edp_write(edp_base + 0x63c, 0x69);
+ edp_write(edp_base + 0x640, 0xff);
+ edp_write(edp_base + 0x644, 0x2f);
+ edp_write(edp_base + 0x648, 0x0);
+ edp_write(edp_base + 0x66c, 0x0a);
+ edp_write(edp_base + 0x674, 0x01);
+ edp_write(edp_base + 0x684, 0x5a);
+ edp_write(edp_base + 0x688, 0x0);
+ edp_write(edp_base + 0x68c, 0x60);
+ edp_write(edp_base + 0x690, 0x0);
+ edp_write(edp_base + 0x694, 0x2a);
+ edp_write(edp_base + 0x698, 0x3);
+ edp_write(edp_base + 0x65c, 0x10);
+ edp_write(edp_base + 0x660, 0x1a);
+ edp_write(edp_base + 0x604, 0x0);
+ edp_write(edp_base + 0x624, 0x0);
+ edp_write(edp_base + 0x628, 0x0);
+
+ edp_write(edp_base + 0x620, 0x1);
+ edp_write(edp_base + 0x620, 0x5);
+ edp_write(edp_base + 0x620, 0x7);
+ edp_write(edp_base + 0x620, 0xf);
+
+ } else if (rate == 138500000) {
+ edp_write(edp_base + 0x664, 0x5); /* UNIPHY_PLL_LKDET_CFG2 */
+ edp_write(edp_base + 0x600, 0x1); /* UNIPHY_PLL_REFCLK_CFG */
+ edp_write(edp_base + 0x638, 0x36); /* UNIPHY_PLL_SDM_CFG0 */
+ edp_write(edp_base + 0x63c, 0x62); /* UNIPHY_PLL_SDM_CFG1 */
+ edp_write(edp_base + 0x640, 0x0); /* UNIPHY_PLL_SDM_CFG2 */
+ edp_write(edp_base + 0x644, 0x28); /* UNIPHY_PLL_SDM_CFG3 */
+ edp_write(edp_base + 0x648, 0x0); /* UNIPHY_PLL_SDM_CFG4 */
+ edp_write(edp_base + 0x64c, 0x80); /* UNIPHY_PLL_SSC_CFG0 */
+ edp_write(edp_base + 0x650, 0x0); /* UNIPHY_PLL_SSC_CFG1 */
+ edp_write(edp_base + 0x654, 0x0); /* UNIPHY_PLL_SSC_CFG2 */
+ edp_write(edp_base + 0x658, 0x0); /* UNIPHY_PLL_SSC_CFG3 */
+ edp_write(edp_base + 0x66c, 0xa); /* UNIPHY_PLL_CAL_CFG0 */
+ edp_write(edp_base + 0x674, 0x1); /* UNIPHY_PLL_CAL_CFG2 */
+ edp_write(edp_base + 0x684, 0x5a); /* UNIPHY_PLL_CAL_CFG6 */
+ edp_write(edp_base + 0x688, 0x0); /* UNIPHY_PLL_CAL_CFG7 */
+ edp_write(edp_base + 0x68c, 0x60); /* UNIPHY_PLL_CAL_CFG8 */
+ edp_write(edp_base + 0x690, 0x0); /* UNIPHY_PLL_CAL_CFG9 */
+ edp_write(edp_base + 0x694, 0x46); /* UNIPHY_PLL_CAL_CFG10 */
+ edp_write(edp_base + 0x698, 0x5); /* UNIPHY_PLL_CAL_CFG11 */
+ edp_write(edp_base + 0x65c, 0x10); /* UNIPHY_PLL_LKDET_CFG0 */
+ edp_write(edp_base + 0x660, 0x1a); /* UNIPHY_PLL_LKDET_CFG1 */
+ edp_write(edp_base + 0x604, 0x0); /* UNIPHY_PLL_POSTDIV1_CFG */
+ edp_write(edp_base + 0x624, 0x0); /* UNIPHY_PLL_POSTDIV2_CFG */
+ edp_write(edp_base + 0x628, 0x0); /* UNIPHY_PLL_POSTDIV3_CFG */
+
+ edp_write(edp_base + 0x620, 0x1); /* UNIPHY_PLL_GLB_CFG */
+ edp_write(edp_base + 0x620, 0x5); /* UNIPHY_PLL_GLB_CFG */
+ edp_write(edp_base + 0x620, 0x7); /* UNIPHY_PLL_GLB_CFG */
+ edp_write(edp_base + 0x620, 0xf); /* UNIPHY_PLL_GLB_CFG */
+ } else {
+ pr_err("%s: Unknown configuration rate\n", __func__);
+ }
+}
+
+void mdss_edp_enable_aux(unsigned char *edp_base, int enable)
+{
+ if (!enable) {
+ edp_write(edp_base + 0x300, 0); /* EDP_AUX_CTRL */
+ return;
+ }
+
+ /*reset AUX */
+ edp_write(edp_base + 0x300, BIT(1)); /* EDP_AUX_CTRL */
+ edp_write(edp_base + 0x300, 0); /* EDP_AUX_CTRL */
+
+ /* Enable AUX */
+ edp_write(edp_base + 0x300, BIT(0)); /* EDP_AUX_CTRL */
+
+ edp_write(edp_base + 0x550, 0x2c); /* AUX_CFG0 */
+ edp_write(edp_base + 0x308, 0xffffffff); /* INTR_STATUS */
+ edp_write(edp_base + 0x568, 0xff); /* INTR_MASK */
+}
+
+void mdss_edp_enable_mainlink(unsigned char *edp_base, int enable)
+{
+ u32 data;
+
+ data = edp_read(edp_base + 0x004);
+ data &= ~BIT(0);
+
+ if (enable) {
+ data |= 0x1;
+ edp_write(edp_base + 0x004, data);
+ edp_write(edp_base + 0x004, 0x1);
+ } else {
+ data |= 0x0;
+ edp_write(edp_base + 0x004, data);
+ }
+}
+
+void mdss_edp_enable_lane_bist(unsigned char *edp_base, int lane, int enable)
+{
+ unsigned char *addr_ln_bist_cfg, *addr_ln_pd_ctrl;
+
+ /* EDP_PHY_EDPPHY_LNn_PD_CTL */
+ addr_ln_pd_ctrl = edp_base + 0x404 + (0x40 * lane);
+ /* EDP_PHY_EDPPHY_LNn_BIST_CFG0 */
+ addr_ln_bist_cfg = edp_base + 0x408 + (0x40 * lane);
+
+ if (enable) {
+ edp_write(addr_ln_pd_ctrl, 0x0);
+ edp_write(addr_ln_bist_cfg, 0x10);
+
+ } else {
+ edp_write(addr_ln_pd_ctrl, 0xf);
+ edp_write(addr_ln_bist_cfg, 0x10);
+ }
+}
+
+void mdss_edp_clk_deinit(struct mdss_edp_drv_pdata *edp_drv)
+{
+ if (edp_drv->aux_clk)
+ clk_put(edp_drv->aux_clk);
+ if (edp_drv->pixel_clk)
+ clk_put(edp_drv->pixel_clk);
+ if (edp_drv->ahb_clk)
+ clk_put(edp_drv->ahb_clk);
+ if (edp_drv->link_clk)
+ clk_put(edp_drv->link_clk);
+}
+
+int mdss_edp_clk_init(struct mdss_edp_drv_pdata *edp_drv)
+{
+ struct device *dev = &(edp_drv->pdev->dev);
+
+ edp_drv->aux_clk = clk_get(dev, "core_clk");
+ if (IS_ERR(edp_drv->aux_clk)) {
+ pr_err("%s: Can't find aux_clk", __func__);
+ edp_drv->aux_clk = NULL;
+ goto mdss_edp_clk_err;
+ }
+
+ edp_drv->pixel_clk = clk_get(dev, "pixel_clk");
+ if (IS_ERR(edp_drv->pixel_clk)) {
+ pr_err("%s: Can't find pixel_clk", __func__);
+ edp_drv->pixel_clk = NULL;
+ goto mdss_edp_clk_err;
+ }
+
+ edp_drv->ahb_clk = clk_get(dev, "iface_clk");
+ if (IS_ERR(edp_drv->ahb_clk)) {
+ pr_err("%s: Can't find ahb_clk", __func__);
+ edp_drv->ahb_clk = NULL;
+ goto mdss_edp_clk_err;
+ }
+
+ edp_drv->link_clk = clk_get(dev, "link_clk");
+ if (IS_ERR(edp_drv->link_clk)) {
+ pr_err("%s: Can't find link_clk", __func__);
+ edp_drv->link_clk = NULL;
+ goto mdss_edp_clk_err;
+ }
+
+ return 0;
+
+mdss_edp_clk_err:
+ mdss_edp_clk_deinit(edp_drv);
+ return -EPERM;
+}
+
+
+void mdss_edp_clk_enable(struct mdss_edp_drv_pdata *edp_drv)
+{
+ if (edp_drv->clk_on) {
+ pr_info("%s: edp clks are already ON\n", __func__);
+ return;
+ }
+
+ if (clk_set_rate(edp_drv->aux_clk, 19200000) < 0)
+ pr_err("%s: aux_clk - clk_set_rate failed\n",
+ __func__);
+
+ if (clk_set_rate(edp_drv->pixel_clk, 138500000) < 0)
+ pr_err("%s: pixel_clk - clk_set_rate failed\n",
+ __func__);
+
+ if (clk_set_rate(edp_drv->link_clk, 270000000) < 0)
+ pr_err("%s: link_clk - clk_set_rate failed\n",
+ __func__);
+
+ clk_enable(edp_drv->aux_clk);
+ clk_enable(edp_drv->pixel_clk);
+ clk_enable(edp_drv->ahb_clk);
+ clk_enable(edp_drv->link_clk);
+
+ edp_drv->clk_on = 1;
+}
+
+void mdss_edp_clk_disable(struct mdss_edp_drv_pdata *edp_drv)
+{
+ if (edp_drv->clk_on == 0) {
+ pr_info("%s: edp clks are already OFF\n", __func__);
+ return;
+ }
+
+ clk_disable(edp_drv->aux_clk);
+ clk_disable(edp_drv->pixel_clk);
+ clk_disable(edp_drv->ahb_clk);
+ clk_disable(edp_drv->link_clk);
+
+ edp_drv->clk_on = 0;
+}
+
+void mdss_edp_prepare_clocks(struct mdss_edp_drv_pdata *edp_drv)
+{
+ clk_prepare(edp_drv->aux_clk);
+ clk_prepare(edp_drv->pixel_clk);
+ clk_prepare(edp_drv->ahb_clk);
+ clk_prepare(edp_drv->link_clk);
+}
+
+void mdss_edp_unprepare_clocks(struct mdss_edp_drv_pdata *edp_drv)
+{
+ clk_unprepare(edp_drv->aux_clk);
+ clk_unprepare(edp_drv->pixel_clk);
+ clk_unprepare(edp_drv->ahb_clk);
+ clk_unprepare(edp_drv->link_clk);
+}
+
+void mdss_edp_enable_pixel_clk(unsigned char *edp_base,
+ unsigned char *mmss_cc_base, int enable)
+{
+ if (!enable) {
+ edp_write(mmss_cc_base + 0x032c, 0); /* CBCR */
+ return;
+ }
+
+ edp_write(edp_base + 0x624, 0x1); /* PostDiv2 */
+
+ /* Configuring MND for Pixel */
+ edp_write(mmss_cc_base + 0x00a8, 0x3f); /* M value */
+ edp_write(mmss_cc_base + 0x00ac, 0xb); /* N value */
+ edp_write(mmss_cc_base + 0x00b0, 0x0); /* D value */
+
+ /* CFG RCGR */
+ edp_write(mmss_cc_base + 0x00a4, (5 << 8) | (2 << 12));
+ edp_write(mmss_cc_base + 0x00a0, 3); /* CMD RCGR */
+
+ edp_write(mmss_cc_base + 0x032c, 1); /* CBCR */
+}
+
+void mdss_edp_enable_link_clk(unsigned char *mmss_cc_base, int enable)
+{
+ if (!enable) {
+ edp_write(mmss_cc_base + 0x0330, 0); /* CBCR */
+ return;
+ }
+
+ edp_write(mmss_cc_base + 0x00c4, (4 << 8)); /* CFG RCGR */
+ edp_write(mmss_cc_base + 0x00c0, 3); /* CMD RCGR */
+
+ edp_write(mmss_cc_base + 0x0330, 1); /* CBCR */
+}
+
+void mdss_edp_config_clk(unsigned char *edp_base, unsigned char *mmss_cc_base)
+{
+ mdss_edp_enable_link_clk(mmss_cc_base, 1);
+ mdss_edp_enable_pixel_clk(edp_base, mmss_cc_base, 1);
+}
+
+void mdss_edp_unconfig_clk(unsigned char *edp_base,
+ unsigned char *mmss_cc_base)
+{
+ mdss_edp_enable_link_clk(mmss_cc_base, 0);
+ mdss_edp_enable_pixel_clk(edp_base, mmss_cc_base, 0);
+}
+
+void mdss_edp_phy_misc_cfg(unsigned char *edp_base)
+{
+ /* EDP_PHY_EDPPHY_GLB_VM_CFG0 */
+ edp_write(edp_base + 0x510, 0x3);
+ /* EDP_PHY_EDPPHY_GLB_VM_CFG1 */
+ edp_write(edp_base + 0x514, 0x64);
+ /* EDP_PHY_EDPPHY_GLB_MISC9 */
+ edp_write(edp_base + 0x518, 0x6c);
+ /* EDP_MISC1_MISC0 */
+ edp_write(edp_base + 0x2c, 0x1);
+}
diff --git a/drivers/video/msm/msm_fb.h b/drivers/video/msm/msm_fb.h
index 7dc89ef..1594825 100644
--- a/drivers/video/msm/msm_fb.h
+++ b/drivers/video/msm/msm_fb.h
@@ -189,6 +189,7 @@
void *copy_splash_buf;
unsigned char *copy_splash_phys;
void *cpu_pm_hdl;
+ u32 avtimer_phy;
};
struct dentry *msm_fb_get_debugfs_root(void);
diff --git a/drivers/video/msm/vidc/1080p/resource_tracker/vcd_res_tracker.c b/drivers/video/msm/vidc/1080p/resource_tracker/vcd_res_tracker.c
index 3670dc81..0bc2228 100644
--- a/drivers/video/msm/vidc/1080p/resource_tracker/vcd_res_tracker.c
+++ b/drivers/video/msm/vidc/1080p/resource_tracker/vcd_res_tracker.c
@@ -537,7 +537,6 @@
u32 enc_perf_level = 0, dec_perf_level = 0;
u32 bus_clk_index, client_type = 0;
int rc = 0;
- bool turbo_enabled = false;
bool turbo_supported =
!resource_context.vidc_platform_data->disable_turbo;
@@ -547,9 +546,6 @@
dec_perf_level += cctxt_itr->reqd_perf_lvl;
else
enc_perf_level += cctxt_itr->reqd_perf_lvl;
-
- if (cctxt_itr->is_turbo_enabled)
- turbo_enabled = true;
cctxt_itr = cctxt_itr->next;
}
@@ -566,18 +562,8 @@
if (dev_ctxt->reqd_perf_lvl + dev_ctxt->curr_perf_lvl == 0)
bus_clk_index = 2;
- else if ((!turbo_supported || !turbo_enabled) && bus_clk_index == 3) {
- if (!turbo_supported)
- VCDRES_MSG_MED("Warning: Turbo mode not supported "\
- " falling back to 1080p bus\n");
+ else if (!turbo_supported && bus_clk_index == 3)
bus_clk_index = 2;
- }
-
- if (bus_clk_index == 3)
- dev_ctxt->turbo_mode_set = true;
- else
- dev_ctxt->turbo_mode_set = false;
-
bus_clk_index = (bus_clk_index << 1) + (client_type + 1);
VCDRES_MSG_LOW("%s(), bus_clk_index = %d", __func__, bus_clk_index);
VCDRES_MSG_LOW("%s(),context.pcl = %x", __func__, resource_context.pcl);
@@ -633,11 +619,8 @@
*pn_set_perf_lvl = RESTRK_1080P_TURBO_PERF_LEVEL;
}
- if ((!turbo_supported || !dev_ctxt->turbo_mode_set) &&
+ if (!turbo_supported &&
*pn_set_perf_lvl == RESTRK_1080P_TURBO_PERF_LEVEL) {
- if (!turbo_supported)
- VCDRES_MSG_ERROR("Warning: Turbo mode not supported "\
- " falling back to 1080p clocks\n");
vidc_freq = vidc_clk_table[2];
*pn_set_perf_lvl = RESTRK_1080P_MAX_PERF_LEVEL;
}
diff --git a/drivers/video/msm/vidc/common/vcd/vcd_core.h b/drivers/video/msm/vidc/common/vcd/vcd_core.h
index ae97561..aba8119 100644
--- a/drivers/video/msm/vidc/common/vcd/vcd_core.h
+++ b/drivers/video/msm/vidc/common/vcd/vcd_core.h
@@ -147,7 +147,6 @@
u32 reqd_perf_lvl;
u32 curr_perf_lvl;
u32 set_perf_lvl_pending;
- bool turbo_mode_set;
};
struct vcd_clnt_status {
diff --git a/drivers/video/msm/vidc/common/vcd/vcd_device_sm.c b/drivers/video/msm/vidc/common/vcd/vcd_device_sm.c
index 0d13028..f670a4a 100644
--- a/drivers/video/msm/vidc/common/vcd/vcd_device_sm.c
+++ b/drivers/video/msm/vidc/common/vcd/vcd_device_sm.c
@@ -219,8 +219,6 @@
VCD_DEVICE_STATE_INITING,
ev_code);
}
- dev_ctxt->turbo_mode_set = 0;
-
return rc;
}
diff --git a/include/linux/diagchar.h b/include/linux/diagchar.h
index 288ed43..f0e42c2 100644
--- a/include/linux/diagchar.h
+++ b/include/linux/diagchar.h
@@ -41,6 +41,7 @@
#define DIAG_IOCTL_DCI_DEINIT 21
#define DIAG_IOCTL_DCI_SUPPORT 22
#define DIAG_IOCTL_DCI_REG 23
+#define DIAG_IOCTL_DCI_STREAM_INIT 24
/* PC Tools IDs */
#define APQ8060_TOOLS_ID 4062
@@ -706,5 +707,6 @@
#define LOG_15 0x0
#define LOG_GET_ITEM_NUM(xx_code) (xx_code & 0x0FFF)
+#define LOG_GET_EQUIP_ID(xx_code) ((xx_code & 0xF000) >> 12)
#endif
diff --git a/include/linux/dvb/video.h b/include/linux/dvb/video.h
index 81475c2..2a2a53d 100644
--- a/include/linux/dvb/video.h
+++ b/include/linux/dvb/video.h
@@ -113,6 +113,7 @@
#define VIDEO_CMD_FREE_H264_MV_BUFFER (17)
#define VIDEO_CMD_CLEAR_INPUT_BUFFER (18)
#define VIDEO_CMD_CLEAR_OUTPUT_BUFFER (19)
+#define VIDEO_CMD_SET_BUFFER_COUNT (20)
/* Flags for VIDEO_CMD_FREEZE */
#define VIDEO_CMD_FREEZE_TO_BLACK (1 << 0)
diff --git a/include/linux/mfd/pm8xxx/pm8921-charger.h b/include/linux/mfd/pm8xxx/pm8921-charger.h
index 0e86f2a..16d4a4b 100644
--- a/include/linux/mfd/pm8xxx/pm8921-charger.h
+++ b/include/linux/mfd/pm8xxx/pm8921-charger.h
@@ -72,6 +72,9 @@
* @uvd_thresh_voltage: the USB falling UVD threshold (mV) (PM8917 only)
* @resume_voltage_delta: the (mV) drop to wait for before resume charging
* after the battery has been fully charged
+ * @resume_charge_percent: the % SOC the charger will drop to after the
+ * battery is fully charged before resuming
+ * charging.
* @term_current: the charger current (mA) at which EOC happens
* @cool_temp: the temperature (degC) at which the battery is
* considered cool charging current and voltage is reduced.
@@ -133,6 +136,7 @@
unsigned int alarm_low_mv;
unsigned int alarm_high_mv;
unsigned int resume_voltage_delta;
+ int resume_charge_percent;
unsigned int term_current;
int cool_temp;
int warm_temp;
diff --git a/include/linux/mfd/wcd9xxx/core.h b/include/linux/mfd/wcd9xxx/core.h
index cf57a64..2dea611 100644
--- a/include/linux/mfd/wcd9xxx/core.h
+++ b/include/linux/mfd/wcd9xxx/core.h
@@ -19,7 +19,7 @@
#include <linux/platform_device.h>
#include <linux/of_irq.h>
-#define WCD9XXX_NUM_IRQ_REGS 3
+#define WCD9XXX_NUM_IRQ_REGS 4
#define WCD9XXX_SLIM_NUM_PORT_REG 3
@@ -47,6 +47,7 @@
enum {
+ /* INTR_REG 0 */
WCD9XXX_IRQ_SLIMBUS = 0,
WCD9XXX_IRQ_MBHC_REMOVAL,
WCD9XXX_IRQ_MBHC_SHORT_TERM,
@@ -55,6 +56,7 @@
WCD9XXX_IRQ_MBHC_POTENTIAL,
WCD9XXX_IRQ_MBHC_INSERTION,
WCD9XXX_IRQ_BG_PRECHARGE,
+ /* INTR_REG 1 */
WCD9XXX_IRQ_PA1_STARTUP,
WCD9XXX_IRQ_PA2_STARTUP,
WCD9XXX_IRQ_PA3_STARTUP,
@@ -63,12 +65,21 @@
WCD9XXX_IRQ_MICBIAS1_PRECHARGE,
WCD9XXX_IRQ_MICBIAS2_PRECHARGE,
WCD9XXX_IRQ_MICBIAS3_PRECHARGE,
+ /* INTR_REG 2 */
WCD9XXX_IRQ_HPH_PA_OCPL_FAULT,
WCD9XXX_IRQ_HPH_PA_OCPR_FAULT,
WCD9XXX_IRQ_EAR_PA_OCPL_FAULT,
WCD9XXX_IRQ_HPH_L_PA_STARTUP,
WCD9XXX_IRQ_HPH_R_PA_STARTUP,
WCD9XXX_IRQ_EAR_PA_STARTUP,
+ WCD9XXX_IRQ_RESERVED_0,
+ WCD9XXX_IRQ_RESERVED_1,
+ /* INTR_REG 3 */
+ WCD9XXX_IRQ_MAD_AUDIO,
+ WCD9XXX_IRQ_MAD_BEACON,
+ WCD9XXX_IRQ_MAD_ULTRASOUND,
+ WCD9XXX_IRQ_SPEAKER_CLIPPING,
+ WCD9XXX_IRQ_MBHC_JACK_SWITCH,
WCD9XXX_NUM_IRQS,
};
@@ -196,8 +207,16 @@
void wcd9xxx_enable_irq(struct wcd9xxx *wcd9xxx, int irq);
void wcd9xxx_disable_irq(struct wcd9xxx *wcd9xxx, int irq);
void wcd9xxx_disable_irq_sync(struct wcd9xxx *wcd9xxx, int irq);
-#ifdef CONFIG_OF
+#if defined(CONFIG_WCD9310_CODEC) || \
+ defined(CONFIG_WCD9304_CODEC) || \
+ defined(CONFIG_WCD9320_CODEC)
int __init wcd9xxx_irq_of_init(struct device_node *node,
struct device_node *parent);
-#endif /* CONFIG_OF */
+#else
+static inline int __init wcd9xxx_irq_of_init(struct device_node *node,
+ struct device_node *parent)
+{
+ return 0;
+}
+#endif
#endif
diff --git a/include/linux/mfd/wcd9xxx/pdata.h b/include/linux/mfd/wcd9xxx/pdata.h
index e831f0b..a7ca417 100644
--- a/include/linux/mfd/wcd9xxx/pdata.h
+++ b/include/linux/mfd/wcd9xxx/pdata.h
@@ -28,6 +28,12 @@
#define SITAR_CFILT2_SEL 0x1
#define SITAR_CFILT3_SEL 0x2
+#define WCD9XXX_LDOH_1P95_V 0x0
+#define WCD9XXX_LDOH_2P35_V 0x1
+#define WCD9XXX_LDOH_2P75_V 0x2
+#define WCD9XXX_LDOH_2P85_V 0x3
+#define WCD9XXX_LDOH_3P0_V 0x3
+
#define TABLA_LDOH_1P95_V 0x0
#define TABLA_LDOH_2P35_V 0x1
#define TABLA_LDOH_2P75_V 0x2
@@ -37,16 +43,6 @@
#define TABLA_CFILT2_SEL 0x1
#define TABLA_CFILT3_SEL 0x2
-#define TAIKO_CFILT1_SEL 0x0
-#define TAIKO_CFILT2_SEL 0x1
-#define TAIKO_CFILT3_SEL 0x2
-
-#define TAIKO_LDOH_1P95_V 0x0
-#define TAIKO_LDOH_2P35_V 0x1
-#define TAIKO_LDOH_2P75_V 0x2
-#define TAIKO_LDOH_2P85_V 0x3
-
-
#define MAX_AMIC_CHANNEL 7
#define TABLA_OCP_300_MA 0x0
diff --git a/include/linux/mfd/wcd9xxx/wcd9xxx_registers.h b/include/linux/mfd/wcd9xxx/wcd9xxx_registers.h
index 357f400..4b7a32c 100644
--- a/include/linux/mfd/wcd9xxx/wcd9xxx_registers.h
+++ b/include/linux/mfd/wcd9xxx/wcd9xxx_registers.h
@@ -16,29 +16,29 @@
#define WCD9XXX_A_CHIP_CTL (0x00)
#define WCD9XXX_A_CHIP_CTL__POR (0x00000000)
#define WCD9XXX_A_CHIP_STATUS (0x01)
-#define WCD9XXX_A_CHIP_STATUS__POR (0x00000000)
-#define WCD9XXX_A_CHIP_ID_BYTE_0 (0x04)
-#define WCD9XXX_A_CHIP_ID_BYTE_0__POR (0x00000000)
-#define WCD9XXX_A_CHIP_ID_BYTE_1 (0x05)
-#define WCD9XXX_A_CHIP_ID_BYTE_1__POR (0x00000000)
-#define WCD9XXX_A_CHIP_ID_BYTE_2 (0x06)
-#define WCD9XXX_A_CHIP_ID_BYTE_2__POR (0x00000000)
-#define WCD9XXX_A_CHIP_ID_BYTE_3 (0x07)
-#define WCD9XXX_A_CHIP_ID_BYTE_3__POR (0x00000001)
+#define WCD9XXX_A_CHIP_STATUS__POR (0x00000000)
+#define WCD9XXX_A_CHIP_ID_BYTE_0 (0x04)
+#define WCD9XXX_A_CHIP_ID_BYTE_0__POR (0x00000000)
+#define WCD9XXX_A_CHIP_ID_BYTE_1 (0x05)
+#define WCD9XXX_A_CHIP_ID_BYTE_1__POR (0x00000000)
+#define WCD9XXX_A_CHIP_ID_BYTE_2 (0x06)
+#define WCD9XXX_A_CHIP_ID_BYTE_2__POR (0x00000000)
+#define WCD9XXX_A_CHIP_ID_BYTE_3 (0x07)
+#define WCD9XXX_A_CHIP_ID_BYTE_3__POR (0x00000001)
#define WCD9XXX_A_CHIP_VERSION (0x08)
-#define WCD9XXX_A_CHIP_VERSION__POR (0x00000020)
+#define WCD9XXX_A_CHIP_VERSION__POR (0x00000020)
#define WCD9XXX_A_SB_VERSION (0x09)
-#define WCD9XXX_A_SB_VERSION__POR (0x00000010)
+#define WCD9XXX_A_SB_VERSION__POR (0x00000010)
#define WCD9XXX_A_SLAVE_ID_1 (0x0C)
-#define WCD9XXX_A_SLAVE_ID_1__POR (0x00000077)
+#define WCD9XXX_A_SLAVE_ID_1__POR (0x00000077)
#define WCD9XXX_A_SLAVE_ID_2 (0x0D)
-#define WCD9XXX_A_SLAVE_ID_2__POR (0x00000066)
+#define WCD9XXX_A_SLAVE_ID_2__POR (0x00000066)
#define WCD9XXX_A_SLAVE_ID_3 (0x0E)
-#define WCD9XXX_A_SLAVE_ID_3__POR (0x00000055)
+#define WCD9XXX_A_SLAVE_ID_3__POR (0x00000055)
#define WCD9XXX_A_CDC_CTL (0x80)
#define WCD9XXX_A_CDC_CTL__POR (0x00000000)
#define WCD9XXX_A_LEAKAGE_CTL (0x88)
-#define WCD9XXX_A_LEAKAGE_CTL__POR (0x00000004)
+#define WCD9XXX_A_LEAKAGE_CTL__POR (0x00000004)
#define WCD9XXX_A_INTR_MODE (0x90)
#define WCD9XXX_A_INTR_MASK0 (0x94)
#define WCD9XXX_A_INTR_STATUS0 (0x98)
@@ -46,5 +46,161 @@
#define WCD9XXX_A_INTR_LEVEL0 (0xA0)
#define WCD9XXX_A_INTR_LEVEL1 (0xA1)
#define WCD9XXX_A_INTR_LEVEL2 (0xA2)
+#define WCD9XXX_A_RX_HPH_CNP_EN (0x1AB)
+#define WCD9XXX_A_RX_HPH_CNP_EN__POR (0x80)
+#define WCD9XXX_A_RX_HPH_CNP_EN (0x1AB)
+#define WCD9XXX_A_RX_HPH_CNP_EN__POR (0x80)
+#define WCD9XXX_A_BIAS_CENTRAL_BG_CTL (0x101)
+#define WCD9XXX_A_BIAS_CENTRAL_BG_CTL__POR (0x50)
+#define WCD9XXX_A_CLK_BUFF_EN1 (0x108)
+#define WCD9XXX_A_CLK_BUFF_EN1__POR (0x04)
+#define WCD9XXX_A_CLK_BUFF_EN2 (0x109)
+#define WCD9XXX_A_CLK_BUFF_EN2__POR (0x02)
+#define WCD9XXX_A_RX_COM_BIAS (0x1A2)
+#define WCD9XXX_A_RX_COM_BIAS__POR (0x00)
+#define WCD9XXX_A_RC_OSC_FREQ (0x1FA)
+#define WCD9XXX_A_RC_OSC_FREQ__POR (0x46)
+#define WCD9XXX_A_BIAS_OSC_BG_CTL (0x105)
+#define WCD9XXX_A_BIAS_OSC_BG_CTL__POR (0x16)
+#define WCD9XXX_A_RC_OSC_TEST (0x1FB)
+#define WCD9XXX_A_RC_OSC_TEST__POR (0x0A)
+#define WCD9XXX_A_CDC_CLK_MCLK_CTL (0x311)
+#define WCD9XXX_A_CDC_CLK_MCLK_CTL__POR (0x00)
+
+#define WCD9XXX_A_CDC_MBHC_EN_CTL (0x3C0)
+#define WCD9XXX_A_CDC_MBHC_EN_CTL__POR (0x00)
+#define WCD9XXX_A_CDC_MBHC_FIR_B1_CFG (0x3C1)
+#define WCD9XXX_A_CDC_MBHC_FIR_B1_CFG__POR (0x00)
+#define WCD9XXX_A_CDC_MBHC_FIR_B2_CFG (0x3C2)
+#define WCD9XXX_A_CDC_MBHC_FIR_B2_CFG__POR (0x06)
+#define WCD9XXX_A_CDC_MBHC_TIMER_B1_CTL (0x3C3)
+#define WCD9XXX_A_CDC_MBHC_TIMER_B1_CTL__POR (0x03)
+#define WCD9XXX_A_CDC_MBHC_TIMER_B2_CTL (0x3C4)
+#define WCD9XXX_A_CDC_MBHC_TIMER_B2_CTL__POR (0x09)
+#define WCD9XXX_A_CDC_MBHC_TIMER_B3_CTL (0x3C5)
+#define WCD9XXX_A_CDC_MBHC_TIMER_B3_CTL__POR (0x1E)
+#define WCD9XXX_A_CDC_MBHC_TIMER_B4_CTL (0x3C6)
+#define WCD9XXX_A_CDC_MBHC_TIMER_B4_CTL__POR (0x45)
+#define WCD9XXX_A_CDC_MBHC_TIMER_B5_CTL (0x3C7)
+#define WCD9XXX_A_CDC_MBHC_TIMER_B5_CTL__POR (0x04)
+#define WCD9XXX_A_CDC_MBHC_TIMER_B6_CTL (0x3C8)
+#define WCD9XXX_A_CDC_MBHC_TIMER_B6_CTL__POR (0x78)
+#define WCD9XXX_A_CDC_MBHC_B1_STATUS (0x3C9)
+#define WCD9XXX_A_CDC_MBHC_B1_STATUS__POR (0x00)
+#define WCD9XXX_A_CDC_MBHC_B2_STATUS (0x3CA)
+#define WCD9XXX_A_CDC_MBHC_B2_STATUS__POR (0x00)
+#define WCD9XXX_A_CDC_MBHC_B3_STATUS (0x3CB)
+#define WCD9XXX_A_CDC_MBHC_B3_STATUS__POR (0x00)
+#define WCD9XXX_A_CDC_MBHC_B4_STATUS (0x3CC)
+#define WCD9XXX_A_CDC_MBHC_B4_STATUS__POR (0x00)
+#define WCD9XXX_A_CDC_MBHC_B5_STATUS (0x3CD)
+#define WCD9XXX_A_CDC_MBHC_B5_STATUS__POR (0x00)
+#define WCD9XXX_A_CDC_MBHC_B1_CTL (0x3CE)
+#define WCD9XXX_A_CDC_MBHC_B1_CTL__POR (0xC0)
+#define WCD9XXX_A_CDC_MBHC_B2_CTL (0x3CF)
+#define WCD9XXX_A_CDC_MBHC_B2_CTL__POR (0x5D)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B1_CTL (0x3D0)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B1_CTL__POR (0x00)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B2_CTL (0x3D1)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B2_CTL__POR (0x00)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL (0x3D2)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL__POR (0x00)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL (0x3D3)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL__POR (0x00)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B5_CTL (0x3D4)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B5_CTL__POR (0x00)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B6_CTL (0x3D5)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B6_CTL__POR (0x00)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B7_CTL (0x3D6)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B7_CTL__POR (0xFF)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B8_CTL (0x3D7)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B8_CTL__POR (0x07)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B9_CTL (0x3D8)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B9_CTL__POR (0xFF)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B10_CTL (0x3D9)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B10_CTL__POR (0x7F)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B11_CTL (0x3DA)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B11_CTL__POR (0x00)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B12_CTL (0x3DB)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B12_CTL__POR (0x80)
+#define WCD9XXX_A_CDC_MBHC_CLK_CTL (0x3DC)
+#define WCD9XXX_A_CDC_MBHC_CLK_CTL__POR (0x00)
+#define WCD9XXX_A_CDC_MBHC_INT_CTL (0x3DD)
+#define WCD9XXX_A_CDC_MBHC_INT_CTL__POR (0x00)
+#define WCD9XXX_A_CDC_MBHC_DEBUG_CTL (0x3DE)
+#define WCD9XXX_A_CDC_MBHC_DEBUG_CTL__POR (0x00)
+#define WCD9XXX_A_CDC_MBHC_SPARE (0x3DF)
+#define WCD9XXX_A_CDC_MBHC_SPARE__POR (0x00)
+#define WCD9XXX_A_MBHC_SCALING_MUX_1 (0x14E)
+#define WCD9XXX_A_MBHC_SCALING_MUX_1__POR (0x00)
+#define WCD9XXX_A_RX_HPH_OCP_CTL (0x1AA)
+#define WCD9XXX_A_RX_HPH_OCP_CTL__POR (0x68)
+#define WCD9XXX_A_MICB_1_CTL (0x12B)
+#define WCD9XXX_A_MICB_1_CTL__POR (0x16)
+#define WCD9XXX_A_MICB_1_INT_RBIAS (0x12C)
+#define WCD9XXX_A_MICB_1_INT_RBIAS__POR (0x24)
+#define WCD9XXX_A_MICB_1_MBHC (0x12D)
+#define WCD9XXX_A_MICB_1_MBHC__POR (0x01)
+#define WCD9XXX_A_MICB_CFILT_2_CTL (0x12E)
+#define WCD9XXX_A_MICB_CFILT_2_CTL__POR (0x40)
+#define WCD9XXX_A_MICB_CFILT_2_VAL (0x12F)
+#define WCD9XXX_A_MICB_CFILT_2_VAL__POR (0x80)
+#define WCD9XXX_A_MICB_CFILT_2_PRECHRG (0x130)
+#define WCD9XXX_A_MICB_CFILT_2_PRECHRG__POR (0x38)
+#define WCD9XXX_A_MICB_2_CTL (0x131)
+#define WCD9XXX_A_MICB_2_CTL__POR (0x16)
+#define WCD9XXX_A_MICB_2_INT_RBIAS (0x132)
+#define WCD9XXX_A_MICB_2_INT_RBIAS__POR (0x24)
+#define WCD9XXX_A_MICB_2_MBHC (0x133)
+#define WCD9XXX_A_MICB_2_MBHC__POR (0x02)
+#define WCD9XXX_A_MICB_CFILT_3_CTL (0x134)
+#define WCD9XXX_A_MICB_CFILT_3_CTL__POR (0x40)
+#define WCD9XXX_A_MICB_CFILT_3_VAL (0x135)
+#define WCD9XXX_A_MICB_CFILT_3_VAL__POR (0x80)
+#define WCD9XXX_A_MICB_CFILT_3_PRECHRG (0x136)
+#define WCD9XXX_A_MICB_CFILT_3_PRECHRG__POR (0x38)
+#define WCD9XXX_A_MICB_3_CTL (0x137)
+#define WCD9XXX_A_MICB_3_CTL__POR (0x16)
+#define WCD9XXX_A_MICB_3_INT_RBIAS (0x138)
+#define WCD9XXX_A_MICB_3_INT_RBIAS__POR (0x24)
+#define WCD9XXX_A_MICB_3_MBHC (0x139)
+#define WCD9XXX_A_MICB_3_MBHC__POR (0x00)
+#define WCD9XXX_A_MICB_4_CTL (0x13D)
+#define WCD9XXX_A_MICB_4_CTL__POR (0x16)
+#define WCD9XXX_A_MICB_4_INT_RBIAS (0x13E)
+#define WCD9XXX_A_MICB_4_INT_RBIAS__POR (0x24)
+#define WCD9XXX_A_MICB_4_MBHC (0x13F)
+#define WCD9XXX_A_MICB_4_MBHC__POR (0x01)
+#define WCD9XXX_A_MICB_CFILT_1_VAL (0x129)
+#define WCD9XXX_A_MICB_CFILT_1_VAL__POR (0x80)
+#define WCD9XXX_A_MBHC_HPH (0x1FE)
+#define WCD9XXX_A_MBHC_HPH__POR (0x44)
+#define WCD9XXX_A_RX_HPH_CNP_WG_TIME (0x1AD)
+#define WCD9XXX_A_RX_HPH_CNP_WG_TIME__POR (0x2A)
+#define WCD9XXX_A_RX_HPH_R_DAC_CTL (0x1B7)
+#define WCD9XXX_A_RX_HPH_R_DAC_CTL__POR (0x00)
+#define WCD9XXX_A_RX_HPH_L_DAC_CTL (0x1B1)
+#define WCD9XXX_A_RX_HPH_L_DAC_CTL__POR (0x00)
+#define WCD9XXX_A_TX_7_MBHC_EN (0x171)
+#define WCD9XXX_A_TX_7_MBHC_EN__POR (0x0C)
+#define WCD9XXX_A_PIN_CTL_OE0 (0x010)
+#define WCD9XXX_A_PIN_CTL_OE0__POR (0x00)
+#define WCD9XXX_A_PIN_CTL_OE1 (0x011)
+#define WCD9XXX_A_PIN_CTL_OE1__POR (0x00)
+#define WCD9XXX_A_MICB_CFILT_1_CTL (0x128)
+#define WCD9XXX_A_LDO_H_MODE_1 (0x110)
+#define WCD9XXX_A_LDO_H_MODE_1__POR (0x65)
+#define WCD9XXX_A_MICB_CFILT_1_CTL__POR (0x40)
+#define WCD9XXX_A_TX_7_MBHC_TEST_CTL (0x174)
+#define WCD9XXX_A_TX_7_MBHC_TEST_CTL__POR (0x38)
+#define WCD9XXX_A_MBHC_SCALING_MUX_2 (0x14F)
+#define WCD9XXX_A_MBHC_SCALING_MUX_2__POR (0x80)
+#define WCD9XXX_A_TX_COM_BIAS (0x14C)
+#define WCD9XXX_A_TX_COM_BIAS__POR (0xF0)
+
+#define WCD9XXX_A_MBHC_INSERT_DETECT (0x14A) /* TAIKO and later */
+#define WCD9XXX_A_MBHC_INSERT_DETECT__POR (0x00)
+#define WCD9XXX_A_MBHC_INSERT_DET_STATUS (0x14B) /* TAIKO and later */
+#define WCD9XXX_A_MBHC_INSERT_DET_STATUS__POR (0x00)
#endif
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index 477733c..fb854ba 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -60,6 +60,7 @@
unsigned int sa_timeout; /* Units: 100ns */
unsigned int generic_cmd6_time; /* Units: 10ms */
unsigned int power_off_longtime; /* Units: ms */
+ u8 power_off_notification; /* state */
unsigned int hs_max_dtr;
#define MMC_HIGH_26_MAX_DTR 26000000
#define MMC_HIGH_52_MAX_DTR 52000000
@@ -229,6 +230,20 @@
#define MMC_BLK_DATA_AREA_GP (1<<2)
};
+#define BKOPS_NUM_OF_SEVERITY_LEVELS 3
+#define BKOPS_SEVERITY_1_INDEX 0
+#define BKOPS_SEVERITY_2_INDEX 1
+#define BKOPS_SEVERITY_3_INDEX 2
+struct mmc_bkops_stats {
+ spinlock_t lock;
+ bool enabled;
+ unsigned int hpi; /* hpi issued */
+ unsigned int suspend;/* card sleed issued */
+ bool print_stats;
+ unsigned int bkops_level[BKOPS_NUM_OF_SEVERITY_LEVELS];
+ bool ignore_card_bkops_status;
+};
+
/**
* struct mmc_bkops_info - BKOPS data
* @dw: Idle time bkops delayed work
@@ -249,6 +264,7 @@
unsigned int host_suspend_tout_ms;
unsigned int delay_ms;
unsigned int min_sectors_to_queue_delayed_work;
+ struct mmc_bkops_stats bkops_stats; /* BKOPS statistics */
/*
* A default time for checking the need for non urgent BKOPS once mmcqd
* is idle.
@@ -294,7 +310,6 @@
#define MMC_CARD_SDXC (1<<6) /* card is SDXC */
#define MMC_CARD_REMOVED (1<<7) /* card has been removed */
#define MMC_STATE_HIGHSPEED_200 (1<<8) /* card is in HS200 mode */
-#define MMC_STATE_SLEEP (1<<9) /* card is in sleep state */
#define MMC_STATE_DOING_BKOPS (1<<10) /* card is doing BKOPS */
unsigned int quirks; /* card quirks */
#define MMC_QUIRK_LENIENT_FN0 (1<<0) /* allow SDIO FN0 writes outside of the VS CCCR range */
@@ -311,10 +326,6 @@
#define MMC_QUIRK_LONG_READ_TIME (1<<9) /* Data read time > CSD says */
/* byte mode */
#define MMC_QUIRK_INAND_DATA_TIMEOUT (1<<8) /* For incorrect data timeout */
- unsigned int poweroff_notify_state; /* eMMC4.5 notify
- feature */
-#define MMC_NO_POWER_NOTIFICATION 0
-#define MMC_POWERED_ON 1
unsigned int erase_size; /* erase size in sectors */
unsigned int erase_shift; /* if erase unit is power 2 */
@@ -470,7 +481,6 @@
#define mmc_sd_card_uhs(c) ((c)->state & MMC_STATE_ULTRAHIGHSPEED)
#define mmc_card_ext_capacity(c) ((c)->state & MMC_CARD_SDXC)
#define mmc_card_removed(c) ((c) && ((c)->state & MMC_CARD_REMOVED))
-#define mmc_card_is_sleep(c) ((c)->state & MMC_STATE_SLEEP)
#define mmc_card_doing_bkops(c) ((c)->state & MMC_STATE_DOING_BKOPS)
#define mmc_card_set_present(c) ((c)->state |= MMC_STATE_PRESENT)
@@ -483,11 +493,9 @@
#define mmc_sd_card_set_uhs(c) ((c)->state |= MMC_STATE_ULTRAHIGHSPEED)
#define mmc_card_set_ext_capacity(c) ((c)->state |= MMC_CARD_SDXC)
#define mmc_card_set_removed(c) ((c)->state |= MMC_CARD_REMOVED)
-#define mmc_card_set_sleep(c) ((c)->state |= MMC_STATE_SLEEP)
#define mmc_card_set_doing_bkops(c) ((c)->state |= MMC_STATE_DOING_BKOPS)
-
#define mmc_card_clr_doing_bkops(c) ((c)->state &= ~MMC_STATE_DOING_BKOPS)
-#define mmc_card_clr_sleep(c) ((c)->state &= ~MMC_STATE_SLEEP)
+
/*
* Quirk add/remove for MMC products.
*/
diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
index 2795734..7247696 100644
--- a/include/linux/mmc/core.h
+++ b/include/linux/mmc/core.h
@@ -173,7 +173,6 @@
extern int mmc_can_discard(struct mmc_card *card);
extern int mmc_can_sanitize(struct mmc_card *card);
extern int mmc_can_secure_erase_trim(struct mmc_card *card);
-extern int mmc_can_poweroff_notify(const struct mmc_card *card);
extern int mmc_erase_group_aligned(struct mmc_card *card, unsigned int from,
unsigned int nr);
extern unsigned int mmc_calc_max_discard(struct mmc_card *card);
@@ -197,6 +196,8 @@
extern int mmc_detect_card_removed(struct mmc_host *host);
+extern void mmc_blk_init_bkops_statistics(struct mmc_card *card);
+
/**
* mmc_claim_host - exclusively claim a host
* @host: mmc host to claim
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 9e536be..f435221 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -241,6 +241,7 @@
#define MMC_CAP2_BROKEN_VOLTAGE (1 << 7) /* Use the broken voltage */
#define MMC_CAP2_DETECT_ON_ERR (1 << 8) /* On I/O err check card removal */
#define MMC_CAP2_HC_ERASE_SZ (1 << 9) /* High-capacity erase size */
+
#define MMC_CAP2_PACKED_RD (1 << 10) /* Allow packed read */
#define MMC_CAP2_PACKED_WR (1 << 11) /* Allow packed write */
#define MMC_CAP2_PACKED_CMD (MMC_CAP2_PACKED_RD | \
@@ -249,8 +250,6 @@
#define MMC_CAP2_SANITIZE (1 << 13) /* Support Sanitize */
#define MMC_CAP2_INIT_BKOPS (1 << 15) /* Need to set BKOPS_EN */
-#define MMC_CAP2_POWER_OFF_VCCQ_DURING_SUSPEND (1 << 16)
-
mmc_pm_flag_t pm_caps; /* supported pm features */
int clk_requests; /* internal reference counter */
diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h
index 92888c3..237a92e 100644
--- a/include/linux/mmc/mmc.h
+++ b/include/linux/mmc/mmc.h
@@ -427,11 +427,4 @@
#define MMC_SWITCH_MODE_CLEAR_BITS 0x02 /* Clear bits which are 1 in value */
#define MMC_SWITCH_MODE_WRITE_BYTE 0x03 /* Set target to value */
-/*
- * MMC Poweroff Notify types
- */
-#define MMC_PW_OFF_NOTIFY_NONE 0
-#define MMC_PW_OFF_NOTIFY_SHORT 1
-#define MMC_PW_OFF_NOTIFY_LONG 2
-
#endif /* LINUX_MMC_MMC_H */
diff --git a/include/linux/msm_audio_acdb.h b/include/linux/msm_audio_acdb.h
index e7f06b5..e907f4a 100644
--- a/include/linux/msm_audio_acdb.h
+++ b/include/linux/msm_audio_acdb.h
@@ -39,7 +39,14 @@
(AUDIO_MAX_COMMON_IOCTL_NUM+16), unsigned)
#define AUDIO_SET_AFE_RX_CAL _IOW(AUDIO_IOCTL_MAGIC, \
(AUDIO_MAX_COMMON_IOCTL_NUM+17), unsigned)
-
+#define AUDIO_SET_VOCPROC_COL_CAL _IOW(AUDIO_IOCTL_MAGIC, \
+ (AUDIO_MAX_COMMON_IOCTL_NUM+18), unsigned)
+#define AUDIO_SET_VOCSTRM_COL_CAL _IOW(AUDIO_IOCTL_MAGIC, \
+ (AUDIO_MAX_COMMON_IOCTL_NUM+19), unsigned)
+#define AUDIO_SET_VOCVOL_COL_CAL _IOW(AUDIO_IOCTL_MAGIC, \
+ (AUDIO_MAX_COMMON_IOCTL_NUM+20), unsigned)
+#define AUDIO_SET_VOCPROC_DEV_CFG_CAL _IOW(AUDIO_IOCTL_MAGIC, \
+ (AUDIO_MAX_COMMON_IOCTL_NUM+21), unsigned)
#define AUDIO_MAX_ACDB_IOCTL (AUDIO_MAX_COMMON_IOCTL_NUM+30)
diff --git a/include/linux/msm_ion.h b/include/linux/msm_ion.h
index 21000f9..c1ea490 100644
--- a/include/linux/msm_ion.h
+++ b/include/linux/msm_ion.h
@@ -102,6 +102,12 @@
*/
#define ION_IOMMU_UNMAP_DELAYED 1
+/*
+ * This flag allows clients to defer unsecuring a buffer until the buffer
+ * is actually freed.
+ */
+#define ION_UNSECURE_DELAYED 1
+
/**
* struct ion_cp_heap_pdata - defines a content protection heap in the given
* platform
@@ -216,6 +222,26 @@
* Returns 0 on success
*/
int msm_ion_unsecure_heap_2_0(int heap_id, enum cp_mem_usage usage);
+
+/**
+ * msm_ion_secure_buffer - secure an individual buffer
+ *
+ * @client - client who has access to the buffer
+ * @handle - buffer to secure
+ * @usage - usage hint to TZ
+ * @flags - flags for the securing
+ */
+int msm_ion_secure_buffer(struct ion_client *client, struct ion_handle *handle,
+ enum cp_mem_usage usage, int flags);
+
+/**
+ * msm_ion_unsecure_buffer - unsecure an individual buffer
+ *
+ * @client - client who has access to the buffer
+ * @handle - buffer to secure
+ */
+int msm_ion_unsecure_buffer(struct ion_client *client,
+ struct ion_handle *handle);
#else
static inline int msm_ion_secure_heap(int heap_id)
{
diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h
index 730c7b2..46724eb 100644
--- a/include/linux/power_supply.h
+++ b/include/linux/power_supply.h
@@ -89,6 +89,7 @@
POWER_SUPPLY_PROP_HEALTH,
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_ONLINE,
+ POWER_SUPPLY_PROP_CHARGING_ENABLED,
POWER_SUPPLY_PROP_TECHNOLOGY,
POWER_SUPPLY_PROP_CYCLE_COUNT,
POWER_SUPPLY_PROP_VOLTAGE_MAX,
diff --git a/include/linux/regulator/onsemi-ncp6335d.h b/include/linux/regulator/onsemi-ncp6335d.h
new file mode 100644
index 0000000..a57c3b7
--- /dev/null
+++ b/include/linux/regulator/onsemi-ncp6335d.h
@@ -0,0 +1,28 @@
+/* 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 __NCP6335D_H__
+#define __NCP6335D_H__
+
+enum {
+ NCP6335D_VSEL0,
+ NCP6335D_VSEL1,
+};
+
+struct ncp6335d_platform_data {
+ struct regulator_init_data *init_data;
+ int default_vsel;
+ int slew_rate_ns;
+ int discharge_enable;
+};
+
+#endif
diff --git a/include/linux/tsif_api.h b/include/linux/tsif_api.h
index fc4d20b..0c18228 100644
--- a/include/linux/tsif_api.h
+++ b/include/linux/tsif_api.h
@@ -3,8 +3,7 @@
*
* Kernel API
*
- * Copyright (c) 2009-2010, Code Aurora Forum. All rights
- * reserved.
+ * Copyright (c) 2009-2010, 2012, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -124,11 +123,13 @@
* Should be called prior to any other tsif_XXX function.
*/
void *tsif_attach(int id, void (*notify)(void *client_data), void *client_data);
+
/**
* tsif_detach - detach from device
* @cookie: TSIF cookie previously obtained with tsif_attach()
*/
void tsif_detach(void *cookie);
+
/**
* tsif_get_info - get data buffer info
* @cookie: TSIF cookie previously obtained with tsif_attach()
@@ -140,6 +141,7 @@
* using data; since data buffer will be re-allocated on tsif_start()
*/
void tsif_get_info(void *cookie, void **pdata, int *psize);
+
/**
* tsif_set_mode - set TSIF mode
* @cookie: TSIF cookie previously obtained with tsif_attach()
@@ -150,6 +152,7 @@
* Mode may be changed only when TSIF device is stopped.
*/
int tsif_set_mode(void *cookie, int mode);
+
/**
* tsif_set_time_limit - set TSIF time limit
* @cookie: TSIF cookie previously obtained with tsif_attach()
@@ -160,6 +163,7 @@
* Time limit may be changed only when TSIF device is stopped.
*/
int tsif_set_time_limit(void *cookie, u32 value);
+
/**
* tsif_set_buf_config - configure data buffer
*
@@ -180,6 +184,7 @@
* stats
*/
int tsif_set_buf_config(void *cookie, u32 pkts_in_chunk, u32 chunks_in_buf);
+
/**
* tsif_get_state - query current data buffer information
* @cookie: TSIF cookie previously obtained with tsif_attach()
@@ -188,6 +193,51 @@
* @state: if not NULL, state will be stored here
*/
void tsif_get_state(void *cookie, int *ri, int *wi, enum tsif_state *state);
+
+/**
+ * tsif_set_clk_inverse - set whether to inverse the clock signal.
+ * @cookie: TSIF cookie previously obtained with tsif_attach()
+ * @inverse: 1 to inverse the clock, 0 otherwise. Default is 0.
+ *
+ * Return error code
+ *
+ * Setting may be changed only when TSIF device is stopped.
+ */
+int tsif_set_clk_inverse(void *cookie, int inverse);
+
+/**
+ * tsif_set_data_inverse - set whether to inverse the data signal.
+ * @cookie: TSIF cookie previously obtained with tsif_attach()
+ * @inverse: 1 to inverse the clock, 0 otherwise. Default is 0.
+ *
+ * Return error code
+ *
+ * Setting may be changed only when TSIF device is stopped.
+ */
+int tsif_set_data_inverse(void *cookie, int inverse);
+
+/**
+ * tsif_set_sync_inverse - set whether to inverse the sync signal.
+ * @cookie: TSIF cookie previously obtained with tsif_attach()
+ * @inverse: 1 to inverse the clock, 0 otherwise. Default is 0.
+ *
+ * Return error code
+ *
+ * Setting may be changed only when TSIF device is stopped.
+ */
+int tsif_set_sync_inverse(void *cookie, int inverse);
+
+/**
+ * tsif_set_enable_inverse - set whether to inverse the enable signal.
+ * @cookie: TSIF cookie previously obtained with tsif_attach()
+ * @inverse: 1 to inverse the clock, 0 otherwise. Default is 0.
+ *
+ * Return error code
+ *
+ * Setting may be changed only when TSIF device is stopped.
+ */
+int tsif_set_enable_inverse(void *cookie, int inverse);
+
/**
* tsif_start - start data acquisition
* @cookie: TSIF cookie previously obtained with tsif_attach()
@@ -195,6 +245,7 @@
* Return error code
*/
int tsif_start(void *cookie);
+
/**
* tsif_stop - stop data acquisition
* @cookie: TSIF cookie previously obtained with tsif_attach()
@@ -203,6 +254,7 @@
* query data buffer info using tsif_get_info() and reset its data pointers.
*/
void tsif_stop(void *cookie);
+
/**
* tsif_reclaim_packets - inform that buffer space may be reclaimed
* @cookie: TSIF cookie previously obtained with tsif_attach()
diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h
index dd091cd..268aa48 100644
--- a/include/linux/usb/hcd.h
+++ b/include/linux/usb/hcd.h
@@ -351,6 +351,7 @@
void (*dump_regs)(struct usb_hcd *);
void (*enable_ulpi_control)(struct usb_hcd *hcd, u32 linestate);
void (*disable_ulpi_control)(struct usb_hcd *hcd);
+ void (*set_autosuspend_delay)(struct usb_device *);
};
extern int usb_hcd_link_urb_to_ep(struct usb_hcd *hcd, struct urb *urb);
diff --git a/include/media/gpio-ir-recv.h b/include/media/gpio-ir-recv.h
index 3eab611..ffdf2f0 100644
--- a/include/media/gpio-ir-recv.h
+++ b/include/media/gpio-ir-recv.h
@@ -17,6 +17,7 @@
unsigned int gpio_nr;
bool active_low;
bool can_wakeup;
+ u32 swfi_latency;
};
#endif /* __GPIO_IR_RECV_H__ */
diff --git a/include/media/vcap_v4l2.h b/include/media/vcap_v4l2.h
index 3f0c862..6d684ef 100644
--- a/include/media/vcap_v4l2.h
+++ b/include/media/vcap_v4l2.h
@@ -184,6 +184,11 @@
uint32_t bus_client_handle;
+ int domain_num;
+ struct device *vc_iommu_ctx;
+ struct device *vp_iommu_ctx;
+ struct iommu_domain *iommu_vcap_domain;
+
struct vcap_client_data *vc_client;
struct vcap_client_data *vp_client;
diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h
index 602fe59..14ccf3e 100644
--- a/include/net/bluetooth/mgmt.h
+++ b/include/net/bluetooth/mgmt.h
@@ -267,6 +267,11 @@
#define MGMT_OP_LE_CANCEL_CREATE_CONN_WHITE_LIST 0xE005
+#define MGMT_OP_LE_CANCEL_CREATE_CONN 0xE006
+struct mgmt_cp_le_cancel_create_conn {
+ bdaddr_t bdaddr;
+} __packed;
+
#define MGMT_EV_CMD_COMPLETE 0x0001
struct mgmt_ev_cmd_complete {
__le16 opcode;
diff --git a/include/sound/apr_audio-v2.h b/include/sound/apr_audio-v2.h
index d902881..07179e9 100644
--- a/include/sound/apr_audio-v2.h
+++ b/include/sound/apr_audio-v2.h
@@ -1627,7 +1627,7 @@
* Supported values: #AFE_API_VERSION_HDMI_CONFIG
*/
-u16 dataype;
+u16 datatype;
/* data type
* Supported values:
* - #LINEAR_PCM_DATA
diff --git a/include/sound/q6asm-v2.h b/include/sound/q6asm-v2.h
index 2ee5ff7..182da1c 100644
--- a/include/sound/q6asm-v2.h
+++ b/include/sound/q6asm-v2.h
@@ -66,6 +66,7 @@
#define SYNC_IO_MODE 0x0001
#define ASYNC_IO_MODE 0x0002
+#define COMPRESSED_IO 0x0040
#define NT_MODE 0x0400
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index d7deaaf..28eb7ea 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -2073,6 +2073,40 @@
return err;
}
+static int le_cancel_create_conn(struct sock *sk, u16 index,
+ unsigned char *data, u16 len)
+{
+ struct mgmt_cp_le_cancel_create_conn *cp = (void *) data;
+ struct hci_dev *hdev;
+ int err = 0;
+
+ if (len != sizeof(*cp))
+ return cmd_status(sk, index, MGMT_OP_LE_CANCEL_CREATE_CONN,
+ EINVAL);
+
+ hdev = hci_dev_get(index);
+
+ if (!hdev)
+ return cmd_status(sk, index, MGMT_OP_LE_CANCEL_CREATE_CONN,
+ ENODEV);
+
+ hci_dev_lock_bh(hdev);
+
+ if (!test_bit(HCI_UP, &hdev->flags)) {
+ err = cmd_status(sk, index, MGMT_OP_LE_CANCEL_CREATE_CONN,
+ ENETDOWN);
+ goto failed;
+ }
+
+ hci_le_cancel_create_connect(hdev, &cp->bdaddr);
+
+failed:
+ hci_dev_unlock_bh(hdev);
+ hci_dev_put(hdev);
+
+return err;
+}
+
static int set_local_name(struct sock *sk, u16 index, unsigned char *data,
u16 len)
{
@@ -2664,6 +2698,9 @@
case MGMT_OP_LE_CANCEL_CREATE_CONN_WHITE_LIST:
err = le_cancel_create_conn_white_list(sk, index);
break;
+ case MGMT_OP_LE_CANCEL_CREATE_CONN:
+ err = le_cancel_create_conn(sk, index, buf + sizeof(*hdr), len);
+ break;
default:
BT_DBG("Unknown op %u", opcode);
err = cmd_status(sk, index, opcode, 0x01);
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 5aabfee..1c9d86b 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -51,7 +51,7 @@
snd-soc-wcd9304-objs := wcd9304.o wcd9304-tables.o
snd-soc-wcd9310-objs := wcd9310.o wcd9310-tables.o
snd-soc-cs8427-objs := cs8427.o
-snd-soc-wcd9320-objs := wcd9320.o wcd9320-tables.o
+snd-soc-wcd9320-objs := wcd9xxx-resmgr.o wcd9320.o wcd9320-tables.o wcd9xxx-mbhc.o
snd-soc-wl1273-objs := wl1273.o
snd-soc-wm1250-ev1-objs := wm1250-ev1.o
snd-soc-wm2000-objs := wm2000.o
diff --git a/sound/soc/codecs/wcd9320.c b/sound/soc/codecs/wcd9320.c
index 0c36c4a..886e4d3 100644
--- a/sound/soc/codecs/wcd9320.c
+++ b/sound/soc/codecs/wcd9320.c
@@ -33,37 +33,19 @@
#include <linux/kernel.h>
#include <linux/gpio.h>
#include "wcd9320.h"
+#include "wcd9xxx-resmgr.h"
#define WCD9320_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\
SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000)
-
#define NUM_DECIMATORS 10
#define NUM_INTERPOLATORS 7
#define BITS_PER_REG 8
-#define TAIKO_CFILT_FAST_MODE 0x00
-#define TAIKO_CFILT_SLOW_MODE 0x40
-#define MBHC_FW_READ_ATTEMPTS 15
-#define MBHC_FW_READ_TIMEOUT 2000000
#define TAIKO_TX_PORT_NUMBER 16
-enum {
- MBHC_USE_HPHL_TRIGGER = 1,
- MBHC_USE_MB_TRIGGER = 2
-};
-
-#define MBHC_NUM_DCE_PLUG_DETECT 3
-#define NUM_ATTEMPTS_INSERT_DETECT 25
-#define NUM_ATTEMPTS_TO_REPORT 5
-
-#define TAIKO_JACK_MASK (SND_JACK_HEADSET | SND_JACK_OC_HPHL | \
- SND_JACK_OC_HPHR | SND_JACK_UNSUPPORTED)
-
#define TAIKO_I2S_MASTER_MODE_MASK 0x08
-#define TAIKO_OCP_ATTEMPT 1
-
enum {
AIF1_PB = 0,
AIF1_CAP,
@@ -92,56 +74,12 @@
#define TAIKO_COMP_DIGITAL_GAIN_OFFSET 3
-#define TAIKO_MCLK_RATE_12288KHZ 12288000
-#define TAIKO_MCLK_RATE_9600KHZ 9600000
-
-#define TAIKO_FAKE_INS_THRESHOLD_MS 2500
-#define TAIKO_FAKE_REMOVAL_MIN_PERIOD_MS 50
-
-#define TAIKO_MBHC_BUTTON_MIN 0x8000
-
-#define TAIKO_MBHC_FAKE_INSERT_LOW 10
-#define TAIKO_MBHC_FAKE_INSERT_HIGH 80
-#define TAIKO_MBHC_FAKE_INS_HIGH_NO_GPIO 150
-
-#define TAIKO_MBHC_STATUS_REL_DETECTION 0x0C
-
-#define TAIKO_MBHC_GPIO_REL_DEBOUNCE_TIME_MS 200
-
-#define TAIKO_MBHC_FAKE_INS_DELTA_MV 200
-#define TAIKO_MBHC_FAKE_INS_DELTA_SCALED_MV 300
-
-#define TAIKO_HS_DETECT_PLUG_TIME_MS (5 * 1000)
-#define TAIKO_HS_DETECT_PLUG_INERVAL_MS 100
-
-#define TAIKO_GPIO_IRQ_DEBOUNCE_TIME_US 5000
-
-#define TAIKO_MBHC_GND_MIC_SWAP_THRESHOLD 2
-
-#define TAIKO_ACQUIRE_LOCK(x) do { mutex_lock(&x); } while (0)
-#define TAIKO_RELEASE_LOCK(x) do { mutex_unlock(&x); } while (0)
-
static const DECLARE_TLV_DB_SCALE(digital_gain, 0, 1, 0);
static const DECLARE_TLV_DB_SCALE(line_gain, 0, 7, 1);
static const DECLARE_TLV_DB_SCALE(analog_gain, 0, 25, 1);
static struct snd_soc_dai_driver taiko_dai[];
static const DECLARE_TLV_DB_SCALE(aux_pga_gain, 0, 2, 0);
-enum taiko_bandgap_type {
- TAIKO_BANDGAP_OFF = 0,
- TAIKO_BANDGAP_AUDIO_MODE,
- TAIKO_BANDGAP_MBHC_MODE,
-};
-
-struct mbhc_micbias_regs {
- u16 cfilt_val;
- u16 cfilt_ctl;
- u16 mbhc_reg;
- u16 int_rbias;
- u16 ctl_reg;
- u8 cfilt_sel;
-};
-
/* Codec supports 2 IIR filters */
enum {
IIR1 = 0,
@@ -174,72 +112,12 @@
COMPANDER_FS_MAX,
};
-/* Flags to track of PA and DAC state.
- * PA and DAC should be tracked separately as AUXPGA loopback requires
- * only PA to be turned on without DAC being on. */
-enum taiko_priv_ack_flags {
- TAIKO_HPHL_PA_OFF_ACK = 0,
- TAIKO_HPHR_PA_OFF_ACK,
- TAIKO_HPHL_DAC_OFF_ACK,
- TAIKO_HPHR_DAC_OFF_ACK
-};
-
-
struct comp_sample_dependent_params {
u32 peak_det_timeout;
u32 rms_meter_div_fact;
u32 rms_meter_resamp_fact;
};
-/* Data used by MBHC */
-struct mbhc_internal_cal_data {
- u16 dce_z;
- u16 dce_mb;
- u16 sta_z;
- u16 sta_mb;
- u32 t_sta_dce;
- u32 t_dce;
- u32 t_sta;
- u32 micb_mv;
- u16 v_ins_hu;
- u16 v_ins_h;
- u16 v_b1_hu;
- u16 v_b1_h;
- u16 v_b1_huc;
- u16 v_brh;
- u16 v_brl;
- u16 v_no_mic;
- u8 npoll;
- u8 nbounce_wait;
- s16 adj_v_hs_max;
- u16 adj_v_ins_hu;
- u16 adj_v_ins_h;
- s16 v_inval_ins_low;
- s16 v_inval_ins_high;
-};
-
-struct taiko_reg_address {
- u16 micb_4_ctl;
- u16 micb_4_int_rbias;
- u16 micb_4_mbhc;
-};
-
-enum taiko_mbhc_plug_type {
- PLUG_TYPE_INVALID = -1,
- PLUG_TYPE_NONE,
- PLUG_TYPE_HEADSET,
- PLUG_TYPE_HEADPHONE,
- PLUG_TYPE_HIGH_HPH,
- PLUG_TYPE_GND_MIC_SWAP,
-};
-
-enum taiko_mbhc_state {
- MBHC_STATE_NONE = -1,
- MBHC_STATE_POTENTIAL,
- MBHC_STATE_POTENTIAL_RECOVERY,
- MBHC_STATE_RELEASE,
-};
-
struct hpf_work {
struct taiko_priv *taiko;
u32 decimator;
@@ -295,58 +173,17 @@
struct taiko_priv {
struct snd_soc_codec *codec;
- struct taiko_reg_address reg_addr;
u32 adc_count;
- u32 cfilt1_cnt;
- u32 cfilt2_cnt;
- u32 cfilt3_cnt;
u32 rx_bias_count;
s32 dmic_1_2_clk_cnt;
s32 dmic_3_4_clk_cnt;
s32 dmic_5_6_clk_cnt;
- enum taiko_bandgap_type bandgap_type;
- bool mclk_enabled;
- bool clock_active;
- bool config_mode_active;
- bool mbhc_polling_active;
- unsigned long mbhc_fake_ins_start;
- int buttons_pressed;
- enum taiko_mbhc_state mbhc_state;
- struct taiko_mbhc_config mbhc_cfg;
- struct mbhc_internal_cal_data mbhc_data;
-
- struct wcd9xxx_pdata *pdata;
u32 anc_slot;
- bool no_mic_headset_override;
- /* Delayed work to report long button press */
- struct delayed_work mbhc_btn_dwork;
-
- struct mbhc_micbias_regs mbhc_bias_regs;
- bool mbhc_micbias_switched;
-
- /* track PA/DAC state */
- unsigned long hph_pa_dac_state;
-
/*track taiko interface type*/
u8 intf_type;
- u32 hph_status; /* track headhpone status */
- /* define separate work for left and right headphone OCP to avoid
- * additional checking on which OCP event to report so no locking
- * to ensure synchronization is required
- */
- struct work_struct hphlocp_work; /* reporting left hph ocp off */
- struct work_struct hphrocp_work; /* reporting right hph ocp off */
-
- u8 hphlocp_cnt; /* headphone left ocp retry */
- u8 hphrocp_cnt; /* headphone right ocp retry */
-
- /* Work to perform MBHC Firmware Read */
- struct delayed_work mbhc_firmware_dwork;
- const struct firmware *mbhc_fw;
-
/* num of slim ports required */
struct wcd9xxx_codec_dai_data dai[NUM_CODEC_DAIS];
@@ -359,29 +196,12 @@
u8 aux_l_gain;
u8 aux_r_gain;
- struct delayed_work mbhc_insert_dwork;
- unsigned long mbhc_last_resume; /* in jiffies */
-
- u8 current_plug;
- struct work_struct hs_correct_plug_work;
- bool hs_detect_work_stop;
- bool hs_polling_irq_prepared;
- bool lpi_enabled; /* low power insertion detection */
- bool in_gpio_handler;
- /* Currently, only used for mbhc purpose, to protect
- * concurrent execution of mbhc threaded irq handlers and
- * kill race between DAPM and MBHC.But can serve as a
- * general lock to protect codec resource
- */
- struct mutex codec_resource_lock;
-
-#ifdef CONFIG_DEBUG_FS
- struct dentry *debugfs_poke;
- struct dentry *debugfs_mbhc;
-#endif
+ /* resmgr module */
+ struct wcd9xxx_resmgr resmgr;
+ /* mbhc module */
+ struct wcd9xxx_mbhc mbhc;
};
-
static const u32 comp_shift[] = {
0,
2,
@@ -1960,173 +1780,6 @@
return 0;
}
-static void taiko_codec_enable_audio_mode_bandgap(struct snd_soc_codec *codec)
-{
- snd_soc_update_bits(codec, TAIKO_A_BIAS_CENTRAL_BG_CTL, 0x80,
- 0x80);
- snd_soc_update_bits(codec, TAIKO_A_BIAS_CENTRAL_BG_CTL, 0x04,
- 0x04);
- snd_soc_update_bits(codec, TAIKO_A_BIAS_CENTRAL_BG_CTL, 0x01,
- 0x01);
- usleep_range(1000, 1000);
- snd_soc_update_bits(codec, TAIKO_A_BIAS_CENTRAL_BG_CTL, 0x80,
- 0x00);
-}
-
-static void taiko_codec_enable_bandgap(struct snd_soc_codec *codec,
- enum taiko_bandgap_type choice)
-{
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-
- /* TODO lock resources accessed by audio streams and threaded
- * interrupt handlers
- */
-
- pr_debug("%s, choice is %d, current is %d\n", __func__, choice,
- taiko->bandgap_type);
-
- if (taiko->bandgap_type == choice)
- return;
-
- if ((taiko->bandgap_type == TAIKO_BANDGAP_OFF) &&
- (choice == TAIKO_BANDGAP_AUDIO_MODE)) {
- taiko_codec_enable_audio_mode_bandgap(codec);
- } else if (choice == TAIKO_BANDGAP_MBHC_MODE) {
- /* bandgap mode becomes fast,
- * mclk should be off or clk buff source souldn't be VBG
- * Let's turn off mclk always */
- WARN_ON(snd_soc_read(codec, TAIKO_A_CLK_BUFF_EN2) & (1 << 2));
- snd_soc_update_bits(codec, TAIKO_A_BIAS_CENTRAL_BG_CTL, 0x2,
- 0x2);
- snd_soc_update_bits(codec, TAIKO_A_BIAS_CENTRAL_BG_CTL, 0x80,
- 0x80);
- snd_soc_update_bits(codec, TAIKO_A_BIAS_CENTRAL_BG_CTL, 0x4,
- 0x4);
- snd_soc_update_bits(codec, TAIKO_A_BIAS_CENTRAL_BG_CTL, 0x01,
- 0x01);
- usleep_range(1000, 1000);
- snd_soc_update_bits(codec, TAIKO_A_BIAS_CENTRAL_BG_CTL, 0x80,
- 0x00);
- } else if ((taiko->bandgap_type == TAIKO_BANDGAP_MBHC_MODE) &&
- (choice == TAIKO_BANDGAP_AUDIO_MODE)) {
- snd_soc_write(codec, TAIKO_A_BIAS_CENTRAL_BG_CTL, 0x00);
- usleep_range(100, 100);
- taiko_codec_enable_audio_mode_bandgap(codec);
- } else if (choice == TAIKO_BANDGAP_OFF) {
- snd_soc_write(codec, TAIKO_A_BIAS_CENTRAL_BG_CTL, 0x50);
- } else {
- pr_err("%s: Error, Invalid bandgap settings\n", __func__);
- }
- taiko->bandgap_type = choice;
-}
-
-static void taiko_codec_disable_clock_block(struct snd_soc_codec *codec)
-{
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
- pr_debug("%s\n", __func__);
- snd_soc_update_bits(codec, TAIKO_A_CLK_BUFF_EN2, 0x04, 0x00);
- usleep_range(50, 50);
- snd_soc_update_bits(codec, TAIKO_A_CLK_BUFF_EN2, 0x02, 0x02);
- snd_soc_update_bits(codec, TAIKO_A_CLK_BUFF_EN1, 0x01, 0x00);
- usleep_range(50, 50);
- taiko->clock_active = false;
-}
-
-static int taiko_codec_mclk_index(const struct taiko_priv *taiko)
-{
- if (taiko->mbhc_cfg.mclk_rate == TAIKO_MCLK_RATE_12288KHZ)
- return 0;
- else if (taiko->mbhc_cfg.mclk_rate == TAIKO_MCLK_RATE_9600KHZ)
- return 1;
- else {
- BUG_ON(1);
- return -EINVAL;
- }
-}
-
-static void taiko_enable_rx_bias(struct snd_soc_codec *codec, u32 enable)
-{
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-
- if (enable) {
- taiko->rx_bias_count++;
- if (taiko->rx_bias_count == 1)
- snd_soc_update_bits(codec, TAIKO_A_RX_COM_BIAS,
- 0x80, 0x80);
- } else {
- taiko->rx_bias_count--;
- if (!taiko->rx_bias_count)
- snd_soc_update_bits(codec, TAIKO_A_RX_COM_BIAS,
- 0x80, 0x00);
- }
-}
-
-static int taiko_codec_enable_config_mode(struct snd_soc_codec *codec,
- int enable)
-{
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-
- pr_debug("%s: enable = %d\n", __func__, enable);
- if (enable) {
-
- snd_soc_update_bits(codec, TAIKO_A_RC_OSC_FREQ, 0x10, 0);
- /* bandgap mode to fast */
- snd_soc_write(codec, TAIKO_A_BIAS_OSC_BG_CTL, 0x17);
- usleep_range(5, 5);
- snd_soc_update_bits(codec, TAIKO_A_RC_OSC_FREQ, 0x80,
- 0x80);
- snd_soc_update_bits(codec, TAIKO_A_RC_OSC_TEST, 0x80,
- 0x80);
- usleep_range(10, 10);
- snd_soc_update_bits(codec, TAIKO_A_RC_OSC_TEST, 0x80, 0);
- usleep_range(10000, 10000);
- snd_soc_update_bits(codec, TAIKO_A_CLK_BUFF_EN1, 0x08, 0x08);
-
- } else {
- snd_soc_update_bits(codec, TAIKO_A_BIAS_OSC_BG_CTL, 0x1,
- 0);
- snd_soc_update_bits(codec, TAIKO_A_RC_OSC_FREQ, 0x80, 0);
- /* clk source to ext clk and clk buff ref to VBG */
- snd_soc_update_bits(codec, TAIKO_A_CLK_BUFF_EN1, 0x0C, 0x04);
- }
- taiko->config_mode_active = enable ? true : false;
-
- return 0;
-}
-
-static int taiko_codec_enable_clock_block(struct snd_soc_codec *codec,
- int config_mode)
-{
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-
- pr_debug("%s: config_mode = %d\n", __func__, config_mode);
-
- /* transit to RCO requires mclk off */
- WARN_ON(snd_soc_read(codec, TAIKO_A_CLK_BUFF_EN2) & (1 << 2));
- if (config_mode) {
- /* enable RCO and switch to it */
- taiko_codec_enable_config_mode(codec, 1);
- snd_soc_write(codec, TAIKO_A_CLK_BUFF_EN2, 0x02);
- usleep_range(1000, 1000);
- } else {
- /* switch to MCLK */
- snd_soc_update_bits(codec, TAIKO_A_CLK_BUFF_EN1, 0x08, 0x00);
-
- if (taiko->mbhc_polling_active)
- snd_soc_write(codec, TAIKO_A_CLK_BUFF_EN2, 0x02);
- taiko_codec_enable_config_mode(codec, 0);
- }
-
- snd_soc_update_bits(codec, TAIKO_A_CLK_BUFF_EN1, 0x01, 0x01);
- snd_soc_update_bits(codec, TAIKO_A_CLK_BUFF_EN2, 0x02, 0x00);
- /* on MCLK */
- snd_soc_update_bits(codec, TAIKO_A_CLK_BUFF_EN2, 0x04, 0x04);
- snd_soc_update_bits(codec, TAIKO_A_CDC_CLK_MCLK_CTL, 0x01, 0x01);
- usleep_range(50, 50);
- taiko->clock_active = true;
- return 0;
-}
-
static int taiko_codec_enable_aux_pga(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
@@ -2137,32 +1790,22 @@
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- taiko_codec_enable_bandgap(codec, TAIKO_BANDGAP_AUDIO_MODE);
- taiko_enable_rx_bias(codec, 1);
-
- if (taiko->aux_pga_cnt++ == 1
- && !taiko->mclk_enabled) {
- taiko_codec_enable_clock_block(codec, 1);
- pr_debug("AUX PGA enabled RC osc\n");
- }
+ WCD9XXX_BCL_LOCK(&taiko->resmgr);
+ wcd9xxx_resmgr_get_bandgap(&taiko->resmgr,
+ WCD9XXX_BANDGAP_AUDIO_MODE);
+ /* AUX PGA requires RCO or MCLK */
+ wcd9xxx_resmgr_get_clk_block(&taiko->resmgr, WCD9XXX_CLK_RCO);
+ wcd9xxx_resmgr_enable_rx_bias(&taiko->resmgr, 1);
+ WCD9XXX_BCL_UNLOCK(&taiko->resmgr);
break;
case SND_SOC_DAPM_POST_PMD:
- taiko_enable_rx_bias(codec, 0);
-
- if (taiko->aux_pga_cnt-- == 0) {
- if (taiko->mbhc_polling_active)
- taiko_codec_enable_bandgap(codec,
- TAIKO_BANDGAP_MBHC_MODE);
- else
- taiko_codec_enable_bandgap(codec,
- TAIKO_BANDGAP_OFF);
-
- if (!taiko->mclk_enabled &&
- !taiko->mbhc_polling_active) {
- taiko_codec_enable_clock_block(codec, 0);
- }
- }
+ WCD9XXX_BCL_LOCK(&taiko->resmgr);
+ wcd9xxx_resmgr_enable_rx_bias(&taiko->resmgr, 0);
+ wcd9xxx_resmgr_put_bandgap(&taiko->resmgr,
+ WCD9XXX_BANDGAP_AUDIO_MODE);
+ wcd9xxx_resmgr_put_clk_block(&taiko->resmgr, WCD9XXX_CLK_RCO);
+ WCD9XXX_BCL_UNLOCK(&taiko->resmgr);
break;
}
return 0;
@@ -2389,358 +2032,47 @@
return 0;
}
-/* called under codec_resource_lock acquisition */
-static void taiko_codec_start_hs_polling(struct snd_soc_codec *codec)
-{
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
- int mbhc_state = taiko->mbhc_state;
-
- pr_debug("%s: enter\n", __func__);
- if (!taiko->mbhc_polling_active) {
- pr_debug("Polling is not active, do not start polling\n");
- return;
- }
- snd_soc_write(codec, TAIKO_A_MBHC_SCALING_MUX_1, 0x84);
-
- if (!taiko->no_mic_headset_override) {
- if (mbhc_state == MBHC_STATE_POTENTIAL) {
- pr_debug("%s recovering MBHC state macine\n", __func__);
- taiko->mbhc_state = MBHC_STATE_POTENTIAL_RECOVERY;
- /* set to max button press threshold */
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B2_CTL,
- 0x7F);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B1_CTL,
- 0xFF);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B4_CTL,
- 0x7F);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B3_CTL,
- 0xFF);
- /* set to max */
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B6_CTL,
- 0x7F);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B5_CTL,
- 0xFF);
- }
- }
-
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_EN_CTL, 0x1);
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x8, 0x0);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_EN_CTL, 0x1);
- pr_debug("%s: leave\n", __func__);
-}
-
-/* called under codec_resource_lock acquisition */
-static void taiko_codec_pause_hs_polling(struct snd_soc_codec *codec)
-{
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-
- pr_debug("%s: enter\n", __func__);
- if (!taiko->mbhc_polling_active) {
- pr_debug("polling not active, nothing to pause\n");
- return;
- }
-
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
- pr_debug("%s: leave\n", __func__);
-}
-
-static void taiko_codec_switch_cfilt_mode(struct snd_soc_codec *codec, int mode)
-{
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
- u8 reg_mode_val, cur_mode_val;
- bool mbhc_was_polling = false;
-
- if (mode)
- reg_mode_val = TAIKO_CFILT_FAST_MODE;
- else
- reg_mode_val = TAIKO_CFILT_SLOW_MODE;
-
- cur_mode_val = snd_soc_read(codec,
- taiko->mbhc_bias_regs.cfilt_ctl) & 0x40;
-
- if (cur_mode_val != reg_mode_val) {
- TAIKO_ACQUIRE_LOCK(taiko->codec_resource_lock);
- if (taiko->mbhc_polling_active) {
- taiko_codec_pause_hs_polling(codec);
- mbhc_was_polling = true;
- }
- snd_soc_update_bits(codec,
- taiko->mbhc_bias_regs.cfilt_ctl, 0x40, reg_mode_val);
- if (mbhc_was_polling)
- taiko_codec_start_hs_polling(codec);
- TAIKO_RELEASE_LOCK(taiko->codec_resource_lock);
- pr_debug("%s: CFILT mode change (%x to %x)\n", __func__,
- cur_mode_val, reg_mode_val);
- } else {
- pr_debug("%s: CFILT Value is already %x\n",
- __func__, cur_mode_val);
- }
-}
-
-static void taiko_codec_update_cfilt_usage(struct snd_soc_codec *codec,
- u8 cfilt_sel, int inc)
-{
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
- u32 *cfilt_cnt_ptr = NULL;
- u16 micb_cfilt_reg;
-
- switch (cfilt_sel) {
- case TAIKO_CFILT1_SEL:
- cfilt_cnt_ptr = &taiko->cfilt1_cnt;
- micb_cfilt_reg = TAIKO_A_MICB_CFILT_1_CTL;
- break;
- case TAIKO_CFILT2_SEL:
- cfilt_cnt_ptr = &taiko->cfilt2_cnt;
- micb_cfilt_reg = TAIKO_A_MICB_CFILT_2_CTL;
- break;
- case TAIKO_CFILT3_SEL:
- cfilt_cnt_ptr = &taiko->cfilt3_cnt;
- micb_cfilt_reg = TAIKO_A_MICB_CFILT_3_CTL;
- break;
- default:
- return; /* should not happen */
- }
-
- if (inc) {
- if (!(*cfilt_cnt_ptr)++) {
- /* Switch CFILT to slow mode if MBHC CFILT being used */
- if (cfilt_sel == taiko->mbhc_bias_regs.cfilt_sel)
- taiko_codec_switch_cfilt_mode(codec, 0);
-
- snd_soc_update_bits(codec, micb_cfilt_reg, 0x80, 0x80);
- }
- } else {
- /* check if count not zero, decrement
- * then check if zero, go ahead disable cfilter
- */
- if ((*cfilt_cnt_ptr) && !--(*cfilt_cnt_ptr)) {
- snd_soc_update_bits(codec, micb_cfilt_reg, 0x80, 0);
-
- /* Switch CFILT to fast mode if MBHC CFILT being used */
- if (cfilt_sel == taiko->mbhc_bias_regs.cfilt_sel)
- taiko_codec_switch_cfilt_mode(codec, 1);
- }
- }
-}
-
-static int taiko_find_k_value(unsigned int ldoh_v, unsigned int cfilt_mv)
-{
- int rc = -EINVAL;
- unsigned min_mv, max_mv;
-
- switch (ldoh_v) {
- case TAIKO_LDOH_1P95_V:
- min_mv = 160;
- max_mv = 1800;
- break;
- case TAIKO_LDOH_2P35_V:
- min_mv = 200;
- max_mv = 2200;
- break;
- case TAIKO_LDOH_2P75_V:
- min_mv = 240;
- max_mv = 2600;
- break;
- case TAIKO_LDOH_2P85_V:
- min_mv = 250;
- max_mv = 2700;
- break;
- default:
- goto done;
- }
-
- if (cfilt_mv < min_mv || cfilt_mv > max_mv)
- goto done;
-
- for (rc = 4; rc <= 44; rc++) {
- min_mv = max_mv * (rc) / 44;
- if (min_mv >= cfilt_mv) {
- rc -= 4;
- break;
- }
- }
-done:
- return rc;
-}
-
-static bool taiko_is_hph_pa_on(struct snd_soc_codec *codec)
-{
- u8 hph_reg_val = 0;
- hph_reg_val = snd_soc_read(codec, TAIKO_A_RX_HPH_CNP_EN);
-
- return (hph_reg_val & 0x30) ? true : false;
-}
-
-static bool taiko_is_hph_dac_on(struct snd_soc_codec *codec, int left)
-{
- u8 hph_reg_val = 0;
- if (left)
- hph_reg_val = snd_soc_read(codec,
- TAIKO_A_RX_HPH_L_DAC_CTL);
- else
- hph_reg_val = snd_soc_read(codec,
- TAIKO_A_RX_HPH_R_DAC_CTL);
-
- return (hph_reg_val & 0xC0) ? true : false;
-}
-
-static void taiko_turn_onoff_override(struct snd_soc_codec *codec, bool on)
-{
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_B1_CTL, 0x04, on << 2);
-}
-
-/* called under codec_resource_lock acquisition */
-static void taiko_codec_drive_v_to_micbias(struct snd_soc_codec *codec,
- int usec)
-{
- int cfilt_k_val;
- bool set = true;
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-
- if (taiko->mbhc_data.micb_mv != VDDIO_MICBIAS_MV &&
- taiko->mbhc_micbias_switched) {
- pr_debug("%s: set mic V to micbias V\n", __func__);
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x2, 0x2);
- taiko_turn_onoff_override(codec, true);
- while (1) {
- cfilt_k_val = taiko_find_k_value(
- taiko->pdata->micbias.ldoh_v,
- set ? taiko->mbhc_data.micb_mv :
- VDDIO_MICBIAS_MV);
- snd_soc_update_bits(codec,
- taiko->mbhc_bias_regs.cfilt_val,
- 0xFC, (cfilt_k_val << 2));
- if (!set)
- break;
- usleep_range(usec, usec);
- set = false;
- }
- taiko_turn_onoff_override(codec, false);
- }
-}
-
-/* called under codec_resource_lock acquisition */
-static void __taiko_codec_switch_micbias(struct snd_soc_codec *codec,
- int vddio_switch, bool restartpolling,
- bool checkpolling)
-{
- int cfilt_k_val;
- bool override;
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-
- if (vddio_switch && !taiko->mbhc_micbias_switched &&
- (!checkpolling || taiko->mbhc_polling_active)) {
- if (restartpolling)
- taiko_codec_pause_hs_polling(codec);
- override = snd_soc_read(codec, TAIKO_A_CDC_MBHC_B1_CTL) & 0x04;
- if (!override)
- taiko_turn_onoff_override(codec, true);
- /* Adjust threshold if Mic Bias voltage changes */
- if (taiko->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) {
- cfilt_k_val = taiko_find_k_value(
- taiko->pdata->micbias.ldoh_v,
- VDDIO_MICBIAS_MV);
- usleep_range(10000, 10000);
- snd_soc_update_bits(codec,
- taiko->mbhc_bias_regs.cfilt_val,
- 0xFC, (cfilt_k_val << 2));
- usleep_range(10000, 10000);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B1_CTL,
- taiko->mbhc_data.adj_v_ins_hu & 0xFF);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B2_CTL,
- (taiko->mbhc_data.adj_v_ins_hu >> 8) &
- 0xFF);
- pr_debug("%s: Programmed MBHC thresholds to VDDIO\n",
- __func__);
- }
-
- /* enable MIC BIAS Switch to VDDIO */
- snd_soc_update_bits(codec, taiko->mbhc_bias_regs.mbhc_reg,
- 0x80, 0x80);
- snd_soc_update_bits(codec, taiko->mbhc_bias_regs.mbhc_reg,
- 0x10, 0x00);
- if (!override)
- taiko_turn_onoff_override(codec, false);
- if (restartpolling)
- taiko_codec_start_hs_polling(codec);
-
- taiko->mbhc_micbias_switched = true;
- pr_debug("%s: VDDIO switch enabled\n", __func__);
- } else if (!vddio_switch && taiko->mbhc_micbias_switched) {
- if ((!checkpolling || taiko->mbhc_polling_active) &&
- restartpolling)
- taiko_codec_pause_hs_polling(codec);
- /* Reprogram thresholds */
- if (taiko->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) {
- cfilt_k_val = taiko_find_k_value(
- taiko->pdata->micbias.ldoh_v,
- taiko->mbhc_data.micb_mv);
- snd_soc_update_bits(codec,
- taiko->mbhc_bias_regs.cfilt_val,
- 0xFC, (cfilt_k_val << 2));
- usleep_range(10000, 10000);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B1_CTL,
- taiko->mbhc_data.v_ins_hu & 0xFF);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B2_CTL,
- (taiko->mbhc_data.v_ins_hu >> 8) & 0xFF);
- pr_debug("%s: Programmed MBHC thresholds to MICBIAS\n",
- __func__);
- }
-
- /* Disable MIC BIAS Switch to VDDIO */
- snd_soc_update_bits(codec, taiko->mbhc_bias_regs.mbhc_reg,
- 0x80, 0x00);
- snd_soc_update_bits(codec, taiko->mbhc_bias_regs.mbhc_reg,
- 0x10, 0x00);
-
- if ((!checkpolling || taiko->mbhc_polling_active) &&
- restartpolling)
- taiko_codec_start_hs_polling(codec);
-
- taiko->mbhc_micbias_switched = false;
- pr_debug("%s: VDDIO switch disabled\n", __func__);
- }
-}
-
-static void taiko_codec_switch_micbias(struct snd_soc_codec *codec,
- int vddio_switch)
-{
- return __taiko_codec_switch_micbias(codec, vddio_switch, true, true);
-}
-
static int taiko_codec_enable_micbias(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = w->codec;
struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
u16 micb_int_reg;
- int micb_line;
u8 cfilt_sel_val = 0;
char *internal1_text = "Internal1";
char *internal2_text = "Internal2";
char *internal3_text = "Internal3";
+ enum wcd9xxx_notify_event e_post_off, e_pre_on, e_post_on;
pr_debug("%s %d\n", __func__, event);
switch (w->reg) {
case TAIKO_A_MICB_1_CTL:
micb_int_reg = TAIKO_A_MICB_1_INT_RBIAS;
- cfilt_sel_val = taiko->pdata->micbias.bias1_cfilt_sel;
- micb_line = TAIKO_MICBIAS1;
+ cfilt_sel_val = taiko->resmgr.pdata->micbias.bias1_cfilt_sel;
+ e_pre_on = WCD9XXX_EVENT_PRE_MICBIAS_1_ON;
+ e_post_on = WCD9XXX_EVENT_POST_MICBIAS_1_ON;
+ e_post_off = WCD9XXX_EVENT_POST_MICBIAS_1_OFF;
break;
case TAIKO_A_MICB_2_CTL:
micb_int_reg = TAIKO_A_MICB_2_INT_RBIAS;
- cfilt_sel_val = taiko->pdata->micbias.bias2_cfilt_sel;
- micb_line = TAIKO_MICBIAS2;
+ cfilt_sel_val = taiko->resmgr.pdata->micbias.bias2_cfilt_sel;
+ e_pre_on = WCD9XXX_EVENT_PRE_MICBIAS_2_ON;
+ e_post_on = WCD9XXX_EVENT_POST_MICBIAS_2_ON;
+ e_post_off = WCD9XXX_EVENT_POST_MICBIAS_2_OFF;
break;
case TAIKO_A_MICB_3_CTL:
micb_int_reg = TAIKO_A_MICB_3_INT_RBIAS;
- cfilt_sel_val = taiko->pdata->micbias.bias3_cfilt_sel;
- micb_line = TAIKO_MICBIAS3;
+ cfilt_sel_val = taiko->resmgr.pdata->micbias.bias3_cfilt_sel;
+ e_pre_on = WCD9XXX_EVENT_PRE_MICBIAS_3_ON;
+ e_post_on = WCD9XXX_EVENT_POST_MICBIAS_3_ON;
+ e_post_off = WCD9XXX_EVENT_POST_MICBIAS_3_OFF;
break;
case TAIKO_A_MICB_4_CTL:
- micb_int_reg = taiko->reg_addr.micb_4_int_rbias;
- cfilt_sel_val = taiko->pdata->micbias.bias4_cfilt_sel;
- micb_line = TAIKO_MICBIAS4;
+ micb_int_reg = taiko->resmgr.reg_addr->micb_4_int_rbias;
+ cfilt_sel_val = taiko->resmgr.pdata->micbias.bias4_cfilt_sel;
+ e_pre_on = WCD9XXX_EVENT_PRE_MICBIAS_4_ON;
+ e_post_on = WCD9XXX_EVENT_POST_MICBIAS_4_ON;
+ e_post_off = WCD9XXX_EVENT_POST_MICBIAS_4_OFF;
break;
default:
pr_err("%s: Error, invalid micbias register\n", __func__);
@@ -2749,15 +2081,12 @@
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- /* Decide whether to switch the micbias for MBHC */
- if (w->reg == taiko->mbhc_bias_regs.ctl_reg) {
- TAIKO_ACQUIRE_LOCK(taiko->codec_resource_lock);
- taiko_codec_switch_micbias(codec, 0);
- TAIKO_RELEASE_LOCK(taiko->codec_resource_lock);
- }
+ /* Let MBHC module know so micbias switch to be off */
+ wcd9xxx_resmgr_notifier_call(&taiko->resmgr, e_pre_on);
snd_soc_update_bits(codec, w->reg, 0x0E, 0x0A);
- taiko_codec_update_cfilt_usage(codec, cfilt_sel_val, 1);
+ /* Get cfilt */
+ wcd9xxx_resmgr_cfilt_get(&taiko->resmgr, cfilt_sel_val);
if (strnstr(w->name, internal1_text, 30))
snd_soc_update_bits(codec, micb_int_reg, 0xE0, 0xE0);
@@ -2768,25 +2097,13 @@
break;
case SND_SOC_DAPM_POST_PMU:
-
usleep_range(20000, 20000);
-
- if (taiko->mbhc_polling_active &&
- taiko->mbhc_cfg.micbias == micb_line) {
- TAIKO_ACQUIRE_LOCK(taiko->codec_resource_lock);
- taiko_codec_pause_hs_polling(codec);
- taiko_codec_start_hs_polling(codec);
- TAIKO_RELEASE_LOCK(taiko->codec_resource_lock);
- }
+ /* Let MBHC module know so micbias is on */
+ wcd9xxx_resmgr_notifier_call(&taiko->resmgr, e_post_on);
break;
-
case SND_SOC_DAPM_POST_PMD:
- if ((w->reg == taiko->mbhc_bias_regs.ctl_reg) &&
- taiko_is_hph_pa_on(codec)) {
- TAIKO_ACQUIRE_LOCK(taiko->codec_resource_lock);
- taiko_codec_switch_micbias(codec, 1);
- TAIKO_RELEASE_LOCK(taiko->codec_resource_lock);
- }
+ /* Let MBHC module know so micbias switch to be off */
+ wcd9xxx_resmgr_notifier_call(&taiko->resmgr, e_post_off);
if (strnstr(w->name, internal1_text, 30))
snd_soc_update_bits(codec, micb_int_reg, 0x80, 0x00);
@@ -2795,7 +2112,8 @@
else if (strnstr(w->name, internal3_text, 30))
snd_soc_update_bits(codec, micb_int_reg, 0x2, 0x0);
- taiko_codec_update_cfilt_usage(codec, cfilt_sel_val, 0);
+ /* Put cfilt */
+ wcd9xxx_resmgr_cfilt_put(&taiko->resmgr, cfilt_sel_val);
break;
}
@@ -2997,15 +2315,16 @@
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = w->codec;
+ struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
pr_debug("%s %d\n", __func__, event);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- taiko_enable_rx_bias(codec, 1);
+ wcd9xxx_resmgr_enable_rx_bias(&taiko->resmgr, 1);
break;
case SND_SOC_DAPM_POST_PMD:
- taiko_enable_rx_bias(codec, 0);
+ wcd9xxx_resmgr_enable_rx_bias(&taiko->resmgr, 0);
break;
}
return 0;
@@ -3028,83 +2347,32 @@
return 0;
}
-static void taiko_snd_soc_jack_report(struct taiko_priv *taiko,
- struct snd_soc_jack *jack, int status,
- int mask)
-{
- /* XXX: wake_lock_timeout()? */
- snd_soc_jack_report_no_dapm(jack, status, mask);
-}
-
-static void hphocp_off_report(struct taiko_priv *taiko,
- u32 jack_status, int irq)
-{
- struct snd_soc_codec *codec;
- if (!taiko) {
- pr_err("%s: Bad taiko private data\n", __func__);
- return;
- }
-
- pr_debug("%s: clear ocp status %x\n", __func__, jack_status);
- codec = taiko->codec;
- if (taiko->hph_status & jack_status) {
- taiko->hph_status &= ~jack_status;
- if (taiko->mbhc_cfg.headset_jack)
- taiko_snd_soc_jack_report(taiko,
- taiko->mbhc_cfg.headset_jack,
- taiko->hph_status,
- TAIKO_JACK_MASK);
- snd_soc_update_bits(codec, TAIKO_A_RX_HPH_OCP_CTL, 0x10, 0x00);
- snd_soc_update_bits(codec, TAIKO_A_RX_HPH_OCP_CTL, 0x10, 0x10);
- /* reset retry counter as PA is turned off signifying
- * start of new OCP detection session
- */
- if (WCD9XXX_IRQ_HPH_PA_OCPL_FAULT)
- taiko->hphlocp_cnt = 0;
- else
- taiko->hphrocp_cnt = 0;
- wcd9xxx_enable_irq(codec->control_data, irq);
- }
-}
-
-static void hphlocp_off_report(struct work_struct *work)
-{
- struct taiko_priv *taiko = container_of(work, struct taiko_priv,
- hphlocp_work);
- hphocp_off_report(taiko, SND_JACK_OC_HPHL,
- WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
-}
-
-static void hphrocp_off_report(struct work_struct *work)
-{
- struct taiko_priv *taiko = container_of(work, struct taiko_priv,
- hphrocp_work);
- hphocp_off_report(taiko, SND_JACK_OC_HPHR,
- WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
-}
-
static int taiko_hph_pa_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
+ struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = w->codec;
struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
- u8 mbhc_micb_ctl_val;
+ enum wcd9xxx_notify_event e_pre_on, e_post_off;
+
pr_debug("%s: %s event = %d\n", __func__, w->name, event);
+ if (w->shift == 5) {
+ e_pre_on = WCD9XXX_EVENT_PRE_HPHR_PA_ON;
+ e_post_off = WCD9XXX_EVENT_POST_HPHR_PA_OFF;
+ } else if (w->shift == 4) {
+ e_pre_on = WCD9XXX_EVENT_PRE_HPHL_PA_ON;
+ e_post_off = WCD9XXX_EVENT_POST_HPHL_PA_OFF;
+ } else {
+ pr_err("%s: Invalid w->shift %d\n", __func__, w->shift);
+ return -EINVAL;
+ }
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- mbhc_micb_ctl_val = snd_soc_read(codec,
- taiko->mbhc_bias_regs.ctl_reg);
-
- if (!(mbhc_micb_ctl_val & 0x80)) {
- TAIKO_ACQUIRE_LOCK(taiko->codec_resource_lock);
- taiko_codec_switch_micbias(codec, 1);
- TAIKO_RELEASE_LOCK(taiko->codec_resource_lock);
- }
+ /* Let MBHC module know PA is turning on */
+ wcd9xxx_resmgr_notifier_call(&taiko->resmgr, e_pre_on);
break;
case SND_SOC_DAPM_POST_PMU:
-
usleep_range(10000, 10000);
snd_soc_update_bits(codec, TAIKO_A_BUCK_MODE_5, 0x02, 0x00);
@@ -3113,100 +2381,26 @@
snd_soc_update_bits(codec, TAIKO_A_BUCK_MODE_3, 0x08, 0x00);
usleep_range(10, 10);
-
break;
case SND_SOC_DAPM_POST_PMD:
- /* schedule work is required because at the time HPH PA DAPM
+ /* Let MBHC module know PA turned off */
+ wcd9xxx_resmgr_notifier_call(&taiko->resmgr, e_post_off);
+
+ /*
+ * schedule work is required because at the time HPH PA DAPM
* event callback is called by DAPM framework, CODEC dapm mutex
* would have been locked while snd_soc_jack_report also
* attempts to acquire same lock.
*/
- if (w->shift == 5) {
- clear_bit(TAIKO_HPHL_PA_OFF_ACK,
- &taiko->hph_pa_dac_state);
- clear_bit(TAIKO_HPHL_DAC_OFF_ACK,
- &taiko->hph_pa_dac_state);
- if (taiko->hph_status & SND_JACK_OC_HPHL)
- schedule_work(&taiko->hphlocp_work);
- } else if (w->shift == 4) {
- clear_bit(TAIKO_HPHR_PA_OFF_ACK,
- &taiko->hph_pa_dac_state);
- clear_bit(TAIKO_HPHR_DAC_OFF_ACK,
- &taiko->hph_pa_dac_state);
- if (taiko->hph_status & SND_JACK_OC_HPHR)
- schedule_work(&taiko->hphrocp_work);
- }
-
- TAIKO_ACQUIRE_LOCK(taiko->codec_resource_lock);
- taiko_codec_switch_micbias(codec, 0);
- TAIKO_RELEASE_LOCK(taiko->codec_resource_lock);
-
pr_debug("%s: sleep 10 ms after %s PA disable.\n", __func__,
- w->name);
+ w->name);
usleep_range(10000, 10000);
break;
}
return 0;
}
-static void taiko_get_mbhc_micbias_regs(struct snd_soc_codec *codec,
- struct mbhc_micbias_regs *micbias_regs)
-{
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
- unsigned int cfilt;
-
- switch (taiko->mbhc_cfg.micbias) {
- case TAIKO_MICBIAS1:
- cfilt = taiko->pdata->micbias.bias1_cfilt_sel;
- micbias_regs->mbhc_reg = TAIKO_A_MICB_1_MBHC;
- micbias_regs->int_rbias = TAIKO_A_MICB_1_INT_RBIAS;
- micbias_regs->ctl_reg = TAIKO_A_MICB_1_CTL;
- break;
- case TAIKO_MICBIAS2:
- cfilt = taiko->pdata->micbias.bias2_cfilt_sel;
- micbias_regs->mbhc_reg = TAIKO_A_MICB_2_MBHC;
- micbias_regs->int_rbias = TAIKO_A_MICB_2_INT_RBIAS;
- micbias_regs->ctl_reg = TAIKO_A_MICB_2_CTL;
- break;
- case TAIKO_MICBIAS3:
- cfilt = taiko->pdata->micbias.bias3_cfilt_sel;
- micbias_regs->mbhc_reg = TAIKO_A_MICB_3_MBHC;
- micbias_regs->int_rbias = TAIKO_A_MICB_3_INT_RBIAS;
- micbias_regs->ctl_reg = TAIKO_A_MICB_3_CTL;
- break;
- case TAIKO_MICBIAS4:
- cfilt = taiko->pdata->micbias.bias4_cfilt_sel;
- micbias_regs->mbhc_reg = taiko->reg_addr.micb_4_mbhc;
- micbias_regs->int_rbias = taiko->reg_addr.micb_4_int_rbias;
- micbias_regs->ctl_reg = taiko->reg_addr.micb_4_ctl;
- break;
- default:
- /* Should never reach here */
- pr_err("%s: Invalid MIC BIAS for MBHC\n", __func__);
- return;
- }
-
- micbias_regs->cfilt_sel = cfilt;
-
- switch (cfilt) {
- case TAIKO_CFILT1_SEL:
- micbias_regs->cfilt_val = TAIKO_A_MICB_CFILT_1_VAL;
- micbias_regs->cfilt_ctl = TAIKO_A_MICB_CFILT_1_CTL;
- taiko->mbhc_data.micb_mv = taiko->pdata->micbias.cfilt1_mv;
- break;
- case TAIKO_CFILT2_SEL:
- micbias_regs->cfilt_val = TAIKO_A_MICB_CFILT_2_VAL;
- micbias_regs->cfilt_ctl = TAIKO_A_MICB_CFILT_2_CTL;
- taiko->mbhc_data.micb_mv = taiko->pdata->micbias.cfilt2_mv;
- break;
- case TAIKO_CFILT3_SEL:
- micbias_regs->cfilt_val = TAIKO_A_MICB_CFILT_3_VAL;
- micbias_regs->cfilt_ctl = TAIKO_A_MICB_CFILT_3_CTL;
- taiko->mbhc_data.micb_mv = taiko->pdata->micbias.cfilt3_mv;
- break;
- }
-}
static const struct snd_soc_dapm_widget taiko_dapm_i2s_widgets[] = {
SND_SOC_DAPM_SUPPLY("RX_I2S_CLK", TAIKO_A_CDC_CLK_RX_I2S_CTL,
4, 0, NULL, 0),
@@ -3790,6 +2984,9 @@
if (reg == TAIKO_A_RX_HPH_L_STATUS || reg == TAIKO_A_RX_HPH_R_STATUS)
return 1;
+ if (reg == TAIKO_A_MBHC_INSERT_DET_STATUS)
+ return 1;
+
return 0;
}
@@ -3838,79 +3035,6 @@
return val;
}
-static s16 taiko_get_current_v_ins(struct taiko_priv *taiko, bool hu)
-{
- s16 v_ins;
- if ((taiko->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) &&
- taiko->mbhc_micbias_switched)
- v_ins = hu ? (s16)taiko->mbhc_data.adj_v_ins_hu :
- (s16)taiko->mbhc_data.adj_v_ins_h;
- else
- v_ins = hu ? (s16)taiko->mbhc_data.v_ins_hu :
- (s16)taiko->mbhc_data.v_ins_h;
- return v_ins;
-}
-
-static s16 taiko_get_current_v_hs_max(struct taiko_priv *taiko)
-{
- s16 v_hs_max;
- struct taiko_mbhc_plug_type_cfg *plug_type;
-
- plug_type = TAIKO_MBHC_CAL_PLUG_TYPE_PTR(taiko->mbhc_cfg.calibration);
- if ((taiko->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) &&
- taiko->mbhc_micbias_switched)
- v_hs_max = taiko->mbhc_data.adj_v_hs_max;
- else
- v_hs_max = plug_type->v_hs_max;
- return v_hs_max;
-}
-
-static void taiko_codec_calibrate_hs_polling(struct snd_soc_codec *codec)
-{
- u8 *n_ready, *n_cic;
- struct taiko_mbhc_btn_detect_cfg *btn_det;
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
- const s16 v_ins_hu = taiko_get_current_v_ins(taiko, true);
-
- btn_det = TAIKO_MBHC_CAL_BTN_DET_PTR(taiko->mbhc_cfg.calibration);
-
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B1_CTL,
- v_ins_hu & 0xFF);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B2_CTL,
- (v_ins_hu >> 8) & 0xFF);
-
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B3_CTL,
- taiko->mbhc_data.v_b1_hu & 0xFF);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B4_CTL,
- (taiko->mbhc_data.v_b1_hu >> 8) & 0xFF);
-
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B5_CTL,
- taiko->mbhc_data.v_b1_h & 0xFF);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B6_CTL,
- (taiko->mbhc_data.v_b1_h >> 8) & 0xFF);
-
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B9_CTL,
- taiko->mbhc_data.v_brh & 0xFF);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B10_CTL,
- (taiko->mbhc_data.v_brh >> 8) & 0xFF);
-
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B11_CTL,
- taiko->mbhc_data.v_brl & 0xFF);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_VOLT_B12_CTL,
- (taiko->mbhc_data.v_brl >> 8) & 0xFF);
-
- n_ready = taiko_mbhc_cal_btn_det_mp(btn_det, TAIKO_BTN_DET_N_READY);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_TIMER_B1_CTL,
- n_ready[taiko_codec_mclk_index(taiko)]);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_TIMER_B2_CTL,
- taiko->mbhc_data.npoll);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_TIMER_B3_CTL,
- taiko->mbhc_data.nbounce_wait);
- n_cic = taiko_mbhc_cal_btn_det_mp(btn_det, TAIKO_BTN_DET_N_CIC);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_TIMER_B6_CTL,
- n_cic[taiko_codec_mclk_index(taiko)]);
-}
-
static int taiko_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
@@ -3945,54 +3069,20 @@
pr_debug("%s: mclk_enable = %u, dapm = %d\n", __func__, mclk_enable,
dapm);
- if (dapm)
- TAIKO_ACQUIRE_LOCK(taiko->codec_resource_lock);
+
+ WCD9XXX_BCL_LOCK(&taiko->resmgr);
if (mclk_enable) {
- taiko->mclk_enabled = true;
-
- if (taiko->mbhc_polling_active) {
- taiko_codec_pause_hs_polling(codec);
- taiko_codec_disable_clock_block(codec);
- taiko_codec_enable_bandgap(codec,
- TAIKO_BANDGAP_AUDIO_MODE);
- taiko_codec_enable_clock_block(codec, 0);
- taiko_codec_calibrate_hs_polling(codec);
- taiko_codec_start_hs_polling(codec);
- } else {
- taiko_codec_disable_clock_block(codec);
- taiko_codec_enable_bandgap(codec,
- TAIKO_BANDGAP_AUDIO_MODE);
- taiko_codec_enable_clock_block(codec, 0);
- }
+ wcd9xxx_resmgr_get_bandgap(&taiko->resmgr,
+ WCD9XXX_BANDGAP_AUDIO_MODE);
+ wcd9xxx_resmgr_get_clk_block(&taiko->resmgr, WCD9XXX_CLK_MCLK);
} else {
-
- if (!taiko->mclk_enabled) {
- if (dapm)
- TAIKO_RELEASE_LOCK(taiko->codec_resource_lock);
- pr_err("Error, MCLK already diabled\n");
- return -EINVAL;
- }
- taiko->mclk_enabled = false;
-
- if (taiko->mbhc_polling_active) {
- taiko_codec_pause_hs_polling(codec);
- taiko_codec_disable_clock_block(codec);
- taiko_codec_enable_bandgap(codec,
- TAIKO_BANDGAP_MBHC_MODE);
- taiko_enable_rx_bias(codec, 1);
- taiko_codec_enable_clock_block(codec, 1);
- taiko_codec_calibrate_hs_polling(codec);
- taiko_codec_start_hs_polling(codec);
- snd_soc_update_bits(codec, TAIKO_A_CLK_BUFF_EN1,
- 0x05, 0x01);
- } else {
- taiko_codec_disable_clock_block(codec);
- taiko_codec_enable_bandgap(codec,
- TAIKO_BANDGAP_OFF);
- }
+ /* Put clock and BG */
+ wcd9xxx_resmgr_put_clk_block(&taiko->resmgr, WCD9XXX_CLK_MCLK);
+ wcd9xxx_resmgr_put_bandgap(&taiko->resmgr,
+ WCD9XXX_BANDGAP_AUDIO_MODE);
}
- if (dapm)
- TAIKO_RELEASE_LOCK(taiko->codec_resource_lock);
+ WCD9XXX_BCL_UNLOCK(&taiko->resmgr);
+
return 0;
}
@@ -5064,2201 +4154,6 @@
};
-static short taiko_codec_read_sta_result(struct snd_soc_codec *codec)
-{
- u8 bias_msb, bias_lsb;
- short bias_value;
-
- bias_msb = snd_soc_read(codec, TAIKO_A_CDC_MBHC_B3_STATUS);
- bias_lsb = snd_soc_read(codec, TAIKO_A_CDC_MBHC_B2_STATUS);
- bias_value = (bias_msb << 8) | bias_lsb;
- return bias_value;
-}
-
-static short taiko_codec_read_dce_result(struct snd_soc_codec *codec)
-{
- u8 bias_msb, bias_lsb;
- short bias_value;
-
- bias_msb = snd_soc_read(codec, TAIKO_A_CDC_MBHC_B5_STATUS);
- bias_lsb = snd_soc_read(codec, TAIKO_A_CDC_MBHC_B4_STATUS);
- bias_value = (bias_msb << 8) | bias_lsb;
- return bias_value;
-}
-
-static void taiko_turn_onoff_rel_detection(struct snd_soc_codec *codec, bool on)
-{
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_B1_CTL, 0x02, on << 1);
-}
-
-static short __taiko_codec_sta_dce(struct snd_soc_codec *codec, int dce,
- bool override_bypass, bool noreldetection)
-{
- short bias_value;
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-
- wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_POTENTIAL);
- if (noreldetection)
- taiko_turn_onoff_rel_detection(codec, false);
-
- /* Turn on the override */
- if (!override_bypass)
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_B1_CTL, 0x4, 0x4);
- if (dce) {
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_EN_CTL, 0x4);
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x8, 0x0);
- usleep_range(taiko->mbhc_data.t_sta_dce,
- taiko->mbhc_data.t_sta_dce);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_EN_CTL, 0x4);
- usleep_range(taiko->mbhc_data.t_dce,
- taiko->mbhc_data.t_dce);
- bias_value = taiko_codec_read_dce_result(codec);
- } else {
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_EN_CTL, 0x2);
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x8, 0x0);
- usleep_range(taiko->mbhc_data.t_sta_dce,
- taiko->mbhc_data.t_sta_dce);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_EN_CTL, 0x2);
- usleep_range(taiko->mbhc_data.t_sta,
- taiko->mbhc_data.t_sta);
- bias_value = taiko_codec_read_sta_result(codec);
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_EN_CTL, 0x0);
- }
- /* Turn off the override after measuring mic voltage */
- if (!override_bypass)
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_B1_CTL, 0x04, 0x00);
-
- if (noreldetection)
- taiko_turn_onoff_rel_detection(codec, true);
- wcd9xxx_enable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_POTENTIAL);
-
- return bias_value;
-}
-
-static short taiko_codec_sta_dce(struct snd_soc_codec *codec, int dce,
- bool norel)
-{
- return __taiko_codec_sta_dce(codec, dce, false, norel);
-}
-
-/* called only from interrupt which is under codec_resource_lock acquisition */
-static short taiko_codec_setup_hs_polling(struct snd_soc_codec *codec)
-{
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
- short bias_value;
- u8 cfilt_mode;
-
- pr_debug("%s: enter, mclk_enabled %d\n", __func__, taiko->mclk_enabled);
- if (!taiko->mbhc_cfg.calibration) {
- pr_err("Error, no taiko calibration\n");
- return -ENODEV;
- }
-
- if (!taiko->mclk_enabled) {
- taiko_codec_disable_clock_block(codec);
- taiko_codec_enable_bandgap(codec, TAIKO_BANDGAP_MBHC_MODE);
- taiko_enable_rx_bias(codec, 1);
- taiko_codec_enable_clock_block(codec, 1);
- }
-
- snd_soc_update_bits(codec, TAIKO_A_CLK_BUFF_EN1, 0x05, 0x01);
-
- /* Make sure CFILT is in fast mode, save current mode */
- cfilt_mode = snd_soc_read(codec, taiko->mbhc_bias_regs.cfilt_ctl);
- snd_soc_update_bits(codec, taiko->mbhc_bias_regs.cfilt_ctl, 0x70, 0x00);
-
- snd_soc_update_bits(codec, taiko->mbhc_bias_regs.ctl_reg, 0x1F, 0x16);
-
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x2, 0x2);
- snd_soc_write(codec, TAIKO_A_MBHC_SCALING_MUX_1, 0x84);
-
- snd_soc_update_bits(codec, TAIKO_A_TX_7_MBHC_EN, 0x80, 0x80);
- snd_soc_update_bits(codec, TAIKO_A_TX_7_MBHC_EN, 0x1F, 0x1C);
- snd_soc_update_bits(codec, TAIKO_A_TX_7_MBHC_TEST_CTL, 0x40, 0x40);
-
- snd_soc_update_bits(codec, TAIKO_A_TX_7_MBHC_EN, 0x80, 0x00);
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x8, 0x00);
-
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_B1_CTL, 0x2, 0x2);
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
-
- taiko_codec_calibrate_hs_polling(codec);
-
- /* don't flip override */
- bias_value = __taiko_codec_sta_dce(codec, 1, true, true);
- snd_soc_update_bits(codec, taiko->mbhc_bias_regs.cfilt_ctl, 0x40,
- cfilt_mode);
- snd_soc_update_bits(codec, TAIKO_A_MBHC_HPH, 0x13, 0x00);
-
- return bias_value;
-}
-
-static int taiko_cancel_btn_work(struct taiko_priv *taiko)
-{
- int r = 0;
- struct wcd9xxx *core = dev_get_drvdata(taiko->codec->dev->parent);
-
- if (cancel_delayed_work_sync(&taiko->mbhc_btn_dwork)) {
- /* if scheduled mbhc_btn_dwork is canceled from here,
- * we have to unlock from here instead btn_work */
- wcd9xxx_unlock_sleep(core);
- r = 1;
- }
- return r;
-}
-
-/* called under codec_resource_lock acquisition */
-void taiko_set_and_turnoff_hph_padac(struct snd_soc_codec *codec)
-{
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
- u8 wg_time;
-
- wg_time = snd_soc_read(codec, TAIKO_A_RX_HPH_CNP_WG_TIME) ;
- wg_time += 1;
-
- /* If headphone PA is on, check if userspace receives
- * removal event to sync-up PA's state */
- if (taiko_is_hph_pa_on(codec)) {
- pr_debug("%s PA is on, setting PA_OFF_ACK\n", __func__);
- set_bit(TAIKO_HPHL_PA_OFF_ACK, &taiko->hph_pa_dac_state);
- set_bit(TAIKO_HPHR_PA_OFF_ACK, &taiko->hph_pa_dac_state);
- } else {
- pr_debug("%s PA is off\n", __func__);
- }
-
- if (taiko_is_hph_dac_on(codec, 1))
- set_bit(TAIKO_HPHL_DAC_OFF_ACK, &taiko->hph_pa_dac_state);
- if (taiko_is_hph_dac_on(codec, 0))
- set_bit(TAIKO_HPHR_DAC_OFF_ACK, &taiko->hph_pa_dac_state);
-
- snd_soc_update_bits(codec, TAIKO_A_RX_HPH_CNP_EN, 0x30, 0x00);
- snd_soc_update_bits(codec, TAIKO_A_RX_HPH_L_DAC_CTL,
- 0xC0, 0x00);
- snd_soc_update_bits(codec, TAIKO_A_RX_HPH_R_DAC_CTL,
- 0xC0, 0x00);
- usleep_range(wg_time * 1000, wg_time * 1000);
-}
-
-static void taiko_clr_and_turnon_hph_padac(struct taiko_priv *taiko)
-{
- bool pa_turned_on = false;
- struct snd_soc_codec *codec = taiko->codec;
- u8 wg_time;
-
- wg_time = snd_soc_read(codec, TAIKO_A_RX_HPH_CNP_WG_TIME) ;
- wg_time += 1;
-
- if (test_and_clear_bit(TAIKO_HPHR_DAC_OFF_ACK,
- &taiko->hph_pa_dac_state)) {
- pr_debug("%s: HPHR clear flag and enable DAC\n", __func__);
- snd_soc_update_bits(taiko->codec, TAIKO_A_RX_HPH_R_DAC_CTL,
- 0xC0, 0xC0);
- }
- if (test_and_clear_bit(TAIKO_HPHL_DAC_OFF_ACK,
- &taiko->hph_pa_dac_state)) {
- pr_debug("%s: HPHL clear flag and enable DAC\n", __func__);
- snd_soc_update_bits(taiko->codec, TAIKO_A_RX_HPH_L_DAC_CTL,
- 0xC0, 0xC0);
- }
-
- if (test_and_clear_bit(TAIKO_HPHR_PA_OFF_ACK,
- &taiko->hph_pa_dac_state)) {
- pr_debug("%s: HPHR clear flag and enable PA\n", __func__);
- snd_soc_update_bits(taiko->codec, TAIKO_A_RX_HPH_CNP_EN, 0x10,
- 1 << 4);
- pa_turned_on = true;
- }
- if (test_and_clear_bit(TAIKO_HPHL_PA_OFF_ACK,
- &taiko->hph_pa_dac_state)) {
- pr_debug("%s: HPHL clear flag and enable PA\n", __func__);
- snd_soc_update_bits(taiko->codec, TAIKO_A_RX_HPH_CNP_EN, 0x20,
- 1 << 5);
- pa_turned_on = true;
- }
-
- if (pa_turned_on) {
- pr_debug("%s: PA was turned off by MBHC and not by DAPM\n",
- __func__);
- usleep_range(wg_time * 1000, wg_time * 1000);
- }
-}
-
-/* called under codec_resource_lock acquisition */
-static void taiko_codec_report_plug(struct snd_soc_codec *codec, int insertion,
- enum snd_jack_types jack_type)
-{
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-
- if (!insertion) {
- /* Report removal */
- taiko->hph_status &= ~jack_type;
- if (taiko->mbhc_cfg.headset_jack) {
- /* cancel possibly scheduled btn work and
- * report release if we reported button press */
- if (taiko_cancel_btn_work(taiko)) {
- pr_debug("%s: button press is canceled\n",
- __func__);
- } else if (taiko->buttons_pressed) {
- pr_debug("%s: release of button press%d\n",
- __func__, jack_type);
- taiko_snd_soc_jack_report(taiko,
- taiko->mbhc_cfg.button_jack, 0,
- taiko->buttons_pressed);
- taiko->buttons_pressed &=
- ~TAIKO_JACK_BUTTON_MASK;
- }
- pr_debug("%s: Reporting removal %d(%x)\n", __func__,
- jack_type, taiko->hph_status);
- taiko_snd_soc_jack_report(taiko,
- taiko->mbhc_cfg.headset_jack,
- taiko->hph_status,
- TAIKO_JACK_MASK);
- }
- taiko_set_and_turnoff_hph_padac(codec);
- hphocp_off_report(taiko, SND_JACK_OC_HPHR,
- WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
- hphocp_off_report(taiko, SND_JACK_OC_HPHL,
- WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
- taiko->current_plug = PLUG_TYPE_NONE;
- taiko->mbhc_polling_active = false;
- } else {
- /* Report insertion */
- taiko->hph_status |= jack_type;
-
- if (jack_type == SND_JACK_HEADPHONE)
- taiko->current_plug = PLUG_TYPE_HEADPHONE;
- else if (jack_type == SND_JACK_UNSUPPORTED)
- taiko->current_plug = PLUG_TYPE_GND_MIC_SWAP;
- else if (jack_type == SND_JACK_HEADSET) {
- taiko->mbhc_polling_active = true;
- taiko->current_plug = PLUG_TYPE_HEADSET;
- }
- if (taiko->mbhc_cfg.headset_jack) {
- pr_debug("%s: Reporting insertion %d(%x)\n", __func__,
- jack_type, taiko->hph_status);
- taiko_snd_soc_jack_report(taiko,
- taiko->mbhc_cfg.headset_jack,
- taiko->hph_status,
- TAIKO_JACK_MASK);
- }
- taiko_clr_and_turnon_hph_padac(taiko);
- }
-}
-
-static int taiko_codec_enable_hs_detect(struct snd_soc_codec *codec,
- int insertion, int trigger,
- bool padac_off)
-{
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
- int central_bias_enabled = 0;
- const struct taiko_mbhc_general_cfg *generic =
- TAIKO_MBHC_CAL_GENERAL_PTR(taiko->mbhc_cfg.calibration);
- const struct taiko_mbhc_plug_detect_cfg *plug_det =
- TAIKO_MBHC_CAL_PLUG_DET_PTR(taiko->mbhc_cfg.calibration);
-
- if (!taiko->mbhc_cfg.calibration) {
- pr_err("Error, no taiko calibration\n");
- return -EINVAL;
- }
-
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_INT_CTL, 0x1, 0);
-
- /* Make sure mic bias and Mic line schmitt trigger
- * are turned OFF
- */
- snd_soc_update_bits(codec, taiko->mbhc_bias_regs.ctl_reg, 0x01, 0x01);
- snd_soc_update_bits(codec, taiko->mbhc_bias_regs.mbhc_reg, 0x90, 0x00);
-
- if (insertion) {
- taiko_codec_switch_micbias(codec, 0);
-
- /* DAPM can manipulate PA/DAC bits concurrently */
- if (padac_off == true)
- taiko_set_and_turnoff_hph_padac(codec);
-
- if (trigger & MBHC_USE_HPHL_TRIGGER) {
- /* Enable HPH Schmitt Trigger */
- snd_soc_update_bits(codec, TAIKO_A_MBHC_HPH, 0x11,
- 0x11);
- snd_soc_update_bits(codec, TAIKO_A_MBHC_HPH, 0x0C,
- plug_det->hph_current << 2);
- snd_soc_update_bits(codec, TAIKO_A_MBHC_HPH, 0x02,
- 0x02);
- }
- if (trigger & MBHC_USE_MB_TRIGGER) {
- /* enable the mic line schmitt trigger */
- snd_soc_update_bits(codec,
- taiko->mbhc_bias_regs.mbhc_reg,
- 0x60, plug_det->mic_current << 5);
- snd_soc_update_bits(codec,
- taiko->mbhc_bias_regs.mbhc_reg,
- 0x80, 0x80);
- usleep_range(plug_det->t_mic_pid, plug_det->t_mic_pid);
- snd_soc_update_bits(codec,
- taiko->mbhc_bias_regs.ctl_reg, 0x01,
- 0x00);
- snd_soc_update_bits(codec,
- taiko->mbhc_bias_regs.mbhc_reg,
- 0x10, 0x10);
- }
-
- /* setup for insetion detection */
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_INT_CTL, 0x2, 0);
- } else {
- pr_debug("setup for removal detection\n");
- /* Make sure the HPH schmitt trigger is OFF */
- snd_soc_update_bits(codec, TAIKO_A_MBHC_HPH, 0x12, 0x00);
-
- /* enable the mic line schmitt trigger */
- snd_soc_update_bits(codec, taiko->mbhc_bias_regs.ctl_reg,
- 0x01, 0x00);
- snd_soc_update_bits(codec, taiko->mbhc_bias_regs.mbhc_reg, 0x60,
- plug_det->mic_current << 5);
- snd_soc_update_bits(codec, taiko->mbhc_bias_regs.mbhc_reg,
- 0x80, 0x80);
- usleep_range(plug_det->t_mic_pid, plug_det->t_mic_pid);
- snd_soc_update_bits(codec, taiko->mbhc_bias_regs.mbhc_reg,
- 0x10, 0x10);
-
- /* Setup for low power removal detection */
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_INT_CTL, 0x2, 0x2);
- }
-
- if (snd_soc_read(codec, TAIKO_A_CDC_MBHC_B1_CTL) & 0x4) {
- /* called called by interrupt */
- if (!(taiko->clock_active)) {
- taiko_codec_enable_config_mode(codec, 1);
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_B1_CTL,
- 0x06, 0);
- usleep_range(generic->t_shutdown_plug_rem,
- generic->t_shutdown_plug_rem);
- taiko_codec_enable_config_mode(codec, 0);
- } else
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_B1_CTL,
- 0x06, 0);
- }
-
- snd_soc_update_bits(codec, taiko->mbhc_bias_regs.int_rbias, 0x80, 0);
-
- /* If central bandgap disabled */
- if (!(snd_soc_read(codec, TAIKO_A_PIN_CTL_OE1) & 1)) {
- snd_soc_update_bits(codec, TAIKO_A_PIN_CTL_OE1, 0x3, 0x3);
- usleep_range(generic->t_bg_fast_settle,
- generic->t_bg_fast_settle);
- central_bias_enabled = 1;
- }
-
- /* If LDO_H disabled */
- if (snd_soc_read(codec, TAIKO_A_PIN_CTL_OE0) & 0x80) {
- snd_soc_update_bits(codec, TAIKO_A_PIN_CTL_OE0, 0x10, 0);
- snd_soc_update_bits(codec, TAIKO_A_PIN_CTL_OE0, 0x80, 0x80);
- usleep_range(generic->t_ldoh, generic->t_ldoh);
- snd_soc_update_bits(codec, TAIKO_A_PIN_CTL_OE0, 0x80, 0);
-
- if (central_bias_enabled)
- snd_soc_update_bits(codec, TAIKO_A_PIN_CTL_OE1, 0x1, 0);
- }
-
- snd_soc_update_bits(codec, taiko->reg_addr.micb_4_mbhc, 0x3,
- taiko->mbhc_cfg.micbias);
-
- wcd9xxx_enable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_INSERTION);
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_INT_CTL, 0x1, 0x1);
- return 0;
-}
-
-static u16 taiko_codec_v_sta_dce(struct snd_soc_codec *codec, bool dce,
- s16 vin_mv)
-{
- struct taiko_priv *taiko;
- s16 diff, zero;
- u32 mb_mv, in;
- u16 value;
-
- taiko = snd_soc_codec_get_drvdata(codec);
- mb_mv = taiko->mbhc_data.micb_mv;
-
- if (mb_mv == 0) {
- pr_err("%s: Mic Bias voltage is set to zero\n", __func__);
- return -EINVAL;
- }
-
- if (dce) {
- diff = (taiko->mbhc_data.dce_mb) - (taiko->mbhc_data.dce_z);
- zero = (taiko->mbhc_data.dce_z);
- } else {
- diff = (taiko->mbhc_data.sta_mb) - (taiko->mbhc_data.sta_z);
- zero = (taiko->mbhc_data.sta_z);
- }
- in = (u32) diff * vin_mv;
-
- value = (u16) (in / mb_mv) + zero;
- return value;
-}
-
-static s32 taiko_codec_sta_dce_v(struct snd_soc_codec *codec, s8 dce,
- u16 bias_value)
-{
- struct taiko_priv *taiko;
- s16 value, z, mb;
- s32 mv;
-
- taiko = snd_soc_codec_get_drvdata(codec);
- value = bias_value;
- if (dce) {
- z = (taiko->mbhc_data.dce_z);
- mb = (taiko->mbhc_data.dce_mb);
- mv = (value - z) * (s32)taiko->mbhc_data.micb_mv / (mb - z);
- } else {
- z = (taiko->mbhc_data.sta_z);
- mb = (taiko->mbhc_data.sta_mb);
- mv = (value - z) * (s32)taiko->mbhc_data.micb_mv / (mb - z);
- }
-
- return mv;
-}
-
-static void btn_lpress_fn(struct work_struct *work)
-{
- struct delayed_work *delayed_work;
- struct taiko_priv *taiko;
- short bias_value;
- int dce_mv, sta_mv;
- struct wcd9xxx *core;
-
- pr_debug("%s:\n", __func__);
-
- delayed_work = to_delayed_work(work);
- taiko = container_of(delayed_work, struct taiko_priv, mbhc_btn_dwork);
- core = dev_get_drvdata(taiko->codec->dev->parent);
-
- if (taiko) {
- if (taiko->mbhc_cfg.button_jack) {
- bias_value = taiko_codec_read_sta_result(taiko->codec);
- sta_mv = taiko_codec_sta_dce_v(taiko->codec, 0,
- bias_value);
- bias_value = taiko_codec_read_dce_result(taiko->codec);
- dce_mv = taiko_codec_sta_dce_v(taiko->codec, 1,
- bias_value);
- pr_debug("%s: Reporting long button press event\n",
- __func__);
- pr_debug("%s: STA: %d, DCE: %d\n", __func__, sta_mv,
- dce_mv);
- taiko_snd_soc_jack_report(taiko,
- taiko->mbhc_cfg.button_jack,
- taiko->buttons_pressed,
- taiko->buttons_pressed);
- }
- } else {
- pr_err("%s: Bad taiko private data\n", __func__);
- }
-
- pr_debug("%s: leave\n", __func__);
- wcd9xxx_unlock_sleep(core);
-}
-
-void taiko_mbhc_cal(struct snd_soc_codec *codec)
-{
- struct taiko_priv *taiko;
- struct taiko_mbhc_btn_detect_cfg *btn_det;
- u8 cfilt_mode, bg_mode;
- u8 ncic, nmeas, navg;
- u32 mclk_rate;
- u32 dce_wait, sta_wait;
- u8 *n_cic;
- void *calibration;
-
- taiko = snd_soc_codec_get_drvdata(codec);
- calibration = taiko->mbhc_cfg.calibration;
-
- wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_POTENTIAL);
- taiko_turn_onoff_rel_detection(codec, false);
-
- /* First compute the DCE / STA wait times
- * depending on tunable parameters.
- * The value is computed in microseconds
- */
- btn_det = TAIKO_MBHC_CAL_BTN_DET_PTR(calibration);
- n_cic = taiko_mbhc_cal_btn_det_mp(btn_det, TAIKO_BTN_DET_N_CIC);
- ncic = n_cic[taiko_codec_mclk_index(taiko)];
- nmeas = TAIKO_MBHC_CAL_BTN_DET_PTR(calibration)->n_meas;
- navg = TAIKO_MBHC_CAL_GENERAL_PTR(calibration)->mbhc_navg;
- mclk_rate = taiko->mbhc_cfg.mclk_rate;
- dce_wait = (1000 * 512 * ncic * (nmeas + 1)) / (mclk_rate / 1000);
- sta_wait = (1000 * 128 * (navg + 1)) / (mclk_rate / 1000);
-
- taiko->mbhc_data.t_dce = dce_wait;
- taiko->mbhc_data.t_sta = sta_wait;
-
- /* LDOH and CFILT are already configured during pdata handling.
- * Only need to make sure CFILT and bandgap are in Fast mode.
- * Need to restore defaults once calculation is done.
- */
- cfilt_mode = snd_soc_read(codec, taiko->mbhc_bias_regs.cfilt_ctl);
- snd_soc_update_bits(codec, taiko->mbhc_bias_regs.cfilt_ctl, 0x40, 0x00);
- bg_mode = snd_soc_update_bits(codec, TAIKO_A_BIAS_CENTRAL_BG_CTL, 0x02,
- 0x02);
-
- /* Micbias, CFILT, LDOH, MBHC MUX mode settings
- * to perform ADC calibration
- */
- snd_soc_update_bits(codec, taiko->mbhc_bias_regs.ctl_reg, 0x60,
- taiko->mbhc_cfg.micbias << 5);
- snd_soc_update_bits(codec, taiko->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
- snd_soc_update_bits(codec, TAIKO_A_LDO_H_MODE_1, 0x60, 0x60);
- snd_soc_write(codec, TAIKO_A_TX_7_MBHC_TEST_CTL, 0x78);
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_B1_CTL, 0x04, 0x04);
-
- /* DCE measurement for 0 volts */
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x0A);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_EN_CTL, 0x04);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x02);
- snd_soc_write(codec, TAIKO_A_MBHC_SCALING_MUX_1, 0x81);
- usleep_range(100, 100);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_EN_CTL, 0x04);
- usleep_range(taiko->mbhc_data.t_dce, taiko->mbhc_data.t_dce);
- taiko->mbhc_data.dce_z = taiko_codec_read_dce_result(codec);
-
- /* DCE measurment for MB voltage */
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x0A);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x02);
- snd_soc_write(codec, TAIKO_A_MBHC_SCALING_MUX_1, 0x82);
- usleep_range(100, 100);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_EN_CTL, 0x04);
- usleep_range(taiko->mbhc_data.t_dce, taiko->mbhc_data.t_dce);
- taiko->mbhc_data.dce_mb = taiko_codec_read_dce_result(codec);
-
- /* Sta measuremnt for 0 volts */
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x0A);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_EN_CTL, 0x02);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x02);
- snd_soc_write(codec, TAIKO_A_MBHC_SCALING_MUX_1, 0x81);
- usleep_range(100, 100);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_EN_CTL, 0x02);
- usleep_range(taiko->mbhc_data.t_sta, taiko->mbhc_data.t_sta);
- taiko->mbhc_data.sta_z = taiko_codec_read_sta_result(codec);
-
- /* STA Measurement for MB Voltage */
- snd_soc_write(codec, TAIKO_A_MBHC_SCALING_MUX_1, 0x82);
- usleep_range(100, 100);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_EN_CTL, 0x02);
- usleep_range(taiko->mbhc_data.t_sta, taiko->mbhc_data.t_sta);
- taiko->mbhc_data.sta_mb = taiko_codec_read_sta_result(codec);
-
- /* Restore default settings. */
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_B1_CTL, 0x04, 0x00);
- snd_soc_update_bits(codec, taiko->mbhc_bias_regs.cfilt_ctl, 0x40,
- cfilt_mode);
- snd_soc_update_bits(codec, TAIKO_A_BIAS_CENTRAL_BG_CTL, 0x02, bg_mode);
-
- snd_soc_write(codec, TAIKO_A_MBHC_SCALING_MUX_1, 0x84);
- usleep_range(100, 100);
-
- wcd9xxx_enable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_POTENTIAL);
- taiko_turn_onoff_rel_detection(codec, true);
-}
-
-void *taiko_mbhc_cal_btn_det_mp(const struct taiko_mbhc_btn_detect_cfg *btn_det,
- const enum taiko_mbhc_btn_det_mem mem)
-{
- void *ret = &btn_det->_v_btn_low;
-
- switch (mem) {
- case TAIKO_BTN_DET_GAIN:
- ret += sizeof(btn_det->_n_cic);
- case TAIKO_BTN_DET_N_CIC:
- ret += sizeof(btn_det->_n_ready);
- case TAIKO_BTN_DET_N_READY:
- ret += sizeof(btn_det->_v_btn_high[0]) * btn_det->num_btn;
- case TAIKO_BTN_DET_V_BTN_HIGH:
- ret += sizeof(btn_det->_v_btn_low[0]) * btn_det->num_btn;
- case TAIKO_BTN_DET_V_BTN_LOW:
- /* do nothing */
- break;
- default:
- ret = NULL;
- }
-
- return ret;
-}
-
-static s16 taiko_scale_v_micb_vddio(struct taiko_priv *taiko, int v,
- bool tovddio)
-{
- int r;
- int vddio_k, mb_k;
- vddio_k = taiko_find_k_value(taiko->pdata->micbias.ldoh_v,
- VDDIO_MICBIAS_MV);
- mb_k = taiko_find_k_value(taiko->pdata->micbias.ldoh_v,
- taiko->mbhc_data.micb_mv);
- if (tovddio)
- r = v * vddio_k / mb_k;
- else
- r = v * mb_k / vddio_k;
- return r;
-}
-
-static void taiko_mbhc_calc_thres(struct snd_soc_codec *codec)
-{
- struct taiko_priv *taiko;
- s16 btn_mv = 0, btn_delta_mv;
- struct taiko_mbhc_btn_detect_cfg *btn_det;
- struct taiko_mbhc_plug_type_cfg *plug_type;
- u16 *btn_high;
- u8 *n_ready;
- int i;
-
- taiko = snd_soc_codec_get_drvdata(codec);
- btn_det = TAIKO_MBHC_CAL_BTN_DET_PTR(taiko->mbhc_cfg.calibration);
- plug_type = TAIKO_MBHC_CAL_PLUG_TYPE_PTR(taiko->mbhc_cfg.calibration);
-
- n_ready = taiko_mbhc_cal_btn_det_mp(btn_det, TAIKO_BTN_DET_N_READY);
- if (taiko->mbhc_cfg.mclk_rate == TAIKO_MCLK_RATE_12288KHZ) {
- taiko->mbhc_data.npoll = 4;
- taiko->mbhc_data.nbounce_wait = 30;
- } else if (taiko->mbhc_cfg.mclk_rate == TAIKO_MCLK_RATE_9600KHZ) {
- taiko->mbhc_data.npoll = 7;
- taiko->mbhc_data.nbounce_wait = 23;
- }
-
- taiko->mbhc_data.t_sta_dce = ((1000 * 256) /
- (taiko->mbhc_cfg.mclk_rate / 1000) *
- n_ready[taiko_codec_mclk_index(taiko)]) +
- 10;
- taiko->mbhc_data.v_ins_hu =
- taiko_codec_v_sta_dce(codec, STA, plug_type->v_hs_max);
- taiko->mbhc_data.v_ins_h =
- taiko_codec_v_sta_dce(codec, DCE, plug_type->v_hs_max);
-
- taiko->mbhc_data.v_inval_ins_low = TAIKO_MBHC_FAKE_INSERT_LOW;
- if (taiko->mbhc_cfg.gpio)
- taiko->mbhc_data.v_inval_ins_high =
- TAIKO_MBHC_FAKE_INSERT_HIGH;
- else
- taiko->mbhc_data.v_inval_ins_high =
- TAIKO_MBHC_FAKE_INS_HIGH_NO_GPIO;
-
- if (taiko->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) {
- taiko->mbhc_data.adj_v_hs_max =
- taiko_scale_v_micb_vddio(taiko, plug_type->v_hs_max, true);
- taiko->mbhc_data.adj_v_ins_hu =
- taiko_codec_v_sta_dce(codec, STA,
- taiko->mbhc_data.adj_v_hs_max);
- taiko->mbhc_data.adj_v_ins_h =
- taiko_codec_v_sta_dce(codec, DCE,
- taiko->mbhc_data.adj_v_hs_max);
- taiko->mbhc_data.v_inval_ins_low =
- taiko_scale_v_micb_vddio(taiko,
- taiko->mbhc_data.v_inval_ins_low,
- false);
- taiko->mbhc_data.v_inval_ins_high =
- taiko_scale_v_micb_vddio(taiko,
- taiko->mbhc_data.v_inval_ins_high,
- false);
- }
-
- btn_high = taiko_mbhc_cal_btn_det_mp(btn_det, TAIKO_BTN_DET_V_BTN_HIGH);
- for (i = 0; i < btn_det->num_btn; i++)
- btn_mv = btn_high[i] > btn_mv ? btn_high[i] : btn_mv;
-
- taiko->mbhc_data.v_b1_h = taiko_codec_v_sta_dce(codec, DCE, btn_mv);
- btn_delta_mv = btn_mv + btn_det->v_btn_press_delta_sta;
- taiko->mbhc_data.v_b1_hu =
- taiko_codec_v_sta_dce(codec, STA, btn_delta_mv);
-
- btn_delta_mv = btn_mv + btn_det->v_btn_press_delta_cic;
-
- taiko->mbhc_data.v_b1_huc =
- taiko_codec_v_sta_dce(codec, DCE, btn_delta_mv);
-
- taiko->mbhc_data.v_brh = taiko->mbhc_data.v_b1_h;
- taiko->mbhc_data.v_brl = TAIKO_MBHC_BUTTON_MIN;
-
- taiko->mbhc_data.v_no_mic =
- taiko_codec_v_sta_dce(codec, STA, plug_type->v_no_mic);
-}
-
-void taiko_mbhc_init(struct snd_soc_codec *codec)
-{
- struct taiko_priv *taiko;
- struct taiko_mbhc_general_cfg *generic;
- struct taiko_mbhc_btn_detect_cfg *btn_det;
- int n;
- u8 *n_cic, *gain;
-
- taiko = snd_soc_codec_get_drvdata(codec);
- generic = TAIKO_MBHC_CAL_GENERAL_PTR(taiko->mbhc_cfg.calibration);
- btn_det = TAIKO_MBHC_CAL_BTN_DET_PTR(taiko->mbhc_cfg.calibration);
-
- for (n = 0; n < 8; n++) {
- snd_soc_update_bits(codec,
- TAIKO_A_CDC_MBHC_FIR_B1_CFG,
- 0x07, n);
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_FIR_B2_CFG,
- btn_det->c[n]);
- }
-
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_B2_CTL, 0x07,
- btn_det->nc);
-
- n_cic = taiko_mbhc_cal_btn_det_mp(btn_det, TAIKO_BTN_DET_N_CIC);
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_TIMER_B6_CTL, 0xFF,
- n_cic[taiko_codec_mclk_index(taiko)]);
-
- gain = taiko_mbhc_cal_btn_det_mp(btn_det, TAIKO_BTN_DET_GAIN);
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_B2_CTL, 0x78,
- gain[taiko_codec_mclk_index(taiko)] << 3);
-
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_TIMER_B4_CTL, 0x70,
- generic->mbhc_nsa << 4);
-
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_TIMER_B4_CTL, 0x0F,
- btn_det->n_meas);
-
- snd_soc_write(codec, TAIKO_A_CDC_MBHC_TIMER_B5_CTL, generic->mbhc_navg);
-
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_B1_CTL, 0x80, 0x80);
-
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_B1_CTL, 0x78,
- btn_det->mbhc_nsc << 3);
-
- snd_soc_update_bits(codec, taiko->reg_addr.micb_4_mbhc, 0x03,
- TAIKO_MICBIAS2);
-
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_B1_CTL, 0x02, 0x02);
-
- snd_soc_update_bits(codec, TAIKO_A_MBHC_SCALING_MUX_2, 0xF0, 0xF0);
-}
-
-static bool taiko_mbhc_fw_validate(const struct firmware *fw)
-{
- u32 cfg_offset;
- struct taiko_mbhc_imped_detect_cfg *imped_cfg;
- struct taiko_mbhc_btn_detect_cfg *btn_cfg;
-
- if (fw->size < TAIKO_MBHC_CAL_MIN_SIZE)
- return false;
-
- /* previous check guarantees that there is enough fw data up
- * to num_btn
- */
- btn_cfg = TAIKO_MBHC_CAL_BTN_DET_PTR(fw->data);
- cfg_offset = (u32) ((void *) btn_cfg - (void *) fw->data);
- if (fw->size < (cfg_offset + TAIKO_MBHC_CAL_BTN_SZ(btn_cfg)))
- return false;
-
- /* previous check guarantees that there is enough fw data up
- * to start of impedance detection configuration
- */
- imped_cfg = TAIKO_MBHC_CAL_IMPED_DET_PTR(fw->data);
- cfg_offset = (u32) ((void *) imped_cfg - (void *) fw->data);
-
- if (fw->size < (cfg_offset + TAIKO_MBHC_CAL_IMPED_MIN_SZ))
- return false;
-
- if (fw->size < (cfg_offset + TAIKO_MBHC_CAL_IMPED_SZ(imped_cfg)))
- return false;
-
- return true;
-}
-
-/* called under codec_resource_lock acquisition */
-static int taiko_determine_button(const struct taiko_priv *priv,
- const s32 micmv)
-{
- s16 *v_btn_low, *v_btn_high;
- struct taiko_mbhc_btn_detect_cfg *btn_det;
- int i, btn = -1;
-
- btn_det = TAIKO_MBHC_CAL_BTN_DET_PTR(priv->mbhc_cfg.calibration);
- v_btn_low = taiko_mbhc_cal_btn_det_mp(btn_det, TAIKO_BTN_DET_V_BTN_LOW);
- v_btn_high = taiko_mbhc_cal_btn_det_mp(btn_det,
- TAIKO_BTN_DET_V_BTN_HIGH);
-
- for (i = 0; i < btn_det->num_btn; i++) {
- if ((v_btn_low[i] <= micmv) && (v_btn_high[i] >= micmv)) {
- btn = i;
- break;
- }
- }
-
- if (btn == -1)
- pr_debug("%s: couldn't find button number for mic mv %d\n",
- __func__, micmv);
-
- return btn;
-}
-
-static int taiko_get_button_mask(const int btn)
-{
- int mask = 0;
- switch (btn) {
- case 0:
- mask = SND_JACK_BTN_0;
- break;
- case 1:
- mask = SND_JACK_BTN_1;
- break;
- case 2:
- mask = SND_JACK_BTN_2;
- break;
- case 3:
- mask = SND_JACK_BTN_3;
- break;
- case 4:
- mask = SND_JACK_BTN_4;
- break;
- case 5:
- mask = SND_JACK_BTN_5;
- break;
- case 6:
- mask = SND_JACK_BTN_6;
- break;
- case 7:
- mask = SND_JACK_BTN_7;
- break;
- }
- return mask;
-}
-
-static irqreturn_t taiko_dce_handler(int irq, void *data)
-{
- int i, mask;
- short dce, sta;
- s32 mv, mv_s, stamv_s;
- bool vddio;
- int btn = -1, meas = 0;
- struct taiko_priv *priv = data;
- const struct taiko_mbhc_btn_detect_cfg *d =
- TAIKO_MBHC_CAL_BTN_DET_PTR(priv->mbhc_cfg.calibration);
- short btnmeas[d->n_btn_meas + 1];
- struct snd_soc_codec *codec = priv->codec;
- struct wcd9xxx *core = dev_get_drvdata(priv->codec->dev->parent);
- int n_btn_meas = d->n_btn_meas;
- u8 mbhc_status = snd_soc_read(codec, TAIKO_A_CDC_MBHC_B1_STATUS) & 0x3E;
-
- pr_debug("%s: enter\n", __func__);
-
- TAIKO_ACQUIRE_LOCK(priv->codec_resource_lock);
- if (priv->mbhc_state == MBHC_STATE_POTENTIAL_RECOVERY) {
- pr_debug("%s: mbhc is being recovered, skip button press\n",
- __func__);
- goto done;
- }
-
- priv->mbhc_state = MBHC_STATE_POTENTIAL;
-
- if (!priv->mbhc_polling_active) {
- pr_warn("%s: mbhc polling is not active, skip button press\n",
- __func__);
- goto done;
- }
-
- dce = taiko_codec_read_dce_result(codec);
- mv = taiko_codec_sta_dce_v(codec, 1, dce);
-
- /* If GPIO interrupt already kicked in, ignore button press */
- if (priv->in_gpio_handler) {
- pr_debug("%s: GPIO State Changed, ignore button press\n",
- __func__);
- btn = -1;
- goto done;
- }
-
- vddio = (priv->mbhc_data.micb_mv != VDDIO_MICBIAS_MV &&
- priv->mbhc_micbias_switched);
- mv_s = vddio ? taiko_scale_v_micb_vddio(priv, mv, false) : mv;
-
- if (mbhc_status != TAIKO_MBHC_STATUS_REL_DETECTION) {
- if (priv->mbhc_last_resume &&
- !time_after(jiffies, priv->mbhc_last_resume + HZ)) {
- pr_debug("%s: Button is already released shortly after resume\n",
- __func__);
- n_btn_meas = 0;
- } else {
- pr_debug("%s: Button is already released without resume",
- __func__);
- sta = taiko_codec_read_sta_result(codec);
- stamv_s = taiko_codec_sta_dce_v(codec, 0, sta);
- if (vddio)
- stamv_s = taiko_scale_v_micb_vddio(priv,
- stamv_s,
- false);
- btn = taiko_determine_button(priv, mv_s);
- if (btn != taiko_determine_button(priv, stamv_s))
- btn = -1;
- goto done;
- }
- }
-
- /* determine pressed button */
- btnmeas[meas++] = taiko_determine_button(priv, mv_s);
- pr_debug("%s: meas %d - DCE %d,%d,%d button %d\n", __func__,
- meas - 1, dce, mv, mv_s, btnmeas[meas - 1]);
- if (n_btn_meas == 0)
- btn = btnmeas[0];
- for (; ((d->n_btn_meas) && (meas < (d->n_btn_meas + 1))); meas++) {
- dce = taiko_codec_sta_dce(codec, 1, false);
- mv = taiko_codec_sta_dce_v(codec, 1, dce);
- mv_s = vddio ? taiko_scale_v_micb_vddio(priv, mv, false) : mv;
-
- btnmeas[meas] = taiko_determine_button(priv, mv_s);
- pr_debug("%s: meas %d - DCE %d,%d,%d button %d\n",
- __func__, meas, dce, mv, mv_s, btnmeas[meas]);
- /* if large enough measurements are collected,
- * start to check if last all n_btn_con measurements were
- * in same button low/high range */
- if (meas + 1 >= d->n_btn_con) {
- for (i = 0; i < d->n_btn_con; i++)
- if ((btnmeas[meas] < 0) ||
- (btnmeas[meas] != btnmeas[meas - i]))
- break;
- if (i == d->n_btn_con) {
- /* button pressed */
- btn = btnmeas[meas];
- break;
- } else if ((n_btn_meas - meas) < (d->n_btn_con - 1)) {
- /* if left measurements are less than n_btn_con,
- * it's impossible to find button number */
- break;
- }
- }
- }
-
- if (btn >= 0) {
- if (priv->in_gpio_handler) {
- pr_debug(
- "%s: GPIO already triggered, ignore button press\n",
- __func__);
- goto done;
- }
- mask = taiko_get_button_mask(btn);
- priv->buttons_pressed |= mask;
- wcd9xxx_lock_sleep(core);
- if (schedule_delayed_work(&priv->mbhc_btn_dwork,
- msecs_to_jiffies(400)) == 0) {
- WARN(1, "Button pressed twice without release event\n");
- wcd9xxx_unlock_sleep(core);
- }
- } else {
- pr_debug("%s: bogus button press, too short press?\n",
- __func__);
- }
-
- done:
- pr_debug("%s: leave\n", __func__);
- TAIKO_RELEASE_LOCK(priv->codec_resource_lock);
- return IRQ_HANDLED;
-}
-
-static int taiko_is_fake_press(struct taiko_priv *priv)
-{
- int i;
- int r = 0;
- struct snd_soc_codec *codec = priv->codec;
- const int dces = MBHC_NUM_DCE_PLUG_DETECT;
- s16 mb_v, v_ins_hu, v_ins_h;
-
- v_ins_hu = taiko_get_current_v_ins(priv, true);
- v_ins_h = taiko_get_current_v_ins(priv, false);
-
- for (i = 0; i < dces; i++) {
- usleep_range(10000, 10000);
- if (i == 0) {
- mb_v = taiko_codec_sta_dce(codec, 0, true);
- pr_debug("%s: STA[0]: %d,%d\n", __func__, mb_v,
- taiko_codec_sta_dce_v(codec, 0, mb_v));
- if (mb_v < (s16)priv->mbhc_data.v_b1_hu ||
- mb_v > v_ins_hu) {
- r = 1;
- break;
- }
- } else {
- mb_v = taiko_codec_sta_dce(codec, 1, true);
- pr_debug("%s: DCE[%d]: %d,%d\n", __func__, i, mb_v,
- taiko_codec_sta_dce_v(codec, 1, mb_v));
- if (mb_v < (s16)priv->mbhc_data.v_b1_h ||
- mb_v > v_ins_h) {
- r = 1;
- break;
- }
- }
- }
-
- return r;
-}
-
-static irqreturn_t taiko_release_handler(int irq, void *data)
-{
- int ret;
- struct taiko_priv *priv = data;
- struct snd_soc_codec *codec = priv->codec;
-
- pr_debug("%s: enter\n", __func__);
-
- TAIKO_ACQUIRE_LOCK(priv->codec_resource_lock);
- priv->mbhc_state = MBHC_STATE_RELEASE;
-
- taiko_codec_drive_v_to_micbias(codec, 10000);
-
- if (priv->buttons_pressed & TAIKO_JACK_BUTTON_MASK) {
- ret = taiko_cancel_btn_work(priv);
- if (ret == 0) {
- pr_debug("%s: Reporting long button release event\n",
- __func__);
- if (priv->mbhc_cfg.button_jack)
- taiko_snd_soc_jack_report(priv,
- priv->mbhc_cfg.button_jack, 0,
- priv->buttons_pressed);
- } else {
- if (taiko_is_fake_press(priv)) {
- pr_debug("%s: Fake button press interrupt\n",
- __func__);
- } else if (priv->mbhc_cfg.button_jack) {
- if (priv->in_gpio_handler) {
- pr_debug("%s: GPIO kicked in, ignore\n",
- __func__);
- } else {
- pr_debug(
- "%s: Reporting short button press and release\n",
- __func__);
- taiko_snd_soc_jack_report(priv,
- priv->mbhc_cfg.button_jack,
- priv->buttons_pressed,
- priv->buttons_pressed);
- taiko_snd_soc_jack_report(priv,
- priv->mbhc_cfg.button_jack, 0,
- priv->buttons_pressed);
- }
- }
- }
-
- priv->buttons_pressed &= ~TAIKO_JACK_BUTTON_MASK;
- }
-
- taiko_codec_calibrate_hs_polling(codec);
-
- if (priv->mbhc_cfg.gpio)
- msleep(TAIKO_MBHC_GPIO_REL_DEBOUNCE_TIME_MS);
-
- taiko_codec_start_hs_polling(codec);
-
- pr_debug("%s: leave\n", __func__);
- TAIKO_RELEASE_LOCK(priv->codec_resource_lock);
- return IRQ_HANDLED;
-}
-
-static void taiko_codec_shutdown_hs_removal_detect(struct snd_soc_codec *codec)
-{
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
- const struct taiko_mbhc_general_cfg *generic =
- TAIKO_MBHC_CAL_GENERAL_PTR(taiko->mbhc_cfg.calibration);
-
- if (!taiko->mclk_enabled && !taiko->mbhc_polling_active)
- taiko_codec_enable_config_mode(codec, 1);
-
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0x2, 0x2);
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_B1_CTL, 0x6, 0x0);
-
- snd_soc_update_bits(codec, taiko->mbhc_bias_regs.mbhc_reg, 0x80, 0x00);
-
- usleep_range(generic->t_shutdown_plug_rem,
- generic->t_shutdown_plug_rem);
-
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL, 0xA, 0x8);
- if (!taiko->mclk_enabled && !taiko->mbhc_polling_active)
- taiko_codec_enable_config_mode(codec, 0);
-
- snd_soc_write(codec, TAIKO_A_MBHC_SCALING_MUX_1, 0x00);
-}
-
-static void taiko_codec_cleanup_hs_polling(struct snd_soc_codec *codec)
-{
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-
- taiko_codec_shutdown_hs_removal_detect(codec);
-
- if (!taiko->mclk_enabled) {
- taiko_codec_disable_clock_block(codec);
- taiko_codec_enable_bandgap(codec, TAIKO_BANDGAP_OFF);
- }
-
- taiko->mbhc_polling_active = false;
- taiko->mbhc_state = MBHC_STATE_NONE;
-}
-
-static irqreturn_t taiko_hphl_ocp_irq(int irq, void *data)
-{
- struct taiko_priv *taiko = data;
- struct snd_soc_codec *codec;
-
- pr_info("%s: received HPHL OCP irq\n", __func__);
-
- if (taiko) {
- codec = taiko->codec;
- if (taiko->hphlocp_cnt++ < TAIKO_OCP_ATTEMPT) {
- pr_info("%s: retry\n", __func__);
- snd_soc_update_bits(codec, TAIKO_A_RX_HPH_OCP_CTL, 0x10,
- 0x00);
- snd_soc_update_bits(codec, TAIKO_A_RX_HPH_OCP_CTL, 0x10,
- 0x10);
- } else {
- wcd9xxx_disable_irq(codec->control_data,
- WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
- taiko->hphlocp_cnt = 0;
- taiko->hph_status |= SND_JACK_OC_HPHL;
- if (taiko->mbhc_cfg.headset_jack)
- taiko_snd_soc_jack_report(taiko,
- taiko->mbhc_cfg.headset_jack,
- taiko->hph_status,
- TAIKO_JACK_MASK);
- }
- } else {
- pr_err("%s: Bad taiko private data\n", __func__);
- }
-
- return IRQ_HANDLED;
-}
-
-static irqreturn_t taiko_hphr_ocp_irq(int irq, void *data)
-{
- struct taiko_priv *taiko = data;
- struct snd_soc_codec *codec;
-
- pr_info("%s: received HPHR OCP irq\n", __func__);
-
- if (taiko) {
- codec = taiko->codec;
- if (taiko->hphrocp_cnt++ < TAIKO_OCP_ATTEMPT) {
- pr_info("%s: retry\n", __func__);
- snd_soc_update_bits(codec, TAIKO_A_RX_HPH_OCP_CTL, 0x10,
- 0x00);
- snd_soc_update_bits(codec, TAIKO_A_RX_HPH_OCP_CTL, 0x10,
- 0x10);
- } else {
- wcd9xxx_disable_irq(codec->control_data,
- WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
- taiko->hphrocp_cnt = 0;
- taiko->hph_status |= SND_JACK_OC_HPHR;
- if (taiko->mbhc_cfg.headset_jack)
- taiko_snd_soc_jack_report(taiko,
- taiko->mbhc_cfg.headset_jack,
- taiko->hph_status,
- TAIKO_JACK_MASK);
- }
- } else {
- pr_err("%s: Bad taiko private data\n", __func__);
- }
-
- return IRQ_HANDLED;
-}
-
-static bool taiko_is_inval_ins_range(struct snd_soc_codec *codec,
- s32 mic_volt, bool highhph, bool *highv)
-{
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
- bool invalid = false;
- s16 v_hs_max;
-
- /* Perform this check only when the high voltage headphone
- * needs to be considered as invalid
- */
- v_hs_max = taiko_get_current_v_hs_max(taiko);
- *highv = mic_volt > v_hs_max;
- if (!highhph && *highv)
- invalid = true;
- else if (mic_volt < taiko->mbhc_data.v_inval_ins_high &&
- (mic_volt > taiko->mbhc_data.v_inval_ins_low))
- invalid = true;
-
- return invalid;
-}
-
-static bool taiko_is_inval_ins_delta(struct snd_soc_codec *codec,
- int mic_volt, int mic_volt_prev,
- int threshold)
-{
- return abs(mic_volt - mic_volt_prev) > threshold;
-}
-
-/* called under codec_resource_lock acquisition */
-void taiko_find_plug_and_report(struct snd_soc_codec *codec,
- enum taiko_mbhc_plug_type plug_type)
-{
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-
- if (plug_type == PLUG_TYPE_HEADPHONE &&
- taiko->current_plug == PLUG_TYPE_NONE) {
- /* Nothing was reported previously
- * report a headphone or unsupported
- */
- taiko_codec_report_plug(codec, 1, SND_JACK_HEADPHONE);
- taiko_codec_cleanup_hs_polling(codec);
- } else if (plug_type == PLUG_TYPE_GND_MIC_SWAP) {
- if (taiko->current_plug == PLUG_TYPE_HEADSET)
- taiko_codec_report_plug(codec, 0, SND_JACK_HEADSET);
- else if (taiko->current_plug == PLUG_TYPE_HEADPHONE)
- taiko_codec_report_plug(codec, 0, SND_JACK_HEADPHONE);
-
- taiko_codec_report_plug(codec, 1, SND_JACK_UNSUPPORTED);
- taiko_codec_cleanup_hs_polling(codec);
- } else if (plug_type == PLUG_TYPE_HEADSET) {
- /* If Headphone was reported previously, this will
- * only report the mic line
- */
- taiko_codec_report_plug(codec, 1, SND_JACK_HEADSET);
- msleep(100);
- taiko_codec_start_hs_polling(codec);
- } else if (plug_type == PLUG_TYPE_HIGH_HPH) {
- if (taiko->current_plug == PLUG_TYPE_NONE)
- taiko_codec_report_plug(codec, 1, SND_JACK_HEADPHONE);
- taiko_codec_cleanup_hs_polling(codec);
- pr_debug("setup mic trigger for further detection\n");
- taiko->lpi_enabled = true;
- taiko_codec_enable_hs_detect(codec, 1,
- MBHC_USE_MB_TRIGGER |
- MBHC_USE_HPHL_TRIGGER,
- false);
- } else {
- WARN(1, "Unexpected current plug_type %d, plug_type %d\n",
- taiko->current_plug, plug_type);
- }
-}
-
-/* should be called under interrupt context that hold suspend */
-static void taiko_schedule_hs_detect_plug(struct taiko_priv *taiko)
-{
- pr_debug("%s: scheduling taiko_hs_correct_gpio_plug\n", __func__);
- taiko->hs_detect_work_stop = false;
- wcd9xxx_lock_sleep(taiko->codec->control_data);
- schedule_work(&taiko->hs_correct_plug_work);
-}
-
-/* called under codec_resource_lock acquisition */
-static void taiko_cancel_hs_detect_plug(struct taiko_priv *taiko)
-{
- pr_debug("%s: canceling hs_correct_plug_work\n", __func__);
- taiko->hs_detect_work_stop = true;
- wmb();
- TAIKO_RELEASE_LOCK(taiko->codec_resource_lock);
- if (cancel_work_sync(&taiko->hs_correct_plug_work)) {
- pr_debug("%s: hs_correct_plug_work is canceled\n", __func__);
- wcd9xxx_unlock_sleep(taiko->codec->control_data);
- }
- TAIKO_ACQUIRE_LOCK(taiko->codec_resource_lock);
-}
-
-static bool taiko_hs_gpio_level_remove(struct taiko_priv *taiko)
-{
- return (gpio_get_value_cansleep(taiko->mbhc_cfg.gpio) !=
- taiko->mbhc_cfg.gpio_level_insert);
-}
-
-/* called under codec_resource_lock acquisition */
-static void taiko_codec_hphr_gnd_switch(struct snd_soc_codec *codec, bool on)
-{
- snd_soc_update_bits(codec, TAIKO_A_MBHC_HPH, 0x01, on);
- if (on)
- usleep_range(5000, 5000);
-}
-
-/* called under codec_resource_lock acquisition and mbhc override = 1 */
-static enum taiko_mbhc_plug_type
-taiko_codec_get_plug_type(struct snd_soc_codec *codec, bool highhph)
-{
- int i;
- bool gndswitch, vddioswitch;
- int scaled;
- struct taiko_mbhc_plug_type_cfg *plug_type_ptr;
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
- const bool vddio = (taiko->mbhc_data.micb_mv != VDDIO_MICBIAS_MV);
- int num_det = (MBHC_NUM_DCE_PLUG_DETECT + vddio);
- enum taiko_mbhc_plug_type plug_type[num_det];
- s16 mb_v[num_det];
- s32 mic_mv[num_det];
- bool inval;
- bool highdelta;
- bool ahighv = false, highv;
-
- /* make sure override is on */
- WARN_ON(!(snd_soc_read(codec, TAIKO_A_CDC_MBHC_B1_CTL) & 0x04));
-
- /* GND and MIC swap detection requires at least 2 rounds of DCE */
- BUG_ON(num_det < 2);
-
- plug_type_ptr =
- TAIKO_MBHC_CAL_PLUG_TYPE_PTR(taiko->mbhc_cfg.calibration);
-
- plug_type[0] = PLUG_TYPE_INVALID;
-
- /* performs DCEs for N times
- * 1st: check if voltage is in invalid range
- * 2nd - N-2nd: check voltage range and delta
- * N-1st: check voltage range, delta with HPHR GND switch
- * Nth: check voltage range with VDDIO switch if micbias V != vddio V*/
- for (i = 0; i < num_det; i++) {
- gndswitch = (i == (num_det - 1 - vddio));
- vddioswitch = (vddio && ((i == num_det - 1) ||
- (i == num_det - 2)));
- if (i == 0) {
- mb_v[i] = taiko_codec_setup_hs_polling(codec);
- mic_mv[i] = taiko_codec_sta_dce_v(codec, 1 , mb_v[i]);
- inval = taiko_is_inval_ins_range(codec, mic_mv[i],
- highhph, &highv);
- ahighv |= highv;
- scaled = mic_mv[i];
- } else {
- if (vddioswitch)
- __taiko_codec_switch_micbias(taiko->codec, 1,
- false, false);
- if (gndswitch)
- taiko_codec_hphr_gnd_switch(codec, true);
- mb_v[i] = __taiko_codec_sta_dce(codec, 1, true, true);
- mic_mv[i] = taiko_codec_sta_dce_v(codec, 1 , mb_v[i]);
- if (vddioswitch)
- scaled = taiko_scale_v_micb_vddio(taiko,
- mic_mv[i],
- false);
- else
- scaled = mic_mv[i];
- /* !gndswitch & vddioswitch means the previous DCE
- * was done with gndswitch, don't compare with DCE
- * with gndswitch */
- highdelta = taiko_is_inval_ins_delta(codec, scaled,
- mic_mv[i - !gndswitch - vddioswitch],
- TAIKO_MBHC_FAKE_INS_DELTA_SCALED_MV);
- inval = (taiko_is_inval_ins_range(codec, mic_mv[i],
- highhph, &highv) ||
- highdelta);
- ahighv |= highv;
- if (gndswitch)
- taiko_codec_hphr_gnd_switch(codec, false);
- if (vddioswitch)
- __taiko_codec_switch_micbias(taiko->codec, 0,
- false, false);
- /* claim UNSUPPORTED plug insertion when
- * good headset is detected but HPHR GND switch makes
- * delta difference */
- if (i == (num_det - 2) && highdelta && !ahighv)
- plug_type[0] = PLUG_TYPE_GND_MIC_SWAP;
- else if (i == (num_det - 1) && inval)
- plug_type[0] = PLUG_TYPE_INVALID;
- }
- pr_debug("%s: DCE #%d, %04x, V %d, scaled V %d, GND %d, VDDIO %d, inval %d\n",
- __func__, i + 1, mb_v[i] & 0xffff, mic_mv[i], scaled,
- gndswitch, vddioswitch, inval);
- /* don't need to run further DCEs */
- if (ahighv && inval)
- break;
- mic_mv[i] = scaled;
- }
-
- for (i = 0; (plug_type[0] != PLUG_TYPE_GND_MIC_SWAP && !inval) &&
- i < num_det; i++) {
- /*
- * If we are here, means none of the all
- * measurements are fake, continue plug type detection.
- * If all three measurements do not produce same
- * plug type, restart insertion detection
- */
- if (mic_mv[i] < plug_type_ptr->v_no_mic) {
- plug_type[i] = PLUG_TYPE_HEADPHONE;
- pr_debug("%s: Detect attempt %d, detected Headphone\n",
- __func__, i);
- } else if (highhph && (mic_mv[i] > plug_type_ptr->v_hs_max)) {
- plug_type[i] = PLUG_TYPE_HIGH_HPH;
- pr_debug(
- "%s: Detect attempt %d, detected High Headphone\n",
- __func__, i);
- } else {
- plug_type[i] = PLUG_TYPE_HEADSET;
- pr_debug("%s: Detect attempt %d, detected Headset\n",
- __func__, i);
- }
-
- if (i > 0 && (plug_type[i - 1] != plug_type[i])) {
- pr_err("%s: Detect attempt %d and %d are not same",
- __func__, i - 1, i);
- plug_type[0] = PLUG_TYPE_INVALID;
- inval = true;
- break;
- }
- }
-
- pr_debug("%s: Detected plug type %d\n", __func__, plug_type[0]);
- return plug_type[0];
-}
-
-static void taiko_hs_correct_gpio_plug(struct work_struct *work)
-{
- struct taiko_priv *taiko;
- struct snd_soc_codec *codec;
- int retry = 0, pt_gnd_mic_swap_cnt = 0;
- bool correction = false;
- enum taiko_mbhc_plug_type plug_type;
- unsigned long timeout;
-
- taiko = container_of(work, struct taiko_priv, hs_correct_plug_work);
- codec = taiko->codec;
-
- pr_debug("%s: enter\n", __func__);
- taiko->mbhc_cfg.mclk_cb_fn(codec, 1, false);
-
- /* Keep override on during entire plug type correction work.
- *
- * This is okay under the assumption that any GPIO irqs which use
- * MBHC block cancel and sync this work so override is off again
- * prior to GPIO interrupt handler's MBHC block usage.
- * Also while this correction work is running, we can guarantee
- * DAPM doesn't use any MBHC block as this work only runs with
- * headphone detection.
- */
- taiko_turn_onoff_override(codec, true);
-
- timeout = jiffies + msecs_to_jiffies(TAIKO_HS_DETECT_PLUG_TIME_MS);
- while (!time_after(jiffies, timeout)) {
- ++retry;
- rmb();
- if (taiko->hs_detect_work_stop) {
- pr_debug("%s: stop requested\n", __func__);
- break;
- }
-
- msleep(TAIKO_HS_DETECT_PLUG_INERVAL_MS);
- if (taiko_hs_gpio_level_remove(taiko)) {
- pr_debug("%s: GPIO value is low\n", __func__);
- break;
- }
-
- /* can race with removal interrupt */
- TAIKO_ACQUIRE_LOCK(taiko->codec_resource_lock);
- plug_type = taiko_codec_get_plug_type(codec, true);
- TAIKO_RELEASE_LOCK(taiko->codec_resource_lock);
-
- if (plug_type == PLUG_TYPE_INVALID) {
- pr_debug("Invalid plug in attempt # %d\n", retry);
- if (retry == NUM_ATTEMPTS_TO_REPORT &&
- taiko->current_plug == PLUG_TYPE_NONE) {
- taiko_codec_report_plug(codec, 1,
- SND_JACK_HEADPHONE);
- }
- } else if (plug_type == PLUG_TYPE_HEADPHONE) {
- pr_debug("Good headphone detected, continue polling mic\n");
- if (taiko->current_plug == PLUG_TYPE_NONE)
- taiko_codec_report_plug(codec, 1,
- SND_JACK_HEADPHONE);
- } else {
- if (plug_type == PLUG_TYPE_GND_MIC_SWAP) {
- pt_gnd_mic_swap_cnt++;
- if (pt_gnd_mic_swap_cnt <
- TAIKO_MBHC_GND_MIC_SWAP_THRESHOLD)
- continue;
- else if (pt_gnd_mic_swap_cnt >
- TAIKO_MBHC_GND_MIC_SWAP_THRESHOLD) {
- /* This is due to GND/MIC switch didn't
- * work, Report unsupported plug */
- } else if (taiko->mbhc_cfg.swap_gnd_mic) {
- /* if switch is toggled, check again,
- * otherwise report unsupported plug */
- if (taiko->mbhc_cfg.swap_gnd_mic(codec))
- continue;
- }
- } else
- pt_gnd_mic_swap_cnt = 0;
-
- TAIKO_ACQUIRE_LOCK(taiko->codec_resource_lock);
- /* Turn off override */
- taiko_turn_onoff_override(codec, false);
- /* The valid plug also includes PLUG_TYPE_GND_MIC_SWAP
- */
- taiko_find_plug_and_report(codec, plug_type);
- TAIKO_RELEASE_LOCK(taiko->codec_resource_lock);
- pr_debug("Attempt %d found correct plug %d\n", retry,
- plug_type);
- correction = true;
- break;
- }
- }
-
- /* Turn off override */
- if (!correction)
- taiko_turn_onoff_override(codec, false);
-
- taiko->mbhc_cfg.mclk_cb_fn(codec, 0, false);
- pr_debug("%s: leave\n", __func__);
- /* unlock sleep */
- wcd9xxx_unlock_sleep(taiko->codec->control_data);
-}
-
-/* called under codec_resource_lock acquisition */
-static void taiko_codec_decide_gpio_plug(struct snd_soc_codec *codec)
-{
- enum taiko_mbhc_plug_type plug_type;
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
-
- pr_debug("%s: enter\n", __func__);
-
- taiko_turn_onoff_override(codec, true);
- plug_type = taiko_codec_get_plug_type(codec, true);
- taiko_turn_onoff_override(codec, false);
-
- if (taiko_hs_gpio_level_remove(taiko)) {
- pr_debug("%s: GPIO value is low when determining plug\n",
- __func__);
- return;
- }
-
- if (plug_type == PLUG_TYPE_INVALID ||
- plug_type == PLUG_TYPE_GND_MIC_SWAP) {
- taiko_schedule_hs_detect_plug(taiko);
- } else if (plug_type == PLUG_TYPE_HEADPHONE) {
- taiko_codec_report_plug(codec, 1, SND_JACK_HEADPHONE);
-
- taiko_schedule_hs_detect_plug(taiko);
- } else {
- pr_debug("%s: Valid plug found, determine plug type %d\n",
- __func__, plug_type);
- taiko_find_plug_and_report(codec, plug_type);
- }
-}
-
-/* called under codec_resource_lock acquisition */
-static void taiko_codec_detect_plug_type(struct snd_soc_codec *codec)
-{
- enum taiko_mbhc_plug_type plug_type;
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
- const struct taiko_mbhc_plug_detect_cfg *plug_det =
- TAIKO_MBHC_CAL_PLUG_DET_PTR(taiko->mbhc_cfg.calibration);
-
- /* Turn on the override,
- * taiko_codec_setup_hs_polling requires override on */
- taiko_turn_onoff_override(codec, true);
-
- if (plug_det->t_ins_complete > 20)
- msleep(plug_det->t_ins_complete);
- else
- usleep_range(plug_det->t_ins_complete * 1000,
- plug_det->t_ins_complete * 1000);
-
- if (taiko->mbhc_cfg.gpio) {
- /* Turn off the override */
- taiko_turn_onoff_override(codec, false);
- if (taiko_hs_gpio_level_remove(taiko))
- pr_debug(
- "%s: GPIO value is low when determining plug\n",
- __func__);
- else
- taiko_codec_decide_gpio_plug(codec);
- return;
- }
-
- plug_type = taiko_codec_get_plug_type(codec, false);
- taiko_turn_onoff_override(codec, false);
-
- if (plug_type == PLUG_TYPE_INVALID) {
- pr_debug("%s: Invalid plug type detected\n", __func__);
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_B1_CTL, 0x02, 0x02);
- taiko_codec_cleanup_hs_polling(codec);
- taiko_codec_enable_hs_detect(codec, 1,
- MBHC_USE_MB_TRIGGER |
- MBHC_USE_HPHL_TRIGGER, false);
- } else if (plug_type == PLUG_TYPE_GND_MIC_SWAP) {
- pr_debug("%s: GND-MIC swapped plug type detected\n", __func__);
- taiko_codec_report_plug(codec, 1, SND_JACK_UNSUPPORTED);
- taiko_codec_cleanup_hs_polling(codec);
- taiko_codec_enable_hs_detect(codec, 0, 0, false);
- } else if (plug_type == PLUG_TYPE_HEADPHONE) {
- pr_debug("%s: Headphone Detected\n", __func__);
- taiko_codec_report_plug(codec, 1, SND_JACK_HEADPHONE);
- taiko_codec_cleanup_hs_polling(codec);
- taiko_codec_enable_hs_detect(codec, 0, 0, false);
- } else if (plug_type == PLUG_TYPE_HEADSET) {
- pr_debug("%s: Headset detected\n", __func__);
- taiko_codec_report_plug(codec, 1, SND_JACK_HEADSET);
-
- /* avoid false button press detect */
- msleep(50);
- taiko_codec_start_hs_polling(codec);
- }
-}
-
-/* called only from interrupt which is under codec_resource_lock acquisition */
-static void taiko_hs_insert_irq_gpio(struct taiko_priv *priv, bool is_removal)
-{
- struct snd_soc_codec *codec = priv->codec;
-
- if (!is_removal) {
- pr_debug("%s: MIC trigger insertion interrupt\n", __func__);
-
- rmb();
- if (priv->lpi_enabled)
- msleep(100);
-
- rmb();
- if (!priv->lpi_enabled) {
- pr_debug("%s: lpi is disabled\n", __func__);
- } else if (gpio_get_value_cansleep(priv->mbhc_cfg.gpio) ==
- priv->mbhc_cfg.gpio_level_insert) {
- pr_debug(
- "%s: Valid insertion, detect plug type\n", __func__);
- taiko_codec_decide_gpio_plug(codec);
- } else {
- pr_debug(
- "%s: Invalid insertion stop plug detection\n",
- __func__);
- }
- } else {
- pr_err("%s: GPIO used, invalid MBHC Removal\n", __func__);
- }
-}
-
-/* called only from interrupt which is under codec_resource_lock acquisition */
-static void taiko_hs_insert_irq_nogpio(struct taiko_priv *priv, bool is_removal,
- bool is_mb_trigger)
-{
- int ret;
- struct snd_soc_codec *codec = priv->codec;
- struct wcd9xxx *core = dev_get_drvdata(priv->codec->dev->parent);
-
- if (is_removal) {
- /* cancel possiblely running hs detect work */
- taiko_cancel_hs_detect_plug(priv);
-
- /*
- * If headphone is removed while playback is in progress,
- * it is possible that micbias will be switched to VDDIO.
- */
- taiko_codec_switch_micbias(codec, 0);
- if (priv->current_plug == PLUG_TYPE_HEADPHONE)
- taiko_codec_report_plug(codec, 0, SND_JACK_HEADPHONE);
- else if (priv->current_plug == PLUG_TYPE_GND_MIC_SWAP)
- taiko_codec_report_plug(codec, 0, SND_JACK_UNSUPPORTED);
- else
- WARN(1, "%s: Unexpected current plug type %d\n",
- __func__, priv->current_plug);
- taiko_codec_shutdown_hs_removal_detect(codec);
- taiko_codec_enable_hs_detect(codec, 1,
- MBHC_USE_MB_TRIGGER |
- MBHC_USE_HPHL_TRIGGER,
- true);
- } else if (is_mb_trigger && !is_removal) {
- pr_debug("%s: Waiting for Headphone left trigger\n",
- __func__);
- wcd9xxx_lock_sleep(core);
- if (schedule_delayed_work(&priv->mbhc_insert_dwork,
- usecs_to_jiffies(1000000)) == 0) {
- pr_err("%s: mbhc_insert_dwork is already scheduled\n",
- __func__);
- wcd9xxx_unlock_sleep(core);
- }
- taiko_codec_enable_hs_detect(codec, 1, MBHC_USE_HPHL_TRIGGER,
- false);
- } else {
- ret = cancel_delayed_work(&priv->mbhc_insert_dwork);
- if (ret != 0) {
- pr_debug(
- "%s: Complete plug insertion, Detecting plug type\n",
- __func__);
- taiko_codec_detect_plug_type(codec);
- wcd9xxx_unlock_sleep(core);
- } else {
- wcd9xxx_enable_irq(codec->control_data,
- WCD9XXX_IRQ_MBHC_INSERTION);
- pr_err("%s: Error detecting plug insertion\n",
- __func__);
- }
- }
-}
-
-static irqreturn_t taiko_hs_insert_irq(int irq, void *data)
-{
- bool is_mb_trigger, is_removal;
- struct taiko_priv *priv = data;
- struct snd_soc_codec *codec = priv->codec;
-
- pr_debug("%s: enter\n", __func__);
- TAIKO_ACQUIRE_LOCK(priv->codec_resource_lock);
- wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_INSERTION);
-
- is_mb_trigger = !!(snd_soc_read(codec, priv->mbhc_bias_regs.mbhc_reg) &
- 0x10);
- is_removal = !!(snd_soc_read(codec, TAIKO_A_CDC_MBHC_INT_CTL) & 0x02);
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_INT_CTL, 0x03, 0x00);
-
- /* Turn off both HPH and MIC line schmitt triggers */
- snd_soc_update_bits(codec, priv->mbhc_bias_regs.mbhc_reg, 0x90, 0x00);
- snd_soc_update_bits(codec, TAIKO_A_MBHC_HPH, 0x13, 0x00);
- snd_soc_update_bits(codec, priv->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
-
- if (priv->mbhc_cfg.gpio)
- taiko_hs_insert_irq_gpio(priv, is_removal);
- else
- taiko_hs_insert_irq_nogpio(priv, is_removal, is_mb_trigger);
-
- TAIKO_RELEASE_LOCK(priv->codec_resource_lock);
- return IRQ_HANDLED;
-}
-
-static bool is_valid_mic_voltage(struct snd_soc_codec *codec, s32 mic_mv)
-{
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
- const struct taiko_mbhc_plug_type_cfg *plug_type =
- TAIKO_MBHC_CAL_PLUG_TYPE_PTR(taiko->mbhc_cfg.calibration);
- const s16 v_hs_max = taiko_get_current_v_hs_max(taiko);
-
- return (!(mic_mv > 10 && mic_mv < 80) && (mic_mv > plug_type->v_no_mic)
- && (mic_mv < v_hs_max)) ? true : false;
-}
-
-/* called under codec_resource_lock acquisition
- * returns true if mic voltage range is back to normal insertion
- * returns false either if timedout or removed */
-static bool taiko_hs_remove_settle(struct snd_soc_codec *codec)
-{
- int i;
- bool timedout, settled = false;
- s32 mic_mv[MBHC_NUM_DCE_PLUG_DETECT];
- short mb_v[MBHC_NUM_DCE_PLUG_DETECT];
- unsigned long retry = 0, timeout;
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
- const s16 v_hs_max = taiko_get_current_v_hs_max(taiko);
-
- timeout = jiffies + msecs_to_jiffies(TAIKO_HS_DETECT_PLUG_TIME_MS);
- while (!(timedout = time_after(jiffies, timeout))) {
- retry++;
- if (taiko->mbhc_cfg.gpio && taiko_hs_gpio_level_remove(taiko)) {
- pr_debug("%s: GPIO indicates removal\n", __func__);
- break;
- }
-
- if (taiko->mbhc_cfg.gpio) {
- if (retry > 1)
- msleep(250);
- else
- msleep(50);
- }
-
- if (taiko->mbhc_cfg.gpio && taiko_hs_gpio_level_remove(taiko)) {
- pr_debug("%s: GPIO indicates removal\n", __func__);
- break;
- }
-
- for (i = 0; i < MBHC_NUM_DCE_PLUG_DETECT; i++) {
- mb_v[i] = taiko_codec_sta_dce(codec, 1, true);
- mic_mv[i] = taiko_codec_sta_dce_v(codec, 1 , mb_v[i]);
- pr_debug("%s : DCE run %lu, mic_mv = %d(%x)\n",
- __func__, retry, mic_mv[i], mb_v[i]);
- }
-
- if (taiko->mbhc_cfg.gpio && taiko_hs_gpio_level_remove(taiko)) {
- pr_debug("%s: GPIO indicates removal\n", __func__);
- break;
- }
-
- if (taiko->current_plug == PLUG_TYPE_NONE) {
- pr_debug("%s : headset/headphone is removed\n",
- __func__);
- break;
- }
-
- for (i = 0; i < MBHC_NUM_DCE_PLUG_DETECT; i++)
- if (!is_valid_mic_voltage(codec, mic_mv[i]))
- break;
-
- if (i == MBHC_NUM_DCE_PLUG_DETECT) {
- pr_debug("%s: MIC voltage settled\n", __func__);
- settled = true;
- msleep(200);
- break;
- }
-
- /* only for non-GPIO remove irq */
- if (!taiko->mbhc_cfg.gpio) {
- for (i = 0; i < MBHC_NUM_DCE_PLUG_DETECT; i++)
- if (mic_mv[i] < v_hs_max)
- break;
- if (i == MBHC_NUM_DCE_PLUG_DETECT) {
- pr_debug("%s: Headset is removed\n", __func__);
- break;
- }
- }
- }
-
- if (timedout)
- pr_debug("%s: Microphone did not settle in %d seconds\n",
- __func__, TAIKO_HS_DETECT_PLUG_TIME_MS);
- return settled;
-}
-
-/* called only from interrupt which is under codec_resource_lock acquisition */
-static void taiko_hs_remove_irq_gpio(struct taiko_priv *priv)
-{
- struct snd_soc_codec *codec = priv->codec;
-
- if (taiko_hs_remove_settle(codec))
- taiko_codec_start_hs_polling(codec);
- pr_debug("%s: remove settle done\n", __func__);
-}
-
-/* called only from interrupt which is under codec_resource_lock acquisition */
-static void taiko_hs_remove_irq_nogpio(struct taiko_priv *priv)
-{
- short bias_value;
- bool removed = true;
- struct snd_soc_codec *codec = priv->codec;
- const struct taiko_mbhc_general_cfg *generic =
- TAIKO_MBHC_CAL_GENERAL_PTR(priv->mbhc_cfg.calibration);
- int min_us = TAIKO_FAKE_REMOVAL_MIN_PERIOD_MS * 1000;
-
- if (priv->current_plug != PLUG_TYPE_HEADSET) {
- pr_debug("%s(): Headset is not inserted, ignore removal\n",
- __func__);
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL,
- 0x08, 0x08);
- return;
- }
-
- usleep_range(generic->t_shutdown_plug_rem,
- generic->t_shutdown_plug_rem);
-
- do {
- bias_value = taiko_codec_sta_dce(codec, 1, true);
- pr_debug("%s: DCE %d,%d, %d us left\n", __func__, bias_value,
- taiko_codec_sta_dce_v(codec, 1, bias_value), min_us);
- if (bias_value < taiko_get_current_v_ins(priv, false)) {
- pr_debug("%s: checking false removal\n", __func__);
- msleep(500);
- removed = !taiko_hs_remove_settle(codec);
- pr_debug("%s: headset %sactually removed\n", __func__,
- removed ? "" : "not ");
- break;
- }
- min_us -= priv->mbhc_data.t_dce;
- } while (min_us > 0);
-
- if (removed) {
- /* cancel possiblely running hs detect work */
- taiko_cancel_hs_detect_plug(priv);
- /*
- * If this removal is not false, first check the micbias
- * switch status and switch it to LDOH if it is already
- * switched to VDDIO.
- */
- taiko_codec_switch_micbias(codec, 0);
-
- taiko_codec_report_plug(codec, 0, SND_JACK_HEADSET);
- taiko_codec_cleanup_hs_polling(codec);
- taiko_codec_enable_hs_detect(codec, 1,
- MBHC_USE_MB_TRIGGER |
- MBHC_USE_HPHL_TRIGGER,
- true);
- } else {
- taiko_codec_start_hs_polling(codec);
- }
-}
-
-static irqreturn_t taiko_hs_remove_irq(int irq, void *data)
-{
- struct taiko_priv *priv = data;
- bool vddio;
- pr_debug("%s: enter, removal interrupt\n", __func__);
-
- TAIKO_ACQUIRE_LOCK(priv->codec_resource_lock);
- vddio = (priv->mbhc_data.micb_mv != VDDIO_MICBIAS_MV &&
- priv->mbhc_micbias_switched);
- if (vddio)
- __taiko_codec_switch_micbias(priv->codec, 0, false, true);
-
- if (priv->mbhc_cfg.gpio)
- taiko_hs_remove_irq_gpio(priv);
- else
- taiko_hs_remove_irq_nogpio(priv);
-
- /* if driver turned off vddio switch and headset is not removed,
- * turn on the vddio switch back, if headset is removed then vddio
- * switch is off by time now and shouldn't be turn on again from here */
- if (vddio && priv->current_plug == PLUG_TYPE_HEADSET)
- __taiko_codec_switch_micbias(priv->codec, 1, true, true);
- TAIKO_RELEASE_LOCK(priv->codec_resource_lock);
-
- return IRQ_HANDLED;
-}
-
-static void taiko_mbhc_insert_work(struct work_struct *work)
-{
- struct delayed_work *dwork;
- struct taiko_priv *taiko;
- struct snd_soc_codec *codec;
- struct wcd9xxx *taiko_core;
-
- dwork = to_delayed_work(work);
- taiko = container_of(dwork, struct taiko_priv, mbhc_insert_dwork);
- codec = taiko->codec;
- taiko_core = dev_get_drvdata(codec->dev->parent);
-
- pr_debug("%s:\n", __func__);
-
- /* Turn off both HPH and MIC line schmitt triggers */
- snd_soc_update_bits(codec, taiko->mbhc_bias_regs.mbhc_reg, 0x90, 0x00);
- snd_soc_update_bits(codec, TAIKO_A_MBHC_HPH, 0x13, 0x00);
- snd_soc_update_bits(codec, taiko->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
- wcd9xxx_disable_irq_sync(codec->control_data,
- WCD9XXX_IRQ_MBHC_INSERTION);
- taiko_codec_detect_plug_type(codec);
- wcd9xxx_unlock_sleep(taiko_core);
-}
-
-static void taiko_hs_gpio_handler(struct snd_soc_codec *codec)
-{
- bool insert;
- struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
- bool is_removed = false;
-
- pr_debug("%s: enter\n", __func__);
-
- taiko->in_gpio_handler = true;
- /* Wait here for debounce time */
- usleep_range(TAIKO_GPIO_IRQ_DEBOUNCE_TIME_US,
- TAIKO_GPIO_IRQ_DEBOUNCE_TIME_US);
-
- TAIKO_ACQUIRE_LOCK(taiko->codec_resource_lock);
-
- /* cancel pending button press */
- if (taiko_cancel_btn_work(taiko))
- pr_debug("%s: button press is canceled\n", __func__);
-
- insert = (gpio_get_value_cansleep(taiko->mbhc_cfg.gpio) ==
- taiko->mbhc_cfg.gpio_level_insert);
- if ((taiko->current_plug == PLUG_TYPE_NONE) && insert) {
- taiko->lpi_enabled = false;
- wmb();
-
- /* cancel detect plug */
- taiko_cancel_hs_detect_plug(taiko);
-
- /* Disable Mic Bias pull down and HPH Switch to GND */
- snd_soc_update_bits(codec, taiko->mbhc_bias_regs.ctl_reg, 0x01,
- 0x00);
- snd_soc_update_bits(codec, TAIKO_A_MBHC_HPH, 0x01, 0x00);
- taiko_codec_detect_plug_type(codec);
- } else if ((taiko->current_plug != PLUG_TYPE_NONE) && !insert) {
- taiko->lpi_enabled = false;
- wmb();
-
- /* cancel detect plug */
- taiko_cancel_hs_detect_plug(taiko);
-
- if (taiko->current_plug == PLUG_TYPE_HEADPHONE) {
- taiko_codec_report_plug(codec, 0, SND_JACK_HEADPHONE);
- is_removed = true;
- } else if (taiko->current_plug == PLUG_TYPE_GND_MIC_SWAP) {
- taiko_codec_report_plug(codec, 0, SND_JACK_UNSUPPORTED);
- is_removed = true;
- } else if (taiko->current_plug == PLUG_TYPE_HEADSET) {
- taiko_codec_pause_hs_polling(codec);
- taiko_codec_cleanup_hs_polling(codec);
- taiko_codec_report_plug(codec, 0, SND_JACK_HEADSET);
- is_removed = true;
- }
-
- if (is_removed) {
- /* Enable Mic Bias pull down and HPH Switch to GND */
- snd_soc_update_bits(codec,
- taiko->mbhc_bias_regs.ctl_reg, 0x01,
- 0x01);
- snd_soc_update_bits(codec, TAIKO_A_MBHC_HPH, 0x01,
- 0x01);
- /* Make sure mic trigger is turned off */
- snd_soc_update_bits(codec,
- taiko->mbhc_bias_regs.ctl_reg,
- 0x01, 0x01);
- snd_soc_update_bits(codec,
- taiko->mbhc_bias_regs.mbhc_reg,
- 0x90, 0x00);
- /* Reset MBHC State Machine */
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL,
- 0x08, 0x08);
- snd_soc_update_bits(codec, TAIKO_A_CDC_MBHC_CLK_CTL,
- 0x08, 0x00);
- /* Turn off override */
- taiko_turn_onoff_override(codec, false);
- }
- }
-
- taiko->in_gpio_handler = false;
- TAIKO_RELEASE_LOCK(taiko->codec_resource_lock);
- pr_debug("%s: leave\n", __func__);
-}
-
-static irqreturn_t taiko_mechanical_plug_detect_irq(int irq, void *data)
-{
- int r = IRQ_HANDLED;
- struct snd_soc_codec *codec = data;
-
- if (unlikely(wcd9xxx_lock_sleep(codec->control_data) == false)) {
- pr_warn("%s: failed to hold suspend\n", __func__);
- r = IRQ_NONE;
- } else {
- taiko_hs_gpio_handler(codec);
- wcd9xxx_unlock_sleep(codec->control_data);
- }
-
- return r;
-}
-
-static int taiko_mbhc_init_and_calibrate(struct taiko_priv *taiko)
-{
- int ret = 0;
- struct snd_soc_codec *codec = taiko->codec;
-
- taiko->mbhc_cfg.mclk_cb_fn(codec, 1, false);
- taiko_mbhc_init(codec);
- taiko_mbhc_cal(codec);
- taiko_mbhc_calc_thres(codec);
- taiko->mbhc_cfg.mclk_cb_fn(codec, 0, false);
- taiko_codec_calibrate_hs_polling(codec);
- if (!taiko->mbhc_cfg.gpio) {
- ret = taiko_codec_enable_hs_detect(codec, 1,
- MBHC_USE_MB_TRIGGER |
- MBHC_USE_HPHL_TRIGGER,
- false);
-
- if (IS_ERR_VALUE(ret))
- pr_err("%s: Failed to setup MBHC detection\n",
- __func__);
- } else {
- /* Enable Mic Bias pull down and HPH Switch to GND */
- snd_soc_update_bits(codec, taiko->mbhc_bias_regs.ctl_reg,
- 0x01, 0x01);
- snd_soc_update_bits(codec, TAIKO_A_MBHC_HPH, 0x01, 0x01);
- INIT_WORK(&taiko->hs_correct_plug_work,
- taiko_hs_correct_gpio_plug);
- }
-
- if (!IS_ERR_VALUE(ret)) {
- snd_soc_update_bits(codec, TAIKO_A_RX_HPH_OCP_CTL, 0x10, 0x10);
- wcd9xxx_enable_irq(codec->control_data,
- WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
- wcd9xxx_enable_irq(codec->control_data,
- WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
-
- if (taiko->mbhc_cfg.gpio) {
- ret = request_threaded_irq(taiko->mbhc_cfg.gpio_irq,
- NULL,
- taiko_mechanical_plug_detect_irq,
- (IRQF_TRIGGER_RISING |
- IRQF_TRIGGER_FALLING),
- "taiko-gpio", codec);
- if (!IS_ERR_VALUE(ret)) {
- ret = enable_irq_wake(taiko->mbhc_cfg.gpio_irq);
- /* Bootup time detection */
- taiko_hs_gpio_handler(codec);
- }
- }
- }
-
- return ret;
-}
-
-static void mbhc_fw_read(struct work_struct *work)
-{
- struct delayed_work *dwork;
- struct taiko_priv *taiko;
- struct snd_soc_codec *codec;
- const struct firmware *fw;
- int ret = -1, retry = 0;
-
- dwork = to_delayed_work(work);
- taiko = container_of(dwork, struct taiko_priv, mbhc_firmware_dwork);
- codec = taiko->codec;
-
- while (retry < MBHC_FW_READ_ATTEMPTS) {
- retry++;
- pr_info("%s:Attempt %d to request MBHC firmware\n",
- __func__, retry);
- ret = request_firmware(&fw, "wcd9320/wcd9320_mbhc.bin",
- codec->dev);
-
- if (ret != 0) {
- usleep_range(MBHC_FW_READ_TIMEOUT,
- MBHC_FW_READ_TIMEOUT);
- } else {
- pr_info("%s: MBHC Firmware read succesful\n", __func__);
- break;
- }
- }
-
- if (ret != 0) {
- pr_err("%s: Cannot load MBHC firmware use default cal\n",
- __func__);
- } else if (taiko_mbhc_fw_validate(fw) == false) {
- pr_err("%s: Invalid MBHC cal data size use default cal\n",
- __func__);
- release_firmware(fw);
- } else {
- taiko->mbhc_cfg.calibration = (void *)fw->data;
- taiko->mbhc_fw = fw;
- }
-
- (void) taiko_mbhc_init_and_calibrate(taiko);
-}
-
-int taiko_hs_detect(struct snd_soc_codec *codec,
- const struct taiko_mbhc_config *cfg)
-{
- struct taiko_priv *taiko;
- int rc = 0;
-
- if (!codec) {
- pr_err("%s: no codec\n", __func__);
- return -EINVAL;
- }
-
- if (!cfg->calibration) {
- pr_warn("%s: mbhc is not configured\n", __func__);
- return 0;
- }
-
- if (cfg->mclk_rate != TAIKO_MCLK_RATE_12288KHZ) {
- if (cfg->mclk_rate == TAIKO_MCLK_RATE_9600KHZ)
- pr_err("Error: clock rate %dHz is not yet supported\n",
- cfg->mclk_rate);
- else
- pr_err("Error: unsupported clock rate %d\n",
- cfg->mclk_rate);
- return -EINVAL;
- }
-
- taiko = snd_soc_codec_get_drvdata(codec);
- taiko->mbhc_cfg = *cfg;
- taiko->in_gpio_handler = false;
- taiko->current_plug = PLUG_TYPE_NONE;
- taiko->lpi_enabled = false;
- taiko_get_mbhc_micbias_regs(codec, &taiko->mbhc_bias_regs);
-
- /* Put CFILT in fast mode by default */
- snd_soc_update_bits(codec, taiko->mbhc_bias_regs.cfilt_ctl,
- 0x40, TAIKO_CFILT_FAST_MODE);
- INIT_DELAYED_WORK(&taiko->mbhc_firmware_dwork, mbhc_fw_read);
- INIT_DELAYED_WORK(&taiko->mbhc_btn_dwork, btn_lpress_fn);
- INIT_WORK(&taiko->hphlocp_work, hphlocp_off_report);
- INIT_WORK(&taiko->hphrocp_work, hphrocp_off_report);
- INIT_DELAYED_WORK(&taiko->mbhc_insert_dwork, taiko_mbhc_insert_work);
-
- if (!taiko->mbhc_cfg.read_fw_bin)
- rc = taiko_mbhc_init_and_calibrate(taiko);
- else
- schedule_delayed_work(&taiko->mbhc_firmware_dwork,
- usecs_to_jiffies(MBHC_FW_READ_TIMEOUT));
-
- return rc;
-}
-EXPORT_SYMBOL_GPL(taiko_hs_detect);
-
static unsigned long slimbus_value;
static irqreturn_t taiko_slimbus_irq(int irq, void *data)
@@ -7285,8 +4180,8 @@
}
wcd9xxx_interface_reg_write(codec->control_data,
TAIKO_SLIM_PGD_PORT_INT_CLR0 + i, 0xFF);
- }
+ }
return IRQ_HANDLED;
}
@@ -7317,7 +4212,6 @@
/** end of Ear PA load 32 */
};
-
static const struct taiko_reg_mask_val taiko_1_0_class_h_hph[] = {
/* CLASS-H HPH IDLE_THRESHOLD Table */
@@ -7372,7 +4266,7 @@
static int taiko_handle_pdata(struct taiko_priv *taiko)
{
struct snd_soc_codec *codec = taiko->codec;
- struct wcd9xxx_pdata *pdata = taiko->pdata;
+ struct wcd9xxx_pdata *pdata = taiko->resmgr.pdata;
int k1, k2, k3, rc = 0;
u8 leg_mode, txfe_bypass, txfe_buff, flag;
u8 i = 0, j = 0;
@@ -7390,46 +4284,38 @@
flag = pdata->amic_settings.use_pdata;
/* Make sure settings are correct */
- if ((pdata->micbias.ldoh_v > TAIKO_LDOH_2P85_V) ||
- (pdata->micbias.bias1_cfilt_sel > TAIKO_CFILT3_SEL) ||
- (pdata->micbias.bias2_cfilt_sel > TAIKO_CFILT3_SEL) ||
- (pdata->micbias.bias3_cfilt_sel > TAIKO_CFILT3_SEL) ||
- (pdata->micbias.bias4_cfilt_sel > TAIKO_CFILT3_SEL)) {
+ if ((pdata->micbias.ldoh_v > WCD9XXX_LDOH_3P0_V) ||
+ (pdata->micbias.bias1_cfilt_sel > WCD9XXX_CFILT3_SEL) ||
+ (pdata->micbias.bias2_cfilt_sel > WCD9XXX_CFILT3_SEL) ||
+ (pdata->micbias.bias3_cfilt_sel > WCD9XXX_CFILT3_SEL) ||
+ (pdata->micbias.bias4_cfilt_sel > WCD9XXX_CFILT3_SEL)) {
rc = -EINVAL;
goto done;
}
-
/* figure out k value */
- k1 = taiko_find_k_value(pdata->micbias.ldoh_v,
- pdata->micbias.cfilt1_mv);
- k2 = taiko_find_k_value(pdata->micbias.ldoh_v,
- pdata->micbias.cfilt2_mv);
- k3 = taiko_find_k_value(pdata->micbias.ldoh_v,
- pdata->micbias.cfilt3_mv);
+ k1 = wcd9xxx_resmgr_get_k_val(&taiko->resmgr, pdata->micbias.cfilt1_mv);
+ k2 = wcd9xxx_resmgr_get_k_val(&taiko->resmgr, pdata->micbias.cfilt2_mv);
+ k3 = wcd9xxx_resmgr_get_k_val(&taiko->resmgr, pdata->micbias.cfilt3_mv);
if (IS_ERR_VALUE(k1) || IS_ERR_VALUE(k2) || IS_ERR_VALUE(k3)) {
rc = -EINVAL;
goto done;
}
-
/* Set voltage level and always use LDO */
snd_soc_update_bits(codec, TAIKO_A_LDO_H_MODE_1, 0x0C,
- (pdata->micbias.ldoh_v << 2));
+ (pdata->micbias.ldoh_v << 2));
- snd_soc_update_bits(codec, TAIKO_A_MICB_CFILT_1_VAL, 0xFC,
- (k1 << 2));
- snd_soc_update_bits(codec, TAIKO_A_MICB_CFILT_2_VAL, 0xFC,
- (k2 << 2));
- snd_soc_update_bits(codec, TAIKO_A_MICB_CFILT_3_VAL, 0xFC,
- (k3 << 2));
+ snd_soc_update_bits(codec, TAIKO_A_MICB_CFILT_1_VAL, 0xFC, (k1 << 2));
+ snd_soc_update_bits(codec, TAIKO_A_MICB_CFILT_2_VAL, 0xFC, (k2 << 2));
+ snd_soc_update_bits(codec, TAIKO_A_MICB_CFILT_3_VAL, 0xFC, (k3 << 2));
snd_soc_update_bits(codec, TAIKO_A_MICB_1_CTL, 0x60,
- (pdata->micbias.bias1_cfilt_sel << 5));
+ (pdata->micbias.bias1_cfilt_sel << 5));
snd_soc_update_bits(codec, TAIKO_A_MICB_2_CTL, 0x60,
- (pdata->micbias.bias2_cfilt_sel << 5));
+ (pdata->micbias.bias2_cfilt_sel << 5));
snd_soc_update_bits(codec, TAIKO_A_MICB_3_CTL, 0x60,
- (pdata->micbias.bias3_cfilt_sel << 5));
- snd_soc_update_bits(codec, taiko->reg_addr.micb_4_ctl, 0x60,
+ (pdata->micbias.bias3_cfilt_sel << 5));
+ snd_soc_update_bits(codec, taiko->resmgr.reg_addr->micb_4_ctl, 0x60,
(pdata->micbias.bias4_cfilt_sel << 5));
for (i = 0; i < 6; j++, i += 2) {
@@ -7672,230 +4558,48 @@
taiko_codec_reg_init_val[i].val);
}
-static void taiko_update_reg_address(struct taiko_priv *priv)
-{
- struct taiko_reg_address *reg_addr = &priv->reg_addr;
- reg_addr->micb_4_mbhc = TAIKO_A_MICB_4_MBHC;
- reg_addr->micb_4_int_rbias = TAIKO_A_MICB_4_INT_RBIAS;
- reg_addr->micb_4_ctl = TAIKO_A_MICB_4_CTL;
-
-}
-
-#ifdef CONFIG_DEBUG_FS
-static int codec_debug_open(struct inode *inode, struct file *file)
-{
- file->private_data = inode->i_private;
- return 0;
-}
-
-static ssize_t codec_debug_write(struct file *filp,
- const char __user *ubuf, size_t cnt, loff_t *ppos)
-{
- char lbuf[32];
- char *buf;
- int rc;
- struct taiko_priv *taiko = filp->private_data;
-
- if (cnt > sizeof(lbuf) - 1)
- return -EINVAL;
-
- rc = copy_from_user(lbuf, ubuf, cnt);
- if (rc)
- return -EFAULT;
-
- lbuf[cnt] = '\0';
- buf = (char *)lbuf;
- taiko->no_mic_headset_override = (*strsep(&buf, " ") == '0') ?
- false : true;
- return rc;
-}
-
-static ssize_t codec_mbhc_debug_read(struct file *file, char __user *buf,
- size_t count, loff_t *pos)
-{
- const int size = 768;
- char buffer[size];
- int n = 0;
- struct taiko_priv *taiko = file->private_data;
- struct snd_soc_codec *codec = taiko->codec;
- const struct mbhc_internal_cal_data *p = &taiko->mbhc_data;
- const s16 v_ins_hu_cur = taiko_get_current_v_ins(taiko, true);
- const s16 v_ins_h_cur = taiko_get_current_v_ins(taiko, false);
-
- n = scnprintf(buffer, size - n, "dce_z = %x(%dmv)\n", p->dce_z,
- taiko_codec_sta_dce_v(codec, 1, p->dce_z));
- n += scnprintf(buffer + n, size - n, "dce_mb = %x(%dmv)\n",
- p->dce_mb, taiko_codec_sta_dce_v(codec, 1, p->dce_mb));
- n += scnprintf(buffer + n, size - n, "sta_z = %x(%dmv)\n",
- p->sta_z, taiko_codec_sta_dce_v(codec, 0, p->sta_z));
- n += scnprintf(buffer + n, size - n, "sta_mb = %x(%dmv)\n",
- p->sta_mb, taiko_codec_sta_dce_v(codec, 0, p->sta_mb));
- n += scnprintf(buffer + n, size - n, "t_dce = %x\n", p->t_dce);
- n += scnprintf(buffer + n, size - n, "t_sta = %x\n", p->t_sta);
- n += scnprintf(buffer + n, size - n, "micb_mv = %dmv\n",
- p->micb_mv);
- n += scnprintf(buffer + n, size - n, "v_ins_hu = %x(%dmv)%s\n",
- p->v_ins_hu,
- taiko_codec_sta_dce_v(codec, 0, p->v_ins_hu),
- p->v_ins_hu == v_ins_hu_cur ? "*" : "");
- n += scnprintf(buffer + n, size - n, "v_ins_h = %x(%dmv)%s\n",
- p->v_ins_h, taiko_codec_sta_dce_v(codec, 1, p->v_ins_h),
- p->v_ins_h == v_ins_h_cur ? "*" : "");
- n += scnprintf(buffer + n, size - n, "adj_v_ins_hu = %x(%dmv)%s\n",
- p->adj_v_ins_hu,
- taiko_codec_sta_dce_v(codec, 0, p->adj_v_ins_hu),
- p->adj_v_ins_hu == v_ins_hu_cur ? "*" : "");
- n += scnprintf(buffer + n, size - n, "adj_v_ins_h = %x(%dmv)%s\n",
- p->adj_v_ins_h,
- taiko_codec_sta_dce_v(codec, 1, p->adj_v_ins_h),
- p->adj_v_ins_h == v_ins_h_cur ? "*" : "");
- n += scnprintf(buffer + n, size - n, "v_b1_hu = %x(%dmv)\n",
- p->v_b1_hu, taiko_codec_sta_dce_v(codec, 0, p->v_b1_hu));
- n += scnprintf(buffer + n, size - n, "v_b1_h = %x(%dmv)\n",
- p->v_b1_h, taiko_codec_sta_dce_v(codec, 1, p->v_b1_h));
- n += scnprintf(buffer + n, size - n, "v_b1_huc = %x(%dmv)\n",
- p->v_b1_huc,
- taiko_codec_sta_dce_v(codec, 1, p->v_b1_huc));
- n += scnprintf(buffer + n, size - n, "v_brh = %x(%dmv)\n",
- p->v_brh, taiko_codec_sta_dce_v(codec, 1, p->v_brh));
- n += scnprintf(buffer + n, size - n, "v_brl = %x(%dmv)\n", p->v_brl,
- taiko_codec_sta_dce_v(codec, 0, p->v_brl));
- n += scnprintf(buffer + n, size - n, "v_no_mic = %x(%dmv)\n",
- p->v_no_mic,
- taiko_codec_sta_dce_v(codec, 0, p->v_no_mic));
- n += scnprintf(buffer + n, size - n, "npoll = %d\n", p->npoll);
- n += scnprintf(buffer + n, size - n, "nbounce_wait = %d\n",
- p->nbounce_wait);
- n += scnprintf(buffer + n, size - n, "v_inval_ins_low = %d\n",
- p->v_inval_ins_low);
- n += scnprintf(buffer + n, size - n, "v_inval_ins_high = %d\n",
- p->v_inval_ins_high);
- if (taiko->mbhc_cfg.gpio)
- n += scnprintf(buffer + n, size - n, "GPIO insert = %d\n",
- taiko_hs_gpio_level_remove(taiko));
- buffer[n] = 0;
-
- return simple_read_from_buffer(buf, count, pos, buffer, n);
-}
-
-static const struct file_operations codec_debug_ops = {
- .open = codec_debug_open,
- .write = codec_debug_write,
-};
-
-static const struct file_operations codec_mbhc_debug_ops = {
- .open = codec_debug_open,
- .read = codec_mbhc_debug_read,
-};
-#endif
-
static int taiko_setup_irqs(struct taiko_priv *taiko)
{
- int ret;
int i;
+ int ret = 0;
struct snd_soc_codec *codec = taiko->codec;
- ret = wcd9xxx_request_irq(codec->control_data,
- WCD9XXX_IRQ_MBHC_INSERTION,
- taiko_hs_insert_irq, "Headset insert detect",
- taiko);
- if (ret) {
- pr_err("%s: Failed to request irq %d\n", __func__,
- WCD9XXX_IRQ_MBHC_INSERTION);
- goto err_insert_irq;
- }
- wcd9xxx_disable_irq(codec->control_data,
- WCD9XXX_IRQ_MBHC_INSERTION);
-
- ret = wcd9xxx_request_irq(codec->control_data, WCD9XXX_IRQ_MBHC_REMOVAL,
- taiko_hs_remove_irq, "Headset remove detect",
- taiko);
- if (ret) {
- pr_err("%s: Failed to request irq %d\n", __func__,
- WCD9XXX_IRQ_MBHC_REMOVAL);
- goto err_remove_irq;
- }
-
- ret = wcd9xxx_request_irq(codec->control_data,
- WCD9XXX_IRQ_MBHC_POTENTIAL,
- taiko_dce_handler, "DC Estimation detect",
- taiko);
- if (ret) {
- pr_err("%s: Failed to request irq %d\n", __func__,
- WCD9XXX_IRQ_MBHC_POTENTIAL);
- goto err_potential_irq;
- }
-
- ret = wcd9xxx_request_irq(codec->control_data, WCD9XXX_IRQ_MBHC_RELEASE,
- taiko_release_handler, "Button Release detect",
- taiko);
- if (ret) {
- pr_err("%s: Failed to request irq %d\n", __func__,
- WCD9XXX_IRQ_MBHC_RELEASE);
- goto err_release_irq;
- }
-
ret = wcd9xxx_request_irq(codec->control_data, WCD9XXX_IRQ_SLIMBUS,
taiko_slimbus_irq, "SLIMBUS Slave", taiko);
if (ret) {
pr_err("%s: Failed to request irq %d\n", __func__,
WCD9XXX_IRQ_SLIMBUS);
- goto err_slimbus_irq;
+ goto exit;
}
for (i = 0; i < WCD9XXX_SLIM_NUM_PORT_REG; i++)
wcd9xxx_interface_reg_write(codec->control_data,
- TAIKO_SLIM_PGD_PORT_INT_EN0 + i,
- 0xFF);
-
- ret = wcd9xxx_request_irq(codec->control_data,
- WCD9XXX_IRQ_HPH_PA_OCPL_FAULT,
- taiko_hphl_ocp_irq,
- "HPH_L OCP detect", taiko);
- if (ret) {
- pr_err("%s: Failed to request irq %d\n", __func__,
- WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
- goto err_hphl_ocp_irq;
- }
- wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
-
- ret = wcd9xxx_request_irq(codec->control_data,
- WCD9XXX_IRQ_HPH_PA_OCPR_FAULT,
- taiko_hphr_ocp_irq,
- "HPH_R OCP detect", taiko);
- if (ret) {
- pr_err("%s: Failed to request irq %d\n", __func__,
- WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
- goto err_hphr_ocp_irq;
- }
- wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
-
- return 0;
-
-err_hphr_ocp_irq:
- wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_HPH_PA_OCPL_FAULT,
- taiko);
-err_hphl_ocp_irq:
- wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_SLIMBUS, taiko);
-err_slimbus_irq:
- wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_RELEASE, taiko);
-err_release_irq:
- wcd9xxx_free_irq(codec->control_data,
- WCD9XXX_IRQ_MBHC_POTENTIAL, taiko);
-err_potential_irq:
- wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_REMOVAL, taiko);
-err_remove_irq:
- wcd9xxx_free_irq(codec->control_data,
- WCD9XXX_IRQ_MBHC_INSERTION, taiko);
-err_insert_irq:
-
+ TAIKO_SLIM_PGD_PORT_INT_EN0 + i,
+ 0xFF);
+exit:
return ret;
}
+int taiko_hs_detect(struct snd_soc_codec *codec,
+ struct wcd9xxx_mbhc_config *mbhc_cfg)
+{
+ struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
+ return wcd9xxx_mbhc_start(&taiko->mbhc, mbhc_cfg);
+}
+EXPORT_SYMBOL_GPL(taiko_hs_detect);
+
+static struct wcd9xxx_reg_address taiko_reg_address = {
+ .micb_4_mbhc = TAIKO_A_MICB_4_MBHC,
+ .micb_4_int_rbias = TAIKO_A_MICB_4_INT_RBIAS,
+ .micb_4_ctl = TAIKO_A_MICB_4_CTL,
+};
+
static int taiko_codec_probe(struct snd_soc_codec *codec)
{
struct wcd9xxx *control;
struct taiko_priv *taiko;
+ struct wcd9xxx_pdata *pdata;
+ struct wcd9xxx *wcd9xxx;
struct snd_soc_dapm_context *dapm = &codec->dapm;
int ret = 0;
int i;
@@ -7918,41 +4622,34 @@
tx_hpf_corner_freq_callback);
}
- /* Make sure mbhc micbias register addresses are zeroed out */
- memset(&taiko->mbhc_bias_regs, 0,
- sizeof(struct mbhc_micbias_regs));
- taiko->mbhc_micbias_switched = false;
-
- /* Make sure mbhc intenal calibration data is zeroed out */
- memset(&taiko->mbhc_data, 0,
- sizeof(struct mbhc_internal_cal_data));
- taiko->mbhc_data.t_sta_dce = DEFAULT_DCE_STA_WAIT;
- taiko->mbhc_data.t_dce = DEFAULT_DCE_WAIT;
- taiko->mbhc_data.t_sta = DEFAULT_STA_WAIT;
snd_soc_codec_set_drvdata(codec, taiko);
- taiko->mclk_enabled = false;
- taiko->bandgap_type = TAIKO_BANDGAP_OFF;
- taiko->clock_active = false;
- taiko->config_mode_active = false;
- taiko->mbhc_polling_active = false;
- taiko->mbhc_fake_ins_start = 0;
- taiko->no_mic_headset_override = false;
- taiko->hs_polling_irq_prepared = false;
- mutex_init(&taiko->codec_resource_lock);
+ /* codec resmgr module init */
+ wcd9xxx = codec->control_data;
+ pdata = dev_get_platdata(codec->dev->parent);
+ ret = wcd9xxx_resmgr_init(&taiko->resmgr, codec, wcd9xxx, pdata,
+ &taiko_reg_address);
+ if (ret) {
+ pr_err("%s: wcd9xxx init failed %d\n", __func__, ret);
+ return ret;
+ }
+
+ /* init and start mbhc */
+ ret = wcd9xxx_mbhc_init(&taiko->mbhc, &taiko->resmgr, codec);
+ if (ret) {
+ pr_err("%s: mbhc init failed %d\n", __func__, ret);
+ return ret;
+ }
+
taiko->codec = codec;
- taiko->mbhc_state = MBHC_STATE_NONE;
- taiko->mbhc_last_resume = 0;
for (i = 0; i < COMPANDER_MAX; i++) {
taiko->comp_enabled[i] = 0;
taiko->comp_fs[i] = COMPANDER_FS_48KHZ;
}
- taiko->pdata = dev_get_platdata(codec->dev->parent);
taiko->intf_type = wcd9xxx_get_intf_type();
taiko->aux_pga_cnt = 0;
taiko->aux_l_gain = 0x1F;
taiko->aux_r_gain = 0x1F;
- taiko_update_reg_address(taiko);
taiko_update_reg_defaults(codec);
taiko_codec_init_reg(codec);
ret = taiko_handle_pdata(taiko);
@@ -7994,48 +4691,24 @@
(void) taiko_setup_irqs(taiko);
-
-#ifdef CONFIG_DEBUG_FS
- if (ret == 0) {
- taiko->debugfs_poke =
- debugfs_create_file("TRRS", S_IFREG | S_IRUGO, NULL, taiko,
- &codec_debug_ops);
- taiko->debugfs_mbhc =
- debugfs_create_file("taiko_mbhc", S_IFREG | S_IRUGO,
- NULL, taiko, &codec_mbhc_debug_ops);
- }
-#endif
codec->ignore_pmdown_time = 1;
return ret;
err_pdata:
kfree(ptr);
err_nomem_slimch:
- mutex_destroy(&taiko->codec_resource_lock);
kfree(taiko);
return ret;
}
static int taiko_codec_remove(struct snd_soc_codec *codec)
{
struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
- wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_SLIMBUS, taiko);
- wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_RELEASE, taiko);
- wcd9xxx_free_irq(codec->control_data,
- WCD9XXX_IRQ_MBHC_POTENTIAL, taiko);
- wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_MBHC_REMOVAL, taiko);
- wcd9xxx_free_irq(codec->control_data,
- WCD9XXX_IRQ_MBHC_INSERTION, taiko);
- TAIKO_ACQUIRE_LOCK(taiko->codec_resource_lock);
- taiko_codec_disable_clock_block(codec);
- TAIKO_RELEASE_LOCK(taiko->codec_resource_lock);
- taiko_codec_enable_bandgap(codec, TAIKO_BANDGAP_OFF);
- if (taiko->mbhc_fw)
- release_firmware(taiko->mbhc_fw);
- mutex_destroy(&taiko->codec_resource_lock);
-#ifdef CONFIG_DEBUG_FS
- debugfs_remove(taiko->debugfs_poke);
- debugfs_remove(taiko->debugfs_mbhc);
-#endif
+
+ /* cleanup MBHC */
+ wcd9xxx_mbhc_deinit(&taiko->mbhc);
+ /* cleanup resmgr */
+ wcd9xxx_resmgr_deinit(&taiko->resmgr);
+
kfree(taiko);
return 0;
}
@@ -8073,7 +4746,8 @@
struct platform_device *pdev = to_platform_device(dev);
struct taiko_priv *taiko = platform_get_drvdata(pdev);
dev_dbg(dev, "%s: system resume\n", __func__);
- taiko->mbhc_last_resume = jiffies;
+ /* Notify */
+ wcd9xxx_resmgr_notifier_call(&taiko->resmgr, WCD9XXX_EVENT_POST_RESUME);
return 0;
}
diff --git a/sound/soc/codecs/wcd9320.h b/sound/soc/codecs/wcd9320.h
index 6f4b0cf..7bc5a57 100644
--- a/sound/soc/codecs/wcd9320.h
+++ b/sound/soc/codecs/wcd9320.h
@@ -15,6 +15,8 @@
#include <sound/soc.h>
#include <sound/jack.h>
#include <linux/mfd/wcd9xxx/wcd9xxx-slimslave.h>
+#include "wcd9xxx-mbhc.h"
+#include "wcd9xxx-resmgr.h"
#define TAIKO_NUM_REGISTERS 0x400
#define TAIKO_MAX_REGISTER (TAIKO_NUM_REGISTERS-1)
@@ -22,27 +24,13 @@
#define TAIKO_REG_VAL(reg, val) {reg, 0, val}
-#define DEFAULT_DCE_STA_WAIT 55
-#define DEFAULT_DCE_WAIT 60000
-#define DEFAULT_STA_WAIT 5000
-#define VDDIO_MICBIAS_MV 1800
-
-#define STA 0
-#define DCE 1
-
-#define TAIKO_JACK_BUTTON_MASK (SND_JACK_BTN_0 | SND_JACK_BTN_1 | \
- SND_JACK_BTN_2 | SND_JACK_BTN_3 | \
- SND_JACK_BTN_4 | SND_JACK_BTN_5 | \
- SND_JACK_BTN_6 | SND_JACK_BTN_7)
-
extern const u8 taiko_reg_readable[TAIKO_CACHE_SIZE];
extern const u8 taiko_reset_reg_defaults[TAIKO_CACHE_SIZE];
-
-enum taiko_micbias_num {
- TAIKO_MICBIAS1 = 0,
- TAIKO_MICBIAS2,
- TAIKO_MICBIAS3,
- TAIKO_MICBIAS4,
+struct taiko_codec_dai_data {
+ u32 rate;
+ u32 *ch_num;
+ u32 ch_act;
+ u32 ch_tot;
};
enum taiko_pid_current {
@@ -58,26 +46,12 @@
u8 val;
};
-enum taiko_mbhc_clk_freq {
- TAIKO_MCLK_12P2MHZ = 0,
- TAIKO_MCLK_9P6MHZ,
- TAIKO_NUM_CLK_FREQS,
-};
-
enum taiko_mbhc_analog_pwr_cfg {
TAIKO_ANALOG_PWR_COLLAPSED = 0,
TAIKO_ANALOG_PWR_ON,
TAIKO_NUM_ANALOG_PWR_CONFIGS,
};
-enum taiko_mbhc_btn_det_mem {
- TAIKO_BTN_DET_V_BTN_LOW,
- TAIKO_BTN_DET_V_BTN_HIGH,
- TAIKO_BTN_DET_N_READY,
- TAIKO_BTN_DET_N_CIC,
- TAIKO_BTN_DET_GAIN
-};
-
/* Number of input and output Slimbus port */
enum {
TAIKO_RX1 = 0,
@@ -116,110 +90,6 @@
TAIKO_TX_MAX,
};
-struct taiko_mbhc_general_cfg {
- u8 t_ldoh;
- u8 t_bg_fast_settle;
- u8 t_shutdown_plug_rem;
- u8 mbhc_nsa;
- u8 mbhc_navg;
- u8 v_micbias_l;
- u8 v_micbias;
- u8 mbhc_reserved;
- u16 settle_wait;
- u16 t_micbias_rampup;
- u16 t_micbias_rampdown;
- u16 t_supply_bringup;
-} __packed;
-
-struct taiko_mbhc_plug_detect_cfg {
- u32 mic_current;
- u32 hph_current;
- u16 t_mic_pid;
- u16 t_ins_complete;
- u16 t_ins_retry;
- u16 v_removal_delta;
- u8 micbias_slow_ramp;
- u8 reserved0;
- u8 reserved1;
- u8 reserved2;
-} __packed;
-
-struct taiko_mbhc_plug_type_cfg {
- u8 av_detect;
- u8 mono_detect;
- u8 num_ins_tries;
- u8 reserved0;
- s16 v_no_mic;
- s16 v_av_min;
- s16 v_av_max;
- s16 v_hs_min;
- s16 v_hs_max;
- u16 reserved1;
-} __packed;
-
-
-struct taiko_mbhc_btn_detect_cfg {
- s8 c[8];
- u8 nc;
- u8 n_meas;
- u8 mbhc_nsc;
- u8 n_btn_meas;
- u8 n_btn_con;
- u8 num_btn;
- u8 reserved0;
- u8 reserved1;
- u16 t_poll;
- u16 t_bounce_wait;
- u16 t_rel_timeout;
- s16 v_btn_press_delta_sta;
- s16 v_btn_press_delta_cic;
- u16 t_btn0_timeout;
- s16 _v_btn_low[0]; /* v_btn_low[num_btn] */
- s16 _v_btn_high[0]; /* v_btn_high[num_btn] */
- u8 _n_ready[TAIKO_NUM_CLK_FREQS];
- u8 _n_cic[TAIKO_NUM_CLK_FREQS];
- u8 _gain[TAIKO_NUM_CLK_FREQS];
-} __packed;
-
-struct taiko_mbhc_imped_detect_cfg {
- u8 _hs_imped_detect;
- u8 _n_rload;
- u8 _hph_keep_on;
- u8 _repeat_rload_calc;
- u16 _t_dac_ramp_time;
- u16 _rhph_high;
- u16 _rhph_low;
- u16 _rload[0]; /* rload[n_rload] */
- u16 _alpha[0]; /* alpha[n_rload] */
- u16 _beta[3];
-} __packed;
-
-struct taiko_mbhc_config {
- struct snd_soc_jack *headset_jack;
- struct snd_soc_jack *button_jack;
- bool read_fw_bin;
- /* void* calibration contains:
- * struct taiko_mbhc_general_cfg generic;
- * struct taiko_mbhc_plug_detect_cfg plug_det;
- * struct taiko_mbhc_plug_type_cfg plug_type;
- * struct taiko_mbhc_btn_detect_cfg btn_det;
- * struct taiko_mbhc_imped_detect_cfg imped_det;
- * Note: various size depends on btn_det->num_btn
- */
- void *calibration;
- enum taiko_micbias_num micbias;
- int (*mclk_cb_fn) (struct snd_soc_codec*, int, bool);
- unsigned int mclk_rate;
- unsigned int gpio;
- unsigned int gpio_irq;
- int gpio_level_insert;
- /* swap_gnd_mic returns true if extern GND/MIC swap switch toggled */
- bool (*swap_gnd_mic) (struct snd_soc_codec *);
-};
-
-extern int taiko_hs_detect(struct snd_soc_codec *codec,
- const struct taiko_mbhc_config *cfg);
-
struct anc_header {
u32 reserved[3];
u32 num_anc_slots;
@@ -227,64 +97,7 @@
extern int taiko_mclk_enable(struct snd_soc_codec *codec, int mclk_enable,
bool dapm);
-
-extern void *taiko_mbhc_cal_btn_det_mp(const struct taiko_mbhc_btn_detect_cfg
- *btn_det,
- const enum taiko_mbhc_btn_det_mem mem);
-
-#define TAIKO_MBHC_CAL_SIZE(buttons, rload) ( \
- sizeof(enum taiko_micbias_num) + \
- sizeof(struct taiko_mbhc_general_cfg) + \
- sizeof(struct taiko_mbhc_plug_detect_cfg) + \
- ((sizeof(s16) + sizeof(s16)) * buttons) + \
- sizeof(struct taiko_mbhc_plug_type_cfg) + \
- sizeof(struct taiko_mbhc_btn_detect_cfg) + \
- sizeof(struct taiko_mbhc_imped_detect_cfg) + \
- ((sizeof(u16) + sizeof(u16)) * rload) \
- )
-
-#define TAIKO_MBHC_CAL_GENERAL_PTR(cali) ( \
- (struct taiko_mbhc_general_cfg *) cali)
-#define TAIKO_MBHC_CAL_PLUG_DET_PTR(cali) ( \
- (struct taiko_mbhc_plug_detect_cfg *) \
- &(TAIKO_MBHC_CAL_GENERAL_PTR(cali)[1]))
-#define TAIKO_MBHC_CAL_PLUG_TYPE_PTR(cali) ( \
- (struct taiko_mbhc_plug_type_cfg *) \
- &(TAIKO_MBHC_CAL_PLUG_DET_PTR(cali)[1]))
-#define TAIKO_MBHC_CAL_BTN_DET_PTR(cali) ( \
- (struct taiko_mbhc_btn_detect_cfg *) \
- &(TAIKO_MBHC_CAL_PLUG_TYPE_PTR(cali)[1]))
-#define TAIKO_MBHC_CAL_IMPED_DET_PTR(cali) ( \
- (struct taiko_mbhc_imped_detect_cfg *) \
- (((void *)&TAIKO_MBHC_CAL_BTN_DET_PTR(cali)[1]) + \
- (TAIKO_MBHC_CAL_BTN_DET_PTR(cali)->num_btn * \
- (sizeof(TAIKO_MBHC_CAL_BTN_DET_PTR(cali)->_v_btn_low[0]) + \
- sizeof(TAIKO_MBHC_CAL_BTN_DET_PTR(cali)->_v_btn_high[0])))) \
- )
-
-/* minimum size of calibration data assuming there is only one button and
- * one rload.
- */
-#define TAIKO_MBHC_CAL_MIN_SIZE ( \
- sizeof(struct taiko_mbhc_general_cfg) + \
- sizeof(struct taiko_mbhc_plug_detect_cfg) + \
- sizeof(struct taiko_mbhc_plug_type_cfg) + \
- sizeof(struct taiko_mbhc_btn_detect_cfg) + \
- sizeof(struct taiko_mbhc_imped_detect_cfg) + \
- (sizeof(u16) * 2))
-
-#define TAIKO_MBHC_CAL_BTN_SZ(cfg_ptr) ( \
- sizeof(struct taiko_mbhc_btn_detect_cfg) + \
- (cfg_ptr->num_btn * (sizeof(cfg_ptr->_v_btn_low[0]) + \
- sizeof(cfg_ptr->_v_btn_high[0]))))
-
-#define TAIKO_MBHC_CAL_IMPED_MIN_SZ ( \
- sizeof(struct taiko_mbhc_imped_detect_cfg) + \
- sizeof(u16) * 2)
-
-#define TAIKO_MBHC_CAL_IMPED_SZ(cfg_ptr) ( \
- sizeof(struct taiko_mbhc_imped_detect_cfg) + \
- (cfg_ptr->_n_rload * (sizeof(cfg_ptr->_rload[0]) + \
- sizeof(cfg_ptr->_alpha[0]))))
+extern int taiko_hs_detect(struct snd_soc_codec *codec,
+ struct wcd9xxx_mbhc_config *mbhc_cfg);
#endif
diff --git a/sound/soc/codecs/wcd9xxx-mbhc.c b/sound/soc/codecs/wcd9xxx-mbhc.c
new file mode 100644
index 0000000..3d7c0d4
--- /dev/null
+++ b/sound/soc/codecs/wcd9xxx-mbhc.c
@@ -0,0 +1,3299 @@
+/* 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/module.h>
+#include <linux/init.h>
+#include <linux/firmware.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/device.h>
+#include <linux/printk.h>
+#include <linux/ratelimit.h>
+#include <linux/debugfs.h>
+#include <linux/mfd/wcd9xxx/core.h>
+#include <linux/mfd/wcd9xxx/wcd9xxx_registers.h>
+#include <linux/mfd/wcd9xxx/wcd9320_registers.h>
+#include <linux/mfd/wcd9xxx/pdata.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/pm_runtime.h>
+#include <linux/kernel.h>
+#include <linux/gpio.h>
+#include "wcd9320.h"
+#include "wcd9xxx-mbhc.h"
+#include "wcd9xxx-resmgr.h"
+
+#define WCD9XXX_JACK_MASK (SND_JACK_HEADSET | SND_JACK_OC_HPHL | \
+ SND_JACK_OC_HPHR | SND_JACK_LINEOUT | \
+ SND_JACK_UNSUPPORTED)
+#define WCD9XXX_JACK_BUTTON_MASK (SND_JACK_BTN_0 | SND_JACK_BTN_1 | \
+ SND_JACK_BTN_2 | SND_JACK_BTN_3 | \
+ SND_JACK_BTN_4 | SND_JACK_BTN_5 | \
+ SND_JACK_BTN_6 | SND_JACK_BTN_7)
+
+#define NUM_DCE_PLUG_DETECT 3
+#define NUM_ATTEMPTS_INSERT_DETECT 25
+#define NUM_ATTEMPTS_TO_REPORT 5
+
+#define FAKE_INS_LOW 10
+#define FAKE_INS_HIGH 80
+#define FAKE_INS_HIGH_NO_SWCH 150
+#define FAKE_REMOVAL_MIN_PERIOD_MS 50
+#define FAKE_INS_DELTA_SCALED_MV 300
+
+#define BUTTON_MIN 0x8000
+#define STATUS_REL_DETECTION 0x0C
+
+#define HS_DETECT_PLUG_TIME_MS (5 * 1000)
+#define HS_DETECT_PLUG_INERVAL_MS 100
+#define SWCH_REL_DEBOUNCE_TIME_MS 50
+#define SWCH_IRQ_DEBOUNCE_TIME_US 5000
+
+#define GND_MIC_SWAP_THRESHOLD 2
+#define OCP_ATTEMPT 1
+
+#define FW_READ_ATTEMPTS 15
+#define FW_READ_TIMEOUT 2000000
+
+#define BUTTON_POLLING_SUPPORTED false
+
+#define MCLK_RATE_12288KHZ 12288000
+#define MCLK_RATE_9600KHZ 9600000
+#define WCD9XXX_RCO_CLK_RATE MCLK_RATE_12288KHZ
+
+#define DEFAULT_DCE_STA_WAIT 55
+#define DEFAULT_DCE_WAIT 60000
+#define DEFAULT_STA_WAIT 5000
+
+#define VDDIO_MICBIAS_MV 1800
+
+enum meas_type {
+ STA = 0,
+ DCE,
+};
+
+enum {
+ MBHC_USE_HPHL_TRIGGER = 1,
+ MBHC_USE_MB_TRIGGER = 2
+};
+
+/*
+ * Flags to track of PA and DAC state.
+ * PA and DAC should be tracked separately as AUXPGA loopback requires
+ * only PA to be turned on without DAC being on.
+ */
+enum pa_dac_ack_flags {
+ WCD9XXX_HPHL_PA_OFF_ACK = 0,
+ WCD9XXX_HPHR_PA_OFF_ACK,
+ WCD9XXX_HPHL_DAC_OFF_ACK,
+ WCD9XXX_HPHR_DAC_OFF_ACK
+};
+
+static bool wcd9xxx_mbhc_polling(struct wcd9xxx_mbhc *mbhc)
+{
+ return mbhc->polling_active;
+}
+
+static void wcd9xxx_turn_onoff_override(struct snd_soc_codec *codec, bool on)
+{
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x04, on << 2);
+}
+
+/* called under codec_resource_lock acquisition */
+static void wcd9xxx_pause_hs_polling(struct wcd9xxx_mbhc *mbhc)
+{
+ struct snd_soc_codec *codec = mbhc->codec;
+
+ pr_debug("%s: enter\n", __func__);
+ if (!mbhc->polling_active) {
+ pr_debug("polling not active, nothing to pause\n");
+ return;
+ }
+
+ /* Soft reset MBHC block */
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
+ pr_debug("%s: leave\n", __func__);
+}
+
+/* called under codec_resource_lock acquisition */
+static void wcd9xxx_start_hs_polling(struct wcd9xxx_mbhc *mbhc)
+{
+ struct snd_soc_codec *codec = mbhc->codec;
+ int mbhc_state = mbhc->mbhc_state;
+
+ pr_debug("%s: enter\n", __func__);
+ if (!mbhc->polling_active) {
+ pr_debug("Polling is not active, do not start polling\n");
+ return;
+ }
+ snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x84);
+
+ if (!mbhc->no_mic_headset_override &&
+ mbhc_state == MBHC_STATE_POTENTIAL) {
+ pr_debug("%s recovering MBHC state macine\n", __func__);
+ mbhc->mbhc_state = MBHC_STATE_POTENTIAL_RECOVERY;
+ /* set to max button press threshold */
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B2_CTL, 0x7F);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B1_CTL, 0xFF);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL, 0x7F);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL, 0xFF);
+ /* set to max */
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B6_CTL, 0x7F);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B5_CTL, 0xFF);
+ }
+
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x1);
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, 0x0);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x1);
+ pr_debug("%s: leave\n", __func__);
+}
+
+/* called under codec_resource_lock acquisition */
+static void __wcd9xxx_switch_micbias(struct wcd9xxx_mbhc *mbhc,
+ int vddio_switch, bool restartpolling,
+ bool checkpolling)
+{
+ int cfilt_k_val;
+ bool override;
+ struct snd_soc_codec *codec;
+
+ codec = mbhc->codec;
+
+ if (vddio_switch && !mbhc->mbhc_micbias_switched &&
+ (!checkpolling || mbhc->polling_active)) {
+ if (restartpolling)
+ wcd9xxx_pause_hs_polling(mbhc);
+ override = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_CTL) &
+ 0x04;
+ if (!override)
+ wcd9xxx_turn_onoff_override(codec, true);
+ /* Adjust threshold if Mic Bias voltage changes */
+ if (mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) {
+ cfilt_k_val = wcd9xxx_resmgr_get_k_val(mbhc->resmgr,
+ VDDIO_MICBIAS_MV);
+ usleep_range(10000, 10000);
+ snd_soc_update_bits(codec,
+ mbhc->mbhc_bias_regs.cfilt_val,
+ 0xFC, (cfilt_k_val << 2));
+ usleep_range(10000, 10000);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B1_CTL,
+ mbhc->mbhc_data.adj_v_ins_hu & 0xFF);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B2_CTL,
+ (mbhc->mbhc_data.adj_v_ins_hu >> 8) &
+ 0xFF);
+ pr_debug("%s: Programmed MBHC thresholds to VDDIO\n",
+ __func__);
+ }
+
+ /* Enable MIC BIAS Switch to VDDIO */
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
+ 0x80, 0x80);
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
+ 0x10, 0x00);
+ if (!override)
+ wcd9xxx_turn_onoff_override(codec, false);
+ if (restartpolling)
+ wcd9xxx_start_hs_polling(mbhc);
+
+ mbhc->mbhc_micbias_switched = true;
+ pr_debug("%s: VDDIO switch enabled\n", __func__);
+ } else if (!vddio_switch && mbhc->mbhc_micbias_switched) {
+ if ((!checkpolling || mbhc->polling_active) &&
+ restartpolling)
+ wcd9xxx_pause_hs_polling(mbhc);
+ /* Reprogram thresholds */
+ if (mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) {
+ cfilt_k_val =
+ wcd9xxx_resmgr_get_k_val(mbhc->resmgr,
+ mbhc->mbhc_data.micb_mv);
+ snd_soc_update_bits(codec,
+ mbhc->mbhc_bias_regs.cfilt_val,
+ 0xFC, (cfilt_k_val << 2));
+ usleep_range(10000, 10000);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B1_CTL,
+ mbhc->mbhc_data.v_ins_hu & 0xFF);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B2_CTL,
+ (mbhc->mbhc_data.v_ins_hu >> 8) & 0xFF);
+ pr_debug("%s: Programmed MBHC thresholds to MICBIAS\n",
+ __func__);
+ }
+
+ /* Disable MIC BIAS Switch to VDDIO */
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x80,
+ 0x00);
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x10,
+ 0x00);
+
+ if ((!checkpolling || mbhc->polling_active) && restartpolling)
+ wcd9xxx_start_hs_polling(mbhc);
+
+ mbhc->mbhc_micbias_switched = false;
+ pr_debug("%s: VDDIO switch disabled\n", __func__);
+ }
+}
+
+static void wcd9xxx_switch_micbias(struct wcd9xxx_mbhc *mbhc, int vddio_switch)
+{
+ return __wcd9xxx_switch_micbias(mbhc, vddio_switch, true, true);
+}
+
+static s16 wcd9xxx_get_current_v_ins(struct wcd9xxx_mbhc *mbhc, bool hu)
+{
+ s16 v_ins;
+ if ((mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) &&
+ mbhc->mbhc_micbias_switched)
+ v_ins = hu ? (s16)mbhc->mbhc_data.adj_v_ins_hu :
+ (s16)mbhc->mbhc_data.adj_v_ins_h;
+ else
+ v_ins = hu ? (s16)mbhc->mbhc_data.v_ins_hu :
+ (s16)mbhc->mbhc_data.v_ins_h;
+ return v_ins;
+}
+
+void *wcd9xxx_mbhc_cal_btn_det_mp(
+ const struct wcd9xxx_mbhc_btn_detect_cfg *btn_det,
+ const enum wcd9xxx_mbhc_btn_det_mem mem)
+{
+ void *ret = &btn_det->_v_btn_low;
+
+ switch (mem) {
+ case MBHC_BTN_DET_GAIN:
+ ret += sizeof(btn_det->_n_cic);
+ case MBHC_BTN_DET_N_CIC:
+ ret += sizeof(btn_det->_n_ready);
+ case MBHC_BTN_DET_N_READY:
+ ret += sizeof(btn_det->_v_btn_high[0]) * btn_det->num_btn;
+ case MBHC_BTN_DET_V_BTN_HIGH:
+ ret += sizeof(btn_det->_v_btn_low[0]) * btn_det->num_btn;
+ case MBHC_BTN_DET_V_BTN_LOW:
+ /* do nothing */
+ break;
+ default:
+ ret = NULL;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(wcd9xxx_mbhc_cal_btn_det_mp);
+
+static void wcd9xxx_calibrate_hs_polling(struct wcd9xxx_mbhc *mbhc)
+{
+ struct snd_soc_codec *codec = mbhc->codec;
+ const s16 v_ins_hu = wcd9xxx_get_current_v_ins(mbhc, true);
+
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B1_CTL, v_ins_hu & 0xFF);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B2_CTL,
+ (v_ins_hu >> 8) & 0xFF);
+
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL,
+ mbhc->mbhc_data.v_b1_hu & 0xFF);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL,
+ (mbhc->mbhc_data.v_b1_hu >> 8) & 0xFF);
+
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B5_CTL,
+ mbhc->mbhc_data.v_b1_h & 0xFF);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B6_CTL,
+ (mbhc->mbhc_data.v_b1_h >> 8) & 0xFF);
+
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B9_CTL,
+ mbhc->mbhc_data.v_brh & 0xFF);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B10_CTL,
+ (mbhc->mbhc_data.v_brh >> 8) & 0xFF);
+
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B11_CTL,
+ mbhc->mbhc_data.v_brl & 0xFF);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B12_CTL,
+ (mbhc->mbhc_data.v_brl >> 8) & 0xFF);
+}
+
+static void wcd9xxx_codec_switch_cfilt_mode(struct wcd9xxx_mbhc *mbhc,
+ bool fast)
+{
+ struct snd_soc_codec *codec = mbhc->codec;
+ u8 reg_mode_val, cur_mode_val;
+
+ if (fast)
+ reg_mode_val = WCD9XXX_CFILT_FAST_MODE;
+ else
+ reg_mode_val = WCD9XXX_CFILT_SLOW_MODE;
+
+ cur_mode_val =
+ snd_soc_read(codec, mbhc->mbhc_bias_regs.cfilt_ctl) & 0x40;
+
+ if (cur_mode_val != reg_mode_val) {
+ if (mbhc->polling_active)
+ wcd9xxx_pause_hs_polling(mbhc);
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl, 0x40,
+ reg_mode_val);
+ if (mbhc->polling_active)
+ wcd9xxx_start_hs_polling(mbhc);
+ pr_debug("%s: CFILT mode change (%x to %x)\n", __func__,
+ cur_mode_val, reg_mode_val);
+ } else {
+ pr_debug("%s: CFILT Value is already %x\n",
+ __func__, cur_mode_val);
+ }
+}
+
+static void wcd9xxx_jack_report(struct snd_soc_jack *jack, int status, int mask)
+{
+ snd_soc_jack_report_no_dapm(jack, status, mask);
+}
+
+static void __hphocp_off_report(struct wcd9xxx_mbhc *mbhc, u32 jack_status,
+ int irq)
+{
+ struct snd_soc_codec *codec;
+
+ pr_debug("%s: clear ocp status %x\n", __func__, jack_status);
+ codec = mbhc->codec;
+ if (mbhc->hph_status & jack_status) {
+ mbhc->hph_status &= ~jack_status;
+ wcd9xxx_jack_report(&mbhc->headset_jack,
+ mbhc->hph_status, WCD9XXX_JACK_MASK);
+ snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x10,
+ 0x00);
+ snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x10,
+ 0x10);
+ /*
+ * reset retry counter as PA is turned off signifying
+ * start of new OCP detection session
+ */
+ if (WCD9XXX_IRQ_HPH_PA_OCPL_FAULT)
+ mbhc->hphlocp_cnt = 0;
+ else
+ mbhc->hphrocp_cnt = 0;
+ wcd9xxx_enable_irq(codec->control_data, irq);
+ }
+}
+
+static void hphrocp_off_report(struct wcd9xxx_mbhc *mbhc, u32 jack_status)
+{
+ __hphocp_off_report(mbhc, SND_JACK_OC_HPHR,
+ WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
+}
+
+static void hphlocp_off_report(struct wcd9xxx_mbhc *mbhc, u32 jack_status)
+{
+ __hphocp_off_report(mbhc, SND_JACK_OC_HPHL,
+ WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
+}
+
+static void wcd9xxx_get_mbhc_micbias_regs(struct wcd9xxx_mbhc *mbhc,
+ struct mbhc_micbias_regs *micbias_regs)
+{
+ unsigned int cfilt;
+ struct wcd9xxx_pdata *pdata = mbhc->resmgr->pdata;
+
+ switch (mbhc->mbhc_cfg->micbias) {
+ case MBHC_MICBIAS1:
+ cfilt = pdata->micbias.bias1_cfilt_sel;
+ micbias_regs->mbhc_reg = WCD9XXX_A_MICB_1_MBHC;
+ micbias_regs->int_rbias = WCD9XXX_A_MICB_1_INT_RBIAS;
+ micbias_regs->ctl_reg = WCD9XXX_A_MICB_1_CTL;
+ break;
+ case MBHC_MICBIAS2:
+ cfilt = pdata->micbias.bias2_cfilt_sel;
+ micbias_regs->mbhc_reg = WCD9XXX_A_MICB_2_MBHC;
+ micbias_regs->int_rbias = WCD9XXX_A_MICB_2_INT_RBIAS;
+ micbias_regs->ctl_reg = WCD9XXX_A_MICB_2_CTL;
+ break;
+ case MBHC_MICBIAS3:
+ cfilt = pdata->micbias.bias3_cfilt_sel;
+ micbias_regs->mbhc_reg = WCD9XXX_A_MICB_3_MBHC;
+ micbias_regs->int_rbias = WCD9XXX_A_MICB_3_INT_RBIAS;
+ micbias_regs->ctl_reg = WCD9XXX_A_MICB_3_CTL;
+ break;
+ case MBHC_MICBIAS4:
+ cfilt = pdata->micbias.bias4_cfilt_sel;
+ micbias_regs->mbhc_reg = mbhc->resmgr->reg_addr->micb_4_mbhc;
+ micbias_regs->int_rbias =
+ mbhc->resmgr->reg_addr->micb_4_int_rbias;
+ micbias_regs->ctl_reg = mbhc->resmgr->reg_addr->micb_4_ctl;
+ break;
+ default:
+ /* Should never reach here */
+ pr_err("%s: Invalid MIC BIAS for MBHC\n", __func__);
+ return;
+ }
+
+ micbias_regs->cfilt_sel = cfilt;
+
+ switch (cfilt) {
+ case WCD9XXX_CFILT1_SEL:
+ micbias_regs->cfilt_val = WCD9XXX_A_MICB_CFILT_1_VAL;
+ micbias_regs->cfilt_ctl = WCD9XXX_A_MICB_CFILT_1_CTL;
+ mbhc->mbhc_data.micb_mv =
+ mbhc->resmgr->pdata->micbias.cfilt1_mv;
+ break;
+ case WCD9XXX_CFILT2_SEL:
+ micbias_regs->cfilt_val = WCD9XXX_A_MICB_CFILT_2_VAL;
+ micbias_regs->cfilt_ctl = WCD9XXX_A_MICB_CFILT_2_CTL;
+ mbhc->mbhc_data.micb_mv =
+ mbhc->resmgr->pdata->micbias.cfilt2_mv;
+ break;
+ case WCD9XXX_CFILT3_SEL:
+ micbias_regs->cfilt_val = WCD9XXX_A_MICB_CFILT_3_VAL;
+ micbias_regs->cfilt_ctl = WCD9XXX_A_MICB_CFILT_3_CTL;
+ mbhc->mbhc_data.micb_mv =
+ mbhc->resmgr->pdata->micbias.cfilt3_mv;
+ break;
+ }
+}
+
+static void wcd9xxx_clr_and_turnon_hph_padac(struct wcd9xxx_mbhc *mbhc)
+{
+ bool pa_turned_on = false;
+ struct snd_soc_codec *codec = mbhc->codec;
+ u8 wg_time;
+
+ wg_time = snd_soc_read(codec, WCD9XXX_A_RX_HPH_CNP_WG_TIME) ;
+ wg_time += 1;
+
+ if (test_and_clear_bit(WCD9XXX_HPHR_DAC_OFF_ACK,
+ &mbhc->hph_pa_dac_state)) {
+ pr_debug("%s: HPHR clear flag and enable DAC\n", __func__);
+ snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_R_DAC_CTL,
+ 0xC0, 0xC0);
+ }
+ if (test_and_clear_bit(WCD9XXX_HPHL_DAC_OFF_ACK,
+ &mbhc->hph_pa_dac_state)) {
+ pr_debug("%s: HPHL clear flag and enable DAC\n", __func__);
+ snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_L_DAC_CTL,
+ 0xC0, 0xC0);
+ }
+
+ if (test_and_clear_bit(WCD9XXX_HPHR_PA_OFF_ACK,
+ &mbhc->hph_pa_dac_state)) {
+ pr_debug("%s: HPHR clear flag and enable PA\n", __func__);
+ snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_CNP_EN, 0x10,
+ 1 << 4);
+ pa_turned_on = true;
+ }
+ if (test_and_clear_bit(WCD9XXX_HPHL_PA_OFF_ACK,
+ &mbhc->hph_pa_dac_state)) {
+ pr_debug("%s: HPHL clear flag and enable PA\n", __func__);
+ snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_CNP_EN, 0x20, 1
+ << 5);
+ pa_turned_on = true;
+ }
+
+ if (pa_turned_on) {
+ pr_debug("%s: PA was turned off by MBHC and not by DAPM\n",
+ __func__);
+ usleep_range(wg_time * 1000, wg_time * 1000);
+ }
+}
+
+static int wcd9xxx_cancel_btn_work(struct wcd9xxx_mbhc *mbhc)
+{
+ int r;
+ r = cancel_delayed_work_sync(&mbhc->mbhc_btn_dwork);
+ if (r)
+ /* if scheduled mbhc.mbhc_btn_dwork is canceled from here,
+ * we have to unlock from here instead btn_work */
+ wcd9xxx_unlock_sleep(mbhc->resmgr->core);
+ return r;
+}
+
+static bool wcd9xxx_is_hph_dac_on(struct snd_soc_codec *codec, int left)
+{
+ u8 hph_reg_val = 0;
+ if (left)
+ hph_reg_val = snd_soc_read(codec, WCD9XXX_A_RX_HPH_L_DAC_CTL);
+ else
+ hph_reg_val = snd_soc_read(codec, WCD9XXX_A_RX_HPH_R_DAC_CTL);
+
+ return (hph_reg_val & 0xC0) ? true : false;
+}
+
+static bool wcd9xxx_is_hph_pa_on(struct snd_soc_codec *codec)
+{
+ u8 hph_reg_val = 0;
+ hph_reg_val = snd_soc_read(codec, WCD9XXX_A_RX_HPH_CNP_EN);
+
+ return (hph_reg_val & 0x30) ? true : false;
+}
+
+/* called under codec_resource_lock acquisition */
+static void wcd9xxx_set_and_turnoff_hph_padac(struct wcd9xxx_mbhc *mbhc)
+{
+ u8 wg_time;
+ struct snd_soc_codec *codec = mbhc->codec;
+
+ wg_time = snd_soc_read(codec, WCD9XXX_A_RX_HPH_CNP_WG_TIME);
+ wg_time += 1;
+
+ /* If headphone PA is on, check if userspace receives
+ * removal event to sync-up PA's state */
+ if (wcd9xxx_is_hph_pa_on(codec)) {
+ pr_debug("%s PA is on, setting PA_OFF_ACK\n", __func__);
+ set_bit(WCD9XXX_HPHL_PA_OFF_ACK, &mbhc->hph_pa_dac_state);
+ set_bit(WCD9XXX_HPHR_PA_OFF_ACK, &mbhc->hph_pa_dac_state);
+ } else {
+ pr_debug("%s PA is off\n", __func__);
+ }
+
+ if (wcd9xxx_is_hph_dac_on(codec, 1))
+ set_bit(WCD9XXX_HPHL_DAC_OFF_ACK, &mbhc->hph_pa_dac_state);
+ if (wcd9xxx_is_hph_dac_on(codec, 0))
+ set_bit(WCD9XXX_HPHR_DAC_OFF_ACK, &mbhc->hph_pa_dac_state);
+
+ snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_CNP_EN, 0x30, 0x00);
+ snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_L_DAC_CTL, 0xC0, 0x00);
+ snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_R_DAC_CTL, 0xC0, 0x00);
+ usleep_range(wg_time * 1000, wg_time * 1000);
+}
+
+static void wcd9xxx_insert_detect_setup(struct wcd9xxx_mbhc *mbhc, bool ins)
+{
+ if (!mbhc->mbhc_cfg->insert_detect)
+ return;
+ pr_debug("%s: Setting up %s detection\n", __func__,
+ ins ? "insert" : "removal");
+ /* Enable interrupt and insertion detection */
+ snd_soc_write(mbhc->codec, WCD9XXX_A_MBHC_INSERT_DETECT,
+ (0x69 | (ins ? (1 << 1) : 0)));
+}
+
+/* called under codec_resource_lock acquisition */
+static void wcd9xxx_report_plug(struct wcd9xxx_mbhc *mbhc, int insertion,
+ enum snd_jack_types jack_type)
+{
+ WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
+
+ pr_debug("%s: enter insertion %d hph_status %x\n",
+ __func__, insertion, mbhc->hph_status);
+ if (!insertion) {
+ /* Report removal */
+ mbhc->hph_status &= ~jack_type;
+ /*
+ * cancel possibly scheduled btn work and
+ * report release if we reported button press
+ */
+ if (wcd9xxx_cancel_btn_work(mbhc))
+ pr_debug("%s: button press is canceled\n", __func__);
+ else if (mbhc->buttons_pressed) {
+ pr_debug("%s: release of button press%d\n",
+ __func__, jack_type);
+ wcd9xxx_jack_report(&mbhc->button_jack, 0,
+ mbhc->buttons_pressed);
+ mbhc->buttons_pressed &=
+ ~WCD9XXX_JACK_BUTTON_MASK;
+ }
+ pr_debug("%s: Reporting removal %d(%x)\n", __func__,
+ jack_type, mbhc->hph_status);
+ wcd9xxx_jack_report(&mbhc->headset_jack, mbhc->hph_status,
+ WCD9XXX_JACK_MASK);
+ wcd9xxx_set_and_turnoff_hph_padac(mbhc);
+ hphrocp_off_report(mbhc, SND_JACK_OC_HPHR);
+ hphlocp_off_report(mbhc, SND_JACK_OC_HPHL);
+ mbhc->current_plug = PLUG_TYPE_NONE;
+ mbhc->polling_active = false;
+ } else {
+ if (mbhc->mbhc_cfg->detect_extn_cable) {
+ /* Report removal of current jack type */
+ if (mbhc->hph_status != jack_type) {
+ pr_debug("%s: Reporting removal (%x)\n",
+ __func__, mbhc->hph_status);
+ wcd9xxx_jack_report(&mbhc->headset_jack,
+ 0, WCD9XXX_JACK_MASK);
+ mbhc->hph_status = 0;
+ }
+ }
+ /* Report insertion */
+ mbhc->hph_status |= jack_type;
+
+ if (jack_type == SND_JACK_HEADPHONE) {
+ mbhc->current_plug = PLUG_TYPE_HEADPHONE;
+ } else if (jack_type == SND_JACK_UNSUPPORTED) {
+ mbhc->current_plug = PLUG_TYPE_GND_MIC_SWAP;
+ } else if (jack_type == SND_JACK_HEADSET) {
+ mbhc->polling_active = BUTTON_POLLING_SUPPORTED;
+ mbhc->current_plug = PLUG_TYPE_HEADSET;
+ } else if (jack_type == SND_JACK_LINEOUT) {
+ mbhc->current_plug = PLUG_TYPE_HIGH_HPH;
+ }
+ pr_debug("%s: Reporting insertion %d(%x)\n", __func__,
+ jack_type, mbhc->hph_status);
+ wcd9xxx_jack_report(&mbhc->headset_jack,
+ mbhc->hph_status, WCD9XXX_JACK_MASK);
+ wcd9xxx_clr_and_turnon_hph_padac(mbhc);
+ }
+ /* Setup insert detect */
+ wcd9xxx_insert_detect_setup(mbhc, !insertion);
+
+ pr_debug("%s: leave hph_status %x\n", __func__, mbhc->hph_status);
+}
+
+/* should be called under interrupt context that hold suspend */
+static void wcd9xxx_schedule_hs_detect_plug(struct wcd9xxx_mbhc *mbhc,
+ struct work_struct *work)
+{
+ pr_debug("%s: scheduling wcd9xxx_correct_swch_plug\n", __func__);
+ WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
+ mbhc->hs_detect_work_stop = false;
+ wcd9xxx_lock_sleep(mbhc->resmgr->core);
+ schedule_work(work);
+}
+
+/* called under codec_resource_lock acquisition */
+static void wcd9xxx_cancel_hs_detect_plug(struct wcd9xxx_mbhc *mbhc,
+ struct work_struct *work)
+{
+ pr_debug("%s: Canceling correct_plug_swch\n", __func__);
+ WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
+ mbhc->hs_detect_work_stop = true;
+ wmb();
+ WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
+ if (cancel_work_sync(work)) {
+ pr_debug("%s: correct_plug_swch is canceled\n",
+ __func__);
+ wcd9xxx_unlock_sleep(mbhc->resmgr->core);
+ }
+ WCD9XXX_BCL_LOCK(mbhc->resmgr);
+}
+
+static s16 wcd9xxx_get_current_v_hs_max(struct wcd9xxx_mbhc *mbhc)
+{
+ s16 v_hs_max;
+ struct wcd9xxx_mbhc_plug_type_cfg *plug_type;
+
+ plug_type = WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
+ if ((mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) &&
+ mbhc->mbhc_micbias_switched)
+ v_hs_max = mbhc->mbhc_data.adj_v_hs_max;
+ else
+ v_hs_max = plug_type->v_hs_max;
+ return v_hs_max;
+}
+
+static bool wcd9xxx_is_inval_ins_range(struct wcd9xxx_mbhc *mbhc,
+ s32 mic_volt, bool highhph, bool *highv)
+{
+ s16 v_hs_max;
+ bool invalid = false;
+
+ /* Perform this check only when the high voltage headphone
+ * needs to be considered as invalid
+ */
+ v_hs_max = wcd9xxx_get_current_v_hs_max(mbhc);
+ *highv = mic_volt > v_hs_max;
+ if (!highhph && *highv)
+ invalid = true;
+ else if (mic_volt < mbhc->mbhc_data.v_inval_ins_high &&
+ (mic_volt > mbhc->mbhc_data.v_inval_ins_low))
+ invalid = true;
+
+ return invalid;
+}
+
+static short wcd9xxx_read_sta_result(struct snd_soc_codec *codec)
+{
+ u8 bias_msb, bias_lsb;
+ short bias_value;
+
+ bias_msb = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B3_STATUS);
+ bias_lsb = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B2_STATUS);
+ bias_value = (bias_msb << 8) | bias_lsb;
+ return bias_value;
+}
+
+static short wcd9xxx_read_dce_result(struct snd_soc_codec *codec)
+{
+ u8 bias_msb, bias_lsb;
+ short bias_value;
+
+ bias_msb = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B5_STATUS);
+ bias_lsb = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B4_STATUS);
+ bias_value = (bias_msb << 8) | bias_lsb;
+ return bias_value;
+}
+
+static void wcd9xxx_turn_onoff_rel_detection(struct snd_soc_codec *codec,
+ bool on)
+{
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x02, on << 1);
+}
+
+static short __wcd9xxx_codec_sta_dce(struct wcd9xxx_mbhc *mbhc, int dce,
+ bool override_bypass, bool noreldetection)
+{
+ short bias_value;
+ struct snd_soc_codec *codec = mbhc->codec;
+
+ wcd9xxx_disable_irq(mbhc->resmgr->core, WCD9XXX_IRQ_MBHC_POTENTIAL);
+ if (noreldetection)
+ wcd9xxx_turn_onoff_rel_detection(codec, false);
+
+ /* Turn on the override */
+ if (!override_bypass)
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x4, 0x4);
+ if (dce) {
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8,
+ 0x8);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x4);
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8,
+ 0x0);
+ usleep_range(mbhc->mbhc_data.t_sta_dce,
+ mbhc->mbhc_data.t_sta_dce);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x4);
+ usleep_range(mbhc->mbhc_data.t_dce, mbhc->mbhc_data.t_dce);
+ bias_value = wcd9xxx_read_dce_result(codec);
+ } else {
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8,
+ 0x8);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x2);
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8,
+ 0x0);
+ usleep_range(mbhc->mbhc_data.t_sta_dce,
+ mbhc->mbhc_data.t_sta_dce);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x2);
+ usleep_range(mbhc->mbhc_data.t_sta,
+ mbhc->mbhc_data.t_sta);
+ bias_value = wcd9xxx_read_sta_result(codec);
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8,
+ 0x8);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x0);
+ }
+ /* Turn off the override after measuring mic voltage */
+ if (!override_bypass)
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x04,
+ 0x00);
+
+ if (noreldetection)
+ wcd9xxx_turn_onoff_rel_detection(codec, true);
+ wcd9xxx_enable_irq(mbhc->resmgr->core, WCD9XXX_IRQ_MBHC_POTENTIAL);
+
+ return bias_value;
+}
+
+static short wcd9xxx_codec_sta_dce(struct wcd9xxx_mbhc *mbhc, int dce,
+ bool norel)
+{
+ return __wcd9xxx_codec_sta_dce(mbhc, dce, false, norel);
+}
+
+static s32 wcd9xxx_codec_sta_dce_v(struct wcd9xxx_mbhc *mbhc, s8 dce,
+ u16 bias_value)
+{
+ s16 value, z, mb;
+ s32 mv;
+
+ value = bias_value;
+ if (dce) {
+ z = (mbhc->mbhc_data.dce_z);
+ mb = (mbhc->mbhc_data.dce_mb);
+ mv = (value - z) * (s32)mbhc->mbhc_data.micb_mv / (mb - z);
+ } else {
+ z = (mbhc->mbhc_data.sta_z);
+ mb = (mbhc->mbhc_data.sta_mb);
+ mv = (value - z) * (s32)mbhc->mbhc_data.micb_mv / (mb - z);
+ }
+
+ return mv;
+}
+
+/* called only from interrupt which is under codec_resource_lock acquisition */
+static short wcd9xxx_mbhc_setup_hs_polling(struct wcd9xxx_mbhc *mbhc)
+{
+ struct snd_soc_codec *codec = mbhc->codec;
+ short bias_value;
+ u8 cfilt_mode;
+
+ WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
+
+ pr_debug("%s: enter\n", __func__);
+ if (!mbhc->mbhc_cfg->calibration) {
+ pr_err("%s: Error, no calibration exists\n", __func__);
+ return -ENODEV;
+ }
+
+ /*
+ * Request BG and clock.
+ * These will be released by wcd9xxx_cleanup_hs_polling
+ */
+ wcd9xxx_resmgr_get_bandgap(mbhc->resmgr, WCD9XXX_BANDGAP_MBHC_MODE);
+ wcd9xxx_resmgr_get_clk_block(mbhc->resmgr, WCD9XXX_CLK_RCO);
+
+ snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1, 0x05, 0x01);
+
+ /* Make sure CFILT is in fast mode, save current mode */
+ cfilt_mode = snd_soc_read(codec, mbhc->mbhc_bias_regs.cfilt_ctl);
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl, 0x70, 0x00);
+
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x1F, 0x16);
+
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x2, 0x2);
+ snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x84);
+
+ snd_soc_update_bits(codec, WCD9XXX_A_TX_7_MBHC_EN, 0x80, 0x80);
+ snd_soc_update_bits(codec, WCD9XXX_A_TX_7_MBHC_EN, 0x1F, 0x1C);
+ snd_soc_update_bits(codec, WCD9XXX_A_TX_7_MBHC_TEST_CTL, 0x40, 0x40);
+
+ snd_soc_update_bits(codec, WCD9XXX_A_TX_7_MBHC_EN, 0x80, 0x00);
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, 0x00);
+
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x2, 0x2);
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
+
+ wcd9xxx_calibrate_hs_polling(mbhc);
+
+ /* don't flip override */
+ bias_value = __wcd9xxx_codec_sta_dce(mbhc, 1, true, true);
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl, 0x40,
+ cfilt_mode);
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x13, 0x00);
+
+ return bias_value;
+}
+
+static void wcd9xxx_shutdown_hs_removal_detect(struct wcd9xxx_mbhc *mbhc)
+{
+ struct snd_soc_codec *codec = mbhc->codec;
+ const struct wcd9xxx_mbhc_general_cfg *generic =
+ WCD9XXX_MBHC_CAL_GENERAL_PTR(mbhc->mbhc_cfg->calibration);
+
+ /* Need MBHC clock */
+ wcd9xxx_resmgr_get_clk_block(mbhc->resmgr, WCD9XXX_CLK_RCO);
+
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x2, 0x2);
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x6, 0x0);
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x80, 0x00);
+
+ usleep_range(generic->t_shutdown_plug_rem,
+ generic->t_shutdown_plug_rem);
+
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0xA, 0x8);
+
+ /* Put requested CLK back */
+ wcd9xxx_resmgr_put_clk_block(mbhc->resmgr, WCD9XXX_CLK_RCO);
+
+ snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x00);
+}
+
+static void wcd9xxx_cleanup_hs_polling(struct wcd9xxx_mbhc *mbhc)
+{
+ WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
+
+ wcd9xxx_shutdown_hs_removal_detect(mbhc);
+
+ /* Release clock and BG requested by wcd9xxx_mbhc_setup_hs_polling */
+ wcd9xxx_resmgr_put_clk_block(mbhc->resmgr, WCD9XXX_CLK_RCO);
+ wcd9xxx_resmgr_put_bandgap(mbhc->resmgr, WCD9XXX_BANDGAP_MBHC_MODE);
+
+ mbhc->polling_active = false;
+ mbhc->mbhc_state = MBHC_STATE_NONE;
+}
+
+static s16 scale_v_micb_vddio(struct wcd9xxx_mbhc *mbhc, int v, bool tovddio)
+{
+ int r;
+ int vddio_k, mb_k;
+ vddio_k = wcd9xxx_resmgr_get_k_val(mbhc->resmgr, VDDIO_MICBIAS_MV);
+ mb_k = wcd9xxx_resmgr_get_k_val(mbhc->resmgr, mbhc->mbhc_data.micb_mv);
+ if (tovddio)
+ r = v * vddio_k / mb_k;
+ else
+ r = v * mb_k / vddio_k;
+ return r;
+}
+
+/* called under codec_resource_lock acquisition */
+static void wcd9xxx_codec_hphr_gnd_switch(struct snd_soc_codec *codec, bool on)
+{
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x01, on);
+ if (on)
+ usleep_range(5000, 5000);
+}
+
+static bool wcd9xxx_is_inval_ins_delta(struct snd_soc_codec *codec,
+ int mic_volt, int mic_volt_prev,
+ int threshold)
+{
+ return abs(mic_volt - mic_volt_prev) > threshold;
+}
+
+/* called under codec_resource_lock acquisition and mbhc override = 1 */
+static enum wcd9xxx_mbhc_plug_type
+wcd9xxx_codec_get_plug_type(struct wcd9xxx_mbhc *mbhc, bool highhph)
+{
+ int i;
+ bool gndswitch, vddioswitch;
+ int scaled;
+ struct wcd9xxx_mbhc_plug_type_cfg *plug_type_ptr;
+ struct snd_soc_codec *codec = mbhc->codec;
+ const bool vddio = (mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV);
+ int num_det = (NUM_DCE_PLUG_DETECT + vddio);
+ enum wcd9xxx_mbhc_plug_type plug_type[num_det];
+ s16 mb_v[num_det];
+ s32 mic_mv[num_det];
+ bool inval;
+ bool highdelta;
+ bool ahighv = false, highv;
+
+ pr_debug("%s: enter\n", __func__);
+ WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
+
+ /* make sure override is on */
+ WARN_ON(!(snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_CTL) & 0x04));
+
+ /* GND and MIC swap detection requires at least 2 rounds of DCE */
+ BUG_ON(num_det < 2);
+
+ plug_type_ptr =
+ WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
+
+ plug_type[0] = PLUG_TYPE_INVALID;
+
+ /* performs DCEs for N times
+ * 1st: check if voltage is in invalid range
+ * 2nd - N-2nd: check voltage range and delta
+ * N-1st: check voltage range, delta with HPHR GND switch
+ * Nth: check voltage range with VDDIO switch if micbias V != vddio V*/
+ for (i = 0; i < num_det; i++) {
+ gndswitch = (i == (num_det - 1 - vddio));
+ vddioswitch = (vddio && ((i == num_det - 1) ||
+ (i == num_det - 2)));
+ if (i == 0) {
+ mb_v[i] = wcd9xxx_mbhc_setup_hs_polling(mbhc);
+ mic_mv[i] = wcd9xxx_codec_sta_dce_v(mbhc, 1 , mb_v[i]);
+ inval = wcd9xxx_is_inval_ins_range(mbhc, mic_mv[i],
+ highhph, &highv);
+ ahighv |= highv;
+ scaled = mic_mv[i];
+ } else {
+ if (vddioswitch)
+ __wcd9xxx_switch_micbias(mbhc, 1,
+ false, false);
+ if (gndswitch)
+ wcd9xxx_codec_hphr_gnd_switch(codec, true);
+ mb_v[i] = __wcd9xxx_codec_sta_dce(mbhc, 1, true, true);
+ mic_mv[i] = wcd9xxx_codec_sta_dce_v(mbhc, 1 , mb_v[i]);
+ if (vddioswitch)
+ scaled = scale_v_micb_vddio(mbhc, mic_mv[i],
+ false);
+ else
+ scaled = mic_mv[i];
+ /* !gndswitch & vddioswitch means the previous DCE
+ * was done with gndswitch, don't compare with DCE
+ * with gndswitch */
+ highdelta = wcd9xxx_is_inval_ins_delta(codec, scaled,
+ mic_mv[i - !gndswitch - vddioswitch],
+ FAKE_INS_DELTA_SCALED_MV);
+ inval = (wcd9xxx_is_inval_ins_range(mbhc, mic_mv[i],
+ highhph, &highv) ||
+ highdelta);
+ ahighv |= highv;
+ if (gndswitch)
+ wcd9xxx_codec_hphr_gnd_switch(codec, false);
+ if (vddioswitch)
+ __wcd9xxx_switch_micbias(mbhc, 0,
+ false, false);
+ /* claim UNSUPPORTED plug insertion when
+ * good headset is detected but HPHR GND switch makes
+ * delta difference */
+ if (i == (num_det - 2) && highdelta && !ahighv)
+ plug_type[0] = PLUG_TYPE_GND_MIC_SWAP;
+ else if (i == (num_det - 1) && inval)
+ plug_type[0] = PLUG_TYPE_INVALID;
+ }
+ pr_debug("%s: DCE #%d, %04x, V %d, scaled V %d, GND %d, VDDIO %d, inval %d\n",
+ __func__, i + 1, mb_v[i] & 0xffff, mic_mv[i], scaled,
+ gndswitch, vddioswitch, inval);
+ /* don't need to run further DCEs */
+ if (ahighv && inval)
+ break;
+ mic_mv[i] = scaled;
+ }
+
+ for (i = 0; (plug_type[0] != PLUG_TYPE_GND_MIC_SWAP && !inval) &&
+ (i < num_det); i++) {
+ /*
+ * If we are here, means none of the all
+ * measurements are fake, continue plug type detection.
+ * If all three measurements do not produce same
+ * plug type, restart insertion detection
+ */
+ if (mic_mv[i] < plug_type_ptr->v_no_mic) {
+ plug_type[i] = PLUG_TYPE_HEADPHONE;
+ pr_debug("%s: Detect attempt %d, detected Headphone\n",
+ __func__, i);
+ } else if (highhph && (mic_mv[i] > plug_type_ptr->v_hs_max)) {
+ plug_type[i] = PLUG_TYPE_HIGH_HPH;
+ pr_debug("%s: Detect attempt %d, detected High Headphone\n",
+ __func__, i);
+ } else {
+ plug_type[i] = PLUG_TYPE_HEADSET;
+ pr_debug("%s: Detect attempt %d, detected Headset\n",
+ __func__, i);
+ }
+
+ if (i > 0 && (plug_type[i - 1] != plug_type[i])) {
+ pr_err("%s: Detect attempt %d and %d are not same",
+ __func__, i - 1, i);
+ plug_type[0] = PLUG_TYPE_INVALID;
+ inval = true;
+ break;
+ }
+ }
+
+ pr_debug("%s: Detected plug type %d\n", __func__, plug_type[0]);
+ pr_debug("%s: leave\n", __func__);
+ return plug_type[0];
+}
+
+static bool wcd9xxx_swch_level_remove(struct wcd9xxx_mbhc *mbhc)
+{
+ if (mbhc->mbhc_cfg->gpio)
+ return (gpio_get_value_cansleep(mbhc->mbhc_cfg->gpio) !=
+ mbhc->mbhc_cfg->gpio_level_insert);
+ else if (mbhc->mbhc_cfg->insert_detect)
+ return snd_soc_read(mbhc->codec,
+ WCD9XXX_A_MBHC_INSERT_DET_STATUS) &
+ (1 << 2);
+ else
+ WARN(1, "Invalid jack detection configuration\n");
+
+ return true;
+}
+
+static bool is_clk_active(struct snd_soc_codec *codec)
+{
+ return !!(snd_soc_read(codec, WCD9XXX_A_CDC_CLK_MCLK_CTL) & 0x05);
+}
+
+static int wcd9xxx_enable_hs_detect(struct wcd9xxx_mbhc *mbhc,
+ int insertion, int trigger, bool padac_off)
+{
+ struct snd_soc_codec *codec = mbhc->codec;
+ int central_bias_enabled = 0;
+ const struct wcd9xxx_mbhc_general_cfg *generic =
+ WCD9XXX_MBHC_CAL_GENERAL_PTR(mbhc->mbhc_cfg->calibration);
+ const struct wcd9xxx_mbhc_plug_detect_cfg *plug_det =
+ WCD9XXX_MBHC_CAL_PLUG_DET_PTR(mbhc->mbhc_cfg->calibration);
+
+ pr_debug("%s: enter insertion(%d) trigger(0x%x)\n",
+ __func__, insertion, trigger);
+
+ if (!mbhc->mbhc_cfg->calibration) {
+ pr_err("Error, no wcd9xxx calibration\n");
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x1, 0);
+
+ /*
+ * Make sure mic bias and Mic line schmitt trigger
+ * are turned OFF
+ */
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x01);
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x90, 0x00);
+
+ if (insertion) {
+ wcd9xxx_switch_micbias(mbhc, 0);
+
+ /* DAPM can manipulate PA/DAC bits concurrently */
+ if (padac_off == true)
+ wcd9xxx_set_and_turnoff_hph_padac(mbhc);
+
+ if (trigger & MBHC_USE_HPHL_TRIGGER) {
+ /* Enable HPH Schmitt Trigger */
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x11,
+ 0x11);
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x0C,
+ plug_det->hph_current << 2);
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x02,
+ 0x02);
+ }
+ if (trigger & MBHC_USE_MB_TRIGGER) {
+ /* enable the mic line schmitt trigger */
+ snd_soc_update_bits(codec,
+ mbhc->mbhc_bias_regs.mbhc_reg,
+ 0x60, plug_det->mic_current << 5);
+ snd_soc_update_bits(codec,
+ mbhc->mbhc_bias_regs.mbhc_reg,
+ 0x80, 0x80);
+ usleep_range(plug_det->t_mic_pid, plug_det->t_mic_pid);
+ snd_soc_update_bits(codec,
+ mbhc->mbhc_bias_regs.ctl_reg, 0x01,
+ 0x00);
+ snd_soc_update_bits(codec,
+ mbhc->mbhc_bias_regs.mbhc_reg,
+ 0x10, 0x10);
+ }
+
+ /* setup for insetion detection */
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x2, 0);
+ } else {
+ pr_debug("setup for removal detection\n");
+ /* Make sure the HPH schmitt trigger is OFF */
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x12, 0x00);
+
+ /* enable the mic line schmitt trigger */
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg,
+ 0x01, 0x00);
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x60,
+ plug_det->mic_current << 5);
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
+ 0x80, 0x80);
+ usleep_range(plug_det->t_mic_pid, plug_det->t_mic_pid);
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
+ 0x10, 0x10);
+
+ /* Setup for low power removal detection */
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x2,
+ 0x2);
+ }
+
+ if (snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_CTL) & 0x4) {
+ /* called by interrupt */
+ if (!is_clk_active(codec)) {
+ wcd9xxx_resmgr_enable_config_mode(codec, 1);
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
+ 0x06, 0);
+ usleep_range(generic->t_shutdown_plug_rem,
+ generic->t_shutdown_plug_rem);
+ wcd9xxx_resmgr_enable_config_mode(codec, 0);
+ } else
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
+ 0x06, 0);
+ }
+
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.int_rbias, 0x80, 0);
+
+ /* If central bandgap disabled */
+ if (!(snd_soc_read(codec, WCD9XXX_A_PIN_CTL_OE1) & 1)) {
+ snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE1, 0x3, 0x3);
+ usleep_range(generic->t_bg_fast_settle,
+ generic->t_bg_fast_settle);
+ central_bias_enabled = 1;
+ }
+
+ /* If LDO_H disabled */
+ if (snd_soc_read(codec, WCD9XXX_A_PIN_CTL_OE0) & 0x80) {
+ snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE0, 0x10, 0);
+ snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE0, 0x80, 0x80);
+ usleep_range(generic->t_ldoh, generic->t_ldoh);
+ snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE0, 0x80, 0);
+
+ if (central_bias_enabled)
+ snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE1, 0x1,
+ 0);
+ }
+
+ snd_soc_update_bits(codec, mbhc->resmgr->reg_addr->micb_4_mbhc, 0x3,
+ mbhc->mbhc_cfg->micbias);
+
+ wcd9xxx_enable_irq(mbhc->resmgr->core, WCD9XXX_IRQ_MBHC_INSERTION);
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x1, 0x1);
+ pr_debug("%s: leave\n", __func__);
+
+ return 0;
+}
+
+/* called under codec_resource_lock acquisition */
+static void wcd9xxx_find_plug_and_report(struct wcd9xxx_mbhc *mbhc,
+ enum wcd9xxx_mbhc_plug_type plug_type)
+{
+ pr_debug("%s: enter current_plug(%d) new_plug(%d)\n",
+ __func__, mbhc->current_plug, plug_type);
+
+ WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
+
+ if (plug_type == PLUG_TYPE_HEADPHONE &&
+ mbhc->current_plug == PLUG_TYPE_NONE) {
+ /*
+ * Nothing was reported previously
+ * report a headphone or unsupported
+ */
+ wcd9xxx_report_plug(mbhc, 1, SND_JACK_HEADPHONE);
+ wcd9xxx_cleanup_hs_polling(mbhc);
+ } else if (plug_type == PLUG_TYPE_GND_MIC_SWAP) {
+ if (!mbhc->mbhc_cfg->detect_extn_cable) {
+ if (mbhc->current_plug == PLUG_TYPE_HEADSET)
+ wcd9xxx_report_plug(mbhc, 0,
+ SND_JACK_HEADSET);
+ else if (mbhc->current_plug == PLUG_TYPE_HEADPHONE)
+ wcd9xxx_report_plug(mbhc, 0,
+ SND_JACK_HEADPHONE);
+ }
+ wcd9xxx_report_plug(mbhc, 1, SND_JACK_UNSUPPORTED);
+ wcd9xxx_cleanup_hs_polling(mbhc);
+ } else if (plug_type == PLUG_TYPE_HEADSET) {
+ /*
+ * If Headphone was reported previously, this will
+ * only report the mic line
+ */
+ wcd9xxx_report_plug(mbhc, 1, SND_JACK_HEADSET);
+ msleep(100);
+ wcd9xxx_start_hs_polling(mbhc);
+ } else if (plug_type == PLUG_TYPE_HIGH_HPH) {
+ if (mbhc->mbhc_cfg->detect_extn_cable) {
+ /* High impedance device found. Report as LINEOUT*/
+ wcd9xxx_report_plug(mbhc, 1, SND_JACK_LINEOUT);
+ wcd9xxx_cleanup_hs_polling(mbhc);
+ pr_debug("%s: setup mic trigger for further detection\n",
+ __func__);
+ mbhc->lpi_enabled = true;
+ /*
+ * Do not enable HPHL trigger. If playback is active,
+ * it might lead to continuous false HPHL triggers
+ */
+ wcd9xxx_enable_hs_detect(mbhc, 1, MBHC_USE_MB_TRIGGER,
+ false);
+ } else {
+ if (mbhc->current_plug == PLUG_TYPE_NONE)
+ wcd9xxx_report_plug(mbhc, 1,
+ SND_JACK_HEADPHONE);
+ wcd9xxx_cleanup_hs_polling(mbhc);
+ pr_debug("setup mic trigger for further detection\n");
+ mbhc->lpi_enabled = true;
+ wcd9xxx_enable_hs_detect(mbhc, 1, MBHC_USE_MB_TRIGGER |
+ MBHC_USE_HPHL_TRIGGER,
+ false);
+ }
+ } else {
+ WARN(1, "Unexpected current plug_type %d, plug_type %d\n",
+ mbhc->current_plug, plug_type);
+ }
+ pr_debug("%s: leave\n", __func__);
+}
+
+/* called under codec_resource_lock acquisition */
+static void wcd9xxx_mbhc_decide_swch_plug(struct wcd9xxx_mbhc *mbhc)
+{
+ enum wcd9xxx_mbhc_plug_type plug_type;
+ struct snd_soc_codec *codec = mbhc->codec;
+
+ pr_debug("%s: enter\n", __func__);
+
+ WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
+
+ wcd9xxx_turn_onoff_override(codec, true);
+ plug_type = wcd9xxx_codec_get_plug_type(mbhc, true);
+ wcd9xxx_turn_onoff_override(codec, false);
+
+ if (wcd9xxx_swch_level_remove(mbhc)) {
+ pr_debug("%s: Switch level is low when determining plug\n",
+ __func__);
+ return;
+ }
+
+ if (plug_type == PLUG_TYPE_INVALID ||
+ plug_type == PLUG_TYPE_GND_MIC_SWAP) {
+ wcd9xxx_schedule_hs_detect_plug(mbhc,
+ &mbhc->correct_plug_swch);
+ } else if (plug_type == PLUG_TYPE_HEADPHONE) {
+ wcd9xxx_report_plug(mbhc, 1, SND_JACK_HEADPHONE);
+ wcd9xxx_schedule_hs_detect_plug(mbhc,
+ &mbhc->correct_plug_swch);
+ } else {
+ pr_debug("%s: Valid plug found, determine plug type %d\n",
+ __func__, plug_type);
+ wcd9xxx_find_plug_and_report(mbhc, plug_type);
+ }
+ pr_debug("%s: leave\n", __func__);
+}
+
+/* called under codec_resource_lock acquisition */
+static void wcd9xxx_mbhc_detect_plug_type(struct wcd9xxx_mbhc *mbhc)
+{
+ struct snd_soc_codec *codec = mbhc->codec;
+ const struct wcd9xxx_mbhc_plug_detect_cfg *plug_det =
+ WCD9XXX_MBHC_CAL_PLUG_DET_PTR(mbhc->mbhc_cfg->calibration);
+
+ pr_debug("%s: enter\n", __func__);
+ WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
+
+ /*
+ * Turn on the override,
+ * wcd9xxx_mbhc_setup_hs_polling requires override on
+ */
+ wcd9xxx_turn_onoff_override(codec, true);
+ if (plug_det->t_ins_complete > 20)
+ msleep(plug_det->t_ins_complete);
+ else
+ usleep_range(plug_det->t_ins_complete * 1000,
+ plug_det->t_ins_complete * 1000);
+ /* Turn off the override */
+ wcd9xxx_turn_onoff_override(codec, false);
+
+ if (wcd9xxx_swch_level_remove(mbhc))
+ pr_debug("%s: Switch level low when determining plug\n",
+ __func__);
+ else
+ wcd9xxx_mbhc_decide_swch_plug(mbhc);
+ pr_debug("%s: leave\n", __func__);
+}
+
+/* called only from interrupt which is under codec_resource_lock acquisition */
+static void wcd9xxx_hs_insert_irq_swch(struct wcd9xxx_mbhc *mbhc,
+ bool is_removal)
+{
+ if (!is_removal) {
+ pr_debug("%s: MIC trigger insertion interrupt\n", __func__);
+
+ rmb();
+ if (mbhc->lpi_enabled)
+ msleep(100);
+
+ rmb();
+ if (!mbhc->lpi_enabled) {
+ pr_debug("%s: lpi is disabled\n", __func__);
+ } else if (!wcd9xxx_swch_level_remove(mbhc)) {
+ pr_debug("%s: Valid insertion, detect plug type\n",
+ __func__);
+ wcd9xxx_mbhc_decide_swch_plug(mbhc);
+ } else {
+ pr_debug("%s: Invalid insertion stop plug detection\n",
+ __func__);
+ }
+ } else if (mbhc->mbhc_cfg->detect_extn_cable) {
+ pr_debug("%s: Removal\n", __func__);
+ if (!wcd9xxx_swch_level_remove(mbhc)) {
+ /*
+ * Switch indicates, something is still inserted.
+ * This could be extension cable i.e. headset is
+ * removed from extension cable.
+ */
+ /* cancel detect plug */
+ wcd9xxx_cancel_hs_detect_plug(mbhc,
+ &mbhc->correct_plug_swch);
+ wcd9xxx_mbhc_decide_swch_plug(mbhc);
+ }
+ } else {
+ pr_err("%s: Switch IRQ used, invalid MBHC Removal\n", __func__);
+ }
+}
+
+static bool is_valid_mic_voltage(struct wcd9xxx_mbhc *mbhc, s32 mic_mv)
+{
+ const struct wcd9xxx_mbhc_plug_type_cfg *plug_type =
+ WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
+ const s16 v_hs_max = wcd9xxx_get_current_v_hs_max(mbhc);
+
+ return (!(mic_mv > 10 && mic_mv < 80) && (mic_mv > plug_type->v_no_mic)
+ && (mic_mv < v_hs_max)) ? true : false;
+}
+
+/*
+ * called under codec_resource_lock acquisition
+ * returns true if mic voltage range is back to normal insertion
+ * returns false either if timedout or removed
+ */
+static bool wcd9xxx_hs_remove_settle(struct wcd9xxx_mbhc *mbhc)
+{
+ int i;
+ bool timedout, settled = false;
+ s32 mic_mv[NUM_DCE_PLUG_DETECT];
+ short mb_v[NUM_DCE_PLUG_DETECT];
+ unsigned long retry = 0, timeout;
+
+ timeout = jiffies + msecs_to_jiffies(HS_DETECT_PLUG_TIME_MS);
+ while (!(timedout = time_after(jiffies, timeout))) {
+ retry++;
+ if (wcd9xxx_swch_level_remove(mbhc)) {
+ pr_debug("%s: Switch indicates removal\n", __func__);
+ break;
+ }
+
+ if (retry > 1)
+ msleep(250);
+ else
+ msleep(50);
+
+ if (wcd9xxx_swch_level_remove(mbhc)) {
+ pr_debug("%s: Switch indicates removal\n", __func__);
+ break;
+ }
+
+ for (i = 0; i < NUM_DCE_PLUG_DETECT; i++) {
+ mb_v[i] = wcd9xxx_codec_sta_dce(mbhc, 1, true);
+ mic_mv[i] = wcd9xxx_codec_sta_dce_v(mbhc, 1 , mb_v[i]);
+ pr_debug("%s : DCE run %lu, mic_mv = %d(%x)\n",
+ __func__, retry, mic_mv[i], mb_v[i]);
+ }
+
+ if (wcd9xxx_swch_level_remove(mbhc)) {
+ pr_debug("%s: Switcn indicates removal\n", __func__);
+ break;
+ }
+
+ if (mbhc->current_plug == PLUG_TYPE_NONE) {
+ pr_debug("%s : headset/headphone is removed\n",
+ __func__);
+ break;
+ }
+
+ for (i = 0; i < NUM_DCE_PLUG_DETECT; i++)
+ if (!is_valid_mic_voltage(mbhc, mic_mv[i]))
+ break;
+
+ if (i == NUM_DCE_PLUG_DETECT) {
+ pr_debug("%s: MIC voltage settled\n", __func__);
+ settled = true;
+ msleep(200);
+ break;
+ }
+ }
+
+ if (timedout)
+ pr_debug("%s: Microphone did not settle in %d seconds\n",
+ __func__, HS_DETECT_PLUG_TIME_MS);
+ return settled;
+}
+
+/* called only from interrupt which is under codec_resource_lock acquisition */
+static void wcd9xxx_hs_remove_irq_swch(struct wcd9xxx_mbhc *mbhc)
+{
+ pr_debug("%s: enter\n", __func__);
+ if (wcd9xxx_hs_remove_settle(mbhc))
+ wcd9xxx_start_hs_polling(mbhc);
+ pr_debug("%s: leave\n", __func__);
+}
+
+/* called only from interrupt which is under codec_resource_lock acquisition */
+static void wcd9xxx_hs_remove_irq_noswch(struct wcd9xxx_mbhc *mbhc)
+{
+ short bias_value;
+ bool removed = true;
+ struct snd_soc_codec *codec = mbhc->codec;
+ const struct wcd9xxx_mbhc_general_cfg *generic =
+ WCD9XXX_MBHC_CAL_GENERAL_PTR(mbhc->mbhc_cfg->calibration);
+ int min_us = FAKE_REMOVAL_MIN_PERIOD_MS * 1000;
+
+ pr_debug("%s: enter\n", __func__);
+ if (mbhc->current_plug != PLUG_TYPE_HEADSET) {
+ pr_debug("%s(): Headset is not inserted, ignore removal\n",
+ __func__);
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL,
+ 0x08, 0x08);
+ return;
+ }
+
+ usleep_range(generic->t_shutdown_plug_rem,
+ generic->t_shutdown_plug_rem);
+
+ do {
+ bias_value = wcd9xxx_codec_sta_dce(mbhc, 1, true);
+ pr_debug("%s: DCE %d,%d, %d us left\n", __func__, bias_value,
+ wcd9xxx_codec_sta_dce_v(mbhc, 1, bias_value), min_us);
+ if (bias_value < wcd9xxx_get_current_v_ins(mbhc, false)) {
+ pr_debug("%s: checking false removal\n", __func__);
+ msleep(500);
+ removed = !wcd9xxx_hs_remove_settle(mbhc);
+ pr_debug("%s: headset %sactually removed\n", __func__,
+ removed ? "" : "not ");
+ break;
+ }
+ min_us -= mbhc->mbhc_data.t_dce;
+ } while (min_us > 0);
+
+ if (removed) {
+ if (mbhc->mbhc_cfg->detect_extn_cable) {
+ if (!wcd9xxx_swch_level_remove(mbhc)) {
+ /*
+ * extension cable is still plugged in
+ * report it as LINEOUT device
+ */
+ wcd9xxx_report_plug(mbhc, 1, SND_JACK_LINEOUT);
+ wcd9xxx_cleanup_hs_polling(mbhc);
+ wcd9xxx_enable_hs_detect(mbhc, 1,
+ MBHC_USE_MB_TRIGGER,
+ false);
+ }
+ } else {
+ /* Cancel possibly running hs_detect_work */
+ wcd9xxx_cancel_hs_detect_plug(mbhc,
+ &mbhc->correct_plug_noswch);
+ /*
+ * If this removal is not false, first check the micbias
+ * switch status and switch it to LDOH if it is already
+ * switched to VDDIO.
+ */
+ wcd9xxx_switch_micbias(mbhc, 0);
+
+ wcd9xxx_report_plug(mbhc, 0, SND_JACK_HEADSET);
+ wcd9xxx_cleanup_hs_polling(mbhc);
+ wcd9xxx_enable_hs_detect(mbhc, 1, MBHC_USE_MB_TRIGGER |
+ MBHC_USE_HPHL_TRIGGER,
+ true);
+ }
+ } else {
+ wcd9xxx_start_hs_polling(mbhc);
+ }
+ pr_debug("%s: leave\n", __func__);
+}
+
+/* called only from interrupt which is under codec_resource_lock acquisition */
+static void wcd9xxx_hs_insert_irq_extn(struct wcd9xxx_mbhc *mbhc,
+ bool is_mb_trigger)
+{
+ /* Cancel possibly running hs_detect_work */
+ wcd9xxx_cancel_hs_detect_plug(mbhc, &mbhc->correct_plug_swch);
+
+ if (is_mb_trigger) {
+ pr_debug("%s: Waiting for Headphone left trigger\n", __func__);
+ wcd9xxx_enable_hs_detect(mbhc, 1, MBHC_USE_HPHL_TRIGGER, false);
+ } else {
+ pr_debug("%s: HPHL trigger received, detecting plug type\n",
+ __func__);
+ wcd9xxx_mbhc_detect_plug_type(mbhc);
+ }
+}
+
+static irqreturn_t wcd9xxx_hs_remove_irq(int irq, void *data)
+{
+ bool vddio;
+ struct wcd9xxx_mbhc *mbhc = data;
+
+ pr_debug("%s: enter, removal interrupt\n", __func__);
+ WCD9XXX_BCL_LOCK(mbhc->resmgr);
+ vddio = (mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV &&
+ mbhc->mbhc_micbias_switched);
+ if (vddio)
+ __wcd9xxx_switch_micbias(mbhc, 0, false, true);
+
+ if (mbhc->mbhc_cfg->detect_extn_cable &&
+ !wcd9xxx_swch_level_remove(mbhc))
+ wcd9xxx_hs_remove_irq_noswch(mbhc);
+ else
+ wcd9xxx_hs_remove_irq_swch(mbhc);
+
+ /*
+ * if driver turned off vddio switch and headset is not removed,
+ * turn on the vddio switch back, if headset is removed then vddio
+ * switch is off by time now and shouldn't be turn on again from here
+ */
+ if (vddio && mbhc->current_plug == PLUG_TYPE_HEADSET)
+ __wcd9xxx_switch_micbias(mbhc, 1, true, true);
+ WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t wcd9xxx_hs_insert_irq(int irq, void *data)
+{
+ bool is_mb_trigger, is_removal;
+ struct wcd9xxx_mbhc *mbhc = data;
+ struct snd_soc_codec *codec = mbhc->codec;
+
+ pr_debug("%s: enter\n", __func__);
+ WCD9XXX_BCL_LOCK(mbhc->resmgr);
+ wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_INSERTION);
+
+ is_mb_trigger = !!(snd_soc_read(codec, mbhc->mbhc_bias_regs.mbhc_reg) &
+ 0x10);
+ is_removal = !!(snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_INT_CTL) & 0x02);
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x03, 0x00);
+
+ /* Turn off both HPH and MIC line schmitt triggers */
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x90, 0x00);
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x13, 0x00);
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
+
+ if (mbhc->mbhc_cfg->detect_extn_cable &&
+ mbhc->current_plug == PLUG_TYPE_HIGH_HPH)
+ wcd9xxx_hs_insert_irq_extn(mbhc, is_mb_trigger);
+ else
+ wcd9xxx_hs_insert_irq_swch(mbhc, is_removal);
+
+ WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
+ return IRQ_HANDLED;
+}
+
+static void wcd9xxx_btn_lpress_fn(struct work_struct *work)
+{
+ struct delayed_work *dwork;
+ short bias_value;
+ int dce_mv, sta_mv;
+ struct wcd9xxx_mbhc *mbhc;
+
+ pr_debug("%s:\n", __func__);
+
+ dwork = to_delayed_work(work);
+ mbhc = container_of(dwork, struct wcd9xxx_mbhc, mbhc_btn_dwork);
+
+ bias_value = wcd9xxx_read_sta_result(mbhc->codec);
+ sta_mv = wcd9xxx_codec_sta_dce_v(mbhc, 0, bias_value);
+
+ bias_value = wcd9xxx_read_dce_result(mbhc->codec);
+ dce_mv = wcd9xxx_codec_sta_dce_v(mbhc, 1, bias_value);
+ pr_debug("%s: STA: %d, DCE: %d\n", __func__, sta_mv, dce_mv);
+
+ pr_debug("%s: Reporting long button press event\n", __func__);
+ wcd9xxx_jack_report(&mbhc->button_jack, mbhc->buttons_pressed,
+ mbhc->buttons_pressed);
+
+ pr_debug("%s: leave\n", __func__);
+ wcd9xxx_unlock_sleep(mbhc->resmgr->core);
+}
+
+static void wcd9xxx_mbhc_insert_work(struct work_struct *work)
+{
+ struct delayed_work *dwork;
+ struct wcd9xxx_mbhc *mbhc;
+ struct snd_soc_codec *codec;
+ struct wcd9xxx *core;
+
+ dwork = to_delayed_work(work);
+ mbhc = container_of(dwork, struct wcd9xxx_mbhc, mbhc_insert_dwork);
+ codec = mbhc->codec;
+ core = mbhc->resmgr->core;
+
+ pr_debug("%s:\n", __func__);
+
+ /* Turn off both HPH and MIC line schmitt triggers */
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x90, 0x00);
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x13, 0x00);
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
+ wcd9xxx_disable_irq_sync(core, WCD9XXX_IRQ_MBHC_INSERTION);
+ wcd9xxx_mbhc_detect_plug_type(mbhc);
+ wcd9xxx_unlock_sleep(core);
+}
+
+static bool wcd9xxx_mbhc_fw_validate(const struct firmware *fw)
+{
+ u32 cfg_offset;
+ struct wcd9xxx_mbhc_imped_detect_cfg *imped_cfg;
+ struct wcd9xxx_mbhc_btn_detect_cfg *btn_cfg;
+
+ if (fw->size < WCD9XXX_MBHC_CAL_MIN_SIZE)
+ return false;
+
+ /*
+ * Previous check guarantees that there is enough fw data up
+ * to num_btn
+ */
+ btn_cfg = WCD9XXX_MBHC_CAL_BTN_DET_PTR(fw->data);
+ cfg_offset = (u32) ((void *) btn_cfg - (void *) fw->data);
+ if (fw->size < (cfg_offset + WCD9XXX_MBHC_CAL_BTN_SZ(btn_cfg)))
+ return false;
+
+ /*
+ * Previous check guarantees that there is enough fw data up
+ * to start of impedance detection configuration
+ */
+ imped_cfg = WCD9XXX_MBHC_CAL_IMPED_DET_PTR(fw->data);
+ cfg_offset = (u32) ((void *) imped_cfg - (void *) fw->data);
+
+ if (fw->size < (cfg_offset + WCD9XXX_MBHC_CAL_IMPED_MIN_SZ))
+ return false;
+
+ if (fw->size < (cfg_offset + WCD9XXX_MBHC_CAL_IMPED_SZ(imped_cfg)))
+ return false;
+
+ return true;
+}
+
+static u16 wcd9xxx_codec_v_sta_dce(struct wcd9xxx_mbhc *mbhc,
+ enum meas_type dce, s16 vin_mv)
+{
+ s16 diff, zero;
+ u32 mb_mv, in;
+ u16 value;
+
+ mb_mv = mbhc->mbhc_data.micb_mv;
+
+ if (mb_mv == 0) {
+ pr_err("%s: Mic Bias voltage is set to zero\n", __func__);
+ return -EINVAL;
+ }
+
+ if (dce) {
+ diff = (mbhc->mbhc_data.dce_mb) - (mbhc->mbhc_data.dce_z);
+ zero = (mbhc->mbhc_data.dce_z);
+ } else {
+ diff = (mbhc->mbhc_data.sta_mb) - (mbhc->mbhc_data.sta_z);
+ zero = (mbhc->mbhc_data.sta_z);
+ }
+ in = (u32) diff * vin_mv;
+
+ value = (u16) (in / mb_mv) + zero;
+ return value;
+}
+
+static void wcd9xxx_mbhc_calc_thres(struct wcd9xxx_mbhc *mbhc)
+{
+ struct snd_soc_codec *codec;
+ s16 btn_mv = 0, btn_delta_mv;
+ struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
+ struct wcd9xxx_mbhc_plug_type_cfg *plug_type;
+ u16 *btn_high;
+ int i;
+
+ pr_debug("%s: enter\n", __func__);
+ codec = mbhc->codec;
+ btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
+ plug_type = WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
+
+ mbhc->mbhc_data.v_ins_hu =
+ wcd9xxx_codec_v_sta_dce(mbhc, STA, plug_type->v_hs_max);
+ mbhc->mbhc_data.v_ins_h =
+ wcd9xxx_codec_v_sta_dce(mbhc, DCE, plug_type->v_hs_max);
+
+ mbhc->mbhc_data.v_inval_ins_low = FAKE_INS_LOW;
+ mbhc->mbhc_data.v_inval_ins_high = FAKE_INS_HIGH;
+
+ if (mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) {
+ mbhc->mbhc_data.adj_v_hs_max =
+ scale_v_micb_vddio(mbhc, plug_type->v_hs_max, true);
+ mbhc->mbhc_data.adj_v_ins_hu =
+ wcd9xxx_codec_v_sta_dce(mbhc, STA,
+ mbhc->mbhc_data.adj_v_hs_max);
+ mbhc->mbhc_data.adj_v_ins_h =
+ wcd9xxx_codec_v_sta_dce(mbhc, DCE,
+ mbhc->mbhc_data.adj_v_hs_max);
+ mbhc->mbhc_data.v_inval_ins_low =
+ scale_v_micb_vddio(mbhc, mbhc->mbhc_data.v_inval_ins_low,
+ false);
+ mbhc->mbhc_data.v_inval_ins_high =
+ scale_v_micb_vddio(mbhc, mbhc->mbhc_data.v_inval_ins_high,
+ false);
+ }
+
+ btn_high = wcd9xxx_mbhc_cal_btn_det_mp(btn_det,
+ MBHC_BTN_DET_V_BTN_HIGH);
+ for (i = 0; i < btn_det->num_btn; i++)
+ btn_mv = btn_high[i] > btn_mv ? btn_high[i] : btn_mv;
+
+ mbhc->mbhc_data.v_b1_h = wcd9xxx_codec_v_sta_dce(mbhc, DCE, btn_mv);
+ btn_delta_mv = btn_mv + btn_det->v_btn_press_delta_sta;
+ mbhc->mbhc_data.v_b1_hu =
+ wcd9xxx_codec_v_sta_dce(mbhc, STA, btn_delta_mv);
+
+ btn_delta_mv = btn_mv + btn_det->v_btn_press_delta_cic;
+
+ mbhc->mbhc_data.v_b1_huc =
+ wcd9xxx_codec_v_sta_dce(mbhc, DCE, btn_delta_mv);
+
+ mbhc->mbhc_data.v_brh = mbhc->mbhc_data.v_b1_h;
+ mbhc->mbhc_data.v_brl = BUTTON_MIN;
+
+ mbhc->mbhc_data.v_no_mic =
+ wcd9xxx_codec_v_sta_dce(mbhc, STA, plug_type->v_no_mic);
+ pr_debug("%s: leave\n", __func__);
+}
+
+static void wcd9xxx_onoff_ext_mclk(struct wcd9xxx_mbhc *mbhc, bool on)
+{
+ /*
+ * XXX: {codec}_mclk_enable holds WCD9XXX_BCL_LOCK,
+ * therefore wcd9xxx_onoff_ext_mclk caller SHOULDN'T hold
+ * WCD9XXX_BCL_LOCK when it calls wcd9xxx_onoff_ext_mclk()
+ */
+ mbhc->mbhc_cfg->mclk_cb_fn(mbhc->codec, on, false);
+}
+
+static void wcd9xxx_correct_swch_plug(struct work_struct *work)
+{
+ struct wcd9xxx_mbhc *mbhc;
+ struct snd_soc_codec *codec;
+ enum wcd9xxx_mbhc_plug_type plug_type = PLUG_TYPE_INVALID;
+ unsigned long timeout;
+ int retry = 0, pt_gnd_mic_swap_cnt = 0;
+ bool correction = false;
+
+ pr_debug("%s: enter\n", __func__);
+
+ mbhc = container_of(work, struct wcd9xxx_mbhc, correct_plug_swch);
+ codec = mbhc->codec;
+
+ wcd9xxx_onoff_ext_mclk(mbhc, true);
+
+ /*
+ * Keep override on during entire plug type correction work.
+ *
+ * This is okay under the assumption that any switch irqs which use
+ * MBHC block cancel and sync this work so override is off again
+ * prior to switch interrupt handler's MBHC block usage.
+ * Also while this correction work is running, we can guarantee
+ * DAPM doesn't use any MBHC block as this work only runs with
+ * headphone detection.
+ */
+ wcd9xxx_turn_onoff_override(codec, true);
+
+ timeout = jiffies + msecs_to_jiffies(HS_DETECT_PLUG_TIME_MS);
+ while (!time_after(jiffies, timeout)) {
+ ++retry;
+ rmb();
+ if (mbhc->hs_detect_work_stop) {
+ pr_debug("%s: stop requested\n", __func__);
+ break;
+ }
+
+ msleep(HS_DETECT_PLUG_INERVAL_MS);
+ if (wcd9xxx_swch_level_remove(mbhc)) {
+ pr_debug("%s: Switch level is low\n", __func__);
+ break;
+ }
+
+ /* can race with removal interrupt */
+ WCD9XXX_BCL_LOCK(mbhc->resmgr);
+ plug_type = wcd9xxx_codec_get_plug_type(mbhc, true);
+ WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
+
+ pr_debug("%s: attempt(%d) current_plug(%d) new_plug(%d)\n",
+ __func__, retry, mbhc->current_plug, plug_type);
+ if (plug_type == PLUG_TYPE_INVALID) {
+ pr_debug("Invalid plug in attempt # %d\n", retry);
+ if (!mbhc->mbhc_cfg->detect_extn_cable &&
+ retry == NUM_ATTEMPTS_TO_REPORT &&
+ mbhc->current_plug == PLUG_TYPE_NONE) {
+ wcd9xxx_report_plug(mbhc, 1,
+ SND_JACK_HEADPHONE);
+ }
+ } else if (plug_type == PLUG_TYPE_HEADPHONE) {
+ pr_debug("Good headphone detected, continue polling\n");
+ if (mbhc->mbhc_cfg->detect_extn_cable) {
+ if (mbhc->current_plug != plug_type)
+ wcd9xxx_report_plug(mbhc, 1,
+ SND_JACK_HEADPHONE);
+ } else if (mbhc->current_plug == PLUG_TYPE_NONE) {
+ wcd9xxx_report_plug(mbhc, 1,
+ SND_JACK_HEADPHONE);
+ }
+ } else {
+ if (plug_type == PLUG_TYPE_GND_MIC_SWAP) {
+ pt_gnd_mic_swap_cnt++;
+ if (pt_gnd_mic_swap_cnt <
+ GND_MIC_SWAP_THRESHOLD)
+ continue;
+ else if (pt_gnd_mic_swap_cnt >
+ GND_MIC_SWAP_THRESHOLD) {
+ /*
+ * This is due to GND/MIC switch didn't
+ * work, Report unsupported plug
+ */
+ } else if (mbhc->mbhc_cfg->swap_gnd_mic) {
+ /*
+ * if switch is toggled, check again,
+ * otherwise report unsupported plug
+ */
+ if (mbhc->mbhc_cfg->swap_gnd_mic(codec))
+ continue;
+ }
+ } else
+ pt_gnd_mic_swap_cnt = 0;
+
+ WCD9XXX_BCL_LOCK(mbhc->resmgr);
+ /* Turn off override */
+ wcd9xxx_turn_onoff_override(codec, false);
+ /*
+ * The valid plug also includes PLUG_TYPE_GND_MIC_SWAP
+ */
+ wcd9xxx_find_plug_and_report(mbhc, plug_type);
+ WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
+ pr_debug("Attempt %d found correct plug %d\n", retry,
+ plug_type);
+ correction = true;
+ break;
+ }
+ }
+
+ /* Turn off override */
+ if (!correction)
+ wcd9xxx_turn_onoff_override(codec, false);
+
+ wcd9xxx_onoff_ext_mclk(mbhc, false);
+
+ if (mbhc->mbhc_cfg->detect_extn_cable) {
+ WCD9XXX_BCL_LOCK(mbhc->resmgr);
+ if (mbhc->current_plug == PLUG_TYPE_HEADPHONE ||
+ mbhc->current_plug == PLUG_TYPE_GND_MIC_SWAP ||
+ mbhc->current_plug == PLUG_TYPE_INVALID ||
+ plug_type == PLUG_TYPE_INVALID) {
+ /* Enable removal detection */
+ wcd9xxx_cleanup_hs_polling(mbhc);
+ wcd9xxx_enable_hs_detect(mbhc, 0, 0, false);
+ }
+ WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
+ }
+ pr_debug("%s: leave current_plug(%d)\n", __func__, mbhc->current_plug);
+ /* unlock sleep */
+ wcd9xxx_unlock_sleep(mbhc->resmgr->core);
+}
+
+static void wcd9xxx_swch_irq_handler(struct wcd9xxx_mbhc *mbhc)
+{
+ bool insert;
+ bool is_removed = false;
+ struct snd_soc_codec *codec = mbhc->codec;
+
+ pr_debug("%s: enter\n", __func__);
+
+ mbhc->in_swch_irq_handler = true;
+ /* Wait here for debounce time */
+ usleep_range(SWCH_IRQ_DEBOUNCE_TIME_US, SWCH_IRQ_DEBOUNCE_TIME_US);
+
+ WCD9XXX_BCL_LOCK(mbhc->resmgr);
+
+ /* cancel pending button press */
+ if (wcd9xxx_cancel_btn_work(mbhc))
+ pr_debug("%s: button press is canceled\n", __func__);
+
+ insert = !wcd9xxx_swch_level_remove(mbhc);
+ pr_debug("%s: Current plug type %d, insert %d\n", __func__,
+ mbhc->current_plug, insert);
+ if ((mbhc->current_plug == PLUG_TYPE_NONE) && insert) {
+ mbhc->lpi_enabled = false;
+ wmb();
+
+ /* cancel detect plug */
+ wcd9xxx_cancel_hs_detect_plug(mbhc,
+ &mbhc->correct_plug_swch);
+
+ /* Disable Mic Bias pull down and HPH Switch to GND */
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01,
+ 0x00);
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x01, 0x00);
+ wcd9xxx_mbhc_detect_plug_type(mbhc);
+ } else if ((mbhc->current_plug != PLUG_TYPE_NONE) && !insert) {
+ mbhc->lpi_enabled = false;
+ wmb();
+
+ /* cancel detect plug */
+ wcd9xxx_cancel_hs_detect_plug(mbhc,
+ &mbhc->correct_plug_swch);
+
+ if (mbhc->current_plug == PLUG_TYPE_HEADPHONE) {
+ wcd9xxx_report_plug(mbhc, 0, SND_JACK_HEADPHONE);
+ is_removed = true;
+ } else if (mbhc->current_plug == PLUG_TYPE_GND_MIC_SWAP) {
+ wcd9xxx_report_plug(mbhc, 0, SND_JACK_UNSUPPORTED);
+ is_removed = true;
+ } else if (mbhc->current_plug == PLUG_TYPE_HEADSET) {
+ wcd9xxx_pause_hs_polling(mbhc);
+ wcd9xxx_cleanup_hs_polling(mbhc);
+ wcd9xxx_report_plug(mbhc, 0, SND_JACK_HEADSET);
+ is_removed = true;
+ } else if (mbhc->current_plug == PLUG_TYPE_HIGH_HPH) {
+ wcd9xxx_report_plug(mbhc, 0, SND_JACK_LINEOUT);
+ is_removed = true;
+ }
+
+ if (is_removed) {
+ /* Enable Mic Bias pull down and HPH Switch to GND */
+ snd_soc_update_bits(codec,
+ mbhc->mbhc_bias_regs.ctl_reg, 0x01,
+ 0x01);
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x01,
+ 0x01);
+ /* Make sure mic trigger is turned off */
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg,
+ 0x01, 0x01);
+ snd_soc_update_bits(codec,
+ mbhc->mbhc_bias_regs.mbhc_reg,
+ 0x90, 0x00);
+ /* Reset MBHC State Machine */
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL,
+ 0x08, 0x08);
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL,
+ 0x08, 0x00);
+ /* Turn off override */
+ wcd9xxx_turn_onoff_override(codec, false);
+ }
+ }
+
+ mbhc->in_swch_irq_handler = false;
+ WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
+ pr_debug("%s: leave\n", __func__);
+}
+
+static irqreturn_t wcd9xxx_mech_plug_detect_irq(int irq, void *data)
+{
+ int r = IRQ_HANDLED;
+ struct wcd9xxx_mbhc *mbhc = data;
+
+ pr_debug("%s: enter\n", __func__);
+ if (unlikely(wcd9xxx_lock_sleep(mbhc->resmgr->core) == false)) {
+ pr_warn("%s: failed to hold suspend\n", __func__);
+ r = IRQ_NONE;
+ } else {
+ /* Call handler */
+ wcd9xxx_swch_irq_handler(mbhc);
+ wcd9xxx_unlock_sleep(mbhc->resmgr->core);
+ }
+
+ pr_debug("%s: leave %d\n", __func__, r);
+ return r;
+}
+
+/* called under codec_resource_lock acquisition */
+static void wcd9xxx_codec_drive_v_to_micbias(struct wcd9xxx_mbhc *mbhc,
+ int usec)
+{
+ int cfilt_k_val;
+ bool set = true;
+
+ if (mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV &&
+ mbhc->mbhc_micbias_switched) {
+ pr_debug("%s: set mic V to micbias V\n", __func__);
+ snd_soc_update_bits(mbhc->codec, WCD9XXX_A_CDC_MBHC_CLK_CTL,
+ 0x2, 0x2);
+ wcd9xxx_turn_onoff_override(mbhc->codec, true);
+ while (1) {
+ cfilt_k_val =
+ wcd9xxx_resmgr_get_k_val(mbhc->resmgr,
+ set ? mbhc->mbhc_data.micb_mv :
+ VDDIO_MICBIAS_MV);
+ snd_soc_update_bits(mbhc->codec,
+ mbhc->mbhc_bias_regs.cfilt_val,
+ 0xFC, (cfilt_k_val << 2));
+ if (!set)
+ break;
+ usleep_range(usec, usec);
+ set = false;
+ }
+ wcd9xxx_turn_onoff_override(mbhc->codec, false);
+ }
+}
+
+static int wcd9xxx_is_fake_press(struct wcd9xxx_mbhc *mbhc)
+{
+ int i;
+ int r = 0;
+ const int dces = NUM_DCE_PLUG_DETECT;
+ s16 mb_v, v_ins_hu, v_ins_h;
+
+ v_ins_hu = wcd9xxx_get_current_v_ins(mbhc, true);
+ v_ins_h = wcd9xxx_get_current_v_ins(mbhc, false);
+
+ for (i = 0; i < dces; i++) {
+ usleep_range(10000, 10000);
+ if (i == 0) {
+ mb_v = wcd9xxx_codec_sta_dce(mbhc, 0, true);
+ pr_debug("%s: STA[0]: %d,%d\n", __func__, mb_v,
+ wcd9xxx_codec_sta_dce_v(mbhc, 0, mb_v));
+ if (mb_v < (s16)mbhc->mbhc_data.v_b1_hu ||
+ mb_v > v_ins_hu) {
+ r = 1;
+ break;
+ }
+ } else {
+ mb_v = wcd9xxx_codec_sta_dce(mbhc, 1, true);
+ pr_debug("%s: DCE[%d]: %d,%d\n", __func__, i, mb_v,
+ wcd9xxx_codec_sta_dce_v(mbhc, 1, mb_v));
+ if (mb_v < (s16)mbhc->mbhc_data.v_b1_h ||
+ mb_v > v_ins_h) {
+ r = 1;
+ break;
+ }
+ }
+ }
+
+ return r;
+}
+
+/* called under codec_resource_lock acquisition */
+static int wcd9xxx_determine_button(const struct wcd9xxx_mbhc *mbhc,
+ const s32 micmv)
+{
+ s16 *v_btn_low, *v_btn_high;
+ struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
+ int i, btn = -1;
+
+ btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
+ v_btn_low = wcd9xxx_mbhc_cal_btn_det_mp(btn_det,
+ MBHC_BTN_DET_V_BTN_LOW);
+ v_btn_high = wcd9xxx_mbhc_cal_btn_det_mp(btn_det,
+ MBHC_BTN_DET_V_BTN_HIGH);
+
+ for (i = 0; i < btn_det->num_btn; i++) {
+ if ((v_btn_low[i] <= micmv) && (v_btn_high[i] >= micmv)) {
+ btn = i;
+ break;
+ }
+ }
+
+ if (btn == -1)
+ pr_debug("%s: couldn't find button number for mic mv %d\n",
+ __func__, micmv);
+
+ return btn;
+}
+
+static int wcd9xxx_get_button_mask(const int btn)
+{
+ int mask = 0;
+ switch (btn) {
+ case 0:
+ mask = SND_JACK_BTN_0;
+ break;
+ case 1:
+ mask = SND_JACK_BTN_1;
+ break;
+ case 2:
+ mask = SND_JACK_BTN_2;
+ break;
+ case 3:
+ mask = SND_JACK_BTN_3;
+ break;
+ case 4:
+ mask = SND_JACK_BTN_4;
+ break;
+ case 5:
+ mask = SND_JACK_BTN_5;
+ break;
+ case 6:
+ mask = SND_JACK_BTN_6;
+ break;
+ case 7:
+ mask = SND_JACK_BTN_7;
+ break;
+ }
+ return mask;
+}
+
+irqreturn_t wcd9xxx_dce_handler(int irq, void *data)
+{
+ int i, mask;
+ short dce, sta;
+ s32 mv, mv_s, stamv_s;
+ bool vddio;
+ u8 mbhc_status;
+ int btn = -1, meas = 0;
+ struct wcd9xxx_mbhc *mbhc = data;
+ const struct wcd9xxx_mbhc_btn_detect_cfg *d =
+ WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
+ short btnmeas[d->n_btn_meas + 1];
+ struct snd_soc_codec *codec = mbhc->codec;
+ struct wcd9xxx *core = mbhc->resmgr->core;
+ int n_btn_meas = d->n_btn_meas;
+
+ pr_debug("%s: enter\n", __func__);
+
+ WCD9XXX_BCL_LOCK(mbhc->resmgr);
+ mbhc_status = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_STATUS) & 0x3E;
+
+ if (mbhc->mbhc_state == MBHC_STATE_POTENTIAL_RECOVERY) {
+ pr_debug("%s: mbhc is being recovered, skip button press\n",
+ __func__);
+ goto done;
+ }
+
+ mbhc->mbhc_state = MBHC_STATE_POTENTIAL;
+
+ if (!mbhc->polling_active) {
+ pr_warn("%s: mbhc polling is not active, skip button press\n",
+ __func__);
+ goto done;
+ }
+
+ dce = wcd9xxx_read_dce_result(codec);
+ mv = wcd9xxx_codec_sta_dce_v(mbhc, 1, dce);
+
+ /* If switch nterrupt already kicked in, ignore button press */
+ if (mbhc->in_swch_irq_handler) {
+ pr_debug("%s: Swtich level changed, ignore button press\n",
+ __func__);
+ btn = -1;
+ goto done;
+ }
+
+ /* Measure scaled HW DCE */
+ vddio = (mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV &&
+ mbhc->mbhc_micbias_switched);
+ mv_s = vddio ? scale_v_micb_vddio(mbhc, mv, false) : mv;
+
+ /* Measure scaled HW STA */
+ sta = wcd9xxx_read_sta_result(codec);
+ stamv_s = wcd9xxx_codec_sta_dce_v(mbhc, 0, sta);
+ if (vddio)
+ stamv_s = scale_v_micb_vddio(mbhc, stamv_s, false);
+ if (mbhc_status != STATUS_REL_DETECTION) {
+ if (mbhc->mbhc_last_resume &&
+ !time_after(jiffies, mbhc->mbhc_last_resume + HZ)) {
+ pr_debug("%s: Button is released after resume\n",
+ __func__);
+ n_btn_meas = 0;
+ } else {
+ pr_debug("%s: Button is released without resume",
+ __func__);
+ btn = wcd9xxx_determine_button(mbhc, mv_s);
+ if (btn != wcd9xxx_determine_button(mbhc, stamv_s))
+ btn = -1;
+ goto done;
+ }
+ }
+
+ pr_debug("%s: Meas HW - STA 0x%x,%d,%d\n", __func__,
+ sta & 0xFFFF, wcd9xxx_codec_sta_dce_v(mbhc, 0, sta), stamv_s);
+
+ /* determine pressed button */
+ btnmeas[meas++] = wcd9xxx_determine_button(mbhc, mv_s);
+ pr_debug("%s: Meas HW - DCE 0x%x,%d,%d button %d\n", __func__,
+ dce & 0xFFFF, mv, mv_s, btnmeas[meas - 1]);
+ if (n_btn_meas == 0)
+ btn = btnmeas[0];
+ for (; ((d->n_btn_meas) && (meas < (d->n_btn_meas + 1))); meas++) {
+ dce = wcd9xxx_codec_sta_dce(mbhc, 1, false);
+ mv = wcd9xxx_codec_sta_dce_v(mbhc, 1, dce);
+ mv_s = vddio ? scale_v_micb_vddio(mbhc, mv, false) : mv;
+
+ btnmeas[meas] = wcd9xxx_determine_button(mbhc, mv_s);
+ pr_debug("%s: Meas %d - DCE 0x%x,%d,%d button %d\n",
+ __func__, meas, dce & 0xFFFF, mv, mv_s, btnmeas[meas]);
+ /*
+ * if large enough measurements are collected,
+ * start to check if last all n_btn_con measurements were
+ * in same button low/high range
+ */
+ if (meas + 1 >= d->n_btn_con) {
+ for (i = 0; i < d->n_btn_con; i++)
+ if ((btnmeas[meas] < 0) ||
+ (btnmeas[meas] != btnmeas[meas - i]))
+ break;
+ if (i == d->n_btn_con) {
+ /* button pressed */
+ btn = btnmeas[meas];
+ break;
+ } else if ((n_btn_meas - meas) < (d->n_btn_con - 1)) {
+ /*
+ * if left measurements are less than n_btn_con,
+ * it's impossible to find button number
+ */
+ break;
+ }
+ }
+ }
+
+ if (btn >= 0) {
+ if (mbhc->in_swch_irq_handler) {
+ pr_debug(
+ "%s: Switch irq triggered, ignore button press\n",
+ __func__);
+ goto done;
+ }
+ mask = wcd9xxx_get_button_mask(btn);
+ mbhc->buttons_pressed |= mask;
+ wcd9xxx_lock_sleep(core);
+ if (schedule_delayed_work(&mbhc->mbhc_btn_dwork,
+ msecs_to_jiffies(400)) == 0) {
+ WARN(1, "Button pressed twice without release event\n");
+ wcd9xxx_unlock_sleep(core);
+ }
+ } else {
+ pr_debug("%s: bogus button press, too short press?\n",
+ __func__);
+ }
+
+ done:
+ pr_debug("%s: leave\n", __func__);
+ WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t wcd9xxx_release_handler(int irq, void *data)
+{
+ int ret;
+ struct wcd9xxx_mbhc *mbhc = data;
+
+ pr_debug("%s: enter\n", __func__);
+ WCD9XXX_BCL_LOCK(mbhc->resmgr);
+ mbhc->mbhc_state = MBHC_STATE_RELEASE;
+
+ wcd9xxx_codec_drive_v_to_micbias(mbhc, 10000);
+
+ if (mbhc->buttons_pressed & WCD9XXX_JACK_BUTTON_MASK) {
+ ret = wcd9xxx_cancel_btn_work(mbhc);
+ if (ret == 0) {
+ pr_debug("%s: Reporting long button release event\n",
+ __func__);
+ wcd9xxx_jack_report(&mbhc->button_jack, 0,
+ mbhc->buttons_pressed);
+ } else {
+ if (wcd9xxx_is_fake_press(mbhc)) {
+ pr_debug("%s: Fake button press interrupt\n",
+ __func__);
+ } else {
+ if (mbhc->in_swch_irq_handler) {
+ pr_debug("%s: Switch irq kicked in, ignore\n",
+ __func__);
+ } else {
+ pr_debug("%s: Reporting btn press\n",
+ __func__);
+ wcd9xxx_jack_report(&mbhc->button_jack,
+ mbhc->buttons_pressed,
+ mbhc->buttons_pressed);
+ pr_debug("%s: Reporting btn release\n",
+ __func__);
+ wcd9xxx_jack_report(&mbhc->button_jack,
+ 0, mbhc->buttons_pressed);
+ }
+ }
+ }
+
+ mbhc->buttons_pressed &= ~WCD9XXX_JACK_BUTTON_MASK;
+ }
+
+ wcd9xxx_calibrate_hs_polling(mbhc);
+
+ msleep(SWCH_REL_DEBOUNCE_TIME_MS);
+ wcd9xxx_start_hs_polling(mbhc);
+
+ pr_debug("%s: leave\n", __func__);
+ WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t wcd9xxx_hphl_ocp_irq(int irq, void *data)
+{
+ struct wcd9xxx_mbhc *mbhc = data;
+ struct snd_soc_codec *codec;
+
+ pr_info("%s: received HPHL OCP irq\n", __func__);
+
+ if (mbhc) {
+ codec = mbhc->codec;
+ if (mbhc->hphlocp_cnt++ < OCP_ATTEMPT) {
+ pr_info("%s: retry\n", __func__);
+ snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL,
+ 0x10, 0x00);
+ snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL,
+ 0x10, 0x10);
+ } else {
+ wcd9xxx_disable_irq(codec->control_data,
+ WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
+ mbhc->hphlocp_cnt = 0;
+ mbhc->hph_status |= SND_JACK_OC_HPHL;
+ wcd9xxx_jack_report(&mbhc->headset_jack,
+ mbhc->hph_status,
+ WCD9XXX_JACK_MASK);
+ }
+ } else {
+ pr_err("%s: Bad wcd9xxx private data\n", __func__);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t wcd9xxx_hphr_ocp_irq(int irq, void *data)
+{
+ struct wcd9xxx_mbhc *mbhc = data;
+ struct snd_soc_codec *codec;
+
+ pr_info("%s: received HPHR OCP irq\n", __func__);
+ codec = mbhc->codec;
+ if (mbhc->hphrocp_cnt++ < OCP_ATTEMPT) {
+ pr_info("%s: retry\n", __func__);
+ snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x10,
+ 0x00);
+ snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x10,
+ 0x10);
+ } else {
+ wcd9xxx_disable_irq(mbhc->resmgr->core,
+ WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
+ mbhc->hphrocp_cnt = 0;
+ mbhc->hph_status |= SND_JACK_OC_HPHR;
+ wcd9xxx_jack_report(&mbhc->headset_jack,
+ mbhc->hph_status, WCD9XXX_JACK_MASK);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int wcd9xxx_acdb_mclk_index(const int rate)
+{
+ if (rate == MCLK_RATE_12288KHZ)
+ return 0;
+ else if (rate == MCLK_RATE_9600KHZ)
+ return 1;
+ else {
+ BUG_ON(1);
+ return -EINVAL;
+ }
+}
+
+static void wcd9xxx_update_mbhc_clk_rate(struct wcd9xxx_mbhc *mbhc, u32 rate)
+{
+ u32 dce_wait, sta_wait;
+ u8 ncic, nmeas, navg;
+ void *calibration;
+ u8 *n_cic, *n_ready;
+ struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
+ u8 npoll = 4, nbounce_wait = 30;
+ struct snd_soc_codec *codec = mbhc->codec;
+ int idx = wcd9xxx_acdb_mclk_index(rate);
+ int idxmclk = wcd9xxx_acdb_mclk_index(mbhc->mbhc_cfg->mclk_rate);
+
+ pr_debug("%s: Updating clock rate dependents, rate = %u\n", __func__,
+ rate);
+ calibration = mbhc->mbhc_cfg->calibration;
+
+ /*
+ * First compute the DCE / STA wait times depending on tunable
+ * parameters. The value is computed in microseconds
+ */
+ btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(calibration);
+ n_ready = wcd9xxx_mbhc_cal_btn_det_mp(btn_det, MBHC_BTN_DET_N_READY);
+ n_cic = wcd9xxx_mbhc_cal_btn_det_mp(btn_det, MBHC_BTN_DET_N_CIC);
+ nmeas = WCD9XXX_MBHC_CAL_BTN_DET_PTR(calibration)->n_meas;
+ navg = WCD9XXX_MBHC_CAL_GENERAL_PTR(calibration)->mbhc_navg;
+
+ /* ncic stays with the same what we had during calibration */
+ ncic = n_cic[idxmclk];
+ dce_wait = (1000 * 512 * ncic * (nmeas + 1)) / (rate / 1000);
+ sta_wait = (1000 * 128 * (navg + 1)) / (rate / 1000);
+ mbhc->mbhc_data.t_dce = dce_wait;
+ mbhc->mbhc_data.t_sta = sta_wait;
+ mbhc->mbhc_data.t_sta_dce = ((1000 * 256) / (rate / 1000) *
+ n_ready[idx]) + 10;
+
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B1_CTL, n_ready[idx]);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B6_CTL, ncic);
+
+ if (rate == MCLK_RATE_12288KHZ) {
+ npoll = 4;
+ nbounce_wait = 30;
+ } else if (rate == MCLK_RATE_9600KHZ) {
+ npoll = 3;
+ nbounce_wait = 23;
+ }
+
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B2_CTL, npoll);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B3_CTL, nbounce_wait);
+ pr_debug("%s: leave\n", __func__);
+}
+
+static void wcd9xxx_mbhc_cal(struct wcd9xxx_mbhc *mbhc)
+{
+ u8 cfilt_mode, bg_mode;
+ struct snd_soc_codec *codec = mbhc->codec;
+
+ pr_debug("%s: enter\n", __func__);
+ wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_POTENTIAL);
+ wcd9xxx_turn_onoff_rel_detection(codec, false);
+
+ /* t_dce and t_sta are updated by wcd9xxx_update_mbhc_clk_rate() */
+ WARN_ON(!mbhc->mbhc_data.t_dce);
+ WARN_ON(!mbhc->mbhc_data.t_sta);
+
+ /*
+ * LDOH and CFILT are already configured during pdata handling.
+ * Only need to make sure CFILT and bandgap are in Fast mode.
+ * Need to restore defaults once calculation is done.
+ */
+ cfilt_mode = snd_soc_read(codec, mbhc->mbhc_bias_regs.cfilt_ctl);
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl, 0x40, 0x00);
+ bg_mode = snd_soc_update_bits(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL,
+ 0x02, 0x02);
+
+ /*
+ * Micbias, CFILT, LDOH, MBHC MUX mode settings
+ * to perform ADC calibration
+ */
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x60,
+ mbhc->mbhc_cfg->micbias << 5);
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
+ snd_soc_update_bits(codec, WCD9XXX_A_LDO_H_MODE_1, 0x60, 0x60);
+ snd_soc_write(codec, WCD9XXX_A_TX_7_MBHC_TEST_CTL, 0x78);
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x04, 0x04);
+
+ /* DCE measurement for 0 volts */
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x0A);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x04);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02);
+ snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x81);
+ usleep_range(100, 100);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x04);
+ usleep_range(mbhc->mbhc_data.t_dce, mbhc->mbhc_data.t_dce);
+ mbhc->mbhc_data.dce_z = wcd9xxx_read_dce_result(codec);
+
+ /* DCE measurment for MB voltage */
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x0A);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02);
+ snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x82);
+ usleep_range(100, 100);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x04);
+ usleep_range(mbhc->mbhc_data.t_dce, mbhc->mbhc_data.t_dce);
+ mbhc->mbhc_data.dce_mb = wcd9xxx_read_dce_result(codec);
+
+ /* STA measuremnt for 0 volts */
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x0A);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x02);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02);
+ snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x81);
+ usleep_range(100, 100);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x02);
+ usleep_range(mbhc->mbhc_data.t_sta, mbhc->mbhc_data.t_sta);
+ mbhc->mbhc_data.sta_z = wcd9xxx_read_sta_result(codec);
+
+ /* STA Measurement for MB Voltage */
+ snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x82);
+ usleep_range(100, 100);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x02);
+ usleep_range(mbhc->mbhc_data.t_sta, mbhc->mbhc_data.t_sta);
+ mbhc->mbhc_data.sta_mb = wcd9xxx_read_sta_result(codec);
+
+ /* Restore default settings. */
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x04, 0x00);
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl, 0x40,
+ cfilt_mode);
+ snd_soc_update_bits(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x02,
+ bg_mode);
+
+ snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x84);
+ usleep_range(100, 100);
+
+ wcd9xxx_enable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_POTENTIAL);
+ wcd9xxx_turn_onoff_rel_detection(codec, true);
+ pr_debug("%s: leave\n", __func__);
+}
+
+static void wcd9xxx_mbhc_setup(struct wcd9xxx_mbhc *mbhc)
+{
+ int n;
+ u8 *gain;
+ struct wcd9xxx_mbhc_general_cfg *generic;
+ struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
+ struct snd_soc_codec *codec = mbhc->codec;
+ const int idx = wcd9xxx_acdb_mclk_index(mbhc->mbhc_cfg->mclk_rate);
+
+ pr_debug("%s: enter\n", __func__);
+ generic = WCD9XXX_MBHC_CAL_GENERAL_PTR(mbhc->mbhc_cfg->calibration);
+ btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
+
+ for (n = 0; n < 8; n++) {
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_FIR_B1_CFG,
+ 0x07, n);
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_FIR_B2_CFG,
+ btn_det->c[n]);
+ }
+
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B2_CTL, 0x07,
+ btn_det->nc);
+
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_TIMER_B4_CTL, 0x70,
+ generic->mbhc_nsa << 4);
+
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_TIMER_B4_CTL, 0x0F,
+ btn_det->n_meas);
+
+ snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B5_CTL,
+ generic->mbhc_navg);
+
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x80, 0x80);
+
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x78,
+ btn_det->mbhc_nsc << 3);
+
+ snd_soc_update_bits(codec, mbhc->resmgr->reg_addr->micb_4_mbhc, 0x03,
+ MBHC_MICBIAS2);
+
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x02, 0x02);
+
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_2, 0xF0, 0xF0);
+
+ gain = wcd9xxx_mbhc_cal_btn_det_mp(btn_det, MBHC_BTN_DET_GAIN);
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B2_CTL, 0x78,
+ gain[idx] << 3);
+
+ pr_debug("%s: leave\n", __func__);
+}
+
+static int wcd9xxx_setup_jack_detect_irq(struct wcd9xxx_mbhc *mbhc)
+{
+ int ret = 0;
+ void *core = mbhc->resmgr->core;
+
+ if (mbhc->mbhc_cfg->gpio) {
+ ret = request_threaded_irq(mbhc->mbhc_cfg->gpio_irq, NULL,
+ wcd9xxx_mech_plug_detect_irq,
+ (IRQF_TRIGGER_RISING |
+ IRQF_TRIGGER_FALLING |
+ IRQF_DISABLED),
+ "headset detect", mbhc);
+ if (ret) {
+ pr_err("%s: Failed to request gpio irq %d\n", __func__,
+ mbhc->mbhc_cfg->gpio_irq);
+ } else {
+ ret = enable_irq_wake(mbhc->mbhc_cfg->gpio_irq);
+ if (ret)
+ pr_err("%s: Failed to enable wake up irq %d\n",
+ __func__, mbhc->mbhc_cfg->gpio_irq);
+ }
+ } else if (mbhc->mbhc_cfg->insert_detect) {
+ /* Enable HPHL_10K_SW */
+ snd_soc_update_bits(mbhc->codec, WCD9XXX_A_RX_HPH_OCP_CTL,
+ 1 << 1, 1 << 1);
+ ret = wcd9xxx_request_irq(core, WCD9XXX_IRQ_MBHC_JACK_SWITCH,
+ wcd9xxx_mech_plug_detect_irq,
+ "Jack Detect",
+ mbhc);
+ if (ret)
+ pr_err("%s: Failed to request insert detect irq %d\n",
+ __func__, WCD9XXX_IRQ_MBHC_JACK_SWITCH);
+ }
+
+ return ret;
+}
+
+static int wcd9xxx_init_and_calibrate(struct wcd9xxx_mbhc *mbhc)
+{
+ int ret = 0;
+ struct snd_soc_codec *codec = mbhc->codec;
+
+ pr_debug("%s: enter\n", __func__);
+
+ /* Enable MCLK during calibration */
+ wcd9xxx_onoff_ext_mclk(mbhc, true);
+ wcd9xxx_mbhc_setup(mbhc);
+ wcd9xxx_mbhc_cal(mbhc);
+ wcd9xxx_mbhc_calc_thres(mbhc);
+ wcd9xxx_onoff_ext_mclk(mbhc, false);
+ wcd9xxx_calibrate_hs_polling(mbhc);
+
+ /* Enable Mic Bias pull down and HPH Switch to GND */
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x01);
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x01, 0x01);
+ INIT_WORK(&mbhc->correct_plug_swch, wcd9xxx_correct_swch_plug);
+
+ if (!IS_ERR_VALUE(ret)) {
+ snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x10,
+ 0x10);
+ wcd9xxx_enable_irq(codec->control_data,
+ WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
+ wcd9xxx_enable_irq(codec->control_data,
+ WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
+
+ /* Initialize mechanical mbhc */
+ ret = wcd9xxx_setup_jack_detect_irq(mbhc);
+
+ if (!ret && mbhc->mbhc_cfg->gpio) {
+ /* Requested with IRQF_DISABLED */
+ enable_irq(mbhc->mbhc_cfg->gpio_irq);
+
+ /* Bootup time detection */
+ wcd9xxx_swch_irq_handler(mbhc);
+ } else if (!ret && mbhc->mbhc_cfg->insert_detect) {
+ pr_debug("%s: Setting up codec own insert detection\n",
+ __func__);
+ /* Setup for insertion detection */
+ wcd9xxx_insert_detect_setup(mbhc, true);
+ }
+ }
+
+ pr_debug("%s: leave\n", __func__);
+
+ return ret;
+}
+
+static void wcd9xxx_mbhc_fw_read(struct work_struct *work)
+{
+ struct delayed_work *dwork;
+ struct wcd9xxx_mbhc *mbhc;
+ struct snd_soc_codec *codec;
+ const struct firmware *fw;
+ int ret = -1, retry = 0;
+
+ dwork = to_delayed_work(work);
+ mbhc = container_of(dwork, struct wcd9xxx_mbhc, mbhc_firmware_dwork);
+ codec = mbhc->codec;
+
+ while (retry < FW_READ_ATTEMPTS) {
+ retry++;
+ pr_info("%s:Attempt %d to request MBHC firmware\n",
+ __func__, retry);
+ ret = request_firmware(&fw, "wcd9320/wcd9320_mbhc.bin",
+ codec->dev);
+
+ if (ret != 0) {
+ usleep_range(FW_READ_TIMEOUT, FW_READ_TIMEOUT);
+ } else {
+ pr_info("%s: MBHC Firmware read succesful\n", __func__);
+ break;
+ }
+ }
+
+ if (ret != 0) {
+ pr_err("%s: Cannot load MBHC firmware use default cal\n",
+ __func__);
+ } else if (wcd9xxx_mbhc_fw_validate(fw) == false) {
+ pr_err("%s: Invalid MBHC cal data size use default cal\n",
+ __func__);
+ release_firmware(fw);
+ } else {
+ mbhc->mbhc_cfg->calibration = (void *)fw->data;
+ mbhc->mbhc_fw = fw;
+ }
+
+ (void) wcd9xxx_init_and_calibrate(mbhc);
+}
+
+#ifdef CONFIG_DEBUG_FS
+ssize_t codec_mbhc_debug_read(struct file *file, char __user *buf,
+ size_t count, loff_t *pos)
+{
+ const int size = 768;
+ char buffer[size];
+ int n = 0;
+ struct wcd9xxx_mbhc *mbhc = file->private_data;
+ const struct mbhc_internal_cal_data *p = &mbhc->mbhc_data;
+ const s16 v_ins_hu_cur = wcd9xxx_get_current_v_ins(mbhc, true);
+ const s16 v_ins_h_cur = wcd9xxx_get_current_v_ins(mbhc, false);
+
+ n = scnprintf(buffer, size - n, "dce_z = %x(%dmv)\n", p->dce_z,
+ wcd9xxx_codec_sta_dce_v(mbhc, 1, p->dce_z));
+ n += scnprintf(buffer + n, size - n, "dce_mb = %x(%dmv)\n",
+ p->dce_mb, wcd9xxx_codec_sta_dce_v(mbhc, 1, p->dce_mb));
+ n += scnprintf(buffer + n, size - n, "sta_z = %x(%dmv)\n",
+ p->sta_z, wcd9xxx_codec_sta_dce_v(mbhc, 0, p->sta_z));
+ n += scnprintf(buffer + n, size - n, "sta_mb = %x(%dmv)\n",
+ p->sta_mb, wcd9xxx_codec_sta_dce_v(mbhc, 0, p->sta_mb));
+ n += scnprintf(buffer + n, size - n, "t_dce = %x\n", p->t_dce);
+ n += scnprintf(buffer + n, size - n, "t_sta = %x\n", p->t_sta);
+ n += scnprintf(buffer + n, size - n, "micb_mv = %dmv\n",
+ p->micb_mv);
+ n += scnprintf(buffer + n, size - n, "v_ins_hu = %x(%dmv)%s\n",
+ p->v_ins_hu,
+ wcd9xxx_codec_sta_dce_v(mbhc, 0, p->v_ins_hu),
+ p->v_ins_hu == v_ins_hu_cur ? "*" : "");
+ n += scnprintf(buffer + n, size - n, "v_ins_h = %x(%dmv)%s\n",
+ p->v_ins_h, wcd9xxx_codec_sta_dce_v(mbhc, 1, p->v_ins_h),
+ p->v_ins_h == v_ins_h_cur ? "*" : "");
+ n += scnprintf(buffer + n, size - n, "adj_v_ins_hu = %x(%dmv)%s\n",
+ p->adj_v_ins_hu,
+ wcd9xxx_codec_sta_dce_v(mbhc, 0, p->adj_v_ins_hu),
+ p->adj_v_ins_hu == v_ins_hu_cur ? "*" : "");
+ n += scnprintf(buffer + n, size - n, "adj_v_ins_h = %x(%dmv)%s\n",
+ p->adj_v_ins_h,
+ wcd9xxx_codec_sta_dce_v(mbhc, 1, p->adj_v_ins_h),
+ p->adj_v_ins_h == v_ins_h_cur ? "*" : "");
+ n += scnprintf(buffer + n, size - n, "v_b1_hu = %x(%dmv)\n",
+ p->v_b1_hu,
+ wcd9xxx_codec_sta_dce_v(mbhc, 0, p->v_b1_hu));
+ n += scnprintf(buffer + n, size - n, "v_b1_h = %x(%dmv)\n",
+ p->v_b1_h, wcd9xxx_codec_sta_dce_v(mbhc, 1, p->v_b1_h));
+ n += scnprintf(buffer + n, size - n, "v_b1_huc = %x(%dmv)\n",
+ p->v_b1_huc,
+ wcd9xxx_codec_sta_dce_v(mbhc, 1, p->v_b1_huc));
+ n += scnprintf(buffer + n, size - n, "v_brh = %x(%dmv)\n",
+ p->v_brh, wcd9xxx_codec_sta_dce_v(mbhc, 1, p->v_brh));
+ n += scnprintf(buffer + n, size - n, "v_brl = %x(%dmv)\n", p->v_brl,
+ wcd9xxx_codec_sta_dce_v(mbhc, 0, p->v_brl));
+ n += scnprintf(buffer + n, size - n, "v_no_mic = %x(%dmv)\n",
+ p->v_no_mic,
+ wcd9xxx_codec_sta_dce_v(mbhc, 0, p->v_no_mic));
+ n += scnprintf(buffer + n, size - n, "v_inval_ins_low = %d\n",
+ p->v_inval_ins_low);
+ n += scnprintf(buffer + n, size - n, "v_inval_ins_high = %d\n",
+ p->v_inval_ins_high);
+ n += scnprintf(buffer + n, size - n, "Insert detect insert = %d\n",
+ !wcd9xxx_swch_level_remove(mbhc));
+ buffer[n] = 0;
+
+ return simple_read_from_buffer(buf, count, pos, buffer, n);
+}
+
+static int codec_debug_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+static ssize_t codec_debug_write(struct file *filp,
+ const char __user *ubuf, size_t cnt,
+ loff_t *ppos)
+{
+ char lbuf[32];
+ char *buf;
+ int rc;
+ struct wcd9xxx_mbhc *mbhc = filp->private_data;
+
+ if (cnt > sizeof(lbuf) - 1)
+ return -EINVAL;
+
+ rc = copy_from_user(lbuf, ubuf, cnt);
+ if (rc)
+ return -EFAULT;
+
+ lbuf[cnt] = '\0';
+ buf = (char *)lbuf;
+ mbhc->no_mic_headset_override = (*strsep(&buf, " ") == '0') ?
+ false : true;
+ return rc;
+}
+
+static const struct file_operations mbhc_trrs_debug_ops = {
+ .open = codec_debug_open,
+ .write = codec_debug_write,
+};
+
+static const struct file_operations mbhc_debug_ops = {
+ .open = codec_debug_open,
+ .read = codec_mbhc_debug_read,
+};
+
+static void wcd9xxx_init_debugfs(struct wcd9xxx_mbhc *mbhc)
+{
+ mbhc->debugfs_poke =
+ debugfs_create_file("TRRS", S_IFREG | S_IRUGO, NULL, mbhc,
+ &mbhc_trrs_debug_ops);
+ mbhc->debugfs_mbhc =
+ debugfs_create_file("wcd9xxx_mbhc", S_IFREG | S_IRUGO,
+ NULL, mbhc, &mbhc_debug_ops);
+}
+
+static void wcd9xxx_cleanup_debugfs(struct wcd9xxx_mbhc *mbhc)
+{
+ debugfs_remove(mbhc->debugfs_poke);
+ debugfs_remove(mbhc->debugfs_mbhc);
+}
+#else
+static void wcd9xxx_init_debugfs(struct wcd9xxx_mbhc *mbhc)
+{
+}
+
+static void wcd9xxx_cleanup_debugfs(struct wcd9xxx_mbhc *mbhc)
+{
+}
+#endif
+
+int wcd9xxx_mbhc_start(struct wcd9xxx_mbhc *mbhc,
+ struct wcd9xxx_mbhc_config *mbhc_cfg)
+{
+ int rc = 0;
+ struct snd_soc_codec *codec = mbhc->codec;
+
+ pr_debug("%s: enter\n", __func__);
+
+ if (!codec) {
+ pr_err("%s: no codec\n", __func__);
+ return -EINVAL;
+ }
+
+ if (mbhc_cfg->mclk_rate != MCLK_RATE_12288KHZ &&
+ mbhc_cfg->mclk_rate != MCLK_RATE_9600KHZ) {
+ pr_err("Error: unsupported clock rate %d\n",
+ mbhc_cfg->mclk_rate);
+ return -EINVAL;
+ }
+
+ /* Save mbhc config */
+ mbhc->mbhc_cfg = mbhc_cfg;
+
+ /* Get HW specific mbhc registers' address */
+ wcd9xxx_get_mbhc_micbias_regs(mbhc, &mbhc->mbhc_bias_regs);
+
+ /* Put CFILT in fast mode by default */
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl,
+ 0x40, WCD9XXX_CFILT_FAST_MODE);
+
+ if (!mbhc->mbhc_cfg->read_fw_bin)
+ rc = wcd9xxx_init_and_calibrate(mbhc);
+ else
+ schedule_delayed_work(&mbhc->mbhc_firmware_dwork,
+ usecs_to_jiffies(FW_READ_TIMEOUT));
+
+ pr_debug("%s: leave %d\n", __func__, rc);
+ return rc;
+}
+EXPORT_SYMBOL_GPL(wcd9xxx_mbhc_start);
+
+static enum wcd9xxx_micbias_num
+wcd9xxx_event_to_micbias(const enum wcd9xxx_notify_event event)
+{
+ enum wcd9xxx_micbias_num ret;
+ switch (event) {
+ case WCD9XXX_EVENT_PRE_MICBIAS_1_ON:
+ ret = MBHC_MICBIAS1;
+ case WCD9XXX_EVENT_PRE_MICBIAS_2_ON:
+ ret = MBHC_MICBIAS2;
+ case WCD9XXX_EVENT_PRE_MICBIAS_3_ON:
+ ret = MBHC_MICBIAS3;
+ case WCD9XXX_EVENT_PRE_MICBIAS_4_ON:
+ ret = MBHC_MICBIAS4;
+ default:
+ ret = MBHC_MICBIAS_INVALID;
+ }
+ return ret;
+}
+
+static int wcd9xxx_event_to_cfilt(const enum wcd9xxx_notify_event event)
+{
+ int ret;
+ switch (event) {
+ case WCD9XXX_EVENT_PRE_CFILT_1_OFF:
+ case WCD9XXX_EVENT_POST_CFILT_1_OFF:
+ case WCD9XXX_EVENT_PRE_CFILT_1_ON:
+ case WCD9XXX_EVENT_POST_CFILT_1_ON:
+ ret = WCD9XXX_CFILT1_SEL;
+ break;
+ case WCD9XXX_EVENT_PRE_CFILT_2_OFF:
+ case WCD9XXX_EVENT_POST_CFILT_2_OFF:
+ case WCD9XXX_EVENT_PRE_CFILT_2_ON:
+ case WCD9XXX_EVENT_POST_CFILT_2_ON:
+ ret = WCD9XXX_CFILT2_SEL;
+ break;
+ case WCD9XXX_EVENT_PRE_CFILT_3_OFF:
+ case WCD9XXX_EVENT_POST_CFILT_3_OFF:
+ case WCD9XXX_EVENT_PRE_CFILT_3_ON:
+ case WCD9XXX_EVENT_POST_CFILT_3_ON:
+ ret = WCD9XXX_CFILT3_SEL;
+ break;
+ default:
+ ret = -1;
+ }
+ return ret;
+}
+
+static int wcd9xxx_get_mbhc_cfilt_sel(struct wcd9xxx_mbhc *mbhc)
+{
+ int cfilt;
+ const struct wcd9xxx_pdata *pdata = mbhc->resmgr->pdata;
+
+ switch (mbhc->mbhc_cfg->micbias) {
+ case MBHC_MICBIAS1:
+ cfilt = pdata->micbias.bias1_cfilt_sel;
+ break;
+ case MBHC_MICBIAS2:
+ cfilt = pdata->micbias.bias2_cfilt_sel;
+ break;
+ case MBHC_MICBIAS3:
+ cfilt = pdata->micbias.bias3_cfilt_sel;
+ break;
+ case MBHC_MICBIAS4:
+ cfilt = pdata->micbias.bias4_cfilt_sel;
+ break;
+ default:
+ cfilt = MBHC_MICBIAS_INVALID;
+ break;
+ }
+ return cfilt;
+}
+
+static int wcd9xxx_event_notify(struct notifier_block *self, unsigned long val,
+ void *data)
+{
+ int ret = 0;
+ struct wcd9xxx_mbhc *mbhc = ((struct wcd9xxx_resmgr *)data)->mbhc;
+ struct snd_soc_codec *codec = mbhc->codec;
+ enum wcd9xxx_notify_event event = (enum wcd9xxx_notify_event)val;
+
+ pr_debug("%s: enter event %s(%d)\n", __func__,
+ wcd9xxx_get_event_string(event), event);
+
+ switch (event) {
+ /* MICBIAS usage change */
+ case WCD9XXX_EVENT_PRE_MICBIAS_1_ON:
+ case WCD9XXX_EVENT_PRE_MICBIAS_2_ON:
+ case WCD9XXX_EVENT_PRE_MICBIAS_3_ON:
+ case WCD9XXX_EVENT_PRE_MICBIAS_4_ON:
+ if (mbhc->mbhc_cfg->micbias == wcd9xxx_event_to_micbias(event))
+ wcd9xxx_switch_micbias(mbhc, 0);
+ break;
+ case WCD9XXX_EVENT_POST_MICBIAS_1_ON:
+ case WCD9XXX_EVENT_POST_MICBIAS_2_ON:
+ case WCD9XXX_EVENT_POST_MICBIAS_3_ON:
+ case WCD9XXX_EVENT_POST_MICBIAS_4_ON:
+ if (mbhc->mbhc_cfg->micbias ==
+ wcd9xxx_event_to_micbias(event) &&
+ wcd9xxx_mbhc_polling(mbhc)) {
+ /* if polling is on, restart it */
+ wcd9xxx_pause_hs_polling(mbhc);
+ wcd9xxx_start_hs_polling(mbhc);
+ }
+ break;
+ case WCD9XXX_EVENT_POST_MICBIAS_1_OFF:
+ case WCD9XXX_EVENT_POST_MICBIAS_2_OFF:
+ case WCD9XXX_EVENT_POST_MICBIAS_3_OFF:
+ case WCD9XXX_EVENT_POST_MICBIAS_4_OFF:
+ if (mbhc->mbhc_cfg->micbias ==
+ wcd9xxx_event_to_micbias(event) &&
+ wcd9xxx_is_hph_pa_on(codec))
+ wcd9xxx_switch_micbias(mbhc, 1);
+ break;
+ /* PA usage change */
+ case WCD9XXX_EVENT_PRE_HPHL_PA_ON:
+ if (!(snd_soc_read(codec, mbhc->mbhc_bias_regs.ctl_reg & 0x80)))
+ /* if micbias is enabled, switch to vddio */
+ wcd9xxx_switch_micbias(mbhc, 1);
+ break;
+ case WCD9XXX_EVENT_PRE_HPHR_PA_ON:
+ /* Not used now */
+ break;
+ case WCD9XXX_EVENT_POST_HPHL_PA_OFF:
+ /* if HPH PAs are off, report OCP and switch back to CFILT */
+ clear_bit(WCD9XXX_HPHL_PA_OFF_ACK, &mbhc->hph_pa_dac_state);
+ clear_bit(WCD9XXX_HPHL_DAC_OFF_ACK, &mbhc->hph_pa_dac_state);
+ if (mbhc->hph_status & SND_JACK_OC_HPHL)
+ hphlocp_off_report(mbhc, SND_JACK_OC_HPHL);
+ wcd9xxx_switch_micbias(mbhc, 0);
+ break;
+ case WCD9XXX_EVENT_POST_HPHR_PA_OFF:
+ /* if HPH PAs are off, report OCP and switch back to CFILT */
+ clear_bit(WCD9XXX_HPHR_PA_OFF_ACK, &mbhc->hph_pa_dac_state);
+ clear_bit(WCD9XXX_HPHR_DAC_OFF_ACK, &mbhc->hph_pa_dac_state);
+ if (mbhc->hph_status & SND_JACK_OC_HPHR)
+ hphrocp_off_report(mbhc, SND_JACK_OC_HPHL);
+ wcd9xxx_switch_micbias(mbhc, 0);
+ break;
+ /* Clock usage change */
+ case WCD9XXX_EVENT_PRE_MCLK_ON:
+ break;
+ case WCD9XXX_EVENT_POST_MCLK_ON:
+ /* Change to lower TxAAF frequency */
+ snd_soc_update_bits(codec, WCD9XXX_A_TX_COM_BIAS, 1 << 4,
+ 1 << 4);
+ /* Re-calibrate clock rate dependent values */
+ wcd9xxx_update_mbhc_clk_rate(mbhc, mbhc->mbhc_cfg->mclk_rate);
+ /* If clock source changes, stop and restart polling */
+ if (wcd9xxx_mbhc_polling(mbhc)) {
+ wcd9xxx_calibrate_hs_polling(mbhc);
+ wcd9xxx_start_hs_polling(mbhc);
+ }
+ break;
+ case WCD9XXX_EVENT_PRE_MCLK_OFF:
+ /* If clock source changes, stop and restart polling */
+ if (wcd9xxx_mbhc_polling(mbhc))
+ wcd9xxx_pause_hs_polling(mbhc);
+ break;
+ case WCD9XXX_EVENT_POST_MCLK_OFF:
+ break;
+ case WCD9XXX_EVENT_PRE_RCO_ON:
+ break;
+ case WCD9XXX_EVENT_POST_RCO_ON:
+ /* Change to higher TxAAF frequency */
+ snd_soc_update_bits(codec, WCD9XXX_A_TX_COM_BIAS, 1 << 4,
+ 0 << 4);
+ /* Re-calibrate clock rate dependent values */
+ wcd9xxx_update_mbhc_clk_rate(mbhc, WCD9XXX_RCO_CLK_RATE);
+ /* If clock source changes, stop and restart polling */
+ if (wcd9xxx_mbhc_polling(mbhc)) {
+ wcd9xxx_calibrate_hs_polling(mbhc);
+ wcd9xxx_start_hs_polling(mbhc);
+ }
+ break;
+ case WCD9XXX_EVENT_PRE_RCO_OFF:
+ /* If clock source changes, stop and restart polling */
+ if (wcd9xxx_mbhc_polling(mbhc))
+ wcd9xxx_pause_hs_polling(mbhc);
+ break;
+ case WCD9XXX_EVENT_POST_RCO_OFF:
+ break;
+ /* CFILT usage change */
+ case WCD9XXX_EVENT_PRE_CFILT_1_ON:
+ case WCD9XXX_EVENT_PRE_CFILT_2_ON:
+ case WCD9XXX_EVENT_PRE_CFILT_3_ON:
+ if (wcd9xxx_get_mbhc_cfilt_sel(mbhc) ==
+ wcd9xxx_event_to_cfilt(event))
+ /*
+ * Switch CFILT to slow mode if MBHC CFILT is being
+ * used.
+ */
+ wcd9xxx_codec_switch_cfilt_mode(mbhc, false);
+ break;
+ case WCD9XXX_EVENT_POST_CFILT_1_OFF:
+ case WCD9XXX_EVENT_POST_CFILT_2_OFF:
+ case WCD9XXX_EVENT_POST_CFILT_3_OFF:
+ if (wcd9xxx_get_mbhc_cfilt_sel(mbhc) ==
+ wcd9xxx_event_to_cfilt(event))
+ /*
+ * Switch CFILT to fast mode if MBHC CFILT is not
+ * used anymore.
+ */
+ wcd9xxx_codec_switch_cfilt_mode(mbhc, true);
+ break;
+ /* System resume */
+ case WCD9XXX_EVENT_POST_RESUME:
+ mbhc->mbhc_last_resume = jiffies;
+ break;
+ /* BG mode chage */
+ case WCD9XXX_EVENT_PRE_BG_OFF:
+ case WCD9XXX_EVENT_POST_BG_OFF:
+ case WCD9XXX_EVENT_PRE_BG_AUDIO_ON:
+ case WCD9XXX_EVENT_POST_BG_AUDIO_ON:
+ case WCD9XXX_EVENT_PRE_BG_MBHC_ON:
+ case WCD9XXX_EVENT_POST_BG_MBHC_ON:
+ /* Not used for now */
+ break;
+ default:
+ WARN(1, "Unknown event %d\n", event);
+ ret = -EINVAL;
+ }
+
+ pr_debug("%s: leave\n", __func__);
+
+ return 0;
+}
+
+/*
+ * wcd9xxx_mbhc_init : initialize MBHC internal structures.
+ *
+ * NOTE: mbhc->mbhc_cfg is not YET configure so shouldn't be used
+ */
+int wcd9xxx_mbhc_init(struct wcd9xxx_mbhc *mbhc, struct wcd9xxx_resmgr *resmgr,
+ struct snd_soc_codec *codec)
+{
+ int ret;
+ void *core;
+
+ pr_debug("%s: enter\n", __func__);
+ memset(&mbhc->mbhc_bias_regs, 0, sizeof(struct mbhc_micbias_regs));
+ memset(&mbhc->mbhc_data, 0, sizeof(struct mbhc_internal_cal_data));
+
+ mbhc->mbhc_data.t_sta_dce = DEFAULT_DCE_STA_WAIT;
+ mbhc->mbhc_data.t_dce = DEFAULT_DCE_WAIT;
+ mbhc->mbhc_data.t_sta = DEFAULT_STA_WAIT;
+ mbhc->mbhc_micbias_switched = false;
+ mbhc->polling_active = false;
+ mbhc->mbhc_state = MBHC_STATE_NONE;
+ mbhc->in_swch_irq_handler = false;
+ mbhc->current_plug = PLUG_TYPE_NONE;
+ mbhc->lpi_enabled = false;
+ mbhc->no_mic_headset_override = false;
+ mbhc->mbhc_last_resume = 0;
+ mbhc->codec = codec;
+ mbhc->resmgr = resmgr;
+ mbhc->resmgr->mbhc = mbhc;
+
+ ret = snd_soc_jack_new(codec, "Headset Jack", WCD9XXX_JACK_MASK,
+ &mbhc->headset_jack);
+ if (ret) {
+ pr_err("%s: Failed to create new jack\n", __func__);
+ return ret;
+ }
+
+ ret = snd_soc_jack_new(codec, "Button Jack", WCD9XXX_JACK_BUTTON_MASK,
+ &mbhc->button_jack);
+ if (ret) {
+ pr_err("Failed to create new jack\n");
+ return ret;
+ }
+
+ mbhc->mbhc_cfg = kzalloc(sizeof(*mbhc->mbhc_cfg), GFP_KERNEL);
+ if (!mbhc->mbhc_cfg)
+ return -ENOMEM;
+
+ INIT_DELAYED_WORK(&mbhc->mbhc_firmware_dwork, wcd9xxx_mbhc_fw_read);
+ INIT_DELAYED_WORK(&mbhc->mbhc_btn_dwork, wcd9xxx_btn_lpress_fn);
+ INIT_DELAYED_WORK(&mbhc->mbhc_insert_dwork, wcd9xxx_mbhc_insert_work);
+
+ /* Register event notifier */
+ mbhc->nblock.notifier_call = wcd9xxx_event_notify;
+ ret = wcd9xxx_resmgr_register_notifier(mbhc->resmgr, &mbhc->nblock);
+ if (ret) {
+ pr_err("%s: Failed to register notifier %d\n", __func__, ret);
+ return ret;
+ }
+
+ wcd9xxx_init_debugfs(mbhc);
+
+ core = mbhc->resmgr->core;
+ ret = wcd9xxx_request_irq(core, WCD9XXX_IRQ_MBHC_INSERTION,
+ wcd9xxx_hs_insert_irq,
+ "Headset insert detect", mbhc);
+ if (ret) {
+ pr_err("%s: Failed to request irq %d\n", __func__,
+ WCD9XXX_IRQ_MBHC_INSERTION);
+ goto err_insert_irq;
+ }
+ wcd9xxx_disable_irq(core, WCD9XXX_IRQ_MBHC_INSERTION);
+
+ ret = wcd9xxx_request_irq(core, WCD9XXX_IRQ_MBHC_REMOVAL,
+ wcd9xxx_hs_remove_irq,
+ "Headset remove detect", mbhc);
+ if (ret) {
+ pr_err("%s: Failed to request irq %d\n", __func__,
+ WCD9XXX_IRQ_MBHC_REMOVAL);
+ goto err_remove_irq;
+ }
+
+ ret = wcd9xxx_request_irq(core, WCD9XXX_IRQ_MBHC_POTENTIAL,
+ wcd9xxx_dce_handler, "DC Estimation detect",
+ mbhc);
+ if (ret) {
+ pr_err("%s: Failed to request irq %d\n", __func__,
+ WCD9XXX_IRQ_MBHC_POTENTIAL);
+ goto err_potential_irq;
+ }
+
+ ret = wcd9xxx_request_irq(core, WCD9XXX_IRQ_MBHC_RELEASE,
+ wcd9xxx_release_handler,
+ "Button Release detect", mbhc);
+ if (ret) {
+ pr_err("%s: Failed to request irq %d\n", __func__,
+ WCD9XXX_IRQ_MBHC_RELEASE);
+ goto err_release_irq;
+ }
+
+ ret = wcd9xxx_request_irq(core, WCD9XXX_IRQ_HPH_PA_OCPL_FAULT,
+ wcd9xxx_hphl_ocp_irq, "HPH_L OCP detect",
+ mbhc);
+ if (ret) {
+ pr_err("%s: Failed to request irq %d\n", __func__,
+ WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
+ goto err_hphl_ocp_irq;
+ }
+ wcd9xxx_disable_irq(core, WCD9XXX_IRQ_HPH_PA_OCPL_FAULT);
+
+ ret = wcd9xxx_request_irq(core, WCD9XXX_IRQ_HPH_PA_OCPR_FAULT,
+ wcd9xxx_hphr_ocp_irq, "HPH_R OCP detect",
+ mbhc);
+ if (ret) {
+ pr_err("%s: Failed to request irq %d\n", __func__,
+ WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
+ goto err_hphr_ocp_irq;
+ }
+ wcd9xxx_disable_irq(codec->control_data, WCD9XXX_IRQ_HPH_PA_OCPR_FAULT);
+
+ pr_debug("%s: leave ret %d\n", __func__, ret);
+ return ret;
+
+err_hphr_ocp_irq:
+ wcd9xxx_free_irq(core, WCD9XXX_IRQ_HPH_PA_OCPL_FAULT, mbhc);
+err_hphl_ocp_irq:
+ wcd9xxx_free_irq(core, WCD9XXX_IRQ_MBHC_RELEASE, mbhc);
+err_release_irq:
+ wcd9xxx_free_irq(core, WCD9XXX_IRQ_MBHC_POTENTIAL, mbhc);
+err_potential_irq:
+ wcd9xxx_free_irq(core, WCD9XXX_IRQ_MBHC_REMOVAL, mbhc);
+err_remove_irq:
+ wcd9xxx_free_irq(core, WCD9XXX_IRQ_MBHC_INSERTION, mbhc);
+err_insert_irq:
+ wcd9xxx_resmgr_unregister_notifier(mbhc->resmgr, &mbhc->nblock);
+
+ pr_debug("%s: leave ret %d\n", __func__, ret);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(wcd9xxx_mbhc_init);
+
+void wcd9xxx_mbhc_deinit(struct wcd9xxx_mbhc *mbhc)
+{
+ void *cdata = mbhc->codec->control_data;
+
+ wcd9xxx_free_irq(cdata, WCD9XXX_IRQ_SLIMBUS, mbhc);
+ wcd9xxx_free_irq(cdata, WCD9XXX_IRQ_MBHC_RELEASE, mbhc);
+ wcd9xxx_free_irq(cdata, WCD9XXX_IRQ_MBHC_POTENTIAL, mbhc);
+ wcd9xxx_free_irq(cdata, WCD9XXX_IRQ_MBHC_REMOVAL, mbhc);
+ wcd9xxx_free_irq(cdata, WCD9XXX_IRQ_MBHC_INSERTION, mbhc);
+
+ if (mbhc->mbhc_fw)
+ release_firmware(mbhc->mbhc_fw);
+
+ wcd9xxx_resmgr_unregister_notifier(mbhc->resmgr, &mbhc->nblock);
+
+ wcd9xxx_cleanup_debugfs(mbhc);
+
+ kfree(mbhc->mbhc_cfg);
+}
+EXPORT_SYMBOL_GPL(wcd9xxx_mbhc_deinit);
+
+MODULE_DESCRIPTION("wcd9xxx MBHC module");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/wcd9xxx-mbhc.h b/sound/soc/codecs/wcd9xxx-mbhc.h
new file mode 100644
index 0000000..fb1dfdc
--- /dev/null
+++ b/sound/soc/codecs/wcd9xxx-mbhc.h
@@ -0,0 +1,315 @@
+/* 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 __WCD9XXX_MBHC_H__
+#define __WCD9XXX_MBHC_H__
+
+#include "wcd9xxx-resmgr.h"
+
+#define WCD9XXX_CFILT_FAST_MODE 0x00
+#define WCD9XXX_CFILT_SLOW_MODE 0x40
+
+struct mbhc_micbias_regs {
+ u16 cfilt_val;
+ u16 cfilt_ctl;
+ u16 mbhc_reg;
+ u16 int_rbias;
+ u16 ctl_reg;
+ u8 cfilt_sel;
+};
+
+/* Data used by MBHC */
+struct mbhc_internal_cal_data {
+ u16 dce_z;
+ u16 dce_mb;
+ u16 sta_z;
+ u16 sta_mb;
+ u32 t_sta_dce;
+ u32 t_dce;
+ u32 t_sta;
+ u32 micb_mv;
+ u16 v_ins_hu;
+ u16 v_ins_h;
+ u16 v_b1_hu;
+ u16 v_b1_h;
+ u16 v_b1_huc;
+ u16 v_brh;
+ u16 v_brl;
+ u16 v_no_mic;
+ s16 adj_v_hs_max;
+ u16 adj_v_ins_hu;
+ u16 adj_v_ins_h;
+ s16 v_inval_ins_low;
+ s16 v_inval_ins_high;
+};
+
+enum wcd9xxx_mbhc_plug_type {
+ PLUG_TYPE_INVALID = -1,
+ PLUG_TYPE_NONE,
+ PLUG_TYPE_HEADSET,
+ PLUG_TYPE_HEADPHONE,
+ PLUG_TYPE_HIGH_HPH,
+ PLUG_TYPE_GND_MIC_SWAP,
+};
+
+enum wcd9xxx_micbias_num {
+ MBHC_MICBIAS_INVALID = -1,
+ MBHC_MICBIAS1,
+ MBHC_MICBIAS2,
+ MBHC_MICBIAS3,
+ MBHC_MICBIAS4,
+};
+
+enum wcd9xxx_mbhc_state {
+ MBHC_STATE_NONE = -1,
+ MBHC_STATE_POTENTIAL,
+ MBHC_STATE_POTENTIAL_RECOVERY,
+ MBHC_STATE_RELEASE,
+};
+
+enum wcd9xxx_mbhc_btn_det_mem {
+ MBHC_BTN_DET_V_BTN_LOW,
+ MBHC_BTN_DET_V_BTN_HIGH,
+ MBHC_BTN_DET_N_READY,
+ MBHC_BTN_DET_N_CIC,
+ MBHC_BTN_DET_GAIN
+};
+
+enum wcd9xxx_mbhc_clk_freq {
+ TAIKO_MCLK_12P2MHZ = 0,
+ TAIKO_MCLK_9P6MHZ,
+ TAIKO_NUM_CLK_FREQS,
+};
+
+struct wcd9xxx_mbhc_general_cfg {
+ u8 t_ldoh;
+ u8 t_bg_fast_settle;
+ u8 t_shutdown_plug_rem;
+ u8 mbhc_nsa;
+ u8 mbhc_navg;
+ u8 v_micbias_l;
+ u8 v_micbias;
+ u8 mbhc_reserved;
+ u16 settle_wait;
+ u16 t_micbias_rampup;
+ u16 t_micbias_rampdown;
+ u16 t_supply_bringup;
+} __packed;
+
+struct wcd9xxx_mbhc_plug_detect_cfg {
+ u32 mic_current;
+ u32 hph_current;
+ u16 t_mic_pid;
+ u16 t_ins_complete;
+ u16 t_ins_retry;
+ u16 v_removal_delta;
+ u8 micbias_slow_ramp;
+ u8 reserved0;
+ u8 reserved1;
+ u8 reserved2;
+} __packed;
+
+struct wcd9xxx_mbhc_plug_type_cfg {
+ u8 av_detect;
+ u8 mono_detect;
+ u8 num_ins_tries;
+ u8 reserved0;
+ s16 v_no_mic;
+ s16 v_av_min;
+ s16 v_av_max;
+ s16 v_hs_min;
+ s16 v_hs_max;
+ u16 reserved1;
+} __packed;
+
+struct wcd9xxx_mbhc_btn_detect_cfg {
+ s8 c[8];
+ u8 nc;
+ u8 n_meas;
+ u8 mbhc_nsc;
+ u8 n_btn_meas;
+ u8 n_btn_con;
+ u8 num_btn;
+ u8 reserved0;
+ u8 reserved1;
+ u16 t_poll;
+ u16 t_bounce_wait;
+ u16 t_rel_timeout;
+ s16 v_btn_press_delta_sta;
+ s16 v_btn_press_delta_cic;
+ u16 t_btn0_timeout;
+ s16 _v_btn_low[0]; /* v_btn_low[num_btn] */
+ s16 _v_btn_high[0]; /* v_btn_high[num_btn] */
+ u8 _n_ready[TAIKO_NUM_CLK_FREQS];
+ u8 _n_cic[TAIKO_NUM_CLK_FREQS];
+ u8 _gain[TAIKO_NUM_CLK_FREQS];
+} __packed;
+
+struct wcd9xxx_mbhc_imped_detect_cfg {
+ u8 _hs_imped_detect;
+ u8 _n_rload;
+ u8 _hph_keep_on;
+ u8 _repeat_rload_calc;
+ u16 _t_dac_ramp_time;
+ u16 _rhph_high;
+ u16 _rhph_low;
+ u16 _rload[0]; /* rload[n_rload] */
+ u16 _alpha[0]; /* alpha[n_rload] */
+ u16 _beta[3];
+} __packed;
+
+struct wcd9xxx_mbhc_config {
+ bool read_fw_bin;
+ /*
+ * void* calibration contains:
+ * struct wcd9xxx_mbhc_general_cfg generic;
+ * struct wcd9xxx_mbhc_plug_detect_cfg plug_det;
+ * struct wcd9xxx_mbhc_plug_type_cfg plug_type;
+ * struct wcd9xxx_mbhc_btn_detect_cfg btn_det;
+ * struct wcd9xxx_mbhc_imped_detect_cfg imped_det;
+ * Note: various size depends on btn_det->num_btn
+ */
+ void *calibration;
+ enum wcd9xxx_micbias_num micbias;
+ int (*mclk_cb_fn) (struct snd_soc_codec*, int, bool);
+ unsigned int mclk_rate;
+ unsigned int gpio;
+ unsigned int gpio_irq;
+ int gpio_level_insert;
+ bool insert_detect; /* codec has own MBHC_INSERT_DETECT */
+ bool detect_extn_cable;
+ /* swap_gnd_mic returns true if extern GND/MIC swap switch toggled */
+ bool (*swap_gnd_mic) (struct snd_soc_codec *);
+};
+
+struct wcd9xxx_mbhc {
+ bool polling_active;
+ /* Delayed work to report long button press */
+ struct delayed_work mbhc_btn_dwork;
+ int buttons_pressed;
+ enum wcd9xxx_mbhc_state mbhc_state;
+ struct wcd9xxx_mbhc_config *mbhc_cfg;
+
+ struct mbhc_internal_cal_data mbhc_data;
+
+ struct mbhc_micbias_regs mbhc_bias_regs;
+ bool mbhc_micbias_switched;
+
+ u32 hph_status; /* track headhpone status */
+ u8 hphlocp_cnt; /* headphone left ocp retry */
+ u8 hphrocp_cnt; /* headphone right ocp retry */
+
+ /* Work to perform MBHC Firmware Read */
+ struct delayed_work mbhc_firmware_dwork;
+ const struct firmware *mbhc_fw;
+
+ struct delayed_work mbhc_insert_dwork;
+
+ u8 current_plug;
+ struct work_struct correct_plug_swch;
+ /*
+ * Work to perform polling on microphone voltage
+ * in order to correct plug type once plug type
+ * is detected as headphone
+ */
+ struct work_struct correct_plug_noswch;
+ bool hs_detect_work_stop;
+
+ bool lpi_enabled; /* low power insertion detection */
+ bool in_swch_irq_handler;
+
+ struct wcd9xxx_resmgr *resmgr;
+ struct snd_soc_codec *codec;
+
+ bool no_mic_headset_override;
+
+ /* track PA/DAC state */
+ unsigned long hph_pa_dac_state;
+
+ unsigned long mbhc_last_resume; /* in jiffies */
+
+ bool insert_detect_level_insert;
+
+ struct snd_soc_jack headset_jack;
+ struct snd_soc_jack button_jack;
+
+ struct notifier_block nblock;
+
+#ifdef CONFIG_DEBUG_FS
+ struct dentry *debugfs_poke;
+ struct dentry *debugfs_mbhc;
+#endif
+};
+
+#define WCD9XXX_MBHC_CAL_SIZE(buttons, rload) ( \
+ sizeof(enum wcd9xxx_micbias_num) + \
+ sizeof(struct wcd9xxx_mbhc_general_cfg) + \
+ sizeof(struct wcd9xxx_mbhc_plug_detect_cfg) + \
+ ((sizeof(s16) + sizeof(s16)) * buttons) + \
+ sizeof(struct wcd9xxx_mbhc_plug_type_cfg) + \
+ sizeof(struct wcd9xxx_mbhc_btn_detect_cfg) + \
+ sizeof(struct wcd9xxx_mbhc_imped_detect_cfg) + \
+ ((sizeof(u16) + sizeof(u16)) * rload) \
+ )
+
+#define WCD9XXX_MBHC_CAL_GENERAL_PTR(cali) ( \
+ (struct wcd9xxx_mbhc_general_cfg *) cali)
+#define WCD9XXX_MBHC_CAL_PLUG_DET_PTR(cali) ( \
+ (struct wcd9xxx_mbhc_plug_detect_cfg *) \
+ &(WCD9XXX_MBHC_CAL_GENERAL_PTR(cali)[1]))
+#define WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(cali) ( \
+ (struct wcd9xxx_mbhc_plug_type_cfg *) \
+ &(WCD9XXX_MBHC_CAL_PLUG_DET_PTR(cali)[1]))
+#define WCD9XXX_MBHC_CAL_BTN_DET_PTR(cali) ( \
+ (struct wcd9xxx_mbhc_btn_detect_cfg *) \
+ &(WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(cali)[1]))
+#define WCD9XXX_MBHC_CAL_IMPED_DET_PTR(cali) ( \
+ (struct wcd9xxx_mbhc_imped_detect_cfg *) \
+ (((void *)&WCD9XXX_MBHC_CAL_BTN_DET_PTR(cali)[1]) + \
+ (WCD9XXX_MBHC_CAL_BTN_DET_PTR(cali)->num_btn * \
+ (sizeof(WCD9XXX_MBHC_CAL_BTN_DET_PTR(cali)->_v_btn_low[0]) + \
+ sizeof(WCD9XXX_MBHC_CAL_BTN_DET_PTR(cali)->_v_btn_high[0])))) \
+ )
+
+/* minimum size of calibration data assuming there is only one button and
+ * one rload.
+ */
+#define WCD9XXX_MBHC_CAL_MIN_SIZE ( \
+ sizeof(struct wcd9xxx_mbhc_general_cfg) + \
+ sizeof(struct wcd9xxx_mbhc_plug_detect_cfg) + \
+ sizeof(struct wcd9xxx_mbhc_plug_type_cfg) + \
+ sizeof(struct wcd9xxx_mbhc_btn_detect_cfg) + \
+ sizeof(struct wcd9xxx_mbhc_imped_detect_cfg) + \
+ (sizeof(u16) * 2) \
+ )
+
+#define WCD9XXX_MBHC_CAL_BTN_SZ(cfg_ptr) ( \
+ sizeof(struct wcd9xxx_mbhc_btn_detect_cfg) + \
+ (cfg_ptr->num_btn * (sizeof(cfg_ptr->_v_btn_low[0]) + \
+ sizeof(cfg_ptr->_v_btn_high[0]))))
+
+#define WCD9XXX_MBHC_CAL_IMPED_MIN_SZ ( \
+ sizeof(struct wcd9xxx_mbhc_imped_detect_cfg) + sizeof(u16) * 2)
+
+#define WCD9XXX_MBHC_CAL_IMPED_SZ(cfg_ptr) ( \
+ sizeof(struct wcd9xxx_mbhc_imped_detect_cfg) + \
+ (cfg_ptr->_n_rload * \
+ (sizeof(cfg_ptr->_rload[0]) + sizeof(cfg_ptr->_alpha[0]))))
+
+int wcd9xxx_mbhc_start(struct wcd9xxx_mbhc *mbhc,
+ struct wcd9xxx_mbhc_config *mbhc_cfg);
+int wcd9xxx_mbhc_init(struct wcd9xxx_mbhc *mbhc, struct wcd9xxx_resmgr *resmgr,
+ struct snd_soc_codec *codec);
+void wcd9xxx_mbhc_deinit(struct wcd9xxx_mbhc *mbhc);
+void *wcd9xxx_mbhc_cal_btn_det_mp(
+ const struct wcd9xxx_mbhc_btn_detect_cfg *btn_det,
+ const enum wcd9xxx_mbhc_btn_det_mem mem);
+#endif /* __WCD9XXX_MBHC_H__ */
diff --git a/sound/soc/codecs/wcd9xxx-resmgr.c b/sound/soc/codecs/wcd9xxx-resmgr.c
new file mode 100644
index 0000000..5dfa41c
--- /dev/null
+++ b/sound/soc/codecs/wcd9xxx-resmgr.c
@@ -0,0 +1,654 @@
+/* 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/module.h>
+#include <linux/init.h>
+#include <linux/firmware.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/device.h>
+#include <linux/printk.h>
+#include <linux/ratelimit.h>
+#include <linux/debugfs.h>
+#include <linux/mfd/wcd9xxx/core.h>
+#include <linux/mfd/wcd9xxx/wcd9xxx_registers.h>
+#include <linux/mfd/wcd9xxx/wcd9320_registers.h>
+#include <linux/mfd/wcd9xxx/pdata.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/pm_runtime.h>
+#include <linux/kernel.h>
+#include <linux/gpio.h>
+#include "wcd9xxx-resmgr.h"
+
+static char wcd9xxx_event_string[][64] = {
+ "WCD9XXX_EVENT_INVALID",
+
+ "WCD9XXX_EVENT_PRE_RCO_ON",
+ "WCD9XXX_EVENT_POST_RCO_ON",
+ "WCD9XXX_EVENT_PRE_RCO_OFF",
+ "WCD9XXX_EVENT_POST_RCO_OFF",
+
+ "WCD9XXX_EVENT_PRE_MCLK_ON",
+ "WCD9XXX_EVENT_POST_MCLK_ON",
+ "WCD9XXX_EVENT_PRE_MCLK_OFF",
+ "WCD9XXX_EVENT_POST_MCLK_OFF",
+
+ "WCD9XXX_EVENT_PRE_BG_OFF",
+ "WCD9XXX_EVENT_POST_BG_OFF",
+ "WCD9XXX_EVENT_PRE_BG_AUDIO_ON",
+ "WCD9XXX_EVENT_POST_BG_AUDIO_ON",
+ "WCD9XXX_EVENT_PRE_BG_MBHC_ON",
+ "WCD9XXX_EVENT_POST_BG_MBHC_ON",
+
+ "WCD9XXX_EVENT_PRE_MICBIAS_1_OFF",
+ "WCD9XXX_EVENT_POST_MICBIAS_1_OFF",
+ "WCD9XXX_EVENT_PRE_MICBIAS_2_OFF",
+ "WCD9XXX_EVENT_POST_MICBIAS_2_OFF",
+ "WCD9XXX_EVENT_PRE_MICBIAS_3_OFF",
+ "WCD9XXX_EVENT_POST_MICBIAS_3_OFF",
+ "WCD9XXX_EVENT_PRE_MICBIAS_4_OFF",
+ "WCD9XXX_EVENT_POST_MICBIAS_4_OFF",
+ "WCD9XXX_EVENT_PRE_MICBIAS_1_ON",
+ "WCD9XXX_EVENT_POST_MICBIAS_1_ON",
+ "WCD9XXX_EVENT_PRE_MICBIAS_2_ON",
+ "WCD9XXX_EVENT_POST_MICBIAS_2_ON",
+ "WCD9XXX_EVENT_PRE_MICBIAS_3_ON",
+ "WCD9XXX_EVENT_POST_MICBIAS_3_ON",
+ "WCD9XXX_EVENT_PRE_MICBIAS_4_ON",
+ "WCD9XXX_EVENT_POST_MICBIAS_4_ON",
+
+ "WCD9XXX_EVENT_PRE_CFILT_1_OFF",
+ "WCD9XXX_EVENT_POST_CFILT_1_OFF",
+ "WCD9XXX_EVENT_PRE_CFILT_2_OFF",
+ "WCD9XXX_EVENT_POST_CFILT_2_OFF",
+ "WCD9XXX_EVENT_PRE_CFILT_3_OFF",
+ "WCD9XXX_EVENT_POST_CFILT_3_OFF",
+ "WCD9XXX_EVENT_PRE_CFILT_1_ON",
+ "WCD9XXX_EVENT_POST_CFILT_1_ON",
+ "WCD9XXX_EVENT_PRE_CFILT_2_ON",
+ "WCD9XXX_EVENT_POST_CFILT_2_ON",
+ "WCD9XXX_EVENT_PRE_CFILT_3_ON",
+ "WCD9XXX_EVENT_POST_CFILT_3_ON",
+
+ "WCD9XXX_EVENT_PRE_HPHL_PA_ON",
+ "WCD9XXX_EVENT_POST_HPHL_PA_OFF",
+ "WCD9XXX_EVENT_PRE_HPHR_PA_ON",
+ "WCD9XXX_EVENT_POST_HPHR_PA_OFF",
+
+ "WCD9XXX_EVENT_POST_RESUME",
+
+ "WCD9XXX_EVENT_LAST",
+};
+
+static enum wcd9xxx_clock_type wcd9xxx_save_clock(struct wcd9xxx_resmgr
+ *resmgr);
+static void wcd9xxx_restore_clock(struct wcd9xxx_resmgr *resmgr,
+ enum wcd9xxx_clock_type type);
+
+const char *wcd9xxx_get_event_string(enum wcd9xxx_notify_event type)
+{
+ return wcd9xxx_event_string[type];
+}
+
+void wcd9xxx_resmgr_notifier_call(struct wcd9xxx_resmgr *resmgr,
+ const enum wcd9xxx_notify_event e)
+{
+ pr_debug("%s: notifier call event %d\n", __func__, e);
+ blocking_notifier_call_chain(&resmgr->notifier, e, resmgr);
+}
+
+static void wcd9xxx_codec_disable_bg(struct wcd9xxx_resmgr *resmgr)
+{
+ /* Notify bg mode change */
+ wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_PRE_BG_OFF);
+ /* Disable bg */
+ snd_soc_write(resmgr->codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x00);
+ usleep_range(100, 100);
+ /* Notify bg mode change */
+ wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_POST_BG_OFF);
+}
+
+static void wcd9xxx_codec_enable_bg_audio(struct wcd9xxx_resmgr *resmgr)
+{
+ struct snd_soc_codec *codec = resmgr->codec;
+
+ /* Notify bandgap mode change */
+ wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_PRE_BG_AUDIO_ON);
+ /* Enable bg */
+ snd_soc_update_bits(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x80, 0x80);
+ snd_soc_update_bits(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x04, 0x04);
+ snd_soc_update_bits(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x01, 0x01);
+ usleep_range(1000, 1000);
+ snd_soc_update_bits(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x80, 0x00);
+ /* Notify bandgap mode change */
+ wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_POST_BG_AUDIO_ON);
+}
+
+static void wcd9xxx_enable_bg_mbhc(struct wcd9xxx_resmgr *resmgr)
+{
+ struct snd_soc_codec *codec = resmgr->codec;
+
+ /* Notify bandgap mode change */
+ wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_PRE_BG_MBHC_ON);
+
+ /*
+ * bandgap mode becomes fast,
+ * mclk should be off or clk buff source souldn't be VBG
+ * Let's turn off mclk always
+ */
+ WARN_ON(snd_soc_read(codec, WCD9XXX_A_CLK_BUFF_EN2) & (1 << 2));
+ snd_soc_update_bits(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x2, 0x2);
+ snd_soc_update_bits(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x80, 0x80);
+ snd_soc_update_bits(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x4, 0x4);
+ snd_soc_update_bits(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x01, 0x01);
+ usleep_range(1000, 1000);
+ snd_soc_update_bits(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x80, 0x00);
+
+ /* Notify bandgap mode change */
+ wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_POST_BG_MBHC_ON);
+}
+
+static void wcd9xxx_disable_bg(struct wcd9xxx_resmgr *resmgr)
+{
+ struct snd_soc_codec *codec = resmgr->codec;
+ snd_soc_write(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x00);
+}
+
+static void wcd9xxx_disable_clock_block(struct wcd9xxx_resmgr *resmgr)
+{
+ struct snd_soc_codec *codec = resmgr->codec;
+
+ pr_debug("%s: enter\n", __func__);
+ WCD9XXX_BCL_ASSERT_LOCKED(resmgr);
+
+ /* Notify */
+ if (resmgr->clk_type == WCD9XXX_CLK_RCO)
+ wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_PRE_RCO_OFF);
+ else
+ wcd9xxx_resmgr_notifier_call(resmgr,
+ WCD9XXX_EVENT_PRE_MCLK_OFF);
+ /* Disable clock */
+ snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN2, 0x04, 0x00);
+ usleep_range(50, 50);
+ snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN2, 0x02, 0x02);
+ snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1, 0x05, 0x00);
+ usleep_range(50, 50);
+ /* Notify */
+ if (resmgr->clk_type == WCD9XXX_CLK_RCO)
+ wcd9xxx_resmgr_notifier_call(resmgr,
+ WCD9XXX_EVENT_POST_RCO_OFF);
+ else
+ wcd9xxx_resmgr_notifier_call(resmgr,
+ WCD9XXX_EVENT_POST_MCLK_OFF);
+ pr_debug("%s: leave\n", __func__);
+}
+
+/*
+ * wcd9xxx_resmgr_get_bandgap : Vote for bandgap ref
+ * choice : WCD9XXX_BANDGAP_AUDIO_MODE, WCD9XXX_BANDGAP_MBHC_MODE
+ */
+void wcd9xxx_resmgr_get_bandgap(struct wcd9xxx_resmgr *resmgr,
+ const enum wcd9xxx_bandgap_type choice)
+{
+ enum wcd9xxx_clock_type clock_save;
+
+ pr_debug("%s: enter, wants %d\n", __func__, choice);
+
+ WCD9XXX_BCL_ASSERT_LOCKED(resmgr);
+ switch (choice) {
+ case WCD9XXX_BANDGAP_AUDIO_MODE:
+ resmgr->bg_audio_users++;
+ if (resmgr->bg_audio_users == 1 && resmgr->bg_mbhc_users) {
+ /*
+ * Current bg is MBHC mode, about to switch to
+ * audio mode.
+ */
+ WARN_ON(resmgr->bandgap_type !=
+ WCD9XXX_BANDGAP_MBHC_MODE);
+
+ /* BG mode can be changed only with clock off */
+ clock_save = wcd9xxx_save_clock(resmgr);
+ /* Swtich BG mode */
+ wcd9xxx_codec_disable_bg(resmgr);
+ wcd9xxx_codec_enable_bg_audio(resmgr);
+ /* restore clock */
+ wcd9xxx_restore_clock(resmgr, clock_save);
+ } else if (resmgr->bg_audio_users == 1) {
+ /* currently off, just enable it */
+ WARN_ON(resmgr->bandgap_type != WCD9XXX_BANDGAP_OFF);
+ wcd9xxx_codec_enable_bg_audio(resmgr);
+ }
+ resmgr->bandgap_type = WCD9XXX_BANDGAP_AUDIO_MODE;
+ break;
+ case WCD9XXX_BANDGAP_MBHC_MODE:
+ resmgr->bg_mbhc_users++;
+ if (resmgr->bandgap_type == WCD9XXX_BANDGAP_MBHC_MODE ||
+ resmgr->bandgap_type == WCD9XXX_BANDGAP_AUDIO_MODE)
+ /* do nothing */
+ break;
+
+ /* bg mode can be changed only with clock off */
+ clock_save = wcd9xxx_save_clock(resmgr);
+ /* enable bg with MBHC mode */
+ wcd9xxx_enable_bg_mbhc(resmgr);
+ /* restore clock */
+ wcd9xxx_restore_clock(resmgr, clock_save);
+ /* save current mode */
+ resmgr->bandgap_type = WCD9XXX_BANDGAP_MBHC_MODE;
+ break;
+ default:
+ pr_err("%s: Error, Invalid bandgap settings\n", __func__);
+ break;
+ }
+
+ pr_debug("%s: bg users audio %d, mbhc %d\n", __func__,
+ resmgr->bg_audio_users, resmgr->bg_mbhc_users);
+}
+
+/*
+ * wcd9xxx_resmgr_put_bandgap : Unvote bandgap ref that has been voted
+ * choice : WCD9XXX_BANDGAP_AUDIO_MODE, WCD9XXX_BANDGAP_MBHC_MODE
+ */
+void wcd9xxx_resmgr_put_bandgap(struct wcd9xxx_resmgr *resmgr,
+ enum wcd9xxx_bandgap_type choice)
+{
+ enum wcd9xxx_clock_type clock_save;
+
+ pr_debug("%s: enter choice %d\n", __func__, choice);
+
+ WCD9XXX_BCL_ASSERT_LOCKED(resmgr);
+ switch (choice) {
+ case WCD9XXX_BANDGAP_AUDIO_MODE:
+ if (--resmgr->bg_audio_users == 0) {
+ if (resmgr->bg_mbhc_users) {
+ /* bg mode can be changed only with clock off */
+ clock_save = wcd9xxx_save_clock(resmgr);
+ /* switch to MBHC mode */
+ wcd9xxx_enable_bg_mbhc(resmgr);
+ /* restore clock */
+ wcd9xxx_restore_clock(resmgr, clock_save);
+ resmgr->bandgap_type =
+ WCD9XXX_BANDGAP_MBHC_MODE;
+ } else {
+ /* turn off */
+ wcd9xxx_disable_bg(resmgr);
+ resmgr->bandgap_type = WCD9XXX_BANDGAP_OFF;
+ }
+ }
+ break;
+ case WCD9XXX_BANDGAP_MBHC_MODE:
+ WARN(resmgr->bandgap_type == WCD9XXX_BANDGAP_OFF,
+ "Unexpected bandgap type %d\n", resmgr->bandgap_type);
+ if (--resmgr->bg_mbhc_users == 0 &&
+ resmgr->bandgap_type == WCD9XXX_BANDGAP_MBHC_MODE) {
+ wcd9xxx_disable_bg(resmgr);
+ resmgr->bandgap_type = WCD9XXX_BANDGAP_OFF;
+ }
+ break;
+ default:
+ pr_err("%s: Error, Invalid bandgap settings\n", __func__);
+ break;
+ }
+
+ pr_debug("%s: bg users audio %d, mbhc %d\n", __func__,
+ resmgr->bg_audio_users, resmgr->bg_mbhc_users);
+}
+
+void wcd9xxx_resmgr_enable_rx_bias(struct wcd9xxx_resmgr *resmgr, u32 enable)
+{
+ struct snd_soc_codec *codec = resmgr->codec;
+
+ if (enable) {
+ resmgr->rx_bias_count++;
+ if (resmgr->rx_bias_count == 1)
+ snd_soc_update_bits(codec, WCD9XXX_A_RX_COM_BIAS,
+ 0x80, 0x80);
+ } else {
+ resmgr->rx_bias_count--;
+ if (!resmgr->rx_bias_count)
+ snd_soc_update_bits(codec, WCD9XXX_A_RX_COM_BIAS,
+ 0x80, 0x00);
+ }
+}
+
+int wcd9xxx_resmgr_enable_config_mode(struct snd_soc_codec *codec, int enable)
+{
+ pr_debug("%s: enable = %d\n", __func__, enable);
+ if (enable) {
+ snd_soc_update_bits(codec, WCD9XXX_A_RC_OSC_FREQ, 0x10, 0);
+ /* bandgap mode to fast */
+ snd_soc_write(codec, WCD9XXX_A_BIAS_OSC_BG_CTL, 0x17);
+ usleep_range(5, 5);
+ snd_soc_update_bits(codec, WCD9XXX_A_RC_OSC_FREQ, 0x80, 0x80);
+ snd_soc_update_bits(codec, WCD9XXX_A_RC_OSC_TEST, 0x80, 0x80);
+ usleep_range(10, 10);
+ snd_soc_update_bits(codec, WCD9XXX_A_RC_OSC_TEST, 0x80, 0);
+ usleep_range(10000, 10000);
+ snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1, 0x08, 0x08);
+ } else {
+ snd_soc_update_bits(codec, WCD9XXX_A_BIAS_OSC_BG_CTL, 0x1, 0);
+ snd_soc_update_bits(codec, WCD9XXX_A_RC_OSC_FREQ, 0x80, 0);
+ /* clk source to ext clk and clk buff ref to VBG */
+ snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1, 0x0C, 0x04);
+ }
+
+ return 0;
+}
+
+static void wcd9xxx_enable_clock_block(struct wcd9xxx_resmgr *resmgr,
+ int config_mode)
+{
+ struct snd_soc_codec *codec = resmgr->codec;
+
+ pr_debug("%s: config_mode = %d\n", __func__, config_mode);
+ /* transit to RCO requires mclk off */
+ WARN_ON(snd_soc_read(codec, WCD9XXX_A_CLK_BUFF_EN2) & (1 << 2));
+ if (config_mode) {
+ /* Notify */
+ wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_PRE_RCO_ON);
+ /* enable RCO and switch to it */
+ wcd9xxx_resmgr_enable_config_mode(codec, 1);
+ snd_soc_write(codec, WCD9XXX_A_CLK_BUFF_EN2, 0x02);
+ usleep_range(1000, 1000);
+ } else {
+ /* Notify */
+ wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_PRE_MCLK_ON);
+ /* switch to MCLK */
+ snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1, 0x08, 0x00);
+ /* if RCO is enabled, switch from it */
+ if (snd_soc_read(codec, WCD9XXX_A_RC_OSC_FREQ) & 0x80) {
+ snd_soc_write(codec, WCD9XXX_A_CLK_BUFF_EN2, 0x02);
+ wcd9xxx_resmgr_enable_config_mode(codec, 0);
+ }
+ }
+
+ snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1, 0x01, 0x01);
+ snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN2, 0x02, 0x00);
+
+ /* on MCLK */
+ snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN2, 0x04, 0x04);
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_CLK_MCLK_CTL, 0x01, 0x01);
+ usleep_range(50, 50);
+
+ /* Notify */
+ if (config_mode)
+ wcd9xxx_resmgr_notifier_call(resmgr,
+ WCD9XXX_EVENT_POST_RCO_ON);
+ else
+ wcd9xxx_resmgr_notifier_call(resmgr,
+ WCD9XXX_EVENT_POST_MCLK_ON);
+}
+
+/*
+ * disable clock and return previous clock state
+ */
+static enum wcd9xxx_clock_type wcd9xxx_save_clock(struct wcd9xxx_resmgr *resmgr)
+{
+ WCD9XXX_BCL_ASSERT_LOCKED(resmgr);
+ if (resmgr->clk_type != WCD9XXX_CLK_OFF)
+ wcd9xxx_disable_clock_block(resmgr);
+ return resmgr->clk_type != WCD9XXX_CLK_OFF;
+}
+
+static void wcd9xxx_restore_clock(struct wcd9xxx_resmgr *resmgr,
+ enum wcd9xxx_clock_type type)
+{
+ if (type != WCD9XXX_CLK_OFF)
+ wcd9xxx_enable_clock_block(resmgr, type == WCD9XXX_CLK_RCO);
+}
+
+void wcd9xxx_resmgr_get_clk_block(struct wcd9xxx_resmgr *resmgr,
+ enum wcd9xxx_clock_type type)
+{
+ pr_debug("%s: current %d, requested %d, rco_users %d, mclk_users %d\n",
+ __func__, resmgr->clk_type, type,
+ resmgr->clk_rco_users, resmgr->clk_mclk_users);
+ WCD9XXX_BCL_ASSERT_LOCKED(resmgr);
+ switch (type) {
+ case WCD9XXX_CLK_RCO:
+ if (++resmgr->clk_rco_users == 1 &&
+ resmgr->clk_type == WCD9XXX_CLK_OFF) {
+ /* enable RCO and switch to it */
+ wcd9xxx_enable_clock_block(resmgr, 1);
+ resmgr->clk_type = WCD9XXX_CLK_RCO;
+ }
+ break;
+ case WCD9XXX_CLK_MCLK:
+ if (++resmgr->clk_mclk_users == 1 &&
+ resmgr->clk_type == WCD9XXX_CLK_OFF) {
+ /* switch to MCLK */
+ wcd9xxx_enable_clock_block(resmgr, 0);
+ resmgr->clk_type = WCD9XXX_CLK_MCLK;
+ } else if (resmgr->clk_mclk_users == 1 &&
+ resmgr->clk_type == WCD9XXX_CLK_RCO) {
+ /* if RCO is enabled, switch from it */
+ WARN_ON(!(snd_soc_read(resmgr->codec,
+ WCD9XXX_A_RC_OSC_FREQ) & 0x80));
+ /* disable clock block */
+ wcd9xxx_disable_clock_block(resmgr);
+ /* switch to RCO */
+ wcd9xxx_enable_clock_block(resmgr, 0);
+ resmgr->clk_type = WCD9XXX_CLK_MCLK;
+ }
+ break;
+ default:
+ pr_err("%s: Error, Invalid clock get request %d\n", __func__,
+ type);
+ break;
+ }
+ pr_debug("%s: leave\n", __func__);
+}
+
+void wcd9xxx_resmgr_put_clk_block(struct wcd9xxx_resmgr *resmgr,
+ enum wcd9xxx_clock_type type)
+{
+ pr_debug("%s: current %d, put %d\n", __func__, resmgr->clk_type, type);
+
+ WCD9XXX_BCL_ASSERT_LOCKED(resmgr);
+ switch (type) {
+ case WCD9XXX_CLK_RCO:
+ if (--resmgr->clk_rco_users == 0 &&
+ resmgr->clk_type == WCD9XXX_CLK_RCO) {
+ wcd9xxx_disable_clock_block(resmgr);
+ resmgr->clk_type = WCD9XXX_CLK_OFF;
+ }
+ break;
+ case WCD9XXX_CLK_MCLK:
+ if (--resmgr->clk_mclk_users == 0 &&
+ resmgr->clk_rco_users == 0) {
+ wcd9xxx_disable_clock_block(resmgr);
+ resmgr->clk_type = WCD9XXX_CLK_OFF;
+ } else if (resmgr->clk_mclk_users == 0 &&
+ resmgr->clk_rco_users) {
+ /* disable clock */
+ wcd9xxx_disable_clock_block(resmgr);
+ /* switch to RCO */
+ wcd9xxx_enable_clock_block(resmgr, 1);
+ resmgr->clk_type = WCD9XXX_CLK_RCO;
+ }
+ break;
+ default:
+ pr_err("%s: Error, Invalid clock get request %d\n", __func__,
+ type);
+ break;
+ }
+ WARN_ON(resmgr->clk_rco_users < 0);
+ WARN_ON(resmgr->clk_mclk_users < 0);
+
+ pr_debug("%s: new rco_users %d, mclk_users %d\n", __func__,
+ resmgr->clk_rco_users, resmgr->clk_mclk_users);
+}
+
+static void wcd9xxx_resmgr_update_cfilt_usage(struct wcd9xxx_resmgr *resmgr,
+ enum wcd9xxx_cfilt_sel cfilt_sel,
+ bool inc)
+{
+ u16 micb_cfilt_reg;
+ enum wcd9xxx_notify_event e_pre_on, e_post_off;
+ struct snd_soc_codec *codec = resmgr->codec;
+
+ switch (cfilt_sel) {
+ case WCD9XXX_CFILT1_SEL:
+ micb_cfilt_reg = WCD9XXX_A_MICB_CFILT_1_CTL;
+ e_pre_on = WCD9XXX_EVENT_PRE_CFILT_1_ON;
+ e_post_off = WCD9XXX_EVENT_POST_CFILT_1_OFF;
+ break;
+ case WCD9XXX_CFILT2_SEL:
+ micb_cfilt_reg = WCD9XXX_A_MICB_CFILT_2_CTL;
+ e_pre_on = WCD9XXX_EVENT_PRE_CFILT_2_ON;
+ e_post_off = WCD9XXX_EVENT_POST_CFILT_2_OFF;
+ break;
+ case WCD9XXX_CFILT3_SEL:
+ micb_cfilt_reg = WCD9XXX_A_MICB_CFILT_3_CTL;
+ e_pre_on = WCD9XXX_EVENT_PRE_CFILT_3_ON;
+ e_post_off = WCD9XXX_EVENT_POST_CFILT_3_OFF;
+ break;
+ default:
+ WARN(1, "Invalid CFILT selection %d\n", cfilt_sel);
+ return; /* should not happen */
+ }
+
+ if (inc) {
+ if ((resmgr->cfilt_users[cfilt_sel]++) == 0) {
+ /* Notify */
+ wcd9xxx_resmgr_notifier_call(resmgr, e_pre_on);
+ /* Enable CFILT */
+ snd_soc_update_bits(codec, micb_cfilt_reg, 0x80, 0x80);
+ }
+ } else {
+ /*
+ * Check if count not zero, decrease
+ * then check if zero, go ahead disable cfilter
+ */
+ WARN(resmgr->cfilt_users[cfilt_sel] == 0,
+ "Invalid CFILT use count 0\n");
+ if ((--resmgr->cfilt_users[cfilt_sel]) == 0) {
+ /* Disable CFILT */
+ snd_soc_update_bits(codec, micb_cfilt_reg, 0x80, 0);
+ /* Notify MBHC so MBHC can switch CFILT to fast mode */
+ wcd9xxx_resmgr_notifier_call(resmgr, e_post_off);
+ }
+ }
+}
+
+void wcd9xxx_resmgr_cfilt_get(struct wcd9xxx_resmgr *resmgr,
+ enum wcd9xxx_cfilt_sel cfilt_sel)
+{
+ return wcd9xxx_resmgr_update_cfilt_usage(resmgr, cfilt_sel, true);
+}
+
+void wcd9xxx_resmgr_cfilt_put(struct wcd9xxx_resmgr *resmgr,
+ enum wcd9xxx_cfilt_sel cfilt_sel)
+{
+ return wcd9xxx_resmgr_update_cfilt_usage(resmgr, cfilt_sel, false);
+}
+
+int wcd9xxx_resmgr_get_k_val(struct wcd9xxx_resmgr *resmgr,
+ unsigned int cfilt_mv)
+{
+ int rc = -EINVAL;
+ unsigned int ldoh_v = resmgr->pdata->micbias.ldoh_v;
+ unsigned min_mv, max_mv;
+
+ switch (ldoh_v) {
+ case WCD9XXX_LDOH_1P95_V:
+ min_mv = 160;
+ max_mv = 1800;
+ break;
+ case WCD9XXX_LDOH_2P35_V:
+ min_mv = 200;
+ max_mv = 2200;
+ break;
+ case WCD9XXX_LDOH_2P75_V:
+ min_mv = 240;
+ max_mv = 2600;
+ break;
+ case WCD9XXX_LDOH_3P0_V:
+ min_mv = 260;
+ max_mv = 2875;
+ break;
+ default:
+ goto done;
+ }
+
+ if (cfilt_mv < min_mv || cfilt_mv > max_mv)
+ goto done;
+
+ for (rc = 4; rc <= 44; rc++) {
+ min_mv = max_mv * (rc) / 44;
+ if (min_mv >= cfilt_mv) {
+ rc -= 4;
+ break;
+ }
+ }
+done:
+ return rc;
+}
+
+int wcd9xxx_resmgr_register_notifier(struct wcd9xxx_resmgr *resmgr,
+ struct notifier_block *nblock)
+{
+ return blocking_notifier_chain_register(&resmgr->notifier, nblock);
+}
+
+int wcd9xxx_resmgr_unregister_notifier(struct wcd9xxx_resmgr *resmgr,
+ struct notifier_block *nblock)
+{
+ return blocking_notifier_chain_unregister(&resmgr->notifier, nblock);
+}
+
+int wcd9xxx_resmgr_init(struct wcd9xxx_resmgr *resmgr,
+ struct snd_soc_codec *codec,
+ struct wcd9xxx *wcd9xxx,
+ struct wcd9xxx_pdata *pdata,
+ struct wcd9xxx_reg_address *reg_addr)
+{
+ WARN(ARRAY_SIZE(wcd9xxx_event_string) != WCD9XXX_EVENT_LAST + 1,
+ "Event string table isn't up to date!, %d != %d\n",
+ ARRAY_SIZE(wcd9xxx_event_string), WCD9XXX_EVENT_LAST + 1);
+
+ resmgr->bandgap_type = WCD9XXX_BANDGAP_OFF;
+ resmgr->codec = codec;
+ /* This gives access of core handle to lock/unlock suspend */
+ resmgr->core = wcd9xxx;
+ resmgr->pdata = pdata;
+ resmgr->reg_addr = reg_addr;
+
+ BLOCKING_INIT_NOTIFIER_HEAD(&resmgr->notifier);
+
+ mutex_init(&resmgr->codec_resource_lock);
+
+ return 0;
+}
+
+void wcd9xxx_resmgr_deinit(struct wcd9xxx_resmgr *resmgr)
+{
+ mutex_destroy(&resmgr->codec_resource_lock);
+}
+
+void wcd9xxx_resmgr_bcl_lock(struct wcd9xxx_resmgr *resmgr)
+{
+ mutex_lock(&resmgr->codec_resource_lock);
+}
+
+void wcd9xxx_resmgr_bcl_unlock(struct wcd9xxx_resmgr *resmgr)
+{
+ mutex_unlock(&resmgr->codec_resource_lock);
+}
+
+MODULE_DESCRIPTION("wcd9xxx resmgr module");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/wcd9xxx-resmgr.h b/sound/soc/codecs/wcd9xxx-resmgr.h
new file mode 100644
index 0000000..2d04102
--- /dev/null
+++ b/sound/soc/codecs/wcd9xxx-resmgr.h
@@ -0,0 +1,191 @@
+/* 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 __WCD9XXX_COMMON_H__
+#define __WCD9XXX_COMMON_H__
+
+#include <linux/notifier.h>
+#include <linux/mfd/wcd9xxx/wcd9xxx_registers.h>
+
+enum wcd9xxx_bandgap_type {
+ WCD9XXX_BANDGAP_OFF,
+ WCD9XXX_BANDGAP_AUDIO_MODE,
+ WCD9XXX_BANDGAP_MBHC_MODE,
+};
+
+enum wcd9xxx_clock_type {
+ WCD9XXX_CLK_OFF,
+ WCD9XXX_CLK_RCO,
+ WCD9XXX_CLK_MCLK,
+};
+
+enum wcd9xxx_cfilt_sel {
+ WCD9XXX_CFILT1_SEL,
+ WCD9XXX_CFILT2_SEL,
+ WCD9XXX_CFILT3_SEL,
+ WCD9XXX_NUM_OF_CFILT,
+};
+
+struct wcd9xxx_reg_address {
+ u16 micb_4_ctl;
+ u16 micb_4_int_rbias;
+ u16 micb_4_mbhc;
+};
+
+enum wcd9xxx_notify_event {
+ WCD9XXX_EVENT_INVALID,
+
+ WCD9XXX_EVENT_PRE_RCO_ON,
+ WCD9XXX_EVENT_POST_RCO_ON,
+ WCD9XXX_EVENT_PRE_RCO_OFF,
+ WCD9XXX_EVENT_POST_RCO_OFF,
+
+ WCD9XXX_EVENT_PRE_MCLK_ON,
+ WCD9XXX_EVENT_POST_MCLK_ON,
+ WCD9XXX_EVENT_PRE_MCLK_OFF,
+ WCD9XXX_EVENT_POST_MCLK_OFF,
+
+ WCD9XXX_EVENT_PRE_BG_OFF,
+ WCD9XXX_EVENT_POST_BG_OFF,
+ WCD9XXX_EVENT_PRE_BG_AUDIO_ON,
+ WCD9XXX_EVENT_POST_BG_AUDIO_ON,
+ WCD9XXX_EVENT_PRE_BG_MBHC_ON,
+ WCD9XXX_EVENT_POST_BG_MBHC_ON,
+
+ WCD9XXX_EVENT_PRE_MICBIAS_1_OFF,
+ WCD9XXX_EVENT_POST_MICBIAS_1_OFF,
+ WCD9XXX_EVENT_PRE_MICBIAS_2_OFF,
+ WCD9XXX_EVENT_POST_MICBIAS_2_OFF,
+ WCD9XXX_EVENT_PRE_MICBIAS_3_OFF,
+ WCD9XXX_EVENT_POST_MICBIAS_3_OFF,
+ WCD9XXX_EVENT_PRE_MICBIAS_4_OFF,
+ WCD9XXX_EVENT_POST_MICBIAS_4_OFF,
+ WCD9XXX_EVENT_PRE_MICBIAS_1_ON,
+ WCD9XXX_EVENT_POST_MICBIAS_1_ON,
+ WCD9XXX_EVENT_PRE_MICBIAS_2_ON,
+ WCD9XXX_EVENT_POST_MICBIAS_2_ON,
+ WCD9XXX_EVENT_PRE_MICBIAS_3_ON,
+ WCD9XXX_EVENT_POST_MICBIAS_3_ON,
+ WCD9XXX_EVENT_PRE_MICBIAS_4_ON,
+ WCD9XXX_EVENT_POST_MICBIAS_4_ON,
+
+ WCD9XXX_EVENT_PRE_CFILT_1_OFF,
+ WCD9XXX_EVENT_POST_CFILT_1_OFF,
+ WCD9XXX_EVENT_PRE_CFILT_2_OFF,
+ WCD9XXX_EVENT_POST_CFILT_2_OFF,
+ WCD9XXX_EVENT_PRE_CFILT_3_OFF,
+ WCD9XXX_EVENT_POST_CFILT_3_OFF,
+ WCD9XXX_EVENT_PRE_CFILT_1_ON,
+ WCD9XXX_EVENT_POST_CFILT_1_ON,
+ WCD9XXX_EVENT_PRE_CFILT_2_ON,
+ WCD9XXX_EVENT_POST_CFILT_2_ON,
+ WCD9XXX_EVENT_PRE_CFILT_3_ON,
+ WCD9XXX_EVENT_POST_CFILT_3_ON,
+
+ WCD9XXX_EVENT_PRE_HPHL_PA_ON,
+ WCD9XXX_EVENT_POST_HPHL_PA_OFF,
+ WCD9XXX_EVENT_PRE_HPHR_PA_ON,
+ WCD9XXX_EVENT_POST_HPHR_PA_OFF,
+
+ WCD9XXX_EVENT_POST_RESUME,
+
+ WCD9XXX_EVENT_LAST,
+};
+
+struct wcd9xxx_resmgr {
+ struct snd_soc_codec *codec;
+ struct wcd9xxx *core;
+
+ u32 rx_bias_count;
+
+ enum wcd9xxx_bandgap_type bandgap_type;
+ u16 bg_audio_users;
+ u16 bg_mbhc_users;
+
+ enum wcd9xxx_clock_type clk_type;
+ u16 clk_rco_users;
+ u16 clk_mclk_users;
+
+ /* cfilt users per cfilts */
+ u16 cfilt_users[WCD9XXX_NUM_OF_CFILT];
+
+ struct wcd9xxx_reg_address *reg_addr;
+
+ struct wcd9xxx_pdata *pdata;
+
+ struct blocking_notifier_head notifier;
+ /* Notifier needs mbhc pointer with resmgr */
+ struct wcd9xxx_mbhc *mbhc;
+
+ /*
+ * Currently, only used for mbhc purpose, to protect
+ * concurrent execution of mbhc threaded irq handlers and
+ * kill race between DAPM and MBHC. But can serve as a
+ * general lock to protect codec resource
+ */
+ struct mutex codec_resource_lock;
+};
+
+int wcd9xxx_resmgr_init(struct wcd9xxx_resmgr *resmgr,
+ struct snd_soc_codec *codec,
+ struct wcd9xxx *wcd9xxx,
+ struct wcd9xxx_pdata *pdata,
+ struct wcd9xxx_reg_address *reg_addr);
+void wcd9xxx_resmgr_deinit(struct wcd9xxx_resmgr *resmgr);
+
+int wcd9xxx_resmgr_enable_config_mode(struct snd_soc_codec *codec, int enable);
+
+void wcd9xxx_resmgr_enable_rx_bias(struct wcd9xxx_resmgr *resmgr, u32 enable);
+void wcd9xxx_resmgr_get_clk_block(struct wcd9xxx_resmgr *resmgr,
+ enum wcd9xxx_clock_type type);
+void wcd9xxx_resmgr_put_clk_block(struct wcd9xxx_resmgr *resmgr,
+ enum wcd9xxx_clock_type type);
+void wcd9xxx_resmgr_get_bandgap(struct wcd9xxx_resmgr *resmgr,
+ const enum wcd9xxx_bandgap_type choice);
+void wcd9xxx_resmgr_put_bandgap(struct wcd9xxx_resmgr *resmgr,
+ enum wcd9xxx_bandgap_type choice);
+void wcd9xxx_resmgr_cfilt_get(struct wcd9xxx_resmgr *resmgr,
+ enum wcd9xxx_cfilt_sel cfilt_sel);
+void wcd9xxx_resmgr_cfilt_put(struct wcd9xxx_resmgr *resmgr,
+ enum wcd9xxx_cfilt_sel cfilt_sel);
+
+void wcd9xxx_resmgr_bcl_lock(struct wcd9xxx_resmgr *resmgr);
+#define WCD9XXX_BCL_LOCK(resmgr) \
+{ \
+ pr_debug("%s: Acquiring BCL\n", __func__); \
+ wcd9xxx_resmgr_bcl_lock(resmgr); \
+ pr_debug("%s: Acquiring BCL done\n", __func__); \
+}
+
+void wcd9xxx_resmgr_bcl_unlock(struct wcd9xxx_resmgr *resmgr);
+#define WCD9XXX_BCL_UNLOCK(resmgr) \
+{ \
+ pr_debug("%s: Release BCL\n", __func__); \
+ wcd9xxx_resmgr_bcl_unlock(resmgr); \
+}
+
+#define WCD9XXX_BCL_ASSERT_LOCKED(resmgr) \
+{ \
+ WARN_ONCE(!mutex_is_locked(&resmgr->codec_resource_lock), \
+ "%s: BCL should have acquired\n", __func__); \
+}
+
+const char *wcd9xxx_get_event_string(enum wcd9xxx_notify_event type);
+int wcd9xxx_resmgr_get_k_val(struct wcd9xxx_resmgr *resmgr,
+ unsigned int cfilt_mv);
+int wcd9xxx_resmgr_register_notifier(struct wcd9xxx_resmgr *resmgr,
+ struct notifier_block *nblock);
+int wcd9xxx_resmgr_unregister_notifier(struct wcd9xxx_resmgr *resmgr,
+ struct notifier_block *nblock);
+void wcd9xxx_resmgr_notifier_call(struct wcd9xxx_resmgr *resmgr,
+ const enum wcd9xxx_notify_event e);
+
+#endif /* __WCD9XXX_COMMON_H__ */
diff --git a/sound/soc/msm/apq8064-i2s.c b/sound/soc/msm/apq8064-i2s.c
index e309370..795b421 100644
--- a/sound/soc/msm/apq8064-i2s.c
+++ b/sound/soc/msm/apq8064-i2s.c
@@ -2690,8 +2690,7 @@
static void __exit msm_audio_exit(void)
{
- if (!(cpu_is_apq8064() || cpu_is_apq8064ab()) ||
- (socinfo_get_id() == 130)) {
+ if (!soc_class_is_apq8064() || socinfo_get_id() == 130) {
pr_err("%s: Not the right machine type\n", __func__);
return ;
}
diff --git a/sound/soc/msm/apq8064.c b/sound/soc/msm/apq8064.c
index 1316a0e..4fe002b 100644
--- a/sound/soc/msm/apq8064.c
+++ b/sound/soc/msm/apq8064.c
@@ -2076,7 +2076,7 @@
{
int ret;
u32 version = socinfo_get_platform_version();
- if (!(cpu_is_apq8064() || cpu_is_apq8064ab()) ||
+ if (!soc_class_is_apq8064() ||
(socinfo_get_id() == 130) ||
(machine_is_apq8064_mtp() &&
(SOCINFO_VERSION_MINOR(version) == 1))) {
@@ -2117,8 +2117,7 @@
static void __exit msm_audio_exit(void)
{
- if (!(cpu_is_apq8064() || cpu_is_apq8064ab()) ||
- (socinfo_get_id() == 130)) {
+ if (!soc_class_is_apq8064() || socinfo_get_id() == 130) {
pr_err("%s: Not the right machine type\n", __func__);
return ;
}
diff --git a/sound/soc/msm/mpq8064.c b/sound/soc/msm/mpq8064.c
index 23703f2..7656f9f 100644
--- a/sound/soc/msm/mpq8064.c
+++ b/sound/soc/msm/mpq8064.c
@@ -140,6 +140,7 @@
static int msm_slim_0_tx_ch = 1;
static int msm_hdmi_rx_ch = 8;
static int mi2s_rate_variable;
+static int hdmi_rate_variable;
static struct clk *codec_clk;
static int clk_users;
@@ -523,6 +524,8 @@
static const char * const hdmi_rx_ch_text[] = {"Two", "Three", "Four",
"Five", "Six", "Seven", "Eight"};
static const char * const mi2s_rate[] = {"Default", "Variable"};
+static const char * const hdmi_rate[] = {"Default", "Variable"};
+
static const struct soc_enum msm_enum[] = {
@@ -531,6 +534,7 @@
SOC_ENUM_SINGLE_EXT(4, slim0_tx_ch_text),
SOC_ENUM_SINGLE_EXT(7, hdmi_rx_ch_text),
SOC_ENUM_SINGLE_EXT(2, mi2s_rate),
+ SOC_ENUM_SINGLE_EXT(2, hdmi_rate),
};
@@ -606,6 +610,21 @@
return 0;
}
+static int msm_hdmi_rate_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ hdmi_rate_variable = ucontrol->value.integer.value[0];
+ pr_debug("%s: hdmi_rate_variable = %d\n", __func__, hdmi_rate_variable);
+ return 0;
+}
+
+static int msm_hdmi_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = hdmi_rate_variable;
+ return 0;
+}
+
static const struct snd_kcontrol_new tabla_msm_controls[] = {
SOC_ENUM_EXT("Speaker Function", msm_enum[0], msm_get_spk,
msm_set_spk),
@@ -618,6 +637,9 @@
SOC_ENUM_EXT("SEC RX Rate", msm_enum[4],
msm_mi2s_rate_get,
msm_mi2s_rate_put),
+ SOC_ENUM_EXT("HDMI RX Rate", msm_enum[5],
+ msm_hdmi_rate_get,
+ msm_hdmi_rate_put),
};
@@ -867,7 +889,9 @@
pr_debug("%s channels->min %u channels->max %u ()\n", __func__,
channels->min, channels->max);
- rate->min = rate->max = 48000;
+ /*Configure the sample rate as 48000 KHz for the LPCM playback*/
+ if (!hdmi_rate_variable)
+ rate->min = rate->max = 48000;
channels->min = channels->max = msm_hdmi_rx_ch;
return 0;
diff --git a/sound/soc/msm/msm-compr-q6.c b/sound/soc/msm/msm-compr-q6.c
index a3c59b9..8202982 100644
--- a/sound/soc/msm/msm-compr-q6.c
+++ b/sound/soc/msm/msm-compr-q6.c
@@ -202,6 +202,7 @@
wake_up(&the_locks.eos_wait);
atomic_set(&prtd->eos, 0);
}
+ atomic_set(&prtd->pending_buffer, 1);
break;
case ASM_DATA_EVENT_READ_DONE: {
pr_debug("ASM_DATA_EVENT_READ_DONE\n");
@@ -1208,6 +1209,7 @@
prtd->cmd_ack = 1;
wake_up(&the_locks.eos_wait);
atomic_set(&prtd->eos, 0);
+ atomic_set(&prtd->pending_buffer, 1);
}
/* A unlikely race condition possible with FLUSH
diff --git a/sound/soc/msm/msm-dai-q6-hdmi.c b/sound/soc/msm/msm-dai-q6-hdmi.c
index 2b3dd5f..1995f1a 100644
--- a/sound/soc/msm/msm-dai-q6-hdmi.c
+++ b/sound/soc/msm/msm-dai-q6-hdmi.c
@@ -91,11 +91,25 @@
u32 channel_allocation = 0;
u32 level_shift = 0; /* 0dB */
bool down_mix = FALSE;
+ int sample_rate = 48000;
dai_data->channels = params_channels(params);
dai_data->rate = params_rate(params);
dai_data->port_config.hdmi_multi_ch.reserved = 0;
+ switch (dai_data->rate) {
+ case 48000:
+ sample_rate = HDMI_SAMPLE_RATE_48KHZ;
+ break;
+ case 44100:
+ sample_rate = HDMI_SAMPLE_RATE_44_1KHZ;
+ break;
+ case 32000:
+ sample_rate = HDMI_SAMPLE_RATE_32KHZ;
+ break;
+ }
+ hdmi_msm_audio_sample_rate_reset(sample_rate);
+
switch (dai_data->channels) {
case 2:
channel_allocation = 0;
diff --git a/sound/soc/msm/msm-pcm-routing.c b/sound/soc/msm/msm-pcm-routing.c
index 5967bb2..2b889b5 100644
--- a/sound/soc/msm/msm-pcm-routing.c
+++ b/sound/soc/msm/msm-pcm-routing.c
@@ -1490,7 +1490,13 @@
msm_routing_put_audio_mixer),
SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_SLIMBUS_0_TX,
MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
- msm_routing_put_audio_mixer)
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("VOC_REC_DL", MSM_BACKEND_DAI_INCALL_RECORD_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("VOC_REC_UL", MSM_BACKEND_DAI_INCALL_RECORD_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
};
static const struct snd_kcontrol_new pri_rx_voice_mixer_controls[] = {
@@ -2452,6 +2458,8 @@
{"MultiMedia1 Mixer", "VOC_REC_UL", "INCALL_RECORD_TX"},
{"MultiMedia1 Mixer", "VOC_REC_DL", "INCALL_RECORD_RX"},
+ {"MultiMedia4 Mixer", "VOC_REC_UL", "INCALL_RECORD_TX"},
+ {"MultiMedia4 Mixer", "VOC_REC_DL", "INCALL_RECORD_RX"},
{"MultiMedia1 Mixer", "SLIM_4_TX", "SLIMBUS_4_TX"},
{"MultiMedia5 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
{"MI2S_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
diff --git a/sound/soc/msm/msm8930.c b/sound/soc/msm/msm8930.c
index 76b7597..b3db9e1 100644
--- a/sound/soc/msm/msm8930.c
+++ b/sound/soc/msm/msm8930.c
@@ -1307,7 +1307,7 @@
{
int ret;
- if (!cpu_is_msm8930() && !cpu_is_msm8930aa() && !cpu_is_msm8627()) {
+ if (!soc_class_is_msm8930()) {
pr_err("%s: Not the right machine type\n", __func__);
return -ENODEV ;
}
@@ -1346,7 +1346,7 @@
static void __exit msm8930_audio_exit(void)
{
- if (!cpu_is_msm8930() && !cpu_is_msm8930aa() && !cpu_is_msm8627()) {
+ if (!soc_class_is_msm8930()) {
pr_err("%s: Not the right machine type\n", __func__);
return ;
}
diff --git a/sound/soc/msm/msm8960.c b/sound/soc/msm/msm8960.c
index da7a129..ad78255 100644
--- a/sound/soc/msm/msm8960.c
+++ b/sound/soc/msm/msm8960.c
@@ -1745,7 +1745,7 @@
{
int ret;
- if (!cpu_is_msm8960() && !cpu_is_msm8960ab()) {
+ if (!soc_class_is_msm8960()) {
pr_debug("%s: Not the right machine type\n", __func__);
return -ENODEV ;
}
@@ -1818,7 +1818,7 @@
static void __exit msm8960_audio_exit(void)
{
- if (!cpu_is_msm8960() && !cpu_is_msm8960ab()) {
+ if (!soc_class_is_msm8960()) {
pr_debug("%s: Not the right machine type\n", __func__);
return ;
}
diff --git a/sound/soc/msm/msm8974.c b/sound/soc/msm/msm8974.c
index 57a7fa7..e8ea058 100644
--- a/sound/soc/msm/msm8974.c
+++ b/sound/soc/msm/msm8974.c
@@ -52,10 +52,27 @@
#define GPIO_AUX_PCM_SYNC 45
#define GPIO_AUX_PCM_CLK 46
-#define TABLA_EXT_CLK_RATE 12288000
+#define WCD9XXX_MBHC_DEF_BUTTONS 8
+#define WCD9XXX_MBHC_DEF_RLOADS 5
+#define TAIKO_EXT_CLK_RATE 9600000
-#define TABLA_MBHC_DEF_BUTTONS 8
-#define TABLA_MBHC_DEF_RLOADS 5
+void *def_taiko_mbhc_cal(void);
+static int msm_snd_enable_codec_ext_clk(struct snd_soc_codec *codec, int enable,
+ bool dapm);
+
+static struct wcd9xxx_mbhc_config mbhc_cfg = {
+ .read_fw_bin = false,
+ .calibration = NULL,
+ .micbias = MBHC_MICBIAS2,
+ .mclk_cb_fn = msm_snd_enable_codec_ext_clk,
+ .mclk_rate = TAIKO_EXT_CLK_RATE,
+ .gpio = 0,
+ .gpio_irq = 0,
+ .gpio_level_insert = 1,
+ .detect_extn_cable = true,
+ .insert_detect = true,
+ .swap_gnd_mic = NULL,
+};
struct msm8974_asoc_mach_data {
int mclk_gpio;
@@ -83,9 +100,6 @@
static int msm_btsco_rate = BTSCO_RATE_8KHZ;
static int msm_btsco_ch = 1;
-static struct snd_soc_jack hs_jack;
-static struct snd_soc_jack button_jack;
-
static struct mutex cdc_mclk_mutex;
static struct q_clkdiv *codec_clk;
static int clk_users;
@@ -317,7 +331,7 @@
return 0;
}
-static int msm8974_enable_codec_ext_clk(struct snd_soc_codec *codec, int enable,
+static int msm_snd_enable_codec_ext_clk(struct snd_soc_codec *codec, int enable,
bool dapm)
{
int ret = 0;
@@ -366,9 +380,9 @@
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- return msm8974_enable_codec_ext_clk(w->codec, 1, true);
+ return msm_snd_enable_codec_ext_clk(w->codec, 1, true);
case SND_SOC_DAPM_POST_PMD:
- return msm8974_enable_codec_ext_clk(w->codec, 0, true);
+ return msm_snd_enable_codec_ext_clk(w->codec, 0, true);
}
return 0;
@@ -524,6 +538,23 @@
return 0;
}
+static int msm8974_hdmi_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+
+ pr_debug("%s channels->min %u channels->max %u ()\n", __func__,
+ channels->min, channels->max);
+
+ rate->min = rate->max = 48000;
+
+ return 0;
+}
+
static int msm_aux_pcm_get_gpios(void)
{
int ret = 0;
@@ -710,24 +741,15 @@
snd_soc_dapm_sync(dapm);
- err = snd_soc_jack_new(codec, "Headset Jack",
- (SND_JACK_HEADSET | SND_JACK_OC_HPHL |
- SND_JACK_OC_HPHR | SND_JACK_UNSUPPORTED),
- &hs_jack);
- if (err) {
- pr_err("failed to create new jack\n");
- return err;
- }
-
snd_soc_dai_set_channel_map(codec_dai, ARRAY_SIZE(tx_ch),
tx_ch, ARRAY_SIZE(rx_ch), rx_ch);
- err = snd_soc_jack_new(codec, "Button Jack",
- TAIKO_JACK_BUTTON_MASK, &button_jack);
- if (err) {
- pr_err("failed to create new jack\n");
- return err;
- }
+ /* start mbhc */
+ mbhc_cfg.calibration = def_taiko_mbhc_cal();
+ if (mbhc_cfg.calibration)
+ err = taiko_hs_detect(codec, &mbhc_cfg);
+ else
+ err = -ENOMEM;
return err;
}
@@ -739,6 +761,84 @@
return 0;
}
+void *def_taiko_mbhc_cal(void)
+{
+ void *taiko_cal;
+ struct wcd9xxx_mbhc_btn_detect_cfg *btn_cfg;
+ u16 *btn_low, *btn_high;
+ u8 *n_ready, *n_cic, *gain;
+
+ taiko_cal = kzalloc(WCD9XXX_MBHC_CAL_SIZE(WCD9XXX_MBHC_DEF_BUTTONS,
+ WCD9XXX_MBHC_DEF_RLOADS),
+ GFP_KERNEL);
+ if (!taiko_cal) {
+ pr_err("%s: out of memory\n", __func__);
+ return NULL;
+ }
+
+#define S(X, Y) ((WCD9XXX_MBHC_CAL_GENERAL_PTR(taiko_cal)->X) = (Y))
+ S(t_ldoh, 100);
+ S(t_bg_fast_settle, 100);
+ S(t_shutdown_plug_rem, 255);
+ S(mbhc_nsa, 4);
+ S(mbhc_navg, 4);
+#undef S
+#define S(X, Y) ((WCD9XXX_MBHC_CAL_PLUG_DET_PTR(taiko_cal)->X) = (Y))
+ S(mic_current, TAIKO_PID_MIC_5_UA);
+ S(hph_current, TAIKO_PID_MIC_5_UA);
+ S(t_mic_pid, 100);
+ S(t_ins_complete, 250);
+ S(t_ins_retry, 200);
+#undef S
+#define S(X, Y) ((WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(taiko_cal)->X) = (Y))
+ S(v_no_mic, 30);
+ S(v_hs_max, 2400);
+#undef S
+#define S(X, Y) ((WCD9XXX_MBHC_CAL_BTN_DET_PTR(taiko_cal)->X) = (Y))
+ S(c[0], 62);
+ S(c[1], 124);
+ S(nc, 1);
+ S(n_meas, 3);
+ S(mbhc_nsc, 11);
+ S(n_btn_meas, 1);
+ S(n_btn_con, 2);
+ S(num_btn, WCD9XXX_MBHC_DEF_BUTTONS);
+ S(v_btn_press_delta_sta, 100);
+ S(v_btn_press_delta_cic, 50);
+#undef S
+ btn_cfg = WCD9XXX_MBHC_CAL_BTN_DET_PTR(taiko_cal);
+ btn_low = wcd9xxx_mbhc_cal_btn_det_mp(btn_cfg, MBHC_BTN_DET_V_BTN_LOW);
+ btn_high = wcd9xxx_mbhc_cal_btn_det_mp(btn_cfg,
+ MBHC_BTN_DET_V_BTN_HIGH);
+ btn_low[0] = -50;
+ btn_high[0] = 34;
+ btn_low[1] = 35;
+ btn_high[1] = 52;
+ btn_low[2] = 53;
+ btn_high[2] = 94;
+ btn_low[3] = 95;
+ btn_high[3] = 133;
+ btn_low[4] = 134;
+ btn_high[4] = 171;
+ btn_low[5] = 172;
+ btn_high[5] = 208;
+ btn_low[6] = 209;
+ btn_high[6] = 244;
+ btn_low[7] = 245;
+ btn_high[7] = 330;
+ n_ready = wcd9xxx_mbhc_cal_btn_det_mp(btn_cfg, MBHC_BTN_DET_N_READY);
+ n_ready[0] = 80;
+ n_ready[1] = 68;
+ n_cic = wcd9xxx_mbhc_cal_btn_det_mp(btn_cfg, MBHC_BTN_DET_N_CIC);
+ n_cic[0] = 60;
+ n_cic[1] = 47;
+ gain = wcd9xxx_mbhc_cal_btn_det_mp(btn_cfg, MBHC_BTN_DET_GAIN);
+ gain[0] = 11;
+ gain[1] = 9;
+
+ return taiko_cal;
+}
+
static int msm_snd_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
@@ -1044,6 +1144,34 @@
.be_id = MSM_BACKEND_DAI_AFE_PCM_TX,
.be_hw_params_fixup = msm_proxy_be_hw_params_fixup,
},
+ /* HDMI Hostless */
+ {
+ .name = "HDMI_RX_HOSTLESS",
+ .stream_name = "HDMI_RX_HOSTLESS",
+ .cpu_dai_name = "HDMI_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ /* HDMI BACK END DAI Link */
+ {
+ .name = LPASS_BE_HDMI,
+ .stream_name = "HDMI Playback",
+ .cpu_dai_name = "msm-dai-q6-hdmi.8",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .be_id = MSM_BACKEND_DAI_HDMI_RX,
+ .be_hw_params_fixup = msm8974_hdmi_be_hw_params_fixup,
+ .ignore_pmdown_time = 1,
+ },
/* AUX PCM Backend DAI Links */
{
.name = LPASS_BE_AUXPCM_RX,
diff --git a/sound/soc/msm/qdsp6v2/Makefile b/sound/soc/msm/qdsp6v2/Makefile
index acb073d..1d11907 100644
--- a/sound/soc/msm/qdsp6v2/Makefile
+++ b/sound/soc/msm/qdsp6v2/Makefile
@@ -1,5 +1,5 @@
snd-soc-qdsp6v2-objs += msm-dai-q6-v2.o msm-pcm-q6-v2.o msm-pcm-routing-v2.o msm-compr-q6-v2.o msm-multi-ch-pcm-q6-v2.o
-snd-soc-qdsp6v2-objs += msm-pcm-lpa-v2.o msm-pcm-afe-v2.o msm-pcm-voip-v2.o msm-pcm-voice-v2.o
+snd-soc-qdsp6v2-objs += msm-pcm-lpa-v2.o msm-pcm-afe-v2.o msm-pcm-voip-v2.o msm-pcm-voice-v2.o msm-dai-q6-hdmi-v2.o
obj-$(CONFIG_SND_SOC_QDSP6V2) += snd-soc-qdsp6v2.o
obj-y += q6adm.o q6afe.o q6asm.o q6audio-v2.o q6voice.o q6core.o
ocmem-audio-objs += audio_ocmem.o
diff --git a/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c
index 01a9538..d85bbbc 100644
--- a/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c
@@ -148,17 +148,14 @@
((unsigned int)buf[0].phys
+ (prtd->out_head * prtd->pcm_count)));
- if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) {
+ if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE)
time_stamp_flag = SET_TIMESTAMP;
- memcpy(&output_meta_data, (char *)(buf->data +
+ else
+ time_stamp_flag = NO_TIMESTAMP;
+ memcpy(&output_meta_data, (char *)(buf->data +
prtd->out_head * prtd->pcm_count),
COMPRE_OUTPUT_METADATA_SIZE);
- } else {
- time_stamp_flag = NO_TIMESTAMP;
- memset(&output_meta_data, 0,
- COMPRE_OUTPUT_METADATA_SIZE);
- output_meta_data.frame_size = prtd->pcm_count;
- }
+
buffer_length = output_meta_data.frame_size;
pr_debug("meta_data_length: %d, frame_length: %d\n",
output_meta_data.meta_data_length,
@@ -257,17 +254,13 @@
__func__, prtd->out_head,
((unsigned int)buf[0].phys
+ (prtd->out_head * prtd->pcm_count)));
- if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) {
+ if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE)
time_stamp_flag = SET_TIMESTAMP;
- memcpy(&output_meta_data, (char *)(buf->data +
+ else
+ time_stamp_flag = NO_TIMESTAMP;
+ memcpy(&output_meta_data, (char *)(buf->data +
prtd->out_head * prtd->pcm_count),
COMPRE_OUTPUT_METADATA_SIZE);
- } else {
- time_stamp_flag = NO_TIMESTAMP;
- memset(&output_meta_data, 0,
- COMPRE_OUTPUT_METADATA_SIZE);
- output_meta_data.frame_size = prtd->pcm_count;
- }
buffer_length = output_meta_data.frame_size;
pr_debug("meta_data_length: %d, frame_length: %d\n",
output_meta_data.meta_data_length,
@@ -774,7 +767,8 @@
}
}
- ret = q6asm_set_io_mode(prtd->audio_client, ASYNC_IO_MODE);
+ ret = q6asm_set_io_mode(prtd->audio_client,
+ (COMPRESSED_IO | ASYNC_IO_MODE));
if (ret < 0) {
pr_err("%s: Set IO mode failed\n", __func__);
return -ENOMEM;
diff --git a/sound/soc/msm/qdsp6v2/msm-dai-q6-hdmi-v2.c b/sound/soc/msm/qdsp6v2/msm-dai-q6-hdmi-v2.c
new file mode 100644
index 0000000..f80b5891
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-dai-q6-hdmi-v2.c
@@ -0,0 +1,320 @@
+/* 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/device.h>
+#include <linux/platform_device.h>
+#include <linux/bitops.h>
+#include <linux/slab.h>
+#include <linux/of_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/apr_audio-v2.h>
+#include <sound/q6afe-v2.h>
+#include <sound/msm-dai-q6-v2.h>
+#include <mach/msm_hdmi_audio.h>
+
+
+enum {
+ STATUS_PORT_STARTED, /* track if AFE port has started */
+ STATUS_MAX
+};
+
+struct msm_dai_q6_hdmi_dai_data {
+ DECLARE_BITMAP(status_mask, STATUS_MAX);
+ u32 rate;
+ u32 channels;
+ union afe_port_config port_config;
+};
+
+static int msm_dai_q6_hdmi_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+
+ struct msm_dai_q6_hdmi_dai_data *dai_data = kcontrol->private_data;
+ int value = ucontrol->value.integer.value[0];
+ dai_data->port_config.hdmi_multi_ch.datatype = value;
+ pr_debug("%s: value = %d\n", __func__, value);
+ return 0;
+}
+
+static int msm_dai_q6_hdmi_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+
+ struct msm_dai_q6_hdmi_dai_data *dai_data = kcontrol->private_data;
+ ucontrol->value.integer.value[0] =
+ dai_data->port_config.hdmi_multi_ch.datatype;
+ return 0;
+}
+
+
+/* HDMI format field for AFE_PORT_MULTI_CHAN_HDMI_AUDIO_IF_CONFIG command
+ * 0: linear PCM
+ * 1: non-linear PCM
+ */
+static const char * const hdmi_format[] = {
+ "LPCM",
+ "Compr"
+};
+
+static const struct soc_enum hdmi_config_enum[] = {
+ SOC_ENUM_SINGLE_EXT(2, hdmi_format),
+};
+
+static const struct snd_kcontrol_new hdmi_config_controls[] = {
+ SOC_ENUM_EXT("HDMI RX Format", hdmi_config_enum[0],
+ msm_dai_q6_hdmi_format_get,
+ msm_dai_q6_hdmi_format_put),
+};
+
+/* Current implementation assumes hw_param is called once
+ * This may not be the case but what to do when ADM and AFE
+ * port are already opened and parameter changes
+ */
+static int msm_dai_q6_hdmi_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct msm_dai_q6_hdmi_dai_data *dai_data = dev_get_drvdata(dai->dev);
+ u32 channel_allocation = 0;
+ u32 level_shift = 0; /* 0dB */
+ bool down_mix = FALSE;
+
+ dai_data->channels = params_channels(params);
+ dai_data->rate = params_rate(params);
+ dai_data->port_config.hdmi_multi_ch.reserved = 0;
+ dai_data->port_config.hdmi_multi_ch.hdmi_cfg_minor_version = 1;
+ dai_data->port_config.hdmi_multi_ch.sample_rate = dai_data->rate;
+ dai_data->port_config.hdmi_multi_ch.bit_width = 16;
+
+ switch (dai_data->channels) {
+ case 2:
+ channel_allocation = 0;
+ hdmi_msm_audio_info_setup(1, MSM_HDMI_AUDIO_CHANNEL_2,
+ channel_allocation, level_shift, down_mix);
+ dai_data->port_config.hdmi_multi_ch.channel_allocation =
+ channel_allocation;
+ break;
+ case 6:
+ channel_allocation = 0x0B;
+ hdmi_msm_audio_info_setup(1, MSM_HDMI_AUDIO_CHANNEL_6,
+ channel_allocation, level_shift, down_mix);
+ dai_data->port_config.hdmi_multi_ch.channel_allocation =
+ channel_allocation;
+ break;
+ case 8:
+ channel_allocation = 0x1F;
+ hdmi_msm_audio_info_setup(1, MSM_HDMI_AUDIO_CHANNEL_8,
+ channel_allocation, level_shift, down_mix);
+ dai_data->port_config.hdmi_multi_ch.channel_allocation =
+ channel_allocation;
+ break;
+ default:
+ dev_err(dai->dev, "invalid Channels = %u\n",
+ dai_data->channels);
+ return -EINVAL;
+ }
+ dev_dbg(dai->dev, "%s() minor version: %u samplerate: %u bitwidth: %u num_ch = %u channel_allocation = %u datatype = %d\n",
+ __func__,
+ dai_data->port_config.hdmi_multi_ch.hdmi_cfg_minor_version,
+ dai_data->port_config.hdmi_multi_ch.sample_rate,
+ dai_data->port_config.hdmi_multi_ch.bit_width,
+ dai_data->channels,
+ dai_data->port_config.hdmi_multi_ch.channel_allocation,
+ dai_data->port_config.hdmi_multi_ch.datatype);
+
+ return 0;
+}
+
+
+static void msm_dai_q6_hdmi_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct msm_dai_q6_hdmi_dai_data *dai_data = dev_get_drvdata(dai->dev);
+ int rc = 0;
+
+ if (!test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
+ pr_info("%s: afe port not started. dai_data->status_mask = %ld\n",
+ __func__, *dai_data->status_mask);
+ return;
+ }
+
+ rc = afe_close(dai->id); /* can block */
+
+ if (IS_ERR_VALUE(rc))
+ dev_err(dai->dev, "fail to close AFE port\n");
+
+ pr_debug("%s: dai_data->status_mask = %ld\n", __func__,
+ *dai_data->status_mask);
+
+ clear_bit(STATUS_PORT_STARTED, dai_data->status_mask);
+}
+
+
+static int msm_dai_q6_hdmi_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct msm_dai_q6_hdmi_dai_data *dai_data = dev_get_drvdata(dai->dev);
+ int rc = 0;
+
+ if (!test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
+ rc = afe_port_start(dai->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_hdmi_dai_probe(struct snd_soc_dai *dai)
+{
+ struct msm_dai_q6_hdmi_dai_data *dai_data;
+ const struct snd_kcontrol_new *kcontrol;
+ int rc = 0;
+
+ dai_data = kzalloc(sizeof(struct msm_dai_q6_hdmi_dai_data),
+ GFP_KERNEL);
+
+ if (!dai_data) {
+ dev_err(dai->dev, "DAI-%d: fail to allocate dai data\n",
+ dai->id);
+ rc = -ENOMEM;
+ } else
+ dev_set_drvdata(dai->dev, dai_data);
+
+ kcontrol = &hdmi_config_controls[0];
+
+ rc = snd_ctl_add(dai->card->snd_card,
+ snd_ctl_new1(kcontrol, dai_data));
+ return rc;
+}
+
+static int msm_dai_q6_hdmi_dai_remove(struct snd_soc_dai *dai)
+{
+ struct msm_dai_q6_hdmi_dai_data *dai_data;
+ int rc;
+
+ dai_data = dev_get_drvdata(dai->dev);
+
+ /* If AFE port is still up, close it */
+ if (test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
+ rc = afe_close(dai->id); /* can block */
+
+ if (IS_ERR_VALUE(rc))
+ dev_err(dai->dev, "fail to close AFE port\n");
+
+ clear_bit(STATUS_PORT_STARTED, dai_data->status_mask);
+ }
+ kfree(dai_data);
+ snd_soc_unregister_dai(dai->dev);
+
+ return 0;
+}
+
+static struct snd_soc_dai_ops msm_dai_q6_hdmi_ops = {
+ .prepare = msm_dai_q6_hdmi_prepare,
+ .hw_params = msm_dai_q6_hdmi_hw_params,
+ .shutdown = msm_dai_q6_hdmi_shutdown,
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_hdmi_hdmi_rx_dai = {
+ .playback = {
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 2,
+ .channels_max = 6,
+ .rate_max = 48000,
+ .rate_min = 48000,
+ },
+ .ops = &msm_dai_q6_hdmi_ops,
+ .probe = msm_dai_q6_hdmi_dai_probe,
+ .remove = msm_dai_q6_hdmi_dai_remove,
+};
+
+
+/* To do: change to register DAIs as batch */
+static __devinit int msm_dai_q6_hdmi_dev_probe(struct platform_device *pdev)
+{
+ int rc, id;
+ const char *q6_dev_id = "qcom,msm-dai-q6-dev-id";
+
+ rc = of_property_read_u32(pdev->dev.of_node, q6_dev_id, &id);
+ if (rc) {
+ dev_err(&pdev->dev,
+ "%s: missing %s in dt node\n", __func__, q6_dev_id);
+ return rc;
+ }
+
+ pdev->id = id;
+ dev_set_name(&pdev->dev, "%s.%d", "msm-dai-q6-hdmi", id);
+
+ pr_debug("%s: dev name %s, id:%d\n", __func__,
+ dev_name(&pdev->dev), pdev->id);
+
+ switch (pdev->id) {
+ case HDMI_RX:
+ rc = snd_soc_register_dai(&pdev->dev,
+ &msm_dai_q6_hdmi_hdmi_rx_dai);
+ break;
+ default:
+ dev_err(&pdev->dev, "invalid device ID %d\n", pdev->id);
+ rc = -ENODEV;
+ break;
+ }
+ return rc;
+}
+
+static __devexit int msm_dai_q6_hdmi_dev_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_dai(&pdev->dev);
+ return 0;
+}
+
+static const struct of_device_id msm_dai_q6_hdmi_dt_match[] = {
+ {.compatible = "qcom,msm-dai-q6-hdmi"},
+ {}
+};
+MODULE_DEVICE_TABLE(of, msm_dai_q6_hdmi_dt_match);
+
+static struct platform_driver msm_dai_q6_hdmi_driver = {
+ .probe = msm_dai_q6_hdmi_dev_probe,
+ .remove = msm_dai_q6_hdmi_dev_remove,
+ .driver = {
+ .name = "msm-dai-q6-hdmi",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_dai_q6_hdmi_dt_match,
+ },
+};
+
+static int __init msm_dai_q6_hdmi_init(void)
+{
+ return platform_driver_register(&msm_dai_q6_hdmi_driver);
+}
+module_init(msm_dai_q6_hdmi_init);
+
+static void __exit msm_dai_q6_hdmi_exit(void)
+{
+ platform_driver_unregister(&msm_dai_q6_hdmi_driver);
+}
+module_exit(msm_dai_q6_hdmi_exit);
+
+/* Module information */
+MODULE_DESCRIPTION("MSM DSP HDMI DAI driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/qdsp6v2/q6adm.c b/sound/soc/msm/qdsp6v2/q6adm.c
index 0466eb6..6acc136 100644
--- a/sound/soc/msm/qdsp6v2/q6adm.c
+++ b/sound/soc/msm/qdsp6v2/q6adm.c
@@ -51,6 +51,24 @@
static struct adm_ctl this_adm;
+static void adm_callback_debug_print(struct apr_client_data *data)
+{
+ uint32_t *payload;
+ payload = data->payload;
+
+ if (data->payload_size >= 8)
+ pr_debug("%s: code = 0x%x PL#0[%x], PL#1[%x], size = %d\n",
+ __func__, data->opcode, payload[0], payload[1],
+ data->payload_size);
+ else if (data->payload_size >= 4)
+ pr_debug("%s: code = 0x%x PL#0[%x], size = %d\n",
+ __func__, data->opcode, payload[0],
+ data->payload_size);
+ else
+ pr_debug("%s: code = 0x%x, size = %d\n",
+ __func__, data->opcode, data->payload_size);
+}
+
static int32_t adm_callback(struct apr_client_data *data, void *priv)
{
uint32_t *payload;
@@ -86,10 +104,7 @@
return 0;
}
- pr_debug("%s: code = 0x%x PL#0[%x], PL#1[%x], size = %d\n", __func__,
- data->opcode, payload[0], payload[1],
- data->payload_size);
-
+ adm_callback_debug_print(data);
if (data->payload_size) {
index = q6audio_get_port_index(data->token);
if (index < 0 || index >= Q6_AFE_MAX_PORTS) {
@@ -99,6 +114,10 @@
}
if (data->opcode == APR_BASIC_RSP_RESULT) {
pr_debug("APR_BASIC_RSP_RESULT id %x\n", payload[0]);
+ if (payload[1] != 0) {
+ pr_err("%s: cmd = 0x%x returned error = 0x%x\n",
+ __func__, payload[0], payload[1]);
+ }
switch (payload[0]) {
case ADM_CMD_SET_PP_PARAMS_V5:
if (rtac_make_adm_callback(
diff --git a/sound/soc/msm/qdsp6v2/q6asm.c b/sound/soc/msm/qdsp6v2/q6asm.c
index 072e293..875bf47 100644
--- a/sound/soc/msm/qdsp6v2/q6asm.c
+++ b/sound/soc/msm/qdsp6v2/q6asm.c
@@ -476,9 +476,13 @@
return;
}
-int q6asm_set_io_mode(struct audio_client *ac, uint32_t mode)
+int q6asm_set_io_mode(struct audio_client *ac, uint32_t mode1)
{
+ uint32_t mode;
+
ac->io_mode &= 0xFF00;
+ mode = (mode1 & 0xF);
+
pr_debug("%s ac->mode after anding with FF00:0x[%x],\n",
__func__, ac->io_mode);
if (ac == NULL) {
@@ -486,7 +490,7 @@
return -EINVAL;
}
if ((mode == ASYNC_IO_MODE) || (mode == SYNC_IO_MODE)) {
- ac->io_mode |= mode;
+ ac->io_mode |= mode1;
pr_debug("%s:Set Mode to 0x[%x]\n", __func__, ac->io_mode);
return 0;
} else {
@@ -2961,6 +2965,7 @@
struct audio_port_data *port;
u32 lbuf_addr_lsw;
u32 liomode;
+ u32 io_compressed;
if (!ac || ac->apr == NULL) {
pr_err("%s: APR handle NULL\n", __func__);
@@ -2981,9 +2986,12 @@
write.timestamp_msw = param->msw_ts;
write.timestamp_lsw = param->lsw_ts;
liomode = (ASYNC_IO_MODE | NT_MODE);
+ io_compressed = (ASYNC_IO_MODE | COMPRESSED_IO);
if (ac->io_mode == liomode)
lbuf_addr_lsw = (write.buf_addr_lsw - 32);
+ else if (ac->io_mode == io_compressed)
+ lbuf_addr_lsw = (write.buf_addr_lsw - 0x40);
else
lbuf_addr_lsw = write.buf_addr_lsw;