Merge "msm_fb: display: fix dsi_busy_wait race condition"
diff --git a/Documentation/arm/msm/tspp.txt b/Documentation/arm/msm/tspp.txt
index a56f014..d770260 100644
--- a/Documentation/arm/msm/tspp.txt
+++ b/Documentation/arm/msm/tspp.txt
@@ -157,13 +157,12 @@
API
===
-int tspp_open_stream(tspp_device *dev, void *stream, void *channel, tspp_mode
- mode);
-int tspp_close_stream(tspp_device *dev, void *stream);
-int tspp_open_channel(tspp_device *dev, int dest, int bufsize, void *channel);
-int tspp_close_channel(tspp_device *dev, void *channel);
-int tspp_register_filter(tspp_device *dev, void *channel, tspp_filter *filter);
-int tspp_unregister_filter(tspp_device *dev, void *channel, int pid);
+int tspp_open_stream(u32 dev, u32 channel, struct tspp_select_source *source);
+int tspp_close_stream(u32 dev, u32 channel);
+int tspp_open_channel(u32 dev, u32 channel);
+int tspp_close_channelu(32 dev, u32 channel);
+int tspp_add_filter(u32 dev, u32 channel, struct tspp_filter *filter);
+int tspp_remove_filter(u32 dev, u32 channel, struct tspp_filter *filter);
Refer to chrdev implementation in kernel/drivers/misc/tspp.c for an example of
how to use this api.
diff --git a/Documentation/devicetree/bindings/fb/mdss-edp.txt b/Documentation/devicetree/bindings/fb/mdss-edp.txt
index 4fedc72..3c4e1d3 100644
--- a/Documentation/devicetree/bindings/fb/mdss-edp.txt
+++ b/Documentation/devicetree/bindings/fb/mdss-edp.txt
@@ -4,15 +4,20 @@
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.
+- 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 backlight
+ driver.
+- qcom,panel-lpg-channel : LPG channel for backlight.
+- qcom,panel-pwm-period : PWM period in microseconds.
+- 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 {
@@ -22,6 +27,8 @@
reg-names = "edp_base", "mmss_cc_base";
vdda-supply = <&pm8941_l12>;
gpio-panel-en = <&msmgpio 58 0>;
+ qcom,panel-lpg-channel = <7>; /* LPG Channel 8 */
+ qcom,panel-pwm-period = <53>;
status = "disable";
};
diff --git a/Documentation/devicetree/bindings/hwmon/epm_adc.txt b/Documentation/devicetree/bindings/hwmon/epm_adc.txt
index 89edc16..a0ca490 100644
--- a/Documentation/devicetree/bindings/hwmon/epm_adc.txt
+++ b/Documentation/devicetree/bindings/hwmon/epm_adc.txt
@@ -9,7 +9,7 @@
EPM node
Required properties:
-- compatible : should be "qcom,epm-adc" for EPM using PSoC5.
+- compatible : should be "cy,epm-adc-cy8c5568lti-114" for EPM using PSoC5.
- reg : chip select for the device.
- interrupt-parent : should be phandle of the interrupt controller
servicing the interrupt for this device.
diff --git a/Documentation/devicetree/bindings/power/bq28400-battery.txt b/Documentation/devicetree/bindings/power/bq28400-battery.txt
new file mode 100644
index 0000000..3879b4d
--- /dev/null
+++ b/Documentation/devicetree/bindings/power/bq28400-battery.txt
@@ -0,0 +1,18 @@
+TI BQ28400 Battery Gas Gauge
+
+The bq28400 monitors the battery temperature, capacity, voltage, current etc.
+The device interface is I2C, its I2C slave 7-bit address is 0xb.
+The device is usually embedded inside the "smart battery" pack.
+
+node required properties:
+- compatible: Must be "ti,bq28400-battery".
+- reg: I2C Address must be 0xb.
+
+Example:
+ i2c@f9967000 {
+ battery@b {
+ compatible = "ti,bq28400-battery";
+ reg = <0xb>;
+ };
+ };
+
diff --git a/Documentation/devicetree/bindings/power/smb350.txt b/Documentation/devicetree/bindings/power/smb350.txt
new file mode 100644
index 0000000..6f21236
--- /dev/null
+++ b/Documentation/devicetree/bindings/power/smb350.txt
@@ -0,0 +1,43 @@
+Summit smb350 battery charger
+
+The smb350 charger supports stack-cell battery charging.
+
+The smb350 interface is via I2C bus.
+The i2c slave 7-bit address is programmable at manufacture.
+
+Node required properties:
+- compatible: Must be "summit,smb350-charger".
+- reg: The device 7-bit I2C address.
+- summit,stat-gpio gpio which smb350 STAT pin connects to.
+- summit,chg-en-n-gpio gpio which control charging enable.
+- summit,chg-susp-n-gpio gpio which control device shutdown
+- summit,chg-current-ma charging current in milliamps.
+- summit,term-current-ma charging termination current in milliamps.
+ valid values are 200/300/400/500/600/700.
+ A value of zero means no termination current.
+
+Example:
+ i2c@f9967000 {
+ cell-index = <0>;
+ compatible = "qcom,i2c-qup";
+ reg = <0Xf9967000 0x1000>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg-names = "qup_phys_addr";
+ interrupts = <0 105 0>;
+ interrupt-names = "qup_err_intr";
+ qcom,i2c-bus-freq = <100000>;
+ qcom,i2c-src-freq = <24000000>;
+ label = "blsp_11";
+
+ smb350-charger@2b {
+ compatible = "summit,smb350-charger";
+ reg = <0x2b>; /* 0x56/0x57 */
+ summit,stat-gpio = <&pm8941_gpios 30 0x00>;
+ summit,chg-en-n-gpio = <&pm8941_gpios 10 0x00>;
+ summit,chg-susp-n-gpio = <&pm8941_gpios 13 0x00>;
+ summit,chg-current-ma = <1600>;
+ summit,term-current-ma = <200>;
+ };
+ };
+
diff --git a/Documentation/devicetree/bindings/regulator/stub-regulator.txt b/Documentation/devicetree/bindings/regulator/stub-regulator.txt
new file mode 100644
index 0000000..1057e17
--- /dev/null
+++ b/Documentation/devicetree/bindings/regulator/stub-regulator.txt
@@ -0,0 +1,48 @@
+Stub Voltage Regulators
+
+stub-regulators are place-holder regulator devices which do not impact any
+hardware state. They provide a means for consumer devices to utilize all
+regulator features for testing purposes.
+
+Required properties:
+- compatible: Must be "qcom,stub-regulator".
+- regulator-name: A string used as a descriptive name for regulator outputs.
+
+Optional properties:
+- parent-supply: phandle to the parent supply/regulator node if one exists.
+- qcom,hpm-min-load: Load current in uA which corresponds to the minimum load
+ which requires the regulator to be in high power mode.
+- qcom,system-load: Load in uA present on regulator that is not captured by any
+ consumer request.
+
+All properties specified within the core regulator framework can also be used.
+These bindings can be found in regulator.txt.
+
+Example:
+
+/ {
+ pm8026_s3: regulator-s3 {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "8026_s3";
+ qcom,hpm-min-load = <100000>;
+ regulator-min-microvolt = <1300000>;
+ regulator-max-microvolt = <1300000>;
+ };
+
+ pm8026_l1: regulator-l1 {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "8026_l1";
+ parent-supply = <&pm8026_s3>;
+ qcom,hpm-min-load = <10000>;
+ regulator-min-microvolt = <1225000>;
+ regulator-max-microvolt = <1225000>;
+ };
+
+ pm8026_l20: regulator-l20 {
+ compatible = "qcom,stub-regulator";
+ regulator-name = "8026_l20";
+ qcom,hpm-min-load = <5000>;
+ regulator-min-microvolt = <3075000>;
+ regulator-max-microvolt = <3075000>;
+ };
+};
diff --git a/Documentation/devicetree/bindings/sound/taiko_codec.txt b/Documentation/devicetree/bindings/sound/taiko_codec.txt
index 9f3719b..96e3a61 100644
--- a/Documentation/devicetree/bindings/sound/taiko_codec.txt
+++ b/Documentation/devicetree/bindings/sound/taiko_codec.txt
@@ -28,6 +28,10 @@
- qcom,cdc-micbias2-cfilt-sel = cfilt to use for micbias2 (should be from 1 to 3).
- qcom,cdc-micbias3-cfilt-sel = cfilt to use for micbias3 (should be from 1 to 3).
- qcom,cdc-micbias4-cfilt-sel = cfilt to use for micbias4 (should be from 1 to 3).
+ - qcom,cdc-micbias1-ext-cap: Boolean. Enable micbias 1 external capacitor mode.
+ - qcom,cdc-micbias2-ext-cap: Boolean. Enable micbias 2 external capacitor mode.
+ - qcom,cdc-micbias3-ext-cap: Boolean. Enable micbias 3 external capacitor mode.
+ - qcom,cdc-micbias4-ext-cap: Boolean. Enable micbias 4 external capacitor mode.
- qcom,cdc-slim-ifd-dev - namme of the codec slim interface device.
- qcom,cdc-slim-ifd-elemental-addr - codec slimbus slave interface device
@@ -76,6 +80,10 @@
qcom,cdc-micbias2-cfilt-sel = <0x1>;
qcom,cdc-micbias3-cfilt-sel = <0x2>;
qcom,cdc-micbias4-cfilt-sel = <0x2>;
+ qcom,cdc-micbias1-ext-cap;
+ qcom,cdc-micbias2-ext-cap;
+ qcom,cdc-micbias3-ext-cap;
+ qcom,cdc-micbias4-ext-cap;
qcom,cdc-slim-ifd = "taiko-slim-ifd";
qcom,cdc-slim-ifd-elemental-addr = [00 00 A0 00 17 02];
diff --git a/Documentation/devicetree/bindings/usb/msm-ssusb.txt b/Documentation/devicetree/bindings/usb/msm-ssusb.txt
index 9cc9e6e..99274d5 100644
--- a/Documentation/devicetree/bindings/usb/msm-ssusb.txt
+++ b/Documentation/devicetree/bindings/usb/msm-ssusb.txt
@@ -11,7 +11,7 @@
"otg_irq" : Interrupt for DWC3 core's OTG Events
- <supply-name>-supply: phandle to the regulator device tree node
Required "supply-name" examples are "SSUSB_VDDCX", "SSUSB_1p8",
- "HSUSB_VDDCX", "HSUSB_1p8", "HSUSB_3p3".
+ "HSUSB_VDDCX", "HSUSB_1p8", "HSUSB_3p3" and "vbus_dwc3".
- qcom,dwc-usb3-msm-dbm-eps: Number of endpoints avaliable for
the DBM (Device Bus Manager). The DBM is HW unit which is part of
the MSM USB3.0 core (which also includes the Synopsys DesignWare
@@ -25,19 +25,25 @@
- qcom,msm_bus,active_only
- qcom,msm_bus,num_paths
- qcom,msm_bus,vectors
+- interrupt-names : Optional interrupt resource entries are:
+ "hs_phy_irq" : Interrupt from HSPHY for asynchronous events in LPM.
+ This is not used if wakeup events are received externally (e.g. PMIC)
+- qcom,dwc-usb3-msm-otg-capability: If present then depends on PMIC
+ for VBUS notifications, otherwise depends on PHY.
Example MSM USB3.0 controller device node :
usb@f9200000 {
compatible = "qcom,dwc-usb3-msm";
reg = <0xF9200000 0xFA000>,
<0xFD4AB000 0x4>;
- interrupts = <0 131 0 0 179 0>;
- interrupt-names = "irq", "otg_irq";
+ interrupts = <0 131 0>, <0 179 0>, <0 133 0>;
+ interrupt-names = "irq", "otg_irq", "hs_phy_irq";
SSUSB_VDDCX-supply = <&pm8841_s2>;
SSUSB_1p8-supply = <&pm8941_l6>;
HSUSB_VDDCX-supply = <&pm8841_s2>;
HSUSB_1p8-supply = <&pm8941_l6>;
HSUSB_3p3-supply = <&pm8941_l24>;
+ vbus_dwc3-supply = <&pm8941_mvs1>;
qcom,dwc-usb3-msm-dbm-eps = <4>
qcom,msm_bus,name = "usb3";
diff --git a/arch/arm/boot/dts/mpq8092.dtsi b/arch/arm/boot/dts/mpq8092.dtsi
index 252b9f5..470d540 100644
--- a/arch/arm/boot/dts/mpq8092.dtsi
+++ b/arch/arm/boot/dts/mpq8092.dtsi
@@ -228,6 +228,47 @@
<0x1b80009e>, /* LPG_CHAN_8 */
<0x1bc0009f>; /* LPG_PWM */
};
+
+ sdcc1: qcom,sdcc@f9824000 {
+ cell-index = <1>; /* SDC1 eMMC slot */
+ compatible = "qcom,msm-sdcc";
+ reg = <0xf9824000 0x800>;
+ reg-names = "core_mem";
+ interrupts = <0 123 0>;
+ interrupt-names = "core_irq";
+
+ qcom,sdcc-pad-pull-on = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
+ qcom,sdcc-pad-pull-off = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
+ qcom,sdcc-pad-drv-on = <0x7 0x4 0x4>; /* 16mA, 10mA, 10mA */
+ qcom,sdcc-pad-drv-off = <0x0 0x0 0x0>; /* 2mA, 2mA, 2mA */
+
+ qcom,sdcc-clk-rates = <400000 25000000 50000000 100000000 200000000>;
+ qcom,sdcc-sup-voltages = <2950 2950>;
+ qcom,sdcc-bus-width = <8>;
+ qcom,sdcc-nonremovable;
+ qcom,sdcc-bus-speed-mode = "HS200_1p8v", "DDR_1p8v";
+ };
+
+ sdcc2: qcom,sdcc@f98a4000 {
+ cell-index = <2>; /* SDC2 SD card slot */
+ compatible = "qcom,msm-sdcc";
+ reg = <0xf98a4000 0x800>;
+ reg-names = "core_mem";
+ interrupts = <0 125 0>;
+ interrupt-names = "core_irq";
+
+ qcom,sdcc-pad-pull-on = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
+ qcom,sdcc-pad-pull-off = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
+ qcom,sdcc-pad-drv-on = <0x7 0x4 0x4>; /* 16mA, 10mA, 10mA */
+ qcom,sdcc-pad-drv-off = <0x0 0x0 0x0>; /* 2mA, 2mA, 2mA */
+
+ 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>;
+ };
};
/include/ "msm-pm8644.dtsi"
diff --git a/arch/arm/boot/dts/msm8974-liquid.dts b/arch/arm/boot/dts/msm8974-liquid.dts
index 2eacb46..d1a6148 100644
--- a/arch/arm/boot/dts/msm8974-liquid.dts
+++ b/arch/arm/boot/dts/msm8974-liquid.dts
@@ -27,6 +27,13 @@
status = "ok";
};
+ i2c@f9967000 {
+ battery@b {
+ compatible = "ti,bq28400-battery";
+ reg = <0xb>;
+ };
+ };
+
gpio_keys {
compatible = "gpio-keys";
input-name = "gpio-keys";
@@ -314,6 +321,13 @@
};
gpio@e300 { /* GPIO 36 */
+ qcom,mode = <1>; /* QPNP_PIN_MODE_DIG_OUT */
+ qcom,output-type = <0>; /* QPNP_PIN_OUT_BUF_CMOS */
+ qcom,pull = <5>; /* QPNP_PIN_PULL_NO */
+ qcom,vin-sel = <2>; /* QPNP_PIN_VIN2 */
+ qcom,out-strength = <3>; /* QPNP_PIN_OUT_STRENGTH_HIGH */
+ qcom,src-select = <3>; /* QPNP_PIN_SEL_FUNC_2 */
+ qcom,master-en = <1>;
};
};
diff --git a/arch/arm/boot/dts/msm8974-mdss.dtsi b/arch/arm/boot/dts/msm8974-mdss.dtsi
index e791348..a51a38d 100644
--- a/arch/arm/boot/dts/msm8974-mdss.dtsi
+++ b/arch/arm/boot/dts/msm8974-mdss.dtsi
@@ -68,6 +68,9 @@
reg-names = "edp_base", "mmss_cc_base";
vdda-supply = <&pm8941_l12>;
gpio-panel-en = <&msmgpio 58 0>;
+ gpio-panel-pwm = <&pm8941_gpios 36 0>;
+ qcom,panel-lpg-channel = <7>; /* LPG Channel 8 */
+ qcom,panel-pwm-period = <53>;
status = "disable";
};
};
diff --git a/arch/arm/boot/dts/msm8974-mtp.dts b/arch/arm/boot/dts/msm8974-mtp.dts
index 460caf2..c5fabab 100644
--- a/arch/arm/boot/dts/msm8974-mtp.dts
+++ b/arch/arm/boot/dts/msm8974-mtp.dts
@@ -177,6 +177,10 @@
qcom,hsusb-otg-otg-control = <2>;
};
+&usb3 {
+ qcom,dwc-usb3-msm-otg-capability;
+};
+
&pm8941_chg {
status = "ok";
@@ -403,3 +407,9 @@
mpp@a300 { /* MPP 4 */
};
};
+
+&slim_msm {
+ taiko_codec {
+ qcom,cdc-micbias2-ext-cap;
+ };
+};
diff --git a/arch/arm/boot/dts/msm8974.dtsi b/arch/arm/boot/dts/msm8974.dtsi
index 2625e7a..b62bc87 100644
--- a/arch/arm/boot/dts/msm8974.dtsi
+++ b/arch/arm/boot/dts/msm8974.dtsi
@@ -261,9 +261,26 @@
<&msmgpio 54 0>, /* MISO */
<&msmgpio 53 0>; /* MOSI */
cs-gpios = <&msmgpio 55 0>;
+
+ epm-adc@0 {
+ compatible = "cy,epm-adc-cy8c5568lti-114";
+ reg = <0>;
+ interrupt-parent = <&msmgpio>;
+ spi-max-frequency = <960000>;
+ qcom,channels = <31>;
+ qcom,gain = <100 100 100 50 100 100 1 100 1 50
+ 1 100 1 100 50 50 50 50 50 50
+ 100 50 100 50 50 50 50 50 50 50
+ 50>;
+ qcom,rsense = <2 2 2 200 20 2 1 2 1 30
+ 1 10 1 30 50 30 500 30 100 30
+ 100 500 20 200 1000 20 1000 1000 70 200
+ 50>;
+ qcom,channel-type = <0x2a40>;
+ };
};
- slim@fe12f000 {
+ slim_msm: slim@fe12f000 {
cell-index = <1>;
compatible = "qcom,slim-msm";
reg = <0xfe12f000 0x35000>,
@@ -340,10 +357,10 @@
"MIC BIAS1 Internal1", "Handset Mic",
"AMIC2", "MIC BIAS2 External",
"MIC BIAS2 External", "Headset Mic",
- "AMIC3", "MIC BIAS3 Internal1",
- "MIC BIAS3 Internal1", "ANCRight Headset Mic",
- "AMIC4", "MIC BIAS1 Internal2",
- "MIC BIAS1 Internal2", "ANCLeft Headset Mic",
+ "AMIC3", "MIC BIAS2 External",
+ "MIC BIAS2 External", "ANCRight Headset Mic",
+ "AMIC4", "MIC BIAS2 External",
+ "MIC BIAS2 External", "ANCLeft Headset Mic",
"DMIC1", "MIC BIAS1 External",
"MIC BIAS1 External", "Digital Mic1",
"DMIC2", "MIC BIAS1 External",
@@ -564,10 +581,12 @@
};
};
- i2c@f9967000 {
+ i2c@f9967000 { /* BLSP#11 */
cell-index = <0>;
compatible = "qcom,i2c-qup";
reg = <0Xf9967000 0x1000>;
+ #address-cells = <1>;
+ #size-cells = <0>;
reg-names = "qup_phys_addr";
interrupts = <0 105 0>;
interrupt-names = "qup_err_intr";
@@ -628,17 +647,18 @@
l2_hfpll_b-supply = <&pm8941_l12_ao>;
};
- qcom,ssusb@f9200000 {
+ usb3: qcom,ssusb@f9200000 {
compatible = "qcom,dwc-usb3-msm";
reg = <0xf9200000 0xfc000>,
<0xfd4ab000 0x4>;
- interrupts = <0 131 0 0 179 0>;
- interrupt-names = "irq", "otg_irq";
+ interrupts = <0 131 0>, <0 179 0>, <0 133 0>;
+ interrupt-names = "irq", "otg_irq", "hs_phy_irq";
SSUSB_VDDCX-supply = <&pm8841_s2>;
SSUSB_1p8-supply = <&pm8941_l6>;
HSUSB_VDDCX-supply = <&pm8841_s2>;
HSUSB_1p8-supply = <&pm8941_l6>;
HSUSB_3p3-supply = <&pm8941_l24>;
+ vbus_dwc3-supply = <&pm8941_mvs1>;
qcom,dwc-usb3-msm-dbm-eps = <4>;
qcom,msm-bus,name = "usb3";
@@ -1041,7 +1061,7 @@
qcom,dst-bam-physical-address = <0xf9304000>;
qcom,dst-bam-pipe-index = <2>;
qcom,data-fifo-offset = <0xf0000>;
- qcom,data-fifo-size = <0x4000>;
+ qcom,data-fifo-size = <0x1800>;
qcom,descriptor-fifo-offset = <0xf4000>;
qcom,descriptor-fifo-size = <0x1400>;
};
diff --git a/arch/arm/boot/dts/msm9625.dtsi b/arch/arm/boot/dts/msm9625.dtsi
index e2d1496..2f2518d 100644
--- a/arch/arm/boot/dts/msm9625.dtsi
+++ b/arch/arm/boot/dts/msm9625.dtsi
@@ -234,7 +234,6 @@
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 {
diff --git a/arch/arm/configs/fsm9xxx-perf_defconfig b/arch/arm/configs/fsm9xxx-perf_defconfig
index 93e84e9..1dc853b 100644
--- a/arch/arm/configs/fsm9xxx-perf_defconfig
+++ b/arch/arm/configs/fsm9xxx-perf_defconfig
@@ -11,7 +11,6 @@
CONFIG_PANIC_TIMEOUT=5
CONFIG_ASHMEM=y
CONFIG_EMBEDDED=y
-# CONFIG_PERF_EVENTS is not set
CONFIG_SLAB=y
CONFIG_PROFILING=y
CONFIG_OPROFILE=m
@@ -84,6 +83,7 @@
CONFIG_IPV6_MROUTE=y
CONFIG_IPV6_PIMSM_V2=y
# CONFIG_NET_ACTIVITY_STATS is not set
+CONFIG_IP_SCTP=y
CONFIG_RFKILL=y
CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
CONFIG_MTD=y
diff --git a/arch/arm/configs/fsm9xxx_defconfig b/arch/arm/configs/fsm9xxx_defconfig
index c45063f..203d3b7 100644
--- a/arch/arm/configs/fsm9xxx_defconfig
+++ b/arch/arm/configs/fsm9xxx_defconfig
@@ -12,7 +12,6 @@
CONFIG_KALLSYMS_ALL=y
CONFIG_ASHMEM=y
CONFIG_EMBEDDED=y
-# CONFIG_PERF_EVENTS is not set
CONFIG_SLAB=y
CONFIG_PROFILING=y
CONFIG_OPROFILE=m
@@ -83,6 +82,7 @@
CONFIG_IPV6_MROUTE=y
CONFIG_IPV6_PIMSM_V2=y
# CONFIG_NET_ACTIVITY_STATS is not set
+CONFIG_IP_SCTP=y
CONFIG_RFKILL=y
CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
CONFIG_MTD=y
diff --git a/arch/arm/configs/msm8910_defconfig b/arch/arm/configs/msm8910_defconfig
index a9dadee..e4dd4fb 100644
--- a/arch/arm/configs/msm8910_defconfig
+++ b/arch/arm/configs/msm8910_defconfig
@@ -10,6 +10,7 @@
CONFIG_CGROUP_CPUACCT=y
CONFIG_RESOURCE_COUNTERS=y
CONFIG_CGROUP_SCHED=y
+CONFIG_VFP=y
# CONFIG_FAIR_GROUP_SCHED is not set
CONFIG_RT_GROUP_SCHED=y
CONFIG_NAMESPACES=y
@@ -40,6 +41,7 @@
CONFIG_NO_HZ=y
CONFIG_HIGH_RES_TIMERS=y
CONFIG_ARM_ARCH_TIMER=y
+CONFIG_HOTPLUG_CPU=y
CONFIG_PREEMPT=y
CONFIG_AEABI=y
CONFIG_HIGHMEM=y
@@ -75,6 +77,16 @@
CONFIG_USB_GADGET_DEBUG_FS=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_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 53e6260..ec00b68 100644
--- a/arch/arm/configs/msm8960-perf_defconfig
+++ b/arch/arm/configs/msm8960-perf_defconfig
@@ -367,6 +367,7 @@
CONFIG_S5K3L1YX=y
CONFIG_IMX091=y
CONFIG_MSM_WFD=y
+CONFIG_IMX135=y
CONFIG_RADIO_IRIS=y
CONFIG_RADIO_IRIS_TRANSPORT=m
# CONFIG_DVB_FE_CUSTOMISE is not set
diff --git a/arch/arm/configs/msm8960_defconfig b/arch/arm/configs/msm8960_defconfig
index 5770859..33f7987 100644
--- a/arch/arm/configs/msm8960_defconfig
+++ b/arch/arm/configs/msm8960_defconfig
@@ -371,6 +371,7 @@
CONFIG_S5K3L1YX=y
CONFIG_IMX091=y
CONFIG_MSM_WFD=y
+CONFIG_IMX135=y
CONFIG_RADIO_IRIS=y
CONFIG_RADIO_IRIS_TRANSPORT=m
# CONFIG_DVB_FE_CUSTOMISE is not set
diff --git a/arch/arm/configs/msm8974_defconfig b/arch/arm/configs/msm8974_defconfig
index d8d2eae..c49ad93 100644
--- a/arch/arm/configs/msm8974_defconfig
+++ b/arch/arm/configs/msm8974_defconfig
@@ -275,6 +275,7 @@
# CONFIG_BATTERY_MSM is not set
CONFIG_QPNP_CHARGER=y
CONFIG_QPNP_BMS=y
+CONFIG_BATTERY_BQ28400=y
CONFIG_SENSORS_QPNP_ADC_VOLTAGE=y
CONFIG_SENSORS_QPNP_ADC_CURRENT=y
CONFIG_THERMAL=y
diff --git a/arch/arm/configs/msm9625_defconfig b/arch/arm/configs/msm9625_defconfig
index 067b52c..e1d4ca0 100644
--- a/arch/arm/configs/msm9625_defconfig
+++ b/arch/arm/configs/msm9625_defconfig
@@ -37,6 +37,7 @@
# CONFIG_MSM_PROC_COMM is not set
CONFIG_MSM_SMD=y
CONFIG_MSM_SMD_PKG4=y
+CONFIG_MSM_BAM_DMUX=y
CONFIG_MSM_IPC_ROUTER=y
CONFIG_MSM_IPC_ROUTER_SMD_XPRT=y
CONFIG_MSM_RPM_REGULATOR_SMD=y
@@ -51,6 +52,11 @@
CONFIG_VMALLOC_RESERVE=0x19000000
CONFIG_USE_OF=y
CONFIG_CPU_IDLE=y
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_GOV_POWERSAVE=y
+CONFIG_CPU_FREQ_GOV_USERSPACE=y
+CONFIG_CPU_FREQ_GOV_ONDEMAND=y
+CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y
CONFIG_VFP=y
CONFIG_NEON=y
# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
@@ -60,6 +66,10 @@
CONFIG_UNIX=y
CONFIG_INET=y
CONFIG_IPV6=y
+CONFIG_NET_SCHED=y
+CONFIG_NET_SCH_HTB=y
+CONFIG_NET_SCH_PRIO=y
+CONFIG_NET_CLS_FW=y
# CONFIG_WIRELESS is not set
CONFIG_MTD=y
CONFIG_MTD_CMDLINE_PARTS=y
@@ -75,9 +85,17 @@
# CONFIG_NET_VENDOR_CIRRUS is not set
# CONFIG_NET_VENDOR_FARADAY is not set
# CONFIG_NET_VENDOR_INTEL is not set
+CONFIG_WIRELESS=y
+CONFIG_CFG80211=m
+CONFIG_NL80211_TESTMODE=y
+CONFIG_ATH_COMMON=m
+CONFIG_ATH6KL=m
+CONFIG_ATH6KL_SDIO=m
+CONFIG_ATH6KL_DEBUG=y
CONFIG_KS8851=y
# CONFIG_NET_VENDOR_MICROCHIP is not set
# CONFIG_MSM_RMNET is not set
+CONFIG_MSM_RMNET_BAM=y
# CONFIG_NET_VENDOR_NATSEMI is not set
# CONFIG_NET_VENDOR_SEEQ is not set
# CONFIG_NET_VENDOR_SMSC is not set
@@ -109,6 +127,7 @@
CONFIG_GPIO_SYSFS=y
CONFIG_GPIO_QPNP_PIN=y
CONFIG_GPIO_QPNP_PIN_DEBUG=y
+CONFIG_POWER_SUPPLY=y
CONFIG_HWMON=y
CONFIG_SENSORS_QPNP_ADC_VOLTAGE=y
CONFIG_REGULATOR=y
diff --git a/arch/arm/include/asm/outercache.h b/arch/arm/include/asm/outercache.h
index 53426c6..12f71a1 100644
--- a/arch/arm/include/asm/outercache.h
+++ b/arch/arm/include/asm/outercache.h
@@ -92,6 +92,7 @@
static inline void outer_flush_all(void) { }
static inline void outer_inv_all(void) { }
static inline void outer_disable(void) { }
+static inline void outer_resume(void) { }
#endif
diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig
index 3f36017..b258707 100644
--- a/arch/arm/mach-msm/Kconfig
+++ b/arch/arm/mach-msm/Kconfig
@@ -2004,7 +2004,7 @@
config MSM_PIL_TZAPPS
tristate "TZApps Boot Support"
- depends on MSM_PIL
+ depends on MSM_PIL && MSM_SUBSYSTEM_RESTART
help
Support for booting and shutting down TZApps.
@@ -2023,13 +2023,13 @@
config MSM_PIL_VIDC
tristate "Video Core Secure Boot Support"
- depends on MSM_PIL
+ depends on MSM_PIL && MSM_SUBSYSTEM_RESTART
help
Support for authenticating the video core image.
config MSM_PIL_VENUS
tristate "VENUS (Video) Boot Support"
- depends on MSM_PIL
+ depends on MSM_PIL && MSM_SUBSYSTEM_RESTART
help
Support for booting and shutting down the VENUS processor (Video).
Venus is the Video subsystem processor used for video codecs.
diff --git a/arch/arm/mach-msm/acpuclock-8974.c b/arch/arm/mach-msm/acpuclock-8974.c
index 098f854..61213cf 100644
--- a/arch/arm/mach-msm/acpuclock-8974.c
+++ b/arch/arm/mach-msm/acpuclock-8974.c
@@ -143,7 +143,7 @@
};
static struct acpu_level acpu_freq_tbl[] __initdata = {
- { 1, { 300000, PLL_0, 0, 0 }, L2(0), 950000, 3200000 },
+ { 1, { 300000, PLL_0, 0, 0 }, L2(0), 950000, 100000 },
{ 1, { 384000, HFPLL, 2, 40 }, L2(3), 950000, 3200000 },
{ 1, { 460800, HFPLL, 2, 48 }, L2(3), 950000, 3200000 },
{ 1, { 537600, HFPLL, 1, 28 }, L2(5), 950000, 3200000 },
diff --git a/arch/arm/mach-msm/acpuclock-krait.c b/arch/arm/mach-msm/acpuclock-krait.c
index a386e78..bf57eab 100644
--- a/arch/arm/mach-msm/acpuclock-krait.c
+++ b/arch/arm/mach-msm/acpuclock-krait.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011-2012, 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
@@ -209,7 +209,8 @@
}
/* Set the CPU or L2 clock speed. */
-static void set_speed(struct scalable *sc, const struct core_speed *tgt_s)
+static void set_speed(struct scalable *sc, const struct core_speed *tgt_s,
+ bool skip_regulators)
{
const struct core_speed *strt_s = sc->cur_speed;
@@ -232,10 +233,10 @@
set_pri_clk_src(sc, tgt_s->pri_src_sel);
} else if (strt_s->src == HFPLL && tgt_s->src != HFPLL) {
set_pri_clk_src(sc, tgt_s->pri_src_sel);
- hfpll_disable(sc, false);
+ hfpll_disable(sc, skip_regulators);
} else if (strt_s->src != HFPLL && tgt_s->src == HFPLL) {
hfpll_set_rate(sc, tgt_s);
- hfpll_enable(sc, false);
+ hfpll_enable(sc, skip_regulators);
set_pri_clk_src(sc, tgt_s->pri_src_sel);
}
@@ -426,6 +427,47 @@
return tgt->vdd_core + (enable_boost ? drv.boost_uv : 0);
}
+static DEFINE_MUTEX(l2_regulator_lock);
+static int l2_vreg_count;
+
+static int enable_l2_regulators(void)
+{
+ int ret = 0;
+
+ mutex_lock(&l2_regulator_lock);
+ if (l2_vreg_count == 0) {
+ ret = enable_rpm_vreg(&drv.scalable[L2].vreg[VREG_HFPLL_A]);
+ if (ret)
+ goto out;
+ ret = enable_rpm_vreg(&drv.scalable[L2].vreg[VREG_HFPLL_B]);
+ if (ret) {
+ disable_rpm_vreg(&drv.scalable[L2].vreg[VREG_HFPLL_A]);
+ goto out;
+ }
+ }
+ l2_vreg_count++;
+out:
+ mutex_unlock(&l2_regulator_lock);
+
+ return ret;
+}
+
+static void disable_l2_regulators(void)
+{
+ mutex_lock(&l2_regulator_lock);
+
+ if (WARN(!l2_vreg_count, "L2 regulator votes are unbalanced!"))
+ goto out;
+
+ if (l2_vreg_count == 1) {
+ disable_rpm_vreg(&drv.scalable[L2].vreg[VREG_HFPLL_B]);
+ disable_rpm_vreg(&drv.scalable[L2].vreg[VREG_HFPLL_A]);
+ }
+ l2_vreg_count--;
+out:
+ mutex_unlock(&l2_regulator_lock);
+}
+
/* Set the CPU's clock rate and adjust the L2 rate, voltage and BW requests. */
static int acpuclk_krait_set_rate(int cpu, unsigned long rate,
enum setrate_reason reason)
@@ -433,8 +475,9 @@
const struct core_speed *strt_acpu_s, *tgt_acpu_s;
const struct acpu_level *tgt;
int tgt_l2_l;
+ enum src_id prev_l2_src = NUM_SRC_ID;
struct vdd_data vdd_data;
- unsigned long flags;
+ bool skip_regulators;
int rc = 0;
if (cpu > num_possible_cpus())
@@ -478,13 +521,31 @@
rc = increase_vdd(cpu, &vdd_data, reason);
if (rc)
goto out;
+
+ prev_l2_src =
+ drv.l2_freq_tbl[drv.scalable[cpu].l2_vote].speed.src;
+ /* Vote for the L2 regulators here if necessary. */
+ if (drv.l2_freq_tbl[tgt->l2_level].speed.src == HFPLL) {
+ rc = enable_l2_regulators();
+ if (rc)
+ goto out;
+ }
}
dev_dbg(drv.dev, "Switching from ACPU%d rate %lu KHz -> %lu KHz\n",
cpu, strt_acpu_s->khz, tgt_acpu_s->khz);
+ /*
+ * If we are setting the rate as part of power collapse or in the resume
+ * path after power collapse, skip the vote for the HFPLL regulators,
+ * which are active-set-only votes that will be removed when apps enters
+ * its sleep set. This is needed to avoid voting for regulators with
+ * sleeping APIs from an atomic context.
+ */
+ skip_regulators = (reason == SETRATE_PC);
+
/* Set the new CPU speed. */
- set_speed(&drv.scalable[cpu], tgt_acpu_s);
+ set_speed(&drv.scalable[cpu], tgt_acpu_s, skip_regulators);
/*
* Update the L2 vote and apply the rate change. A spinlock is
@@ -493,15 +554,23 @@
* called from an atomic context and the driver_lock mutex is not
* acquired.
*/
- spin_lock_irqsave(&l2_lock, flags);
+ spin_lock(&l2_lock);
tgt_l2_l = compute_l2_level(&drv.scalable[cpu], tgt->l2_level);
- set_speed(&drv.scalable[L2], &drv.l2_freq_tbl[tgt_l2_l].speed);
- spin_unlock_irqrestore(&l2_lock, flags);
+ set_speed(&drv.scalable[L2],
+ &drv.l2_freq_tbl[tgt_l2_l].speed, true);
+ spin_unlock(&l2_lock);
/* Nothing else to do for power collapse or SWFI. */
if (reason == SETRATE_PC || reason == SETRATE_SWFI)
goto out;
+ /*
+ * Remove the vote for the L2 HFPLL regulators only if the L2
+ * was already on an HFPLL source.
+ */
+ if (prev_l2_src == HFPLL)
+ disable_l2_regulators();
+
/* Update bus bandwith request. */
set_bus_bw(drv.l2_freq_tbl[tgt_l2_l].bw_level);
@@ -663,6 +732,14 @@
goto err_core_conf;
}
+ /*
+ * Increment the L2 HFPLL regulator refcount if _this_ CPU's frequency
+ * requires a corresponding target L2 frequency that needs the L2 to
+ * run off of an HFPLL.
+ */
+ if (drv.l2_freq_tbl[acpu_level->l2_level].speed.src == HFPLL)
+ l2_vreg_count++;
+
return 0;
err_core_conf:
diff --git a/arch/arm/mach-msm/board-8064-gpu.c b/arch/arm/mach-msm/board-8064-gpu.c
index fad7092..f35ae6b 100644
--- a/arch/arm/mach-msm/board-8064-gpu.c
+++ b/arch/arm/mach-msm/board-8064-gpu.c
@@ -281,8 +281,11 @@
if (SOCINFO_VERSION_MAJOR(version) == 2) {
kgsl_3d0_pdata.chipid = ADRENO_CHIPID(3, 2, 0, 2);
} else {
+ /* The bootloader has started returning 1.2 for chips that
+ are either 1.1 or 1.2. To handle that and default any
+ future revisions to this path, check for minor version >=1 */
if ((SOCINFO_VERSION_MAJOR(version) == 1) &&
- (SOCINFO_VERSION_MINOR(version) == 1))
+ (SOCINFO_VERSION_MINOR(version) >= 1))
kgsl_3d0_pdata.chipid = ADRENO_CHIPID(3, 2, 0, 1);
else
kgsl_3d0_pdata.chipid = ADRENO_CHIPID(3, 2, 0, 0);
diff --git a/arch/arm/mach-msm/board-8064-pmic.c b/arch/arm/mach-msm/board-8064-pmic.c
index e64a672..c0e325a 100644
--- a/arch/arm/mach-msm/board-8064-pmic.c
+++ b/arch/arm/mach-msm/board-8064-pmic.c
@@ -512,4 +512,7 @@
} else if (machine_is_apq8064_cdp()) {
apq8064_pm8921_chg_pdata.has_dc_supply = true;
}
+
+ if (!machine_is_apq8064_mtp() && !machine_is_apq8064_liquid())
+ apq8064_pm8921_chg_pdata.battery_less_hardware = 1;
}
diff --git a/arch/arm/mach-msm/board-8064-storage.c b/arch/arm/mach-msm/board-8064-storage.c
index 379d7ae..28f7f63 100644
--- a/arch/arm/mach-msm/board-8064-storage.c
+++ b/arch/arm/mach-msm/board-8064-storage.c
@@ -383,6 +383,14 @@
apq8064_sdc3_pdata->pin_data->pad_data->\
drv->on[i].val = GPIO_CFG_10MA;
}
+ if (machine_is_mpq8064_hrd() || machine_is_mpq8064_dtv()) {
+ apq8064_sdc3_pdata->pin_data->pad_data->\
+ drv->on[0].val = GPIO_CFG_16MA;
+ apq8064_sdc3_pdata->pin_data->pad_data->\
+ drv->on[1].val = GPIO_CFG_10MA;
+ apq8064_sdc3_pdata->pin_data->pad_data->\
+ drv->on[2].val = GPIO_CFG_10MA;
+ }
apq8064_add_sdcc(3, apq8064_sdc3_pdata);
}
diff --git a/arch/arm/mach-msm/board-8092.c b/arch/arm/mach-msm/board-8092.c
index bd1762d..3e31f68 100644
--- a/arch/arm/mach-msm/board-8092.c
+++ b/arch/arm/mach-msm/board-8092.c
@@ -18,7 +18,6 @@
#include <linux/of_fdt.h>
#include <linux/of_irq.h>
#include <asm/hardware/gic.h>
-#include <asm/arch_timer.h>
#include <asm/mach/arch.h>
#include <asm/mach/time.h>
#include <mach/socinfo.h>
@@ -30,15 +29,9 @@
#include <linux/irq.h>
#include <linux/irqdomain.h>
+#include "board-dt.h"
#include "clock.h"
-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, },
- {}
-};
-
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),
@@ -81,26 +74,7 @@
msm_reserve();
}
-void __init mpq8092_init_irq(void)
-{
- of_irq_init(irq_match);
-}
-
-static void __init mpq8092_dt_timer_init(void)
-{
- arch_timer_of_register();
-}
-
-static struct sys_timer mpq8092_dt_timer = {
- .init = mpq8092_dt_timer_init
-};
-
-static void __init mpq8092_dt_init_irq(void)
-{
- mpq8092_init_irq();
-}
-
-static void __init mpq8092_dt_map_io(void)
+static void __init mpq8092_map_io(void)
{
msm_map_mpq8092_io();
if (socinfo_init() < 0)
@@ -113,21 +87,19 @@
"msm_serial_hsl.0", NULL),
OF_DEV_AUXDATA("qcom,spmi-pmic-arb", 0xFC4C0000, \
"spmi-pmic-arb.0", NULL),
+ OF_DEV_AUXDATA("qcom,msm-sdcc", 0xF9824000, \
+ "msm_sdcc.1", NULL),
+ OF_DEV_AUXDATA("qcom,msm-sdcc", 0xF98A4000, \
+ "msm_sdcc.2", NULL),
{}
};
-static void __init mpq8092_init(struct of_dev_auxdata **adata)
+static void __init mpq8092_init(void)
{
+ struct of_dev_auxdata *adata = mpq8092_auxdata_lookup;
+
mpq8092_init_gpiomux();
- *adata = mpq8092_auxdata_lookup;
msm_clock_init(&mpq8092_clock_init_data);
-}
-
-static void __init mpq8092_dt_init(void)
-{
- struct of_dev_auxdata *adata = NULL;
-
- mpq8092_init(&adata);
of_platform_populate(NULL, of_default_bus_match_table, adata, NULL);
}
@@ -136,12 +108,12 @@
NULL
};
-DT_MACHINE_START(MSM_DT, "Qualcomm MSM (Flattened Device Tree)")
- .map_io = mpq8092_dt_map_io,
- .init_irq = mpq8092_dt_init_irq,
- .init_machine = mpq8092_dt_init,
+DT_MACHINE_START(MSM8092_DT, "Qualcomm MSM 8092 (Flattened Device Tree)")
+ .map_io = mpq8092_map_io,
+ .init_irq = msm_dt_init_irq_nompm,
+ .init_machine = mpq8092_init,
.handle_irq = gic_handle_irq,
- .timer = &mpq8092_dt_timer,
+ .timer = &msm_dt_timer,
.dt_compat = mpq8092_dt_match,
.reserve = mpq8092_dt_reserve,
.init_very_early = mpq8092_early_memory,
diff --git a/arch/arm/mach-msm/board-8910.c b/arch/arm/mach-msm/board-8910.c
index 1c92494f..7039879 100644
--- a/arch/arm/mach-msm/board-8910.c
+++ b/arch/arm/mach-msm/board-8910.c
@@ -37,6 +37,7 @@
#include <mach/socinfo.h>
#include <mach/board.h>
#include <mach/clk-provider.h>
+#include "board-dt.h"
#include "clock.h"
static struct clk_lookup msm_clocks_dummy[] = {
@@ -44,6 +45,9 @@
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),
+ CLK_DUMMY("iface_clk", NULL, "f9824000.qcom,sdcc", OFF),
+ CLK_DUMMY("core_clk", NULL, "f9824000.qcom,sdcc", OFF),
+ CLK_DUMMY("bus_clk", NULL, "f9824000.qcom,sdcc", OFF),
};
struct clock_init_data msm_dummy_clock_init_data __initdata = {
@@ -51,26 +55,6 @@
.size = ARRAY_SIZE(msm_clocks_dummy),
};
-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, },
- {},
-};
-
-static void __init msm8910_dt_timer_init(void)
-{
- arch_timer_of_register();
-}
-
-static struct sys_timer msm8910_dt_timer = {
- .init = msm8910_dt_timer_init
-};
-
-void __init msm8910_init_irq(void)
-{
- of_irq_init(irq_match);
-}
-
void __init msm8910_init(void)
{
msm_clock_init(&msm_dummy_clock_init_data);
@@ -88,9 +72,9 @@
DT_MACHINE_START(MSM8910_DT, "Qualcomm MSM 8910 (Flattened Device Tree)")
.map_io = msm_map_msm8910_io,
- .init_irq = msm8910_init_irq,
+ .init_irq = msm_dt_init_irq_nompm,
.init_machine = msm8910_init,
.handle_irq = gic_handle_irq,
- .timer = &msm8910_dt_timer,
+ .timer = &msm_dt_timer,
.dt_compat = msm8910_dt_match,
MACHINE_END
diff --git a/arch/arm/mach-msm/board-8930-gpu.c b/arch/arm/mach-msm/board-8930-gpu.c
index 99a5a34..578c665 100644
--- a/arch/arm/mach-msm/board-8930-gpu.c
+++ b/arch/arm/mach-msm/board-8930-gpu.c
@@ -163,10 +163,18 @@
{
unsigned int version = socinfo_get_version();
+ /* Set the turbo speed for the AA and AB respectively */
+
if (cpu_is_msm8930aa())
kgsl_3d0_pdata.pwrlevel[0].gpu_freq = 450000000;
+ else if (cpu_is_msm8930ab())
+ kgsl_3d0_pdata.pwrlevel[0].gpu_freq = 500000000;
- if ((SOCINFO_VERSION_MAJOR(version) == 1) &&
+ /* Set up the chip ID based on the SoC version */
+
+ if (cpu_is_msm8930ab())
+ kgsl_3d0_pdata.chipid = ADRENO_CHIPID(3, 0, 5, 3);
+ else if ((SOCINFO_VERSION_MAJOR(version) == 1) &&
(SOCINFO_VERSION_MINOR(version) == 2))
kgsl_3d0_pdata.chipid = ADRENO_CHIPID(3, 0, 5, 2);
else
diff --git a/arch/arm/mach-msm/board-8930-pmic.c b/arch/arm/mach-msm/board-8930-pmic.c
index 8687b2a..0c7666b 100644
--- a/arch/arm/mach-msm/board-8930-pmic.c
+++ b/arch/arm/mach-msm/board-8930-pmic.c
@@ -596,4 +596,7 @@
else if (machine_is_msm8930_cdp())
pm8921_chg_pdata.has_dc_supply = true;
}
+
+ if (!machine_is_msm8930_mtp())
+ pm8921_chg_pdata.battery_less_hardware = 1;
}
diff --git a/arch/arm/mach-msm/board-8930-regulator-pm8038.c b/arch/arm/mach-msm/board-8930-regulator-pm8038.c
index 16a82b4..727c4c6 100644
--- a/arch/arm/mach-msm/board-8930-regulator-pm8038.c
+++ b/arch/arm/mach-msm/board-8930-regulator-pm8038.c
@@ -97,6 +97,8 @@
REGULATOR_SUPPLY("CDC_VDDA_RX", "sitar1p1-slim"),
REGULATOR_SUPPLY("vddp", "0-0048"),
REGULATOR_SUPPLY("mhl_iovcc18", "0-0039"),
+ REGULATOR_SUPPLY("vdd-io", "spi0.0"),
+ REGULATOR_SUPPLY("vdd-phy", "spi0.0"),
};
VREG_CONSUMERS(L12) = {
REGULATOR_SUPPLY("8038_l12", NULL),
diff --git a/arch/arm/mach-msm/board-8930-regulator-pm8917.c b/arch/arm/mach-msm/board-8930-regulator-pm8917.c
index 8898b50..33e38ab 100644
--- a/arch/arm/mach-msm/board-8930-regulator-pm8917.c
+++ b/arch/arm/mach-msm/board-8930-regulator-pm8917.c
@@ -196,6 +196,8 @@
REGULATOR_SUPPLY("mhl_iovcc18", "0-0039"),
REGULATOR_SUPPLY("CDC_VDD_CP", "sitar-slim"),
REGULATOR_SUPPLY("CDC_VDD_CP", "sitar1p1-slim"),
+ REGULATOR_SUPPLY("vdd-io", "spi0.0"),
+ REGULATOR_SUPPLY("vdd-phy", "spi0.0"),
};
VREG_CONSUMERS(S5) = {
REGULATOR_SUPPLY("8917_s5", NULL),
diff --git a/arch/arm/mach-msm/board-8960-pmic.c b/arch/arm/mach-msm/board-8960-pmic.c
index d8a260b..9efedb1 100644
--- a/arch/arm/mach-msm/board-8960-pmic.c
+++ b/arch/arm/mach-msm/board-8960-pmic.c
@@ -612,4 +612,8 @@
if (machine_is_msm8960_fluid())
pm8921_bms_pdata.rconn_mohm = 20;
+
+ if (!machine_is_msm8960_fluid() && !machine_is_msm8960_liquid()
+ && !machine_is_msm8960_fluid())
+ pm8921_chg_pdata.battery_less_hardware = 1;
}
diff --git a/arch/arm/mach-msm/board-8960.c b/arch/arm/mach-msm/board-8960.c
index 4a78e31..e919d78 100644
--- a/arch/arm/mach-msm/board-8960.c
+++ b/arch/arm/mach-msm/board-8960.c
@@ -3216,6 +3216,17 @@
msm_tsens_early_init(&msm_tsens_pdata);
}
+static void __init msm8960_reset_spm_avs(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(msm_spm_data); i++) {
+ struct msm_spm_platform_data *pdata = &msm_spm_data[i];
+ pdata->reg_init_values[MSM_SPM_REG_SAW2_AVS_CTL] = 0;
+ pdata->reg_init_values[MSM_SPM_REG_SAW2_AVS_HYSTERESIS] = 0;
+ }
+}
+
static void __init msm8960_cdp_init(void)
{
if (meminfo_init(SYS_MEMORY, SZ_256M) < 0)
@@ -3269,8 +3280,12 @@
msm_isa1200_board_info[0].platform_data = &isa1200_1_pdata;
msm8960_i2c_init();
msm8960_gfx_init();
+
+ if (cpu_is_msm8960ab())
+ msm8960_reset_spm_avs();
msm_spm_init(msm_spm_data, ARRAY_SIZE(msm_spm_data));
msm_spm_l2_init(msm_spm_l2_data);
+
msm8960_init_buses();
platform_add_devices(msm8960_footswitch, msm8960_num_footswitch);
if (machine_is_msm8960_liquid())
diff --git a/arch/arm/mach-msm/board-8974.c b/arch/arm/mach-msm/board-8974.c
index 7480437..19fb222 100644
--- a/arch/arm/mach-msm/board-8974.c
+++ b/arch/arm/mach-msm/board-8974.c
@@ -198,7 +198,7 @@
.edge = SMD_APPS_RPM,
.smd_int.irq_name = "rpm_smd_in",
- .smd_int.flags = IRQF_TRIGGER_RISING,
+ .smd_int.flags = IRQF_TRIGGER_RISING | IRQF_NO_SUSPEND,
.smd_int.irq_id = -1,
.smd_int.device_name = "smd_dev",
.smd_int.dev_id = 0,
diff --git a/arch/arm/mach-msm/board-9625.c b/arch/arm/mach-msm/board-9625.c
index 49f2561..5c7eebe 100644
--- a/arch/arm/mach-msm/board-9625.c
+++ b/arch/arm/mach-msm/board-9625.c
@@ -21,11 +21,8 @@
#include <linux/of_irq.h>
#include <linux/memory.h>
#include <asm/mach/map.h>
-#include <asm/hardware/cache-l2x0.h>
#include <asm/hardware/gic.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>
@@ -35,19 +32,15 @@
#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 "board-dt.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] = {
@@ -76,10 +69,6 @@
.paddr_to_memtype = msm9625_paddr_to_memtype,
};
-#define L2CC_AUX_CTRL ((0x1 << L2X0_AUX_CTRL_SHARE_OVERRIDE_SHIFT) | \
- (0x2 << L2X0_AUX_CTRL_WAY_SIZE_SHIFT) | \
- (0x1 << L2X0_AUX_CTRL_EVNT_MON_BUS_EN_SHIFT))
-
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),
@@ -103,13 +92,6 @@
.size = ARRAY_SIZE(msm_clocks_dummy),
};
-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, },
- {}
-};
-
static const char *msm9625_dt_match[] __initconst = {
"qcom,msm9625",
NULL
@@ -129,34 +111,6 @@
{}
};
-static struct of_device_id mpm_match[] __initdata = {
- {.compatible = "qcom,mpm-v2", },
- {},
-};
-
-void __init msm9625_init_irq(void)
-{
- 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)
-{
- arch_timer_of_register();
-}
-
-static struct sys_timer msm_dt_timer = {
- .init = msm_dt_timer_init
-};
-
static void __init msm9625_early_memory(void)
{
reserve_info = &msm9625_reserve_info;
@@ -331,9 +285,9 @@
msm9625_add_drivers();
}
-DT_MACHINE_START(MSM_DT, "Qualcomm MSM (Flattened Device Tree)")
+DT_MACHINE_START(MSM9625_DT, "Qualcomm MSM 9625 (Flattened Device Tree)")
.map_io = msm_map_msm9625_io,
- .init_irq = msm9625_init_irq,
+ .init_irq = msm_dt_init_irq_l2x0,
.init_machine = msm9625_init,
.handle_irq = gic_handle_irq,
.timer = &msm_dt_timer,
diff --git a/arch/arm/mach-msm/board-dt.c b/arch/arm/mach-msm/board-dt.c
index 3654de8..74b0d0d 100644
--- a/arch/arm/mach-msm/board-dt.c
+++ b/arch/arm/mach-msm/board-dt.c
@@ -17,12 +17,18 @@
#include <linux/mfd/wcd9xxx/core.h>
#include <asm/arch_timer.h>
#include <asm/mach/time.h>
+#include <asm/hardware/cache-l2x0.h>
#include <asm/hardware/gic.h>
#include <mach/mpm.h>
#include <mach/qpnp-int.h>
+#include <mach/scm.h>
#include "board-dt.h"
+#define SCM_SVC_L2CC_PL310 16
+#define L2CC_PL310_CTRL_ID 1
+#define L2CC_PL310_ON 1
+
static void __init msm_dt_timer_init(void)
{
arch_timer_of_register();
@@ -62,3 +68,10 @@
{
of_irq_init(irq_match);
}
+
+void __init msm_dt_init_irq_l2x0(void)
+{
+ scm_call_atomic1(SCM_SVC_L2CC_PL310, L2CC_PL310_CTRL_ID, L2CC_PL310_ON);
+ l2x0_of_init(0, ~0UL);
+ msm_dt_init_irq();
+}
diff --git a/arch/arm/mach-msm/board-dt.h b/arch/arm/mach-msm/board-dt.h
index 16a6135..cc3e92c 100644
--- a/arch/arm/mach-msm/board-dt.h
+++ b/arch/arm/mach-msm/board-dt.h
@@ -13,3 +13,4 @@
extern struct sys_timer msm_dt_timer;
void __init msm_dt_init_irq(void);
void __init msm_dt_init_irq_nompm(void);
+void __init msm_dt_init_irq_l2x0(void);
diff --git a/arch/arm/mach-msm/clock-7x30.c b/arch/arm/mach-msm/clock-7x30.c
index e42fe65..e1390db 100644
--- a/arch/arm/mach-msm/clock-7x30.c
+++ b/arch/arm/mach-msm/clock-7x30.c
@@ -160,7 +160,8 @@
VDD_DIG_NONE,
VDD_DIG_LOW,
VDD_DIG_NOMINAL,
- VDD_DIG_HIGH
+ VDD_DIG_HIGH,
+ VDD_DIG_NUM
};
static int set_vdd_dig(struct clk_vdd_class *vdd_class, int level)
@@ -184,15 +185,21 @@
return rc;
}
-static DEFINE_VDD_CLASS(vdd_dig, set_vdd_dig);
+static DEFINE_VDD_CLASS(vdd_dig, set_vdd_dig, VDD_DIG_NUM);
#define VDD_DIG_FMAX_MAP1(l1, f1) \
- .vdd_class = &vdd_dig, \
- .fmax[VDD_DIG_##l1] = (f1)
+ .vdd_class = &vdd_dig, \
+ .fmax = (unsigned long[VDD_DIG_NUM]) { \
+ [VDD_DIG_##l1] = (f1), \
+ }, \
+ .num_fmax = VDD_DIG_NUM
#define VDD_DIG_FMAX_MAP2(l1, f1, l2, f2) \
- .vdd_class = &vdd_dig, \
- .fmax[VDD_DIG_##l1] = (f1), \
- .fmax[VDD_DIG_##l2] = (f2)
+ .vdd_class = &vdd_dig, \
+ .fmax = (unsigned long[VDD_DIG_NUM]) { \
+ [VDD_DIG_##l1] = (f1), \
+ [VDD_DIG_##l2] = (f2), \
+ }, \
+ .num_fmax = VDD_DIG_NUM
#define PCOM_XO_DISABLE 0
#define PCOM_XO_ENABLE 1
diff --git a/arch/arm/mach-msm/clock-8960.c b/arch/arm/mach-msm/clock-8960.c
index adf1733..472cb68 100644
--- a/arch/arm/mach-msm/clock-8960.c
+++ b/arch/arm/mach-msm/clock-8960.c
@@ -376,7 +376,8 @@
VDD_DIG_NONE,
VDD_DIG_LOW,
VDD_DIG_NOMINAL,
- VDD_DIG_HIGH
+ VDD_DIG_HIGH,
+ VDD_DIG_NUM
};
static int set_vdd_dig_8960(struct clk_vdd_class *vdd_class, int level)
@@ -391,7 +392,7 @@
vdd_uv[level], 1150000, 1);
}
-static DEFINE_VDD_CLASS(vdd_dig, set_vdd_dig_8960);
+static DEFINE_VDD_CLASS(vdd_dig, set_vdd_dig_8960, VDD_DIG_NUM);
static int rpm_vreg_dig_8930 = RPM_VREG_ID_PM8038_VDD_DIG_CORNER;
static int set_vdd_dig_8930(struct clk_vdd_class *vdd_class, int level)
@@ -409,21 +410,31 @@
}
#define VDD_DIG_FMAX_MAP1(l1, f1) \
- .vdd_class = &vdd_dig, \
- .fmax[VDD_DIG_##l1] = (f1)
+ .vdd_class = &vdd_dig, \
+ .fmax = (unsigned long[VDD_DIG_NUM]) { \
+ [VDD_DIG_##l1] = (f1), \
+ }, \
+ .num_fmax = VDD_DIG_NUM
#define VDD_DIG_FMAX_MAP2(l1, f1, l2, f2) \
- .vdd_class = &vdd_dig, \
- .fmax[VDD_DIG_##l1] = (f1), \
- .fmax[VDD_DIG_##l2] = (f2)
+ .vdd_class = &vdd_dig, \
+ .fmax = (unsigned long[VDD_DIG_NUM]) { \
+ [VDD_DIG_##l1] = (f1), \
+ [VDD_DIG_##l2] = (f2), \
+ }, \
+ .num_fmax = VDD_DIG_NUM
#define VDD_DIG_FMAX_MAP3(l1, f1, l2, f2, l3, f3) \
- .vdd_class = &vdd_dig, \
- .fmax[VDD_DIG_##l1] = (f1), \
- .fmax[VDD_DIG_##l2] = (f2), \
- .fmax[VDD_DIG_##l3] = (f3)
+ .vdd_class = &vdd_dig, \
+ .fmax = (unsigned long[VDD_DIG_NUM]) { \
+ [VDD_DIG_##l1] = (f1), \
+ [VDD_DIG_##l2] = (f2), \
+ [VDD_DIG_##l3] = (f3), \
+ }, \
+ .num_fmax = VDD_DIG_NUM
enum vdd_sr2_hdmi_pll_levels {
VDD_SR2_HDMI_PLL_OFF,
- VDD_SR2_HDMI_PLL_ON
+ VDD_SR2_HDMI_PLL_ON,
+ VDD_SR2_HDMI_PLL_NUM
};
static int set_vdd_sr2_hdmi_pll_8960(struct clk_vdd_class *vdd_class, int level)
@@ -455,7 +466,8 @@
return rc;
}
-static DEFINE_VDD_CLASS(vdd_sr2_hdmi_pll, set_vdd_sr2_hdmi_pll_8960);
+static DEFINE_VDD_CLASS(vdd_sr2_hdmi_pll, set_vdd_sr2_hdmi_pll_8960,
+ VDD_SR2_HDMI_PLL_NUM);
static int sr2_lreg_uv[] = {
[VDD_SR2_HDMI_PLL_OFF] = 0,
@@ -530,7 +542,10 @@
.rate = 1200000000,
.ops = &clk_ops_local_pll,
.vdd_class = &vdd_sr2_hdmi_pll,
- .fmax[VDD_SR2_HDMI_PLL_ON] = ULONG_MAX,
+ .fmax = (unsigned long[VDD_SR2_HDMI_PLL_NUM]) {
+ [VDD_SR2_HDMI_PLL_ON] = ULONG_MAX
+ },
+ .num_fmax = VDD_SR2_HDMI_PLL_NUM,
CLK_INIT(pll3_clk.c),
},
};
@@ -1208,8 +1223,6 @@
.b = {
.ctl_reg = AHB_EN3_REG,
.en_mask = BIT(1),
- .hwcg_reg = AHB_EN3_REG,
- .hwcg_mask = BIT(0),
.reset_reg = SW_RESET_AHB2_REG,
.reset_mask = BIT(2),
.halt_reg = DBG_BUS_VEC_J_REG,
@@ -1534,7 +1547,7 @@
static CLK_SDC(sdc4_clk, 4, 3, 33000000, 67000000);
static CLK_SDC(sdc5_clk, 5, 2, 33000000, 67000000);
-static unsigned long fmax_sdc1_8064v2[MAX_VDD_LEVELS] __initdata = {
+static unsigned long fmax_sdc1_8064v2[VDD_DIG_NUM] = {
[VDD_DIG_LOW] = 100000000,
[VDD_DIG_NOMINAL] = 200000000,
};
@@ -1935,7 +1948,7 @@
},
};
-static unsigned long fmax_ce3_8064v2[MAX_VDD_LEVELS] __initdata = {
+static unsigned long fmax_ce3_8064v2[VDD_DIG_NUM] = {
[VDD_DIG_LOW] = 57000000,
[VDD_DIG_NOMINAL] = 120000000,
};
@@ -3582,25 +3595,25 @@
F_END
};
-static unsigned long fmax_gfx3d_8064ab[MAX_VDD_LEVELS] __initdata = {
+static unsigned long fmax_gfx3d_8064ab[VDD_DIG_NUM] = {
[VDD_DIG_LOW] = 128000000,
[VDD_DIG_NOMINAL] = 325000000,
[VDD_DIG_HIGH] = 450000000
};
-static unsigned long fmax_gfx3d_8064[MAX_VDD_LEVELS] __initdata = {
+static unsigned long fmax_gfx3d_8064[VDD_DIG_NUM] = {
[VDD_DIG_LOW] = 128000000,
[VDD_DIG_NOMINAL] = 325000000,
[VDD_DIG_HIGH] = 400000000
};
-static unsigned long fmax_gfx3d_8930[MAX_VDD_LEVELS] __initdata = {
+static unsigned long fmax_gfx3d_8930[VDD_DIG_NUM] = {
[VDD_DIG_LOW] = 192000000,
[VDD_DIG_NOMINAL] = 320000000,
[VDD_DIG_HIGH] = 400000000
};
-static unsigned long fmax_gfx3d_8930aa[MAX_VDD_LEVELS] __initdata = {
+static unsigned long fmax_gfx3d_8930aa[VDD_DIG_NUM] = {
[VDD_DIG_LOW] = 192000000,
[VDD_DIG_NOMINAL] = 320000000,
[VDD_DIG_HIGH] = 450000000
@@ -3752,7 +3765,7 @@
F_END
};
-static unsigned long fmax_ijpeg_8064[MAX_VDD_LEVELS] __initdata = {
+static unsigned long fmax_ijpeg_8064[VDD_DIG_NUM] = {
[VDD_DIG_LOW] = 128000000,
[VDD_DIG_NOMINAL] = 266667000,
[VDD_DIG_HIGH] = 320000000
@@ -3879,7 +3892,7 @@
F_END
};
-static unsigned long fmax_mdp_8064[MAX_VDD_LEVELS] __initdata = {
+static unsigned long fmax_mdp_8064[VDD_DIG_NUM] = {
[VDD_DIG_LOW] = 128000000,
[VDD_DIG_NOMINAL] = 266667000
};
@@ -4073,7 +4086,10 @@
.dbg_name = "hdmi_pll_clk",
.ops = &clk_ops_hdmi_pll,
.vdd_class = &vdd_sr2_hdmi_pll,
- .fmax[VDD_SR2_HDMI_PLL_ON] = ULONG_MAX,
+ .fmax = (unsigned long [VDD_SR2_HDMI_PLL_NUM]) {
+ [VDD_SR2_HDMI_PLL_ON] = ULONG_MAX,
+ },
+ .num_fmax = VDD_SR2_HDMI_PLL_NUM,
CLK_INIT(hdmi_pll_clk),
};
@@ -4105,7 +4121,7 @@
F_END
};
-static unsigned long fmax_tv_src_8064[MAX_VDD_LEVELS] __initdata = {
+static unsigned long fmax_tv_src_8064[VDD_DIG_NUM] = {
[VDD_DIG_LOW] = 74250000,
[VDD_DIG_NOMINAL] = 149000000
};
@@ -4344,7 +4360,7 @@
},
};
-static unsigned long fmax_vcodec_8064v2[MAX_VDD_LEVELS] __initdata = {
+static unsigned long fmax_vcodec_8064v2[VDD_DIG_NUM] = {
[VDD_DIG_LOW] = 100000000,
[VDD_DIG_NOMINAL] = 200000000,
[VDD_DIG_HIGH] = 266670000,
@@ -4426,7 +4442,7 @@
F_END
};
-static unsigned long fmax_vfe_8064[MAX_VDD_LEVELS] __initdata = {
+static unsigned long fmax_vfe_8064[VDD_DIG_NUM] = {
[VDD_DIG_LOW] = 128000000,
[VDD_DIG_NOMINAL] = 266667000,
[VDD_DIG_HIGH] = 320000000
@@ -6332,7 +6348,7 @@
}
if (cpu_is_apq8064() || cpu_is_apq8064ab())
- rmwreg(0x00000001, AHB_EN3_REG, 0x00000001);
+ rmwreg(0x00000000, AHB_EN3_REG, 0x00000001);
/* Deassert all locally-owned MM AHB resets. */
rmwreg(0, SW_RESET_AHB_REG, 0xFFF7DFFF);
@@ -6567,37 +6583,25 @@
*/
if (cpu_is_apq8064()) {
gfx3d_clk.freq_tbl = clk_tbl_gfx3d_8064;
-
- memcpy(gfx3d_clk.c.fmax, fmax_gfx3d_8064,
- sizeof(gfx3d_clk.c.fmax));
+ gfx3d_clk.c.fmax = fmax_gfx3d_8064;
}
if (cpu_is_apq8064ab()) {
gfx3d_clk.freq_tbl = clk_tbl_gfx3d_8064;
-
- memcpy(gfx3d_clk.c.fmax, fmax_gfx3d_8064ab,
- sizeof(gfx3d_clk.c.fmax));
+ gfx3d_clk.c.fmax = fmax_gfx3d_8064ab;
}
if ((cpu_is_apq8064() &&
SOCINFO_VERSION_MAJOR(socinfo_get_version()) == 2) ||
cpu_is_apq8064ab()) {
- memcpy(vcodec_clk.c.fmax, fmax_vcodec_8064v2,
- sizeof(vcodec_clk.c.fmax));
- memcpy(ce3_src_clk.c.fmax, fmax_ce3_8064v2,
- sizeof(ce3_src_clk.c.fmax));
- memcpy(sdc1_clk.c.fmax, fmax_sdc1_8064v2,
- sizeof(sdc1_clk.c.fmax));
+ vcodec_clk.c.fmax = fmax_vcodec_8064v2;
+ ce3_src_clk.c.fmax = fmax_ce3_8064v2;
+ sdc1_clk.c.fmax = fmax_sdc1_8064v2;
}
if (cpu_is_apq8064() || cpu_is_apq8064ab()) {
- memcpy(ijpeg_clk.c.fmax, fmax_ijpeg_8064,
- sizeof(ijpeg_clk.c.fmax));
- memcpy(mdp_clk.c.fmax, fmax_mdp_8064,
- sizeof(ijpeg_clk.c.fmax));
- memcpy(tv_src_clk.c.fmax, fmax_tv_src_8064,
- sizeof(tv_src_clk.c.fmax));
- memcpy(vfe_clk.c.fmax, fmax_vfe_8064,
- sizeof(vfe_clk.c.fmax));
-
+ ijpeg_clk.c.fmax = fmax_ijpeg_8064;
+ mdp_clk.c.fmax = fmax_mdp_8064;
+ tv_src_clk.c.fmax = fmax_tv_src_8064;
+ vfe_clk.c.fmax = fmax_vfe_8064;
gmem_axi_clk.c.depends = &gfx3d_axi_clk.c;
}
@@ -6606,11 +6610,9 @@
* clocks which differ between 8960 and 8930.
*/
if (cpu_is_msm8930() || cpu_is_msm8627()) {
- memcpy(gfx3d_clk.c.fmax, fmax_gfx3d_8930,
- sizeof(gfx3d_clk.c.fmax));
+ gfx3d_clk.c.fmax = fmax_gfx3d_8930;
} else if (cpu_is_msm8930aa()) {
- memcpy(gfx3d_clk.c.fmax, fmax_gfx3d_8930aa,
- sizeof(gfx3d_clk.c.fmax));
+ gfx3d_clk.c.fmax = fmax_gfx3d_8930aa;
}
if (cpu_is_msm8930() || cpu_is_msm8930aa() || cpu_is_msm8627()) {
gfx3d_clk.freq_tbl = clk_tbl_gfx3d_8930;
diff --git a/arch/arm/mach-msm/clock-8974.c b/arch/arm/mach-msm/clock-8974.c
index 246ceaf..76b8abf 100644
--- a/arch/arm/mach-msm/clock-8974.c
+++ b/arch/arm/mach-msm/clock-8974.c
@@ -588,23 +588,33 @@
}
#define VDD_DIG_FMAX_MAP1(l1, f1) \
- .vdd_class = &vdd_dig, \
- .fmax[VDD_DIG_##l1] = (f1)
+ .vdd_class = &vdd_dig, \
+ .fmax = (unsigned long[VDD_DIG_NUM]) { \
+ [VDD_DIG_##l1] = (f1), \
+ }, \
+ .num_fmax = VDD_DIG_NUM
#define VDD_DIG_FMAX_MAP2(l1, f1, l2, f2) \
- .vdd_class = &vdd_dig, \
- .fmax[VDD_DIG_##l1] = (f1), \
- .fmax[VDD_DIG_##l2] = (f2)
+ .vdd_class = &vdd_dig, \
+ .fmax = (unsigned long[VDD_DIG_NUM]) { \
+ [VDD_DIG_##l1] = (f1), \
+ [VDD_DIG_##l2] = (f2), \
+ }, \
+ .num_fmax = VDD_DIG_NUM
#define VDD_DIG_FMAX_MAP3(l1, f1, l2, f2, l3, f3) \
- .vdd_class = &vdd_dig, \
- .fmax[VDD_DIG_##l1] = (f1), \
- .fmax[VDD_DIG_##l2] = (f2), \
- .fmax[VDD_DIG_##l3] = (f3)
+ .vdd_class = &vdd_dig, \
+ .fmax = (unsigned long[VDD_DIG_NUM]) { \
+ [VDD_DIG_##l1] = (f1), \
+ [VDD_DIG_##l2] = (f2), \
+ [VDD_DIG_##l3] = (f3), \
+ }, \
+ .num_fmax = VDD_DIG_NUM
enum vdd_dig_levels {
VDD_DIG_NONE,
VDD_DIG_LOW,
VDD_DIG_NOMINAL,
- VDD_DIG_HIGH
+ VDD_DIG_HIGH,
+ VDD_DIG_NUM
};
static const int vdd_corner[] = {
@@ -622,7 +632,7 @@
RPM_REGULATOR_CORNER_SUPER_TURBO);
}
-static DEFINE_VDD_CLASS(vdd_dig, set_vdd_dig);
+static DEFINE_VDD_CLASS(vdd_dig, set_vdd_dig, VDD_DIG_NUM);
#define RPM_MISC_CLK_TYPE 0x306b6c63
#define RPM_BUS_CLK_TYPE 0x316b6c63
diff --git a/arch/arm/mach-msm/clock-8x60.c b/arch/arm/mach-msm/clock-8x60.c
index bc4bb2e..3816b54 100644
--- a/arch/arm/mach-msm/clock-8x60.c
+++ b/arch/arm/mach-msm/clock-8x60.c
@@ -268,7 +268,8 @@
VDD_DIG_NONE,
VDD_DIG_LOW,
VDD_DIG_NOMINAL,
- VDD_DIG_HIGH
+ VDD_DIG_HIGH,
+ VDD_DIG_NUM
};
static int set_vdd_dig(struct clk_vdd_class *vdd_class, int level)
@@ -284,20 +285,29 @@
vdd_uv[level], 1200000, 1);
}
-static DEFINE_VDD_CLASS(vdd_dig, set_vdd_dig);
+static DEFINE_VDD_CLASS(vdd_dig, set_vdd_dig, VDD_DIG_NUM);
#define VDD_DIG_FMAX_MAP1(l1, f1) \
- .vdd_class = &vdd_dig, \
- .fmax[VDD_DIG_##l1] = (f1)
+ .vdd_class = &vdd_dig, \
+ .fmax = (unsigned long[VDD_DIG_NUM]) { \
+ [VDD_DIG_##l1] = (f1), \
+ }, \
+ .num_fmax = VDD_DIG_NUM
#define VDD_DIG_FMAX_MAP2(l1, f1, l2, f2) \
- .vdd_class = &vdd_dig, \
- .fmax[VDD_DIG_##l1] = (f1), \
- .fmax[VDD_DIG_##l2] = (f2)
+ .vdd_class = &vdd_dig, \
+ .fmax = (unsigned long[VDD_DIG_NUM]) { \
+ [VDD_DIG_##l1] = (f1), \
+ [VDD_DIG_##l2] = (f2), \
+ }, \
+ .num_fmax = VDD_DIG_NUM
#define VDD_DIG_FMAX_MAP3(l1, f1, l2, f2, l3, f3) \
- .vdd_class = &vdd_dig, \
- .fmax[VDD_DIG_##l1] = (f1), \
- .fmax[VDD_DIG_##l2] = (f2), \
- .fmax[VDD_DIG_##l3] = (f3)
+ .vdd_class = &vdd_dig, \
+ .fmax = (unsigned long[VDD_DIG_NUM]) { \
+ [VDD_DIG_##l1] = (f1), \
+ [VDD_DIG_##l2] = (f2), \
+ [VDD_DIG_##l3] = (f3), \
+ }, \
+ .num_fmax = VDD_DIG_NUM
DEFINE_CLK_RPM_BRANCH(pxo_clk, pxo_a_clk, PXO, 27000000);
DEFINE_CLK_RPM_BRANCH(cxo_clk, cxo_a_clk, CXO, 19200000);
diff --git a/arch/arm/mach-msm/clock-9615.c b/arch/arm/mach-msm/clock-9615.c
index 035ef5c..338361b 100644
--- a/arch/arm/mach-msm/clock-9615.c
+++ b/arch/arm/mach-msm/clock-9615.c
@@ -182,7 +182,8 @@
VDD_DIG_NONE,
VDD_DIG_LOW,
VDD_DIG_NOMINAL,
- VDD_DIG_HIGH
+ VDD_DIG_HIGH,
+ VDD_DIG_NUM
};
static int set_vdd_dig(struct clk_vdd_class *vdd_class, int level)
@@ -198,15 +199,21 @@
RPM_VREG_VOTER3, vdd_corner[level], RPM_VREG_CORNER_HIGH, 1);
}
-static DEFINE_VDD_CLASS(vdd_dig, set_vdd_dig);
+static DEFINE_VDD_CLASS(vdd_dig, set_vdd_dig, VDD_DIG_NUM);
#define VDD_DIG_FMAX_MAP1(l1, f1) \
- .vdd_class = &vdd_dig, \
- .fmax[VDD_DIG_##l1] = (f1)
+ .vdd_class = &vdd_dig, \
+ .fmax = (unsigned long[VDD_DIG_NUM]) { \
+ [VDD_DIG_##l1] = (f1), \
+ }, \
+ .num_fmax = VDD_DIG_NUM
#define VDD_DIG_FMAX_MAP2(l1, f1, l2, f2) \
- .vdd_class = &vdd_dig, \
- .fmax[VDD_DIG_##l1] = (f1), \
- .fmax[VDD_DIG_##l2] = (f2)
+ .vdd_class = &vdd_dig, \
+ .fmax = (unsigned long[VDD_DIG_NUM]) { \
+ [VDD_DIG_##l1] = (f1), \
+ [VDD_DIG_##l2] = (f2), \
+ }, \
+ .num_fmax = VDD_DIG_NUM
/*
* Clock Descriptions
diff --git a/arch/arm/mach-msm/clock-9625.c b/arch/arm/mach-msm/clock-9625.c
index fb4f32a..b9362cf 100644
--- a/arch/arm/mach-msm/clock-9625.c
+++ b/arch/arm/mach-msm/clock-9625.c
@@ -291,23 +291,33 @@
}
#define VDD_DIG_FMAX_MAP1(l1, f1) \
- .vdd_class = &vdd_dig, \
- .fmax[VDD_DIG_##l1] = (f1)
+ .vdd_class = &vdd_dig, \
+ .fmax = (unsigned long[VDD_DIG_NUM]) { \
+ [VDD_DIG_##l1] = (f1), \
+ }, \
+ .num_fmax = VDD_DIG_NUM
#define VDD_DIG_FMAX_MAP2(l1, f1, l2, f2) \
- .vdd_class = &vdd_dig, \
- .fmax[VDD_DIG_##l1] = (f1), \
- .fmax[VDD_DIG_##l2] = (f2)
+ .vdd_class = &vdd_dig, \
+ .fmax = (unsigned long[VDD_DIG_NUM]) { \
+ [VDD_DIG_##l1] = (f1), \
+ [VDD_DIG_##l2] = (f2), \
+ }, \
+ .num_fmax = VDD_DIG_NUM
#define VDD_DIG_FMAX_MAP3(l1, f1, l2, f2, l3, f3) \
- .vdd_class = &vdd_dig, \
- .fmax[VDD_DIG_##l1] = (f1), \
- .fmax[VDD_DIG_##l2] = (f2), \
- .fmax[VDD_DIG_##l3] = (f3)
+ .vdd_class = &vdd_dig, \
+ .fmax = (unsigned long[VDD_DIG_NUM]) { \
+ [VDD_DIG_##l1] = (f1), \
+ [VDD_DIG_##l2] = (f2), \
+ [VDD_DIG_##l3] = (f3), \
+ }, \
+ .num_fmax = VDD_DIG_NUM
enum vdd_dig_levels {
VDD_DIG_NONE,
VDD_DIG_LOW,
VDD_DIG_NOMINAL,
- VDD_DIG_HIGH
+ VDD_DIG_HIGH,
+ VDD_DIG_NUM
};
static const int vdd_corner[] = {
@@ -325,7 +335,7 @@
RPM_REGULATOR_CORNER_SUPER_TURBO);
}
-static DEFINE_VDD_CLASS(vdd_dig, set_vdd_dig);
+static DEFINE_VDD_CLASS(vdd_dig, set_vdd_dig, VDD_DIG_NUM);
/* TODO: Needs to confirm the below values */
#define RPM_MISC_CLK_TYPE 0x306b6c63
diff --git a/arch/arm/mach-msm/clock-debug.c b/arch/arm/mach-msm/clock-debug.c
index 8bd4433..489d623 100644
--- a/arch/arm/mach-msm/clock-debug.c
+++ b/arch/arm/mach-msm/clock-debug.c
@@ -157,7 +157,7 @@
clock->dbg_name, clock->rate);
return 0;
}
- for (level = 0; level < ARRAY_SIZE(clock->fmax); level++) {
+ for (level = 0; level < clock->num_fmax; level++) {
if (vdd_level == level)
seq_printf(m, "[%lu] ", clock->fmax[level]);
else
@@ -189,7 +189,7 @@
if (!clock->vdd_class) {
fmax = INT_MAX;
} else {
- for (level = 0; level < ARRAY_SIZE(clock->fmax); level++)
+ for (level = 0; level < clock->num_fmax; level++)
if (clock->fmax[level])
fmax = clock->fmax[level];
}
diff --git a/arch/arm/mach-msm/clock.c b/arch/arm/mach-msm/clock.c
index 5100980..e9dd974 100644
--- a/arch/arm/mach-msm/clock.c
+++ b/arch/arm/mach-msm/clock.c
@@ -37,11 +37,11 @@
{
int level;
- for (level = 0; level < ARRAY_SIZE(clk->fmax); level++)
+ for (level = 0; level < clk->num_fmax; level++)
if (rate <= clk->fmax[level])
break;
- if (level == ARRAY_SIZE(clk->fmax)) {
+ if (level == clk->num_fmax) {
pr_err("Rate %lu for %s is greater than highest Fmax\n", rate,
clk->dbg_name);
return -EINVAL;
@@ -55,7 +55,7 @@
{
int level, rc;
- for (level = ARRAY_SIZE(vdd_class->level_votes)-1; level > 0; level--)
+ for (level = vdd_class->num_levels-1; level > 0; level--)
if (vdd_class->level_votes[level])
break;
@@ -74,6 +74,9 @@
{
int rc;
+ if (level >= vdd_class->num_levels)
+ return -EINVAL;
+
mutex_lock(&vdd_class->lock);
vdd_class->level_votes[level]++;
rc = update_vdd(vdd_class);
@@ -89,6 +92,9 @@
{
int rc = 0;
+ if (level >= vdd_class->num_levels)
+ return -EINVAL;
+
mutex_lock(&vdd_class->lock);
if (WARN(!vdd_class->level_votes[level],
"Reference counts are incorrect for %s level %d\n",
diff --git a/arch/arm/mach-msm/include/mach/clk-provider.h b/arch/arm/mach-msm/include/mach/clk-provider.h
index 770713d..d47e88e 100644
--- a/arch/arm/mach-msm/include/mach/clk-provider.h
+++ b/arch/arm/mach-msm/include/mach/clk-provider.h
@@ -39,8 +39,6 @@
#define ENABLE_VOTED 4 /* Bit pol: 1 = running; delay on disable */
#define DELAY 5 /* No bit to check, just delay */
-#define MAX_VDD_LEVELS 4
-
/**
* struct clk_vdd_class - Voltage scaling class
* @class_name: name of the class
@@ -52,16 +50,19 @@
struct clk_vdd_class {
const char *class_name;
int (*set_vdd)(struct clk_vdd_class *v_class, int level);
- int level_votes[MAX_VDD_LEVELS];
+ int *level_votes;
+ int num_levels;
unsigned long cur_level;
struct mutex lock;
};
-#define DEFINE_VDD_CLASS(_name, _set_vdd) \
+#define DEFINE_VDD_CLASS(_name, _set_vdd, _num_levels) \
struct clk_vdd_class _name = { \
.class_name = #_name, \
.set_vdd = _set_vdd, \
- .cur_level = ARRAY_SIZE(_name.level_votes), \
+ .level_votes = (int [_num_levels]) {}, \
+ .num_levels = _num_levels, \
+ .cur_level = _num_levels, \
.lock = __MUTEX_INITIALIZER(_name.lock) \
}
@@ -109,7 +110,8 @@
const char *dbg_name;
struct clk *depends;
struct clk_vdd_class *vdd_class;
- unsigned long fmax[MAX_VDD_LEVELS];
+ unsigned long *fmax;
+ int num_fmax;
unsigned long rate;
struct list_head children;
diff --git a/arch/arm/mach-msm/include/mach/irqs-8910.h b/arch/arm/mach-msm/include/mach/irqs-8910.h
index 22fdc16..e883214 100644
--- a/arch/arm/mach-msm/include/mach/irqs-8910.h
+++ b/arch/arm/mach-msm/include/mach/irqs-8910.h
@@ -31,7 +31,7 @@
#define TLMM_MSM_SUMMARY_IRQ (GIC_SPI_START + 208)
#define NR_MSM_IRQS 256
-#define NR_GPIO_IRQS 146
+#define NR_GPIO_IRQS 102
#define NR_QPNP_IRQS 32768 /* SPARSE_IRQ is required to support this */
#define NR_BOARD_IRQS NR_QPNP_IRQS
#define NR_TLMM_MSM_DIR_CONN_IRQ 8
diff --git a/arch/arm/mach-msm/include/mach/msm_tspp.h b/arch/arm/mach-msm/include/mach/msm_tspp.h
index 48be504..5395b88 100644
--- a/arch/arm/mach-msm/include/mach/msm_tspp.h
+++ b/arch/arm/mach-msm/include/mach/msm_tspp.h
@@ -35,8 +35,8 @@
u32 *phys_base, void *user);
/* Kernel API functions */
-int tspp_open_stream(u32 dev, u32 channel_id, enum tspp_source src,
- enum tspp_tsif_mode mode);
+int tspp_open_stream(u32 dev, u32 channel_id,
+ struct tspp_select_source *source);
int tspp_close_stream(u32 dev, u32 channel_id);
int tspp_open_channel(u32 dev, u32 channel_id);
int tspp_close_channel(u32 dev, u32 channel_id);
diff --git a/arch/arm/mach-msm/include/mach/ocmem_priv.h b/arch/arm/mach-msm/include/mach/ocmem_priv.h
index 0b30c26..380fde1 100644
--- a/arch/arm/mach-msm/include/mach/ocmem_priv.h
+++ b/arch/arm/mach-msm/include/mach/ocmem_priv.h
@@ -127,7 +127,7 @@
struct list_head req_list;
struct work_struct work;
int prio;
- int pending;
+ atomic_t pending;
bool passive;
};
diff --git a/arch/arm/mach-msm/ipc_router.c b/arch/arm/mach-msm/ipc_router.c
index c82eac1..20e56c2 100644
--- a/arch/arm/mach-msm/ipc_router.c
+++ b/arch/arm/mach-msm/ipc_router.c
@@ -174,7 +174,6 @@
static LIST_HEAD(xprt_info_list);
static DEFINE_MUTEX(xprt_info_list_lock);
-DECLARE_COMPLETION(msm_ipc_remote_router_up);
static DECLARE_COMPLETION(msm_ipc_local_router_up);
#define IPC_ROUTER_INIT_TIMEOUT (10 * HZ)
@@ -765,7 +764,7 @@
return ret;
}
-static int msm_ipc_router_send_server_list(
+static int msm_ipc_router_send_server_list(uint32_t node_id,
struct msm_ipc_router_xprt_info *xprt_info)
{
union rr_control_msg ctl;
@@ -787,8 +786,8 @@
ctl.srv.instance = server->name.instance;
list_for_each_entry(server_port,
&server->server_port_list, list) {
- if (server_port->server_addr.node_id ==
- xprt_info->remote_node_id)
+ if (server_port->server_addr.node_id !=
+ node_id)
continue;
ctl.srv.node_id =
@@ -1166,14 +1165,86 @@
msm_ipc_cleanup_routing_table(xprt_info);
}
+static int process_hello_msg(struct msm_ipc_router_xprt_info *xprt_info,
+ struct rr_header *hdr)
+{
+ int i, rc = 0;
+ union rr_control_msg ctl;
+ struct msm_ipc_routing_table_entry *rt_entry;
+
+ if (!hdr)
+ return -EINVAL;
+
+ RR("o HELLO NID %d\n", hdr->src_node_id);
+
+ xprt_info->remote_node_id = hdr->src_node_id;
+ /*
+ * Find the entry from Routing Table corresponding to Node ID.
+ * Under SSR, an entry will be found. When the system boots up
+ * for the 1st time, an entry will not be found and hence allocate
+ * an entry. Update the entry with the Node ID that it corresponds
+ * to and the XPRT through which it can be reached.
+ */
+ mutex_lock(&routing_table_lock);
+ rt_entry = lookup_routing_table(hdr->src_node_id);
+ if (!rt_entry) {
+ rt_entry = alloc_routing_table_entry(hdr->src_node_id);
+ if (!rt_entry) {
+ mutex_unlock(&routing_table_lock);
+ pr_err("%s: rt_entry allocation failed\n", __func__);
+ return -ENOMEM;
+ }
+ add_routing_table_entry(rt_entry);
+ }
+ mutex_lock(&rt_entry->lock);
+ rt_entry->neighbor_node_id = xprt_info->remote_node_id;
+ rt_entry->xprt_info = xprt_info;
+ mutex_unlock(&rt_entry->lock);
+ mutex_unlock(&routing_table_lock);
+
+ /* Cleanup any remote ports, if the node is coming out of reset */
+ msm_ipc_cleanup_remote_port_info(xprt_info->remote_node_id);
+
+ /* Send a reply HELLO message */
+ memset(&ctl, 0, sizeof(ctl));
+ ctl.cmd = IPC_ROUTER_CTRL_CMD_HELLO;
+ rc = msm_ipc_router_send_control_msg(xprt_info, &ctl);
+ if (rc < 0) {
+ pr_err("%s: Error sending reply HELLO message\n", __func__);
+ return rc;
+ }
+ xprt_info->initialized = 1;
+
+ /*
+ * Send list of servers from the local node and from nodes
+ * outside the mesh network in which this XPRT is part of.
+ */
+ mutex_lock(&routing_table_lock);
+ for (i = 0; i < RT_HASH_SIZE; i++) {
+ list_for_each_entry(rt_entry, &routing_table[i], list) {
+ if ((rt_entry->node_id != IPC_ROUTER_NID_LOCAL) &&
+ (rt_entry->xprt_info->xprt->link_id ==
+ xprt_info->xprt->link_id))
+ continue;
+ rc = msm_ipc_router_send_server_list(rt_entry->node_id,
+ xprt_info);
+ if (rc < 0) {
+ mutex_unlock(&routing_table_lock);
+ return rc;
+ }
+ }
+ }
+ mutex_unlock(&routing_table_lock);
+ RR("HELLO message processed\n");
+ return rc;
+}
+
static int process_control_msg(struct msm_ipc_router_xprt_info *xprt_info,
struct rr_packet *pkt)
{
- union rr_control_msg ctl;
union rr_control_msg *msg;
struct msm_ipc_router_remote_port *rport_ptr;
int rc = 0;
- static uint32_t first = 1;
struct sk_buff *temp_ptr;
struct rr_header *hdr;
struct msm_ipc_server *server;
@@ -1199,43 +1270,9 @@
switch (msg->cmd) {
case IPC_ROUTER_CTRL_CMD_HELLO:
- RR("o HELLO NID %d\n", hdr->src_node_id);
- xprt_info->remote_node_id = hdr->src_node_id;
-
- mutex_lock(&routing_table_lock);
- rt_entry = lookup_routing_table(hdr->src_node_id);
- if (!rt_entry) {
- rt_entry = alloc_routing_table_entry(hdr->src_node_id);
- if (!rt_entry) {
- mutex_unlock(&routing_table_lock);
- pr_err("%s: rt_entry allocation failed\n",
- __func__);
- return -ENOMEM;
- }
- add_routing_table_entry(rt_entry);
- }
- mutex_lock(&rt_entry->lock);
- rt_entry->neighbor_node_id = xprt_info->remote_node_id;
- rt_entry->xprt_info = xprt_info;
- mutex_unlock(&rt_entry->lock);
- mutex_unlock(&routing_table_lock);
- msm_ipc_cleanup_remote_port_info(xprt_info->remote_node_id);
-
- memset(&ctl, 0, sizeof(ctl));
- ctl.cmd = IPC_ROUTER_CTRL_CMD_HELLO;
- msm_ipc_router_send_control_msg(xprt_info, &ctl);
-
- xprt_info->initialized = 1;
-
- /* Send list of servers one at a time */
- msm_ipc_router_send_server_list(xprt_info);
-
- if (first) {
- first = 0;
- complete_all(&msm_ipc_remote_router_up);
- }
- RR("HELLO message processed\n");
+ rc = process_hello_msg(xprt_info, hdr);
break;
+
case IPC_ROUTER_CTRL_CMD_RESUME_TX:
RR("o RESUME_TX id=%d:%08x\n",
msg->cli.node_id, msg->cli.port_id);
diff --git a/arch/arm/mach-msm/krait-regulator.c b/arch/arm/mach-msm/krait-regulator.c
index 96c4809..aa9b344 100644
--- a/arch/arm/mach-msm/krait-regulator.c
+++ b/arch/arm/mach-msm/krait-regulator.c
@@ -27,6 +27,7 @@
#include <linux/regulator/machine.h>
#include <linux/regulator/of_regulator.h>
#include <linux/regulator/krait-regulator.h>
+#include <mach/msm_iomap.h>
#include "spm.h"
@@ -87,6 +88,11 @@
#define CPU_TRGTD_DBG_RST 0x00000010
#define APC_PWR_GATE_CTL 0x00000014
#define APC_LDO_VREF_SET 0x00000018
+#define APC_PWR_GATE_MODE 0x0000001C
+#define APC_PWR_GATE_DLY 0x00000020
+
+#define PWR_GATE_CONFIG 0x00000044
+#define VERSION 0x00000FD0
/* bit definitions for APC_PWR_GATE_CTL */
#define BHS_CNT_BIT_POS 24
@@ -135,6 +141,7 @@
int pmic_phase_count;
struct list_head krait_power_vregs;
struct mutex krait_power_vregs_lock;
+ bool pfm_mode;
};
static struct pmic_gang_vreg *the_gang;
@@ -156,6 +163,8 @@
void __iomem *reg_base;
};
+static u32 version;
+
static void krait_masked_write(struct krait_power_vreg *kvreg,
int reg, uint32_t mask, uint32_t val)
{
@@ -534,6 +543,9 @@
return rc;
}
+#define PMIC_FTS_MODE_PFM 0x00
+#define PMIC_FTS_MODE_PWM 0x80
+#define PFM_LOAD_UA 500000
static unsigned int krait_power_get_optimum_mode(struct regulator_dev *rdev,
int input_uV, int output_uV, int load_uA)
{
@@ -545,10 +557,40 @@
mutex_lock(&pvreg->krait_power_vregs_lock);
+ reg_mode = kvreg->mode;
+
kvreg->load_uA = load_uA;
load_total_uA = get_total_load(kvreg);
+ if (load_total_uA < PFM_LOAD_UA) {
+ if (!pvreg->pfm_mode) {
+ rc = msm_spm_enable_fts_lpm(PMIC_FTS_MODE_PFM);
+ if (rc) {
+ dev_err(&rdev->dev,
+ "%s enter PFM failed load %d rc = %d\n",
+ kvreg->name, load_total_uA, rc);
+ goto out;
+ } else {
+ pvreg->pfm_mode = true;
+ }
+ }
+ mutex_unlock(&pvreg->krait_power_vregs_lock);
+ return reg_mode;
+ }
+
+ if (pvreg->pfm_mode) {
+ rc = msm_spm_enable_fts_lpm(PMIC_FTS_MODE_PWM);
+ if (rc) {
+ dev_err(&rdev->dev,
+ "%s exit PFM failed load %d rc = %d\n",
+ kvreg->name, load_total_uA, rc);
+ goto out;
+ } else {
+ pvreg->pfm_mode = false;
+ }
+ }
+
rc = pmic_gang_set_phases(kvreg, load_total_uA);
if (rc < 0) {
dev_err(&rdev->dev, "%s failed set mode %d rc = %d\n",
@@ -556,7 +598,6 @@
goto out;
}
- reg_mode = kvreg->mode;
out:
mutex_unlock(&pvreg->krait_power_vregs_lock);
return reg_mode;
@@ -599,6 +640,15 @@
BHS_SEG_EN_MASK, BHS_SEG_EN_DEFAULT << BHS_SEG_EN_BIT_POS);
}
+static void glb_init(struct platform_device *pdev)
+{
+ /* configure bi-modal switch */
+ writel_relaxed(0x0008736E, MSM_APCS_GCC_BASE + PWR_GATE_CONFIG);
+ /* read kpss version */
+ version = readl_relaxed(MSM_APCS_GCC_BASE + VERSION);
+ pr_debug("version= 0x%x\n", version);
+}
+
static int __devinit krait_power_probe(struct platform_device *pdev)
{
struct krait_power_vreg *kvreg;
@@ -614,6 +664,8 @@
"failed to init pmic gang rc = %d\n", rc);
return rc;
}
+ /* global initializtion */
+ glb_init(pdev);
}
if (pdev->dev.of_node) {
@@ -738,8 +790,10 @@
{
/* 605mV retention and 705mV operational voltage */
writel_relaxed(0x1C30, base_ptr + APC_LDO_VREF_SET);
- writel_relaxed(0x430000, base_ptr + 0x20);
- writel_relaxed(0x21, base_ptr + 0x1C);
+ /* HS_EN_DLY=3; LDO_BYP_DLY=1; */
+ writel_relaxed(0x430000, base_ptr + APC_PWR_GATE_DLY);
+ /* MODE = BHS; EN=1; */
+ writel_relaxed(0x21, base_ptr + APC_PWR_GATE_MODE);
/* Turn on the BHS, turn off LDO Bypass and power down LDO */
writel_relaxed(0x403F007F, base_ptr + APC_PWR_GATE_CTL);
diff --git a/arch/arm/mach-msm/msm_bus/msm_bus_bimc.c b/arch/arm/mach-msm/msm_bus/msm_bus_bimc.c
index ea17efe..e0ab983 100644
--- a/arch/arm/mach-msm/msm_bus/msm_bus_bimc.c
+++ b/arch/arm/mach-msm/msm_bus/msm_bus_bimc.c
@@ -485,7 +485,7 @@
};
#define M_PRIOLVL_OVERRIDE_ADDR(b, n) \
- (M_REG_BASE(b) + (0x4000 * (n)) + 0x00000230)
+ (M_REG_BASE(b) + (0x4000 * (n)) + 0x00000220)
enum bimc_m_priolvl_override {
M_PRIOLVL_OVERRIDE_RMSK = 0x301,
M_PRIOLVL_OVERRIDE_BMSK = 0x300,
@@ -495,10 +495,10 @@
};
#define M_RD_CMD_OVERRIDE_ADDR(b, n) \
- (M_REG_BASE(b) + (0x4000 * (n)) + 0x00000240)
+ (M_REG_BASE(b) + (0x4000 * (n)) + 0x00000230)
enum bimc_m_read_command_override {
- M_RD_CMD_OVERRIDE_RMSK = 0x3071f7f,
- M_RD_CMD_OVERRIDE_AREQPRIO_BMSK = 0x3000000,
+ M_RD_CMD_OVERRIDE_RMSK = 0x37f3f,
+ M_RD_CMD_OVERRIDE_AREQPRIO_BMSK = 0x300000,
M_RD_CMD_OVERRIDE_AREQPRIO_SHFT = 0x18,
M_RD_CMD_OVERRIDE_AMEMTYPE_BMSK = 0x70000,
M_RD_CMD_OVERRIDE_AMEMTYPE_SHFT = 0x10,
@@ -529,15 +529,13 @@
};
#define M_WR_CMD_OVERRIDE_ADDR(b, n) \
- (M_REG_BASE(b) + (0x4000 * (n)) + 0x00000250)
+ (M_REG_BASE(b) + (0x4000 * (n)) + 0x00000240)
enum bimc_m_write_command_override {
- M_WR_CMD_OVERRIDE_RMSK = 0x3071f7f,
- M_WR_CMD_OVERRIDE_AREQPRIO_BMSK = 0x3000000,
- M_WR_CMD_OVERRIDE_AREQPRIO_SHFT = 0x18,
- M_WR_CMD_OVERRIDE_AMEMTYPE_BMSK = 0x70000,
- M_WR_CMD_OVERRIDE_AMEMTYPE_SHFT = 0x10,
- M_WR_CMD_OVERRIDE_ATRANSIENT_BMSK = 0x1000,
- M_WR_CMD_OVERRIDE_ATRANSIENT_SHFT = 0xc,
+ M_WR_CMD_OVERRIDE_RMSK = 0x37f3f,
+ M_WR_CMD_OVERRIDE_AREQPRIO_BMSK = 0x30000,
+ M_WR_CMD_OVERRIDE_AREQPRIO_SHFT = 0x10,
+ M_WR_CMD_OVERRIDE_AMEMTYPE_BMSK = 0x7000,
+ M_WR_CMD_OVERRIDE_AMEMTYPE_SHFT = 0xc,
M_WR_CMD_OVERRIDE_ASHARED_BMSK = 0x800,
M_WR_CMD_OVERRIDE_ASHARED_SHFT = 0xb,
M_WR_CMD_OVERRIDE_AREDIRECT_BMSK = 0x400,
@@ -546,10 +544,8 @@
M_WR_CMD_OVERRIDE_AOOO_SHFT = 0x9,
M_WR_CMD_OVERRIDE_AINNERSHARED_BMSK = 0x100,
M_WR_CMD_OVERRIDE_AINNERSHARED_SHFT = 0x8,
- M_WR_CMD_OVERRIDE_OVERRIDE_AREQPRIO_BMSK = 0x40,
- M_WR_CMD_OVERRIDE_OVERRIDE_AREQPRIO_SHFT = 0x6,
- M_WR_CMD_OVERRIDE_OVERRIDE_ATRANSIENT_BMSK = 0x20,
- M_WR_CMD_OVERRIDE_OVERRIDE_ATRANSIENT_SHFT = 0x5,
+ M_WR_CMD_OVERRIDE_OVERRIDE_AREQPRIO_BMSK = 0x20,
+ M_WR_CMD_OVERRIDE_OVERRIDE_AREQPRIO_SHFT = 0x5,
M_WR_CMD_OVERRIDE_OVERRIDE_AMEMTYPE_BMSK = 0x10,
M_WR_CMD_OVERRIDE_OVERRIDE_AMEMTYPE_SHFT = 0x4,
M_WR_CMD_OVERRIDE_OVERRIDE_ASHARED_BMSK = 0x8,
@@ -1458,7 +1454,7 @@
* boundary in future
*/
wmb();
- set_qos_mode(binfo->base, mas_index, 0, 1, 1);
+ set_qos_mode(binfo->base, mas_index, 1, 1, 1);
break;
case BIMC_QOS_MODE_BYPASS:
diff --git a/arch/arm/mach-msm/msm_bus/msm_bus_board_8974.c b/arch/arm/mach-msm/msm_bus/msm_bus_board_8974.c
index cfd84eb..f0f5cd8 100644
--- a/arch/arm/mach-msm/msm_bus/msm_bus_board_8974.c
+++ b/arch/arm/mach-msm/msm_bus/msm_bus_board_8974.c
@@ -1049,8 +1049,9 @@
.qport = qports_kmpss,
.ws = 10000,
.mas_hw_id = MAS_APPSS_PROC,
- .prio_rd = 1,
- .prio_wr = 1,
+ .prio_lvl = 0,
+ .prio_rd = 2,
+ .prio_wr = 2,
},
{
.id = MSM_BUS_MASTER_AMPSS_M1,
@@ -1063,8 +1064,6 @@
.qport = qports_kmpss,
.ws = 10000,
.mas_hw_id = MAS_APPSS_PROC,
- .prio_rd = 1,
- .prio_wr = 1,
},
{
.id = MSM_BUS_MASTER_MSS_PROC,
diff --git a/arch/arm/mach-msm/ocmem_core.c b/arch/arm/mach-msm/ocmem_core.c
index 5a85eec..3d9639f 100644
--- a/arch/arm/mach-msm/ocmem_core.c
+++ b/arch/arm/mach-msm/ocmem_core.c
@@ -404,7 +404,7 @@
if (offset < 0)
return -EINVAL;
- if (len < region_size)
+ if (len < OCMEM_MIN_ALLOC)
return -EINVAL;
pr_debug("ocmem: mode_transistion to %x\n", new_mode);
@@ -425,7 +425,7 @@
/* Set the region to its new mode */
region->mode = new_mode;
atomic_inc(®ion->mode_counter);
- pr_debug("Region (%d) switching to mode %d\n",
+ pr_debug("Region (%d) switching to mode %d\n",
i, new_mode);
continue;
} else if (region->mode != new_mode) {
diff --git a/arch/arm/mach-msm/ocmem_rdm.c b/arch/arm/mach-msm/ocmem_rdm.c
index 4aba69c..818a20a 100644
--- a/arch/arm/mach-msm/ocmem_rdm.c
+++ b/arch/arm/mach-msm/ocmem_rdm.c
@@ -58,6 +58,7 @@
#define BR_CLIENT_n_IDX(x) ((x) * 0x4)
#define BR_CLIENT_n_ctrl(x) (BR_CLIENT_BASE + (BR_CLIENT_n_IDX(x)))
#define BR_STATUS (0x14)
+#define BR_LAST_ADDR (0x18)
/* 16 entries per client are supported */
/* Use entries 0 - 15 for client0 */
#define BR_CLIENT0_MASK (0x1000)
@@ -76,7 +77,7 @@
#define BR_TBL_ENTRY_ENABLE 0x1
#define BR_TBL_START 0x0
#define BR_TBL_END 0x8
-#define BR_RW_SHIFT 0x2
+#define BR_RW_SHIFT 0x1
#define DM_TBL_START 0x10
#define DM_TBL_END 0x18
@@ -134,13 +135,13 @@
pr_debug("irq:dm_status %x irq_status %x\n", status, irq_status);
if (irq_status & BIT(0)) {
pr_debug("Data mover completed\n");
- irq_status &= ~BIT(0);
- ocmem_write(irq_status, dm_base + DM_INTR_CLR);
+ ocmem_write(BIT(0), dm_base + DM_INTR_CLR);
+ pr_debug("Last re-mapped address block %x\n",
+ ocmem_read(br_base + BR_LAST_ADDR));
complete(&dm_transfer_event);
} else if (irq_status & BIT(1)) {
pr_debug("Data clear engine completed\n");
- irq_status &= ~BIT(1);
- ocmem_write(irq_status, dm_base + DM_INTR_CLR);
+ ocmem_write(BIT(1), dm_base + DM_INTR_CLR);
complete(&dm_clear_event);
} else {
BUG_ON(1);
@@ -259,6 +260,7 @@
pr_debug("ocmem: rdm: dm_ctrl %x br_ctrl %x\n", dm_ctrl, br_ctrl);
wait_for_completion(&dm_transfer_event);
+ pr_debug("Completed transferring %d segments\n", num_chunks);
ocmem_disable_core_clock();
return 0;
}
diff --git a/arch/arm/mach-msm/ocmem_sched.c b/arch/arm/mach-msm/ocmem_sched.c
index c380c54..e5892c3 100644
--- a/arch/arm/mach-msm/ocmem_sched.c
+++ b/arch/arm/mach-msm/ocmem_sched.c
@@ -45,6 +45,7 @@
OP_COMPLETE = 0x0,
OP_RESCHED,
OP_PARTIAL,
+ OP_EVICT,
OP_FAIL = ~0x0,
};
@@ -74,7 +75,8 @@
* hardware state changes can occur. The value will be tweaked on actual
* hardware.
*/
-#define SCHED_DELAY 10
+/* Delay in ms for switching to low power mode for OCMEM */
+#define SCHED_DELAY 5000
static struct list_head rdm_queue;
static struct mutex rdm_mutex;
@@ -128,6 +130,7 @@
static struct rb_root sched_tree;
static struct mutex sched_mutex;
+static struct mutex allocation_mutex;
/* A region represents a continuous interval in OCMEM address space */
struct ocmem_region {
@@ -307,6 +310,7 @@
INIT_LIST_HEAD(&p->sched_list);
init_rwsem(&p->rw_sem);
SET_STATE(p, R_FREE);
+ pr_debug("request %p created\n", p);
return p;
}
@@ -594,6 +598,16 @@
if (rc < 0)
goto br_clock_fail;
+
+ rc = ocmem_lock(req->owner, phys_to_offset(req->req_start), req->req_sz,
+ get_mode(req->owner));
+
+ if (rc < 0) {
+ pr_err("ocmem: Failed to secure request %p for %d\n", req,
+ req->owner);
+ goto lock_failed;
+ }
+
rc = do_map(req);
if (rc < 0) {
@@ -602,19 +616,12 @@
goto process_map_fail;
}
-
- if (ocmem_lock(req->owner, phys_to_offset(req->req_start), req->req_sz,
- get_mode(req->owner))) {
- pr_err("ocmem: Failed to secure request %p for %d\n", req,
- req->owner);
- rc = -EINVAL;
- goto lock_failed;
- }
-
+ pr_debug("ocmem: Mapped request %p\n", req);
return 0;
-lock_failed:
- do_unmap(req);
+
process_map_fail:
+ ocmem_unlock(req->owner, phys_to_offset(req->req_start), req->req_sz);
+lock_failed:
ocmem_disable_br_clock();
br_clock_fail:
ocmem_disable_iface_clock();
@@ -630,22 +637,24 @@
{
int rc = 0;
- if (ocmem_unlock(req->owner, phys_to_offset(req->req_start),
- req->req_sz)) {
- pr_err("ocmem: Failed to un-secure request %p for %d\n", req,
- req->owner);
- rc = -EINVAL;
- goto unlock_failed;
- }
-
rc = do_unmap(req);
if (rc < 0)
goto process_unmap_fail;
+ rc = ocmem_unlock(req->owner, phys_to_offset(req->req_start),
+ req->req_sz);
+
+ if (rc < 0) {
+ pr_err("ocmem: Failed to un-secure request %p for %d\n", req,
+ req->owner);
+ goto unlock_failed;
+ }
+
ocmem_disable_br_clock();
ocmem_disable_iface_clock();
ocmem_disable_core_clock();
+ pr_debug("ocmem: Unmapped request %p\n", req);
return 0;
unlock_failed:
@@ -1009,7 +1018,8 @@
retry = false;
- pr_debug("ocmem: ALLOCATE: request size %lx\n", sz);
+ pr_debug("ocmem: do_allocate: %s request size %lx\n",
+ get_name(owner), sz);
retry_next_step:
@@ -1036,6 +1046,7 @@
/* update request state */
CLEAR_STATE(req, R_FREE);
+ CLEAR_STATE(req, R_PENDING);
SET_STATE(req, R_ALLOCATED);
SET_STATE(req, R_MUST_MAP);
req->op = SCHED_NOP;
@@ -1065,8 +1076,11 @@
/* resolve conflicting regions based on priority */
if (overlap_r->max_prio < prio) {
if (min == max) {
- pr_err("ocmem: Requires eviction support\n");
- goto err_not_supported;
+ req->req_start = zone->z_head;
+ req->req_end = zone->z_head + sz - 1;
+ req->req_sz = 0x0;
+ req->edata = NULL;
+ goto trigger_eviction;
} else {
/* Try to allocate atleast >= 'min' immediately */
sz -= step;
@@ -1109,6 +1123,11 @@
return OP_COMPLETE;
+trigger_eviction:
+ pr_debug("Trigger eviction of region %p\n", overlap_r);
+ destroy_region(region);
+ return OP_EVICT;
+
err_not_supported:
pr_err("ocmem: Scheduled unsupported operation\n");
return OP_FAIL;
@@ -1134,6 +1153,37 @@
return 0;
}
+static void sched_dequeue(struct ocmem_req *victim_req)
+{
+ struct ocmem_req *req = NULL;
+ struct ocmem_req *next = NULL;
+ int id;
+
+ if (!victim_req)
+ return;
+
+ id = victim_req->owner;
+
+ mutex_lock(&sched_queue_mutex);
+
+ if (list_empty(&sched_queue[id]))
+ goto dequeue_done;
+
+ list_for_each_entry_safe(req, next, &sched_queue[id], sched_list)
+ {
+ if (req == victim_req) {
+ pr_debug("ocmem: Cancelling pending request %p\n",
+ req);
+ list_del(&req->sched_list);
+ goto dequeue_done;
+ }
+ }
+
+dequeue_done:
+ mutex_unlock(&sched_queue_mutex);
+ return;
+}
+
static struct ocmem_req *ocmem_fetch_req(void)
{
int i;
@@ -1219,12 +1269,9 @@
if (rc < 0)
return -EINVAL;
- /* Map the newly grown region */
- if (is_tcm(req->owner)) {
- rc = process_map(req, req->req_start, req->req_end);
- if (rc < 0)
- return -EINVAL;
- }
+ rc = process_map(req, req->req_start, req->req_end);
+ if (rc < 0)
+ return -EINVAL;
offset = phys_to_offset(req->req_start);
@@ -1281,8 +1328,23 @@
static int ocmem_schedule_pending(void)
{
- schedule_delayed_work(&ocmem_sched_thread,
- msecs_to_jiffies(SCHED_DELAY));
+
+ bool need_sched = false;
+ int i = 0;
+
+ for (i = MIN_PRIO; i < MAX_OCMEM_PRIO; i++) {
+ if (!list_empty(&sched_queue[i])) {
+ need_sched = true;
+ break;
+ }
+ }
+
+ if (need_sched == true) {
+ cancel_delayed_work(&ocmem_sched_thread);
+ schedule_delayed_work(&ocmem_sched_thread,
+ msecs_to_jiffies(SCHED_DELAY));
+ pr_debug("ocmem: Scheduled delayed work\n");
+ }
return 0;
}
@@ -1298,6 +1360,8 @@
goto err_free_fail;
}
+ pr_debug("ocmem: do_free: client %s req %p\n", get_name(req->owner),
+ req);
/* Grab the sched mutex */
mutex_lock(&sched_mutex);
rc = __sched_free(req);
@@ -1346,10 +1410,19 @@
return -EINVAL;
}
- if (is_tcm(req->owner)) {
+ mutex_lock(&sched_mutex);
+ sched_dequeue(req);
+ mutex_unlock(&sched_mutex);
+
+ if (!TEST_STATE(req, R_FREE)) {
+
rc = process_unmap(req, req->req_start, req->req_end);
if (rc < 0)
return -EINVAL;
+
+ rc = do_free(req);
+ if (rc < 0)
+ return -EINVAL;
}
if (req->req_sz != 0) {
@@ -1365,10 +1438,6 @@
}
- rc = do_free(req);
- if (rc < 0)
- return -EINVAL;
-
inc_ocmem_stat(zone_of(req), NR_FREES);
ocmem_destroy_req(req);
@@ -1437,18 +1506,10 @@
return -EINVAL;
if (!is_mapped(req)) {
- pr_err("Buffer is not already mapped\n");
+ pr_err("Buffer is not currently mapped\n");
goto transfer_out_error;
}
- rc = process_unmap(req, req->req_start, req->req_end);
- if (rc < 0) {
- pr_err("Unmapping the buffer failed\n");
- goto transfer_out_error;
- }
-
- inc_ocmem_stat(zone_of(req), NR_TRANSFERS_TO_DDR);
-
rc = queue_transfer(req, handle, list, TO_DDR);
if (rc < 0) {
@@ -1457,6 +1518,7 @@
goto transfer_out_error;
}
+ inc_ocmem_stat(zone_of(req), NR_TRANSFERS_TO_DDR);
return 0;
transfer_out_error:
@@ -1474,19 +1536,14 @@
if (!req)
return -EINVAL;
- if (is_mapped(req)) {
- pr_err("Buffer is already mapped\n");
+
+ if (!is_mapped(req)) {
+ pr_err("Buffer is not already mapped for transfer\n");
goto transfer_in_error;
}
- rc = process_map(req, req->req_start, req->req_end);
- if (rc < 0) {
- pr_err("Mapping the buffer failed\n");
- goto transfer_in_error;
- }
inc_ocmem_stat(zone_of(req), NR_TRANSFERS_TO_OCMEM);
-
rc = queue_transfer(req, handle, list, TO_OCMEM);
if (rc < 0) {
@@ -1525,13 +1582,21 @@
edata = req->edata;
- if (is_tcm(req->owner))
- do_unmap(req);
+ if (!edata) {
+ pr_err("Unable to find eviction data\n");
+ return -EINVAL;
+ }
+
+ pr_debug("Found edata %p in request %p\n", edata, req);
inc_ocmem_stat(zone_of(req), NR_SHRINKS);
if (size == 0) {
- pr_info("req %p being shrunk to zero\n", req);
+ pr_debug("req %p being shrunk to zero\n", req);
+ if (is_mapped(req))
+ rc = process_unmap(req, req->req_start, req->req_end);
+ if (rc < 0)
+ return -EINVAL;
rc = do_free(req);
if (rc < 0)
return -EINVAL;
@@ -1541,9 +1606,12 @@
return -EINVAL;
}
- edata->pending--;
- if (edata->pending == 0) {
- pr_debug("All regions evicted");
+ req->edata = NULL;
+ CLEAR_STATE(req, R_ALLOCATED);
+ SET_STATE(req, R_FREE);
+
+ if (atomic_dec_and_test(&edata->pending)) {
+ pr_debug("ocmem: All conflicting allocations were shrunk\n");
complete(&edata->completion);
}
@@ -1567,82 +1635,313 @@
return rc;
}
-int ocmem_eviction_thread(struct work_struct *work)
+static struct ocmem_eviction_data *init_eviction(int id)
{
+ struct ocmem_eviction_data *edata = NULL;
+ int prio = ocmem_client_table[id].priority;
+
+ edata = kzalloc(sizeof(struct ocmem_eviction_data), GFP_ATOMIC);
+
+ if (!edata) {
+ pr_err("ocmem: Could not allocate eviction data\n");
+ return NULL;
+ }
+
+ INIT_LIST_HEAD(&edata->victim_list);
+ INIT_LIST_HEAD(&edata->req_list);
+ edata->prio = prio;
+ atomic_set(&edata->pending, 0);
+ return edata;
+}
+
+static void free_eviction(struct ocmem_eviction_data *edata)
+{
+
+ if (!edata)
+ return;
+
+ if (!list_empty(&edata->req_list))
+ pr_err("ocmem: Eviction data %p not empty\n", edata);
+
+ kfree(edata);
+ edata = NULL;
+}
+
+static bool is_overlapping(struct ocmem_req *new, struct ocmem_req *old)
+{
+
+ if (!new || !old)
+ return false;
+
+ pr_debug("check overlap [%lx -- %lx] on [%lx -- %lx]\n",
+ new->req_start, new->req_end,
+ old->req_start, old->req_end);
+
+ if ((new->req_start < old->req_start &&
+ new->req_end >= old->req_start) ||
+ (new->req_start >= old->req_start &&
+ new->req_start <= old->req_end &&
+ new->req_end >= old->req_end)) {
+ pr_debug("request %p overlaps with existing req %p\n",
+ new, old);
+ return true;
+ }
+ return false;
+}
+
+static int __evict_common(struct ocmem_eviction_data *edata,
+ struct ocmem_req *req)
+{
+ struct rb_node *rb_node = NULL;
+ struct ocmem_req *e_req = NULL;
+ bool needs_eviction = false;
+ int j = 0;
+
+ for (rb_node = rb_first(&sched_tree); rb_node;
+ rb_node = rb_next(rb_node)) {
+
+ struct ocmem_region *tmp_region = NULL;
+
+ tmp_region = rb_entry(rb_node, struct ocmem_region, region_rb);
+
+ if (tmp_region->max_prio < edata->prio) {
+ for (j = edata->prio - 1; j > NO_PRIO; j--) {
+ needs_eviction = false;
+ e_req = find_req_match(j, tmp_region);
+ if (!e_req)
+ continue;
+ if (edata->passive == true) {
+ needs_eviction = true;
+ } else {
+ needs_eviction = is_overlapping(req,
+ e_req);
+ }
+
+ if (needs_eviction) {
+ pr_debug("adding %p in region %p to eviction list\n",
+ e_req, tmp_region);
+ list_add_tail(
+ &e_req->eviction_list,
+ &edata->req_list);
+ atomic_inc(&edata->pending);
+ e_req->edata = edata;
+ }
+ }
+ } else {
+ pr_debug("Skipped region %p\n", tmp_region);
+ }
+ }
+
+ pr_debug("%d requests will be evicted\n", atomic_read(&edata->pending));
+
+ if (!atomic_read(&edata->pending))
+ return -EINVAL;
return 0;
}
+static void trigger_eviction(struct ocmem_eviction_data *edata)
+{
+ struct ocmem_req *req = NULL;
+ struct ocmem_req *next = NULL;
+ struct ocmem_buf buffer;
+
+ if (!edata)
+ return;
+
+ BUG_ON(atomic_read(&edata->pending) == 0);
+
+ init_completion(&edata->completion);
+
+ list_for_each_entry_safe(req, next, &edata->req_list, eviction_list)
+ {
+ if (req) {
+ pr_debug("ocmem: Evicting request %p\n", req);
+ buffer.addr = req->req_start;
+ buffer.len = 0x0;
+ dispatch_notification(req->owner, OCMEM_ALLOC_SHRINK,
+ &buffer);
+ }
+ }
+ return;
+}
+
int process_evict(int id)
{
struct ocmem_eviction_data *edata = NULL;
- int prio = ocmem_client_table[id].priority;
- struct rb_node *rb_node = NULL;
- struct ocmem_req *req = NULL;
- struct ocmem_buf buffer;
- int j = 0;
+ int rc = 0;
- edata = kzalloc(sizeof(struct ocmem_eviction_data), GFP_ATOMIC);
+ edata = init_eviction(id);
- INIT_LIST_HEAD(&edata->victim_list);
- INIT_LIST_HEAD(&edata->req_list);
- edata->prio = prio;
- edata->pending = 0;
- edata->passive = 1;
- evictions[id] = edata;
+ if (!edata)
+ return -EINVAL;
+
+ edata->passive = true;
mutex_lock(&sched_mutex);
- for (rb_node = rb_first(&sched_tree); rb_node;
- rb_node = rb_next(rb_node)) {
- struct ocmem_region *tmp_region = NULL;
- tmp_region = rb_entry(rb_node, struct ocmem_region, region_rb);
- if (tmp_region->max_prio < prio) {
- for (j = id - 1; j > NO_PRIO; j--) {
- req = find_req_match(j, tmp_region);
- if (req) {
- pr_info("adding %p to eviction list\n",
- tmp_region);
- list_add_tail(
- &tmp_region->eviction_list,
- &edata->victim_list);
- list_add_tail(
- &req->eviction_list,
- &edata->req_list);
- edata->pending++;
- req->edata = edata;
- buffer.addr = req->req_start;
- buffer.len = 0x0;
- inc_ocmem_stat(zone_of(req),
- NR_EVICTIONS);
- dispatch_notification(req->owner,
- OCMEM_ALLOC_SHRINK, &buffer);
- }
- }
- } else {
- pr_info("skipping %p from eviction\n", tmp_region);
+ rc = __evict_common(edata, NULL);
+
+ if (rc < 0)
+ goto skip_eviction;
+
+ trigger_eviction(edata);
+
+ evictions[id] = edata;
+
+ mutex_unlock(&sched_mutex);
+
+ wait_for_completion(&edata->completion);
+
+ return 0;
+
+skip_eviction:
+ evictions[id] = NULL;
+ mutex_unlock(&sched_mutex);
+ return 0;
+}
+
+static int run_evict(struct ocmem_req *req)
+{
+ struct ocmem_eviction_data *edata = NULL;
+ int rc = 0;
+
+ if (!req)
+ return -EINVAL;
+
+ edata = init_eviction(req->owner);
+
+ if (!edata)
+ return -EINVAL;
+
+ edata->passive = false;
+
+ rc = __evict_common(edata, req);
+
+ if (rc < 0)
+ goto skip_eviction;
+
+ trigger_eviction(edata);
+
+ pr_debug("ocmem: attaching eviction %p to request %p", edata, req);
+ req->edata = edata;
+
+ wait_for_completion(&edata->completion);
+
+ pr_debug("ocmem: eviction completed successfully\n");
+ return 0;
+
+skip_eviction:
+ pr_err("ocmem: Unable to run eviction\n");
+ free_eviction(edata);
+ return -EINVAL;
+}
+
+static int __restore_common(struct ocmem_eviction_data *edata)
+{
+
+ struct ocmem_req *req = NULL;
+ struct ocmem_req *next = NULL;
+
+ if (!edata)
+ return -EINVAL;
+
+ list_for_each_entry_safe(req, next, &edata->req_list, eviction_list)
+ {
+ if (req) {
+ pr_debug("ocmem: restoring evicted request %p\n",
+ req);
+ list_del(&req->eviction_list);
+ req->op = SCHED_ALLOCATE;
+ sched_enqueue(req);
+ inc_ocmem_stat(zone_of(req), NR_RESTORES);
}
}
- mutex_unlock(&sched_mutex);
- pr_debug("Waiting for all regions to be shrunk\n");
- if (edata->pending > 0) {
- init_completion(&edata->completion);
- wait_for_completion(&edata->completion);
+
+ pr_debug("Scheduled all evicted regions\n");
+
+ return 0;
+}
+
+static int sched_restore(struct ocmem_req *req)
+{
+
+ int rc = 0;
+
+ if (!req)
+ return -EINVAL;
+
+ if (!req->edata)
+ return 0;
+
+ rc = __restore_common(req->edata);
+
+ if (rc < 0)
+ return -EINVAL;
+
+ free_eviction(req->edata);
+ return 0;
+}
+
+int process_restore(int id)
+{
+ struct ocmem_eviction_data *edata = evictions[id];
+ int rc = 0;
+
+ if (!edata)
+ return -EINVAL;
+
+ rc = __restore_common(edata);
+
+ if (rc < 0) {
+ pr_err("Failed to restore evicted requests\n");
+ return -EINVAL;
}
+
+ free_eviction(edata);
+ evictions[id] = NULL;
+ ocmem_schedule_pending();
return 0;
}
static int do_allocate(struct ocmem_req *req, bool can_block, bool can_wait)
{
int rc = 0;
+ int ret = 0;
struct ocmem_buf *buffer = req->buffer;
down_write(&req->rw_sem);
+ mutex_lock(&allocation_mutex);
+retry_allocate:
+
/* Take the scheduler mutex */
mutex_lock(&sched_mutex);
rc = __sched_allocate(req, can_block, can_wait);
mutex_unlock(&sched_mutex);
+ if (rc == OP_EVICT) {
+
+ ret = run_evict(req);
+
+ if (ret == 0) {
+ rc = sched_restore(req);
+ if (rc < 0) {
+ pr_err("Failed to restore for req %p\n", req);
+ goto err_allocate_fail;
+ }
+ req->edata = NULL;
+
+ pr_debug("Attempting to re-allocate req %p\n", req);
+ req->req_start = 0x0;
+ req->req_end = 0x0;
+ goto retry_allocate;
+ } else {
+ goto err_allocate_fail;
+ }
+ }
+
+ mutex_unlock(&allocation_mutex);
+
if (rc == OP_FAIL) {
inc_ocmem_stat(zone_of(req), NR_ALLOCATION_FAILS);
goto err_allocate_fail;
@@ -1667,6 +1966,7 @@
up_write(&req->rw_sem);
return 0;
err_allocate_fail:
+ mutex_unlock(&allocation_mutex);
up_write(&req->rw_sem);
return -EINVAL;
}
@@ -1699,33 +1999,6 @@
return -EINVAL;
}
-int process_restore(int id)
-{
- struct ocmem_req *req = NULL;
- struct ocmem_req *next = NULL;
- struct ocmem_eviction_data *edata = evictions[id];
-
- if (!edata)
- return 0;
-
- list_for_each_entry_safe(req, next, &edata->req_list, eviction_list)
- {
- if (req) {
- pr_debug("ocmem: Fetched evicted request %p\n",
- req);
- list_del(&req->sched_list);
- req->op = SCHED_ALLOCATE;
- sched_enqueue(req);
- inc_ocmem_stat(zone_of(req), NR_RESTORES);
- }
- }
- kfree(edata);
- evictions[id] = NULL;
- pr_debug("Restore all evicted regions\n");
- ocmem_schedule_pending();
- return 0;
-}
-
int process_allocate(int id, struct ocmem_handle *handle,
unsigned long min, unsigned long max,
unsigned long step, bool can_block, bool can_wait)
@@ -1774,13 +2047,11 @@
handle->req = req;
- if (is_tcm(id)) {
+ if (req->req_sz != 0) {
+
rc = process_map(req, req->req_start, req->req_end);
if (rc < 0)
goto map_error;
- }
-
- if (req->req_sz != 0) {
offset = phys_to_offset(req->req_start);
@@ -1795,6 +2066,7 @@
return 0;
power_ctl_error:
+ process_unmap(req, req->req_start, req->req_end);
map_error:
handle->req = NULL;
do_free(req);
@@ -1819,15 +2091,18 @@
if (rc < 0)
goto do_allocate_error;
+ /* The request can still be pending */
+ if (TEST_STATE(req, R_PENDING))
+ return 0;
+
inc_ocmem_stat(zone_of(req), NR_ASYNC_ALLOCATIONS);
- if (is_tcm(id)) {
+ if (req->req_sz != 0) {
+
rc = process_map(req, req->req_start, req->req_end);
if (rc < 0)
goto map_error;
- }
- if (req->req_sz != 0) {
offset = phys_to_offset(req->req_start);
@@ -1849,6 +2124,7 @@
return 0;
power_ctl_error:
+ process_unmap(req, req->req_start, req->req_end);
map_error:
handle->req = NULL;
do_free(req);
@@ -1965,6 +2241,7 @@
sched_tree = RB_ROOT;
pdata = platform_get_drvdata(pdev);
+ mutex_init(&allocation_mutex);
mutex_init(&sched_mutex);
mutex_init(&sched_queue_mutex);
ocmem_vaddr = pdata->vbase;
diff --git a/arch/arm/mach-msm/pil-mba.c b/arch/arm/mach-msm/pil-mba.c
index 8432328..daafd1d 100644
--- a/arch/arm/mach-msm/pil-mba.c
+++ b/arch/arm/mach-msm/pil-mba.c
@@ -57,6 +57,7 @@
void __iomem *metadata_base;
unsigned long metadata_phys;
struct pil_device *pil;
+ struct pil_desc desc;
struct subsys_device *subsys;
struct subsys_desc subsys_desc;
struct clk *xo;
@@ -294,6 +295,23 @@
return IRQ_HANDLED;
}
+static int mss_start(const struct subsys_desc *desc)
+{
+ void *ret;
+ struct mba_data *drv = subsys_to_drv(desc);
+
+ ret = pil_get(drv->desc.name);
+ if (IS_ERR(ret))
+ return PTR_ERR(ret);
+ return 0;
+}
+
+static void mss_stop(const struct subsys_desc *desc)
+{
+ struct mba_data *drv = subsys_to_drv(desc);
+ pil_put(drv->pil);
+}
+
static int __devinit pil_mba_driver_probe(struct platform_device *pdev)
{
struct mba_data *drv;
@@ -328,10 +346,7 @@
drv->metadata_phys = res->start;
}
- desc = devm_kzalloc(&pdev->dev, sizeof(*desc), GFP_KERNEL);
- if (!drv)
- return -ENOMEM;
-
+ desc = &drv->desc;
ret = of_property_read_string(pdev->dev.of_node, "qcom,firmware-name",
&desc->name);
if (ret)
@@ -360,6 +375,8 @@
drv->subsys_desc.powerup = modem_powerup;
drv->subsys_desc.ramdump = modem_ramdump;
drv->subsys_desc.crash_shutdown = modem_crash_shutdown;
+ drv->subsys_desc.start = mss_start;
+ drv->subsys_desc.stop = mss_stop;
drv->ramdump_dev = create_ramdump_device("modem");
if (!drv->ramdump_dev) {
diff --git a/arch/arm/mach-msm/pil-modem.c b/arch/arm/mach-msm/pil-modem.c
index ecb3800..ad27cd1 100644
--- a/arch/arm/mach-msm/pil-modem.c
+++ b/arch/arm/mach-msm/pil-modem.c
@@ -342,6 +342,26 @@
return NOTIFY_DONE;
}
+static int modem_start(const struct subsys_desc *subsys)
+{
+ void *ret;
+ struct modem_data *drv;
+
+ drv = container_of(subsys, struct modem_data, subsys_desc);
+ ret = pil_get("modem");
+ if (IS_ERR(ret))
+ return PTR_ERR(ret);
+ return 0;
+}
+
+static void modem_stop(const struct subsys_desc *subsys)
+{
+ struct modem_data *drv;
+
+ drv = container_of(subsys, struct modem_data, subsys_desc);
+ pil_put(drv->pil);
+}
+
static int modem_shutdown(const struct subsys_desc *subsys)
{
struct modem_data *drv;
@@ -467,6 +487,11 @@
goto err_notify;
drv->subsys_desc.name = "modem";
+ drv->subsys_desc.depends_on = "q6";
+ drv->subsys_desc.dev = &pdev->dev;
+ drv->subsys_desc.owner = THIS_MODULE;
+ drv->subsys_desc.start = modem_start;
+ drv->subsys_desc.stop = modem_stop;
drv->subsys_desc.shutdown = modem_shutdown;
drv->subsys_desc.powerup = modem_powerup;
drv->subsys_desc.ramdump = modem_ramdump;
diff --git a/arch/arm/mach-msm/pil-pronto.c b/arch/arm/mach-msm/pil-pronto.c
index 5685787..04b3a21 100644
--- a/arch/arm/mach-msm/pil-pronto.c
+++ b/arch/arm/mach-msm/pil-pronto.c
@@ -27,6 +27,7 @@
#include <linux/workqueue.h>
#include <linux/wcnss_wlan.h>
+#include <mach/peripheral-loader.h>
#include <mach/subsystem_restart.h>
#include <mach/peripheral-loader.h>
#include <mach/msm_smsm.h>
@@ -75,6 +76,7 @@
void __iomem *axi_halt_base;
unsigned long start_addr;
struct pil_device *pil;
+ struct pil_desc desc;
struct subsys_device *subsys;
struct subsys_desc subsys_desc;
struct clk *cxo;
@@ -241,6 +243,23 @@
#define subsys_to_drv(d) container_of(d, struct pronto_data, subsys_desc)
+static int pronto_start(const struct subsys_desc *desc)
+{
+ void *ret;
+ struct pronto_data *drv = subsys_to_drv(desc);
+
+ ret = pil_get(drv->desc.name);
+ if (IS_ERR(ret))
+ return PTR_ERR(ret);
+ return 0;
+}
+
+static void pronto_stop(const struct subsys_desc *desc)
+{
+ struct pronto_data *drv = subsys_to_drv(desc);
+ pil_put(drv->pil);
+}
+
static void log_wcnss_sfr(void)
{
char *smem_reset_reason;
@@ -401,10 +420,7 @@
drv->axi_halt_base = devm_ioremap(&pdev->dev, res->start,
resource_size(res));
- desc = devm_kzalloc(&pdev->dev, sizeof(*desc), GFP_KERNEL);
- if (!desc)
- return -ENOMEM;
-
+ desc = &drv->desc;
ret = of_property_read_string(pdev->dev.of_node, "qcom,firmware-name",
&desc->name);
if (ret)
@@ -456,6 +472,8 @@
drv->subsys_desc.powerup = wcnss_powerup;
drv->subsys_desc.ramdump = wcnss_ramdump;
drv->subsys_desc.crash_shutdown = crash_shutdown;
+ drv->subsys_desc.start = pronto_start;
+ drv->subsys_desc.stop = pronto_stop;
INIT_DELAYED_WORK(&drv->cancel_vote_work, wcnss_post_bootup);
diff --git a/arch/arm/mach-msm/pil-q6v3.c b/arch/arm/mach-msm/pil-q6v3.c
index 1a226de..9de9c60 100644
--- a/arch/arm/mach-msm/pil-q6v3.c
+++ b/arch/arm/mach-msm/pil-q6v3.c
@@ -248,6 +248,26 @@
pr_info("Q6 NMI was sent.\n");
}
+static int lpass_q6_start(const struct subsys_desc *subsys)
+{
+ void *ret;
+ struct q6v3_data *drv;
+
+ drv = container_of(subsys, struct q6v3_data, subsys_desc);
+ ret = pil_get("q6");
+ if (IS_ERR(ret))
+ return PTR_ERR(ret);
+ return 0;
+}
+
+static void lpass_q6_stop(const struct subsys_desc *subsys)
+{
+ struct q6v3_data *drv;
+
+ drv = container_of(subsys, struct q6v3_data, subsys_desc);
+ pil_put(drv->pil);
+}
+
static int lpass_q6_shutdown(const struct subsys_desc *subsys)
{
struct q6v3_data *drv;
@@ -377,6 +397,10 @@
return PTR_ERR(drv->pil);
drv->subsys_desc.name = "lpass";
+ drv->subsys_desc.dev = &pdev->dev;
+ drv->subsys_desc.owner = THIS_MODULE;
+ drv->subsys_desc.start = lpass_q6_start;
+ drv->subsys_desc.stop = lpass_q6_stop;
drv->subsys_desc.shutdown = lpass_q6_shutdown;
drv->subsys_desc.powerup = lpass_q6_powerup;
drv->subsys_desc.ramdump = lpass_q6_ramdump;
diff --git a/arch/arm/mach-msm/pil-q6v4-lpass.c b/arch/arm/mach-msm/pil-q6v4-lpass.c
index a0432550..8164d64 100644
--- a/arch/arm/mach-msm/pil-q6v4-lpass.c
+++ b/arch/arm/mach-msm/pil-q6v4-lpass.c
@@ -43,6 +43,7 @@
void *ramdump_dev;
struct work_struct work;
int loadable;
+ void *pil;
};
static int pil_q6v4_lpass_boot(struct pil_desc *pil)
@@ -192,6 +193,25 @@
#define subsys_to_lpass(d) container_of(d, struct lpass_q6v4, subsys_desc)
+static int lpass_start(const struct subsys_desc *desc)
+{
+ struct lpass_q6v4 *drv = subsys_to_lpass(desc);
+
+ if (drv->loadable) {
+ drv->pil = pil_get("q6");
+ if (IS_ERR(drv->pil))
+ return PTR_ERR(drv->pil);
+ }
+ return 0;
+}
+
+static void lpass_stop(const struct subsys_desc *desc)
+{
+ struct lpass_q6v4 *drv = subsys_to_lpass(desc);
+ if (drv->loadable)
+ pil_put(drv->pil);
+}
+
static int lpass_shutdown(const struct subsys_desc *subsys)
{
struct lpass_q6v4 *drv = subsys_to_lpass(subsys);
@@ -306,6 +326,10 @@
}
drv->subsys_desc.name = "lpass";
+ drv->subsys_desc.dev = &pdev->dev;
+ drv->subsys_desc.owner = THIS_MODULE;
+ drv->subsys_desc.start = lpass_start;
+ drv->subsys_desc.stop = lpass_stop;
drv->subsys_desc.shutdown = lpass_shutdown;
drv->subsys_desc.powerup = lpass_powerup;
drv->subsys_desc.ramdump = lpass_ramdump;
diff --git a/arch/arm/mach-msm/pil-q6v4-mss.c b/arch/arm/mach-msm/pil-q6v4-mss.c
index 61b5536..fe8c3b1 100644
--- a/arch/arm/mach-msm/pil-q6v4-mss.c
+++ b/arch/arm/mach-msm/pil-q6v4-mss.c
@@ -48,6 +48,7 @@
struct subsys_desc subsys_desc;
int crash_shutdown;
int loadable;
+ void *pil;
};
static DEFINE_MUTEX(pil_q6v4_modem_lock);
@@ -169,6 +170,25 @@
#define desc_to_modem(d) container_of(d, struct q6v4_modem, subsys_desc)
+static int modem_start(const struct subsys_desc *desc)
+{
+ struct q6v4_modem *drv = desc_to_modem(desc);
+
+ if (drv->loadable) {
+ drv->pil = pil_get("modem");
+ if (IS_ERR(drv->pil))
+ return PTR_ERR(drv->pil);
+ }
+ return 0;
+}
+
+static void modem_stop(const struct subsys_desc *desc)
+{
+ struct q6v4_modem *drv = desc_to_modem(desc);
+ if (drv->loadable)
+ pil_put(drv->pil);
+}
+
static int modem_shutdown(const struct subsys_desc *subsys)
{
struct q6v4_modem *drv = desc_to_modem(subsys);
@@ -396,6 +416,11 @@
}
drv->subsys_desc.name = "modem";
+ drv->subsys_desc.depends_on = "lpass";
+ drv->subsys_desc.dev = &pdev->dev;
+ drv->subsys_desc.owner = THIS_MODULE;
+ drv->subsys_desc.start = modem_start;
+ drv->subsys_desc.stop = modem_stop;
drv->subsys_desc.shutdown = modem_shutdown;
drv->subsys_desc.powerup = modem_powerup;
drv->subsys_desc.ramdump = modem_ramdump;
diff --git a/arch/arm/mach-msm/pil-q6v5-lpass.c b/arch/arm/mach-msm/pil-q6v5-lpass.c
index c48ea02..3c68da0 100644
--- a/arch/arm/mach-msm/pil-q6v5-lpass.c
+++ b/arch/arm/mach-msm/pil-q6v5-lpass.c
@@ -337,6 +337,23 @@
return IRQ_HANDLED;
}
+static int lpass_start(const struct subsys_desc *desc)
+{
+ void *ret;
+ struct lpass_data *drv = subsys_to_drv(desc);
+
+ ret = pil_get(drv->q6->desc.name);
+ if (IS_ERR(ret))
+ return PTR_ERR(ret);
+ return 0;
+}
+
+static void lpass_stop(const struct subsys_desc *desc)
+{
+ struct lpass_data *drv = subsys_to_drv(desc);
+ pil_put(drv->q6->pil);
+}
+
static int __devinit pil_lpass_driver_probe(struct platform_device *pdev)
{
struct lpass_data *drv;
@@ -397,6 +414,8 @@
drv->subsys_desc.powerup = adsp_powerup;
drv->subsys_desc.ramdump = adsp_ramdump;
drv->subsys_desc.crash_shutdown = adsp_crash_shutdown;
+ drv->subsys_desc.start = lpass_start;
+ drv->subsys_desc.stop = lpass_stop;
INIT_WORK(&drv->work, adsp_fatal_fn);
diff --git a/arch/arm/mach-msm/pil-riva.c b/arch/arm/mach-msm/pil-riva.c
index dbb4408..a0e39ea 100644
--- a/arch/arm/mach-msm/pil-riva.c
+++ b/arch/arm/mach-msm/pil-riva.c
@@ -372,6 +372,27 @@
wcnss_wlan_power(&pdev->dev, pwlanconfig, WCNSS_WLAN_SWITCH_OFF);
}
+static int riva_start(const struct subsys_desc *desc)
+{
+ void *ret;
+ struct riva_data *drv;
+
+ drv = container_of(desc, struct riva_data, subsys_desc);
+
+ ret = pil_get("wcnss");
+ if (IS_ERR(ret))
+ return PTR_ERR(ret);
+ return 0;
+}
+
+static void riva_stop(const struct subsys_desc *desc)
+{
+ struct riva_data *drv;
+
+ drv = container_of(desc, struct riva_data, subsys_desc);
+ pil_put(drv->pil);
+}
+
static int riva_shutdown(const struct subsys_desc *desc)
{
struct riva_data *drv;
@@ -515,6 +536,10 @@
goto err_smsm;
drv->subsys_desc.name = "wcnss";
+ drv->subsys_desc.dev = &pdev->dev;
+ drv->subsys_desc.owner = THIS_MODULE;
+ drv->subsys_desc.start = riva_start;
+ drv->subsys_desc.stop = riva_stop;
drv->subsys_desc.shutdown = riva_shutdown;
drv->subsys_desc.powerup = riva_powerup;
drv->subsys_desc.ramdump = riva_ramdump;
diff --git a/arch/arm/mach-msm/pil-tzapps.c b/arch/arm/mach-msm/pil-tzapps.c
index 2345453..be78fab 100644
--- a/arch/arm/mach-msm/pil-tzapps.c
+++ b/arch/arm/mach-msm/pil-tzapps.c
@@ -16,9 +16,18 @@
#include <linux/elf.h>
#include <linux/err.h>
+#include <mach/peripheral-loader.h>
+#include <mach/subsystem_restart.h>
+
#include "peripheral-loader.h"
#include "scm-pas.h"
+struct tzapps_data {
+ struct pil_device *pil;
+ struct subsys_device *subsys;
+ struct subsys_desc subsys_desc;
+};
+
static int pil_tzapps_init_image(struct pil_desc *pil, const u8 *metadata,
size_t size)
{
@@ -41,10 +50,28 @@
.shutdown = pil_tzapps_shutdown,
};
+#define subsys_to_drv(d) container_of(d, struct tzapps_data, subsys_desc)
+
+static int tzapps_start(const struct subsys_desc *desc)
+{
+ void *ret;
+
+ ret = pil_get("tzapps");
+ if (IS_ERR(ret))
+ return PTR_ERR(ret);
+ return 0;
+}
+
+static void tzapps_stop(const struct subsys_desc *desc)
+{
+ struct tzapps_data *drv = subsys_to_drv(desc);
+ pil_put(drv->pil);
+}
+
static int __devinit pil_tzapps_driver_probe(struct platform_device *pdev)
{
struct pil_desc *desc;
- struct pil_device *pil;
+ struct tzapps_data *drv;
if (pas_supported(PAS_TZAPPS) < 0)
return -ENOSYS;
@@ -53,21 +80,38 @@
if (!desc)
return -ENOMEM;
+ drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
+ if (!drv)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, drv);
+
desc->name = "tzapps";
desc->dev = &pdev->dev;
desc->ops = &pil_tzapps_ops;
desc->owner = THIS_MODULE;
- pil = msm_pil_register(desc);
- if (IS_ERR(pil))
- return PTR_ERR(pil);
- platform_set_drvdata(pdev, pil);
+ drv->pil = msm_pil_register(desc);
+ if (IS_ERR(drv->pil))
+ return PTR_ERR(drv->pil);
+
+ drv->subsys_desc.name = "tzapps";
+ drv->subsys_desc.dev = &pdev->dev;
+ drv->subsys_desc.owner = THIS_MODULE;
+ drv->subsys_desc.start = tzapps_start;
+ drv->subsys_desc.stop = tzapps_stop;
+
+ drv->subsys = subsys_register(&drv->subsys_desc);
+ if (IS_ERR(drv->subsys)) {
+ msm_pil_unregister(drv->pil);
+ return PTR_ERR(drv->subsys);
+ }
return 0;
}
static int __devexit pil_tzapps_driver_exit(struct platform_device *pdev)
{
- struct pil_device *pil = platform_get_drvdata(pdev);
- msm_pil_unregister(pil);
+ struct tzapps_data *drv = platform_get_drvdata(pdev);
+ subsys_unregister(drv->subsys);
+ msm_pil_unregister(drv->pil);
return 0;
}
diff --git a/arch/arm/mach-msm/pil-venus.c b/arch/arm/mach-msm/pil-venus.c
index e331296..e3125cf 100644
--- a/arch/arm/mach-msm/pil-venus.c
+++ b/arch/arm/mach-msm/pil-venus.c
@@ -28,6 +28,8 @@
#include <mach/iommu.h>
#include <mach/iommu_domains.h>
+#include <mach/subsystem_restart.h>
+#include <mach/peripheral-loader.h>
#include "peripheral-loader.h"
#include "scm-pas.h"
@@ -66,6 +68,8 @@
void __iomem *venus_wrapper_base;
void __iomem *venus_vbif_base;
struct pil_device *pil;
+ struct subsys_device *subsys;
+ struct subsys_desc subsys_desc;
struct regulator *gdsc;
phys_addr_t start_addr;
struct clk *clks[ARRAY_SIZE(clk_names)];
@@ -78,6 +82,8 @@
u32 fw_max_paddr;
};
+#define subsys_to_drv(d) container_of(d, struct venus_data, subsys_desc)
+
static int venus_register_domain(u32 fw_max_sz)
{
struct msm_iova_partition venus_fw_partition = {
@@ -381,6 +387,23 @@
.proxy_unvote = pil_venus_remove_proxy_vote,
};
+static int venus_start(const struct subsys_desc *desc)
+{
+ void *ret;
+ struct venus_data *drv = subsys_to_drv(desc);
+
+ ret = pil_get(drv->subsys_desc.name);
+ if (IS_ERR(ret))
+ return PTR_ERR(ret);
+ return 0;
+}
+
+static void venus_stop(const struct subsys_desc *desc)
+{
+ struct venus_data *drv = subsys_to_drv(desc);
+ pil_put(drv->pil);
+}
+
static int __devinit pil_venus_probe(struct platform_device *pdev)
{
struct venus_data *drv;
@@ -488,12 +511,25 @@
if (IS_ERR(drv->pil))
return PTR_ERR(drv->pil);
+ drv->subsys_desc.name = desc->name;
+ drv->subsys_desc.owner = THIS_MODULE;
+ drv->subsys_desc.dev = &pdev->dev;
+ drv->subsys_desc.start = venus_start;
+ drv->subsys_desc.stop = venus_stop;
+
+ drv->subsys = subsys_register(&drv->subsys_desc);
+ if (IS_ERR(drv->subsys)) {
+ msm_pil_unregister(drv->pil);
+ return PTR_ERR(drv->subsys);
+ }
+
return 0;
}
static int __devexit pil_venus_remove(struct platform_device *pdev)
{
struct venus_data *drv = platform_get_drvdata(pdev);
+ subsys_unregister(drv->subsys);
msm_pil_unregister(drv->pil);
return 0;
diff --git a/arch/arm/mach-msm/pil-vidc.c b/arch/arm/mach-msm/pil-vidc.c
index e4c6a2d..b2609b1 100644
--- a/arch/arm/mach-msm/pil-vidc.c
+++ b/arch/arm/mach-msm/pil-vidc.c
@@ -17,6 +17,9 @@
#include <linux/err.h>
#include <linux/clk.h>
+#include <mach/peripheral-loader.h>
+#include <mach/subsystem_restart.h>
+
#include "peripheral-loader.h"
#include "scm-pas.h"
@@ -24,6 +27,8 @@
struct clk *smmu_iface;
struct clk *core;
struct pil_device *pil;
+ struct subsys_device *subsys;
+ struct subsys_desc subsys_desc;
};
static int pil_vidc_init_image(struct pil_desc *pil, const u8 *metadata,
@@ -63,6 +68,24 @@
.shutdown = pil_vidc_shutdown,
};
+#define subsys_to_drv(d) container_of(d, struct vidc_data, subsys_desc)
+
+static int vidc_start(const struct subsys_desc *desc)
+{
+ void *ret;
+
+ ret = pil_get("vidc");
+ if (IS_ERR(ret))
+ return PTR_ERR(ret);
+ return 0;
+}
+
+static void vidc_stop(const struct subsys_desc *desc)
+{
+ struct vidc_data *drv = subsys_to_drv(desc);
+ pil_put(drv->pil);
+}
+
static int __devinit pil_vidc_driver_probe(struct platform_device *pdev)
{
struct pil_desc *desc;
@@ -95,12 +118,25 @@
drv->pil = msm_pil_register(desc);
if (IS_ERR(drv->pil))
return PTR_ERR(drv->pil);
+
+ drv->subsys_desc.name = "vidc";
+ drv->subsys_desc.dev = &pdev->dev;
+ drv->subsys_desc.owner = THIS_MODULE;
+ drv->subsys_desc.start = vidc_start;
+ drv->subsys_desc.stop = vidc_stop;
+
+ drv->subsys = subsys_register(&drv->subsys_desc);
+ if (IS_ERR(drv->subsys)) {
+ msm_pil_unregister(drv->pil);
+ return PTR_ERR(drv->subsys);
+ }
return 0;
}
static int __devexit pil_vidc_driver_exit(struct platform_device *pdev)
{
struct vidc_data *drv = platform_get_drvdata(pdev);
+ subsys_unregister(drv->subsys);
msm_pil_unregister(drv->pil);
return 0;
}
diff --git a/arch/arm/mach-msm/qdsp6v2/adsprpc.c b/arch/arm/mach-msm/qdsp6v2/adsprpc.c
index 49008de..822da91 100644
--- a/arch/arm/mach-msm/qdsp6v2/adsprpc.c
+++ b/arch/arm/mach-msm/qdsp6v2/adsprpc.c
@@ -11,6 +11,7 @@
* GNU General Public License for more details.
*
*/
+#include <linux/scatterlist.h>
#include "adsprpc.h"
struct smq_invoke_ctx {
@@ -70,14 +71,25 @@
static int alloc_mem(struct fastrpc_buf *buf)
{
struct ion_client *clnt = gfa.iclient;
+ struct sg_table *sg;
int err = 0;
buf->handle = ion_alloc(clnt, buf->size, SZ_4K,
ION_HEAP(ION_AUDIO_HEAP_ID), 0);
- VERIFY(0 == IS_ERR_OR_NULL(buf->handle));
+ VERIFY(err, 0 == IS_ERR_OR_NULL(buf->handle));
+ if (err)
+ goto bail;
buf->virt = 0;
- VERIFY(0 != (buf->virt = ion_map_kernel(clnt, buf->handle)));
- VERIFY(0 == ion_phys(clnt, buf->handle, &buf->phys, &buf->size));
+ VERIFY(err, 0 != (buf->virt = ion_map_kernel(clnt, buf->handle)));
+ if (err)
+ goto bail;
+ VERIFY(err, 0 != (sg = ion_sg_table(clnt, buf->handle)));
+ if (err)
+ goto bail;
+ VERIFY(err, 1 == sg->nents);
+ if (err)
+ goto bail;
+ buf->phys = sg_dma_address(sg->sgl);
bail:
if (err && !IS_ERR_OR_NULL(buf->handle))
free_mem(buf);
@@ -87,7 +99,9 @@
static int context_list_ctor(struct smq_context_list *me, int size)
{
int err = 0;
- VERIFY(0 != (me->ls = kzalloc(size, GFP_KERNEL)));
+ VERIFY(err, 0 != (me->ls = kzalloc(size, GFP_KERNEL)));
+ if (err)
+ goto bail;
me->size = size / sizeof(*me->ls);
me->last = 0;
bail:
@@ -103,18 +117,18 @@
static void context_list_alloc_ctx(struct smq_context_list *me,
struct smq_invoke_ctx **po)
{
- int ii = me->last;
+ int i = me->last;
struct smq_invoke_ctx *ctx;
for (;;) {
- ii = ii % me->size;
- ctx = &me->ls[ii];
+ i = i % me->size;
+ ctx = &me->ls[i];
if (atomic_read(&ctx->free) == 0)
- if (0 == atomic_cmpxchg(&ctx->free, 0, 1))
+ if (atomic_cmpxchg(&ctx->free, 0, 1) == 0)
break;
- ii++;
+ i++;
}
- me->last = ii;
+ me->last = i;
ctx->retval = -1;
init_completion(&ctx->work);
*po = ctx;
@@ -134,13 +148,13 @@
static void context_notify_all_users(struct smq_context_list *me)
{
- int ii;
+ int i;
if (!me->ls)
return;
- for (ii = 0; ii < me->size; ++ii) {
- if (atomic_read(&me->ls[ii].free) != 0)
- complete(&me->ls[ii].work);
+ for (i = 0; i < me->size; ++i) {
+ if (atomic_read(&me->ls[i].free) != 0)
+ complete(&me->ls[i].work);
}
}
@@ -149,11 +163,10 @@
{
struct smq_phy_page *pgstart, *pages;
struct smq_invoke_buf *list;
- int ii, rlen, err = 0;
+ int i, rlen, err = 0;
int inbufs = REMOTE_SCALARS_INBUFS(sc);
int outbufs = REMOTE_SCALARS_OUTBUFS(sc);
- VERIFY(0 != try_module_get(THIS_MODULE));
LOCK_MMAP(kernel);
*obuf = *ibuf;
retry:
@@ -165,38 +178,46 @@
rlen = ((uint32_t)pages - (uint32_t)obuf->virt) - obuf->size;
obuf->size += buf_page_size(rlen);
obuf->handle = 0;
- VERIFY(0 == alloc_mem(obuf));
+ VERIFY(err, 0 == alloc_mem(obuf));
+ if (err)
+ goto bail;
goto retry;
}
pgstart->addr = obuf->phys;
pgstart->size = obuf->size;
- for (ii = 0; ii < inbufs + outbufs; ++ii) {
+ for (i = 0; i < inbufs + outbufs; ++i) {
void *buf;
int len, num;
- len = pra[ii].buf.len;
+ list[i].num = 0;
+ list[i].pgidx = 0;
+ len = pra[i].buf.len;
if (!len)
continue;
- buf = pra[ii].buf.pv;
+ buf = pra[i].buf.pv;
num = buf_num_pages(buf, len);
if (!kernel)
- list[ii].num = buf_get_pages(buf, len, num,
- ii >= inbufs, pages, rlen / sizeof(*pages));
+ list[i].num = buf_get_pages(buf, len, num,
+ i >= inbufs, pages, rlen / sizeof(*pages));
else
- list[ii].num = 0;
- VERIFY(list[ii].num >= 0);
- if (list[ii].num) {
- list[ii].pgidx = pages - pgstart;
- pages = pages + list[ii].num;
+ list[i].num = 0;
+ VERIFY(err, list[i].num >= 0);
+ if (err)
+ goto bail;
+ if (list[i].num) {
+ list[i].pgidx = pages - pgstart;
+ pages = pages + list[i].num;
} else if (rlen > sizeof(*pages)) {
- list[ii].pgidx = pages - pgstart;
+ list[i].pgidx = pages - pgstart;
pages = pages + 1;
} else {
if (obuf->handle != ibuf->handle)
free_mem(obuf);
obuf->size += buf_page_size(sizeof(*pages));
obuf->handle = 0;
- VERIFY(0 == alloc_mem(obuf));
+ VERIFY(err, 0 == alloc_mem(obuf));
+ if (err)
+ goto bail;
goto retry;
}
rlen = obuf->size - ((uint32_t) pages - (uint32_t) obuf->virt);
@@ -206,7 +227,6 @@
if (err && (obuf->handle != ibuf->handle))
free_mem(obuf);
UNLOCK_MMAP(kernel);
- module_put(THIS_MODULE);
return err;
}
@@ -219,74 +239,86 @@
struct fastrpc_buf *pbuf = ibuf, *obufs = 0;
struct smq_phy_page *pages;
void *args;
- int ii, rlen, size, used, inh, bufs = 0, err = 0;
+ int i, rlen, size, used, inh, bufs = 0, err = 0;
int inbufs = REMOTE_SCALARS_INBUFS(sc);
int outbufs = REMOTE_SCALARS_OUTBUFS(sc);
list = smq_invoke_buf_start(rpra, sc);
pages = smq_phy_page_start(sc, list);
- used = ALIGN_8(pbuf->used);
+ used = ALIGN(pbuf->used, BALIGN);
args = (void *)((char *)pbuf->virt + used);
rlen = pbuf->size - used;
- for (ii = 0; ii < inbufs + outbufs; ++ii) {
+ for (i = 0; i < inbufs + outbufs; ++i) {
int num;
- rpra[ii].buf.len = pra[ii].buf.len;
- if (list[ii].num) {
- rpra[ii].buf.pv = pra[ii].buf.pv;
+ rpra[i].buf.len = pra[i].buf.len;
+ if (!rpra[i].buf.len)
+ continue;
+ if (list[i].num) {
+ rpra[i].buf.pv = pra[i].buf.pv;
continue;
}
- if (rlen < pra[ii].buf.len) {
+ if (rlen < pra[i].buf.len) {
struct fastrpc_buf *b;
pbuf->used = pbuf->size - rlen;
- VERIFY(0 != (b = krealloc(obufs,
+ VERIFY(err, 0 != (b = krealloc(obufs,
(bufs + 1) * sizeof(*obufs), GFP_KERNEL)));
+ if (err)
+ goto bail;
obufs = b;
pbuf = obufs + bufs;
- pbuf->size = buf_num_pages(0, pra[ii].buf.len) *
+ pbuf->size = buf_num_pages(0, pra[i].buf.len) *
PAGE_SIZE;
- VERIFY(0 == alloc_mem(pbuf));
+ VERIFY(err, 0 == alloc_mem(pbuf));
+ if (err)
+ goto bail;
bufs++;
args = pbuf->virt;
rlen = pbuf->size;
}
- num = buf_num_pages(args, pra[ii].buf.len);
+ num = buf_num_pages(args, pra[i].buf.len);
if (pbuf == ibuf) {
- list[ii].num = num;
- list[ii].pgidx = 0;
+ list[i].num = num;
+ list[i].pgidx = 0;
} else {
- list[ii].num = 1;
- pages[list[ii].pgidx].addr =
+ list[i].num = 1;
+ pages[list[i].pgidx].addr =
buf_page_start((void *)(pbuf->phys +
(pbuf->size - rlen)));
- pages[list[ii].pgidx].size =
- buf_page_size(pra[ii].buf.len);
+ pages[list[i].pgidx].size =
+ buf_page_size(pra[i].buf.len);
}
- if (ii < inbufs) {
- if (!kernel)
- VERIFY(0 == copy_from_user(args, pra[ii].buf.pv,
- pra[ii].buf.len));
- else
- memmove(args, pra[ii].buf.pv, pra[ii].buf.len);
+ if (i < inbufs) {
+ if (!kernel) {
+ VERIFY(err, 0 == copy_from_user(args,
+ pra[i].buf.pv, pra[i].buf.len));
+ if (err)
+ goto bail;
+ } else {
+ memmove(args, pra[i].buf.pv, pra[i].buf.len);
+ }
}
- rpra[ii].buf.pv = args;
- args = (void *)((char *)args + ALIGN_8(pra[ii].buf.len));
- rlen -= ALIGN_8(pra[ii].buf.len);
+ rpra[i].buf.pv = args;
+ args = (void *)((char *)args + ALIGN(pra[i].buf.len, BALIGN));
+ rlen -= ALIGN(pra[i].buf.len, BALIGN);
}
- for (ii = 0; ii < inbufs; ++ii) {
- if (rpra[ii].buf.len)
- dmac_flush_range(rpra[ii].buf.pv,
- (char *)rpra[ii].buf.pv + rpra[ii].buf.len);
+ for (i = 0; i < inbufs; ++i) {
+ if (rpra[i].buf.len)
+ dmac_flush_range(rpra[i].buf.pv,
+ (char *)rpra[i].buf.pv + rpra[i].buf.len);
}
pbuf->used = pbuf->size - rlen;
size = sizeof(*rpra) * REMOTE_SCALARS_INHANDLES(sc);
if (size) {
inh = inbufs + outbufs;
- if (!kernel)
- VERIFY(0 == copy_from_user(&rpra[inh], &upra[inh],
+ if (!kernel) {
+ VERIFY(err, 0 == copy_from_user(&rpra[inh], &upra[inh],
size));
- else
+ if (err)
+ goto bail;
+ } else {
memmove(&rpra[inh], &upra[inh], size);
+ }
}
dmac_flush_range(rpra, (char *)rpra + used);
bail:
@@ -298,24 +330,30 @@
static int put_args(uint32_t kernel, uint32_t sc, remote_arg_t *pra,
remote_arg_t *rpra, remote_arg_t *upra)
{
- int ii, inbufs, outbufs, outh, size;
+ int i, inbufs, outbufs, outh, size;
int err = 0;
inbufs = REMOTE_SCALARS_INBUFS(sc);
outbufs = REMOTE_SCALARS_OUTBUFS(sc);
- for (ii = inbufs; ii < inbufs + outbufs; ++ii) {
- if (rpra[ii].buf.pv != pra[ii].buf.pv)
- VERIFY(0 == copy_to_user(pra[ii].buf.pv,
- rpra[ii].buf.pv, rpra[ii].buf.len));
+ for (i = inbufs; i < inbufs + outbufs; ++i) {
+ if (rpra[i].buf.pv != pra[i].buf.pv) {
+ VERIFY(err, 0 == copy_to_user(pra[i].buf.pv,
+ rpra[i].buf.pv, rpra[i].buf.len));
+ if (err)
+ goto bail;
+ }
}
size = sizeof(*rpra) * REMOTE_SCALARS_OUTHANDLES(sc);
if (size) {
outh = inbufs + outbufs + REMOTE_SCALARS_INHANDLES(sc);
- if (!kernel)
- VERIFY(0 == copy_to_user(&upra[outh], &rpra[outh],
+ if (!kernel) {
+ VERIFY(err, 0 == copy_to_user(&upra[outh], &rpra[outh],
size));
- else
+ if (err)
+ goto bail;
+ } else {
memmove(&upra[outh], &rpra[outh], size);
+ }
}
bail:
return err;
@@ -323,24 +361,24 @@
static void inv_args(uint32_t sc, remote_arg_t *rpra, int used)
{
- int ii, inbufs, outbufs;
+ int i, inbufs, outbufs;
int inv = 0;
inbufs = REMOTE_SCALARS_INBUFS(sc);
outbufs = REMOTE_SCALARS_OUTBUFS(sc);
- for (ii = inbufs; ii < inbufs + outbufs; ++ii) {
- if (buf_page_start(rpra) == buf_page_start(rpra[ii].buf.pv))
+ for (i = inbufs; i < inbufs + outbufs; ++i) {
+ if (buf_page_start(rpra) == buf_page_start(rpra[i].buf.pv))
inv = 1;
- else
- dmac_inv_range(rpra[ii].buf.pv,
- (char *)rpra[ii].buf.pv + rpra[ii].buf.len);
+ else if (rpra[i].buf.len)
+ dmac_inv_range(rpra[i].buf.pv,
+ (char *)rpra[i].buf.pv + rpra[i].buf.len);
}
if (inv || REMOTE_SCALARS_OUTHANDLES(sc))
dmac_inv_range(rpra, (char *)rpra + used);
}
-static int fastrpc_invoke_send(struct fastrpc_apps *me, remote_handle_t handle,
+static int fastrpc_invoke_send(struct fastrpc_apps *me, uint32_t handle,
uint32_t sc, struct smq_invoke_ctx *ctx,
struct fastrpc_buf *buf)
{
@@ -357,8 +395,7 @@
spin_lock(&me->wrlock);
len = smd_write(me->chan, &msg, sizeof(msg));
spin_unlock(&me->wrlock);
- VERIFY(len == sizeof(msg));
- bail:
+ VERIFY(err, len == sizeof(msg));
return err;
}
@@ -369,7 +406,8 @@
if (me->chan)
(void)smd_close(me->chan);
context_list_dtor(&me->clst);
- ion_client_destroy(me->iclient);
+ if (me->iclient)
+ ion_client_destroy(me->iclient);
me->iclient = 0;
me->chan = 0;
}
@@ -381,8 +419,10 @@
int err = 0;
do {
- VERIFY(sizeof(rsp) ==
+ VERIFY(err, sizeof(rsp) ==
smd_read_from_cb(me->chan, &rsp, sizeof(rsp)));
+ if (err)
+ goto bail;
context_notify_user(rsp.ctx, rsp.retval);
} while (!err);
bail:
@@ -412,21 +452,29 @@
struct fastrpc_apps *me = &gfa;
if (me->chan == 0) {
- int ii;
+ int i;
spin_lock_init(&me->hlock);
spin_lock_init(&me->wrlock);
init_completion(&me->work);
- for (ii = 0; ii < RPC_HASH_SZ; ++ii)
- INIT_HLIST_HEAD(&me->htbl[ii]);
- VERIFY(0 == context_list_ctor(&me->clst, SZ_4K));
+ for (i = 0; i < RPC_HASH_SZ; ++i)
+ INIT_HLIST_HEAD(&me->htbl[i]);
+ VERIFY(err, 0 == context_list_ctor(&me->clst, SZ_4K));
+ if (err)
+ goto bail;
me->iclient = msm_ion_client_create(ION_HEAP_CARVEOUT_MASK,
DEVICE_NAME);
- VERIFY(0 == IS_ERR_OR_NULL(me->iclient));
- VERIFY(0 == smd_named_open_on_edge(FASTRPC_SMD_GUID,
+ VERIFY(err, 0 == IS_ERR_OR_NULL(me->iclient));
+ if (err)
+ goto bail;
+ VERIFY(err, 0 == smd_named_open_on_edge(FASTRPC_SMD_GUID,
SMD_APPS_QDSP, &me->chan,
me, smd_event_handler));
- VERIFY(0 != wait_for_completion_timeout(&me->work,
+ if (err)
+ goto bail;
+ VERIFY(err, 0 != wait_for_completion_timeout(&me->work,
RPC_TIMEOUT));
+ if (err)
+ goto bail;
}
bail:
if (err)
@@ -448,10 +496,16 @@
int err = 0;
struct fastrpc_device *fd = 0;
- VERIFY(0 != try_module_get(THIS_MODULE));
- VERIFY(0 != (fd = kzalloc(sizeof(*fd), GFP_KERNEL)));
+ VERIFY(err, 0 != try_module_get(THIS_MODULE));
+ if (err)
+ goto bail;
+ VERIFY(err, 0 != (fd = kzalloc(sizeof(*fd), GFP_KERNEL)));
+ if (err)
+ goto bail;
fd->buf.size = PAGE_SIZE;
- VERIFY(0 == alloc_mem(&fd->buf));
+ VERIFY(err, 0 == alloc_mem(&fd->buf));
+ if (err)
+ goto bail;
fd->tgid = current->tgid;
INIT_HLIST_NODE(&fd->hn);
*dev = fd;
@@ -478,7 +532,9 @@
}
}
spin_unlock(&me->hlock);
- VERIFY(dev != 0);
+ VERIFY(err, dev != 0);
+ if (err)
+ goto bail;
*rdev = dev;
bail:
if (err) {
@@ -511,34 +567,49 @@
struct fastrpc_buf obuf, *abufs = 0, *b;
int interrupted = 0;
uint32_t sc;
- int ii, nbufs = 0, err = 0;
+ int i, nbufs = 0, err = 0;
sc = invoke->sc;
obuf.handle = 0;
if (REMOTE_SCALARS_LENGTH(sc)) {
- VERIFY(0 == get_dev(me, &dev));
- VERIFY(0 == get_page_list(kernel, sc, pra, &dev->buf, &obuf));
+ VERIFY(err, 0 == get_dev(me, &dev));
+ if (err)
+ goto bail;
+ VERIFY(err, 0 == get_page_list(kernel, sc, pra, &dev->buf,
+ &obuf));
+ if (err)
+ goto bail;
rpra = (remote_arg_t *)obuf.virt;
- VERIFY(0 == get_args(kernel, sc, pra, rpra, invoke->pra, &obuf,
- &abufs, &nbufs));
+ VERIFY(err, 0 == get_args(kernel, sc, pra, rpra, invoke->pra,
+ &obuf, &abufs, &nbufs));
+ if (err)
+ goto bail;
}
context_list_alloc_ctx(&me->clst, &ctx);
- VERIFY(0 == fastrpc_invoke_send(me, invoke->handle, sc, ctx, &obuf));
+ VERIFY(err, 0 == fastrpc_invoke_send(me, invoke->handle, sc, ctx,
+ &obuf));
+ if (err)
+ goto bail;
inv_args(sc, rpra, obuf.used);
- VERIFY(0 == (interrupted =
+ VERIFY(err, 0 == (interrupted =
wait_for_completion_interruptible(&ctx->work)));
- VERIFY(0 == (err = ctx->retval));
- VERIFY(0 == put_args(kernel, sc, pra, rpra, invoke->pra));
+ if (err)
+ goto bail;
+ VERIFY(err, 0 == (err = ctx->retval));
+ if (err)
+ goto bail;
+ VERIFY(err, 0 == put_args(kernel, sc, pra, rpra, invoke->pra));
+ if (err)
+ goto bail;
bail:
if (interrupted) {
- init_completion(&ctx->work);
if (!kernel)
(void)fastrpc_release_current_dsp_process();
wait_for_completion(&ctx->work);
}
context_free(ctx);
- for (ii = 0, b = abufs; ii < nbufs; ++ii, ++b)
+ for (i = 0, b = abufs; i < nbufs; ++i, ++b)
free_mem(b);
kfree(abufs);
if (dev) {
@@ -563,8 +634,7 @@
ioctl.handle = 1;
ioctl.sc = REMOTE_SCALARS_MAKE(0, 1, 0);
ioctl.pra = ra;
- VERIFY(0 == fastrpc_internal_invoke(me, 1, &ioctl, ra));
- bail:
+ VERIFY(err, 0 == fastrpc_internal_invoke(me, 1, &ioctl, ra));
return err;
}
@@ -582,8 +652,7 @@
ioctl.handle = 1;
ioctl.sc = REMOTE_SCALARS_MAKE(1, 1, 0);
ioctl.pra = ra;
- VERIFY(0 == fastrpc_internal_invoke(me, 1, &ioctl, ra));
- bail:
+ VERIFY(err, 0 == fastrpc_internal_invoke(me, 1, &ioctl, ra));
return err;
}
@@ -628,8 +697,7 @@
/* This call will cause a dev to be created
* which will addref this module
*/
- VERIFY(0 == fastrpc_create_current_dsp_process());
- bail:
+ VERIFY(err, 0 == fastrpc_create_current_dsp_process());
if (err)
cleanup_current_dev();
module_put(THIS_MODULE);
@@ -649,19 +717,28 @@
switch (ioctl_num) {
case FASTRPC_IOCTL_INVOKE:
- VERIFY(0 == copy_from_user(&invoke, param, sizeof(invoke)));
+ VERIFY(err, 0 == copy_from_user(&invoke, param,
+ sizeof(invoke)));
+ if (err)
+ goto bail;
bufs = REMOTE_SCALARS_INBUFS(invoke.sc) +
REMOTE_SCALARS_OUTBUFS(invoke.sc);
if (bufs) {
bufs = bufs * sizeof(*pra);
- VERIFY(0 != (pra = kmalloc(bufs, GFP_KERNEL)));
+ VERIFY(err, 0 != (pra = kmalloc(bufs, GFP_KERNEL)));
+ if (err)
+ goto bail;
}
- VERIFY(0 == copy_from_user(pra, invoke.pra, bufs));
- VERIFY(0 == (err = fastrpc_internal_invoke(me, 0, &invoke,
+ VERIFY(err, 0 == copy_from_user(pra, invoke.pra, bufs));
+ if (err)
+ goto bail;
+ VERIFY(err, 0 == (err = fastrpc_internal_invoke(me, 0, &invoke,
pra)));
+ if (err)
+ goto bail;
break;
default:
- err = -EINVAL;
+ err = -ENOTTY;
break;
}
bail:
@@ -680,13 +757,24 @@
struct fastrpc_apps *me = &gfa;
int err = 0;
- VERIFY(0 == fastrpc_init());
- VERIFY(0 == alloc_chrdev_region(&me->dev_no, 0, 1, DEVICE_NAME));
+ VERIFY(err, 0 == fastrpc_init());
+ if (err)
+ goto bail;
+ VERIFY(err, 0 == alloc_chrdev_region(&me->dev_no, 0, 1, DEVICE_NAME));
+ if (err)
+ goto bail;
cdev_init(&me->cdev, &fops);
me->cdev.owner = THIS_MODULE;
- VERIFY(0 == cdev_add(&me->cdev, MKDEV(MAJOR(me->dev_no), 0), 1));
+ VERIFY(err, 0 == cdev_add(&me->cdev, MKDEV(MAJOR(me->dev_no), 0), 1));
+ if (err)
+ goto bail;
pr_info("'mknod /dev/%s c %d 0'\n", DEVICE_NAME, MAJOR(me->dev_no));
bail:
+ if (err) {
+ if (me->dev_no)
+ unregister_chrdev_region(me->dev_no, 1);
+ fastrpc_deinit();
+ }
return err;
}
diff --git a/arch/arm/mach-msm/qdsp6v2/adsprpc.h b/arch/arm/mach-msm/qdsp6v2/adsprpc.h
index c6c7d23..3f1b4a7 100644
--- a/arch/arm/mach-msm/qdsp6v2/adsprpc.h
+++ b/arch/arm/mach-msm/qdsp6v2/adsprpc.h
@@ -36,8 +36,7 @@
#define RPC_TIMEOUT (5 * HZ)
#define RPC_HASH_BITS 5
#define RPC_HASH_SZ (1 << RPC_HASH_BITS)
-
-#define ALIGN_8(a) ALIGN(a, 8)
+#define BALIGN 32
#define LOCK_MMAP(kernel)\
do {\
@@ -84,18 +83,26 @@
struct vm_area_struct *vma;
uint32_t start = buf_page_start(addr);
uint32_t len = nr_pages << PAGE_SHIFT;
- uint32_t pfn;
+ unsigned long pfn;
int n = -1, err = 0;
- VERIFY(0 != access_ok(access ? VERIFY_WRITE : VERIFY_READ,
+ VERIFY(err, 0 != access_ok(access ? VERIFY_WRITE : VERIFY_READ,
(void __user *)start, len));
- VERIFY(0 != (vma = find_vma(current->mm, start)));
- VERIFY(((uint32_t)addr + sz) <= vma->vm_end);
+ if (err)
+ goto bail;
+ VERIFY(err, 0 != (vma = find_vma(current->mm, start)));
+ if (err)
+ goto bail;
+ VERIFY(err, ((uint32_t)addr + sz) <= vma->vm_end);
+ if (err)
+ goto bail;
n = 0;
- VERIFY(0 != (vma->vm_flags & VM_PFNMAP));
- VERIFY(0 != (vma->vm_flags & VM_PFN_AT_MMAP));
- VERIFY(nr_elems > 0);
- pfn = vma->vm_pgoff + ((start - vma->vm_start) >> PAGE_SHIFT);
+ VERIFY(err, 0 == follow_pfn(vma, start, &pfn));
+ if (err)
+ goto bail;
+ VERIFY(err, nr_elems > 0);
+ if (err)
+ goto bail;
pages->addr = __pfn_to_phys(pfn);
pages->size = len;
n++;
diff --git a/arch/arm/mach-msm/qdsp6v2/adsprpc_shared.h b/arch/arm/mach-msm/qdsp6v2/adsprpc_shared.h
index 04b1d4a..dc6ab6f 100644
--- a/arch/arm/mach-msm/qdsp6v2/adsprpc_shared.h
+++ b/arch/arm/mach-msm/qdsp6v2/adsprpc_shared.h
@@ -60,20 +60,18 @@
#define __TOSTR__(x) __STR__(x)
#define __FILE_LINE__ __FILE__ ":" __TOSTR__(__LINE__)
-#define VERIFY(val) \
+#define VERIFY(err, val) \
do {\
VERIFY_IPRINTF(__FILE_LINE__"info: calling: " #val "\n");\
if (0 == (val)) {\
- err = err == 0 ? -1 : err;\
- VERIFY_EPRINTF(__FILE_LINE__"error: %d: " #val "\n", err);\
- goto bail;\
+ (err) = (err) == 0 ? -1 : (err);\
+ VERIFY_EPRINTF(__FILE_LINE__"error: %d: " #val "\n", (err));\
} else {\
VERIFY_IPRINTF(__FILE_LINE__"info: passed: " #val "\n");\
} \
} while (0)
#endif
-#define remote_handle_t uint32_t
#define remote_arg_t union remote_arg
struct remote_buf {
@@ -83,18 +81,18 @@
union remote_arg {
struct remote_buf buf; /* buffer info */
- remote_handle_t h; /* remote handle */
+ uint32_t h; /* remote handle */
};
struct fastrpc_ioctl_invoke {
- remote_handle_t handle; /* remote handle */
+ uint32_t handle; /* remote handle */
uint32_t sc; /* scalars describing the data */
remote_arg_t *pra; /* remote arguments list */
};
struct smq_null_invoke {
struct smq_invoke_ctx *ctx; /* invoke caller context */
- remote_handle_t handle; /* handle to invoke */
+ uint32_t handle; /* handle to invoke */
uint32_t sc; /* scalars structure describing the data */
};
diff --git a/arch/arm/mach-msm/socinfo.c b/arch/arm/mach-msm/socinfo.c
index b865daa..62085f6 100644
--- a/arch/arm/mach-msm/socinfo.c
+++ b/arch/arm/mach-msm/socinfo.c
@@ -272,6 +272,7 @@
/* 9625 IDs */
[134] = MSM_CPU_9625,
+ [152] = MSM_CPU_9625,
/* 8960AB IDs */
[138] = MSM_CPU_8960AB,
@@ -283,6 +284,7 @@
[142] = MSM_CPU_8930AA,
[143] = MSM_CPU_8930AA,
[144] = MSM_CPU_8930AA,
+ [160] = MSM_CPU_8930AA,
/* 8226 IDs */
[145] = MSM_CPU_8226,
diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c
index cb9fc76..bb4da0f 100644
--- a/arch/arm/mm/cache-l2x0.c
+++ b/arch/arm/mm/cache-l2x0.c
@@ -38,6 +38,8 @@
static unsigned int l2x0_ways;
static unsigned long sync_reg_offset = L2X0_CACHE_SYNC;
static void pl310_save(void);
+static void pl310_resume(void);
+static void l2x0_resume(void);
static inline bool is_pl310_rev(int rev)
{
@@ -378,15 +380,18 @@
sync_reg_offset = L2X0_DUMMY_REG;
#endif
outer_cache.set_debug = pl310_set_debug;
+ outer_cache.resume = pl310_resume;
break;
case L2X0_CACHE_ID_PART_L210:
l2x0_ways = (aux >> 13) & 0xf;
type = "L210";
+ outer_cache.resume = l2x0_resume;
break;
default:
/* Assume unknown chips have 8 ways */
l2x0_ways = 8;
type = "L2x0 series";
+ outer_cache.resume = l2x0_resume;
break;
}
diff --git a/block/test-iosched.c b/block/test-iosched.c
index 0a033dc..52070ac 100644
--- a/block/test-iosched.c
+++ b/block/test-iosched.c
@@ -95,6 +95,9 @@
return;
}
+ ptd->test_info.test_duration = jiffies -
+ ptd->test_info.test_duration;
+
test_pr_info("%s: Test is completed", __func__);
test_iosched_mark_test_completion();
@@ -124,7 +127,7 @@
test_rq = (struct test_request *)rq->elv.priv[0];
BUG_ON(!test_rq);
- test_pr_info("%s: request %d completed, err=%d",
+ test_pr_debug("%s: request %d completed, err=%d",
__func__, test_rq->req_id, err);
test_rq->req_completed = true;
@@ -669,6 +672,7 @@
goto error;
}
+ ptd->test_info.test_duration = jiffies;
ret = run_test(ptd);
if (ret) {
test_pr_err("%s: failed to run the test\n", __func__);
@@ -678,6 +682,7 @@
test_pr_info("%s: Waiting for the test completion", __func__);
wait_event(ptd->wait_q, ptd->test_state == TEST_COMPLETED);
+ t_info->test_duration = ptd->test_info.test_duration;
del_timer_sync(&ptd->timeout_timer);
ret = check_test_result(ptd);
diff --git a/drivers/char/diag/diagchar.h b/drivers/char/diag/diagchar.h
index a37260b..28d0565 100644
--- a/drivers/char/diag/diagchar.h
+++ b/drivers/char/diag/diagchar.h
@@ -257,6 +257,7 @@
int mask_check;
int logging_process_id;
struct task_struct *socket_process;
+ struct task_struct *callback_process;
#ifdef CONFIG_DIAG_SDIO_PIPE
unsigned char *buf_in_sdio;
unsigned char *usb_buf_mdm_out;
diff --git a/drivers/char/diag/diagchar_core.c b/drivers/char/diag/diagchar_core.c
index c29a1d3f..8a7ae9f 100644
--- a/drivers/char/diag/diagchar_core.c
+++ b/drivers/char/diag/diagchar_core.c
@@ -262,6 +262,10 @@
(driver->socket_process->tgid == current->tgid)) {
driver->socket_process = NULL;
}
+ if (driver->callback_process &&
+ (driver->callback_process->tgid == current->tgid)) {
+ driver->callback_process = NULL;
+ }
#ifdef CONFIG_DIAG_OVER_USB
/* If the SD logging process exits, change logging to USB mode */
@@ -522,6 +526,11 @@
mutex_lock(&driver->diagchar_mutex);
temp = driver->logging_mode;
driver->logging_mode = (int)ioarg;
+ if (temp == driver->logging_mode) {
+ mutex_unlock(&driver->diagchar_mutex);
+ pr_alert("diag: forbidden logging change requested\n");
+ return 0;
+ }
if (driver->logging_mode == MEMORY_DEVICE_MODE) {
diag_clear_hsic_tbl();
driver->mask_check = 1;
@@ -540,17 +549,17 @@
}
}
}
- if (driver->logging_mode == UART_MODE) {
+ if (driver->logging_mode == UART_MODE ||
+ driver->logging_mode == SOCKET_MODE ||
+ driver->logging_mode == CALLBACK_MODE) {
diag_clear_hsic_tbl();
driver->mask_check = 0;
driver->logging_mode = MEMORY_DEVICE_MODE;
}
- if (driver->logging_mode == SOCKET_MODE) {
- diag_clear_hsic_tbl();
+ if (driver->logging_mode == SOCKET_MODE)
driver->socket_process = current;
- driver->mask_check = 0;
- driver->logging_mode = MEMORY_DEVICE_MODE;
- }
+ if (driver->logging_mode == CALLBACK_MODE)
+ driver->callback_process = current;
driver->logging_process_id = current->tgid;
mutex_unlock(&driver->diagchar_mutex);
if (temp == MEMORY_DEVICE_MODE && driver->logging_mode
@@ -680,7 +689,7 @@
driver->data_ready[index]);
mutex_lock(&driver->diagchar_mutex);
- if ((driver->data_ready[index] & USER_SPACE_LOG_TYPE) && (driver->
+ if ((driver->data_ready[index] & USER_SPACE_DATA_TYPE) && (driver->
logging_mode == MEMORY_DEVICE_MODE)) {
#ifdef CONFIG_DIAG_BRIDGE_CODE
unsigned long spin_lock_flags;
@@ -689,7 +698,7 @@
pr_debug("diag: process woken up\n");
/*Copy the type of data being passed*/
- data_type = driver->data_ready[index] & USER_SPACE_LOG_TYPE;
+ data_type = driver->data_ready[index] & USER_SPACE_DATA_TYPE;
COPY_USER_SPACE_OR_EXIT(buf, data_type, 4);
/* place holder for number of data field */
ret += 4;
@@ -892,7 +901,7 @@
/* copy number of data fields */
COPY_USER_SPACE_OR_EXIT(buf+4, num_data, 4);
ret -= 4;
- driver->data_ready[index] ^= USER_SPACE_LOG_TYPE;
+ driver->data_ready[index] ^= USER_SPACE_DATA_TYPE;
if (driver->ch)
queue_work(driver->diag_wq,
&(driver->diag_read_smd_work));
@@ -909,10 +918,10 @@
#endif
APPEND_DEBUG('n');
goto exit;
- } else if (driver->data_ready[index] & USER_SPACE_LOG_TYPE) {
+ } else if (driver->data_ready[index] & USER_SPACE_DATA_TYPE) {
/* In case, the thread wakes up and the logging mode is
not memory device any more, the condition needs to be cleared */
- driver->data_ready[index] ^= USER_SPACE_LOG_TYPE;
+ driver->data_ready[index] ^= USER_SPACE_DATA_TYPE;
}
if (driver->data_ready[index] & DEINIT_TYPE) {
@@ -1026,7 +1035,7 @@
payload_size);
return err;
}
- if (pkt_type == USER_SPACE_LOG_TYPE) {
+ if (pkt_type == USER_SPACE_DATA_TYPE) {
err = copy_from_user(driver->user_space_data, buf + 4,
payload_size);
/* Check masks for On-Device logging */
@@ -1409,6 +1418,7 @@
driver->num_clients = max_clients;
driver->logging_mode = USB_MODE;
driver->socket_process = NULL;
+ driver->callback_process = NULL;
driver->mask_check = 0;
mutex_init(&driver->diagchar_mutex);
init_waitqueue_head(&driver->wait_q);
diff --git a/drivers/char/diag/diagfwd.c b/drivers/char/diag/diagfwd.c
index d27ebcf..978b63b 100644
--- a/drivers/char/diag/diagfwd.c
+++ b/drivers/char/diag/diagfwd.c
@@ -212,8 +212,8 @@
* have their data read/logged. Detect and remedy this
* situation.
*/
- if ((driver->data_ready[i] & USER_SPACE_LOG_TYPE) == 0) {
- driver->data_ready[i] |= USER_SPACE_LOG_TYPE;
+ if ((driver->data_ready[i] & USER_SPACE_DATA_TYPE) == 0) {
+ driver->data_ready[i] |= USER_SPACE_DATA_TYPE;
pr_debug("diag: Force wakeup of logging process\n");
wake_up_interruptible(&driver->wait_q);
}
@@ -357,7 +357,7 @@
driver->logging_process_id)
break;
if (i < driver->num_clients) {
- driver->data_ready[i] |= USER_SPACE_LOG_TYPE;
+ driver->data_ready[i] |= USER_SPACE_DATA_TYPE;
pr_debug("diag: wake up logging process\n");
wake_up_interruptible(&driver->wait_q);
} else
diff --git a/drivers/crypto/msm/qce50.c b/drivers/crypto/msm/qce50.c
index 8a9567b..1312448 100644
--- a/drivers/crypto/msm/qce50.c
+++ b/drivers/crypto/msm/qce50.c
@@ -382,7 +382,7 @@
break;
case QCE_MODE_XTS:
- if (creq->encklen == AES128_KEY_SIZE)
+ if (creq->encklen/2 == AES128_KEY_SIZE)
*cmdlistinfo = &cmdlistptr->cipher_aes_128_xts;
else
*cmdlistinfo = &cmdlistptr->cipher_aes_256_xts;
@@ -767,15 +767,30 @@
if ((pce_dev->mode == QCE_MODE_CTR) ||
(pce_dev->mode == QCE_MODE_XTS)) {
uint32_t num_blk = 0;
- uint32_t cntr_iv = 0;
+ uint32_t cntr_iv3 = 0;
+ unsigned long long cntr_iv64 = 0;
+ unsigned char *b = (unsigned char *)(&cntr_iv3);
memcpy(iv, areq->info, sizeof(iv));
- if (pce_dev->mode == QCE_MODE_CTR)
+ if (pce_dev->mode != QCE_MODE_XTS)
num_blk = areq->nbytes/16;
- cntr_iv = (u32)(((u32)(*(iv + 14))) << 8) |
- (u32)(*(iv + 15));
- *(iv + 14) = (char)((cntr_iv + num_blk) >> 8);
- *(iv + 15) = (char)((cntr_iv + num_blk) & 0xFF);
+ else
+ num_blk = 1;
+ cntr_iv3 = ((*(iv + 12) << 24) & 0xff000000) |
+ (((*(iv + 13)) << 16) & 0xff0000) |
+ (((*(iv + 14)) << 8) & 0xff00) |
+ (*(iv + 15) & 0xff);
+ cntr_iv64 =
+ (((unsigned long long)cntr_iv3 &
+ (unsigned long long)0xFFFFFFFFULL) +
+ (unsigned long long)num_blk) %
+ (unsigned long long)(0x100000000ULL);
+
+ cntr_iv3 = (u32)(cntr_iv64 & 0xFFFFFFFF);
+ *(iv + 15) = (char)(*b);
+ *(iv + 14) = (char)(*(b + 1));
+ *(iv + 13) = (char)(*(b + 2));
+ *(iv + 12) = (char)(*(b + 3));
}
} else {
memcpy(iv,
@@ -1456,10 +1471,11 @@
0, &pcl_info->encr_xts_key);
for (i = 1; i < xts_key_reg; i++)
qce_add_cmd_element(pdev, &ce_vaddr,
- (CRYPTO_ENCR_KEY0_REG + i * sizeof(uint32_t)),
- 0, NULL);
+ (CRYPTO_ENCR_XTS_KEY0_REG +
+ i * sizeof(uint32_t)), 0, NULL);
qce_add_cmd_element(pdev, &ce_vaddr,
- CRYPTO_ENCR_XTS_DU_SIZE_REG, 0, NULL);
+ CRYPTO_ENCR_XTS_DU_SIZE_REG, 0,
+ &pcl_info->encr_xts_du_size);
}
if (iv_reg) {
qce_add_cmd_element(pdev, &ce_vaddr, CRYPTO_CNTR0_IV0_REG, 0,
@@ -1624,13 +1640,6 @@
0, &pcl_info->auth_seg_size);
qce_add_cmd_element(pdev, &ce_vaddr, CRYPTO_AUTH_SEG_CFG_REG,
0, &pcl_info->auth_seg_size);
- } else {
- qce_add_cmd_element(pdev, &ce_vaddr, CRYPTO_AUTH_SEG_SIZE_REG,
- 0, &pcl_info->auth_seg_size);
- qce_add_cmd_element(pdev, &ce_vaddr, CRYPTO_AUTH_SEG_CFG_REG,
- 0, &pcl_info->auth_seg_size);
- qce_add_cmd_element(pdev, &ce_vaddr, CRYPTO_AUTH_SEG_START_REG,
- 0, &pcl_info->auth_seg_size);
}
qce_add_cmd_element(pdev, &ce_vaddr, CRYPTO_CONFIG_REG,
(crypto_cfg | CRYPTO_LITTLE_ENDIAN_MASK),
diff --git a/drivers/crypto/msm/qcryptohw_50.h b/drivers/crypto/msm/qcryptohw_50.h
index 1c904ed..d77311d 100644
--- a/drivers/crypto/msm/qcryptohw_50.h
+++ b/drivers/crypto/msm/qcryptohw_50.h
@@ -144,7 +144,7 @@
#define CRYPTO_ENCR_CCM_INT_CNTR2_REG 0x1A228
#define CRYPTO_ENCR_CCM_INT_CNTR3_REG 0x1A22C
-#define CRYPTO_ENCR_XTS_DU_SIZE_REG 0xA1230
+#define CRYPTO_ENCR_XTS_DU_SIZE_REG 0x1A230
#define CRYPTO_AUTH_SEG_CFG_REG 0x1A300
#define CRYPTO_AUTH_SEG_SIZE_REG 0x1A304
diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c
index d0817b2..67cb34a 100644
--- a/drivers/gpu/msm/adreno.c
+++ b/drivers/gpu/msm/adreno.c
@@ -1400,6 +1400,11 @@
} else {
adreno_context = context->devctxt;
adreno_context->flags |= CTXT_FLAGS_GPU_HANG;
+ /*
+ * set the invalid ts flag to 0 for this context since we have
+ * detected a hang for it
+ */
+ context->wait_on_invalid_ts = false;
}
/* Extract valid contents from rb which can still be executed after
@@ -1835,7 +1840,7 @@
goto err;
/* now, wait for the GPU to finish its operations */
- wait_time = jiffies + ADRENO_IDLE_TIMEOUT;
+ wait_time = jiffies + msecs_to_jiffies(ADRENO_IDLE_TIMEOUT);
wait_time_part = jiffies + msecs_to_jiffies(KGSL_TIMEOUT_PART);
while (time_before(jiffies, wait_time)) {
@@ -1864,18 +1869,46 @@
KGSL_DRV_ERR(device, "spun too long waiting for RB to idle\n");
if (KGSL_STATE_DUMP_AND_RECOVER != device->state &&
!adreno_dump_and_recover(device)) {
- wait_time = jiffies + ADRENO_IDLE_TIMEOUT;
+ wait_time = jiffies + msecs_to_jiffies(ADRENO_IDLE_TIMEOUT);
goto retry;
}
return -ETIMEDOUT;
}
+/**
+ * is_adreno_rbbm_status_idle - Check if GPU core is idle by probing
+ * rbbm_status register
+ * @device - Pointer to the GPU device whose idle status is to be
+ * checked
+ * @returns - Returns whether the core is idle (based on rbbm_status)
+ * false if the core is active, true if the core is idle
+ */
+static bool is_adreno_rbbm_status_idle(struct kgsl_device *device)
+{
+ unsigned int reg_rbbm_status;
+ bool status = false;
+ struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+
+ /* Is the core idle? */
+ adreno_regread(device,
+ adreno_dev->gpudev->reg_rbbm_status,
+ ®_rbbm_status);
+
+ if (adreno_is_a2xx(adreno_dev)) {
+ if (reg_rbbm_status == 0x110)
+ status = true;
+ } else {
+ if (!(reg_rbbm_status & 0x80000000))
+ status = true;
+ }
+ return status;
+}
+
static unsigned int adreno_isidle(struct kgsl_device *device)
{
int status = false;
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
struct adreno_ringbuffer *rb = &adreno_dev->ringbuffer;
- unsigned int rbbm_status;
WARN_ON(device->state == KGSL_STATE_INIT);
/* If the device isn't active, don't force it on. */
@@ -1884,17 +1917,7 @@
GSL_RB_GET_READPTR(rb, &rb->rptr);
if (!device->active_cnt && (rb->rptr == rb->wptr)) {
/* Is the core idle? */
- adreno_regread(device,
- adreno_dev->gpudev->reg_rbbm_status,
- &rbbm_status);
-
- if (adreno_is_a2xx(adreno_dev)) {
- if (rbbm_status == 0x110)
- status = true;
- } else {
- if (!(rbbm_status & 0x80000000))
- status = true;
- }
+ status = is_adreno_rbbm_status_idle(device);
}
} else {
status = true;
@@ -2137,7 +2160,7 @@
if (!adreno_dev->fast_hang_detect)
return 0;
- if (device->ftbl->isidle(device))
+ if (is_adreno_rbbm_status_idle(device))
return 0;
for (i = 0; i < hang_detect_regs_count; i++) {
@@ -2193,14 +2216,24 @@
if (timestamp_cmp(timestamp, ts_issued) > 0) {
if (adreno_ctx == NULL ||
!(adreno_ctx->flags & CTXT_FLAGS_USER_GENERATED_TS)) {
- KGSL_DRV_ERR(device,
+ if (context && !context->wait_on_invalid_ts) {
+ KGSL_DRV_ERR(device,
"Cannot wait for invalid ts <%d:0x%x>, "
"last issued ts <%d:0x%x>\n",
context_id, timestamp, context_id, ts_issued);
+ /*
+ * Prevent the above message from spamming the
+ * kernel logs and causing a watchdog
+ */
+ context->wait_on_invalid_ts = true;
+ }
status = -EINVAL;
goto done;
} else
retry_ts_cmp = 1;
+ } else if (context && context->wait_on_invalid_ts) {
+ /* Once we wait for a valid ts reset the invalid wait flag */
+ context->wait_on_invalid_ts = false;
}
/*
@@ -2276,13 +2309,19 @@
ts_issued =
adreno_dev->ringbuffer.timestamp[context_id];
if (timestamp_cmp(timestamp, ts_issued) > 0) {
- KGSL_DRV_ERR(device,
- "Cannot wait for user-generated ts <%d:0x%x>, "
- "not submitted within server timeout period. "
- "last issued ts <%d:0x%x>\n",
- context_id, timestamp, context_id, ts_issued);
+ if (context && !context->wait_on_invalid_ts) {
+ KGSL_DRV_ERR(device,
+ "Cannot wait for user-generated ts <%d:0x%x>, "
+ "not submitted within server timeout period. "
+ "last issued ts <%d:0x%x>\n",
+ context_id, timestamp, context_id,
+ ts_issued);
+ context->wait_on_invalid_ts = true;
+ }
status = -EINVAL;
goto done;
+ } else if (context && context->wait_on_invalid_ts) {
+ context->wait_on_invalid_ts = false;
}
retry_ts_cmp = 0;
}
diff --git a/drivers/gpu/msm/adreno_a2xx_snapshot.c b/drivers/gpu/msm/adreno_a2xx_snapshot.c
index 282440c..ce74c1b 100644
--- a/drivers/gpu/msm/adreno_a2xx_snapshot.c
+++ b/drivers/gpu/msm/adreno_a2xx_snapshot.c
@@ -224,6 +224,31 @@
return DEBUG_SECTION_SZ(MIUDEBUG_COUNT);
}
+/* Snapshot the istore memory */
+static int a2xx_snapshot_istore(struct kgsl_device *device, void *snapshot,
+ int remain, void *priv)
+{
+ struct kgsl_snapshot_istore *header = snapshot;
+ unsigned int *data = snapshot + sizeof(*header);
+ struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+ int count, i;
+
+ count = adreno_dev->istore_size * adreno_dev->instruction_size;
+
+ if (remain < (count * 4) + sizeof(*header)) {
+ KGSL_DRV_ERR(device,
+ "snapshot: Not enough memory for the istore section");
+ return 0;
+ }
+
+ header->count = adreno_dev->istore_size;
+
+ for (i = 0; i < count; i++)
+ kgsl_regread(device, ADRENO_ISTORE_START + i, &data[i]);
+
+ return (count * 4) + sizeof(*header);
+}
+
/* A2XX GPU snapshot function - this is where all of the A2XX specific
* bits and pieces are grabbed into the snapshot memory
*/
@@ -338,6 +363,18 @@
}
}
+ /*
+ * Only dump the istore on a hang - reading it on a running system
+ * has a non zero chance of hanging the GPU.
+ */
+
+ if (adreno_is_a2xx(adreno_dev) && hang) {
+ snapshot = kgsl_snapshot_add_section(device,
+ KGSL_SNAPSHOT_SECTION_ISTORE, snapshot, remain,
+ a2xx_snapshot_istore, NULL);
+ }
+
+
/* Reset the clock gating */
adreno_regwrite(device, REG_RBBM_PM_OVERRIDE2, pmoverride);
diff --git a/drivers/gpu/msm/adreno_a3xx_snapshot.c b/drivers/gpu/msm/adreno_a3xx_snapshot.c
index a410445..e4f5733 100644
--- a/drivers/gpu/msm/adreno_a3xx_snapshot.c
+++ b/drivers/gpu/msm/adreno_a3xx_snapshot.c
@@ -220,30 +220,46 @@
return DEBUG_SECTION_SZ(size);
}
-#define DEBUGFS_BLOCK_SIZE 0x40
+struct debugbus_block {
+ unsigned int block_id;
+ unsigned int dwords;
+};
static int a3xx_snapshot_debugbus_block(struct kgsl_device *device,
void *snapshot, int remain, void *priv)
{
+ struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+
struct kgsl_snapshot_debugbus *header = snapshot;
- unsigned int id = (unsigned int) priv;
+ struct debugbus_block *block = priv;
unsigned int val;
int i;
unsigned int *data = snapshot + sizeof(*header);
- int size =
- (DEBUGFS_BLOCK_SIZE * sizeof(unsigned int)) + sizeof(*header);
+ unsigned int dwords;
+ int size;
+
+ /*
+ * For A305 and A320 all debug bus regions are the same size (0x40). For
+ * A330, they can be different sizes - most are still 0x40, but some
+ * like CP are larger
+ */
+
+ dwords = adreno_is_a330(adreno_dev) ?
+ block->dwords : 0x40;
+
+ size = (dwords * sizeof(unsigned int)) + sizeof(*header);
if (remain < size) {
SNAPSHOT_ERR_NOMEM(device, "DEBUGBUS");
return 0;
}
- val = (id << 8) | (1 << 16);
+ val = (block->block_id << 8) | (1 << 16);
- header->id = id;
- header->count = DEBUGFS_BLOCK_SIZE;
+ header->id = block->block_id;
+ header->count = dwords;
- for (i = 0; i < DEBUGFS_BLOCK_SIZE; i++) {
+ for (i = 0; i < dwords; i++) {
adreno_regwrite(device, A3XX_RBBM_DEBUG_BUS_CTL, val | i);
adreno_regread(device, A3XX_RBBM_DEBUG_BUS_DATA_STATUS,
&data[i]);
@@ -252,34 +268,34 @@
return size;
}
-static unsigned int debugbus_blocks[] = {
- RBBM_BLOCK_ID_CP,
- RBBM_BLOCK_ID_RBBM,
- RBBM_BLOCK_ID_VBIF,
- RBBM_BLOCK_ID_HLSQ,
- RBBM_BLOCK_ID_UCHE,
- RBBM_BLOCK_ID_PC,
- RBBM_BLOCK_ID_VFD,
- RBBM_BLOCK_ID_VPC,
- RBBM_BLOCK_ID_TSE,
- RBBM_BLOCK_ID_RAS,
- RBBM_BLOCK_ID_VSC,
- RBBM_BLOCK_ID_SP_0,
- RBBM_BLOCK_ID_SP_1,
- RBBM_BLOCK_ID_SP_2,
- RBBM_BLOCK_ID_SP_3,
- RBBM_BLOCK_ID_TPL1_0,
- RBBM_BLOCK_ID_TPL1_1,
- RBBM_BLOCK_ID_TPL1_2,
- RBBM_BLOCK_ID_TPL1_3,
- RBBM_BLOCK_ID_RB_0,
- RBBM_BLOCK_ID_RB_1,
- RBBM_BLOCK_ID_RB_2,
- RBBM_BLOCK_ID_RB_3,
- RBBM_BLOCK_ID_MARB_0,
- RBBM_BLOCK_ID_MARB_1,
- RBBM_BLOCK_ID_MARB_2,
- RBBM_BLOCK_ID_MARB_3,
+static struct debugbus_block debugbus_blocks[] = {
+ { RBBM_BLOCK_ID_CP, 0x52, },
+ { RBBM_BLOCK_ID_RBBM, 0x40, },
+ { RBBM_BLOCK_ID_VBIF, 0x40, },
+ { RBBM_BLOCK_ID_HLSQ, 0x40, },
+ { RBBM_BLOCK_ID_UCHE, 0x40, },
+ { RBBM_BLOCK_ID_PC, 0x40, },
+ { RBBM_BLOCK_ID_VFD, 0x40, },
+ { RBBM_BLOCK_ID_VPC, 0x40, },
+ { RBBM_BLOCK_ID_TSE, 0x40, },
+ { RBBM_BLOCK_ID_RAS, 0x40, },
+ { RBBM_BLOCK_ID_VSC, 0x40, },
+ { RBBM_BLOCK_ID_SP_0, 0x40, },
+ { RBBM_BLOCK_ID_SP_1, 0x40, },
+ { RBBM_BLOCK_ID_SP_2, 0x40, },
+ { RBBM_BLOCK_ID_SP_3, 0x40, },
+ { RBBM_BLOCK_ID_TPL1_0, 0x40, },
+ { RBBM_BLOCK_ID_TPL1_1, 0x40, },
+ { RBBM_BLOCK_ID_TPL1_2, 0x40, },
+ { RBBM_BLOCK_ID_TPL1_3, 0x40, },
+ { RBBM_BLOCK_ID_RB_0, 0x40, },
+ { RBBM_BLOCK_ID_RB_1, 0x40, },
+ { RBBM_BLOCK_ID_RB_2, 0x40, },
+ { RBBM_BLOCK_ID_RB_3, 0x40, },
+ { RBBM_BLOCK_ID_MARB_0, 0x40, },
+ { RBBM_BLOCK_ID_MARB_1, 0x40, },
+ { RBBM_BLOCK_ID_MARB_2, 0x40, },
+ { RBBM_BLOCK_ID_MARB_3, 0x40, },
};
static void *a3xx_snapshot_debugbus(struct kgsl_device *device,
@@ -291,7 +307,7 @@
snapshot = kgsl_snapshot_add_section(device,
KGSL_SNAPSHOT_SECTION_DEBUGBUS, snapshot, remain,
a3xx_snapshot_debugbus_block,
- (void *) debugbus_blocks[i]);
+ (void *) &debugbus_blocks[i]);
}
return snapshot;
@@ -307,6 +323,7 @@
struct kgsl_device *device = &adreno_dev->dev;
struct kgsl_snapshot_registers_list list;
struct kgsl_snapshot_registers regs[2];
+ int size;
regs[0].regs = (unsigned int *) a3xx_registers;
regs[0].count = a3xx_registers_count;
@@ -326,10 +343,14 @@
KGSL_SNAPSHOT_SECTION_REGS, snapshot, remain,
kgsl_snapshot_dump_regs, &list);
- /* CP_STATE_DEBUG indexed registers */
+ /*
+ * CP_STATE_DEBUG indexed registers - 20 on 305 and 320 and 46 on A330
+ */
+ size = adreno_is_a330(adreno_dev) ? 0x2E : 0x14;
+
snapshot = kgsl_snapshot_indexed_registers(device, snapshot,
remain, REG_CP_STATE_DEBUG_INDEX,
- REG_CP_STATE_DEBUG_DATA, 0x0, 0x14);
+ REG_CP_STATE_DEBUG_DATA, 0x0, size);
/* CP_ME indexed registers */
snapshot = kgsl_snapshot_indexed_registers(device, snapshot,
diff --git a/drivers/gpu/msm/adreno_snapshot.c b/drivers/gpu/msm/adreno_snapshot.c
index 93be980..696073f 100644
--- a/drivers/gpu/msm/adreno_snapshot.c
+++ b/drivers/gpu/msm/adreno_snapshot.c
@@ -510,10 +510,28 @@
break;
if (pkt_is_type3(src[i])) {
- if (adreno_cmd_is_ib(src[i]))
- ib_add_gpu_object(device, ptbase,
- src[i + 1], src[i + 2]);
- else
+ if (adreno_cmd_is_ib(src[i])) {
+ unsigned int gpuaddr = src[i + 1];
+ unsigned int size = src[i + 2];
+ unsigned int ibbase;
+
+ /* Address of the last processed IB2 */
+ kgsl_regread(device, REG_CP_IB2_BASE, &ibbase);
+
+ /*
+ * If this is the last IB2 that was executed,
+ * then push it to make sure it goes into the
+ * static space
+ */
+
+ if (ibbase == gpuaddr)
+ push_object(device,
+ SNAPSHOT_OBJ_TYPE_IB, ptbase,
+ gpuaddr, size);
+ else
+ ib_add_gpu_object(device, ptbase,
+ gpuaddr, size);
+ } else
ib_parse_type3(device, &src[i], ptbase);
} else if (pkt_is_type0(src[i])) {
ib_parse_type0(device, &src[i], ptbase);
@@ -529,31 +547,6 @@
snapshot_frozen_objsize += ret;
}
-/* Snapshot the istore memory */
-static int snapshot_istore(struct kgsl_device *device, void *snapshot,
- int remain, void *priv)
-{
- struct kgsl_snapshot_istore *header = snapshot;
- unsigned int *data = snapshot + sizeof(*header);
- struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
- int count, i;
-
- count = adreno_dev->istore_size * adreno_dev->instruction_size;
-
- if (remain < (count * 4) + sizeof(*header)) {
- KGSL_DRV_ERR(device,
- "snapshot: Not enough memory for the istore section");
- return 0;
- }
-
- header->count = adreno_dev->istore_size;
-
- for (i = 0; i < count; i++)
- kgsl_regread(device, ADRENO_ISTORE_START + i, &data[i]);
-
- return (count * 4) + sizeof(*header);
-}
-
/* Snapshot the ringbuffer memory */
static int snapshot_rb(struct kgsl_device *device, void *snapshot,
int remain, void *priv)
@@ -870,17 +863,6 @@
for (i = 0; i < objbufptr; i++)
snapshot = dump_object(device, i, snapshot, remain);
- /*
- * Only dump the istore on a hang - reading it on a running system
- * has a non 0 chance of hanging the GPU
- */
-
- if (hang) {
- snapshot = kgsl_snapshot_add_section(device,
- KGSL_SNAPSHOT_SECTION_ISTORE, snapshot, remain,
- snapshot_istore, NULL);
- }
-
/* Add GPU specific sections - registers mainly, but other stuff too */
if (adreno_dev->gpudev->snapshot)
snapshot = adreno_dev->gpudev->snapshot(adreno_dev, snapshot,
diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c
index afe384b..1b1f0ac 100644
--- a/drivers/gpu/msm/kgsl.c
+++ b/drivers/gpu/msm/kgsl.c
@@ -1469,11 +1469,10 @@
}
static int memdesc_sg_virt(struct kgsl_memdesc *memdesc,
- void *addr, int size)
+ unsigned long paddr, int size)
{
int i;
int sglen = PAGE_ALIGN(size) / PAGE_SIZE;
- unsigned long paddr = (unsigned long) addr;
memdesc->sg = kgsl_sg_alloc(sglen);
@@ -1524,34 +1523,33 @@
return -EINVAL;
}
-static int kgsl_setup_hostptr(struct kgsl_mem_entry *entry,
+static int kgsl_setup_useraddr(struct kgsl_mem_entry *entry,
struct kgsl_pagetable *pagetable,
- void *hostptr, unsigned int offset,
+ unsigned long useraddr, unsigned int offset,
size_t size)
{
struct vm_area_struct *vma;
unsigned int len;
down_read(¤t->mm->mmap_sem);
- vma = find_vma(current->mm, (unsigned int) hostptr);
+ vma = find_vma(current->mm, useraddr);
up_read(¤t->mm->mmap_sem);
if (!vma) {
- KGSL_CORE_ERR("find_vma(%p) failed\n", hostptr);
+ KGSL_CORE_ERR("find_vma(%lx) failed\n", useraddr);
return -EINVAL;
}
/* We don't necessarily start at vma->vm_start */
- len = vma->vm_end - (unsigned long) hostptr;
+ len = vma->vm_end - useraddr;
if (offset >= len)
return -EINVAL;
- if (!KGSL_IS_PAGE_ALIGNED((unsigned long) hostptr) ||
+ if (!KGSL_IS_PAGE_ALIGNED(useraddr) ||
!KGSL_IS_PAGE_ALIGNED(len)) {
- KGSL_CORE_ERR("user address len(%u)"
- "and start(%p) must be page"
- "aligned\n", len, hostptr);
+ KGSL_CORE_ERR("bad alignment: start(%lx) len(%u)\n",
+ useraddr, len);
return -EINVAL;
}
@@ -1572,28 +1570,27 @@
entry->memdesc.pagetable = pagetable;
entry->memdesc.size = size;
- entry->memdesc.hostptr = hostptr + (offset & PAGE_MASK);
+ entry->memdesc.useraddr = useraddr + (offset & PAGE_MASK);
- return memdesc_sg_virt(&entry->memdesc,
- hostptr + (offset & PAGE_MASK), size);
+ return memdesc_sg_virt(&entry->memdesc, entry->memdesc.useraddr,
+ size);
}
#ifdef CONFIG_ASHMEM
static int kgsl_setup_ashmem(struct kgsl_mem_entry *entry,
struct kgsl_pagetable *pagetable,
- int fd, void *hostptr, size_t size)
+ int fd, unsigned long useraddr, size_t size)
{
int ret;
struct vm_area_struct *vma;
struct file *filep, *vmfile;
unsigned long len;
- unsigned int hostaddr = (unsigned int) hostptr;
- vma = kgsl_get_vma_from_start_addr(hostaddr);
+ vma = kgsl_get_vma_from_start_addr(useraddr);
if (vma == NULL)
return -EINVAL;
- if (vma->vm_pgoff || vma->vm_start != hostaddr) {
+ if (vma->vm_pgoff || vma->vm_start != useraddr) {
KGSL_CORE_ERR("Invalid vma region\n");
return -EINVAL;
}
@@ -1604,8 +1601,8 @@
size = len;
if (size != len) {
- KGSL_CORE_ERR("Invalid size %d for vma region %p\n",
- size, hostptr);
+ KGSL_CORE_ERR("Invalid size %d for vma region %lx\n",
+ size, useraddr);
return -EINVAL;
}
@@ -1625,9 +1622,9 @@
entry->priv_data = filep;
entry->memdesc.pagetable = pagetable;
entry->memdesc.size = ALIGN(size, PAGE_SIZE);
- entry->memdesc.hostptr = hostptr;
+ entry->memdesc.useraddr = useraddr;
- ret = memdesc_sg_virt(&entry->memdesc, hostptr, size);
+ ret = memdesc_sg_virt(&entry->memdesc, useraddr, size);
if (ret)
goto err;
@@ -1640,7 +1637,7 @@
#else
static int kgsl_setup_ashmem(struct kgsl_mem_entry *entry,
struct kgsl_pagetable *pagetable,
- int fd, void *hostptr, size_t size)
+ int fd, unsigned long useraddr, size_t size)
{
return -EINVAL;
}
@@ -1713,6 +1710,8 @@
else
memtype = param->memtype;
+ entry->memdesc.flags = param->flags;
+
switch (memtype) {
case KGSL_USER_MEM_TYPE_PMEM:
if (param->fd == 0 || param->len == 0)
@@ -1737,8 +1736,8 @@
if (param->hostptr == 0)
break;
- result = kgsl_setup_hostptr(entry, private->pagetable,
- (void *) param->hostptr,
+ result = kgsl_setup_useraddr(entry, private->pagetable,
+ param->hostptr,
param->offset, param->len);
entry->memtype = KGSL_MEM_ENTRY_USER;
break;
@@ -1755,7 +1754,7 @@
break;
result = kgsl_setup_ashmem(entry, private->pagetable,
- param->fd, (void *) param->hostptr,
+ param->fd, param->hostptr,
param->len);
entry->memtype = KGSL_MEM_ENTRY_ASHMEM;
@@ -1771,12 +1770,10 @@
if (result)
goto error;
- entry->memdesc.priv |= param->flags & KGSL_MEMTYPE_MASK;
-
if (entry->memdesc.size >= SZ_1M)
- entry->memdesc.priv |= ilog2(SZ_1M) << KGSL_MEMALIGN_SHIFT;
+ kgsl_memdesc_set_align(&entry->memdesc, ilog2(SZ_1M));
else if (entry->memdesc.size >= SZ_64K)
- entry->memdesc.priv |= ilog2(SZ_64K) << KGSL_MEMALIGN_SHIFT;
+ kgsl_memdesc_set_align(&entry->memdesc, ilog2(SZ_64));
result = kgsl_mmu_map(private->pagetable,
&entry->memdesc,
@@ -2232,6 +2229,8 @@
kgsl_gpumem_vm_close(struct vm_area_struct *vma)
{
struct kgsl_mem_entry *entry = vma->vm_private_data;
+
+ entry->memdesc.useraddr = 0;
kgsl_mem_entry_put(entry);
}
@@ -2243,6 +2242,7 @@
static int kgsl_mmap(struct file *file, struct vm_area_struct *vma)
{
+ unsigned int ret;
unsigned long vma_offset = vma->vm_pgoff << PAGE_SHIFT;
struct kgsl_device_private *dev_priv = file->private_data;
struct kgsl_process_private *private = dev_priv->process_priv;
@@ -2269,8 +2269,20 @@
if (!entry->memdesc.ops ||
!entry->memdesc.ops->vmflags ||
- !entry->memdesc.ops->vmfault)
- return -EINVAL;
+ !entry->memdesc.ops->vmfault) {
+ ret = -EINVAL;
+ goto err_put;
+ }
+
+ if (entry->memdesc.useraddr != 0) {
+ ret = -EBUSY;
+ goto err_put;
+ }
+
+ if (entry->memdesc.size != (vma->vm_end - vma->vm_start)) {
+ ret = -ERANGE;
+ goto err_put;
+ }
vma->vm_flags |= entry->memdesc.ops->vmflags(&entry->memdesc);
@@ -2279,7 +2291,12 @@
vma->vm_ops = &kgsl_gpumem_vm_ops;
vma->vm_file = file;
+ entry->memdesc.useraddr = vma->vm_start;
+
return 0;
+err_put:
+ kgsl_mem_entry_put(entry);
+ return ret;
}
static irqreturn_t kgsl_irq_handler(int irq, void *data)
@@ -2521,6 +2538,9 @@
KGSL_LOG_DUMP(device, "POWER: INTERVAL TIMEOUT = %08X ",
pwr->interval_timeout);
+ KGSL_LOG_DUMP(device, "POWER: NAP ALLOWED = %d | START_STOP_SLEEP_WAKE = %d\n",
+ pwr->nap_allowed, pwr->strtstp_sleepwake);
+
KGSL_LOG_DUMP(device, "GRP_CLK = %lu ",
kgsl_get_clkrate(pwr->grp_clks[0]));
diff --git a/drivers/gpu/msm/kgsl.h b/drivers/gpu/msm/kgsl.h
index 2861117..17a5b67 100644
--- a/drivers/gpu/msm/kgsl.h
+++ b/drivers/gpu/msm/kgsl.h
@@ -124,21 +124,25 @@
int (*map_kernel_mem)(struct kgsl_memdesc *);
};
+/* Internal definitions for memdesc->priv */
#define KGSL_MEMDESC_GUARD_PAGE BIT(0)
+/* Set if the memdesc is mapped into all pagetables */
+#define KGSL_MEMDESC_GLOBAL BIT(1)
/* shared memory allocation */
struct kgsl_memdesc {
struct kgsl_pagetable *pagetable;
- void *hostptr;
+ void *hostptr; /* kernel virtual address */
+ unsigned long useraddr; /* userspace address */
unsigned int gpuaddr;
unsigned int physaddr;
unsigned int size;
- unsigned int priv;
+ unsigned int priv; /* Internal flags and settings */
struct scatterlist *sg;
unsigned int sglen; /* Active entries in the sglist */
unsigned int sglen_alloc; /* Allocated entries in the sglist */
struct kgsl_memdesc_ops *ops;
- int flags;
+ unsigned int flags; /* Flags set from userspace */
};
/* List of different memory entry types */
diff --git a/drivers/gpu/msm/kgsl_debugfs.c b/drivers/gpu/msm/kgsl_debugfs.c
index b49c260..52097dc 100644
--- a/drivers/gpu/msm/kgsl_debugfs.c
+++ b/drivers/gpu/msm/kgsl_debugfs.c
@@ -163,6 +163,16 @@
return "unknown";
}
+static char get_alignflag(const struct kgsl_memdesc *m)
+{
+ int align = kgsl_memdesc_get_align(m);
+ if (align >= ilog2(SZ_1M))
+ return 'L';
+ else if (align >= ilog2(SZ_64K))
+ return 'l';
+ return '-';
+}
+
static int process_mem_print(struct seq_file *s, void *unused)
{
struct kgsl_mem_entry *entry;
@@ -170,7 +180,6 @@
struct kgsl_process_private *private = s->private;
char flags[4];
char usage[16];
- unsigned int align;
spin_lock(&private->mem_lock);
seq_printf(s, "%8s %8s %5s %10s %16s %5s\n",
@@ -181,20 +190,12 @@
entry = rb_entry(node, struct kgsl_mem_entry, node);
m = &entry->memdesc;
- flags[0] = m->priv & KGSL_MEMFLAGS_GLOBAL ? 'g' : '-';
- flags[1] = m->priv & KGSL_MEMFLAGS_GPUREADONLY ? 'r' : '-';
-
- align = (m->priv & KGSL_MEMALIGN_MASK) >> KGSL_MEMALIGN_SHIFT;
- if (align >= ilog2(SZ_1M))
- flags[2] = 'L';
- else if (align >= ilog2(SZ_64K))
- flags[2] = 'l';
- else
- flags[2] = '-';
-
+ flags[0] = m->priv & KGSL_MEMDESC_GLOBAL ? 'g' : '-';
+ flags[1] = m->flags & KGSL_MEMFLAGS_GPUREADONLY ? 'r' : '-';
+ flags[2] = get_alignflag(m);
flags[3] = '\0';
- kgsl_get_memory_usage(usage, sizeof(usage), m->priv);
+ kgsl_get_memory_usage(usage, sizeof(usage), m->flags);
seq_printf(s, "%08x %8d %5s %10s %16s %5d\n",
m->gpuaddr, m->size, flags,
diff --git a/drivers/gpu/msm/kgsl_device.h b/drivers/gpu/msm/kgsl_device.h
index 4394118..d962bf1 100644
--- a/drivers/gpu/msm/kgsl_device.h
+++ b/drivers/gpu/msm/kgsl_device.h
@@ -235,7 +235,8 @@
* context was responsible for causing it
*/
unsigned int reset_status;
-
+ /* Flag indicating if we tried to wait for bad timestamp for this ctx */
+ bool wait_on_invalid_ts;
/*
* Timeline used to create fences that can be signaled when a
* sync_pt timestamp expires.
diff --git a/drivers/gpu/msm/kgsl_drm.c b/drivers/gpu/msm/kgsl_drm.c
index 870a7d7..2003098 100644
--- a/drivers/gpu/msm/kgsl_drm.c
+++ b/drivers/gpu/msm/kgsl_drm.c
@@ -237,14 +237,16 @@
}
}
+ /* Set the flags for the memdesc (probably 0, unless it is cached) */
+ priv->memdesc.priv = 0;
+
if (TYPE_IS_PMEM(priv->type)) {
if (priv->type == DRM_KGSL_GEM_TYPE_EBI ||
priv->type & DRM_KGSL_GEM_PMEM_EBI) {
result = kgsl_sharedmem_ebimem_user(
&priv->memdesc,
priv->pagetable,
- obj->size * priv->bufcount,
- 0);
+ obj->size * priv->bufcount);
if (result) {
DRM_ERROR(
"Unable to allocate PMEM memory\n");
@@ -262,7 +264,7 @@
result = kgsl_sharedmem_page_alloc_user(&priv->memdesc,
priv->pagetable,
- obj->size * priv->bufcount, 0);
+ obj->size * priv->bufcount);
if (result != 0) {
DRM_ERROR(
diff --git a/drivers/gpu/msm/kgsl_iommu.c b/drivers/gpu/msm/kgsl_iommu.c
index 62108f2..07ea48e 100644
--- a/drivers/gpu/msm/kgsl_iommu.c
+++ b/drivers/gpu/msm/kgsl_iommu.c
@@ -792,13 +792,13 @@
if (msm_soc_version_supports_iommu_v1()) {
for (i = 0; i < iommu->unit_count; i++) {
iommu->iommu_units[i].reg_map.priv |=
- KGSL_MEMFLAGS_GLOBAL;
+ KGSL_MEMDESC_GLOBAL;
status = kgsl_mmu_map(pagetable,
&(iommu->iommu_units[i].reg_map),
GSL_PT_PAGE_RV | GSL_PT_PAGE_WV);
if (status) {
iommu->iommu_units[i].reg_map.priv &=
- ~KGSL_MEMFLAGS_GLOBAL;
+ ~KGSL_MEMDESC_GLOBAL;
goto err;
}
}
@@ -808,7 +808,7 @@
for (i--; i >= 0; i--) {
kgsl_mmu_unmap(pagetable,
&(iommu->iommu_units[i].reg_map));
- iommu->iommu_units[i].reg_map.priv &= ~KGSL_MEMFLAGS_GLOBAL;
+ iommu->iommu_units[i].reg_map.priv &= ~KGSL_MEMDESC_GLOBAL;
}
if (mmu->priv_bank_table) {
kgsl_mmu_putpagetable(mmu->priv_bank_table);
diff --git a/drivers/gpu/msm/kgsl_mmu.c b/drivers/gpu/msm/kgsl_mmu.c
index dbb88ee..68cd167 100644
--- a/drivers/gpu/msm/kgsl_mmu.c
+++ b/drivers/gpu/msm/kgsl_mmu.c
@@ -593,7 +593,7 @@
_get_pool(struct kgsl_pagetable *pagetable, unsigned int flags)
{
if (pagetable->kgsl_pool &&
- (KGSL_MEMFLAGS_GLOBAL & flags))
+ (KGSL_MEMDESC_GLOBAL & flags))
return pagetable->kgsl_pool;
return pagetable->pool;
}
@@ -637,10 +637,9 @@
* the address space is so small.
*/
if (KGSL_MMU_TYPE_IOMMU == kgsl_mmu_get_mmutype() &&
- (memdesc->priv & KGSL_MEMALIGN_MASK)) {
- page_align = (memdesc->priv & KGSL_MEMALIGN_MASK)
- >> KGSL_MEMALIGN_SHIFT;
- }
+ kgsl_memdesc_get_align(memdesc) > 0)
+ page_align = kgsl_memdesc_get_align(memdesc);
+
memdesc->gpuaddr = gen_pool_alloc_aligned(pool, size, page_align);
if (memdesc->gpuaddr == 0) {
KGSL_CORE_ERR("gen_pool_alloc(%d) failed from pool: %s\n",
@@ -719,7 +718,7 @@
* Don't clear the gpuaddr on global mappings because they
* may be in use by other pagetables
*/
- if (!(memdesc->priv & KGSL_MEMFLAGS_GLOBAL))
+ if (!(memdesc->priv & KGSL_MEMDESC_GLOBAL))
memdesc->gpuaddr = 0;
return 0;
}
@@ -740,8 +739,7 @@
return 0;
gpuaddr = memdesc->gpuaddr;
- memdesc->priv |= KGSL_MEMFLAGS_GLOBAL
- | (KGSL_MEMTYPE_KERNEL << KGSL_MEMTYPE_SHIFT);
+ memdesc->priv |= KGSL_MEMDESC_GLOBAL;
result = kgsl_mmu_map(pagetable, memdesc, protflags);
if (result)
diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c
index 739dcb5..422bd55 100644
--- a/drivers/gpu/msm/kgsl_pwrctrl.c
+++ b/drivers/gpu/msm/kgsl_pwrctrl.c
@@ -23,6 +23,7 @@
#include "kgsl_pwrscale.h"
#include "kgsl_device.h"
#include "kgsl_trace.h"
+#include "kgsl_sharedmem.h"
#define KGSL_PWRFLAGS_POWER_ON 0
#define KGSL_PWRFLAGS_CLK_ON 1
@@ -951,6 +952,11 @@
void kgsl_pwrctrl_wake(struct kgsl_device *device)
{
int status;
+ unsigned int context_id;
+ unsigned int state = device->state;
+ unsigned int ts_processed = 0xdeaddead;
+ struct kgsl_context *context;
+
kgsl_pwrctrl_request_state(device, KGSL_STATE_ACTIVE);
switch (device->state) {
case KGSL_STATE_SLUMBER:
@@ -964,6 +970,17 @@
case KGSL_STATE_SLEEP:
kgsl_pwrctrl_axi(device, KGSL_PWRFLAGS_ON);
kgsl_pwrscale_wake(device);
+ kgsl_sharedmem_readl(&device->memstore,
+ (unsigned int *) &context_id,
+ KGSL_MEMSTORE_OFFSET(KGSL_MEMSTORE_GLOBAL,
+ current_context));
+ context = idr_find(&device->context_idr, context_id);
+ if (context)
+ ts_processed = kgsl_readtimestamp(device, context,
+ KGSL_TIMESTAMP_RETIRED);
+ KGSL_PWR_INFO(device, "Wake from %s state. CTXT: %d RTRD TS: %08X\n",
+ kgsl_pwrstate_to_str(state),
+ context ? context->id : -1, ts_processed);
/* fall through */
case KGSL_STATE_NAP:
/* Turn on the core clocks */
diff --git a/drivers/gpu/msm/kgsl_sharedmem.c b/drivers/gpu/msm/kgsl_sharedmem.c
index 77617ba..a70647a 100644
--- a/drivers/gpu/msm/kgsl_sharedmem.c
+++ b/drivers/gpu/msm/kgsl_sharedmem.c
@@ -367,7 +367,7 @@
int sglen = memdesc->sglen;
/* Don't free the guard page if it was used */
- if (memdesc->flags & KGSL_MEMDESC_GUARD_PAGE)
+ if (memdesc->priv & KGSL_MEMDESC_GUARD_PAGE)
sglen--;
kgsl_driver.stats.page_alloc -= memdesc->size;
@@ -405,7 +405,7 @@
int i, count = 0;
/* Don't map the guard page if it exists */
- if (memdesc->flags & KGSL_MEMDESC_GUARD_PAGE)
+ if (memdesc->priv & KGSL_MEMDESC_GUARD_PAGE)
sglen--;
/* create a list of pages to call vmap */
@@ -514,18 +514,19 @@
void *addr = memdesc->hostptr;
int size = memdesc->size;
- switch (op) {
- case KGSL_CACHE_OP_FLUSH:
- dmac_flush_range(addr, addr + size);
- break;
- case KGSL_CACHE_OP_CLEAN:
- dmac_clean_range(addr, addr + size);
- break;
- case KGSL_CACHE_OP_INV:
- dmac_inv_range(addr, addr + size);
- break;
+ if (addr != NULL) {
+ switch (op) {
+ case KGSL_CACHE_OP_FLUSH:
+ dmac_flush_range(addr, addr + size);
+ break;
+ case KGSL_CACHE_OP_CLEAN:
+ dmac_clean_range(addr, addr + size);
+ break;
+ case KGSL_CACHE_OP_INV:
+ dmac_inv_range(addr, addr + size);
+ break;
+ }
}
-
outer_cache_range_op_sg(memdesc->sg, memdesc->sglen, op);
}
EXPORT_SYMBOL(kgsl_cache_range_op);
@@ -533,7 +534,7 @@
static int
_kgsl_sharedmem_page_alloc(struct kgsl_memdesc *memdesc,
struct kgsl_pagetable *pagetable,
- size_t size, unsigned int flags, unsigned int protflags)
+ size_t size, unsigned int protflags)
{
int pcount = 0, order, ret = 0;
int j, len, page_size, sglen_alloc, sglen = 0;
@@ -561,10 +562,12 @@
if (size >= ((si.freeram << PAGE_SHIFT) - SZ_32M))
return -ENOMEM;
- align = (flags & KGSL_MEMALIGN_MASK) >> KGSL_MEMALIGN_SHIFT;
+ align = (memdesc->flags & KGSL_MEMALIGN_MASK) >> KGSL_MEMALIGN_SHIFT;
page_size = (align >= ilog2(SZ_64K) && size >= SZ_64K)
? SZ_64K : PAGE_SIZE;
+ /* update align flags for what we actually use */
+ kgsl_memdesc_set_align(memdesc, ilog2(page_size));
/*
* There needs to be enough room in the sg structure to be able to
@@ -583,7 +586,6 @@
memdesc->size = size;
memdesc->pagetable = pagetable;
- memdesc->priv |= (flags & KGSL_MEMALIGN_MASK);
memdesc->ops = &kgsl_page_alloc_ops;
memdesc->sg = kgsl_sg_alloc(sglen_alloc);
@@ -663,7 +665,7 @@
if (kgsl_guard_page != NULL) {
sg_set_page(&memdesc->sg[sglen++], kgsl_guard_page,
PAGE_SIZE, 0);
- memdesc->flags |= KGSL_MEMDESC_GUARD_PAGE;
+ memdesc->priv |= KGSL_MEMDESC_GUARD_PAGE;
}
}
@@ -740,7 +742,7 @@
size = ALIGN(size, PAGE_SIZE * 2);
ret = _kgsl_sharedmem_page_alloc(memdesc, pagetable, size,
- 0, GSL_PT_PAGE_RV | GSL_PT_PAGE_WV);
+ GSL_PT_PAGE_RV | GSL_PT_PAGE_WV);
if (!ret)
ret = kgsl_page_alloc_map_kernel(memdesc);
if (ret)
@@ -752,7 +754,7 @@
int
kgsl_sharedmem_page_alloc_user(struct kgsl_memdesc *memdesc,
struct kgsl_pagetable *pagetable,
- size_t size, int flags)
+ size_t size)
{
unsigned int protflags;
@@ -760,11 +762,11 @@
return -EINVAL;
protflags = GSL_PT_PAGE_RV;
- if (!(flags & KGSL_MEMFLAGS_GPUREADONLY))
+ if (!(memdesc->flags & KGSL_MEMFLAGS_GPUREADONLY))
protflags |= GSL_PT_PAGE_WV;
return _kgsl_sharedmem_page_alloc(memdesc, pagetable, size,
- flags, protflags);
+ protflags);
}
EXPORT_SYMBOL(kgsl_sharedmem_page_alloc_user);
@@ -861,7 +863,7 @@
int
kgsl_sharedmem_ebimem_user(struct kgsl_memdesc *memdesc,
struct kgsl_pagetable *pagetable,
- size_t size, int flags)
+ size_t size)
{
size = ALIGN(size, PAGE_SIZE);
return _kgsl_sharedmem_ebimem(memdesc, pagetable, size);
diff --git a/drivers/gpu/msm/kgsl_sharedmem.h b/drivers/gpu/msm/kgsl_sharedmem.h
index 5a6c4c2..92a6f27 100644
--- a/drivers/gpu/msm/kgsl_sharedmem.h
+++ b/drivers/gpu/msm/kgsl_sharedmem.h
@@ -20,6 +20,8 @@
#include <linux/slab.h>
#include <linux/kmemleak.h>
+#include "kgsl_log.h"
+
struct kgsl_device;
struct kgsl_process_private;
@@ -27,9 +29,6 @@
#define KGSL_CACHE_OP_FLUSH 0x02
#define KGSL_CACHE_OP_CLEAN 0x03
-/** Set if the memdesc is mapped into all pagetables */
-#define KGSL_MEMFLAGS_GLOBAL 0x00000002
-
extern struct kgsl_memdesc_ops kgsl_page_alloc_ops;
int kgsl_sharedmem_page_alloc(struct kgsl_memdesc *memdesc,
@@ -37,13 +36,13 @@
int kgsl_sharedmem_page_alloc_user(struct kgsl_memdesc *memdesc,
struct kgsl_pagetable *pagetable,
- size_t size, int flags);
+ size_t size);
int kgsl_sharedmem_alloc_coherent(struct kgsl_memdesc *memdesc, size_t size);
int kgsl_sharedmem_ebimem_user(struct kgsl_memdesc *memdesc,
struct kgsl_pagetable *pagetable,
- size_t size, int flags);
+ size_t size);
int kgsl_sharedmem_ebimem(struct kgsl_memdesc *memdesc,
struct kgsl_pagetable *pagetable,
@@ -71,6 +70,36 @@
int kgsl_sharedmem_init_sysfs(void);
void kgsl_sharedmem_uninit_sysfs(void);
+/*
+ * kgsl_memdesc_get_align - Get alignment flags from a memdesc
+ * @memdesc - the memdesc
+ *
+ * Returns the alignment requested, as power of 2 exponent.
+ */
+static inline int
+kgsl_memdesc_get_align(const struct kgsl_memdesc *memdesc)
+{
+ return (memdesc->flags & KGSL_MEMALIGN_MASK) >> KGSL_MEMALIGN_SHIFT;
+}
+
+/*
+ * kgsl_memdesc_set_align - Set alignment flags of a memdesc
+ * @memdesc - the memdesc
+ * @align - alignment requested, as a power of 2 exponent.
+ */
+static inline int
+kgsl_memdesc_set_align(struct kgsl_memdesc *memdesc, unsigned int align)
+{
+ if (align > 32) {
+ KGSL_CORE_ERR("Alignment too big, restricting to 2^32\n");
+ align = 32;
+ }
+
+ memdesc->flags &= ~KGSL_MEMALIGN_MASK;
+ memdesc->flags |= (align << KGSL_MEMALIGN_SHIFT) & KGSL_MEMALIGN_MASK;
+ return 0;
+}
+
static inline unsigned int kgsl_get_sg_pa(struct scatterlist *sg)
{
/*
@@ -134,7 +163,7 @@
{
if (kgsl_mmu_get_mmutype() == KGSL_MMU_TYPE_NONE)
return kgsl_sharedmem_ebimem(memdesc, pagetable, size);
- memdesc->priv |= (KGSL_MEMTYPE_KERNEL << KGSL_MEMTYPE_SHIFT);
+ memdesc->flags |= (KGSL_MEMTYPE_KERNEL << KGSL_MEMTYPE_SHIFT);
return kgsl_sharedmem_page_alloc(memdesc, pagetable, size);
}
@@ -144,15 +173,14 @@
size_t size, unsigned int flags)
{
int ret;
- unsigned int mask = (KGSL_MEMTYPE_MASK | KGSL_MEMFLAGS_GPUREADONLY);
+
+ memdesc->flags = flags;
+
if (kgsl_mmu_get_mmutype() == KGSL_MMU_TYPE_NONE)
- ret = kgsl_sharedmem_ebimem_user(memdesc, pagetable, size,
- flags);
+ ret = kgsl_sharedmem_ebimem_user(memdesc, pagetable, size);
else
- ret = kgsl_sharedmem_page_alloc_user(memdesc, pagetable, size,
- flags);
- if (ret == 0)
- memdesc->priv |= flags & mask;
+ ret = kgsl_sharedmem_page_alloc_user(memdesc, pagetable, size);
+
return ret;
}
@@ -162,6 +190,8 @@
int ret = kgsl_sharedmem_alloc_coherent(memdesc, size);
if (!ret && (kgsl_mmu_get_mmutype() == KGSL_MMU_TYPE_NONE))
memdesc->gpuaddr = memdesc->physaddr;
+
+ memdesc->flags |= (KGSL_MEMTYPE_KERNEL << KGSL_MEMTYPE_SHIFT);
return ret;
}
diff --git a/drivers/gpu/msm/kgsl_trace.h b/drivers/gpu/msm/kgsl_trace.h
index 81cb34f..0b247e5 100644
--- a/drivers/gpu/msm/kgsl_trace.h
+++ b/drivers/gpu/msm/kgsl_trace.h
@@ -324,7 +324,7 @@
__entry->size = mem_entry->memdesc.size;
__entry->tgid = mem_entry->priv->pid;
kgsl_get_memory_usage(__entry->usage, sizeof(__entry->usage),
- mem_entry->memdesc.priv);
+ mem_entry->memdesc.flags);
),
TP_printk(
@@ -356,7 +356,7 @@
__entry->type = mem_entry->memtype;
__entry->tgid = mem_entry->priv->pid;
kgsl_get_memory_usage(__entry->usage, sizeof(__entry->usage),
- mem_entry->memdesc.priv);
+ mem_entry->memdesc.flags);
),
TP_printk(
@@ -388,7 +388,7 @@
__entry->type = mem_entry->memtype;
__entry->tgid = mem_entry->priv->pid;
kgsl_get_memory_usage(__entry->usage, sizeof(__entry->usage),
- mem_entry->memdesc.priv);
+ mem_entry->memdesc.flags);
),
TP_printk(
@@ -421,7 +421,7 @@
__entry->gpuaddr = mem_entry->memdesc.gpuaddr;
__entry->size = mem_entry->memdesc.size;
kgsl_get_memory_usage(__entry->usage, sizeof(__entry->usage),
- mem_entry->memdesc.priv);
+ mem_entry->memdesc.flags);
__entry->drawctxt_id = id;
__entry->type = mem_entry->memtype;
__entry->curr_ts = curr_ts;
diff --git a/drivers/hwmon/epm_adc.c b/drivers/hwmon/epm_adc.c
index 368aac4..69a2f1c 100644
--- a/drivers/hwmon/epm_adc.c
+++ b/drivers/hwmon/epm_adc.c
@@ -1703,7 +1703,7 @@
}
static const struct of_device_id epm_adc_psoc_match_table[] = {
- { .compatible = "qcom,epm-adc",
+ { .compatible = "cy,epm-adc-cy8c5568lti-114",
},
{}
};
diff --git a/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tspp_v1.c b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tspp_v1.c
index e4f00c0..68af7e1 100644
--- a/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tspp_v1.c
+++ b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tspp_v1.c
@@ -272,23 +272,27 @@
static int mpq_tspp_dmx_add_channel(struct dvb_demux_feed *feed)
{
struct mpq_demux *mpq_demux = feed->demux->priv;
- enum tspp_source tspp_source;
+ struct tspp_select_source tspp_source;
struct tspp_filter tspp_filter;
int tsif;
int ret;
int channel_id;
int *channel_ref_count;
- enum tspp_tsif_mode mode;
+
+ tspp_source.clk_inverse = 0;
+ tspp_source.data_inverse = 0;
+ tspp_source.sync_inverse = 0;
+ tspp_source.enable_inverse = 0;
/* determine the TSIF we are reading from */
if (mpq_demux->source == DMX_SOURCE_FRONT0) {
tsif = 0;
- tspp_source = TSPP_SOURCE_TSIF0;
- mode = (enum tspp_tsif_mode)tsif0_mode;
+ tspp_source.source = TSPP_SOURCE_TSIF0;
+ tspp_source.mode = (enum tspp_tsif_mode)tsif0_mode;
} else if (mpq_demux->source == DMX_SOURCE_FRONT1) {
tsif = 1;
- tspp_source = TSPP_SOURCE_TSIF1;
- mode = (enum tspp_tsif_mode)tsif1_mode;
+ tspp_source.source = TSPP_SOURCE_TSIF1;
+ tspp_source.mode = (enum tspp_tsif_mode)tsif1_mode;
} else {
/* invalid source */
MPQ_DVB_ERR_PRINT(
@@ -341,13 +345,13 @@
}
/* set TSPP source */
- ret = tspp_open_stream(0, channel_id, tspp_source, mode);
+ ret = tspp_open_stream(0, channel_id, &tspp_source);
if (ret < 0) {
MPQ_DVB_ERR_PRINT(
"%s: tspp_select_source(%d,%d) failed (%d)\n",
__func__,
channel_id,
- tspp_source,
+ tspp_source.source,
ret);
goto add_channel_close_ch;
@@ -418,7 +422,7 @@
* accordingly.
*/
tspp_filter.mode = TSPP_MODE_RAW;
- tspp_filter.source = tspp_source;
+ tspp_filter.source = tspp_source.source;
tspp_filter.decrypt = 0;
ret = tspp_add_filter(0, channel_id, &tspp_filter);
if (ret < 0) {
@@ -553,6 +557,7 @@
/* channel is not used any more, release it */
tspp_unregister_notification(0, channel_id);
tspp_close_channel(0, channel_id);
+ tspp_close_stream(0, channel_id);
}
mutex_unlock(&mpq_dmx_tspp_info.tsif[tsif].mutex);
diff --git a/drivers/media/dvb/mpq/video/mpq_dvb_video.c b/drivers/media/dvb/mpq/video/mpq_dvb_video.c
index 4628ba7..22d37e9 100644
--- a/drivers/media/dvb/mpq/video/mpq_dvb_video.c
+++ b/drivers/media/dvb/mpq/video/mpq_dvb_video.c
@@ -2085,7 +2085,7 @@
struct video_event *ev)
{
int rc;
- struct vdec_msginfo vdec_msg_info;
+ struct vdec_msginfo vdec_msg_info = {};
memset(ev, 0, sizeof(struct video_event));
diff --git a/drivers/media/video/msm/msm_isp.c b/drivers/media/video/msm/msm_isp.c
index 77922e2..48ce577 100644
--- a/drivers/media/video/msm/msm_isp.c
+++ b/drivers/media/video/msm/msm_isp.c
@@ -426,6 +426,10 @@
stats.aec.buff = stats.buffer;
stats.aec.fd = stats.fd;
break;
+ case MSG_ID_STATS_BE:
+ stats.be.buff = stats.buffer;
+ stats.be.fd = stats.fd;
+ break;
case MSG_ID_STATS_AF:
case MSG_ID_STATS_BF:
stats.af.buff = stats.buffer;
@@ -514,6 +518,7 @@
CDBG("%s: cmd_type %d\n", __func__, cfgcmd.cmd_type);
switch (cfgcmd.cmd_type) {
case CMD_STATS_BG_ENABLE:
+ case CMD_STATS_BE_ENABLE:
case CMD_STATS_BF_ENABLE:
case CMD_STATS_BHIST_ENABLE:
case CMD_STATS_AF_ENABLE:
@@ -611,6 +616,8 @@
cfgcmd.cmd_type = CMD_STATS_BUF_RELEASE;
else if (buf.type == STAT_BG)
cfgcmd.cmd_type = CMD_STATS_BG_BUF_RELEASE;
+ else if (buf.type == STAT_BE)
+ cfgcmd.cmd_type = CMD_STATS_BE_BUF_RELEASE;
else if (buf.type == STAT_BF)
cfgcmd.cmd_type = CMD_STATS_BF_BUF_RELEASE;
else if (buf.type == STAT_BHIST)
diff --git a/drivers/media/video/msm/msm_mem.c b/drivers/media/video/msm/msm_mem.c
index 1875df2..e131193 100644
--- a/drivers/media/video/msm/msm_mem.c
+++ b/drivers/media/video/msm/msm_mem.c
@@ -210,6 +210,7 @@
case MSM_PMEM_SKIN:
case MSM_PMEM_AEC_AWB:
case MSM_PMEM_BAYER_GRID:
+ case MSM_PMEM_BAYER_EXPOSURE:
case MSM_PMEM_BAYER_FOCUS:
case MSM_PMEM_BAYER_HIST:
rc = msm_pmem_table_add(ptype, pinfo, client, domain_num);
@@ -241,6 +242,7 @@
case MSM_PMEM_SKIN:
case MSM_PMEM_AEC_AWB:
case MSM_PMEM_BAYER_GRID:
+ case MSM_PMEM_BAYER_EXPOSURE:
case MSM_PMEM_BAYER_FOCUS:
case MSM_PMEM_BAYER_HIST:
hlist_for_each_entry_safe(region, node, n,
diff --git a/drivers/media/video/msm/vfe/msm_vfe40.c b/drivers/media/video/msm/vfe/msm_vfe40.c
index 6091a0d..287c77c 100644
--- a/drivers/media/video/msm/vfe/msm_vfe40.c
+++ b/drivers/media/video/msm/vfe/msm_vfe40.c
@@ -254,6 +254,8 @@
V40_COLORXFORM_ENC_CFG_OFF, 0xFF},
[161] = {VFE_CMD_COLORXFORM_VIEW_UPDATE, V40_COLORXFORM_VIEW_CFG_LEN,
V40_COLORXFORM_VIEW_CFG_OFF, 0xFF},
+ [163] = {VFE_CMD_STATS_BE_START, V40_STATS_BE_LEN, V40_STATS_BE_OFF},
+ [164] = {VFE_CMD_STATS_BE_STOP},
};
static const uint32_t vfe40_AXI_WM_CFG[] = {
@@ -818,29 +820,34 @@
CDBG("%s: Use bayer stats = %d\n", __func__,
vfe40_use_bayer_stats(vfe40_ctrl));
- msm_camera_io_w(0x8350001F,
- vfe40_ctrl->share_ctrl->vfebase +
- VFE_BUS_STATS_HIST_WR_UB_CFG);
- msm_camera_io_w(0x8370002F,
- vfe40_ctrl->share_ctrl->vfebase +
- VFE_BUS_STATS_BG_WR_UB_CFG);
- msm_camera_io_w(0x83A0002F,
- vfe40_ctrl->share_ctrl->vfebase +
- VFE_BUS_STATS_BF_WR_UB_CFG);
- msm_camera_io_w(0x83D0000F,
+ msm_camera_io_w(0x82F80007,
vfe40_ctrl->share_ctrl->vfebase +
VFE_BUS_STATS_RS_WR_UB_CFG);
- msm_camera_io_w(0x83E00007,
+ msm_camera_io_w(0x8300000F,
vfe40_ctrl->share_ctrl->vfebase +
VFE_BUS_STATS_CS_WR_UB_CFG);
- msm_camera_io_w(0x83E80007,
+
+ msm_camera_io_w(0x8310003F,
+ vfe40_ctrl->share_ctrl->vfebase +
+ VFE_BUS_STATS_BG_WR_UB_CFG);
+ msm_camera_io_w(0x8350003F,
+ vfe40_ctrl->share_ctrl->vfebase +
+ VFE_BUS_STATS_BE_WR_UB_CFG);
+ msm_camera_io_w(0x8390003F,
+ vfe40_ctrl->share_ctrl->vfebase +
+ VFE_BUS_STATS_BF_WR_UB_CFG);
+
+ msm_camera_io_w(0x83D0000F,
+ vfe40_ctrl->share_ctrl->vfebase +
+ VFE_BUS_STATS_HIST_WR_UB_CFG);
+ msm_camera_io_w(0x83E0000F,
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 +
@@ -850,6 +857,9 @@
VFE_BUS_STATS_BG_WR_FRAMEDROP_PATTERN);
msm_camera_io_w(0xFFFFFFFF,
vfe40_ctrl->share_ctrl->vfebase +
+ VFE_BUS_STATS_BE_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 +
@@ -873,6 +883,9 @@
VFE_BUS_STATS_BG_WR_IRQ_SUBSAMPLE_PATTERN);
msm_camera_io_w(0xFFFFFFFF,
vfe40_ctrl->share_ctrl->vfebase +
+ VFE_BUS_STATS_BE_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 +
@@ -894,13 +907,16 @@
struct vfe40_ctrl_type *vfe40_ctrl)
{
/* Stats control variables. */
- memset(&(vfe40_ctrl->afbfStatsControl), 0,
+ memset(&(vfe40_ctrl->bfStatsControl), 0,
sizeof(struct vfe_stats_control));
memset(&(vfe40_ctrl->awbStatsControl), 0,
sizeof(struct vfe_stats_control));
- memset(&(vfe40_ctrl->aecbgStatsControl), 0,
+ memset(&(vfe40_ctrl->bgStatsControl), 0,
+ sizeof(struct vfe_stats_control));
+
+ memset(&(vfe40_ctrl->beStatsControl), 0,
sizeof(struct vfe_stats_control));
memset(&(vfe40_ctrl->bhistStatsControl), 0,
@@ -1086,7 +1102,6 @@
{
uint32_t addr;
unsigned long flags;
-
spin_lock_irqsave(&vfe40_ctrl->stats_bufq_lock, flags);
addr = (uint32_t)vfe40_stats_dqbuf(vfe40_ctrl, MSM_STATS_TYPE_AWB);
spin_unlock_irqrestore(&vfe40_ctrl->stats_bufq_lock, flags);
@@ -1111,7 +1126,40 @@
return 0;
}
-static uint32_t vfe_stats_aec_bg_buf_init(
+static uint32_t vfe_stats_be_buf_init(
+ struct vfe40_ctrl_type *vfe40_ctrl)
+{
+ uint32_t addr;
+ unsigned long flags;
+ uint32_t stats_type;
+
+ stats_type = MSM_STATS_TYPE_BE;
+ 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 BE ping buf from free buf queue\n",
+ __func__);
+ return -ENOMEM;
+ }
+ msm_camera_io_w(addr,
+ vfe40_ctrl->share_ctrl->vfebase +
+ VFE_BUS_STATS_BE_WR_PING_ADDR);
+ 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 BE pong buf from free buf queue\n",
+ __func__);
+ return -ENOMEM;
+ }
+ msm_camera_io_w(addr,
+ vfe40_ctrl->share_ctrl->vfebase +
+ VFE_BUS_STATS_BE_WR_PONG_ADDR);
+ return 0;
+}
+
+static uint32_t vfe_stats_bg_buf_init(
struct vfe40_ctrl_type *vfe40_ctrl)
{
uint32_t addr;
@@ -1144,7 +1192,7 @@
return 0;
}
-static int vfe_stats_af_bf_buf_init(
+static int vfe_stats_bf_buf_init(
struct vfe40_ctrl_type *vfe40_ctrl)
{
uint32_t addr;
@@ -1189,7 +1237,6 @@
{
uint32_t addr;
unsigned long flags;
-
spin_lock_irqsave(&vfe40_ctrl->stats_bufq_lock, flags);
addr = (uint32_t)vfe40_stats_dqbuf(vfe40_ctrl, MSM_STATS_TYPE_BHIST);
spin_unlock_irqrestore(&vfe40_ctrl->stats_bufq_lock, flags);
@@ -1856,7 +1903,7 @@
rc = -EFAULT;
goto proc_general_done;
}
- rc = vfe_stats_aec_bg_buf_init(vfe40_ctrl);
+ rc = vfe_stats_bg_buf_init(vfe40_ctrl);
if (rc < 0) {
pr_err("%s: cannot config ping/pong address of AEC",
__func__);
@@ -1890,7 +1937,7 @@
rc = -EFAULT;
goto proc_general_done;
}
- rc = vfe_stats_af_bf_buf_init(vfe40_ctrl);
+ rc = vfe_stats_bf_buf_init(vfe40_ctrl);
if (rc < 0) {
pr_err("%s: cannot config ping/pong address of AF",
__func__);
@@ -2030,15 +2077,25 @@
break;
case VFE_CMD_STATS_BG_START:
+ case VFE_CMD_STATS_BE_START:
case VFE_CMD_STATS_BF_START:
case VFE_CMD_STATS_BHIST_START: {
old_val = msm_camera_io_r(
vfe40_ctrl->share_ctrl->vfebase + VFE_STATS_CFG);
module_val = msm_camera_io_r(
vfe40_ctrl->share_ctrl->vfebase + VFE_MODULE_CFG);
- if (VFE_CMD_STATS_BG_START == cmd->id) {
+
+ if (VFE_CMD_STATS_BE_START == cmd->id) {
+ module_val |= BE_ENABLE_MASK;
+ rc = vfe_stats_be_buf_init(vfe40_ctrl);
+ if (rc < 0) {
+ pr_err("%s: cannot config ping/pong address of BG",
+ __func__);
+ goto proc_general_done;
+ }
+ } else if (VFE_CMD_STATS_BG_START == cmd->id) {
module_val |= BG_ENABLE_MASK;
- rc = vfe_stats_aec_bg_buf_init(vfe40_ctrl);
+ rc = vfe_stats_bg_buf_init(vfe40_ctrl);
if (rc < 0) {
pr_err("%s: cannot config ping/pong address of BG",
__func__);
@@ -2046,7 +2103,7 @@
}
} else if (VFE_CMD_STATS_BF_START == cmd->id) {
module_val |= BF_ENABLE_MASK;
- rc = vfe_stats_af_bf_buf_init(vfe40_ctrl);
+ rc = vfe_stats_bf_buf_init(vfe40_ctrl);
if (rc < 0) {
pr_err("%s: cannot config ping/pong address of BF",
__func__);
@@ -2679,11 +2736,6 @@
break;
case VFE_CMD_STATS_AWB_STOP: {
- 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_MODULE_CFG);
old_val &= ~AWB_ENABLE_MASK;
@@ -2691,29 +2743,36 @@
vfe40_ctrl->share_ctrl->vfebase + VFE_MODULE_CFG);
}
break;
- case VFE_CMD_STATS_AE_STOP: {
- if (vfe40_use_bayer_stats(vfe40_ctrl)) {
- /* Error */
- rc = -EFAULT;
- goto proc_general_done;
- }
+ case VFE_CMD_STATS_BG_STOP: {
old_val = msm_camera_io_r(
vfe40_ctrl->share_ctrl->vfebase + VFE_MODULE_CFG);
- old_val &= BG_ENABLE_MASK;
+ old_val &= ~BG_ENABLE_MASK;
msm_camera_io_w(old_val,
vfe40_ctrl->share_ctrl->vfebase + VFE_MODULE_CFG);
}
break;
- case VFE_CMD_STATS_AF_STOP: {
- if (vfe40_use_bayer_stats(vfe40_ctrl)) {
- /* Error */
- rc = -EFAULT;
- goto proc_general_done;
- }
+ case VFE_CMD_STATS_BF_STOP: {
old_val = msm_camera_io_r(
vfe40_ctrl->share_ctrl->vfebase + VFE_MODULE_CFG);
old_val &= ~BF_ENABLE_MASK;
msm_camera_io_w(old_val,
+ vfe40_ctrl->share_ctrl->vfebase + VFE_MODULE_CFG);
+
+ rc = vfe40_stats_flush_enqueue(vfe40_ctrl,
+ MSM_STATS_TYPE_BF);
+ if (rc < 0) {
+ pr_err("%s: dq stats buf err = %d",
+ __func__, rc);
+ return -EINVAL;
+ }
+ }
+ break;
+
+ case VFE_CMD_STATS_BE_STOP: {
+ old_val = msm_camera_io_r(
+ vfe40_ctrl->share_ctrl->vfebase + VFE_MODULE_CFG);
+ old_val &= ~BE_ENABLE_MASK;
+ msm_camera_io_w(old_val,
vfe40_ctrl->share_ctrl->vfebase + VFE_MODULE_CFG);
}
break;
@@ -2745,8 +2804,6 @@
}
break;
- case VFE_CMD_STATS_BG_STOP:
- case VFE_CMD_STATS_BF_STOP:
case VFE_CMD_STATS_BHIST_STOP: {
if (!vfe40_use_bayer_stats(vfe40_ctrl)) {
/* Error */
@@ -2761,15 +2818,6 @@
msm_camera_io_w(old_val,
vfe40_ctrl->share_ctrl->vfebase + VFE_STATS_CFG);
- if (VFE_CMD_STATS_BF_STOP == cmd->id) {
- rc = vfe40_stats_flush_enqueue(vfe40_ctrl,
- MSM_STATS_TYPE_BF);
- if (rc < 0) {
- pr_err("%s: dq stats buf err = %d",
- __func__, rc);
- return -EINVAL;
- }
- }
}
break;
@@ -4015,6 +4063,16 @@
vfe40_ctrl->stats_ops.client);
}
break;
+ case statsBeNum:{
+ msgStats.id = MSG_ID_STATS_BE;
+ stats_type = MSM_STATS_TYPE_BE;
+ rc = vfe40_ctrl->stats_ops.dispatch(
+ vfe40_ctrl->stats_ops.stats_ctrl,
+ stats_type, bufAddress,
+ &msgStats.buf_idx, &vaddr, &msgStats.fd,
+ vfe40_ctrl->stats_ops.client);
+ }
+ break;
case statsBfNum:{
msgStats.id = MSG_ID_STATS_BF;
stats_type = MSM_STATS_TYPE_BF;
@@ -4101,9 +4159,9 @@
msgStats.status_bits = status_bits;
- msgStats.aec.buff = vfe40_ctrl->aecbgStatsControl.bufToRender;
+ msgStats.aec.buff = vfe40_ctrl->bgStatsControl.bufToRender;
msgStats.awb.buff = vfe40_ctrl->awbStatsControl.bufToRender;
- msgStats.af.buff = vfe40_ctrl->afbfStatsControl.bufToRender;
+ msgStats.af.buff = vfe40_ctrl->bfStatsControl.bufToRender;
msgStats.ihist.buff = vfe40_ctrl->ihistStatsControl.bufToRender;
msgStats.rs.buff = vfe40_ctrl->rsStatsControl.bufToRender;
@@ -4118,6 +4176,29 @@
&msgStats);
}
+static void vfe40_process_stats_be_irq(struct vfe40_ctrl_type *vfe40_ctrl)
+{
+ unsigned long flags;
+ uint32_t addr;
+ uint32_t stats_type;
+ stats_type = MSM_STATS_TYPE_BE;
+ 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->beStatsControl.bufToRender =
+ vfe40_process_stats_irq_common(vfe40_ctrl, statsBeNum,
+ addr);
+
+ vfe_send_stats_msg(vfe40_ctrl,
+ vfe40_ctrl->beStatsControl.bufToRender, statsBeNum);
+ } else{
+ vfe40_ctrl->beStatsControl.droppedStatsFrameCount++;
+ CDBG("%s: droppedStatsFrameCount = %d", __func__,
+ vfe40_ctrl->beStatsControl.droppedStatsFrameCount);
+ }
+}
+
static void vfe40_process_stats_bg_irq(struct vfe40_ctrl_type *vfe40_ctrl)
{
unsigned long flags;
@@ -4128,16 +4209,16 @@
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_ctrl->bgStatsControl.bufToRender =
vfe40_process_stats_irq_common(vfe40_ctrl, statsBgNum,
addr);
vfe_send_stats_msg(vfe40_ctrl,
- vfe40_ctrl->aecbgStatsControl.bufToRender, statsBgNum);
+ vfe40_ctrl->bgStatsControl.bufToRender, statsBgNum);
} else{
- vfe40_ctrl->aecbgStatsControl.droppedStatsFrameCount++;
+ vfe40_ctrl->bgStatsControl.droppedStatsFrameCount++;
CDBG("%s: droppedStatsFrameCount = %d", __func__,
- vfe40_ctrl->aecbgStatsControl.droppedStatsFrameCount);
+ vfe40_ctrl->bgStatsControl.droppedStatsFrameCount);
}
}
@@ -4172,16 +4253,16 @@
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_ctrl->bfStatsControl.bufToRender =
vfe40_process_stats_irq_common(vfe40_ctrl, statsBfNum,
addr);
vfe_send_stats_msg(vfe40_ctrl,
- vfe40_ctrl->afbfStatsControl.bufToRender, statsBfNum);
+ vfe40_ctrl->bfStatsControl.bufToRender, statsBfNum);
} else{
- vfe40_ctrl->afbfStatsControl.droppedStatsFrameCount++;
+ vfe40_ctrl->bfStatsControl.droppedStatsFrameCount++;
CDBG("%s: droppedStatsFrameCount = %d", __func__,
- vfe40_ctrl->afbfStatsControl.droppedStatsFrameCount);
+ vfe40_ctrl->bfStatsControl.droppedStatsFrameCount);
}
}
@@ -4282,22 +4363,39 @@
CDBG("%s, stats = 0x%x\n", __func__, status_bits);
spin_lock_irqsave(&vfe40_ctrl->stats_bufq_lock, flags);
- stats_type = MSM_STATS_TYPE_BG;
+ stats_type = MSM_STATS_TYPE_BE;
+ if (status_bits & VFE_IRQ_STATUS0_STATS_BE) {
+ addr = (uint32_t)vfe40_stats_dqbuf(vfe40_ctrl,
+ stats_type);
+ if (addr) {
+ vfe40_ctrl->beStatsControl.bufToRender =
+ vfe40_process_stats_irq_common(
+ vfe40_ctrl, statsBeNum, addr);
+ process_stats = true;
+ } else{
+ vfe40_ctrl->beStatsControl.bufToRender = 0;
+ vfe40_ctrl->beStatsControl.droppedStatsFrameCount++;
+ }
+ } else {
+ vfe40_ctrl->beStatsControl.bufToRender = 0;
+ }
+
+ stats_type = MSM_STATS_TYPE_BG;
if (status_bits & VFE_IRQ_STATUS0_STATS_BG) {
addr = (uint32_t)vfe40_stats_dqbuf(vfe40_ctrl,
stats_type);
if (addr) {
- vfe40_ctrl->aecbgStatsControl.bufToRender =
+ vfe40_ctrl->bgStatsControl.bufToRender =
vfe40_process_stats_irq_common(
vfe40_ctrl, statsBgNum, addr);
process_stats = true;
} else{
- vfe40_ctrl->aecbgStatsControl.bufToRender = 0;
- vfe40_ctrl->aecbgStatsControl.droppedStatsFrameCount++;
+ vfe40_ctrl->bgStatsControl.bufToRender = 0;
+ vfe40_ctrl->bgStatsControl.droppedStatsFrameCount++;
}
} else {
- vfe40_ctrl->aecbgStatsControl.bufToRender = 0;
+ vfe40_ctrl->bgStatsControl.bufToRender = 0;
}
if (status_bits & VFE_IRQ_STATUS0_STATS_AWB) {
@@ -4322,17 +4420,17 @@
addr = (uint32_t)vfe40_stats_dqbuf(vfe40_ctrl,
stats_type);
if (addr) {
- vfe40_ctrl->afbfStatsControl.bufToRender =
+ vfe40_ctrl->bfStatsControl.bufToRender =
vfe40_process_stats_irq_common(
vfe40_ctrl, statsBfNum,
addr);
process_stats = true;
} else {
- vfe40_ctrl->afbfStatsControl.bufToRender = 0;
- vfe40_ctrl->afbfStatsControl.droppedStatsFrameCount++;
+ vfe40_ctrl->bfStatsControl.bufToRender = 0;
+ vfe40_ctrl->bfStatsControl.droppedStatsFrameCount++;
}
} else {
- vfe40_ctrl->afbfStatsControl.bufToRender = 0;
+ vfe40_ctrl->bfStatsControl.bufToRender = 0;
}
if (status_bits & VFE_IRQ_STATUS0_STATS_IHIST) {
@@ -4441,6 +4539,10 @@
CDBG("Stats BG irq occured.\n");
vfe40_process_stats_bg_irq(vfe40_ctrl);
break;
+ case VFE_IRQ_STATUS0_STATS_BE:
+ CDBG("Stats BE irq occured.\n");
+ vfe40_process_stats_be_irq(vfe40_ctrl);
+ break;
case VFE_IRQ_STATUS0_STATS_BF:
CDBG("Stats BF irq occured.\n");
vfe40_process_stats_bf_irq(vfe40_ctrl);
@@ -4522,6 +4624,8 @@
(qcmd->vfeInterruptStatus0 &
VFE_IRQ_STATUS0_STATS_BG) |
(qcmd->vfeInterruptStatus0 &
+ VFE_IRQ_STATUS0_STATS_BE) |
+ (qcmd->vfeInterruptStatus0 &
VFE_IRQ_STATUS0_STATS_AWB) |
(qcmd->vfeInterruptStatus0 &
VFE_IRQ_STATUS0_STATS_BF) |
@@ -4610,6 +4714,12 @@
(void *)VFE_IRQ_STATUS0_STATS_BG);
if (qcmd->vfeInterruptStatus0 &
+ VFE_IRQ_STATUS0_STATS_BE)
+ v4l2_subdev_notify(&vfe40_ctrl->subdev,
+ NOTIFY_VFE_IRQ,
+ (void *)VFE_IRQ_STATUS0_STATS_BE);
+
+ if (qcmd->vfeInterruptStatus0 &
VFE_IRQ_STATUS0_STATS_AWB)
v4l2_subdev_notify(&vfe40_ctrl->subdev,
NOTIFY_VFE_IRQ,
@@ -4879,6 +4989,7 @@
cmd->cmd_type != CMD_STATS_CS_BUF_RELEASE &&
cmd->cmd_type != CMD_STATS_AF_BUF_RELEASE &&
cmd->cmd_type != CMD_STATS_BG_BUF_RELEASE &&
+ cmd->cmd_type != CMD_STATS_BE_BUF_RELEASE &&
cmd->cmd_type != CMD_STATS_BF_BUF_RELEASE &&
cmd->cmd_type != CMD_STATS_BHIST_BUF_RELEASE &&
cmd->cmd_type != CMD_VFE_PIX_SOF_COUNT_UPDATE &&
@@ -4922,6 +5033,7 @@
(cmd->cmd_type == CMD_STATS_CS_ENABLE) ||
(cmd->cmd_type == CMD_STATS_AEC_ENABLE) ||
(cmd->cmd_type == CMD_STATS_BG_ENABLE) ||
+ (cmd->cmd_type == CMD_STATS_BE_ENABLE) ||
(cmd->cmd_type == CMD_STATS_BF_ENABLE) ||
(cmd->cmd_type == CMD_STATS_BHIST_ENABLE)) {
struct axidata *axid;
@@ -4945,6 +5057,7 @@
switch (cmd->cmd_type) {
case CMD_STATS_AEC_ENABLE:
case CMD_STATS_BG_ENABLE:
+ case CMD_STATS_BE_ENABLE:
case CMD_STATS_BF_ENABLE:
case CMD_STATS_BHIST_ENABLE:
case CMD_STATS_AWB_ENABLE:
diff --git a/drivers/media/video/msm/vfe/msm_vfe40.h b/drivers/media/video/msm/vfe/msm_vfe40.h
index 6363bfb..4acc7e4 100644
--- a/drivers/media/video/msm/vfe/msm_vfe40.h
+++ b/drivers/media/video/msm/vfe/msm_vfe40.h
@@ -796,7 +796,7 @@
#define VFE_BUS_STATS_BE_WR_PING_ADDR 0x00000168
#define VFE_BUS_STATS_BE_WR_PONG_ADDR 0x0000016C
#define VFE_BUS_STATS_BE_WR_ADDR_CFG 0x00000170
-#define VFE_BUS_STATS_BE_UB_CFG 0x00000174
+#define VFE_BUS_STATS_BE_WR_UB_CFG 0x00000174
#define VFE_BUS_STATS_BE_WR_FRAMEDROP_PATTERN 0x00000178
#define VFE_BUS_STATS_BE_WR_IRQ_SUBSAMPLE_PATTERN 0x0000017C
@@ -1014,9 +1014,10 @@
uint32_t sync_timer_number;
struct msm_ver_num_info ver_num;
- struct vfe_stats_control afbfStatsControl;
+ struct vfe_stats_control beStatsControl;
+ struct vfe_stats_control bfStatsControl;
struct vfe_stats_control awbStatsControl;
- struct vfe_stats_control aecbgStatsControl;
+ struct vfe_stats_control bgStatsControl;
struct vfe_stats_control ihistStatsControl;
struct vfe_stats_control rsStatsControl;
struct vfe_stats_control csStatsControl;
diff --git a/drivers/media/video/msm_vidc/msm_v4l2_vidc.c b/drivers/media/video/msm_vidc/msm_v4l2_vidc.c
index e1a0994..f856e7e 100644
--- a/drivers/media/video/msm_vidc/msm_v4l2_vidc.c
+++ b/drivers/media/video/msm_vidc/msm_v4l2_vidc.c
@@ -442,12 +442,13 @@
struct buffer_info {
struct list_head list;
int type;
- int fd;
- int buff_off;
- int size;
- u32 uvaddr;
- u32 device_addr;
- struct msm_smem *handle;
+ int num_planes;
+ int fd[VIDEO_MAX_PLANES];
+ int buff_off[VIDEO_MAX_PLANES];
+ int size[VIDEO_MAX_PLANES];
+ u32 uvaddr[VIDEO_MAX_PLANES];
+ u32 device_addr[VIDEO_MAX_PLANES];
+ struct msm_smem *handle[VIDEO_MAX_PLANES];
};
struct msm_v4l2_vid_inst {
@@ -472,26 +473,37 @@
}
struct buffer_info *get_registered_buf(struct list_head *list,
- int fd, u32 buff_off, u32 size)
+ int fd, u32 buff_off, u32 size, int *plane)
{
struct buffer_info *temp;
struct buffer_info *ret = NULL;
- if (!list || fd < 0) {
+ int i;
+ if (!list || fd < 0 || !plane) {
dprintk(VIDC_ERR, "Invalid input\n");
goto err_invalid_input;
}
+ *plane = 0;
if (!list_empty(list)) {
list_for_each_entry(temp, list, list) {
- if (temp && temp->fd == fd &&
- (CONTAINS(temp->buff_off, temp->size, buff_off)
- || CONTAINS(buff_off, size, temp->buff_off)
- || OVERLAPS(buff_off, size,
- temp->buff_off, temp->size))) {
- dprintk(VIDC_INFO,
- "This memory region is already mapped\n");
- ret = temp;
- break;
+ for (i = 0; (i < temp->num_planes)
+ && (i < VIDEO_MAX_PLANES); i++) {
+ if (temp && temp->fd[i] == fd &&
+ (CONTAINS(temp->buff_off[i],
+ temp->size[i], buff_off)
+ || CONTAINS(buff_off,
+ size, temp->buff_off[i])
+ || OVERLAPS(buff_off, size,
+ temp->buff_off[i],
+ temp->size[i]))) {
+ dprintk(VIDC_DBG,
+ "This memory region is already mapped\n");
+ ret = temp;
+ *plane = i;
+ break;
+ }
}
+ if (ret)
+ break;
}
}
err_invalid_input:
@@ -499,25 +511,62 @@
}
struct buffer_info *get_same_fd_buffer(struct list_head *list,
- int fd)
+ int fd, int *plane)
{
struct buffer_info *temp;
struct buffer_info *ret = NULL;
- if (!list || fd < 0) {
+ int i;
+ if (!list || fd < 0 || !plane) {
+ dprintk(VIDC_ERR, "Invalid input\n");
+ goto err_invalid_input;
+ }
+ *plane = 0;
+ if (!list_empty(list)) {
+ list_for_each_entry(temp, list, list) {
+ for (i = 0; (i < temp->num_planes)
+ && (i < VIDEO_MAX_PLANES); i++) {
+ if (temp && temp->fd[i] == fd) {
+ dprintk(VIDC_INFO,
+ "Found same fd buffer\n");
+ ret = temp;
+ *plane = i;
+ break;
+ }
+ }
+ if (ret)
+ break;
+ }
+ }
+err_invalid_input:
+ return ret;
+}
+static u32 device_to_uvaddr(struct list_head *list, u32 device_addr)
+{
+ struct buffer_info *temp;
+ u32 uvaddr = 0;
+ int i;
+ if (!list || !device_addr) {
dprintk(VIDC_ERR, "Invalid input\n");
goto err_invalid_input;
}
if (!list_empty(list)) {
list_for_each_entry(temp, list, list) {
- if (temp && temp->fd == fd) {
- dprintk(VIDC_INFO, "Found same fd buffer\n");
- ret = temp;
- break;
+ for (i = 0; (i < temp->num_planes)
+ && (i < VIDEO_MAX_PLANES); i++) {
+ if (temp && temp->device_addr[i]
+ == device_addr) {
+ dprintk(VIDC_INFO,
+ "Found same fd buffer\n");
+ uvaddr = temp->uvaddr[i];
+ break;
+ }
}
+ if (uvaddr)
+ break;
}
}
err_invalid_input:
- return ret;
+ return uvaddr;
}
static int msm_v4l2_open(struct file *filp)
@@ -567,23 +616,27 @@
struct list_head *ptr, *next;
struct buffer_info *bi;
struct v4l2_buffer buffer_info;
- struct v4l2_plane plane;
+ struct v4l2_plane plane[VIDEO_MAX_PLANES];
int rc = 0;
+ int i;
list_for_each_safe(ptr, next, &v4l2_inst->registered_bufs) {
bi = list_entry(ptr, struct buffer_info, list);
if (bi->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
buffer_info.type = bi->type;
- plane.reserved[0] = bi->fd;
- plane.reserved[1] = bi->buff_off;
- plane.length = bi->size;
- plane.m.userptr = bi->device_addr;
- buffer_info.m.planes = &plane;
- buffer_info.length = 1;
- dprintk(VIDC_DBG,
- "Releasing buffer: %d, %d, %d\n",
- buffer_info.m.planes[0].reserved[0],
- buffer_info.m.planes[0].reserved[1],
- buffer_info.m.planes[0].length);
+ for (i = 0; (i < bi->num_planes)
+ && (i < VIDEO_MAX_PLANES); i++) {
+ plane[i].reserved[0] = bi->fd[i];
+ plane[i].reserved[1] = bi->buff_off[i];
+ plane[i].length = bi->size[i];
+ plane[i].m.userptr = bi->device_addr[i];
+ buffer_info.m.planes = plane;
+ dprintk(VIDC_DBG,
+ "Releasing buffer: %d, %d, %d\n",
+ buffer_info.m.planes[i].reserved[0],
+ buffer_info.m.planes[i].reserved[1],
+ buffer_info.m.planes[i].length);
+ }
+ buffer_info.length = bi->num_planes;
rc = msm_vidc_release_buf(v4l2_inst->vidc_inst,
&buffer_info);
if (rc)
@@ -593,11 +646,13 @@
buffer_info.m.planes[0].reserved[1],
buffer_info.m.planes[0].length);
list_del(&bi->list);
- if (bi->handle)
- msm_smem_free(v4l2_inst->mem_client,
- bi->handle);
- kfree(bi);
+ for (i = 0; i < bi->num_planes; i++) {
+ if (bi->handle[i])
+ msm_smem_free(v4l2_inst->mem_client,
+ bi->handle[i]);
}
+ kfree(bi);
+ }
}
return rc;
}
@@ -609,24 +664,28 @@
struct buffer_info *bi;
struct msm_vidc_inst *vidc_inst;
struct msm_v4l2_vid_inst *v4l2_inst;
+ int i;
vidc_inst = get_vidc_inst(filp, NULL);
v4l2_inst = get_v4l2_inst(filp, NULL);
rc = msm_v4l2_release_output_buffers(v4l2_inst);
if (rc)
dprintk(VIDC_WARN,
"Failed in %s for release output buffers\n", __func__);
- rc = msm_vidc_close(vidc_inst);
list_for_each_safe(ptr, next, &v4l2_inst->registered_bufs) {
bi = list_entry(ptr, struct buffer_info, list);
if (bi->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
list_del(&bi->list);
- if (bi->handle)
- msm_smem_free(v4l2_inst->mem_client,
- bi->handle);
+ for (i = 0; (i < bi->num_planes)
+ && (i < VIDEO_MAX_PLANES); i++) {
+ if (bi->handle[i])
+ msm_smem_free(v4l2_inst->mem_client,
+ bi->handle[i]);
+ }
kfree(bi);
}
}
msm_smem_delete_client(v4l2_inst->mem_client);
+ rc = msm_vidc_close(vidc_inst);
kfree(v4l2_inst);
return rc;
}
@@ -696,6 +755,7 @@
struct buffer_info *temp;
struct msm_vidc_inst *vidc_inst;
struct msm_v4l2_vid_inst *v4l2_inst;
+ int plane = 0;
int i, rc = 0;
vidc_inst = get_vidc_inst(file, fh);
v4l2_inst = get_v4l2_inst(file, fh);
@@ -704,34 +764,41 @@
rc = -ENOMEM;
goto exit;
}
+ binfo = kzalloc(sizeof(*binfo), GFP_KERNEL);
+ if (!binfo) {
+ dprintk(VIDC_ERR, "Out of memory\n");
+ rc = -ENOMEM;
+ goto exit;
+ }
+ if (b->length > VIDEO_MAX_PLANES) {
+ dprintk(VIDC_ERR, "Num planes exceeds max: %d, %d\n",
+ b->length, VIDEO_MAX_PLANES);
+ rc = -EINVAL;
+ goto exit;
+ }
for (i = 0; i < b->length; ++i) {
- binfo = get_registered_buf(&v4l2_inst->registered_bufs,
+ temp = get_registered_buf(&v4l2_inst->registered_bufs,
b->m.planes[i].reserved[0],
b->m.planes[i].reserved[1],
- b->m.planes[i].length);
- if (binfo) {
- dprintk(VIDC_INFO,
+ b->m.planes[i].length, &plane);
+ if (temp) {
+ dprintk(VIDC_DBG,
"This memory region has already been prepared\n");
rc = -EINVAL;
- goto exit;
- }
- binfo = kzalloc(sizeof(*binfo), GFP_KERNEL);
- if (!binfo) {
- dprintk(VIDC_ERR, "Out of memory\n");
- rc = -ENOMEM;
+ kfree(binfo);
goto exit;
}
temp = get_same_fd_buffer(&v4l2_inst->registered_bufs,
- b->m.planes[i].reserved[0]);
+ b->m.planes[i].reserved[0], &plane);
if (temp) {
binfo->type = b->type;
- binfo->fd = b->m.planes[i].reserved[0];
- binfo->buff_off = b->m.planes[i].reserved[1];
- binfo->size = b->m.planes[i].length;
- binfo->uvaddr = b->m.planes[i].m.userptr;
- binfo->device_addr =
- temp->handle->device_addr + binfo->buff_off;
- binfo->handle = NULL;
+ binfo->fd[i] = b->m.planes[i].reserved[0];
+ binfo->buff_off[i] = b->m.planes[i].reserved[1];
+ binfo->size[i] = b->m.planes[i].length;
+ binfo->uvaddr[i] = b->m.planes[i].m.userptr;
+ binfo->device_addr[i] =
+ temp->handle[plane]->device_addr + binfo->buff_off[i];
+ binfo->handle[i] = NULL;
} else {
handle = msm_smem_user_to_kernel(v4l2_inst->mem_client,
b->m.planes[i].reserved[0],
@@ -745,21 +812,22 @@
goto exit;
}
binfo->type = b->type;
- binfo->fd = b->m.planes[i].reserved[0];
- binfo->buff_off = b->m.planes[i].reserved[1];
- binfo->size = b->m.planes[i].length;
- binfo->uvaddr = b->m.planes[i].m.userptr;
- binfo->device_addr =
- handle->device_addr + binfo->buff_off;
- binfo->handle = handle;
+ binfo->fd[i] = b->m.planes[i].reserved[0];
+ binfo->buff_off[i] = b->m.planes[i].reserved[1];
+ binfo->size[i] = b->m.planes[i].length;
+ binfo->uvaddr[i] = b->m.planes[i].m.userptr;
+ binfo->device_addr[i] =
+ handle->device_addr + binfo->buff_off[i];
+ binfo->handle[i] = handle;
dprintk(VIDC_DBG, "Registering buffer: %d, %d, %d\n",
b->m.planes[i].reserved[0],
b->m.planes[i].reserved[1],
b->m.planes[i].length);
}
- list_add_tail(&binfo->list, &v4l2_inst->registered_bufs);
- b->m.planes[i].m.userptr = binfo->device_addr;
+ b->m.planes[i].m.userptr = binfo->device_addr[i];
}
+ binfo->num_planes = b->length;
+ list_add_tail(&binfo->list, &v4l2_inst->registered_bufs);
rc = msm_vidc_prepare_buf(v4l2_inst->vidc_inst, b);
exit:
return rc;
@@ -771,15 +839,21 @@
struct msm_vidc_inst *vidc_inst;
struct msm_v4l2_vid_inst *v4l2_inst;
struct buffer_info *binfo;
+ int plane = 0;
int rc = 0;
int i;
+ if (b->length > VIDEO_MAX_PLANES) {
+ dprintk(VIDC_ERR, "num planes exceeds max: %d\n",
+ b->length);
+ return -EINVAL;
+ }
vidc_inst = get_vidc_inst(file, fh);
v4l2_inst = get_v4l2_inst(file, fh);
for (i = 0; i < b->length; ++i) {
binfo = get_registered_buf(&v4l2_inst->registered_bufs,
b->m.planes[i].reserved[0],
b->m.planes[i].reserved[1],
- b->m.planes[i].length);
+ b->m.planes[i].length, &plane);
if (!binfo) {
dprintk(VIDC_ERR,
"This buffer is not registered: %d, %d, %d\n",
@@ -789,12 +863,12 @@
rc = -EINVAL;
goto err_invalid_buff;
}
- b->m.planes[i].m.userptr = binfo->device_addr;
+ b->m.planes[i].m.userptr = binfo->device_addr[i];
dprintk(VIDC_DBG, "Queueing device address = 0x%x\n",
- binfo->device_addr);
- if (binfo->handle) {
+ binfo->device_addr[i]);
+ if (binfo->handle[i]) {
rc = msm_smem_clean_invalidate(v4l2_inst->mem_client,
- binfo->handle);
+ binfo->handle[i]);
if (rc) {
dprintk(VIDC_ERR,
"Failed to clean caches: %d\n", rc);
@@ -810,8 +884,37 @@
int msm_v4l2_dqbuf(struct file *file, void *fh,
struct v4l2_buffer *b)
{
+ int rc = 0;
+ int i;
+ struct msm_v4l2_vid_inst *v4l2_inst;
struct msm_vidc_inst *vidc_inst = get_vidc_inst(file, fh);
- return msm_vidc_dqbuf((void *)vidc_inst, b);
+ if (b->length > VIDEO_MAX_PLANES) {
+ dprintk(VIDC_ERR, "num planes exceed maximum: %d\n",
+ b->length);
+ return -EINVAL;
+ }
+ v4l2_inst = get_v4l2_inst(file, fh);
+ rc = msm_vidc_dqbuf((void *)vidc_inst, b);
+ if (rc) {
+ dprintk(VIDC_DBG,
+ "Failed to dqbuf, capability: %d, rc: %d\n",
+ b->type, rc);
+ goto fail_dq_buf;
+ }
+ for (i = 0; i < b->length; i++) {
+ b->m.planes[i].m.userptr = device_to_uvaddr(
+ &v4l2_inst->registered_bufs,
+ b->m.planes[i].m.userptr);
+ if (!b->m.planes[i].m.userptr) {
+ dprintk(VIDC_ERR,
+ "Failed to find user virtual address, 0x%lx, %d, %d\n",
+ b->m.planes[i].m.userptr, b->type, i);
+ rc = -EINVAL;
+ goto fail_dq_buf;
+ }
+ }
+fail_dq_buf:
+ return rc;
}
int msm_v4l2_streamon(struct file *file, void *fh,
@@ -855,14 +958,22 @@
rc = msm_v4l2_release_output_buffers(v4l2_inst);
if (rc)
dprintk(VIDC_WARN,
- "Failed in %s for release output buffers\n", __func__);
+ "Failed to release dec output buffers: %d\n", rc);
return msm_vidc_decoder_cmd((void *)vidc_inst, dec);
}
static int msm_v4l2_encoder_cmd(struct file *file, void *fh,
struct v4l2_encoder_cmd *enc)
{
+ struct msm_v4l2_vid_inst *v4l2_inst;
struct msm_vidc_inst *vidc_inst = get_vidc_inst(file, fh);
+ int rc = 0;
+ v4l2_inst = get_v4l2_inst(file, NULL);
+ if (enc->cmd == V4L2_ENC_CMD_STOP)
+ rc = msm_v4l2_release_output_buffers(v4l2_inst);
+ if (rc)
+ dprintk(VIDC_WARN,
+ "Failed to release enc output buffers: %d\n", rc);
return msm_vidc_encoder_cmd((void *)vidc_inst, enc);
}
diff --git a/drivers/media/video/msm_vidc/msm_vdec.c b/drivers/media/video/msm_vidc/msm_vdec.c
index b615352..058c835 100644
--- a/drivers/media/video/msm_vidc/msm_vdec.c
+++ b/drivers/media/video/msm_vidc/msm_vdec.c
@@ -20,7 +20,6 @@
#include "msm_vidc_debug.h"
#define MSM_VDEC_DVC_NAME "msm_vdec_8974"
-#define MAX_PLANES 1
#define DEFAULT_HEIGHT 720
#define DEFAULT_WIDTH 1280
#define MAX_SUPPORTED_WIDTH 1920
@@ -150,6 +149,17 @@
.menu_skip_mask = 0,
.qmenu = NULL,
},
+ {
+ .id = V4L2_CID_MPEG_VIDC_VIDEO_SYNC_FRAME_DECODE,
+ .name = "Sync Frame Decode",
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .minimum = V4L2_MPEG_VIDC_VIDEO_SYNC_FRAME_DECODE_DISABLE,
+ .maximum = V4L2_MPEG_VIDC_VIDEO_SYNC_FRAME_DECODE_ENABLE,
+ .default_value = V4L2_MPEG_VIDC_VIDEO_SYNC_FRAME_DECODE_DISABLE,
+ .step = 1,
+ .menu_skip_mask = 0,
+ .qmenu = NULL,
+ },
};
#define NUM_CTRLS ARRAY_SIZE(msm_vdec_ctrls)
@@ -175,11 +185,6 @@
size = ALIGN(size, SZ_4K);
return size;
}
-static u32 get_frame_size_nv21(int plane,
- u32 height, u32 width)
-{
- return height * width * 2;
-}
static u32 get_frame_size_compressed(int plane,
u32 height, u32 width)
@@ -192,7 +197,7 @@
.name = "YCbCr Semiplanar 4:2:0",
.description = "Y/CbCr 4:2:0",
.fourcc = V4L2_PIX_FMT_NV12,
- .num_planes = 1,
+ .num_planes = 2,
.get_frame_size = get_frame_size_nv12,
.type = CAPTURE_PORT,
},
@@ -253,14 +258,6 @@
.type = OUTPUT_PORT,
},
{
- .name = "YCrCb Semiplanar 4:2:0",
- .description = "Y/CrCb 4:2:0",
- .fourcc = V4L2_PIX_FMT_NV21,
- .num_planes = 1,
- .get_frame_size = get_frame_size_nv21,
- .type = CAPTURE_PORT,
- },
- {
.name = "DIVX 311",
.description = "DIVX 311 compressed format",
.fourcc = V4L2_PIX_FMT_DIVX_311,
@@ -321,45 +318,48 @@
struct v4l2_buffer *b)
{
int rc = 0;
- int i;
struct vidc_buffer_addr_info buffer_info;
+ int extra_idx = 0;
+ int i;
switch (b->type) {
case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
break;
case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
- for (i = 0; i < b->length; i++) {
- dprintk(VIDC_DBG, "device_addr = %ld, size = %d\n",
- b->m.planes[i].m.userptr,
+ if (b->length != inst->fmts[CAPTURE_PORT]->num_planes) {
+ dprintk(VIDC_ERR,
+ "Planes mismatch: needed: %d, allocated: %d\n",
+ inst->fmts[CAPTURE_PORT]->num_planes,
+ b->length);
+ rc = -EINVAL;
+ break;
+ }
+ for (i = 0; (i < b->length)
+ && (i < VIDEO_MAX_PLANES); ++i) {
+ dprintk(VIDC_DBG,
+ "prepare plane: %d, device_addr = 0x%lx, size = %d\n",
+ i, b->m.planes[i].m.userptr,
b->m.planes[i].length);
- buffer_info.buffer_size = b->m.planes[i].length;
+ }
+ buffer_info.buffer_size = b->m.planes[0].length;
buffer_info.buffer_type = HAL_BUFFER_OUTPUT;
buffer_info.num_buffers = 1;
buffer_info.align_device_addr =
- b->m.planes[i].m.userptr;
- if (!inst->extradata_handle) {
- inst->extradata_handle =
- msm_smem_alloc(inst->mem_client,
- 4096 * 1024, 1, SMEM_UNCACHED,
- inst->core->resources.io_map[NS_MAP].domain,
- 0, 0);
- if (!inst->extradata_handle) {
- dprintk(VIDC_ERR,
- "Failed to allocate extradata memory\n");
- rc = -ENOMEM;
- break;
- }
+ b->m.planes[0].m.userptr;
+ extra_idx = EXTRADATA_IDX(b->length);
+ if (extra_idx && (extra_idx < VIDEO_MAX_PLANES)) {
+ buffer_info.extradata_addr =
+ b->m.planes[extra_idx].m.userptr;
+ dprintk(VIDC_DBG,
+ "extradata: 0x%lx\n",
+ b->m.planes[extra_idx].m.userptr);
+ buffer_info.extradata_size =
+ b->m.planes[extra_idx].length;
}
- buffer_info.extradata_addr =
- inst->extradata_handle->device_addr;
- buffer_info.extradata_size = 4096 * 1024;
rc = vidc_hal_session_set_buffers((void *)inst->session,
&buffer_info);
- if (rc) {
+ if (rc)
dprintk(VIDC_ERR,
- "vidc_hal_session_set_buffers failed\n");
- break;
- }
- }
+ "vidc_hal_session_set_buffers failed");
break;
default:
dprintk(VIDC_ERR, "Buffer type not recognized: %d\n", b->type);
@@ -372,9 +372,10 @@
struct v4l2_buffer *b)
{
int rc = 0;
- int i;
struct vidc_buffer_addr_info buffer_info;
struct msm_vidc_core *core = inst->core;
+ int extra_idx = 0;
+ int i;
if (inst->state == MSM_VIDC_CORE_INVALID ||
core->state == VIDC_CORE_INVALID) {
dprintk(VIDC_ERR,
@@ -382,28 +383,40 @@
core);
goto exit;
}
+
switch (b->type) {
case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
break;
case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
- for (i = 0; i < b->length; i++) {
- dprintk(VIDC_DBG,
- "Release device_addr = %ld, size = %d\n",
- b->m.planes[i].m.userptr,
+ if (b->length !=
+ inst->fmts[CAPTURE_PORT]->num_planes) {
+ dprintk(VIDC_ERR,
+ "Planes mismatch: needed: %d, to release: %d\n",
+ inst->fmts[CAPTURE_PORT]->num_planes,
+ b->length);
+ rc = -EINVAL;
+ break;
+ }
+ for (i = 0; i < b->length; ++i) {
+ dprintk(VIDC_DBG,
+ "Release plane: %d device_addr = 0x%lx, size = %d\n",
+ i, b->m.planes[i].m.userptr,
b->m.planes[i].length);
- buffer_info.buffer_size = b->m.planes[i].length;
+ }
+ buffer_info.buffer_size = b->m.planes[0].length;
buffer_info.buffer_type = HAL_BUFFER_OUTPUT;
buffer_info.num_buffers = 1;
buffer_info.align_device_addr =
- b->m.planes[i].m.userptr;
- buffer_info.extradata_addr =
- inst->extradata_handle->device_addr;
+ b->m.planes[0].m.userptr;
+ extra_idx = EXTRADATA_IDX(b->length);
+ if (extra_idx && (extra_idx < VIDEO_MAX_PLANES))
+ buffer_info.extradata_addr =
+ b->m.planes[extra_idx].m.userptr;
rc = vidc_hal_session_release_buffers(
(void *)inst->session, &buffer_info);
if (rc)
dprintk(VIDC_ERR,
- "vidc_hal_session_release_buffers failed\n");
- }
+ "vidc_hal_session_release_buffers failed");
break;
default:
dprintk(VIDC_ERR, "Buffer type not recognized: %d\n", b->type);
@@ -475,7 +488,10 @@
int msm_vdec_g_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f)
{
const struct msm_vidc_format *fmt = NULL;
+ struct hal_frame_size frame_sz;
+ int extra_idx = 0;
int rc = 0;
+ int ret;
int i;
if (!inst || !f) {
dprintk(VIDC_ERR,
@@ -489,21 +505,41 @@
if (fmt) {
f->fmt.pix_mp.pixelformat = fmt->fourcc;
+ f->fmt.pix_mp.num_planes = fmt->num_planes;
if (inst->in_reconfig == true) {
inst->prop.height = inst->reconfig_height;
inst->prop.width = inst->reconfig_width;
}
f->fmt.pix_mp.height = inst->prop.height;
f->fmt.pix_mp.width = inst->prop.width;
- f->fmt.pix_mp.num_planes = fmt->num_planes;
- for (i = 0; i < fmt->num_planes; ++i) {
- f->fmt.pix_mp.plane_fmt[i].sizeimage =
- fmt->get_frame_size(i, inst->prop.height,
- inst->prop.width);
+ frame_sz.buffer_type = HAL_BUFFER_OUTPUT;
+ frame_sz.width = inst->prop.width;
+ frame_sz.height = inst->prop.height;
+ dprintk(VIDC_DBG, "width = %d, height = %d\n",
+ frame_sz.width, frame_sz.height);
+ ret = msm_comm_try_set_prop(inst,
+ HAL_PARAM_FRAME_SIZE, &frame_sz);
+ ret = ret || msm_comm_try_get_bufreqs(inst);
+ if (ret || (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)) {
+ for (i = 0; i < fmt->num_planes; ++i) {
+ f->fmt.pix_mp.plane_fmt[i].sizeimage =
+ fmt->get_frame_size(i,
+ f->fmt.pix_mp.height,
+ f->fmt.pix_mp.width);
+ }
+ } else {
+ f->fmt.pix_mp.plane_fmt[0].sizeimage =
+ inst->buff_req.buffer[HAL_BUFFER_OUTPUT].buffer_size;
+ extra_idx = EXTRADATA_IDX(fmt->num_planes);
+ if (extra_idx && (extra_idx < VIDEO_MAX_PLANES)) {
+ f->fmt.pix_mp.plane_fmt[extra_idx].sizeimage =
+ inst->buff_req.buffer[HAL_BUFFER_EXTRADATA_OUTPUT].buffer_size;
+ }
}
} else {
- dprintk(VIDC_ERR, "Buf type not recognized, type = %d\n",
- f->type);
+ dprintk(VIDC_ERR,
+ "Buf type not recognized, type = %d\n",
+ f->type);
rc = -EINVAL;
}
return rc;
@@ -512,7 +548,10 @@
int msm_vdec_s_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f)
{
const struct msm_vidc_format *fmt = NULL;
+ struct hal_frame_size frame_sz;
+ int extra_idx = 0;
int rc = 0;
+ int ret = 0;
int i;
if (!inst || !f) {
dprintk(VIDC_ERR,
@@ -525,64 +564,67 @@
fmt = msm_comm_get_pixel_fmt_fourcc(vdec_formats,
ARRAY_SIZE(vdec_formats), f->fmt.pix_mp.pixelformat,
CAPTURE_PORT);
- if (fmt && fmt->type != CAPTURE_PORT) {
+ if (!fmt || (fmt && fmt->type != CAPTURE_PORT)) {
dprintk(VIDC_ERR,
- "Format: %d not supported on CAPTURE"
- "port\n", f->fmt.pix_mp.pixelformat);
- rc = -EINVAL;
- goto err_invalid_fmt;
- }
-
- inst->prop.width = f->fmt.pix_mp.width;
- inst->prop.height = f->fmt.pix_mp.height;
-
- frame_sz.buffer_type = HAL_BUFFER_OUTPUT;
- frame_sz.width = inst->prop.width;
- frame_sz.height = inst->prop.height;
- dprintk(VIDC_DBG,
- "width = %d, height = %d\n",
- frame_sz.width, frame_sz.height);
- rc = vidc_hal_session_set_property((void *)inst->session,
- HAL_PARAM_FRAME_SIZE, &frame_sz);
- if (rc) {
- dprintk(VIDC_ERR,
- "Failed to set hal property for framesize\n");
- goto err_invalid_fmt;
- }
- } else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
- inst->prop.width = f->fmt.pix_mp.width;
- inst->prop.height = f->fmt.pix_mp.height;
- fmt = msm_comm_get_pixel_fmt_fourcc(vdec_formats,
- ARRAY_SIZE(vdec_formats), f->fmt.pix_mp.pixelformat,
- OUTPUT_PORT);
- if (fmt && fmt->type != OUTPUT_PORT) {
- dprintk(VIDC_ERR,
- "Format: %d not supported on OUTPUT port\n",
+ "Format: %d not supported on CAPTURE port\n",
f->fmt.pix_mp.pixelformat);
rc = -EINVAL;
goto err_invalid_fmt;
}
- }
-
- if (fmt) {
- f->fmt.pix_mp.num_planes = fmt->num_planes;
- for (i = 0; i < fmt->num_planes; ++i) {
- f->fmt.pix_mp.plane_fmt[i].sizeimage =
- fmt->get_frame_size(i, f->fmt.pix_mp.height,
- f->fmt.pix_mp.width);
- }
inst->fmts[fmt->type] = fmt;
- if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
- rc = msm_comm_try_state(inst, MSM_VIDC_OPEN);
- if (rc) {
- dprintk(VIDC_ERR, "Failed to open instance\n");
- goto err_invalid_fmt;
+ frame_sz.buffer_type = HAL_BUFFER_OUTPUT;
+ frame_sz.width = inst->prop.width;
+ frame_sz.height = inst->prop.height;
+ dprintk(VIDC_DBG, "width = %d, height = %d\n",
+ frame_sz.width, frame_sz.height);
+ ret = msm_comm_try_set_prop(inst,
+ HAL_PARAM_FRAME_SIZE, &frame_sz);
+ ret = ret || msm_comm_try_get_bufreqs(inst);
+ if (ret) {
+ for (i = 0; i < fmt->num_planes; ++i) {
+ f->fmt.pix_mp.plane_fmt[i].sizeimage =
+ fmt->get_frame_size(i,
+ f->fmt.pix_mp.height,
+ f->fmt.pix_mp.width);
+ }
+ } else {
+ f->fmt.pix_mp.plane_fmt[0].sizeimage =
+ inst->buff_req.buffer[HAL_BUFFER_OUTPUT].buffer_size;
+ extra_idx = EXTRADATA_IDX(fmt->num_planes);
+ if (extra_idx && (extra_idx < VIDEO_MAX_PLANES)) {
+ f->fmt.pix_mp.plane_fmt[1].sizeimage =
+ inst->buff_req.buffer[HAL_BUFFER_EXTRADATA_OUTPUT].buffer_size;
}
}
- } else {
- dprintk(VIDC_ERR,
- "Buf type not recognized, type = %d\n", f->type);
- rc = -EINVAL;
+ f->fmt.pix_mp.num_planes = fmt->num_planes;
+ } else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ inst->prop.width = f->fmt.pix_mp.width;
+ inst->prop.height = f->fmt.pix_mp.height;
+ fmt = msm_comm_get_pixel_fmt_fourcc(vdec_formats,
+ ARRAY_SIZE(vdec_formats),
+ f->fmt.pix_mp.pixelformat,
+ OUTPUT_PORT);
+ if (!fmt || fmt->type != OUTPUT_PORT) {
+ dprintk(VIDC_ERR,
+ "Format: %d not supported on OUTPUT port\n",
+ f->fmt.pix_mp.pixelformat);
+ rc = -EINVAL;
+ goto err_invalid_fmt;
+ }
+ inst->fmts[fmt->type] = fmt;
+ rc = msm_comm_try_state(inst, MSM_VIDC_OPEN_DONE);
+ if (rc) {
+ dprintk(VIDC_ERR, "Failed to open instance\n");
+ goto err_invalid_fmt;
+ }
+ frame_sz.buffer_type = HAL_BUFFER_INPUT;
+ frame_sz.width = inst->prop.width;
+ frame_sz.height = inst->prop.height;
+ msm_comm_try_set_prop(inst, HAL_PARAM_FRAME_SIZE, &frame_sz);
+ f->fmt.pix_mp.plane_fmt[0].sizeimage =
+ fmt->get_frame_size(0, f->fmt.pix_mp.height,
+ f->fmt.pix_mp.width);
+ f->fmt.pix_mp.num_planes = fmt->num_planes;
}
err_invalid_fmt:
return rc;
@@ -646,14 +688,17 @@
struct msm_vidc_inst *inst;
unsigned long flags;
struct hal_buffer_requirements *bufreq;
- if (!q || !q->drv_priv) {
- dprintk(VIDC_ERR, "Invalid input, q = %p\n", q);
+ int extra_idx = 0;
+ if (!q || !num_buffers || !num_planes
+ || !sizes || !q->drv_priv) {
+ dprintk(VIDC_ERR, "Invalid input, q = %p, %p, %p\n",
+ q, num_buffers, num_planes);
return -EINVAL;
}
inst = q->drv_priv;
switch (q->type) {
case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
- *num_planes = 1;
+ *num_planes = inst->fmts[OUTPUT_PORT]->num_planes;
if (*num_buffers < MIN_NUM_OUTPUT_BUFFERS ||
*num_buffers > MAX_NUM_OUTPUT_BUFFERS)
*num_buffers = MIN_NUM_OUTPUT_BUFFERS;
@@ -664,6 +709,7 @@
break;
case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
dprintk(VIDC_DBG, "Getting bufreqs on capture plane\n");
+ *num_planes = inst->fmts[CAPTURE_PORT]->num_planes;
rc = msm_comm_try_state(inst, MSM_VIDC_OPEN_DONE);
if (rc) {
dprintk(VIDC_ERR, "Failed to open instance\n");
@@ -675,7 +721,6 @@
"Failed to get buffer requirements: %d\n", rc);
break;
}
- *num_planes = 1;
spin_lock_irqsave(&inst->lock, flags);
if (*num_buffers && *num_buffers >
inst->buff_req.buffer[HAL_BUFFER_OUTPUT].
@@ -700,11 +745,13 @@
inst->buff_req.buffer[1].buffer_count_actual,
inst->buff_req.buffer[1].buffer_size,
inst->buff_req.buffer[1].buffer_alignment);
- for (i = 0; i < *num_planes; i++) {
- sizes[i] = inst->fmts[CAPTURE_PORT]->get_frame_size(
- i, inst->prop.height, inst->prop.width);
+ sizes[0] = inst->buff_req.buffer[HAL_BUFFER_OUTPUT].buffer_size;
+ extra_idx =
+ EXTRADATA_IDX(inst->fmts[CAPTURE_PORT]->num_planes);
+ if (extra_idx && (extra_idx < VIDEO_MAX_PLANES)) {
+ sizes[extra_idx] =
+ inst->buff_req.buffer[HAL_BUFFER_EXTRADATA_OUTPUT].buffer_size;
}
-
break;
default:
dprintk(VIDC_ERR, "Invalid q type = %d\n", q->type);
@@ -987,6 +1034,12 @@
hal_property.enable = control.value;
pdata = &hal_property;
break;
+ case V4L2_CID_MPEG_VIDC_VIDEO_SYNC_FRAME_DECODE:
+ property_id =
+ HAL_PARAM_VDEC_SYNC_FRAME_DECODE;
+ hal_property.enable = control.value;
+ pdata = &hal_property;
+ break;
default:
break;
}
diff --git a/drivers/media/video/msm_vidc/msm_venc.c b/drivers/media/video/msm_vidc/msm_venc.c
index 0ea2a60..30aa802 100644
--- a/drivers/media/video/msm_vidc/msm_venc.c
+++ b/drivers/media/video/msm_vidc/msm_venc.c
@@ -1274,6 +1274,14 @@
v4l2_event_queue_fh(&inst->event_handler, &dqevent);
return rc;
}
+ rc = msm_comm_release_scratch_buffers(inst);
+ if (rc)
+ dprintk(VIDC_ERR, "Failed to release scratch buf:%d\n",
+ rc);
+ rc = msm_comm_release_persist_buffers(inst);
+ if (rc)
+ dprintk(VIDC_ERR, "Failed to release persist buf:%d\n",
+ rc);
rc = msm_comm_try_state(inst, MSM_VIDC_CLOSE_DONE);
break;
}
@@ -1507,6 +1515,43 @@
return rc;
}
+int msm_venc_release_buf(struct msm_vidc_inst *inst,
+ struct v4l2_buffer *b)
+{
+ int rc = 0;
+ int i;
+ struct vidc_buffer_addr_info buffer_info;
+
+ switch (b->type) {
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+ break;
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+ for (i = 0; i < b->length; i++) {
+ dprintk(VIDC_DBG,
+ "Release device_addr = %ld, size = %d, %d\n",
+ b->m.planes[i].m.userptr,
+ b->m.planes[i].length, inst->state);
+ buffer_info.buffer_size = b->m.planes[i].length;
+ buffer_info.buffer_type = HAL_BUFFER_OUTPUT;
+ buffer_info.num_buffers = 1;
+ buffer_info.align_device_addr =
+ b->m.planes[i].m.userptr;
+ buffer_info.extradata_size = 0;
+ buffer_info.extradata_addr = 0;
+ rc = vidc_hal_session_release_buffers(
+ (void *)inst->session, &buffer_info);
+ if (rc)
+ dprintk(VIDC_ERR,
+ "vidc_hal_session_release_buffers failed\n");
+ }
+ break;
+ default:
+ dprintk(VIDC_ERR, "Buffer type not recognized: %d\n", b->type);
+ break;
+ }
+ return rc;
+}
+
int msm_venc_qbuf(struct msm_vidc_inst *inst, struct v4l2_buffer *b)
{
struct buf_queue *q = NULL;
diff --git a/drivers/media/video/msm_vidc/msm_venc.h b/drivers/media/video/msm_vidc/msm_venc.h
index 83610b3..70671b6 100644
--- a/drivers/media/video/msm_vidc/msm_venc.h
+++ b/drivers/media/video/msm_vidc/msm_venc.h
@@ -26,6 +26,7 @@
int msm_venc_g_ctrl(void *instance, struct v4l2_control *a);
int msm_venc_reqbufs(void *instance, struct v4l2_requestbuffers *b);
int msm_venc_prepare_buf(struct msm_vidc_inst *inst, struct v4l2_buffer *b);
+int msm_venc_release_buf(struct msm_vidc_inst *inst, struct v4l2_buffer *b);
int msm_venc_qbuf(struct msm_vidc_inst *inst, struct v4l2_buffer *b);
int msm_venc_dqbuf(struct msm_vidc_inst *inst, struct v4l2_buffer *b);
int msm_venc_streamon(struct msm_vidc_inst *inst, enum v4l2_buf_type i);
diff --git a/drivers/media/video/msm_vidc/msm_vidc.c b/drivers/media/video/msm_vidc/msm_vidc.c
index 886113f..eebc07e 100644
--- a/drivers/media/video/msm_vidc/msm_vidc.c
+++ b/drivers/media/video/msm_vidc/msm_vidc.c
@@ -211,6 +211,8 @@
if (inst->session_type == MSM_VIDC_DECODER)
return msm_vdec_release_buf(instance, b);
+ if (inst->session_type == MSM_VIDC_ENCODER)
+ return msm_venc_release_buf(instance, b);
return -EINVAL;
}
@@ -403,6 +405,8 @@
goto err_invalid_core;
}
+ pr_info(VIDC_DBG_TAG "Opening video instance: %p, %d\n",
+ VIDC_INFO, inst, session_type);
mutex_init(&inst->sync_lock);
mutex_init(&inst->bufq[CAPTURE_PORT].lock);
mutex_init(&inst->bufq[OUTPUT_PORT].lock);
@@ -538,14 +542,14 @@
list_del(&inst->list);
}
mutex_unlock(&core->sync_lock);
+ cleanup_instance(inst);
if (inst->state != MSM_VIDC_CORE_INVALID &&
core->state != VIDC_CORE_INVALID)
rc = msm_comm_try_state(inst, MSM_VIDC_CORE_UNINIT);
if (rc)
dprintk(VIDC_ERR,
"Failed to move video instance to uninit state\n");
- cleanup_instance(inst);
+ pr_info(VIDC_DBG_TAG "Closed video instance: %p\n", VIDC_INFO, inst);
kfree(inst);
- dprintk(VIDC_DBG, "Closed the instance\n");
return 0;
}
diff --git a/drivers/media/video/msm_vidc/msm_vidc_common.c b/drivers/media/video/msm_vidc/msm_vidc_common.c
index ed234b9..038f03e 100644
--- a/drivers/media/video/msm_vidc/msm_vidc_common.c
+++ b/drivers/media/video/msm_vidc/msm_vidc_common.c
@@ -528,6 +528,7 @@
dqevent.type = V4L2_EVENT_MSM_VIDC_CLOSE_DONE;
dqevent.id = 0;
v4l2_event_queue_fh(&inst->event_handler, &dqevent);
+ inst->session = NULL;
wake_up(&inst->kernel_event_queue);
show_stats(inst);
} else {
@@ -685,7 +686,7 @@
struct vb2_buffer *vb;
struct vidc_hal_fbd *fill_buf_done;
if (!response) {
- pr_err("Invalid response from vidc_hal\n");
+ dprintk(VIDC_ERR, "Invalid response from vidc_hal\n");
return;
}
inst = (struct msm_vidc_inst *)response->session_id;
@@ -1545,14 +1546,14 @@
dprintk(VIDC_DBG, "Sent etb to HAL\n");
} else if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
struct vidc_seq_hdr seq_hdr;
+ int extra_idx = 0;
frame_data.filled_len = 0;
frame_data.buffer_type = HAL_BUFFER_OUTPUT;
- if (inst->extradata_handle) {
+ extra_idx =
+ EXTRADATA_IDX(inst->fmts[CAPTURE_PORT]->num_planes);
+ if (extra_idx)
frame_data.extradata_addr =
- inst->extradata_handle->device_addr;
- } else {
- frame_data.extradata_addr = 0;
- }
+ vb->v4l2_planes[extra_idx].m.userptr;
dprintk(VIDC_DBG,
"Sending ftb to hal: Alloc: %d :filled: %d",
frame_data.alloc_len, frame_data.filled_len);
@@ -1596,6 +1597,12 @@
{
int rc = 0;
mutex_lock(&inst->sync_lock);
+ if (inst->state < MSM_VIDC_OPEN_DONE || inst->state >= MSM_VIDC_CLOSE) {
+ dprintk(VIDC_ERR,
+ "Not in proper state to query buffer requirements\n");
+ rc = -EAGAIN;
+ goto exit;
+ }
init_completion(
&inst->completions[SESSION_MSG_INDEX(SESSION_PROPERTY_INFO)]);
rc = vidc_hal_session_get_buf_req((void *) inst->session);
@@ -1721,6 +1728,29 @@
return rc;
}
+int msm_comm_try_set_prop(struct msm_vidc_inst *inst,
+ enum hal_property ptype, void *pdata)
+{
+ int rc = 0;
+ if (!inst) {
+ dprintk(VIDC_ERR, "Invalid input: %p\n", inst);
+ return -EINVAL;
+ }
+ mutex_lock(&inst->sync_lock);
+ if (inst->state < MSM_VIDC_OPEN_DONE || inst->state >= MSM_VIDC_CLOSE) {
+ dprintk(VIDC_ERR, "Not in proper state to set property\n");
+ rc = -EAGAIN;
+ goto exit;
+ }
+ rc = vidc_hal_session_set_property((void *)inst->session,
+ ptype, pdata);
+ if (rc)
+ dprintk(VIDC_ERR, "Failed to set hal property for framesize\n");
+exit:
+ mutex_unlock(&inst->sync_lock);
+ return rc;
+}
+
int msm_comm_set_scratch_buffers(struct msm_vidc_inst *inst)
{
int rc = 0;
diff --git a/drivers/media/video/msm_vidc/msm_vidc_common.h b/drivers/media/video/msm_vidc/msm_vidc_common.h
index 20b4bc2..7562058 100644
--- a/drivers/media/video/msm_vidc/msm_vidc_common.h
+++ b/drivers/media/video/msm_vidc/msm_vidc_common.h
@@ -27,6 +27,8 @@
struct msm_vidc_inst *inst, enum v4l2_buf_type type);
int msm_comm_try_state(struct msm_vidc_inst *inst, int state);
int msm_comm_try_get_bufreqs(struct msm_vidc_inst *inst);
+int msm_comm_try_set_prop(struct msm_vidc_inst *inst,
+ enum hal_property ptype, void *pdata);
int msm_comm_set_scratch_buffers(struct msm_vidc_inst *inst);
int msm_comm_set_persist_buffers(struct msm_vidc_inst *inst);
int msm_comm_qbuf(struct vb2_buffer *vb);
diff --git a/drivers/media/video/msm_vidc/msm_vidc_internal.h b/drivers/media/video/msm_vidc/msm_vidc_internal.h
index 0f6740f..f288cc6 100644
--- a/drivers/media/video/msm_vidc/msm_vidc_internal.h
+++ b/drivers/media/video/msm_vidc/msm_vidc_internal.h
@@ -74,6 +74,8 @@
uv_buf_size = (uv_stride * uv_buf_height) + uv_alignment; \
buf_size = y_buf_size + uv_buf_size; }
+#define EXTRADATA_IDX(__num_planes) (__num_planes - 1)
+
enum vidc_ports {
OUTPUT_PORT,
CAPTURE_PORT,
diff --git a/drivers/media/video/msm_vidc/vidc_hal.c b/drivers/media/video/msm_vidc/vidc_hal.c
index 89f0273..190e132 100644
--- a/drivers/media/video/msm_vidc/vidc_hal.c
+++ b/drivers/media/video/msm_vidc/vidc_hal.c
@@ -1188,6 +1188,16 @@
pkt->size += sizeof(u32) * 2;
break;
}
+ case HAL_PARAM_VDEC_SYNC_FRAME_DECODE:
+ {
+ struct hfi_enable *hfi;
+ pkt->rg_property_data[0] =
+ HFI_PROPERTY_PARAM_VDEC_THUMBNAIL_MODE;
+ hfi = (struct hfi_enable *) &pkt->rg_property_data[1];
+ hfi->enable = ((struct hfi_enable *) pdata)->enable;
+ pkt->size += sizeof(u32) * 2;
+ break;
+ }
case HAL_PARAM_VENC_SYNC_FRAME_SEQUENCE_HEADER:
{
struct hfi_enable *hfi;
diff --git a/drivers/media/video/msm_vidc/vidc_hal.h b/drivers/media/video/msm_vidc/vidc_hal.h
index c586172..0e70e30 100644
--- a/drivers/media/video/msm_vidc/vidc_hal.h
+++ b/drivers/media/video/msm_vidc/vidc_hal.h
@@ -224,6 +224,15 @@
(HFI_PROPERTY_PARAM_VDEC_OX_START + 0x008)
#define HFI_PROPERTY_PARAM_VDEC_OUTPUT2_KEEP_ASPECT_RATIO\
(HFI_PROPERTY_PARAM_VDEC_OX_START + 0x009)
+#define HFI_PROPERTY_PARAM_VDEC_FRAME_RATE_EXTRADATA \
+ (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x00A)
+#define HFI_PROPERTY_PARAM_VDEC_PANSCAN_WNDW_EXTRADATA \
+ (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x00B)
+#define HFI_PROPERTY_PARAM_VDEC_RECOVERY_POINT_SEI_EXTRADATA \
+ (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x00C)
+#define HFI_PROPERTY_PARAM_VDEC_THUMBNAIL_MODE \
+ (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x00D)
+
#define HFI_PROPERTY_CONFIG_VDEC_OX_START \
(HFI_DOMAIN_BASE_VDEC + HFI_ARCH_OX_OFFSET + 0x0000)
diff --git a/drivers/media/video/msm_vidc/vidc_hal_api.h b/drivers/media/video/msm_vidc/vidc_hal_api.h
index 9def3e3..d3fa1d0 100644
--- a/drivers/media/video/msm_vidc/vidc_hal_api.h
+++ b/drivers/media/video/msm_vidc/vidc_hal_api.h
@@ -138,6 +138,7 @@
HAL_CONFIG_VENC_TIMESTAMP_SCALE,
HAL_PARAM_VENC_LOW_LATENCY,
HAL_PARAM_VENC_SYNC_FRAME_SEQUENCE_HEADER,
+ HAL_PARAM_VDEC_SYNC_FRAME_DECODE,
};
enum hal_domain {
@@ -358,7 +359,7 @@
HAL_BUFFER_EXTRADATA_OUTPUT2,
HAL_BUFFER_INTERNAL_SCRATCH,
HAL_BUFFER_INTERNAL_PERSIST,
- HAL_UNUSED_BUFFER = 0x10000000,
+ HAL_BUFFER_MAX
};
struct hal_frame_rate {
@@ -941,7 +942,7 @@
};
struct buffer_requirements {
- struct hal_buffer_requirements buffer[8];
+ struct hal_buffer_requirements buffer[HAL_BUFFER_MAX];
};
/* VIDC_HAL CORE API's */
diff --git a/drivers/media/video/vcap_v4l2.c b/drivers/media/video/vcap_v4l2.c
index 7757b5c..72a3f3b 100644
--- a/drivers/media/video/vcap_v4l2.c
+++ b/drivers/media/video/vcap_v4l2.c
@@ -501,7 +501,7 @@
buf = container_of(vb, struct vcap_buffer, vb);
buf->ion_handle = ion_import_dma_buf(dev->ion_client, b->m.userptr);
- if (IS_ERR((void *)buf->ion_handle)) {
+ if (IS_ERR_OR_NULL((void *)buf->ion_handle)) {
pr_err("%s: Could not alloc memory\n", __func__);
buf->ion_handle = NULL;
return -ENOMEM;
@@ -521,23 +521,32 @@
return 0;
}
-void free_ion_handle_work(struct vcap_dev *dev, struct vb2_buffer *vb)
+void free_ion_handle_work(struct vcap_client_data *c_data,
+ struct vb2_buffer *vb)
{
struct vcap_buffer *buf;
+ struct vcap_dev *dev = c_data->dev;
+ struct ion_handle *handle;
+ unsigned long flags = 0;
buf = container_of(vb, struct vcap_buffer, vb);
- if (buf->ion_handle == NULL) {
+
+ spin_lock_irqsave(&c_data->cap_slock, flags);
+ handle = buf->ion_handle;
+ buf->ion_handle = NULL;
+ spin_unlock_irqrestore(&c_data->cap_slock, flags);
+
+ if (handle == NULL) {
pr_debug("%s: no ION handle to free\n", __func__);
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;
+ ion_unmap_iommu(dev->ion_client, handle, dev->domain_num, 0);
+ ion_free(dev->ion_client, handle);
return;
}
-int free_ion_handle(struct vcap_dev *dev, struct vb2_queue *q,
+int free_ion_handle(struct vcap_client_data *c_data, struct vb2_queue *q,
struct v4l2_buffer *b)
{
struct vb2_buffer *vb;
@@ -555,7 +564,7 @@
if (NULL == vb)
return -EINVAL;
- free_ion_handle_work(dev, vb);
+ free_ion_handle_work(c_data, vb);
return 0;
}
@@ -627,7 +636,7 @@
/* clean ion handles */
list_for_each_entry(vb, &vq->queued_list, queued_entry)
- free_ion_handle_work(c_data->dev, vb);
+ free_ion_handle_work(c_data, vb);
return 0;
}
@@ -725,7 +734,7 @@
/* clean ion handles */
list_for_each_entry(vb, &vq->queued_list, queued_entry)
- free_ion_handle_work(c_data->dev, vb);
+ free_ion_handle_work(c_data, vb);
return 0;
}
@@ -823,7 +832,7 @@
/* clean ion handles */
list_for_each_entry(vb, &vq->queued_list, queued_entry)
- free_ion_handle_work(c_data->dev, vb);
+ free_ion_handle_work(c_data, vb);
return 0;
}
@@ -1056,7 +1065,7 @@
return rc;
rc = vcvp_qbuf(&c_data->vc_vidq, p);
if (rc < 0)
- free_ion_handle(c_data->dev,
+ free_ion_handle(c_data,
&c_data->vc_vidq, p);
return rc;
}
@@ -1065,7 +1074,7 @@
return rc;
rc = vb2_qbuf(&c_data->vc_vidq, p);
if (rc < 0)
- free_ion_handle(c_data->dev, &c_data->vc_vidq, p);
+ free_ion_handle(c_data, &c_data->vc_vidq, p);
return rc;
case V4L2_BUF_TYPE_INTERLACED_IN_DECODER:
if (c_data->op_mode == VC_AND_VP_VCAP_OP)
@@ -1075,7 +1084,7 @@
return rc;
rc = vb2_qbuf(&c_data->vp_in_vidq, p);
if (rc < 0)
- free_ion_handle(c_data->dev, &c_data->vp_in_vidq, p);
+ free_ion_handle(c_data, &c_data->vp_in_vidq, p);
return rc;
case V4L2_BUF_TYPE_VIDEO_OUTPUT:
rc = get_phys_addr(c_data->dev, &c_data->vp_out_vidq, p);
@@ -1083,7 +1092,7 @@
return rc;
rc = vb2_qbuf(&c_data->vp_out_vidq, p);
if (rc < 0)
- free_ion_handle(c_data->dev, &c_data->vp_out_vidq, p);
+ free_ion_handle(c_data, &c_data->vp_out_vidq, p);
return rc;
default:
pr_err("VCAP Error: %s: Unknown buffer type\n", __func__);
@@ -1097,6 +1106,9 @@
struct vcap_client_data *c_data = to_client_data(file->private_data);
int rc;
+ if (c_data->streaming == 0)
+ return -EPERM;
+
pr_debug("VCAP In DQ Buf %08x\n", (unsigned int)p->type);
switch (p->type) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
@@ -1105,7 +1117,7 @@
rc = vb2_dqbuf(&c_data->vc_vidq, p, file->f_flags & O_NONBLOCK);
if (rc < 0)
return rc;
- return free_ion_handle(c_data->dev, &c_data->vc_vidq, p);
+ return free_ion_handle(c_data, &c_data->vc_vidq, p);
case V4L2_BUF_TYPE_INTERLACED_IN_DECODER:
if (c_data->op_mode == VC_AND_VP_VCAP_OP)
return -EINVAL;
@@ -1113,13 +1125,13 @@
O_NONBLOCK);
if (rc < 0)
return rc;
- return free_ion_handle(c_data->dev, &c_data->vp_in_vidq, p);
+ return free_ion_handle(c_data, &c_data->vp_in_vidq, p);
case V4L2_BUF_TYPE_VIDEO_OUTPUT:
rc = vb2_dqbuf(&c_data->vp_out_vidq, p, file->f_flags &
O_NONBLOCK);
if (rc < 0)
return rc;
- return free_ion_handle(c_data->dev, &c_data->vp_out_vidq, p);
+ return free_ion_handle(c_data, &c_data->vp_out_vidq, p);
default:
pr_err("VCAP Error: %s: Unknown buffer type", __func__);
return -EINVAL;
@@ -1860,6 +1872,9 @@
struct vb2_queue *q;
unsigned int mask = 0;
+ if (c_data->streaming == 0)
+ return 0;
+
pr_debug("%s: Enter slect/poll\n", __func__);
switch (c_data->op_mode) {
diff --git a/drivers/media/video/vcap_vc.c b/drivers/media/video/vcap_vc.c
index 92b205e..f3c9362 100644
--- a/drivers/media/video/vcap_vc.c
+++ b/drivers/media/video/vcap_vc.c
@@ -248,6 +248,20 @@
c_data->vc_action.top_field =
!c_data->vc_action.top_field;
+ if (c_data->vc_format.mode == HAL_VCAP_MODE_INT)
+ c_data->vc_action.field_dropped =
+ !c_data->vc_action.field_dropped;
+
+ atomic_inc(&dev->dbg_p.vc_drop_count);
+ continue;
+ }
+ if (c_data->vc_format.mode == HAL_VCAP_MODE_INT &&
+ c_data->vc_action.field_dropped) {
+ spin_unlock(&c_data->cap_slock);
+ c_data->vc_action.field_dropped =
+ !c_data->vc_action.field_dropped;
+ c_data->vc_action.top_field =
+ !c_data->vc_action.top_field;
atomic_inc(&dev->dbg_p.vc_drop_count);
continue;
}
@@ -425,11 +439,10 @@
vc_format->mode << 10,
VCAP_VC_CTRL);
- writel_relaxed(vc_format->h_polar << 4 |
+ writel_relaxed(vc_format->d_polar << 8 |
+ vc_format->h_polar << 4 |
vc_format->v_polar << 0, VCAP_VC_POLARITY);
- writel_relaxed(vc_format->h_polar << 4 |
- vc_format->v_polar << 0, VCAP_VC_POLARITY);
writel_relaxed(((vc_format->htotal << 16) | vc_format->vtotal),
VCAP_VC_V_H_TOTAL);
writel_relaxed(((vc_format->hactive_end << 16) |
diff --git a/drivers/mfd/pm8xxx-pwm.c b/drivers/mfd/pm8xxx-pwm.c
index 70f4cd5..0e4240c 100644
--- a/drivers/mfd/pm8xxx-pwm.c
+++ b/drivers/mfd/pm8xxx-pwm.c
@@ -216,6 +216,7 @@
struct mutex pwm_mutex;
struct device *dev;
bool is_lpg_supported;
+ bool is_pwm_enable_sync_workaround_needed;
};
static struct pm8xxx_pwm_chip *pwm_chip;
@@ -815,9 +816,18 @@
if (pwm_chip->is_lpg_supported) {
if (pwm->dtest_mode_supported)
pm8xxx_pwm_set_dtest(pwm, 1);
+
pm8xxx_pwm_bank_sel(pwm);
rc = pm8xxx_pwm_bank_enable(pwm, 1);
pm8xxx_pwm_start(pwm, 1, 0);
+
+ /* In PM8038, due to hardware bug, PWM_VALUE register
+ * needs to be written one more time after enabling
+ * PWM mode.
+ */
+ if (pwm->chip->is_pwm_enable_sync_workaround_needed)
+ rc = pm8xxx_lpg_pwm_write(pwm, 3, 4);
+
} else {
pm8xxx_pwm_enable(pwm);
}
@@ -1391,6 +1401,12 @@
version == PM8XXX_VERSION_8038) {
chip->is_lpg_supported = 1;
}
+
+ if (version == PM8XXX_VERSION_8038)
+ chip->is_pwm_enable_sync_workaround_needed = 1;
+ else
+ chip->is_pwm_enable_sync_workaround_needed = 0;
+
if (chip->is_lpg_supported) {
if (version == PM8XXX_VERSION_8922 ||
version == PM8XXX_VERSION_8038) {
diff --git a/drivers/mfd/wcd9xxx-core.c b/drivers/mfd/wcd9xxx-core.c
index 62f1a93..a8e40f7 100644
--- a/drivers/mfd/wcd9xxx-core.c
+++ b/drivers/mfd/wcd9xxx-core.c
@@ -988,6 +988,20 @@
}
micbias->bias4_cfilt_sel = (u8)prop_val;
+ /* micbias external cap */
+ micbias->bias1_cap_mode =
+ (of_property_read_bool(dev->of_node, "qcom,cdc-micbias1-ext-cap") ?
+ MICBIAS_EXT_BYP_CAP : MICBIAS_NO_EXT_BYP_CAP);
+ micbias->bias2_cap_mode =
+ (of_property_read_bool(dev->of_node, "qcom,cdc-micbias2-ext-cap") ?
+ MICBIAS_EXT_BYP_CAP : MICBIAS_NO_EXT_BYP_CAP);
+ micbias->bias3_cap_mode =
+ (of_property_read_bool(dev->of_node, "qcom,cdc-micbias3-ext-cap") ?
+ MICBIAS_EXT_BYP_CAP : MICBIAS_NO_EXT_BYP_CAP);
+ micbias->bias4_cap_mode =
+ (of_property_read_bool(dev->of_node, "qcom,cdc-micbias4-ext-cap") ?
+ MICBIAS_EXT_BYP_CAP : MICBIAS_NO_EXT_BYP_CAP);
+
dev_dbg(dev, "ldoh_v %u cfilt1_mv %u cfilt2_mv %u cfilt3_mv %u",
(u32)micbias->ldoh_v, (u32)micbias->cfilt1_mv,
(u32)micbias->cfilt2_mv, (u32)micbias->cfilt3_mv);
@@ -998,6 +1012,11 @@
dev_dbg(dev, "bias3_cfilt_sel %u bias4_cfilt_sel %u\n",
(u32)micbias->bias3_cfilt_sel, (u32)micbias->bias4_cfilt_sel);
+ dev_dbg(dev, "bias1_ext_cap %d bias2_ext_cap %d\n",
+ micbias->bias1_cap_mode, micbias->bias2_cap_mode);
+ dev_dbg(dev, "bias3_ext_cap %d bias4_ext_cap %d\n",
+ micbias->bias3_cap_mode, micbias->bias4_cap_mode);
+
return 0;
}
diff --git a/drivers/mfd/wcd9xxx-irq.c b/drivers/mfd/wcd9xxx-irq.c
index 103c1a3..53e965c 100644
--- a/drivers/mfd/wcd9xxx-irq.c
+++ b/drivers/mfd/wcd9xxx-irq.c
@@ -218,6 +218,8 @@
if (ret < 0) {
dev_err(wcd9xxx->dev, "Failed to read interrupt status: %d\n",
ret);
+ dev_err(wcd9xxx->dev, "Disable irq %d\n", wcd9xxx->irq);
+ disable_irq_nosync(wcd9xxx->irq);
wcd9xxx_unlock_sleep(wcd9xxx);
return IRQ_NONE;
}
diff --git a/drivers/misc/tspp.c b/drivers/misc/tspp.c
index 1792104..8a1e0da 100644
--- a/drivers/misc/tspp.c
+++ b/drivers/misc/tspp.c
@@ -323,6 +323,10 @@
u32 time_limit;
u32 ref_count;
enum tspp_tsif_mode mode;
+ int clock_inverse;
+ int data_inverse;
+ int sync_inverse;
+ int enable_inverse;
/* debugfs */
struct dentry *dent_tsif;
@@ -698,6 +702,19 @@
if (start_hardware) {
ctl = TSIF_STS_CTL_EN_IRQ |
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;
+
switch (tsif_device->mode) {
case TSPP_TSIF_MODE_LOOPBACK:
ctl |= TSIF_STS_CTL_EN_NULL |
@@ -852,6 +869,10 @@
pdev->tsif[i].ref_count = 1; /* allows stopping hw */
tspp_stop_tsif(&pdev->tsif[i]); /* will reset ref_count to 0 */
pdev->tsif[i].time_limit = TSPP_TSIF_DEFAULT_TIME_LIMIT;
+ pdev->tsif[i].clock_inverse = 0;
+ pdev->tsif[i].data_inverse = 0;
+ pdev->tsif[i].sync_inverse = 0;
+ pdev->tsif[i].enable_inverse = 0;
}
writel_relaxed(TSPP_RST_RESET, pdev->base + TSPP_RST);
wmb();
@@ -913,7 +934,7 @@
}
/* open the stream */
- tspp_open_stream(dev, channel_id, src->source, src->mode);
+ tspp_open_stream(dev, channel_id, src);
return 0;
}
@@ -1011,14 +1032,37 @@
channel->pdev->tsif[index].mode = mode;
}
+static void tspp_set_signal_inversion(struct tspp_channel *channel,
+ int clock_inverse, int data_inverse,
+ int sync_inverse, int enable_inverse)
+{
+ int index;
+
+ switch (channel->src) {
+ case TSPP_SOURCE_TSIF0:
+ index = 0;
+ break;
+ case TSPP_SOURCE_TSIF1:
+ index = 1;
+ break;
+ default:
+ return;
+ }
+ channel->pdev->tsif[index].clock_inverse = clock_inverse;
+ channel->pdev->tsif[index].data_inverse = data_inverse;
+ channel->pdev->tsif[index].sync_inverse = sync_inverse;
+ channel->pdev->tsif[index].enable_inverse = enable_inverse;
+}
+
/*** TSPP API functions ***/
-int tspp_open_stream(u32 dev, u32 channel_id, enum tspp_source src, enum tspp_tsif_mode mode)
+int tspp_open_stream(u32 dev, u32 channel_id, struct tspp_select_source *source)
{
u32 val;
struct tspp_device *pdev;
struct tspp_channel *channel;
- TSPP_DEBUG("tspp_open_stream %i %i %i %i", dev, channel_id, src, mode);
+ TSPP_DEBUG("tspp_open_stream %i %i %i %i",
+ dev, channel_id, source->source, source->mode);
if (dev >= TSPP_MAX_DEVICES) {
pr_err("tspp: device id out of range");
return -ENODEV;
@@ -1035,10 +1079,13 @@
return -ENODEV;
}
channel = &pdev->channels[channel_id];
- channel->src = src;
- tspp_set_tsif_mode(channel, mode);
+ channel->src = source->source;
+ tspp_set_tsif_mode(channel, source->mode);
+ tspp_set_signal_inversion(channel, source->clk_inverse,
+ source->data_inverse, source->sync_inverse,
+ source->enable_inverse);
- switch (src) {
+ switch (source->source) {
case TSPP_SOURCE_TSIF0:
/* make sure TSIF0 is running & enabled */
if (tspp_start_tsif(&pdev->tsif[0]) != 0) {
@@ -1064,7 +1111,8 @@
case TSPP_SOURCE_MEM:
break;
default:
- pr_err("tspp: channel %i invalid source %i", channel->id, src);
+ pr_err("tspp: channel %i invalid source %i",
+ channel->id, source->source);
return -EBUSY;
}
diff --git a/drivers/mmc/card/mmc_block_test.c b/drivers/mmc/card/mmc_block_test.c
index 7d3ac83..2307d7a 100644
--- a/drivers/mmc/card/mmc_block_test.c
+++ b/drivers/mmc/card/mmc_block_test.c
@@ -20,6 +20,7 @@
#include <linux/mmc/host.h>
#include <linux/delay.h>
#include <linux/test-iosched.h>
+#include <linux/jiffies.h>
#include "queue.h"
#include <linux/mmc/mmc.h>
@@ -36,6 +37,28 @@
#define SECTOR_SIZE 512
#define NUM_OF_SECTORS_PER_BIO ((BIO_U32_SIZE * 4) / SECTOR_SIZE)
#define BIO_TO_SECTOR(x) (x * NUM_OF_SECTORS_PER_BIO)
+/* the desired long test size to be written or read */
+#define LONG_TEST_MAX_NUM_BYTES (50*1024*1024) /* 50MB */
+/* request queue limitation is 128 requests, and we leave 10 spare requests */
+#define TEST_MAX_REQUESTS 118
+#define LONG_TEST_MAX_NUM_REQS (LONG_TEST_MAX_NUM_BYTES / \
+ (TEST_MAX_BIOS_PER_REQ * sizeof(int) * BIO_U32_SIZE))
+/* this doesn't allow the test requests num to be greater than the maximum */
+#define LONG_TEST_ACTUAL_NUM_REQS \
+ ((TEST_MAX_REQUESTS < LONG_TEST_MAX_NUM_REQS) ? \
+ TEST_MAX_REQUESTS : LONG_TEST_MAX_NUM_REQS)
+#define MB_MSEC_RATIO_APPROXIMATION ((1024 * 1024) / 1000)
+/* actual number of bytes in test */
+#define LONG_TEST_ACTUAL_BYTE_NUM (LONG_TEST_ACTUAL_NUM_REQS * \
+ (TEST_MAX_BIOS_PER_REQ * sizeof(int) * BIO_U32_SIZE))
+/* actual number of MiB in test multiplied by 10, for single digit precision*/
+#define LONG_TEST_ACTUAL_MB_NUM_X_10 ((LONG_TEST_ACTUAL_BYTE_NUM * 10) / \
+ (1024 * 1024))
+/* extract integer value */
+#define LONG_TEST_SIZE_INTEGER (LONG_TEST_ACTUAL_MB_NUM_X_10 / 10)
+/* and calculate the MiB value fraction */
+#define LONG_TEST_SIZE_FRACTION (LONG_TEST_ACTUAL_MB_NUM_X_10 - \
+ (LONG_TEST_SIZE_INTEGER * 10))
#define 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)
@@ -127,6 +150,9 @@
BKOPS_URGENT_LEVEL_2_TWO_REQS,
BKOPS_URGENT_LEVEL_3,
BKOPS_MAX_TESTCASE = BKOPS_URGENT_LEVEL_3,
+
+ TEST_LONG_SEQUENTIAL_READ,
+ TEST_LONG_SEQUENTIAL_WRITE,
};
enum mmc_block_test_group {
@@ -154,6 +180,8 @@
struct dentry *packing_control_test;
struct dentry *discard_sanitize_test;
struct dentry *bkops_test;
+ struct dentry *long_sequential_read_test;
+ struct dentry *long_sequential_write_test;
};
struct mmc_block_test_data {
@@ -562,6 +590,10 @@
return "\nTest urgent BKOPS level 2, followed by a request";
case BKOPS_URGENT_LEVEL_3:
return "\nTest urgent BKOPS level 3";
+ case TEST_LONG_SEQUENTIAL_READ:
+ return "Test long sequential read";
+ case TEST_LONG_SEQUENTIAL_WRITE:
+ return "Test long sequential write";
default:
return "Unknown testcase";
}
@@ -818,8 +850,10 @@
test_pr_info("%s: Adding %d write requests, first req_id=%d", __func__,
num_requests, td->wr_rd_next_req_id);
- for (i = 1; i <= num_requests; i++) {
- start_sec = td->start_sector + 4096 * td->num_of_write_bios;
+ for (i = 1 ; i <= num_requests ; i++) {
+ start_sec =
+ td->start_sector + sizeof(int) *
+ BIO_U32_SIZE * td->num_of_write_bios;
if (is_random)
pseudo_rnd_num_of_bios(bio_seed, &num_bios);
else
@@ -1139,7 +1173,8 @@
if (i > (num_requests / 2))
is_err_expected = 1;
- start_address = td->start_sector + 4096 * td->num_of_write_bios;
+ start_address = td->start_sector +
+ sizeof(int) * BIO_U32_SIZE * td->num_of_write_bios;
ret = test_iosched_add_wr_rd_test_req(is_err_expected, WRITE,
start_address, (i % 5) + 1, TEST_PATTERN_5A,
NULL);
@@ -1243,6 +1278,48 @@
return num_requests;
}
+static int prepare_long_test_requests(struct test_data *td)
+{
+
+ int ret;
+ int start_sec;
+ int j;
+ int test_direction;
+
+ if (td)
+ start_sec = td->start_sector;
+ else {
+ test_pr_err("%s: NULL td\n", __func__);
+ return -EINVAL;
+ }
+
+ if (td->test_info.testcase == TEST_LONG_SEQUENTIAL_WRITE)
+ test_direction = WRITE;
+ else
+ test_direction = READ;
+
+ test_pr_info("%s: Adding %d write requests, first req_id=%d", __func__,
+ LONG_TEST_ACTUAL_NUM_REQS, td->wr_rd_next_req_id);
+
+ for (j = 0; j < LONG_TEST_ACTUAL_NUM_REQS; j++) {
+
+ ret = test_iosched_add_wr_rd_test_req(0, test_direction,
+ start_sec,
+ TEST_MAX_BIOS_PER_REQ,
+ TEST_NO_PATTERN, NULL);
+ if (ret) {
+ test_pr_err("%s: failed to add a bio request",
+ __func__);
+ return ret;
+ }
+
+ start_sec +=
+ (TEST_MAX_BIOS_PER_REQ * sizeof(int) * BIO_U32_SIZE);
+ }
+
+ return 0;
+}
+
/*
* An implementation for the prepare_test_fn pointer in the test_info
* data structure. According to the testcase we add the right number of requests
@@ -1351,9 +1428,15 @@
ret = prepare_packed_control_tests_requests(td, 0,
test_packed_trigger, is_random);
break;
+ case TEST_LONG_SEQUENTIAL_WRITE:
+ ret = prepare_long_test_requests(td);
+ break;
+ case TEST_LONG_SEQUENTIAL_READ:
+ ret = prepare_long_test_requests(td);
+ break;
default:
test_pr_info("%s: Invalid test case...", __func__);
- return -EINVAL;
+ ret = -EINVAL;
}
return ret;
@@ -2430,6 +2513,185 @@
.read = bkops_test_read,
};
+static ssize_t long_sequential_read_test_write(struct file *file,
+ const char __user *buf,
+ size_t count,
+ loff_t *ppos)
+{
+ int ret = 0;
+ int i = 0;
+ int number = -1;
+ unsigned int mtime, integer, fraction;
+
+ test_pr_info("%s: -- Long Sequential Read TEST --", __func__);
+
+ sscanf(buf, "%d", &number);
+
+ if (number <= 0)
+ number = 1;
+
+ 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_test;
+ mbtd->test_info.get_test_case_str_fn = get_test_case_str;
+
+ for (i = 0 ; i < number ; ++i) {
+ test_pr_info("%s: Cycle # %d / %d", __func__, i+1, number);
+ test_pr_info("%s: ====================", __func__);
+
+ mbtd->test_info.testcase = TEST_LONG_SEQUENTIAL_READ;
+ mbtd->is_random = NON_RANDOM_TEST;
+ ret = test_iosched_start_test(&mbtd->test_info);
+ if (ret)
+ break;
+
+ mtime = jiffies_to_msecs(mbtd->test_info.test_duration);
+
+ test_pr_info("%s: time is %u msec, size is %u.%u MiB",
+ __func__, mtime, LONG_TEST_SIZE_INTEGER,
+ LONG_TEST_SIZE_FRACTION);
+
+ /* we first multiply in order not to lose precision */
+ mtime *= MB_MSEC_RATIO_APPROXIMATION;
+ /* divide values to get a MiB/sec integer value with one
+ digit of precision. Multiply by 10 for one digit precision
+ */
+ fraction = integer = (LONG_TEST_ACTUAL_BYTE_NUM * 10) / mtime;
+ integer /= 10;
+ /* and calculate the MiB value fraction */
+ fraction -= integer * 10;
+
+ test_pr_info("%s: Throughput: %u.%u MiB/sec\n"
+ , __func__, integer, fraction);
+
+ /* Allow FS requests to be dispatched */
+ msleep(1000);
+ }
+
+ return count;
+}
+
+static ssize_t long_sequential_read_test_read(struct file *file,
+ char __user *buffer,
+ size_t count,
+ loff_t *offset)
+{
+ memset((void *)buffer, 0, count);
+
+ snprintf(buffer, count,
+ "\nlong_sequential_read_test\n"
+ "=========\n"
+ "Description:\n"
+ "This test runs the following scenarios\n"
+ "- Long Sequential Read Test: this test measures read "
+ "throughput at the driver level by sequentially reading many "
+ "large requests.\n");
+
+ if (message_repeat == 1) {
+ message_repeat = 0;
+ return strnlen(buffer, count);
+ } else
+ return 0;
+}
+
+const struct file_operations long_sequential_read_test_ops = {
+ .open = test_open,
+ .write = long_sequential_read_test_write,
+ .read = long_sequential_read_test_read,
+};
+
+static ssize_t long_sequential_write_test_write(struct file *file,
+ const char __user *buf,
+ size_t count,
+ loff_t *ppos)
+{
+ int ret = 0;
+ int i = 0;
+ int number = -1;
+ unsigned int mtime, integer, fraction;
+
+ test_pr_info("%s: -- Long Sequential Write TEST --", __func__);
+
+ sscanf(buf, "%d", &number);
+
+ if (number <= 0)
+ number = 1;
+
+ 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_test;
+ mbtd->test_info.get_test_case_str_fn = get_test_case_str;
+
+ for (i = 0 ; i < number ; ++i) {
+ test_pr_info("%s: Cycle # %d / %d", __func__, i+1, number);
+ test_pr_info("%s: ====================", __func__);
+
+ mbtd->test_info.testcase = TEST_LONG_SEQUENTIAL_WRITE;
+ mbtd->is_random = NON_RANDOM_TEST;
+ ret = test_iosched_start_test(&mbtd->test_info);
+ if (ret)
+ break;
+
+ mtime = jiffies_to_msecs(mbtd->test_info.test_duration);
+
+ test_pr_info("%s: time is %u msec, size is %u.%u MiB",
+ __func__, mtime, LONG_TEST_SIZE_INTEGER,
+ LONG_TEST_SIZE_FRACTION);
+
+ /* we first multiply in order not to lose precision */
+ mtime *= MB_MSEC_RATIO_APPROXIMATION;
+ /* divide values to get a MiB/sec integer value with one
+ digit of precision
+ */
+ fraction = integer = (LONG_TEST_ACTUAL_BYTE_NUM * 10) / mtime;
+ integer /= 10;
+ /* and calculate the MiB value fraction */
+ fraction -= integer * 10;
+
+ test_pr_info("%s: Throughput: %u.%u MiB/sec\n",
+ __func__, integer, fraction);
+
+ /* Allow FS requests to be dispatched */
+ msleep(1000);
+ }
+
+ return count;
+}
+
+static ssize_t long_sequential_write_test_read(struct file *file,
+ char __user *buffer,
+ size_t count,
+ loff_t *offset)
+{
+ memset((void *)buffer, 0, count);
+
+ snprintf(buffer, count,
+ "\nlong_sequential_write_test\n"
+ "=========\n"
+ "Description:\n"
+ "This test runs the following scenarios\n"
+ "- Long Sequential Write Test: this test measures write "
+ "throughput at the driver level by sequentially writing many "
+ "large requests\n");
+
+ if (message_repeat == 1) {
+ message_repeat = 0;
+ return strnlen(buffer, count);
+ } else
+ return 0;
+}
+
+const struct file_operations long_sequential_write_test_ops = {
+ .open = test_open,
+ .write = long_sequential_write_test_write,
+ .read = long_sequential_write_test_read,
+};
+
+
static void mmc_block_test_debugfs_cleanup(void)
{
debugfs_remove(mbtd->debug.random_test_seed);
@@ -2439,6 +2701,8 @@
debugfs_remove(mbtd->debug.packing_control_test);
debugfs_remove(mbtd->debug.discard_sanitize_test);
debugfs_remove(mbtd->debug.bkops_test);
+ debugfs_remove(mbtd->debug.long_sequential_read_test);
+ debugfs_remove(mbtd->debug.long_sequential_write_test);
}
static int mmc_block_test_debugfs_init(void)
@@ -2521,6 +2785,26 @@
if (!mbtd->debug.bkops_test)
goto err_nomem;
+ mbtd->debug.long_sequential_read_test = debugfs_create_file(
+ "long_sequential_read_test",
+ S_IRUGO | S_IWUGO,
+ tests_root,
+ NULL,
+ &long_sequential_read_test_ops);
+
+ if (!mbtd->debug.long_sequential_read_test)
+ goto err_nomem;
+
+ mbtd->debug.long_sequential_write_test = debugfs_create_file(
+ "long_sequential_write_test",
+ S_IRUGO | S_IWUGO,
+ tests_root,
+ NULL,
+ &long_sequential_write_test_ops);
+
+ if (!mbtd->debug.long_sequential_write_test)
+ goto err_nomem;
+
return 0;
err_nomem:
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 3aa38d2..48516b6 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -2639,7 +2639,9 @@
mmc_card_is_removable(host))
return err;
- mmc_claim_host(host);
+ if (!mmc_try_claim_host(host))
+ return -EBUSY;
+
if (card && mmc_card_mmc(card) &&
(card->ext_csd.cache_size > 0)) {
enable = !!enable;
diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c
index 2fbd804..72f4a5c 100644
--- a/drivers/mmc/host/msm_sdcc.c
+++ b/drivers/mmc/host/msm_sdcc.c
@@ -3197,8 +3197,21 @@
/*
* For DDR50 mode, controller needs clock rate to be
* double than what is required on the SD card CLK pin.
+ *
+ * Setting DDR timing mode in controller before setting the
+ * clock rate will make sure that card don't see the double
+ * clock rate even for very small duration. Some eMMC
+ * cards seems to lock up if they see clock frequency > 52MHz.
*/
if (ios->timing == MMC_TIMING_UHS_DDR50) {
+ u32 clk;
+
+ clk = readl_relaxed(host->base + MMCICLOCK);
+ clk &= ~(0x7 << 14); /* clear SELECT_IN field */
+ clk |= (3 << 14); /* set DDR timing mode */
+ writel_relaxed(clk, host->base + MMCICLOCK);
+ msmsdcc_sync_reg_wr(host);
+
/*
* Make sure that we don't double the clock if
* doubled clock rate is already set
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index 376750f..7b7e05e 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -343,6 +343,18 @@
SMB349 be operated as a slave device via the power supply
framework.
+config SMB350_CHARGER
+ tristate "smb350 charger"
+ depends on I2C
+ help
+ Say Y to enable battery charging by SMB350 switching mode based
+ external charger. The device supports stack-cell battery charging.
+ The driver configures the device volatile parameters
+ and the charger device works autonomously.
+ The driver supports charger-enable and charger-suspend/resume.
+ The driver reports the charger status via the power supply framework.
+ A charger status change triggers an IRQ via the device STAT pin.
+
config BATTERY_MSM_FAKE
tristate "Fake MSM battery"
depends on ARCH_MSM && BATTERY_MSM
@@ -388,6 +400,18 @@
help
Say Y here to enable Test sysfs Interface for BQ27520 Drivers.
+config BATTERY_BQ28400
+ tristate "BQ28400 battery driver"
+ depends on I2C
+ default n
+ help
+ Say Y here to enable support for batteries with BQ28400 (I2C) chips.
+ The bq28400 Texas Instruments Inc device monitors the battery
+ charging/discharging status via Rsens resistor, typically 10 mohm.
+ It monitors the battery temperature via Thermistor.
+ The device monitors the battery level (Relative-State-Of-Charge).
+ The device is SBS compliant, providing battery info over I2C.
+
config PM8921_CHARGER
tristate "PM8921 Charger driver"
depends on MFD_PM8921_CORE
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index 3521cfd..3e74f35 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -48,10 +48,12 @@
obj-$(CONFIG_PM8058_CHARGER) += pmic8058-charger.o
obj-$(CONFIG_ISL9519_CHARGER) += isl9519q.o
obj-$(CONFIG_SMB349_CHARGER) += smb349.o
+obj-$(CONFIG_SMB350_CHARGER) += smb350_charger.o
obj-$(CONFIG_PM8058_FIX_USB) += pm8058_usb_fix.o
obj-$(CONFIG_BATTERY_QCIBAT) += qci_battery.o
obj-$(CONFIG_BATTERY_BQ27520) += bq27520_fuelgauger.o
obj-$(CONFIG_BATTERY_BQ27541) += bq27541_fuelgauger.o
+obj-$(CONFIG_BATTERY_BQ28400) += bq28400_battery.o
obj-$(CONFIG_SMB137B_CHARGER) += smb137b.o
obj-$(CONFIG_PM8XXX_CCADC) += pm8xxx-ccadc.o
obj-$(CONFIG_PM8921_BMS) += pm8921-bms.o
diff --git a/drivers/power/bq28400_battery.c b/drivers/power/bq28400_battery.c
new file mode 100644
index 0000000..39d52cb
--- /dev/null
+++ b/drivers/power/bq28400_battery.c
@@ -0,0 +1,917 @@
+/* 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.
+ *
+ */
+
+/*
+ * High Level description:
+ * http://www.ti.com/lit/ds/symlink/bq28400.pdf
+ * Thechnical Reference:
+ * http://www.ti.com/lit/ug/sluu431/sluu431.pdf
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/debugfs.h>
+#include <linux/workqueue.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/power_supply.h>
+#include <linux/bitops.h>
+#include <linux/regulator/consumer.h>
+#include <linux/printk.h>
+
+#define BQ28400_NAME "bq28400"
+#define BQ28400_REV "1.0"
+
+/* SBS Commands (page 63) */
+
+#define SBS_MANUFACTURER_ACCESS 0x00
+#define SBS_BATTERY_MODE 0x03
+#define SBS_TEMPERATURE 0x08
+#define SBS_VOLTAGE 0x09
+#define SBS_CURRENT 0x0A
+#define SBS_AVG_CURRENT 0x0B
+#define SBS_MAX_ERROR 0x0C
+#define SBS_RSOC 0x0D /* Relative State Of Charge */
+#define SBS_REMAIN_CAPACITY 0x0F
+#define SBS_FULL_CAPACITY 0x10
+#define SBS_CHG_CURRENT 0x14
+#define SBS_CHG_VOLTAGE 0x15
+#define SBS_BATTERY_STATUS 0x16
+#define SBS_CYCLE_COUNT 0x17
+#define SBS_DESIGN_CAPACITY 0x18
+#define SBS_DESIGN_VOLTAGE 0x19
+#define SBS_SPEC_INFO 0x1A
+#define SBS_MANUFACTURE_DATE 0x1B
+#define SBS_SERIAL_NUMBER 0x1C
+#define SBS_MANUFACTURER_NAME 0x20
+#define SBS_DEVICE_NAME 0x21
+#define SBS_DEVICE_CHEMISTRY 0x22
+#define SBS_MANUFACTURER_DATA 0x23
+#define SBS_AUTHENTICATE 0x2F
+#define SBS_CELL_VOLTAGE1 0x3E
+#define SBS_CELL_VOLTAGE2 0x3F
+
+/* Extended SBS Commands (page 71) */
+
+#define SBS_FET_CONTROL 0x46
+#define SBS_SAFETY_ALERT 0x50
+#define SBS_SAFETY_STATUS 0x51
+#define SBS_PE_ALERT 0x52
+#define SBS_PE_STATUS 0x53
+#define SBS_OPERATION_STATUS 0x54
+#define SBS_CHARGING_STATUS 0x55
+#define SBS_FET_STATUS 0x56
+#define SBS_PACK_VOLTAGE 0x5A
+#define SBS_TS0_TEMPERATURE 0x5E
+#define SBS_FULL_ACCESS_KEY 0x61
+#define SBS_PF_KEY 0x62
+#define SBS_AUTH_KEY3 0x63
+#define SBS_AUTH_KEY2 0x64
+#define SBS_AUTH_KEY1 0x65
+#define SBS_AUTH_KEY0 0x66
+#define SBS_MANUFACTURER_INFO 0x70
+#define SBS_SENSE_RESISTOR 0x71
+#define SBS_TEMP_RANGE 0x72
+
+/* SBS Sub-Commands (16 bits) */
+/* SBS_MANUFACTURER_ACCESS CMD */
+#define SUBCMD_DEVICE_TYPE 0x01
+#define SUBCMD_FIRMWARE_VERSION 0x02
+#define SUBCMD_HARDWARE_VERSION 0x03
+#define SUBCMD_DF_CHECKSUM 0x04
+#define SUBCMD_EDV 0x05
+#define SUBCMD_CHEMISTRY_ID 0x08
+
+/* SBS_CHARGING_STATUS */
+#define CHG_STATUS_BATTERY_DEPLETED BIT(0)
+#define CHG_STATUS_OVERCHARGE BIT(1)
+#define CHG_STATUS_OVERCHARGE_CURRENT BIT(2)
+#define CHG_STATUS_OVERCHARGE_VOLTAGE BIT(3)
+#define CHG_STATUS_CELL_BALANCING BIT(6)
+#define CHG_STATUS_HOT_TEMP_CHARGING BIT(8)
+#define CHG_STATUS_STD1_TEMP_CHARGING BIT(9)
+#define CHG_STATUS_STD2_TEMP_CHARGING BIT(10)
+#define CHG_STATUS_LOW_TEMP_CHARGING BIT(11)
+#define CHG_STATUS_PRECHARGING_EXIT BIT(13)
+#define CHG_STATUS_SUSPENDED BIT(14)
+#define CHG_STATUS_DISABLED BIT(15)
+
+/* SBS_FET_STATUS */
+#define FET_STATUS_DISCHARGE BIT(1)
+#define FET_STATUS_CHARGE BIT(2)
+#define FET_STATUS_PRECHARGE BIT(3)
+
+/* SBS_BATTERY_STATUS */
+#define BAT_STATUS_SBS_ERROR 0x0F
+#define BAT_STATUS_EMPTY BIT(4)
+#define BAT_STATUS_FULL BIT(5)
+#define BAT_STATUS_DISCHARGING BIT(6)
+#define BAT_STATUS_OVER_TEMPERATURE BIT(12)
+#define BAT_STATUS_OVER_CHARGED BIT(15)
+
+#define ZERO_DEGREE_CELSIUS_IN_TENTH_KELVIN (-2731)
+#define BQ_TERMINATION_CURRENT_MA 200
+
+#define BQ_MAX_STR_LEN 32
+
+struct bq28400_device {
+ struct i2c_client *client;
+ struct delayed_work periodic_user_space_update_work;
+ struct dentry *dent;
+ struct power_supply batt_psy;
+ struct power_supply *dc_psy;
+ bool is_charging_enabled;
+};
+
+static struct bq28400_device *bq28400_dev;
+
+static enum power_supply_property pm_power_props[] = {
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_CHARGE_TYPE,
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_TECHNOLOGY,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_CAPACITY,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+ POWER_SUPPLY_PROP_CURRENT_AVG,
+ POWER_SUPPLY_PROP_TEMP,
+ POWER_SUPPLY_PROP_CHARGE_FULL,
+ POWER_SUPPLY_PROP_CHARGE_NOW,
+ POWER_SUPPLY_PROP_MODEL_NAME,
+ POWER_SUPPLY_PROP_MANUFACTURER,
+};
+
+struct debug_reg {
+ char *name;
+ u8 reg;
+ u16 subcmd;
+};
+
+#define BQ28400_DEBUG_REG(x) {#x, SBS_##x, 0}
+#define BQ28400_DEBUG_SUBREG(x, y) {#y, SBS_##x, SUBCMD_##y}
+
+/* Note: Some register can be read only in Unsealed mode */
+static struct debug_reg bq28400_debug_regs[] = {
+ BQ28400_DEBUG_REG(MANUFACTURER_ACCESS),
+ BQ28400_DEBUG_REG(BATTERY_MODE),
+ BQ28400_DEBUG_REG(TEMPERATURE),
+ BQ28400_DEBUG_REG(VOLTAGE),
+ BQ28400_DEBUG_REG(CURRENT),
+ BQ28400_DEBUG_REG(AVG_CURRENT),
+ BQ28400_DEBUG_REG(MAX_ERROR),
+ BQ28400_DEBUG_REG(RSOC),
+ BQ28400_DEBUG_REG(REMAIN_CAPACITY),
+ BQ28400_DEBUG_REG(FULL_CAPACITY),
+ BQ28400_DEBUG_REG(CHG_CURRENT),
+ BQ28400_DEBUG_REG(CHG_VOLTAGE),
+ BQ28400_DEBUG_REG(BATTERY_STATUS),
+ BQ28400_DEBUG_REG(CYCLE_COUNT),
+ BQ28400_DEBUG_REG(DESIGN_CAPACITY),
+ BQ28400_DEBUG_REG(DESIGN_VOLTAGE),
+ BQ28400_DEBUG_REG(SPEC_INFO),
+ BQ28400_DEBUG_REG(MANUFACTURE_DATE),
+ BQ28400_DEBUG_REG(SERIAL_NUMBER),
+ BQ28400_DEBUG_REG(MANUFACTURER_NAME),
+ BQ28400_DEBUG_REG(DEVICE_NAME),
+ BQ28400_DEBUG_REG(DEVICE_CHEMISTRY),
+ BQ28400_DEBUG_REG(MANUFACTURER_DATA),
+ BQ28400_DEBUG_REG(AUTHENTICATE),
+ BQ28400_DEBUG_REG(CELL_VOLTAGE1),
+ BQ28400_DEBUG_REG(CELL_VOLTAGE2),
+ BQ28400_DEBUG_REG(SAFETY_ALERT),
+ BQ28400_DEBUG_REG(SAFETY_STATUS),
+ BQ28400_DEBUG_REG(PE_ALERT),
+ BQ28400_DEBUG_REG(PE_STATUS),
+ BQ28400_DEBUG_REG(OPERATION_STATUS),
+ BQ28400_DEBUG_REG(CHARGING_STATUS),
+ BQ28400_DEBUG_REG(FET_STATUS),
+ BQ28400_DEBUG_REG(FULL_ACCESS_KEY),
+ BQ28400_DEBUG_REG(PF_KEY),
+ BQ28400_DEBUG_REG(MANUFACTURER_INFO),
+ BQ28400_DEBUG_REG(SENSE_RESISTOR),
+ BQ28400_DEBUG_REG(TEMP_RANGE),
+ BQ28400_DEBUG_SUBREG(MANUFACTURER_ACCESS, DEVICE_TYPE),
+ BQ28400_DEBUG_SUBREG(MANUFACTURER_ACCESS, FIRMWARE_VERSION),
+ BQ28400_DEBUG_SUBREG(MANUFACTURER_ACCESS, HARDWARE_VERSION),
+ BQ28400_DEBUG_SUBREG(MANUFACTURER_ACCESS, DF_CHECKSUM),
+ BQ28400_DEBUG_SUBREG(MANUFACTURER_ACCESS, EDV),
+ BQ28400_DEBUG_SUBREG(MANUFACTURER_ACCESS, CHEMISTRY_ID),
+};
+
+static int bq28400_read_reg(struct i2c_client *client, u8 reg)
+{
+ int val;
+
+ val = i2c_smbus_read_word_data(client, reg);
+ if (val < 0)
+ pr_err("i2c read fail. reg = 0x%x.ret = %d.\n", reg, val);
+ else
+ pr_debug("reg = 0x%02X.val = 0x%04X.\n", reg , val);
+
+ return val;
+}
+
+static int bq28400_write_reg(struct i2c_client *client, u8 reg, u16 val)
+{
+ int ret;
+
+ ret = i2c_smbus_write_word_data(client, reg, val);
+ if (ret < 0)
+ pr_err("i2c read fail. reg = 0x%x.val = 0x%x.ret = %d.\n",
+ reg, val, ret);
+ else
+ pr_debug("reg = 0x%02X.val = 0x%02X.\n", reg , val);
+
+ return ret;
+}
+
+static int bq28400_read_subcmd(struct i2c_client *client, u8 reg, u16 subcmd)
+{
+ int ret;
+ u8 buf[4];
+ u16 val = 0;
+
+ buf[0] = reg;
+ buf[1] = subcmd & 0xFF;
+ buf[2] = (subcmd >> 8) & 0xFF;
+
+ /* Control sub-command */
+ ret = i2c_master_send(client, buf, 3);
+ if (ret < 0) {
+ pr_err("i2c tx fail. reg = 0x%x.ret = %d.\n", reg, ret);
+ return ret;
+ }
+ udelay(66);
+
+ /* Read Result of subcmd */
+ ret = i2c_master_send(client, buf, 1);
+ memset(buf, 0xAA, sizeof(buf));
+ ret = i2c_master_recv(client, buf, 2);
+ if (ret < 0) {
+ pr_err("i2c rx fail. reg = 0x%x.ret = %d.\n", reg, ret);
+ return ret;
+ }
+ val = (buf[1] << 8) + buf[0];
+
+ pr_debug("reg = 0x%02X.subcmd = 0x%x.val = 0x%04X.\n",
+ reg , subcmd, val);
+
+ return val;
+}
+
+static int bq28400_read_block(struct i2c_client *client, u8 reg,
+ u8 len, u8 *buf)
+{
+ int ret;
+ u32 val;
+
+ ret = i2c_smbus_read_i2c_block_data(client, reg, len, buf);
+ val = buf[0] + (buf[1] << 8) + (buf[2] << 16) + (buf[3] << 24);
+
+ if (ret < 0)
+ pr_err("i2c read fail. reg = 0x%x.ret = %d.\n", reg, ret);
+ else
+ pr_debug("reg = 0x%02X.val = 0x%04X.\n", reg , val);
+
+ return val;
+}
+
+/*
+ * Read a string from a device.
+ * Returns string length on success or error on failure (negative value).
+ */
+static int bq28400_read_string(struct i2c_client *client, u8 reg, char *str,
+ u8 max_len)
+{
+ int ret;
+ int len;
+
+ ret = bq28400_read_block(client, reg, max_len, str);
+ if (ret < 0)
+ return ret;
+
+ len = str[0]; /* Actual length */
+ if (len > max_len - 2) { /* reduce len byte and null */
+ pr_err("len = %d invalid.\n", len);
+ return -EINVAL;
+ }
+
+ memcpy(&str[0], &str[1], len); /* Move sting to the start */
+ str[len] = 0; /* put NULL after actual size */
+
+ pr_debug("len = %d.str = %s.\n", len, str);
+
+ return len;
+}
+
+#define BQ28400_INVALID_TEMPERATURE -999
+/*
+ * Return the battery temperature in tenths of degree Celsius
+ * Or -99.9 C if something fails.
+ */
+static int bq28400_read_temperature(struct i2c_client *client)
+{
+ int temp;
+
+ /* temperature resolution 0.1 Kelvin */
+ temp = bq28400_read_reg(client, SBS_TEMPERATURE);
+ if (temp < 0)
+ return BQ28400_INVALID_TEMPERATURE;
+
+ temp = temp + ZERO_DEGREE_CELSIUS_IN_TENTH_KELVIN;
+
+ pr_debug("temp = %d C\n", temp/10);
+
+ return temp;
+}
+
+/*
+ * Return the battery Voltage in milivolts 0..20 V
+ * Or < 0 if something fails.
+ */
+static int bq28400_read_voltage(struct i2c_client *client)
+{
+ int mvolt = 0;
+
+ mvolt = bq28400_read_reg(client, SBS_VOLTAGE);
+ if (mvolt < 0)
+ return mvolt;
+
+ pr_debug("volt = %d mV.\n", mvolt);
+
+ return mvolt;
+}
+
+/*
+ * Return the battery Current in miliamps
+ * Or 0 if something fails.
+ * Positive current indicates charging
+ * Negative current indicates discharging.
+ * Current-now is calculated every second.
+ */
+static int bq28400_read_current(struct i2c_client *client)
+{
+ s16 current_ma = 0;
+
+ current_ma = bq28400_read_reg(client, SBS_CURRENT);
+
+ pr_debug("current = %d mA.\n", current_ma);
+
+ return current_ma;
+}
+
+/*
+ * Return the Average battery Current in miliamps
+ * Or 0 if something fails.
+ * Positive current indicates charging
+ * Negative current indicates discharging.
+ * Average Current is the rolling 1 minute average current.
+ */
+static int bq28400_read_avg_current(struct i2c_client *client)
+{
+ s16 current_ma = 0;
+
+ current_ma = bq28400_read_reg(client, SBS_AVG_CURRENT);
+
+ pr_debug("avg_current=%d mA.\n", current_ma);
+
+ return current_ma;
+}
+
+/*
+ * Return the battery Relative-State-Of-Charge 0..100 %
+ * Or 0 if something fails.
+ */
+static int bq28400_read_rsoc(struct i2c_client *client)
+{
+ int percentage = 0;
+
+ /* This register is only 1 byte */
+ percentage = i2c_smbus_read_byte_data(client, SBS_RSOC);
+
+ if (percentage < 0)
+ return 0;
+
+ pr_debug("percentage = %d.\n", percentage);
+
+ return percentage;
+}
+
+/*
+ * Return the battery Capacity in mAh.
+ * Or 0 if something fails.
+ */
+static int bq28400_read_full_capacity(struct i2c_client *client)
+{
+ int capacity = 0;
+
+ capacity = bq28400_read_reg(client, SBS_FULL_CAPACITY);
+ if (capacity < 0)
+ return 0;
+
+ pr_debug("full-capacity = %d mAh.\n", capacity);
+
+ return capacity;
+}
+
+/*
+ * Return the battery Capacity in mAh.
+ * Or 0 if something fails.
+ */
+static int bq28400_read_remain_capacity(struct i2c_client *client)
+{
+ int capacity = 0;
+
+ capacity = bq28400_read_reg(client, SBS_REMAIN_CAPACITY);
+ if (capacity < 0)
+ return 0;
+
+ pr_debug("remain-capacity = %d mAh.\n", capacity);
+
+ return capacity;
+}
+
+static int bq28400_enable_charging(struct bq28400_device *bq28400_dev,
+ bool enable)
+{
+ int ret;
+ static bool is_charging_enabled;
+
+ if (bq28400_dev->dc_psy == NULL) {
+ bq28400_dev->dc_psy = power_supply_get_by_name("dc");
+ if (bq28400_dev->dc_psy == NULL) {
+ pr_err("fail to get dc-psy.\n");
+ return -ENODEV;
+ }
+ }
+
+ if (is_charging_enabled == enable) {
+ pr_debug("Charging enable already = %d.\n", enable);
+ return 0;
+ }
+
+ ret = power_supply_set_online(bq28400_dev->dc_psy, enable);
+ if (ret < 0) {
+ pr_err("fail to set dc-psy online to %d.\n", enable);
+ return ret;
+ }
+
+ is_charging_enabled = enable;
+
+ pr_debug("Charging enable = %d.\n", enable);
+
+ return 0;
+}
+
+static int bq28400_get_prop_status(struct i2c_client *client)
+{
+ int status = POWER_SUPPLY_STATUS_UNKNOWN;
+ int rsoc;
+ s16 current_ma = 0;
+ u16 battery_status;
+
+ battery_status = bq28400_read_reg(client, SBS_BATTERY_STATUS);
+
+ if (battery_status & BAT_STATUS_EMPTY)
+ pr_debug("Battery report Empty.\n");
+
+ /* Battery may report FULL before rsoc is 100%
+ * for protection and cell-balancing.
+ * The FULL report may remain when rsoc drops from 100%.
+ */
+ if (battery_status & BAT_STATUS_FULL) {
+ pr_debug("Battery report Full.\n");
+ bq28400_enable_charging(bq28400_dev, false);
+ return POWER_SUPPLY_STATUS_FULL;
+ }
+
+ rsoc = bq28400_read_rsoc(client);
+ current_ma = bq28400_read_current(client);
+
+ if (rsoc == 100) {
+ bq28400_enable_charging(bq28400_dev, false);
+ pr_debug("Full.\n");
+ return POWER_SUPPLY_STATUS_FULL;
+ }
+
+ /*
+ * Positive current indicates charging
+ * Negative current indicates discharging.
+ * Charging is stopped at termination-current.
+ */
+ if (current_ma < 0) {
+ bq28400_enable_charging(bq28400_dev, true);
+ pr_debug("Discharging.\n");
+ status = POWER_SUPPLY_STATUS_DISCHARGING;
+ } else if (current_ma > BQ_TERMINATION_CURRENT_MA) {
+ pr_debug("Charging.\n");
+ status = POWER_SUPPLY_STATUS_CHARGING;
+ } else {
+ pr_debug("Not Charging.\n");
+ status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+ }
+
+ return status;
+}
+
+static int bq28400_get_prop_charge_type(struct i2c_client *client)
+{
+ u16 battery_status;
+ u16 chg_status;
+ u16 fet_status;
+
+ battery_status = bq28400_read_reg(client, SBS_BATTERY_STATUS);
+ chg_status = bq28400_read_reg(client, SBS_CHARGING_STATUS);
+ fet_status = bq28400_read_reg(client, SBS_FET_STATUS);
+
+ if (battery_status & BAT_STATUS_DISCHARGING) {
+ pr_debug("Discharging.\n");
+ return POWER_SUPPLY_CHARGE_TYPE_NONE;
+ }
+
+ if (fet_status & FET_STATUS_PRECHARGE) {
+ pr_debug("Pre-Charging.\n");
+ return POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
+ }
+
+ if (chg_status & CHG_STATUS_HOT_TEMP_CHARGING) {
+ pr_debug("Hot-Temp-Charging.\n");
+ return POWER_SUPPLY_CHARGE_TYPE_FAST;
+ }
+
+ if (chg_status & CHG_STATUS_LOW_TEMP_CHARGING) {
+ pr_debug("Low-Temp-Charging.\n");
+ return POWER_SUPPLY_CHARGE_TYPE_FAST;
+ }
+
+ if (chg_status & CHG_STATUS_STD1_TEMP_CHARGING) {
+ pr_debug("STD1-Temp-Charging.\n");
+ return POWER_SUPPLY_CHARGE_TYPE_FAST;
+ }
+
+ if (chg_status & CHG_STATUS_STD2_TEMP_CHARGING) {
+ pr_debug("STD2-Temp-Charging.\n");
+ return POWER_SUPPLY_CHARGE_TYPE_FAST;
+ }
+
+ if (chg_status & CHG_STATUS_BATTERY_DEPLETED)
+ pr_debug("battery_depleted.\n");
+
+ if (chg_status & CHG_STATUS_CELL_BALANCING)
+ pr_debug("cell_balancing.\n");
+
+ if (chg_status & CHG_STATUS_OVERCHARGE) {
+ pr_err("overcharge fault.\n");
+ return POWER_SUPPLY_CHARGE_TYPE_NONE;
+ }
+
+ if (chg_status & CHG_STATUS_SUSPENDED) {
+ pr_info("Suspended.\n");
+ return POWER_SUPPLY_CHARGE_TYPE_NONE;
+ }
+
+ if (chg_status & CHG_STATUS_DISABLED) {
+ pr_info("Disabled.\n");
+ return POWER_SUPPLY_CHARGE_TYPE_NONE;
+ }
+
+ return POWER_SUPPLY_CHARGE_TYPE_UNKNOWN;
+}
+
+static bool bq28400_get_prop_present(struct i2c_client *client)
+{
+ int val;
+
+ val = bq28400_read_reg(client, SBS_BATTERY_STATUS);
+
+ /* If the bq28400 is inside the battery pack
+ * then when battery is removed the i2c transfer will fail.
+ */
+
+ if (val < 0)
+ return false;
+
+ /* TODO - support when bq28400 is not embedded in battery pack */
+
+ return true;
+}
+
+/*
+ * User sapce read the battery info.
+ * Get data online via I2C from the battery gauge.
+ */
+static int bq28400_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ int ret = 0;
+ struct bq28400_device *dev = container_of(psy,
+ struct bq28400_device,
+ batt_psy);
+ struct i2c_client *client = dev->client;
+ static char str[BQ_MAX_STR_LEN];
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_STATUS:
+ val->intval = bq28400_get_prop_status(client);
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_TYPE:
+ val->intval = bq28400_get_prop_charge_type(client);
+ break;
+ case POWER_SUPPLY_PROP_PRESENT:
+ val->intval = bq28400_get_prop_present(client);
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ val->intval = bq28400_read_voltage(client);
+ val->intval *= 1000; /* mV to uV */
+ break;
+ case POWER_SUPPLY_PROP_CAPACITY:
+ val->intval = bq28400_read_rsoc(client);
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_NOW:
+ /* Positive current indicates drawing */
+ val->intval = -bq28400_read_current(client);
+ val->intval *= 1000; /* mA to uA */
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_AVG:
+ /* Positive current indicates drawing */
+ val->intval = -bq28400_read_avg_current(client);
+ val->intval *= 1000; /* mA to uA */
+ break;
+ case POWER_SUPPLY_PROP_TEMP:
+ val->intval = bq28400_read_temperature(client);
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_FULL:
+ val->intval = bq28400_read_full_capacity(client);
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_NOW:
+ val->intval = bq28400_read_remain_capacity(client);
+ break;
+ case POWER_SUPPLY_PROP_TECHNOLOGY:
+ val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
+ break;
+ case POWER_SUPPLY_PROP_MODEL_NAME:
+ bq28400_read_string(client, SBS_DEVICE_NAME, str, 20);
+ val->strval = str;
+ break;
+ case POWER_SUPPLY_PROP_MANUFACTURER:
+ bq28400_read_string(client, SBS_MANUFACTURER_NAME, str, 20);
+ val->strval = str;
+ break;
+ default:
+ pr_err(" psp %d Not supoprted.\n", psp);
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int bq28400_set_reg(void *data, u64 val)
+{
+ struct debug_reg *dbg = data;
+ u8 reg = dbg->reg;
+ int ret;
+ struct i2c_client *client = bq28400_dev->client;
+
+ ret = bq28400_write_reg(client, reg, val);
+
+ return ret;
+}
+
+static int bq28400_get_reg(void *data, u64 *val)
+{
+ struct debug_reg *dbg = data;
+ u8 reg = dbg->reg;
+ u16 subcmd = dbg->subcmd;
+ int ret;
+ struct i2c_client *client = bq28400_dev->client;
+
+ if (subcmd)
+ ret = bq28400_read_subcmd(client, reg, subcmd);
+ else
+ ret = bq28400_read_reg(client, reg);
+ if (ret < 0)
+ return ret;
+
+ *val = ret;
+
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(reg_fops, bq28400_get_reg, bq28400_set_reg,
+ "0x%04llx\n");
+
+static int bq28400_create_debugfs_entries(struct bq28400_device *bq28400_dev)
+{
+ int i;
+
+ bq28400_dev->dent = debugfs_create_dir(BQ28400_NAME, NULL);
+ if (IS_ERR(bq28400_dev->dent)) {
+ pr_err("bq28400 driver couldn't create debugfs dir\n");
+ return -EFAULT;
+ }
+
+ for (i = 0 ; i < ARRAY_SIZE(bq28400_debug_regs) ; i++) {
+ char *name = bq28400_debug_regs[i].name;
+ struct dentry *file;
+ void *data = &bq28400_debug_regs[i];
+
+ file = debugfs_create_file(name, 0644, bq28400_dev->dent,
+ data, ®_fops);
+ if (IS_ERR(file)) {
+ pr_err("debugfs_create_file %s failed.\n", name);
+ return -EFAULT;
+ }
+ }
+
+ return 0;
+}
+
+static int bq28400_set_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ const union power_supply_propval *val)
+{
+ pr_debug("psp = %d.val = %d.\n", psp, val->intval);
+
+ return -EINVAL;
+}
+
+static void bq28400_external_power_changed(struct power_supply *psy)
+{
+ pr_debug("Notify power_supply_changed.\n");
+ /* Update LEDs and notify uevents */
+ power_supply_changed(&bq28400_dev->batt_psy);
+}
+
+static int __devinit bq28400_register_psy(struct bq28400_device *bq28400_dev)
+{
+ int ret;
+
+ bq28400_dev->batt_psy.name = "battery";
+ bq28400_dev->batt_psy.type = POWER_SUPPLY_TYPE_BATTERY;
+ bq28400_dev->batt_psy.num_supplicants = 0;
+ bq28400_dev->batt_psy.properties = pm_power_props;
+ bq28400_dev->batt_psy.num_properties = ARRAY_SIZE(pm_power_props);
+ bq28400_dev->batt_psy.get_property = bq28400_get_property;
+ bq28400_dev->batt_psy.set_property = bq28400_set_property;
+ bq28400_dev->batt_psy.external_power_changed =
+ bq28400_external_power_changed;
+
+ ret = power_supply_register(&bq28400_dev->client->dev,
+ &bq28400_dev->batt_psy);
+ if (ret) {
+ pr_err("failed to register power_supply. ret=%d.\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * Update userspace every 1 minute.
+ * Normally it takes more than 120 minutes (two hours) to
+ * charge/discahrge the battery,
+ * so updating every 1 minute should be enough for 1% change
+ * detection.
+ * Any immidiate change detected by the DC charger is notified
+ * by the bq28400_external_power_changed callback, which notify
+ * the user space.
+ */
+static void bq28400_periodic_user_space_update_worker(struct work_struct *work)
+{
+ u32 delay_msec = 60*1000;
+
+ pr_debug("Notify user space.\n");
+
+ /* Notify user space via kobject_uevent change notification */
+ power_supply_changed(&bq28400_dev->batt_psy);
+
+ schedule_delayed_work(&bq28400_dev->periodic_user_space_update_work,
+ round_jiffies_relative(msecs_to_jiffies
+ (delay_msec)));
+}
+
+static int __devinit bq28400_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int ret = 0;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_BYTE_DATA)) {
+ pr_err(" i2c func fail.\n");
+ return -EIO;
+ }
+
+ bq28400_dev = kzalloc(sizeof(*bq28400_dev), GFP_KERNEL);
+ if (!bq28400_dev) {
+ pr_err(" alloc fail.\n");
+ return -ENOMEM;
+ }
+
+ bq28400_dev->client = client;
+ i2c_set_clientdata(client, bq28400_dev);
+
+ ret = bq28400_register_psy(bq28400_dev);
+ if (ret) {
+ pr_err(" bq28400_register_psy fail.\n");
+ goto err_register_psy;
+ }
+
+ ret = bq28400_create_debugfs_entries(bq28400_dev);
+ if (ret) {
+ pr_err(" bq28400_create_debugfs_entries fail.\n");
+ goto err_debugfs;
+ }
+
+ INIT_DELAYED_WORK(&bq28400_dev->periodic_user_space_update_work,
+ bq28400_periodic_user_space_update_worker);
+
+ schedule_delayed_work(&bq28400_dev->periodic_user_space_update_work,
+ msecs_to_jiffies(1000));
+
+ pr_info("Device is ready.\n");
+
+ return 0;
+
+err_debugfs:
+ if (bq28400_dev->dent)
+ debugfs_remove_recursive(bq28400_dev->dent);
+ power_supply_unregister(&bq28400_dev->batt_psy);
+err_register_psy:
+ kfree(bq28400_dev);
+ bq28400_dev = NULL;
+
+ pr_info("FAIL.\n");
+
+ return ret;
+}
+
+static int __devexit bq28400_remove(struct i2c_client *client)
+{
+ struct bq28400_device *bq28400_dev = i2c_get_clientdata(client);
+
+ power_supply_unregister(&bq28400_dev->batt_psy);
+ if (bq28400_dev->dent)
+ debugfs_remove_recursive(bq28400_dev->dent);
+ kfree(bq28400_dev);
+ bq28400_dev = NULL;
+
+ return 0;
+}
+
+static const struct of_device_id bq28400_match[] = {
+ { .compatible = "ti,bq28400-battery", },
+ { },
+ };
+
+static const struct i2c_device_id bq28400_id[] = {
+ {BQ28400_NAME, 0},
+ {},
+};
+
+MODULE_DEVICE_TABLE(i2c, bq28400_id);
+
+static struct i2c_driver bq28400_driver = {
+ .driver = {
+ .name = BQ28400_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(bq28400_match),
+ },
+ .probe = bq28400_probe,
+ .remove = __devexit_p(bq28400_remove),
+ .id_table = bq28400_id,
+};
+
+static int __init bq28400_init(void)
+{
+ pr_info(" bq28400 driver rev %s.\n", BQ28400_REV);
+
+ return i2c_add_driver(&bq28400_driver);
+}
+module_init(bq28400_init);
+
+static void __exit bq28400_exit(void)
+{
+ return i2c_del_driver(&bq28400_driver);
+}
+module_exit(bq28400_exit);
+
+MODULE_DESCRIPTION("Driver for BQ28400 charger chip");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("i2c:" BQ28400_NAME);
diff --git a/drivers/power/pm8921-charger.c b/drivers/power/pm8921-charger.c
index 3977f17..d2d0c03 100644
--- a/drivers/power/pm8921-charger.c
+++ b/drivers/power/pm8921-charger.c
@@ -289,6 +289,7 @@
bool has_dc_supply;
u8 active_path;
int recent_reported_soc;
+ int battery_less_hardware;
};
/* user space parameter to limit usb current */
@@ -1420,6 +1421,9 @@
{
int percent_soc;
+ if (chip->battery_less_hardware)
+ return 100;
+
if (!get_prop_batt_present(chip))
percent_soc = voltage_based_capacity(chip);
else
@@ -1582,6 +1586,9 @@
int rc;
struct pm8xxx_adc_chan_result result;
+ if (chip->battery_less_hardware)
+ return 300;
+
rc = pm8xxx_adc_read(chip->batt_temp_channel, &result);
if (rc) {
pr_err("error reading adc channel = %d, rc = %d\n",
@@ -4288,6 +4295,10 @@
chip->rconn_mohm = pdata->rconn_mohm;
chip->led_src_config = pdata->led_src_config;
chip->has_dc_supply = pdata->has_dc_supply;
+ chip->battery_less_hardware = pdata->battery_less_hardware;
+
+ if (chip->battery_less_hardware)
+ charging_disabled = 1;
rc = pm8921_chg_hw_init(chip);
if (rc) {
diff --git a/drivers/power/smb350_charger.c b/drivers/power/smb350_charger.c
new file mode 100644
index 0000000..93e208c
--- /dev/null
+++ b/drivers/power/smb350_charger.c
@@ -0,0 +1,865 @@
+/* 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.
+ *
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/debugfs.h>
+#include <linux/workqueue.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/power_supply.h>
+#include <linux/i2c/smb350.h>
+#include <linux/bitops.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/printk.h>
+
+/* Register definitions */
+#define CHG_CURRENT_REG 0x00 /* Non-Volatile + mirror */
+#define CHG_OTHER_CURRENT_REG 0x01 /* Non-Volatile + mirror */
+#define VAR_FUNC_REG 0x02 /* Non-Volatile + mirror */
+#define FLOAT_VOLTAGE_REG 0x03 /* Non-Volatile + mirror */
+#define CHG_CTRL_REG 0x04 /* Non-Volatile + mirror */
+#define STAT_TIMER_REG 0x05 /* Non-Volatile + mirror */
+#define PIN_ENABLE_CTRL_REG 0x06 /* Non-Volatile + mirror */
+#define THERM_CTRL_A_REG 0x07 /* Non-Volatile + mirror */
+#define SYSOK_USB3_SELECT_REG 0x08 /* Non-Volatile + mirror */
+#define CTRL_FUNCTIONS_REG 0x09 /* Non-Volatile + mirror */
+#define OTG_TLIM_THERM_CNTRL_REG 0x0A /* Non-Volatile + mirror */
+#define TEMP_MONITOR_REG 0x0B /* Non-Volatile + mirror */
+#define FAULT_IRQ_REG 0x0C /* Non-Volatile */
+#define IRQ_ENABLE_REG 0x0D /* Non-Volatile */
+#define SYSOK_REG 0x0E /* Non-Volatile + mirror */
+
+#define AUTO_INPUT_VOLT_DETECT_REG 0x10 /* Non-Volatile Read-Only */
+#define STATUS_IRQ_REG 0x11 /* Non-Volatile Read-Only */
+#define I2C_SLAVE_ADDR_REG 0x12 /* Non-Volatile Read-Only */
+
+#define CMD_A_REG 0x30 /* Volatile Read-Write */
+#define CMD_B_REG 0x31 /* Volatile Read-Write */
+#define CMD_C_REG 0x33 /* Volatile Read-Write */
+
+#define IRQ_STATUS_A_REG 0x35 /* Volatile Read-Only */
+#define IRQ_STATUS_B_REG 0x36 /* Volatile Read-Only */
+#define IRQ_STATUS_C_REG 0x37 /* Volatile Read-Only */
+#define IRQ_STATUS_D_REG 0x38 /* Volatile Read-Only */
+#define IRQ_STATUS_E_REG 0x39 /* Volatile Read-Only */
+#define IRQ_STATUS_F_REG 0x3A /* Volatile Read-Only */
+
+#define STATUS_A_REG 0x3B /* Volatile Read-Only */
+#define STATUS_B_REG 0x3D /* Volatile Read-Only */
+/* Note: STATUS_C_REG was removed from SMB349 to SMB350 */
+#define STATUS_D_REG 0x3E /* Volatile Read-Only */
+#define STATUS_E_REG 0x3F /* Volatile Read-Only */
+
+#define IRQ_STATUS_NUM (IRQ_STATUS_F_REG - IRQ_STATUS_A_REG + 1)
+
+/* Status bits and masks */
+#define SMB350_MASK(BITS, POS) ((u8)(((1 << BITS) - 1) << POS))
+#define FAST_CHG_CURRENT_MASK SMB350_MASK(4, 4)
+
+#define SMB350_FAST_CHG_MIN_MA 1000
+#define SMB350_FAST_CHG_STEP_MA 200
+#define SMB350_FAST_CHG_MAX_MA 3600
+
+#define TERM_CURRENT_MASK SMB350_MASK(3, 2)
+
+#define SMB350_TERM_CUR_MIN_MA 200
+#define SMB350_TERM_CUR_STEP_MA 100
+#define SMB350_TERM_CUR_MAX_MA 700
+
+#define CMD_A_VOLATILE_WR_PERM BIT(7)
+#define CHG_CTRL_CURR_TERM_END_CHG BIT(6)
+
+enum smb350_chg_status {
+ SMB_CHG_STATUS_NONE = 0,
+ SMB_CHG_STATUS_PRE_CHARGE = 1,
+ SMB_CHG_STATUS_FAST_CHARGE = 2,
+ SMB_CHG_STATUS_TAPER_CHARGE = 3,
+};
+
+static const char * const smb350_chg_status[] = {
+ "none",
+ "pre-charge",
+ "fast-charge",
+ "taper-charge"
+};
+
+struct smb350_device {
+ /* setup */
+ int chg_current_ma;
+ int term_current_ma;
+ int chg_en_n_gpio;
+ int chg_susp_n_gpio;
+ int stat_gpio;
+ int irq;
+ /* internal */
+ enum smb350_chg_status chg_status;
+ struct i2c_client *client;
+ struct delayed_work irq_work;
+ struct dentry *dent;
+ struct wake_lock chg_wake_lock;
+ struct power_supply dc_psy;
+};
+
+static struct smb350_device *smb350_dev;
+
+struct debug_reg {
+ char *name;
+ u8 reg;
+};
+
+#define SMB350_DEBUG_REG(x) {#x, x##_REG}
+
+static struct debug_reg smb350_debug_regs[] = {
+ SMB350_DEBUG_REG(CHG_CURRENT),
+ SMB350_DEBUG_REG(CHG_OTHER_CURRENT),
+ SMB350_DEBUG_REG(VAR_FUNC),
+ SMB350_DEBUG_REG(FLOAT_VOLTAGE),
+ SMB350_DEBUG_REG(CHG_CTRL),
+ SMB350_DEBUG_REG(STAT_TIMER),
+ SMB350_DEBUG_REG(PIN_ENABLE_CTRL),
+ SMB350_DEBUG_REG(THERM_CTRL_A),
+ SMB350_DEBUG_REG(SYSOK_USB3_SELECT),
+ SMB350_DEBUG_REG(CTRL_FUNCTIONS),
+ SMB350_DEBUG_REG(OTG_TLIM_THERM_CNTRL),
+ SMB350_DEBUG_REG(TEMP_MONITOR),
+ SMB350_DEBUG_REG(FAULT_IRQ),
+ SMB350_DEBUG_REG(IRQ_ENABLE),
+ SMB350_DEBUG_REG(SYSOK),
+ SMB350_DEBUG_REG(AUTO_INPUT_VOLT_DETECT),
+ SMB350_DEBUG_REG(STATUS_IRQ),
+ SMB350_DEBUG_REG(I2C_SLAVE_ADDR),
+ SMB350_DEBUG_REG(CMD_A),
+ SMB350_DEBUG_REG(CMD_B),
+ SMB350_DEBUG_REG(CMD_C),
+ SMB350_DEBUG_REG(IRQ_STATUS_A),
+ SMB350_DEBUG_REG(IRQ_STATUS_B),
+ SMB350_DEBUG_REG(IRQ_STATUS_C),
+ SMB350_DEBUG_REG(IRQ_STATUS_D),
+ SMB350_DEBUG_REG(IRQ_STATUS_E),
+ SMB350_DEBUG_REG(IRQ_STATUS_F),
+ SMB350_DEBUG_REG(STATUS_A),
+ SMB350_DEBUG_REG(STATUS_B),
+ SMB350_DEBUG_REG(STATUS_D),
+ SMB350_DEBUG_REG(STATUS_E),
+};
+
+/*
+ * Read 8-bit register value. return negative value on error.
+ */
+static int smb350_read_reg(struct i2c_client *client, u8 reg)
+{
+ int val;
+
+ val = i2c_smbus_read_byte_data(client, reg);
+ if (val < 0)
+ pr_err("i2c read fail. reg=0x%x.ret=%d.\n", reg, val);
+ else
+ pr_debug("reg=0x%02X.val=0x%02X.\n", reg , val);
+
+ return val;
+}
+
+/*
+ * Write 8-bit register value. return negative value on error.
+ */
+static int smb350_write_reg(struct i2c_client *client, u8 reg, u8 val)
+{
+ int ret;
+
+ ret = i2c_smbus_write_byte_data(client, reg, val);
+ if (ret < 0)
+ pr_err("i2c read fail. reg=0x%x.val=0x%x.ret=%d.\n",
+ reg, val, ret);
+ else
+ pr_debug("reg=0x%02X.val=0x%02X.\n", reg , val);
+
+ return ret;
+}
+
+static int smb350_masked_write(struct i2c_client *client, int reg, u8 mask,
+ u8 val)
+{
+ int ret;
+ int temp;
+ int shift = find_first_bit((unsigned long *) &mask, 8);
+
+ temp = smb350_read_reg(client, reg);
+ if (temp < 0)
+ return temp;
+
+ temp &= ~mask;
+ temp |= (val << shift) & mask;
+ ret = smb350_write_reg(client, reg, temp);
+
+ return ret;
+}
+
+#define SMB350_FLOAT_VOLT_BASE_MV 6920
+#define SMB350_FLOAT_VOLT_STEP_MV 40
+#define SMB350_FLOAT_VOLT_MAX_MV (6920 + 0x2F * 40)
+
+/* Fast-to-Taper charging volatge */
+static int smb350_get_float_voltage(struct i2c_client *client)
+{
+ u16 val = smb350_read_reg(client, STATUS_A_REG);
+
+ val = SMB350_FLOAT_VOLT_BASE_MV +
+ ((val & 0x2F) * SMB350_FLOAT_VOLT_STEP_MV);
+
+ return val;
+}
+
+static bool smb350_is_dc_present(struct i2c_client *client)
+{
+ u16 irq_status_f = smb350_read_reg(client, IRQ_STATUS_F_REG);
+ bool power_ok = irq_status_f & 0x01;
+
+ /* Power-ok , IRQ_STATUS_F_REG bit#0 */
+ if (power_ok)
+ pr_debug("DC is present.\n");
+ else
+ pr_debug("DC is missing.\n");
+
+ return power_ok;
+}
+
+static bool smb350_is_charging(struct i2c_client *client)
+{
+ int val;
+ bool is_charging;
+
+ val = smb350_read_reg(client, STATUS_B_REG);
+ if (val < 0)
+ return false;
+
+ val = (val >> 1) & 0x3;
+
+ is_charging = (val != 0);
+
+ return is_charging;
+}
+
+static int smb350_get_prop_charge_type(struct smb350_device *dev)
+{
+ int status_b;
+ enum smb350_chg_status status;
+ int chg_type = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN;
+ bool chg_enabled;
+ bool charger_err;
+ struct i2c_client *client = dev->client;
+
+ status_b = smb350_read_reg(client, STATUS_B_REG);
+ if (status_b < 0) {
+ pr_err("failed to read STATUS_B_REG.\n");
+ return POWER_SUPPLY_CHARGE_TYPE_UNKNOWN;
+ }
+
+ chg_enabled = (bool) (status_b & 0x01);
+ charger_err = (bool) (status_b & (1<<6));
+
+ if (!chg_enabled) {
+ pr_warn("Charging not enabled.\n");
+ return POWER_SUPPLY_CHARGE_TYPE_NONE;
+ }
+
+ if (charger_err) {
+ pr_warn("Charger error detected.\n");
+ return POWER_SUPPLY_CHARGE_TYPE_NONE;
+ }
+
+ status = (status_b >> 1) & 0x3;
+
+ if (status == SMB_CHG_STATUS_NONE)
+ chg_type = POWER_SUPPLY_CHARGE_TYPE_NONE;
+ else if (status == SMB_CHG_STATUS_FAST_CHARGE) /* constant current */
+ chg_type = POWER_SUPPLY_CHARGE_TYPE_FAST;
+ else if (status == SMB_CHG_STATUS_TAPER_CHARGE) /* constant voltage */
+ chg_type = POWER_SUPPLY_CHARGE_TYPE_FAST;
+ else if (status == SMB_CHG_STATUS_PRE_CHARGE)
+ chg_type = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
+
+ pr_debug("smb-chg-status=%d=%s.\n", status, smb350_chg_status[status]);
+
+ if (dev->chg_status != status) { /* Status changed */
+ if (status == SMB_CHG_STATUS_NONE) {
+ pr_debug("Charging stopped.\n");
+ wake_unlock(&dev->chg_wake_lock);
+ } else {
+ pr_debug("Charging started.\n");
+ wake_lock(&dev->chg_wake_lock);
+ }
+ }
+
+ dev->chg_status = status;
+
+ return chg_type;
+}
+
+static void smb350_enable_charging(struct smb350_device *dev, bool enable)
+{
+ int val = !enable; /* active low */
+
+ pr_debug("enable=%d.\n", enable);
+
+ gpio_set_value_cansleep(dev->chg_en_n_gpio, val);
+}
+
+/* When the status bit of a certain condition is read,
+ * the corresponding IRQ signal is cleared.
+ */
+static int smb350_clear_irq(struct i2c_client *client)
+{
+ int ret;
+
+ ret = smb350_read_reg(client, IRQ_STATUS_A_REG);
+ if (ret < 0)
+ return ret;
+ ret = smb350_read_reg(client, IRQ_STATUS_B_REG);
+ if (ret < 0)
+ return ret;
+ ret = smb350_read_reg(client, IRQ_STATUS_C_REG);
+ if (ret < 0)
+ return ret;
+ ret = smb350_read_reg(client, IRQ_STATUS_D_REG);
+ if (ret < 0)
+ return ret;
+ ret = smb350_read_reg(client, IRQ_STATUS_E_REG);
+ if (ret < 0)
+ return ret;
+ ret = smb350_read_reg(client, IRQ_STATUS_F_REG);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+/*
+ * Do the IRQ work from a thread context rather than interrupt context.
+ * Read status registers to clear interrupt source.
+ * Notify the power-supply driver about change detected.
+ * Relevant events for start/stop charging:
+ * 1. DC insert/remove
+ * 2. End-Of-Charging
+ * 3. Battery insert/remove
+ * 4. Temperture too hot/cold
+ * 5. Charging timeout expired.
+ */
+static void smb350_irq_worker(struct work_struct *work)
+{
+ int ret = 0;
+ struct smb350_device *dev =
+ container_of(work, struct smb350_device, irq_work.work);
+
+ ret = smb350_clear_irq(dev->client);
+ if (ret == 0) { /* Cleared ok */
+ /* Notify Battery-psy about status changed */
+ pr_debug("Notify power_supply_changed.\n");
+ power_supply_changed(&dev->dc_psy);
+ }
+}
+
+/*
+ * The STAT pin is low when charging and high when not charging.
+ * When the smb350 start/stop charging the STAT pin triggers an interrupt.
+ * Interrupt is triggered on both rising or falling edge.
+ */
+static irqreturn_t smb350_irq(int irq, void *dev_id)
+{
+ struct smb350_device *dev = dev_id;
+
+ pr_debug("\n");
+
+ /* I2C transfers API should not run in interrupt context */
+ schedule_delayed_work(&dev->irq_work, msecs_to_jiffies(100));
+
+ return IRQ_HANDLED;
+}
+
+static enum power_supply_property pm_power_props[] = {
+ /* real time */
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_ONLINE,
+ POWER_SUPPLY_PROP_CHARGE_TYPE,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ /* fixed */
+ POWER_SUPPLY_PROP_MANUFACTURER,
+ POWER_SUPPLY_PROP_MODEL_NAME,
+ POWER_SUPPLY_PROP_CURRENT_MAX,
+};
+
+static char *pm_power_supplied_to[] = {
+ "battery",
+};
+
+static int smb350_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ int ret = 0;
+ struct smb350_device *dev = container_of(psy,
+ struct smb350_device,
+ dc_psy);
+ struct i2c_client *client = dev->client;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_PRESENT:
+ val->intval = smb350_is_dc_present(client);
+ break;
+ case POWER_SUPPLY_PROP_ONLINE:
+ val->intval = smb350_is_charging(client);
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_TYPE:
+ val->intval = smb350_get_prop_charge_type(dev);
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ val->intval = smb350_get_float_voltage(client);
+ val->intval *= 1000; /* mV to uV */
+ break;
+ case POWER_SUPPLY_PROP_MODEL_NAME:
+ val->strval = SMB350_NAME;
+ break;
+ case POWER_SUPPLY_PROP_MANUFACTURER:
+ val->strval = "Summit Microelectronics";
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_MAX:
+ val->intval = dev->chg_current_ma;
+ break;
+ default:
+ pr_err("Invalid prop = %d.\n", psp);
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int smb350_set_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ const union power_supply_propval *val)
+{
+ int ret = 0;
+ struct smb350_device *dev =
+ container_of(psy, struct smb350_device, dc_psy);
+
+ switch (psp) {
+ /*
+ * Allow a smart battery to Start/Stop charging.
+ * i.e. when End-Of-Charging detected.
+ * The SMB350 can be configured to terminate charging
+ * when charge-current reaching Termination-Current.
+ */
+ case POWER_SUPPLY_PROP_ONLINE:
+ smb350_enable_charging(dev, val->intval);
+ break;
+ default:
+ pr_err("Invalid prop = %d.\n", psp);
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int smb350_set_chg_current(struct i2c_client *client, int current_ma)
+{
+ int ret;
+ u8 temp;
+
+ if ((current_ma < SMB350_FAST_CHG_MIN_MA) ||
+ (current_ma > SMB350_FAST_CHG_MAX_MA)) {
+ pr_err("invalid current %d mA.\n", current_ma);
+ return -EINVAL;
+ }
+
+ temp = (current_ma - SMB350_FAST_CHG_MIN_MA) / SMB350_FAST_CHG_STEP_MA;
+
+ pr_debug("fast-chg-current=%d mA setting %02x\n", current_ma, temp);
+
+ ret = smb350_masked_write(client, CHG_CURRENT_REG,
+ FAST_CHG_CURRENT_MASK, temp);
+
+ return ret;
+}
+
+static int smb350_set_term_current(struct i2c_client *client, int current_ma)
+{
+ int ret;
+ u8 temp;
+
+ if ((current_ma < SMB350_TERM_CUR_MIN_MA) ||
+ (current_ma > SMB350_TERM_CUR_MAX_MA)) {
+ pr_err("invalid current %d mA to set\n", current_ma);
+ return -EINVAL;
+ }
+
+ temp = (current_ma - SMB350_TERM_CUR_MIN_MA) / SMB350_TERM_CUR_STEP_MA;
+
+ pr_debug("term-current=%d mA setting %02x\n", current_ma, temp);
+
+ ret = smb350_masked_write(client, CHG_OTHER_CURRENT_REG,
+ TERM_CURRENT_MASK, temp);
+
+ return ret;
+}
+
+static int smb350_set_reg(void *data, u64 val)
+{
+ u32 addr = (u32) data;
+ int ret;
+ struct i2c_client *client = smb350_dev->client;
+
+ ret = smb350_write_reg(client, addr, (u8) val);
+
+ return ret;
+}
+
+static int smb350_get_reg(void *data, u64 *val)
+{
+ u32 addr = (u32) data;
+ int ret;
+ struct i2c_client *client = smb350_dev->client;
+
+ ret = smb350_read_reg(client, addr);
+ if (ret < 0)
+ return ret;
+
+ *val = ret;
+
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(reg_fops, smb350_get_reg, smb350_set_reg, "0x%02llx\n");
+
+static int smb350_create_debugfs_entries(struct smb350_device *dev)
+{
+ int i;
+ dev->dent = debugfs_create_dir(SMB350_NAME, NULL);
+ if (IS_ERR(dev->dent)) {
+ pr_err("smb350 driver couldn't create debugfs dir\n");
+ return -EFAULT;
+ }
+
+ for (i = 0 ; i < ARRAY_SIZE(smb350_debug_regs) ; i++) {
+ char *name = smb350_debug_regs[i].name;
+ u32 reg = smb350_debug_regs[i].reg;
+ struct dentry *file;
+
+ file = debugfs_create_file(name, 0644, dev->dent,
+ (void *) reg, ®_fops);
+ if (IS_ERR(file)) {
+ pr_err("debugfs_create_file %s failed.\n", name);
+ return -EFAULT;
+ }
+ }
+
+ return 0;
+}
+
+static int smb350_set_volatile_params(struct smb350_device *dev)
+{
+ int ret;
+ struct i2c_client *client = dev->client;
+
+ pr_debug("\n");
+
+ ret = smb350_write_reg(client, CMD_A_REG, CMD_A_VOLATILE_WR_PERM);
+ if (ret) {
+ pr_err("Failed to set VOLATILE_WR_PERM ret=%d\n", ret);
+ return ret;
+ }
+
+ /* Disable SMB350 pulse-IRQ mechanism,
+ * we use interrupts based on charging-status-transition
+ */
+ /* Enable STATUS output (regardless of IRQ-pulses) */
+ smb350_masked_write(client, CMD_A_REG, BIT(0), 0);
+
+ /* Disable LED blinking - avoid periodic irq */
+ smb350_masked_write(client, PIN_ENABLE_CTRL_REG, BIT(7), 0);
+
+ /* Disable Failure SMB-IRQ */
+ ret = smb350_write_reg(client, FAULT_IRQ_REG, 0x00);
+ if (ret) {
+ pr_err("Failed to set FAULT_IRQ_REG ret=%d\n", ret);
+ return ret;
+ }
+
+ /* Disable Event IRQ */
+ ret = smb350_write_reg(client, IRQ_ENABLE_REG, 0x00);
+ if (ret) {
+ pr_err("Failed to set IRQ_ENABLE_REG ret=%d\n", ret);
+ return ret;
+ }
+
+ /* Enable charging/not-charging status output via STAT pin */
+ smb350_masked_write(client, STAT_TIMER_REG, BIT(5), 0);
+
+ /* Disable Automatic Recharge */
+ smb350_masked_write(client, CHG_CTRL_REG, BIT(7), 1);
+
+ /* Set fast-charge current */
+ ret = smb350_set_chg_current(client, dev->chg_current_ma);
+ if (ret) {
+ pr_err("Failed to set FAST_CHG_CURRENT ret=%d\n", ret);
+ return ret;
+ }
+
+ if (dev->term_current_ma > 0) {
+ /* Enable Current Termination */
+ smb350_masked_write(client, CHG_CTRL_REG, BIT(6), 0);
+
+ /* Set Termination current */
+ smb350_set_term_current(client, dev->term_current_ma);
+ } else {
+ /* Disable Current Termination */
+ smb350_masked_write(client, CHG_CTRL_REG, BIT(6), 1);
+ }
+
+ return 0;
+}
+
+static int __devinit smb350_register_psy(struct smb350_device *dev)
+{
+ int ret;
+
+ dev->dc_psy.name = "dc";
+ dev->dc_psy.type = POWER_SUPPLY_TYPE_MAINS;
+ dev->dc_psy.supplied_to = pm_power_supplied_to;
+ dev->dc_psy.num_supplicants = ARRAY_SIZE(pm_power_supplied_to);
+ dev->dc_psy.properties = pm_power_props;
+ dev->dc_psy.num_properties = ARRAY_SIZE(pm_power_props);
+ dev->dc_psy.get_property = smb350_get_property;
+ dev->dc_psy.set_property = smb350_set_property;
+
+ ret = power_supply_register(&dev->client->dev,
+ &dev->dc_psy);
+ if (ret) {
+ pr_err("failed to register power_supply. ret=%d.\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int __devinit smb350_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int ret = 0;
+ const struct smb350_platform_data *pdata;
+ struct device_node *dev_node = client->dev.of_node;
+ struct smb350_device *dev;
+
+ /* STAT pin change on start/stop charging */
+ u32 irq_flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_BYTE_DATA)) {
+ pr_err("i2c func fail.\n");
+ return -EIO;
+ }
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev) {
+ pr_err("alloc fail.\n");
+ return -ENOMEM;
+ }
+
+ smb350_dev = dev;
+ dev->client = client;
+
+ if (dev_node) {
+ dev->chg_en_n_gpio =
+ of_get_named_gpio(dev_node, "summit,chg-en-n-gpio", 0);
+ pr_debug("chg_en_n_gpio = %d.\n", dev->chg_en_n_gpio);
+
+ dev->chg_susp_n_gpio =
+ of_get_named_gpio(dev_node,
+ "summit,chg-susp-n-gpio", 0);
+ pr_debug("chg_susp_n_gpio = %d.\n", dev->chg_susp_n_gpio);
+
+ dev->stat_gpio =
+ of_get_named_gpio(dev_node, "summit,stat-gpio", 0);
+ pr_debug("stat_gpio = %d.\n", dev->stat_gpio);
+
+ ret = of_property_read_u32(dev_node, "summit,chg-current-ma",
+ &(dev->chg_current_ma));
+ pr_debug("chg_current_ma = %d.\n", dev->chg_current_ma);
+ if (ret) {
+ pr_err("Unable to read chg_current.\n");
+ return ret;
+ }
+ ret = of_property_read_u32(dev_node, "summit,term-current-ma",
+ &(dev->term_current_ma));
+ pr_debug("term_current_ma = %d.\n", dev->term_current_ma);
+ if (ret) {
+ pr_err("Unable to read term_current_ma.\n");
+ return ret;
+ }
+ } else {
+ pdata = client->dev.platform_data;
+
+ if (pdata == NULL) {
+ pr_err("no platform data.\n");
+ return -EINVAL;
+ }
+
+ dev->chg_en_n_gpio = pdata->chg_en_n_gpio;
+ dev->chg_susp_n_gpio = pdata->chg_susp_n_gpio;
+ dev->stat_gpio = pdata->stat_gpio;
+
+ dev->chg_current_ma = pdata->chg_current_ma;
+ dev->term_current_ma = pdata->term_current_ma;
+ }
+
+ ret = gpio_request(dev->stat_gpio, "smb350_stat");
+ if (ret) {
+ pr_err("gpio_request failed for %d ret=%d\n",
+ dev->stat_gpio, ret);
+ goto err_stat_gpio;
+ }
+ dev->irq = gpio_to_irq(dev->stat_gpio);
+ pr_debug("irq#=%d.\n", dev->irq);
+
+ ret = gpio_request(dev->chg_susp_n_gpio, "smb350_suspend");
+ if (ret) {
+ pr_err("gpio_request failed for %d ret=%d\n",
+ dev->chg_susp_n_gpio, ret);
+ goto err_susp_gpio;
+ }
+
+ ret = gpio_request(dev->chg_en_n_gpio, "smb350_charger_enable");
+ if (ret) {
+ pr_err("gpio_request failed for %d ret=%d\n",
+ dev->chg_en_n_gpio, ret);
+ goto err_en_gpio;
+ }
+
+ i2c_set_clientdata(client, dev);
+
+ pr_debug("set charge-enable + not suspend.\n");
+ gpio_set_value_cansleep(dev->chg_en_n_gpio, 1); /* Disable */
+ msleep(100);
+ gpio_set_value_cansleep(dev->chg_susp_n_gpio, 1); /* Normal */
+ msleep(100); /* Allow the device to exist shutdown */
+
+ smb350_read_reg(client, I2C_SLAVE_ADDR_REG);
+
+ ret = smb350_set_volatile_params(dev);
+ if (ret)
+ goto err_set_params;
+
+ ret = smb350_register_psy(dev);
+ if (ret)
+ goto err_set_params;
+
+ ret = smb350_create_debugfs_entries(dev);
+ if (ret)
+ goto err_debugfs;
+
+ INIT_DELAYED_WORK(&dev->irq_work, smb350_irq_worker);
+ wake_lock_init(&dev->chg_wake_lock,
+ WAKE_LOCK_SUSPEND, SMB350_NAME);
+
+ ret = request_irq(dev->irq, smb350_irq, irq_flags,
+ "smb350_irq", dev);
+ if (ret) {
+ pr_err("request_irq %d failed.ret=%d\n", dev->irq, ret);
+ goto err_irq;
+ }
+
+ smb350_enable_charging(dev, true);
+
+ return 0;
+
+err_irq:
+err_debugfs:
+ if (dev->dent)
+ debugfs_remove_recursive(dev->dent);
+err_set_params:
+ gpio_free(dev->chg_en_n_gpio);
+err_en_gpio:
+ gpio_free(dev->chg_susp_n_gpio);
+err_susp_gpio:
+ gpio_free(dev->stat_gpio);
+err_stat_gpio:
+ kfree(smb350_dev);
+ smb350_dev = NULL;
+
+ pr_info("FAIL.\n");
+
+ return ret;
+}
+
+static int __devexit smb350_remove(struct i2c_client *client)
+{
+ struct smb350_device *dev = i2c_get_clientdata(client);
+
+ power_supply_unregister(&dev->dc_psy);
+ gpio_free(dev->chg_en_n_gpio);
+ gpio_free(dev->chg_susp_n_gpio);
+ if (dev->stat_gpio)
+ gpio_free(dev->stat_gpio);
+ if (dev->irq)
+ free_irq(dev->irq, dev);
+ if (dev->dent)
+ debugfs_remove_recursive(dev->dent);
+ kfree(smb350_dev);
+ smb350_dev = NULL;
+
+ return 0;
+}
+
+static const struct i2c_device_id smb350_id[] = {
+ {SMB350_NAME, 0},
+ {},
+};
+MODULE_DEVICE_TABLE(i2c, smb350_id);
+
+static const struct of_device_id smb350_match[] = {
+ { .compatible = "summit,smb350-charger", },
+ { },
+};
+
+static struct i2c_driver smb350_driver = {
+ .driver = {
+ .name = SMB350_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(smb350_match),
+ },
+ .probe = smb350_probe,
+ .remove = __devexit_p(smb350_remove),
+ .id_table = smb350_id,
+};
+
+static int __init smb350_init(void)
+{
+ return i2c_add_driver(&smb350_driver);
+}
+module_init(smb350_init);
+
+static void __exit smb350_exit(void)
+{
+ return i2c_del_driver(&smb350_driver);
+}
+module_exit(smb350_exit);
+
+MODULE_DESCRIPTION("Driver for SMB350 charger chip");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("i2c:" SMB350_NAME);
diff --git a/drivers/regulator/stub-regulator.c b/drivers/regulator/stub-regulator.c
index 1c4b935..85c5972 100644
--- a/drivers/regulator/stub-regulator.c
+++ b/drivers/regulator/stub-regulator.c
@@ -1,5 +1,5 @@
/*
- * 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
@@ -16,11 +16,14 @@
#include <linux/module.h>
#include <linux/err.h>
#include <linux/kernel.h>
-#include <linux/regulator/driver.h>
-#include <linux/regulator/stub-regulator.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/types.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/regulator/stub-regulator.h>
#define STUB_REGULATOR_MAX_NAME 40
@@ -138,27 +141,74 @@
static int __devinit regulator_stub_probe(struct platform_device *pdev)
{
+ struct regulator_init_data *init_data = NULL;
+ struct device *dev = &pdev->dev;
struct stub_regulator_pdata *vreg_pdata;
struct regulator_desc *rdesc;
struct regulator_stub *vreg_priv;
int rc;
- vreg_pdata = pdev->dev.platform_data;
- if (!vreg_pdata) {
- dev_err(&pdev->dev, "%s: no platform data\n", __func__);
- return -EINVAL;
- }
-
vreg_priv = kzalloc(sizeof(*vreg_priv), GFP_KERNEL);
if (!vreg_priv) {
- dev_err(&pdev->dev, "%s: Unable to allocate memory\n",
+ dev_err(dev, "%s: Unable to allocate memory\n",
__func__);
return -ENOMEM;
}
- dev_set_drvdata(&pdev->dev, vreg_priv);
+
+ if (dev->of_node) {
+ /* Use device tree. */
+ init_data = of_get_regulator_init_data(dev,
+ dev->of_node);
+ if (!init_data) {
+ dev_err(dev, "%s: unable to allocate memory\n",
+ __func__);
+ rc = -ENOMEM;
+ goto err_probe;
+ }
+
+ if (init_data->constraints.name == NULL) {
+ dev_err(dev, "%s: regulator name not specified\n",
+ __func__);
+ rc = -EINVAL;
+ goto err_probe;
+ }
+
+ if (of_get_property(dev->of_node, "parent-supply", NULL))
+ init_data->supply_regulator = "parent";
+
+ of_property_read_u32(dev->of_node, "qcom,system-load",
+ &vreg_priv->system_uA);
+ of_property_read_u32(dev->of_node, "qcom,hpm-min-load",
+ &vreg_priv->hpm_min_load);
+
+ init_data->constraints.input_uV = init_data->constraints.max_uV;
+
+ init_data->constraints.valid_ops_mask
+ |= REGULATOR_CHANGE_STATUS;
+ init_data->constraints.valid_ops_mask
+ |= REGULATOR_CHANGE_VOLTAGE;
+ init_data->constraints.valid_ops_mask
+ |= REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_DRMS;
+ init_data->constraints.valid_modes_mask
+ = REGULATOR_MODE_NORMAL | REGULATOR_MODE_IDLE;
+ } else {
+ /* Use platform data. */
+ vreg_pdata = dev->platform_data;
+ if (!vreg_pdata) {
+ dev_err(dev, "%s: no platform data\n", __func__);
+ rc = -EINVAL;
+ goto err_probe;
+ }
+ init_data = &vreg_pdata->init_data;
+
+ vreg_priv->system_uA = vreg_pdata->system_uA;
+ vreg_priv->hpm_min_load = vreg_pdata->hpm_min_load;
+ }
+
+ dev_set_drvdata(dev, vreg_priv);
rdesc = &vreg_priv->rdesc;
- strncpy(vreg_priv->name, vreg_pdata->init_data.constraints.name,
+ strlcpy(vreg_priv->name, init_data->constraints.name,
STUB_REGULATOR_MAX_NAME);
rdesc->name = vreg_priv->name;
rdesc->ops = ®ulator_stub_ops;
@@ -168,8 +218,8 @@
* which have a specified voltage constraint range, as well as those
* that do not.
*/
- if (vreg_pdata->init_data.constraints.min_uV == 0 &&
- vreg_pdata->init_data.constraints.max_uV == 0)
+ if (init_data->constraints.min_uV == 0 &&
+ init_data->constraints.max_uV == 0)
rdesc->n_voltages = 0;
else
rdesc->n_voltages = 2;
@@ -177,16 +227,20 @@
rdesc->id = pdev->id;
rdesc->owner = THIS_MODULE;
rdesc->type = REGULATOR_VOLTAGE;
- vreg_priv->system_uA = vreg_pdata->system_uA;
- vreg_priv->hpm_min_load = vreg_pdata->hpm_min_load;
- vreg_priv->voltage = vreg_pdata->init_data.constraints.min_uV;
+ vreg_priv->voltage = init_data->constraints.min_uV;
+ if (vreg_priv->system_uA >= vreg_priv->hpm_min_load)
+ vreg_priv->mode = REGULATOR_MODE_NORMAL;
+ else
+ vreg_priv->mode = REGULATOR_MODE_IDLE;
- vreg_priv->rdev = regulator_register(rdesc, &pdev->dev,
- &(vreg_pdata->init_data), vreg_priv, NULL);
+ vreg_priv->rdev = regulator_register(rdesc, dev, init_data, vreg_priv,
+ dev->of_node);
+
if (IS_ERR(vreg_priv->rdev)) {
rc = PTR_ERR(vreg_priv->rdev);
vreg_priv->rdev = NULL;
- dev_err(&pdev->dev, "%s: regulator_register failed\n",
+ if (rc != -EPROBE_DEFER)
+ dev_err(dev, "%s: regulator_register failed\n",
__func__);
goto err_probe;
}
@@ -206,12 +260,18 @@
return 0;
}
+static struct of_device_id regulator_stub_match_table[] = {
+ { .compatible = "qcom," STUB_REGULATOR_DRIVER_NAME, },
+ {}
+};
+
static struct platform_driver regulator_stub_driver = {
.probe = regulator_stub_probe,
.remove = __devexit_p(regulator_stub_remove),
.driver = {
.name = STUB_REGULATOR_DRIVER_NAME,
.owner = THIS_MODULE,
+ .of_match_table = regulator_stub_match_table,
},
};
diff --git a/drivers/slimbus/Kconfig b/drivers/slimbus/Kconfig
index a6a068d..72bd28a 100644
--- a/drivers/slimbus/Kconfig
+++ b/drivers/slimbus/Kconfig
@@ -16,4 +16,13 @@
help
Select driver for Qualcomm's Slimbus Master Component.
+config SLIMBUS_MSM_NGD
+ tristate "Qualcomm Slimbus Satellite Component"
+ help
+ Select driver for Qualcomm's Slimbus Satellite Component.
+ This is light-weight slimbus controller driver responsible for
+ communicating with slave HW directly over the bus using messaging
+ interface, and communicating with master component residing on ADSP
+ for bandwidth and data-channel management.
+
endif
diff --git a/drivers/slimbus/Makefile b/drivers/slimbus/Makefile
index 674f057..45d6e6e 100644
--- a/drivers/slimbus/Makefile
+++ b/drivers/slimbus/Makefile
@@ -3,3 +3,4 @@
#
obj-$(CONFIG_SLIMBUS) += slimbus.o
obj-$(CONFIG_SLIMBUS_MSM_CTRL) += slim-msm.o slim-msm-ctrl.o
+obj-$(CONFIG_SLIMBUS_MSM_NGD) += slim-msm.o slim-msm-ngd.o
diff --git a/drivers/slimbus/slim-msm-ngd.c b/drivers/slimbus/slim-msm-ngd.c
new file mode 100644
index 0000000..1f2a95e
--- /dev/null
+++ b/drivers/slimbus/slim-msm-ngd.c
@@ -0,0 +1,962 @@
+/* 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/irq.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/slimbus/slimbus.h>
+#include <linux/delay.h>
+#include <linux/kthread.h>
+#include <linux/clk.h>
+#include <linux/pm_runtime.h>
+#include <linux/of.h>
+#include <linux/of_slimbus.h>
+#include <linux/timer.h>
+#include <mach/sps.h>
+#include "slim-msm.h"
+#include <mach/qdsp6v2/apr.h>
+
+#define NGD_SLIM_NAME "ngd_msm_ctrl"
+#define SLIM_LA_MGR 0xFF
+#define SLIM_ROOT_FREQ 24576000
+
+#define NGD_BASE_V1(r) (((r) % 2) ? 0x800 : 0xA00)
+#define NGD_BASE_V2(r) (((r) % 2) ? 0x1000 : 0x2000)
+#define NGD_BASE(r, v) ((v) ? NGD_BASE_V2(r) : NGD_BASE_V1(r))
+/* NGD (Non-ported Generic Device) registers */
+enum ngd_reg {
+ NGD_CFG = 0x0,
+ NGD_STATUS = 0x4,
+ NGD_RX_MSGQ_CFG = 0x8,
+ NGD_INT_EN = 0x10,
+ NGD_INT_STAT = 0x14,
+ NGD_INT_CLR = 0x18,
+ NGD_TX_MSG = 0x30,
+ NGD_RX_MSG = 0x70,
+ NGD_IE_STAT = 0xF0,
+ NGD_VE_STAT = 0x100,
+};
+
+enum ngd_msg_cfg {
+ NGD_CFG_ENABLE = 1,
+ NGD_CFG_RX_MSGQ_EN = 1 << 1,
+ NGD_CFG_TX_MSGQ_EN = 1 << 2,
+};
+
+enum ngd_intr {
+ NGD_INT_RECFG_DONE = 1 << 24,
+ NGD_INT_TX_NACKED_2 = 1 << 25,
+ NGD_INT_MSG_BUF_CONTE = 1 << 26,
+ NGD_INT_MSG_TX_INVAL = 1 << 27,
+ NGD_INT_IE_VE_CHG = 1 << 28,
+ NGD_INT_DEV_ERR = 1 << 29,
+ NGD_INT_RX_MSG_RCVD = 1 << 30,
+ NGD_INT_TX_MSG_SENT = 1 << 31,
+};
+
+enum ngd_offsets {
+ NGD_NACKED_MC = 0x7F00000,
+ NGD_ACKED_MC = 0xFE000,
+ NGD_ERROR = 0x1800,
+ NGD_MSGQ_SUPPORT = 0x400,
+ NGD_RX_MSGQ_TIME_OUT = 0x16,
+ NGD_ENUMERATED = 0x1,
+ NGD_TX_BUSY = 0x0,
+};
+
+static irqreturn_t ngd_slim_interrupt(int irq, void *d)
+{
+ struct msm_slim_ctrl *dev = (struct msm_slim_ctrl *)d;
+ void __iomem *ngd = dev->base + NGD_BASE(dev->ctrl.nr, dev->ver);
+ u32 stat = readl_relaxed(ngd + NGD_INT_STAT);
+
+ if (stat & NGD_INT_TX_MSG_SENT) {
+ writel_relaxed(NGD_INT_TX_MSG_SENT, ngd + NGD_INT_CLR);
+ /* Make sure interrupt is cleared */
+ mb();
+ if (dev->wr_comp)
+ complete(dev->wr_comp);
+ } else if ((stat & NGD_INT_MSG_BUF_CONTE) ||
+ (stat & NGD_INT_MSG_TX_INVAL) || (stat & NGD_INT_DEV_ERR) ||
+ (stat & NGD_INT_TX_NACKED_2)) {
+ dev_err(dev->dev, "NGD interrupt error:0x%x", stat);
+ writel_relaxed(stat, ngd + NGD_INT_CLR);
+ /* Guarantee that error interrupts are cleared */
+ mb();
+ if (((stat & NGD_INT_TX_NACKED_2) ||
+ (stat & NGD_INT_MSG_TX_INVAL))) {
+ dev->err = -EIO;
+ if (dev->wr_comp)
+ complete(dev->wr_comp);
+ }
+ }
+ if (stat & NGD_INT_RX_MSG_RCVD) {
+ u32 rx_buf[10];
+ u8 len, i;
+ rx_buf[0] = readl_relaxed(ngd + NGD_RX_MSG);
+ len = rx_buf[0] & 0x1F;
+ for (i = 1; i < ((len + 3) >> 2); i++) {
+ rx_buf[i] = readl_relaxed(ngd + NGD_RX_MSG +
+ (4 * i));
+ dev_dbg(dev->dev, "REG-RX data: %x\n", rx_buf[i]);
+ }
+ msm_slim_rx_enqueue(dev, rx_buf, len);
+ writel_relaxed(NGD_INT_RX_MSG_RCVD,
+ ngd + NGD_INT_CLR);
+ /*
+ * Guarantee that CLR bit write goes through before
+ * queuing work
+ */
+ mb();
+ if (dev->use_rx_msgqs)
+ dev_err(dev->dev,
+ "direct message received even with RX MSGQs");
+ else
+ complete(&dev->rx_msgq_notify);
+ }
+ if (stat & NGD_INT_RECFG_DONE) {
+ writel_relaxed(NGD_INT_RECFG_DONE, ngd + NGD_INT_CLR);
+ /* Guarantee RECONFIG DONE interrupt is cleared */
+ mb();
+ /* In satellite mode, just log the reconfig done IRQ */
+ dev_dbg(dev->dev, "reconfig done IRQ for NGD");
+ }
+ if (stat & NGD_INT_IE_VE_CHG) {
+ writel_relaxed(NGD_INT_IE_VE_CHG, ngd + NGD_INT_CLR);
+ /* Guarantee IE VE change interrupt is cleared */
+ mb();
+ dev_err(dev->dev, "NGD IE VE change");
+ }
+ return IRQ_HANDLED;
+}
+
+static int ngd_get_tid(struct slim_controller *ctrl, struct slim_msg_txn *txn,
+ u8 *tid, struct completion *done)
+{
+ struct msm_slim_ctrl *dev = slim_get_ctrldata(ctrl);
+ if (ctrl->last_tid <= 255) {
+ ctrl->txnt = krealloc(ctrl->txnt,
+ (ctrl->last_tid + 1) *
+ sizeof(struct slim_msg_txn *),
+ GFP_KERNEL);
+ if (!ctrl->txnt)
+ return -ENOMEM;
+ dev->msg_cnt = ctrl->last_tid;
+ ctrl->last_tid++;
+ } else {
+ int i;
+ for (i = 0; i < 256; i++) {
+ dev->msg_cnt = ((dev->msg_cnt + 1) & 0xFF);
+ if (ctrl->txnt[dev->msg_cnt] == NULL)
+ break;
+ }
+ if (i >= 256) {
+ dev_err(&ctrl->dev, "out of TID");
+ return -ENOMEM;
+ }
+ }
+ ctrl->txnt[dev->msg_cnt] = txn;
+ txn->tid = dev->msg_cnt;
+ txn->comp = done;
+ *tid = dev->msg_cnt;
+ return 0;
+}
+static int ngd_xfer_msg(struct slim_controller *ctrl, struct slim_msg_txn *txn)
+{
+ DECLARE_COMPLETION_ONSTACK(done);
+ DECLARE_COMPLETION_ONSTACK(tx_sent);
+
+ struct msm_slim_ctrl *dev = slim_get_ctrldata(ctrl);
+ u32 *pbuf;
+ u8 *puc;
+ int ret = 0;
+ int msgv = -1;
+ u8 la = txn->la;
+ u8 wbuf[SLIM_RX_MSGQ_BUF_LEN];
+
+ if (txn->mt == SLIM_MSG_MT_CORE &&
+ (txn->mc >= SLIM_MSG_MC_BEGIN_RECONFIGURATION &&
+ txn->mc <= SLIM_MSG_MC_RECONFIGURE_NOW)) {
+ return 0;
+ }
+ msgv = msm_slim_get_ctrl(dev);
+ mutex_lock(&dev->tx_lock);
+ if (txn->mc != SLIM_USR_MC_REPORT_SATELLITE &&
+ (dev->state == MSM_CTRL_ASLEEP ||
+ dev->state == MSM_CTRL_SLEEPING)) {
+ int timeout;
+ dev_err(dev->dev, "controller not ready");
+ mutex_unlock(&dev->tx_lock);
+ /* Reconf is signalled when master responds */
+ timeout = wait_for_completion_timeout(&dev->reconf, HZ);
+ if (timeout) {
+ mutex_lock(&dev->tx_lock);
+ } else {
+ if (msgv >= 0)
+ msm_slim_put_ctrl(dev);
+ return -EBUSY;
+ }
+ }
+ if (txn->mt == SLIM_MSG_MT_CORE &&
+ (txn->mc == SLIM_MSG_MC_CONNECT_SOURCE ||
+ txn->mc == SLIM_MSG_MC_CONNECT_SINK ||
+ txn->mc == SLIM_MSG_MC_DISCONNECT_PORT)) {
+ int i = 0;
+ txn->mt = SLIM_MSG_MT_DEST_REFERRED_USER;
+ if (txn->mc == SLIM_MSG_MC_CONNECT_SOURCE)
+ txn->mc = SLIM_USR_MC_CONNECT_SRC;
+ else if (txn->mc == SLIM_MSG_MC_CONNECT_SINK)
+ txn->mc = SLIM_USR_MC_CONNECT_SINK;
+ else if (txn->mc == SLIM_MSG_MC_DISCONNECT_PORT)
+ txn->mc = SLIM_USR_MC_DISCONNECT_PORT;
+ if (txn->la == SLIM_LA_MGR)
+ txn->la = dev->pgdla;
+ wbuf[i++] = txn->la;
+ la = SLIM_LA_MGR;
+ wbuf[i++] = txn->wbuf[0];
+ if (txn->mc != SLIM_USR_MC_DISCONNECT_PORT)
+ wbuf[i++] = txn->wbuf[1];
+ ret = ngd_get_tid(ctrl, txn, &wbuf[i++], &done);
+ if (ret) {
+ pr_err("TID for connect/disconnect fail:%d", ret);
+ goto ngd_xfer_err;
+ }
+ txn->len = i;
+ txn->wbuf = wbuf;
+ txn->rl = txn->len + 4;
+ }
+ txn->rl--;
+ pbuf = msm_get_msg_buf(dev, txn->rl);
+ if (!pbuf) {
+ dev_err(dev->dev, "Message buffer unavailable");
+ ret = -ENOMEM;
+ goto ngd_xfer_err;
+ }
+ dev->err = 0;
+
+ if (txn->dt == SLIM_MSG_DEST_ENUMADDR) {
+ ret = -EPROTONOSUPPORT;
+ goto ngd_xfer_err;
+ }
+ if (txn->dt == SLIM_MSG_DEST_LOGICALADDR)
+ *pbuf = SLIM_MSG_ASM_FIRST_WORD(txn->rl, txn->mt, txn->mc, 0,
+ la);
+ else
+ *pbuf = SLIM_MSG_ASM_FIRST_WORD(txn->rl, txn->mt, txn->mc, 1,
+ la);
+ if (txn->dt == SLIM_MSG_DEST_LOGICALADDR)
+ puc = ((u8 *)pbuf) + 3;
+ else
+ puc = ((u8 *)pbuf) + 2;
+ if (txn->rbuf)
+ *(puc++) = txn->tid;
+ if ((txn->mt == SLIM_MSG_MT_CORE) &&
+ ((txn->mc >= SLIM_MSG_MC_REQUEST_INFORMATION &&
+ txn->mc <= SLIM_MSG_MC_REPORT_INFORMATION) ||
+ (txn->mc >= SLIM_MSG_MC_REQUEST_VALUE &&
+ txn->mc <= SLIM_MSG_MC_CHANGE_VALUE))) {
+ *(puc++) = (txn->ec & 0xFF);
+ *(puc++) = (txn->ec >> 8)&0xFF;
+ }
+ if (txn->wbuf)
+ memcpy(puc, txn->wbuf, txn->len);
+ if (txn->mt == SLIM_MSG_MT_DEST_REFERRED_USER &&
+ (txn->mc == SLIM_USR_MC_CONNECT_SRC ||
+ txn->mc == SLIM_USR_MC_CONNECT_SINK ||
+ txn->mc == SLIM_USR_MC_DISCONNECT_PORT) && txn->wbuf &&
+ wbuf[0] == dev->pgdla) {
+ if (txn->mc != SLIM_MSG_MC_DISCONNECT_PORT)
+ dev->err = msm_slim_connect_pipe_port(dev, wbuf[1]);
+ else {
+ struct msm_slim_endp *endpoint = &dev->pipes[wbuf[1]];
+ struct sps_register_event sps_event;
+ memset(&sps_event, 0, sizeof(sps_event));
+ sps_register_event(endpoint->sps, &sps_event);
+ sps_disconnect(endpoint->sps);
+ /*
+ * Remove channel disconnects master-side ports from
+ * channel. No need to send that again on the bus
+ */
+ dev->pipes[wbuf[1]].connected = false;
+ mutex_unlock(&dev->tx_lock);
+ if (msgv >= 0)
+ msm_slim_put_ctrl(dev);
+ return 0;
+ }
+ if (dev->err) {
+ dev_err(dev->dev, "pipe-port connect err:%d", dev->err);
+ goto ngd_xfer_err;
+ }
+ }
+ dev->err = 0;
+ dev->wr_comp = &tx_sent;
+ ret = msm_send_msg_buf(dev, pbuf, txn->rl,
+ NGD_BASE(dev->ctrl.nr, dev->ver) + NGD_TX_MSG);
+ if (!ret) {
+ int timeout = wait_for_completion_timeout(&tx_sent, HZ);
+ if (!timeout)
+ ret = -ETIMEDOUT;
+ else
+ ret = dev->err;
+ }
+ dev->wr_comp = NULL;
+ if (ret) {
+ u32 conf, stat, rx_msgq, int_stat, int_en, int_clr;
+ void __iomem *ngd = dev->base + NGD_BASE(dev->ctrl.nr,
+ dev->ver);
+ dev_err(dev->dev, "TX failed :MC:0x%x,mt:0x%x, ret:%d, ver:%d",
+ txn->mc, txn->mt, ret, dev->ver);
+ conf = readl_relaxed(ngd);
+ stat = readl_relaxed(ngd + NGD_STATUS);
+ rx_msgq = readl_relaxed(ngd + NGD_RX_MSGQ_CFG);
+ int_stat = readl_relaxed(ngd + NGD_INT_STAT);
+ int_en = readl_relaxed(ngd + NGD_INT_EN);
+ int_clr = readl_relaxed(ngd + NGD_INT_CLR);
+
+ pr_err("conf:0x%x,stat:0x%x,rxmsgq:0x%x", conf, stat, rx_msgq);
+ pr_err("int_stat:0x%x,int_en:0x%x,int_cll:0x%x", int_stat,
+ int_en, int_clr);
+ } else if (txn->mt == SLIM_MSG_MT_DEST_REFERRED_USER &&
+ (txn->mc == SLIM_USR_MC_CONNECT_SRC ||
+ txn->mc == SLIM_USR_MC_CONNECT_SINK ||
+ txn->mc == SLIM_USR_MC_DISCONNECT_PORT)) {
+ int timeout;
+ mutex_unlock(&dev->tx_lock);
+ if (msgv >= 0)
+ msm_slim_put_ctrl(dev);
+ timeout = wait_for_completion_timeout(txn->comp, HZ);
+ if (!timeout) {
+ pr_err("connect/disc :0x%x, tid:%d timed out", txn->mc,
+ txn->tid);
+ ret = -ETIMEDOUT;
+ } else {
+ ret = txn->ec;
+ }
+ if (ret)
+ pr_err("connect/disconnect:0x%x,tid:%d err:%d", txn->mc,
+ txn->tid, ret);
+ return ret ? ret : dev->err;
+ }
+ngd_xfer_err:
+ mutex_unlock(&dev->tx_lock);
+ if (msgv >= 0)
+ msm_slim_put_ctrl(dev);
+ return ret ? ret : dev->err;
+}
+
+static int ngd_xferandwait_ack(struct slim_controller *ctrl,
+ struct slim_msg_txn *txn)
+{
+ int ret = ngd_xfer_msg(ctrl, txn);
+ if (!ret) {
+ int timeout;
+ timeout = wait_for_completion_timeout(txn->comp, HZ);
+ if (!timeout) {
+ pr_err("master req:0x%x, tid:%d timed out", txn->mc,
+ txn->tid);
+ ret = -ETIMEDOUT;
+ } else {
+ ret = txn->ec;
+ }
+ }
+ if (ret)
+ pr_err("master msg:0x%x,tid:%d ret:%d", txn->mc,
+ txn->tid, ret);
+
+ return ret;
+}
+
+static int ngd_allocbw(struct slim_device *sb, int *subfrmc, int *clkgear)
+{
+ int ret;
+ struct slim_pending_ch *pch;
+ struct slim_msg_txn txn;
+ struct slim_controller *ctrl = sb->ctrl;
+ DECLARE_COMPLETION_ONSTACK(done);
+ u8 wbuf[SLIM_RX_MSGQ_BUF_LEN];
+
+ txn.mt = SLIM_MSG_MT_DEST_REFERRED_USER;
+ txn.dt = SLIM_MSG_DEST_LOGICALADDR;
+ txn.la = SLIM_LA_MGR;
+ txn.len = 0;
+ txn.ec = 0;
+ txn.wbuf = wbuf;
+ txn.rbuf = NULL;
+
+ list_for_each_entry(pch, &sb->mark_define, pending) {
+ struct slim_ich *slc;
+ slc = &ctrl->chans[pch->chan];
+ if (!slc) {
+ pr_err("no channel in define?");
+ return -ENXIO;
+ }
+ if (txn.len == 0) {
+ wbuf[txn.len++] = (u8) (slc->prop.dataf << 5) |
+ sb->laddr;
+ wbuf[txn.len] = slc->seglen;
+ if (slc->coeff == SLIM_COEFF_3)
+ wbuf[txn.len] |= 1 << 5;
+ wbuf[txn.len++] |= slc->prop.auxf << 6;
+ wbuf[txn.len++] = slc->rootexp << 4 | slc->prop.prot;
+ wbuf[txn.len++] = slc->prrate;
+ ret = ngd_get_tid(ctrl, &txn, &wbuf[txn.len++], &done);
+ if (ret) {
+ pr_err("no tid for channel define?");
+ return -ENXIO;
+ }
+ }
+ wbuf[txn.len++] = slc->chan;
+ }
+ if (txn.len) {
+ txn.mc = SLIM_USR_MC_DEF_ACT_CHAN;
+ txn.rl = txn.len + 4;
+ ret = ngd_xferandwait_ack(ctrl, &txn);
+ if (ret)
+ return ret;
+
+ txn.mc = SLIM_USR_MC_RECONFIG_NOW;
+ txn.len = 2;
+ wbuf[1] = sb->laddr;
+ txn.rl = txn.len + 4;
+ ret = ngd_get_tid(ctrl, &txn, &wbuf[0], &done);
+ if (ret)
+ return ret;
+ ret = ngd_xferandwait_ack(ctrl, &txn);
+ if (ret)
+ return ret;
+ }
+ txn.len = 0;
+ list_for_each_entry(pch, &sb->mark_removal, pending) {
+ struct slim_ich *slc;
+ slc = &ctrl->chans[pch->chan];
+ if (!slc) {
+ pr_err("no channel in removal?");
+ return -ENXIO;
+ }
+ if (txn.len == 0) {
+ wbuf[txn.len++] = (u8) (SLIM_CH_REMOVE << 6) |
+ sb->laddr;
+ ret = ngd_get_tid(ctrl, &txn, &wbuf[txn.len++], &done);
+ if (ret) {
+ pr_err("no tid for channel define?");
+ return -ENXIO;
+ }
+ }
+ wbuf[txn.len++] = slc->chan;
+ }
+ if (txn.len) {
+ txn.mc = SLIM_USR_MC_CHAN_CTRL;
+ txn.rl = txn.len + 4;
+ ret = ngd_xferandwait_ack(ctrl, &txn);
+ if (ret)
+ return ret;
+
+ txn.mc = SLIM_USR_MC_RECONFIG_NOW;
+ txn.len = 2;
+ wbuf[1] = sb->laddr;
+ txn.rl = txn.len + 4;
+ ret = ngd_get_tid(ctrl, &txn, &wbuf[0], &done);
+ if (ret)
+ return ret;
+ ret = ngd_xferandwait_ack(ctrl, &txn);
+ if (ret)
+ return ret;
+ txn.len = 0;
+ }
+ return ret;
+}
+
+static int ngd_set_laddr(struct slim_controller *ctrl, const u8 *ea,
+ u8 elen, u8 laddr)
+{
+ return 0;
+}
+
+static int ngd_get_laddr(struct slim_controller *ctrl, const u8 *ea,
+ u8 elen, u8 *laddr)
+{
+ int ret;
+ u8 wbuf[10];
+ struct slim_msg_txn txn;
+ DECLARE_COMPLETION_ONSTACK(done);
+ txn.mt = SLIM_MSG_MT_DEST_REFERRED_USER;
+ txn.dt = SLIM_MSG_DEST_LOGICALADDR;
+ txn.la = SLIM_LA_MGR;
+ txn.ec = 0;
+ mutex_lock(&ctrl->m_ctrl);
+ ret = ngd_get_tid(ctrl, &txn, &wbuf[0], &done);
+ if (ret) {
+ mutex_unlock(&ctrl->m_ctrl);
+ return ret;
+ }
+ memcpy(&wbuf[1], ea, elen);
+ txn.mc = SLIM_USR_MC_ADDR_QUERY;
+ txn.rl = 11;
+ txn.len = 7;
+ txn.wbuf = wbuf;
+ txn.rbuf = NULL;
+ ret = ngd_xferandwait_ack(ctrl, &txn);
+ if (!ret && txn.la == 0xFF)
+ ret = -ENXIO;
+ else if (!ret)
+ *laddr = txn.la;
+ mutex_unlock(&ctrl->m_ctrl);
+ return ret;
+}
+
+static void ngd_slim_rx(struct msm_slim_ctrl *dev, u8 *buf)
+{
+ u8 mc, mt, len;
+ int ret;
+ u32 msgq_en = 1;
+
+ len = buf[0] & 0x1F;
+ mt = (buf[0] >> 5) & 0x7;
+ mc = buf[1];
+ if (mc == SLIM_USR_MC_MASTER_CAPABILITY &&
+ mt == SLIM_MSG_MT_SRC_REFERRED_USER) {
+ struct slim_msg_txn txn;
+ u8 wbuf[8];
+ txn.dt = SLIM_MSG_DEST_LOGICALADDR;
+ txn.ec = 0;
+ txn.rbuf = NULL;
+ txn.mc = SLIM_USR_MC_REPORT_SATELLITE;
+ txn.mt = SLIM_MSG_MT_SRC_REFERRED_USER;
+ txn.la = SLIM_LA_MGR;
+ txn.rl = 8;
+ wbuf[0] = SAT_MAGIC_LSB;
+ wbuf[1] = SAT_MAGIC_MSB;
+ wbuf[2] = SAT_MSG_VER;
+ wbuf[3] = SAT_MSG_PROT;
+ txn.wbuf = wbuf;
+ txn.len = 4;
+ dev->use_rx_msgqs = 1;
+ msm_slim_sps_init(dev, dev->bam_mem,
+ NGD_BASE(dev->ctrl.nr, dev->ver) + NGD_STATUS, true);
+ if (dev->use_rx_msgqs)
+ msgq_en |= NGD_CFG_RX_MSGQ_EN;
+ writel_relaxed(msgq_en, dev->base +
+ NGD_BASE(dev->ctrl.nr, dev->ver));
+ /* make sure NGD MSG-Q config goes through */
+ mb();
+
+ ret = ngd_xfer_msg(&dev->ctrl, &txn);
+ if (!ret) {
+ dev->state = MSM_CTRL_AWAKE;
+ complete(&dev->reconf);
+ }
+ }
+ if (mc == SLIM_MSG_MC_REPLY_INFORMATION ||
+ mc == SLIM_MSG_MC_REPLY_VALUE) {
+ u8 tid = buf[3];
+ dev_dbg(dev->dev, "tid:%d, len:%d\n", tid, len);
+ slim_msg_response(&dev->ctrl, &buf[4], tid,
+ len - 4);
+ pm_runtime_mark_last_busy(dev->dev);
+ }
+ if (mc == SLIM_USR_MC_ADDR_REPLY &&
+ mt == SLIM_MSG_MT_SRC_REFERRED_USER) {
+ struct slim_msg_txn *txn = dev->ctrl.txnt[buf[3]];
+ u8 failed_ea[6] = {0, 0, 0, 0, 0, 0};
+ if (!txn)
+ return;
+ if (memcmp(&buf[4], failed_ea, 6))
+ txn->la = buf[10];
+ dev->ctrl.txnt[buf[3]] = NULL;
+ complete(txn->comp);
+ }
+ if (mc == SLIM_USR_MC_GENERIC_ACK &&
+ mt == SLIM_MSG_MT_SRC_REFERRED_USER) {
+ struct slim_msg_txn *txn = dev->ctrl.txnt[buf[3]];
+ if (!txn)
+ return;
+ dev_dbg(dev->dev, "got response:tid:%d, response:0x%x",
+ (int)buf[3], buf[4]);
+ if (!(buf[4] & MSM_SAT_SUCCSS)) {
+ dev_err(dev->dev, "TID:%d, NACK code:0x%x", (int)buf[3],
+ buf[4]);
+ txn->ec = -EIO;
+ }
+ dev->ctrl.txnt[buf[3]] = NULL;
+ complete(txn->comp);
+ }
+}
+static int ngd_slim_rx_msgq_thread(void *data)
+{
+ struct msm_slim_ctrl *dev = (struct msm_slim_ctrl *)data;
+ struct completion *notify = &dev->rx_msgq_notify;
+ int ret = 0, index = 0;
+ u32 mc = 0;
+ u32 mt = 0;
+ u32 buffer[10];
+ u8 msg_len = 0;
+
+ while (!kthread_should_stop()) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ ret = wait_for_completion_interruptible(notify);
+ if (ret) {
+ dev_err(dev->dev, "rx thread wait err:%d", ret);
+ continue;
+ }
+ /* 1 irq notification per message */
+ if (!dev->use_rx_msgqs) {
+ msm_slim_rx_dequeue(dev, (u8 *)buffer);
+ ngd_slim_rx(dev, (u8 *)buffer);
+ continue;
+ }
+ ret = msm_slim_rx_msgq_get(dev, buffer, index);
+ if (ret) {
+ dev_err(dev->dev, "rx_msgq_get() failed 0x%x\n", ret);
+ continue;
+ }
+
+ /* Wait for complete message */
+ if (index++ == 0) {
+ msg_len = *buffer & 0x1F;
+ mt = (buffer[0] >> 5) & 0x7;
+ mc = (buffer[0] >> 8) & 0xff;
+ dev_dbg(dev->dev, "MC: %x, MT: %x\n", mc, mt);
+ }
+ if ((index * 4) >= msg_len) {
+ index = 0;
+ ngd_slim_rx(dev, (u8 *)buffer);
+ } else
+ continue;
+ }
+ return 0;
+}
+
+static int __devinit ngd_slim_probe(struct platform_device *pdev)
+{
+ struct msm_slim_ctrl *dev;
+ int ret;
+ struct resource *bam_mem;
+ struct resource *slim_mem;
+ struct resource *irq, *bam_irq;
+ enum apr_subsys_state q6_state;
+ u32 ngd_int;
+
+ q6_state = apr_get_q6_state();
+ if (q6_state == APR_SUBSYS_DOWN) {
+ dev_dbg(&pdev->dev, "defering %s, adsp_state %d\n", __func__,
+ q6_state);
+ return -EPROBE_DEFER;
+ } else
+ dev_dbg(&pdev->dev, "adsp is ready\n");
+
+ slim_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "slimbus_physical");
+ if (!slim_mem) {
+ dev_err(&pdev->dev, "no slimbus physical memory resource\n");
+ return -ENODEV;
+ }
+ bam_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "slimbus_bam_physical");
+ if (!bam_mem) {
+ dev_err(&pdev->dev, "no slimbus BAM memory resource\n");
+ return -ENODEV;
+ }
+ irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
+ "slimbus_irq");
+ if (!irq) {
+ dev_err(&pdev->dev, "no slimbus IRQ resource\n");
+ return -ENODEV;
+ }
+ bam_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
+ "slimbus_bam_irq");
+ if (!bam_irq) {
+ dev_err(&pdev->dev, "no slimbus BAM IRQ resource\n");
+ return -ENODEV;
+ }
+
+ dev = kzalloc(sizeof(struct msm_slim_ctrl), GFP_KERNEL);
+ if (IS_ERR(dev)) {
+ dev_err(&pdev->dev, "no memory for MSM slimbus controller\n");
+ return PTR_ERR(dev);
+ }
+ dev->dev = &pdev->dev;
+ platform_set_drvdata(pdev, dev);
+ slim_set_ctrldata(&dev->ctrl, dev);
+ dev->base = ioremap(slim_mem->start, resource_size(slim_mem));
+ if (!dev->base) {
+ dev_err(&pdev->dev, "IOremap failed\n");
+ ret = -ENOMEM;
+ goto err_ioremap_failed;
+ }
+ dev->bam.base = ioremap(bam_mem->start, resource_size(bam_mem));
+ if (!dev->bam.base) {
+ dev_err(&pdev->dev, "BAM IOremap failed\n");
+ ret = -ENOMEM;
+ goto err_ioremap_bam_failed;
+ }
+ if (pdev->dev.of_node) {
+
+ ret = of_property_read_u32(pdev->dev.of_node, "cell-index",
+ &dev->ctrl.nr);
+ if (ret) {
+ dev_err(&pdev->dev, "Cell index not specified:%d", ret);
+ goto err_ctrl_failed;
+ }
+ } else {
+ dev->ctrl.nr = pdev->id;
+ }
+ dev->ctrl.nchans = MSM_SLIM_NCHANS;
+ dev->ctrl.nports = MSM_SLIM_NPORTS;
+ dev->framer.rootfreq = SLIM_ROOT_FREQ >> 3;
+ dev->framer.superfreq =
+ dev->framer.rootfreq / SLIM_CL_PER_SUPERFRAME_DIV8;
+ dev->ctrl.a_framer = &dev->framer;
+ dev->ctrl.clkgear = SLIM_MAX_CLK_GEAR;
+ dev->ctrl.set_laddr = ngd_set_laddr;
+ dev->ctrl.get_laddr = ngd_get_laddr;
+ dev->ctrl.allocbw = ngd_allocbw;
+ dev->ctrl.xfer_msg = ngd_xfer_msg;
+ dev->ctrl.wakeup = NULL;
+ dev->ctrl.config_port = msm_config_port;
+ dev->ctrl.port_xfer = msm_slim_port_xfer;
+ dev->ctrl.port_xfer_status = msm_slim_port_xfer_status;
+ /* Reserve some messaging BW for satellite-apps driver communication */
+ dev->ctrl.sched.pending_msgsl = 30;
+ dev->bam_mem = bam_mem;
+
+ init_completion(&dev->reconf);
+ mutex_init(&dev->tx_lock);
+ spin_lock_init(&dev->rx_lock);
+ dev->ee = 1;
+ dev->irq = irq->start;
+ dev->bam.irq = bam_irq->start;
+
+ dev->ver = readl_relaxed(dev->base);
+ /* Version info in 16 MSbits */
+ dev->ver >>= 16;
+ ngd_int = (NGD_INT_RECFG_DONE | NGD_INT_TX_NACKED_2 |
+ NGD_INT_MSG_BUF_CONTE | NGD_INT_MSG_TX_INVAL |
+ NGD_INT_IE_VE_CHG | NGD_INT_DEV_ERR |
+ NGD_INT_TX_MSG_SENT | NGD_INT_RX_MSG_RCVD);
+ init_completion(&dev->rx_msgq_notify);
+
+ /* Register with framework */
+ ret = slim_add_numbered_controller(&dev->ctrl);
+ if (ret) {
+ dev_err(dev->dev, "error adding controller\n");
+ goto err_ctrl_failed;
+ }
+
+ dev->ctrl.dev.parent = &pdev->dev;
+ dev->ctrl.dev.of_node = pdev->dev.of_node;
+ dev->state = MSM_CTRL_ASLEEP;
+
+ ret = request_irq(dev->irq, ngd_slim_interrupt,
+ IRQF_TRIGGER_HIGH, "ngd_slim_irq", dev);
+
+ if (ret) {
+ dev_err(&pdev->dev, "request IRQ failed\n");
+ goto err_request_irq_failed;
+ }
+
+ /* Fire up the Rx message queue thread */
+ dev->rx_msgq_thread = kthread_run(ngd_slim_rx_msgq_thread, dev,
+ NGD_SLIM_NAME "_ngd_msgq_thread");
+ if (IS_ERR(dev->rx_msgq_thread)) {
+ ret = PTR_ERR(dev->rx_msgq_thread);
+ dev_err(dev->dev, "Failed to start Rx message queue thread\n");
+ goto err_thread_create_failed;
+ }
+
+ writel_relaxed(ngd_int, dev->base + NGD_INT_EN +
+ NGD_BASE(dev->ctrl.nr, dev->ver));
+ /*
+ * Enable NGD. Configure NGD in register access mode until master
+ * announcement is received
+ */
+ writel_relaxed(1, dev->base + NGD_BASE(dev->ctrl.nr, dev->ver));
+ /* make sure NGD enabling goes through */
+ mb();
+
+ if (pdev->dev.of_node)
+ of_register_slim_devices(&dev->ctrl);
+
+ /* Add devices registered with board-info now that controller is up */
+ slim_ctrl_add_boarddevs(&dev->ctrl);
+
+ pm_runtime_use_autosuspend(&pdev->dev);
+ pm_runtime_set_autosuspend_delay(&pdev->dev, MSM_SLIM_AUTOSUSPEND);
+ pm_runtime_set_active(&pdev->dev);
+
+ dev_dbg(dev->dev, "NGD SB controller is up!\n");
+ return 0;
+
+err_thread_create_failed:
+ free_irq(dev->irq, dev);
+err_request_irq_failed:
+ slim_del_controller(&dev->ctrl);
+err_ctrl_failed:
+ iounmap(dev->bam.base);
+err_ioremap_bam_failed:
+ iounmap(dev->base);
+err_ioremap_failed:
+ kfree(dev);
+ return ret;
+}
+
+static int __devexit ngd_slim_remove(struct platform_device *pdev)
+{
+ struct msm_slim_ctrl *dev = platform_get_drvdata(pdev);
+ pm_runtime_disable(&pdev->dev);
+ pm_runtime_set_suspended(&pdev->dev);
+ free_irq(dev->irq, dev);
+ slim_del_controller(&dev->ctrl);
+ kthread_stop(dev->rx_msgq_thread);
+ iounmap(dev->bam.base);
+ iounmap(dev->base);
+ kfree(dev);
+ return 0;
+}
+
+#ifdef CONFIG_PM_RUNTIME
+static int ngd_slim_runtime_idle(struct device *device)
+{
+ dev_dbg(device, "pm_runtime: idle...\n");
+ pm_request_autosuspend(device);
+ return -EAGAIN;
+}
+#endif
+
+/*
+ * If PM_RUNTIME is not defined, these 2 functions become helper
+ * functions to be called from system suspend/resume. So they are not
+ * inside ifdef CONFIG_PM_RUNTIME
+ */
+#ifdef CONFIG_PM_SLEEP
+static int ngd_slim_runtime_suspend(struct device *device)
+{
+ struct platform_device *pdev = to_platform_device(device);
+ struct msm_slim_ctrl *dev = platform_get_drvdata(pdev);
+ int ret;
+ dev_dbg(device, "pm_runtime: suspending...\n");
+ dev->state = MSM_CTRL_SLEEPING;
+ ret = slim_ctrl_clk_pause(&dev->ctrl, false, SLIM_CLK_UNSPECIFIED);
+ if (ret) {
+ dev_err(device, "clk pause not entered:%d", ret);
+ dev->state = MSM_CTRL_AWAKE;
+ } else {
+ dev->state = MSM_CTRL_ASLEEP;
+ }
+ return ret;
+}
+
+static int ngd_slim_runtime_resume(struct device *device)
+{
+ struct platform_device *pdev = to_platform_device(device);
+ struct msm_slim_ctrl *dev = platform_get_drvdata(pdev);
+ int ret = 0;
+ dev_dbg(device, "pm_runtime: resuming...\n");
+ if (dev->state == MSM_CTRL_ASLEEP)
+ ret = slim_ctrl_clk_pause(&dev->ctrl, true, 0);
+ if (ret) {
+ dev_err(device, "clk pause not exited:%d", ret);
+ dev->state = MSM_CTRL_ASLEEP;
+ } else {
+ dev->state = MSM_CTRL_AWAKE;
+ }
+ return ret;
+}
+
+static int ngd_slim_suspend(struct device *dev)
+{
+ int ret = -EBUSY;
+ if (!pm_runtime_enabled(dev) || !pm_runtime_suspended(dev)) {
+ dev_dbg(dev, "system suspend");
+ ret = ngd_slim_runtime_suspend(dev);
+ }
+ if (ret == -EBUSY) {
+ /*
+ * There is a possibility that some audio stream is active
+ * during suspend. We dont want to return suspend failure in
+ * that case so that display and relevant components can still
+ * go to suspend.
+ * If there is some other error, then it should be passed-on
+ * to system level suspend
+ */
+ ret = 0;
+ }
+ return ret;
+}
+
+static int ngd_slim_resume(struct device *dev)
+{
+ /* If runtime_pm is enabled, this resume shouldn't do anything */
+ if (!pm_runtime_enabled(dev) || !pm_runtime_suspended(dev)) {
+ int ret;
+ dev_dbg(dev, "system resume");
+ ret = ngd_slim_runtime_resume(dev);
+ if (!ret) {
+ pm_runtime_mark_last_busy(dev);
+ pm_request_autosuspend(dev);
+ }
+ return ret;
+
+ }
+ return 0;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static const struct dev_pm_ops ngd_slim_dev_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(
+ ngd_slim_suspend,
+ ngd_slim_resume
+ )
+ SET_RUNTIME_PM_OPS(
+ ngd_slim_runtime_suspend,
+ ngd_slim_runtime_resume,
+ ngd_slim_runtime_idle
+ )
+};
+
+static struct of_device_id ngd_slim_dt_match[] = {
+ {
+ .compatible = "qcom,slim-ngd",
+ },
+ {}
+};
+
+static struct platform_driver ngd_slim_driver = {
+ .probe = ngd_slim_probe,
+ .remove = ngd_slim_remove,
+ .driver = {
+ .name = NGD_SLIM_NAME,
+ .owner = THIS_MODULE,
+ .pm = &ngd_slim_dev_pm_ops,
+ .of_match_table = ngd_slim_dt_match,
+ },
+};
+
+static int ngd_slim_init(void)
+{
+ return platform_driver_register(&ngd_slim_driver);
+}
+late_initcall(ngd_slim_init);
+
+static void ngd_slim_exit(void)
+{
+ platform_driver_unregister(&ngd_slim_driver);
+}
+module_exit(ngd_slim_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("MSM Slimbus controller");
+MODULE_ALIAS("platform:msm-slim-ngd");
diff --git a/drivers/slimbus/slim-msm.h b/drivers/slimbus/slim-msm.h
index 35bb040..7d50620 100644
--- a/drivers/slimbus/slim-msm.h
+++ b/drivers/slimbus/slim-msm.h
@@ -167,6 +167,7 @@
struct device *dev;
void __iomem *base;
struct resource *slew_mem;
+ struct resource *bam_mem;
u32 curr_bw;
u8 msg_cnt;
u32 tx_buf[10];
diff --git a/drivers/thermal/msm8960_tsens.c b/drivers/thermal/msm8960_tsens.c
index f60e318..a932f6b 100644
--- a/drivers/thermal/msm8960_tsens.c
+++ b/drivers/thermal/msm8960_tsens.c
@@ -64,7 +64,7 @@
#define TSENS_UPPER_STATUS_CLR BIT((tsens_status_cntl_start + 2))
#define TSENS_MAX_STATUS_MASK BIT((tsens_status_cntl_start + 3))
-#define TSENS_MEASURE_PERIOD 4 /* 1 sec. default */
+#define TSENS_MEASURE_PERIOD 1
#define TSENS_8960_SLP_CLK_ENA BIT(26)
#define TSENS_THRESHOLD_ADDR (MSM_CLK_CTL_BASE + 0x00003624)
diff --git a/drivers/thermal/msm8974-tsens.c b/drivers/thermal/msm8974-tsens.c
index 6887fef..8e13fbf 100644
--- a/drivers/thermal/msm8974-tsens.c
+++ b/drivers/thermal/msm8974-tsens.c
@@ -50,7 +50,13 @@
#define TSENS_TRDY_MASK BIT(0)
#define TSENS_CTRL_ADDR(n) (n)
+#define TSENS_EN BIT(0)
#define TSENS_SW_RST BIT(1)
+#define TSENS_ADC_CLK_SEL BIT(2)
+#define TSENS_SENSOR0_SHIFT 3
+#define TSENS_312_5_MS_MEAS_PERIOD 2
+#define TSENS_MEAS_PERIOD_SHIFT 18
+
#define TSENS_SN_MIN_MAX_STATUS_CTRL(n) ((n) + 4)
#define TSENS_GLOBAL_CONFIG(n) ((n) + 0x34)
#define TSENS_S0_MAIN_CONFIG(n) ((n) + 0x38)
@@ -156,7 +162,6 @@
#define TSENS_THRESHOLD_MAX_CODE 0x3ff
#define TSENS_THRESHOLD_MIN_CODE 0x0
-#define TSENS_CTRL_INIT_DATA1 0x1cfff9
#define TSENS_GLOBAL_INIT_DATA 0x302f16c
#define TSENS_S0_MAIN_CFG_INIT_DATA 0x1c3
#define TSENS_SN_MIN_MAX_STATUS_CTRL_DATA 0x3ffc00
@@ -521,8 +526,10 @@
reg_cntl = readl_relaxed(TSENS_CTRL_ADDR(tmdev->tsens_addr));
writel_relaxed(reg_cntl | TSENS_SW_RST,
TSENS_CTRL_ADDR(tmdev->tsens_addr));
- writel_relaxed(TSENS_CTRL_INIT_DATA1,
- TSENS_CTRL_ADDR(tmdev->tsens_addr));
+ reg_cntl |= ((TSENS_312_5_MS_MEAS_PERIOD << TSENS_MEAS_PERIOD_SHIFT) |
+ (((1 << tmdev->tsens_num_sensor) - 1) << TSENS_SENSOR0_SHIFT) |
+ TSENS_EN);
+ writel_relaxed(reg_cntl, TSENS_CTRL_ADDR(tmdev->tsens_addr));
writel_relaxed(TSENS_GLOBAL_INIT_DATA,
TSENS_GLOBAL_CONFIG(tmdev->tsens_addr));
writel_relaxed(TSENS_S0_MAIN_CFG_INIT_DATA,
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 356b6f6..c0b4b57 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -300,6 +300,18 @@
}
}
+/* XHCI reset, resets other CORE registers as well, re-init those */
+void dwc3_post_host_reset_core_init(struct dwc3 *dwc)
+{
+ /*
+ * XHCI reset clears EVENT buffer register as well, re-init
+ * EVENT buffers and also do device specific re-initialization
+ */
+ dwc3_event_buffers_setup(dwc);
+
+ dwc3_gadget_restart(dwc);
+}
+
static void __devinit dwc3_cache_hwparams(struct dwc3 *dwc)
{
struct dwc3_hwparams *parms = &dwc->hwparams;
@@ -572,6 +584,13 @@
goto err1;
}
+ ret = dwc3_host_init(dwc);
+ if (ret) {
+ dev_err(dev, "failed to initialize host\n");
+ dwc3_otg_exit(dwc);
+ goto err1;
+ }
+
ret = dwc3_gadget_init(dwc);
if (ret) {
dev_err(dev, "failed to initialize gadget\n");
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 4485a43..3fb89cd 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -917,6 +917,9 @@
int dwc3_gadget_init(struct dwc3 *dwc);
void dwc3_gadget_exit(struct dwc3 *dwc);
+void dwc3_gadget_restart(struct dwc3 *dwc);
+void dwc3_post_host_reset_core_init(struct dwc3 *dwc);
+
extern int dwc3_get_device_id(void);
extern void dwc3_put_device_id(int id);
diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c
index b71bd3e..0531f83 100644
--- a/drivers/usb/dwc3/dwc3-msm.c
+++ b/drivers/usb/dwc3/dwc3-msm.c
@@ -17,6 +17,7 @@
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <linux/pm_runtime.h>
+#include <linux/ratelimit.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/clk.h>
@@ -32,8 +33,10 @@
#include <linux/usb/gadget.h>
#include <linux/usb/msm_hsusb.h>
#include <linux/regulator/consumer.h>
+#include <linux/power_supply.h>
#include <mach/rpm-regulator.h>
+#include <mach/msm_xo.h>
#include <mach/msm_bus.h>
#include "dwc3_otg.h"
@@ -127,6 +130,7 @@
u8 ep_num_mapping[DBM_MAX_EPS];
const struct usb_ep_ops *original_ep_ops[DWC3_ENDPOINTS_NUM];
struct list_head req_complete_list;
+ struct msm_xo_voter *xo_handle;
struct clk *ref_clk;
struct clk *core_clk;
struct clk *iface_clk;
@@ -143,6 +147,8 @@
bool resume_pending;
atomic_t pm_suspended;
atomic_t in_lpm;
+ int hs_phy_irq;
+ bool lpm_irq_seen;
struct delayed_work resume_work;
struct wake_lock wlock;
struct dwc3_charger charger;
@@ -152,6 +158,11 @@
u8 dcd_retries;
u32 bus_perf_client;
struct msm_bus_scale_pdata *bus_scale_table;
+ struct power_supply usb_psy;
+ unsigned int online;
+ unsigned int host_mode;
+ unsigned int current_max;
+ bool vbus_active;
};
#define USB_HSPHY_3P3_VOL_MIN 3050000 /* uV */
@@ -615,7 +626,8 @@
params.param0 = 0; /* TDAddr High */
params.param1 = lower_32_bits(req->trb_dma); /* DAddr Low */
- cmd = DWC3_DEPCMD_STARTTRANSFER;
+ /* DBM requires IOC to be set */
+ cmd = DWC3_DEPCMD_STARTTRANSFER | DWC3_DEPCMD_CMDIOC;
ret = dwc3_send_gadget_ep_cmd(dep->dwc, dep->number, cmd, ¶ms);
if (ret < 0) {
dev_dbg(dep->dwc->dev,
@@ -1238,12 +1250,35 @@
return 0;
}
- clk_disable_unprepare(mdwc->iface_clk);
- clk_disable_unprepare(mdwc->core_clk);
+ /* Sequence to put hardware in low power state:
+ * 1. Set OTGDISABLE to disable OTG block in HSPHY (saves power)
+ * 2. Clear charger detection control fields
+ * 3. SUSPEND PHY and turn OFF core clock after some delay
+ * 4. Clear interrupt latch register and enable BSV, ID HV interrupts
+ * 5. Enable PHY retention
+ */
+ dwc3_msm_write_readback(mdwc->base, HS_PHY_CTRL_REG, 0x1000, 0x1000);
+ dwc3_msm_write_readback(mdwc->base, CHARGING_DET_CTRL_REG, 0x37, 0x0);
+ dwc3_msm_write_readback(mdwc->base, HS_PHY_CTRL_REG,
+ 0xC00000, 0x800000);
+
+ usleep_range(1000, 1200);
clk_disable_unprepare(mdwc->ref_clk);
- dwc3_hsusb_ldo_enable(0);
- dwc3_ssusb_ldo_enable(0);
- wake_unlock(&mdwc->wlock);
+
+ dwc3_msm_write_reg(mdwc->base, HS_PHY_IRQ_STAT_REG, 0xFFF);
+ dwc3_msm_write_readback(mdwc->base, HS_PHY_CTRL_REG, 0x18000, 0x18000);
+ dwc3_msm_write_readback(mdwc->base, HS_PHY_CTRL_REG, 0x2, 0x0);
+
+ /* make sure above writes are completed before turning off clocks */
+ wmb();
+ clk_disable_unprepare(mdwc->core_clk);
+ clk_disable_unprepare(mdwc->iface_clk);
+
+ /* USB PHY no more requires TCXO */
+ ret = msm_xo_mode_vote(mdwc->xo_handle, MSM_XO_MODE_OFF);
+ if (ret)
+ dev_err(mdwc->dev, "%s failed to devote for TCXO buffer%d\n",
+ __func__, ret);
if (mdwc->bus_perf_client) {
ret = msm_bus_scale_client_update_request(
@@ -1252,7 +1287,13 @@
dev_err(mdwc->dev, "Failed to reset bus bw vote\n");
}
+ if (mdwc->otg_xceiv && mdwc->ext_xceiv.otg_capability)
+ dwc3_hsusb_ldo_enable(0);
+
+ dwc3_hsusb_config_vddcx(0);
+ wake_unlock(&mdwc->wlock);
atomic_set(&mdwc->in_lpm, 1);
+
dev_info(mdwc->dev, "DWC3 in low power mode\n");
return 0;
@@ -1269,6 +1310,8 @@
return 0;
}
+ wake_lock(&mdwc->wlock);
+
if (mdwc->bus_perf_client) {
ret = msm_bus_scale_client_update_request(
mdwc->bus_perf_client, 1);
@@ -1276,14 +1319,45 @@
dev_err(mdwc->dev, "Failed to vote for bus scaling\n");
}
- wake_lock(&mdwc->wlock);
+ /* Vote for TCXO while waking up USB HSPHY */
+ ret = msm_xo_mode_vote(mdwc->xo_handle, MSM_XO_MODE_ON);
+ if (ret)
+ dev_err(mdwc->dev, "%s failed to vote for TCXO buffer%d\n",
+ __func__, ret);
+
+ if (mdwc->otg_xceiv && mdwc->ext_xceiv.otg_capability)
+ dwc3_hsusb_ldo_enable(1);
+
+ dwc3_hsusb_config_vddcx(1);
clk_prepare_enable(mdwc->ref_clk);
- clk_prepare_enable(mdwc->core_clk);
+ usleep_range(1000, 1200);
+
clk_prepare_enable(mdwc->iface_clk);
- dwc3_hsusb_ldo_enable(1);
- dwc3_ssusb_ldo_enable(1);
+ clk_prepare_enable(mdwc->core_clk);
+
+ /* Disable HV interrupt */
+ dwc3_msm_write_readback(mdwc->base, HS_PHY_CTRL_REG, 0x18000, 0x0);
+ /* Disable Retention */
+ dwc3_msm_write_readback(mdwc->base, HS_PHY_CTRL_REG, 0x2, 0x2);
+
+ dwc3_msm_write_reg(mdwc->base, DWC3_GUSB2PHYCFG(0),
+ dwc3_msm_read_reg(mdwc->base, DWC3_GUSB2PHYCFG(0)) | 0xF0000000);
+ /* 20usec delay required before de-asserting PHY RESET */
+ udelay(20);
+ dwc3_msm_write_reg(mdwc->base, DWC3_GUSB2PHYCFG(0),
+ dwc3_msm_read_reg(mdwc->base, DWC3_GUSB2PHYCFG(0)) & 0x7FFFFFFF);
+
+ /* Bring PHY out of suspend */
+ dwc3_msm_write_readback(mdwc->base, HS_PHY_CTRL_REG, 0xC00000, 0x0);
atomic_set(&mdwc->in_lpm, 0);
+
+ /* match disable_irq call from isr */
+ if (mdwc->lpm_irq_seen && mdwc->hs_phy_irq) {
+ enable_irq(mdwc->hs_phy_irq);
+ mdwc->lpm_irq_seen = false;
+ }
+
dev_info(mdwc->dev, "DWC3 exited from low power mode\n");
return 0;
@@ -1313,10 +1387,13 @@
mdwc->ext_xceiv.notify_ext_events(mdwc->otg_xceiv->otg,
DWC3_EVENT_PHY_RESUME);
pm_runtime_put_sync(mdwc->dev);
+ if (mdwc->otg_xceiv && (mdwc->ext_xceiv.otg_capability))
+ mdwc->ext_xceiv.notify_ext_events(mdwc->otg_xceiv->otg,
+ DWC3_EVENT_XCEIV_STATE);
}
}
-static bool debug_id, debug_bsv, debug_connect;
+static u32 debug_id, debug_bsv, debug_connect;
static int dwc3_connect_show(struct seq_file *s, void *unused)
{
@@ -1386,11 +1463,11 @@
return;
if (!debugfs_create_bool("id", S_IRUGO | S_IWUSR, dwc3_debugfs_root,
- (u32 *)&debug_id))
+ &debug_id))
goto error;
if (!debugfs_create_bool("bsv", S_IRUGO | S_IWUSR, dwc3_debugfs_root,
- (u32 *)&debug_bsv))
+ &debug_bsv))
goto error;
if (!debugfs_create_file("connect", S_IRUGO | S_IWUSR,
@@ -1403,6 +1480,106 @@
debugfs_remove_recursive(dwc3_debugfs_root);
}
+static irqreturn_t msm_dwc3_irq(int irq, void *data)
+{
+ struct dwc3_msm *mdwc = data;
+
+ if (atomic_read(&mdwc->in_lpm)) {
+ dev_dbg(mdwc->dev, "%s received in LPM\n", __func__);
+ mdwc->lpm_irq_seen = true;
+ disable_irq_nosync(irq);
+ queue_delayed_work(system_nrt_wq, &mdwc->resume_work, 0);
+ } else {
+ pr_info_ratelimited("%s: IRQ outside LPM\n", __func__);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int dwc3_msm_power_get_property_usb(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct dwc3_msm *mdwc = container_of(psy, struct dwc3_msm,
+ usb_psy);
+ switch (psp) {
+ case POWER_SUPPLY_PROP_SCOPE:
+ val->intval = mdwc->host_mode;
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_MAX:
+ val->intval = mdwc->current_max;
+ break;
+ case POWER_SUPPLY_PROP_PRESENT:
+ val->intval = mdwc->vbus_active;
+ break;
+ case POWER_SUPPLY_PROP_ONLINE:
+ val->intval = mdwc->online;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int dwc3_msm_power_set_property_usb(struct power_supply *psy,
+ enum power_supply_property psp,
+ const union power_supply_propval *val)
+{
+ static bool init;
+ struct dwc3_msm *mdwc = container_of(psy, struct dwc3_msm,
+ usb_psy);
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_SCOPE:
+ mdwc->host_mode = val->intval;
+ break;
+ /* Process PMIC notification in PRESENT prop */
+ case POWER_SUPPLY_PROP_PRESENT:
+ dev_dbg(mdwc->dev, "%s: notify xceiv event\n", __func__);
+ if (mdwc->otg_xceiv && (mdwc->ext_xceiv.otg_capability ||
+ !init)) {
+ mdwc->ext_xceiv.bsv = val->intval;
+ mdwc->ext_xceiv.id = DWC3_ID_FLOAT;
+ if (atomic_read(&mdwc->in_lpm)) {
+ dev_dbg(mdwc->dev,
+ "%s received in LPM\n", __func__);
+ queue_delayed_work(system_nrt_wq,
+ &mdwc->resume_work, 0);
+ } else {
+ mdwc->ext_xceiv.notify_ext_events(
+ mdwc->otg_xceiv->otg,
+ DWC3_EVENT_XCEIV_STATE);
+ }
+ }
+ if (!init)
+ init = true;
+ mdwc->vbus_active = val->intval;
+ break;
+ case POWER_SUPPLY_PROP_ONLINE:
+ mdwc->online = val->intval;
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_MAX:
+ mdwc->current_max = val->intval;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ power_supply_changed(&mdwc->usb_psy);
+ return 0;
+}
+
+static char *dwc3_msm_pm_power_supplied_to[] = {
+ "battery",
+};
+
+static enum power_supply_property dwc3_msm_pm_power_props_usb[] = {
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_ONLINE,
+ POWER_SUPPLY_PROP_CURRENT_MAX,
+ POWER_SUPPLY_PROP_SCOPE,
+};
+
static int __devinit dwc3_msm_probe(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;
@@ -1426,6 +1603,20 @@
INIT_DELAYED_WORK(&msm->chg_work, dwc3_chg_detect_work);
INIT_DELAYED_WORK(&msm->resume_work, dwc3_resume_work);
+ msm->xo_handle = msm_xo_get(MSM_XO_TCXO_D0, "usb");
+ if (IS_ERR(msm->xo_handle)) {
+ dev_err(&pdev->dev, "%s unable to get TCXO buffer handle\n",
+ __func__);
+ return PTR_ERR(msm->xo_handle);
+ }
+
+ ret = msm_xo_mode_vote(msm->xo_handle, MSM_XO_MODE_ON);
+ if (ret) {
+ dev_err(&pdev->dev, "%s failed to vote for TCXO buffer%d\n",
+ __func__, ret);
+ goto free_xo_handle;
+ }
+
/*
* DWC3 Core requires its CORE CLK (aka master / bus clk) to
* run at 125Mhz in SSUSB mode and >60MHZ for HSUSB mode.
@@ -1433,7 +1624,8 @@
msm->core_clk = devm_clk_get(&pdev->dev, "core_clk");
if (IS_ERR(msm->core_clk)) {
dev_err(&pdev->dev, "failed to get core_clk\n");
- return PTR_ERR(msm->core_clk);
+ ret = PTR_ERR(msm->core_clk);
+ goto free_xo_handle;
}
clk_set_rate(msm->core_clk, 125000000);
clk_prepare_enable(msm->core_clk);
@@ -1548,6 +1740,26 @@
goto free_hs_ldo_init;
}
+ msm->ext_xceiv.otg_capability = of_property_read_bool(node,
+ "qcom,dwc-usb3-msm-otg-capability");
+
+ if (!msm->ext_xceiv.otg_capability) {
+ /* DWC3 has separate IRQ line for OTG events (ID/BSV etc.) */
+ msm->hs_phy_irq = platform_get_irq_byname(pdev, "hs_phy_irq");
+ if (msm->hs_phy_irq < 0) {
+ dev_dbg(&pdev->dev, "pget_irq for hs_phy_irq failed\n");
+ msm->hs_phy_irq = 0;
+ } else {
+ ret = request_irq(msm->hs_phy_irq, msm_dwc3_irq,
+ IRQF_TRIGGER_RISING, "msm_dwc3", msm);
+ if (ret) {
+ dev_err(&pdev->dev, "irqreq HSPHYINT failed\n");
+ goto disable_hs_ldo;
+ }
+ enable_irq_wake(msm->hs_phy_irq);
+ }
+ }
+
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
if (!res) {
dev_dbg(&pdev->dev, "missing TCSR memory resource\n");
@@ -1571,7 +1783,7 @@
if (!res) {
dev_err(&pdev->dev, "missing memory base resource\n");
ret = -ENODEV;
- goto disable_hs_ldo;
+ goto free_hsphy_irq;
}
msm->base = devm_ioremap_nocache(&pdev->dev, res->start,
@@ -1579,14 +1791,14 @@
if (!msm->base) {
dev_err(&pdev->dev, "ioremap failed\n");
ret = -ENODEV;
- goto disable_hs_ldo;
+ goto free_hsphy_irq;
}
dwc3 = platform_device_alloc("dwc3", -1);
if (!dwc3) {
dev_err(&pdev->dev, "couldn't allocate dwc3 device\n");
ret = -ENODEV;
- goto disable_hs_ldo;
+ goto free_hsphy_irq;
}
dwc3->dev.parent = &pdev->dev;
@@ -1614,10 +1826,11 @@
*/
dwc3_msm_write_reg(msm->base, HS_PHY_CTRL_REG, 0x5220bb2);
usleep_range(2000, 2200);
- /* Disable (bypass) VBUS filter */
- dwc3_msm_write_reg(msm->base, QSCRATCH_GENERAL_CFG, 0x38);
+ /* Disable (bypass) VBUS and ID filters */
+ dwc3_msm_write_reg(msm->base, QSCRATCH_GENERAL_CFG, 0x78);
pm_runtime_set_active(msm->dev);
+ pm_runtime_enable(msm->dev);
if (of_property_read_u32(node, "qcom,dwc-usb3-msm-dbm-eps",
&msm->dbm_num_eps)) {
@@ -1635,17 +1848,35 @@
goto put_pdev;
}
+ msm->usb_psy.name = "usb";
+ msm->usb_psy.type = POWER_SUPPLY_TYPE_USB;
+ msm->usb_psy.supplied_to = dwc3_msm_pm_power_supplied_to;
+ msm->usb_psy.num_supplicants = ARRAY_SIZE(
+ dwc3_msm_pm_power_supplied_to);
+ msm->usb_psy.properties = dwc3_msm_pm_power_props_usb;
+ msm->usb_psy.num_properties = ARRAY_SIZE(dwc3_msm_pm_power_props_usb);
+ msm->usb_psy.get_property = dwc3_msm_power_get_property_usb;
+ msm->usb_psy.set_property = dwc3_msm_power_set_property_usb;
+
+ ret = power_supply_register(&pdev->dev, &msm->usb_psy);
+ if (ret < 0) {
+ dev_err(&pdev->dev,
+ "%s:power_supply_register usb failed\n",
+ __func__);
+ goto put_pdev;
+ }
+
ret = platform_device_add_resources(dwc3, pdev->resource,
pdev->num_resources);
if (ret) {
dev_err(&pdev->dev, "couldn't add resources to dwc3 device\n");
- goto put_pdev;
+ goto put_psupply;
}
ret = platform_device_add(dwc3);
if (ret) {
dev_err(&pdev->dev, "failed to register dwc3 device\n");
- goto put_pdev;
+ goto put_psupply;
}
msm->bus_scale_table = msm_bus_cl_get_pdata(pdev);
@@ -1665,8 +1896,9 @@
usleep_range(1000, 1200);
dwc3_msm_dbm_soft_reset(0);
- dwc3_msm_event_buffer_config(dwc3_readl(msm->base, DWC3_GEVNTADRLO(0)),
- dwc3_readl(msm->base, DWC3_GEVNTSIZ(0)));
+ dwc3_msm_event_buffer_config(dwc3_msm_read_reg(msm->base,
+ DWC3_GEVNTADRLO(0)),
+ dwc3_msm_read_reg(msm->base, DWC3_GEVNTSIZ(0)));
msm->otg_xceiv = usb_get_transceiver();
if (msm->otg_xceiv) {
@@ -1697,8 +1929,13 @@
put_xcvr:
usb_put_transceiver(msm->otg_xceiv);
platform_device_del(dwc3);
+put_psupply:
+ power_supply_unregister(&msm->usb_psy);
put_pdev:
platform_device_put(dwc3);
+free_hsphy_irq:
+ if (msm->hs_phy_irq)
+ free_irq(msm->hs_phy_irq, msm);
disable_hs_ldo:
dwc3_hsusb_ldo_enable(0);
free_hs_ldo_init:
@@ -1725,6 +1962,8 @@
clk_disable_unprepare(msm->iface_clk);
disable_core_clk:
clk_disable_unprepare(msm->core_clk);
+free_xo_handle:
+ msm_xo_put(msm->xo_handle);
return ret;
}
@@ -1756,6 +1995,7 @@
clk_disable_unprepare(msm->sleep_clk);
clk_disable_unprepare(msm->hsphy_sleep_clk);
clk_disable_unprepare(msm->ref_clk);
+ msm_xo_put(msm->xo_handle);
return 0;
}
@@ -1792,9 +2032,14 @@
pm_runtime_enable(dev);
/* Let OTG know about resume event and update pm_count */
- if (mdwc->otg_xceiv)
+ if (mdwc->otg_xceiv) {
mdwc->ext_xceiv.notify_ext_events(mdwc->otg_xceiv->otg,
DWC3_EVENT_PHY_RESUME);
+ if (mdwc->ext_xceiv.otg_capability)
+ mdwc->ext_xceiv.notify_ext_events(
+ mdwc->otg_xceiv->otg,
+ DWC3_EVENT_XCEIV_STATE);
+ }
}
return ret;
@@ -1849,7 +2094,7 @@
},
};
-MODULE_LICENSE("GPLV2");
+MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("DesignWare USB3 MSM Glue Layer");
static int __devinit dwc3_msm_init(void)
diff --git a/drivers/usb/dwc3/dwc3_otg.c b/drivers/usb/dwc3/dwc3_otg.c
index 4a37f03..1aa8519 100644
--- a/drivers/usb/dwc3/dwc3_otg.c
+++ b/drivers/usb/dwc3/dwc3_otg.c
@@ -16,12 +16,17 @@
#include <linux/usb.h>
#include <linux/usb/hcd.h>
#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
#include "core.h"
#include "dwc3_otg.h"
#include "io.h"
#include "xhci.h"
+static void dwc3_otg_reset(struct dwc3_otg *dotg);
+
+static void dwc3_otg_notify_host_mode(struct usb_otg *otg, int host_mode);
+static void dwc3_otg_reset(struct dwc3_otg *dotg);
/**
* dwc3_otg_set_host_regs - reset dwc3 otg registers to host operation.
@@ -29,7 +34,7 @@
* This function sets the OTG registers to work in A-Device host mode.
* This function should be called just before entering to A-Device mode.
*
- * @w: Pointer to the dwc3 otg workqueue.
+ * @w: Pointer to the dwc3 otg struct
*/
static void dwc3_otg_set_host_regs(struct dwc3_otg *dotg)
{
@@ -39,11 +44,26 @@
octl = dwc3_readl(dotg->regs, DWC3_OCTL);
octl &= ~DWC3_OTG_OCTL_PERIMODE;
dwc3_writel(dotg->regs, DWC3_OCTL, octl);
+}
- /*
- * TODO: add more OTG registers writes for HOST mode here,
- * see figure 12-10 A-device flow in dwc3 Synopsis spec
- */
+/**
+ * dwc3_otg_set_host_power - Enable port power control for host operation
+ *
+ * This function enables the OTG Port Power required to operate in Host mode
+ * This function should be called only after XHCI driver has set the port
+ * power in PORTSC register.
+ *
+ * @w: Pointer to the dwc3 otg struct
+ */
+void dwc3_otg_set_host_power(struct dwc3_otg *dotg)
+{
+ u32 osts;
+
+ osts = dwc3_readl(dotg->regs, DWC3_OSTS);
+ if (!(osts & 0x8))
+ dev_err(dotg->dwc->dev, "%s: xHCIPrtPower not set\n", __func__);
+
+ dwc3_writel(dotg->regs, DWC3_OCTL, DWC3_OTG_OCTL_PRTPWRCTL);
}
/**
@@ -80,19 +100,13 @@
static int dwc3_otg_start_host(struct usb_otg *otg, int on)
{
struct dwc3_otg *dotg = container_of(otg, struct dwc3_otg, otg);
- struct usb_hcd *hcd;
- struct xhci_hcd *xhci;
int ret = 0;
- if (!otg->host)
+ if (!dotg->dwc->xhci)
return -EINVAL;
- hcd = bus_to_hcd(otg->host);
- xhci = hcd_to_xhci(hcd);
if (on) {
- dev_dbg(otg->phy->dev, "%s: turn on host %s\n",
- __func__, otg->host->bus_name);
- dwc3_otg_set_host_regs(dotg);
+ dev_dbg(otg->phy->dev, "%s: turn on host\n", __func__);
/*
* This should be revisited for more testing post-silicon.
@@ -104,25 +118,40 @@
* remove_hcd, But we may not use standard set_host method
* anymore.
*/
- ret = hcd->driver->start(hcd);
+ dwc3_otg_set_host_regs(dotg);
+ ret = platform_device_add(dotg->dwc->xhci);
if (ret) {
dev_err(otg->phy->dev,
- "%s: failed to start primary hcd, ret=%d\n",
+ "%s: failed to add XHCI pdev ret=%d\n",
__func__, ret);
return ret;
}
- ret = xhci->shared_hcd->driver->start(xhci->shared_hcd);
+ dwc3_otg_notify_host_mode(otg, on);
+ ret = regulator_enable(dotg->vbus_otg);
if (ret) {
- dev_err(otg->phy->dev,
- "%s: failed to start secondary hcd, ret=%d\n",
- __func__, ret);
+ dev_err(otg->phy->dev, "unable to enable vbus_otg\n");
+ platform_device_del(dotg->dwc->xhci);
return ret;
}
+
+ /* re-init OTG EVTEN register as XHCI reset clears it */
+ dwc3_otg_reset(dotg);
} else {
- dev_dbg(otg->phy->dev, "%s: turn off host %s\n",
- __func__, otg->host->bus_name);
- hcd->driver->stop(hcd);
+ dev_dbg(otg->phy->dev, "%s: turn off host\n", __func__);
+
+ platform_device_del(dotg->dwc->xhci);
+
+ ret = regulator_disable(dotg->vbus_otg);
+ if (ret) {
+ dev_err(otg->phy->dev, "unable to disable vbus_otg\n");
+ return ret;
+ }
+ dwc3_otg_notify_host_mode(otg, on);
+
+ /* re-init core and OTG register as XHCI reset clears it */
+ dwc3_post_host_reset_core_init(dotg->dwc);
+ dwc3_otg_reset(dotg);
}
return 0;
@@ -141,26 +170,18 @@
struct dwc3_otg *dotg = container_of(otg, struct dwc3_otg, otg);
if (host) {
- dev_dbg(otg->phy->dev, "%s: set host %s\n",
+ dev_dbg(otg->phy->dev, "%s: set host %s, portpower\n",
__func__, host->bus_name);
otg->host = host;
-
/*
- * Only after both peripheral and host are set then check
- * OTG sm. This prevents unnecessary activation of the sm
- * in case the ID is high.
+ * Though XHCI power would be set by now, but some delay is
+ * required for XHCI controller before setting OTG Port Power
+ * TODO: Tune this delay
*/
- if (otg->gadget)
- schedule_work(&dotg->sm_work);
+ msleep(300);
+ dwc3_otg_set_host_power(dotg);
} else {
- if (otg->phy->state == OTG_STATE_A_HOST) {
- dwc3_otg_start_host(otg, 0);
- otg->host = NULL;
- otg->phy->state = OTG_STATE_UNDEFINED;
- schedule_work(&dotg->sm_work);
- } else {
- otg->host = NULL;
- }
+ otg->host = NULL;
}
return 0;
@@ -212,14 +233,7 @@
dev_dbg(otg->phy->dev, "%s: set gadget %s\n",
__func__, gadget->name);
otg->gadget = gadget;
-
- /*
- * Only after both peripheral and host are set then check
- * OTG sm. This prevents unnecessary activation of the sm
- * in case the ID is grounded.
- */
- if (otg->host)
- schedule_work(&dotg->sm_work);
+ schedule_work(&dotg->sm_work);
} else {
if (otg->phy->state == OTG_STATE_B_PERIPHERAL) {
dwc3_otg_start_peripheral(otg, 0);
@@ -281,9 +295,11 @@
static void dwc3_ext_event_notify(struct usb_otg *otg,
enum dwc3_ext_events event)
{
+ static bool init;
struct dwc3_otg *dotg = container_of(otg, struct dwc3_otg, otg);
struct dwc3_ext_xceiv *ext_xceiv = dotg->ext_xceiv;
struct usb_phy *phy = dotg->otg.phy;
+ int ret = 0;
if (event == DWC3_EVENT_PHY_RESUME) {
if (!pm_runtime_status_suspended(phy->dev)) {
@@ -291,21 +307,39 @@
} else {
dev_dbg(phy->dev, "ext PHY_RESUME event received\n");
/* ext_xceiver would have taken h/w out of LPM by now */
- pm_runtime_get(phy->dev);
+ ret = pm_runtime_get(phy->dev);
+ if (ret == -EACCES) {
+ /* pm_runtime_get may fail during system
+ resume with -EACCES error */
+ pm_runtime_disable(phy->dev);
+ pm_runtime_set_active(phy->dev);
+ pm_runtime_enable(phy->dev);
+ } else if (ret < 0) {
+ dev_warn(phy->dev, "pm_runtime_get failed!\n");
+ }
}
+ } else if (event == DWC3_EVENT_XCEIV_STATE) {
+ if (ext_xceiv->id == DWC3_ID_FLOAT)
+ set_bit(ID, &dotg->inputs);
+ else
+ clear_bit(ID, &dotg->inputs);
+
+ if (ext_xceiv->bsv) {
+ dev_dbg(phy->dev, "XCVR: BSV set\n");
+ set_bit(B_SESS_VLD, &dotg->inputs);
+ } else {
+ dev_dbg(phy->dev, "XCVR: BSV clear\n");
+ clear_bit(B_SESS_VLD, &dotg->inputs);
+ }
+
+ if (!init) {
+ init = true;
+ complete(&dotg->dwc3_xcvr_vbus_init);
+ dev_dbg(phy->dev, "XCVR: BSV init complete\n");
+ return;
+ }
+ schedule_work(&dotg->sm_work);
}
-
- if (ext_xceiv->id == DWC3_ID_FLOAT)
- set_bit(ID, &dotg->inputs);
- else
- clear_bit(ID, &dotg->inputs);
-
- if (ext_xceiv->bsv)
- set_bit(B_SESS_VLD, &dotg->inputs);
- else
- clear_bit(B_SESS_VLD, &dotg->inputs);
-
- schedule_work(&dotg->sm_work);
}
/**
@@ -326,6 +360,72 @@
return 0;
}
+static void dwc3_otg_notify_host_mode(struct usb_otg *otg, int host_mode)
+{
+ struct dwc3_otg *dotg = container_of(otg, struct dwc3_otg, otg);
+
+ if (!dotg->psy) {
+ dev_err(otg->phy->dev, "no usb power supply registered\n");
+ return;
+ }
+
+ if (host_mode)
+ power_supply_set_scope(dotg->psy, POWER_SUPPLY_SCOPE_SYSTEM);
+ else
+ power_supply_set_scope(dotg->psy, POWER_SUPPLY_SCOPE_DEVICE);
+}
+
+static int dwc3_otg_set_power(struct usb_phy *phy, unsigned mA)
+{
+ static int power_supply_type;
+ struct dwc3_otg *dotg = container_of(phy->otg, struct dwc3_otg, otg);
+
+
+ if (!dotg->psy) {
+ dev_err(phy->dev, "no usb power supply registered\n");
+ return 0;
+ }
+
+ if (dotg->charger->chg_type == DWC3_SDP_CHARGER)
+ power_supply_type = POWER_SUPPLY_TYPE_USB;
+ else if (dotg->charger->chg_type == DWC3_CDP_CHARGER)
+ power_supply_type = POWER_SUPPLY_TYPE_USB_CDP;
+ else if (dotg->charger->chg_type == DWC3_DCP_CHARGER)
+ power_supply_type = POWER_SUPPLY_TYPE_USB_DCP;
+ else
+ power_supply_type = POWER_SUPPLY_TYPE_BATTERY;
+
+ power_supply_set_supply_type(dotg->psy, power_supply_type);
+
+ if (dotg->charger->max_power == mA)
+ return 0;
+
+ dev_info(phy->dev, "Avail curr from USB = %u\n", mA);
+
+ if (dotg->charger->max_power <= 2 && mA > 2) {
+ /* Enable charging */
+ if (power_supply_set_online(dotg->psy, true))
+ goto psy_error;
+ if (power_supply_set_current_limit(dotg->psy, 1000*mA))
+ goto psy_error;
+ } else if (dotg->charger->max_power > 0 && (mA == 0 || mA == 2)) {
+ /* Disable charging */
+ if (power_supply_set_online(dotg->psy, false))
+ goto psy_error;
+ /* Set max current limit */
+ if (power_supply_set_current_limit(dotg->psy, 0))
+ goto psy_error;
+ }
+
+ power_supply_changed(dotg->psy);
+ dotg->charger->max_power = mA;
+ return 0;
+
+psy_error:
+ dev_dbg(phy->dev, "power supply error when setting property\n");
+ return -ENXIO;
+}
+
/* IRQs which OTG driver is interested in handling */
#define DWC3_OEVT_MASK (DWC3_OEVTEN_OTGCONIDSTSCHNGEVNT | \
DWC3_OEVTEN_OTGBDEVVBUSCHNGEVNT)
@@ -339,21 +439,10 @@
static irqreturn_t dwc3_otg_interrupt(int irq, void *_dotg)
{
struct dwc3_otg *dotg = (struct dwc3_otg *)_dotg;
- struct usb_phy *phy = dotg->otg.phy;
u32 osts, oevt_reg;
int ret = IRQ_NONE;
int handled_irqs = 0;
- /*
- * If PHY is in retention mode then this interrupt would not be fired.
- * ext_xcvr (parent) is responsible for bringing h/w out of LPM.
- * OTG driver just need to increment power count and can safely
- * assume that h/w is out of low power state now.
- * TODO: explicitly disable OEVTEN interrupts if ext_xceiv is present
- */
- if (pm_runtime_status_suspended(phy->dev))
- pm_runtime_get(phy->dev);
-
oevt_reg = dwc3_readl(dotg->regs, DWC3_OEVT);
if (!(oevt_reg & DWC3_OEVT_MASK))
@@ -403,23 +492,32 @@
{
u32 osts = dwc3_readl(dotg->regs, DWC3_OSTS);
struct usb_phy *phy = dotg->otg.phy;
-
- /*
- * TODO: If using external notifications then wait here till initial
- * state is reported
- */
+ struct dwc3_ext_xceiv *ext_xceiv;
+ int ret;
dev_dbg(phy->dev, "Initialize OTG inputs, osts: 0x%x\n", osts);
- if (osts & DWC3_OTG_OSTS_CONIDSTS)
- set_bit(ID, &dotg->inputs);
- else
- clear_bit(ID, &dotg->inputs);
+ /*
+ * VBUS initial state is reported after PMIC
+ * driver initialization. Wait for it.
+ */
+ ret = wait_for_completion_timeout(&dotg->dwc3_xcvr_vbus_init, HZ * 5);
+ if (!ret)
+ dev_err(phy->dev, "%s: completion timeout\n", __func__);
- if (osts & DWC3_OTG_OSTS_BSESVALID)
- set_bit(B_SESS_VLD, &dotg->inputs);
- else
- clear_bit(B_SESS_VLD, &dotg->inputs);
+ ext_xceiv = dotg->ext_xceiv;
+ dwc3_otg_reset(dotg);
+ if (ext_xceiv && !ext_xceiv->otg_capability) {
+ if (osts & DWC3_OTG_OSTS_CONIDSTS)
+ set_bit(ID, &dotg->inputs);
+ else
+ clear_bit(ID, &dotg->inputs);
+
+ if (osts & DWC3_OTG_OSTS_BSESVALID)
+ set_bit(B_SESS_VLD, &dotg->inputs);
+ else
+ clear_bit(B_SESS_VLD, &dotg->inputs);
+ }
}
/**
@@ -444,8 +542,16 @@
switch (phy->state) {
case OTG_STATE_UNDEFINED:
dwc3_otg_init_sm(dotg);
+ if (!dotg->psy) {
+ dotg->psy = power_supply_get_by_name("usb");
+
+ if (!dotg->psy)
+ dev_err(phy->dev,
+ "couldn't get usb power supply\n");
+ }
+
/* Switch to A or B-Device according to ID / BSV */
- if (!test_bit(ID, &dotg->inputs) && phy->otg->host) {
+ if (!test_bit(ID, &dotg->inputs)) {
dev_dbg(phy->dev, "!id\n");
phy->state = OTG_STATE_A_IDLE;
work = 1;
@@ -461,7 +567,7 @@
break;
case OTG_STATE_B_IDLE:
- if (!test_bit(ID, &dotg->inputs) && phy->otg->host) {
+ if (!test_bit(ID, &dotg->inputs)) {
dev_dbg(phy->dev, "!id\n");
phy->state = OTG_STATE_A_IDLE;
work = 1;
@@ -480,9 +586,13 @@
switch (charger->chg_type) {
case DWC3_DCP_CHARGER:
dev_dbg(phy->dev, "lpm, DCP charger\n");
+ dwc3_otg_set_power(phy,
+ DWC3_IDEV_CHG_MAX);
pm_runtime_put_sync(phy->dev);
break;
case DWC3_CDP_CHARGER:
+ dwc3_otg_set_power(phy,
+ DWC3_IDEV_CHG_MAX);
dwc3_otg_start_peripheral(&dotg->otg,
1);
phy->state = OTG_STATE_B_PERIPHERAL;
@@ -523,6 +633,7 @@
charger->chg_type =
DWC3_INVALID_CHARGER;
}
+ dwc3_otg_set_power(phy, 0);
dev_dbg(phy->dev, "No device, trying to suspend\n");
pm_runtime_put_sync(phy->dev);
}
@@ -588,6 +699,9 @@
*/
static void dwc3_otg_reset(struct dwc3_otg *dotg)
{
+ static int once;
+ struct dwc3_ext_xceiv *ext_xceiv = dotg->ext_xceiv;
+
/*
* OCFG[2] - OTG-Version = 1
* OCFG[1] - HNPCap = 0
@@ -604,15 +718,19 @@
* OCTL[1] - DevSetHNPEn = 0
* OCTL[0] - HstSetHNPEn = 0
*/
- dwc3_writel(dotg->regs, DWC3_OCTL, 0x40);
+ if (!once) {
+ dwc3_writel(dotg->regs, DWC3_OCTL, 0x40);
+ once++;
+ }
/* Clear all otg events (interrupts) indications */
dwc3_writel(dotg->regs, DWC3_OEVT, 0xFFFF);
/* Enable ID/BSV StsChngEn event*/
- dwc3_writel(dotg->regs, DWC3_OEVTEN,
- DWC3_OEVTEN_OTGCONIDSTSCHNGEVNT |
- DWC3_OEVTEN_OTGBDEVVBUSCHNGEVNT);
+ if (ext_xceiv && !ext_xceiv->otg_capability)
+ dwc3_writel(dotg->regs, DWC3_OEVTEN,
+ DWC3_OEVTEN_OTGCONIDSTSCHNGEVNT |
+ DWC3_OEVTEN_OTGBDEVVBUSCHNGEVNT);
}
/**
@@ -651,6 +769,13 @@
return -ENOMEM;
}
+ dotg->vbus_otg = devm_regulator_get(dwc->dev->parent, "vbus_dwc3");
+ if (IS_ERR(dotg->vbus_otg)) {
+ dev_err(dwc->dev, "Unable to get vbus_dwc3 regulator\n");
+ ret = PTR_ERR(dotg->vbus_otg);
+ goto err1;
+ }
+
/* DWC3 has separate IRQ line for OTG events (ID/BSV etc.) */
dotg->irq = platform_get_irq_byname(to_platform_device(dwc->dev),
"otg_irq");
@@ -675,8 +800,10 @@
goto err1;
}
+ dotg->dwc = dwc;
dotg->otg.phy->otg = &dotg->otg;
dotg->otg.phy->dev = dwc->dev;
+ dotg->otg.phy->set_power = dwc3_otg_set_power;
ret = usb_set_transceiver(dotg->otg.phy);
if (ret) {
@@ -686,10 +813,9 @@
goto err2;
}
- dwc3_otg_reset(dotg);
-
dotg->otg.phy->state = OTG_STATE_UNDEFINED;
+ init_completion(&dotg->dwc3_xcvr_vbus_init);
INIT_WORK(&dotg->sm_work, dwc3_otg_sm_work);
ret = request_irq(dotg->irq, dwc3_otg_interrupt, IRQF_SHARED,
diff --git a/drivers/usb/dwc3/dwc3_otg.h b/drivers/usb/dwc3/dwc3_otg.h
index b60b42a..4384888 100644
--- a/drivers/usb/dwc3/dwc3_otg.h
+++ b/drivers/usb/dwc3/dwc3_otg.h
@@ -17,9 +17,12 @@
#define __LINUX_USB_DWC3_OTG_H
#include <linux/workqueue.h>
+#include <linux/power_supply.h>
#include <linux/usb/otg.h>
+#define DWC3_IDEV_CHG_MAX 1500
+
struct dwc3_charger;
/**
@@ -32,15 +35,19 @@
* @inputs: OTG state machine inputs
*/
struct dwc3_otg {
- struct usb_otg otg;
- int irq;
- void __iomem *regs;
+ struct usb_otg otg;
+ int irq;
+ struct dwc3 *dwc;
+ void __iomem *regs;
+ struct regulator *vbus_otg;
struct work_struct sm_work;
struct dwc3_charger *charger;
struct dwc3_ext_xceiv *ext_xceiv;
#define ID 0
#define B_SESS_VLD 1
unsigned long inputs;
+ struct power_supply *psy;
+ struct completion dwc3_xcvr_vbus_init;
};
/**
@@ -62,6 +69,7 @@
struct dwc3_charger {
enum dwc3_chg_type chg_type;
+ unsigned max_power;
/* start/stop charger detection, provided by external charger module */
void (*start_detection)(struct dwc3_charger *charger, bool start);
@@ -89,6 +97,7 @@
struct dwc3_ext_xceiv {
enum dwc3_id_state id;
bool bsv;
+ bool otg_capability;
/* to notify OTG about LPM exit event, provided by OTG */
void (*notify_ext_events)(struct usb_otg *otg,
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 9b1576b..9c1ebf8 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -1455,6 +1455,17 @@
return 0;
}
+static int dwc3_gadget_vbus_draw(struct usb_gadget *g, unsigned mA)
+{
+ struct dwc3 *dwc = gadget_to_dwc(g);
+ struct dwc3_otg *dotg = dwc->dotg;
+
+ if (dotg && dotg->otg.phy)
+ return usb_phy_set_power(dotg->otg.phy, mA);
+
+ return -ENOTSUPP;
+}
+
static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
{
struct dwc3 *dwc = gadget_to_dwc(g);
@@ -1523,6 +1534,37 @@
return ret;
}
+/* Required gadget re-initialization before switching to gadget in OTG mode */
+void dwc3_gadget_restart(struct dwc3 *dwc)
+{
+ struct dwc3_ep *dep;
+ int ret = 0;
+
+ /* reinitialize physical ep0-1 */
+
+ dwc->delayed_status = false;
+
+ dep = dwc->eps[0];
+ dep->flags = 0;
+ ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false);
+ if (ret) {
+ dev_err(dwc->dev, "failed to enable %s\n", dep->name);
+ return;
+ }
+
+ dep = dwc->eps[1];
+ dep->flags = 0;
+ ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false);
+ if (ret) {
+ dev_err(dwc->dev, "failed to enable %s\n", dep->name);
+ return;
+ }
+
+ /* begin to receive SETUP packets */
+ dwc->ep0state = EP0_SETUP_PHASE;
+ dwc3_ep0_out_start(dwc);
+}
+
static int dwc3_gadget_start(struct usb_gadget *g,
struct usb_gadget_driver *driver)
{
@@ -1627,6 +1669,7 @@
.wakeup = dwc3_gadget_wakeup,
.set_selfpowered = dwc3_gadget_set_selfpowered,
.vbus_session = dwc3_gadget_vbus_session,
+ .vbus_draw = dwc3_gadget_vbus_draw,
.pullup = dwc3_gadget_pullup,
.udc_start = dwc3_gadget_start,
.udc_stop = dwc3_gadget_stop,
@@ -2052,6 +2095,7 @@
static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
{
u32 reg;
+ struct dwc3_otg *dotg = dwc->dotg;
dev_vdbg(dwc->dev, "%s\n", __func__);
@@ -2096,6 +2140,9 @@
dwc3_gadget_usb3_phy_suspend(dwc, false);
}
+ if (dotg && dotg->otg.phy)
+ usb_phy_set_power(dotg->otg.phy, 0);
+
if (dwc->gadget.speed != USB_SPEED_UNKNOWN)
dwc3_disconnect_gadget(dwc);
@@ -2303,6 +2350,13 @@
}
}
+ if (next == DWC3_LINK_STATE_U0) {
+ if (dwc->link_state == DWC3_LINK_STATE_U3)
+ dwc->gadget_driver->resume(&dwc->gadget);
+ } else if (next == DWC3_LINK_STATE_U3) {
+ dwc->gadget_driver->suspend(&dwc->gadget);
+ }
+
dwc->link_state = next;
dev_vdbg(dwc->dev, "%s link %d\n", __func__, dwc->link_state);
diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c
index 099708b..644a779 100644
--- a/drivers/usb/dwc3/host.c
+++ b/drivers/usb/dwc3/host.c
@@ -78,10 +78,13 @@
goto err1;
}
- ret = platform_device_add(xhci);
- if (ret) {
- dev_err(dwc->dev, "failed to register xHCI device\n");
- goto err1;
+ /* Add XHCI device if !OTG, otherwise OTG takes care of this */
+ if (!dwc->dotg) {
+ ret = platform_device_add(xhci);
+ if (ret) {
+ dev_err(dwc->dev, "failed to register xHCI device\n");
+ goto err1;
+ }
}
return 0;
@@ -95,5 +98,6 @@
void dwc3_host_exit(struct dwc3 *dwc)
{
- platform_device_unregister(dwc->xhci);
+ if (!dwc->dotg)
+ platform_device_unregister(dwc->xhci);
}
diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c
index d895f27..e55fed7 100644
--- a/drivers/usb/host/xhci-plat.c
+++ b/drivers/usb/host/xhci-plat.c
@@ -166,7 +166,6 @@
phy = usb_get_transceiver();
if (phy && phy->otg) {
dev_dbg(&pdev->dev, "%s otg support available\n", __func__);
- hcd->driver->stop(hcd);
ret = otg_set_host(phy->otg, &hcd->self);
if (ret) {
dev_err(&pdev->dev, "%s otg_set_host failed\n",
@@ -211,6 +210,7 @@
usb_remove_hcd(hcd);
iounmap(hcd->regs);
+ release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
usb_put_hcd(hcd);
kfree(xhci);
diff --git a/drivers/usb/otg/msm_otg.c b/drivers/usb/otg/msm_otg.c
index a4c7131..92cbe6f 100644
--- a/drivers/usb/otg/msm_otg.c
+++ b/drivers/usb/otg/msm_otg.c
@@ -844,9 +844,14 @@
motg->caps & ALLOW_LPM_ON_DEV_SUSPEND;
dcp = motg->chg_type == USB_DCP_CHARGER;
- /* charging detection in progress due to cable plug-in */
- if (test_bit(B_SESS_VLD, &motg->inputs) && !device_bus_suspend &&
- !dcp) {
+ /*
+ * Abort suspend when,
+ * 1. charging detection in progress due to cable plug-in
+ * 2. host mode activation in progress due to Micro-A cable insertion
+ */
+
+ if ((test_bit(B_SESS_VLD, &motg->inputs) && !device_bus_suspend &&
+ !dcp) || test_bit(A_BUS_REQ, &motg->inputs)) {
enable_irq(motg->irq);
return -EBUSY;
}
diff --git a/drivers/video/msm/mdp.c b/drivers/video/msm/mdp.c
index 77193e7..25da094 100644
--- a/drivers/video/msm/mdp.c
+++ b/drivers/video/msm/mdp.c
@@ -2865,7 +2865,7 @@
pdata->on = mdp4_dtv_on;
pdata->off = mdp4_dtv_off;
mfd->hw_refresh = TRUE;
- mfd->cursor_update = mdp_hw_cursor_update;
+ mfd->cursor_update = mdp_hw_cursor_sync_update;
mfd->dma_fnc = mdp4_dtv_overlay;
mfd->dma = &dma_e_data;
mfd->do_histogram = mdp_do_histogram;
diff --git a/drivers/video/msm/mdp4_util.c b/drivers/video/msm/mdp4_util.c
index 516722e..ee9ca3c 100644
--- a/drivers/video/msm/mdp4_util.c
+++ b/drivers/video/msm/mdp4_util.c
@@ -630,6 +630,7 @@
if (panel & MDP4_PANEL_ATV)
mdp4_overlay1_done_atv();
#endif
+ mdp_hw_cursor_done();
}
#if defined(CONFIG_FB_MSM_WRITEBACK_MSM_PANEL)
if (isr & INTR_OVERLAY2_DONE) {
diff --git a/drivers/video/msm/mdp_cursor.c b/drivers/video/msm/mdp_cursor.c
index f8c08e3..b5930a1 100644
--- a/drivers/video/msm/mdp_cursor.c
+++ b/drivers/video/msm/mdp_cursor.c
@@ -52,7 +52,11 @@
/* disable vsync */
spin_lock_irqsave(&mdp_spin_lock, flag);
- mdp_disable_irq(MDP_OVERLAY0_TERM);
+ if (hdmi_prim_display)
+ mdp_disable_irq(MDP_OVERLAY1_TERM);
+ else
+ mdp_disable_irq(MDP_OVERLAY0_TERM);
+
spin_unlock_irqrestore(&mdp_spin_lock, flag);
}
@@ -78,29 +82,37 @@
*
* Moving this code out of the ISR will cause the MDP to underrun!
*/
+ uint32_t base = 0;
+
+ if (hdmi_prim_display)
+ base = ((uint32_t)(MDP_BASE + 0xB0000));
+ else
+ base = ((uint32_t)(MDP_BASE + 0x90000));
+
+
spin_lock(&mdp_spin_lock);
if (sync_disabled) {
spin_unlock(&mdp_spin_lock);
return;
}
- MDP_OUTP(MDP_BASE + 0x90044, (height << 16) | width);
- MDP_OUTP(MDP_BASE + 0x90048, cursor_buf_phys);
+ MDP_OUTP(base + 0x44, (height << 16) | width);
+ MDP_OUTP(base + 0x48, cursor_buf_phys);
- MDP_OUTP(MDP_BASE + 0x90060,
+ MDP_OUTP(base + 0x60,
(transp_en << 3) | (calpha_en << 1) |
- (inp32(MDP_BASE + 0x90060) & 0x1));
+ (inp32(base + 0x60) & 0x1));
- MDP_OUTP(MDP_BASE + 0x90064, (alpha << 24));
- MDP_OUTP(MDP_BASE + 0x90068, (0xffffff & bg_color));
- MDP_OUTP(MDP_BASE + 0x9006C, (0xffffff & bg_color));
+ MDP_OUTP(base + 0x64, (alpha << 24));
+ MDP_OUTP(base + 0x68, (0xffffff & bg_color));
+ MDP_OUTP(base + 0x6C, (0xffffff & bg_color));
/* enable/disable the cursor as per the last request */
- if (cursor_enabled && !(inp32(MDP_BASE + 0x90060) & (0x1)))
- MDP_OUTP(MDP_BASE + 0x90060, inp32(MDP_BASE + 0x90060) | 0x1);
- else if (!cursor_enabled && (inp32(MDP_BASE + 0x90060) & (0x1)))
- MDP_OUTP(MDP_BASE + 0x90060,
- inp32(MDP_BASE + 0x90060) & (~0x1));
+ if (cursor_enabled && !(inp32(base + 0x60) & (0x1)))
+ MDP_OUTP(base + 0x60, inp32(base + 0x60) | 0x1);
+ else if (!cursor_enabled && (inp32(base + 0x60) & (0x1)))
+ MDP_OUTP(base + 0x60,
+ inp32(base + 0x60) & (~0x1));
/* enqueue the task to disable MDP interrupts */
queue_work(mdp_cursor_ctrl_wq, &mdp_cursor_ctrl_worker);
@@ -119,17 +131,26 @@
if (sync_disabled) {
/* cancel pending task to disable MDP interrupts */
- if (work_pending(&mdp_cursor_ctrl_worker))
+ if (work_pending(&mdp_cursor_ctrl_worker)) {
cancel_work_sync(&mdp_cursor_ctrl_worker);
- else
+ } else {
/* enable irq */
- mdp_enable_irq(MDP_OVERLAY0_TERM);
+ if (hdmi_prim_display)
+ mdp_enable_irq(MDP_OVERLAY1_TERM);
+ else
+ mdp_enable_irq(MDP_OVERLAY0_TERM);
+ }
sync_disabled = 0;
/* enable vsync intr */
- outp32(MDP_INTR_CLEAR, INTR_OVERLAY0_DONE);
- mdp_intr_mask |= INTR_OVERLAY0_DONE;
+ if (hdmi_prim_display) {
+ outp32(MDP_INTR_CLEAR, INTR_OVERLAY1_DONE);
+ mdp_intr_mask |= INTR_OVERLAY1_DONE;
+ } else {
+ outp32(MDP_INTR_CLEAR, INTR_OVERLAY0_DONE);
+ mdp_intr_mask |= INTR_OVERLAY0_DONE;
+ }
outp32(MDP_INTR_ENABLE, mdp_intr_mask);
}
}
@@ -140,14 +161,20 @@
struct fb_image *img = &cursor->image;
unsigned long flag;
int sync_needed = 0, ret = 0;
+ uint32_t base = 0;
if ((img->width > MDP_CURSOR_WIDTH) ||
(img->height > MDP_CURSOR_HEIGHT) ||
(img->depth != 32))
return -EINVAL;
+ if (hdmi_prim_display)
+ base = ((uint32_t)(MDP_BASE + 0xB0000));
+ else
+ base = ((uint32_t)(MDP_BASE + 0x90000));
+
if (cursor->set & FB_CUR_SETPOS)
- MDP_OUTP(MDP_BASE + 0x9004c, (img->dy << 16) | img->dx);
+ MDP_OUTP(base + 0x4c, (img->dy << 16) | img->dx);
if (cursor->set & FB_CUR_SETIMAGE) {
ret = copy_from_user(mfd->cursor_buf, img->data,
diff --git a/drivers/video/msm/mdss/mdss_edp.c b/drivers/video/msm/mdss/mdss_edp.c
index 9460d71..b35be75 100644
--- a/drivers/video/msm/mdss/mdss_edp.c
+++ b/drivers/video/msm/mdss/mdss_edp.c
@@ -22,6 +22,8 @@
#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include <linux/err.h>
+#include <linux/regulator/consumer.h>
+#include <linux/pwm.h>
#include <asm/system.h>
#include <asm/mach-types.h>
@@ -37,15 +39,14 @@
#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 int mdss_edp_pwm_config(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);
@@ -134,7 +135,7 @@
}
/*
- * Enables the gpio that supply power to the panel
+ * Enables the gpio that supply power to the panel and enable the backlight
*/
static int mdss_edp_gpio_panel_en(struct mdss_edp_drv_pdata *edp_drv)
{
@@ -143,7 +144,8 @@
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__);
+ pr_err("%s: gpio_panel_en=%d not specified\n", __func__,
+ edp_drv->gpio_panel_en);
goto gpio_err;
}
@@ -171,7 +173,94 @@
return -ENODEV;
}
-static void mdss_edp_config_sync(unsigned char *edp_base)
+static int mdss_edp_pwm_config(struct mdss_edp_drv_pdata *edp_drv)
+{
+ int ret = 0;
+
+ ret = of_property_read_u32(edp_drv->pdev->dev.of_node,
+ "qcom,panel-pwm-period", &edp_drv->pwm_period);
+ if (ret) {
+ pr_err("%s: panel pwm period is not specified, %d", __func__,
+ edp_drv->pwm_period);
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32(edp_drv->pdev->dev.of_node,
+ "qcom,panel-lpg-channel", &edp_drv->lpg_channel);
+ if (ret) {
+ pr_err("%s: panel lpg channel is not specified, %d", __func__,
+ edp_drv->lpg_channel);
+ return -EINVAL;
+ }
+
+ edp_drv->bl_pwm = pwm_request(edp_drv->lpg_channel, "lcd-backlight");
+ if (edp_drv->bl_pwm == NULL || IS_ERR(edp_drv->bl_pwm)) {
+ pr_err("%s: pwm request failed", __func__);
+ edp_drv->bl_pwm = NULL;
+ return -EIO;
+ }
+
+ edp_drv->gpio_panel_pwm = of_get_named_gpio(edp_drv->pdev->dev.of_node,
+ "gpio-panel-pwm", 0);
+ if (!gpio_is_valid(edp_drv->gpio_panel_pwm)) {
+ pr_err("%s: gpio_panel_pwm=%d not specified\n", __func__,
+ edp_drv->gpio_panel_pwm);
+ goto edp_free_pwm;
+ }
+
+ ret = gpio_request(edp_drv->gpio_panel_pwm, "disp_pwm");
+ if (ret) {
+ pr_err("%s: Request reset gpio_panel_pwm failed, ret=%d\n",
+ __func__, ret);
+ goto edp_free_gpio_pwm;
+ }
+
+ return 0;
+
+edp_free_gpio_pwm:
+ gpio_free(edp_drv->gpio_panel_pwm);
+edp_free_pwm:
+ pwm_free(edp_drv->bl_pwm);
+ return -ENODEV;
+}
+
+void mdss_edp_set_backlight(struct mdss_panel_data *pdata, u32 bl_level)
+{
+ int ret = 0;
+ struct mdss_edp_drv_pdata *edp_drv = NULL;
+ int bl_max;
+
+ edp_drv = container_of(pdata, struct mdss_edp_drv_pdata, panel_data);
+ if (!edp_drv) {
+ pr_err("%s: Invalid input data\n", __func__);
+ return;
+ }
+
+ bl_max = edp_drv->panel_data.panel_info.bl_max;
+ if (bl_level > bl_max)
+ bl_level = bl_max;
+
+ if (edp_drv->bl_pwm == NULL) {
+ pr_err("%s: edp_drv->bl_pwm=NULL.\n", __func__);
+ return;
+ }
+
+ ret = pwm_config(edp_drv->bl_pwm,
+ bl_level * edp_drv->pwm_period / bl_max,
+ edp_drv->pwm_period);
+ if (ret) {
+ pr_err("%s: pwm_config() failed err=%d.\n", __func__, ret);
+ return;
+ }
+
+ ret = pwm_enable(edp_drv->bl_pwm);
+ if (ret) {
+ pr_err("%s: pwm_enable() failed err=%d\n", __func__, ret);
+ return;
+ }
+}
+
+void mdss_edp_config_sync(unsigned char *edp_base)
{
int ret = 0;
@@ -251,6 +340,7 @@
return -EINVAL;
}
+ pwm_disable(edp_drv->bl_pwm);
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);
@@ -322,9 +412,12 @@
int ret;
mdss_edp_edid2pinfo(edp_drv);
+ edp_drv->panel_data.panel_info.bl_min = 1;
+ edp_drv->panel_data.panel_info.bl_max = 255;
edp_drv->panel_data.on = mdss_edp_on;
edp_drv->panel_data.off = mdss_edp_off;
+ edp_drv->panel_data.set_backlight = mdss_edp_set_backlight;
ret = mdss_register_panel(&edp_drv->panel_data);
if (ret) {
@@ -468,12 +561,19 @@
if (ret)
goto edp_clk_deinit;
+ ret = mdss_edp_pwm_config(edp_drv);
+ if (ret)
+ goto edp_free_gpio_panel_en;
+
mdss_edp_fill_edid_data(edp_drv);
mdss_edp_fill_dpcd_data(edp_drv);
mdss_edp_device_register(edp_drv);
return 0;
+
+edp_free_gpio_panel_en:
+ gpio_free(edp_drv->gpio_panel_en);
edp_clk_deinit:
mdss_edp_clk_deinit(edp_drv);
mdss_edp_regulator_off(edp_drv);
diff --git a/drivers/video/msm/mdss/mdss_edp.h b/drivers/video/msm/mdss/mdss_edp.h
index 72c061f..00ef206 100644
--- a/drivers/video/msm/mdss/mdss_edp.h
+++ b/drivers/video/msm/mdss/mdss_edp.h
@@ -92,6 +92,12 @@
/* gpios */
int gpio_panel_en;
+ int gpio_panel_pwm;
+
+ /* backlight */
+ struct pwm_device *bl_pwm;
+ int lpg_channel;
+ int pwm_period;
};
void mdss_edp_phy_sw_reset(unsigned char *edp_base);
diff --git a/drivers/video/msm/mdss/mdss_fb.c b/drivers/video/msm/mdss/mdss_fb.c
index ca5f890..b711fd9 100644
--- a/drivers/video/msm/mdss/mdss_fb.c
+++ b/drivers/video/msm/mdss/mdss_fb.c
@@ -81,6 +81,42 @@
unsigned long arg);
static int mdss_fb_mmap(struct fb_info *info, struct vm_area_struct *vma);
+void mdss_fb_no_update_notify_timer_cb(unsigned long data)
+{
+ struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)data;
+ if (!mfd)
+ pr_err("%s mfd NULL\n", __func__);
+ complete(&mfd->no_update.comp);
+}
+
+static int mdss_fb_notify_update(struct msm_fb_data_type *mfd,
+ unsigned long *argp)
+{
+ int ret, notify;
+
+ ret = copy_from_user(¬ify, argp, sizeof(int));
+ if (ret) {
+ pr_err("%s:ioctl failed\n", __func__);
+ return ret;
+ }
+
+ if (notify > NOTIFY_UPDATE_STOP)
+ return -EINVAL;
+
+ if (notify == NOTIFY_UPDATE_START) {
+ INIT_COMPLETION(mfd->update.comp);
+ ret = wait_for_completion_interruptible_timeout(
+ &mfd->update.comp, 4 * HZ);
+ } else {
+ INIT_COMPLETION(mfd->no_update.comp);
+ ret = wait_for_completion_interruptible_timeout(
+ &mfd->no_update.comp, 4 * HZ);
+ }
+ if (ret == 0)
+ ret = -ETIMEDOUT;
+ return (ret > 0) ? 0 : ret;
+}
+
#define MAX_BACKLIGHT_BRIGHTNESS 255
static int lcd_backlight_registered;
@@ -101,7 +137,9 @@
if (!bl_lvl && value)
bl_lvl = 1;
+ mutex_lock(&mfd->lock);
mdss_fb_set_backlight(mfd, bl_lvl);
+ mutex_unlock(&mfd->lock);
}
static struct led_classdev backlight_led = {
@@ -209,6 +247,8 @@
mfd->mdp_fb_page_protection = MDP_FB_PAGE_PROTECTION_WRITECOMBINE;
mfd->panel_info.frame_count = 0;
mfd->bl_level = 0;
+ mfd->bl_scale = 1024;
+ mfd->bl_min_lvl = 30;
mfd->fb_imgType = MDP_RGBA_8888;
mfd->pdev = pdev;
@@ -269,6 +309,11 @@
pr_err("msm_fb_remove: can't stop the device %d\n",
mfd->index);
+ if (mfd->no_update.timer.function)
+ del_timer(&mfd->no_update.timer);
+ complete(&mfd->no_update.comp);
+ complete(&mfd->update.comp);
+
/* remove /dev/fb* */
unregister_framebuffer(mfd->fbi);
@@ -416,9 +461,46 @@
static int unset_bl_level, bl_updated;
static int bl_level_old;
+static int mdss_bl_scale_config(struct msm_fb_data_type *mfd,
+ struct mdp_bl_scale_data *data)
+{
+ int ret = 0;
+ int curr_bl;
+ mutex_lock(&mfd->lock);
+ curr_bl = mfd->bl_level;
+ mfd->bl_scale = data->scale;
+ mfd->bl_min_lvl = data->min_lvl;
+ pr_debug("update scale = %d, min_lvl = %d\n", mfd->bl_scale,
+ mfd->bl_min_lvl);
+
+ /* update current backlight to use new scaling*/
+ mdss_fb_set_backlight(mfd, curr_bl);
+ mutex_unlock(&mfd->lock);
+ return ret;
+}
+
+static void mdss_fb_scale_bl(struct msm_fb_data_type *mfd, u32 *bl_lvl)
+{
+ u32 temp = *bl_lvl;
+ pr_debug("input = %d, scale = %d", temp, mfd->bl_scale);
+ if (temp >= mfd->bl_min_lvl) {
+ /* bl_scale is the numerator of scaling fraction (x/1024)*/
+ temp = (temp * mfd->bl_scale) / 1024;
+
+ /*if less than minimum level, use min level*/
+ if (temp < mfd->bl_min_lvl)
+ temp = mfd->bl_min_lvl;
+ }
+ pr_debug("output = %d", temp);
+
+ (*bl_lvl) = temp;
+}
+
+/* must call this function from within mfd->lock */
void mdss_fb_set_backlight(struct msm_fb_data_type *mfd, u32 bkl_lvl)
{
struct mdss_panel_data *pdata;
+ u32 temp = bkl_lvl;
if (!mfd->panel_power_on || !bl_updated) {
unset_bl_level = bkl_lvl;
@@ -430,15 +512,22 @@
pdata = dev_get_platdata(&mfd->pdev->dev);
if ((pdata) && (pdata->set_backlight)) {
- mutex_lock(&mfd->lock);
- if (bl_level_old == bkl_lvl) {
- mutex_unlock(&mfd->lock);
+ mdss_fb_scale_bl(mfd, &temp);
+ /*
+ * Even though backlight has been scaled, want to show that
+ * backlight has been set to bkl_lvl to those that read from
+ * sysfs node. Thus, need to set bl_level even if it appears
+ * the backlight has already been set to the level it is at,
+ * as well as setting bl_level to bkl_lvl even though the
+ * backlight has been set to the scaled value.
+ */
+ if (bl_level_old == temp) {
+ mfd->bl_level = bkl_lvl;
return;
}
+ pdata->set_backlight(pdata, temp);
mfd->bl_level = bkl_lvl;
- pdata->set_backlight(pdata, mfd->bl_level);
- bl_level_old = mfd->bl_level;
- mutex_unlock(&mfd->lock);
+ bl_level_old = temp;
}
}
@@ -822,6 +911,13 @@
mfd->op_enable = true;
+ mutex_init(&mfd->no_update.lock);
+ init_timer(&mfd->no_update.timer);
+ mfd->no_update.timer.function = mdss_fb_no_update_notify_timer_cb;
+ mfd->no_update.timer.data = (unsigned long)mfd;
+ init_completion(&mfd->update.comp);
+ init_completion(&mfd->no_update.comp);
+
if (mfd->lut_update) {
ret = fb_alloc_cmap(&fbi->cmap, 256, 0);
if (ret)
@@ -1124,7 +1220,8 @@
return 0;
}
-static int mdss_fb_handle_pp_ioctl(void __user *argp)
+static int mdss_fb_handle_pp_ioctl(struct msm_fb_data_type *mfd,
+ void __user *argp)
{
int ret;
struct msmfb_mdp_pp mdp_pp;
@@ -1179,6 +1276,10 @@
ret = mdss_mdp_gamut_config(&mdp_pp.data.gamut_cfg_data,
©back);
break;
+ case mdp_bl_scale_cfg:
+ ret = mdss_bl_scale_config(mfd, (struct mdp_bl_scale_data *)
+ &mdp_pp.data.bl_scale_data);
+ break;
default:
pr_err("Unsupported request to MDP_PP IOCTL.\n");
ret = -EINVAL;
@@ -1256,7 +1357,11 @@
break;
case MSMFB_MDP_PP:
- ret = mdss_fb_handle_pp_ioctl(argp);
+ ret = mdss_fb_handle_pp_ioctl(mfd, argp);
+ break;
+
+ case MSMFB_NOTIFY_UPDATE:
+ ret = mdss_fb_notify_update(mfd, argp);
break;
default:
diff --git a/drivers/video/msm/mdss/mdss_fb.h b/drivers/video/msm/mdss/mdss_fb.h
index 25c39f6..78f2b9a 100644
--- a/drivers/video/msm/mdss/mdss_fb.h
+++ b/drivers/video/msm/mdss/mdss_fb.h
@@ -41,6 +41,13 @@
int panel_power_on;
};
+struct disp_info_notify {
+ int type;
+ struct timer_list timer;
+ struct completion comp;
+ struct mutex lock;
+};
+
struct msm_fb_data_type {
u32 key;
u32 index;
@@ -83,6 +90,8 @@
unsigned long cursor_buf_iova;
u32 bl_level;
+ u32 bl_scale;
+ u32 bl_min_lvl;
struct mutex lock;
struct platform_device *pdev;
@@ -98,6 +107,8 @@
struct list_head overlay_list;
struct list_head pipes_used;
struct list_head pipes_cleanup;
+ struct disp_info_notify update;
+ struct disp_info_notify no_update;
};
int mdss_fb_get_phys_info(unsigned long *start, unsigned long *len, int fb_num);
diff --git a/drivers/video/msm/mdss/mdss_hdmi_tx.c b/drivers/video/msm/mdss/mdss_hdmi_tx.c
index 7f41221..c2d5f28 100644
--- a/drivers/video/msm/mdss/mdss_hdmi_tx.c
+++ b/drivers/video/msm/mdss/mdss_hdmi_tx.c
@@ -1173,7 +1173,7 @@
if (rc) {
DEV_ERR("%s: core hdmi_msm_enable_power failed rc = %d\n",
__func__, rc);
- goto disable_hpd_power;
+ return rc;
}
rc = hdmi_tx_enable_power(hdmi_ctrl, HDMI_TX_CEC_PM, 1);
if (rc) {
@@ -1185,8 +1185,6 @@
return rc;
disable_core_power:
hdmi_tx_enable_power(hdmi_ctrl, HDMI_TX_CORE_PM, 0);
-disable_hpd_power:
- hdmi_tx_enable_power(hdmi_ctrl, HDMI_TX_HPD_PM, 0);
return rc;
} /* hdmi_tx_core_on */
diff --git a/drivers/video/msm/mdss/mdss_mdp_overlay.c b/drivers/video/msm/mdss/mdss_mdp_overlay.c
index 452ebdc..d52df66 100644
--- a/drivers/video/msm/mdss/mdss_mdp_overlay.c
+++ b/drivers/video/msm/mdss/mdss_mdp_overlay.c
@@ -406,6 +406,15 @@
if (IS_ERR_VALUE(ret))
return ret;
+ complete(&mfd->update.comp);
+ mutex_lock(&mfd->no_update.lock);
+ if (mfd->no_update.timer.function)
+ del_timer(&(mfd->no_update.timer));
+
+ mfd->no_update.timer.expires = jiffies + (2 * HZ);
+ add_timer(&mfd->no_update.timer);
+ mutex_unlock(&mfd->no_update.lock);
+
mutex_lock(&mfd->lock);
list_for_each_entry_safe(pipe, tmp, &mfd->pipes_cleanup, cleanup_list) {
list_del(&pipe->cleanup_list);
diff --git a/drivers/video/msm/mdss/mdss_mdp_pp.c b/drivers/video/msm/mdss/mdss_mdp_pp.c
index 6e04124..e4be407 100644
--- a/drivers/video/msm/mdss/mdss_mdp_pp.c
+++ b/drivers/video/msm/mdss/mdss_mdp_pp.c
@@ -99,6 +99,7 @@
u32 hist_cnt_read;
u32 hist_cnt_sent;
u32 frame_cnt;
+ u32 is_kick_ready;
struct completion comp;
u32 data[HIST_V_SIZE];
};
@@ -365,6 +366,7 @@
struct pp_hist_col_info *hist_info;
struct pp_sts_type *pp_sts;
u32 data, tbl_idx, col_state;
+ unsigned long flag;
int i;
dspp_num = mixer->num;
/* no corresponding dspp */
@@ -377,20 +379,19 @@
/* HIST_EN & AUTO_CLEAR */
opmode |= (1 << 16) | (1 << 17);
mutex_lock(&mdss_mdp_hist_mutex);
- if (hist_info->col_state == HIST_READY)
- pp_hist_read(base + MDSS_MDP_REG_DSPP_HIST_CTL_BASE +
- 0x1C, hist_info);
- spin_lock(&mdss_hist_lock);
+ spin_lock_irqsave(&mdss_hist_lock, flag);
col_state = hist_info->col_state;
- if ((col_state == HIST_IDLE) ||
- (col_state == HIST_READY) ||
- (col_state == HIST_START)) {
+ if (hist_info->is_kick_ready &&
+ ((col_state == HIST_IDLE) ||
+ ((false == hist_info->read_request) &&
+ col_state == HIST_READY))) {
/* Kick off collection */
MDSS_MDP_REG_WRITE(base +
MDSS_MDP_REG_DSPP_HIST_CTL_BASE, 1);
hist_info->col_state = HIST_START;
}
- spin_unlock(&mdss_hist_lock);
+ hist_info->is_kick_ready = true;
+ spin_unlock_irqrestore(&mdss_hist_lock, flag);
mutex_unlock(&mdss_mdp_hist_mutex);
}
@@ -1263,6 +1264,7 @@
int i, ret = 0;
u32 disp_num, dspp_num = 0;
u32 mixer_cnt, mixer_id[MDSS_MDP_MAX_LAYERMIXER];
+ unsigned long flag;
if ((req->block < MDP_LOGICAL_BLOCK_DISP_0) ||
(req->block >= MDP_BLOCK_MAX))
@@ -1296,7 +1298,7 @@
__func__, dspp_num);
goto hist_start_exit;
}
- spin_lock(&mdss_hist_lock);
+ spin_lock_irqsave(&mdss_hist_lock, flag);
hist_info->frame_cnt = req->frame_cnt;
init_completion(&hist_info->comp);
hist_info->hist_cnt_read = 0;
@@ -1304,7 +1306,8 @@
hist_info->read_request = false;
hist_info->col_state = HIST_RESET;
hist_info->col_en = true;
- spin_unlock(&mdss_hist_lock);
+ hist_info->is_kick_ready = false;
+ spin_unlock_irqrestore(&mdss_hist_lock, flag);
mdss_pp_res->hist_col[disp_num][i] =
&mdss_pp_res->dspp_hist[dspp_num];
mdss_mdp_hist_irq_enable(3 << done_shift_bit);
@@ -1329,6 +1332,7 @@
u32 dspp_num, disp_num, ctl_base, done_bit;
struct pp_hist_col_info *hist_info;
u32 mixer_cnt, mixer_id[MDSS_MDP_MAX_LAYERMIXER];
+ unsigned long flag;
if ((block < MDP_LOGICAL_BLOCK_DISP_0) ||
(block >= MDP_BLOCK_MAX))
@@ -1362,10 +1366,11 @@
goto hist_stop_exit;
}
complete_all(&hist_info->comp);
- spin_lock(&mdss_hist_lock);
+ spin_lock_irqsave(&mdss_hist_lock, flag);
hist_info->col_en = false;
hist_info->col_state = HIST_UNKNOWN;
- spin_unlock(&mdss_hist_lock);
+ hist_info->is_kick_ready = false;
+ spin_unlock_irqrestore(&mdss_hist_lock, flag);
mdss_mdp_hist_irq_disable(done_bit);
MDSS_MDP_REG_WRITE(ctl_base, (1 << 1));/* cancel */
}
@@ -1386,6 +1391,7 @@
struct pp_hist_col_info *hist_info;
u32 dspp_num, disp_num, ctl_base;
u32 mixer_cnt, mixer_id[MDSS_MDP_MAX_LAYERMIXER];
+ unsigned long flag;
if ((hist->block < MDP_LOGICAL_BLOCK_DISP_0) ||
(hist->block >= MDP_BLOCK_MAX))
@@ -1418,55 +1424,50 @@
ret = -EINVAL;
goto hist_collect_exit;
}
- spin_lock(&mdss_hist_lock);
- if ((hist_info->col_state == HIST_READY) ||
- (hist_info->hist_cnt_read == 0)) {
- /* wait for hist done if cache has no data */
- if ((hist_info->col_state != HIST_READY) &&
- (hist_info->hist_cnt_read == 0)) {
- hist_info->read_request = true;
- spin_unlock(&mdss_hist_lock);
- timeout = HIST_WAIT_TIMEOUT *
- hist_info->frame_cnt;
- mutex_unlock(&mdss_mdp_hist_mutex);
- wait_ret = wait_for_completion_killable_timeout(
+ spin_lock_irqsave(&mdss_hist_lock, flag);
+ /* wait for hist done if cache has no data */
+ if (hist_info->col_state != HIST_READY) {
+ hist_info->read_request = true;
+ spin_unlock_irqrestore(&mdss_hist_lock, flag);
+ timeout = HIST_WAIT_TIMEOUT *
+ hist_info->frame_cnt;
+ mutex_unlock(&mdss_mdp_hist_mutex);
+ wait_ret = wait_for_completion_killable_timeout(
&(hist_info->comp), timeout);
- mutex_lock(&mdss_mdp_hist_mutex);
- hist_info->read_request = false;
- if (wait_ret == 0) {
- ret = -ETIMEDOUT;
- pr_debug("%s: bin collection timedout",
+ mutex_lock(&mdss_mdp_hist_mutex);
+ if (wait_ret == 0) {
+ ret = -ETIMEDOUT;
+ pr_debug("%s: bin collection timedout",
__func__);
- goto hist_collect_exit;
- } else if (wait_ret < 0) {
- ret = -EINTR;
- pr_debug("%s: bin collection interrupted",
+ goto hist_collect_exit;
+ } else if (wait_ret < 0) {
+ ret = -EINTR;
+ pr_debug("%s: bin collection interrupted",
__func__);
- goto hist_collect_exit;
- }
- if (hist_info->col_state != HIST_READY) {
- ret = -EBUSY;
- pr_err("%s: collection state is not ready: %d",
- __func__, hist_info->col_state);
- goto hist_collect_exit;
- }
- } else {
- spin_unlock(&mdss_hist_lock);
+ goto hist_collect_exit;
}
- if (hist_info->col_state == HIST_READY) {
- v_base = ctl_base + 0x1C;
- mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
- pp_hist_read(v_base, hist_info);
- mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
- spin_lock(&mdss_hist_lock);
- hist_info->col_state = HIST_IDLE;
- spin_unlock(&mdss_hist_lock);
+ if (hist_info->col_state != HIST_READY) {
+ ret = -ENODATA;
+ pr_debug("%s: collection state is not ready: %d",
+ __func__, hist_info->col_state);
+ goto hist_collect_exit;
}
} else {
- spin_unlock(&mdss_hist_lock);
+ spin_unlock_irqrestore(&mdss_hist_lock, flag);
}
- hist_info->hist_cnt_sent = hist_info->hist_cnt_read;
+ spin_lock_irqsave(&mdss_hist_lock, flag);
+ if (hist_info->col_state == HIST_READY) {
+ spin_unlock_irqrestore(&mdss_hist_lock, flag);
+ v_base = ctl_base + 0x1C;
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
+ pp_hist_read(v_base, hist_info);
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
+ spin_lock_irqsave(&mdss_hist_lock, flag);
+ hist_info->read_request = false;
+ hist_info->col_state = HIST_IDLE;
+ }
+ spin_unlock_irqrestore(&mdss_hist_lock, flag);
}
if (mixer_cnt > 1) {
memset(&mdss_pp_res->hist_data[disp_num][0],
@@ -1482,7 +1483,7 @@
} else {
*hist_data_addr = (u32)hist_info->data;
}
-
+ hist_info->hist_cnt_sent++;
hist_collect_exit:
mutex_unlock(&mdss_mdp_hist_mutex);
return ret;
diff --git a/include/linux/diagchar.h b/include/linux/diagchar.h
index f0e42c2..60fa04e 100644
--- a/include/linux/diagchar.h
+++ b/include/linux/diagchar.h
@@ -18,14 +18,14 @@
#define EVENT_MASKS_TYPE 4
#define PKT_TYPE 8
#define DEINIT_TYPE 16
-#define USER_SPACE_LOG_TYPE 32
+#define USER_SPACE_DATA_TYPE 32
#define DCI_DATA_TYPE 64
#define USB_MODE 1
#define MEMORY_DEVICE_MODE 2
#define NO_LOGGING_MODE 3
#define UART_MODE 4
#define SOCKET_MODE 5
-
+#define CALLBACK_MODE 6
/* different values that go in for diag_data_type */
#define DATA_TYPE_EVENT 0
#define DATA_TYPE_F3 1
diff --git a/include/linux/i2c/smb350.h b/include/linux/i2c/smb350.h
new file mode 100644
index 0000000..5bb5cec
--- /dev/null
+++ b/include/linux/i2c/smb350.h
@@ -0,0 +1,35 @@
+/* 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 __SMB350_H__
+#define __SMB350_H__
+
+#define SMB350_NAME "smb350"
+
+/**
+ * struct smb350_platform_data
+ * structure to pass board specific information to the smb137b charger driver
+ * @chg_current_ma: maximum fast charge current in mA
+ * @term_current_ma: charge termination current in mA
+ * @chg_en_n_gpio: gpio to enable or disable charging
+ * @chg_susp_n_gpio: put active low to allow chip to suspend and disable I2C
+ * @stat_gpio: STAT pin, active low, '0' when charging.
+ */
+struct smb350_platform_data {
+ int chg_en_n_gpio;
+ int chg_susp_n_gpio;
+ int chg_current_ma;
+ int term_current_ma;
+ int stat_gpio;
+};
+
+#endif
diff --git a/include/linux/mfd/pm8xxx/pm8921-charger.h b/include/linux/mfd/pm8xxx/pm8921-charger.h
index 16d4a4b..130fb54 100644
--- a/include/linux/mfd/pm8xxx/pm8921-charger.h
+++ b/include/linux/mfd/pm8xxx/pm8921-charger.h
@@ -163,6 +163,7 @@
enum pm8921_chg_hot_thr hot_thr;
int rconn_mohm;
enum pm8921_chg_led_src_config led_src_config;
+ int battery_less_hardware;
};
enum pm8921_charger_source {
diff --git a/include/linux/msm_mdp.h b/include/linux/msm_mdp.h
index 1cdc434..98050ce 100644
--- a/include/linux/msm_mdp.h
+++ b/include/linux/msm_mdp.h
@@ -399,7 +399,7 @@
uint32_t block;
uint8_t frame_cnt;
uint8_t bit_mask;
- uint8_t num_bins;
+ uint16_t num_bins;
};
/*
diff --git a/include/linux/regulator/stub-regulator.h b/include/linux/regulator/stub-regulator.h
index e7f4110..1155d82 100644
--- a/include/linux/regulator/stub-regulator.h
+++ b/include/linux/regulator/stub-regulator.h
@@ -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
@@ -31,5 +31,24 @@
int system_uA;
};
+#ifdef CONFIG_REGULATOR_STUB
+
+/**
+ * regulator_stub_init() - register platform driver for stub-regulator
+ *
+ * This initialization function should be called in systems in which driver
+ * registration ordering must be controlled precisely.
+ */
+
int __init regulator_stub_init(void);
+
+#else
+
+static inline int __init regulator_stub_init(void)
+{
+ return -ENODEV;
+}
+
+#endif /* CONFIG_REGULATOR_STUB */
+
#endif
diff --git a/include/linux/test-iosched.h b/include/linux/test-iosched.h
index 1e428c5..b52762c 100644
--- a/include/linux/test-iosched.h
+++ b/include/linux/test-iosched.h
@@ -129,6 +129,8 @@
* @check_test_result_fn: Test specific test result checking
* callback
* @get_test_case_str_fn: Test specific function to get the test name
+ * @test_duration: A jiffies value saved for timing
+ * calculations
* @data: Test specific private data
*/
struct test_info {
@@ -139,6 +141,7 @@
check_test_result_fn *check_test_result_fn;
post_test_fn *post_test_fn;
get_test_case_str_fn *get_test_case_str_fn;
+ unsigned long test_duration;
void *data;
};
diff --git a/include/linux/tspp.h b/include/linux/tspp.h
index 3f0cc81..551fbb0 100644
--- a/include/linux/tspp.h
+++ b/include/linux/tspp.h
@@ -42,6 +42,10 @@
struct tspp_select_source {
enum tspp_source source;
enum tspp_tsif_mode mode;
+ int clk_inverse;
+ int data_inverse;
+ int sync_inverse;
+ int enable_inverse;
};
struct tspp_pid {
diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h
index cc390d0..41ff312 100644
--- a/include/linux/videodev2.h
+++ b/include/linux/videodev2.h
@@ -1804,6 +1804,12 @@
V4L2_MPEG_VIDC_VIDEO_H264_AU_DELIMITER_DISABLED = 0,
V4L2_MPEG_VIDC_VIDEO_H264_AU_DELIMITER_ENABLED = 1
};
+#define V4L2_CID_MPEG_VIDC_VIDEO_SYNC_FRAME_DECODE \
+ (V4L2_CID_MPEG_MSM_VIDC_BASE + 23)
+enum v4l2_mpeg_vidc_video_sync_frame_decode {
+ V4L2_MPEG_VIDC_VIDEO_SYNC_FRAME_DECODE_DISABLE = 0,
+ V4L2_MPEG_VIDC_VIDEO_SYNC_FRAME_DECODE_ENABLE = 1
+};
/* Camera class control IDs */
#define V4L2_CID_CAMERA_CLASS_BASE (V4L2_CTRL_CLASS_CAMERA | 0x900)
#define V4L2_CID_CAMERA_CLASS (V4L2_CTRL_CLASS_CAMERA | 1)
diff --git a/include/media/msm_camera.h b/include/media/msm_camera.h
index 6658b8c..9af15e3 100644
--- a/include/media/msm_camera.h
+++ b/include/media/msm_camera.h
@@ -536,6 +536,8 @@
#define CMD_STATS_BHIST_BUF_RELEASE 58
#define CMD_VFE_PIX_SOF_COUNT_UPDATE 59
#define CMD_VFE_COUNT_PIX_SOF_ENABLE 60
+#define CMD_STATS_BE_ENABLE 61
+#define CMD_STATS_BE_BUF_RELEASE 62
#define CMD_AXI_CFG_PRIM BIT(8)
#define CMD_AXI_CFG_PRIM_ALL_CHNLS BIT(9)
@@ -599,7 +601,8 @@
#define MSM_PMEM_BAYER_GRID 20
#define MSM_PMEM_BAYER_FOCUS 21
#define MSM_PMEM_BAYER_HIST 22
-#define MSM_PMEM_MAX 23
+#define MSM_PMEM_BAYER_EXPOSURE 23
+#define MSM_PMEM_MAX 24
#define STAT_AEAW 0
#define STAT_AEC 1
@@ -611,8 +614,9 @@
#define STAT_SKIN 7
#define STAT_BG 8
#define STAT_BF 9
-#define STAT_BHIST 10
-#define STAT_MAX 11
+#define STAT_BE 10
+#define STAT_BHIST 11
+#define STAT_MAX 12
#define FRAME_PREVIEW_OUTPUT1 0
#define FRAME_PREVIEW_OUTPUT2 1
@@ -631,6 +635,7 @@
MSM_STATS_TYPE_SKIN, /* legacy based SKIN */
MSM_STATS_TYPE_BG, /* Bayer Grids */
MSM_STATS_TYPE_BF, /* Bayer Focus */
+ MSM_STATS_TYPE_BE, /* Bayer Exposure*/
MSM_STATS_TYPE_BHIST, /* Bayer Hist */
MSM_STATS_TYPE_AE_AW, /* legacy stats for vfe 2.x*/
MSM_STATS_TYPE_COMP, /* Composite stats */
@@ -807,6 +812,7 @@
struct stats_buff aec;
struct stats_buff awb;
struct stats_buff af;
+ struct stats_buff be;
struct stats_buff ihist;
struct stats_buff rs;
struct stats_buff cs;
diff --git a/include/media/msm_isp.h b/include/media/msm_isp.h
index faaa522..8b4ae19 100644
--- a/include/media/msm_isp.h
+++ b/include/media/msm_isp.h
@@ -70,6 +70,7 @@
#define MSG_ID_RDI2_UPDATE_ACK 51
#define MSG_ID_PIX0_UPDATE_ACK 52
#define MSG_ID_PREV_STOP_ACK 53
+#define MSG_ID_STATS_BE 54
/* ISP command IDs */
@@ -236,7 +237,8 @@
#define VFE_CMD_COLORXFORM_ENC_UPDATE 160
#define VFE_CMD_COLORXFORM_VIEW_UPDATE 161
#define VFE_CMD_TEST_GEN_CFG 162
-
+#define VFE_CMD_STATS_BE_START 163
+#define VFE_CMD_STATS_BE_STOP 164
struct msm_isp_cmd {
int32_t id;
uint16_t length;
diff --git a/include/media/vcap_v4l2.h b/include/media/vcap_v4l2.h
index 6d684ef..3db949c 100644
--- a/include/media/vcap_v4l2.h
+++ b/include/media/vcap_v4l2.h
@@ -110,6 +110,7 @@
uint8_t buf_num;
bool top_field;
+ bool field_dropped;
struct timeval vc_ts;
uint32_t last_ts;
diff --git a/include/sound/asound.h b/include/sound/asound.h
index a2e4ff5..7bf01b6 100644
--- a/include/sound/asound.h
+++ b/include/sound/asound.h
@@ -458,6 +458,36 @@
SNDRV_PCM_TSTAMP_TYPE_LAST = SNDRV_PCM_TSTAMP_TYPE_MONOTONIC,
};
+/* channel positions */
+enum {
+ SNDRV_CHMAP_UNKNOWN = 0,
+ SNDRV_CHMAP_FL, /* front left */
+ SNDRV_CHMAP_FC, /* front center */
+ SNDRV_CHMAP_FR, /* front right */
+ SNDRV_CHMAP_FLC, /* front left center */
+ SNDRV_CHMAP_FRC, /* front right center */
+ SNDRV_CHMAP_RL, /* rear left */
+ SNDRV_CHMAP_RC, /* rear center */
+ SNDRV_CHMAP_RR, /* rear right */
+ SNDRV_CHMAP_RLC, /* rear left center */
+ SNDRV_CHMAP_RRC, /* rear right center */
+ SNDRV_CHMAP_SL, /* side left */
+ SNDRV_CHMAP_SR, /* side right */
+ SNDRV_CHMAP_LFE, /* LFE */
+ SNDRV_CHMAP_FLW, /* front left wide */
+ SNDRV_CHMAP_FRW, /* front right wide */
+ SNDRV_CHMAP_FLH, /* front left high */
+ SNDRV_CHMAP_FCH, /* front center high */
+ SNDRV_CHMAP_FRH, /* front right high */
+ SNDRV_CHMAP_TC, /* top center */
+ SNDRV_CHMAP_NA, /* N/A, silent */
+ SNDRV_CHMAP_LAST = SNDRV_CHMAP_NA,
+};
+
+#define SNDRV_CHMAP_POSITION_MASK 0xffff
+#define SNDRV_CHMAP_PHASE_INVERSE (0x01 << 16)
+#define SNDRV_CHMAP_DRIVER_SPEC (0x02 << 16)
+
#define SNDRV_PCM_IOCTL_PVERSION _IOR('A', 0x00, int)
#define SNDRV_PCM_IOCTL_INFO _IOR('A', 0x01, struct snd_pcm_info)
#define SNDRV_PCM_IOCTL_TSTAMP _IOW('A', 0x02, int)
diff --git a/include/sound/pcm.h b/include/sound/pcm.h
index 485e1c5..028e683 100644
--- a/include/sound/pcm.h
+++ b/include/sound/pcm.h
@@ -446,6 +446,7 @@
struct snd_info_entry *proc_xrun_debug_entry;
#endif
#endif
+ struct snd_kcontrol *chmap_kctl; /* channel-mapping controls */
};
struct snd_pcm {
@@ -1080,4 +1081,51 @@
const char *snd_pcm_format_name(snd_pcm_format_t format);
+/*
+ * PCM channel-mapping control API
+ */
+/* array element of channel maps */
+struct snd_pcm_chmap_elem {
+ unsigned char channels;
+ unsigned char map[15];
+};
+
+/* channel map information; retrieved via snd_kcontrol_chip() */
+struct snd_pcm_chmap {
+ struct snd_pcm *pcm; /* assigned PCM instance */
+ int stream; /* PLAYBACK or CAPTURE */
+ struct snd_kcontrol *kctl;
+ const struct snd_pcm_chmap_elem *chmap;
+ unsigned int max_channels;
+ unsigned int channel_mask; /* optional: active channels bitmask */
+ void *private_data; /* optional: private data pointer */
+};
+
+/* get the PCM substream assigned to the given chmap info */
+static inline struct snd_pcm_substream *
+snd_pcm_chmap_substream(struct snd_pcm_chmap *info, unsigned int idx)
+{
+ struct snd_pcm_substream *s;
+ for (s = info->pcm->streams[info->stream].substream; s; s = s->next)
+ if (s->number == idx)
+ return s;
+ return NULL;
+}
+
+/* ALSA-standard channel maps (RL/RR prior to C/LFE) */
+extern const struct snd_pcm_chmap_elem snd_pcm_std_chmaps[];
+/* Other world's standard channel maps (C/LFE prior to RL/RR) */
+extern const struct snd_pcm_chmap_elem snd_pcm_alt_chmaps[];
+
+/* bit masks to be passed to snd_pcm_chmap.channel_mask field */
+#define SND_PCM_CHMAP_MASK_24 ((1U << 2) | (1U << 4))
+#define SND_PCM_CHMAP_MASK_246 (SND_PCM_CHMAP_MASK_24 | (1U << 6))
+#define SND_PCM_CHMAP_MASK_2468 (SND_PCM_CHMAP_MASK_246 | (1U << 8))
+
+int snd_pcm_add_chmap_ctls(struct snd_pcm *pcm, int stream,
+ const struct snd_pcm_chmap_elem *chmap,
+ int max_channels,
+ unsigned long private_value,
+ struct snd_pcm_chmap **info_ret);
+
#endif /* __SOUND_PCM_H */
diff --git a/include/sound/tlv.h b/include/sound/tlv.h
index 7067e2d..de36aaa 100644
--- a/include/sound/tlv.h
+++ b/include/sound/tlv.h
@@ -73,4 +73,12 @@
#define TLV_DB_GAIN_MUTE -9999999
+/*
+ * channel-mapping TLV items
+ * TLV length must match with num_channels
+ */
+#define SNDRV_CTL_TLVT_CHMAP_FIXED 0x101 /* fixed channel position */
+#define SNDRV_CTL_TLVT_CHMAP_VAR 0x102 /* channels freely swappable */
+#define SNDRV_CTL_TLVT_CHMAP_PAIRED 0x103 /* pair-wise swappable */
+
#endif /* __SOUND_TLV_H */
diff --git a/sound/core/pcm.c b/sound/core/pcm.c
index 09bf06e..d98e160 100644
--- a/sound/core/pcm.c
+++ b/sound/core/pcm.c
@@ -1195,6 +1195,10 @@
break;
}
snd_unregister_device(devtype, pcm->card, pcm->device);
+ if (pcm->streams[cidx].chmap_kctl) {
+ snd_ctl_remove(pcm->card, pcm->streams[cidx].chmap_kctl);
+ pcm->streams[cidx].chmap_kctl = NULL;
+ }
}
unlock:
mutex_unlock(®ister_mutex);
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c
index b5d5a75..e0ab899 100644
--- a/sound/core/pcm_lib.c
+++ b/sound/core/pcm_lib.c
@@ -25,6 +25,7 @@
#include <linux/export.h>
#include <sound/core.h>
#include <sound/control.h>
+#include <sound/tlv.h>
#include <sound/info.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -2289,3 +2290,216 @@
}
EXPORT_SYMBOL(snd_pcm_lib_readv);
+
+/*
+ * standard channel mapping helpers
+ */
+
+/* default channel maps for multi-channel playbacks, up to 8 channels */
+const struct snd_pcm_chmap_elem snd_pcm_std_chmaps[] = {
+ { .channels = 1,
+ .map = { SNDRV_CHMAP_FC } },
+ { .channels = 2,
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR } },
+ { .channels = 4,
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR,
+ SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } },
+ { .channels = 6,
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR,
+ SNDRV_CHMAP_RL, SNDRV_CHMAP_RR,
+ SNDRV_CHMAP_FC, SNDRV_CHMAP_LFE } },
+ { .channels = 8,
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR,
+ SNDRV_CHMAP_RL, SNDRV_CHMAP_RR,
+ SNDRV_CHMAP_FC, SNDRV_CHMAP_LFE,
+ SNDRV_CHMAP_SL, SNDRV_CHMAP_SR } },
+ { }
+};
+EXPORT_SYMBOL_GPL(snd_pcm_std_chmaps);
+
+/* alternative channel maps with CLFE <-> surround swapped for 6/8 channels */
+const struct snd_pcm_chmap_elem snd_pcm_alt_chmaps[] = {
+ { .channels = 1,
+ .map = { SNDRV_CHMAP_FC } },
+ { .channels = 2,
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR } },
+ { .channels = 4,
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR,
+ SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } },
+ { .channels = 6,
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR,
+ SNDRV_CHMAP_FC, SNDRV_CHMAP_LFE,
+ SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } },
+ { .channels = 8,
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR,
+ SNDRV_CHMAP_FC, SNDRV_CHMAP_LFE,
+ SNDRV_CHMAP_RL, SNDRV_CHMAP_RR,
+ SNDRV_CHMAP_SL, SNDRV_CHMAP_SR } },
+ { }
+};
+EXPORT_SYMBOL_GPL(snd_pcm_alt_chmaps);
+
+static bool valid_chmap_channels(const struct snd_pcm_chmap *info, int ch)
+{
+ if (ch > info->max_channels)
+ return false;
+ return !info->channel_mask || (info->channel_mask & (1U << ch));
+}
+
+static int pcm_chmap_ctl_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 0;
+ uinfo->count = info->max_channels;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = SNDRV_CHMAP_LAST;
+ return 0;
+}
+
+/* get callback for channel map ctl element
+ * stores the channel position firstly matching with the current channels
+ */
+static int pcm_chmap_ctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
+ unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+ struct snd_pcm_substream *substream;
+ const struct snd_pcm_chmap_elem *map;
+
+ if (snd_BUG_ON(!info->chmap))
+ return -EINVAL;
+ substream = snd_pcm_chmap_substream(info, idx);
+ if (!substream)
+ return -ENODEV;
+ memset(ucontrol->value.integer.value, 0,
+ sizeof(ucontrol->value.integer.value));
+ if (!substream->runtime)
+ return 0; /* no channels set */
+ for (map = info->chmap; map->channels; map++) {
+ int i;
+ if (map->channels == substream->runtime->channels &&
+ valid_chmap_channels(info, map->channels)) {
+ for (i = 0; i < map->channels; i++)
+ ucontrol->value.integer.value[i] = map->map[i];
+ return 0;
+ }
+ }
+ return -EINVAL;
+}
+
+/* tlv callback for channel map ctl element
+ * expands the pre-defined channel maps in a form of TLV
+ */
+static int pcm_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag,
+ unsigned int size, unsigned int __user *tlv)
+{
+ struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
+ const struct snd_pcm_chmap_elem *map;
+ unsigned int __user *dst;
+ int c, count = 0;
+
+ if (snd_BUG_ON(!info->chmap))
+ return -EINVAL;
+ if (size < 8)
+ return -ENOMEM;
+ if (put_user(SNDRV_CTL_TLVT_CONTAINER, tlv))
+ return -EFAULT;
+ size -= 8;
+ dst = tlv + 2;
+ for (map = info->chmap; map->channels; map++) {
+ int chs_bytes = map->channels * 4;
+ if (!valid_chmap_channels(info, map->channels))
+ continue;
+ if (size < 8)
+ return -ENOMEM;
+ if (put_user(SNDRV_CTL_TLVT_CHMAP_FIXED, dst) ||
+ put_user(chs_bytes, dst + 1))
+ return -EFAULT;
+ dst += 2;
+ size -= 8;
+ count += 8;
+ if (size < chs_bytes)
+ return -ENOMEM;
+ size -= chs_bytes;
+ count += chs_bytes;
+ for (c = 0; c < map->channels; c++) {
+ if (put_user(map->map[c], dst))
+ return -EFAULT;
+ dst++;
+ }
+ }
+ if (put_user(count, tlv + 1))
+ return -EFAULT;
+ return 0;
+}
+
+static void pcm_chmap_ctl_private_free(struct snd_kcontrol *kcontrol)
+{
+ struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
+ info->pcm->streams[info->stream].chmap_kctl = NULL;
+ kfree(info);
+}
+
+/**
+ * snd_pcm_add_chmap_ctls - create channel-mapping control elements
+ * @pcm: the assigned PCM instance
+ * @stream: stream direction
+ * @chmap: channel map elements (for query)
+ * @max_channels: the max number of channels for the stream
+ * @private_value: the value passed to each kcontrol's private_value field
+ * @info_ret: store struct snd_pcm_chmap instance if non-NULL
+ *
+ * Create channel-mapping control elements assigned to the given PCM stream(s).
+ * Returns zero if succeed, or a negative error value.
+ */
+int snd_pcm_add_chmap_ctls(struct snd_pcm *pcm, int stream,
+ const struct snd_pcm_chmap_elem *chmap,
+ int max_channels,
+ unsigned long private_value,
+ struct snd_pcm_chmap **info_ret)
+{
+ struct snd_pcm_chmap *info;
+ struct snd_kcontrol_new knew = {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+ SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK,
+ .info = pcm_chmap_ctl_info,
+ .get = pcm_chmap_ctl_get,
+ .tlv.c = pcm_chmap_ctl_tlv,
+ };
+ int err;
+
+ info = kzalloc(sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+ info->pcm = pcm;
+ info->stream = stream;
+ info->chmap = chmap;
+ info->max_channels = max_channels;
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ knew.name = "Playback Channel Map";
+ else
+ knew.name = "Capture Channel Map";
+ knew.device = pcm->device;
+ knew.count = pcm->streams[stream].substream_count;
+ knew.private_value = private_value;
+ info->kctl = snd_ctl_new1(&knew, info);
+ if (!info->kctl) {
+ kfree(info);
+ return -ENOMEM;
+ }
+ info->kctl->private_free = pcm_chmap_ctl_private_free;
+ err = snd_ctl_add(pcm->card, info->kctl);
+ if (err < 0)
+ return err;
+ pcm->streams[stream].chmap_kctl = info->kctl;
+ if (info_ret)
+ *info_ret = info;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_pcm_add_chmap_ctls);
diff --git a/sound/soc/codecs/wcd9320.c b/sound/soc/codecs/wcd9320.c
index dcaf67f..1703c37 100644
--- a/sound/soc/codecs/wcd9320.c
+++ b/sound/soc/codecs/wcd9320.c
@@ -297,6 +297,7 @@
snd_soc_update_bits(codec, TAIKO_A_BUCK_MODE_5, 0x02, 0x02);
snd_soc_update_bits(codec, TAIKO_A_BUCK_MODE_4, 0xFF, 0xFF);
snd_soc_update_bits(codec, TAIKO_A_BUCK_MODE_1, 0x04, 0x04);
+ snd_soc_update_bits(codec, TAIKO_A_BUCK_MODE_1, 0x04, 0x00);
snd_soc_update_bits(codec, TAIKO_A_BUCK_MODE_3, 0x04, 0x00);
snd_soc_update_bits(codec, TAIKO_A_BUCK_MODE_3, 0x08, 0x00);
snd_soc_update_bits(codec, TAIKO_A_BUCK_MODE_1, 0x80, 0x80);
@@ -309,12 +310,26 @@
static int taiko_codec_enable_charge_pump(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
+ struct snd_soc_codec *codec = w->codec;
+
pr_debug("%s %s %d\n", __func__, w->name, event);
switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_update_bits(codec, w->reg, 0x01, 0x01);
+ snd_soc_update_bits(codec, w->reg, 0x40, 0x00);
+ snd_soc_update_bits(codec, TAIKO_A_NCP_STATIC, 0x0f, 0x01);
+ break;
+
case SND_SOC_DAPM_POST_PMU:
usleep_range(1000, 1000);
break;
+
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_update_bits(codec, w->reg, 0x01, 0x00);
+ snd_soc_update_bits(codec, w->reg, 0x40, 0x40);
+ snd_soc_update_bits(codec, TAIKO_A_NCP_STATIC, 0x0f, 0x08);
+ break;
}
return 0;
}
@@ -1932,7 +1947,7 @@
int anc_size_remaining;
u32 *anc_ptr;
u16 reg;
- u8 mask, val, old_val;
+ u8 mask, val;
pr_debug("%s %d\n", __func__, event);
switch (event) {
@@ -1999,9 +2014,7 @@
for (i = 0; i < anc_writes_size; i++) {
TAIKO_CODEC_UNPACK_ENTRY(anc_ptr[i], reg,
mask, val);
- old_val = snd_soc_read(codec, reg);
- snd_soc_write(codec, reg, (old_val & ~mask) |
- (val & mask));
+ snd_soc_write(codec, reg, val);
}
release_firmware(fw);
@@ -2599,15 +2612,27 @@
{"LINEOUT4", NULL, "LINEOUT4 PA"},
{"SPK_OUT", NULL, "SPK PA"},
+ {"LINEOUT1 PA", NULL, "CP"},
{"LINEOUT1 PA", NULL, "LINEOUT1_PA_MIXER"},
{"LINEOUT1_PA_MIXER", NULL, "LINEOUT1 DAC"},
+
+ {"LINEOUT2 PA", NULL, "CP"},
{"LINEOUT2 PA", NULL, "LINEOUT2_PA_MIXER"},
{"LINEOUT2_PA_MIXER", NULL, "LINEOUT2 DAC"},
+
+ {"LINEOUT3 PA", NULL, "CP"},
{"LINEOUT3 PA", NULL, "LINEOUT3_PA_MIXER"},
{"LINEOUT3_PA_MIXER", NULL, "LINEOUT3 DAC"},
+
+ {"LINEOUT4 PA", NULL, "CP"},
{"LINEOUT4 PA", NULL, "LINEOUT4_PA_MIXER"},
{"LINEOUT4_PA_MIXER", NULL, "LINEOUT4 DAC"},
+ {"CP", NULL, "CLASS_H_LINEOUTS_PA"},
+ {"CLASS_H_LINEOUTS_PA", NULL, "CLASS_H_CLK"},
+
+
+
{"LINEOUT1 DAC", NULL, "RX3 MIX1"},
{"RX4 DSM MUX", "DSM_INV", "RX3 MIX1"},
@@ -3886,6 +3911,9 @@
SND_SOC_DAPM_SUPPLY("CLASS_H_HPH_R", TAIKO_A_CDC_CLSH_B1_CTL, 2, 0,
taiko_codec_enable_class_h, SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_SUPPLY("CLASS_H_LINEOUTS_PA", SND_SOC_NOPM, 0, 0,
+ taiko_codec_enable_class_h, SND_SOC_DAPM_POST_PMU),
+
SND_SOC_DAPM_SUPPLY("CP", TAIKO_A_NCP_EN, 0, 0,
taiko_codec_enable_charge_pump, SND_SOC_DAPM_PRE_PMU |
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
@@ -4369,6 +4397,20 @@
}
}
+ /* Set micbias capless mode with tail current */
+ value = (pdata->micbias.bias1_cap_mode == MICBIAS_EXT_BYP_CAP ?
+ 0x00 : 0x16);
+ snd_soc_update_bits(codec, TAIKO_A_MICB_1_CTL, 0x1E, value);
+ value = (pdata->micbias.bias2_cap_mode == MICBIAS_EXT_BYP_CAP ?
+ 0x00 : 0x16);
+ snd_soc_update_bits(codec, TAIKO_A_MICB_2_CTL, 0x1E, value);
+ value = (pdata->micbias.bias3_cap_mode == MICBIAS_EXT_BYP_CAP ?
+ 0x00 : 0x16);
+ snd_soc_update_bits(codec, TAIKO_A_MICB_3_CTL, 0x1E, value);
+ value = (pdata->micbias.bias4_cap_mode == MICBIAS_EXT_BYP_CAP ?
+ 0x00 : 0x16);
+ snd_soc_update_bits(codec, TAIKO_A_MICB_4_CTL, 0x1E, value);
+
taiko_config_ear_class_h(codec, 32);
taiko_config_hph_class_h(codec, 16);
@@ -4423,6 +4465,9 @@
TAIKO_REG_VAL(TAIKO_A_CDC_RX5_B6_CTL, 0x80),
TAIKO_REG_VAL(TAIKO_A_CDC_RX6_B6_CTL, 0x80),
TAIKO_REG_VAL(TAIKO_A_CDC_RX7_B6_CTL, 0x80),
+
+ /* TX VHIGH comparator */
+ TAIKO_REG_VAL(TAIKO_A_TX_SUP_SWITCH_CTRL_2, 0x90),
};
static const struct taiko_reg_mask_val taiko_1_0_reg_defaults[] = {
diff --git a/sound/soc/codecs/wcd9xxx-mbhc.c b/sound/soc/codecs/wcd9xxx-mbhc.c
index 3d7c0d4..653effa 100644
--- a/sound/soc/codecs/wcd9xxx-mbhc.c
+++ b/sound/soc/codecs/wcd9xxx-mbhc.c
@@ -68,7 +68,7 @@
#define FW_READ_ATTEMPTS 15
#define FW_READ_TIMEOUT 2000000
-#define BUTTON_POLLING_SUPPORTED false
+#define BUTTON_POLLING_SUPPORTED true
#define MCLK_RATE_12288KHZ 12288000
#define MCLK_RATE_9600KHZ 9600000
@@ -564,9 +564,21 @@
return;
pr_debug("%s: Setting up %s detection\n", __func__,
ins ? "insert" : "removal");
- /* Enable interrupt and insertion detection */
+ /* Disable detection to avoid glitch */
+ snd_soc_update_bits(mbhc->codec, WCD9XXX_A_MBHC_INSERT_DETECT, 1, 0);
+ /* Override mbhc power switch to avoid false IRQs */
+ snd_soc_update_bits(mbhc->codec, WCD9XXX_A_MICB_1_MBHC, 1 << 2,
+ !ins << 2);
+ snd_soc_update_bits(mbhc->codec, WCD9XXX_A_MICB_2_MBHC, 1 << 2,
+ !ins << 2);
+ snd_soc_update_bits(mbhc->codec, WCD9XXX_A_MICB_3_MBHC, 1 << 2,
+ !ins << 2);
+ snd_soc_update_bits(mbhc->codec, WCD9XXX_A_MICB_4_MBHC, 1 << 2,
+ !ins << 2);
snd_soc_write(mbhc->codec, WCD9XXX_A_MBHC_INSERT_DETECT,
- (0x69 | (ins ? (1 << 1) : 0)));
+ (0x68 | (ins ? (1 << 1) : 0)));
+ /* Re-enable detection */
+ snd_soc_update_bits(mbhc->codec, WCD9XXX_A_MBHC_INSERT_DETECT, 1, 1);
}
/* called under codec_resource_lock acquisition */
@@ -835,8 +847,6 @@
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);
diff --git a/sound/soc/msm/msm-lowlatency-pcm-q6.c b/sound/soc/msm/msm-lowlatency-pcm-q6.c
index fcfcb66..98c28aa 100644
--- a/sound/soc/msm/msm-lowlatency-pcm-q6.c
+++ b/sound/soc/msm/msm-lowlatency-pcm-q6.c
@@ -224,14 +224,14 @@
prtd->channel_map[0] = PCM_CHANNEL_FL;
} else if (prtd->channel_mode == 2) {
prtd->channel_map[0] = PCM_CHANNEL_FL;
- prtd->channel_map[0] = PCM_CHANNEL_FR;
+ prtd->channel_map[1] = PCM_CHANNEL_FR;
} else if (prtd->channel_mode == 6) {
prtd->channel_map[0] = PCM_CHANNEL_FC;
- prtd->channel_map[0] = PCM_CHANNEL_FL;
- prtd->channel_map[0] = PCM_CHANNEL_FR;
- prtd->channel_map[0] = PCM_CHANNEL_LB;
- prtd->channel_map[0] = PCM_CHANNEL_RB;
- prtd->channel_map[0] = PCM_CHANNEL_LFE;
+ prtd->channel_map[1] = PCM_CHANNEL_FL;
+ prtd->channel_map[2] = PCM_CHANNEL_FR;
+ prtd->channel_map[3] = PCM_CHANNEL_LB;
+ prtd->channel_map[4] = PCM_CHANNEL_RB;
+ prtd->channel_map[5] = PCM_CHANNEL_LFE;
} else {
pr_err("%s: ERROR.unsupported num_ch = %u\n", __func__,
prtd->channel_mode);
diff --git a/sound/soc/msm/msm-multi-ch-pcm-q6.c b/sound/soc/msm/msm-multi-ch-pcm-q6.c
index 7d04f95..bd4a521 100644
--- a/sound/soc/msm/msm-multi-ch-pcm-q6.c
+++ b/sound/soc/msm/msm-multi-ch-pcm-q6.c
@@ -276,8 +276,8 @@
if (prtd->channel_mode == 1) {
prtd->channel_map[0] = PCM_CHANNEL_FL;
} else if (prtd->channel_mode == 2) {
- prtd->channel_map[1] = PCM_CHANNEL_FL;
- prtd->channel_map[2] = PCM_CHANNEL_FR;
+ prtd->channel_map[0] = PCM_CHANNEL_FL;
+ prtd->channel_map[1] = PCM_CHANNEL_FR;
} else if (prtd->channel_mode == 6) {
prtd->channel_map[0] = PCM_CHANNEL_FC;
prtd->channel_map[1] = PCM_CHANNEL_FL;
@@ -513,16 +513,6 @@
return rc;
}
-void multi_ch_pcm_set_channel_map(char *channel_mapping)
-{
- pr_debug("%s\n", __func__);
- if (multi_ch_pcm_audio.prtd) {
- multi_ch_pcm_audio.prtd->set_channel_map = true;
- memcpy(multi_ch_pcm_audio.prtd->channel_map, channel_mapping,
- PCM_FORMAT_MAX_NUM_CHANNEL);
- }
-}
-
static int msm_pcm_playback_copy(struct snd_pcm_substream *substream, int a,
snd_pcm_uframes_t hwoff, void __user *buf, snd_pcm_uframes_t frames)
{
@@ -823,13 +813,51 @@
.mmap = msm_pcm_mmap,
};
+
+static int pcm_chmap_ctl_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int i;
+ char channel_mapping[PCM_FORMAT_MAX_NUM_CHANNEL];
+
+ pr_debug("%s", __func__);
+ for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL; i++)
+ channel_mapping[i] = (char)(ucontrol->value.integer.value[i]);
+ if (multi_ch_pcm_audio.prtd) {
+ multi_ch_pcm_audio.prtd->set_channel_map = true;
+ memcpy(multi_ch_pcm_audio.prtd->channel_map, channel_mapping,
+ PCM_FORMAT_MAX_NUM_CHANNEL);
+ }
+ return 0;
+}
+
+
static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd)
{
struct snd_card *card = rtd->card->snd_card;
- int ret = 0;
+ struct snd_pcm *pcm = rtd->pcm->streams[0].pcm;
+ struct snd_pcm_chmap *chmap_info;
+ struct snd_kcontrol *kctl;
+ char device_num[3];
+ int i, ret = 0;
if (!card->dev->coherent_dma_mask)
card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+
+ pr_debug("%s, Channel map cntrl add\n", __func__);
+ ret = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ NULL, PCM_FORMAT_MAX_NUM_CHANNEL, 0,
+ &chmap_info);
+ if (ret < 0)
+ return ret;
+ kctl = chmap_info->kctl;
+ for (i = 0; i < kctl->count; i++)
+ kctl->vd[i].access |= SNDRV_CTL_ELEM_ACCESS_WRITE;
+ snprintf(device_num, sizeof(device_num), "%d", pcm->device);
+ strlcat(kctl->id.name, device_num, sizeof(kctl->id.name));
+ pr_debug("%s, Overwriting channel map control name to: %s",
+ __func__, kctl->id.name);
+ kctl->put = pcm_chmap_ctl_put;
return ret;
}
diff --git a/sound/soc/msm/msm-pcm-routing.c b/sound/soc/msm/msm-pcm-routing.c
index 2b889b5..800bea8 100644
--- a/sound/soc/msm/msm-pcm-routing.c
+++ b/sound/soc/msm/msm-pcm-routing.c
@@ -78,7 +78,6 @@
static const DECLARE_TLV_DB_LINEAR(compressed2_rx_vol_gain, 0,
INT_RX_VOL_MAX_STEPS);
static int msm_route_ec_ref_rx;
-static char channel_mapping[PCM_FORMAT_MAX_NUM_CHANNEL];
/* Equal to Frontend after last of the MULTIMEDIA SESSIONS */
#define MAX_EQ_SESSIONS MSM_FRONTEND_DAI_CS_VOICE
@@ -864,27 +863,6 @@
return 0;
}
-static int msm_routing_get_channel_map_mixer(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- int i;
- for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL; i++)
- ucontrol->value.integer.value[i] = channel_mapping[i];
- return 0;
-}
-
-static int msm_routing_put_channel_map_mixer(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- int i;
-
- for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL; i++)
- channel_mapping[i] = (char)(ucontrol->value.integer.value[i]);
- multi_ch_pcm_set_channel_map(channel_mapping);
-
- return 0;
-}
-
static int msm_routing_set_compressed_vol_mixer(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -1938,12 +1916,6 @@
msm_routing_set_compressed2_vol_mixer, compressed2_rx_vol_gain),
};
-static const struct snd_kcontrol_new multi_ch_channel_map_mixer_controls[] = {
- SOC_SINGLE_MULTI_EXT("Playback Channel Map", SND_SOC_NOPM, 0, 8,
- 0, 8, msm_routing_get_channel_map_mixer,
- msm_routing_put_channel_map_mixer),
-};
-
static const struct snd_kcontrol_new lpa_SRS_trumedia_controls[] = {
{.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "SRS TruMedia",
@@ -2931,9 +2903,6 @@
ec_ref_rx_mixer_controls,
ARRAY_SIZE(ec_ref_rx_mixer_controls));
- snd_soc_add_platform_controls(platform,
- multi_ch_channel_map_mixer_controls,
- ARRAY_SIZE(multi_ch_channel_map_mixer_controls));
return 0;
}
diff --git a/sound/soc/msm/qdsp6v2/audio_ocmem.c b/sound/soc/msm/qdsp6v2/audio_ocmem.c
index c046b63..d38bcbb 100644
--- a/sound/soc/msm/qdsp6v2/audio_ocmem.c
+++ b/sound/soc/msm/qdsp6v2/audio_ocmem.c
@@ -31,6 +31,7 @@
#define AUDIO_OCMEM_BUF_SIZE (512 * SZ_1K)
enum {
+ OCMEM_STATE_DEFAULT = 0,
OCMEM_STATE_ALLOC = 1,
OCMEM_STATE_MAP_TRANSITION,
OCMEM_STATE_MAP_COMPL,
@@ -80,6 +81,8 @@
{
int rc = NOTIFY_DONE;
unsigned long flags;
+ struct ocmem_buf *rbuf;
+ int vwait = 0;
pr_debug("%s: event[%ld] cur state[%x]\n", __func__,
event1, atomic_read(&audio_ocmem_lcl.audio_state));
@@ -105,11 +108,21 @@
OCMEM_STATE_UNMAP_FAIL);
break;
case OCMEM_ALLOC_GROW:
- audio_ocmem_lcl.buf = data;
- pr_debug("%s: Alloc grow request received buf->addr: 0x%ld\n",
+ rbuf = data;
+ if (rbuf->len == AUDIO_OCMEM_BUF_SIZE) {
+ audio_ocmem_lcl.buf = data;
+ pr_debug("%s: Alloc grow request received buf->addr: 0x%08lx\n",
__func__,
(audio_ocmem_lcl.buf)->addr);
- atomic_set(&audio_ocmem_lcl.audio_state, OCMEM_STATE_GROW);
+ atomic_set(&audio_ocmem_lcl.audio_state,
+ OCMEM_STATE_GROW);
+ } else {
+ pr_debug("%s: Alloc grow request with size: %ld",
+ __func__,
+ rbuf->len);
+ vwait = 1;
+ }
+
break;
case OCMEM_ALLOC_SHRINK:
pr_debug("%s: Alloc shrink request received\n", __func__);
@@ -120,7 +133,7 @@
break;
}
spin_unlock_irqrestore(&audio_ocmem_lcl.audio_lock, flags);
- if (atomic_read(&audio_ocmem_lcl.audio_cond)) {
+ if (!vwait && (atomic_read(&audio_ocmem_lcl.audio_cond))) {
atomic_set(&audio_ocmem_lcl.audio_cond, 0);
wake_up(&audio_ocmem_lcl.audio_wait);
}
@@ -189,7 +202,7 @@
lp_segptr->mem_segment[i].start_address_lsw;
audio_ocmem_lcl.mlist.chunks[j].size =
lp_segptr->mem_segment[i].size;
- pr_debug("%s: ro:%d, ddr_paddr[%x], size[%x]\n", __func__,
+ pr_debug("%s: ro:%d, ddr_paddr[0x%08x], size[0x%x]\n", __func__,
audio_ocmem_lcl.mlist.chunks[j].ro,
(uint32_t)audio_ocmem_lcl.mlist.chunks[j].ddr_paddr,
(uint32_t)audio_ocmem_lcl.mlist.chunks[j].size);
@@ -204,7 +217,7 @@
atomic_set(&audio_ocmem_lcl.audio_state, OCMEM_STATE_MAP_TRANSITION);
- pr_debug("%s: buf->addr: 0x%ld, len: %ld, audio_state[0x%x]\n",
+ pr_debug("%s: buf->addr: 0x%08lx, len: %ld, audio_state[0x%x]\n",
__func__,
audio_ocmem_lcl.buf->addr,
audio_ocmem_lcl.buf->len,
@@ -214,7 +227,7 @@
ret = ocmem_map(cid, audio_ocmem_lcl.buf, &audio_ocmem_lcl.mlist);
if (ret) {
pr_err("%s: ocmem_map failed\n", __func__);
- goto fail_cmd;
+ atomic_set(&audio_ocmem_lcl.audio_state, OCMEM_STATE_MAP_FAIL);
}
pr_debug("%s: audio_cond[%d] audio_state[0x%x]\n", __func__,
@@ -285,6 +298,7 @@
break;
}
}
+ ret = 0;
fail_cmd:
pr_debug("%s: exit\n", __func__);
return ret;
@@ -301,15 +315,19 @@
int audio_ocmem_disable(int cid)
{
int ret;
+ int cur_state;
pr_debug("%s: disable\n", __func__);
- if (atomic_read(&audio_ocmem_lcl.audio_cond))
- atomic_set(&audio_ocmem_lcl.audio_cond, 0);
+ cur_state = atomic_read(&audio_ocmem_lcl.audio_state);
+ if (atomic_cmpxchg(&audio_ocmem_lcl.audio_cond, 1, 0)) {
+ atomic_set(&audio_ocmem_lcl.audio_state, OCMEM_STATE_EXIT);
+ wake_up(&audio_ocmem_lcl.audio_wait);
+ }
pr_debug("%s: audio_cond[0x%x], audio_state[0x%x]\n", __func__,
atomic_read(&audio_ocmem_lcl.audio_cond),
atomic_read(&audio_ocmem_lcl.audio_state));
- switch (atomic_read(&audio_ocmem_lcl.audio_state)) {
+ switch (cur_state) {
case OCMEM_STATE_MAP_COMPL:
atomic_set(&audio_ocmem_lcl.audio_cond, 1);
ret = ocmem_unmap(cid, audio_ocmem_lcl.buf,
@@ -326,6 +344,9 @@
wait_event_interruptible(audio_ocmem_lcl.audio_wait,
atomic_read(&audio_ocmem_lcl.audio_cond) == 0);
case OCMEM_STATE_UNMAP_COMPL:
+ case OCMEM_STATE_MAP_FAIL:
+ case OCMEM_STATE_MAP_TRANSITION:
+ case OCMEM_STATE_ALLOC:
ret = ocmem_free(OCMEM_LP_AUDIO, audio_ocmem_lcl.buf);
if (ret) {
pr_err("%s: ocmem_free failed, state[%d]\n",
@@ -333,10 +354,14 @@
atomic_read(&audio_ocmem_lcl.audio_state));
goto fail_cmd;
}
+ pr_debug("%s: state=%d", __func__,
+ atomic_read(&audio_ocmem_lcl.audio_state));
atomic_set(&audio_ocmem_lcl.audio_state, OCMEM_STATE_EXIT);
pr_debug("%s: ocmem_free success\n", __func__);
+ break;
+
default:
- pr_debug("%s: state=%d", __func__,
+ pr_debug("%s:error: state=%d", __func__,
atomic_read(&audio_ocmem_lcl.audio_state));
break;
@@ -556,6 +581,7 @@
init_waitqueue_head(&audio_ocmem_lcl.audio_wait);
atomic_set(&audio_ocmem_lcl.audio_cond, 1);
+ atomic_set(&audio_ocmem_lcl.audio_state, OCMEM_STATE_DEFAULT);
atomic_set(&audio_ocmem_lcl.audio_exit, 0);
spin_lock_init(&audio_ocmem_lcl.audio_lock);
diff --git a/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c
index d85bbbc..7ba6514 100644
--- a/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c
@@ -48,6 +48,7 @@
struct snd_msm {
struct msm_audio *prtd;
unsigned volume;
+ atomic_t audio_ocmem_req;
};
static struct snd_msm compressed_audio = {NULL, 0x2000} ;
@@ -434,8 +435,11 @@
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
prtd->pcm_irq_pos = 0;
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- audio_ocmem_process_req(AUDIO, true);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (!atomic_cmpxchg(&compressed_audio.audio_ocmem_req,
+ 0, 1))
+ audio_ocmem_process_req(AUDIO, true);
+ }
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
switch (compr->info.codec_param.codec.id) {
@@ -456,9 +460,6 @@
break;
case SNDRV_PCM_TRIGGER_STOP:
pr_debug("SNDRV_PCM_TRIGGER_STOP\n");
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- audio_ocmem_process_req(AUDIO, false);
-
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
switch (compr->info.codec_param.codec.id) {
case SND_AUDIOCODEC_AMRWB:
@@ -565,6 +566,7 @@
populate_codec_list(compr, runtime);
runtime->private_data = compr;
atomic_set(&prtd->eos, 0);
+ atomic_set(&compressed_audio.audio_ocmem_req, 0);
compressed_audio.prtd = &compr->prtd;
ret = compressed_set_volume(compressed_audio.volume);
if (ret < 0)
@@ -611,6 +613,8 @@
dir = IN;
atomic_set(&prtd->pending_buffer, 0);
+ if (atomic_cmpxchg(&compressed_audio.audio_ocmem_req, 1, 0))
+ audio_ocmem_process_req(AUDIO, false);
prtd->pcm_irq_pos = 0;
q6asm_cmd(prtd->audio_client, CMD_CLOSE);
compressed_audio.prtd = NULL;
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-lpa-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-lpa-v2.c
index 19e0464..2f0a9d7 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-lpa-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-lpa-v2.c
@@ -42,6 +42,7 @@
struct snd_msm {
struct msm_audio *prtd;
unsigned volume;
+ atomic_t audio_ocmem_req;
};
static struct snd_msm lpa_audio;
@@ -227,7 +228,8 @@
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
prtd->pcm_irq_pos = 0;
- audio_ocmem_process_req(AUDIO, true);
+ if (!atomic_cmpxchg(&lpa_audio.audio_ocmem_req, 0, 1))
+ audio_ocmem_process_req(AUDIO, true);
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
pr_debug("SNDRV_PCM_TRIGGER_START\n");
@@ -237,7 +239,6 @@
break;
case SNDRV_PCM_TRIGGER_STOP:
pr_debug("SNDRV_PCM_TRIGGER_STOP\n");
- audio_ocmem_process_req(AUDIO, false);
atomic_set(&prtd->start, 0);
atomic_set(&prtd->stop, 1);
if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
@@ -328,6 +329,7 @@
prtd->dsp_cnt = 0;
atomic_set(&prtd->pending_buffer, 1);
atomic_set(&prtd->stop, 1);
+ atomic_set(&lpa_audio.audio_ocmem_req, 0);
runtime->private_data = prtd;
lpa_audio.prtd = prtd;
lpa_set_volume(lpa_audio.volume);
@@ -387,6 +389,9 @@
dir = IN;
atomic_set(&prtd->pending_buffer, 0);
+
+ if (atomic_cmpxchg(&lpa_audio.audio_ocmem_req, 1, 0))
+ audio_ocmem_process_req(AUDIO, false);
lpa_audio.prtd = NULL;
q6asm_cmd(prtd->audio_client, CMD_CLOSE);
q6asm_audio_client_buf_free_contiguous(dir,