Merge "msm: kgsl: Add power settings to the postmortem dump"
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/arm/msm/acpuclock/acpuclock-9625.txt b/Documentation/devicetree/bindings/arm/msm/acpuclock/acpuclock-9625.txt
new file mode 100644
index 0000000..ad0a6db
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/msm/acpuclock/acpuclock-9625.txt
@@ -0,0 +1,22 @@
+* Qualcomm Application CPU clock driver
+
+acpuclock-9625 is the application cpu clock driver for MDM9625. It is used for
+cpu frequency scaling, voltage scaling and bus bandwidth scaling.
+
+Required properties:
+- compatible: "qcom,acpuclk-9625"
+- reg: offset and length of the register sets for the acpuclock controller
+- reg-names: name of the bases for the above registers. "rcg_base", "pwr_base"
+ are expected.
+- a5_cpu-supply: regulator to supply a5 cpu
+- a5_mem-supply: regulator to supply a5 l2 cache
+
+Example:
+ qcom,acpuclk@f9010000 {
+ compatible = "qcom,acpuclk-9625";
+ reg = <0xf9010008 0x10>,
+ <0xf9008004 0x4>;
+ reg-names = "rcg_base", "pwr_base";
+ a5_cpu-supply = <&pm8019_l10_corner_ao>;
+ a5_mem-supply = <&pm8019_l12_ao>;
+ };
diff --git a/Documentation/devicetree/bindings/arm/msm/msm_bus.txt b/Documentation/devicetree/bindings/arm/msm/msm_bus.txt
index 1ec3081..fb72525 100644
--- a/Documentation/devicetree/bindings/arm/msm/msm_bus.txt
+++ b/Documentation/devicetree/bindings/arm/msm/msm_bus.txt
@@ -12,23 +12,23 @@
the clients' device nodes. The clients can register with the bus driver
using the following properties:
-- qcom,msm_bus,name: String representing the client-name
-- qcom,msm_bus,num_cases: Total number of usecases
-- qcom,msm_bus,active_only: Context flag for requests in active or
+- qcom,msm-bus,name: String representing the client-name
+- qcom,msm-bus,num-cases: Total number of usecases
+- qcom,msm-bus,active-only: Context flag for requests in active or
dual (active & sleep) contex
-- qcom,msm_bus,num_paths: Total number of master-slave pairs
-- qcom,msm_bus,vectors: Arrays of unsigned integers representing:
- master-id, slave-id, arbitrated bandwidth,
- instantaneous bandwidth
+- qcom,msm-bus,num-paths: Total number of master-slave pairs
+- qcom,msm-bus,vectors-KBps: Arrays of unsigned integers representing:
+ master-id, slave-id, arbitrated bandwidth
+ in KBps, instantaneous bandwidth in KBps
Example:
- qcom,msm_bus,name = "client-name";
- qcom,msm_bus,num_cases = <3>;
- qcom,msm_bus,active_only = <0>;
- qcom,msm_bus,num_paths = <2>;
- qcom,msm_bus,vectors =
+ qcom,msm-bus,name = "client-name";
+ qcom,msm-bus,num-cases = <3>;
+ qcom,msm-bus,active-only = <0>;
+ qcom,msm-bus,num-paths = <2>;
+ qcom,msm-bus,vectors =
<22 512 0 0>, <26 512 0 0>,
- <22 512 320000 320000000>, <26 512 3200000 320000000>,
- <22 512 160000 160000000>, <26 512 1600000 160000000>;
+ <22 512 320000 3200000>, <26 512 3200000 3200000>,
+ <22 512 160000 1600000>, <26 512 1600000 1600000>;
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/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/qpnp-charger.txt b/Documentation/devicetree/bindings/power/qpnp-charger.txt
index 244e622..2103bbc 100644
--- a/Documentation/devicetree/bindings/power/qpnp-charger.txt
+++ b/Documentation/devicetree/bindings/power/qpnp-charger.txt
@@ -29,6 +29,12 @@
- qcom,chg-ibatmax-ma: Maximum battery charge current in mA
- qcom,chg-ibatterm-ma: Current at which charging is terminated in mA.
+Parent node optional properties:
+- qcom,chg-charging-disabled: Set this property to disable charging
+ by default. This can then be overriden
+ writing the the module parameter
+ "charging_disabled".
+
Sub node required structure:
- A qcom,chg node must be a child of an SPMI node that has specified
the spmi-dev-container property. Each subnode reflects
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..af841dd 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,23 @@
- 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)
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/msm-pm8019.dtsi b/arch/arm/boot/dts/msm-pm8019.dtsi
index e70eb36..2105e8a 100755
--- a/arch/arm/boot/dts/msm-pm8019.dtsi
+++ b/arch/arm/boot/dts/msm-pm8019.dtsi
@@ -152,6 +152,47 @@
qcom,pin-num = <6>;
};
};
+
+ pm8019_vadc: vadc@3100 {
+ compatible = "qcom,qpnp-vadc";
+ reg = <0x3100 0x100>;
+ interrupts = <0x0 0x31 0x0>;
+ qcom,adc-bit-resolution = <15>;
+ qcom,adc-vdd-reference = <1800>;
+
+ chan@8 {
+ label = "die_temp";
+ qcom,channel-num = <8>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "absolute";
+ qcom,scale-function = <3>;
+ qcom,hw-settle-time = <0>;
+ qcom,fast-avg-setup = <0>;
+ };
+
+ chan@9 {
+ label = "ref_625mv";
+ qcom,channel-num = <9>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "absolute";
+ qcom,scale-function = <0>;
+ qcom,hw-settle-time = <0>;
+ qcom,fast-avg-setup = <0>;
+ };
+
+ chan@10 {
+ label = "ref_1250v";
+ qcom,channel-num = <10>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "absolute";
+ qcom,scale-function = <0>;
+ qcom,hw-settle-time = <0>;
+ qcom,fast-avg-setup = <0>;
+ };
+ };
};
qcom,pm8019@1 {
diff --git a/arch/arm/boot/dts/msm8974-gpu.dtsi b/arch/arm/boot/dts/msm8974-gpu.dtsi
index 4fe0eda..403a5cc 100644
--- a/arch/arm/boot/dts/msm8974-gpu.dtsi
+++ b/arch/arm/boot/dts/msm8974-gpu.dtsi
@@ -29,12 +29,15 @@
qcom,clk-map = <0x0000006>; //KGSL_CLK_CORE | KGSL_CLK_IFACE
/* Bus Scale Settings */
- qcom,grp3d-vectors = <0 0 0 0>, <2 1 0 0>,
- <0 0 0 2000>, <2 1 0 3000>,
- <0 0 0 4000>, <2 1 0 5000>,
- <0 0 0 6400>, <2 1 0 7600>;
- qcom,grp3d-num-vectors-per-usecase = <2>;
- qcom,grp3d-num-bus-scale-usecases = <4>;
+ qcom,msm-bus,name = "grp3d";
+ qcom,msm-bus,num-cases = <4>;
+ qcom,msm-bus,active-only = <0>;
+ qcom,msm-bus,num-paths = <2>;
+ qcom,msm-bus,vectors-KBps =
+ <26 512 0 0>, <89 604 0 0>,
+ <26 512 0 2000000>, <89 604 0 3000000>,
+ <26 512 0 4000000>, <89 604 0 5000000>,
+ <26 512 0 6400000>, <89 604 0 7600000>;
/* GDSC oxili regulators */
vddcx-supply = <&gdsc_oxili_cx>;
diff --git a/arch/arm/boot/dts/msm8974-liquid.dts b/arch/arm/boot/dts/msm8974-liquid.dts
index 6ccd933..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";
@@ -161,6 +168,21 @@
};
};
};
+
+ ext_5v: regulator-smb210 {
+ compatible = "regulator-fixed";
+ regulator-name = "ext_5v";
+ gpio = <&pm8941_mpps 2 0>;
+ enable-active-high;
+ };
+};
+
+&pm8941_mvs1 {
+ parent-supply = <&ext_5v>;
+};
+
+&pm8941_mvs2 {
+ parent-supply = <&ext_5v>;
};
&pm8941_gpios {
@@ -299,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>;
};
};
@@ -308,6 +337,12 @@
};
mpp@a100 { /* MPP 2 */
+ /* ext_5v regulator enable */
+ qcom,mode = <1>; /* Digital output */
+ qcom,invert = <0>; /* Output low initially */
+ qcom,vin-sel = <2>; /* PM8941 S3 = 1.8 V */
+ qcom,src-select = <0>; /* Constant */
+ qcom,master-en = <1>; /* Enable MPP */
};
mpp@a200 { /* MPP 3 */
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 f75ebbe..b63595b 100644
--- a/arch/arm/boot/dts/msm8974-mtp.dts
+++ b/arch/arm/boot/dts/msm8974-mtp.dts
@@ -180,6 +180,8 @@
&pm8941_chg {
status = "ok";
+ qcom,chg-charging-disabled;
+
qcom,chg-chgr@1000 {
status = "ok";
};
@@ -401,3 +403,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 8d54585..3509e01 100644
--- a/arch/arm/boot/dts/msm8974.dtsi
+++ b/arch/arm/boot/dts/msm8974.dtsi
@@ -110,13 +110,13 @@
qcom,hsusb-otg-disable-reset;
qcom,hsusb-otg-pnoc-errata-fix;
- qcom,msm_bus,name = "usb2";
- qcom,msm_bus,num_cases = <2>;
- qcom,msm_bus,active_only = <0>;
- qcom,msm_bus,num_paths = <1>;
- qcom,msm_bus,vectors =
+ qcom,msm-bus,name = "usb2";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,active-only = <0>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
<87 512 0 0>,
- <87 512 60000000 960000000>;
+ <87 512 60000 960000>;
};
android_usb@fc42b0c8 {
@@ -263,7 +263,7 @@
cs-gpios = <&msmgpio 55 0>;
};
- slim@fe12f000 {
+ slim_msm: slim@fe12f000 {
cell-index = <1>;
compatible = "qcom,slim-msm";
reg = <0xfe12f000 0x35000>,
@@ -340,10 +340,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 +564,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";
@@ -639,15 +641,16 @@
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";
- qcom,msm_bus,num_cases = <2>;
- qcom,msm_bus,active_only = <0>;
- qcom,msm_bus,num_paths = <1>;
- qcom,msm_bus,vectors =
+ qcom,msm-bus,name = "usb3";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,active-only = <0>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
<61 512 0 0>,
- <61 512 240000000 960000000>;
+ <61 512 240000 960000>;
};
gdsc_oxili_gx: qcom,gdsc@fd8c4024 {
@@ -790,13 +793,13 @@
qcom,msm-ocmem-audio {
compatible = "qcom,msm-ocmem-audio";
- qcom,msm_bus,name = "audio-ocmem";
- qcom,msm_bus,num_cases = <2>;
- qcom,msm_bus,active_only = <0>;
- qcom,msm_bus,num_paths = <1>;
- qcom,msm_bus,vectors =
+ qcom,msm-bus,name = "audio-ocmem";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,active-only = <0>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
<11 604 0 0>,
- <11 604 32505856 32505856>;
+ <11 604 32506 32506>;
};
qcom,mss@fc880000 {
@@ -917,15 +920,15 @@
qcom,qseecom@fe806000 {
compatible = "qcom,qseecom";
- qcom,msm_bus,name = "qseecom-noc";
- qcom,msm_bus,num_cases = <4>;
- qcom,msm_bus,active_only = <0>;
- qcom,msm_bus,num_paths = <1>;
- qcom,msm_bus,vectors =
+ qcom,msm-bus,name = "qseecom-noc";
+ qcom,msm-bus,num-cases = <4>;
+ qcom,msm-bus,active-only = <0>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
<55 512 0 0>,
- <55 512 3936000000 393600000>,
- <55 512 3936000000 393600000>,
- <55 512 3936000000 393600000>;
+ <55 512 3936000 393600>,
+ <55 512 3936000 393600>,
+ <55 512 3936000 393600>;
};
qcom,wdt@f9017000 {
diff --git a/arch/arm/boot/dts/msm9625.dtsi b/arch/arm/boot/dts/msm9625.dtsi
index 90e1118..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 {
@@ -242,8 +241,74 @@
reg = <0xfc834000 0x7000>;
interrupts = <0 29 1>;
};
+
+ qcom,acpuclk@f9010000 {
+ compatible = "qcom,acpuclk-9625";
+ reg = <0xf9010008 0x10>,
+ <0xf9008004 0x4>;
+ reg-names = "rcg_base", "pwr_base";
+ a5_cpu-supply = <&pm8019_l10_corner_ao>;
+ a5_mem-supply = <&pm8019_l12_ao>;
+ };
};
/include/ "msm-pm8019-rpm-regulator.dtsi"
/include/ "msm-pm8019.dtsi"
/include/ "msm9625-regulator.dtsi"
+
+&pm8019_vadc {
+ chan@49 {
+ label = "batt_id_therm";
+ qcom,channel-num = <49>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <0>;
+ qcom,hw-settle-time = <0>;
+ qcom,fast-avg-setup = <0>;
+ };
+
+ chan@51 {
+ label = "pa_therm1";
+ qcom,channel-num = <51>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <2>;
+ qcom,hw-settle-time = <0>;
+ qcom,fast-avg-setup = <0>;
+ };
+
+ chan@52 {
+ label = "pa_therm2";
+ qcom,channel-num = <52>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <2>;
+ qcom,hw-settle-time = <0>;
+ qcom,fast-avg-setup = <0>;
+ };
+
+ chan@50 {
+ label = "xo_therm";
+ qcom,channel-num = <50>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <4>;
+ qcom,hw-settle-time = <0>;
+ qcom,fast-avg-setup = <0>;
+ };
+
+ chan@60 {
+ label = "xo_therm_amux";
+ qcom,channel-num = <60>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <4>;
+ qcom,hw-settle-time = <0>;
+ qcom,fast-avg-setup = <0>;
+ };
+};
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/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 4e34ebd..e1d4ca0 100644
--- a/arch/arm/configs/msm9625_defconfig
+++ b/arch/arm/configs/msm9625_defconfig
@@ -37,6 +37,9 @@
# 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
CONFIG_MSM_DIRECT_SCLK_ACCESS=y
CONFIG_MSM_WATCHDOG_V2=y
@@ -49,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
@@ -58,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
@@ -73,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
@@ -107,7 +127,9 @@
CONFIG_GPIO_SYSFS=y
CONFIG_GPIO_QPNP_PIN=y
CONFIG_GPIO_QPNP_PIN_DEBUG=y
-# CONFIG_HWMON is not set
+CONFIG_POWER_SUPPLY=y
+CONFIG_HWMON=y
+CONFIG_SENSORS_QPNP_ADC_VOLTAGE=y
CONFIG_REGULATOR=y
CONFIG_REGULATOR_QPNP=y
CONFIG_ION=y
@@ -169,3 +191,5 @@
# CONFIG_CRYPTO_HW is not set
CONFIG_CRC_CCITT=y
CONFIG_LIBCRC32C=y
+CONFIG_PANIC_TIMEOUT=5
+CONFIG_MSM_DLOAD_MODE=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/kernel/perf_event_v7.c b/arch/arm/kernel/perf_event_v7.c
index 678c55d..3163b2a 100644
--- a/arch/arm/kernel/perf_event_v7.c
+++ b/arch/arm/kernel/perf_event_v7.c
@@ -1235,6 +1235,8 @@
}
static struct arm_pmu armv7pmu = {
+ .request_pmu_irq = armpmu_generic_request_irq,
+ .free_pmu_irq = armpmu_generic_free_irq,
.handle_irq = armv7pmu_handle_irq,
.enable = armv7pmu_enable_event,
.disable = armv7pmu_disable_event,
diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig
index 10c7089..b258707 100644
--- a/arch/arm/mach-msm/Kconfig
+++ b/arch/arm/mach-msm/Kconfig
@@ -351,7 +351,6 @@
config ARCH_MSM9625
bool "MSM9625"
select ARM_GIC
- select GIC_SECURE
select MIGHT_HAVE_CACHE_L2X0
select ARCH_MSM_CORTEX_A5
select SMP
@@ -2005,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.
@@ -2024,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.
@@ -2191,7 +2190,7 @@
config MSM_DLOAD_MODE
bool "Enable download mode on crashes"
- depends on ARCH_MSM8X60 || ARCH_MSM8960 || ARCH_MSM9615 || ARCH_MSM8974
+ depends on ARCH_MSM8X60 || ARCH_MSM8960 || ARCH_MSM9615 || ARCH_MSM8974 || ARCH_MSM9625
default n
help
This makes the SoC enter download mode when it resets
diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile
index 6a62f8c..11d2d2f 100644
--- a/arch/arm/mach-msm/Makefile
+++ b/arch/arm/mach-msm/Makefile
@@ -295,7 +295,7 @@
obj-$(CONFIG_ARCH_MSM8974) += gdsc.o
obj-$(CONFIG_ARCH_MSM8974) += krait-regulator.o
obj-$(CONFIG_ARCH_MSM9625) += board-9625.o board-9625-gpiomux.o
-obj-$(CONFIG_ARCH_MSM9625) += clock-local2.o clock-pll.o clock-9625.o clock-rpm.o clock-voter.o
+obj-$(CONFIG_ARCH_MSM9625) += clock-local2.o clock-pll.o clock-9625.o clock-rpm.o clock-voter.o acpuclock-9625.o
obj-$(CONFIG_ARCH_MSM8930) += acpuclock-8930.o acpuclock-8627.o acpuclock-8930aa.o
obj-$(CONFIG_ARCH_MPQ8092) += board-8092.o board-8092-gpiomux.o
obj-$(CONFIG_ARCH_MSM8226) += board-8226.o board-8226-gpiomux.o
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-9625.c b/arch/arm/mach-msm/acpuclock-9625.c
new file mode 100644
index 0000000..7fd00e6
--- /dev/null
+++ b/arch/arm/mach-msm/acpuclock-9625.c
@@ -0,0 +1,475 @@
+/*
+ * 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/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+#include <linux/errno.h>
+#include <linux/cpufreq.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/iopoll.h>
+
+#include <mach/board.h>
+#include <mach/msm_iomap.h>
+#include <mach/msm_bus.h>
+#include <mach/msm_bus_board.h>
+#include <mach/rpm-regulator.h>
+#include <mach/clk-provider.h>
+#include <mach/rpm-regulator-smd.h>
+
+#include "acpuclock.h"
+
+#define RCG_SRC_DIV_MASK BM(7, 0)
+#define RCG_CONFIG_PGM_DATA_BIT BIT(11)
+#define RCG_CONFIG_PGM_ENA_BIT BIT(10)
+#define POLL_INTERVAL_US 1
+#define APCS_RCG_UPDATE_TIMEOUT_US 20
+#define GPLL0_TO_A5_ALWAYS_ENABLE BIT(18)
+
+#define MAX_VDD_MEM 1050000
+#define MAX_VDD_CPU 1050000
+
+/* Corner type vreg VDD values */
+#define LVL_NONE RPM_REGULATOR_CORNER_NONE
+#define LVL_LOW RPM_REGULATOR_CORNER_SVS_SOC
+#define LVL_NOM RPM_REGULATOR_CORNER_NORMAL
+#define LVL_HIGH RPM_REGULATOR_CORNER_SUPER_TURBO
+
+enum clk_src {
+ CXO,
+ PLL0,
+ ACPUPLL,
+ NUM_SRC,
+};
+
+struct src_clock {
+ struct clk *clk;
+ const char *name;
+};
+
+static struct src_clock src_clocks[NUM_SRC] = {
+ [PLL0].name = "pll0",
+ [ACPUPLL].name = "pll14",
+};
+
+struct clkctl_acpu_speed {
+ bool use_for_scaling;
+ unsigned int khz;
+ int src;
+ unsigned int src_sel;
+ unsigned int src_div;
+ unsigned int vdd_cpu;
+ unsigned int vdd_mem;
+ unsigned int bw_level;
+};
+
+struct acpuclk_drv_data {
+ struct mutex lock;
+ struct clkctl_acpu_speed *current_speed;
+ void __iomem *apcs_rcg_config;
+ void __iomem *apcs_cpu_pwr_ctl;
+ struct regulator *vdd_cpu;
+ struct regulator *vdd_mem;
+};
+
+static struct acpuclk_drv_data drv_data = {
+ .current_speed = &(struct clkctl_acpu_speed){ 0 },
+};
+
+/* Instantaneous bandwidth requests in MB/s. */
+#define BW_MBPS(_bw) \
+ { \
+ .vectors = &(struct msm_bus_vectors){ \
+ .src = MSM_BUS_MASTER_AMPSS_M0, \
+ .dst = MSM_BUS_SLAVE_EBI_CH0, \
+ .ib = (_bw) * 1000000UL, \
+ .ab = 0, \
+ }, \
+ .num_paths = 1, \
+ }
+
+static struct msm_bus_paths bw_level_tbl[] = {
+ [0] = BW_MBPS(152), /* At least 19 MHz on bus. */
+ [1] = BW_MBPS(264), /* At least 33 MHz on bus. */
+ [2] = BW_MBPS(528), /* At least 66 MHz on bus. */
+ [3] = BW_MBPS(664), /* At least 83 MHz on bus. */
+ [4] = BW_MBPS(1064), /* At least 133 MHz on bus. */
+ [5] = BW_MBPS(1328), /* At least 166 MHz on bus. */
+ [6] = BW_MBPS(2128), /* At least 266 MHz on bus. */
+ [7] = BW_MBPS(2664), /* At least 333 MHz on bus. */
+};
+
+static struct msm_bus_scale_pdata bus_client_pdata = {
+ .usecase = bw_level_tbl,
+ .num_usecases = ARRAY_SIZE(bw_level_tbl),
+ .active_only = 1,
+ .name = "acpuclock",
+};
+
+static uint32_t bus_perf_client;
+
+/* TODO:
+ * 1) Update MX voltage when they are avaiable
+ * 2) Update bus bandwidth
+ */
+static struct clkctl_acpu_speed acpu_freq_tbl[] = {
+ { 0, 19200, CXO, 0, 0, LVL_LOW, 950000, 0 },
+ { 1, 300000, PLL0, 1, 2, LVL_LOW, 950000, 4 },
+ { 1, 600000, PLL0, 1, 0, LVL_NOM, 950000, 4 },
+ { 1, 748800, ACPUPLL, 5, 0, LVL_HIGH, 1050000, 7 },
+ { 1, 998400, ACPUPLL, 5, 0, LVL_HIGH, 1050000, 7 },
+ { 0 }
+};
+
+/* Update the bus bandwidth request. */
+static void set_bus_bw(unsigned int bw)
+{
+ int ret;
+
+ if (bw >= ARRAY_SIZE(bw_level_tbl)) {
+ pr_err("invalid bandwidth request (%d)\n", bw);
+ return;
+ }
+
+ /* Update bandwidth if request has changed. This may sleep. */
+ ret = msm_bus_scale_client_update_request(bus_perf_client, bw);
+ if (ret)
+ pr_err("bandwidth request failed (%d)\n", ret);
+
+ return;
+}
+
+/* Apply any per-cpu voltage increases. */
+static int increase_vdd(unsigned int vdd_cpu, unsigned int vdd_mem)
+{
+ int rc = 0;
+
+ /* Increase vdd_mem before vdd_cpu. vdd_mem should be >= vdd_cpu. */
+ rc = regulator_set_voltage(drv_data.vdd_mem, vdd_mem, MAX_VDD_MEM);
+ if (rc) {
+ pr_err("vdd_mem increase failed (%d)\n", rc);
+ return rc;
+ }
+
+ rc = regulator_set_voltage(drv_data.vdd_cpu, vdd_cpu, MAX_VDD_CPU);
+ if (rc)
+ pr_err("vdd_cpu increase failed (%d)\n", rc);
+
+ return rc;
+}
+
+/* Apply any per-cpu voltage decreases. */
+static void decrease_vdd(unsigned int vdd_cpu, unsigned int vdd_mem)
+{
+ int ret;
+
+ /* Update CPU voltage. */
+ ret = regulator_set_voltage(drv_data.vdd_cpu, vdd_cpu, MAX_VDD_CPU);
+ if (ret) {
+ pr_err("vdd_cpu decrease failed (%d)\n", ret);
+ return;
+ }
+
+ /* Decrease vdd_mem after vdd_cpu. vdd_mem should be >= vdd_cpu. */
+ ret = regulator_set_voltage(drv_data.vdd_mem, vdd_mem, MAX_VDD_MEM);
+ if (ret)
+ pr_err("vdd_mem decrease failed (%d)\n", ret);
+}
+
+static void select_clk_source_div(struct clkctl_acpu_speed *s)
+{
+ u32 regval, rc, src_div;
+ void __iomem *apcs_rcg_config = drv_data.apcs_rcg_config;
+
+ src_div = s->src_div ? ((2 * s->src_div) - 1) : s->src_div;
+
+ regval = readl_relaxed(apcs_rcg_config);
+ regval &= ~RCG_SRC_DIV_MASK;
+ regval |= BVAL(2, 0, s->src_sel) | BVAL(7, 3, src_div);
+ writel_relaxed(regval, apcs_rcg_config);
+
+ /*
+ * Make sure writing of src and div finishes before update
+ * the configuration
+ */
+ mb();
+
+ /* Update the configruation */
+ regval = readl_relaxed(apcs_rcg_config);
+ regval |= RCG_CONFIG_PGM_DATA_BIT | RCG_CONFIG_PGM_ENA_BIT;
+ writel_relaxed(regval, apcs_rcg_config);
+
+ /* Wait for update to take effect */
+ rc = readl_poll_timeout(apcs_rcg_config, regval,
+ !(regval & RCG_CONFIG_PGM_DATA_BIT),
+ POLL_INTERVAL_US,
+ APCS_RCG_UPDATE_TIMEOUT_US);
+ if (rc)
+ pr_warn("acpu rcg didn't update its configuration\n");
+}
+
+static int set_speed(struct clkctl_acpu_speed *tgt_s)
+{
+ int rc = 0;
+ unsigned int tgt_freq_hz = tgt_s->khz * 1000;
+ struct clkctl_acpu_speed *strt_s = drv_data.current_speed;
+ struct clkctl_acpu_speed *cxo_s = &acpu_freq_tbl[0];
+ struct clk *strt = src_clocks[strt_s->src].clk;
+ struct clk *tgt = src_clocks[tgt_s->src].clk;
+
+ if (strt_s->src == ACPUPLL && tgt_s->src == ACPUPLL) {
+ /* Switch to another always on src */
+ select_clk_source_div(cxo_s);
+
+ /* Re-program acpu pll */
+ clk_disable(tgt);
+ rc = clk_set_rate(tgt, tgt_freq_hz);
+ if (rc)
+ pr_err("Failed to set ACPU PLL to %u\n", tgt_freq_hz);
+ BUG_ON(clk_enable(tgt));
+
+ /* Switch back to acpu pll */
+ select_clk_source_div(tgt_s);
+ } else if (strt_s->src != ACPUPLL && tgt_s->src == ACPUPLL) {
+ rc = clk_set_rate(tgt, tgt_freq_hz);
+ if (rc) {
+ pr_err("Failed to set ACPU PLL to %u\n", tgt_freq_hz);
+ return rc;
+ }
+
+ rc = clk_enable(tgt);
+ if (rc) {
+ pr_err("ACPU PLL enable failed\n");
+ return rc;
+ }
+
+ select_clk_source_div(tgt_s);
+
+ clk_disable(strt);
+ } else {
+ rc = clk_enable(tgt);
+ if (rc) {
+ pr_err("%s enable failed\n",
+ src_clocks[tgt_s->src].name);
+ return rc;
+ }
+
+ select_clk_source_div(tgt_s);
+
+ clk_disable(strt);
+ }
+
+ return rc;
+}
+
+static int acpuclk_9625_set_rate(int cpu, unsigned long rate,
+ enum setrate_reason reason)
+{
+ struct clkctl_acpu_speed *tgt_s, *strt_s;
+ int rc = 0;
+
+ if (reason == SETRATE_CPUFREQ)
+ mutex_lock(&drv_data.lock);
+
+ strt_s = drv_data.current_speed;
+
+ /* Return early if rate didn't change */
+ if (rate == strt_s->khz)
+ goto out;
+
+ /* Find target frequency */
+ for (tgt_s = acpu_freq_tbl; tgt_s->khz != 0; tgt_s++)
+ if (tgt_s->khz == rate)
+ break;
+ if (tgt_s->khz == 0) {
+ rc = -EINVAL;
+ goto out;
+ }
+
+ /* Increase VDD levels if needed */
+ if ((reason == SETRATE_CPUFREQ || reason == SETRATE_INIT)
+ && (tgt_s->khz > strt_s->khz)) {
+ rc = increase_vdd(tgt_s->vdd_cpu, tgt_s->vdd_mem);
+ if (rc)
+ goto out;
+ }
+
+ pr_debug("Switching from CPU rate %u KHz -> %u KHz\n",
+ strt_s->khz, tgt_s->khz);
+
+ /* Switch CPU speed. */
+ rc = set_speed(tgt_s);
+ if (rc)
+ goto out;
+
+ drv_data.current_speed = tgt_s;
+ pr_debug("CPU speed change complete\n");
+
+ /* Nothing else to do for SWFI or power-collapse. */
+ if (reason == SETRATE_SWFI || reason == SETRATE_PC)
+ goto out;
+
+ /* Update bus bandwith request */
+ set_bus_bw(tgt_s->bw_level);
+
+ /* Drop VDD levels if we can. */
+ if (tgt_s->khz < strt_s->khz)
+ decrease_vdd(tgt_s->vdd_cpu, tgt_s->vdd_mem);
+
+out:
+ if (reason == SETRATE_CPUFREQ)
+ mutex_unlock(&drv_data.lock);
+ return rc;
+}
+
+static unsigned long acpuclk_9625_get_rate(int cpu)
+{
+ return drv_data.current_speed->khz;
+}
+
+#ifdef CONFIG_CPU_FREQ_MSM
+static struct cpufreq_frequency_table freq_table[30];
+
+static void __init cpufreq_table_init(void)
+{
+ int i, freq_cnt = 0;
+
+ /* Construct the freq_table tables from acpu_freq_tbl. */
+ for (i = 0; acpu_freq_tbl[i].khz != 0
+ && freq_cnt < ARRAY_SIZE(freq_table); i++) {
+ if (!acpu_freq_tbl[i].use_for_scaling)
+ continue;
+ freq_table[freq_cnt].index = freq_cnt;
+ freq_table[freq_cnt].frequency = acpu_freq_tbl[i].khz;
+ freq_cnt++;
+ }
+ /* freq_table not big enough to store all usable freqs. */
+ BUG_ON(acpu_freq_tbl[i].khz != 0);
+
+ freq_table[freq_cnt].index = freq_cnt;
+ freq_table[freq_cnt].frequency = CPUFREQ_TABLE_END;
+
+ pr_info("CPU: %d scaling frequencies supported.\n", freq_cnt);
+
+ /* Register table with CPUFreq. */
+ cpufreq_frequency_table_get_attr(freq_table, smp_processor_id());
+}
+#else
+static void __init cpufreq_table_init(void) {}
+#endif
+
+static struct acpuclk_data acpuclk_9625_data = {
+ .set_rate = acpuclk_9625_set_rate,
+ .get_rate = acpuclk_9625_get_rate,
+ .power_collapse_khz = 19200,
+ .wait_for_irq_khz = 19200,
+};
+
+static int __init acpuclk_9625_probe(struct platform_device *pdev)
+{
+ unsigned long max_cpu_khz = 0;
+ struct resource *res;
+ int i;
+ u32 regval;
+
+ mutex_init(&drv_data.lock);
+
+ bus_perf_client = msm_bus_scale_register_client(&bus_client_pdata);
+ if (!bus_perf_client) {
+ pr_err("Unable to register bus client\n");
+ BUG();
+ }
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rcg_base");
+ if (!res)
+ return -EINVAL;
+
+ drv_data.apcs_rcg_config = ioremap(res->start, resource_size(res));
+ if (!drv_data.apcs_rcg_config)
+ return -ENOMEM;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pwr_base");
+ if (!res)
+ return -EINVAL;
+
+ drv_data.apcs_cpu_pwr_ctl = ioremap(res->start, resource_size(res));
+ if (!drv_data.apcs_cpu_pwr_ctl)
+ return -ENOMEM;
+
+ drv_data.vdd_cpu = regulator_get(&pdev->dev, "a5_cpu");
+ if (IS_ERR(drv_data.vdd_cpu)) {
+ dev_err(&pdev->dev, "regulator for %s get failed\n", "a5_cpu");
+ return PTR_ERR(drv_data.vdd_cpu);
+ }
+
+ drv_data.vdd_mem = regulator_get(&pdev->dev, "a5_mem");
+ if (IS_ERR(drv_data.vdd_mem)) {
+ dev_err(&pdev->dev, "regulator for %s get failed\n", "a5_mem");
+ return PTR_ERR(drv_data.vdd_mem);
+ }
+
+ /* Disable hardware gating of gpll0 to A5SS */
+ regval = readl_relaxed(drv_data.apcs_cpu_pwr_ctl);
+ regval |= GPLL0_TO_A5_ALWAYS_ENABLE;
+ writel_relaxed(regval, drv_data.apcs_cpu_pwr_ctl);
+
+ for (i = 0; i < NUM_SRC; i++) {
+ if (!src_clocks[i].name)
+ continue;
+ src_clocks[i].clk = clk_get(&pdev->dev, src_clocks[i].name);
+ BUG_ON(IS_ERR(src_clocks[i].clk));
+ /*
+ * Prepare the PLLs because we enable/disable them
+ * in atomic context during power collapse/restore.
+ */
+ BUG_ON(clk_prepare(src_clocks[i].clk));
+ }
+
+ /* Improve boot time by ramping up CPU immediately */
+ for (i = 0; acpu_freq_tbl[i].khz != 0 &&
+ acpu_freq_tbl[i].use_for_scaling; i++)
+ max_cpu_khz = acpu_freq_tbl[i].khz;
+
+ acpuclk_9625_set_rate(smp_processor_id(), max_cpu_khz, SETRATE_INIT);
+
+ acpuclk_register(&acpuclk_9625_data);
+ cpufreq_table_init();
+
+ return 0;
+}
+
+static struct of_device_id acpuclk_9625_match_table[] = {
+ {.compatible = "qcom,acpuclk-9625"},
+ {}
+};
+
+static struct platform_driver acpuclk_9625_driver = {
+ .driver = {
+ .name = "acpuclk-9625",
+ .of_match_table = acpuclk_9625_match_table,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init acpuclk_9625_init(void)
+{
+ return platform_driver_probe(&acpuclk_9625_driver, acpuclk_9625_probe);
+}
+device_initcall(acpuclk_9625_init);
diff --git a/arch/arm/mach-msm/acpuclock-krait.h b/arch/arm/mach-msm/acpuclock-krait.h
index 245f276..ca8013e 100644
--- a/arch/arm/mach-msm/acpuclock-krait.h
+++ b/arch/arm/mach-msm/acpuclock-krait.h
@@ -21,12 +21,12 @@
{\
.src = MSM_BUS_MASTER_AMPSS_M0, \
.dst = MSM_BUS_SLAVE_EBI_CH0, \
- .ib = (_bw) * 1000000UL, \
+ .ib = (_bw) * 1000000ULL, \
}, \
{ \
.src = MSM_BUS_MASTER_AMPSS_M1, \
.dst = MSM_BUS_SLAVE_EBI_CH0, \
- .ib = (_bw) * 1000000UL, \
+ .ib = (_bw) * 1000000ULL, \
}, \
}, \
.num_paths = 2, \
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-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-8226.c b/arch/arm/mach-msm/board-8226.c
index b27382f..33f18a2 100644
--- a/arch/arm/mach-msm/board-8226.c
+++ b/arch/arm/mach-msm/board-8226.c
@@ -28,7 +28,6 @@
#endif
#include <asm/mach/map.h>
#include <asm/hardware/gic.h>
-#include <asm/arch_timer.h>
#include <asm/mach/arch.h>
#include <asm/mach/time.h>
#include <mach/board.h>
@@ -41,6 +40,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[] = {
@@ -55,36 +55,10 @@
.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 msm8226_dt_timer_init(void)
-{
- arch_timer_of_register();
-}
-
-static struct sys_timer msm8226_dt_timer = {
- .init = msm8226_dt_timer_init
-};
-
-void __init msm8226_init_irq(void)
-{
- of_irq_init(irq_match);
-}
-
void __init msm8226_init(void)
{
msm8226_init_gpiomux();
-
msm_clock_init(&msm_dummy_clock_init_data);
-}
-
-void __init msm8226_dt_init(void)
-{
- msm8226_init();
if (socinfo_init() < 0)
pr_err("%s: socinfo_init() failed\n", __func__);
@@ -100,9 +74,9 @@
DT_MACHINE_START(MSM8226_DT, "Qualcomm MSM 8226 (Flattened Device Tree)")
.map_io = msm_map_msm8226_io,
- .init_irq = msm8226_init_irq,
- .init_machine = msm8226_dt_init,
+ .init_irq = msm_dt_init_irq_nompm,
+ .init_machine = msm8226_init,
.handle_irq = gic_handle_irq,
- .timer = &msm8226_dt_timer,
+ .timer = &msm_dt_timer,
.dt_compat = msm8226_dt_match,
MACHINE_END
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 15a544a..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();
@@ -57,3 +63,15 @@
if (node)
of_mpm_init(node);
}
+
+void __init msm_dt_init_irq_nompm(void)
+{
+ 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 31143a5..cc3e92c 100644
--- a/arch/arm/mach-msm/board-dt.h
+++ b/arch/arm/mach-msm/board-dt.h
@@ -12,3 +12,5 @@
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/board-msm7627a-io.c b/arch/arm/mach-msm/board-msm7627a-io.c
index 6e3d10a..2983dc0 100644
--- a/arch/arm/mach-msm/board-msm7627a-io.c
+++ b/arch/arm/mach-msm/board-msm7627a-io.c
@@ -607,6 +607,8 @@
#define FT5X06_IRQ_GPIO 48
#define FT5X06_RESET_GPIO 26
+#define FT5X16_IRQ_GPIO 122
+
static ssize_t
ft5x06_virtual_keys_register(struct kobject *kobj,
struct kobj_attribute *attr,
@@ -620,6 +622,17 @@
"\n");
}
+static ssize_t ft5x16_virtual_keys_register(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ return snprintf(buf, 200, \
+ __stringify(EV_KEY) ":" __stringify(KEY_HOME) ":68:984:135:50" \
+ ":" __stringify(EV_KEY) ":" __stringify(KEY_MENU) ":203:984:135:50" \
+ ":" __stringify(EV_KEY) ":" __stringify(KEY_BACK) ":338:984:135:50" \
+ ":" __stringify(EV_KEY) ":" __stringify(KEY_SEARCH) ":473:984:135:50" \
+ "\n");
+}
+
static struct kobj_attribute ft5x06_virtual_keys_attr = {
.attr = {
.name = "virtualkeys.ft5x06_ts",
@@ -658,13 +671,28 @@
static void __init ft5x06_touchpad_setup(void)
{
int rc;
+ int irq_gpio;
- rc = gpio_tlmm_config(GPIO_CFG(FT5X06_IRQ_GPIO, 0,
+ if (machine_is_qrd_skud_prime()) {
+ irq_gpio = FT5X16_IRQ_GPIO;
+
+ ft5x06_platformdata.x_max = 540;
+ ft5x06_platformdata.y_max = 960;
+ ft5x06_platformdata.irq_gpio = FT5X16_IRQ_GPIO;
+
+ ft5x06_device_info[0].irq = MSM_GPIO_TO_INT(FT5X16_IRQ_GPIO);
+
+ ft5x06_virtual_keys_attr.show = &ft5x16_virtual_keys_register;
+ } else {
+ irq_gpio = FT5X06_IRQ_GPIO;
+ }
+
+ rc = gpio_tlmm_config(GPIO_CFG(irq_gpio, 0,
GPIO_CFG_INPUT, GPIO_CFG_PULL_UP,
GPIO_CFG_8MA), GPIO_CFG_ENABLE);
if (rc)
pr_err("%s: gpio_tlmm_config for %d failed\n",
- __func__, FT5X06_IRQ_GPIO);
+ __func__, irq_gpio);
rc = gpio_tlmm_config(GPIO_CFG(FT5X06_RESET_GPIO, 0,
GPIO_CFG_OUTPUT, GPIO_CFG_PULL_DOWN,
@@ -845,7 +873,8 @@
i2c_register_board_info(MSM_GSBI1_QUP_I2C_BUS_ID,
mxt_device_info,
ARRAY_SIZE(mxt_device_info));
- } else if (machine_is_msm7627a_qrd3() || machine_is_msm8625_qrd7()) {
+ } else if (machine_is_msm7627a_qrd3() || machine_is_msm8625_qrd7()
+ || machine_is_qrd_skud_prime()) {
ft5x06_touchpad_setup();
}
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/devices-msm7x27a.c b/arch/arm/mach-msm/devices-msm7x27a.c
index 9943812..2c49b21 100644
--- a/arch/arm/mach-msm/devices-msm7x27a.c
+++ b/arch/arm/mach-msm/devices-msm7x27a.c
@@ -1963,6 +1963,41 @@
.size = ARRAY_SIZE(msm_clock_8625_dummy),
};
+
+static int __init msm_gpio_config_gps(void)
+{
+ unsigned int gps_gpio = 7;
+ int ret = 0;
+
+ if (!machine_is_msm8625_evb())
+ return ret;
+
+ ret = gpio_tlmm_config(GPIO_CFG(gps_gpio, 0, GPIO_CFG_OUTPUT,
+ GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA), GPIO_CFG_ENABLE);
+ if (ret < 0) {
+ pr_err("gpio tlmm failed for gpio-%d\n", gps_gpio);
+ return ret;
+ }
+
+ ret = gpio_request(gps_gpio, "gnss-gpio");
+ if (ret < 0) {
+ pr_err("failed to request gpio-%d\n", gps_gpio);
+ return ret;
+ }
+
+ ret = gpio_direction_input(gps_gpio);
+ if (ret < 0) {
+ pr_err("failed to change direction for gpio-%d\n", gps_gpio);
+ return ret;
+ }
+
+ ret = gpio_export(gps_gpio, true);
+ if (ret < 0)
+ pr_err("failed to export gpio for user\n");
+
+ return ret;
+}
+
int __init msm7x2x_misc_init(void)
{
if (machine_is_msm8625_rumi3()) {
@@ -1994,6 +2029,9 @@
platform_device_register(&pl310_erp_device);
+ if (msm_gpio_config_gps() < 0)
+ pr_err("Error for gpio config for GPS gpio\n");
+
return 0;
}
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/msm72k_otg.h b/arch/arm/mach-msm/include/mach/msm72k_otg.h
index 623de2a..50e2936 100644
--- a/arch/arm/mach-msm/include/mach/msm72k_otg.h
+++ b/arch/arm/mach-msm/include/mach/msm72k_otg.h
@@ -154,6 +154,7 @@
struct work_struct otg_resume_work;
struct notifier_block usbdev_nb;
struct msm_xo_voter *xo_handle; /*handle to vote for TCXO D1 buffer*/
+ unsigned curr_power;
#ifdef CONFIG_USB_MSM_ACA
struct timer_list id_timer; /* drives id_status polling */
unsigned b_max_power; /* ACA: max power of accessory*/
diff --git a/arch/arm/mach-msm/include/mach/msm_bus.h b/arch/arm/mach-msm/include/mach/msm_bus.h
index c94bf80..6b94a43 100644
--- a/arch/arm/mach-msm/include/mach/msm_bus.h
+++ b/arch/arm/mach-msm/include/mach/msm_bus.h
@@ -44,8 +44,8 @@
struct msm_bus_vectors {
int src; /* Master */
int dst; /* Slave */
- unsigned int ab; /* Arbitrated bandwidth */
- unsigned int ib; /* Instantaneous bandwidth */
+ uint64_t ab; /* Arbitrated bandwidth */
+ uint64_t ib; /* Instantaneous bandwidth */
};
struct msm_bus_paths {
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/include/mach/socinfo.h b/arch/arm/mach-msm/include/mach/socinfo.h
index 5c88101..34bdc79 100644
--- a/arch/arm/mach-msm/include/mach/socinfo.h
+++ b/arch/arm/mach-msm/include/mach/socinfo.h
@@ -101,6 +101,7 @@
MSM_CPU_8064AB,
MSM_CPU_8930,
MSM_CPU_8930AA,
+ MSM_CPU_8930AB,
MSM_CPU_7X27AA,
MSM_CPU_9615,
MSM_CPU_8974,
@@ -342,6 +343,15 @@
#endif
}
+static inline int cpu_is_msm8930ab(void)
+{
+#ifdef CONFIG_ARCH_MSM8930
+ return read_msm_cpu_type() == MSM_CPU_8930AB;
+#else
+ return 0;
+#endif
+}
+
static inline int cpu_is_msm8627(void)
{
/* 8930 and 8627 will share the same CONFIG_ARCH type unless otherwise needed */
@@ -449,7 +459,8 @@
static inline int soc_class_is_msm8930(void)
{
- return cpu_is_msm8930() || cpu_is_msm8930aa() || cpu_is_msm8627();
+ return cpu_is_msm8930() || cpu_is_msm8930aa() || cpu_is_msm8930ab() ||
+ cpu_is_msm8627();
}
#endif
diff --git a/arch/arm/mach-msm/include/mach/subsystem_restart.h b/arch/arm/mach-msm/include/mach/subsystem_restart.h
index d3c4eb8..a95e943 100644
--- a/arch/arm/mach-msm/include/mach/subsystem_restart.h
+++ b/arch/arm/mach-msm/include/mach/subsystem_restart.h
@@ -36,6 +36,12 @@
* @depends_on: subsystem this subsystem depends on to operate
* @dev: parent device
* @owner: module the descriptor belongs to
+ * @start: Start a subsystem
+ * @stop: Stop a subsystem
+ * @shutdown: Stop a subsystem
+ * @powerup: Start a subsystem
+ * @crash_shutdown: Shutdown a subsystem when the system crashes (can't sleep)
+ * @ramdump: Collect a ramdump of the subsystem
*/
struct subsys_desc {
const char *name;
@@ -43,6 +49,9 @@
struct device *dev;
struct module *owner;
+ int (*start)(const struct subsys_desc *desc);
+ void (*stop)(const struct subsys_desc *desc);
+
int (*shutdown)(const struct subsys_desc *desc);
int (*powerup)(const struct subsys_desc *desc);
void (*crash_shutdown)(const struct subsys_desc *desc);
@@ -55,6 +64,9 @@
extern int subsystem_restart_dev(struct subsys_device *dev);
extern int subsystem_restart(const char *name);
+extern void *subsystem_get(const char *name);
+extern void subsystem_put(void *subsystem);
+
extern struct subsys_device *subsys_register(struct subsys_desc *desc);
extern void subsys_unregister(struct subsys_device *dev);
@@ -75,6 +87,13 @@
return 0;
}
+static inline void *subsystem_get(const char *name)
+{
+ return NULL;
+}
+
+static inline void subsystem_put(void *subsystem) { }
+
static inline
struct subsys_device *subsys_register(struct subsys_desc *desc)
{
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/lpm_resources.c b/arch/arm/mach-msm/lpm_resources.c
index 2db92f3..255cd46 100644
--- a/arch/arm/mach-msm/lpm_resources.c
+++ b/arch/arm/mach-msm/lpm_resources.c
@@ -695,6 +695,10 @@
{
struct msm_lpm_resource *rs = &msm_lpm_l2;
switch (action) {
+ case CPU_UP_PREPARE:
+ case CPU_UP_PREPARE_FROZEN:
+ rs->rs_data.value = MSM_LPM_L2_CACHE_ACTIVE;
+ break;
case CPU_ONLINE_FROZEN:
case CPU_ONLINE:
if (num_online_cpus() > 1)
diff --git a/arch/arm/mach-msm/msm_bus/msm_bus_arb.c b/arch/arm/mach-msm/msm_bus/msm_bus_arb.c
index 07082b7..65539c6 100644
--- a/arch/arm/mach-msm/msm_bus/msm_bus_arb.c
+++ b/arch/arm/mach-msm/msm_bus/msm_bus_arb.c
@@ -32,8 +32,9 @@
#define SEL_FAB_CLK 1
#define SEL_SLAVE_CLK 0
-#define BW_TO_CLK_FREQ_HZ(width, bw) ((unsigned long)\
- DIV_ROUND_UP((bw), (width)))
+#define BW_TO_CLK_FREQ_HZ(width, bw) \
+ msm_bus_div64(width, bw)
+
#define IS_MASTER_VALID(mas) \
(((mas >= MSM_BUS_MASTER_FIRST) && (mas <= MSM_BUS_MASTER_LAST)) \
? 1 : 0)
@@ -43,6 +44,33 @@
static DEFINE_MUTEX(msm_bus_lock);
+/* This function uses shift operations to divide 64 bit value for higher
+ * efficiency. The divisor expected are number of ports or bus-width.
+ * These are expected to be 1, 2, 4, 8, 16 and 32 in most cases.
+ *
+ * To account for exception to the above divisor values, the standard
+ * do_div function is used.
+ * */
+uint64_t msm_bus_div64(unsigned int w, uint64_t bw)
+{
+ uint64_t *b = &bw;
+
+ if ((bw > 0) && (bw < w))
+ return 1;
+
+ switch (w) {
+ case 1: return bw;
+ case 2: return (bw >> 1);
+ case 4: return (bw >> 2);
+ case 8: return (bw >> 3);
+ case 16: return (bw >> 4);
+ case 32: return (bw >> 5);
+ }
+
+ do_div(*b, w);
+ return *b;
+}
+
/**
* add_path_node: Adds the path information to the current node
* @info: Internal node info structure
@@ -278,21 +306,21 @@
* frequencies is calculated at each node on the path. Commit data to be sent
* to RPM for each master and slave is also calculated here.
*/
-static int update_path(int curr, int pnode, unsigned long req_clk, unsigned
- long req_bw, unsigned long curr_clk, unsigned long curr_bw,
- unsigned int ctx, unsigned int cl_active_flag)
+static int update_path(int curr, int pnode, uint64_t req_clk, uint64_t req_bw,
+ uint64_t curr_clk, uint64_t curr_bw, unsigned int ctx, unsigned int
+ cl_active_flag)
{
int index, ret = 0;
struct msm_bus_inode_info *info;
int next_pnode;
- long int add_bw = req_bw - curr_bw;
+ int64_t add_bw = req_bw - curr_bw;
unsigned bwsum = 0;
- unsigned req_clk_hz, curr_clk_hz, bwsum_hz;
+ uint64_t req_clk_hz, curr_clk_hz, bwsum_hz;
int *master_tiers;
struct msm_bus_fabric_device *fabdev = msm_bus_get_fabric_device
(GET_FABID(curr));
- MSM_BUS_DBG("args: %d %d %d %lu %lu %lu %lu %u\n",
+ MSM_BUS_DBG("args: %d %d %d %llu %llu %llu %llu %u\n",
curr, GET_NODE(pnode), GET_INDEX(pnode), req_clk, req_bw,
curr_clk, curr_bw, ctx);
index = GET_INDEX(pnode);
@@ -378,8 +406,8 @@
req_clk);
bwsum_hz = BW_TO_CLK_FREQ_HZ(hop->node_info->buswidth,
bwsum);
- MSM_BUS_DBG("Calling update-clks: curr_hz: %lu, req_hz: %lu,"
- " bw_hz %u\n", curr_clk, req_clk, bwsum_hz);
+ MSM_BUS_DBG("up-clk: curr_hz: %llu, req_hz: %llu, bw_hz %llu\n",
+ curr_clk, req_clk, bwsum_hz);
ret = fabdev->algo->update_clks(fabdev, hop, index,
curr_clk_hz, req_clk_hz, bwsum_hz, SEL_FAB_CLK,
ctx, cl_active_flag);
@@ -532,7 +560,7 @@
int i, ret = 0;
struct msm_bus_scale_pdata *pdata;
int pnode, src, curr, ctx;
- unsigned long req_clk, req_bw, curr_clk, curr_bw;
+ uint64_t req_clk, req_bw, curr_clk, curr_bw;
struct msm_bus_client *client = (struct msm_bus_client *)cl;
if (IS_ERR(client)) {
MSM_BUS_ERR("msm_bus_scale_client update req error %d\n",
@@ -554,9 +582,8 @@
goto err;
}
- MSM_BUS_DBG("cl: %u index: %d curr: %d"
- " num_paths: %d\n", cl, index, client->curr,
- client->pdata->usecase->num_paths);
+ MSM_BUS_DBG("cl: %u index: %d curr: %d num_paths: %d\n",
+ cl, index, client->curr, client->pdata->usecase->num_paths);
for (i = 0; i < pdata->usecase->num_paths; i++) {
src = msm_bus_board_get_iid(client->pdata->usecase[index].
@@ -584,7 +611,7 @@
} else {
curr_clk = client->pdata->usecase[curr].vectors[i].ib;
curr_bw = client->pdata->usecase[curr].vectors[i].ab;
- MSM_BUS_DBG("ab: %lu ib: %lu\n", curr_bw, curr_clk);
+ MSM_BUS_DBG("ab: %llu ib: %llu\n", curr_bw, curr_clk);
}
if (!pdata->active_only) {
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 97299a0..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:
@@ -1807,7 +1803,7 @@
struct msm_bus_inode_info *info,
struct msm_bus_fabric_registration *fab_pdata,
void *sel_cdata, int *master_tiers,
- long int add_bw)
+ int64_t add_bw)
{
struct msm_bus_bimc_info *binfo;
struct msm_bus_bimc_qos_bw qbw;
@@ -1817,7 +1813,7 @@
struct msm_bus_bimc_commit *sel_cd =
(struct msm_bus_bimc_commit *)sel_cdata;
- MSM_BUS_DBG("BIMC: Update bw for ID %d, with IID: %d: %ld\n",
+ MSM_BUS_DBG("BIMC: Update bw for ID %d, with IID: %d: %lld\n",
info->node_info->id, info->node_info->priv_id, add_bw);
binfo = (struct msm_bus_bimc_info *)fab_pdata->hw_data;
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/msm_bus/msm_bus_core.h b/arch/arm/mach-msm/msm_bus/msm_bus_core.h
index 333fe4b..12d6862 100644
--- a/arch/arm/mach-msm/msm_bus/msm_bus_core.h
+++ b/arch/arm/mach-msm/msm_bus/msm_bus_core.h
@@ -36,7 +36,7 @@
(((slv >= MSM_BUS_SLAVE_FIRST) && (slv <= MSM_BUS_SLAVE_LAST)) ? 1 : 0)
#define INTERLEAVED_BW(fab_pdata, bw, ports) \
- ((fab_pdata->il_flag) ? DIV_ROUND_UP((bw), (ports)) : (bw))
+ ((fab_pdata->il_flag) ? msm_bus_div64((bw), (ports)) : (bw))
#define INTERLEAVED_VAL(fab_pdata, n) \
((fab_pdata->il_flag) ? (n) : 1)
@@ -83,34 +83,34 @@
};
struct path_node {
- unsigned long clk[NUM_CTX];
- unsigned long bw[NUM_CTX];
- unsigned long *sel_clk;
- unsigned long *sel_bw;
+ uint64_t clk[NUM_CTX];
+ uint64_t bw[NUM_CTX];
+ uint64_t *sel_clk;
+ uint64_t *sel_bw;
int next;
};
struct msm_bus_link_info {
- unsigned long clk[NUM_CTX];
- unsigned long *sel_clk;
- unsigned long memclk;
- long bw[NUM_CTX];
- long *sel_bw;
+ uint64_t clk[NUM_CTX];
+ uint64_t *sel_clk;
+ uint64_t memclk;
+ int64_t bw[NUM_CTX];
+ int64_t *sel_bw;
int *tier;
int num_tiers;
};
struct nodeclk {
struct clk *clk;
- unsigned long rate;
+ uint64_t rate;
bool dirty;
bool enable;
};
struct msm_bus_inode_info {
struct msm_bus_node_info *node_info;
- unsigned long max_bw;
- unsigned long max_clk;
+ uint64_t max_bw;
+ uint64_t max_clk;
struct msm_bus_link_info link_info;
int num_pnodes;
struct path_node *pnode;
@@ -137,7 +137,7 @@
struct msm_bus_inode_info *info,
struct msm_bus_fabric_registration *fab_pdata,
void *sel_cdata, int *master_tiers,
- long int add_bw);
+ int64_t add_bw);
void (*fill_cdata_buffer)(int *curr, char *buf, const int max_size,
void *cdata, int nmasters, int nslaves, int ntslaves);
int (*commit)(struct msm_bus_fabric_registration
@@ -162,8 +162,8 @@
struct msm_bus_fab_algorithm {
int (*update_clks)(struct msm_bus_fabric_device *fabdev,
struct msm_bus_inode_info *pme, int index,
- unsigned long curr_clk, unsigned long req_clk,
- unsigned long bwsum, int flag, int ctx,
+ uint64_t curr_clk, uint64_t req_clk,
+ uint64_t bwsum, int flag, int ctx,
unsigned int cl_active_flag);
int (*port_halt)(struct msm_bus_fabric_device *fabdev, int portid);
int (*port_unhalt)(struct msm_bus_fabric_device *fabdev, int portid);
@@ -175,7 +175,7 @@
struct list_head *(*get_gw_list)(struct msm_bus_fabric_device *fabdev);
void (*update_bw)(struct msm_bus_fabric_device *fabdev, struct
msm_bus_inode_info * hop, struct msm_bus_inode_info *info,
- long int add_bw, int *master_tiers, int ctx);
+ int64_t add_bw, int *master_tiers, int ctx);
};
struct msm_bus_board_algorithm {
@@ -202,6 +202,7 @@
int curr;
};
+uint64_t msm_bus_div64(unsigned int width, uint64_t bw);
int msm_bus_remote_hw_commit(struct msm_bus_fabric_registration
*fab_pdata, void *hw_data, void **cdata);
int msm_bus_fabric_device_register(struct msm_bus_fabric_device *fabric);
diff --git a/arch/arm/mach-msm/msm_bus/msm_bus_dbg.c b/arch/arm/mach-msm/msm_bus/msm_bus_dbg.c
index 76f85c6..a44c53a 100644
--- a/arch/arm/mach-msm/msm_bus/msm_bus_dbg.c
+++ b/arch/arm/mach-msm/msm_bus/msm_bus_dbg.c
@@ -385,11 +385,11 @@
pdata->usecase[index].vectors[j].dst);
i += scnprintf(buf + i, MAX_BUFF_SIZE - i, "\nab : ");
for (j = 0; j < pdata->usecase->num_paths; j++)
- i += scnprintf(buf + i, MAX_BUFF_SIZE - i, "%u ",
+ i += scnprintf(buf + i, MAX_BUFF_SIZE - i, "%llu ",
pdata->usecase[index].vectors[j].ab);
i += scnprintf(buf + i, MAX_BUFF_SIZE - i, "\nib : ");
for (j = 0; j < pdata->usecase->num_paths; j++)
- i += scnprintf(buf + i, MAX_BUFF_SIZE - i, "%u ",
+ i += scnprintf(buf + i, MAX_BUFF_SIZE - i, "%llu ",
pdata->usecase[index].vectors[j].ib);
i += scnprintf(buf + i, MAX_BUFF_SIZE - i, "\n");
diff --git a/arch/arm/mach-msm/msm_bus/msm_bus_fabric.c b/arch/arm/mach-msm/msm_bus/msm_bus_fabric.c
index be3e06f..7169440 100644
--- a/arch/arm/mach-msm/msm_bus/msm_bus_fabric.c
+++ b/arch/arm/mach-msm/msm_bus/msm_bus_fabric.c
@@ -227,13 +227,13 @@
*/
static int msm_bus_fabric_update_clks(struct msm_bus_fabric_device *fabdev,
struct msm_bus_inode_info *slave, int index,
- unsigned long curr_clk_hz, unsigned long req_clk_hz,
- unsigned long bwsum_hz, int clk_flag, int ctx,
+ uint64_t curr_clk_hz, uint64_t req_clk_hz,
+ uint64_t bwsum_hz, int clk_flag, int ctx,
unsigned int cl_active_flag)
{
int i, status = 0;
- unsigned long max_pclk = 0, rate;
- unsigned long *pclk = NULL;
+ uint64_t max_pclk = 0, rate;
+ uint64_t *pclk = NULL;
struct msm_bus_fabric *fabric = to_msm_bus_fabric(fabdev);
struct nodeclk *nodeclk;
@@ -266,7 +266,7 @@
info->link_info.sel_clk = &info->link_info.clk[ctx];
max_pclk = max(max_pclk, *info->link_info.sel_clk);
}
- MSM_BUS_DBG("max_pclk from gateways: %lu\n", max_pclk);
+ MSM_BUS_DBG("max_pclk from gateways: %llu\n", max_pclk);
/* Maximum of all slave clocks. */
@@ -283,7 +283,7 @@
}
- MSM_BUS_DBG("max_pclk from slaves & gws: %lu\n", max_pclk);
+ MSM_BUS_DBG("max_pclk from slaves & gws: %llu\n", max_pclk);
fabric->info.link_info.sel_clk =
&fabric->info.link_info.clk[ctx];
pclk = fabric->info.link_info.sel_clk;
@@ -301,7 +301,7 @@
if (clk_flag) {
nodeclk = &fabric->info.nodeclk[ctx];
if (nodeclk->clk) {
- MSM_BUS_DBG("clks: id: %d set-clk: %lu bwsum_hz:%lu\n",
+ MSM_BUS_DBG("clks: id: %d set-clk: %llu bws_hz:%llu\n",
fabric->fabdev.id, *pclk, bwsum_hz);
if (nodeclk->rate != *pclk) {
nodeclk->dirty = true;
@@ -313,8 +313,8 @@
nodeclk = &slave->nodeclk[ctx];
if (nodeclk->clk) {
rate = *pclk;
- MSM_BUS_DBG("AXI_clks: id: %d set-clk: %lu "
- "bwsum_hz: %lu\n" , slave->node_info->priv_id, rate,
+ MSM_BUS_DBG("clks: id: %d set-clk: %llu bws_hz: %llu\n",
+ slave->node_info->priv_id, rate,
bwsum_hz);
if (nodeclk->rate != rate) {
nodeclk->dirty = true;
@@ -337,7 +337,7 @@
void msm_bus_fabric_update_bw(struct msm_bus_fabric_device *fabdev,
struct msm_bus_inode_info *hop, struct msm_bus_inode_info *info,
- long int add_bw, int *master_tiers, int ctx)
+ int64_t add_bw, int *master_tiers, int ctx)
{
struct msm_bus_fabric *fabric = to_msm_bus_fabric(fabdev);
void *sel_cdata;
diff --git a/arch/arm/mach-msm/msm_bus/msm_bus_noc.c b/arch/arm/mach-msm/msm_bus/msm_bus_noc.c
index 049e8c7..fb2e5da 100644
--- a/arch/arm/mach-msm/msm_bus/msm_bus_noc.c
+++ b/arch/arm/mach-msm/msm_bus/msm_bus_noc.c
@@ -504,7 +504,7 @@
struct msm_bus_inode_info *info,
struct msm_bus_fabric_registration *fab_pdata,
void *sel_cdata, int *master_tiers,
- long int add_bw)
+ int64_t add_bw)
{
struct msm_bus_noc_info *ninfo;
struct msm_bus_noc_qos_bw qos_bw;
@@ -527,7 +527,7 @@
ports = info->node_info->num_mports;
bw = INTERLEAVED_BW(fab_pdata, add_bw, ports);
- MSM_BUS_DBG("NOC: Update bw for: %d: %ld\n",
+ MSM_BUS_DBG("NOC: Update bw for: %d: %lld\n",
info->node_info->priv_id, add_bw);
for (i = 0; i < ports; i++) {
sel_cd->mas[info->node_info->masterp[i]].bw += bw;
diff --git a/arch/arm/mach-msm/msm_bus/msm_bus_of.c b/arch/arm/mach-msm/msm_bus/msm_bus_of.c
index 24b0ce2..8ae1b46 100644
--- a/arch/arm/mach-msm/msm_bus/msm_bus_of.c
+++ b/arch/arm/mach-msm/msm_bus/msm_bus_of.c
@@ -19,6 +19,7 @@
#include <linux/platform_device.h>
#include <mach/msm_bus.h>
+#define KBTOMB(a) (a * 1000ULL)
/**
* msm_bus_cl_get_pdata() - Generate bus client data from device tree
* provided by clients.
@@ -53,22 +54,22 @@
goto err;
}
- ret = of_property_read_string(of_node, "qcom,msm_bus,name",
+ ret = of_property_read_string(of_node, "qcom,msm-bus,name",
&pdata->name);
if (ret) {
pr_err("Error: Client name not found\n");
goto err;
}
- ret = of_property_read_u32(of_node, "qcom,msm_bus,num_cases",
+ ret = of_property_read_u32(of_node, "qcom,msm-bus,num-cases",
&num_usecases);
if (ret) {
- pr_err("Error: num_usecases not found\n");
+ pr_err("Error: num-usecases not found\n");
goto err;
}
pdata->num_usecases = num_usecases;
- ret = of_property_read_u32(of_node, "qcom,msm_bus,active_only",
+ ret = of_property_read_u32(of_node, "qcom,msm-bus,active-only",
&pdata->active_only);
if (ret) {
pr_info("active_only flag absent.\n");
@@ -83,15 +84,15 @@
goto err;
}
- ret = of_property_read_u32(of_node, "qcom,msm_bus,num_paths",
+ ret = of_property_read_u32(of_node, "qcom,msm-bus,num-paths",
&num_paths);
if (ret) {
pr_err("Error: num_paths not found\n");
goto err;
}
- vec_arr = of_get_property(of_node, "qcom,msm_bus,vectors", &len);
- if (len != num_usecases * num_paths * sizeof(struct msm_bus_vectors)) {
+ vec_arr = of_get_property(of_node, "qcom,msm-bus,vectors-KBps", &len);
+ if (len != num_usecases * num_paths * sizeof(uint32_t) * 4) {
pr_err("Error: Length-error on getting vectors\n");
goto err;
}
@@ -111,10 +112,10 @@
usecase[i].vectors[j].src = be32_to_cpu(vec_arr[index]);
usecase[i].vectors[j].dst =
be32_to_cpu(vec_arr[index + 1]);
- usecase[i].vectors[j].ab =
- be32_to_cpu(vec_arr[index + 2]);
- usecase[i].vectors[j].ib =
- be32_to_cpu(vec_arr[index + 3]);
+ usecase[i].vectors[j].ab = (uint64_t)
+ KBTOMB(be32_to_cpu(vec_arr[index + 2]));
+ usecase[i].vectors[j].ib = (uint64_t)
+ KBTOMB(be32_to_cpu(vec_arr[index + 3]));
}
}
diff --git a/arch/arm/mach-msm/msm_bus/msm_bus_rpm.c b/arch/arm/mach-msm/msm_bus/msm_bus_rpm.c
index 2213132..fc38ef7 100644
--- a/arch/arm/mach-msm/msm_bus/msm_bus_rpm.c
+++ b/arch/arm/mach-msm/msm_bus/msm_bus_rpm.c
@@ -239,7 +239,7 @@
struct msm_bus_inode_info *info,
struct msm_bus_fabric_registration *fab_pdata,
void *sel_cdata, int *master_tiers,
- long int add_bw)
+ int64_t add_bw)
{
int index, i, j, tiers, ports;
struct commit_data *sel_cd = (struct commit_data *)sel_cdata;
@@ -302,9 +302,9 @@
msm_bus_create_bw_tier_pair_bytes(tier,
tieredbw);
sel_cd->actarb[index] = tieredbw;
- MSM_BUS_DBG("tier:%d mport: %d tiered_bw:%ld "
- "bwsum: %ld\n", hop_tier, info->node_info->
- masterp[i], tieredbw, *hop->link_info.sel_bw);
+ MSM_BUS_DBG("tr:%d mpor:%d tbw:%ld bws: %lld\n",
+ hop_tier, info->node_info->masterp[i],
+ tieredbw, *hop->link_info.sel_bw);
}
}
}
@@ -314,10 +314,12 @@
for (i = 0; i < ports; i++) {
sel_cd->bwsum[hop->node_info->slavep[i]]
= (uint16_t)msm_bus_create_bw_tier_pair_bytes(0,
- (*hop->link_info.sel_bw/hop->node_info->num_sports));
- MSM_BUS_DBG("slavep:%d, link_bw: %ld\n",
- hop->node_info->slavep[i], (*hop->link_info.sel_bw/
- hop->node_info->num_sports));
+ (uint32_t)msm_bus_div64(hop->node_info->num_sports,
+ *hop->link_info.sel_bw));
+ MSM_BUS_DBG("slavep:%d, link_bw: %u\n",
+ hop->node_info->slavep[i], (uint32_t)
+ msm_bus_div64(hop->node_info->num_sports,
+ *hop->link_info.sel_bw));
}
}
@@ -756,7 +758,7 @@
struct msm_bus_inode_info *info,
struct msm_bus_fabric_registration *fab_pdata,
void *sel_cdata, int *master_tiers,
- long int add_bw)
+ int64_t add_bw)
{
int index, i, j, tiers, ports;
struct commit_data *sel_cd = (struct commit_data *)sel_cdata;
@@ -808,9 +810,9 @@
sel_cd->arb[tier][index] =
msm_bus_create_bw_tier_pair_bytes(0, tieredbw);
sel_cd->actarb[tier][index] = tieredbw;
- MSM_BUS_DBG("tier:%d mport: %d tiered_bw:%lu "
- "bwsum: %ld\n", hop_tier, info->node_info->
- masterp[i], tieredbw, *hop->link_info.sel_bw);
+ MSM_BUS_DBG("tr:%d mpor:%d tbw:%lu bws: %lld\n",
+ hop_tier, info->node_info->masterp[i], tieredbw,
+ *hop->link_info.sel_bw);
}
}
}
@@ -820,11 +822,13 @@
ports = INTERLEAVED_VAL(fab_pdata, hop->node_info->num_sports);
for (i = 0; i < ports; i++) {
sel_cd->bwsum[hop->node_info->slavep[i]]
- = msm_bus_pack_bwsum_bytes((*hop->link_info.
- sel_bw/hop->node_info->num_sports));
- MSM_BUS_DBG("slavep:%d, link_bw: %ld\n",
- hop->node_info->slavep[i], (*hop->link_info.sel_bw/
- hop->node_info->num_sports));
+ = msm_bus_pack_bwsum_bytes((uint32_t)
+ msm_bus_div64(hop->node_info->num_sports,
+ *hop->link_info.sel_bw));
+ MSM_BUS_DBG("slavep:%d, link_bw: %lld\n",
+ hop->node_info->slavep[i],
+ msm_bus_div64(hop->node_info->num_sports,
+ *hop->link_info.sel_bw));
}
}
diff --git a/arch/arm/mach-msm/msm_dcvs.c b/arch/arm/mach-msm/msm_dcvs.c
index 310197e..b2160c5 100644
--- a/arch/arm/mach-msm/msm_dcvs.c
+++ b/arch/arm/mach-msm/msm_dcvs.c
@@ -61,6 +61,8 @@
struct kobj_attribute thermal_poll_ms;
+ struct kobj_attribute freq_tbl;
+
struct attribute_group attrib_group;
};
@@ -633,11 +635,83 @@
DCVS_PARAM_STORE(thermal_poll_ms)
+static ssize_t msm_dcvs_attr_freq_tbl_show(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ char *buf)
+{
+ struct msm_dcvs_freq_entry *freq_tbl;
+ char *buf_idx = buf;
+ int i, len;
+ struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, freq_tbl);
+
+ freq_tbl = core->info->freq_tbl;
+ *buf_idx = '\0';
+
+ /* limit the number of frequencies we will print into
+ * the PAGE_SIZE sysfs show buffer. */
+ if (core->info->power_param.num_freq > 64)
+ return 0;
+
+ for (i = 0; i < core->info->power_param.num_freq; i++) {
+ if (freq_tbl[i].is_trans_level) {
+ len = snprintf(buf_idx, 10, "%7d ", freq_tbl[i].freq);
+ /* buf_idx always points at terminating null */
+ buf_idx += len;
+ }
+ }
+ /* overwrite final trailing space with newline */
+ if (buf_idx > buf)
+ *(buf_idx - 1) = '\n';
+
+ return buf_idx - buf;
+}
+
+static ssize_t msm_dcvs_attr_freq_tbl_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ struct msm_dcvs_freq_entry *freq_tbl;
+ uint32_t freq;
+ int i, ret;
+ struct dcvs_core *core = CORE_FROM_ATTRIBS(attr, freq_tbl);
+
+ freq_tbl = core->info->freq_tbl;
+
+ ret = kstrtouint(buf, 10, &freq);
+ if (ret) {
+ __err("Invalid input %s for freq_tbl\n", buf);
+ return count;
+ }
+
+ for (i = 0; i < core->info->power_param.num_freq; i++)
+ if (freq_tbl[i].freq == freq) {
+ freq_tbl[i].is_trans_level ^= 1;
+ break;
+ }
+
+ if (i >= core->info->power_param.num_freq) {
+ __err("Invalid frequency for freq_tbl: %d\n", freq);
+ return count;
+ }
+
+ ret = msm_dcvs_scm_set_power_params(core->dcvs_core_id,
+ &core->info->power_param,
+ &core->info->freq_tbl[0],
+ &core->coeffs);
+ if (ret) {
+ freq_tbl[i].is_trans_level ^= 1;
+ __err("Error %d in toggling freq %d (orig enable val %d)\n",
+ ret, freq_tbl[i].freq, freq_tbl[i].is_trans_level);
+ }
+ return count;
+}
+
static int msm_dcvs_setup_core_sysfs(struct dcvs_core *core)
{
int ret = 0;
struct kobject *core_kobj = NULL;
- const int attr_count = 24;
+ const int attr_count = 25;
BUG_ON(!cores_kobj);
@@ -675,7 +749,9 @@
DCVS_RW_ATTRIB(21, leakage_coeff_d);
DCVS_RW_ATTRIB(22, thermal_poll_ms);
- core->attrib.attrib_group.attrs[23] = NULL;
+ DCVS_RW_ATTRIB(23, freq_tbl);
+
+ core->attrib.attrib_group.attrs[24] = NULL;
core_kobj = kobject_create_and_add(core->core_name, cores_kobj);
if (!core_kobj) {
diff --git a/arch/arm/mach-msm/msm_dsps.c b/arch/arm/mach-msm/msm_dsps.c
index b85c812..859fc15 100644
--- a/arch/arm/mach-msm/msm_dsps.c
+++ b/arch/arm/mach-msm/msm_dsps.c
@@ -32,7 +32,6 @@
#include <linux/msm_dsps.h>
#include <mach/irqs.h>
-#include <mach/peripheral-loader.h>
#include <mach/msm_iomap.h>
#include <mach/msm_smsm.h>
#include <mach/msm_dsps.h>
@@ -90,14 +89,13 @@
/**
* Load DSPS Firmware.
*/
-static int dsps_load(const char *name)
+static int dsps_load(void)
{
pr_debug("%s.\n", __func__);
- drv->pil = pil_get(name);
-
+ drv->pil = subsystem_get("dsps");
if (IS_ERR(drv->pil)) {
- pr_err("%s: fail to load DSPS firmware %s.\n", __func__, name);
+ pr_err("%s: fail to load DSPS firmware.\n", __func__);
return -ENODEV;
}
msleep(20);
@@ -111,7 +109,7 @@
{
pr_debug("%s.\n", __func__);
- pil_put(drv->pil);
+ subsystem_put(drv->pil);
}
/**
@@ -531,7 +529,7 @@
if (ret)
return ret;
- ret = dsps_load(drv->pdata->pil_name);
+ ret = dsps_load();
if (ret) {
dsps_power_off_handler();
@@ -679,6 +677,8 @@
goto cdev_add_err;
}
+ return 0;
+
cdev_add_err:
kfree(drv->cdev);
cdev_alloc_err:
diff --git a/arch/arm/mach-msm/ocmem_core.c b/arch/arm/mach-msm/ocmem_core.c
index 9a85a17..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) {
@@ -856,7 +856,7 @@
/* Interfaces invoked from the scheduler */
int ocmem_memory_off(int id, unsigned long offset, unsigned long len)
{
- return switch_power_state(id, offset, len, REGION_DEFAULT_ON);
+ return switch_power_state(id, offset, len, REGION_DEFAULT_OFF);
}
int ocmem_memory_on(int id, unsigned long offset, unsigned long len)
@@ -866,7 +866,7 @@
int ocmem_memory_retain(int id, unsigned long offset, unsigned long len)
{
- return switch_power_state(id, offset, len, REGION_DEFAULT_ON);
+ return switch_power_state(id, offset, len, REGION_DEFAULT_RETENTION);
}
static int ocmem_power_show_sw_state(struct seq_file *f, void *dummy)
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-dsps.c b/arch/arm/mach-msm/pil-dsps.c
index dc80a3a..c074086 100644
--- a/arch/arm/mach-msm/pil-dsps.c
+++ b/arch/arm/mach-msm/pil-dsps.c
@@ -163,6 +163,23 @@
}
}
+static int dsps_start(const struct subsys_desc *desc)
+{
+ void *ret;
+ struct dsps_data *drv = desc_to_drv(desc);
+
+ ret = pil_get(drv->desc.name);
+ if (IS_ERR(ret))
+ return PTR_ERR(ret);
+ return 0;
+}
+
+static void dsps_stop(const struct subsys_desc *desc)
+{
+ struct dsps_data *drv = desc_to_drv(desc);
+ pil_put(drv->pil);
+}
+
static int dsps_shutdown(const struct subsys_desc *desc)
{
struct dsps_data *drv = desc_to_drv(desc);
@@ -290,6 +307,10 @@
}
drv->subsys_desc.name = "dsps";
+ drv->subsys_desc.dev = &pdev->dev;
+ drv->subsys_desc.owner = THIS_MODULE;
+ drv->subsys_desc.start = dsps_start;
+ drv->subsys_desc.stop = dsps_stop;
drv->subsys_desc.shutdown = dsps_shutdown;
drv->subsys_desc.powerup = dsps_powerup;
drv->subsys_desc.ramdump = dsps_ramdump,
diff --git a/arch/arm/mach-msm/pil-gss.c b/arch/arm/mach-msm/pil-gss.c
index bccbce2..0c8f4e3 100644
--- a/arch/arm/mach-msm/pil-gss.c
+++ b/arch/arm/mach-msm/pil-gss.c
@@ -75,7 +75,7 @@
struct subsys_desc subsys_desc;
int crash_shutdown;
int irq;
- void *pil_handle;
+ void *subsys_handle;
struct ramdump_device *ramdump_dev;
struct ramdump_device *smem_ramdump_dev;
};
@@ -371,6 +371,26 @@
}
}
+static int gss_start(const struct subsys_desc *desc)
+{
+ void *ret;
+ struct gss_data *drv;
+
+ drv = container_of(desc, struct gss_data, subsys_desc);
+ ret = pil_get("gss");
+ if (IS_ERR(ret))
+ return PTR_ERR(ret);
+ return 0;
+}
+
+static void gss_stop(const struct subsys_desc *desc)
+{
+ struct gss_data *drv;
+
+ drv = container_of(desc, struct gss_data, subsys_desc);
+ pil_put(drv->pil);
+}
+
static int gss_shutdown(const struct subsys_desc *desc)
{
struct gss_data *drv = container_of(desc, struct gss_data, subsys_desc);
@@ -443,13 +463,12 @@
static int gss_open(struct inode *inode, struct file *filp)
{
- void *ret;
struct miscdevice *c = filp->private_data;
struct gss_data *drv = container_of(c, struct gss_data, misc_dev);
- drv->pil_handle = ret = pil_get("gss");
- if (!ret)
- pr_debug("%s - pil_get returned NULL\n", __func__);
+ drv->subsys_handle = subsystem_get("gss");
+ if (!drv->subsys_handle)
+ pr_debug("%s - subsystem_get returned NULL\n", __func__);
return 0;
}
@@ -459,8 +478,8 @@
struct miscdevice *c = filp->private_data;
struct gss_data *drv = container_of(c, struct gss_data, misc_dev);
- pil_put(drv->pil_handle);
- pr_debug("%s pil_put called on GSS\n", __func__);
+ subsystem_put(drv->subsys_handle);
+ pr_debug("%s subsystem_put called on GSS\n", __func__);
return 0;
}
@@ -538,6 +557,10 @@
dev_warn(&pdev->dev, "Unable to register SMSM callback\n");
drv->subsys_desc.name = "gss";
+ drv->subsys_desc.dev = &pdev->dev;
+ drv->subsys_desc.owner = THIS_MODULE;
+ drv->subsys_desc.start = gss_start;
+ drv->subsys_desc.stop = gss_stop;
drv->subsys_desc.shutdown = gss_shutdown;
drv->subsys_desc.powerup = gss_powerup;
drv->subsys_desc.ramdump = gss_ramdump;
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/pm-8x60.c b/arch/arm/mach-msm/pm-8x60.c
index 5c40750..f55d509 100644
--- a/arch/arm/mach-msm/pm-8x60.c
+++ b/arch/arm/mach-msm/pm-8x60.c
@@ -1071,7 +1071,7 @@
msm_pc_debug_counters_phys = res->start;
WARN_ON(resource_size(res) < SZ_64);
- msm_pc_debug_counters = devm_ioremap(&pdev->dev, res->start,
+ msm_pc_debug_counters = devm_ioremap_nocache(&pdev->dev, res->start,
resource_size(res));
if (!msm_pc_debug_counters)
diff --git a/arch/arm/mach-msm/scm.c b/arch/arm/mach-msm/scm.c
index 6052918..6013efc 100644
--- a/arch/arm/mach-msm/scm.c
+++ b/arch/arm/mach-msm/scm.c
@@ -196,6 +196,7 @@
* side in the buffer.
*/
flush_cache_all();
+ outer_flush_all();
ret = smc(cmd_addr);
if (ret < 0)
ret = scm_remap_error(ret);
@@ -209,6 +210,7 @@
{
start = round_down(start, cacheline_size);
end = round_up(end, cacheline_size);
+ outer_inv_range(start, end);
while (start < end) {
asm ("mcr p15, 0, %0, c7, c6, 1" : : "r" (start)
: "memory");
diff --git a/arch/arm/mach-msm/smd.c b/arch/arm/mach-msm/smd.c
index 71fb87c..c6da910 100644
--- a/arch/arm/mach-msm/smd.c
+++ b/arch/arm/mach-msm/smd.c
@@ -871,6 +871,7 @@
/*
* Returns a pointer to the subsystem name given the
* remote processor ID.
+ * subsystem is not necessarily PIL-loadable
*
* @pid Remote processor ID
* @returns Pointer to subsystem name or NULL if not found
@@ -881,11 +882,14 @@
int i;
for (i = 0; i < ARRAY_SIZE(edge_to_pids); ++i) {
- if (pid == edge_to_pids[i].remote_pid &&
- edge_to_pids[i].subsys_name[0] != 0x0
- ) {
- subsys = edge_to_pids[i].subsys_name;
- break;
+ if (pid == edge_to_pids[i].remote_pid) {
+ if (edge_to_pids[i].subsys_name[0] != 0x0) {
+ subsys = edge_to_pids[i].subsys_name;
+ break;
+ } else if (pid == SMD_RPM) {
+ subsys = "rpm";
+ break;
+ }
}
}
diff --git a/arch/arm/mach-msm/socinfo.c b/arch/arm/mach-msm/socinfo.c
index 0beb952..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,
@@ -296,6 +298,12 @@
/* 8064AB IDs */
[153] = MSM_CPU_8064AB,
+ /* 8930AB IDs */
+ [154] = MSM_CPU_8930AB,
+ [155] = MSM_CPU_8930AB,
+ [156] = MSM_CPU_8930AB,
+ [157] = MSM_CPU_8930AB
+
/* Uninitialized IDs are not known to run Linux.
MSM_CPU_UNKNOWN is set to 0 to ensure these IDs are
considered as unknown CPU. */
diff --git a/arch/arm/mach-msm/spm-v2.c b/arch/arm/mach-msm/spm-v2.c
index 17997d2..620aa1d 100644
--- a/arch/arm/mach-msm/spm-v2.c
+++ b/arch/arm/mach-msm/spm-v2.c
@@ -309,6 +309,15 @@
}
#ifdef CONFIG_MSM_AVS_HW
+static bool msm_spm_drv_is_avs_enabled(struct msm_spm_driver_data *dev)
+{
+ msm_spm_drv_load_shadow(dev, MSM_SPM_REG_SAW2_AVS_CTL);
+ if (dev->major == SAW2_MAJOR_2)
+ return dev->reg_shadow[MSM_SPM_REG_SAW2_AVS_CTL] & BIT(0);
+ else
+ return dev->reg_shadow[MSM_SPM_REG_SAW2_AVS_CTL] & BIT(27);
+}
+
static void msm_spm_drv_disable_avs(struct msm_spm_driver_data *dev)
{
msm_spm_drv_load_shadow(dev, MSM_SPM_REG_SAW2_AVS_CTL);
@@ -333,6 +342,10 @@
}
#else
+static bool msm_spm_drv_is_avs_enabled(struct msm_spm_driver_data *dev)
+{
+ return false;
+}
static void msm_spm_drv_disable_avs(struct msm_spm_driver_data *dev) { }
@@ -345,6 +358,7 @@
int msm_spm_drv_set_vdd(struct msm_spm_driver_data *dev, unsigned int vlevel)
{
uint32_t timeout_us, new_level;
+ bool avs_enabled = msm_spm_drv_is_avs_enabled(dev);
if (!dev)
return -EINVAL;
@@ -355,7 +369,8 @@
if (msm_spm_debug_mask & MSM_SPM_DEBUG_VCTL)
pr_info("%s: requesting vlevel %#x\n", __func__, vlevel);
- msm_spm_drv_disable_avs(dev);
+ if (avs_enabled)
+ msm_spm_drv_disable_avs(dev);
/* Kick the state machine back to idle */
dev->reg_shadow[MSM_SPM_REG_SAW2_RST] = 1;
@@ -384,14 +399,18 @@
__func__, timeout_us);
/* Set AVS min/max */
- msm_spm_drv_set_avs_vlevel(dev, vlevel);
- msm_spm_drv_enable_avs(dev);
+ if (avs_enabled) {
+ msm_spm_drv_set_avs_vlevel(dev, vlevel);
+ msm_spm_drv_enable_avs(dev);
+ }
return 0;
set_vdd_bail:
- msm_spm_drv_enable_avs(dev);
- pr_err("%s: failed %#x, remaining timeout %u us, vlevel %#x\n",
+ if (avs_enabled)
+ msm_spm_drv_enable_avs(dev);
+
+ pr_err("%s: failed %#x, remaining timeout %uus, vlevel %#x\n",
__func__, vlevel, timeout_us, new_level);
return -EIO;
}
diff --git a/arch/arm/mach-msm/spm.c b/arch/arm/mach-msm/spm.c
index 3d90678..8337fd1 100644
--- a/arch/arm/mach-msm/spm.c
+++ b/arch/arm/mach-msm/spm.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2010-2012, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -235,6 +235,12 @@
return -EIO;
}
+unsigned int msm_spm_get_vdd(unsigned int cpu)
+{
+ struct msm_spm_device *dev = &per_cpu(msm_spm_devices, cpu);
+ return dev->reg_shadow[MSM_SPM_REG_SAW_VCTL];
+}
+
void msm_spm_reinit(void)
{
struct msm_spm_device *dev = &__get_cpu_var(msm_spm_devices);
diff --git a/arch/arm/mach-msm/subsystem_restart.c b/arch/arm/mach-msm/subsystem_restart.c
index a58f2ab..e5cc4ec 100644
--- a/arch/arm/mach-msm/subsystem_restart.c
+++ b/arch/arm/mach-msm/subsystem_restart.c
@@ -139,7 +139,7 @@
struct module *owner;
int count;
int id;
- void *restart_order;
+ struct subsys_soc_restart_order *restart_order;
#ifdef CONFIG_DEBUG_FS
struct dentry *dentry;
#endif
@@ -444,6 +444,127 @@
return dev ? to_subsys(dev) : NULL;
}
+static int subsys_start(struct subsys_device *subsys)
+{
+ int ret;
+
+ ret = subsys->desc->start(subsys->desc);
+ if (!ret)
+ subsys_set_state(subsys, SUBSYS_ONLINE);
+
+ return ret;
+}
+
+static void subsys_stop(struct subsys_device *subsys)
+{
+ subsys->desc->stop(subsys->desc);
+ subsys_set_state(subsys, SUBSYS_OFFLINE);
+}
+
+static struct subsys_tracking *subsys_get_track(struct subsys_device *subsys)
+{
+ struct subsys_soc_restart_order *order = subsys->restart_order;
+
+ if (restart_level != RESET_SUBSYS_INDEPENDENT && order)
+ return &order->track;
+ else
+ return &subsys->track;
+}
+
+/**
+ * subsytem_get() - Boot a subsystem
+ * @name: pointer to a string containing the name of the subsystem to boot
+ *
+ * This function returns a pointer if it succeeds. If an error occurs an
+ * ERR_PTR is returned.
+ *
+ * If this feature is disable, the value %NULL will be returned.
+ */
+void *subsystem_get(const char *name)
+{
+ struct subsys_device *subsys;
+ struct subsys_device *subsys_d;
+ int ret;
+ void *retval;
+ struct subsys_tracking *track;
+
+ if (!name)
+ return NULL;
+
+ subsys = retval = find_subsys(name);
+ if (!subsys)
+ return ERR_PTR(-ENODEV);
+ if (!try_module_get(subsys->owner)) {
+ retval = ERR_PTR(-ENODEV);
+ goto err_module;
+ }
+
+ subsys_d = subsystem_get(subsys->desc->depends_on);
+ if (IS_ERR(subsys_d)) {
+ retval = subsys_d;
+ goto err_depends;
+ }
+
+ track = subsys_get_track(subsys);
+ mutex_lock(&track->lock);
+ if (!subsys->count) {
+ ret = subsys_start(subsys);
+ if (ret) {
+ retval = ERR_PTR(ret);
+ goto err_start;
+ }
+ }
+ subsys->count++;
+ mutex_unlock(&track->lock);
+ return retval;
+err_start:
+ mutex_unlock(&track->lock);
+ subsystem_put(subsys_d);
+err_depends:
+ module_put(subsys->owner);
+err_module:
+ put_device(&subsys->dev);
+ return retval;
+}
+EXPORT_SYMBOL(subsystem_get);
+
+/**
+ * subsystem_put() - Shutdown a subsystem
+ * @peripheral_handle: pointer from a previous call to subsystem_get()
+ *
+ * This doesn't imply that a subsystem is shutdown until all callers of
+ * subsystem_get() have called subsystem_put().
+ */
+void subsystem_put(void *subsystem)
+{
+ struct subsys_device *subsys_d, *subsys = subsystem;
+ struct subsys_tracking *track;
+
+ if (IS_ERR_OR_NULL(subsys))
+ return;
+
+ track = subsys_get_track(subsys);
+ mutex_lock(&track->lock);
+ if (WARN(!subsys->count, "%s: %s: Reference count mismatch\n",
+ subsys->desc->name, __func__))
+ goto err_out;
+ if (!--subsys->count)
+ subsys_stop(subsys);
+ mutex_unlock(&track->lock);
+
+ subsys_d = find_subsys(subsys->desc->depends_on);
+ if (subsys_d) {
+ subsystem_put(subsys_d);
+ put_device(&subsys_d->dev);
+ }
+ module_put(subsys->owner);
+ put_device(&subsys->dev);
+ return;
+err_out:
+ mutex_unlock(&track->lock);
+}
+EXPORT_SYMBOL(subsystem_put);
+
static void subsystem_restart_wq_func(struct work_struct *work)
{
struct subsys_device *dev = container_of(work,
@@ -513,17 +634,12 @@
{
struct subsys_desc *desc = dev->desc;
const char *name = dev->desc->name;
- struct subsys_soc_restart_order *order = dev->restart_order;
struct subsys_tracking *track;
unsigned long flags;
- if (restart_level != RESET_SUBSYS_INDEPENDENT && order)
- track = &order->track;
- else
- track = &dev->track;
-
pr_debug("Restarting %s [level=%d]!\n", desc->name, restart_level);
+ track = subsys_get_track(dev);
/*
* Allow drivers to call subsystem_restart{_dev}() as many times as
* they want up until the point where the subsystem is shutdown.
@@ -620,6 +736,11 @@
if (!strcmp(cmp, "restart")) {
if (subsystem_restart_dev(subsys))
return -EIO;
+ } else if (!strcmp(cmp, "get")) {
+ if (subsystem_get(subsys->desc->name))
+ return -EIO;
+ } else if (!strcmp(cmp, "put")) {
+ subsystem_put(subsys);
} else {
return -EINVAL;
}
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 0bce4e9..978b63b 100644
--- a/drivers/char/diag/diagfwd.c
+++ b/drivers/char/diag/diagfwd.c
@@ -115,6 +115,7 @@
return APQ8064_TOOLS_ID;
case MSM_CPU_8930:
case MSM_CPU_8930AA:
+ case MSM_CPU_8930AB:
return MSM8930_TOOLS_ID;
case MSM_CPU_8974:
return MSM8974_TOOLS_ID;
@@ -142,6 +143,7 @@
case MSM_CPU_8064AB:
case MSM_CPU_8930:
case MSM_CPU_8930AA:
+ case MSM_CPU_8930AB:
case MSM_CPU_8627:
case MSM_CPU_9615:
case MSM_CPU_8974:
@@ -210,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);
}
@@ -355,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/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c
index 78161b6..785ba6c 100644
--- a/drivers/cpufreq/cpufreq_ondemand.c
+++ b/drivers/cpufreq/cpufreq_ondemand.c
@@ -869,11 +869,7 @@
static void dbs_input_event(struct input_handle *handle, unsigned int type,
unsigned int code, int value)
{
- int i, j;
- struct cpumask cpus_scheduled;
- struct cpu_dbs_info_s *dbs_info;
- cpumask_clear(&cpus_scheduled);
-
+ int i;
if ((dbs_tuners_ins.powersave_bias == POWERSAVE_BIAS_MAXLEVEL) ||
(dbs_tuners_ins.powersave_bias == POWERSAVE_BIAS_MINLEVEL)) {
@@ -882,23 +878,7 @@
}
for_each_online_cpu(i) {
- dbs_info = &per_cpu(od_cpu_dbs_info, i);
-
- if (!dbs_info->cur_policy) {
- pr_err("Dbs policy is NULL\n");
- continue;
- }
-
- for_each_cpu(j, &cpus_scheduled) {
- if (cpumask_test_cpu(j, dbs_info->cur_policy->cpus))
- goto skip_schedule;
- }
- cpumask_set_cpu(i, &cpus_scheduled);
queue_work_on(i, input_wq, &per_cpu(dbs_refresh_work, i));
-
- /* This CPU is already running at new frequency */
-skip_schedule:
- ;
}
}
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 930d233..67cb34a 100644
--- a/drivers/gpu/msm/adreno.c
+++ b/drivers/gpu/msm/adreno.c
@@ -731,101 +731,6 @@
return ret;
}
-static void adreno_of_free_bus_scale_info(struct msm_bus_scale_pdata *pdata)
-{
- int i;
-
- if (pdata == NULL)
- return;
-
- for (i = 0; pdata->usecase && i < pdata->num_usecases; i++)
- kfree(pdata->usecase[i].vectors);
-
- kfree(pdata->usecase);
- kfree(pdata);
-}
-
-struct msm_bus_scale_pdata *adreno_of_get_bus_scale(struct device_node *node)
-{
- static int bus_vectors_src[3] = {MSM_BUS_MASTER_GRAPHICS_3D,
- MSM_BUS_MASTER_GRAPHICS_3D_PORT1, MSM_BUS_MASTER_V_OCMEM_GFX3D};
- static int bus_vectors_dst[2] = {MSM_BUS_SLAVE_EBI_CH0,
- MSM_BUS_SLAVE_OCMEM};
- const unsigned int *vectors;
- struct msm_bus_scale_pdata *pdata;
- int i, j, len, num_paths;
- int ret = -EINVAL;
-
- pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
-
- if (!pdata) {
- KGSL_CORE_ERR("kzalloc(%d) failed\n", sizeof(*pdata));
- return ERR_PTR(-ENOMEM);
- }
-
- if (adreno_of_read_property(node, "qcom,grp3d-num-bus-scale-usecases",
- &pdata->num_usecases)) {
- pdata->num_usecases = 0;
- goto err;
- }
-
- pdata->usecase = kzalloc(pdata->num_usecases *
- sizeof(struct msm_bus_paths), GFP_KERNEL);
-
- if (pdata->usecase == NULL) {
- KGSL_CORE_ERR("kzalloc (%d) failed\n",
- pdata->num_usecases * sizeof(struct msm_bus_paths));
- ret = -ENOMEM;
- goto err;
- }
-
- if (adreno_of_read_property(node, "qcom,grp3d-num-vectors-per-usecase",
- &num_paths))
- goto err;
-
- vectors = of_get_property(node, "qcom,grp3d-vectors", &len);
-
- if (len != pdata->num_usecases * num_paths *
- sizeof(struct msm_bus_vectors)) {
- KGSL_CORE_ERR("Invalid size for the bus scale vectors\n");
- goto err;
- }
-
- for (i = 0; i < pdata->num_usecases; i++) {
- pdata->usecase[i].num_paths = num_paths;
- pdata->usecase[i].vectors = kzalloc(num_paths *
- sizeof(struct msm_bus_vectors),
- GFP_KERNEL);
- if (!pdata->usecase[i].vectors) {
- KGSL_CORE_ERR("kzalloc(%d) failed\n",
- num_paths * sizeof(struct msm_bus_vectors));
- ret = -ENOMEM;
- goto err;
- }
- for (j = 0; j < num_paths; j++) {
- int index = (i * num_paths + j) * 4;
- pdata->usecase[i].vectors[j].src =
- bus_vectors_src[be32_to_cpu(vectors[index])];
- pdata->usecase[i].vectors[j].dst =
- bus_vectors_dst[
- be32_to_cpu(vectors[index + 1])];
- pdata->usecase[i].vectors[j].ab =
- be32_to_cpu(vectors[index + 2]);
- pdata->usecase[i].vectors[j].ib =
- KGSL_CONVERT_TO_MBPS(
- be32_to_cpu(vectors[index + 3]));
- }
- }
-
- pdata->name = "grp3d";
-
- return pdata;
-
-err:
- adreno_of_free_bus_scale_info(pdata);
-
- return ERR_PTR(ret);
-}
static struct msm_dcvs_core_info *adreno_of_get_dcvs(struct device_node *parent)
{
@@ -1121,7 +1026,7 @@
/* Bus Scale Data */
- pdata->bus_scale_table = adreno_of_get_bus_scale(pdev->dev.of_node);
+ pdata->bus_scale_table = msm_bus_cl_get_pdata(pdev);
if (IS_ERR_OR_NULL(pdata->bus_scale_table)) {
ret = PTR_ERR(pdata->bus_scale_table);
goto err;
@@ -1142,7 +1047,6 @@
err:
if (pdata) {
- adreno_of_free_bus_scale_info(pdata->bus_scale_table);
if (pdata->core_info)
kfree(pdata->core_info->freq_tbl);
kfree(pdata->core_info);
@@ -1496,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
@@ -1931,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)) {
@@ -1960,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. */
@@ -1980,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;
@@ -2233,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++) {
@@ -2289,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;
}
/*
@@ -2372,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 7032db0..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)
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_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/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 5a1eec7..7b28e9d 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -79,3 +79,4 @@
obj-$(CONFIG_TOUCHSCREEN_MSM_LEGACY) += msm_touch.o
obj-$(CONFIG_TOUCHSCREEN_CY8C_TS) += cy8c_ts.o
obj-$(CONFIG_TOUCHSCREEN_CYTTSP_I2C_QC) += cyttsp-i2c-qc.o
+obj-$(CONFIG_TOUCHSCREEN_FT5X06) += ft5x06_ts.o
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 a084a6d..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[] = {
@@ -421,7 +423,16 @@
{
.src = MSM_BUS_MASTER_VFE,
.dst = MSM_BUS_SLAVE_EBI_CH0,
- .ab = 154275840,
+ .ab = 274406400,
+ .ib = 617103360,
+ },
+};
+
+static struct msm_bus_vectors vfe_liveshot_vectors[] = {
+ {
+ .src = MSM_BUS_MASTER_VFE,
+ .dst = MSM_BUS_SLAVE_EBI_CH0,
+ .ab = 348192000,
.ib = 617103360,
},
};
@@ -465,6 +476,10 @@
ARRAY_SIZE(vfe_zsl_vectors),
vfe_zsl_vectors,
},
+ {
+ ARRAY_SIZE(vfe_liveshot_vectors),
+ vfe_liveshot_vectors,
+ },
};
static struct msm_bus_scale_pdata vfe_bus_client_pdata = {
@@ -564,23 +579,6 @@
VFE_IRQ_MASK_0);
}
- if (share_ctrl->axi_ref_cnt == 1) {
- atomic_set(&share_ctrl->handle_common_irq, 0);
- msm_camera_io_w(VFE_DISABLE_ALL_IRQS,
- share_ctrl->vfebase + VFE_IRQ_MASK_0);
- msm_camera_io_w(VFE_DISABLE_ALL_IRQS,
- share_ctrl->vfebase + VFE_IRQ_MASK_1);
-
- /* clear all pending interrupts*/
- msm_camera_io_w(0xFFFFFFFF,
- share_ctrl->vfebase + VFE_IRQ_CLEAR_0);
- msm_camera_io_w(0xFFFFFFFF,
- share_ctrl->vfebase + VFE_IRQ_CLEAR_1);
- /* Ensure the write order while writing
- to the command register using the barrier */
- msm_camera_io_w_mb(1,
- share_ctrl->vfebase + VFE_IRQ_CMD);
- }
}
static void vfe40_stop(struct vfe40_ctrl_type *vfe40_ctrl)
@@ -822,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 +
@@ -854,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 +
@@ -877,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 +
@@ -898,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,
@@ -1090,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);
@@ -1115,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;
@@ -1148,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;
@@ -1193,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);
@@ -1860,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__);
@@ -1894,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__);
@@ -2034,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__);
@@ -2050,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__);
@@ -2683,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;
@@ -2695,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;
@@ -2749,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 */
@@ -2765,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;
@@ -3338,42 +3382,51 @@
share_ctrl->liveshot_state =
VFE_STATE_STARTED;
+ msm_camera_io_w_mb(1, share_ctrl->vfebase +
+ VFE_REG_UPDATE_CMD);
}
break;
case VFE_STATE_STARTED:
- share_ctrl->vfe_capture_count--;
- if (!share_ctrl->vfe_capture_count &&
+ CDBG("%s disabling liveshot output\n", __func__);
+ if (share_ctrl->vfe_capture_count >= 1) {
+ if (share_ctrl->vfe_capture_count == 1 &&
(share_ctrl->comp_output_mode &
VFE40_OUTPUT_MODE_PRIMARY)) {
msm_camera_io_w(0, share_ctrl->vfebase +
vfe40_AXI_WM_CFG[
- share_ctrl->outpath.out0.ch0]);
+ share_ctrl->outpath.out0.ch0]);
msm_camera_io_w(0, share_ctrl->vfebase +
vfe40_AXI_WM_CFG[
- share_ctrl->outpath.out0.ch1]);
+ share_ctrl->outpath.out0.ch1]);
+ msm_camera_io_w_mb(1, share_ctrl->vfebase +
+ VFE_REG_UPDATE_CMD);
+ }
+ share_ctrl->vfe_capture_count--;
}
break;
case VFE_STATE_STOP_REQUESTED:
+ CDBG("%s disabling liveshot output from stream off\n",
+ __func__);
if (share_ctrl->comp_output_mode &
VFE40_OUTPUT_MODE_PRIMARY) {
/* Stop requested, stop write masters, and
* trigger REG_UPDATE. Send STOP_LS_ACK in
* next reg update. */
- msm_camera_io_w(0, share_ctrl->vfebase +
- vfe40_AXI_WM_CFG[
- share_ctrl->outpath.out0.ch0]);
- msm_camera_io_w(0, share_ctrl->vfebase +
- vfe40_AXI_WM_CFG[
- share_ctrl->outpath.out0.ch1]);
- share_ctrl->liveshot_state = VFE_STATE_STOPPED;
- msm_camera_io_w_mb(1, share_ctrl->vfebase +
+ msm_camera_io_w(0, share_ctrl->vfebase +
+ vfe40_AXI_WM_CFG[
+ share_ctrl->outpath.out0.ch0]);
+ msm_camera_io_w(0, share_ctrl->vfebase +
+ vfe40_AXI_WM_CFG[
+ share_ctrl->outpath.out0.ch1]);
+ share_ctrl->liveshot_state = VFE_STATE_STOPPED;
+ msm_camera_io_w_mb(1, share_ctrl->vfebase +
VFE_REG_UPDATE_CMD);
}
break;
case VFE_STATE_STOPPED:
CDBG("%s Sending STOP_LS ACK\n", __func__);
vfe40_send_isp_msg(&vfe40_ctrl->subdev,
- share_ctrl->vfeFrameId, MSG_ID_STOP_LS_ACK);
+ share_ctrl->vfeFrameId, MSG_ID_STOP_LS_ACK);
share_ctrl->liveshot_state = VFE_STATE_IDLE;
break;
default:
@@ -4010,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;
@@ -4096,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;
@@ -4113,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;
@@ -4123,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);
}
}
@@ -4167,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);
}
}
@@ -4277,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) {
@@ -4317,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) {
@@ -4436,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);
@@ -4517,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) |
@@ -4605,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,
@@ -4874,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 &&
@@ -4917,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;
@@ -4940,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/Makefile b/drivers/media/video/msm_vidc/Makefile
index 2a1f40f..9b3af9d 100644
--- a/drivers/media/video/msm_vidc/Makefile
+++ b/drivers/media/video/msm_vidc/Makefile
@@ -5,5 +5,6 @@
msm_venc.o \
msm_smem.o \
msm_vidc_debug.o \
+ msm_vidc_ssr.o \
vidc_hal.o \
vidc_hal_interrupt_handler.o \
diff --git a/drivers/media/video/msm_vidc/msm_v4l2_vidc.c b/drivers/media/video/msm_vidc/msm_v4l2_vidc.c
index a608e1ab..7d44fea 100644
--- a/drivers/media/video/msm_vidc/msm_v4l2_vidc.c
+++ b/drivers/media/video/msm_vidc/msm_v4l2_vidc.c
@@ -29,6 +29,7 @@
#include "msm_vidc_debug.h"
#include "vidc_hal_api.h"
#include "msm_smem.h"
+#include "msm_vidc_ssr.h"
#define BASE_DEVICE_NUMBER 32
#define SHARED_QSIZE 0x1000000
@@ -441,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 {
@@ -471,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:
@@ -498,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)
@@ -566,25 +616,29 @@
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);
+ &buffer_info);
if (rc)
dprintk(VIDC_ERR,
"Failed Release buffer: %d, %d, %d\n",
@@ -592,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;
}
@@ -608,6 +664,7 @@
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);
@@ -619,9 +676,12 @@
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);
}
}
@@ -695,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);
@@ -703,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],
@@ -744,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;
@@ -770,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",
@@ -788,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);
@@ -809,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,
@@ -1243,6 +1347,9 @@
core->debugfs_root = msm_vidc_debugfs_init_core(
core, vidc_driver->debugfs_root);
pdev->dev.platform_data = core;
+ rc = msm_vidc_ssr_init(core);
+ if (rc < 0)
+ dprintk(VIDC_ERR, "msm_vidc : Sub Systrem Restart failed\n");
return rc;
err_cores_exceeded:
@@ -1271,6 +1378,7 @@
vidc_hal_delete_device(core->device);
video_unregister_device(&core->vdev[MSM_VIDC_ENCODER].vdev);
video_unregister_device(&core->vdev[MSM_VIDC_DECODER].vdev);
+ rc = msm_vidc_ssr_uninit(core);
v4l2_device_unregister(&core->v4l2_dev);
if (core->resources.ocmem.handle)
ocmem_notifier_unregister(core->resources.ocmem.handle,
diff --git a/drivers/media/video/msm_vidc/msm_vdec.c b/drivers/media/video/msm_vidc/msm_vdec.c
index 49b28ae..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,36 +372,57 @@
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,
+ "Core %p in bad state, ignoring release output buf\n",
+ 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);
+ (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);
break;
}
+exit:
return rc;
}
@@ -467,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,
@@ -481,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;
@@ -504,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,
@@ -517,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;
@@ -638,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;
@@ -656,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");
@@ -667,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].
@@ -692,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);
@@ -842,6 +897,8 @@
int msm_vdec_cmd(struct msm_vidc_inst *inst, struct v4l2_decoder_cmd *dec)
{
int rc = 0;
+ struct v4l2_event dqevent = {0};
+ struct msm_vidc_core *core = inst->core;
switch (dec->cmd) {
case V4L2_DEC_QCOM_CMD_FLUSH:
rc = msm_comm_flush(inst, dec->flags);
@@ -853,6 +910,15 @@
rc = msm_comm_release_persist_buffers(inst);
if (rc)
pr_err("Failed to release persist buffers: %d\n", rc);
+ if (inst->state == MSM_VIDC_CORE_INVALID ||
+ core->state == VIDC_CORE_INVALID) {
+ dprintk(VIDC_ERR,
+ "Core %p in bad state, Sending CLOSE event\n",
+ core);
+ dqevent.type = V4L2_EVENT_MSM_VIDC_CLOSE_DONE;
+ v4l2_event_queue_fh(&inst->event_handler, &dqevent);
+ goto exit;
+ }
rc = msm_comm_try_state(inst, MSM_VIDC_CLOSE_DONE);
break;
default:
@@ -968,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 4573018..0ea2a60 100644
--- a/drivers/media/video/msm_vidc/msm_venc.c
+++ b/drivers/media/video/msm_vidc/msm_venc.c
@@ -1260,11 +1260,20 @@
int msm_venc_cmd(struct msm_vidc_inst *inst, struct v4l2_encoder_cmd *enc)
{
int rc = 0;
+ struct v4l2_event dqevent = {0};
+ struct msm_vidc_core *core;
+ core = inst->core;
switch (enc->cmd) {
case V4L2_ENC_QCOM_CMD_FLUSH:
rc = msm_comm_flush(inst, enc->flags);
break;
case V4L2_ENC_CMD_STOP:
+ if (inst->state == MSM_VIDC_CORE_INVALID ||
+ core->state == VIDC_CORE_INVALID) {
+ dqevent.type = V4L2_EVENT_MSM_VIDC_CLOSE_DONE;
+ v4l2_event_queue_fh(&inst->event_handler, &dqevent);
+ return rc;
+ }
rc = msm_comm_try_state(inst, MSM_VIDC_CLOSE_DONE);
break;
}
diff --git a/drivers/media/video/msm_vidc/msm_vidc.c b/drivers/media/video/msm_vidc/msm_vidc.c
index 8ff7714..7ceb017 100644
--- a/drivers/media/video/msm_vidc/msm_vidc.c
+++ b/drivers/media/video/msm_vidc/msm_vidc.c
@@ -403,6 +403,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,12 +540,14 @@
list_del(&inst->list);
}
mutex_unlock(&core->sync_lock);
- rc = msm_comm_try_state(inst, MSM_VIDC_CORE_UNINIT);
+ 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 4ff28d62..54c3d5f 100644
--- a/drivers/media/video/msm_vidc/msm_vidc_common.c
+++ b/drivers/media/video/msm_vidc/msm_vidc_common.c
@@ -23,6 +23,7 @@
#include "vidc_hal_api.h"
#include "msm_smem.h"
#include "msm_vidc_debug.h"
+#include "msm_vidc_ssr.h"
#define HW_RESPONSE_TIMEOUT (5 * 60 * 1000)
@@ -312,7 +313,7 @@
complete(&core->completions[SYS_MSG_INDEX(cmd)]);
}
-static inline void change_inst_state(struct msm_vidc_inst *inst,
+void change_inst_state(struct msm_vidc_inst *inst,
enum instance_state state)
{
unsigned long flags;
@@ -508,6 +509,13 @@
}
}
+static void handle_sys_watchdog_timeout(enum command_response cmd, void *data)
+{
+ subsystem_restart("msm_vidc");
+ dprintk(VIDC_ERR,
+ "msm_vidc: Sub System Restart initiated\n");
+}
+
static void handle_session_close(enum command_response cmd, void *data)
{
@@ -677,7 +685,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;
@@ -743,6 +751,9 @@
case SESSION_GET_SEQ_HDR_DONE:
handle_seq_hdr_done(cmd, data);
break;
+ case SYS_WATCHDOG_TIMEOUT:
+ handle_sys_watchdog_timeout(cmd, data);
+ break;
default:
dprintk(VIDC_ERR, "response unhandled\n");
break;
@@ -854,7 +865,7 @@
return rc;
}
-static void msm_comm_unload_fw(struct msm_vidc_core *core)
+void msm_comm_unload_fw(struct msm_vidc_core *core)
{
if (!core) {
dprintk(VIDC_ERR, "Invalid paramter: %p\n", core);
@@ -901,7 +912,7 @@
return rc;
}
-static int msm_comm_unset_ocmem(struct msm_vidc_core *core)
+int msm_comm_unset_ocmem(struct msm_vidc_core *core)
{
struct vidc_resource_hdr rhdr;
int rc = 0;
@@ -967,7 +978,7 @@
return rc;
}
-static int msm_comm_free_ocmem(struct msm_vidc_core *core)
+int msm_comm_free_ocmem(struct msm_vidc_core *core)
{
int rc = 0;
if (core->resources.ocmem.buf) {
@@ -1343,30 +1354,43 @@
{
int rc = 0;
int flipped_state;
+ struct msm_vidc_core *core;
if (!inst) {
dprintk(VIDC_ERR,
- "Invalid instance pointer = %p\n", inst);
+ "Invalid instance pointer = %p\n", inst);
return -EINVAL;
}
dprintk(VIDC_DBG,
- "Trying to move inst: %p from: 0x%x to 0x%x\n",
- inst, inst->state, state);
+ "Trying to move inst: %p from: 0x%x to 0x%x\n",
+ inst, inst->state, state);
+ core = inst->core;
+ if (!core) {
+ dprintk(VIDC_ERR,
+ "Invalid core pointer = %p\n", inst);
+ return -EINVAL;
+ }
mutex_lock(&inst->sync_lock);
+ if (inst->state == MSM_VIDC_CORE_INVALID ||
+ core->state == VIDC_CORE_INVALID) {
+ dprintk(VIDC_ERR,
+ "Core is in bad state can't change the state");
+ goto exit;
+ }
flipped_state = inst->state;
if (flipped_state < MSM_VIDC_STOP
- && state > MSM_VIDC_STOP) {
+ && state > MSM_VIDC_STOP) {
flipped_state = MSM_VIDC_STOP + (MSM_VIDC_STOP - flipped_state);
flipped_state &= 0xFFFE;
flipped_state = flipped_state - 1;
} else if (flipped_state > MSM_VIDC_STOP
- && state < MSM_VIDC_STOP) {
+ && state < MSM_VIDC_STOP) {
flipped_state = MSM_VIDC_STOP -
- (flipped_state - MSM_VIDC_STOP + 1);
+ (flipped_state - MSM_VIDC_STOP + 1);
flipped_state &= 0xFFFE;
flipped_state = flipped_state - 1;
}
dprintk(VIDC_DBG,
- "flipped_state = 0x%x\n", flipped_state);
+ "flipped_state = 0x%x\n", flipped_state);
switch (flipped_state) {
case MSM_VIDC_CORE_UNINIT_DONE:
case MSM_VIDC_CORE_INIT:
@@ -1421,14 +1445,14 @@
if (rc || state <= inst->state)
break;
dprintk(VIDC_DBG,
- "Moving to release resources done state\n");
+ "Moving to release resources done state\n");
case MSM_VIDC_CLOSE:
rc = msm_comm_session_close(flipped_state, inst);
if (rc || state <= inst->state)
break;
case MSM_VIDC_CLOSE_DONE:
rc = wait_for_state(inst, flipped_state, MSM_VIDC_CLOSE_DONE,
- SESSION_END_DONE);
+ SESSION_END_DONE);
if (rc || state <= inst->state)
break;
case MSM_VIDC_CORE_UNINIT:
@@ -1441,11 +1465,12 @@
rc = -EINVAL;
break;
}
+exit:
mutex_unlock(&inst->sync_lock);
if (rc)
dprintk(VIDC_ERR,
- "Failed to move from state: %d to %d\n",
- inst->state, state);
+ "Failed to move from state: %d to %d\n",
+ inst->state, state);
return rc;
}
@@ -1456,13 +1481,25 @@
struct msm_vidc_inst *inst;
struct vb2_buf_entry *entry;
struct vidc_frame_data frame_data;
+ struct msm_vidc_core *core;
q = vb->vb2_queue;
inst = q->drv_priv;
-
if (!inst || !vb) {
dprintk(VIDC_ERR, "Invalid input: %p, %p\n", inst, vb);
return -EINVAL;
}
+ core = inst->core;
+ if (!core) {
+ dprintk(VIDC_ERR,
+ "Invalid input: %p, %p, %p\n", inst, core, vb);
+ return -EINVAL;
+ }
+
+ if (inst->state == MSM_VIDC_CORE_INVALID ||
+ core->state == VIDC_CORE_INVALID) {
+ dprintk(VIDC_ERR, "Core is in bad state. Can't Queue\n");
+ return -EINVAL;
+ }
if (inst->state != MSM_VIDC_START_DONE) {
entry = kzalloc(sizeof(*entry), GFP_KERNEL);
if (!entry) {
@@ -1508,14 +1545,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);
@@ -1559,6 +1596,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);
@@ -1580,7 +1623,6 @@
mutex_unlock(&inst->sync_lock);
return rc;
}
-
int msm_comm_release_scratch_buffers(struct msm_vidc_inst *inst)
{
struct msm_smem *handle;
@@ -1589,6 +1631,18 @@
struct vidc_buffer_addr_info buffer_info;
int rc = 0;
unsigned long flags;
+ struct msm_vidc_core *core;
+ if (!inst) {
+ dprintk(VIDC_ERR,
+ "Invalid instance pointer = %p\n", inst);
+ return -EINVAL;
+ }
+ core = inst->core;
+ if (!core) {
+ dprintk(VIDC_ERR,
+ "Invalid core pointer = %p\n", core);
+ return -EINVAL;
+ }
spin_lock_irqsave(&inst->lock, flags);
if (!list_empty(&inst->internalbufs)) {
list_for_each_safe(ptr, next, &inst->internalbufs) {
@@ -1599,13 +1653,17 @@
buffer_info.buffer_type = HAL_BUFFER_INTERNAL_SCRATCH;
buffer_info.num_buffers = 1;
buffer_info.align_device_addr = handle->device_addr;
- rc = vidc_hal_session_release_buffers(
- (void *) inst->session, &buffer_info);
- if (rc)
- dprintk(VIDC_WARN,
- "Failed to release scratch buffer: 0x%x, %d",
- buffer_info.align_device_addr,
- buffer_info.buffer_size);
+ if (inst->state != MSM_VIDC_CORE_INVALID &&
+ core->state != VIDC_CORE_INVALID) {
+ rc = vidc_hal_session_release_buffers(
+ (void *) inst->session,
+ &buffer_info);
+ if (rc)
+ dprintk(VIDC_WARN,
+ "Rel scrtch buf fail:0x%x, %d",
+ buffer_info.align_device_addr,
+ buffer_info.buffer_size);
+ }
list_del(&buf->list);
spin_unlock_irqrestore(&inst->lock, flags);
msm_smem_free(inst->mem_client, buf->handle);
@@ -1625,23 +1683,39 @@
struct vidc_buffer_addr_info buffer_info;
int rc = 0;
unsigned long flags;
+ struct msm_vidc_core *core;
+ if (!inst) {
+ dprintk(VIDC_ERR,
+ "Invalid instance pointer = %p\n", inst);
+ return -EINVAL;
+ }
+ core = inst->core;
+ if (!core) {
+ dprintk(VIDC_ERR,
+ "Invalid core pointer = %p\n", core);
+ return -EINVAL;
+ }
spin_lock_irqsave(&inst->lock, flags);
if (!list_empty(&inst->persistbufs)) {
list_for_each_safe(ptr, next, &inst->persistbufs) {
buf = list_entry(ptr, struct internal_buf,
- list);
+ list);
handle = buf->handle;
buffer_info.buffer_size = handle->size;
buffer_info.buffer_type = HAL_BUFFER_INTERNAL_PERSIST;
buffer_info.num_buffers = 1;
buffer_info.align_device_addr = handle->device_addr;
- rc = vidc_hal_session_release_buffers(
- (void *) inst->session, &buffer_info);
- if (rc)
- dprintk(VIDC_WARN,
- "Failed to release persist buffer 0x%x, %d\n",
- buffer_info.align_device_addr,
- buffer_info.buffer_size);
+ if (inst->state != MSM_VIDC_CORE_INVALID &&
+ core->state != VIDC_CORE_INVALID) {
+ rc = vidc_hal_session_release_buffers(
+ (void *) inst->session,
+ &buffer_info);
+ if (rc)
+ dprintk(VIDC_WARN,
+ "Rel prst buf fail:0x%x, %d",
+ buffer_info.align_device_addr,
+ buffer_info.buffer_size);
+ }
list_del(&buf->list);
spin_unlock_irqrestore(&inst->lock, flags);
msm_smem_free(inst->mem_client, buf->handle);
@@ -1653,6 +1727,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;
@@ -1779,6 +1876,50 @@
return rc;
}
+static void msm_comm_flush_in_invalid_state(struct msm_vidc_inst *inst)
+{
+ struct v4l2_event dqevent = {0};
+ struct list_head *ptr, *next;
+ struct vb2_buffer *vb;
+ if (!list_empty(&inst->bufq[CAPTURE_PORT].
+ vb2_bufq.queued_list)) {
+ list_for_each_safe(ptr, next,
+ &inst->bufq[CAPTURE_PORT].
+ vb2_bufq.queued_list) {
+ vb = container_of(ptr,
+ struct vb2_buffer,
+ queued_entry);
+ if (vb) {
+ vb->v4l2_planes[0].bytesused = 0;
+ mutex_lock(&inst->bufq[CAPTURE_PORT].lock);
+ vb2_buffer_done(vb,
+ VB2_BUF_STATE_DONE);
+ mutex_unlock(&inst->bufq[CAPTURE_PORT].lock);
+ }
+ }
+ }
+ if (!list_empty(&inst->bufq[OUTPUT_PORT].
+ vb2_bufq.queued_list)) {
+ list_for_each_safe(ptr, next,
+ &inst->bufq[OUTPUT_PORT].
+ vb2_bufq.queued_list) {
+ vb = container_of(ptr,
+ struct vb2_buffer,
+ queued_entry);
+ if (vb) {
+ vb->v4l2_planes[0].bytesused = 0;
+ mutex_lock(&inst->bufq[OUTPUT_PORT].lock);
+ vb2_buffer_done(vb,
+ VB2_BUF_STATE_DONE);
+ mutex_unlock(&inst->bufq[OUTPUT_PORT].lock);
+ }
+ }
+ }
+ dqevent.type = V4L2_EVENT_MSM_VIDC_FLUSH_DONE;
+ dqevent.id = 0;
+ v4l2_event_queue_fh(&inst->event_handler, &dqevent);
+ return;
+}
int msm_comm_flush(struct msm_vidc_inst *inst, u32 flags)
{
int rc = 0;
@@ -1787,12 +1928,34 @@
struct list_head *ptr, *next;
struct vb2_buf_entry *temp;
struct mutex *lock;
+ struct msm_vidc_core *core;
+ if (!inst) {
+ dprintk(VIDC_ERR,
+ "Invalid instance pointer = %p\n", inst);
+ return -EINVAL;
+ }
+ core = inst->core;
+ if (!core) {
+ dprintk(VIDC_ERR,
+ "Invalid core pointer = %p\n", core);
+ return -EINVAL;
+ }
+
ip_flush = flags & V4L2_QCOM_CMD_FLUSH_OUTPUT;
op_flush = flags & V4L2_QCOM_CMD_FLUSH_CAPTURE;
+
if (ip_flush && !op_flush) {
dprintk(VIDC_WARN, "Input only flush not supported\n");
return 0;
}
+ if (inst->state == MSM_VIDC_CORE_INVALID ||
+ core->state == VIDC_CORE_INVALID) {
+ dprintk(VIDC_ERR,
+ "Core %p and inst %p are in bad state\n",
+ core, inst);
+ msm_comm_flush_in_invalid_state(inst);
+ }
+
mutex_lock(&inst->sync_lock);
if (inst->in_reconfig && !ip_flush && op_flush) {
if (!list_empty(&inst->pendingq)) {
diff --git a/drivers/media/video/msm_vidc/msm_vidc_common.h b/drivers/media/video/msm_vidc/msm_vidc_common.h
index 0708724..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);
@@ -34,6 +36,11 @@
int msm_comm_flush(struct msm_vidc_inst *inst, u32 flags);
int msm_comm_release_scratch_buffers(struct msm_vidc_inst *inst);
int msm_comm_release_persist_buffers(struct msm_vidc_inst *inst);
+void msm_comm_unload_fw(struct msm_vidc_core *core);
+void change_inst_state(struct msm_vidc_inst *inst,
+ enum instance_state state);
+int msm_comm_unset_ocmem(struct msm_vidc_core *core);
+int msm_comm_free_ocmem(struct msm_vidc_core *core);
#define IS_PRIV_CTRL(idx) (\
(V4L2_CTRL_ID2CLASS(idx) == V4L2_CTRL_CLASS_MPEG) && \
V4L2_CTRL_DRIVER_PRIV(idx))
diff --git a/drivers/media/video/msm_vidc/msm_vidc_internal.h b/drivers/media/video/msm_vidc/msm_vidc_internal.h
index 9806d771..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,
@@ -84,6 +86,7 @@
VIDC_CORE_UNINIT = 0,
VIDC_CORE_INIT,
VIDC_CORE_INIT_DONE,
+ VIDC_CORE_INVALID
};
/*Donot change the enum values unless
@@ -105,6 +108,7 @@
MSM_VIDC_CLOSE,
MSM_VIDC_CLOSE_DONE,
MSM_VIDC_CORE_UNINIT,
+ MSM_VIDC_CORE_INVALID
};
struct buf_info {
@@ -224,6 +228,13 @@
int counter;
};
+struct msm_vidc_ssr_info {
+ struct subsys_device *msm_vidc_dev;
+ struct subsys_desc *msm_vidc_subsys_desc;
+ void *msm_vidc_ramdump_dev;
+ bool ssr_in_progress;
+};
+
struct msm_vidc_core {
struct list_head list;
struct mutex sync_lock;
@@ -241,6 +252,7 @@
enum vidc_core_state state;
struct msm_vidc_resources resources;
struct completion completions[SYS_MSG_END - SYS_MSG_START + 1];
+ struct msm_vidc_ssr_info ssr_info;
};
struct msm_vidc_inst {
diff --git a/drivers/media/video/msm_vidc/msm_vidc_ssr.c b/drivers/media/video/msm_vidc/msm_vidc_ssr.c
new file mode 100644
index 0000000..e8a6745
--- /dev/null
+++ b/drivers/media/video/msm_vidc/msm_vidc_ssr.c
@@ -0,0 +1,174 @@
+/* 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 <msm_vidc_ssr.h>
+
+static struct msm_vidc_core *get_vidc_core_from_dev(struct device *dev)
+{
+ struct video_device *vdev;
+ struct msm_video_device *videodev;
+ struct msm_vidc_core *core;
+ vdev = container_of(dev, struct video_device, dev);
+ videodev = container_of(vdev, struct msm_video_device, vdev);
+ core = container_of(videodev, struct msm_vidc_core,
+ vdev[MSM_VIDC_DECODER]);
+ return core;
+}
+int msm_vidc_shutdown(const struct subsys_desc *subsys)
+{
+ struct msm_vidc_inst *inst;
+ struct msm_vidc_core *core = NULL;
+ struct v4l2_event dqevent;
+ struct device *dev;
+ unsigned long flags;
+ int rc = 0;
+ if (!subsys) {
+ dprintk(VIDC_ERR, "Invalid subsys: %p\n", subsys);
+ rc = -EINVAL;
+ goto exit;
+ }
+ dev = subsys->dev;
+ if (dev)
+ core = get_vidc_core_from_dev(dev);
+ if (!core) {
+ dprintk(VIDC_ERR, "Invalid core: %p\n", core);
+ rc = -EINVAL;
+ goto exit;
+ }
+ core->ssr_info.ssr_in_progress = true;
+ spin_lock_irqsave(&core->lock, flags);
+ core->state = VIDC_CORE_INVALID;
+ spin_unlock_irqrestore(&core->lock, flags);
+ dqevent.type = V4L2_EVENT_MSM_VIDC_SYS_ERROR;
+ dqevent.id = 0;
+ list_for_each_entry(inst, &core->instances, list) {
+ if (inst) {
+ v4l2_event_queue_fh(&inst->event_handler, &dqevent);
+ spin_lock_irqsave(&inst->lock, flags);
+ inst->state = MSM_VIDC_CORE_INVALID;
+ spin_unlock_irqrestore(&inst->lock, flags);
+ }
+ }
+exit:
+ return rc;
+}
+int msm_vidc_ramdump(int enable, const struct subsys_desc *subsys)
+{
+ struct ramdump_segment memory_segments[] = {{0x0f500000, 0xFF000} };
+ struct msm_vidc_core *core = NULL;
+ void *dump_addr = NULL;
+ int rc = 0;
+ struct device *dev;
+ if (!subsys) {
+ dprintk(VIDC_ERR, "Invalid subsys: %p\n", subsys);
+ rc = -EINVAL;
+ goto exit;
+ }
+ dev = subsys->dev;
+ if (dev)
+ core = get_vidc_core_from_dev(dev);
+ if (!core) {
+ dprintk(VIDC_ERR, "Invalid core: %p\n", core);
+ rc = -EINVAL;
+ goto exit;
+ }
+ if (enable) {
+ rc = do_ramdump(core->ssr_info.msm_vidc_ramdump_dev,
+ memory_segments,
+ ARRAY_SIZE(memory_segments));
+ if (rc < 0)
+ dprintk(VIDC_DBG, "Failed : FW image memory dump\n");
+ dump_addr = kzalloc(core->resources.ocmem.buf->len, GFP_KERNEL);
+ if (dump_addr)
+ rc = ocmem_dump(OCMEM_VIDEO, core->resources.ocmem.buf,
+ (unsigned long)dump_addr);
+ if (rc < 0) {
+ dprintk(VIDC_DBG, "Failed : OCMEM copy\n");
+ } else {
+ memory_segments[0].address = (unsigned long)dump_addr;
+ memory_segments[0].size =
+ (unsigned long)core->resources.ocmem.buf->len;
+ rc = do_ramdump(core->ssr_info.msm_vidc_ramdump_dev,
+ memory_segments,
+ ARRAY_SIZE(memory_segments));
+ if (rc < 0)
+ dprintk(VIDC_DBG, "Failed : OCMEM dump\n");
+ }
+ kfree(dump_addr);
+ }
+exit:
+ return rc;
+}
+int msm_vidc_powerup(const struct subsys_desc *subsys)
+{
+ unsigned long flags;
+ struct msm_vidc_core *core = NULL;
+ int rc = 0;
+ struct device *dev;
+ if (!subsys) {
+ dprintk(VIDC_ERR, "Invalid subsys: %p\n", subsys);
+ rc = -EINVAL;
+ goto exit;
+ }
+ dev = subsys->dev;
+ if (dev)
+ core = get_vidc_core_from_dev(dev);
+ if (!core) {
+ dprintk(VIDC_ERR, "Invalid core: %p\n", core);
+ rc = -EINVAL;
+ goto exit;
+ }
+ msm_comm_free_ocmem(core);
+ vidc_hal_core_release(core->device);
+ spin_lock_irqsave(&core->lock, flags);
+ core->state = VIDC_CORE_UNINIT;
+ spin_unlock_irqrestore(&core->lock, flags);
+ msm_comm_unload_fw(core);
+exit:
+ return rc;
+}
+void msm_vidc_crash_shutdown(const struct subsys_desc *subsys)
+{
+ dprintk(VIDC_DBG, "Nothing implemented in crash shutdown\n");
+}
+static struct subsys_desc msm_vidc_subsystem = {
+ .name = "msm_vidc",
+ .dev = NULL,
+ .shutdown = msm_vidc_shutdown,
+ .powerup = msm_vidc_powerup,
+ .ramdump = msm_vidc_ramdump,
+ .crash_shutdown = msm_vidc_crash_shutdown
+};
+int msm_vidc_ssr_init(struct msm_vidc_core *core)
+{
+ int rc = 0;
+ msm_vidc_subsystem.dev = &core->vdev[MSM_VIDC_DECODER].vdev.dev;
+ core->ssr_info.msm_vidc_dev = subsys_register(&msm_vidc_subsystem);
+ if (IS_ERR_OR_NULL(core->ssr_info.msm_vidc_dev)) {
+ dprintk(VIDC_ERR, "msm_vidc Sub System registration failed\n");
+ rc = -ENODEV;
+ }
+ core->ssr_info.msm_vidc_ramdump_dev = create_ramdump_device("msm_vidc");
+ if (!core->ssr_info.msm_vidc_ramdump_dev) {
+ dprintk(VIDC_ERR, "Unable to create msm_vidc ramdump device\n");
+ rc = -ENODEV;
+ }
+ core->ssr_info.ssr_in_progress = false;
+ return rc;
+}
+
+int msm_vidc_ssr_uninit(struct msm_vidc_core *core)
+{
+ subsys_unregister(core->ssr_info.msm_vidc_dev);
+ destroy_ramdump_device(core->ssr_info.msm_vidc_ramdump_dev);
+ return 0;
+}
diff --git a/drivers/media/video/msm_vidc/msm_vidc_ssr.h b/drivers/media/video/msm_vidc/msm_vidc_ssr.h
new file mode 100644
index 0000000..90f7380
--- /dev/null
+++ b/drivers/media/video/msm_vidc/msm_vidc_ssr.h
@@ -0,0 +1,28 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __MSM_VIDC_SSR__
+#define __MSM_VIDC_SSR__
+
+#include <../ramdump.h>
+#include <mach/subsystem_restart.h>
+#include <mach/subsystem_notif.h>
+#include <media/msm_vidc.h>
+#include "msm_vidc_internal.h"
+#include "msm_vidc_common.h"
+#include "msm_vidc_debug.h"
+#include "vidc_hal_api.h"
+int msm_vidc_ssr_init(struct msm_vidc_core *core);
+int msm_vidc_ssr_uninit(struct msm_vidc_core *core);
+
+#endif
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 879418d..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 {
@@ -812,6 +813,7 @@
PC_PREP_DONE,
SYS_IDLE,
SYS_DEBUG,
+ SYS_WATCHDOG_TIMEOUT,
/* SESSION COMMANDS_DONE */
SESSION_LOAD_RESOURCE_DONE,
SESSION_INIT_DONE,
@@ -940,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/msm_vidc/vidc_hal_interrupt_handler.c b/drivers/media/video/msm_vidc/vidc_hal_interrupt_handler.c
index 043ed73..7eb0ae1 100644
--- a/drivers/media/video/msm_vidc/vidc_hal_interrupt_handler.c
+++ b/drivers/media/video/msm_vidc/vidc_hal_interrupt_handler.c
@@ -13,7 +13,9 @@
#include <linux/slab.h>
#include <linux/list.h>
+#include <linux/interrupt.h>
#include "vidc_hal.h"
+#include "vidc_hal_io.h"
#include "msm_vidc_debug.h"
static enum vidc_status vidc_map_hal_err_status(int hfi_err)
@@ -136,7 +138,14 @@
cmd_done.data = &event_notify;
device->callback(VIDC_EVENT_CHANGE, &cmd_done);
}
-
+static void hal_process_sys_watchdog_timeout(struct hal_device *device)
+{
+ struct msm_vidc_cb_cmd_done cmd_done;
+ disable_irq_nosync(device->hal_data->irq);
+ memset(&cmd_done, 0, sizeof(struct msm_vidc_cb_cmd_done));
+ cmd_done.device_id = device->device_id;
+ device->callback(SYS_WATCHDOG_TIMEOUT, &cmd_done);
+}
static void hal_process_event_notify(struct hal_device *device,
struct hfi_msg_event_notify_packet *pkt)
{
@@ -151,6 +160,7 @@
switch (pkt->event_id) {
case HFI_EVENT_SYS_ERROR:
dprintk(VIDC_INFO, "HFI_EVENT_SYS_ERROR");
+ hal_process_sys_watchdog_timeout(device);
break;
case HFI_EVENT_SESSION_ERROR:
dprintk(VIDC_INFO, "HFI_EVENT_SESSION_ERROR");
@@ -167,7 +177,6 @@
break;
}
}
-
static void hal_process_sys_init_done(struct hal_device *device,
struct hfi_msg_sys_init_done_packet *pkt)
{
@@ -764,6 +773,11 @@
}
dprintk(VIDC_INFO, "Received: 0x%x in ", msg_hdr->packet);
+ if ((device->intr_status & VIDC_WRAPPER_INTR_CLEAR_A2HWD_BMSK)) {
+ dprintk(VIDC_ERR, "Received: Watchdog timeout %s", __func__);
+ hal_process_sys_watchdog_timeout(device);
+ }
+
switch (msg_hdr->packet) {
case HFI_MSG_EVENT_NOTIFY:
hal_process_event_notify(device,
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/mfd/wcd9xxx-slimslave.c b/drivers/mfd/wcd9xxx-slimslave.c
index 6e6de37..948cb6e 100644
--- a/drivers/mfd/wcd9xxx-slimslave.c
+++ b/drivers/mfd/wcd9xxx-slimslave.c
@@ -482,3 +482,56 @@
return ret;
}
EXPORT_SYMBOL_GPL(wcd9xxx_disconnect_port);
+
+/* This function is called with mutex acquired */
+int wcd9xxx_rx_vport_validation(u32 port_id,
+ struct list_head *codec_dai_list)
+{
+ struct wcd9xxx_ch *ch;
+ int ret = 0;
+
+ pr_debug("%s: port_id %u\n", __func__, port_id);
+
+ list_for_each_entry(ch,
+ codec_dai_list, list) {
+ pr_debug("%s: ch->port %u\n", __func__, ch->port);
+ if (ch->port == port_id) {
+ ret = -EINVAL;
+ break;
+ }
+ }
+ return ret;
+}
+EXPORT_SYMBOL_GPL(wcd9xxx_rx_vport_validation);
+
+
+/* This function is called with mutex acquired */
+int wcd9xxx_tx_vport_validation(u32 vtable, u32 port_id,
+ struct wcd9xxx_codec_dai_data *codec_dai)
+{
+ struct wcd9xxx_ch *ch;
+ int ret = 0;
+ u32 index;
+ u32 size = sizeof(vtable) * 8;
+ pr_debug("%s: vtable 0x%x port_id %u size %d\n", __func__,
+ vtable, port_id, size);
+ for_each_set_bit(index, (unsigned long *)&vtable, size) {
+ list_for_each_entry(ch,
+ &codec_dai[index].wcd9xxx_ch_list,
+ list) {
+ pr_debug("%s: index %u ch->port %u vtable 0x%x\n",
+ __func__, index, ch->port, vtable);
+ if (ch->port == port_id) {
+ pr_err("%s: TX%u is used by AIF%u_CAP Mixer\n",
+ __func__, port_id + 1,
+ (index + 1)/2);
+ ret = -EINVAL;
+ break;
+ }
+ }
+ if (ret)
+ break;
+ }
+ return ret;
+}
+EXPORT_SYMBOL_GPL(wcd9xxx_tx_vport_validation);
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/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c
index 29413ab..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
@@ -5841,6 +5854,7 @@
mmc->caps2 |= MMC_CAP2_SANITIZE;
mmc->caps2 |= MMC_CAP2_CACHE_CTRL;
mmc->caps2 |= MMC_CAP2_INIT_BKOPS;
+ mmc->caps2 |= MMC_CAP2_POWEROFF_NOTIFY;
if (plat->nonremovable)
mmc->caps |= MMC_CAP_NONREMOVABLE;
diff --git a/drivers/platform/msm/qpnp-power-on.c b/drivers/platform/msm/qpnp-power-on.c
index 76a758b..1907adc 100644
--- a/drivers/platform/msm/qpnp-power-on.c
+++ b/drivers/platform/msm/qpnp-power-on.c
@@ -64,7 +64,7 @@
#define QPNP_PON_RESET_TYPE_MAX 0xF
#define PON_S1_COUNT_MAX 0xF
-#define QPNP_KEY_STATUS_DELAY msecs_to_jiffies(500)
+#define QPNP_KEY_STATUS_DELAY msecs_to_jiffies(250)
enum pon_type {
PON_KPDPWR,
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/qpnp-charger.c b/drivers/power/qpnp-charger.c
index 8d5244b..ef555f7 100644
--- a/drivers/power/qpnp-charger.c
+++ b/drivers/power/qpnp-charger.c
@@ -1145,6 +1145,10 @@
goto fail_chg_enable;
}
+ /* Get the charging-disabled property */
+ charging_disabled = of_property_read_bool(spmi->dev.of_node,
+ "qcom,chg-charging-disabled");
+
spmi_for_each_container_dev(spmi_resource, spmi) {
if (!spmi_resource) {
pr_err("qpnp_chg: spmi resource absent\n");
@@ -1278,6 +1282,8 @@
qpnp_chg_charge_en(chip, 1);
the_chip = chip;
+ qpnp_chg_charge_dis(chip, charging_disabled);
+
pr_info("Probe success !\n");
return 0;
@@ -1293,7 +1299,6 @@
qpnp_charger_remove(struct spmi_device *spmi)
{
struct qpnp_chg_chip *chip = dev_get_drvdata(&spmi->dev);
-
dev_set_drvdata(&spmi->dev, NULL);
kfree(chip);
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/slimbus/slimbus.c b/drivers/slimbus/slimbus.c
index bd25875..1e79dce 100644
--- a/drivers/slimbus/slimbus.c
+++ b/drivers/slimbus/slimbus.c
@@ -2875,6 +2875,9 @@
mutex_lock(&sb->sldev_reconf);
mutex_lock(&ctrl->m_ctrl);
do {
+ struct slim_pending_ch *pch;
+ u8 add_mark_removal = true;
+
slc = &ctrl->chans[chan];
dev_dbg(&ctrl->dev, "chan:%d,ctrl:%d,def:%d", chan, chctrl,
slc->def);
@@ -2899,9 +2902,30 @@
ret = -ENOTCONN;
break;
}
- ret = add_pending_ch(&sb->mark_removal, chan);
- if (ret)
- break;
+ /* If channel removal request comes when pending
+ * in the mark_define, remove it from the define
+ * list instead of adding it to removal list
+ */
+ if (!list_empty(&sb->mark_define)) {
+ struct list_head *pos, *next;
+ list_for_each_safe(pos, next,
+ &sb->mark_define) {
+ pch = list_entry(pos,
+ struct slim_pending_ch,
+ pending);
+ if (pch->chan == slc->chan) {
+ list_del(&pch->pending);
+ kfree(pch);
+ add_mark_removal = false;
+ break;
+ }
+ }
+ }
+ if (add_mark_removal == true) {
+ ret = add_pending_ch(&sb->mark_removal, chan);
+ if (ret)
+ break;
+ }
}
if (!(slc->nextgrp & SLIM_END_GRP))
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 f3387d9..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
@@ -205,19 +210,15 @@
static int tsens_tz_code_to_degc(int adc_code, int sensor_num)
{
- int degcbeforefactor, degc;
- degcbeforefactor = ((adc_code * tmdev->tsens_factor) -
- tmdev->sensor[sensor_num].offset)/
- tmdev->sensor[sensor_num].slope_mul_tsens_factor;
+ int degc, num, den;
- if (degcbeforefactor == 0)
- degc = degcbeforefactor;
- else if (degcbeforefactor > 0)
- degc = ((degcbeforefactor * tmdev->tsens_factor) +
- tmdev->tsens_factor/2)/tmdev->tsens_factor;
- else
- degc = ((degcbeforefactor * tmdev->tsens_factor) -
- tmdev->tsens_factor/2)/tmdev->tsens_factor;
+ num = ((adc_code * tmdev->tsens_factor) -
+ tmdev->sensor[sensor_num].offset);
+ den = (int) tmdev->sensor[sensor_num].slope_mul_tsens_factor;
+ degc = num/den;
+
+ if ((degc >= 0) && (num % den != 0))
+ degc++;
return degc;
}
@@ -525,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/Kconfig b/drivers/usb/dwc3/Kconfig
index 882eb97..423e104 100644
--- a/drivers/usb/dwc3/Kconfig
+++ b/drivers/usb/dwc3/Kconfig
@@ -2,8 +2,7 @@
tristate "DesignWare USB3 DRD Core Support"
depends on (USB || USB_GADGET)
select USB_OTG_UTILS
- select USB_GADGET_DUALSPEED
- select USB_XHCI_PLATFORM
+ select USB_XHCI_PLATFORM if USB_SUPPORT && USB_XHCI_HCD
help
Say Y or M here if your system has a Dual Role SuperSpeed
USB controller based on the DesignWare USB3 IP Core.
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index f517340..c0b4b57 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -99,6 +99,7 @@
ret = test_bit(id, dwc3_devs);
WARN(!ret, "dwc3: ID %d not in use\n", id);
+ smp_mb__before_clear_bit();
clear_bit(id, dwc3_devs);
}
EXPORT_SYMBOL_GPL(dwc3_put_device_id);
@@ -148,6 +149,8 @@
reg &= ~DWC3_GUSB2PHYCFG_PHYSOFTRST;
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
+ mdelay(100);
+
/* After PHYs are stable we can take Core out of reset state */
reg = dwc3_readl(dwc->regs, DWC3_GCTL);
reg &= ~DWC3_GCTL_CORESOFTRESET;
@@ -255,7 +258,7 @@
*
* Returns 0 on success otherwise negative errno.
*/
-static int __devinit dwc3_event_buffers_setup(struct dwc3 *dwc)
+static int dwc3_event_buffers_setup(struct dwc3 *dwc)
{
struct dwc3_event_buffer *evt;
int n;
@@ -266,6 +269,8 @@
evt->buf, (unsigned long long) evt->dma,
evt->length);
+ evt->lpos = 0;
+
dwc3_writel(dwc->regs, DWC3_GEVNTADRLO(n),
lower_32_bits(evt->dma));
dwc3_writel(dwc->regs, DWC3_GEVNTADRHI(n),
@@ -285,6 +290,9 @@
for (n = 0; n < dwc->num_event_buffers; n++) {
evt = dwc->ev_buffs[n];
+
+ evt->lpos = 0;
+
dwc3_writel(dwc->regs, DWC3_GEVNTADRLO(n), 0);
dwc3_writel(dwc->regs, DWC3_GEVNTADRHI(n), 0);
dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(n), 0);
@@ -292,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;
@@ -328,8 +348,6 @@
}
dwc->revision = reg;
- dwc3_core_soft_reset(dwc);
-
/* issue device SoftReset too */
timeout = jiffies + msecs_to_jiffies(500);
dwc3_writel(dwc->regs, DWC3_DCTL, DWC3_DCTL_CSFTRST);
@@ -347,6 +365,8 @@
cpu_relax();
} while (true);
+ dwc3_core_soft_reset(dwc);
+
dwc3_cache_hwparams(dwc);
reg = dwc3_readl(dwc->regs, DWC3_GCTL);
@@ -471,16 +491,21 @@
dev_err(dev, "missing IRQ\n");
return -ENODEV;
}
- dwc->xhci_resources[1] = *res;
+ dwc->xhci_resources[1].start = res->start;
+ dwc->xhci_resources[1].end = res->end;
+ dwc->xhci_resources[1].flags = res->flags;
+ dwc->xhci_resources[1].name = res->name;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(dev, "missing memory resource\n");
return -ENODEV;
}
- dwc->xhci_resources[0] = *res;
+ dwc->xhci_resources[0].start = res->start;
dwc->xhci_resources[0].end = dwc->xhci_resources[0].start +
DWC3_XHCI_REGS_END;
+ dwc->xhci_resources[0].flags = res->flags;
+ dwc->xhci_resources[0].name = res->name;
/*
* Request memory region but exclude xHCI regs,
@@ -495,7 +520,7 @@
return -ENOMEM;
}
- regs = devm_ioremap(dev, res->start, resource_size(res));
+ regs = devm_ioremap_nocache(dev, res->start, resource_size(res));
if (!regs) {
dev_err(dev, "ioremap failed\n");
return -ENOMEM;
@@ -559,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 92e28f5..3fb89cd 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -53,6 +53,7 @@
#include "dwc3_otg.h"
/* Global constants */
+#define DWC3_EP0_BOUNCE_SIZE 512
#define DWC3_ENDPOINTS_NUM 32
#define DWC3_XHCI_RESOURCES_NUM 2
@@ -68,6 +69,7 @@
#define DWC3_DEVICE_EVENT_CONNECT_DONE 2
#define DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE 3
#define DWC3_DEVICE_EVENT_WAKEUP 4
+#define DWC3_DEVICE_EVENT_HIBER_REQ 5
#define DWC3_DEVICE_EVENT_EOPF 6
#define DWC3_DEVICE_EVENT_SOF 7
#define DWC3_DEVICE_EVENT_ERRATIC_ERROR 9
@@ -174,38 +176,47 @@
#define DWC3_GCTL_PRTCAP_DEVICE 2
#define DWC3_GCTL_PRTCAP_OTG 3
-#define DWC3_GCTL_CORESOFTRESET (1 << 11)
-#define DWC3_GCTL_SCALEDOWN(n) ((n) << 4)
-#define DWC3_GCTL_SCALEDOWN_MASK DWC3_GCTL_SCALEDOWN(3)
-#define DWC3_GCTL_DISSCRAMBLE (1 << 3)
-#define DWC3_GCTL_DSBLCLKGTNG (1 << 0)
+#define DWC3_GCTL_CORESOFTRESET (1 << 11)
+#define DWC3_GCTL_SCALEDOWN(n) ((n) << 4)
+#define DWC3_GCTL_SCALEDOWN_MASK DWC3_GCTL_SCALEDOWN(3)
+#define DWC3_GCTL_DISSCRAMBLE (1 << 3)
+#define DWC3_GCTL_GBLHIBERNATIONEN (1 << 1)
+#define DWC3_GCTL_DSBLCLKGTNG (1 << 0)
/* Global User Control Register */
#define DWC3_GUCTL_REFCLKPER (0x3FF << 22)
/* Global USB2 PHY Configuration Register */
-#define DWC3_GUSB2PHYCFG_PHYSOFTRST (1 << 31)
-#define DWC3_GUSB2PHYCFG_SUSPHY (1 << 6)
+#define DWC3_GUSB2PHYCFG_PHYSOFTRST (1 << 31)
+#define DWC3_GUSB2PHYCFG_SUSPHY (1 << 6)
/* Global USB3 PIPE Control Register */
-#define DWC3_GUSB3PIPECTL_PHYSOFTRST (1 << 31)
-#define DWC3_GUSB3PIPECTL_SUSPHY (1 << 17)
-#define DWC3_GUSB3PIPECTL_DELAY_P1P2P3 (7 << 19)
-#define DWC3_GUSB3PIPECTL_DIS_RXDET_U3_RXDET (1 << 22)
+#define DWC3_GUSB3PIPECTL_PHYSOFTRST (1 << 31)
+#define DWC3_GUSB3PIPECTL_SUSPHY (1 << 17)
+#define DWC3_GUSB3PIPECTL_DELAY_P1P2P3 (7 << 19)
+#define DWC3_GUSB3PIPECTL_DIS_RXDET_U3_RXDET (1 << 22)
/* Global TX Fifo Size Register */
-#define DWC3_GTXFIFOSIZ_TXFDEF(n) ((n) & 0xffff)
-#define DWC3_GTXFIFOSIZ_TXFSTADDR(n) ((n) & 0xffff0000)
+#define DWC3_GTXFIFOSIZ_TXFDEF(n) ((n) & 0xffff)
+#define DWC3_GTXFIFOSIZ_TXFSTADDR(n) ((n) & 0xffff0000)
/* Global HWPARAMS1 Register */
#define DWC3_GHWPARAMS1_EN_PWROPT(n) (((n) & (3 << 24)) >> 24)
#define DWC3_GHWPARAMS1_EN_PWROPT_NO 0
#define DWC3_GHWPARAMS1_EN_PWROPT_CLK 1
+#define DWC3_GHWPARAMS1_EN_PWROPT_HIB 2
+#define DWC3_GHWPARAMS1_PWROPT(n) ((n) << 24)
+#define DWC3_GHWPARAMS1_PWROPT_MASK DWC3_GHWPARAMS1_PWROPT(3)
+
+/* Global HWPARAMS4 Register */
+#define DWC3_GHWPARAMS4_HIBER_SCRATCHBUFS(n) (((n) & (0x0f << 13)) >> 13)
+#define DWC3_MAX_HIBER_SCRATCHBUFS 15
/* Global HWPARAMS6 Register */
#define DWC3_GHWPARAMS6_SRP_SUPPORT (1 << 10)
/* Device Configuration Register */
+#define DWC3_DCFG_LPM_CAP (1 << 22)
#define DWC3_DCFG_DEVADDR(addr) ((addr) << 3)
#define DWC3_DCFG_DEVADDR_MASK DWC3_DCFG_DEVADDR(0x7f)
@@ -216,24 +227,32 @@
#define DWC3_DCFG_LOWSPEED (2 << 0)
#define DWC3_DCFG_FULLSPEED1 (3 << 0)
+#define DWC3_DCFG_LPM_CAP (1 << 22)
+
/* Device Control Register */
#define DWC3_DCTL_RUN_STOP (1 << 31)
#define DWC3_DCTL_CSFTRST (1 << 30)
#define DWC3_DCTL_LSFTRST (1 << 29)
#define DWC3_DCTL_HIRD_THRES_MASK (0x1f << 24)
-#define DWC3_DCTL_HIRD_THRES(n) (((n) & DWC3_DCTL_HIRD_THRES_MASK) >> 24)
+#define DWC3_DCTL_HIRD_THRES(n) ((n) << 24)
#define DWC3_DCTL_APPL1RES (1 << 23)
-#define DWC3_DCTL_TRGTULST_MASK (0x0f << 17)
-#define DWC3_DCTL_TRGTULST(n) ((n) << 17)
+/* These apply for core versions 1.87a and earlier */
+#define DWC3_DCTL_TRGTULST_MASK (0x0f << 17)
+#define DWC3_DCTL_TRGTULST(n) ((n) << 17)
+#define DWC3_DCTL_TRGTULST_U2 (DWC3_DCTL_TRGTULST(2))
+#define DWC3_DCTL_TRGTULST_U3 (DWC3_DCTL_TRGTULST(3))
+#define DWC3_DCTL_TRGTULST_SS_DIS (DWC3_DCTL_TRGTULST(4))
+#define DWC3_DCTL_TRGTULST_RX_DET (DWC3_DCTL_TRGTULST(5))
+#define DWC3_DCTL_TRGTULST_SS_INACT (DWC3_DCTL_TRGTULST(6))
-#define DWC3_DCTL_TRGTULST_U2 (DWC3_DCTL_TRGTULST(2))
-#define DWC3_DCTL_TRGTULST_U3 (DWC3_DCTL_TRGTULST(3))
-#define DWC3_DCTL_TRGTULST_SS_DIS (DWC3_DCTL_TRGTULST(4))
-#define DWC3_DCTL_TRGTULST_RX_DET (DWC3_DCTL_TRGTULST(5))
-#define DWC3_DCTL_TRGTULST_SS_INACT (DWC3_DCTL_TRGTULST(6))
+/* These apply for core versions 1.94a and later */
+#define DWC3_DCTL_KEEP_CONNECT (1 << 19)
+#define DWC3_DCTL_L1_HIBER_EN (1 << 18)
+#define DWC3_DCTL_CRS (1 << 17)
+#define DWC3_DCTL_CSS (1 << 16)
#define DWC3_DCTL_INITU2ENA (1 << 12)
#define DWC3_DCTL_ACCEPTU2ENA (1 << 11)
@@ -259,6 +278,7 @@
#define DWC3_DEVTEN_ERRTICERREN (1 << 9)
#define DWC3_DEVTEN_SOFEN (1 << 7)
#define DWC3_DEVTEN_EOPFEN (1 << 6)
+#define DWC3_DEVTEN_HIBERNATIONREQEVTEN (1 << 5)
#define DWC3_DEVTEN_WKUPEVTEN (1 << 4)
#define DWC3_DEVTEN_ULSTCNGEN (1 << 3)
#define DWC3_DEVTEN_CONNECTDONEEN (1 << 2)
@@ -266,7 +286,15 @@
#define DWC3_DEVTEN_DISCONNEVTEN (1 << 0)
/* Device Status Register */
+#define DWC3_DSTS_DCNRD (1 << 29)
+
+/* This applies for core versions 1.87a and earlier */
#define DWC3_DSTS_PWRUPREQ (1 << 24)
+
+/* These apply for core versions 1.94a and later */
+#define DWC3_DSTS_RSS (1 << 25)
+#define DWC3_DSTS_SSS (1 << 24)
+
#define DWC3_DSTS_COREIDLE (1 << 23)
#define DWC3_DSTS_DEVCTRLHLT (1 << 22)
@@ -275,7 +303,7 @@
#define DWC3_DSTS_RXFIFOEMPTY (1 << 17)
-#define DWC3_DSTS_SOFFN_MASK (0x3ff << 3)
+#define DWC3_DSTS_SOFFN_MASK (0x3fff << 3)
#define DWC3_DSTS_SOFFN(n) (((n) & DWC3_DSTS_SOFFN_MASK) >> 3)
#define DWC3_DSTS_CONNECTSPD (7 << 0)
@@ -290,17 +318,33 @@
#define DWC3_DGCMD_SET_LMP 0x01
#define DWC3_DGCMD_SET_PERIODIC_PAR 0x02
#define DWC3_DGCMD_XMIT_FUNCTION 0x03
+
+/* These apply for core versions 1.94a and later */
+#define DWC3_DGCMD_SET_SCRATCHPAD_ADDR_LO 0x04
+#define DWC3_DGCMD_SET_SCRATCHPAD_ADDR_HI 0x05
+
#define DWC3_DGCMD_SELECTED_FIFO_FLUSH 0x09
#define DWC3_DGCMD_ALL_FIFO_FLUSH 0x0a
#define DWC3_DGCMD_SET_ENDPOINT_NRDY 0x0c
#define DWC3_DGCMD_RUN_SOC_BUS_LOOPBACK 0x10
+#define DWC3_DGCMD_STATUS(n) (((n) >> 15) & 1)
+#define DWC3_DGCMD_CMDACT (1 << 10)
+#define DWC3_DGCMD_CMDIOC (1 << 8)
+
+/* Device Generic Command Parameter Register */
+#define DWC3_DGCMDPAR_FORCE_LINKPM_ACCEPT (1 << 0)
+#define DWC3_DGCMDPAR_FIFO_NUM(n) ((n) << 0)
+#define DWC3_DGCMDPAR_RX_FIFO (0 << 5)
+#define DWC3_DGCMDPAR_TX_FIFO (1 << 5)
+#define DWC3_DGCMDPAR_LOOPBACK_DIS (0 << 0)
+#define DWC3_DGCMDPAR_LOOPBACK_ENA (1 << 0)
+
/* Device Endpoint Command Register */
#define DWC3_DEPCMD_PARAM_SHIFT 16
#define DWC3_DEPCMD_PARAM(x) ((x) << DWC3_DEPCMD_PARAM_SHIFT)
#define DWC3_DEPCMD_GET_RSC_IDX(x) (((x) >> DWC3_DEPCMD_PARAM_SHIFT) & 0x7f)
-#define DWC3_DEPCMD_STATUS_MASK (0x0f << 12)
-#define DWC3_DEPCMD_STATUS(x) (((x) & DWC3_DEPCMD_STATUS_MASK) >> 12)
+#define DWC3_DEPCMD_STATUS(x) (((x) >> 15) & 1)
#define DWC3_DEPCMD_HIPRI_FORCERM (1 << 11)
#define DWC3_DEPCMD_CMDACT (1 << 10)
#define DWC3_DEPCMD_CMDIOC (1 << 8)
@@ -311,7 +355,10 @@
#define DWC3_DEPCMD_STARTTRANSFER (0x06 << 0)
#define DWC3_DEPCMD_CLEARSTALL (0x05 << 0)
#define DWC3_DEPCMD_SETSTALL (0x04 << 0)
+/* This applies for core versions 1.90a and earlier */
#define DWC3_DEPCMD_GETSEQNUMBER (0x03 << 0)
+/* This applies for core versions 1.94a and later */
+#define DWC3_DEPCMD_GETEPSTATE (0x03 << 0)
#define DWC3_DEPCMD_SETTRANSFRESOURCE (0x02 << 0)
#define DWC3_DEPCMD_SETEPCONFIG (0x01 << 0)
@@ -400,7 +447,8 @@
* @current_trb: index of current used trb
* @number: endpoint number (1 - 15)
* @type: set to bmAttributes & USB_ENDPOINT_XFERTYPE_MASK
- * @res_trans_idx: Resource transfer index
+ * @resource_index: Resource transfer index
+ * @current_uf: Current uf received through last event parameter
* @interval: the intervall on which the ISOC transfer is started
* @name: a human readable name e.g. ep1out-bulk
* @direction: true for TX, false for RX
@@ -415,7 +463,6 @@
dma_addr_t trb_pool_dma;
u32 free_slot;
u32 busy_slot;
- const struct usb_endpoint_descriptor *desc;
const struct usb_ss_ep_comp_descriptor *comp_desc;
struct dwc3 *dwc;
@@ -425,6 +472,7 @@
#define DWC3_EP_WEDGE (1 << 2)
#define DWC3_EP_BUSY (1 << 4)
#define DWC3_EP_PENDING_REQUEST (1 << 5)
+#define DWC3_EP_MISSED_ISOC (1 << 6)
/* This last one is specific to EP0 */
#define DWC3_EP0_DIR_IN (1 << 31)
@@ -433,7 +481,8 @@
u8 number;
u8 type;
- u8 res_trans_idx;
+ u8 resource_index;
+ u16 current_uf;
u32 interval;
char name[20];
@@ -451,7 +500,6 @@
enum dwc3_ep0_next {
DWC3_EP0_UNKNOWN = 0,
DWC3_EP0_COMPLETE,
- DWC3_EP0_NRDY_SETUP,
DWC3_EP0_NRDY_DATA,
DWC3_EP0_NRDY_STATUS,
};
@@ -477,6 +525,8 @@
DWC3_LINK_STATE_HRESET = 0x09,
DWC3_LINK_STATE_CMPLY = 0x0a,
DWC3_LINK_STATE_LPBK = 0x0b,
+ DWC3_LINK_STATE_RESET = 0x0e,
+ DWC3_LINK_STATE_RESUME = 0x0f,
DWC3_LINK_STATE_MASK = 0x0f,
};
@@ -490,11 +540,12 @@
#define DWC3_TRB_SIZE_MASK (0x00ffffff)
#define DWC3_TRB_SIZE_LENGTH(n) ((n) & DWC3_TRB_SIZE_MASK)
#define DWC3_TRB_SIZE_PCM1(n) (((n) & 0x03) << 24)
-#define DWC3_TRB_SIZE_TRBSTS(n) (((n) & (0x0f << 28) >> 28))
+#define DWC3_TRB_SIZE_TRBSTS(n) (((n) & (0x0f << 28)) >> 28)
#define DWC3_TRBSTS_OK 0
#define DWC3_TRBSTS_MISSED_ISOC 1
#define DWC3_TRBSTS_SETUP_PENDING 2
+#define DWC3_TRB_STS_XFER_IN_PROG 4
/* TRB Control */
#define DWC3_TRB_CTRL_HWO (1 << 0)
@@ -583,6 +634,14 @@
unsigned queued:1;
};
+/*
+ * struct dwc3_scratchpad_array - hibernation scratchpad array
+ * (format defined by hw)
+ */
+struct dwc3_scratchpad_array {
+ __le64 dma_adr[DWC3_MAX_HIBER_SCRATCHBUFS];
+};
+
/**
* struct dwc3 - representation of our controller
* @ctrl_req: usb control request which is used for ep0
@@ -615,6 +674,11 @@
* @setup_packet_pending: true when there's a Setup Packet in FIFO. Workaround
* @needs_fifo_resize: not all users might want fifo resizing, flag it
* @resize_fifos: tells us it's ok to reconfigure our TxFIFO sizes.
+ * @isoch_delay: wValue from Set Isochronous Delay request;
+ * @u2sel: parameter from Set SEL request.
+ * @u2pel: parameter from Set SEL request.
+ * @u1sel: parameter from Set SEL request.
+ * @u1pel: parameter from Set SEL request.
* @ep0_next_event: hold the next expected event
* @ep0state: state of endpoint zero
* @link_state: link state
@@ -660,8 +724,14 @@
#define DWC3_REVISION_180A 0x5533180a
#define DWC3_REVISION_183A 0x5533183a
#define DWC3_REVISION_185A 0x5533185a
+#define DWC3_REVISION_187A 0x5533187a
#define DWC3_REVISION_188A 0x5533188a
#define DWC3_REVISION_190A 0x5533190a
+#define DWC3_REVISION_194A 0x5533194a
+#define DWC3_REVISION_200A 0x5533200a
+#define DWC3_REVISION_202A 0x5533202a
+#define DWC3_REVISION_210A 0x5533210a
+#define DWC3_REVISION_220A 0x5533220a
#define DWC3_REVISION_230A 0x5533230a
unsigned is_selfpowered:1;
@@ -679,7 +749,14 @@
enum dwc3_link_state link_state;
enum dwc3_device_state dev_state;
+ u16 isoch_delay;
+ u16 u2sel;
+ u16 u2pel;
+ u8 u1sel;
+ u8 u1pel;
+
u8 speed;
+
void *mem;
struct dwc3_hwparams hwparams;
@@ -752,7 +829,6 @@
#define DEPEVT_STREAMEVT_NOTFOUND 2
/* Control-only Status */
-#define DEPEVT_STATUS_CONTROL_SETUP 0
#define DEPEVT_STATUS_CONTROL_DATA 1
#define DEPEVT_STATUS_CONTROL_STATUS 2
@@ -841,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-exynos.c b/drivers/usb/dwc3/dwc3-exynos.c
index d190301..b8f0038 100644
--- a/drivers/usb/dwc3/dwc3-exynos.c
+++ b/drivers/usb/dwc3/dwc3-exynos.c
@@ -18,7 +18,6 @@
#include <linux/platform_device.h>
#include <linux/platform_data/dwc3-exynos.h>
#include <linux/dma-mapping.h>
-#include <linux/module.h>
#include <linux/clk.h>
#include "core.h"
diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c
index b71bd3e..0fd9ab5 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>
@@ -34,6 +35,7 @@
#include <linux/regulator/consumer.h>
#include <mach/rpm-regulator.h>
+#include <mach/msm_xo.h>
#include <mach/msm_bus.h>
#include "dwc3_otg.h"
@@ -127,6 +129,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 +146,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;
@@ -615,7 +620,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 +1244,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 +1281,9 @@
dev_err(mdwc->dev, "Failed to reset bus bw vote\n");
}
+ wake_unlock(&mdwc->wlock);
atomic_set(&mdwc->in_lpm, 1);
+
dev_info(mdwc->dev, "DWC3 in low power mode\n");
return 0;
@@ -1269,6 +1300,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 +1309,41 @@
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);
+
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;
@@ -1403,6 +1463,22 @@
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 __devinit dwc3_msm_probe(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;
@@ -1426,6 +1502,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 +1523,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 +1639,21 @@
goto free_hs_ldo_init;
}
+ /* 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, "platform_get_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, "request irq failed (HSPHY INT)\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 +1677,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 +1685,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 +1720,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)) {
@@ -1665,8 +1772,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) {
@@ -1699,6 +1807,9 @@
platform_device_del(dwc3);
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 +1836,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 +1869,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;
}
@@ -1849,7 +1963,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..f96b88a 100644
--- a/drivers/usb/dwc3/dwc3_otg.c
+++ b/drivers/usb/dwc3/dwc3_otg.c
@@ -16,12 +16,14 @@
#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);
/**
* dwc3_otg_set_host_regs - reset dwc3 otg registers to host operation.
@@ -29,7 +31,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 +41,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 +97,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 +115,38 @@
* 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);
+ 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;
+ }
+
+ /* 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 +165,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 +228,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);
@@ -293,19 +302,19 @@
/* ext_xceiver would have taken h/w out of LPM by now */
pm_runtime_get(phy->dev);
}
+ } 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)
+ set_bit(B_SESS_VLD, &dotg->inputs);
+ else
+ clear_bit(B_SESS_VLD, &dotg->inputs);
+
+ 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);
}
/**
@@ -339,21 +348,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))
@@ -445,7 +443,7 @@
case OTG_STATE_UNDEFINED:
dwc3_otg_init_sm(dotg);
/* 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 +459,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;
@@ -588,6 +586,7 @@
*/
static void dwc3_otg_reset(struct dwc3_otg *dotg)
{
+ static int once;
/*
* OCFG[2] - OTG-Version = 1
* OCFG[1] - HNPCap = 0
@@ -604,7 +603,10 @@
* 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);
@@ -651,6 +653,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,6 +684,7 @@
goto err1;
}
+ dotg->dwc = dwc;
dotg->otg.phy->otg = &dotg->otg;
dotg->otg.phy->dev = dwc->dev;
diff --git a/drivers/usb/dwc3/dwc3_otg.h b/drivers/usb/dwc3/dwc3_otg.h
index b60b42a..dd4cdf4 100644
--- a/drivers/usb/dwc3/dwc3_otg.h
+++ b/drivers/usb/dwc3/dwc3_otg.h
@@ -32,9 +32,11 @@
* @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;
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index 3584a16..1512513 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -54,7 +54,9 @@
#include "gadget.h"
#include "io.h"
-static void dwc3_ep0_do_control_status(struct dwc3 *dwc, u32 epnum);
+static void __dwc3_ep0_do_control_status(struct dwc3 *dwc, struct dwc3_ep *dep);
+static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
+ struct dwc3_ep *dep, struct dwc3_request *req);
static const char *dwc3_ep0_state_string(enum dwc3_ep0_state state)
{
@@ -111,7 +113,7 @@
}
dep->flags |= DWC3_EP_BUSY;
- dep->res_trans_idx = dwc3_gadget_ep_get_transfer_index(dwc,
+ dep->resource_index = dwc3_gadget_ep_get_transfer_index(dwc,
dep->number);
dwc->ep0_next_event = DWC3_EP0_COMPLETE;
@@ -123,7 +125,6 @@
struct dwc3_request *req)
{
struct dwc3 *dwc = dep->dwc;
- int ret = 0;
req->request.actual = 0;
req->request.status = -EINPROGRESS;
@@ -150,21 +151,76 @@
return 0;
}
- ret = dwc3_ep0_start_trans(dwc, direction,
- req->request.dma, req->request.length,
- DWC3_TRBCTL_CONTROL_DATA);
+ __dwc3_ep0_do_control_data(dwc, dwc->eps[direction], req);
+
dep->flags &= ~(DWC3_EP_PENDING_REQUEST |
DWC3_EP0_DIR_IN);
- } else if (dwc->delayed_status) {
+
+ return 0;
+ }
+
+ /*
+ * In case gadget driver asked us to delay the STATUS phase,
+ * handle it here.
+ */
+ if (dwc->delayed_status) {
+ unsigned direction;
+
+ direction = !dwc->ep0_expect_in;
dwc->delayed_status = false;
if (dwc->ep0state == EP0_STATUS_PHASE)
- dwc3_ep0_do_control_status(dwc, 1);
+ __dwc3_ep0_do_control_status(dwc, dwc->eps[direction]);
else
dev_dbg(dwc->dev, "too early for delayed status\n");
+
+ return 0;
}
- return ret;
+ /*
+ * Unfortunately we have uncovered a limitation wrt the Data Phase.
+ *
+ * Section 9.4 says we can wait for the XferNotReady(DATA) event to
+ * come before issueing Start Transfer command, but if we do, we will
+ * miss situations where the host starts another SETUP phase instead of
+ * the DATA phase. Such cases happen at least on TD.7.6 of the Link
+ * Layer Compliance Suite.
+ *
+ * The problem surfaces due to the fact that in case of back-to-back
+ * SETUP packets there will be no XferNotReady(DATA) generated and we
+ * will be stuck waiting for XferNotReady(DATA) forever.
+ *
+ * By looking at tables 9-13 and 9-14 of the Databook, we can see that
+ * it tells us to start Data Phase right away. It also mentions that if
+ * we receive a SETUP phase instead of the DATA phase, core will issue
+ * XferComplete for the DATA phase, before actually initiating it in
+ * the wire, with the TRB's status set to "SETUP_PENDING". Such status
+ * can only be used to print some debugging logs, as the core expects
+ * us to go through to the STATUS phase and start a CONTROL_STATUS TRB,
+ * just so it completes right away, without transferring anything and,
+ * only then, we can go back to the SETUP phase.
+ *
+ * Because of this scenario, SNPS decided to change the programming
+ * model of control transfers and support on-demand transfers only for
+ * the STATUS phase. To fix the issue we have now, we will always wait
+ * for gadget driver to queue the DATA phase's struct usb_request, then
+ * start it right away.
+ *
+ * If we're actually in a 2-stage transfer, we will wait for
+ * XferNotReady(STATUS).
+ */
+ if (dwc->three_stage_setup) {
+ unsigned direction;
+
+ direction = dwc->ep0_expect_in;
+ dwc->ep0state = EP0_DATA_PHASE;
+
+ __dwc3_ep0_do_control_data(dwc, dwc->eps[direction], req);
+
+ dep->flags &= ~DWC3_EP0_DIR_IN;
+ }
+
+ return 0;
}
int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
@@ -179,7 +235,7 @@
int ret;
spin_lock_irqsave(&dwc->lock, flags);
- if (!dep->desc) {
+ if (!dep->endpoint.desc) {
dev_dbg(dwc->dev, "trying to queue request %p to disabled %s\n",
request, dep->name);
ret = -ESHUTDOWN;
@@ -206,9 +262,14 @@
static void dwc3_ep0_stall_and_restart(struct dwc3 *dwc)
{
- struct dwc3_ep *dep = dwc->eps[0];
+ struct dwc3_ep *dep;
+
+ /* reinitialize physical ep1 */
+ dep = dwc->eps[1];
+ dep->flags = DWC3_EP_ENABLED;
/* stall is always issued on EP0 */
+ dep = dwc->eps[0];
__dwc3_gadget_ep_set_halt(dep, 1);
dep->flags = DWC3_EP_ENABLED;
dwc->delayed_status = false;
@@ -224,6 +285,16 @@
dwc3_ep0_out_start(dwc);
}
+int dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value)
+{
+ struct dwc3_ep *dep = to_dwc3_ep(ep);
+ struct dwc3 *dwc = dep->dwc;
+
+ dwc3_ep0_stall_and_restart(dwc);
+
+ return 0;
+}
+
void dwc3_ep0_out_start(struct dwc3 *dwc)
{
int ret;
@@ -261,6 +332,7 @@
{
struct dwc3_ep *dep;
u32 recip;
+ u32 reg;
u16 usb_status = 0;
__le16 *response_pkt;
@@ -268,10 +340,18 @@
switch (recip) {
case USB_RECIP_DEVICE:
/*
- * We are self-powered. U1/U2/LTM will be set later
- * once we handle this states. RemoteWakeup is 0 on SS
+ * LTM will be set once we know how to set this in HW.
*/
usb_status |= dwc->is_selfpowered << USB_DEVICE_SELF_POWERED;
+
+ if (dwc->speed == DWC3_DSTS_SUPERSPEED) {
+ reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ if (reg & DWC3_DCTL_INITU1ENA)
+ usb_status |= 1 << USB_DEV_STAT_U1_ENABLED;
+ if (reg & DWC3_DCTL_INITU2ENA)
+ usb_status |= 1 << USB_DEV_STAT_U2_ENABLED;
+ }
+
break;
case USB_RECIP_INTERFACE:
@@ -312,6 +392,7 @@
u32 recip;
u32 wValue;
u32 wIndex;
+ u32 reg;
int ret;
wValue = le16_to_cpu(ctrl->wValue);
@@ -320,29 +401,43 @@
switch (recip) {
case USB_RECIP_DEVICE:
+ switch (wValue) {
+ case USB_DEVICE_REMOTE_WAKEUP:
+ break;
/*
* 9.4.1 says only only for SS, in AddressState only for
* default control pipe
*/
- switch (wValue) {
case USB_DEVICE_U1_ENABLE:
- case USB_DEVICE_U2_ENABLE:
- case USB_DEVICE_LTM_ENABLE:
if (dwc->dev_state != DWC3_CONFIGURED_STATE)
return -EINVAL;
if (dwc->speed != DWC3_DSTS_SUPERSPEED)
return -EINVAL;
- }
- /* XXX add U[12] & LTM */
- switch (wValue) {
- case USB_DEVICE_REMOTE_WAKEUP:
+ reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ if (set)
+ reg |= DWC3_DCTL_INITU1ENA;
+ else
+ reg &= ~DWC3_DCTL_INITU1ENA;
+ dwc3_writel(dwc->regs, DWC3_DCTL, reg);
break;
- case USB_DEVICE_U1_ENABLE:
- break;
+
case USB_DEVICE_U2_ENABLE:
+ if (dwc->dev_state != DWC3_CONFIGURED_STATE)
+ return -EINVAL;
+ if (dwc->speed != DWC3_DSTS_SUPERSPEED)
+ return -EINVAL;
+
+ reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ if (set)
+ reg |= DWC3_DCTL_INITU2ENA;
+ else
+ reg &= ~DWC3_DCTL_INITU2ENA;
+ dwc3_writel(dwc->regs, DWC3_DCTL, reg);
break;
+
case USB_DEVICE_LTM_ENABLE:
+ return -EINVAL;
break;
case USB_DEVICE_TEST_MODE:
@@ -380,6 +475,10 @@
dep = dwc3_wIndex_to_dep(dwc, wIndex);
if (!dep)
return -EINVAL;
+
+ if (!set && (dep->flags & DWC3_EP_WEDGE))
+ return 0;
+
ret = __dwc3_gadget_ep_set_halt(dep, set);
if (ret)
return -EINVAL;
@@ -439,6 +538,7 @@
{
u32 cfg;
int ret;
+ u32 reg;
dwc->start_config_issued = false;
cfg = le16_to_cpu(ctrl->wValue);
@@ -453,6 +553,14 @@
/* if the cfg matches and the cfg is non zero */
if (cfg && (!ret || (ret == USB_GADGET_DELAYED_STATUS))) {
dwc->dev_state = DWC3_CONFIGURED_STATE;
+ /*
+ * Enable transition to U1/U2 state when
+ * nothing is pending from application.
+ */
+ reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ reg |= (DWC3_DCTL_ACCEPTU1ENA | DWC3_DCTL_ACCEPTU2ENA);
+ dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+
dwc->resize_fifos = true;
dev_dbg(dwc->dev, "resize fifos flag SET\n");
}
@@ -469,6 +577,107 @@
return ret;
}
+static void dwc3_ep0_set_sel_cmpl(struct usb_ep *ep, struct usb_request *req)
+{
+ struct dwc3_ep *dep = to_dwc3_ep(ep);
+ struct dwc3 *dwc = dep->dwc;
+
+ u32 param = 0;
+ u32 reg;
+
+ struct timing {
+ u8 u1sel;
+ u8 u1pel;
+ u16 u2sel;
+ u16 u2pel;
+ } __packed timing;
+
+ int ret;
+
+ memcpy(&timing, req->buf, sizeof(timing));
+
+ dwc->u1sel = timing.u1sel;
+ dwc->u1pel = timing.u1pel;
+ dwc->u2sel = le16_to_cpu(timing.u2sel);
+ dwc->u2pel = le16_to_cpu(timing.u2pel);
+
+ reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ if (reg & DWC3_DCTL_INITU2ENA)
+ param = dwc->u2pel;
+ if (reg & DWC3_DCTL_INITU1ENA)
+ param = dwc->u1pel;
+
+ /*
+ * According to Synopsys Databook, if parameter is
+ * greater than 125, a value of zero should be
+ * programmed in the register.
+ */
+ if (param > 125)
+ param = 0;
+
+ /* now that we have the time, issue DGCMD Set Sel */
+ ret = dwc3_send_gadget_generic_command(dwc,
+ DWC3_DGCMD_SET_PERIODIC_PAR, param);
+ WARN_ON(ret < 0);
+}
+
+static int dwc3_ep0_set_sel(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
+{
+ struct dwc3_ep *dep;
+ u16 wLength;
+ u16 wValue;
+
+ if (dwc->dev_state == DWC3_DEFAULT_STATE)
+ return -EINVAL;
+
+ wValue = le16_to_cpu(ctrl->wValue);
+ wLength = le16_to_cpu(ctrl->wLength);
+
+ if (wLength != 6) {
+ dev_err(dwc->dev, "Set SEL should be 6 bytes, got %d\n",
+ wLength);
+ return -EINVAL;
+ }
+
+ /*
+ * To handle Set SEL we need to receive 6 bytes from Host. So let's
+ * queue a usb_request for 6 bytes.
+ *
+ * Remember, though, this controller can't handle non-wMaxPacketSize
+ * aligned transfers on the OUT direction, so we queue a request for
+ * wMaxPacketSize instead.
+ */
+ dep = dwc->eps[0];
+ dwc->ep0_usb_req.dep = dep;
+ dwc->ep0_usb_req.request.length = dep->endpoint.maxpacket;
+ dwc->ep0_usb_req.request.buf = dwc->setup_buf;
+ dwc->ep0_usb_req.request.complete = dwc3_ep0_set_sel_cmpl;
+
+ return __dwc3_gadget_ep0_queue(dep, &dwc->ep0_usb_req);
+}
+
+static int dwc3_ep0_set_isoch_delay(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
+{
+ u16 wLength;
+ u16 wValue;
+ u16 wIndex;
+
+ wValue = le16_to_cpu(ctrl->wValue);
+ wLength = le16_to_cpu(ctrl->wLength);
+ wIndex = le16_to_cpu(ctrl->wIndex);
+
+ if (wIndex || wLength)
+ return -EINVAL;
+
+ /*
+ * REVISIT It's unclear from Databook what to do with this
+ * value. For now, just cache it.
+ */
+ dwc->isoch_delay = wValue;
+
+ return 0;
+}
+
static int dwc3_ep0_std_request(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
{
int ret;
@@ -494,6 +703,14 @@
dev_vdbg(dwc->dev, "USB_REQ_SET_CONFIGURATION\n");
ret = dwc3_ep0_set_config(dwc, ctrl);
break;
+ case USB_REQ_SET_SEL:
+ dev_vdbg(dwc->dev, "USB_REQ_SET_SEL\n");
+ ret = dwc3_ep0_set_sel(dwc, ctrl);
+ break;
+ case USB_REQ_SET_ISOCH_DELAY:
+ dev_vdbg(dwc->dev, "USB_REQ_SET_ISOCH_DELAY\n");
+ ret = dwc3_ep0_set_isoch_delay(dwc, ctrl);
+ break;
default:
dev_vdbg(dwc->dev, "Forwarding to gadget driver\n");
ret = dwc3_ep0_delegate_req(dwc, ctrl);
@@ -507,11 +724,11 @@
const struct dwc3_event_depevt *event)
{
struct usb_ctrlrequest *ctrl = dwc->ctrl_req;
- int ret;
+ int ret = -EINVAL;
u32 len;
if (!dwc->gadget_driver)
- goto err;
+ goto out;
len = le16_to_cpu(ctrl->wLength);
if (!len) {
@@ -532,11 +749,9 @@
if (ret == USB_GADGET_DELAYED_STATUS)
dwc->delayed_status = true;
- if (ret >= 0)
- return;
-
-err:
- dwc3_ep0_stall_and_restart(dwc);
+out:
+ if (ret < 0)
+ dwc3_ep0_stall_and_restart(dwc);
}
static void dwc3_ep0_complete_data(struct dwc3 *dwc,
@@ -547,6 +762,7 @@
struct dwc3_trb *trb;
struct dwc3_ep *ep0;
u32 transferred;
+ u32 status;
u32 length;
u8 epnum;
@@ -559,6 +775,17 @@
ur = &r->request;
trb = dwc->ep0_trb;
+
+ status = DWC3_TRB_SIZE_TRBSTS(trb->size);
+ if (status == DWC3_TRBSTS_SETUP_PENDING) {
+ dev_dbg(dwc->dev, "Setup Pending received\n");
+
+ if (r)
+ dwc3_gadget_giveback(ep0, r, -ECONNRESET);
+
+ return;
+ }
+
length = trb->size & DWC3_TRB_SIZE_MASK;
if (dwc->ep0_bounced) {
@@ -569,7 +796,6 @@
transferred = min_t(u32, ur->length,
transfer_size - length);
memcpy(ur->buf, dwc->ep0_bounce, transferred);
- dwc->ep0_bounced = false;
} else {
transferred = ur->length - length;
}
@@ -590,13 +816,16 @@
}
}
-static void dwc3_ep0_complete_req(struct dwc3 *dwc,
+static void dwc3_ep0_complete_status(struct dwc3 *dwc,
const struct dwc3_event_depevt *event)
{
struct dwc3_request *r;
struct dwc3_ep *dep;
+ struct dwc3_trb *trb;
+ u32 status;
dep = dwc->eps[0];
+ trb = dwc->ep0_trb;
if (!list_empty(&dep->request_list)) {
r = next_request(&dep->request_list);
@@ -612,9 +841,14 @@
dev_dbg(dwc->dev, "Invalid Test #%d\n",
dwc->test_mode_nr);
dwc3_ep0_stall_and_restart(dwc);
+ return;
}
}
+ status = DWC3_TRB_SIZE_TRBSTS(trb->size);
+ if (status == DWC3_TRBSTS_SETUP_PENDING)
+ dev_dbg(dwc->dev, "Setup Pending received\n");
+
dwc->ep0state = EP0_SETUP_PHASE;
dwc3_ep0_out_start(dwc);
}
@@ -625,7 +859,7 @@
struct dwc3_ep *dep = dwc->eps[event->endpoint_number];
dep->flags &= ~DWC3_EP_BUSY;
- dep->res_trans_idx = 0;
+ dep->resource_index = 0;
dwc->setup_packet_pending = false;
switch (dwc->ep0state) {
@@ -641,76 +875,60 @@
case EP0_STATUS_PHASE:
dev_vdbg(dwc->dev, "Status Phase\n");
- dwc3_ep0_complete_req(dwc, event);
+ dwc3_ep0_complete_status(dwc, event);
break;
default:
WARN(true, "UNKNOWN ep0state %d\n", dwc->ep0state);
}
}
-static void dwc3_ep0_do_control_setup(struct dwc3 *dwc,
- const struct dwc3_event_depevt *event)
+static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
+ struct dwc3_ep *dep, struct dwc3_request *req)
{
- dwc3_ep0_out_start(dwc);
-}
-
-static void dwc3_ep0_do_control_data(struct dwc3 *dwc,
- const struct dwc3_event_depevt *event)
-{
- struct dwc3_ep *dep;
- struct dwc3_request *req;
int ret;
- dep = dwc->eps[0];
-
- if (list_empty(&dep->request_list)) {
- dev_vdbg(dwc->dev, "pending request for EP0 Data phase\n");
- dep->flags |= DWC3_EP_PENDING_REQUEST;
-
- if (event->endpoint_number)
- dep->flags |= DWC3_EP0_DIR_IN;
- return;
- }
-
- req = next_request(&dep->request_list);
- req->direction = !!event->endpoint_number;
+ req->direction = !!dep->number;
if (req->request.length == 0) {
- ret = dwc3_ep0_start_trans(dwc, event->endpoint_number,
+ ret = dwc3_ep0_start_trans(dwc, dep->number,
dwc->ctrl_req_addr, 0,
DWC3_TRBCTL_CONTROL_DATA);
- } else if ((req->request.length % dep->endpoint.maxpacket)
- && (event->endpoint_number == 0)) {
+ } else if (!IS_ALIGNED(req->request.length, dep->endpoint.maxpacket)
+ && (dep->number == 0)) {
+ u32 transfer_size;
+
ret = usb_gadget_map_request(&dwc->gadget, &req->request,
- event->endpoint_number);
+ dep->number);
if (ret) {
dev_dbg(dwc->dev, "failed to map request\n");
return;
}
- WARN_ON(req->request.length > dep->endpoint.maxpacket);
+ WARN_ON(req->request.length > DWC3_EP0_BOUNCE_SIZE);
+
+ transfer_size = roundup(req->request.length,
+ (u32) dep->endpoint.maxpacket);
dwc->ep0_bounced = true;
/*
- * REVISIT in case request length is bigger than EP0
- * wMaxPacketSize, we will need two chained TRBs to handle
- * the transfer.
+ * REVISIT in case request length is bigger than
+ * DWC3_EP0_BOUNCE_SIZE we will need two chained
+ * TRBs to handle the transfer.
*/
- ret = dwc3_ep0_start_trans(dwc, event->endpoint_number,
- dwc->ep0_bounce_addr, dep->endpoint.maxpacket,
+ ret = dwc3_ep0_start_trans(dwc, dep->number,
+ dwc->ep0_bounce_addr, transfer_size,
DWC3_TRBCTL_CONTROL_DATA);
} else {
ret = usb_gadget_map_request(&dwc->gadget, &req->request,
- event->endpoint_number);
+ dep->number);
if (ret) {
dev_dbg(dwc->dev, "failed to map request\n");
return;
}
- ret = dwc3_ep0_start_trans(dwc, event->endpoint_number,
- req->request.dma, req->request.length,
- DWC3_TRBCTL_CONTROL_DATA);
+ ret = dwc3_ep0_start_trans(dwc, dep->number, req->request.dma,
+ req->request.length, DWC3_TRBCTL_CONTROL_DATA);
}
WARN_ON(ret < 0);
@@ -728,10 +946,8 @@
dwc->ctrl_req_addr, 0, type);
}
-static void dwc3_ep0_do_control_status(struct dwc3 *dwc, u32 epnum)
+static void __dwc3_ep0_do_control_status(struct dwc3 *dwc, struct dwc3_ep *dep)
{
- struct dwc3_ep *dep = dwc->eps[epnum];
-
if (dwc->resize_fifos) {
dev_dbg(dwc->dev, "starting to resize fifos\n");
dwc3_gadget_resize_tx_fifos(dwc);
@@ -741,107 +957,78 @@
WARN_ON(dwc3_ep0_start_control_status(dep));
}
+static void dwc3_ep0_do_control_status(struct dwc3 *dwc,
+ const struct dwc3_event_depevt *event)
+{
+ struct dwc3_ep *dep = dwc->eps[event->endpoint_number];
+
+ __dwc3_ep0_do_control_status(dwc, dep);
+}
+
+static void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep)
+{
+ struct dwc3_gadget_ep_cmd_params params;
+ u32 cmd;
+ int ret;
+
+ if (!dep->resource_index)
+ return;
+
+ cmd = DWC3_DEPCMD_ENDTRANSFER;
+ cmd |= DWC3_DEPCMD_CMDIOC;
+ cmd |= DWC3_DEPCMD_PARAM(dep->resource_index);
+ memset(¶ms, 0, sizeof(params));
+ ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, cmd, ¶ms);
+ WARN_ON_ONCE(ret);
+ dep->resource_index = 0;
+}
+
static void dwc3_ep0_xfernotready(struct dwc3 *dwc,
const struct dwc3_event_depevt *event)
{
dwc->setup_packet_pending = true;
- /*
- * This part is very tricky: If we has just handled
- * XferNotReady(Setup) and we're now expecting a
- * XferComplete but, instead, we receive another
- * XferNotReady(Setup), we should STALL and restart
- * the state machine.
- *
- * In all other cases, we just continue waiting
- * for the XferComplete event.
- *
- * We are a little bit unsafe here because we're
- * not trying to ensure that last event was, indeed,
- * XferNotReady(Setup).
- *
- * Still, we don't expect any condition where that
- * should happen and, even if it does, it would be
- * another error condition.
- */
- if (dwc->ep0_next_event == DWC3_EP0_COMPLETE) {
- switch (event->status) {
- case DEPEVT_STATUS_CONTROL_SETUP:
- dev_vdbg(dwc->dev, "Unexpected XferNotReady(Setup)\n");
- dwc3_ep0_stall_and_restart(dwc);
- break;
- case DEPEVT_STATUS_CONTROL_DATA:
- /* FALLTHROUGH */
- case DEPEVT_STATUS_CONTROL_STATUS:
- /* FALLTHROUGH */
- default:
- dev_vdbg(dwc->dev, "waiting for XferComplete\n");
- }
-
- return;
- }
-
switch (event->status) {
- case DEPEVT_STATUS_CONTROL_SETUP:
- dev_vdbg(dwc->dev, "Control Setup\n");
-
- dwc->ep0state = EP0_SETUP_PHASE;
-
- dwc3_ep0_do_control_setup(dwc, event);
- break;
-
case DEPEVT_STATUS_CONTROL_DATA:
dev_vdbg(dwc->dev, "Control Data\n");
- dwc->ep0state = EP0_DATA_PHASE;
-
- if (dwc->ep0_next_event != DWC3_EP0_NRDY_DATA) {
- dev_vdbg(dwc->dev, "Expected %d got %d\n",
- dwc->ep0_next_event,
- DWC3_EP0_NRDY_DATA);
-
- dwc3_ep0_stall_and_restart(dwc);
- return;
- }
-
/*
- * One of the possible error cases is when Host _does_
- * request for Data Phase, but it does so on the wrong
- * direction.
+ * We already have a DATA transfer in the controller's cache,
+ * if we receive a XferNotReady(DATA) we will ignore it, unless
+ * it's for the wrong direction.
*
- * Here, we already know ep0_next_event is DATA (see above),
- * so we only need to check for direction.
+ * In that case, we must issue END_TRANSFER command to the Data
+ * Phase we already have started and issue SetStall on the
+ * control endpoint.
*/
if (dwc->ep0_expect_in != event->endpoint_number) {
+ struct dwc3_ep *dep = dwc->eps[dwc->ep0_expect_in];
+
dev_vdbg(dwc->dev, "Wrong direction for Data phase\n");
+ dwc3_ep0_end_control_data(dwc, dep);
dwc3_ep0_stall_and_restart(dwc);
return;
}
- dwc3_ep0_do_control_data(dwc, event);
break;
case DEPEVT_STATUS_CONTROL_STATUS:
+ if (dwc->ep0_next_event != DWC3_EP0_NRDY_STATUS)
+ return;
+
dev_vdbg(dwc->dev, "Control Status\n");
dwc->ep0state = EP0_STATUS_PHASE;
- if (dwc->ep0_next_event != DWC3_EP0_NRDY_STATUS) {
- dev_vdbg(dwc->dev, "Expected %d got %d\n",
- dwc->ep0_next_event,
- DWC3_EP0_NRDY_STATUS);
-
- dwc3_ep0_stall_and_restart(dwc);
- return;
- }
-
- if (dwc->delayed_status) {
+ if (dwc->delayed_status &&
+ list_empty(&dwc->eps[0]->request_list)) {
WARN_ON_ONCE(event->endpoint_number != 1);
dev_vdbg(dwc->dev, "Mass Storage delayed status\n");
return;
}
+ dwc->delayed_status = false;
- dwc3_ep0_do_control_status(dwc, event->endpoint_number);
+ dwc3_ep0_do_control_status(dwc, event);
}
}
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index f431e9d..451de18 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -101,6 +101,23 @@
int retries = 10000;
u32 reg;
+ /*
+ * Wait until device controller is ready. Only applies to 1.94a and
+ * later RTL.
+ */
+ if (dwc->revision >= DWC3_REVISION_194A) {
+ while (--retries) {
+ reg = dwc3_readl(dwc->regs, DWC3_DSTS);
+ if (reg & DWC3_DSTS_DCNRD)
+ udelay(5);
+ else
+ break;
+ }
+
+ if (retries <= 0)
+ return -ETIMEDOUT;
+ }
+
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
reg &= ~DWC3_DCTL_ULSTCHNGREQ_MASK;
@@ -108,7 +125,15 @@
reg |= DWC3_DCTL_ULSTCHNGREQ(state);
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+ /*
+ * The following code is racy when called from dwc3_gadget_wakeup,
+ * and is not needed, at least on newer versions
+ */
+ if (dwc->revision >= DWC3_REVISION_194A)
+ return 0;
+
/* wait for a change in DSTS */
+ retries = 10000;
while (--retries) {
reg = dwc3_readl(dwc->regs, DWC3_DSTS);
@@ -179,8 +204,8 @@
if (!(dep->flags & DWC3_EP_ENABLED))
continue;
- if (usb_endpoint_xfer_bulk(dep->desc)
- || usb_endpoint_xfer_isoc(dep->desc))
+ if (usb_endpoint_xfer_bulk(dep->endpoint.desc)
+ || usb_endpoint_xfer_isoc(dep->endpoint.desc))
mult = 3;
/*
@@ -230,7 +255,7 @@
* completed (not the LINK TRB).
*/
if (((dep->busy_slot & DWC3_TRB_MASK) == DWC3_TRB_NUM - 1) &&
- usb_endpoint_xfer_isoc(dep->desc))
+ usb_endpoint_xfer_isoc(dep->endpoint.desc))
dep->busy_slot++;
}
list_del(&req->list);
@@ -239,8 +264,11 @@
if (req->request.status == -EINPROGRESS)
req->request.status = status;
- usb_gadget_unmap_request(&dwc->gadget, &req->request,
- req->direction);
+ if (dwc->ep0_bounced && dep->number == 0)
+ dwc->ep0_bounced = false;
+ else
+ usb_gadget_unmap_request(&dwc->gadget, &req->request,
+ req->direction);
dev_dbg(dwc->dev, "request %p from %s completed %d/%d ===> %d\n",
req, dep->name, req->request.actual,
@@ -266,8 +294,8 @@
return "Clear Stall";
case DWC3_DEPCMD_SETSTALL:
return "Set Stall";
- case DWC3_DEPCMD_GETSEQNUMBER:
- return "Get Data Sequence Number";
+ case DWC3_DEPCMD_GETEPSTATE:
+ return "Get Endpoint State";
case DWC3_DEPCMD_SETTRANSFRESOURCE:
return "Set Endpoint Transfer Resource";
case DWC3_DEPCMD_SETEPCONFIG:
@@ -277,6 +305,33 @@
}
}
+int dwc3_send_gadget_generic_command(struct dwc3 *dwc, int cmd, u32 param)
+{
+ u32 timeout = 500;
+ u32 reg;
+
+ dwc3_writel(dwc->regs, DWC3_DGCMDPAR, param);
+ dwc3_writel(dwc->regs, DWC3_DGCMD, cmd | DWC3_DGCMD_CMDACT);
+
+ do {
+ reg = dwc3_readl(dwc->regs, DWC3_DGCMD);
+ if (!(reg & DWC3_DGCMD_CMDACT)) {
+ dev_vdbg(dwc->dev, "Command Complete --> %d\n",
+ DWC3_DGCMD_STATUS(reg));
+ return 0;
+ }
+
+ /*
+ * We can't sleep here, because it's also called from
+ * interrupt context.
+ */
+ timeout--;
+ if (!timeout)
+ return -ETIMEDOUT;
+ udelay(1);
+ } while (1);
+}
+
int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
unsigned cmd, struct dwc3_gadget_ep_cmd_params *params)
{
@@ -380,15 +435,25 @@
static int dwc3_gadget_set_ep_config(struct dwc3 *dwc, struct dwc3_ep *dep,
const struct usb_endpoint_descriptor *desc,
- const struct usb_ss_ep_comp_descriptor *comp_desc)
+ const struct usb_ss_ep_comp_descriptor *comp_desc,
+ bool ignore)
{
struct dwc3_gadget_ep_cmd_params params;
memset(¶ms, 0x00, sizeof(params));
params.param0 = DWC3_DEPCFG_EP_TYPE(usb_endpoint_type(desc))
- | DWC3_DEPCFG_MAX_PACKET_SIZE(usb_endpoint_maxp(desc))
- | DWC3_DEPCFG_BURST_SIZE(dep->endpoint.maxburst);
+ | DWC3_DEPCFG_MAX_PACKET_SIZE(usb_endpoint_maxp(desc));
+
+ /* Burst size is only needed in SuperSpeed mode */
+ if (dwc->gadget.speed == USB_SPEED_SUPER) {
+ u32 burst = dep->endpoint.maxburst - 1;
+
+ params.param0 |= DWC3_DEPCFG_BURST_SIZE(burst);
+ }
+
+ if (ignore)
+ params.param0 |= DWC3_DEPCFG_IGN_SEQ_NUM;
params.param1 = DWC3_DEPCFG_XFER_COMPLETE_EN
| DWC3_DEPCFG_XFER_NOT_READY_EN;
@@ -447,7 +512,8 @@
*/
static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep,
const struct usb_endpoint_descriptor *desc,
- const struct usb_ss_ep_comp_descriptor *comp_desc)
+ const struct usb_ss_ep_comp_descriptor *comp_desc,
+ bool ignore)
{
struct dwc3 *dwc = dep->dwc;
u32 reg;
@@ -459,7 +525,7 @@
return ret;
}
- ret = dwc3_gadget_set_ep_config(dwc, dep, desc, comp_desc);
+ ret = dwc3_gadget_set_ep_config(dwc, dep, desc, comp_desc, ignore);
if (ret)
return ret;
@@ -471,7 +537,7 @@
if (ret)
return ret;
- dep->desc = desc;
+ dep->endpoint.desc = desc;
dep->comp_desc = comp_desc;
dep->type = usb_endpoint_type(desc);
dep->flags |= DWC3_EP_ENABLED;
@@ -504,9 +570,17 @@
{
struct dwc3_request *req;
- if (!list_empty(&dep->req_queued))
+ if (!list_empty(&dep->req_queued)) {
dwc3_stop_active_transfer(dwc, dep->number);
+ /* - giveback all requests to gadget driver */
+ while (!list_empty(&dep->req_queued)) {
+ req = next_request(&dep->req_queued);
+
+ dwc3_gadget_giveback(dep, req, -ESHUTDOWN);
+ }
+ }
+
while (!list_empty(&dep->request_list)) {
req = next_request(&dep->request_list);
@@ -534,7 +608,6 @@
dwc3_writel(dwc->regs, DWC3_DALEPENA, reg);
dep->stream_capable = false;
- dep->desc = NULL;
dep->endpoint.desc = NULL;
dep->comp_desc = NULL;
dep->type = 0;
@@ -579,6 +652,12 @@
dep = to_dwc3_ep(ep);
dwc = dep->dwc;
+ if (dep->flags & DWC3_EP_ENABLED) {
+ dev_WARN_ONCE(dwc->dev, true, "%s is already enabled\n",
+ dep->name);
+ return 0;
+ }
+
switch (usb_endpoint_type(desc)) {
case USB_ENDPOINT_XFER_CONTROL:
strlcat(dep->name, "-control", sizeof(dep->name));
@@ -596,16 +675,10 @@
dev_err(dwc->dev, "invalid endpoint transfer type\n");
}
- if (dep->flags & DWC3_EP_ENABLED) {
- dev_WARN_ONCE(dwc->dev, true, "%s is already enabled\n",
- dep->name);
- return 0;
- }
-
dev_vdbg(dwc->dev, "Enabling %s\n", dep->name);
spin_lock_irqsave(&dwc->lock, flags);
- ret = __dwc3_gadget_ep_enable(dep, desc, ep->comp_desc);
+ ret = __dwc3_gadget_ep_enable(dep, desc, ep->comp_desc, false);
spin_unlock_irqrestore(&dwc->lock, flags);
return ret;
@@ -695,7 +768,7 @@
/* Skip the LINK-TRB on ISOC */
if (((cur_slot & DWC3_TRB_MASK) == DWC3_TRB_NUM - 1) &&
- usb_endpoint_xfer_isoc(dep->desc))
+ usb_endpoint_xfer_isoc(dep->endpoint.desc))
return;
if (!req->trb) {
@@ -708,7 +781,7 @@
trb->bpl = lower_32_bits(dma);
trb->bph = upper_32_bits(dma);
- switch (usb_endpoint_type(dep->desc)) {
+ switch (usb_endpoint_type(dep->endpoint.desc)) {
case USB_ENDPOINT_XFER_CONTROL:
trb->ctrl = DWC3_TRBCTL_CONTROL_SETUP;
break;
@@ -716,8 +789,7 @@
case USB_ENDPOINT_XFER_ISOC:
trb->ctrl = DWC3_TRBCTL_ISOCHRONOUS_FIRST;
- /* IOC every DWC3_TRB_NUM / 4 so we can refill */
- if (!(cur_slot % (DWC3_TRB_NUM / 4)))
+ if (!req->request.no_interrupt)
trb->ctrl |= DWC3_TRB_CTRL_IOC;
break;
@@ -733,7 +805,7 @@
BUG();
}
- if (usb_endpoint_xfer_isoc(dep->desc)) {
+ if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
trb->ctrl |= DWC3_TRB_CTRL_ISP_IMI;
trb->ctrl |= DWC3_TRB_CTRL_CSP;
} else {
@@ -744,7 +816,7 @@
trb->ctrl |= DWC3_TRB_CTRL_LST;
}
- if (usb_endpoint_xfer_bulk(dep->desc) && dep->stream_capable)
+ if (usb_endpoint_xfer_bulk(dep->endpoint.desc) && dep->stream_capable)
trb->ctrl |= DWC3_TRB_CTRL_SID_SOFN(req->request.stream_id);
trb->ctrl |= DWC3_TRB_CTRL_HWO;
@@ -772,7 +844,7 @@
trbs_left = (dep->busy_slot - dep->free_slot) & DWC3_TRB_MASK;
/* Can't wrap around on a non-isoc EP since there's no link TRB */
- if (!usb_endpoint_xfer_isoc(dep->desc)) {
+ if (!usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
max = DWC3_TRB_NUM - (dep->free_slot & DWC3_TRB_MASK);
if (trbs_left > max)
trbs_left = max;
@@ -798,7 +870,7 @@
* processed from the first TRB until the last one. Since we
* don't wrap around we have to start at the beginning.
*/
- if (usb_endpoint_xfer_isoc(dep->desc)) {
+ if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
dep->busy_slot = 1;
dep->free_slot = 1;
} else {
@@ -808,7 +880,7 @@
}
/* The last TRB is a link TRB, not used for xfer */
- if ((trbs_left <= 1) && usb_endpoint_xfer_isoc(dep->desc))
+ if ((trbs_left <= 1) && usb_endpoint_xfer_isoc(dep->endpoint.desc))
return;
list_for_each_entry_safe(req, n, &dep->request_list, list) {
@@ -931,14 +1003,45 @@
}
dep->flags |= DWC3_EP_BUSY;
- dep->res_trans_idx = dwc3_gadget_ep_get_transfer_index(dwc,
- dep->number);
- WARN_ON_ONCE(!dep->res_trans_idx);
+ if (start_new) {
+ dep->resource_index = dwc3_gadget_ep_get_transfer_index(dwc,
+ dep->number);
+ WARN_ON_ONCE(!dep->resource_index);
+ }
return 0;
}
+static void __dwc3_gadget_start_isoc(struct dwc3 *dwc,
+ struct dwc3_ep *dep, u32 cur_uf)
+{
+ u32 uf;
+
+ if (list_empty(&dep->request_list)) {
+ dev_vdbg(dwc->dev, "ISOC ep %s run out for requests.\n",
+ dep->name);
+ dep->flags |= DWC3_EP_PENDING_REQUEST;
+ return;
+ }
+
+ /* 4 micro frames in the future */
+ uf = cur_uf + dep->interval * 4;
+
+ __dwc3_gadget_kick_transfer(dep, uf, 1);
+}
+
+static void dwc3_gadget_start_isoc(struct dwc3 *dwc,
+ struct dwc3_ep *dep, const struct dwc3_event_depevt *event)
+{
+ u32 cur_uf, mask;
+
+ mask = ~(dep->interval - 1);
+ cur_uf = event->parameters & mask;
+
+ __dwc3_gadget_start_isoc(dwc, dep, cur_uf);
+}
+
static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
{
struct dwc3 *dwc = dep->dwc;
@@ -969,34 +1072,61 @@
list_add_tail(&req->list, &dep->request_list);
/*
- * There is one special case: XferNotReady with
- * empty list of requests. We need to kick the
- * transfer here in that situation, otherwise
- * we will be NAKing forever.
+ * There are a few special cases:
*
- * If we get XferNotReady before gadget driver
- * has a chance to queue a request, we will ACK
- * the IRQ but won't be able to receive the data
- * until the next request is queued. The following
- * code is handling exactly that.
+ * 1. XferNotReady with empty list of requests. We need to kick the
+ * transfer here in that situation, otherwise we will be NAKing
+ * forever. If we get XferNotReady before gadget driver has a
+ * chance to queue a request, we will ACK the IRQ but won't be
+ * able to receive the data until the next request is queued.
+ * The following code is handling exactly that.
+ *
*/
if (dep->flags & DWC3_EP_PENDING_REQUEST) {
- int ret;
- int start_trans;
+ int ret;
- start_trans = 1;
- if (usb_endpoint_xfer_isoc(dep->desc) &&
- (dep->flags & DWC3_EP_BUSY))
- start_trans = 0;
+ /*
+ * If xfernotready is already elapsed and it is a case
+ * of isoc transfer, then issue END TRANSFER, so that
+ * you can receive xfernotready again and can have
+ * notion of current microframe.
+ */
+ if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
+ dwc3_stop_active_transfer(dwc, dep->number);
+ return 0;
+ }
- ret = __dwc3_gadget_kick_transfer(dep, 0, start_trans);
- if (ret && ret != -EBUSY) {
- struct dwc3 *dwc = dep->dwc;
-
+ ret = __dwc3_gadget_kick_transfer(dep, 0, true);
+ if (ret && ret != -EBUSY)
dev_dbg(dwc->dev, "%s: failed to kick transfers\n",
dep->name);
- }
- };
+ }
+
+ /*
+ * 2. XferInProgress on Isoc EP with an active transfer. We need to
+ * kick the transfer here after queuing a request, otherwise the
+ * core may not see the modified TRB(s).
+ */
+ if (usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
+ (dep->flags & DWC3_EP_BUSY) &&
+ !(dep->flags & DWC3_EP_MISSED_ISOC)) {
+ WARN_ON_ONCE(!dep->resource_index);
+ ret = __dwc3_gadget_kick_transfer(dep, dep->resource_index,
+ false);
+ if (ret && ret != -EBUSY)
+ dev_dbg(dwc->dev, "%s: failed to kick transfers\n",
+ dep->name);
+ }
+
+ /*
+ * 3. Missed ISOC Handling. We need to start isoc transfer on the saved
+ * uframe number.
+ */
+ if (usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
+ (dep->flags & DWC3_EP_MISSED_ISOC)) {
+ __dwc3_gadget_start_isoc(dwc, dep, dep->current_uf);
+ dep->flags &= ~DWC3_EP_MISSED_ISOC;
+ }
return 0;
}
@@ -1012,7 +1142,7 @@
int ret;
- if (!dep->desc) {
+ if (!dep->endpoint.desc) {
dev_dbg(dwc->dev, "trying to queue request %p to disabled %s\n",
request, ep->name);
return -ESHUTDOWN;
@@ -1058,7 +1188,7 @@
if (r == req) {
/* wait until it is processed */
dwc3_stop_active_transfer(dwc, dep->number);
- goto out0;
+ goto out1;
}
dev_err(dwc->dev, "request %p was not queued to %s\n",
request, ep->name);
@@ -1066,6 +1196,7 @@
goto out0;
}
+out1:
/* giveback the request */
dwc3_gadget_giveback(dep, req, -ECONNRESET);
@@ -1084,15 +1215,6 @@
memset(¶ms, 0x00, sizeof(params));
if (value) {
- if (dep->number == 0 || dep->number == 1) {
- /*
- * Whenever EP0 is stalled, we will restart
- * the state machine, thus moving back to
- * Setup Phase
- */
- dwc->ep0state = EP0_SETUP_PHASE;
- }
-
ret = dwc3_send_gadget_ep_cmd(dwc, dep->number,
DWC3_DEPCMD_SETSTALL, ¶ms);
if (ret)
@@ -1102,9 +1224,6 @@
else
dep->flags |= DWC3_EP_STALL;
} else {
- if (dep->flags & DWC3_EP_WEDGE)
- return 0;
-
ret = dwc3_send_gadget_ep_cmd(dwc, dep->number,
DWC3_DEPCMD_CLEARSTALL, ¶ms);
if (ret)
@@ -1112,7 +1231,7 @@
value ? "set" : "clear",
dep->name);
else
- dep->flags &= ~DWC3_EP_STALL;
+ dep->flags &= ~(DWC3_EP_STALL | DWC3_EP_WEDGE);
}
return ret;
@@ -1129,7 +1248,7 @@
spin_lock_irqsave(&dwc->lock, flags);
- if (usb_endpoint_xfer_isoc(dep->desc)) {
+ if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
dev_err(dwc->dev, "%s is of Isochronous type\n", dep->name);
ret = -EINVAL;
goto out;
@@ -1152,7 +1271,10 @@
dep->flags |= DWC3_EP_WEDGE;
spin_unlock_irqrestore(&dwc->lock, flags);
- return dwc3_gadget_ep_set_halt(ep, 1);
+ if (dep->number == 0 || dep->number == 1)
+ return dwc3_gadget_ep0_set_halt(ep, 1);
+ else
+ return dwc3_gadget_ep_set_halt(ep, 1);
}
/* -------------------------------------------------------------------------- */
@@ -1170,7 +1292,7 @@
.free_request = dwc3_gadget_ep_free_request,
.queue = dwc3_gadget_ep0_queue,
.dequeue = dwc3_gadget_ep_dequeue,
- .set_halt = dwc3_gadget_ep_set_halt,
+ .set_halt = dwc3_gadget_ep0_set_halt,
.set_wedge = dwc3_gadget_ep_set_wedge,
};
@@ -1246,9 +1368,13 @@
goto out;
}
- /* write zeroes to Link Change Request */
- reg &= ~DWC3_DCTL_ULSTCHNGREQ_MASK;
- dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+ /* Recent versions do this automatically */
+ if (dwc->revision < DWC3_REVISION_194A) {
+ /* write zeroes to Link Change Request */
+ reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ reg &= ~DWC3_DCTL_ULSTCHNGREQ_MASK;
+ dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+ }
/* poll until Link State changes to ON */
timeout = jiffies + msecs_to_jiffies(100);
@@ -1285,16 +1411,21 @@
return 0;
}
-static void dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on)
+static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on)
{
u32 reg;
u32 timeout = 500;
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
if (is_on) {
- reg &= ~DWC3_DCTL_TRGTULST_MASK;
- reg |= (DWC3_DCTL_RUN_STOP
- | DWC3_DCTL_TRGTULST_RX_DET);
+ if (dwc->revision <= DWC3_REVISION_187A) {
+ reg &= ~DWC3_DCTL_TRGTULST_MASK;
+ reg |= DWC3_DCTL_TRGTULST_RX_DET;
+ }
+
+ if (dwc->revision >= DWC3_REVISION_194A)
+ reg &= ~DWC3_DCTL_KEEP_CONNECT;
+ reg |= DWC3_DCTL_RUN_STOP;
} else {
reg &= ~DWC3_DCTL_RUN_STOP;
}
@@ -1312,7 +1443,7 @@
}
timeout--;
if (!timeout)
- break;
+ return -ETIMEDOUT;
udelay(1);
} while (1);
@@ -1320,12 +1451,15 @@
dwc->gadget_driver
? dwc->gadget_driver->function : "no-function",
is_on ? "connect" : "disconnect");
+
+ return 0;
}
static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
{
struct dwc3 *dwc = gadget_to_dwc(g);
unsigned long flags;
+ int ret;
is_on = !!is_on;
@@ -1345,17 +1479,18 @@
return 0;
}
- dwc3_gadget_run_stop(dwc, is_on);
+ ret = dwc3_gadget_run_stop(dwc, is_on);
spin_unlock_irqrestore(&dwc->lock, flags);
- return 0;
+ return ret;
}
static int dwc3_gadget_vbus_session(struct usb_gadget *_gadget, int is_active)
{
struct dwc3 *dwc = gadget_to_dwc(_gadget);
unsigned long flags;
+ int ret = 0;
if (!dwc->dotg)
return -EPERM;
@@ -1377,15 +1512,46 @@
* Both vbus was activated by otg and pullup was
* signaled by the gadget driver.
*/
- dwc3_gadget_run_stop(dwc, 1);
+ ret = dwc3_gadget_run_stop(dwc, 1);
} else {
- dwc3_gadget_run_stop(dwc, 0);
+ ret = dwc3_gadget_run_stop(dwc, 0);
}
}
spin_unlock_irqrestore(&dwc->lock, flags);
- return 0;
+ 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,
@@ -1412,7 +1578,24 @@
reg = dwc3_readl(dwc->regs, DWC3_DCFG);
reg &= ~(DWC3_DCFG_SPEED_MASK);
- reg |= dwc->maximum_speed;
+
+ /**
+ * WORKAROUND: DWC3 revision < 2.20a have an issue
+ * which would cause metastability state on Run/Stop
+ * bit if we try to force the IP to USB2-only mode.
+ *
+ * Because of that, we cannot configure the IP to any
+ * speed other than the SuperSpeed
+ *
+ * Refers to:
+ *
+ * STAR#9000525659: Clock Domain Crossing on DCTL in
+ * USB 2.0 Mode
+ */
+ if (dwc->revision < DWC3_REVISION_220A)
+ reg |= DWC3_DCFG_SUPERSPEED;
+ else
+ reg |= dwc->maximum_speed;
dwc3_writel(dwc->regs, DWC3_DCFG, reg);
dwc->start_config_issued = false;
@@ -1421,14 +1604,14 @@
dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);
dep = dwc->eps[0];
- ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL);
+ 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);
goto err0;
}
dep = dwc->eps[1];
- ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL);
+ 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);
goto err1;
@@ -1469,6 +1652,7 @@
return 0;
}
+
static const struct usb_gadget_ops dwc3_gadget_ops = {
.get_frame = dwc3_gadget_get_frame,
.wakeup = dwc3_gadget_wakeup,
@@ -1560,6 +1744,7 @@
struct dwc3_trb *trb;
unsigned int count;
unsigned int s_pkt = 0;
+ unsigned int trb_status;
do {
req = next_request(&dep->req_queued);
@@ -1585,9 +1770,18 @@
if (dep->direction) {
if (count) {
- dev_err(dwc->dev, "incomplete IN transfer %s\n",
- dep->name);
- status = -ECONNRESET;
+ trb_status = DWC3_TRB_SIZE_TRBSTS(trb->size);
+ if (trb_status == DWC3_TRBSTS_MISSED_ISOC) {
+ dev_dbg(dwc->dev, "incomplete IN transfer %s\n",
+ dep->name);
+ dep->current_uf = event->parameters &
+ ~(dep->interval - 1);
+ dep->flags |= DWC3_EP_MISSED_ISOC;
+ } else {
+ dev_err(dwc->dev, "incomplete IN transfer %s\n",
+ dep->name);
+ status = -ECONNRESET;
+ }
}
} else {
if (count && (event->status & DEPEVT_STATUS_SHORT))
@@ -1606,7 +1800,8 @@
if (s_pkt)
break;
if ((event->status & DEPEVT_STATUS_LST) &&
- (trb->ctrl & DWC3_TRB_CTRL_LST))
+ (trb->ctrl & (DWC3_TRB_CTRL_LST |
+ DWC3_TRB_CTRL_HWO)))
break;
if ((event->status & DEPEVT_STATUS_IOC) &&
(trb->ctrl & DWC3_TRB_CTRL_IOC))
@@ -1642,7 +1837,7 @@
int i;
for (i = 0; i < DWC3_ENDPOINTS_NUM; i++) {
- struct dwc3_ep *dep = dwc->eps[i];
+ dep = dwc->eps[i];
if (!(dep->flags & DWC3_EP_ENABLED))
continue;
@@ -1659,65 +1854,6 @@
}
}
-static void dwc3_gadget_start_isoc(struct dwc3 *dwc,
- struct dwc3_ep *dep, const struct dwc3_event_depevt *event)
-{
- u32 uf, mask;
-
- if (list_empty(&dep->request_list)) {
- dev_vdbg(dwc->dev, "ISOC ep %s run out for requests.\n",
- dep->name);
- return;
- }
-
- mask = ~(dep->interval - 1);
- uf = event->parameters & mask;
- /* 4 micro frames in the future */
- uf += dep->interval * 4;
-
- __dwc3_gadget_kick_transfer(dep, uf, 1);
-}
-
-static void dwc3_process_ep_cmd_complete(struct dwc3_ep *dep,
- const struct dwc3_event_depevt *event)
-{
- struct dwc3 *dwc = dep->dwc;
- struct dwc3_event_depevt mod_ev = *event;
-
- /*
- * We were asked to remove one request. It is possible that this
- * request and a few others were started together and have the same
- * transfer index. Since we stopped the complete endpoint we don't
- * know how many requests were already completed (and not yet)
- * reported and how could be done (later). We purge them all until
- * the end of the list.
- */
- mod_ev.status = DEPEVT_STATUS_LST;
- dwc3_cleanup_done_reqs(dwc, dep, &mod_ev, -ESHUTDOWN);
- dep->flags &= ~DWC3_EP_BUSY;
- /* pending requests are ignored and are queued on XferNotReady */
-}
-
-static void dwc3_ep_cmd_compl(struct dwc3_ep *dep,
- const struct dwc3_event_depevt *event)
-{
- u32 param = event->parameters;
- u32 cmd_type = (param >> 8) & ((1 << 5) - 1);
-
- switch (cmd_type) {
- case DWC3_DEPCMD_ENDTRANSFER:
- dwc3_process_ep_cmd_complete(dep, event);
- break;
- case DWC3_DEPCMD_STARTTRANSFER:
- dep->res_trans_idx = param & 0x7f;
- break;
- default:
- printk(KERN_ERR "%s() unknown /unexpected type: %d\n",
- __func__, cmd_type);
- break;
- };
-}
-
static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
const struct dwc3_event_depevt *event)
{
@@ -1726,6 +1862,9 @@
dep = dwc->eps[epnum];
+ if (!(dep->flags & DWC3_EP_ENABLED))
+ return;
+
dev_vdbg(dwc->dev, "%s: %s\n", dep->name,
dwc3_ep_event_string(event->endpoint_event));
@@ -1736,9 +1875,9 @@
switch (event->endpoint_event) {
case DWC3_DEPEVT_XFERCOMPLETE:
- dep->res_trans_idx = 0;
+ dep->resource_index = 0;
- if (usb_endpoint_xfer_isoc(dep->desc)) {
+ if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
dev_dbg(dwc->dev, "%s is an Isochronous endpoint\n",
dep->name);
return;
@@ -1747,7 +1886,7 @@
dwc3_endpoint_transfer_complete(dwc, dep, event, 1);
break;
case DWC3_DEPEVT_XFERINPROGRESS:
- if (!usb_endpoint_xfer_isoc(dep->desc)) {
+ if (!usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
dev_dbg(dwc->dev, "%s is not an Isochronous endpoint\n",
dep->name);
return;
@@ -1756,7 +1895,7 @@
dwc3_endpoint_transfer_complete(dwc, dep, event, 0);
break;
case DWC3_DEPEVT_XFERNOTREADY:
- if (usb_endpoint_xfer_isoc(dep->desc)) {
+ if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
dwc3_gadget_start_isoc(dwc, dep, event);
} else {
int ret;
@@ -1777,7 +1916,7 @@
break;
case DWC3_DEPEVT_STREAMEVT:
- if (!usb_endpoint_xfer_bulk(dep->desc)) {
+ if (!usb_endpoint_xfer_bulk(dep->endpoint.desc)) {
dev_err(dwc->dev, "Stream event for non-Bulk %s\n",
dep->name);
return;
@@ -1799,7 +1938,7 @@
dev_dbg(dwc->dev, "%s FIFO Overrun\n", dep->name);
break;
case DWC3_DEPEVT_EPCMDCMPLT:
- dwc3_ep_cmd_compl(dep, event);
+ dev_vdbg(dwc->dev, "Endpoint Command Complete\n");
break;
}
}
@@ -1822,16 +1961,37 @@
dep = dwc->eps[epnum];
- WARN_ON(!dep->res_trans_idx);
- if (dep->res_trans_idx) {
- cmd = DWC3_DEPCMD_ENDTRANSFER;
- cmd |= DWC3_DEPCMD_HIPRI_FORCERM | DWC3_DEPCMD_CMDIOC;
- cmd |= DWC3_DEPCMD_PARAM(dep->res_trans_idx);
- memset(¶ms, 0, sizeof(params));
- ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, cmd, ¶ms);
- WARN_ON_ONCE(ret);
- dep->res_trans_idx = 0;
- }
+ if (!dep->resource_index)
+ return;
+
+ /*
+ * NOTICE: We are violating what the Databook says about the
+ * EndTransfer command. Ideally we would _always_ wait for the
+ * EndTransfer Command Completion IRQ, but that's causing too
+ * much trouble synchronizing between us and gadget driver.
+ *
+ * We have discussed this with the IP Provider and it was
+ * suggested to giveback all requests here, but give HW some
+ * extra time to synchronize with the interconnect. We're using
+ * an arbitraty 100us delay for that.
+ *
+ * Note also that a similar handling was tested by Synopsys
+ * (thanks a lot Paul) and nothing bad has come out of it.
+ * In short, what we're doing is:
+ *
+ * - Issue EndTransfer WITH CMDIOC bit set
+ * - Wait 100us
+ */
+
+ cmd = DWC3_DEPCMD_ENDTRANSFER;
+ cmd |= DWC3_DEPCMD_HIPRI_FORCERM | DWC3_DEPCMD_CMDIOC;
+ cmd |= DWC3_DEPCMD_PARAM(dep->resource_index);
+ memset(¶ms, 0, sizeof(params));
+ ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, cmd, ¶ms);
+ WARN_ON_ONCE(ret);
+ dep->resource_index = 0;
+
+ udelay(100);
}
static void dwc3_stop_active_transfers(struct dwc3 *dwc)
@@ -1874,11 +2034,9 @@
static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc)
{
+ int reg;
+
dev_vdbg(dwc->dev, "%s\n", __func__);
-#if 0
- XXX
- U1/U2 is powersave optimization. Skip it for now. Anyway we need to
- enable it before we can disable it.
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
reg &= ~DWC3_DCTL_INITU1ENA;
@@ -1886,9 +2044,7 @@
reg &= ~DWC3_DCTL_INITU2ENA;
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
-#endif
- dwc3_stop_active_transfers(dwc);
dwc3_disconnect_gadget(dwc);
dwc->start_config_issued = false;
@@ -1896,30 +2052,30 @@
dwc->setup_packet_pending = false;
}
-static void dwc3_gadget_usb3_phy_power(struct dwc3 *dwc, int on)
+static void dwc3_gadget_usb3_phy_suspend(struct dwc3 *dwc, int suspend)
{
u32 reg;
reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));
- if (on)
- reg &= ~DWC3_GUSB3PIPECTL_SUSPHY;
- else
+ if (suspend)
reg |= DWC3_GUSB3PIPECTL_SUSPHY;
+ else
+ reg &= ~DWC3_GUSB3PIPECTL_SUSPHY;
dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg);
}
-static void dwc3_gadget_usb2_phy_power(struct dwc3 *dwc, int on)
+static void dwc3_gadget_usb2_phy_suspend(struct dwc3 *dwc, int suspend)
{
u32 reg;
reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
- if (on)
- reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
- else
+ if (suspend)
reg |= DWC3_GUSB2PHYCFG_SUSPHY;
+ else
+ reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
}
@@ -1964,9 +2120,12 @@
/* after reset -> Default State */
dwc->dev_state = DWC3_DEFAULT_STATE;
- /* Enable PHYs */
- dwc3_gadget_usb2_phy_power(dwc, true);
- dwc3_gadget_usb3_phy_power(dwc, true);
+ /* Recent versions support automatic phy suspend and don't need this */
+ if (dwc->revision < DWC3_REVISION_194A) {
+ /* Resume PHYs */
+ dwc3_gadget_usb2_phy_suspend(dwc, false);
+ dwc3_gadget_usb3_phy_suspend(dwc, false);
+ }
if (dwc->gadget.speed != USB_SPEED_UNKNOWN)
dwc3_disconnect_gadget(dwc);
@@ -2011,16 +2170,16 @@
dwc3_writel(dwc->regs, DWC3_GCTL, reg);
}
-static void dwc3_gadget_disable_phy(struct dwc3 *dwc, u8 speed)
+static void dwc3_gadget_phy_suspend(struct dwc3 *dwc, u8 speed)
{
switch (speed) {
case USB_SPEED_SUPER:
- dwc3_gadget_usb2_phy_power(dwc, false);
+ dwc3_gadget_usb2_phy_suspend(dwc, true);
break;
case USB_SPEED_HIGH:
case USB_SPEED_FULL:
case USB_SPEED_LOW:
- dwc3_gadget_usb3_phy_power(dwc, false);
+ dwc3_gadget_usb3_phy_suspend(dwc, true);
break;
}
}
@@ -2083,18 +2242,21 @@
break;
}
- /* Disable unneded PHY */
- dwc3_gadget_disable_phy(dwc, dwc->gadget.speed);
+ /* Recent versions support automatic phy suspend and don't need this */
+ if (dwc->revision < DWC3_REVISION_194A) {
+ /* Suspend unneeded PHY */
+ dwc3_gadget_phy_suspend(dwc, dwc->gadget.speed);
+ }
dep = dwc->eps[0];
- ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL);
+ ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, true);
if (ret) {
dev_err(dwc->dev, "failed to enable %s\n", dep->name);
return;
}
dep = dwc->eps[1];
- ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL);
+ ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, true);
if (ret) {
dev_err(dwc->dev, "failed to enable %s\n", dep->name);
return;
@@ -2347,8 +2509,7 @@
goto err1;
}
- dwc->setup_buf = kzalloc(sizeof(*dwc->setup_buf) * 2,
- GFP_KERNEL);
+ dwc->setup_buf = kzalloc(DWC3_EP0_BOUNCE_SIZE, GFP_KERNEL);
if (!dwc->setup_buf) {
dev_err(dwc->dev, "failed to allocate setup buffer\n");
ret = -ENOMEM;
@@ -2356,7 +2517,8 @@
}
dwc->ep0_bounce = dma_alloc_coherent(dwc->dev,
- 512, &dwc->ep0_bounce_addr, GFP_KERNEL);
+ DWC3_EP0_BOUNCE_SIZE, &dwc->ep0_bounce_addr,
+ GFP_KERNEL);
if (!dwc->ep0_bounce) {
dev_err(dwc->dev, "failed to allocate ep0 bounce buffer\n");
ret = -ENOMEM;
@@ -2397,6 +2559,10 @@
goto err5;
}
+ reg = dwc3_readl(dwc->regs, DWC3_DCFG);
+ reg |= DWC3_DCFG_LPM_CAP;
+ dwc3_writel(dwc->regs, DWC3_DCFG, reg);
+
/* Enable all but Start and End of Frame IRQs */
reg = (DWC3_DEVTEN_EVNTOVERFLOWEN |
DWC3_DEVTEN_CMDCMPLTEN |
@@ -2408,6 +2574,24 @@
DWC3_DEVTEN_DISCONNEVTEN);
dwc3_writel(dwc->regs, DWC3_DEVTEN, reg);
+ /* Enable USB2 LPM and automatic phy suspend only on recent versions */
+ if (dwc->revision >= DWC3_REVISION_194A) {
+ reg = dwc3_readl(dwc->regs, DWC3_DCFG);
+ reg |= DWC3_DCFG_LPM_CAP;
+ dwc3_writel(dwc->regs, DWC3_DCFG, reg);
+
+ reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ reg &= ~(DWC3_DCTL_HIRD_THRES_MASK | DWC3_DCTL_L1_HIBER_EN);
+
+ /* TODO: This should be configurable */
+ reg |= DWC3_DCTL_HIRD_THRES(28);
+
+ dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+
+ dwc3_gadget_usb2_phy_suspend(dwc, false);
+ dwc3_gadget_usb3_phy_suspend(dwc, false);
+ }
+
ret = device_register(&dwc->gadget.dev);
if (ret) {
dev_err(dwc->dev, "failed to register gadget device\n");
@@ -2448,8 +2632,8 @@
dwc3_gadget_free_endpoints(dwc);
err4:
- dma_free_coherent(dwc->dev, 512, dwc->ep0_bounce,
- dwc->ep0_bounce_addr);
+ dma_free_coherent(dwc->dev, DWC3_EP0_BOUNCE_SIZE,
+ dwc->ep0_bounce, dwc->ep0_bounce_addr);
err3:
kfree(dwc->setup_buf);
@@ -2483,8 +2667,8 @@
dwc3_gadget_free_endpoints(dwc);
- dma_free_coherent(dwc->dev, 512, dwc->ep0_bounce,
- dwc->ep0_bounce_addr);
+ dma_free_coherent(dwc->dev, DWC3_EP0_BOUNCE_SIZE,
+ dwc->ep0_bounce, dwc->ep0_bounce_addr);
kfree(dwc->setup_buf);
diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h
index 662682e..dc7a3c1 100644
--- a/drivers/usb/dwc3/gadget.h
+++ b/drivers/usb/dwc3/gadget.h
@@ -66,7 +66,12 @@
#define DWC3_DEPCFG_FIFO_NUMBER(n) ((n) << 17)
#define DWC3_DEPCFG_BURST_SIZE(n) ((n) << 22)
#define DWC3_DEPCFG_DATA_SEQ_NUM(n) ((n) << 26)
+/* This applies for core versions earlier than 1.94a */
#define DWC3_DEPCFG_IGN_SEQ_NUM (1 << 31)
+/* These apply for core versions 1.94a and later */
+#define DWC3_DEPCFG_ACTION_INIT (0 << 30)
+#define DWC3_DEPCFG_ACTION_RESTORE (1 << 30)
+#define DWC3_DEPCFG_ACTION_MODIFY (2 << 30)
/* DEPXFERCFG parameter 0 */
#define DWC3_DEPXFERCFG_NUM_XFER_RES(n) ((n) & 0xffff)
@@ -106,11 +111,13 @@
void dwc3_ep0_interrupt(struct dwc3 *dwc,
const struct dwc3_event_depevt *event);
void dwc3_ep0_out_start(struct dwc3 *dwc);
+int dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value);
int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
gfp_t gfp_flags);
int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value);
int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
unsigned cmd, struct dwc3_gadget_ep_cmd_params *params);
+int dwc3_send_gadget_generic_command(struct dwc3 *dwc, int cmd, u32 param);
dma_addr_t dwc3_trb_dma_offset(struct dwc3_ep *dep,
struct dwc3_trb *trb);
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/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index de9a7aa..ee1ff46 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -143,7 +143,6 @@
config USB_ATMEL_USBA
tristate "Atmel USBA"
- select USB_GADGET_DUALSPEED
depends on AVR32 || ARCH_AT91SAM9RL || ARCH_AT91SAM9G45
help
USBA is the integrated high-speed USB Device controller on
@@ -152,7 +151,6 @@
config USB_FSL_USB2
tristate "Freescale Highspeed USB DR Peripheral Controller"
depends on FSL_SOC || ARCH_MXC
- select USB_GADGET_DUALSPEED
select USB_FSL_MPH_DR_OF if OF
help
Some of Freescale PowerPC processors have a High Speed
@@ -168,7 +166,6 @@
config USB_FUSB300
tristate "Faraday FUSB300 USB Peripheral Controller"
depends on !PHYS_ADDR_T_64BIT
- select USB_GADGET_DUALSPEED
help
Faraday usb device controller FUSB300 driver
@@ -216,7 +213,6 @@
config USB_R8A66597
tristate "Renesas R8A66597 USB Peripheral Controller"
- select USB_GADGET_DUALSPEED
help
R8A66597 is a discrete USB host and peripheral controller chip that
supports both full and high speed USB 2.0 data transfers.
@@ -229,7 +225,6 @@
config USB_RENESAS_USBHS_UDC
tristate 'Renesas USBHS controller'
depends on USB_RENESAS_USBHS
- select USB_GADGET_DUALSPEED
help
Renesas USBHS is a discrete USB host and peripheral controller chip
that supports both full and high speed USB 2.0 data transfers.
@@ -257,7 +252,6 @@
config USB_S3C_HSOTG
tristate "S3C HS/OtG USB Device controller"
depends on S3C_DEV_USB_HSOTG
- select USB_GADGET_DUALSPEED
help
The Samsung S3C64XX USB2.0 high-speed gadget controller
integrated into the S3C64XX series SoC.
@@ -294,7 +288,6 @@
config USB_S3C_HSUDC
tristate "S3C2416, S3C2443 and S3C2450 USB Device Controller"
depends on ARCH_S3C24XX
- select USB_GADGET_DUALSPEED
help
Samsung's S3C2416, S3C2443 and S3C2450 is an ARM9 based SoC
integrated with dual speed USB 2.0 device controller. It has
@@ -304,7 +297,6 @@
config USB_MV_UDC
tristate "Marvell USB2.0 Device Controller"
- select USB_GADGET_DUALSPEED
help
Marvell Socs (including PXA and MMP series) include a high speed
USB2.0 OTG controller, which can be configured as high speed or
@@ -318,14 +310,12 @@
config USB_GADGET_MUSB_HDRC
tristate "Inventra HDRC USB Peripheral (TI, ADI, ...)"
depends on USB_MUSB_HDRC
- select USB_GADGET_DUALSPEED
help
This OTG-capable silicon IP is used in dual designs including
the TI DaVinci, OMAP 243x, OMAP 343x, TUSB 6010, and ADI Blackfin
config USB_M66592
tristate "Renesas M66592 USB Peripheral Controller"
- select USB_GADGET_DUALSPEED
help
M66592 is a discrete USB peripheral controller chip that
supports both full and high speed USB 2.0 data transfers.
@@ -342,7 +332,6 @@
config USB_AMD5536UDC
tristate "AMD5536 UDC"
depends on PCI
- select USB_GADGET_DUALSPEED
help
The AMD5536 UDC is part of the AMD Geode CS5536, an x86 southbridge.
It is a USB Highspeed DMA capable USB device controller. Beside ep0
@@ -370,7 +359,6 @@
config USB_CI13XXX_PCI
tristate "MIPS USB CI13xxx PCI UDC"
depends on PCI
- select USB_GADGET_DUALSPEED
help
MIPS USB IP core family device controller
Currently it only supports IP part number CI13412
@@ -381,7 +369,6 @@
config USB_NET2272
tristate "PLX NET2272"
- select USB_GADGET_DUALSPEED
help
PLX NET2272 is a USB peripheral controller which supports
both full and high speed USB 2.0 data transfers.
@@ -405,7 +392,6 @@
config USB_NET2280
tristate "NetChip 228x"
depends on PCI
- select USB_GADGET_DUALSPEED
help
NetChip 2280 / 2282 is a PCI based USB peripheral controller which
supports both full and high speed USB 2.0 data transfers.
@@ -436,7 +422,6 @@
tristate "Intel Langwell USB Device Controller"
depends on PCI
depends on !PHYS_ADDR_T_64BIT
- select USB_GADGET_DUALSPEED
help
Intel Langwell USB Device Controller is a High-Speed USB
On-The-Go device controller.
@@ -451,7 +436,6 @@
config USB_EG20T
tristate "Intel EG20T PCH/LAPIS Semiconductor IOH(ML7213/ML7831) UDC"
depends on PCI
- select USB_GADGET_DUALSPEED
help
This is a USB device driver for EG20T PCH.
EG20T PCH is the platform controller hub that is used in Intel's
@@ -474,7 +458,6 @@
config USB_CI13XXX_MSM
tristate "MIPS USB CI13xxx for MSM"
depends on ARCH_MSM
- select USB_GADGET_DUALSPEED
select USB_MSM_OTG
help
MSM SoC has chipidea USB controller. This driver uses
@@ -491,7 +474,6 @@
config USB_CI13XXX_MSM_HSIC
tristate "MIPS HSIC CI13xxx for MSM"
depends on ARCH_MSM
- select USB_GADGET_DUALSPEED
help
MSM SoC has chipidea USB controller. This driver uses
ci13xxx_udc core. Support USB-HSIC core.
@@ -504,7 +486,6 @@
tristate "DesignWare USB3.0 (DRD) Controller for MSM"
depends on ARCH_MSM
select USB_DWC3
- select USB_GADGET_DUALSPEED
select USB_GADGET_SELECTED
help
The DesignWare USB3.0 controller is a SuperSpeed USB3.0 Controller
@@ -516,8 +497,6 @@
tristate "DesignWare USB3.0 (DRD) Controller for OMAP"
depends on ARCH_OMAP
select USB_DWC3
- select USB_GADGET_DUALSPEED
- select USB_GADGET_SUPERSPEED
select USB_GADGET_SELECTED
help
DesignWare USB3.0 controller is a SuperSpeed USB3.0 Controller
@@ -533,7 +512,6 @@
config USB_MSM_72K
tristate "MSM 72K Device Controller"
depends on ARCH_MSM
- select USB_GADGET_DUALSPEED
help
USB gadget driver for Qualcomm MSM 72K architecture.
@@ -544,8 +522,6 @@
config USB_DUMMY_HCD
tristate "Dummy HCD (DEVELOPMENT)"
depends on USB=y || (USB=m && USB_GADGET=m)
- select USB_GADGET_DUALSPEED
- select USB_GADGET_SUPERSPEED
help
This host controller driver emulates USB, looping all data transfer
requests back to a USB "gadget driver" in the same host. The host
@@ -570,22 +546,6 @@
endmenu
-# Selected by UDC drivers that support high-speed operation.
-config USB_GADGET_DUALSPEED
- bool
-
-# Selected by UDC drivers that support super-speed opperation
-config USB_GADGET_SUPERSPEED
- bool "Operate as superspeed"
- depends on USB_GADGET
- depends on USB_GADGET_DUALSPEED
- default n
- help
- When a superspeed peripheral controller is selected
- (for example DesignWare USB3.0 controller), use this flag to
- indicate if the device should operate in superspeed(=y)
- or not.
-
#
# USB Gadget Drivers
#
diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index 86c0e73..576ea1e 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -117,6 +117,7 @@
struct usb_function *f,
struct usb_ep *_ep)
{
+ struct usb_composite_dev *cdev = get_gadget_data(g);
struct usb_endpoint_descriptor *chosen_desc = NULL;
struct usb_descriptor_header **speed_desc = NULL;
@@ -177,14 +178,16 @@
switch (usb_endpoint_type(_ep->desc)) {
case USB_ENDPOINT_XFER_BULK:
case USB_ENDPOINT_XFER_INT:
- _ep->maxburst = comp_desc->bMaxBurst;
+ _ep->maxburst = comp_desc->bMaxBurst + 1;
break;
case USB_ENDPOINT_XFER_ISOC:
/* mult: bits 1:0 of bmAttributes */
_ep->mult = comp_desc->bmAttributes & 0x3;
break;
default:
- /* Do nothing for control endpoints */
+ if (comp_desc->bMaxBurst != 0)
+ ERROR(cdev, "ep0 bMaxBurst must be 0\n");
+ _ep->maxburst = 1;
break;
}
}
@@ -1582,12 +1585,6 @@
/*-------------------------------------------------------------------------*/
static struct usb_gadget_driver composite_driver = {
-#ifdef CONFIG_USB_GADGET_SUPERSPEED
- .max_speed = USB_SPEED_SUPER,
-#else
- .max_speed = USB_SPEED_HIGH,
-#endif
-
.unbind = composite_unbind,
.setup = composite_setup,
@@ -1634,8 +1631,7 @@
driver->iProduct = driver->name;
composite_driver.function = (char *) driver->name;
composite_driver.driver.name = driver->name;
- composite_driver.max_speed =
- min_t(u8, composite_driver.max_speed, driver->max_speed);
+ composite_driver.max_speed = driver->max_speed;
composite = driver;
composite_gadget_bind = bind;
diff --git a/drivers/usb/gadget/inode.c b/drivers/usb/gadget/inode.c
index e58b164..ae13a10 100644
--- a/drivers/usb/gadget/inode.c
+++ b/drivers/usb/gadget/inode.c
@@ -828,7 +828,6 @@
if (value == 0)
data->state = STATE_EP_ENABLED;
break;
-#ifdef CONFIG_USB_GADGET_DUALSPEED
case USB_SPEED_HIGH:
/* fails if caller didn't provide that descriptor... */
ep->desc = &data->hs_desc;
@@ -836,7 +835,6 @@
if (value == 0)
data->state = STATE_EP_ENABLED;
break;
-#endif
default:
DBG(data->dev, "unconnected, %s init abandoned\n",
data->name);
@@ -1324,7 +1322,6 @@
* Unrecognized ep0 requests may be handled in user space.
*/
-#ifdef CONFIG_USB_GADGET_DUALSPEED
static void make_qualifier (struct dev_data *dev)
{
struct usb_qualifier_descriptor qual;
@@ -1347,7 +1344,6 @@
memcpy (dev->rbuf, &qual, sizeof qual);
}
-#endif
static int
config_buf (struct dev_data *dev, u8 type, unsigned index)
@@ -1427,7 +1423,6 @@
dev->dev->bMaxPacketSize0 = dev->gadget->ep0->maxpacket;
req->buf = dev->dev;
break;
-#ifdef CONFIG_USB_GADGET_DUALSPEED
case USB_DT_DEVICE_QUALIFIER:
if (!dev->hs_config)
break;
@@ -1437,7 +1432,6 @@
break;
case USB_DT_OTHER_SPEED_CONFIG:
// FALLTHROUGH
-#endif
case USB_DT_CONFIG:
value = config_buf (dev,
w_value >> 8,
@@ -1763,11 +1757,6 @@
}
static struct usb_gadget_driver gadgetfs_driver = {
-#ifdef CONFIG_USB_GADGET_DUALSPEED
- .max_speed = USB_SPEED_HIGH,
-#else
- .max_speed = USB_SPEED_FULL,
-#endif
.function = (char *) driver_desc,
.unbind = gadgetfs_unbind,
.setup = gadgetfs_setup,
@@ -1900,6 +1889,10 @@
/* triggers gadgetfs_bind(); then we can enumerate. */
spin_unlock_irq (&dev->lock);
+ if (dev->hs_config)
+ gadgetfs_driver.max_speed = USB_SPEED_HIGH;
+ else
+ gadgetfs_driver.max_speed = USB_SPEED_FULL;
value = usb_gadget_probe_driver(&gadgetfs_driver, gadgetfs_bind);
if (value != 0) {
kfree (dev->buf);
diff --git a/drivers/usb/gadget/u_ether.c b/drivers/usb/gadget/u_ether.c
index b84c74d..f7b908b 100644
--- a/drivers/usb/gadget/u_ether.c
+++ b/drivers/usb/gadget/u_ether.c
@@ -91,16 +91,10 @@
#define DEFAULT_QLEN 2 /* double buffering by default */
-#ifdef CONFIG_USB_GADGET_DUALSPEED
-
static unsigned qmult = 10;
module_param(qmult, uint, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(qmult, "queue length multiplier at high/super speed");
-#else /* full speed (low speed doesn't do bulk) */
-#define qmult 1
-#endif
-
/* for dual-speed hardware, use deeper queues at high/super speed */
static inline int qlen(struct usb_gadget *gadget)
{
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/musb/Kconfig b/drivers/usb/musb/Kconfig
index f70cab3..5d35287 100644
--- a/drivers/usb/musb/Kconfig
+++ b/drivers/usb/musb/Kconfig
@@ -11,7 +11,6 @@
select TWL4030_USB if MACH_OMAP_3430SDP
select TWL6030_USB if MACH_OMAP_4430SDP || MACH_OMAP4_PANDA
select USB_OTG_UTILS
- select USB_GADGET_DUALSPEED
help
Say Y here if your system has a dual role high speed USB
controller based on the Mentor Graphics silicon IP. Then
diff --git a/drivers/usb/otg/msm72k_otg.c b/drivers/usb/otg/msm72k_otg.c
index ca1b155..36a91f1 100644
--- a/drivers/usb/otg/msm72k_otg.c
+++ b/drivers/usb/otg/msm72k_otg.c
@@ -536,6 +536,9 @@
test_bit(ID_B, &dev->inputs))
charge = USB_IDCHG_MAX;
+ if (dev->curr_power == charge)
+ return 0;
+
pr_debug("Charging with %dmA current\n", charge);
/* Call vbus_draw only if the charger is of known type and also
* ignore request to stop charging as a result of suspend interrupt
@@ -545,6 +548,8 @@
(charge || new_chg != USB_CHG_TYPE__WALLCHARGER))
pdata->chg_vbus_draw(charge);
+ dev->curr_power = charge;
+
if (new_chg == USB_CHG_TYPE__WALLCHARGER) {
wake_lock(&dev->wlock);
queue_work(dev->wq, &dev->sm_work);
diff --git a/drivers/usb/otg/msm_otg.c b/drivers/usb/otg/msm_otg.c
index 18f8729..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;
}
@@ -1120,14 +1125,12 @@
static void msm_otg_notify_host_mode(struct msm_otg *motg, bool host_mode)
{
- struct power_supply *usb = psy ? psy : &motg->usb_psy;
-
- if (!usb) {
+ if (!psy) {
pr_err("No USB power supply registered!\n");
return;
}
- if (psy) {
+ if (legacy_power_supply) {
/* legacy support */
if (host_mode)
power_supply_set_scope(psy, POWER_SUPPLY_SCOPE_SYSTEM);
@@ -1136,14 +1139,13 @@
return;
} else {
motg->host_mode = host_mode;
- power_supply_changed(usb);
+ power_supply_changed(psy);
}
}
static int msm_otg_notify_chg_type(struct msm_otg *motg)
{
static int charger_type;
- struct power_supply *usb = psy ? psy : &motg->usb_psy;
/*
* TODO
@@ -1167,40 +1169,38 @@
else
charger_type = POWER_SUPPLY_TYPE_BATTERY;
- if (!usb) {
+ if (!psy) {
pr_err("No USB power supply registered!\n");
return -EINVAL;
}
pr_debug("setting usb power supply type %d\n", charger_type);
- power_supply_set_supply_type(usb, charger_type);
+ power_supply_set_supply_type(psy, charger_type);
return 0;
}
static int msm_otg_notify_power_supply(struct msm_otg *motg, unsigned mA)
{
- struct power_supply *usb = psy ? psy : &motg->usb_psy;
-
- if (!usb) {
+ if (!psy) {
dev_dbg(motg->phy.dev, "no usb power supply registered\n");
goto psy_error;
}
if (motg->cur_power == 0 && mA > 2) {
/* Enable charging */
- if (power_supply_set_online(usb, true))
+ if (power_supply_set_online(psy, true))
goto psy_error;
- if (power_supply_set_current_limit(usb, 1000*mA))
+ if (power_supply_set_current_limit(psy, 1000*mA))
goto psy_error;
} else if (motg->cur_power > 0 && (mA == 0 || mA == 2)) {
/* Disable charging */
- if (power_supply_set_online(usb, false))
+ if (power_supply_set_online(psy, false))
goto psy_error;
/* Set max current limit */
- if (power_supply_set_current_limit(usb, 0))
+ if (power_supply_set_current_limit(psy, 0))
goto psy_error;
}
- power_supply_changed(usb);
+ power_supply_changed(psy);
return 0;
psy_error:
@@ -3386,8 +3386,7 @@
enum power_supply_property psp,
union power_supply_propval *val)
{
- struct msm_otg *motg = container_of(psy, struct msm_otg,
- usb_psy);
+ struct msm_otg *motg = container_of(psy, struct msm_otg, usb_psy);
switch (psp) {
case POWER_SUPPLY_PROP_SCOPE:
if (motg->host_mode)
@@ -3413,8 +3412,7 @@
enum power_supply_property psp,
const union power_supply_propval *val)
{
- struct msm_otg *motg = container_of(psy, struct msm_otg,
- usb_psy);
+ struct msm_otg *motg = container_of(psy, struct msm_otg, usb_psy);
switch (psp) {
/* Process PMIC notification in PRESENT prop */
@@ -4011,19 +4009,19 @@
motg->usb_psy.get_property = otg_power_get_property_usb;
motg->usb_psy.set_property = otg_power_set_property_usb;
- if (motg->pdata->otg_control == OTG_PMIC_CONTROL) {
+ if (!pm8921_charger_register_vbus_sn(NULL)) {
/* if pm8921 use legacy implementation */
- if (!pm8921_charger_register_vbus_sn(&msm_otg_set_vbus_state)) {
- dev_dbg(motg->phy.dev, "%s: legacy support\n",
- __func__);
- legacy_power_supply = true;
- } else {
- ret = msm_otg_register_power_supply(pdev, motg);
- if (ret)
- goto remove_phy;
- }
+ dev_dbg(motg->phy.dev, "%s: legacy support\n", __func__);
+ legacy_power_supply = true;
+ } else {
+ /* otherwise register our own power supply */
+ if (!msm_otg_register_power_supply(pdev, motg))
+ psy = &motg->usb_psy;
}
+ if (legacy_power_supply && pdata->otg_control == OTG_PMIC_CONTROL)
+ pm8921_charger_register_vbus_sn(&msm_otg_set_vbus_state);
+
return 0;
remove_phy:
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_mdp.c b/drivers/video/msm/mdss/mdss_mdp.c
index 0f6cfe9..c2d107a 100644
--- a/drivers/video/msm/mdss/mdss_mdp.c
+++ b/drivers/video/msm/mdss/mdss_mdp.c
@@ -320,7 +320,7 @@
vect->ab = ab_quota;
vect->ib = ib_quota;
- pr_debug("bus scale idx=%d ab=%u ib=%u\n", bus_idx,
+ pr_debug("bus scale idx=%d ab=%llu ib=%llu\n", bus_idx,
vect->ab, vect->ib);
}
current_bus_idx = bus_idx;
diff --git a/drivers/video/msm/mdss/mdss_mdp_intf_writeback.c b/drivers/video/msm/mdss/mdss_mdp_intf_writeback.c
index 8b4434e..c9acc65 100644
--- a/drivers/video/msm/mdss/mdss_mdp_intf_writeback.c
+++ b/drivers/video/msm/mdss/mdss_mdp_intf_writeback.c
@@ -150,8 +150,11 @@
(fmt->bits[C1_B_Cb] << 2) |
(fmt->bits[C0_G_Y] << 0);
- if (fmt->alpha_enable)
+ if (fmt->bits[C3_ALPHA] || fmt->alpha_enable) {
dst_format |= BIT(8); /* DSTC3_EN */
+ if (!fmt->alpha_enable)
+ dst_format |= BIT(14); /* DST_ALPHA_X */
+ }
if (fmt->fetch_planes != MDSS_MDP_PLANE_PLANAR) {
pattern = (fmt->element[3] << 24) | (fmt->element[2] << 15) |
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/mfd/wcd9xxx/wcd9xxx-slimslave.h b/include/linux/mfd/wcd9xxx/wcd9xxx-slimslave.h
index 45abd92..aaa8fd6 100644
--- a/include/linux/mfd/wcd9xxx/wcd9xxx-slimslave.h
+++ b/include/linux/mfd/wcd9xxx/wcd9xxx-slimslave.h
@@ -109,4 +109,8 @@
int wcd9xxx_get_slave_port(unsigned int ch_num);
int wcd9xxx_disconnect_port(struct wcd9xxx *wcd9xxx,
struct list_head *wcd9xxx_ch_list, u16 grph);
+int wcd9xxx_rx_vport_validation(u32 port_id,
+ struct list_head *codec_dai_list);
+int wcd9xxx_tx_vport_validation(u32 vtable, u32 port_id,
+ struct wcd9xxx_codec_dai_data *codec_dai);
#endif /* __WCD9310_SLIMSLAVE_H_ */
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/usb/ch9.h b/include/linux/usb/ch9.h
index c918b74..1608e84 100644
--- a/include/linux/usb/ch9.h
+++ b/include/linux/usb/ch9.h
@@ -88,6 +88,8 @@
#define USB_REQ_GET_INTERFACE 0x0A
#define USB_REQ_SET_INTERFACE 0x0B
#define USB_REQ_SYNCH_FRAME 0x0C
+#define USB_REQ_SET_SEL 0x30
+#define USB_REQ_SET_ISOCH_DELAY 0x31
#define USB_REQ_SET_ENCRYPTION 0x0D /* Wireless USB */
#define USB_REQ_GET_ENCRYPTION 0x0E
diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h
index 2e51781..82044f7 100644
--- a/include/linux/usb/gadget.h
+++ b/include/linux/usb/gadget.h
@@ -567,14 +567,7 @@
*/
static inline int gadget_is_dualspeed(struct usb_gadget *g)
{
-#ifdef CONFIG_USB_GADGET_DUALSPEED
- /* runtime test would check "g->max_speed" ... that might be
- * useful to work around hardware bugs, but is mostly pointless
- */
- return 1;
-#else
- return 0;
-#endif
+ return g->max_speed >= USB_SPEED_HIGH;
}
/**
@@ -584,15 +577,7 @@
*/
static inline int gadget_is_superspeed(struct usb_gadget *g)
{
-#ifdef CONFIG_USB_GADGET_SUPERSPEED
- /*
- * runtime test would check "g->max_speed" ... that might be
- * useful to work around hardware bugs, but is mostly pointless
- */
- return 1;
-#else
- return 0;
-#endif
+ return g->max_speed >= USB_SPEED_SUPER;
}
/**
diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h
index 07beb50..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)
@@ -2366,6 +2372,7 @@
#define V4L2_EVENT_MSM_VIDC_PORT_SETTINGS_CHANGED_INSUFFICIENT \
(V4L2_EVENT_MSM_VIDC_START + 3)
#define V4L2_EVENT_MSM_VIDC_CLOSE_DONE (V4L2_EVENT_MSM_VIDC_START + 4)
+#define V4L2_EVENT_MSM_VIDC_SYS_ERROR (V4L2_EVENT_MSM_VIDC_START + 5)
/* Payload for V4L2_EVENT_VSYNC */
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/wcd9304.c b/sound/soc/codecs/wcd9304.c
index 9f02134..5dcfefd 100644
--- a/sound/soc/codecs/wcd9304.c
+++ b/sound/soc/codecs/wcd9304.c
@@ -951,39 +951,6 @@
SOC_DAPM_SINGLE("Switch", SITAR_A_RX_EAR_EN, 5, 1, 0),
};
-static int slim_tx_vport_validation(u32 dai_id, u32 port_id,
- struct sitar_priv *sitar_p)
-{
- struct wcd9xxx_ch *ch;
- int ret = 0;
- int index = 0;
- u32 vtable = vport_check_table[dai_id];
- pr_debug("%s: dai_id %u vtable 0x%x port_id %u\n", __func__,
- dai_id, vtable, port_id);
- while (vtable) {
- if (vtable & 1) {
- list_for_each_entry(ch,
- &sitar_p->dai[index].wcd9xxx_ch_list,
- list) {
- pr_debug("%s: index %u ch->port%u vtable 0x%x\n",
- __func__, index, ch->port, vtable);
- if (ch->port == port_id) {
- pr_err("%s: TX%u is used by AIF%u_CAP Mixer\n",
- __func__, port_id + 1,
- (index + 1)/2);
- ret = -EINVAL;
- break;
- }
- }
- }
- if (ret)
- break;
- index++;
- vtable = vtable >> 1;
- }
- return ret;
-}
-
static int slim_tx_mixer_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -1022,8 +989,10 @@
switch (dai_id) {
case AIF1_CAP:
if (enable && !(widget->value & 1 << port_id)) {
- if (slim_tx_vport_validation(dai_id,
- port_id, sitar_p)) {
+ if (wcd9xxx_tx_vport_validation(
+ vport_check_table[dai_id],
+ port_id,
+ sitar_p->dai)) {
pr_info("%s: TX%u is used by other virtual port\n",
__func__, port_id + 1);
mutex_unlock(&codec->mutex);
@@ -1093,8 +1062,7 @@
if (widget->value > 1) {
dev_err(codec->dev, "%s: invalid AIF for I2C mode\n",
__func__);
- mutex_unlock(&codec->mutex);
- return -EINVAL;
+ goto err;
}
}
@@ -1103,17 +1071,29 @@
list_del_init(&core->rx_chs[port_id].list);
break;
case 1:
+ if (wcd9xxx_rx_vport_validation(port_id + core->num_tx_port,
+ &sitar_p->dai[AIF1_PB].wcd9xxx_ch_list))
+ goto pr_err;
list_add_tail(&core->rx_chs[port_id].list,
&sitar_p->dai[AIF1_PB].wcd9xxx_ch_list);
break;
+ break;
default:
pr_err("Unknown AIF %d\n", widget->value);
- mutex_unlock(&codec->mutex);
- return -EINVAL;
+ goto err;
}
+
+
snd_soc_dapm_mux_update_power(widget, kcontrol, 1, widget->value, e);
+
mutex_unlock(&codec->mutex);
return 0;
+pr_err:
+ pr_err("%s: RX%u is used by current requesting AIF_PB itself\n",
+ __func__, port_id + 1);
+err:
+ mutex_unlock(&codec->mutex);
+ return -EINVAL;
}
static const struct soc_enum slim_rx_mux_enum =
diff --git a/sound/soc/codecs/wcd9310.c b/sound/soc/codecs/wcd9310.c
index 564cad6..6b3287e 100644
--- a/sound/soc/codecs/wcd9310.c
+++ b/sound/soc/codecs/wcd9310.c
@@ -1722,39 +1722,6 @@
static const struct snd_kcontrol_new lineout4_ground_switch =
SOC_DAPM_SINGLE("Switch", TABLA_A_RX_LINE_4_DAC_CTL, 6, 1, 0);
-static int slim_tx_vport_validation(u32 dai_id, u32 port_id,
- struct tabla_priv *tabla_p)
-{
- struct wcd9xxx_ch *ch;
- int ret = 0;
- int index = 0;
- u32 vtable = vport_check_table[dai_id];
- pr_debug("%s: dai_id %u vtable 0x%x port_id %u\n", __func__,
- dai_id, vtable, port_id);
- while (vtable) {
- if (vtable & 1) {
- list_for_each_entry(ch,
- &tabla_p->dai[index].wcd9xxx_ch_list,
- list) {
- pr_debug("%s: index %u ch->port %u vtable 0x%x\n",
- __func__, index, ch->port, vtable);
- if (ch->port == port_id) {
- pr_err("%s: TX%u is used by AIF%u_CAP Mixer\n",
- __func__, port_id + 1,
- (index + 1)/2);
- ret = -EINVAL;
- break;
- }
- }
- }
- if (ret)
- break;
- index++;
- vtable = vtable >> 1;
- }
- return ret;
-}
-
/* virtual port entries */
static int slim_tx_mixer_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
@@ -1800,8 +1767,10 @@
/* only add to the list if value not set
*/
if (enable && !(widget->value & 1 << port_id)) {
- if (slim_tx_vport_validation(dai_id,
- port_id, tabla_p)) {
+ if (wcd9xxx_tx_vport_validation(
+ vport_check_table[dai_id],
+ port_id,
+ tabla_p->dai)) {
pr_info("%s: TX%u is used by other virtual port\n",
__func__, port_id + 1);
mutex_unlock(&codec->mutex);
@@ -1877,8 +1846,7 @@
if (widget->value > 1) {
dev_err(codec->dev, "%s: invalid AIF for I2C mode\n",
__func__);
- mutex_unlock(&codec->mutex);
- return -EINVAL;
+ goto err;
}
}
/* value need to match the Virtual port and AIF number
@@ -1888,27 +1856,42 @@
list_del_init(&core->rx_chs[port_id].list);
break;
case 1:
+ if (wcd9xxx_rx_vport_validation(port_id + core->num_tx_port,
+ &tabla_p->dai[AIF1_PB].wcd9xxx_ch_list))
+ goto pr_err;
list_add_tail(&core->rx_chs[port_id].list,
&tabla_p->dai[AIF1_PB].wcd9xxx_ch_list);
break;
case 2:
+ if (wcd9xxx_rx_vport_validation(port_id + core->num_tx_port,
+ &tabla_p->dai[AIF1_PB].wcd9xxx_ch_list))
+ goto pr_err;
list_add_tail(&core->rx_chs[port_id].list,
&tabla_p->dai[AIF2_PB].wcd9xxx_ch_list);
break;
case 3:
+ if (wcd9xxx_rx_vport_validation(port_id + core->num_tx_port,
+ &tabla_p->dai[AIF1_PB].wcd9xxx_ch_list))
+ goto pr_err;
list_add_tail(&core->rx_chs[port_id].list,
&tabla_p->dai[AIF3_PB].wcd9xxx_ch_list);
break;
default:
pr_err("Unknown AIF %d\n", widget->value);
- mutex_unlock(&codec->mutex);
- return -EINVAL;
+ goto err;
}
snd_soc_dapm_mux_update_power(widget, kcontrol, 1, widget->value, e);
mutex_unlock(&codec->mutex);
return 0;
+
+pr_err:
+ pr_err("%s: RX%u is used by current requesting AIF_PB itself\n",
+ __func__, port_id + 1);
+err:
+ mutex_unlock(&codec->mutex);
+ return -EINVAL;
}
static const struct soc_enum slim_rx_mux_enum =
diff --git a/sound/soc/codecs/wcd9320.c b/sound/soc/codecs/wcd9320.c
index 886e4d3..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;
}
@@ -1480,39 +1495,6 @@
static const struct snd_kcontrol_new lineout4_ground_switch =
SOC_DAPM_SINGLE("Switch", TAIKO_A_RX_LINE_4_DAC_CTL, 6, 1, 0);
-static int slim_tx_vport_validation(u32 dai_id, u32 port_id,
- struct taiko_priv *taiko_p)
-{
- struct wcd9xxx_ch *ch;
- int ret = 0;
- int index = 0;
- u32 vtable = vport_check_table[dai_id];
- pr_debug("%s: dai_id %u vtable 0x%x port_id %u\n", __func__,
- dai_id, vtable, port_id);
- while (vtable) {
- if (vtable & 1) {
- list_for_each_entry(ch,
- &taiko_p->dai[index].wcd9xxx_ch_list,
- list) {
- pr_debug("%s: index %u ch->port %u vtable 0x%x\n",
- __func__, index, ch->port, vtable);
- if (ch->port == port_id) {
- pr_err("%s: TX%u is used by AIF%u_CAP Mixer\n",
- __func__, port_id + 1,
- (index + 1)/2);
- ret = -EINVAL;
- break;
- }
- }
- }
- if (ret)
- break;
- index++;
- vtable = vtable >> 1;
- }
- return ret;
-}
-
/* virtual port entries */
static int slim_tx_mixer_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
@@ -1560,8 +1542,10 @@
/* only add to the list if value not set
*/
if (enable && !(widget->value & 1 << port_id)) {
- if (slim_tx_vport_validation(dai_id,
- port_id, taiko_p)) {
+ if (wcd9xxx_tx_vport_validation(
+ vport_check_table[dai_id],
+ port_id,
+ taiko_p->dai)) {
pr_info("%s: TX%u is used by other virtual port\n",
__func__, port_id + 1);
mutex_unlock(&codec->mutex);
@@ -1637,8 +1621,7 @@
if (widget->value > 1) {
dev_err(codec->dev, "%s: invalid AIF for I2C mode\n",
__func__);
- mutex_unlock(&codec->mutex);
- return -EINVAL;
+ goto err;
}
}
/* value need to match the Virtual port and AIF number
@@ -1648,27 +1631,41 @@
list_del_init(&core->rx_chs[port_id].list);
break;
case 1:
+ if (wcd9xxx_rx_vport_validation(port_id + core->num_tx_port,
+ &taiko_p->dai[AIF1_PB].wcd9xxx_ch_list))
+ goto pr_err;
list_add_tail(&core->rx_chs[port_id].list,
&taiko_p->dai[AIF1_PB].wcd9xxx_ch_list);
break;
case 2:
+ if (wcd9xxx_rx_vport_validation(port_id + core->num_tx_port,
+ &taiko_p->dai[AIF1_PB].wcd9xxx_ch_list))
+ goto pr_err;
list_add_tail(&core->rx_chs[port_id].list,
&taiko_p->dai[AIF2_PB].wcd9xxx_ch_list);
break;
case 3:
+ if (wcd9xxx_rx_vport_validation(port_id + core->num_tx_port,
+ &taiko_p->dai[AIF1_PB].wcd9xxx_ch_list))
+ goto pr_err;
list_add_tail(&core->rx_chs[port_id].list,
&taiko_p->dai[AIF3_PB].wcd9xxx_ch_list);
break;
default:
pr_err("Unknown AIF %d\n", widget->value);
- mutex_unlock(&codec->mutex);
- return -EINVAL;
+ goto err;
}
snd_soc_dapm_mux_update_power(widget, kcontrol, 1, widget->value, e);
mutex_unlock(&codec->mutex);
return 0;
+pr_err:
+ pr_err("%s: RX%u is used by current requesting AIF_PB itself\n",
+ __func__, port_id + 1);
+err:
+ mutex_unlock(&codec->mutex);
+ return -EINVAL;
}
static const struct soc_enum slim_rx_mux_enum =
@@ -1950,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) {
@@ -2017,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);
@@ -2617,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"},
@@ -3173,8 +3180,8 @@
}
list_for_each_entry(ch, &taiko_p->dai[dai->id].wcd9xxx_ch_list,
list) {
- pr_debug("%s: tx_slot[%d] %d, ch->ch_num %d\n",
- __func__, i, tx_slot[i], ch->ch_num);
+ pr_debug("%s: rx_slot[%d] %d, ch->ch_num %d\n",
+ __func__, i, rx_slot[i], ch->ch_num);
rx_slot[i++] = ch->ch_num;
}
pr_debug("%s: rx_num %d\n", __func__, i);
@@ -3904,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),
@@ -4387,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);
@@ -4441,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/mpq8064.c b/sound/soc/msm/mpq8064.c
index 7656f9f..69256363 100644
--- a/sound/soc/msm/mpq8064.c
+++ b/sound/soc/msm/mpq8064.c
@@ -144,8 +144,6 @@
static struct clk *codec_clk;
static int clk_users;
-static int msm_headset_gpios_configured;
-
static struct snd_soc_jack hs_jack;
static struct snd_soc_jack button_jack;
@@ -1622,57 +1620,6 @@
static struct platform_device *msm_snd_device;
-static int msm_configure_headset_mic_gpios(void)
-{
- int ret;
- struct pm_gpio param = {
- .direction = PM_GPIO_DIR_OUT,
- .output_buffer = PM_GPIO_OUT_BUF_CMOS,
- .output_value = 1,
- .pull = PM_GPIO_PULL_NO,
- .vin_sel = PM_GPIO_VIN_S4,
- .out_strength = PM_GPIO_STRENGTH_MED,
- .function = PM_GPIO_FUNC_NORMAL,
- };
-
- ret = gpio_request(PM8921_GPIO_PM_TO_SYS(23), "AV_SWITCH");
- if (ret) {
- pr_err("%s: Failed to request gpio %d\n", __func__,
- PM8921_GPIO_PM_TO_SYS(23));
- return ret;
- }
-
- ret = pm8xxx_gpio_config(PM8921_GPIO_PM_TO_SYS(23), ¶m);
- if (ret)
- pr_err("%s: Failed to configure gpio %d\n", __func__,
- PM8921_GPIO_PM_TO_SYS(23));
- else
- gpio_direction_output(PM8921_GPIO_PM_TO_SYS(23), 0);
-
- ret = gpio_request(PM8921_GPIO_PM_TO_SYS(35), "US_EURO_SWITCH");
- if (ret) {
- pr_err("%s: Failed to request gpio %d\n", __func__,
- PM8921_GPIO_PM_TO_SYS(35));
- gpio_free(PM8921_GPIO_PM_TO_SYS(23));
- return ret;
- }
- ret = pm8xxx_gpio_config(PM8921_GPIO_PM_TO_SYS(35), ¶m);
- if (ret)
- pr_err("%s: Failed to configure gpio %d\n", __func__,
- PM8921_GPIO_PM_TO_SYS(35));
- else
- gpio_direction_output(PM8921_GPIO_PM_TO_SYS(35), 0);
-
- return 0;
-}
-static void msm_free_headset_mic_gpios(void)
-{
- if (msm_headset_gpios_configured) {
- gpio_free(PM8921_GPIO_PM_TO_SYS(23));
- gpio_free(PM8921_GPIO_PM_TO_SYS(35));
- }
-}
-
static int __init msm_audio_init(void)
{
int ret;
@@ -1706,12 +1653,6 @@
return ret;
}
- if (msm_configure_headset_mic_gpios()) {
- pr_err("%s Fail to configure headset mic gpios\n", __func__);
- msm_headset_gpios_configured = 0;
- } else
- msm_headset_gpios_configured = 1;
-
return ret;
}
@@ -1723,7 +1664,6 @@
pr_err("%s: Not the right machine type\n", __func__);
return ;
}
- msm_free_headset_mic_gpios();
platform_device_unregister(msm_snd_device);
kfree(mbhc_cfg.calibration);
}
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,
diff --git a/sound/soc/msm/qdsp6v2/q6voice.c b/sound/soc/msm/qdsp6v2/q6voice.c
index 1f6dbf1..4e41c80 100644
--- a/sound/soc/msm/qdsp6v2/q6voice.c
+++ b/sound/soc/msm/qdsp6v2/q6voice.c
@@ -42,6 +42,12 @@
/* CVS CAL Size: 49152 = 48 * 1024 */
#define CVS_CAL_SIZE 49152
+enum {
+ VOC_TOKEN_NONE,
+ VOIP_MEM_MAP_TOKEN,
+ VOC_CAL_MEM_MAP_TOKEN,
+};
+
static struct common_data common;
static int voice_send_enable_vocproc_cmd(struct voice_data *v);
@@ -50,7 +56,6 @@
static int voice_send_set_device_cmd(struct voice_data *v);
static int voice_send_disable_vocproc_cmd(struct voice_data *v);
static int voice_send_vol_index_cmd(struct voice_data *v);
-static int voice_send_mvm_map_memory_physical_cmd(struct voice_data *v);
static int voice_send_mvm_unmap_memory_physical_cmd(struct voice_data *v,
unsigned int bufcnt);
static int voice_send_mvm_cal_network_cmd(struct voice_data *v);
@@ -59,6 +64,16 @@
static int voice_send_cvs_packet_exchange_config_cmd(struct voice_data *v);
static int voice_set_packet_exchange_mode_and_config(uint16_t session_id,
uint32_t mode);
+
+static int voice_send_cvs_register_cal_cmd(struct voice_data *v);
+static int voice_send_cvs_deregister_cal_cmd(struct voice_data *v);
+static int voice_send_cvp_register_dev_cfg_cmd(struct voice_data *v);
+static int voice_send_cvp_deregister_dev_cfg_cmd(struct voice_data *v);
+static int voice_send_cvp_register_cal_cmd(struct voice_data *v);
+static int voice_send_cvp_deregister_cal_cmd(struct voice_data *v);
+static int voice_send_cvp_register_vol_cal_cmd(struct voice_data *v);
+static int voice_send_cvp_deregister_vol_cal_cmd(struct voice_data *v);
+
static int voice_cvs_stop_playback(struct voice_data *v);
static int voice_cvs_start_playback(struct voice_data *v);
static int voice_cvs_start_record(struct voice_data *v, uint32_t rec_mode);
@@ -1263,32 +1278,557 @@
return -EINVAL;
}
-static int voice_send_mvm_map_memory_physical_cmd(struct voice_data *v)
+static int voice_send_cvs_register_cal_cmd(struct voice_data *v)
+{
+ struct cvs_register_cal_data_cmd cvs_reg_cal_cmd;
+ struct acdb_cal_block cal_block;
+ int ret = 0;
+ memset(&cvs_reg_cal_cmd, 0, sizeof(cvs_reg_cal_cmd));
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+
+ goto fail;
+ }
+
+ if (!common.apr_q6_cvs) {
+ pr_err("%s: apr_cvs is NULL\n", __func__);
+
+ goto fail;
+ }
+
+ if (!common.cal_mem_handle) {
+ pr_err("%s: Cal mem handle is NULL\n", __func__);
+
+ goto fail;
+ }
+
+ get_all_vocstrm_cal(&cal_block);
+ if (cal_block.cal_size == 0) {
+ pr_err("%s: CVS cal size is 0\n", __func__);
+
+ goto fail;
+ }
+
+ cvs_reg_cal_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ cvs_reg_cal_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(cvs_reg_cal_cmd) - APR_HDR_SIZE);
+ cvs_reg_cal_cmd.hdr.src_port = v->session_id;
+ cvs_reg_cal_cmd.hdr.dest_port = voice_get_cvs_handle(v);
+ cvs_reg_cal_cmd.hdr.token = 0;
+ cvs_reg_cal_cmd.hdr.opcode =
+ VSS_ISTREAM_CMD_REGISTER_CALIBRATION_DATA_V2;
+
+ cvs_reg_cal_cmd.cvs_cal_data.cal_mem_handle = common.cal_mem_handle;
+ cvs_reg_cal_cmd.cvs_cal_data.cal_mem_address = cal_block.cal_paddr;
+ cvs_reg_cal_cmd.cvs_cal_data.cal_mem_size = cal_block.cal_size;
+
+ /* Get the column info corresponding to CVS cal from ACDB. */
+ get_voice_col_data(VOCSTRM_CAL, &cal_block);
+ memcpy(&cvs_reg_cal_cmd.cvs_cal_data.column_info[0],
+ (void *) cal_block.cal_kvaddr,
+ cal_block.cal_size);
+
+ v->cvs_state = CMD_STATUS_FAIL;
+ ret = apr_send_pkt(common.apr_q6_cvs, (uint32_t *) &cvs_reg_cal_cmd);
+ if (ret < 0) {
+ pr_err("%s: Error %d registering CVS cal\n", __func__, ret);
+
+ goto fail;
+ }
+ ret = wait_event_timeout(v->cvs_wait,
+ (v->cvs_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: Command timeout\n", __func__);
+
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ return -EINVAL;
+}
+
+static int voice_send_cvs_deregister_cal_cmd(struct voice_data *v)
+{
+ struct cvs_deregister_cal_data_cmd cvs_dereg_cal_cmd;
+ struct acdb_cal_block cal_block;
+ int ret = 0;
+ memset(&cvs_dereg_cal_cmd, 0, sizeof(cvs_dereg_cal_cmd));
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+
+ goto fail;
+ }
+
+ if (!common.apr_q6_cvs) {
+ pr_err("%s: apr_cvs is NULL\n", __func__);
+
+ goto fail;
+ }
+
+ get_all_vocstrm_cal(&cal_block);
+ if (cal_block.cal_size == 0)
+ return 0;
+
+ cvs_dereg_cal_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ cvs_dereg_cal_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(cvs_dereg_cal_cmd) - APR_HDR_SIZE);
+ cvs_dereg_cal_cmd.hdr.src_port = v->session_id;
+ cvs_dereg_cal_cmd.hdr.dest_port = voice_get_cvs_handle(v);
+ cvs_dereg_cal_cmd.hdr.token = 0;
+ cvs_dereg_cal_cmd.hdr.opcode =
+ VSS_ISTREAM_CMD_DEREGISTER_CALIBRATION_DATA;
+
+ v->cvs_state = CMD_STATUS_FAIL;
+ ret = apr_send_pkt(common.apr_q6_cvs, (uint32_t *) &cvs_dereg_cal_cmd);
+ if (ret < 0) {
+ pr_err("%s: Error %d de-registering CVS cal\n", __func__, ret);
+
+ goto fail;
+ }
+ ret = wait_event_timeout(v->cvs_wait,
+ (v->cvs_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: Command timeout\n", __func__);
+
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ return -EINVAL;
+
+}
+
+static int voice_send_cvp_register_dev_cfg_cmd(struct voice_data *v)
+{
+ struct cvp_register_dev_cfg_cmd cvp_reg_dev_cfg_cmd;
+ struct acdb_cal_block cal_block;
+ int ret = 0;
+ memset(&cvp_reg_dev_cfg_cmd, 0, sizeof(cvp_reg_dev_cfg_cmd));
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+
+ goto fail;
+ }
+
+ if (!common.apr_q6_cvp) {
+ pr_err("%s: apr_cvp is NULL\n", __func__);
+
+ goto fail;
+ }
+
+ if (!common.cal_mem_handle) {
+ pr_err("%s: Cal mem handle is NULL\n", __func__);
+
+ goto fail;
+ }
+
+ get_vocproc_dev_cfg_cal(&cal_block);
+ if (cal_block.cal_size == 0) {
+ pr_err("%s: CVP cal size is 0\n", __func__);
+
+ goto fail;
+ }
+
+ cvp_reg_dev_cfg_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ cvp_reg_dev_cfg_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(cvp_reg_dev_cfg_cmd) - APR_HDR_SIZE);
+ cvp_reg_dev_cfg_cmd.hdr.src_port = v->session_id;
+ cvp_reg_dev_cfg_cmd.hdr.dest_port = voice_get_cvp_handle(v);
+ cvp_reg_dev_cfg_cmd.hdr.token = 0;
+ cvp_reg_dev_cfg_cmd.hdr.opcode =
+ VSS_IVOCPROC_CMD_REGISTER_DEVICE_CONFIG;
+
+ cvp_reg_dev_cfg_cmd.cvp_dev_cfg_data.mem_handle = common.cal_mem_handle;
+ cvp_reg_dev_cfg_cmd.cvp_dev_cfg_data.mem_address = cal_block.cal_paddr;
+ cvp_reg_dev_cfg_cmd.cvp_dev_cfg_data.mem_size = cal_block.cal_size;
+
+ v->cvp_state = CMD_STATUS_FAIL;
+ ret = apr_send_pkt(common.apr_q6_cvp,
+ (uint32_t *) &cvp_reg_dev_cfg_cmd);
+ if (ret < 0) {
+ pr_err("%s: Error %d registering CVP dev cfg cal\n",
+ __func__, ret);
+
+ goto fail;
+ }
+ ret = wait_event_timeout(v->cvp_wait,
+ (v->cvp_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: Command timeout\n", __func__);
+
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ return -EINVAL;
+}
+
+static int voice_send_cvp_deregister_dev_cfg_cmd(struct voice_data *v)
+{
+ struct cvp_deregister_dev_cfg_cmd cvp_dereg_dev_cfg_cmd;
+ struct acdb_cal_block cal_block;
+ int ret = 0;
+ memset(&cvp_dereg_dev_cfg_cmd, 0, sizeof(cvp_dereg_dev_cfg_cmd));
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+
+ goto fail;
+ }
+
+ if (!common.apr_q6_cvp) {
+ pr_err("%s: apr_cvp is NULL.\n", __func__);
+
+ goto fail;
+ }
+
+ get_vocproc_dev_cfg_cal(&cal_block);
+ if (cal_block.cal_size == 0)
+ return 0;
+
+ cvp_dereg_dev_cfg_cmd.hdr.hdr_field =
+ APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ cvp_dereg_dev_cfg_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(cvp_dereg_dev_cfg_cmd) - APR_HDR_SIZE);
+ cvp_dereg_dev_cfg_cmd.hdr.src_port = v->session_id;
+ cvp_dereg_dev_cfg_cmd.hdr.dest_port = voice_get_cvp_handle(v);
+ cvp_dereg_dev_cfg_cmd.hdr.token = 0;
+ cvp_dereg_dev_cfg_cmd.hdr.opcode =
+ VSS_IVOCPROC_CMD_DEREGISTER_DEVICE_CONFIG;
+
+ v->cvp_state = CMD_STATUS_FAIL;
+ ret = apr_send_pkt(common.apr_q6_cvp,
+ (uint32_t *) &cvp_dereg_dev_cfg_cmd);
+ if (ret < 0) {
+ pr_err("%s: Error %d de-registering CVP dev cfg cal\n",
+ __func__, ret);
+
+ goto fail;
+ }
+ ret = wait_event_timeout(v->cvp_wait,
+ (v->cvp_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: Command timeout\n", __func__);
+
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ return -EINVAL;
+}
+
+static int voice_send_cvp_register_cal_cmd(struct voice_data *v)
+{
+ struct cvp_register_cal_data_cmd cvp_reg_cal_cmd;
+ struct acdb_cal_block cal_block;
+ int ret = 0;
+ memset(&cvp_reg_cal_cmd, 0, sizeof(cvp_reg_cal_cmd));
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+
+ goto fail;
+ }
+
+ if (!common.apr_q6_cvp) {
+ pr_err("%s: apr_cvp is NULL\n", __func__);
+
+ goto fail;
+ }
+
+ if (!common.cal_mem_handle) {
+ pr_err("%s: Cal mem handle is NULL\n", __func__);
+
+ goto fail;
+ }
+
+ get_all_vocproc_cal(&cal_block);
+ if (cal_block.cal_size == 0) {
+ pr_err("%s: CVP cal size is 0\n", __func__);
+
+ goto fail;
+ }
+
+ cvp_reg_cal_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ cvp_reg_cal_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(cvp_reg_cal_cmd) - APR_HDR_SIZE);
+ cvp_reg_cal_cmd.hdr.src_port = v->session_id;
+ cvp_reg_cal_cmd.hdr.dest_port = voice_get_cvp_handle(v);
+ cvp_reg_cal_cmd.hdr.token = 0;
+ cvp_reg_cal_cmd.hdr.opcode =
+ VSS_IVOCPROC_CMD_REGISTER_CALIBRATION_DATA_V2;
+
+ cvp_reg_cal_cmd.cvp_cal_data.cal_mem_handle = common.cal_mem_handle;
+ cvp_reg_cal_cmd.cvp_cal_data.cal_mem_address = cal_block.cal_paddr;
+ cvp_reg_cal_cmd.cvp_cal_data.cal_mem_size = cal_block.cal_size;
+
+ /* Get the column info corresponding to CVP cal from ACDB. */
+ get_voice_col_data(VOCPROC_CAL, &cal_block);
+ memcpy(&cvp_reg_cal_cmd.cvp_cal_data.column_info[0],
+ (void *) cal_block.cal_kvaddr,
+ cal_block.cal_size);
+
+ v->cvp_state = CMD_STATUS_FAIL;
+ ret = apr_send_pkt(common.apr_q6_cvp, (uint32_t *) &cvp_reg_cal_cmd);
+ if (ret < 0) {
+ pr_err("%s: Error %d registering CVP cal\n", __func__, ret);
+
+ goto fail;
+ }
+ ret = wait_event_timeout(v->cvp_wait,
+ (v->cvp_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: Command timeout\n", __func__);
+
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ return -EINVAL;
+}
+
+static int voice_send_cvp_deregister_cal_cmd(struct voice_data *v)
+{
+ struct cvp_deregister_cal_data_cmd cvp_dereg_cal_cmd;
+ struct acdb_cal_block cal_block;
+ int ret = 0;
+ memset(&cvp_dereg_cal_cmd, 0, sizeof(cvp_dereg_cal_cmd));
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+
+ goto fail;
+ }
+
+ if (!common.apr_q6_cvp) {
+ pr_err("%s: apr_cvp is NULL.\n", __func__);
+
+ goto fail;
+ }
+
+ get_all_vocproc_cal(&cal_block);
+ if (cal_block.cal_size == 0)
+ return 0;
+
+ cvp_dereg_cal_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ cvp_dereg_cal_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(cvp_dereg_cal_cmd) - APR_HDR_SIZE);
+ cvp_dereg_cal_cmd.hdr.src_port = v->session_id;
+ cvp_dereg_cal_cmd.hdr.dest_port = voice_get_cvp_handle(v);
+ cvp_dereg_cal_cmd.hdr.token = 0;
+ cvp_dereg_cal_cmd.hdr.opcode =
+ VSS_IVOCPROC_CMD_DEREGISTER_CALIBRATION_DATA;
+
+ v->cvp_state = CMD_STATUS_FAIL;
+ ret = apr_send_pkt(common.apr_q6_cvp, (uint32_t *) &cvp_dereg_cal_cmd);
+ if (ret < 0) {
+ pr_err("%s: Error %d de-registering CVP cal\n", __func__, ret);
+
+ goto fail;
+ }
+ ret = wait_event_timeout(v->cvp_wait,
+ (v->cvp_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: Command timeout\n", __func__);
+
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ return -EINVAL;
+}
+
+static int voice_send_cvp_register_vol_cal_cmd(struct voice_data *v)
+{
+ struct cvp_register_vol_cal_data_cmd cvp_reg_vol_cal_cmd;
+ struct acdb_cal_block cal_block;
+ int ret = 0;
+ memset(&cvp_reg_vol_cal_cmd, 0, sizeof(cvp_reg_vol_cal_cmd));
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+
+ goto fail;
+ }
+
+ if (!common.apr_q6_cvp) {
+ pr_err("%s: apr_cvp is NULL.\n", __func__);
+
+ goto fail;
+ }
+
+ if (!common.cal_mem_handle) {
+ pr_err("%s: Cal mem handle is NULL\n", __func__);
+
+ goto fail;
+ }
+
+ get_all_vocvol_cal(&cal_block);
+ if (cal_block.cal_size == 0) {
+ pr_err("%s: CVP vol cal size is 0\n", __func__);
+
+ goto fail;
+ }
+
+ cvp_reg_vol_cal_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ cvp_reg_vol_cal_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(cvp_reg_vol_cal_cmd) - APR_HDR_SIZE);
+ cvp_reg_vol_cal_cmd.hdr.src_port = v->session_id;
+ cvp_reg_vol_cal_cmd.hdr.dest_port = voice_get_cvp_handle(v);
+ cvp_reg_vol_cal_cmd.hdr.token = 0;
+ cvp_reg_vol_cal_cmd.hdr.opcode =
+ VSS_IVOCPROC_CMD_REGISTER_VOL_CALIBRATION_DATA;
+
+ cvp_reg_vol_cal_cmd.cvp_vol_cal_data.cal_mem_handle =
+ common.cal_mem_handle;
+ cvp_reg_vol_cal_cmd.cvp_vol_cal_data.cal_mem_address =
+ cal_block.cal_paddr;
+ cvp_reg_vol_cal_cmd.cvp_vol_cal_data.cal_mem_size = cal_block.cal_size;
+
+ /* Get the column info corresponding to CVP volume cal from ACDB. */
+ get_voice_col_data(VOCVOL_CAL, &cal_block);
+ memcpy(&cvp_reg_vol_cal_cmd.cvp_vol_cal_data.column_info[0],
+ (void *) cal_block.cal_kvaddr,
+ cal_block.cal_size);
+
+ v->cvp_state = CMD_STATUS_FAIL;
+ ret = apr_send_pkt(common.apr_q6_cvp,
+ (uint32_t *) &cvp_reg_vol_cal_cmd);
+ if (ret < 0) {
+ pr_err("%s: Error %d registering CVP vol cal\n", __func__, ret);
+
+ goto fail;
+ }
+ ret = wait_event_timeout(v->cvp_wait,
+ (v->cvp_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: Command timeout\n", __func__);
+
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ return -EINVAL;
+}
+
+static int voice_send_cvp_deregister_vol_cal_cmd(struct voice_data *v)
+{
+ struct cvp_deregister_vol_cal_data_cmd cvp_dereg_vol_cal_cmd;
+ struct acdb_cal_block cal_block;
+ int ret = 0;
+ memset(&cvp_dereg_vol_cal_cmd, 0, sizeof(cvp_dereg_vol_cal_cmd));
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+
+ goto fail;
+ }
+
+ if (!common.apr_q6_cvp) {
+ pr_err("%s: apr_cvp is NULL\n", __func__);
+
+ goto fail;
+ }
+
+ get_all_vocvol_cal(&cal_block);
+ if (cal_block.cal_size == 0)
+ return 0;
+
+ cvp_dereg_vol_cal_cmd.hdr.hdr_field =
+ APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ cvp_dereg_vol_cal_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(cvp_dereg_vol_cal_cmd) - APR_HDR_SIZE);
+ cvp_dereg_vol_cal_cmd.hdr.src_port = v->session_id;
+ cvp_dereg_vol_cal_cmd.hdr.dest_port = voice_get_cvp_handle(v);
+ cvp_dereg_vol_cal_cmd.hdr.token = 0;
+ cvp_dereg_vol_cal_cmd.hdr.opcode =
+ VSS_IVOCPROC_CMD_DEREGISTER_VOL_CALIBRATION_DATA;
+
+ v->cvp_state = CMD_STATUS_FAIL;
+ ret = apr_send_pkt(common.apr_q6_cvp,
+ (uint32_t *) &cvp_dereg_vol_cal_cmd);
+ if (ret < 0) {
+ pr_err("%s: Error %d de-registering CVP vol cal\n",
+ __func__, ret);
+
+ goto fail;
+ }
+ ret = wait_event_timeout(v->cvp_wait,
+ (v->cvp_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: Command timeout\n", __func__);
+
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ return -EINVAL;
+}
+
+static int voice_map_memory_physical_cmd(struct voice_data *v,
+ struct mem_map_table *table_info,
+ dma_addr_t phys,
+ uint32_t size,
+ uint32_t token)
{
struct vss_imemory_cmd_map_physical_t mvm_map_phys_cmd;
uint32_t *memtable;
int ret = 0;
- void *apr_mvm;
- u16 mvm_handle;
if (v == NULL) {
pr_err("%s: v is NULL\n", __func__);
- return -EINVAL;
+
+ goto fail;
}
- apr_mvm = common.apr_q6_mvm;
-
- if (!apr_mvm) {
+ if (!common.apr_q6_mvm) {
pr_err("%s: apr_mvm is NULL.\n", __func__);
- return -EINVAL;
+
+ goto fail;
}
- if (!v->shmem_info.memtbl.data) {
- pr_err("%s: shmem_info.memtbl.data is NULL.\n", __func__);
- return -EINVAL;
+ if (!table_info->data) {
+ pr_err("%s: memory table is NULL.\n", __func__);
+
+ goto fail;
}
- memtable = (uint32_t *)v->shmem_info.memtbl.data;
+ memtable = (uint32_t *) table_info->data;
/*
* Store next table descriptor's address(64 bit) as NULL as there
@@ -1301,25 +1841,22 @@
memtable[2] = 0;
/* Store shared mem add */
- memtable[3] = v->shmem_info.sh_buf.buf[0].phys;
+ memtable[3] = phys;
memtable[4] = 0;
/* Store shared memory size */
- memtable[5] = v->shmem_info.sh_buf.buf[0].size * NUM_OF_BUFFERS;
-
- mvm_handle = voice_get_mvm_handle(v);
+ memtable[5] = size;
mvm_map_phys_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
mvm_map_phys_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
sizeof(mvm_map_phys_cmd) - APR_HDR_SIZE);
mvm_map_phys_cmd.hdr.src_port = v->session_id;
- mvm_map_phys_cmd.hdr.dest_port = mvm_handle;
- mvm_map_phys_cmd.hdr.token = 0;
+ mvm_map_phys_cmd.hdr.dest_port = voice_get_mvm_handle(v);
+ mvm_map_phys_cmd.hdr.token = token;
mvm_map_phys_cmd.hdr.opcode = VSS_IMEMORY_CMD_MAP_PHYSICAL;
- mvm_map_phys_cmd.table_descriptor.mem_address =
- v->shmem_info.memtbl.phys;
+ mvm_map_phys_cmd.table_descriptor.mem_address = table_info->phys;
mvm_map_phys_cmd.table_descriptor.mem_size =
sizeof(struct vss_imemory_block_t) +
sizeof(struct vss_imemory_table_descriptor_t);
@@ -1330,36 +1867,71 @@
mvm_map_phys_cmd.min_data_width = 8;
mvm_map_phys_cmd.max_data_width = 64;
- pr_debug("%s: ntd->add: %lld, ntd->size: %d, table->add: 0x%x\n",
- __func__,
- *((uint64_t *)v->shmem_info.memtbl.data),
- *(((uint32_t *)(v->shmem_info.memtbl.data)) + 2),
- *(((uint32_t *)(v->shmem_info.memtbl.data)) + 3));
- pr_debug("%s: table->size: %d, pkt_size: %d, mvm_handle: 0x%x\n",
- __func__,
- *(((uint32_t *)(v->shmem_info.memtbl.data)) + 5),
- mvm_map_phys_cmd.hdr.pkt_size, mvm_handle);
+ pr_debug("%s: next table desc: add: %lld, size: %d\n",
+ __func__, *((uint64_t *) memtable),
+ *(((uint32_t *) memtable) + 2));
+ pr_debug("%s: phy add of of mem being mapped 0x%x, size: %d\n",
+ __func__, *(((uint32_t *) memtable) + 3),
+ *(((uint32_t *) memtable) + 5));
v->mvm_state = CMD_STATUS_FAIL;
- ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_map_phys_cmd);
+ ret = apr_send_pkt(common.apr_q6_mvm, (uint32_t *) &mvm_map_phys_cmd);
if (ret < 0) {
- pr_err("Fail: sending mvm map phy cmd %d\n", ret);
+ pr_err("%s: Error %d sending mvm map phy cmd\n", __func__, ret);
+
goto fail;
}
ret = wait_event_timeout(v->mvm_wait,
- (v->mvm_state == CMD_STATUS_SUCCESS),
- msecs_to_jiffies(TIMEOUT_MS));
+ (v->mvm_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
if (!ret) {
- pr_err("%s: wait_event timeout %d\n", __func__, ret);
+ pr_err("%s: Command timeout\n", __func__);
+
goto fail;
}
return 0;
+
fail:
return -EINVAL;
}
+static int voice_mem_map_cal_block(struct voice_data *v)
+{
+ int ret = 0;
+ struct acdb_cal_block cal_block;
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+
+ return -EINVAL;
+ }
+
+ if (common.cal_mem_handle != 0) {
+ pr_debug("%s: Cal block already mem mapped\n", __func__);
+
+ return ret;
+ }
+
+ /* Get the physical address of calibration memory block from ACDB. */
+ get_voice_cal_allocation(&cal_block);
+
+ if (!cal_block.cal_paddr) {
+ pr_err("%s: Cal block not allocated\n", __func__);
+
+ return -EINVAL;
+ }
+
+ ret = voice_map_memory_physical_cmd(v,
+ &common.cal_mem_map_table,
+ cal_block.cal_paddr,
+ cal_block.cal_size,
+ VOC_CAL_MEM_MAP_TOKEN);
+
+ return ret;
+}
+
static int voice_setup_vocproc(struct voice_data *v)
{
struct cvp_create_full_ctl_session_cmd cvp_session_cmd;
@@ -1437,6 +2009,12 @@
goto fail;
}
+ voice_send_cvs_register_cal_cmd(v);
+
+ voice_send_cvp_register_dev_cfg_cmd(v);
+ voice_send_cvp_register_cal_cmd(v);
+ voice_send_cvp_register_vol_cal_cmd(v);
+
/* enable vocproc */
ret = voice_send_enable_vocproc_cmd(v);
if (ret < 0)
@@ -1789,6 +2367,11 @@
goto fail;
}
+ voice_send_cvp_deregister_vol_cal_cmd(v);
+ voice_send_cvp_deregister_cal_cmd(v);
+ voice_send_cvp_deregister_dev_cfg_cmd(v);
+
+ voice_send_cvs_deregister_cal_cmd(v);
/* destrop cvp session */
cvp_destroy_session_cmd.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
@@ -1997,20 +2580,18 @@
{
struct cvs_set_mute_cmd cvs_mute_cmd;
int ret = 0;
- void *apr_cvs;
- u16 cvs_handle;
if (v == NULL) {
pr_err("%s: v is NULL\n", __func__);
- return -EINVAL;
- }
- apr_cvs = common.apr_q6_cvs;
- if (!apr_cvs) {
- pr_err("%s: apr_cvs is NULL.\n", __func__);
- return -EINVAL;
+ goto fail;
}
- cvs_handle = voice_get_cvs_handle(v);
+
+ if (!common.apr_q6_cvs) {
+ pr_err("%s: apr_cvs is NULL.\n", __func__);
+
+ goto fail;
+ }
/* send mute/unmute to cvs */
cvs_mute_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
@@ -2019,25 +2600,31 @@
cvs_mute_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
sizeof(cvs_mute_cmd) - APR_HDR_SIZE);
cvs_mute_cmd.hdr.src_port = v->session_id;
- cvs_mute_cmd.hdr.dest_port = cvs_handle;
+ cvs_mute_cmd.hdr.dest_port = voice_get_cvs_handle(v);
cvs_mute_cmd.hdr.token = 0;
- cvs_mute_cmd.hdr.opcode = VSS_ISTREAM_CMD_SET_MUTE;
- cvs_mute_cmd.cvs_set_mute.direction = 0; /*tx*/
+ cvs_mute_cmd.hdr.opcode = VSS_IVOLUME_CMD_MUTE_V2;
+ cvs_mute_cmd.cvs_set_mute.direction = VSS_IVOLUME_DIRECTION_TX;
cvs_mute_cmd.cvs_set_mute.mute_flag = v->dev_tx.mute;
+ cvs_mute_cmd.cvs_set_mute.ramp_duration_ms = DEFAULT_MUTE_RAMP_DURATION;
v->cvs_state = CMD_STATUS_FAIL;
- ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_mute_cmd);
+ ret = apr_send_pkt(common.apr_q6_cvs, (uint32_t *) &cvs_mute_cmd);
if (ret < 0) {
- pr_err("Fail: send STREAM SET MUTE\n");
+ pr_err("%s: Error %d sending stream mute\n", __func__, ret);
+
goto fail;
}
ret = wait_event_timeout(v->cvs_wait,
(v->cvs_state == CMD_STATUS_SUCCESS),
msecs_to_jiffies(TIMEOUT_MS));
- if (!ret)
- pr_err("%s: wait_event timeout\n", __func__);
+ if (!ret) {
+ pr_err("%s: Command timeout\n", __func__);
+
+ goto fail;
+ }
return 0;
+
fail:
return -EINVAL;
}
@@ -2046,19 +2633,18 @@
{
struct cvp_set_mute_cmd cvp_mute_cmd;
int ret = 0;
- void *apr_cvp;
- u16 cvp_handle;
+
if (v == NULL) {
pr_err("%s: v is NULL\n", __func__);
- return -EINVAL;
- }
- apr_cvp = common.apr_q6_cvp;
- if (!apr_cvp) {
- pr_err("%s: apr_cvp is NULL.\n", __func__);
- return -EINVAL;
+ goto fail;
}
- cvp_handle = voice_get_cvp_handle(v);
+
+ if (!common.apr_q6_cvp) {
+ pr_err("%s: apr_cvp is NULL.\n", __func__);
+
+ goto fail;
+ }
cvp_mute_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
APR_HDR_LEN(APR_HDR_SIZE),
@@ -2066,25 +2652,32 @@
cvp_mute_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
sizeof(cvp_mute_cmd) - APR_HDR_SIZE);
cvp_mute_cmd.hdr.src_port = v->session_id;
- cvp_mute_cmd.hdr.dest_port = cvp_handle;
+ cvp_mute_cmd.hdr.dest_port = voice_get_cvp_handle(v);
cvp_mute_cmd.hdr.token = 0;
- cvp_mute_cmd.hdr.opcode = VSS_IVOCPROC_CMD_SET_MUTE;
- cvp_mute_cmd.cvp_set_mute.direction = 1;
+ cvp_mute_cmd.hdr.opcode = VSS_IVOLUME_CMD_MUTE_V2;
+ cvp_mute_cmd.cvp_set_mute.direction = VSS_IVOLUME_DIRECTION_RX;
cvp_mute_cmd.cvp_set_mute.mute_flag = v->dev_rx.mute;
+
v->cvp_state = CMD_STATUS_FAIL;
- ret = apr_send_pkt(apr_cvp, (uint32_t *) &cvp_mute_cmd);
+ ret = apr_send_pkt(common.apr_q6_cvp, (uint32_t *) &cvp_mute_cmd);
if (ret < 0) {
- pr_err("Fail in sending RX device mute cmd\n");
- return -EINVAL;
+ pr_err("%s: Error %d sending rx device cmd\n", __func__, ret);
+
+ goto fail;
}
ret = wait_event_timeout(v->cvp_wait,
(v->cvp_state == CMD_STATUS_SUCCESS),
msecs_to_jiffies(TIMEOUT_MS));
if (!ret) {
- pr_err("%s: wait_event timeout\n", __func__);
- return -EINVAL;
+ pr_err("%s: Command timeout\n", __func__);
+
+ goto fail;
}
+
return 0;
+
+fail:
+ return -EINVAL;
}
static int voice_send_vol_index_cmd(struct voice_data *v)
@@ -2569,6 +3162,9 @@
goto fail;
}
+ voice_send_cvp_deregister_vol_cal_cmd(v);
+ voice_send_cvp_deregister_cal_cmd(v);
+ voice_send_cvp_deregister_dev_cfg_cmd(v);
v->voc_state = VOC_CHANGE;
}
@@ -2598,6 +3194,10 @@
goto fail;
}
+ voice_send_cvp_register_dev_cfg_cmd(v);
+ voice_send_cvp_register_cal_cmd(v);
+ voice_send_cvp_register_vol_cal_cmd(v);
+
ret = voice_send_enable_vocproc_cmd(v);
if (ret < 0) {
pr_err("%s: enable vocproc failed %d\n", __func__, ret);
@@ -2985,8 +3585,21 @@
pr_err("create mvm and cvs failed\n");
goto fail;
}
+
+ /* Memory map the calibration memory block. */
+ ret = voice_mem_map_cal_block(v);
+ if (ret < 0) {
+ pr_err("%s: Memory map of cal block failed %d\n",
+ __func__, ret);
+ /* Allow call to continue, call quality will be bad. */
+ }
+
if (is_voip_session(session_id)) {
- ret = voice_send_mvm_map_memory_physical_cmd(v);
+ ret = voice_map_memory_physical_cmd(v,
+ &v->shmem_info.memtbl,
+ v->shmem_info.sh_buf.buf[0].phys,
+ v->shmem_info.sh_buf.buf[0].size * NUM_OF_BUFFERS,
+ VOIP_MEM_MAP_TOKEN);
if (ret) {
pr_err("%s: mvm_map_memory_phy failed %d\n",
__func__, ret);
@@ -3132,7 +3745,8 @@
}
} else if (data->opcode == VSS_IMEMORY_RSP_MAP) {
pr_debug("%s, Revd VSS_IMEMORY_RSP_MAP response\n", __func__);
- if (data->payload_size) {
+
+ if (data->payload_size && data->token == VOIP_MEM_MAP_TOKEN) {
ptr = data->payload;
if (ptr[0]) {
v->shmem_info.mem_handle = ptr[0];
@@ -3141,6 +3755,21 @@
v->mvm_state = CMD_STATUS_SUCCESS;
wake_up(&v->mvm_wait);
}
+ } else if (data->payload_size &&
+ data->token == VOC_CAL_MEM_MAP_TOKEN) {
+ ptr = data->payload;
+ if (ptr[0]) {
+ c->cal_mem_handle = ptr[0];
+
+ pr_debug("%s: cal mem handle 0x%x\n",
+ __func__, c->cal_mem_handle);
+
+ v->mvm_state = CMD_STATUS_SUCCESS;
+ wake_up(&v->mvm_wait);
+ }
+ } else {
+ pr_err("%s: Unknown mem map token %d\n",
+ __func__, data->token);
}
}
return 0;
@@ -3204,14 +3833,14 @@
v->cvs_state = CMD_STATUS_SUCCESS;
wake_up(&v->cvs_wait);
break;
- case VSS_ISTREAM_CMD_SET_MUTE:
+ case VSS_IVOLUME_CMD_MUTE_V2:
case VSS_ISTREAM_CMD_SET_MEDIA_TYPE:
case VSS_ISTREAM_CMD_VOC_AMR_SET_ENC_RATE:
case VSS_ISTREAM_CMD_VOC_AMRWB_SET_ENC_RATE:
case VSS_ISTREAM_CMD_SET_ENC_DTX_MODE:
case VSS_ISTREAM_CMD_CDMA_SET_ENC_MINMAX_RATE:
case APRV2_IBASIC_CMD_DESTROY_SESSION:
- case VSS_ISTREAM_CMD_REGISTER_CALIBRATION_DATA:
+ case VSS_ISTREAM_CMD_REGISTER_CALIBRATION_DATA_V2:
case VSS_ISTREAM_CMD_DEREGISTER_CALIBRATION_DATA:
case VSS_ICOMMON_CMD_MAP_MEMORY:
case VSS_ICOMMON_CMD_UNMAP_MEMORY:
@@ -3410,13 +4039,15 @@
case VSS_IVOCPROC_CMD_ENABLE:
case VSS_IVOCPROC_CMD_DISABLE:
case APRV2_IBASIC_CMD_DESTROY_SESSION:
- case VSS_IVOCPROC_CMD_REGISTER_VOLUME_CAL_TABLE:
- case VSS_IVOCPROC_CMD_DEREGISTER_VOLUME_CAL_TABLE:
- case VSS_IVOCPROC_CMD_REGISTER_CALIBRATION_DATA:
+ case VSS_IVOCPROC_CMD_REGISTER_VOL_CALIBRATION_DATA:
+ case VSS_IVOCPROC_CMD_DEREGISTER_VOL_CALIBRATION_DATA:
+ case VSS_IVOCPROC_CMD_REGISTER_CALIBRATION_DATA_V2:
case VSS_IVOCPROC_CMD_DEREGISTER_CALIBRATION_DATA:
+ case VSS_IVOCPROC_CMD_REGISTER_DEVICE_CONFIG:
+ case VSS_IVOCPROC_CMD_DEREGISTER_DEVICE_CONFIG:
case VSS_ICOMMON_CMD_MAP_MEMORY:
case VSS_ICOMMON_CMD_UNMAP_MEMORY:
- case VSS_IVOCPROC_CMD_SET_MUTE:
+ case VSS_IVOLUME_CMD_MUTE_V2:
v->cvp_state = CMD_STATUS_SUCCESS;
wake_up(&v->cvp_wait);
break;
@@ -3564,6 +4195,74 @@
return -EINVAL;
}
+static int voice_alloc_cal_mem_map_table(void)
+{
+ int ret = 0;
+ int len;
+
+ common.cal_mem_map_table.client = msm_ion_client_create(UINT_MAX,
+ "voc_client");
+
+ if (IS_ERR_OR_NULL((void *) common.cal_mem_map_table.client)) {
+ pr_err("%s: ION create client for cal mem map table failed\n",
+ __func__);
+
+ goto err;
+ }
+
+ common.cal_mem_map_table.handle =
+ ion_alloc(common.cal_mem_map_table.client,
+ sizeof(struct vss_imemory_table_t),
+ SZ_4K, (0x1 << ION_AUDIO_HEAP_ID), 0);
+ if (IS_ERR_OR_NULL((void *) common.cal_mem_map_table.handle)) {
+ pr_err("%s: ION memory alloc for cal mem map table failed\n",
+ __func__);
+
+ goto err_ion_client;
+ }
+
+ ret = ion_phys(common.cal_mem_map_table.client,
+ common.cal_mem_map_table.handle,
+ (ion_phys_addr_t *) &common.cal_mem_map_table.phys,
+ (size_t *) &len);
+ if (ret) {
+ pr_err("%s: Phy addr for cal mem map table failed %d\n",
+ __func__, ret);
+
+ goto err_ion_handle;
+ }
+
+ common.cal_mem_map_table.data =
+ ion_map_kernel(common.cal_mem_map_table.client,
+ common.cal_mem_map_table.handle);
+ if (IS_ERR_OR_NULL((void *) common.cal_mem_map_table.data)) {
+ pr_err("%s: Virtual addr for cal memory map table failed\n",
+ __func__);
+
+ goto err_ion_handle;
+ }
+
+ memset(common.cal_mem_map_table.data, 0,
+ sizeof(struct vss_imemory_table_t));
+
+ common.cal_mem_map_table.size = sizeof(struct vss_imemory_table_t);
+
+ pr_debug("%s: data 0x%x phys 0x%x\n", __func__,
+ (unsigned int) common.cal_mem_map_table.data,
+ common.cal_mem_map_table.phys);
+
+ return 0;
+
+err_ion_handle:
+ ion_free(common.cal_mem_map_table.client,
+ common.cal_mem_map_table.handle);
+err_ion_client:
+ ion_client_destroy(common.cal_mem_map_table.client);
+ memset(&common.cal_mem_map_table, 0, sizeof(common.cal_mem_map_table));
+err:
+ return -EINVAL;
+}
+
static int __init voice_init(void)
{
int rc = 0, i = 0;
@@ -3612,6 +4311,9 @@
pr_err("failed to alloc mem map talbe %d\n", rc);
}
+ /* Allocate memory for calibration memory map table. */
+ rc = voice_alloc_cal_mem_map_table();
+
return rc;
}
diff --git a/sound/soc/msm/qdsp6v2/q6voice.h b/sound/soc/msm/qdsp6v2/q6voice.h
index df0cbec..9f82694 100644
--- a/sound/soc/msm/qdsp6v2/q6voice.h
+++ b/sound/soc/msm/qdsp6v2/q6voice.h
@@ -25,6 +25,8 @@
*/
#define BUFFER_BLOCK_SIZE 4096
+#define MAX_COL_INFO_SIZE 324
+
#define VOC_REC_UPLINK 0x00
#define VOC_REC_DOWNLINK 0x01
#define VOC_REC_BOTH 0x02
@@ -437,9 +439,13 @@
#define APRV2_IBASIC_CMD_DESTROY_SESSION 0x0001003C
-#define VSS_ISTREAM_CMD_SET_MUTE 0x00011022
+/*
+ * This command changes the mute setting. The new mute setting will
+ * be applied over the specified ramp duration.
+ */
+#define VSS_IVOLUME_CMD_MUTE_V2 0x0001138B
-#define VSS_ISTREAM_CMD_REGISTER_CALIBRATION_DATA 0x00011279
+#define VSS_ISTREAM_CMD_REGISTER_CALIBRATION_DATA_V2 0x00011369
#define VSS_ISTREAM_CMD_DEREGISTER_CALIBRATION_DATA 0x0001127A
@@ -541,22 +547,33 @@
*/
} __packed;
-struct vss_istream_cmd_set_mute_t {
+#define VSS_IVOLUME_DIRECTION_TX 0
+#define VSS_IVOLUME_DIRECTION_RX 1
+
+#define VSS_IVOLUME_MUTE_OFF 0
+#define VSS_IVOLUME_MUTE_ON 1
+
+#define DEFAULT_MUTE_RAMP_DURATION 500
+
+struct vss_ivolume_cmd_mute_v2_t {
uint16_t direction;
- /**<
- * 0 : TX only
- * 1 : RX only
- * 2 : TX and Rx
- */
+ /*
+ * The direction field sets the direction to apply the mute command.
+ * The Supported values:
+ * VSS_IVOLUME_DIRECTION_TX
+ * VSS_IVOLUME_DIRECTION_RX
+ */
uint16_t mute_flag;
- /**<
- * Mute, un-mute.
- *
- * 0 : Silence disable
- * 1 : Silence enable
- * 2 : CNG enable. Applicable to TX only. If set on RX behavior
- * will be the same as 1
- */
+ /*
+ * Turn mute on or off. The Supported values:
+ * VSS_IVOLUME_MUTE_OFF
+ * VSS_IVOLUME_MUTE_ON
+ */
+ uint16_t ramp_duration_ms;
+ /*
+ * Mute change ramp duration in milliseconds.
+ * The Supported values: 0 to 5000.
+ */
} __packed;
struct vss_istream_cmd_create_full_control_session_t {
@@ -666,14 +683,21 @@
*/
} __packed;
-struct vss_istream_cmd_register_calibration_data_t {
- uint32_t phys_addr;
- /* Phsical address to be registered with stream. The calibration data
- * is stored at this address.
- */
- uint32_t mem_size;
+struct vss_istream_cmd_register_calibration_data_v2_t {
+ uint32_t cal_mem_handle;
+ /* Handle to the shared memory that holds the calibration data. */
+ uint64_t cal_mem_address;
+ /* Location of calibration data. */
+ uint32_t cal_mem_size;
/* Size of the calibration data in bytes. */
-};
+ uint8_t column_info[MAX_COL_INFO_SIZE];
+ /*
+ * Column info contains the number of columns and the array of columns
+ * in the calibration table. The order in which the columns are provided
+ * here must match the order in which they exist in the calibration
+ * table provided.
+ */
+} __packed;
struct vss_icommon_cmd_set_ui_property_enable_t {
uint32_t module_id;
@@ -705,7 +729,7 @@
struct cvs_set_mute_cmd {
struct apr_hdr hdr;
- struct vss_istream_cmd_set_mute_t cvs_set_mute;
+ struct vss_ivolume_cmd_mute_v2_t cvs_set_mute;
} __packed;
struct cvs_set_media_type_cmd {
@@ -740,7 +764,7 @@
struct cvs_register_cal_data_cmd {
struct apr_hdr hdr;
- struct vss_istream_cmd_register_calibration_data_t cvs_cal_data;
+ struct vss_istream_cmd_register_calibration_data_v2_t cvs_cal_data;
} __packed;
struct cvs_deregister_cal_data_cmd {
@@ -797,11 +821,24 @@
#define VSS_IVOCPROC_CMD_DISABLE 0x000110E1
/**< No payload. Wait for APRV2_IBASIC_RSP_RESULT response. */
-#define VSS_IVOCPROC_CMD_REGISTER_CALIBRATION_DATA 0x00011275
-#define VSS_IVOCPROC_CMD_DEREGISTER_CALIBRATION_DATA 0x00011276
+/*
+ * Registers the memory that contains device specific configuration data with
+ * the vocproc. The client must register device configuration data with the
+ * vocproc that corresponds with the device being set on the vocproc.
+ */
+#define VSS_IVOCPROC_CMD_REGISTER_DEVICE_CONFIG 0x00011371
-#define VSS_IVOCPROC_CMD_REGISTER_VOLUME_CAL_TABLE 0x00011277
-#define VSS_IVOCPROC_CMD_DEREGISTER_VOLUME_CAL_TABLE 0x00011278
+/*
+ * Deregisters the memory that holds device configuration data from the
+ vocproc.
+*/
+#define VSS_IVOCPROC_CMD_DEREGISTER_DEVICE_CONFIG 0x00011372
+
+#define VSS_IVOCPROC_CMD_REGISTER_CALIBRATION_DATA_V2 0x00011373
+#define VSS_IVOCPROC_CMD_DEREGISTER_CALIBRATION_DATA 0x00011276
+
+#define VSS_IVOCPROC_CMD_REGISTER_VOL_CALIBRATION_DATA 0x00011374
+#define VSS_IVOCPROC_CMD_DEREGISTER_VOL_CALIBRATION_DATA 0x00011375
#define VSS_IVOCPROC_TOPOLOGY_ID_NONE 0x00010F70
#define VSS_IVOCPROC_TOPOLOGY_ID_TX_SM_ECNS 0x00010F71
@@ -847,8 +884,6 @@
#define VSS_MEDIA_ID_4GV_WB_MODEM 0x00010FC4
/*CDMA EVRC-WB vocoder modem format */
-#define VSS_IVOCPROC_CMD_SET_MUTE 0x000110EF
-
#define VOICE_CMD_SET_PARAM 0x00011006
#define VOICE_CMD_GET_PARAM 0x00011007
#define VOICE_EVT_GET_PARAM_ACK 0x00011008
@@ -941,39 +976,54 @@
*/
} __packed;
-struct vss_ivocproc_cmd_register_calibration_data_t {
- uint32_t phys_addr;
- /* Phsical address to be registered with vocproc. Calibration data
- * is stored at this address.
+struct vss_ivocproc_cmd_register_device_config_t {
+ uint32_t mem_handle;
+ /*
+ * Handle to the shared memory that holds the per-network calibration
+ * data.
*/
+ uint64_t mem_address;
+ /* Location of calibration data. */
uint32_t mem_size;
/* Size of the calibration data in bytes. */
} __packed;
-struct vss_ivocproc_cmd_register_volume_cal_table_t {
- uint32_t phys_addr;
- /* Phsical address to be registered with the vocproc. The volume
- * calibration table is stored at this location.
+struct vss_ivocproc_cmd_register_calibration_data_v2_t {
+ uint32_t cal_mem_handle;
+ /*
+ * Handle to the shared memory that holds the per-network calibration
+ * data.
*/
-
- uint32_t mem_size;
- /* Size of the volume calibration table in bytes. */
+ uint64_t cal_mem_address;
+ /* Location of calibration data. */
+ uint32_t cal_mem_size;
+ /* Size of the calibration data in bytes. */
+ uint8_t column_info[MAX_COL_INFO_SIZE];
+ /*
+ * Column info contains the number of columns and the array of columns
+ * in the calibration table. The order in which the columns are provided
+ * here must match the order in which they exist in the calibration
+ * table provided.
+ */
} __packed;
-struct vss_ivocproc_cmd_set_mute_t {
- uint16_t direction;
+struct vss_ivocproc_cmd_register_volume_cal_data_t {
+ uint32_t cal_mem_handle;
/*
- * 0 : TX only.
- * 1 : RX only.
- * 2 : TX and Rx.
- */
- uint16_t mute_flag;
+ * Handle to the shared memory that holds the volume calibration
+ * data.
+ */
+ uint64_t cal_mem_address;
+ /* Location of volume calibration data. */
+ uint32_t cal_mem_size;
+ /* Size of the volume calibration data in bytes. */
+ uint8_t column_info[MAX_COL_INFO_SIZE];
/*
- * Mute, un-mute.
- *
- * 0 : Disable.
- * 1 : Enable.
- */
+ * Column info contains the number of columns and the array of columns
+ * in the calibration table. The order in which the columns are provided
+ * here must match the order in which they exist in the calibration
+ * table provided.
+ */
} __packed;
struct cvp_create_full_ctl_session_cmd {
@@ -999,27 +1049,36 @@
struct vss_ivocproc_cmd_set_volume_index_t cvp_set_vol_idx;
} __packed;
+struct cvp_register_dev_cfg_cmd {
+ struct apr_hdr hdr;
+ struct vss_ivocproc_cmd_register_device_config_t cvp_dev_cfg_data;
+} __packed;
+
+struct cvp_deregister_dev_cfg_cmd {
+ struct apr_hdr hdr;
+} __packed;
+
struct cvp_register_cal_data_cmd {
struct apr_hdr hdr;
- struct vss_ivocproc_cmd_register_calibration_data_t cvp_cal_data;
+ struct vss_ivocproc_cmd_register_calibration_data_v2_t cvp_cal_data;
} __packed;
struct cvp_deregister_cal_data_cmd {
struct apr_hdr hdr;
} __packed;
-struct cvp_register_vol_cal_table_cmd {
+struct cvp_register_vol_cal_data_cmd {
struct apr_hdr hdr;
- struct vss_ivocproc_cmd_register_volume_cal_table_t cvp_vol_cal_tbl;
+ struct vss_ivocproc_cmd_register_volume_cal_data_t cvp_vol_cal_data;
} __packed;
-struct cvp_deregister_vol_cal_table_cmd {
+struct cvp_deregister_vol_cal_data_cmd {
struct apr_hdr hdr;
} __packed;
struct cvp_set_mute_cmd {
struct apr_hdr hdr;
- struct vss_ivocproc_cmd_set_mute_t cvp_set_mute;
+ struct vss_ivolume_cmd_mute_v2_t cvp_set_mute;
} __packed;
/* CB for up-link packets. */
@@ -1130,7 +1189,8 @@
/* APR to CVP in the Q6 */
void *apr_q6_cvp;
- struct ion_client *client;
+ struct mem_map_table cal_mem_map_table;
+ uint32_t cal_mem_handle;
struct cal_mem cvp_cal;
struct cal_mem cvs_cal;