Merge "usb: rndis: reset ul aggregation stats for every set_alt"
diff --git a/Documentation/devicetree/bindings/arm/msm/cpr-regulator.txt b/Documentation/devicetree/bindings/arm/msm/cpr-regulator.txt
index 203730f..439418d 100644
--- a/Documentation/devicetree/bindings/arm/msm/cpr-regulator.txt
+++ b/Documentation/devicetree/bindings/arm/msm/cpr-regulator.txt
@@ -13,8 +13,10 @@
Required properties:
- compatible: Must be "qcom,cpr-regulator"
- reg: Register addresses for RBCPR and efuse
-- reg-names: Register names. Must be "rbcpr" and "efuse_phys"
+- reg-names: Register names. Must be "rbcpr", "pvs_efuse"
+ and "cpr_efuse"
- regulator-name: A string used to describe the regulator
+- interrupts: Interrupt line from RBCPR to interrupt controller.
- regulator-min-microvolt: Minimum corner value as min constraint, which
should be 1 for SVS corner
- regulator-max-microvolt: Maximum corner value as max constraint, which
@@ -23,9 +25,6 @@
represent total number of PVS bins. It should
not exceed a maximum of 5 for total number of
32 bins.
-- qcom,efuse-bit-pos: A list of integers whose length must equal
- to qcom,num-efuse-bits and each integer indicates
- bit position in efuse memory from LSB to MSB
- qcom,pvs-bin-process: A list of integers whose length is equal to 2 to
the power of qcom,num-efuse-bits. The location or
0-based index of an element in the list corresponds
@@ -44,8 +43,25 @@
0 (SVS voltage): 1050000 uV
1 (NORMAL voltage): 1150000 uV
2 (TURBO voltage): 1275000 uV
- 3 (SUPER_TURBO voltage): 1275000 uV
- vdd-apc-supply: Regulator to supply VDD APC power
+- qcom,vdd-apc-step-up-limit: Limit of vdd-apc-supply steps for scaling up.
+- qcom,vdd-apc-step-down-limit: Limit of vdd-apc-supply steps for scaling down.
+- qcom,cpr-ref-clk: The reference clock in kHz.
+- qcom,cpr-timer-delay: The delay in microseconds for the timer interval.
+- qcom,cpr-timer-cons-up: Consecutive number of timer interval (qcom,cpr-timer-delay)
+ occurred before issuing UP interrupt.
+- qcom,cpr-timer-cons-down: Consecutive number of timer interval (qcom,cpr-timer-delay)
+ occurred before issuing DOWN interrupt.
+- qcom,cpr-irq-line: Internal interrupt route signal of RBCPR, one of 0, 1 or 2.
+- qcom,cpr-step-quotient: Number of CPR quotient (RO count) per vdd-apc-supply step
+ to issue error_steps.
+- qcom,cpr-up-threshold: The threshold for CPR to issue interrupt when
+ error_steps is greater than it when stepping up.
+- qcom,cpr-down-threshold: The threshold for CPR to issue interrupt when
+ error_steps is greater than it when stepping down.
+- qcom,cpr-idle-clocks: Idle clock cycles RO can be in.
+- qcom,cpr-gcnt-time: The time for gate count in microseconds.
+- qcom,cpr-apc-volt-step: The voltage in microvolt per CPR step, such as 5000uV.
Optional properties:
@@ -61,28 +77,42 @@
2 => equal to slow speed corner ceiling
3 => equal to qcom,vdd-mx-vmax
This is required when vdd-mx-supply is present.
+- qcom,cpr-enable: Present: CPR enabled by default.
+ Not Present: CPR disable by default.
Example:
apc_vreg_corner: regulator@f9018000 {
status = "okay";
compatible = "qcom,cpr-regulator";
- reg = <0xf9018000 0x1000>,
- <0xfc4b80b0 8>;
- reg-names = "rbcpr", "efuse_phys";
+ reg = <0xf9018000 0x1000>, <0xfc4b80b0 8>, <0xfc4bc450 16>;
+ reg-names = "rbcpr", "pvs_efuse", "cpr_efuse";
+ interrupts = <0 15 0>;
regulator-name = "apc_corner";
regulator-min-microvolt = <1>;
- regulator-max-microvolt = <4>;
+ regulator-max-microvolt = <3>;
qcom,num-efuse-bits = <5>;
- qcom,efuse-bit-pos = <6 7 8 9 10>;
- qcom,pvs-bin-process = <0 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2
+ qcom,pvs-bin-process = <0 0 0 0 0 1 1 1 1 1 2 2 2 2 2 2
2 2 2 2 3 3 3 3 3 3 3 3 0 0 0 0>;
- qcom,pvs-corner-ceiling-slow = <1050000 1150000 1275000 1350000>;
- qcom,pvs-corner-ceiling-nom = <975000 1075000 1200000 1200000>;
- qcom,pvs-corner-ceiling-fast = <900000 1000000 1140000 1140000>;
+ qcom,pvs-corner-ceiling-slow = <1050000 1160000 1275000>;
+ qcom,pvs-corner-ceiling-nom = <975000 1075000 1200000>;
+ qcom,pvs-corner-ceiling-fast = <900000 1000000 1140000>;
vdd-apc-supply = <&pm8226_s2>;
vdd-mx-supply = <&pm8226_l3_ao>;
qcom,vdd-mx-vmax = <1350000>;
qcom,vdd-mx-vmin-method = <1>;
+ qcom,vdd-apc-step-up-limit = <1>;
+ qcom,vdd-apc-step-down-limit = <1>;
+ qcom,cpr-ref-clk = <19200>;
+ qcom,cpr-timer-delay = <5000>;
+ qcom,cpr-timer-cons-up = <1>;
+ qcom,cpr-timer-cons-down = <2>;
+ qcom,cpr-irq-line = <0>;
+ qcom,cpr-step-quotient = <15>;
+ qcom,cpr-up-threshold = <1>;
+ qcom,cpr-down-threshold = <2>;
+ qcom,cpr-idle-clocks = <5>;
+ qcom,cpr-gcnt-time = <1>;
+ qcom,cpr-apc-volt-step = <5000>;
};
diff --git a/Documentation/devicetree/bindings/arm/msm/msm_tspp.txt b/Documentation/devicetree/bindings/arm/msm/msm_tspp.txt
index 2b5e143..cfda474 100644
--- a/Documentation/devicetree/bindings/arm/msm/msm_tspp.txt
+++ b/Documentation/devicetree/bindings/arm/msm/msm_tspp.txt
@@ -28,6 +28,13 @@
Note: it is assumed that the functionality value (e.g. 1 in 8974 case)
is applicable to all TSIF GPIOs.
+Optional properties:
+
+- vdd_cx-supply: Reference to the regulator that supplies the CX rail.
+ Some hardware platforms (e.g. 8974-v2) require the voltage of the rail
+ supplying power to the TSIF hardware block to be elevated before
+ enabling the TSIF clocks.
+
Example (for 8974 platform, avaialble at msm8974.dtsi):
tspp: msm_tspp@f99d8000 {
diff --git a/Documentation/devicetree/bindings/coresight/coresight.txt b/Documentation/devicetree/bindings/coresight/coresight.txt
index 25219cd..ce797d3 100644
--- a/Documentation/devicetree/bindings/coresight/coresight.txt
+++ b/Documentation/devicetree/bindings/coresight/coresight.txt
@@ -19,7 +19,8 @@
"arm,coresight-stm" for coresight stm trace device,
"arm,coresight-etm" for coresight etm trace devices,
"qcom,coresight-csr" for coresight csr device,
- "arm,coresight-cti" for coresight cti devices
+ "arm,coresight-cti" for coresight cti devices,
+ "qcom,coresight-hwevent" for coresight hardware event devices
- reg : physical base address and length of the register set(s) of the component
- reg-names : names corresponding to each reg property value. The reg-names that
need to be used with corresponding compatible string for a coresight device
@@ -61,6 +62,12 @@
compatible : should be "arm,coresight-cti"
reg-names : should be:
"cti<num>-base" - physical base address of cti registers
+ - for coresight hardware event devices:
+ compatible : should be "qcom,coresight-hwevent"
+ reg-names : should be:
+ "<ss-mux>" - physical base address of hardware event mux
+ control registers where <ss-mux> is subsystem mux it
+ represents
- coresight-id : unique integer identifier for the component
- coresight-name : unique descriptive name of the component
- coresight-nr-inports : number of input ports on the component
@@ -105,6 +112,7 @@
- qcom,setb-gpios-drv : active drive strength for set B gpios
- qcom,setb-gpios-pull : active pull configuration for set B gpios
- qcom,setb-gpios-dir : active direction for set B gpios
+- qcom,hwevent-clks : list of clocks required by hardware event driver
Examples:
@@ -213,3 +221,17 @@
coresight-name = "coresight-cti1";
coresight-nr-inports = <0>;
};
+
+ hwevent: hwevent@fdf30018 {
+ compatible = "qcom,coresight-hwevent";
+ reg = <0xfdf30018 0x80>,
+ <0xf9011080 0x80>,
+ <0xfd4ab160 0x80>;
+ reg-names = "mmss-mux", "apcs-mux", "ppss-mux";
+
+ coresight-id = <29>;
+ coresight-name = "coresight-hwevent";
+ coresight-nr-inports = <0>;
+
+ qcom,hwevent-clks = "core_mmss_clk";
+ };
diff --git a/Documentation/devicetree/bindings/input/gen_vkeys.txt b/Documentation/devicetree/bindings/input/gen_vkeys.txt
index da99e19..2f8d65e 100644
--- a/Documentation/devicetree/bindings/input/gen_vkeys.txt
+++ b/Documentation/devicetree/bindings/input/gen_vkeys.txt
@@ -12,6 +12,9 @@
- qcom,panel-maxy : Maximum y-coordinate of touch panel
- qcom,key-codes : Array of key codes for virtual keys
+Optional properties:
+ - qcom,y-offset : Offset of y-location for virtual keys, default 0
+
Example:
gen-vkeys {
compatible = "qcom,gen-vkeys";
@@ -21,4 +24,5 @@
qcom,panel-maxx = <760>;
qcom,panel-maxy = <1424>;
qcom,key-codes = <158 139 102 217>;
+ qcom,y-offset = <35>;
};
diff --git a/Documentation/devicetree/bindings/iommu/msm_iommu_v0.txt b/Documentation/devicetree/bindings/iommu/msm_iommu_v0.txt
index cc1ffc2..706ffe6 100644
--- a/Documentation/devicetree/bindings/iommu/msm_iommu_v0.txt
+++ b/Documentation/devicetree/bindings/iommu/msm_iommu_v0.txt
@@ -11,17 +11,21 @@
- qcom,iommu-pmu-ngroups: Number of Performance Monitor Unit (PMU) groups.
- qcom,iommu-pmu-ncounters: Number of PMU counters per group.
- qcom,iommu-pmu-event-classes: List of event classes supported.
+- qcom,needs-alt-core-clk : boolean to enable the secondary core clock for
+ access to the IOMMU configuration registers
+
- List of sub nodes, one for each of the translation context banks supported.
- Each sub node has the following required properties:
+ Required properties for each sub-node:
- - reg : offset and length of the register set for the context bank.
- - interrupts : should contain the context bank interrupt.
- - qcom,iommu-ctx-mids : List of machine identifiers associated with this
- translation context.
- - label : Name of the context bank
+ - compatible : "qcom,msm-smmu-v0-ctx"
+ - reg : offset and length of the register set for the context bank.
+ - interrupts : should contain the context bank interrupt.
+ - qcom,iommu-ctx-mids : List of machine identifiers associated with this
+ translation context.
+ - label : Name of the context bank
-Optional properties:
- - none
+ Optional properties for each sub-node:
+ - none
Example:
@@ -39,6 +43,7 @@
0x11>;
qcom,iommu-ctx@fd000000 {
+ compatible = "qcom,msm-smmu-v0-ctx";
reg = <0xfd000000 0x1000>;
interrupts = <0 250 0>;
qcom,iommu-ctx-mids = <0 3>;
diff --git a/Documentation/devicetree/bindings/iommu/msm_iommu_v1.txt b/Documentation/devicetree/bindings/iommu/msm_iommu_v1.txt
index 2c47f74..56f4767 100644
--- a/Documentation/devicetree/bindings/iommu/msm_iommu_v1.txt
+++ b/Documentation/devicetree/bindings/iommu/msm_iommu_v1.txt
@@ -25,6 +25,7 @@
- List of sub nodes, one for each of the translation context banks supported.
Each sub node has the following required properties:
+ - compatible : "qcom,msm-smmu-v1-ctx"
- reg : offset and length of the register set for the context bank.
- interrupts : should contain the context bank interrupt.
- qcom,iommu-ctx-sids : List of stream identifiers associated with this
@@ -65,12 +66,14 @@
0x01>;
qcom,iommu-ctx@fda6c000 {
+ compatible = "qcom,msm-smmu-v1-ctx";
reg = <0xfda6c000 0x1000>;
interrupts = <0 70 0>;
qcom,iommu-ctx-sids = <0 2>;
label = "ctx_0";
};
qcom,iommu-ctx@fda6d000 {
+ compatible = "qcom,msm-smmu-v1-ctx";
reg = <0xfda6d000 0x1000>;
interrupts = <0 71 0>;
qcom,iommu-ctx-sids = <1>;
diff --git a/Documentation/devicetree/bindings/leds/leds-qpnp.txt b/Documentation/devicetree/bindings/leds/leds-qpnp.txt
index 96d95da..c409ea6 100644
--- a/Documentation/devicetree/bindings/leds/leds-qpnp.txt
+++ b/Documentation/devicetree/bindings/leds/leds-qpnp.txt
@@ -45,6 +45,7 @@
- qcom,saftey-timer: include for safety timer use, otherwise watchdog timer will be used
- linux,default-trigger: trigger the led from external modules such as display
- qcom,default-state: default state of the led, should be "on" or "off"
+- qcom,torch-enable: set flash led to torch mode
RGB Led is a tri-colored led, Red, Blue & Green.
diff --git a/Documentation/devicetree/bindings/pil/pil-q6v5-lpass.txt b/Documentation/devicetree/bindings/pil/pil-q6v5-lpass.txt
index 4cbff52..a7a3f0c 100644
--- a/Documentation/devicetree/bindings/pil/pil-q6v5-lpass.txt
+++ b/Documentation/devicetree/bindings/pil/pil-q6v5-lpass.txt
@@ -15,6 +15,7 @@
- vdd_cx-supply: Reference to the regulator that supplies the vdd_cx domain.
- qcom,firmware-name: Base name of the firmware image. Ex. "lpass"
- qcom,gpio-err-fatal: GPIO used by the lpass to indicate error fatal to the apps.
+- qcom,gpio-err-ready: GPIO used by the lpass to indicate apps error service is ready.
- qcom,gpio-force-stop: GPIO used by the apps to force the lpass to shutdown.
- qcom,gpio-proxy-unvote: GPIO used by the lpass to indicate apps clock is ready.
diff --git a/Documentation/devicetree/bindings/pil/pil-q6v5-mss.txt b/Documentation/devicetree/bindings/pil/pil-q6v5-mss.txt
index c3d929c..ded8f77 100644
--- a/Documentation/devicetree/bindings/pil/pil-q6v5-mss.txt
+++ b/Documentation/devicetree/bindings/pil/pil-q6v5-mss.txt
@@ -12,7 +12,6 @@
- reg-names: Names of the bases for the above registers. "qdsp6_base",
"halt_base", "rmb_base", and "restart_reg" are expected.
- interrupts: The modem watchdog interrupt
-- vdd_mss-supply: Reference to the regulator that supplies the processor.
- vdd_cx-supply: Reference to the regulator that supplies the vdd_cx domain.
- vdd_mx-supply: Reference to the regulator that supplies the memory rail.
- qcom,firmware-name: Base name of the firmware image. Ex. "mdsp"
@@ -23,11 +22,16 @@
- qcom,gpio-force-stop: GPIO used by the apps to force the modem to shutdown.
Optional properties:
+- vdd_mss-supply: Reference to the regulator that supplies the processor.
+ This may be a shared regulator that is already voted
+ on in the PIL proxy voting code (and also managed by the
+ modem on its own), hence we mark it as as optional.
- vdd_pll-supply: Reference to the regulator that supplies the PLL's rail.
- qcom,vdd_pll: Voltage to be set for the PLL's rail.
- reg-names: "cxrail_bhs_reg" - control register for modem power
domain.
-- qcom,is-loadable: Boolean- Present if the image needs to be loaded.
+- qcom,is-not-loadable: Boolean- Present if the image does not need to
+ be loaded.
- qcom,pil-self-auth: Boolean- True if authentication is required.
Example:
@@ -44,7 +48,7 @@
vdd_cx-supply = <&pm8841_s2>;
vdd_mx-supply = <&pm8841_s1>;
- qcom,is-loadable;
+ qcom,is-not-loadable;
qcom,firmware-name = "mba";
qcom,pil-self-auth;
diff --git a/Documentation/devicetree/bindings/power/qpnp-bms.txt b/Documentation/devicetree/bindings/power/qpnp-bms.txt
index 6d093f0..b350e24 100644
--- a/Documentation/devicetree/bindings/power/qpnp-bms.txt
+++ b/Documentation/devicetree/bindings/power/qpnp-bms.txt
@@ -47,7 +47,7 @@
SoC recalculations when the current SoC is below
qcom,low-soc-calculate-soc-threshold or when battery
voltage is below qcom,low-voltage-threshold.
-- qcom,soc-calculate-soc-ms : The time period between subsequent SoC
+- qcom,calculate-soc-ms : The time period between subsequent SoC
recalculations when the current SoC is above or equal
qcom,low-soc-calculate-soc-threshold.
- qcom,chg-term-ua : current in micro-amps when charging is considered done.
@@ -64,6 +64,9 @@
curve.
- qcom,hold-soc-est: if the voltage-based estimated SoC is above this percent,
the BMS will clamp SoC to be at least 1.
+- qcom,tm-temp-margin: if the pmic die temperature changes by more than this
+ value, recalibrate the ADCs. The unit of this property
+ is in millidegrees celsius.
Parent node optional properties:
- qcom,ignore-shutdown-soc: A boolean that controls whether BMS will
@@ -122,6 +125,7 @@
qcom,low-ocv-correction-limit-uv = <100>;
qcom,high-ocv-correction-limit-uv = <50>;
qcom,hold-soc-est = <3>;
+ qcom,tm-temp-margin = <5000>;
qcom,bms-iadc@3800 {
reg = <0x3800 0x100>;
diff --git a/Documentation/devicetree/bindings/regulator/krait-regulator.txt b/Documentation/devicetree/bindings/regulator/krait-regulator.txt
index 6a02e86..a8195df 100644
--- a/Documentation/devicetree/bindings/regulator/krait-regulator.txt
+++ b/Documentation/devicetree/bindings/regulator/krait-regulator.txt
@@ -9,16 +9,27 @@
[First Level Nodes]
Required properties:
- compatible: Must be "qcom,krait-pdn"
-- reg: Specifies the physical address of the APCS GCC
- register base
-- reg-names: "apcs_gcc" -string to identify the area where
- the APCS GCC registers reside.
+- reg: This property contains a list of physical
+ addresses for Krait PDN features. The list
+ should contain the address of the APCS GCC
+ register base and the address of the phase
+ scaling factor eFuse.
+- reg-names: This property contains a list of strings naming
+ the registers listed in the reg property.
+ "apcs_gcc" is a string to identify the area
+ where the APCS GCC registers reside.
+ "phase-scaling-efuse" should be used to identify
+ the phase scaling factor eFuse address.
- qcom,pfm-threshold The power coeff threshold in abstract power units below which
pmic will be made to operate in PFM mode.
Optional properties:
- qcom,use-phase-switching indicates whether the driver should add/shed phases on the PMIC
ganged regulator as cpus are hotplugged.
+- qcom,use-phase-scaling-factor Boolean which indicates if the value stored in
+ the phase scaling eFuse should be used or not.
+ If this property is not specified, then worst
+ case scaling will be assumed.
[Second Level Nodes]
Required properties:
@@ -49,10 +60,12 @@
Example:
krait_pdn: krait-pdn@f9011000 {
- reg = <0xf9011000 0x1000>;
- reg-names = "apcs_gcc";
+ reg = <0xf9011000 0x1000>,
+ <0xfc4b80b0 8>;
+ reg-names = "apcs_gcc", "phase-scaling-efuse";
compatible = "qcom,krait-pdn";
qcom,use-phase-switching;
+ qcom,use-phase-scaling-factor;
qcom,pfm-threshold = <376975>;
#address-cells = <1>;
#size-cells = <1>;
diff --git a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
index 9f6cb16..74ea2cd 100644
--- a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
+++ b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
@@ -465,6 +465,8 @@
- qcom,ext-ult-lo-amp-gpio: GPIO to enable external ultrasound lineout
amplifier.
+- qcom,headset-jack-type-NO: Adjust GPIO level based on the headset jack type.
+
Example:
sound {
@@ -667,3 +669,32 @@
compatible = "qcom,msm-audio-ion;
qcom,smmu-enabled;
};
+
+* MSM8226 ASoC Machine driver
+
+Required properties:
+- compatible : "qcom,msm8226-audio-tapan"
+- qcom,model : The user-visible name of this sound card.
+- qcom,tapan-mclk-clk-freq : Tapan mclk Freq in Hz. currently only 9600000Hz
+ is supported.
+- qcom,prim-auxpcm-gpio-clk : GPIO on which Primary AUXPCM clk signal is coming.
+- qcom,prim-auxpcm-gpio-sync : GPIO on which Primary AUXPCM SYNC signal is coming.
+- qcom,prim-auxpcm-gpio-din : GPIO on which Primary AUXPCM DIN signal is coming.
+- qcom,prim-auxpcm-gpio-dout : GPIO on which Primary AUXPCM DOUT signal is coming.
+- qcom,prim-auxpcm-gpio-set : set of GPIO lines used for Primary AUXPCM port
+ Possible Values:
+ prim-gpio-prim : Primary AUXPCM shares GPIOs with Primary MI2S
+ prim-gpio-tert : Primary AUXPCM shares GPIOs with Tertiary MI2S
+
+Example:
+
+sound {
+ compatible = "qcom,msm8226-audio-tapan";
+ qcom,model = "msm8226-tapan-snd-card";
+ qcom,tapan-mclk-clk-freq = <9600000>;
+ qcom,prim-auxpcm-gpio-clk = <&msmgpio 63 0>;
+ qcom,prim-auxpcm-gpio-sync = <&msmgpio 64 0>;
+ qcom,prim-auxpcm-gpio-din = <&msmgpio 65 0>;
+ qcom,prim-auxpcm-gpio-dout = <&msmgpio 66 0>;
+ qcom,prim-auxpcm-gpio-set = "prim-gpio-prim";
+};
diff --git a/Documentation/devicetree/bindings/sound/taiko_codec.txt b/Documentation/devicetree/bindings/sound/taiko_codec.txt
index 777933a..9abf54e 100644
--- a/Documentation/devicetree/bindings/sound/taiko_codec.txt
+++ b/Documentation/devicetree/bindings/sound/taiko_codec.txt
@@ -76,6 +76,8 @@
dynamically.
Supplies in this list are off by default.
+ - qcom,cdc-micbias2-headset-only: Boolean. Allow micbias 2 only to headset mic.
+
Example:
taiko_codec {
@@ -138,6 +140,7 @@
qcom,cdc-slim-ifd = "taiko-slim-ifd";
qcom,cdc-slim-ifd-elemental-addr = [00 00 A0 00 17 02];
qcom,cdc-dmic-sample-rate = <4800000>;
+ qcom,cdc-micbias2-headset-only;
};
Wcd9xxx audio CODEC in I2C mode
diff --git a/Documentation/workqueue.txt b/Documentation/workqueue.txt
index a0b577d..a6ab4b6 100644
--- a/Documentation/workqueue.txt
+++ b/Documentation/workqueue.txt
@@ -89,25 +89,28 @@
The cmwq design differentiates between the user-facing workqueues that
subsystems and drivers queue work items on and the backend mechanism
-which manages thread-pool and processes the queued work items.
+which manages thread-pools and processes the queued work items.
The backend is called gcwq. There is one gcwq for each possible CPU
-and one gcwq to serve work items queued on unbound workqueues.
+and one gcwq to serve work items queued on unbound workqueues. Each
+gcwq has two thread-pools - one for normal work items and the other
+for high priority ones.
Subsystems and drivers can create and queue work items through special
workqueue API functions as they see fit. They can influence some
aspects of the way the work items are executed by setting flags on the
workqueue they are putting the work item on. These flags include
-things like CPU locality, reentrancy, concurrency limits and more. To
-get a detailed overview refer to the API description of
+things like CPU locality, reentrancy, concurrency limits, priority and
+more. To get a detailed overview refer to the API description of
alloc_workqueue() below.
-When a work item is queued to a workqueue, the target gcwq is
-determined according to the queue parameters and workqueue attributes
-and appended on the shared worklist of the gcwq. For example, unless
-specifically overridden, a work item of a bound workqueue will be
-queued on the worklist of exactly that gcwq that is associated to the
-CPU the issuer is running on.
+When a work item is queued to a workqueue, the target gcwq and
+thread-pool is determined according to the queue parameters and
+workqueue attributes and appended on the shared worklist of the
+thread-pool. For example, unless specifically overridden, a work item
+of a bound workqueue will be queued on the worklist of either normal
+or highpri thread-pool of the gcwq that is associated to the CPU the
+issuer is running on.
For any worker pool implementation, managing the concurrency level
(how many execution contexts are active) is an important issue. cmwq
@@ -115,26 +118,26 @@
Minimal to save resources and sufficient in that the system is used at
its full capacity.
-Each gcwq bound to an actual CPU implements concurrency management by
-hooking into the scheduler. The gcwq is notified whenever an active
-worker wakes up or sleeps and keeps track of the number of the
-currently runnable workers. Generally, work items are not expected to
-hog a CPU and consume many cycles. That means maintaining just enough
-concurrency to prevent work processing from stalling should be
-optimal. As long as there are one or more runnable workers on the
-CPU, the gcwq doesn't start execution of a new work, but, when the
-last running worker goes to sleep, it immediately schedules a new
-worker so that the CPU doesn't sit idle while there are pending work
-items. This allows using a minimal number of workers without losing
-execution bandwidth.
+Each thread-pool bound to an actual CPU implements concurrency
+management by hooking into the scheduler. The thread-pool is notified
+whenever an active worker wakes up or sleeps and keeps track of the
+number of the currently runnable workers. Generally, work items are
+not expected to hog a CPU and consume many cycles. That means
+maintaining just enough concurrency to prevent work processing from
+stalling should be optimal. As long as there are one or more runnable
+workers on the CPU, the thread-pool doesn't start execution of a new
+work, but, when the last running worker goes to sleep, it immediately
+schedules a new worker so that the CPU doesn't sit idle while there
+are pending work items. This allows using a minimal number of workers
+without losing execution bandwidth.
Keeping idle workers around doesn't cost other than the memory space
for kthreads, so cmwq holds onto idle ones for a while before killing
them.
For an unbound wq, the above concurrency management doesn't apply and
-the gcwq for the pseudo unbound CPU tries to start executing all work
-items as soon as possible. The responsibility of regulating
+the thread-pools for the pseudo unbound CPU try to start executing all
+work items as soon as possible. The responsibility of regulating
concurrency level is on the users. There is also a flag to mark a
bound wq to ignore the concurrency management. Please refer to the
API section for details.
@@ -205,31 +208,22 @@
WQ_HIGHPRI
- Work items of a highpri wq are queued at the head of the
- worklist of the target gcwq and start execution regardless of
- the current concurrency level. In other words, highpri work
- items will always start execution as soon as execution
- resource is available.
+ Work items of a highpri wq are queued to the highpri
+ thread-pool of the target gcwq. Highpri thread-pools are
+ served by worker threads with elevated nice level.
- Ordering among highpri work items is preserved - a highpri
- work item queued after another highpri work item will start
- execution after the earlier highpri work item starts.
-
- Although highpri work items are not held back by other
- runnable work items, they still contribute to the concurrency
- level. Highpri work items in runnable state will prevent
- non-highpri work items from starting execution.
-
- This flag is meaningless for unbound wq.
+ Note that normal and highpri thread-pools don't interact with
+ each other. Each maintain its separate pool of workers and
+ implements concurrency management among its workers.
WQ_CPU_INTENSIVE
Work items of a CPU intensive wq do not contribute to the
concurrency level. In other words, runnable CPU intensive
- work items will not prevent other work items from starting
- execution. This is useful for bound work items which are
- expected to hog CPU cycles so that their execution is
- regulated by the system scheduler.
+ work items will not prevent other work items in the same
+ thread-pool from starting execution. This is useful for bound
+ work items which are expected to hog CPU cycles so that their
+ execution is regulated by the system scheduler.
Although CPU intensive work items don't contribute to the
concurrency level, start of their executions is still
@@ -239,14 +233,6 @@
This flag is meaningless for unbound wq.
- WQ_HIGHPRI | WQ_CPU_INTENSIVE
-
- This combination makes the wq avoid interaction with
- concurrency management completely and behave as a simple
- per-CPU execution context provider. Work items queued on a
- highpri CPU-intensive wq start execution as soon as resources
- are available and don't affect execution of other work items.
-
@max_active:
@max_active determines the maximum number of execution contexts per
@@ -328,20 +314,7 @@
35 w2 wakes up and finishes
Now, let's assume w1 and w2 are queued to a different wq q1 which has
-WQ_HIGHPRI set,
-
- TIME IN MSECS EVENT
- 0 w1 and w2 start and burn CPU
- 5 w1 sleeps
- 10 w2 sleeps
- 10 w0 starts and burns CPU
- 15 w0 sleeps
- 15 w1 wakes up and finishes
- 20 w2 wakes up and finishes
- 25 w0 wakes up and burns CPU
- 30 w0 finishes
-
-If q1 has WQ_CPU_INTENSIVE set,
+WQ_CPU_INTENSIVE set,
TIME IN MSECS EVENT
0 w0 starts and burns CPU
diff --git a/arch/arm/boot/dts/apq8084.dtsi b/arch/arm/boot/dts/apq8084.dtsi
index ba1ccd7..7a1bbef 100644
--- a/arch/arm/boot/dts/apq8084.dtsi
+++ b/arch/arm/boot/dts/apq8084.dtsi
@@ -10,7 +10,7 @@
* GNU General Public License for more details.
*/
-/include/ "skeleton.dtsi"
+/include/ "skeleton64.dtsi"
/ {
model = "Qualcomm APQ 8084";
@@ -24,7 +24,7 @@
&soc {
#address-cells = <1>;
#size-cells = <1>;
- ranges;
+ ranges = <0 0 0 0xffffffff>;
intc: interrupt-controller@f9000000 {
compatible = "qcom,msm-qgic2";
@@ -165,6 +165,42 @@
android_usb {
compatible = "qcom,android-usb";
};
+
+ qcom,ocmem@fdd00000 {
+ compatible = "qcom,msm-ocmem";
+ reg = <0xfdd00000 0x2000>,
+ <0xfdd02000 0x2000>,
+ <0xfe039000 0x400>,
+ <0xfec00000 0x200000>;
+ reg-names = "ocmem_ctrl_physical", "dm_ctrl_physical", "br_ctrl_physical", "ocmem_physical";
+ interrupts = <0 76 0 0 77 0>;
+ interrupt-names = "ocmem_irq", "dm_irq";
+ qcom,ocmem-num-regions = <0x4>;
+ qcom,ocmem-num-macros = <0x20>;
+ qcom,resource-type = <0x706d636f>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges = <0x0 0xfec00000 0x200000>;
+
+ partition@0 {
+ reg = <0x0 0x180000>;
+ qcom,ocmem-part-name = "graphics";
+ qcom,ocmem-part-min = <0x80000>;
+ };
+
+ partition@80000 {
+ reg = <0x180000 0x80000>;
+ qcom,ocmem-part-name = "lp_audio";
+ qcom,ocmem-part-min = <0x80000>;
+ };
+
+ partition@100000 {
+ reg = <0x180000 0x80000>;
+ qcom,ocmem-part-name = "video";
+ qcom,ocmem-part-min = <0x55000>;
+ };
+
+ };
};
/include/ "msm-pma8084.dtsi"
diff --git a/arch/arm/boot/dts/msm-iommu-v0.dtsi b/arch/arm/boot/dts/msm-iommu-v0.dtsi
index 1cc3c59..35829a7 100644
--- a/arch/arm/boot/dts/msm-iommu-v0.dtsi
+++ b/arch/arm/boot/dts/msm-iommu-v0.dtsi
@@ -30,6 +30,7 @@
status = "disabled";
lpass_q6_fw: qcom,iommu-ctx@fd000000 {
+ compatible = "qcom,msm-smmu-v0-ctx";
reg = <0xfd000000 0x1000>;
interrupts = <0 250 0>;
qcom,iommu-ctx-mids = <0 15>;
@@ -37,6 +38,7 @@
};
lpass_audio_shared: qcom,iommu-ctx@fd001000 {
+ compatible = "qcom,msm-smmu-v0-ctx";
reg = <0xfd001000 0x1000>;
interrupts = <0 250 0>;
qcom,iommu-ctx-mids = <1>;
@@ -44,6 +46,7 @@
};
lpass_video_shared: qcom,iommu-ctx@fd002000 {
+ compatible = "qcom,msm-smmu-v0-ctx";
reg = <0xfd002000 0x1000>;
interrupts = <0 250 0>;
qcom,iommu-ctx-mids = <2>;
@@ -51,6 +54,7 @@
};
lpass_q6_spare: qcom,iommu-ctx@fd003000 {
+ compatible = "qcom,msm-smmu-v0-ctx";
reg = <0xfd003000 0x1000>;
interrupts = <0 250 0>;
qcom,iommu-ctx-mids = <3 4 5 6 7 8 9 10 11 12 13 14>;
@@ -77,6 +81,7 @@
status = "disabled";
qcom,iommu-ctx@fd010000 {
+ compatible = "qcom,msm-smmu-v0-ctx";
reg = <0xfd010000 0x1000>;
interrupts = <0 254 0>;
qcom,iommu-ctx-mids = <0>;
@@ -84,6 +89,7 @@
};
qcom,iommu-ctx@fd011000 {
+ compatible = "qcom,msm-smmu-v0-ctx";
reg = <0xfd011000 0x1000>;
interrupts = <0 254 0>;
qcom,iommu-ctx-mids = <1>;
@@ -91,6 +97,7 @@
};
qcom,iommu-ctx@fd012000 {
+ compatible = "qcom,msm-smmu-v0-ctx";
reg = <0xfd012000 0x1000>;
interrupts = <0 254 0>;
qcom,iommu-ctx-mids = <2>;
@@ -98,6 +105,7 @@
};
qcom,iommu-ctx@fd013000 {
+ compatible = "qcom,msm-smmu-v0-ctx";
reg = <0xfd013000 0x1000>;
interrupts = <0 254 0>;
qcom,iommu-ctx-mids = <3>;
@@ -105,6 +113,7 @@
};
qcom,iommu-ctx@fd014000 {
+ compatible = "qcom,msm-smmu-v0-ctx";
reg = <0xfd014000 0x1000>;
interrupts = <0 254 0>;
qcom,iommu-ctx-mids = <4>;
@@ -112,6 +121,7 @@
};
qcom,iommu-ctx@fd015000 {
+ compatible = "qcom,msm-smmu-v0-ctx";
reg = <0xfd015000 0x1000>;
interrupts = <0 254 0>;
qcom,iommu-ctx-mids = <5>;
@@ -119,6 +129,7 @@
};
qcom,iommu-ctx@fd016000 {
+ compatible = "qcom,msm-smmu-v0-ctx";
reg = <0xfd016000 0x1000>;
interrupts = <0 254 0>;
qcom,iommu-ctx-mids = <6>;
@@ -126,6 +137,7 @@
};
qcom,iommu-ctx@fd017000 {
+ compatible = "qcom,msm-smmu-v0-ctx";
reg = <0xfd017000 0x1000>;
interrupts = <0 254 0>;
qcom,iommu-ctx-mids = <7>;
@@ -152,6 +164,7 @@
status = "disabled";
qcom,iommu-ctx@fd860000 {
+ compatible = "qcom,msm-smmu-v0-ctx";
reg = <0xfd860000 0x1000>;
interrupts = <0 247 0>;
qcom,iommu-ctx-mids = <0 1 3>;
@@ -159,6 +172,7 @@
};
qcom,iommu-ctx@fd861000 {
+ compatible = "qcom,msm-smmu-v0-ctx";
reg = <0xfd861000 0x1000>;
interrupts = <0 247 0>;
qcom,iommu-ctx-mids = <2>;
@@ -185,6 +199,7 @@
status = "disabled";
qcom,iommu-ctx@fd870000 {
+ compatible = "qcom,msm-smmu-v0-ctx";
reg = <0xfd870000 0x1000>;
interrupts = <0 47 0>;
qcom,iommu-ctx-mids = <0>;
@@ -192,6 +207,7 @@
};
qcom,iommu-ctx@fd871000 {
+ compatible = "qcom,msm-smmu-v0-ctx";
reg = <0xfd871000 0x1000>;
interrupts = <0 47 0>;
qcom,iommu-ctx-mids = <1>;
@@ -207,6 +223,7 @@
reg = <0xfd880000 0x10000>;
interrupts = <0 38 0>;
qcom,glb-offset = <0xF000>;
+ qcom,needs-alt-core-clk;
label = "gfx_iommu";
qcom,iommu-pmu-ngroups = <1>;
qcom,iommu-pmu-ncounters = <4>;
@@ -218,6 +235,7 @@
status = "disabled";
qcom,iommu-ctx@fd880000 {
+ compatible = "qcom,msm-smmu-v0-ctx";
reg = <0xfd880000 0x1000>;
interrupts = <0 241 0>;
qcom,iommu-ctx-mids = <0 1 2 3 4 5 6 7 8 9 10 11 12 13
@@ -226,6 +244,7 @@
};
qcom,iommu-ctx@fd881000 {
+ compatible = "qcom,msm-smmu-v0-ctx";
reg = <0xfd881000 0x1000>;
interrupts = <0 241 0>;
qcom,iommu-ctx-mids = <16 17 18 19 20 21 22 23 24 25
@@ -234,6 +253,7 @@
};
qcom,iommu-ctx@fd882000 {
+ compatible = "qcom,msm-smmu-v0-ctx";
reg = <0xfd882000 0x1000>;
interrupts = <0 241 0>;
qcom,iommu-ctx-mids = <>;
@@ -260,6 +280,7 @@
status = "disabled";
qcom,iommu-ctx@fd890000 {
+ compatible = "qcom,msm-smmu-v0-ctx";
reg = <0xfd890000 0x1000>;
interrupts = <0 65 0>;
qcom,iommu-ctx-mids = <0>;
@@ -267,6 +288,7 @@
};
qcom,iommu-ctx@fd891000 {
+ compatible = "qcom,msm-smmu-v0-ctx";
reg = <0xfd891000 0x1000>;
interrupts = <0 65 0>;
qcom,iommu-ctx-mids = <1>;
diff --git a/arch/arm/boot/dts/msm-iommu-v1.dtsi b/arch/arm/boot/dts/msm-iommu-v1.dtsi
index f495850..ab46861 100644
--- a/arch/arm/boot/dts/msm-iommu-v1.dtsi
+++ b/arch/arm/boot/dts/msm-iommu-v1.dtsi
@@ -77,6 +77,7 @@
0x0>;
qcom,iommu-ctx@fda6c000 {
+ compatible = "qcom,msm-smmu-v1-ctx";
reg = <0xfda6c000 0x1000>;
interrupts = <0 70 0>;
qcom,iommu-ctx-sids = <0>;
@@ -84,6 +85,7 @@
};
qcom,iommu-ctx@fda6d000 {
+ compatible = "qcom,msm-smmu-v1-ctx";
reg = <0xfda6d000 0x1000>;
interrupts = <0 70 0>;
qcom,iommu-ctx-sids = <1>;
@@ -91,6 +93,7 @@
};
qcom,iommu-ctx@fda6e000 {
+ compatible = "qcom,msm-smmu-v1-ctx";
reg = <0xfda6e000 0x1000>;
interrupts = <0 70 0>;
qcom,iommu-ctx-sids = <2>;
@@ -170,6 +173,7 @@
0x0>;
qcom,iommu-ctx@fd930000 {
+ compatible = "qcom,msm-smmu-v1-ctx";
reg = <0xfd930000 0x1000>;
interrupts = <0 47 0>;
qcom,iommu-ctx-sids = <0>;
@@ -177,6 +181,7 @@
};
qcom,iommu-ctx@fd931000 {
+ compatible = "qcom,msm-smmu-v1-ctx";
reg = <0xfd931000 0x1000>;
interrupts = <0 47 0>;
qcom,iommu-ctx-sids = <1>;
@@ -185,6 +190,7 @@
};
qcom,iommu-ctx@fd932000 {
+ compatible = "qcom,msm-smmu-v1-ctx";
reg = <0xfd932000 0x1000>;
interrupts = <0 47 0>;
qcom,iommu-ctx-sids = <>;
@@ -279,6 +285,7 @@
0x0>;
venus_ns: qcom,iommu-ctx@fdc8c000 {
+ compatible = "qcom,msm-smmu-v1-ctx";
reg = <0xfdc8c000 0x1000>;
interrupts = <0 42 0>;
qcom,iommu-ctx-sids = <0 1 2 3 4 5>;
@@ -286,6 +293,7 @@
};
venus_cp: qcom,iommu-ctx@fdc8d000 {
+ compatible = "qcom,msm-smmu-v1-ctx";
reg = <0xfdc8d000 0x1000>;
interrupts = <0 42 0>;
qcom,iommu-ctx-sids = <0x80 0x81 0x82 0x83 0x84 0x85>;
@@ -294,6 +302,7 @@
};
venus_fw: qcom,iommu-ctx@fdc8e000 {
+ compatible = "qcom,msm-smmu-v1-ctx";
reg = <0xfdc8e000 0x1000>;
interrupts = <0 42 0>;
qcom,iommu-ctx-sids = <0xc0 0xc6>;
@@ -363,6 +372,7 @@
0x0>;
qcom,iommu-ctx@fdb18000 {
+ compatible = "qcom,msm-smmu-v1-ctx";
reg = <0xfdb18000 0x1000>;
interrupts = <0 241 0>;
qcom,iommu-ctx-sids = <0>;
@@ -370,6 +380,7 @@
};
qcom,iommu-ctx@fdb19000 {
+ compatible = "qcom,msm-smmu-v1-ctx";
reg = <0xfdb19000 0x1000>;
interrupts = <0 241 0>;
qcom,iommu-ctx-sids = <1>;
@@ -449,6 +460,7 @@
0x0>;
qcom,iommu-ctx@fda4c000 {
+ compatible = "qcom,msm-smmu-v1-ctx";
reg = <0xfda4c000 0x1000>;
interrupts = <0 65 0>;
qcom,iommu-ctx-sids = <0>;
@@ -456,6 +468,7 @@
};
qcom,iommu-ctx@fda4d000 {
+ compatible = "qcom,msm-smmu-v1-ctx";
reg = <0xfda4d000 0x1000>;
interrupts = <0 65 0>;
qcom,iommu-ctx-sids = <1>;
@@ -463,6 +476,7 @@
};
qcom,iommu-ctx@fda4e000 {
+ compatible = "qcom,msm-smmu-v1-ctx";
reg = <0xfda4e000 0x1000>;
interrupts = <0 65 0>;
qcom,iommu-ctx-sids = <2>;
diff --git a/arch/arm/boot/dts/msm-pm8019.dtsi b/arch/arm/boot/dts/msm-pm8019.dtsi
index 689bf0f..fad9d86 100755
--- a/arch/arm/boot/dts/msm-pm8019.dtsi
+++ b/arch/arm/boot/dts/msm-pm8019.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2013, 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
@@ -196,6 +196,21 @@
qcom,fast-avg-setup = <0>;
};
};
+
+ pm8019_adc_tm: vadc@3400 {
+ compatible = "qcom,qpnp-adc-tm";
+ reg = <0x3400 0x100>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ interrupts = <0x0 0x34 0x0>,
+ <0x0 0x34 0x3>,
+ <0x0 0x34 0x4>;
+ interrupt-names = "eoc-int-en-set",
+ "high-thr-en-set",
+ "low-thr-en-set";
+ qcom,adc-bit-resolution = <15>;
+ qcom,adc-vdd-reference = <1800>;
+ };
};
qcom,pm8019@1 {
diff --git a/arch/arm/boot/dts/msm-pm8110.dtsi b/arch/arm/boot/dts/msm-pm8110.dtsi
index 391c564..e1f0e61 100644
--- a/arch/arm/boot/dts/msm-pm8110.dtsi
+++ b/arch/arm/boot/dts/msm-pm8110.dtsi
@@ -22,6 +22,29 @@
#address-cells = <1>;
#size-cells = <1>;
+ qcom,power-on@800 {
+ compatible = "qcom,qpnp-power-on";
+ reg = <0x800 0x100>;
+ interrupts = <0x0 0x8 0x0>,
+ <0x0 0x8 0x1>,
+ <0x0 0x8 0x4>;
+ interrupt-names = "kpdpwr", "resin", "resin-bark";
+ qcom,pon-dbc-delay = <15625>;
+ qcom,system-reset;
+
+ qcom,pon_1 {
+ qcom,pon-type = <0>;
+ qcom,pull-up = <1>;
+ linux,code = <116>;
+ };
+
+ qcom,pon_2 {
+ qcom,pon-type = <1>;
+ qcom,pull-up = <1>;
+ linux,code = <114>;
+ };
+ };
+
pm8110_chg: qcom,charger {
spmi-dev-container;
compatible = "qcom,qpnp-charger";
@@ -242,6 +265,58 @@
};
};
+ pm8110_bms: qcom,bms {
+ spmi-dev-container;
+ compatible = "qcom,qpnp-bms";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ status = "disabled";
+
+ qcom,r-sense-uohm = <10000>;
+ qcom,v-cutoff-uv = <3400000>;
+ qcom,max-voltage-uv = <4200000>;
+ qcom,r-conn-mohm = <0>;
+ qcom,shutdown-soc-valid-limit = <20>;
+ qcom,adjust-soc-low-threshold = <15>;
+ qcom,ocv-voltage-high-threshold-uv = <3750000>;
+ qcom,ocv-voltage-low-threshold-uv = <3650000>;
+ qcom,low-soc-calculate-soc-threshold = <15>;
+ qcom,low-soc-calculate-soc-ms = <5000>;
+ qcom,calculate-soc-ms = <20000>;
+ qcom,chg-term-ua = <100000>;
+ qcom,batt-type = <0>;
+ qcom,low-voltage-threshold = <3420000>;
+ qcom,tm-temp-margin = <5000>;
+ qcom,low-ocv-correction-limit-uv = <100>;
+ qcom,high-ocv-correction-limit-uv = <50>;
+ qcom,hold-soc-est = <3>;
+
+ qcom,bms-iadc@3800 {
+ reg = <0x3800 0x100>;
+ };
+
+ qcom,bms-bms@4000 {
+ reg = <0x4000 0x100>;
+ interrupts = <0x0 0x40 0x0>,
+ <0x0 0x40 0x1>,
+ <0x0 0x40 0x2>,
+ <0x0 0x40 0x3>,
+ <0x0 0x40 0x4>,
+ <0x0 0x40 0x5>,
+ <0x0 0x40 0x6>,
+ <0x0 0x40 0x7>;
+
+ interrupt-names = "vsense_for_r",
+ "vsense_avg",
+ "sw_cc_thr",
+ "ocv_thr",
+ "charge_begin",
+ "good_ocv",
+ "ocv_for_r",
+ "cc_thr";
+ };
+ };
+
qcom,pm8110_rtc {
spmi-dev-container;
compatible = "qcom,qpnp-rtc";
diff --git a/arch/arm/boot/dts/msm-pm8226.dtsi b/arch/arm/boot/dts/msm-pm8226.dtsi
index 41920d5..0db886b 100644
--- a/arch/arm/boot/dts/msm-pm8226.dtsi
+++ b/arch/arm/boot/dts/msm-pm8226.dtsi
@@ -61,7 +61,7 @@
qcom,vinmin-mv = <4200>;
qcom,vbatdet-delta-mv = <150>;
qcom,ibatmax-ma = <1500>;
- qcom,ibatterm-ma = <200>;
+ qcom,ibatterm-ma = <100>;
qcom,ibatsafe-ma = <1500>;
qcom,thermal-mitigation = <1500 700 600 325>;
qcom,tchg-mins = <150>;
@@ -173,6 +173,7 @@
qcom,calculate-soc-ms = <20000>;
qcom,chg-term-ua = <100000>;
qcom,batt-type = <0>;
+ qcom,tm-temp-margin = <5000>;
qcom,low-ocv-correction-limit-uv = <100>;
qcom,high-ocv-correction-limit-uv = <50>;
qcom,hold-soc-est = <3>;
diff --git a/arch/arm/boot/dts/msm-pm8941.dtsi b/arch/arm/boot/dts/msm-pm8941.dtsi
index eadd5d3..34ea33d 100644
--- a/arch/arm/boot/dts/msm-pm8941.dtsi
+++ b/arch/arm/boot/dts/msm-pm8941.dtsi
@@ -116,6 +116,7 @@
qcom,chg-term-ua = <100000>;
qcom,batt-type = <0>;
qcom,low-voltage-threshold = <3420000>;
+ qcom,tm-temp-margin = <5000>;
qcom,low-ocv-correction-limit-uv = <100>;
qcom,high-ocv-correction-limit-uv = <50>;
qcom,hold-soc-est = <3>;
@@ -173,8 +174,9 @@
qcom,vddmax-mv = <4200>;
qcom,vddsafe-mv = <4200>;
- qcom,vinmin-mv = <4200>;
+ qcom,vinmin-mv = <4300>;
qcom,ibatmax-ma = <1500>;
+ qcom,ibatterm-ma = <100>;
qcom,ibatsafe-ma = <1500>;
qcom,thermal-mitigation = <1500 700 600 325>;
qcom,cool-bat-decidegc = <100>;
@@ -183,7 +185,7 @@
qcom,warm-bat-decidegc = <450>;
qcom,warm-bat-mv = <4100>;
qcom,ibatmax-cool-ma = <350>;
- qcom,vbatdet-delta-mv = <350>;
+ qcom,vbatdet-delta-mv = <100>;
qcom,tchg-mins = <150>;
qcom,chgr@1000 {
@@ -584,10 +586,10 @@
};
chan@3 {
- label = "spare1";
+ label = "spare1_div3";
reg = <3>;
qcom,decimation = <0>;
- qcom,pre-div-channel-scaling = <6>;
+ qcom,pre-div-channel-scaling = <1>;
qcom,calibration-type = "absolute";
qcom,scale-function = <0>;
qcom,hw-settle-time = <0>;
@@ -595,10 +597,10 @@
};
chan@4 {
- label = "spare2";
+ label = "usb_id_mv";
reg = <4>;
qcom,decimation = <0>;
- qcom,pre-div-channel-scaling = <6>;
+ qcom,pre-div-channel-scaling = <1>;
qcom,calibration-type = "absolute";
qcom,scale-function = <0>;
qcom,hw-settle-time = <0>;
@@ -872,7 +874,7 @@
reg = <0xb5>;
qcom,decimation = <0>;
qcom,pre-div-channel-scaling = <0>;
- qcom,calibration-type = "absolute";
+ qcom,calibration-type = "ratiometric";
qcom,scale-function = <2>;
qcom,hw-settle-time = <2>;
qcom,fast-avg-setup = <3>;
diff --git a/arch/arm/boot/dts/msm8226-cdp.dts b/arch/arm/boot/dts/msm8226-cdp.dts
index f887740..916345c 100644
--- a/arch/arm/boot/dts/msm8226-cdp.dts
+++ b/arch/arm/boot/dts/msm8226-cdp.dts
@@ -116,6 +116,7 @@
qcom,cdc-mclk-gpios = <&pm8226_gpios 1 0>;
qcom,cdc-vdd-spkr-gpios = <&pm8226_gpios 2 0>;
+ qcom,headset-jack-type-NO;
};
};
diff --git a/arch/arm/boot/dts/msm8226-coresight.dtsi b/arch/arm/boot/dts/msm8226-coresight.dtsi
index e4a42fa..7c19bc0 100644
--- a/arch/arm/boot/dts/msm8226-coresight.dtsi
+++ b/arch/arm/boot/dts/msm8226-coresight.dtsi
@@ -355,4 +355,18 @@
coresight-name = "coresight-cti-cpu3";
coresight-nr-inports = <0>;
};
+
+ hwevent: hwevent@fd828018 {
+ compatible = "qcom,coresight-hwevent";
+ reg = <0xfd828018 0x80>,
+ <0xf9011080 0x80>,
+ <0xfd4ab160 0x80>;
+ reg-names = "mmss-mux", "apcs-mux", "ppss-mux";
+
+ coresight-id = <29>;
+ coresight-name = "coresight-hwevent";
+ coresight-nr-inports = <0>;
+
+ qcom,hwevent-clks = "core_mmss_clk";
+ };
};
diff --git a/arch/arm/boot/dts/msm8226-mtp.dts b/arch/arm/boot/dts/msm8226-mtp.dts
index 3dd517b..589fe69 100644
--- a/arch/arm/boot/dts/msm8226-mtp.dts
+++ b/arch/arm/boot/dts/msm8226-mtp.dts
@@ -365,3 +365,10 @@
&pm8226_chg {
qcom,charging-disabled;
};
+
+&slim_msm {
+ tapan_codec {
+ qcom,cdc-micbias1-ext-cap;
+ qcom,cdc-micbias2-ext-cap;
+ };
+};
diff --git a/arch/arm/boot/dts/msm8226-pm.dtsi b/arch/arm/boot/dts/msm8226-pm.dtsi
index 8a1ac1d..3240efb 100644
--- a/arch/arm/boot/dts/msm8226-pm.dtsi
+++ b/arch/arm/boot/dts/msm8226-pm.dtsi
@@ -153,10 +153,10 @@
qcom,mode = "wfi";
qcom,xo = "xo_on";
qcom,l2 = "l2_cache_active";
- qcom,vdd-mem-upper-bound = <5>; /* SUPER TURBO */
- qcom,vdd-mem-lower-bound = <3>; /* NORMAL */
- qcom,vdd-dig-upper-bound = <5>; /* SUPER TURBO */
- qcom,vdd-dig-lower-bound = <3>; /* NORMAL */
+ qcom,vdd-mem-upper-bound = <6>; /* SUPER TURBO */
+ qcom,vdd-mem-lower-bound = <4>; /* NORMAL */
+ qcom,vdd-dig-upper-bound = <6>; /* SUPER TURBO */
+ qcom,vdd-dig-lower-bound = <4>; /* NORMAL */
qcom,irqs-detectable;
qcom,gpio-detectable;
qcom,latency-us = <1>;
@@ -170,10 +170,10 @@
qcom,mode = "standalone_pc";
qcom,xo = "xo_on";
qcom,l2 = "l2_cache_active";
- qcom,vdd-mem-upper-bound = <5>; /* SUPER TURBO */
- qcom,vdd-mem-lower-bound = <3>; /* NORMAL */
- qcom,vdd-dig-upper-bound = <5>; /* SUPER TURBO */
- qcom,vdd-dig-lower-bound = <3>; /* NORMAL */
+ qcom,vdd-mem-upper-bound = <6>; /* SUPER TURBO */
+ qcom,vdd-mem-lower-bound = <4>; /* NORMAL */
+ qcom,vdd-dig-upper-bound = <6>; /* SUPER TURBO */
+ qcom,vdd-dig-lower-bound = <4>; /* NORMAL */
qcom,irqs-detectable;
qcom,gpio-detectable;
qcom,latency-us = <3000>;
@@ -187,10 +187,10 @@
qcom,mode = "pc";
qcom,xo = "xo_on";
qcom,l2 = "l2_cache_retention";
- qcom,vdd-mem-upper-bound = <5>; /* SUPER TURBO */
- qcom,vdd-mem-lower-bound = <3>; /* NORMAL */
- qcom,vdd-dig-upper-bound = <5>; /* SUPER TURBO */
- qcom,vdd-dig-lower-bound = <3>; /* NORMAL */
+ qcom,vdd-mem-upper-bound = <6>; /* SUPER TURBO */
+ qcom,vdd-mem-lower-bound = <4>; /* NORMAL */
+ qcom,vdd-dig-upper-bound = <6>; /* SUPER TURBO */
+ qcom,vdd-dig-lower-bound = <4>; /* NORMAL */
qcom,irqs-detectable;
qcom,gpio-detectable;
qcom,latency-us = <8000>;
@@ -204,10 +204,10 @@
qcom,mode = "pc";
qcom,xo = "xo_on";
qcom,l2 = "l2_cache_pc";
- qcom,vdd-mem-upper-bound = <3>; /* NORMAL */
- qcom,vdd-mem-lower-bound = <2>; /* SVS SOC */
- qcom,vdd-dig-upper-bound = <3>; /* NORMAL */
- qcom,vdd-dig-lower-bound = <2>; /* SVS SOC */
+ qcom,vdd-mem-upper-bound = <4>; /* NORMAL */
+ qcom,vdd-mem-lower-bound = <3>; /* SVS SOC */
+ qcom,vdd-dig-upper-bound = <4>; /* NORMAL */
+ qcom,vdd-dig-lower-bound = <3>; /* SVS SOC */
qcom,irqs-detectable;
qcom,gpio-detectable;
qcom,latency-us = <9000>;
@@ -221,10 +221,10 @@
qcom,mode = "pc";
qcom,xo = "xo_off";
qcom,l2 = "l2_cache_pc";
- qcom,vdd-mem-upper-bound = <5>; /* SUPER TURBO */
- qcom,vdd-mem-lower-bound = <3>; /* NORMAL */
- qcom,vdd-dig-upper-bound = <5>; /* SUPER TURBO */
- qcom,vdd-dig-lower-bound = <3>; /* NORMAL */
+ qcom,vdd-mem-upper-bound = <6>; /* SUPER TURBO */
+ qcom,vdd-mem-lower-bound = <4>; /* NORMAL */
+ qcom,vdd-dig-upper-bound = <6>; /* SUPER TURBO */
+ qcom,vdd-dig-lower-bound = <4>; /* NORMAL */
qcom,latency-us = <16300>;
qcom,ss-power = <63>;
qcom,energy-overhead = <2128000>;
@@ -236,10 +236,10 @@
qcom,mode = "pc";
qcom,xo = "xo_off";
qcom,l2 = "l2_cache_pc";
- qcom,vdd-mem-upper-bound = <3>; /* NORMAL */
- qcom,vdd-mem-lower-bound = <2>; /* SVS SOC */
- qcom,vdd-dig-upper-bound = <3>; /* NORMAL */
- qcom,vdd-dig-lower-bound = <2>; /* SVS SOC */
+ qcom,vdd-mem-upper-bound = <4>; /* NORMAL */
+ qcom,vdd-mem-lower-bound = <3>; /* SVS SOC */
+ qcom,vdd-dig-upper-bound = <4>; /* NORMAL */
+ qcom,vdd-dig-lower-bound = <3>; /* SVS SOC */
qcom,latency-us = <24000>;
qcom,ss-power = <10>;
qcom,energy-overhead = <3202600>;
@@ -251,10 +251,10 @@
qcom,mode = "pc";
qcom,xo = "xo_off";
qcom,l2 = "l2_cache_pc";
- qcom,vdd-mem-upper-bound = <2>; /* SVS SOC */
- qcom,vdd-mem-lower-bound = <0>; /* RETENTION */
- qcom,vdd-dig-upper-bound = <2>; /* SVS SOC */
- qcom,vdd-dig-lower-bound = <0>; /* RETENTION */
+ qcom,vdd-mem-upper-bound = <3>; /* SVS SOC */
+ qcom,vdd-mem-lower-bound = <1>; /* RETENTION */
+ qcom,vdd-dig-upper-bound = <3>; /* SVS SOC */
+ qcom,vdd-dig-lower-bound = <1>; /* RETENTION */
qcom,latency-us = <26000>;
qcom,ss-power = <2>;
qcom,energy-overhead = <4252000>;
diff --git a/arch/arm/boot/dts/msm8226-qrd.dts b/arch/arm/boot/dts/msm8226-qrd.dts
index 721bcbb..f917d45 100644
--- a/arch/arm/boot/dts/msm8226-qrd.dts
+++ b/arch/arm/boot/dts/msm8226-qrd.dts
@@ -331,3 +331,10 @@
mpp@a700 { /* MPP 8 */
};
};
+
+&slim_msm {
+ tapan_codec {
+ qcom,cdc-micbias1-ext-cap;
+ };
+
+};
diff --git a/arch/arm/boot/dts/msm8226-regulator.dtsi b/arch/arm/boot/dts/msm8226-regulator.dtsi
index 7fa9081..6aeaf49 100644
--- a/arch/arm/boot/dts/msm8226-regulator.dtsi
+++ b/arch/arm/boot/dts/msm8226-regulator.dtsi
@@ -30,23 +30,37 @@
apc_vreg_corner: regulator@f9018000 {
status = "okay";
compatible = "qcom,cpr-regulator";
- reg = <0xf9018000 0x1000>,
- <0xfc4b80b0 8>;
- reg-names = "rbcpr", "efuse_phys";
+ reg = <0xf9018000 0x1000>, <0xfc4b80b0 8>, <0xfc4bc450 16>;
+ reg-names = "rbcpr", "pvs_efuse", "cpr_efuse";
+ interrupts = <0 15 0>;
regulator-name = "apc_corner";
regulator-min-microvolt = <1>;
- regulator-max-microvolt = <4>;
+ regulator-max-microvolt = <3>;
qcom,num-efuse-bits = <5>;
- qcom,efuse-bit-pos = <6 7 8 9 10>;
- qcom,pvs-bin-process = <0 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2
+ qcom,pvs-bin-process = <0 0 0 0 0 1 1 1 1 1 2 2 2 2 2 2
2 2 2 2 3 3 3 3 3 3 3 3 0 0 0 0>;
- qcom,pvs-corner-ceiling-slow = <1050000 1150000 1275000 1350000>;
- qcom,pvs-corner-ceiling-nom = <975000 1075000 1200000 1200000>;
- qcom,pvs-corner-ceiling-fast = <900000 1000000 1140000 1140000>;
+ qcom,pvs-corner-ceiling-slow = <1155000 1160000 1275000>;
+ qcom,pvs-corner-ceiling-nom = <975000 1075000 1200000>;
+ qcom,pvs-corner-ceiling-fast = <900000 1000000 1140000>;
vdd-apc-supply = <&pm8226_s2>;
+
vdd-mx-supply = <&pm8226_l3_ao>;
qcom,vdd-mx-vmax = <1350000>;
qcom,vdd-mx-vmin-method = <1>;
+
+ qcom,cpr-ref-clk = <19200>;
+ qcom,cpr-timer-delay = <5000>;
+ qcom,cpr-timer-cons-up = <1>;
+ qcom,cpr-timer-cons-down = <2>;
+ qcom,cpr-irq-line = <0>;
+ qcom,cpr-step-quotient = <15>;
+ qcom,cpr-up-threshold = <1>;
+ qcom,cpr-down-threshold = <2>;
+ qcom,cpr-idle-clocks = <5>;
+ qcom,cpr-gcnt-time = <1>;
+ qcom,vdd-apc-step-up-limit = <1>;
+ qcom,vdd-apc-step-down-limit = <1>;
+ qcom,cpr-apc-volt-step = <5000>;
};
};
diff --git a/arch/arm/boot/dts/msm8226.dtsi b/arch/arm/boot/dts/msm8226.dtsi
index 6c06336..b2933db 100644
--- a/arch/arm/boot/dts/msm8226.dtsi
+++ b/arch/arm/boot/dts/msm8226.dtsi
@@ -282,7 +282,7 @@
interrupt-names = "cdc-int";
};
- slim@fe12f000 {
+ slim_msm: slim@fe12f000 {
cell-index = <1>;
compatible = "qcom,slim-ngd";
reg = <0xfe12f000 0x35000>,
@@ -350,6 +350,11 @@
compatible = "qcom,msm8226-audio-tapan";
qcom,model = "msm8226-tapan-snd-card";
qcom,tapan-mclk-clk-freq = <9600000>;
+ qcom,prim-auxpcm-gpio-clk = <&msmgpio 63 0>;
+ qcom,prim-auxpcm-gpio-sync = <&msmgpio 64 0>;
+ qcom,prim-auxpcm-gpio-din = <&msmgpio 65 0>;
+ qcom,prim-auxpcm-gpio-dout = <&msmgpio 66 0>;
+ qcom,prim-auxpcm-gpio-set = "prim-gpio-prim";
};
qcom,msm-pcm {
@@ -502,6 +507,28 @@
compatible = "qcom,msm-pcm-hostless";
};
+ qcom,msm-auxpcm {
+ compatible = "qcom,msm-auxpcm-resource";
+ qcom,msm-cpudai-auxpcm-clk = "pcm_clk";
+ qcom,msm-cpudai-auxpcm-mode = <0>, <0>;
+ qcom,msm-cpudai-auxpcm-sync = <1>, <1>;
+ qcom,msm-cpudai-auxpcm-frame = <5>, <4>;
+ qcom,msm-cpudai-auxpcm-quant = <2>, <2>;
+ qcom,msm-cpudai-auxpcm-slot = <1>, <1>;
+ qcom,msm-cpudai-auxpcm-data = <0>, <0>;
+ qcom,msm-cpudai-auxpcm-pcm-clk-rate = <2048000>, <2048000>;
+
+ qcom,msm-prim-auxpcm-rx {
+ qcom,msm-auxpcm-dev-id = <4106>;
+ compatible = "qcom,msm-auxpcm-dev";
+ };
+
+ qcom,msm-prim-auxpcm-tx {
+ qcom,msm-auxpcm-dev-id = <4107>;
+ compatible = "qcom,msm-auxpcm-dev";
+ };
+ };
+
qcom,wcnss-wlan@fb000000 {
compatible = "qcom,wcnss_wlan";
reg = <0xfb000000 0x280000>,
@@ -784,6 +811,7 @@
/* GPIO inputs from lpass */
qcom,gpio-err-fatal = <&smp2pgpio_ssr_smp2p_2_in 0 0>;
qcom,gpio-proxy-unvote = <&smp2pgpio_ssr_smp2p_2_in 2 0>;
+ qcom,gpio-err-ready = <&smp2pgpio_ssr_smp2p_2_in 1 0>;
/* GPIO output to lpass */
qcom,gpio-force-stop = <&smp2pgpio_ssr_smp2p_2_out 0 0>;
@@ -800,7 +828,6 @@
"restart_reg", "cxrail_bhs_reg";
interrupts = <0 24 1>;
- vdd_mss-supply = <&pm8226_s1>;
vdd_cx-supply = <&pm8226_s1_corner>;
vdd_mx-supply = <&pm8226_l3>;
vdd_pll-supply = <&pm8226_l8>;
@@ -980,22 +1007,27 @@
};
&gdsc_venus {
+ qcom,clock-names = "core_clk";
status = "ok";
};
&gdsc_mdss {
+ qcom,clock-names = "core_clk", "lut_clk";
status = "ok";
};
&gdsc_jpeg {
+ qcom,clock-names = "core_clk";
status = "ok";
};
&gdsc_vfe {
+ qcom,clock-names = "core_clk", "csi_clk", "cpp_clk";
status = "ok";
};
&gdsc_oxili_cx {
+ qcom,clock-names = "core_clk";
status = "ok";
};
diff --git a/arch/arm/boot/dts/msm8610-cdp.dts b/arch/arm/boot/dts/msm8610-cdp.dts
index 28e9dc5..d3fc917 100644
--- a/arch/arm/boot/dts/msm8610-cdp.dts
+++ b/arch/arm/boot/dts/msm8610-cdp.dts
@@ -88,6 +88,49 @@
};
};
};
+
+ gen-vkeys {
+ compatible = "qcom,gen-vkeys";
+ label = "atmel_mxt_ts";
+ qcom,disp-maxx = <480>;
+ qcom,disp-maxy = <800>;
+ qcom,panel-maxx = <508>;
+ qcom,panel-maxy = <880>;
+ qcom,key-codes = <158 102 139>;
+ qcom,y-offset = <35>;
+ };
+
+ gpio_keys {
+ compatible = "gpio-keys";
+ input-name = "gpio-keys";
+
+ camera_snapshot {
+ label = "camera_snapshot";
+ gpios = <&msmgpio 73 0x1>;
+ linux,input-type = <1>;
+ linux,code = <0x2fe>;
+ gpio-key,wakeup;
+ debounce-interval = <15>;
+ };
+
+ camera_focus {
+ label = "camera_focus";
+ gpios = <&msmgpio 74 0x1>;
+ linux,input-type = <1>;
+ linux,code = <0x210>;
+ gpio-key,wakeup;
+ debounce-interval = <15>;
+ };
+
+ vol_up {
+ label = "volume_up";
+ gpios = <&msmgpio 72 0x1>;
+ linux,input-type = <1>;
+ linux,code = <115>;
+ gpio-key,wakeup;
+ debounce-interval = <15>;
+ };
+ };
};
&i2c_cdc {
@@ -156,7 +199,7 @@
qcom,led_mpp_3 {
label = "mpp";
linux,name = "wled-backlight";
- linux-default-trigger = "none";
+ linux,default-trigger = "bkl-trigger";
qcom,default-state = "on";
qcom,max-current = <40>;
qcom,id = <6>;
@@ -165,38 +208,6 @@
};
};
};
-
- gpio_keys {
- compatible = "gpio-keys";
- input-name = "gpio-keys";
-
- camera_snapshot {
- label = "camera_snapshot";
- gpios = <&msmgpio 73 0x1>;
- linux,input-type = <1>;
- linux,code = <0x2fe>;
- gpio-key,wakeup;
- debounce-interval = <15>;
- };
-
- camera_focus {
- label = "camera_focus";
- gpios = <&msmgpio 74 0x1>;
- linux,input-type = <1>;
- linux,code = <0x210>;
- gpio-key,wakeup;
- debounce-interval = <15>;
- };
-
- vol_up {
- label = "volume_up";
- gpios = <&msmgpio 72 0x1>;
- linux,input-type = <1>;
- linux,code = <115>;
- gpio-key,wakeup;
- debounce-interval = <15>;
- };
- };
};
&spmi_bus {
@@ -316,3 +327,52 @@
mpp@a300 { /* MPP 4 */
};
};
+
+/* CoreSight */
+&tpiu {
+ qcom,seta-gpios = <&msmgpio 4 0>,
+ <&msmgpio 5 0>,
+ <&msmgpio 6 0>,
+ <&msmgpio 7 0>,
+ <&msmgpio 22 0>,
+ <&msmgpio 23 0>,
+ <&msmgpio 24 0>,
+ <&msmgpio 25 0>,
+ <&msmgpio 26 0>,
+ <&msmgpio 27 0>,
+ <&msmgpio 28 0>,
+ <&msmgpio 29 0>,
+ <&msmgpio 30 0>,
+ <&msmgpio 31 0>,
+ <&msmgpio 94 0>,
+ <&msmgpio 95 0>,
+ <&msmgpio 96 0>,
+ <&msmgpio 97 0>;
+ qcom,seta-gpios-func = <9 9 8 11 2 2 2 2 2 2 3 2 3 3 4 4 4 4>;
+ qcom,seta-gpios-drv = <7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7>;
+ qcom,seta-gpios-pull = <0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0>;
+ qcom,seta-gpios-dir = <2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2>;
+
+ qcom,setb-gpios = <&msmgpio 8 0>,
+ <&msmgpio 10 0>,
+ <&msmgpio 11 0>,
+ <&msmgpio 13 0>,
+ <&msmgpio 14 0>,
+ <&msmgpio 15 0>,
+ <&msmgpio 16 0>,
+ <&msmgpio 17 0>,
+ <&msmgpio 18 0>,
+ <&msmgpio 19 0>,
+ <&msmgpio 20 0>,
+ <&msmgpio 21 0>,
+ <&msmgpio 42 0>,
+ <&msmgpio 80 0>,
+ <&msmgpio 81 0>,
+ <&msmgpio 82 0>,
+ <&msmgpio 83 0>,
+ <&msmgpio 84 0>;
+ qcom,setb-gpios-func = <10 8 8 6 9 9 9 9 9 9 9 9 5 7 7 8 8 8>;
+ qcom,setb-gpios-drv = <7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7>;
+ qcom,setb-gpios-pull = <0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0>;
+ qcom,setb-gpios-dir = <2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2>;
+};
diff --git a/arch/arm/boot/dts/msm8610-coresight.dtsi b/arch/arm/boot/dts/msm8610-coresight.dtsi
index 4945693..516522e 100644
--- a/arch/arm/boot/dts/msm8610-coresight.dtsi
+++ b/arch/arm/boot/dts/msm8610-coresight.dtsi
@@ -34,6 +34,11 @@
coresight-id = <1>;
coresight-name = "coresight-tpiu";
coresight-nr-inports = <1>;
+
+ vdd-supply = <&pm8110_l18>;
+
+ qcom,vdd-voltage-level = <2950000 2950000>;
+ qcom,vdd-current-level = <15000 400000>;
};
replicator: replicator@fc324000 {
@@ -335,4 +340,18 @@
coresight-name = "coresight-cti-cpu3";
coresight-nr-inports = <0>;
};
+
+ hwevent: hwevent@fd820018 {
+ compatible = "qcom,coresight-hwevent";
+ reg = <0xfd820018 0x80>,
+ <0xf9011080 0x80>,
+ <0xfd4ab160 0x80>;
+ reg-names = "mmss-mux", "apcs-mux", "ppss-mux";
+
+ coresight-id = <27>;
+ coresight-name = "coresight-hwevent";
+ coresight-nr-inports = <0>;
+
+ qcom,hwevent-clks = "core_mmss_clk";
+ };
};
diff --git a/arch/arm/boot/dts/msm8610-mtp.dts b/arch/arm/boot/dts/msm8610-mtp.dts
index 83e7b48..e1fe66a 100644
--- a/arch/arm/boot/dts/msm8610-mtp.dts
+++ b/arch/arm/boot/dts/msm8610-mtp.dts
@@ -88,6 +88,49 @@
};
};
};
+
+ gen-vkeys {
+ compatible = "qcom,gen-vkeys";
+ label = "atmel_mxt_ts";
+ qcom,disp-maxx = <480>;
+ qcom,disp-maxy = <800>;
+ qcom,panel-maxx = <508>;
+ qcom,panel-maxy = <880>;
+ qcom,key-codes = <158 102 139>;
+ qcom,y-offset = <35>;
+ };
+
+ gpio_keys {
+ compatible = "gpio-keys";
+ input-name = "gpio-keys";
+
+ camera_snapshot {
+ label = "camera_snapshot";
+ gpios = <&msmgpio 73 0x1>;
+ linux,input-type = <1>;
+ linux,code = <0x2fe>;
+ gpio-key,wakeup;
+ debounce-interval = <15>;
+ };
+
+ camera_focus {
+ label = "camera_focus";
+ gpios = <&msmgpio 74 0x1>;
+ linux,input-type = <1>;
+ linux,code = <0x210>;
+ gpio-key,wakeup;
+ debounce-interval = <15>;
+ };
+
+ vol_up {
+ label = "volume_up";
+ gpios = <&msmgpio 72 0x1>;
+ linux,input-type = <1>;
+ linux,code = <115>;
+ gpio-key,wakeup;
+ debounce-interval = <15>;
+ };
+ };
};
&i2c_cdc {
@@ -156,7 +199,7 @@
qcom,led_mpp_3 {
label = "mpp";
linux,name = "wled-backlight";
- linux-default-trigger = "none";
+ linux,default-trigger = "bkl-trigger";
qcom,default-state = "on";
qcom,max-current = <40>;
qcom,id = <6>;
@@ -165,38 +208,6 @@
};
};
};
-
- gpio_keys {
- compatible = "gpio-keys";
- input-name = "gpio-keys";
-
- camera_snapshot {
- label = "camera_snapshot";
- gpios = <&msmgpio 73 0x1>;
- linux,input-type = <1>;
- linux,code = <0x2fe>;
- gpio-key,wakeup;
- debounce-interval = <15>;
- };
-
- camera_focus {
- label = "camera_focus";
- gpios = <&msmgpio 74 0x1>;
- linux,input-type = <1>;
- linux,code = <0x210>;
- gpio-key,wakeup;
- debounce-interval = <15>;
- };
-
- vol_up {
- label = "volume_up";
- gpios = <&msmgpio 72 0x1>;
- linux,input-type = <1>;
- linux,code = <115>;
- gpio-key,wakeup;
- debounce-interval = <15>;
- };
- };
};
&spmi_bus {
@@ -319,3 +330,7 @@
mpp@a300 { /* MPP 4 */
};
};
+
+&pm8110_bms {
+ status = "ok";
+};
diff --git a/arch/arm/boot/dts/msm8610-pm.dtsi b/arch/arm/boot/dts/msm8610-pm.dtsi
index 34382c5..938b2aa 100644
--- a/arch/arm/boot/dts/msm8610-pm.dtsi
+++ b/arch/arm/boot/dts/msm8610-pm.dtsi
@@ -114,7 +114,7 @@
qcom,type = <0x61706d73>; /* "smpa" */
qcom,id = <0x01>;
qcom,key = <0x6e726f63>; /* "corn" */
- qcom,init-value = <5>; /* Super Turbo */
+ qcom,init-value = <3>; /* SVS SOC */
};
qcom,lpm-resources@1 {
@@ -123,7 +123,7 @@
qcom,type = <0x616F646C>; /* "ldoa" */
qcom,id = <0x03>;
qcom,key = <0x6e726f63>; /* "corn" */
- qcom,init-value = <3>; /* Active */
+ qcom,init-value = <3>; /* SVS SOC */
};
qcom,lpm-resources@2 {
@@ -153,10 +153,10 @@
qcom,mode = "wfi";
qcom,xo = "xo_on";
qcom,l2 = "l2_cache_active";
- qcom,vdd-mem-upper-bound = <5>; /* SUPER TURBO */
- qcom,vdd-mem-lower-bound = <3>; /* NORMAL */
- qcom,vdd-dig-upper-bound = <5>; /* SUPER TURBO */
- qcom,vdd-dig-lower-bound = <3>; /* NORMAL */
+ qcom,vdd-mem-upper-bound = <6>; /* SUPER TURBO */
+ qcom,vdd-mem-lower-bound = <4>; /* NORMAL */
+ qcom,vdd-dig-upper-bound = <6>; /* SUPER TURBO */
+ qcom,vdd-dig-lower-bound = <4>; /* NORMAL */
qcom,irqs-detectable;
qcom,gpio-detectable;
qcom,latency-us = <1>;
@@ -170,10 +170,10 @@
qcom,mode = "standalone_pc";
qcom,xo = "xo_on";
qcom,l2 = "l2_cache_active";
- qcom,vdd-mem-upper-bound = <5>; /* SUPER TURBO */
- qcom,vdd-mem-lower-bound = <3>; /* NORMAL */
- qcom,vdd-dig-upper-bound = <5>; /* SUPER TURBO */
- qcom,vdd-dig-lower-bound = <3>; /* NORMAL */
+ qcom,vdd-mem-upper-bound = <6>; /* SUPER TURBO */
+ qcom,vdd-mem-lower-bound = <4>; /* NORMAL */
+ qcom,vdd-dig-upper-bound = <6>; /* SUPER TURBO */
+ qcom,vdd-dig-lower-bound = <4>; /* NORMAL */
qcom,irqs-detectable;
qcom,gpio-detectable;
qcom,latency-us = <3000>;
@@ -187,10 +187,10 @@
qcom,mode = "pc";
qcom,xo = "xo_on";
qcom,l2 = "l2_cache_retention";
- qcom,vdd-mem-upper-bound = <5>; /* SUPER TURBO */
- qcom,vdd-mem-lower-bound = <3>; /* NORMAL */
- qcom,vdd-dig-upper-bound = <5>; /* SUPER TURBO */
- qcom,vdd-dig-lower-bound = <3>; /* NORMAL */
+ qcom,vdd-mem-upper-bound = <6>; /* SUPER TURBO */
+ qcom,vdd-mem-lower-bound = <4>; /* NORMAL */
+ qcom,vdd-dig-upper-bound = <6>; /* SUPER TURBO */
+ qcom,vdd-dig-lower-bound = <4>; /* NORMAL */
qcom,irqs-detectable;
qcom,gpio-detectable;
qcom,latency-us = <8000>;
@@ -204,10 +204,10 @@
qcom,mode = "pc";
qcom,xo = "xo_on";
qcom,l2 = "l2_cache_pc";
- qcom,vdd-mem-upper-bound = <3>; /* NORMAL */
- qcom,vdd-mem-lower-bound = <2>; /* SVS SOC */
- qcom,vdd-dig-upper-bound = <3>; /* NORMAL */
- qcom,vdd-dig-lower-bound = <2>; /* SVS SOC */
+ qcom,vdd-mem-upper-bound = <4>; /* NORMAL */
+ qcom,vdd-mem-lower-bound = <3>; /* SVS SOC */
+ qcom,vdd-dig-upper-bound = <4>; /* NORMAL */
+ qcom,vdd-dig-lower-bound = <3>; /* SVS SOC */
qcom,irqs-detectable;
qcom,gpio-detectable;
qcom,latency-us = <9000>;
@@ -221,10 +221,10 @@
qcom,mode = "pc";
qcom,xo = "xo_off";
qcom,l2 = "l2_cache_pc";
- qcom,vdd-mem-upper-bound = <5>; /* SUPER TURBO */
- qcom,vdd-mem-lower-bound = <3>; /* NORMAL */
- qcom,vdd-dig-upper-bound = <5>; /* SUPER TURBO */
- qcom,vdd-dig-lower-bound = <3>; /* NORMAL */
+ qcom,vdd-mem-upper-bound = <6>; /* SUPER TURBO */
+ qcom,vdd-mem-lower-bound = <4>; /* NORMAL */
+ qcom,vdd-dig-upper-bound = <6>; /* SUPER TURBO */
+ qcom,vdd-dig-lower-bound = <4>; /* NORMAL */
qcom,latency-us = <16300>;
qcom,ss-power = <63>;
qcom,energy-overhead = <2128000>;
@@ -236,10 +236,10 @@
qcom,mode = "pc";
qcom,xo = "xo_off";
qcom,l2 = "l2_cache_pc";
- qcom,vdd-mem-upper-bound = <3>; /* NORMAL */
- qcom,vdd-mem-lower-bound = <2>; /* SVS SOC */
- qcom,vdd-dig-upper-bound = <3>; /* NORMAL */
- qcom,vdd-dig-lower-bound = <2>; /* SVS SOC */
+ qcom,vdd-mem-upper-bound = <4>; /* NORMAL */
+ qcom,vdd-mem-lower-bound = <3>; /* SVS SOC */
+ qcom,vdd-dig-upper-bound = <4>; /* NORMAL */
+ qcom,vdd-dig-lower-bound = <3>; /* SVS SOC */
qcom,latency-us = <24000>;
qcom,ss-power = <10>;
qcom,energy-overhead = <3202600>;
@@ -251,10 +251,10 @@
qcom,mode = "pc";
qcom,xo = "xo_off";
qcom,l2 = "l2_cache_pc";
- qcom,vdd-mem-upper-bound = <2>; /* SVS SOC */
- qcom,vdd-mem-lower-bound = <0>; /* RETENTION */
- qcom,vdd-dig-upper-bound = <2>; /* SVS SOC */
- qcom,vdd-dig-lower-bound = <0>; /* RETENTION */
+ qcom,vdd-mem-upper-bound = <3>; /* SVS SOC */
+ qcom,vdd-mem-lower-bound = <1>; /* RETENTION */
+ qcom,vdd-dig-upper-bound = <3>; /* SVS SOC */
+ qcom,vdd-dig-lower-bound = <1>; /* RETENTION */
qcom,latency-us = <26000>;
qcom,ss-power = <2>;
qcom,energy-overhead = <4252000>;
diff --git a/arch/arm/boot/dts/msm8610-regulator.dtsi b/arch/arm/boot/dts/msm8610-regulator.dtsi
index 9e79df1..a90f053 100644
--- a/arch/arm/boot/dts/msm8610-regulator.dtsi
+++ b/arch/arm/boot/dts/msm8610-regulator.dtsi
@@ -30,20 +30,37 @@
apc_vreg_corner: regulator@f9018000 {
status = "okay";
compatible = "qcom,cpr-regulator";
- reg = <0xf9018000 0x1000>,
- <0xfc4b80b0 8>;
- reg-names = "rbcpr", "efuse_phys";
+ reg = <0xf9018000 0x1000>, <0xfc4b80b0 8>, <0xfc4bc450 16>;
+ reg-names = "rbcpr", "pvs_efuse", "cpr_efuse";
+ interrupts = <0 15 0>;
regulator-name = "apc_corner";
regulator-min-microvolt = <1>;
- regulator-max-microvolt = <4>;
+ regulator-max-microvolt = <3>;
qcom,num-efuse-bits = <5>;
- qcom,efuse-bit-pos = <6 7 8 9 10>;
- qcom,pvs-bin-process = <0 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2
+ qcom,pvs-bin-process = <0 0 0 0 0 1 1 1 1 1 2 2 2 2 2 2
2 2 2 2 3 3 3 3 3 3 3 3 0 0 0 0>;
- qcom,pvs-corner-ceiling-slow = <1050000 1150000 1275000 1350000>;
- qcom,pvs-corner-ceiling-nom = <975000 1075000 1200000 1200000>;
- qcom,pvs-corner-ceiling-fast = <900000 1000000 1140000 1140000>;
+ qcom,pvs-corner-ceiling-slow = <1150000 1150000 1275000>;
+ qcom,pvs-corner-ceiling-nom = <975000 1075000 1200000>;
+ qcom,pvs-corner-ceiling-fast = <900000 1000000 1140000>;
vdd-apc-supply = <&pm8110_s2>;
+
+ vdd-mx-supply = <&pm8110_l3_ao>;
+ qcom,vdd-mx-vmax = <1350000>;
+ qcom,vdd-mx-vmin-method = <1>;
+
+ qcom,cpr-ref-clk = <19200>;
+ qcom,cpr-timer-delay = <5000>;
+ qcom,cpr-timer-cons-up = <1>;
+ qcom,cpr-timer-cons-down = <2>;
+ qcom,cpr-irq-line = <0>;
+ qcom,cpr-step-quotient = <15>;
+ qcom,cpr-up-threshold = <1>;
+ qcom,cpr-down-threshold = <2>;
+ qcom,cpr-idle-clocks = <5>;
+ qcom,cpr-gcnt-time = <1>;
+ qcom,vdd-apc-step-up-limit = <1>;
+ qcom,vdd-apc-step-down-limit = <1>;
+ qcom,cpr-apc-volt-step = <5000>;
};
};
diff --git a/arch/arm/boot/dts/msm8610.dtsi b/arch/arm/boot/dts/msm8610.dtsi
index 4b861ed..9dbd71d 100644
--- a/arch/arm/boot/dts/msm8610.dtsi
+++ b/arch/arm/boot/dts/msm8610.dtsi
@@ -144,6 +144,11 @@
qcom,adsp-state = <0>;
};
+ qcom,msm-audio-ion {
+ compatible = "qcom,msm-audio-ion";
+ qcom,smmu-enabled;
+ };
+
qcom,msm-imem@fe805000 {
compatible = "qcom,msm-imem";
reg = <0xfe805000 0x1000>; /* Address and size of IMEM */
@@ -676,7 +681,6 @@
"restart_reg", "cxrail_bhs_reg";
interrupts = <0 24 1>;
- vdd_mss-supply = <&pm8110_s1>;
vdd_cx-supply = <&pm8110_s1_corner>;
vdd_mx-supply = <&pm8110_l3>;
vdd_pll-supply = <&pm8110_l10>;
@@ -707,6 +711,7 @@
/* GPIO inputs from lpass */
qcom,gpio-err-fatal = <&smp2pgpio_ssr_smp2p_2_in 0 0>;
qcom,gpio-proxy-unvote = <&smp2pgpio_ssr_smp2p_2_in 2 0>;
+ qcom,gpio-err-ready = <&smp2pgpio_ssr_smp2p_2_in 1 0>;
/* GPIO output to lpass */
qcom,gpio-force-stop = <&smp2pgpio_ssr_smp2p_2_out 0 0>;
@@ -721,14 +726,12 @@
qcom,sensors = <2>;
qcom,slope = <2901 2846>;
qcom,calib-mode = "fuse_map3";
- qcom,calibration-less-mode;
- qcom,tsens-local-init;
qcom,sensor-id = <0 5>;
};
qcom,msm-thermal {
compatible = "qcom,msm-thermal";
- qcom,sensor-id = <0>;
+ qcom,sensor-id = <5>;
qcom,poll-ms = <250>;
qcom,limit-temp = <60>;
qcom,temp-hysteresis = <10>;
diff --git a/arch/arm/boot/dts/msm8974-cdp.dtsi b/arch/arm/boot/dts/msm8974-cdp.dtsi
index 72b6223..5fa7c08 100644
--- a/arch/arm/boot/dts/msm8974-cdp.dtsi
+++ b/arch/arm/boot/dts/msm8974-cdp.dtsi
@@ -195,6 +195,7 @@
qcom,model = "msm8974-taiko-cdp-snd-card";
qcom,hdmi-audio-rx;
qcom,us-euro-gpios = <&pm8941_gpios 20 0>;
+ qcom,cdc-micbias2-headset-only;
};
usb2_otg_sw: regulator-tpd4s214 {
@@ -499,7 +500,7 @@
qcom,output-type = <0>;
qcom,pull = <5>;
qcom,vin-sel = <2>;
- qcom,out-strength = <3>;
+ qcom,out-strength = <1>;
qcom,src-sel = <2>;
qcom,master-en = <1>;
};
diff --git a/arch/arm/boot/dts/msm8974-coresight.dtsi b/arch/arm/boot/dts/msm8974-coresight.dtsi
index cccedd8..1610f1f 100644
--- a/arch/arm/boot/dts/msm8974-coresight.dtsi
+++ b/arch/arm/boot/dts/msm8974-coresight.dtsi
@@ -364,4 +364,18 @@
coresight-name = "coresight-cti-cpu3";
coresight-nr-inports = <0>;
};
+
+ hwevent: hwevent@fdf30018 {
+ compatible = "qcom,coresight-hwevent";
+ reg = <0xfdf30018 0x80>,
+ <0xf9011080 0x80>,
+ <0xfd4ab160 0x80>;
+ reg-names = "mmss-mux", "apcs-mux", "ppss-mux";
+
+ coresight-id = <29>;
+ coresight-name = "coresight-hwevent";
+ coresight-nr-inports = <0>;
+
+ qcom,hwevent-clks = "core_mmss_clk";
+ };
};
diff --git a/arch/arm/boot/dts/msm8974-fluid.dtsi b/arch/arm/boot/dts/msm8974-fluid.dtsi
index 06b13b8..ad5f175 100644
--- a/arch/arm/boot/dts/msm8974-fluid.dtsi
+++ b/arch/arm/boot/dts/msm8974-fluid.dtsi
@@ -230,6 +230,7 @@
qcom,hdmi-audio-rx;
qcom,ext-ult-lo-amp-gpio = <&pm8941_gpios 6 0>;
+ qcom,cdc-micbias2-headset-only;
};
};
@@ -505,7 +506,7 @@
qcom,output-type = <0>;
qcom,pull = <5>;
qcom,vin-sel = <2>;
- qcom,out-strength = <3>;
+ qcom,out-strength = <1>;
qcom,src-sel = <2>;
qcom,master-en = <1>;
};
diff --git a/arch/arm/boot/dts/msm8974-leds.dtsi b/arch/arm/boot/dts/msm8974-leds.dtsi
index befd206..b39cc21 100644
--- a/arch/arm/boot/dts/msm8974-leds.dtsi
+++ b/arch/arm/boot/dts/msm8974-leds.dtsi
@@ -93,6 +93,20 @@
linux,name = "led:flash_1";
qcom,current = <625>;
};
+
+ pm8941_torch: qcom,flash_torch {
+ qcom,max-current = <200>;
+ qcom,default-state = "off";
+ qcom,headroom = <0>;
+ qcom,startup-dly = <1>;
+ linux,default-trigger =
+ "torch_trigger";
+ label = "flash";
+ qcom,id = <2>;
+ linux,name = "led:flash_torch";
+ qcom,current = <200>;
+ qcom,torch-enable;
+ };
};
qcom,leds@d400 {
diff --git a/arch/arm/boot/dts/msm8974-liquid.dtsi b/arch/arm/boot/dts/msm8974-liquid.dtsi
index 3f4e035..e63b53b 100644
--- a/arch/arm/boot/dts/msm8974-liquid.dtsi
+++ b/arch/arm/boot/dts/msm8974-liquid.dtsi
@@ -511,7 +511,7 @@
qcom,output-type = <0>;
qcom,pull = <5>;
qcom,vin-sel = <2>;
- qcom,out-strength = <3>;
+ qcom,out-strength = <1>;
qcom,src-sel = <2>;
qcom,master-en = <1>;
};
diff --git a/arch/arm/boot/dts/msm8974-mtp.dtsi b/arch/arm/boot/dts/msm8974-mtp.dtsi
index 37286af..4d28a1d 100644
--- a/arch/arm/boot/dts/msm8974-mtp.dtsi
+++ b/arch/arm/boot/dts/msm8974-mtp.dtsi
@@ -187,6 +187,7 @@
sound {
qcom,model = "msm8974-taiko-mtp-snd-card";
+ qcom,cdc-micbias2-headset-only;
};
};
@@ -489,7 +490,7 @@
qcom,output-type = <0>;
qcom,pull = <5>;
qcom,vin-sel = <2>;
- qcom,out-strength = <3>;
+ qcom,out-strength = <1>;
qcom,src-sel = <2>;
qcom,master-en = <1>;
};
diff --git a/arch/arm/boot/dts/msm8974-regulator.dtsi b/arch/arm/boot/dts/msm8974-regulator.dtsi
index 2cd3d24..35f3993 100644
--- a/arch/arm/boot/dts/msm8974-regulator.dtsi
+++ b/arch/arm/boot/dts/msm8974-regulator.dtsi
@@ -460,8 +460,9 @@
&soc {
krait_pdn: krait-pdn@f9011000 {
- reg = <0xf9011000 0x1000>;
- reg-names = "apcs_gcc";
+ reg = <0xf9011000 0x1000>,
+ <0xfc4b80b0 8>;
+ reg-names = "apcs_gcc", "phase-scaling-efuse";
compatible = "qcom,krait-pdn";
#address-cells = <1>;
#size-cells = <1>;
diff --git a/arch/arm/boot/dts/msm8974-v1-cdp.dts b/arch/arm/boot/dts/msm8974-v1-cdp.dts
index cb58026..c3fd98d 100644
--- a/arch/arm/boot/dts/msm8974-v1-cdp.dts
+++ b/arch/arm/boot/dts/msm8974-v1-cdp.dts
@@ -18,7 +18,9 @@
/ {
model = "Qualcomm MSM 8974 CDP";
compatible = "qcom,msm8974-cdp", "qcom,msm8974", "qcom,cdp";
- qcom,msm-id = <126 1 0>;
+ qcom,msm-id = <126 1 0>,
+ <185 1 0>,
+ <186 1 0>;
};
&ehci {
diff --git a/arch/arm/boot/dts/msm8974-v1-fluid.dts b/arch/arm/boot/dts/msm8974-v1-fluid.dts
index 8ab24df..2b96ecb 100644
--- a/arch/arm/boot/dts/msm8974-v1-fluid.dts
+++ b/arch/arm/boot/dts/msm8974-v1-fluid.dts
@@ -18,7 +18,9 @@
/ {
model = "Qualcomm MSM 8974 FLUID";
compatible = "qcom,msm8974-fluid", "qcom,msm8974", "qcom,fluid";
- qcom,msm-id = <126 3 0>;
+ qcom,msm-id = <126 3 0>,
+ <185 3 0>,
+ <186 3 0>;
};
diff --git a/arch/arm/boot/dts/msm8974-v1-liquid.dts b/arch/arm/boot/dts/msm8974-v1-liquid.dts
index ccbd82f..29d6150 100644
--- a/arch/arm/boot/dts/msm8974-v1-liquid.dts
+++ b/arch/arm/boot/dts/msm8974-v1-liquid.dts
@@ -18,5 +18,7 @@
/ {
model = "Qualcomm MSM 8974 LIQUID";
compatible = "qcom,msm8974-liquid", "qcom,msm8974", "qcom,liquid";
- qcom,msm-id = <126 9 0>;
+ qcom,msm-id = <126 9 0>,
+ <185 9 0>,
+ <186 9 0>;
};
diff --git a/arch/arm/boot/dts/msm8974-v1-mtp.dts b/arch/arm/boot/dts/msm8974-v1-mtp.dts
index 09ea84b..8cbcca0 100644
--- a/arch/arm/boot/dts/msm8974-v1-mtp.dts
+++ b/arch/arm/boot/dts/msm8974-v1-mtp.dts
@@ -18,7 +18,9 @@
/ {
model = "Qualcomm MSM 8974 MTP";
compatible = "qcom,msm8974-mtp", "qcom,msm8974", "qcom,mtp";
- qcom,msm-id = <126 8 0>;
+ qcom,msm-id = <126 8 0>,
+ <185 8 0>,
+ <186 8 0>;
};
&pm8941_chg {
diff --git a/arch/arm/boot/dts/msm8974-v1-pm.dtsi b/arch/arm/boot/dts/msm8974-v1-pm.dtsi
index 1a88749..cebc99a 100644
--- a/arch/arm/boot/dts/msm8974-v1-pm.dtsi
+++ b/arch/arm/boot/dts/msm8974-v1-pm.dtsi
@@ -340,7 +340,8 @@
qcom,ipc-bit-offset = <1>;
qcom,gic-parent = <&intc>;
- qcom,gic-map = <47 165>, /* usb30_hs_phy_irq */
+ qcom,gic-map = <2 216>, /* tsens_upper_lower_int */
+ <47 165>, /* usb30_hs_phy_irq */
<50 172>, /* usb1_hs_async_wakeup_irq */
<53 104>, /* mdss_irq */
<62 222>, /* ee0_krait_hlos_spmi_periph_irq */
diff --git a/arch/arm/boot/dts/msm8974-v1-rumi.dts b/arch/arm/boot/dts/msm8974-v1-rumi.dts
index caf89ee..85aab17 100644
--- a/arch/arm/boot/dts/msm8974-v1-rumi.dts
+++ b/arch/arm/boot/dts/msm8974-v1-rumi.dts
@@ -18,5 +18,7 @@
/ {
model = "Qualcomm MSM 8974 RUMI";
compatible = "qcom,msm8974-rumi", "qcom,msm8974", "qcom,rumi";
- qcom,msm-id = <126 15 0>;
+ qcom,msm-id = <126 15 0>,
+ <185 15 0>,
+ <186 15 0>;
};
diff --git a/arch/arm/boot/dts/msm8974-v1-sim.dts b/arch/arm/boot/dts/msm8974-v1-sim.dts
index c4b29c2..fc9858d 100644
--- a/arch/arm/boot/dts/msm8974-v1-sim.dts
+++ b/arch/arm/boot/dts/msm8974-v1-sim.dts
@@ -18,5 +18,7 @@
/ {
model = "Qualcomm MSM 8974 Simulator";
compatible = "qcom,msm8974-sim", "qcom,msm8974", "qcom,sim";
- qcom,msm-id = <126 16 0>;
+ qcom,msm-id = <126 16 0>,
+ <185 16 0>,
+ <186 16 0>;
};
diff --git a/arch/arm/boot/dts/msm8974-v2-cdp.dts b/arch/arm/boot/dts/msm8974-v2-cdp.dts
index 4fa1f2a..85d478b 100644
--- a/arch/arm/boot/dts/msm8974-v2-cdp.dts
+++ b/arch/arm/boot/dts/msm8974-v2-cdp.dts
@@ -18,7 +18,9 @@
/ {
model = "Qualcomm MSM 8974v2 CDP";
compatible = "qcom,msm8974-cdp", "qcom,msm8974", "qcom,cdp";
- qcom,msm-id = <126 1 0x20000>;
+ qcom,msm-id = <126 1 0x20000>,
+ <185 1 0x20000>,
+ <186 1 0x20000>;
};
&usb3 {
diff --git a/arch/arm/boot/dts/msm8974-v2-fluid.dts b/arch/arm/boot/dts/msm8974-v2-fluid.dts
index c5779b1..d83d130 100644
--- a/arch/arm/boot/dts/msm8974-v2-fluid.dts
+++ b/arch/arm/boot/dts/msm8974-v2-fluid.dts
@@ -18,7 +18,9 @@
/ {
model = "Qualcomm MSM 8974v2 FLUID";
compatible = "qcom,msm8974-fluid", "qcom,msm8974", "qcom,fluid";
- qcom,msm-id = <126 3 0x20000>;
+ qcom,msm-id = <126 3 0x20000>,
+ <185 3 0x20000>,
+ <186 3 0x20000>;
};
&usb3 {
diff --git a/arch/arm/boot/dts/msm8974-v2-iommu.dtsi b/arch/arm/boot/dts/msm8974-v2-iommu.dtsi
index c974884..b5652d1 100644
--- a/arch/arm/boot/dts/msm8974-v2-iommu.dtsi
+++ b/arch/arm/boot/dts/msm8974-v2-iommu.dtsi
@@ -76,6 +76,7 @@
};
venus_sec_pixel: qcom,iommu-ctx@fdc8f000 {
+ compatible = "qcom,msm-smmu-v1-ctx";
reg = <0xfdc8f000 0x1000>;
interrupts = <0 42 0>;
qcom,iommu-ctx-sids = <0x85>;
@@ -84,6 +85,7 @@
};
venus_sec_non_pixel: qcom,iommu-ctx@fdc90000 {
+ compatible = "qcom,msm-smmu-v1-ctx";
reg = <0xfdc90000 0x1000>;
interrupts = <0 42 0>;
qcom,iommu-ctx-sids = <0x87 0xA0>;
diff --git a/arch/arm/boot/dts/msm8974-v2-liquid.dts b/arch/arm/boot/dts/msm8974-v2-liquid.dts
index 7132f43..53983dc 100644
--- a/arch/arm/boot/dts/msm8974-v2-liquid.dts
+++ b/arch/arm/boot/dts/msm8974-v2-liquid.dts
@@ -18,7 +18,9 @@
/ {
model = "Qualcomm MSM 8974v2 LIQUID";
compatible = "qcom,msm8974-liquid", "qcom,msm8974", "qcom,liquid";
- qcom,msm-id = <126 9 0x20000>;
+ qcom,msm-id = <126 9 0x20000>,
+ <185 9 0x20000>,
+ <186 9 0x20000>;
};
&usb3 {
diff --git a/arch/arm/boot/dts/msm8974-v2-mtp.dts b/arch/arm/boot/dts/msm8974-v2-mtp.dts
index d38e663..c25c385 100644
--- a/arch/arm/boot/dts/msm8974-v2-mtp.dts
+++ b/arch/arm/boot/dts/msm8974-v2-mtp.dts
@@ -18,7 +18,9 @@
/ {
model = "Qualcomm MSM 8974v2 MTP";
compatible = "qcom,msm8974-mtp", "qcom,msm8974", "qcom,mtp";
- qcom,msm-id = <126 8 0x20000>;
+ qcom,msm-id = <126 8 0x20000>,
+ <185 8 0x20000>,
+ <186 8 0x20000>;
};
&usb3 {
diff --git a/arch/arm/boot/dts/msm8974-v2-pm.dtsi b/arch/arm/boot/dts/msm8974-v2-pm.dtsi
index f8492c0..29dce2a 100644
--- a/arch/arm/boot/dts/msm8974-v2-pm.dtsi
+++ b/arch/arm/boot/dts/msm8974-v2-pm.dtsi
@@ -340,7 +340,8 @@
qcom,ipc-bit-offset = <1>;
qcom,gic-parent = <&intc>;
- qcom,gic-map = <47 165>, /* usb30_hs_phy_irq */
+ qcom,gic-map = <2 216>, /* tsens_upper_lower_int */
+ <47 165>, /* usb30_hs_phy_irq */
<50 172>, /* usb1_hs_async_wakeup_irq */
<53 104>, /* mdss_irq */
<62 222>, /* ee0_krait_hlos_spmi_periph_irq */
diff --git a/arch/arm/boot/dts/msm8974-v2.dtsi b/arch/arm/boot/dts/msm8974-v2.dtsi
index 75dce17..b37a509 100644
--- a/arch/arm/boot/dts/msm8974-v2.dtsi
+++ b/arch/arm/boot/dts/msm8974-v2.dtsi
@@ -131,3 +131,7 @@
&krait_pdn {
qcom,use-phase-switching;
};
+
+&tspp {
+ vdd_cx-supply = <&pm8841_s2_corner>;
+};
diff --git a/arch/arm/boot/dts/msm8974.dtsi b/arch/arm/boot/dts/msm8974.dtsi
index 71bbdc5..a4a3efe 100644
--- a/arch/arm/boot/dts/msm8974.dtsi
+++ b/arch/arm/boot/dts/msm8974.dtsi
@@ -869,6 +869,7 @@
/* GPIO inputs from lpass */
qcom,gpio-err-fatal = <&smp2pgpio_ssr_smp2p_2_in 0 0>;
qcom,gpio-proxy-unvote = <&smp2pgpio_ssr_smp2p_2_in 2 0>;
+ qcom,gpio-err-ready = <&smp2pgpio_ssr_smp2p_2_in 1 0>;
/* GPIO output to lpass */
qcom,gpio-force-stop = <&smp2pgpio_ssr_smp2p_2_out 0 0>;
@@ -1127,7 +1128,6 @@
vdd_mx-supply = <&pm8841_s1>;
vdd_pll-supply = <&pm8941_l12>;
qcom,vdd_pll = <1800000>;
- qcom,is-loadable;
qcom,firmware-name = "mba";
qcom,pil-self-auth;
diff --git a/arch/arm/boot/dts/msm9625-coresight.dtsi b/arch/arm/boot/dts/msm9625-coresight.dtsi
index 8520b19..9b18b72 100644
--- a/arch/arm/boot/dts/msm9625-coresight.dtsi
+++ b/arch/arm/boot/dts/msm9625-coresight.dtsi
@@ -243,4 +243,15 @@
coresight-name = "coresight-cti-cpu";
coresight-nr-inports = <0>;
};
+
+ hwevent: hwevent@f9011038 {
+ compatible = "qcom,coresight-hwevent";
+ reg = <0xf9011038 0x8>,
+ <0xfd4ab160 0x80>;
+ reg-names = "apcs-mux", "ppss-mux";
+
+ coresight-id = <20>;
+ coresight-name = "coresight-hwevent";
+ coresight-nr-inports = <0>;
+ };
};
diff --git a/arch/arm/boot/dts/msm9625-pm.dtsi b/arch/arm/boot/dts/msm9625-pm.dtsi
index 6c45f80..1d10f8c 100644
--- a/arch/arm/boot/dts/msm9625-pm.dtsi
+++ b/arch/arm/boot/dts/msm9625-pm.dtsi
@@ -201,7 +201,8 @@
qcom,ipc-bit-offset = <1>;
qcom,gic-parent = <&intc>;
- qcom,gic-map = <47 172>, /* usb2_hsic_async_wakeup_irq */
+ qcom,gic-map = <2 216>, /* tsens_upper_lower_int */
+ <47 172>, /* usb2_hsic_async_wakeup_irq */
<41 180>, /* usb_async_wakeup_irq */
<62 222>, /* ee0_krait_hlos_spmi_periph_irq */
<0xff 57>, /* mss_to_apps_irq(0) */
diff --git a/arch/arm/boot/dts/msm9625.dtsi b/arch/arm/boot/dts/msm9625.dtsi
index f614d61..6e258b5 100644
--- a/arch/arm/boot/dts/msm9625.dtsi
+++ b/arch/arm/boot/dts/msm9625.dtsi
@@ -763,6 +763,7 @@
qcom,mss {
compatible = "qcom,pil-q6v5-mss";
interrupts = <0 24 1>;
+ qcom,is-not-loadable;
/* GPIO inputs from mss */
qcom,gpio-err-fatal = <&smp2pgpio_ssr_smp2p_1_in 0 0>;
@@ -967,3 +968,32 @@
qcom,fast-avg-setup = <0>;
};
};
+
+&pm8019_adc_tm {
+ /* Channel Node */
+ chan@33 {
+ label = "pa_therm0";
+ reg = <0x33>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <2>;
+ qcom,hw-settle-time = <2>;
+ qcom,fast-avg-setup = <0>;
+ qcom,btm-channel-number = <0x48>;
+ qcom,thermal-node;
+ };
+
+ chan@34 {
+ label = "pa_therm1";
+ reg = <0x34>;
+ qcom,decimation = <0>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <2>;
+ qcom,hw-settle-time = <2>;
+ qcom,fast-avg-setup = <0>;
+ qcom,btm-channel-number = <0x68>;
+ qcom,thermal-node;
+ };
+};
diff --git a/arch/arm/boot/dts/skeleton64.dtsi b/arch/arm/boot/dts/skeleton64.dtsi
new file mode 100644
index 0000000..5bf6a82
--- /dev/null
+++ b/arch/arm/boot/dts/skeleton64.dtsi
@@ -0,0 +1,18 @@
+/*
+ * Skeleton device tree; the bare minimum needed to boot; just include and
+ * add a compatible value. The bootloader will typically populate the memory
+ * node.
+ */
+
+/ {
+ #address-cells = <2>;
+ #size-cells = <2>;
+ chosen { };
+ aliases { };
+ memory {
+ #address-cells = <2>;
+ #size-cells = <2>;
+ device_type = "memory";
+ reg = <0 0 0 0>;
+ };
+};
diff --git a/arch/arm/configs/mpq8092_defconfig b/arch/arm/configs/mpq8092_defconfig
new file mode 100644
index 0000000..28ca32f
--- /dev/null
+++ b/arch/arm/configs/mpq8092_defconfig
@@ -0,0 +1,368 @@
+# CONFIG_ARM_PATCH_PHYS_VIRT is not set
+CONFIG_EXPERIMENTAL=y
+CONFIG_SYSVIPC=y
+CONFIG_RCU_FAST_NO_HZ=y
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_CGROUPS=y
+CONFIG_CGROUP_DEBUG=y
+CONFIG_CGROUP_FREEZER=y
+CONFIG_CGROUP_CPUACCT=y
+CONFIG_RESOURCE_COUNTERS=y
+CONFIG_CGROUP_SCHED=y
+CONFIG_RT_GROUP_SCHED=y
+CONFIG_NAMESPACES=y
+# CONFIG_UTS_NS is not set
+# CONFIG_IPC_NS is not set
+# CONFIG_PID_NS is not set
+CONFIG_RELAY=y
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_RD_BZIP2=y
+CONFIG_RD_LZMA=y
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+CONFIG_PANIC_TIMEOUT=5
+CONFIG_KALLSYMS_ALL=y
+CONFIG_EMBEDDED=y
+CONFIG_PROFILING=y
+CONFIG_KPROBES=y
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODULE_FORCE_UNLOAD=y
+CONFIG_MODVERSIONS=y
+CONFIG_PARTITION_ADVANCED=y
+CONFIG_EFI_PARTITION=y
+CONFIG_IOSCHED_TEST=y
+CONFIG_ARCH_MSM=y
+CONFIG_ARCH_MPQ8092=y
+CONFIG_MSM_KRAIT_TBB_ABORT_HANDLER=y
+CONFIG_MSM_MPM_OF=y
+# CONFIG_MSM_STACKED_MEMORY is not set
+CONFIG_CPU_HAS_L2_PMU=y
+# CONFIG_MSM_FIQ_SUPPORT is not set
+# CONFIG_MSM_PROC_COMM is not set
+CONFIG_MSM_SMD=y
+CONFIG_MSM_SMD_PKG4=y
+CONFIG_MSM_IPC_LOGGING=y
+CONFIG_MSM_IPC_ROUTER=y
+CONFIG_MSM_IPC_ROUTER_SMD_XPRT=y
+CONFIG_MSM_IPC_ROUTER_SECURITY=y
+CONFIG_MSM_SUBSYSTEM_RESTART=y
+CONFIG_MSM_SYSMON_COMM=y
+CONFIG_MSM_DIRECT_SCLK_ACCESS=y
+CONFIG_MSM_WATCHDOG_V2=y
+CONFIG_MSM_DLOAD_MODE=y
+CONFIG_MSM_RUN_QUEUE_STATS=y
+CONFIG_MSM_SPM_V2=y
+CONFIG_MSM_L2_SPM=y
+CONFIG_MSM_OCMEM=y
+CONFIG_MSM_OCMEM_LOCAL_POWER_CTRL=y
+CONFIG_MSM_OCMEM_DEBUG=y
+CONFIG_MSM_OCMEM_NONSECURE=y
+CONFIG_SENSORS_ADSP=y
+CONFIG_MSM_RTB=y
+CONFIG_MSM_RTB_SEPARATE_CPUS=y
+CONFIG_MSM_CACHE_ERP=y
+CONFIG_MSM_L1_ERR_PANIC=y
+CONFIG_MSM_L1_RECOV_ERR_PANIC=y
+CONFIG_MSM_L1_ERR_LOG=y
+CONFIG_MSM_L2_ERP_PRINT_ACCESS_ERRORS=y
+CONFIG_MSM_L2_ERP_PORT_PANIC=y
+CONFIG_MSM_L2_ERP_1BIT_PANIC=y
+CONFIG_MSM_L2_ERP_2BIT_PANIC=y
+CONFIG_MSM_CACHE_DUMP=y
+CONFIG_MSM_CACHE_DUMP_ON_PANIC=y
+CONFIG_MSM_ENABLE_WDOG_DEBUG_CONTROL=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_SMP=y
+# CONFIG_SMP_ON_UP is not set
+CONFIG_SCHED_MC=y
+CONFIG_ARM_ARCH_TIMER=y
+CONFIG_PREEMPT=y
+CONFIG_AEABI=y
+CONFIG_HIGHMEM=y
+CONFIG_CC_STACKPROTECTOR=y
+CONFIG_CP_ACCESS=y
+CONFIG_USE_OF=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_CPU_IDLE=y
+CONFIG_VFP=y
+CONFIG_NEON=y
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+CONFIG_PM_RUNTIME=y
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_INET=y
+CONFIG_IP_ADVANCED_ROUTER=y
+CONFIG_IP_MULTIPLE_TABLES=y
+CONFIG_IP_ROUTE_VERBOSE=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+# CONFIG_INET_XFRM_MODE_TRANSPORT is not set
+# CONFIG_INET_XFRM_MODE_TUNNEL is not set
+# CONFIG_INET_XFRM_MODE_BEET is not set
+# CONFIG_INET_LRO is not set
+CONFIG_IPV6=y
+CONFIG_IPV6_PRIVACY=y
+CONFIG_IPV6_ROUTER_PREF=y
+CONFIG_IPV6_ROUTE_INFO=y
+CONFIG_IPV6_OPTIMISTIC_DAD=y
+CONFIG_INET6_AH=y
+CONFIG_INET6_ESP=y
+CONFIG_INET6_IPCOMP=y
+CONFIG_IPV6_MIP6=y
+CONFIG_IPV6_MULTIPLE_TABLES=y
+CONFIG_IPV6_SUBTREES=y
+CONFIG_NETFILTER=y
+CONFIG_NETFILTER_NETLINK_LOG=y
+CONFIG_NF_CONNTRACK=y
+CONFIG_NF_CONNTRACK_EVENTS=y
+CONFIG_NF_CT_PROTO_DCCP=y
+CONFIG_NF_CT_PROTO_SCTP=y
+CONFIG_NF_CT_PROTO_UDPLITE=y
+CONFIG_NF_CONNTRACK_AMANDA=y
+CONFIG_NF_CONNTRACK_FTP=y
+CONFIG_NF_CONNTRACK_H323=y
+CONFIG_NF_CONNTRACK_IRC=y
+CONFIG_NF_CONNTRACK_NETBIOS_NS=y
+CONFIG_NF_CONNTRACK_PPTP=y
+CONFIG_NF_CONNTRACK_SANE=y
+CONFIG_NF_CONNTRACK_SIP=y
+CONFIG_NF_CONNTRACK_TFTP=y
+CONFIG_NF_CT_NETLINK=y
+CONFIG_NETFILTER_TPROXY=y
+CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y
+CONFIG_NETFILTER_XT_TARGET_CONNMARK=y
+CONFIG_NETFILTER_XT_TARGET_LOG=y
+CONFIG_NETFILTER_XT_TARGET_MARK=y
+CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y
+CONFIG_NETFILTER_XT_MATCH_COMMENT=y
+CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y
+CONFIG_NETFILTER_XT_MATCH_CONNMARK=y
+CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y
+CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y
+CONFIG_NETFILTER_XT_MATCH_HELPER=y
+CONFIG_NETFILTER_XT_MATCH_IPRANGE=y
+CONFIG_NETFILTER_XT_MATCH_LENGTH=y
+CONFIG_NETFILTER_XT_MATCH_LIMIT=y
+CONFIG_NETFILTER_XT_MATCH_MAC=y
+CONFIG_NETFILTER_XT_MATCH_MARK=y
+CONFIG_NETFILTER_XT_MATCH_MULTIPORT=y
+CONFIG_NETFILTER_XT_MATCH_POLICY=y
+CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y
+CONFIG_NETFILTER_XT_MATCH_QTAGUID=y
+CONFIG_NETFILTER_XT_MATCH_QUOTA=y
+CONFIG_NETFILTER_XT_MATCH_QUOTA2=y
+CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG=y
+CONFIG_NETFILTER_XT_MATCH_SOCKET=y
+CONFIG_NETFILTER_XT_MATCH_STATE=y
+CONFIG_NETFILTER_XT_MATCH_STATISTIC=y
+CONFIG_NETFILTER_XT_MATCH_STRING=y
+CONFIG_NETFILTER_XT_MATCH_TIME=y
+CONFIG_NETFILTER_XT_MATCH_U32=y
+CONFIG_NF_CONNTRACK_IPV4=y
+CONFIG_IP_NF_IPTABLES=y
+CONFIG_IP_NF_MATCH_AH=y
+CONFIG_IP_NF_MATCH_ECN=y
+CONFIG_IP_NF_MATCH_TTL=y
+CONFIG_IP_NF_FILTER=y
+CONFIG_IP_NF_TARGET_REJECT=y
+CONFIG_IP_NF_MANGLE=y
+CONFIG_IP_NF_RAW=y
+CONFIG_IP_NF_ARPTABLES=y
+CONFIG_IP_NF_ARPFILTER=y
+CONFIG_IP_NF_ARP_MANGLE=y
+CONFIG_NF_CONNTRACK_IPV6=y
+CONFIG_IP6_NF_IPTABLES=y
+CONFIG_IP6_NF_FILTER=y
+CONFIG_IP6_NF_TARGET_REJECT=y
+CONFIG_IP6_NF_MANGLE=y
+CONFIG_IP6_NF_RAW=y
+CONFIG_NET_SCHED=y
+CONFIG_NET_SCH_HTB=y
+CONFIG_NET_SCH_PRIO=y
+CONFIG_NET_CLS_FW=y
+CONFIG_NET_CLS_U32=y
+CONFIG_CLS_U32_MARK=y
+CONFIG_NET_CLS_FLOW=y
+CONFIG_NET_EMATCH=y
+CONFIG_NET_EMATCH_CMP=y
+CONFIG_NET_EMATCH_NBYTE=y
+CONFIG_NET_EMATCH_U32=y
+CONFIG_NET_EMATCH_META=y
+CONFIG_NET_EMATCH_TEXT=y
+CONFIG_NET_CLS_ACT=y
+CONFIG_CFG80211=y
+CONFIG_RFKILL=y
+CONFIG_GENLOCK=y
+CONFIG_GENLOCK_MISCDEVICE=y
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_RAM=y
+CONFIG_HAPTIC_ISA1200=y
+CONFIG_USB_HSIC_SMSC_HUB=y
+CONFIG_TI_DRV2667=y
+CONFIG_SCSI=y
+CONFIG_SCSI_TGT=y
+CONFIG_BLK_DEV_SD=y
+CONFIG_CHR_DEV_SG=y
+CONFIG_CHR_DEV_SCH=y
+CONFIG_SCSI_MULTI_LUN=y
+CONFIG_SCSI_CONSTANTS=y
+CONFIG_SCSI_LOGGING=y
+CONFIG_SCSI_SCAN_ASYNC=y
+CONFIG_MD=y
+CONFIG_BLK_DEV_DM=y
+CONFIG_DM_CRYPT=y
+CONFIG_NETDEVICES=y
+CONFIG_DUMMY=y
+CONFIG_TUN=y
+CONFIG_KS8851=m
+# CONFIG_MSM_RMNET is not set
+CONFIG_SLIP=y
+CONFIG_SLIP_COMPRESSED=y
+CONFIG_SLIP_MODE_SLIP6=y
+CONFIG_USB_USBNET=y
+CONFIG_INPUT_EVDEV=y
+CONFIG_INPUT_EVBUG=m
+CONFIG_KEYBOARD_GPIO=y
+CONFIG_INPUT_JOYSTICK=y
+CONFIG_INPUT_TOUCHSCREEN=y
+CONFIG_TOUCHSCREEN_ATMEL_MXT=y
+CONFIG_INPUT_MISC=y
+CONFIG_INPUT_UINPUT=y
+CONFIG_SERIAL_MSM_HSL=y
+CONFIG_SERIAL_MSM_HSL_CONSOLE=y
+CONFIG_HW_RANDOM=y
+CONFIG_HW_RANDOM_MSM=y
+CONFIG_I2C=y
+CONFIG_I2C_CHARDEV=y
+CONFIG_I2C_QUP=y
+CONFIG_SPI=y
+CONFIG_SPI_SPIDEV=m
+CONFIG_SPMI=y
+CONFIG_SPMI_MSM_PMIC_ARB=y
+CONFIG_MSM_QPNP_INT=y
+CONFIG_DEBUG_GPIO=y
+CONFIG_GPIO_SYSFS=y
+CONFIG_GPIO_QPNP_PIN=y
+CONFIG_GPIO_QPNP_PIN_DEBUG=y
+CONFIG_POWER_SUPPLY=y
+CONFIG_SMB350_CHARGER=y
+CONFIG_BATTERY_BQ28400=y
+CONFIG_QPNP_CHARGER=y
+CONFIG_BATTERY_BCL=y
+CONFIG_SENSORS_EPM_ADC=y
+CONFIG_SENSORS_QPNP_ADC_VOLTAGE=y
+CONFIG_SENSORS_QPNP_ADC_CURRENT=y
+CONFIG_THERMAL=y
+CONFIG_THERMAL_QPNP=y
+CONFIG_THERMAL_QPNP_ADC_TM=y
+CONFIG_REGULATOR=y
+CONFIG_REGULATOR_FIXED_VOLTAGE=y
+CONFIG_REGULATOR_STUB=y
+CONFIG_REGULATOR_QPNP=y
+CONFIG_MEDIA_SUPPORT=y
+CONFIG_MEDIA_CONTROLLER=y
+CONFIG_MSM_KGSL=y
+CONFIG_FB=y
+CONFIG_FB_MSM=y
+# CONFIG_FB_MSM_BACKLIGHT is not set
+CONFIG_FB_MSM_MDSS=y
+CONFIG_FB_MSM_MDSS_WRITEBACK=y
+CONFIG_FB_MSM_MDSS_HDMI_PANEL=y
+CONFIG_FB_MSM_MDSS_HDMI_MHL_SII8334=y
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+# CONFIG_LCD_CLASS_DEVICE is not set
+CONFIG_BACKLIGHT_CLASS_DEVICE=y
+# CONFIG_BACKLIGHT_GENERIC is not set
+CONFIG_USB=y
+CONFIG_USB_SUSPEND=y
+CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_EHCI_MSM=y
+CONFIG_USB_EHCI_MSM_HSIC=y
+CONFIG_USB_ACM=y
+CONFIG_USB_STORAGE=y
+CONFIG_USB_STORAGE_DATAFAB=y
+CONFIG_USB_STORAGE_FREECOM=y
+CONFIG_USB_STORAGE_ISD200=y
+CONFIG_USB_STORAGE_USBAT=y
+CONFIG_USB_STORAGE_SDDR09=y
+CONFIG_USB_STORAGE_SDDR55=y
+CONFIG_USB_STORAGE_JUMPSHOT=y
+CONFIG_USB_STORAGE_ALAUDA=y
+CONFIG_USB_STORAGE_ONETOUCH=y
+CONFIG_USB_STORAGE_KARMA=y
+CONFIG_USB_STORAGE_CYPRESS_ATACB=y
+CONFIG_USB_STORAGE_ENE_UB6250=y
+CONFIG_LEDS_QPNP=y
+CONFIG_LEDS_TRIGGERS=y
+CONFIG_LEDS_TRIGGER_BACKLIGHT=y
+CONFIG_LEDS_TRIGGER_DEFAULT_ON=y
+CONFIG_SWITCH=y
+CONFIG_STAGING=y
+CONFIG_ANDROID=y
+CONFIG_ANDROID_BINDER_IPC=y
+CONFIG_ASHMEM=y
+CONFIG_ANDROID_LOGGER=y
+CONFIG_ANDROID_TIMED_GPIO=y
+CONFIG_ANDROID_LOW_MEMORY_KILLER=y
+CONFIG_MSM_SSBI=y
+CONFIG_SPS=y
+CONFIG_SPS_SUPPORT_BAMDMA=y
+CONFIG_SPS_SUPPORT_NDP_BAM=y
+CONFIG_QPNP_PWM=y
+CONFIG_QPNP_POWER_ON=y
+CONFIG_QPNP_CLKDIV=y
+CONFIG_MSM_IOMMU=y
+CONFIG_EXT2_FS=y
+CONFIG_EXT2_FS_XATTR=y
+CONFIG_EXT3_FS=y
+# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
+CONFIG_EXT4_FS=y
+CONFIG_FUSE_FS=y
+CONFIG_VFAT_FS=y
+CONFIG_TMPFS=y
+CONFIG_PSTORE=y
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_ASCII=y
+CONFIG_NLS_ISO8859_1=y
+CONFIG_PRINTK_TIME=y
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_LOCKUP_DETECTOR=y
+# CONFIG_DETECT_HUNG_TASK is not set
+CONFIG_SCHEDSTATS=y
+CONFIG_TIMER_STATS=y
+CONFIG_DEBUG_KMEMLEAK=y
+CONFIG_DEBUG_KMEMLEAK_DEFAULT_OFF=y
+CONFIG_DEBUG_SPINLOCK=y
+CONFIG_DEBUG_MUTEXES=y
+CONFIG_DEBUG_ATOMIC_SLEEP=y
+CONFIG_DEBUG_STACK_USAGE=y
+CONFIG_DEBUG_INFO=y
+CONFIG_DEBUG_MEMORY_INIT=y
+CONFIG_DEBUG_LIST=y
+CONFIG_FAULT_INJECTION=y
+CONFIG_FAIL_PAGE_ALLOC=y
+CONFIG_FAULT_INJECTION_DEBUG_FS=y
+CONFIG_FAULT_INJECTION_STACKTRACE_FILTER=y
+CONFIG_DEBUG_PAGEALLOC=y
+CONFIG_CPU_FREQ_SWITCH_PROFILER=y
+CONFIG_DYNAMIC_DEBUG=y
+CONFIG_DEBUG_USER=y
+CONFIG_DEBUG_LL=y
+CONFIG_EARLY_PRINTK=y
+CONFIG_PID_IN_CONTEXTIDR=y
+CONFIG_KEYS=y
+CONFIG_CRYPTO_MD4=y
+CONFIG_CRYPTO_SHA256=y
+CONFIG_CRYPTO_ARC4=y
+CONFIG_CRYPTO_TWOFISH=y
+CONFIG_CRYPTO_DEV_QCRYPTO=m
+CONFIG_CRYPTO_DEV_QCE=m
+CONFIG_CRYPTO_DEV_QCEDEV=m
+CONFIG_CRC_CCITT=y
diff --git a/arch/arm/configs/msm8226_defconfig b/arch/arm/configs/msm8226_defconfig
new file mode 100644
index 0000000..07a15d9
--- /dev/null
+++ b/arch/arm/configs/msm8226_defconfig
@@ -0,0 +1,383 @@
+# CONFIG_ARM_PATCH_PHYS_VIRT is not set
+CONFIG_EXPERIMENTAL=y
+# CONFIG_LOCALVERSION_AUTO is not set
+CONFIG_SYSVIPC=y
+CONFIG_RCU_FAST_NO_HZ=y
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_CGROUPS=y
+CONFIG_CGROUP_DEBUG=y
+CONFIG_CGROUP_FREEZER=y
+CONFIG_CGROUP_CPUACCT=y
+CONFIG_RESOURCE_COUNTERS=y
+CONFIG_CGROUP_SCHED=y
+CONFIG_RT_GROUP_SCHED=y
+CONFIG_NAMESPACES=y
+# CONFIG_UTS_NS is not set
+# CONFIG_IPC_NS is not set
+# CONFIG_USER_NS is not set
+# CONFIG_PID_NS is not set
+CONFIG_RELAY=y
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_RD_BZIP2=y
+CONFIG_RD_LZMA=y
+CONFIG_PANIC_TIMEOUT=5
+CONFIG_KALLSYMS_ALL=y
+CONFIG_EMBEDDED=y
+CONFIG_PROFILING=y
+CONFIG_OPROFILE=m
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODULE_FORCE_UNLOAD=y
+CONFIG_MODVERSIONS=y
+CONFIG_PARTITION_ADVANCED=y
+CONFIG_EFI_PARTITION=y
+CONFIG_ARCH_MSM=y
+CONFIG_ARCH_MSM8226=y
+# CONFIG_MSM_STACKED_MEMORY is not set
+CONFIG_CPU_HAS_L2_PMU=y
+# CONFIG_MSM_FIQ_SUPPORT is not set
+# CONFIG_MSM_PROC_COMM is not set
+CONFIG_MSM_SMD=y
+CONFIG_MSM_SMD_PKG4=y
+CONFIG_MSM_BAM_DMUX=y
+CONFIG_MSM_SMP2P=y
+CONFIG_MSM_SMP2P_TEST=y
+CONFIG_MSM_IPC_LOGGING=y
+CONFIG_MSM_IPC_ROUTER=y
+CONFIG_MSM_IPC_ROUTER_SMD_XPRT=y
+CONFIG_MSM_QMI_INTERFACE=y
+CONFIG_MSM_SUBSYSTEM_RESTART=y
+CONFIG_MSM_SYSMON_COMM=y
+CONFIG_MSM_PIL_LPASS_QDSP6V5=y
+CONFIG_MSM_PIL_MSS_QDSP6V5=y
+CONFIG_MSM_PIL_VENUS=y
+CONFIG_MSM_PIL_PRONTO=y
+CONFIG_MSM_TZ_LOG=y
+CONFIG_MSM_DIRECT_SCLK_ACCESS=y
+CONFIG_MSM_WATCHDOG_V2=y
+CONFIG_MSM_MEMORY_DUMP=y
+CONFIG_MSM_DLOAD_MODE=y
+CONFIG_MSM_ADSP_LOADER=m
+CONFIG_MSM_OCMEM=y
+CONFIG_MSM_OCMEM_LOCAL_POWER_CTRL=y
+CONFIG_MSM_OCMEM_DEBUG=y
+CONFIG_MSM_OCMEM_NONSECURE=y
+CONFIG_MSM_OCMEM_POWER_DISABLE=y
+CONFIG_SENSORS_ADSP=y
+CONFIG_MSM_RTB=y
+CONFIG_MSM_RTB_SEPARATE_CPUS=y
+CONFIG_MSM_ENABLE_WDOG_DEBUG_CONTROL=y
+CONFIG_MSM_BOOT_STATS=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_SMP=y
+CONFIG_SCHED_MC=y
+CONFIG_ARM_ARCH_TIMER=y
+CONFIG_PREEMPT=y
+CONFIG_AEABI=y
+CONFIG_HIGHMEM=y
+CONFIG_USE_OF=y
+CONFIG_CPU_IDLE=y
+CONFIG_VFP=y
+CONFIG_NEON=y
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+CONFIG_PM_AUTOSLEEP=y
+CONFIG_PM_WAKELOCKS=y
+CONFIG_PM_WAKELOCKS_LIMIT=0
+# CONFIG_PM_WAKELOCKS_GC is not set
+CONFIG_PM_RUNTIME=y
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_INET=y
+CONFIG_IP_ADVANCED_ROUTER=y
+CONFIG_IP_MULTIPLE_TABLES=y
+CONFIG_IP_ROUTE_VERBOSE=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+# CONFIG_INET_XFRM_MODE_TRANSPORT is not set
+# CONFIG_INET_XFRM_MODE_TUNNEL is not set
+# CONFIG_INET_XFRM_MODE_BEET is not set
+# CONFIG_INET_LRO is not set
+CONFIG_IPV6=y
+CONFIG_IPV6_PRIVACY=y
+CONFIG_IPV6_ROUTER_PREF=y
+CONFIG_IPV6_ROUTE_INFO=y
+CONFIG_IPV6_OPTIMISTIC_DAD=y
+CONFIG_INET6_AH=y
+CONFIG_INET6_ESP=y
+CONFIG_INET6_IPCOMP=y
+CONFIG_IPV6_MIP6=y
+CONFIG_IPV6_MULTIPLE_TABLES=y
+CONFIG_IPV6_SUBTREES=y
+CONFIG_NETFILTER=y
+CONFIG_NETFILTER_NETLINK_LOG=y
+CONFIG_NF_CONNTRACK=y
+CONFIG_NF_CONNTRACK_EVENTS=y
+CONFIG_NF_CT_PROTO_DCCP=y
+CONFIG_NF_CT_PROTO_SCTP=y
+CONFIG_NF_CT_PROTO_UDPLITE=y
+CONFIG_NF_CONNTRACK_AMANDA=y
+CONFIG_NF_CONNTRACK_FTP=y
+CONFIG_NF_CONNTRACK_H323=y
+CONFIG_NF_CONNTRACK_IRC=y
+CONFIG_NF_CONNTRACK_NETBIOS_NS=y
+CONFIG_NF_CONNTRACK_PPTP=y
+CONFIG_NF_CONNTRACK_SANE=y
+CONFIG_NF_CONNTRACK_SIP=y
+CONFIG_NF_CONNTRACK_TFTP=y
+CONFIG_NF_CT_NETLINK=y
+CONFIG_NETFILTER_TPROXY=y
+CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y
+CONFIG_NETFILTER_XT_TARGET_CONNMARK=y
+CONFIG_NETFILTER_XT_TARGET_LOG=y
+CONFIG_NETFILTER_XT_TARGET_MARK=y
+CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y
+CONFIG_NETFILTER_XT_MATCH_COMMENT=y
+CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y
+CONFIG_NETFILTER_XT_MATCH_CONNMARK=y
+CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y
+CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y
+CONFIG_NETFILTER_XT_MATCH_HELPER=y
+CONFIG_NETFILTER_XT_MATCH_IPRANGE=y
+CONFIG_NETFILTER_XT_MATCH_LENGTH=y
+CONFIG_NETFILTER_XT_MATCH_LIMIT=y
+CONFIG_NETFILTER_XT_MATCH_MAC=y
+CONFIG_NETFILTER_XT_MATCH_MARK=y
+CONFIG_NETFILTER_XT_MATCH_MULTIPORT=y
+CONFIG_NETFILTER_XT_MATCH_POLICY=y
+CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y
+CONFIG_NETFILTER_XT_MATCH_QTAGUID=y
+CONFIG_NETFILTER_XT_MATCH_QUOTA=y
+CONFIG_NETFILTER_XT_MATCH_QUOTA2=y
+CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG=y
+CONFIG_NETFILTER_XT_MATCH_SOCKET=y
+CONFIG_NETFILTER_XT_MATCH_STATE=y
+CONFIG_NETFILTER_XT_MATCH_STATISTIC=y
+CONFIG_NETFILTER_XT_MATCH_STRING=y
+CONFIG_NETFILTER_XT_MATCH_TIME=y
+CONFIG_NETFILTER_XT_MATCH_U32=y
+CONFIG_NF_CONNTRACK_IPV4=y
+CONFIG_IP_NF_IPTABLES=y
+CONFIG_IP_NF_MATCH_AH=y
+CONFIG_IP_NF_MATCH_ECN=y
+CONFIG_IP_NF_MATCH_TTL=y
+CONFIG_IP_NF_FILTER=y
+CONFIG_IP_NF_TARGET_REJECT=y
+CONFIG_NF_NAT=y
+CONFIG_IP_NF_TARGET_MASQUERADE=y
+CONFIG_IP_NF_TARGET_NETMAP=y
+CONFIG_IP_NF_TARGET_REDIRECT=y
+CONFIG_IP_NF_MANGLE=y
+CONFIG_IP_NF_RAW=y
+CONFIG_IP_NF_ARPTABLES=y
+CONFIG_IP_NF_ARPFILTER=y
+CONFIG_IP_NF_ARP_MANGLE=y
+CONFIG_NF_CONNTRACK_IPV6=y
+CONFIG_IP6_NF_IPTABLES=y
+CONFIG_IP6_NF_FILTER=y
+CONFIG_IP6_NF_TARGET_REJECT=y
+CONFIG_IP6_NF_MANGLE=y
+CONFIG_IP6_NF_RAW=y
+CONFIG_BRIDGE_NF_EBTABLES=y
+CONFIG_BRIDGE_EBT_BROUTE=y
+CONFIG_BRIDGE=y
+CONFIG_NET_SCHED=y
+CONFIG_NET_SCH_HTB=y
+CONFIG_NET_SCH_PRIO=y
+CONFIG_NET_CLS_FW=y
+CONFIG_BT=y
+CONFIG_BT_RFCOMM=y
+CONFIG_BT_RFCOMM_TTY=y
+CONFIG_BT_BNEP=y
+CONFIG_BT_BNEP_MC_FILTER=y
+CONFIG_BT_BNEP_PROTO_FILTER=y
+CONFIG_BT_HIDP=y
+CONFIG_BT_HCISMD=y
+CONFIG_CFG80211=y
+CONFIG_NL80211_TESTMODE=y
+CONFIG_CMA=y
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_RAM=y
+CONFIG_MD=y
+CONFIG_BLK_DEV_DM=y
+CONFIG_DM_CRYPT=y
+CONFIG_NETDEVICES=y
+CONFIG_DUMMY=y
+# CONFIG_MSM_RMNET is not set
+CONFIG_MSM_RMNET_BAM=y
+CONFIG_WCNSS_CORE=y
+CONFIG_WCNSS_CORE_PRONTO=y
+CONFIG_WCNSS_MEM_PRE_ALLOC=y
+CONFIG_INPUT_EVDEV=y
+CONFIG_INPUT_EVBUG=m
+CONFIG_KEYBOARD_GPIO=y
+CONFIG_INPUT_TOUCHSCREEN=y
+CONFIG_TOUCHSCREEN_ATMEL_MXT=y
+CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4=y
+CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_RMI4_DEV=y
+CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE=y
+CONFIG_INPUT_MISC=y
+CONFIG_INPUT_UINPUT=y
+CONFIG_INPUT_GPIO=m
+CONFIG_SERIAL_MSM_HSL=y
+CONFIG_SERIAL_MSM_HSL_CONSOLE=y
+CONFIG_DIAG_CHAR=y
+CONFIG_HW_RANDOM=y
+CONFIG_HW_RANDOM_MSM=y
+CONFIG_I2C=y
+CONFIG_I2C_CHARDEV=y
+CONFIG_I2C_QUP=y
+CONFIG_SPI=y
+CONFIG_SPI_QUP=y
+CONFIG_SPI_SPIDEV=m
+CONFIG_SPMI=y
+CONFIG_MSM_BUS_SCALING=y
+CONFIG_SPMI_MSM_PMIC_ARB=y
+CONFIG_MSM_QPNP_INT=y
+CONFIG_SLIMBUS_MSM_NGD=y
+CONFIG_DEBUG_GPIO=y
+CONFIG_GPIO_SYSFS=y
+CONFIG_GPIO_QPNP_PIN=y
+CONFIG_POWER_SUPPLY=y
+CONFIG_QPNP_CHARGER=y
+CONFIG_QPNP_BMS=y
+CONFIG_SENSORS_QPNP_ADC_VOLTAGE=y
+CONFIG_SENSORS_QPNP_ADC_CURRENT=y
+CONFIG_THERMAL=y
+CONFIG_THERMAL_TSENS8974=y
+CONFIG_THERMAL_MONITOR=y
+CONFIG_THERMAL_QPNP_ADC_TM=y
+CONFIG_WCD9306_CODEC=y
+CONFIG_REGULATOR_STUB=y
+CONFIG_REGULATOR_QPNP=y
+CONFIG_MEDIA_SUPPORT=y
+CONFIG_MEDIA_CONTROLLER=y
+CONFIG_VIDEO_DEV=y
+CONFIG_VIDEO_V4L2_SUBDEV_API=y
+# CONFIG_MSM_CAMERA is not set
+CONFIG_OV8825=y
+CONFIG_MSM_CAMERA_SENSOR=y
+CONFIG_MSM_CPP=y
+CONFIG_MSM_CCI=y
+CONFIG_MSM_CSI30_HEADER=y
+CONFIG_MSM_CSIPHY=y
+CONFIG_MSM_CSID=y
+CONFIG_MSM_ISPIF=y
+CONFIG_MSMB_CAMERA=y
+CONFIG_OV9724=y
+CONFIG_MSMB_JPEG=y
+CONFIG_SWITCH=y
+CONFIG_MSM_WFD=y
+CONFIG_MSM_VIDC_V4L2=y
+CONFIG_VIDEOBUF2_MSM_MEM=y
+CONFIG_V4L_PLATFORM_DRIVERS=y
+CONFIG_RADIO_IRIS=y
+CONFIG_RADIO_IRIS_TRANSPORT=m
+CONFIG_ION=y
+CONFIG_ION_MSM=y
+CONFIG_MSM_KGSL=y
+CONFIG_FB=y
+CONFIG_FB_MSM=y
+# CONFIG_FB_MSM_BACKLIGHT is not set
+CONFIG_FB_MSM_MDSS=y
+CONFIG_FB_MSM_MDSS_WRITEBACK=y
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+# CONFIG_LCD_CLASS_DEVICE is not set
+CONFIG_BACKLIGHT_CLASS_DEVICE=y
+# CONFIG_BACKLIGHT_GENERIC is not set
+CONFIG_SOUND=y
+CONFIG_SND=y
+CONFIG_SND_SOC=y
+CONFIG_SND_SOC_MSM8226=y
+CONFIG_USB_GADGET=y
+CONFIG_USB_GADGET_DEBUG_FILES=y
+CONFIG_USB_GADGET_DEBUG_FS=y
+CONFIG_USB_CI13XXX_MSM=y
+CONFIG_USB_G_ANDROID=y
+CONFIG_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_SDHCI=y
+CONFIG_MMC_SDHCI_PLTFM=y
+CONFIG_MMC_MSM=y
+CONFIG_MMC_SDHCI_MSM=y
+CONFIG_MMC_MSM_SPS_SUPPORT=y
+CONFIG_LEDS_QPNP=y
+CONFIG_LEDS_TRIGGERS=y
+CONFIG_RTC_CLASS=y
+# CONFIG_RTC_DRV_MSM is not set
+CONFIG_RTC_DRV_QPNP=y
+CONFIG_STAGING=y
+CONFIG_ANDROID=y
+CONFIG_ANDROID_BINDER_IPC=y
+CONFIG_ASHMEM=y
+CONFIG_ANDROID_LOGGER=y
+CONFIG_ANDROID_RAM_CONSOLE=y
+CONFIG_ANDROID_TIMED_GPIO=y
+CONFIG_ANDROID_LOW_MEMORY_KILLER=y
+CONFIG_SPS=y
+CONFIG_USB_BAM=y
+CONFIG_SPS_SUPPORT_NDP_BAM=y
+CONFIG_QPNP_PWM=y
+CONFIG_QPNP_POWER_ON=y
+CONFIG_MSM_IOMMU=y
+CONFIG_CORESIGHT=y
+CONFIG_CORESIGHT_TMC=y
+CONFIG_CORESIGHT_TPIU=y
+CONFIG_CORESIGHT_FUNNEL=y
+CONFIG_CORESIGHT_REPLICATOR=y
+CONFIG_CORESIGHT_STM=y
+CONFIG_CORESIGHT_HWEVENT=y
+CONFIG_CORESIGHT_ETM=y
+CONFIG_CORESIGHT_EVENT=m
+CONFIG_EXT2_FS=y
+CONFIG_EXT2_FS_XATTR=y
+CONFIG_EXT3_FS=y
+# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
+CONFIG_EXT4_FS=y
+CONFIG_FUSE_FS=y
+CONFIG_VFAT_FS=y
+CONFIG_TMPFS=y
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_ASCII=y
+CONFIG_NLS_ISO8859_1=y
+CONFIG_PRINTK_TIME=y
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_SCHEDSTATS=y
+CONFIG_TIMER_STATS=y
+CONFIG_DEBUG_KMEMLEAK=y
+CONFIG_DEBUG_KMEMLEAK_DEFAULT_OFF=y
+CONFIG_DEBUG_SPINLOCK=y
+CONFIG_DEBUG_MUTEXES=y
+CONFIG_DEBUG_ATOMIC_SLEEP=y
+CONFIG_DEBUG_STACK_USAGE=y
+CONFIG_DEBUG_INFO=y
+CONFIG_DEBUG_MEMORY_INIT=y
+CONFIG_DEBUG_LIST=y
+CONFIG_FAULT_INJECTION=y
+CONFIG_FAIL_PAGE_ALLOC=y
+CONFIG_FAULT_INJECTION_DEBUG_FS=y
+CONFIG_FAULT_INJECTION_STACKTRACE_FILTER=y
+CONFIG_DEBUG_PAGEALLOC=y
+CONFIG_ENABLE_DEFAULT_TRACERS=y
+CONFIG_DYNAMIC_DEBUG=y
+CONFIG_DEBUG_USER=y
+CONFIG_KEYS=y
+CONFIG_CRYPTO_MD4=y
+CONFIG_CRYPTO_ARC4=y
+CONFIG_CRYPTO_TWOFISH=y
+# CONFIG_CRYPTO_HW is not set
+CONFIG_CRC_CCITT=y
+CONFIG_QPNP_VIBRATOR=y
+CONFIG_QSEECOM=y
+CONFIG_CRYPTO_HW=y
+CONFIG_CRYPTO_DEV_QCRYPTO=m
+CONFIG_CRYPTO_DEV_QCE=y
+CONFIG_CRYPTO_DEV_QCEDEV=m
\ No newline at end of file
diff --git a/arch/arm/configs/msm8610-perf_defconfig b/arch/arm/configs/msm8610-perf_defconfig
index 5660dec..b385669 100644
--- a/arch/arm/configs/msm8610-perf_defconfig
+++ b/arch/arm/configs/msm8610-perf_defconfig
@@ -62,6 +62,7 @@
CONFIG_MSM_MEMORY_DUMP=y
CONFIG_MSM_DLOAD_MODE=y
CONFIG_MSM_ENABLE_WDOG_DEBUG_CONTROL=y
+CONFIG_MSM_BOOT_STATS=y
CONFIG_MSM_ADSP_LOADER=m
CONFIG_MSM_OCMEM=y
CONFIG_MSM_OCMEM_LOCAL_POWER_CTRL=y
@@ -331,14 +332,6 @@
CONFIG_QPNP_PWM=y
CONFIG_QPNP_POWER_ON=y
CONFIG_MSM_IOMMU=y
-CONFIG_CORESIGHT=y
-CONFIG_CORESIGHT_TMC=y
-CONFIG_CORESIGHT_TPIU=y
-CONFIG_CORESIGHT_FUNNEL=y
-CONFIG_CORESIGHT_REPLICATOR=y
-CONFIG_CORESIGHT_STM=y
-CONFIG_CORESIGHT_ETM=y
-CONFIG_CORESIGHT_EVENT=m
CONFIG_EXT2_FS=y
CONFIG_EXT2_FS_XATTR=y
CONFIG_EXT3_FS=y
diff --git a/arch/arm/configs/msm8610_defconfig b/arch/arm/configs/msm8610_defconfig
index 09b038c..5e38ef1 100644
--- a/arch/arm/configs/msm8610_defconfig
+++ b/arch/arm/configs/msm8610_defconfig
@@ -69,6 +69,7 @@
CONFIG_MSM_RTB=y
CONFIG_MSM_RTB_SEPARATE_CPUS=y
CONFIG_MSM_ENABLE_WDOG_DEBUG_CONTROL=y
+CONFIG_MSM_BOOT_STATS=y
CONFIG_NO_HZ=y
CONFIG_HIGH_RES_TIMERS=y
CONFIG_SMP=y
@@ -335,6 +336,7 @@
CONFIG_CORESIGHT_FUNNEL=y
CONFIG_CORESIGHT_REPLICATOR=y
CONFIG_CORESIGHT_STM=y
+CONFIG_CORESIGHT_HWEVENT=y
CONFIG_CORESIGHT_ETM=y
CONFIG_CORESIGHT_EVENT=m
CONFIG_EXT2_FS=y
diff --git a/arch/arm/configs/msm8974-perf_defconfig b/arch/arm/configs/msm8974-perf_defconfig
index dc84558..bae4ee9 100644
--- a/arch/arm/configs/msm8974-perf_defconfig
+++ b/arch/arm/configs/msm8974-perf_defconfig
@@ -443,15 +443,6 @@
CONFIG_IOMMU_PGTABLES_L2=y
CONFIG_MOBICORE_SUPPORT=m
CONFIG_MOBICORE_API=m
-CONFIG_CORESIGHT=y
-CONFIG_CORESIGHT_TMC=y
-CONFIG_CORESIGHT_TPIU=y
-CONFIG_CORESIGHT_FUNNEL=y
-CONFIG_CORESIGHT_REPLICATOR=y
-CONFIG_CORESIGHT_STM=y
-CONFIG_CORESIGHT_ETM=y
-CONFIG_CORESIGHT_ETM_PCSAVE_DEFAULT_ENABLE=y
-CONFIG_CORESIGHT_EVENT=m
CONFIG_BIF=y
CONFIG_BIF_QPNP=y
CONFIG_EXT2_FS=y
@@ -484,3 +475,4 @@
CONFIG_CRYPTO_DEV_QCRYPTO=m
CONFIG_CRYPTO_DEV_QCE=y
CONFIG_CRYPTO_DEV_QCEDEV=y
+CONFIG_SND_SOC_MSM_HDMI_CODEC_RX=y
diff --git a/arch/arm/configs/msm8974_defconfig b/arch/arm/configs/msm8974_defconfig
index fd8a639..8036a44 100644
--- a/arch/arm/configs/msm8974_defconfig
+++ b/arch/arm/configs/msm8974_defconfig
@@ -455,6 +455,7 @@
CONFIG_CORESIGHT_FUNNEL=y
CONFIG_CORESIGHT_REPLICATOR=y
CONFIG_CORESIGHT_STM=y
+CONFIG_CORESIGHT_HWEVENT=y
CONFIG_CORESIGHT_ETM=y
CONFIG_CORESIGHT_ETM_PCSAVE_DEFAULT_ENABLE=y
CONFIG_CORESIGHT_EVENT=m
@@ -506,3 +507,4 @@
CONFIG_CRYPTO_DEV_QCRYPTO=m
CONFIG_CRYPTO_DEV_QCE=y
CONFIG_CRYPTO_DEV_QCEDEV=y
+CONFIG_SND_SOC_MSM_HDMI_CODEC_RX=y
diff --git a/arch/arm/configs/msm9625-perf_defconfig b/arch/arm/configs/msm9625-perf_defconfig
index 1e0134a..f434199 100644
--- a/arch/arm/configs/msm9625-perf_defconfig
+++ b/arch/arm/configs/msm9625-perf_defconfig
@@ -225,6 +225,7 @@
CONFIG_SENSORS_QPNP_ADC_VOLTAGE=y
CONFIG_THERMAL=y
CONFIG_THERMAL_TSENS8974=y
+CONFIG_THERMAL_QPNP_ADC_TM=y
CONFIG_WCD9320_CODEC=y
CONFIG_REGULATOR=y
CONFIG_REGULATOR_FIXED_VOLTAGE=y
@@ -257,6 +258,7 @@
CONFIG_USB_STORAGE_CYPRESS_ATACB=y
CONFIG_USB_EHSET_TEST_FIXTURE=y
CONFIG_USB_GADGET=y
+CONFIG_USB_GADGET_DEBUG_FILES=y
CONFIG_USB_CI13XXX_MSM=y
CONFIG_USB_G_ANDROID=y
CONFIG_MMC=y
@@ -278,14 +280,6 @@
CONFIG_SPS_SUPPORT_NDP_BAM=y
CONFIG_QPNP_POWER_ON=y
CONFIG_IPA=y
-CONFIG_CORESIGHT=y
-CONFIG_CORESIGHT_TMC=y
-CONFIG_CORESIGHT_TPIU=y
-CONFIG_CORESIGHT_FUNNEL=y
-CONFIG_CORESIGHT_REPLICATOR=y
-CONFIG_CORESIGHT_STM=y
-CONFIG_CORESIGHT_ETM=y
-CONFIG_CORESIGHT_EVENT=m
CONFIG_EXT3_FS=y
CONFIG_VFAT_FS=y
CONFIG_TMPFS=y
diff --git a/arch/arm/configs/msm9625_defconfig b/arch/arm/configs/msm9625_defconfig
index f7c3bff..2a1215d 100644
--- a/arch/arm/configs/msm9625_defconfig
+++ b/arch/arm/configs/msm9625_defconfig
@@ -225,6 +225,7 @@
CONFIG_SENSORS_QPNP_ADC_VOLTAGE=y
CONFIG_THERMAL=y
CONFIG_THERMAL_TSENS8974=y
+CONFIG_THERMAL_QPNP_ADC_TM=y
CONFIG_WCD9320_CODEC=y
CONFIG_REGULATOR=y
CONFIG_REGULATOR_FIXED_VOLTAGE=y
@@ -257,6 +258,7 @@
CONFIG_USB_STORAGE_CYPRESS_ATACB=y
CONFIG_USB_EHSET_TEST_FIXTURE=y
CONFIG_USB_GADGET=y
+CONFIG_USB_GADGET_DEBUG_FILES=y
CONFIG_USB_CI13XXX_MSM=y
CONFIG_USB_G_ANDROID=y
CONFIG_MMC=y
@@ -284,6 +286,7 @@
CONFIG_CORESIGHT_FUNNEL=y
CONFIG_CORESIGHT_REPLICATOR=y
CONFIG_CORESIGHT_STM=y
+CONFIG_CORESIGHT_HWEVENT=y
CONFIG_CORESIGHT_ETM=y
CONFIG_CORESIGHT_EVENT=m
CONFIG_EXT3_FS=y
@@ -317,8 +320,8 @@
CONFIG_CRYPTO_DES=y
CONFIG_CRYPTO_TWOFISH=y
CONFIG_CRYPTO_DEFLATE=y
-CONFIG_CRYPTO_DEV_QCRYPTO=m
-CONFIG_CRYPTO_DEV_QCE=m
-CONFIG_CRYPTO_DEV_QCEDEV=m
+#CONFIG_CRYPTO_DEV_QCRYPTO is not set
+#CONFIG_CRYPTO_DEV_QCE is not set
+#CONFIG_CRYPTO_DEV_QCEDEV is not set
CONFIG_CRC_CCITT=y
CONFIG_LIBCRC32C=y
diff --git a/arch/arm/include/asm/setup.h b/arch/arm/include/asm/setup.h
index 2a46914..07a09b5 100644
--- a/arch/arm/include/asm/setup.h
+++ b/arch/arm/include/asm/setup.h
@@ -196,7 +196,7 @@
struct membank {
phys_addr_t start;
- unsigned long size;
+ phys_addr_t size;
unsigned int highmem;
};
@@ -218,7 +218,7 @@
#define bank_phys_end(bank) ((bank)->start + (bank)->size)
#define bank_phys_size(bank) (bank)->size
-extern int arm_add_memory(phys_addr_t start, unsigned long size);
+extern int arm_add_memory(phys_addr_t start, phys_addr_t size);
extern void early_print(const char *str, ...);
extern void dump_machine_table(void);
diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c
index 4aabf0e..28b114f 100644
--- a/arch/arm/kernel/setup.c
+++ b/arch/arm/kernel/setup.c
@@ -510,7 +510,7 @@
/* can't use cpu_relax() here as it may require MMU setup */;
}
-int __init arm_add_memory(phys_addr_t start, unsigned long size)
+int __init arm_add_memory(phys_addr_t start, phys_addr_t size)
{
struct membank *bank = &meminfo.bank[meminfo.nr_banks];
@@ -540,7 +540,7 @@
}
#endif
- bank->size = size & PAGE_MASK;
+ bank->size = size & ~(phys_addr_t)(PAGE_SIZE - 1);
/*
* Check whether this memory region has non-zero size or
@@ -560,7 +560,7 @@
static int __init early_mem(char *p)
{
static int usermem __initdata = 0;
- unsigned long size;
+ phys_addr_t size;
phys_addr_t start;
char *endp;
diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile
index 8efc000..321040e 100644
--- a/arch/arm/mach-msm/Makefile
+++ b/arch/arm/mach-msm/Makefile
@@ -297,6 +297,7 @@
obj-$(CONFIG_ARCH_MSM9615) += board-9615.o devices-9615.o board-9615-regulator.o board-9615-gpiomux.o board-9615-storage.o board-9615-display.o
obj-$(CONFIG_ARCH_MSM9615) += clock-local.o clock-9615.o acpuclock-9615.o clock-rpm.o clock-pll.o
obj-$(CONFIG_ARCH_APQ8084) += board-8084.o board-8084-gpiomux.o
+obj-$(CONFIG_ARCH_APQ8084) += clock-8084.o
obj-$(CONFIG_ARCH_MSM8974) += board-8974.o board-8974-gpiomux.o
obj-$(CONFIG_ARCH_MSM8974) += acpuclock-8974.o
obj-$(CONFIG_ARCH_MSM8974) += clock-local2.o clock-pll.o clock-8974.o clock-rpm.o clock-voter.o clock-mdss-8974.o
diff --git a/arch/arm/mach-msm/acpuclock-8226.c b/arch/arm/mach-msm/acpuclock-8226.c
index a6f772d..5793326 100644
--- a/arch/arm/mach-msm/acpuclock-8226.c
+++ b/arch/arm/mach-msm/acpuclock-8226.c
@@ -80,8 +80,8 @@
{ 1, 384000, ACPUPLL, 5, 0, CPR_CORNER_SVS, 0, 3 },
{ 1, 600000, PLL0, 4, 0, CPR_CORNER_NORMAL, 0, 4 },
{ 1, 787200, ACPUPLL, 5, 0, CPR_CORNER_NORMAL, 0, 4 },
- { 0, 998400, ACPUPLL, 5, 0, CPR_CORNER_TURBO, 0, 5 },
- { 0, 1190400, ACPUPLL, 5, 0, CPR_CORNER_TURBO, 0, 5 },
+ { 1, 998400, ACPUPLL, 5, 0, CPR_CORNER_TURBO, 0, 5 },
+ { 1, 1190400, ACPUPLL, 5, 0, CPR_CORNER_TURBO, 0, 5 },
{ 0 }
};
diff --git a/arch/arm/mach-msm/board-8084.c b/arch/arm/mach-msm/board-8084.c
index c20ba92..500c302 100644
--- a/arch/arm/mach-msm/board-8084.c
+++ b/arch/arm/mach-msm/board-8084.c
@@ -74,27 +74,6 @@
of_scan_flat_dt(dt_scan_for_memory_hole, apq8084_reserve_table);
}
-static struct clk_lookup msm_clocks_dummy[] = {
- CLK_DUMMY("core_clk", BLSP1_UART_CLK, "f991f000.serial", OFF),
- CLK_DUMMY("iface_clk", BLSP1_UART_CLK, "f991f000.serial", OFF),
- CLK_DUMMY("core_clk", SDC1_CLK, "msm_sdcc.1", OFF),
- CLK_DUMMY("iface_clk", SDC1_P_CLK, "msm_sdcc.1", OFF),
- CLK_DUMMY("core_clk", SDC2_CLK, "msm_sdcc.2", OFF),
- CLK_DUMMY("iface_clk", SDC2_P_CLK, "msm_sdcc.2", OFF),
- CLK_DUMMY("xo", NULL, "f9200000.qcom,ssusb", OFF),
- CLK_DUMMY("core_clk", NULL, "f9200000.qcom,ssusb", OFF),
- CLK_DUMMY("iface_clk", NULL, "f9200000.qcom,ssusb", OFF),
- CLK_DUMMY("sleep_clk", NULL, "f9200000.qcom,ssusb", OFF),
- CLK_DUMMY("sleep_a_clk", NULL, "f9200000.qcom,ssusb", OFF),
- CLK_DUMMY("utmi_clk", NULL, "f9200000.qcom,ssusb", OFF),
- CLK_DUMMY("ref_clk", NULL, "f9200000.qcom,ssusb", OFF),
-};
-
-static struct clock_init_data msm_dummy_clock_init_data __initdata = {
- .table = msm_clocks_dummy,
- .size = ARRAY_SIZE(msm_clocks_dummy),
-};
-
/*
* Used to satisfy dependencies for devices that need to be
* run early or in a particular order. Most likely your device doesn't fall
@@ -104,7 +83,7 @@
void __init apq8084_add_drivers(void)
{
msm_smd_init();
- msm_clock_init(&msm_dummy_clock_init_data);
+ msm_clock_init(&msm8084_clock_init_data);
}
static void __init apq8084_map_io(void)
diff --git a/arch/arm/mach-msm/board-8092.c b/arch/arm/mach-msm/board-8092.c
index 3da3e2d..6adff30 100644
--- a/arch/arm/mach-msm/board-8092.c
+++ b/arch/arm/mach-msm/board-8092.c
@@ -29,6 +29,7 @@
#include <linux/gpio.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>
+#include <mach/clk-provider.h>
#include "board-dt.h"
#include "clock.h"
diff --git a/arch/arm/mach-msm/board-8226-gpiomux.c b/arch/arm/mach-msm/board-8226-gpiomux.c
index 819ca56..ad4a516 100644
--- a/arch/arm/mach-msm/board-8226-gpiomux.c
+++ b/arch/arm/mach-msm/board-8226-gpiomux.c
@@ -397,6 +397,49 @@
};
+static struct gpiomux_setting auxpcm_act_cfg = {
+ .func = GPIOMUX_FUNC_1,
+ .drv = GPIOMUX_DRV_8MA,
+ .pull = GPIOMUX_PULL_NONE,
+};
+
+static struct gpiomux_setting auxpcm_sus_cfg = {
+ .func = GPIOMUX_FUNC_1,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_DOWN,
+};
+
+static struct msm_gpiomux_config msm_auxpcm_configs[] __initdata = {
+ {
+ .gpio = 63,
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &auxpcm_sus_cfg,
+ [GPIOMUX_ACTIVE] = &auxpcm_act_cfg,
+ },
+ },
+ {
+ .gpio = 64,
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &auxpcm_sus_cfg,
+ [GPIOMUX_ACTIVE] = &auxpcm_act_cfg,
+ },
+ },
+ {
+ .gpio = 65,
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &auxpcm_sus_cfg,
+ [GPIOMUX_ACTIVE] = &auxpcm_act_cfg,
+ },
+ },
+ {
+ .gpio = 66,
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &auxpcm_sus_cfg,
+ [GPIOMUX_ACTIVE] = &auxpcm_act_cfg,
+ },
+ },
+};
+
void __init msm8226_init_gpiomux(void)
{
int rc;
@@ -423,4 +466,6 @@
msm_gpiomux_install_nowrite(msm_lcd_configs,
ARRAY_SIZE(msm_lcd_configs));
msm_gpiomux_install(msm_sensor_configs, ARRAY_SIZE(msm_sensor_configs));
+ msm_gpiomux_install(msm_auxpcm_configs,
+ ARRAY_SIZE(msm_auxpcm_configs));
}
diff --git a/arch/arm/mach-msm/board-8960.c b/arch/arm/mach-msm/board-8960.c
index 5d96389..cb88cdc 100644
--- a/arch/arm/mach-msm/board-8960.c
+++ b/arch/arm/mach-msm/board-8960.c
@@ -1308,6 +1308,7 @@
.gpios = tsif_gpios,
.tsif_pclk = "tsif_pclk",
.tsif_ref_clk = "tsif_ref_clk",
+ .tsif_vreg_present = 0,
};
static struct platform_device msm_device_tspp = {
diff --git a/arch/arm/mach-msm/clock-8084.c b/arch/arm/mach-msm/clock-8084.c
new file mode 100644
index 0000000..424b694
--- /dev/null
+++ b/arch/arm/mach-msm/clock-8084.c
@@ -0,0 +1,351 @@
+/* Copyright (c) 2013, 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/kernel.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/ctype.h>
+#include <linux/io.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/iopoll.h>
+#include <linux/regulator/consumer.h>
+
+#include <mach/rpm-regulator-smd.h>
+#include <mach/socinfo.h>
+#include <mach/rpm-smd.h>
+
+#include "clock-local2.h"
+#include "clock-pll.h"
+#include "clock-rpm.h"
+#include "clock-voter.h"
+#include "clock.h"
+
+/*
+ * TODO: Drivers need to fill in the clock names and device names for the clocks
+ * they need to control.
+ */
+static struct clk_lookup msm_clocks_8084[] = {
+ CLK_DUMMY("core_clk", BLSP1_UART_CLK, "f991f000.serial", OFF),
+ CLK_DUMMY("iface_clk", BLSP1_UART_CLK, "f991f000.serial", OFF),
+ CLK_DUMMY("core_clk", SDC1_CLK, "msm_sdcc.1", OFF),
+ CLK_DUMMY("iface_clk", SDC1_P_CLK, "msm_sdcc.1", OFF),
+ CLK_DUMMY("core_clk", SDC2_CLK, "msm_sdcc.2", OFF),
+ CLK_DUMMY("iface_clk", SDC2_P_CLK, "msm_sdcc.2", OFF),
+ CLK_DUMMY("xo", NULL, "f9200000.qcom,ssusb", OFF),
+ CLK_DUMMY("core_clk", NULL, "f9200000.qcom,ssusb", OFF),
+ CLK_DUMMY("iface_clk", NULL, "f9200000.qcom,ssusb", OFF),
+ CLK_DUMMY("sleep_clk", NULL, "f9200000.qcom,ssusb", OFF),
+ CLK_DUMMY("sleep_a_clk", NULL, "f9200000.qcom,ssusb", OFF),
+ CLK_DUMMY("utmi_clk", NULL, "f9200000.qcom,ssusb", OFF),
+ CLK_DUMMY("ref_clk", NULL, "f9200000.qcom,ssusb", OFF),
+ CLK_DUMMY("", ufs_axi_clk_src.c, "", OFF),
+ CLK_DUMMY("", usb30_master_clk_src.c, "", OFF),
+ CLK_DUMMY("", usb30_sec_master_clk_src.c, "", OFF),
+ CLK_DUMMY("", usb_hsic_ahb_clk_src.c, "", OFF),
+ CLK_DUMMY("", sata_asic0_clk_src.c, "", OFF),
+ CLK_DUMMY("", sata_pmalive_clk_src.c, "", OFF),
+ CLK_DUMMY("", sata_rx_clk_src.c, "", OFF),
+ CLK_DUMMY("", sata_rx_oob_clk_src.c, "", OFF),
+ CLK_DUMMY("", sdcc1_apps_clk_src.c, "", OFF),
+ CLK_DUMMY("", sdcc2_apps_clk_src.c, "", OFF),
+ CLK_DUMMY("", sdcc3_apps_clk_src.c, "", OFF),
+ CLK_DUMMY("", sdcc4_apps_clk_src.c, "", OFF),
+ CLK_DUMMY("", tsif_ref_clk_src.c, "", OFF),
+ CLK_DUMMY("", ufs_rx_cfg_postdiv_clk_src.c, "", OFF),
+ CLK_DUMMY("", ufs_tx_cfg_postdiv_clk_src.c, "", OFF),
+ CLK_DUMMY("", usb30_mock_utmi_clk_src.c, "", OFF),
+ CLK_DUMMY("", usb30_sec_mock_utmi_clk_src.c, "", OFF),
+ CLK_DUMMY("", usb_hs_system_clk_src.c, "", OFF),
+ CLK_DUMMY("", usb_hsic_clk_src.c, "", OFF),
+ CLK_DUMMY("", usb_hsic_io_cal_clk_src.c, "", OFF),
+ CLK_DUMMY("", usb_hsic_mock_utmi_clk_src.c, "", OFF),
+ CLK_DUMMY("", usb_hsic_system_clk_src.c, "", OFF),
+ CLK_DUMMY("", gcc_bam_dma_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_bam_dma_inactivity_timers_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp1_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp1_qup1_i2c_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp1_qup1_spi_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp1_qup2_i2c_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp1_qup2_spi_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp1_qup3_i2c_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp1_qup3_spi_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp1_qup4_i2c_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp1_qup4_spi_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp1_qup5_i2c_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp1_qup5_spi_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp1_qup6_i2c_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp1_qup6_spi_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp1_uart1_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp1_uart2_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp1_uart3_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp1_uart4_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp1_uart5_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp1_uart6_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp2_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp2_qup1_i2c_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp2_qup1_spi_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp2_qup2_i2c_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp2_qup2_spi_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp2_qup3_i2c_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp2_qup3_spi_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp2_qup4_i2c_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp2_qup4_spi_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp2_qup5_i2c_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp2_qup5_spi_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp2_qup6_i2c_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp2_qup6_spi_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp2_uart1_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp2_uart2_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp2_uart3_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp2_uart4_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp2_uart5_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_blsp2_uart6_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_boot_rom_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_ce1_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_ce1_axi_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_ce1_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_ce2_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_ce2_axi_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_ce2_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_ce3_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_ce3_axi_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_ce3_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_copss_smmu_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_copss_smmu_axi_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_dcd_xo_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_bimc_gfx_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_xo_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_xo_div4_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_gp1_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_gp2_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_gp3_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_lpass_mport_axi_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_lpass_q6_axi_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_lpass_sway_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_mmss_bimc_gfx_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_mmss_vpu_maple_sys_noc_axi_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_ocmem_noc_cfg_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_msg_ram_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_pdm2_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_pdm_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_pdm_xo4_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_periph_noc_usb_hsic_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_prng_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_sata_asic0_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_sata_axi_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_sata_cfg_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_sata_pmalive_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_sata_rx_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_sata_rx_oob_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_sdcc1_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_sdcc1_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_sdcc1_cdccal_ff_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_sdcc1_cdccal_sleep_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_sdcc2_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_sdcc2_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_sdcc2_inactivity_timers_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_sdcc3_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_sdcc3_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_sdcc3_inactivity_timers_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_sdcc4_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_sdcc4_apps_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_sdcc4_inactivity_timers_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_spss_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_sys_noc_ufs_axi_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_sys_noc_usb3_axi_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_sys_noc_usb3_sec_axi_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_tsif_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_tsif_inactivity_timers_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_tsif_ref_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_ufs_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_ufs_axi_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_ufs_rx_cfg_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_ufs_rx_symbol_0_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_ufs_rx_symbol_1_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_ufs_tx_cfg_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_ufs_tx_symbol_0_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_ufs_tx_symbol_1_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_usb2a_phy_sleep_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_usb2b_phy_sleep_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_usb30_master_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_usb30_mock_utmi_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_usb30_sleep_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_usb30_sec_master_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_usb30_sec_mock_utmi_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_usb30_sec_sleep_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_usb_hs_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_usb_hs_inactivity_timers_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_usb_hs_system_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_usb_hsic_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_usb_hsic_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_usb_hsic_io_cal_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_usb_hsic_io_cal_sleep_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_usb_hsic_mock_utmi_clk.c, "", OFF),
+ CLK_DUMMY("", gcc_usb_hsic_system_clk.c, "", OFF),
+
+ CLK_DUMMY("", axi_clk_src.c, "", OFF),
+ CLK_DUMMY("", mmpll0_pll_clk_src.c, "", OFF),
+ CLK_DUMMY("", mmpll1_pll_clk_src.c, "", OFF),
+ CLK_DUMMY("", mmpll2_pll_clk_src.c, "", OFF),
+ CLK_DUMMY("", mmpll3_pll_clk_src.c, "", OFF),
+ CLK_DUMMY("", mmpll4_pll_clk_src.c, "", OFF),
+ CLK_DUMMY("", csi0_clk_src.c, "", OFF),
+ CLK_DUMMY("", csi1_clk_src.c, "", OFF),
+ CLK_DUMMY("", csi2_clk_src.c, "", OFF),
+ CLK_DUMMY("", csi3_clk_src.c, "", OFF),
+ CLK_DUMMY("", vcodec0_clk_src.c, "", OFF),
+ CLK_DUMMY("", vfe0_clk_src.c, "", OFF),
+ CLK_DUMMY("", vfe1_clk_src.c, "", OFF),
+ CLK_DUMMY("", edppixel_clk_src.c, "", OFF),
+ CLK_DUMMY("", extpclk_clk_src.c, "", OFF),
+ CLK_DUMMY("", mdp_clk_src.c, "", OFF),
+ CLK_DUMMY("", pclk0_clk_src.c, "", OFF),
+ CLK_DUMMY("", pclk1_clk_src.c, "", OFF),
+ CLK_DUMMY("", ocmemnoc_clk_src.c, "", OFF),
+ CLK_DUMMY("", gfx3d_clk_src.c, "", OFF),
+ CLK_DUMMY("", vp_clk_src.c, "", OFF),
+ CLK_DUMMY("", cci_clk_src.c, "", OFF),
+ CLK_DUMMY("", gp0_clk_src.c, "", OFF),
+ CLK_DUMMY("", gp1_clk_src.c, "", OFF),
+ CLK_DUMMY("", jpeg0_clk_src.c, "", OFF),
+ CLK_DUMMY("", jpeg1_clk_src.c, "", OFF),
+ CLK_DUMMY("", jpeg2_clk_src.c, "", OFF),
+ CLK_DUMMY("", mclk0_clk_src.c, "", OFF),
+ CLK_DUMMY("", mclk1_clk_src.c, "", OFF),
+ CLK_DUMMY("", mclk2_clk_src.c, "", OFF),
+ CLK_DUMMY("", mclk3_clk_src.c, "", OFF),
+ CLK_DUMMY("", csi0phytimer_clk_src.c, "", OFF),
+ CLK_DUMMY("", csi1phytimer_clk_src.c, "", OFF),
+ CLK_DUMMY("", csi2phytimer_clk_src.c, "", OFF),
+ CLK_DUMMY("", cpp_clk_src.c, "", OFF),
+ CLK_DUMMY("", byte0_clk_src.c, "", OFF),
+ CLK_DUMMY("", byte1_clk_src.c, "", OFF),
+ CLK_DUMMY("", edpaux_clk_src.c, "", OFF),
+ CLK_DUMMY("", edplink_clk_src.c, "", OFF),
+ CLK_DUMMY("", esc0_clk_src.c, "", OFF),
+ CLK_DUMMY("", esc1_clk_src.c, "", OFF),
+ CLK_DUMMY("", hdmi_clk_src.c, "", OFF),
+ CLK_DUMMY("", vsync_clk_src.c, "", OFF),
+ CLK_DUMMY("", rbbmtimer_clk_src.c, "", OFF),
+ CLK_DUMMY("", maple_clk_src.c, "", OFF),
+ CLK_DUMMY("", vdp_clk_src.c, "", OFF),
+ CLK_DUMMY("", vpu_bus_clk_src.c, "", OFF),
+ CLK_DUMMY("", dsi0_phy_pll_out_byteclk.c, "", OFF),
+ CLK_DUMMY("", dsi0_phy_pll_out_dsiclk.c, "", OFF),
+ CLK_DUMMY("", dsi1_phy_pll_out_byteclk.c, "", OFF),
+ CLK_DUMMY("", dsi1_phy_pll_out_dsiclk.c, "", OFF),
+ CLK_DUMMY("", edpphy_cc_link_clk.c, "", OFF),
+ CLK_DUMMY("", edpphy_cc_vco_div_clk.c, "", OFF),
+ CLK_DUMMY("", hdmi_phy_pll_out.c, "", OFF),
+ CLK_DUMMY("", csiphy_bist_clk.c, "", OFF),
+ CLK_DUMMY("", avsync_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", avsync_edppixel_clk.c, "", OFF),
+ CLK_DUMMY("", avsync_extpclk_clk.c, "", OFF),
+ CLK_DUMMY("", avsync_pclk0_clk.c, "", OFF),
+ CLK_DUMMY("", avsync_pclk1_clk.c, "", OFF),
+ CLK_DUMMY("", avsync_vp_clk.c, "", OFF),
+ CLK_DUMMY("", camss_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", camss_cci_cci_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", camss_cci_cci_clk.c, "", OFF),
+ CLK_DUMMY("", camss_csi0_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", camss_csi0_clk.c, "", OFF),
+ CLK_DUMMY("", camss_csi0phy_clk.c, "", OFF),
+ CLK_DUMMY("", camss_csi0pix_clk.c, "", OFF),
+ CLK_DUMMY("", camss_csi0rdi_clk.c, "", OFF),
+ CLK_DUMMY("", camss_csi1_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", camss_csi1_clk.c, "", OFF),
+ CLK_DUMMY("", camss_csi1phy_clk.c, "", OFF),
+ CLK_DUMMY("", camss_csi1pix_clk.c, "", OFF),
+ CLK_DUMMY("", camss_csi1rdi_clk.c, "", OFF),
+ CLK_DUMMY("", camss_csi2_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", camss_csi2_clk.c, "", OFF),
+ CLK_DUMMY("", camss_csi2phy_clk.c, "", OFF),
+ CLK_DUMMY("", camss_csi2pix_clk.c, "", OFF),
+ CLK_DUMMY("", camss_csi2rdi_clk.c, "", OFF),
+ CLK_DUMMY("", camss_csi3_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", camss_csi3_clk.c, "", OFF),
+ CLK_DUMMY("", camss_csi3phy_clk.c, "", OFF),
+ CLK_DUMMY("", camss_csi3pix_clk.c, "", OFF),
+ CLK_DUMMY("", camss_csi3rdi_clk.c, "", OFF),
+ CLK_DUMMY("", camss_csi_vfe0_clk.c, "", OFF),
+ CLK_DUMMY("", camss_csi_vfe1_clk.c, "", OFF),
+ CLK_DUMMY("", camss_gp0_clk.c, "", OFF),
+ CLK_DUMMY("", camss_gp1_clk.c, "", OFF),
+ CLK_DUMMY("", camss_ispif_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", camss_jpeg_jpeg0_clk.c, "", OFF),
+ CLK_DUMMY("", camss_jpeg_jpeg1_clk.c, "", OFF),
+ CLK_DUMMY("", camss_jpeg_jpeg2_clk.c, "", OFF),
+ CLK_DUMMY("", camss_jpeg_jpeg_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", camss_jpeg_jpeg_axi_clk.c, "", OFF),
+ CLK_DUMMY("", camss_mclk0_clk.c, "", OFF),
+ CLK_DUMMY("", camss_mclk1_clk.c, "", OFF),
+ CLK_DUMMY("", camss_mclk2_clk.c, "", OFF),
+ CLK_DUMMY("", camss_mclk3_clk.c, "", OFF),
+ CLK_DUMMY("", camss_micro_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", camss_phy0_csi0phytimer_clk.c, "", OFF),
+ CLK_DUMMY("", camss_phy1_csi1phytimer_clk.c, "", OFF),
+ CLK_DUMMY("", camss_phy2_csi2phytimer_clk.c, "", OFF),
+ CLK_DUMMY("", camss_top_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", camss_vfe_cpp_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", camss_vfe_cpp_clk.c, "", OFF),
+ CLK_DUMMY("", camss_vfe_vfe0_clk.c, "", OFF),
+ CLK_DUMMY("", camss_vfe_vfe1_clk.c, "", OFF),
+ CLK_DUMMY("", camss_vfe_vfe_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", camss_vfe_vfe_axi_clk.c, "", OFF),
+ CLK_DUMMY("", mdss_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", mdss_axi_clk.c, "", OFF),
+ CLK_DUMMY("", mdss_byte0_clk.c, "", OFF),
+ CLK_DUMMY("", mdss_byte1_clk.c, "", OFF),
+ CLK_DUMMY("", mdss_edpaux_clk.c, "", OFF),
+ CLK_DUMMY("", mdss_edplink_clk.c, "", OFF),
+ CLK_DUMMY("", mdss_edppixel_clk.c, "", OFF),
+ CLK_DUMMY("", mdss_esc0_clk.c, "", OFF),
+ CLK_DUMMY("", mdss_esc1_clk.c, "", OFF),
+ CLK_DUMMY("", mdss_extpclk_clk.c, "", OFF),
+ CLK_DUMMY("", mdss_hdmi_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", mdss_hdmi_clk.c, "", OFF),
+ CLK_DUMMY("", mdss_mdp_clk.c, "", OFF),
+ CLK_DUMMY("", mdss_mdp_lut_clk.c, "", OFF),
+ CLK_DUMMY("", mdss_pclk0_clk.c, "", OFF),
+ CLK_DUMMY("", mdss_pclk1_clk.c, "", OFF),
+ CLK_DUMMY("", mdss_vsync_clk.c, "", OFF),
+ CLK_DUMMY("", mmss_misc_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", mmss_mmssnoc_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", mmss_mmssnoc_axi_clk.c, "", OFF),
+ CLK_DUMMY("", mmss_s0_axi_clk.c, "", OFF),
+ CLK_DUMMY("", ocmemcx_ocmemnoc_clk.c, "", OFF),
+ CLK_DUMMY("", oxili_ocmemgx_clk.c, "", OFF),
+ CLK_DUMMY("", oxili_gfx3d_clk.c, "", OFF),
+ CLK_DUMMY("", oxili_rbbmtimer_clk.c, "", OFF),
+ CLK_DUMMY("", oxilicx_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", venus0_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", venus0_axi_clk.c, "", OFF),
+ CLK_DUMMY("", venus0_core0_vcodec_clk.c, "", OFF),
+ CLK_DUMMY("", venus0_core1_vcodec_clk.c, "", OFF),
+ CLK_DUMMY("", venus0_ocmemnoc_clk.c, "", OFF),
+ CLK_DUMMY("", venus0_vcodec0_clk.c, "", OFF),
+ CLK_DUMMY("", vpu_ahb_clk.c, "", OFF),
+ CLK_DUMMY("", vpu_axi_clk.c, "", OFF),
+ CLK_DUMMY("", vpu_bus_clk.c, "", OFF),
+ CLK_DUMMY("", vpu_cxo_clk.c, "", OFF),
+ CLK_DUMMY("", vpu_maple_clk.c, "", OFF),
+ CLK_DUMMY("", vpu_sleep_clk.c, "", OFF),
+ CLK_DUMMY("", vpu_vdp_clk.c, "", OFF),
+};
+
+struct clock_init_data msm8084_clock_init_data __initdata = {
+ .table = msm_clocks_8084,
+ .size = ARRAY_SIZE(msm_clocks_8084),
+};
diff --git a/arch/arm/mach-msm/clock-8226.c b/arch/arm/mach-msm/clock-8226.c
index 27bfcac..ac5efc1 100644
--- a/arch/arm/mach-msm/clock-8226.c
+++ b/arch/arm/mach-msm/clock-8226.c
@@ -3123,6 +3123,7 @@
CLK_LOOKUP("core_clk", qdss_clk.c, "fc342000.cti"),
CLK_LOOKUP("core_clk", qdss_clk.c, "fc343000.cti"),
CLK_LOOKUP("core_clk", qdss_clk.c, "fc344000.cti"),
+ CLK_LOOKUP("core_clk", qdss_clk.c, "fd828018.hwevent"),
CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc322000.tmc"),
CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc318000.tpiu"),
@@ -3138,10 +3139,10 @@
CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc33d000.etm"),
CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc33e000.etm"),
CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc33f000.etm"),
- CLK_LOOKUP("core_a_clk", qdss_clk.c, "fc33c000.jtagmm"),
- CLK_LOOKUP("core_a_clk", qdss_clk.c, "fc33d000.jtagmm"),
- CLK_LOOKUP("core_a_clk", qdss_clk.c, "fc33e000.jtagmm"),
- CLK_LOOKUP("core_a_clk", qdss_clk.c, "fc33f000.jtagmm"),
+ CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc33c000.jtagmm"),
+ CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc33d000.jtagmm"),
+ CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc33e000.jtagmm"),
+ CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc33f000.jtagmm"),
CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc308000.cti"),
CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc309000.cti"),
CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc30a000.cti"),
@@ -3156,6 +3157,9 @@
CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc342000.cti"),
CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc343000.cti"),
CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc344000.cti"),
+ CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fd828018.hwevent"),
+
+ CLK_LOOKUP("core_mmss_clk", mmss_misc_ahb_clk.c, "fd828018.hwevent"),
/* HSUSB-OTG Clocks */
CLK_LOOKUP("xo", cxo_otg_clk.c, "f9a55000.usb"),
@@ -3279,6 +3283,15 @@
CLK_LOOKUP("iface_clk", mdss_ahb_clk.c, "fd928000.qcom,iommu"),
CLK_LOOKUP("core_clk", mdss_axi_clk.c, "fd928000.qcom,iommu"),
+ CLK_LOOKUP("core_clk", venus0_vcodec0_clk.c, "fd8c1024.qcom,gdsc"),
+ CLK_LOOKUP("core_clk", mdss_mdp_clk.c, "fd8c2304.qcom,gdsc"),
+ CLK_LOOKUP("lut_clk", mdss_mdp_lut_clk.c, "fd8c2304.qcom,gdsc"),
+ CLK_LOOKUP("core_clk", camss_jpeg_jpeg0_clk.c, "fd8c35a4.qcom,gdsc"),
+ CLK_LOOKUP("core_clk", camss_vfe_vfe0_clk.c, "fd8c36a4.qcom,gdsc"),
+ CLK_LOOKUP("csi_clk", camss_csi_vfe0_clk.c, "fd8c36a4.qcom,gdsc"),
+ CLK_LOOKUP("cpp_clk", camss_vfe_cpp_clk.c, "fd8c36a4.qcom,gdsc"),
+ CLK_LOOKUP("core_clk", oxili_gfx3d_clk.c, "fd8c4034.qcom,gdsc"),
+
/* MM sensor clocks */
CLK_LOOKUP("cam_src_clk", mclk0_clk_src.c, "6f.qcom,camera"),
CLK_LOOKUP("cam_src_clk", mclk1_clk_src.c, "90.qcom,camera"),
diff --git a/arch/arm/mach-msm/clock-8610.c b/arch/arm/mach-msm/clock-8610.c
index 5e60bd5..17468d2 100644
--- a/arch/arm/mach-msm/clock-8610.c
+++ b/arch/arm/mach-msm/clock-8610.c
@@ -2819,6 +2819,7 @@
CLK_LOOKUP("core_clk", qdss_clk.c, "fc34d000.jtagmm"),
CLK_LOOKUP("core_clk", qdss_clk.c, "fc34e000.jtagmm"),
CLK_LOOKUP("core_clk", qdss_clk.c, "fc34f000.jtagmm"),
+ CLK_LOOKUP("core_clk", qdss_clk.c, "fd820018.hwevent"),
CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc326000.tmc"),
@@ -2852,8 +2853,9 @@
CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc34d000.jtagmm"),
CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc34e000.jtagmm"),
CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc34f000.jtagmm"),
+ CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fd820018.hwevent"),
-
+ CLK_LOOKUP("core_mmss_clk", mmss_misc_ahb_clk.c, "fd820018.hwevent"),
CLK_LOOKUP("core_clk_src", blsp1_qup1_spi_apps_clk_src.c, ""),
CLK_LOOKUP("core_clk_src", blsp1_qup2_spi_apps_clk_src.c, ""),
@@ -3061,6 +3063,7 @@
CLK_DUMMY("core_clk", NULL, "fd870000.qcom,iommu", OFF),
CLK_DUMMY("iface_clk", NULL, "fd880000.qcom,iommu", OFF),
CLK_DUMMY("core_clk", NULL, "fd880000.qcom,iommu", OFF),
+ CLK_DUMMY("alt_core_clk", NULL, "fd880000.qcom,iommu", OFF),
CLK_DUMMY("iface_clk", NULL, "fd000000.qcom,iommu", OFF),
CLK_DUMMY("core_clk", NULL, "fd000000.qcom,iommu", OFF),
CLK_DUMMY("iface_clk", NULL, "fd010000.qcom,iommu", OFF),
diff --git a/arch/arm/mach-msm/clock-8974.c b/arch/arm/mach-msm/clock-8974.c
index 4fb348d..ec94f00 100644
--- a/arch/arm/mach-msm/clock-8974.c
+++ b/arch/arm/mach-msm/clock-8974.c
@@ -3194,7 +3194,9 @@
F_HDMI( 25200000, hdmipll, 1, 0, 0),
F_HDMI( 27000000, hdmipll, 1, 0, 0),
F_HDMI( 27030000, hdmipll, 1, 0, 0),
+ F_HDMI( 65000000, hdmipll, 1, 0, 0),
F_HDMI( 74250000, hdmipll, 1, 0, 0),
+ F_HDMI(108000000, hdmipll, 1, 0, 0),
F_HDMI(148500000, hdmipll, 1, 0, 0),
F_HDMI(268500000, hdmipll, 1, 0, 0),
F_HDMI(297000000, hdmipll, 1, 0, 0),
@@ -5207,6 +5209,7 @@
CLK_LOOKUP("core_clk", qdss_clk.c, "fc342000.cti"),
CLK_LOOKUP("core_clk", qdss_clk.c, "fc343000.cti"),
CLK_LOOKUP("core_clk", qdss_clk.c, "fc344000.cti"),
+ CLK_LOOKUP("core_clk", qdss_clk.c, "fdf30018.hwevent"),
CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc322000.tmc"),
CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc318000.tpiu"),
@@ -5236,6 +5239,9 @@
CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc342000.cti"),
CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc343000.cti"),
CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc344000.cti"),
+ CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fdf30018.hwevent"),
+
+ CLK_LOOKUP("core_mmss_clk", mmss_misc_ahb_clk.c, "fdf30018.hwevent"),
CLK_LOOKUP("l2_m_clk", l2_m_clk, ""),
CLK_LOOKUP("krait0_m_clk", krait0_m_clk, ""),
diff --git a/arch/arm/mach-msm/clock-9625.c b/arch/arm/mach-msm/clock-9625.c
index 313e04c..5bfc4bb 100644
--- a/arch/arm/mach-msm/clock-9625.c
+++ b/arch/arm/mach-msm/clock-9625.c
@@ -1924,6 +1924,7 @@
CLK_LOOKUP("core_clk", qdss_clk.c, "fc30f000.cti"),
CLK_LOOKUP("core_clk", qdss_clk.c, "fc310000.cti"),
CLK_LOOKUP("core_clk", qdss_clk.c, "fc333000.cti"),
+ CLK_LOOKUP("core_clk", qdss_clk.c, "f9011038.hwevent"),
CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc322000.tmc"),
CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc318000.tpiu"),
@@ -1945,7 +1946,7 @@
CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc30f000.cti"),
CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc310000.cti"),
CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc333000.cti"),
-
+ CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "f9011038.hwevent"),
};
#define PLL_AUX_OUTPUT_BIT 1
diff --git a/arch/arm/mach-msm/clock-mdss-8974.c b/arch/arm/mach-msm/clock-mdss-8974.c
index d866874..17a6801 100644
--- a/arch/arm/mach-msm/clock-mdss-8974.c
+++ b/arch/arm/mach-msm/clock-mdss-8974.c
@@ -409,9 +409,9 @@
static enum handoff mdss_dsi_pll_byte_handoff(struct clk *c)
{
if (mdss_gdsc_enabled() && mdss_dsi_check_pll_lock()) {
- c->rate = 53000000;
- dsi_pll_rate = 53000000;
- pll_byte_clk_rate = 53000000;
+ c->rate = 52954560;
+ dsi_pll_rate = 52954560;
+ pll_byte_clk_rate = 52954560;
pll_pclk_rate = 105000000;
dsipll_refcount++;
return HANDOFF_ENABLED_CLK;
@@ -648,7 +648,49 @@
REG_W(0x05, hdmi_phy_base + HDMI_PHY_TXCAL_CFG3);
udelay(200);
break;
+ case 65000000:
+ REG_W(0x81, hdmi_phy_base + HDMI_PHY_GLB_CFG);
+ REG_W(0x01, hdmi_phy_pll_base + HDMI_UNI_PLL_GLB_CFG);
+ REG_W(0x01, hdmi_phy_pll_base + HDMI_UNI_PLL_REFCLK_CFG);
+ REG_W(0x19, hdmi_phy_pll_base + HDMI_UNI_PLL_VCOLPF_CFG);
+ REG_W(0x0E, hdmi_phy_pll_base + HDMI_UNI_PLL_LPFR_CFG);
+ REG_W(0x20, hdmi_phy_pll_base + HDMI_UNI_PLL_LPFC1_CFG);
+ REG_W(0x0D, hdmi_phy_pll_base + HDMI_UNI_PLL_LPFC2_CFG);
+ REG_W(0x00, hdmi_phy_pll_base + HDMI_UNI_PLL_SDM_CFG0);
+ REG_W(0x4F, hdmi_phy_pll_base + HDMI_UNI_PLL_SDM_CFG1);
+ REG_W(0x55, hdmi_phy_pll_base + HDMI_UNI_PLL_SDM_CFG2);
+ REG_W(0xED, hdmi_phy_pll_base + HDMI_UNI_PLL_SDM_CFG3);
+ REG_W(0x00, hdmi_phy_pll_base + HDMI_UNI_PLL_SDM_CFG4);
+ REG_W(0x10, hdmi_phy_pll_base + HDMI_UNI_PLL_LKDET_CFG0);
+ REG_W(0x1A, hdmi_phy_pll_base + HDMI_UNI_PLL_LKDET_CFG1);
+ REG_W(0x05, hdmi_phy_pll_base + HDMI_UNI_PLL_LKDET_CFG2);
+ REG_W(0x01, hdmi_phy_pll_base + HDMI_UNI_PLL_POSTDIV1_CFG);
+ REG_W(0x00, hdmi_phy_pll_base + HDMI_UNI_PLL_POSTDIV2_CFG);
+ REG_W(0x00, hdmi_phy_pll_base + HDMI_UNI_PLL_POSTDIV3_CFG);
+ REG_W(0x01, hdmi_phy_pll_base + HDMI_UNI_PLL_CAL_CFG2);
+ REG_W(0x60, hdmi_phy_pll_base + HDMI_UNI_PLL_CAL_CFG8);
+ REG_W(0x00, hdmi_phy_pll_base + HDMI_UNI_PLL_CAL_CFG9);
+ REG_W(0x8A, hdmi_phy_pll_base + HDMI_UNI_PLL_CAL_CFG10);
+ REG_W(0x02, hdmi_phy_pll_base + HDMI_UNI_PLL_CAL_CFG11);
+ REG_W(0x1F, hdmi_phy_base + HDMI_PHY_PD_CTRL0);
+ udelay(50);
+ REG_W(0x0F, hdmi_phy_pll_base + HDMI_UNI_PLL_GLB_CFG);
+ REG_W(0x00, hdmi_phy_base + HDMI_PHY_PD_CTRL1);
+ REG_W(0x10, hdmi_phy_base + HDMI_PHY_ANA_CFG2);
+ REG_W(0xDB, hdmi_phy_base + HDMI_PHY_ANA_CFG0);
+ REG_W(0x43, hdmi_phy_base + HDMI_PHY_ANA_CFG1);
+ REG_W(0x02, hdmi_phy_base + HDMI_PHY_ANA_CFG2);
+ REG_W(0x00, hdmi_phy_base + HDMI_PHY_ANA_CFG3);
+ REG_W(0x04, hdmi_phy_pll_base + HDMI_UNI_PLL_VREG_CFG);
+ REG_W(0xD0, hdmi_phy_base + HDMI_PHY_DCC_CFG0);
+ REG_W(0x1A, hdmi_phy_base + HDMI_PHY_DCC_CFG1);
+ REG_W(0x00, hdmi_phy_base + HDMI_PHY_TXCAL_CFG0);
+ REG_W(0x00, hdmi_phy_base + HDMI_PHY_TXCAL_CFG1);
+ REG_W(0x02, hdmi_phy_base + HDMI_PHY_TXCAL_CFG2);
+ REG_W(0x05, hdmi_phy_base + HDMI_PHY_TXCAL_CFG3);
+ udelay(200);
+ break;
case 74250000:
/*
* 720p60/720p50/1080i60/1080i50
@@ -697,6 +739,50 @@
udelay(200);
break;
+ case 108000000:
+ REG_W(0x81, hdmi_phy_base + HDMI_PHY_GLB_CFG);
+ REG_W(0x01, hdmi_phy_pll_base + HDMI_UNI_PLL_GLB_CFG);
+ REG_W(0x01, hdmi_phy_pll_base + HDMI_UNI_PLL_REFCLK_CFG);
+ REG_W(0x19, hdmi_phy_pll_base + HDMI_UNI_PLL_VCOLPF_CFG);
+ REG_W(0x0E, hdmi_phy_pll_base + HDMI_UNI_PLL_LPFR_CFG);
+ REG_W(0x20, hdmi_phy_pll_base + HDMI_UNI_PLL_LPFC1_CFG);
+ REG_W(0x0D, hdmi_phy_pll_base + HDMI_UNI_PLL_LPFC2_CFG);
+ REG_W(0x00, hdmi_phy_pll_base + HDMI_UNI_PLL_SDM_CFG0);
+ REG_W(0x5B, hdmi_phy_pll_base + HDMI_UNI_PLL_SDM_CFG1);
+ REG_W(0x00, hdmi_phy_pll_base + HDMI_UNI_PLL_SDM_CFG2);
+ REG_W(0x20, hdmi_phy_pll_base + HDMI_UNI_PLL_SDM_CFG3);
+ REG_W(0x00, hdmi_phy_pll_base + HDMI_UNI_PLL_SDM_CFG4);
+ REG_W(0x10, hdmi_phy_pll_base + HDMI_UNI_PLL_LKDET_CFG0);
+ REG_W(0x1A, hdmi_phy_pll_base + HDMI_UNI_PLL_LKDET_CFG1);
+ REG_W(0x05, hdmi_phy_pll_base + HDMI_UNI_PLL_LKDET_CFG2);
+ REG_W(0x01, hdmi_phy_pll_base + HDMI_UNI_PLL_POSTDIV1_CFG);
+ REG_W(0x00, hdmi_phy_pll_base + HDMI_UNI_PLL_POSTDIV2_CFG);
+ REG_W(0x00, hdmi_phy_pll_base + HDMI_UNI_PLL_POSTDIV3_CFG);
+ REG_W(0x01, hdmi_phy_pll_base + HDMI_UNI_PLL_CAL_CFG2);
+ REG_W(0x60, hdmi_phy_pll_base + HDMI_UNI_PLL_CAL_CFG8);
+ REG_W(0x00, hdmi_phy_pll_base + HDMI_UNI_PLL_CAL_CFG9);
+ REG_W(0x38, hdmi_phy_pll_base + HDMI_UNI_PLL_CAL_CFG10);
+ REG_W(0x04, hdmi_phy_pll_base + HDMI_UNI_PLL_CAL_CFG11);
+ REG_W(0x1F, hdmi_phy_base + HDMI_PHY_PD_CTRL0);
+ udelay(50);
+
+ REG_W(0x0F, hdmi_phy_pll_base + HDMI_UNI_PLL_GLB_CFG);
+ REG_W(0x00, hdmi_phy_base + HDMI_PHY_PD_CTRL1);
+ REG_W(0x10, hdmi_phy_base + HDMI_PHY_ANA_CFG2);
+ REG_W(0xDB, hdmi_phy_base + HDMI_PHY_ANA_CFG0);
+ REG_W(0x43, hdmi_phy_base + HDMI_PHY_ANA_CFG1);
+ REG_W(0x02, hdmi_phy_base + HDMI_PHY_ANA_CFG2);
+ REG_W(0x00, hdmi_phy_base + HDMI_PHY_ANA_CFG3);
+ REG_W(0x04, hdmi_phy_pll_base + HDMI_UNI_PLL_VREG_CFG);
+ REG_W(0xD0, hdmi_phy_base + HDMI_PHY_DCC_CFG0);
+ REG_W(0x1A, hdmi_phy_base + HDMI_PHY_DCC_CFG1);
+ REG_W(0x00, hdmi_phy_base + HDMI_PHY_TXCAL_CFG0);
+ REG_W(0x00, hdmi_phy_base + HDMI_PHY_TXCAL_CFG1);
+ REG_W(0x02, hdmi_phy_base + HDMI_PHY_TXCAL_CFG2);
+ REG_W(0x05, hdmi_phy_base + HDMI_PHY_TXCAL_CFG3);
+ udelay(200);
+ break;
+
case 148500000:
REG_W(0x81, hdmi_phy_base + HDMI_PHY_GLB_CFG);
REG_W(0x01, hdmi_phy_pll_base + HDMI_UNI_PLL_GLB_CFG);
diff --git a/arch/arm/mach-msm/clock.h b/arch/arm/mach-msm/clock.h
index 9ca1965..674ef77 100644
--- a/arch/arm/mach-msm/clock.h
+++ b/arch/arm/mach-msm/clock.h
@@ -53,6 +53,7 @@
extern struct clock_init_data msm8610_rumi_clock_init_data;
extern struct clock_init_data msm8226_clock_init_data;
extern struct clock_init_data msm8226_rumi_clock_init_data;
+extern struct clock_init_data msm8084_clock_init_data;
int msm_clock_init(struct clock_init_data *data);
int find_vdd_level(struct clk *clk, unsigned long rate);
diff --git a/arch/arm/mach-msm/cpr-regulator.c b/arch/arm/mach-msm/cpr-regulator.c
index 08923e4..e51a1f5 100644
--- a/arch/arm/mach-msm/cpr-regulator.c
+++ b/arch/arm/mach-msm/cpr-regulator.c
@@ -24,26 +24,139 @@
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
+#include <linux/interrupt.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/of_regulator.h>
#include <linux/regulator/cpr-regulator.h>
+/* Register Offsets for RB-CPR and Bit Definitions */
+
+/* RBCPR Gate Count and Target Registers */
+#define REG_RBCPR_GCNT_TARGET(n) (0x60 + 4 * n)
+
+#define RBCPR_GCNT_TARGET_GCNT_BITS 10
+#define RBCPR_GCNT_TARGET_GCNT_SHIFT 12
+#define RBCPR_GCNT_TARGET_GCNT_MASK ((1<<RBCPR_GCNT_TARGET_GCNT_BITS)-1)
+
+/* RBCPR Timer Control */
+#define REG_RBCPR_TIMER_INTERVAL 0x44
+#define REG_RBIF_TIMER_ADJUST 0x4C
+
+#define RBIF_TIMER_ADJ_CONS_UP_BITS 4
+#define RBIF_TIMER_ADJ_CONS_UP_MASK ((1<<RBIF_TIMER_ADJ_CONS_UP_BITS)-1)
+#define RBIF_TIMER_ADJ_CONS_DOWN_BITS 4
+#define RBIF_TIMER_ADJ_CONS_DOWN_MASK ((1<<RBIF_TIMER_ADJ_CONS_DOWN_BITS)-1)
+#define RBIF_TIMER_ADJ_CONS_DOWN_SHIFT 4
+
+/* RBCPR Config Register */
+#define REG_RBIF_LIMIT 0x48
+#define REG_RBCPR_STEP_QUOT 0x80
+#define REG_RBIF_SW_VLEVEL 0x94
+
+#define RBIF_LIMIT_CEILING_BITS 6
+#define RBIF_LIMIT_CEILING_MASK ((1<<RBIF_LIMIT_CEILING_BITS)-1)
+#define RBIF_LIMIT_CEILING_SHIFT 6
+#define RBIF_LIMIT_FLOOR_BITS 6
+#define RBIF_LIMIT_FLOOR_MASK ((1<<RBIF_LIMIT_FLOOR_BITS)-1)
+
+#define RBIF_LIMIT_CEILING_DEFAULT RBIF_LIMIT_CEILING_MASK
+#define RBIF_LIMIT_FLOOR_DEFAULT 0
+#define RBIF_SW_VLEVEL_DEFAULT 0x20
+
+#define RBCPR_STEP_QUOT_STEPQUOT_BITS 8
+#define RBCPR_STEP_QUOT_STEPQUOT_MASK ((1<<RBCPR_STEP_QUOT_STEPQUOT_BITS)-1)
+#define RBCPR_STEP_QUOT_IDLE_CLK_BITS 4
+#define RBCPR_STEP_QUOT_IDLE_CLK_MASK ((1<<RBCPR_STEP_QUOT_IDLE_CLK_BITS)-1)
+#define RBCPR_STEP_QUOT_IDLE_CLK_SHIFT 8
+
+/* RBCPR Control Register */
+#define REG_RBCPR_CTL 0x90
+
+#define RBCPR_CTL_LOOP_EN BIT(0)
+#define RBCPR_CTL_TIMER_EN BIT(3)
+#define RBCPR_CTL_SW_AUTO_CONT_ACK_EN BIT(5)
+#define RBCPR_CTL_SW_AUTO_CONT_NACK_DN_EN BIT(6)
+#define RBCPR_CTL_COUNT_MODE BIT(10)
+#define RBCPR_CTL_UP_THRESHOLD_BITS 4
+#define RBCPR_CTL_UP_THRESHOLD_MASK ((1<<RBCPR_CTL_UP_THRESHOLD_BITS)-1)
+#define RBCPR_CTL_UP_THRESHOLD_SHIFT 24
+#define RBCPR_CTL_DN_THRESHOLD_BITS 4
+#define RBCPR_CTL_DN_THRESHOLD_MASK ((1<<RBCPR_CTL_DN_THRESHOLD_BITS)-1)
+#define RBCPR_CTL_DN_THRESHOLD_SHIFT 28
+
+/* RBCPR Ack/Nack Response */
+#define REG_RBIF_CONT_ACK_CMD 0x98
+#define REG_RBIF_CONT_NACK_CMD 0x9C
+
+/* RBCPR Result status Register */
+#define REG_RBCPR_RESULT_0 0xA0
+
+#define RBCPR_RESULT0_ERROR_STEPS_SHIFT 2
+#define RBCPR_RESULT0_ERROR_STEPS_BITS 4
+#define RBCPR_RESULT0_ERROR_STEPS_MASK ((1<<RBCPR_RESULT0_ERROR_STEPS_BITS)-1)
+
+/* RBCPR Interrupt Control Register */
+#define REG_RBIF_IRQ_EN(n) (0x100 + 4 * n)
+#define REG_RBIF_IRQ_CLEAR 0x110
+#define REG_RBIF_IRQ_STATUS 0x114
+
+#define CPR_INT_DONE BIT(0)
+#define CPR_INT_MIN BIT(1)
+#define CPR_INT_DOWN BIT(2)
+#define CPR_INT_MID BIT(3)
+#define CPR_INT_UP BIT(4)
+#define CPR_INT_MAX BIT(5)
+#define CPR_INT_CLAMP BIT(6)
+#define CPR_INT_ALL (CPR_INT_DONE | CPR_INT_MIN | CPR_INT_DOWN | \
+ CPR_INT_MID | CPR_INT_UP | CPR_INT_MAX | CPR_INT_CLAMP)
+#define CPR_INT_DEFAULT (CPR_INT_UP | CPR_INT_DOWN)
+
+#define CPR_NUM_RING_OSC 8
+#define CPR_NUM_SAVE_REGS 10
+
+/* CPR eFuse parameters */
+#define CPR_FUSE_TARGET_QUOT_BITS 12
+#define CPR_FUSE_TARGET_QUOT_BITS_MASK ((1<<CPR_FUSE_TARGET_QUOT_BITS)-1)
+#define CPR_FUSE_RO_SEL_BITS 3
+#define CPR_FUSE_RO_SEL_BITS_MASK ((1<<CPR_FUSE_RO_SEL_BITS)-1)
+
+#define CPR_FUSE_TARGET_QUOT_TURBO_SHIFT 0
+#define CPR_FUSE_TARGET_QUOT_NOMINAL_SHIFT 12
+#define CPR_FUSE_TARGET_QUOT_SVS_SHIFT 24
+
+#define CPR_FUSE_DISABLE_CPR_SHIFT 36
+#define CPR_FUSE_LOCAL_APPROACH_SHIFT 37
+#define CPR_FUSE_REDUNDANT_SHIFT 57
+
+/* PVS eFuse parameters */
+#define PVS_FUSE_REDUNDANT_SHIFT 24
+#define PVS_FUSE_REDUNDANT_BITS 3
+#define PVS_FUSE_REDUNDANT_MASK ((1<<PVS_FUSE_REDUNDANT_BITS)-1)
+
+#define PVS_FUSE_BINS_SHIFT 6
+#define PVS_FUSE_BINS_REDUNDANT_SHIFT 27
+
+enum voltage_change_dir {
+ NO_CHANGE,
+ DOWN,
+ UP,
+};
+
struct cpr_regulator {
struct regulator_desc rdesc;
struct regulator_dev *rdev;
- bool enabled;
+ bool vreg_enabled;
int corner;
+ int ceiling_max;
/* Process voltage parameters */
- phys_addr_t efuse_phys;
+ phys_addr_t pvs_efuse;
u32 num_efuse_bits;
- u32 efuse_bit_pos[CPR_PVS_EFUSE_BITS_MAX];
u32 pvs_bin_process[CPR_PVS_EFUSE_BINS_MAX];
- u32 pvs_corner_ceiling[NUM_APC_PVS][CPR_CORNER_MAX];
+ u32 pvs_corner_v[NUM_APC_PVS][CPR_CORNER_MAX];
/* Process voltage variables */
u32 pvs_bin;
- u32 pvs_process;
- u32 *corner_ceiling;
+ u32 process;
/* APC voltage regulator */
struct regulator *vdd_apc;
@@ -53,13 +166,499 @@
int vdd_mx_vmax;
int vdd_mx_vmin_method;
int vdd_mx_vmin;
+
+ /* CPR parameters */
+ phys_addr_t cpr_fuse_addr;
+ u64 cpr_fuse_bits;
+ u64 cpr_fuse_bits_2;
+ bool cpr_fuse_disable;
+ bool cpr_fuse_local;
+ bool cpr_fuse_redundancy;
+ int cpr_fuse_target_quot[CPR_CORNER_MAX];
+ int cpr_fuse_ro_sel[CPR_CORNER_MAX];
+ int gcnt;
+
+ unsigned int cpr_irq;
+ void __iomem *rbcpr_base;
+ struct mutex cpr_mutex;
+
+ int ceiling_volt[CPR_CORNER_MAX];
+ int floor_volt[CPR_CORNER_MAX];
+ int last_volt[CPR_CORNER_MAX];
+ int step_volt;
+
+ int save_ctl[CPR_CORNER_MAX];
+ int save_irq[CPR_CORNER_MAX];
+
+ u32 save_regs[CPR_NUM_SAVE_REGS];
+ u32 save_reg_val[CPR_NUM_SAVE_REGS];
+
+ /* Config parameters */
+ bool enable;
+ u32 ref_clk_khz;
+ u32 timer_delay_us;
+ u32 timer_cons_up;
+ u32 timer_cons_down;
+ u32 irq_line;
+ u32 step_quotient;
+ u32 up_threshold;
+ u32 down_threshold;
+ u32 idle_clocks;
+ u32 gcnt_time_us;
+ u32 vdd_apc_step_up_limit;
+ u32 vdd_apc_step_down_limit;
};
+static int cpr_debug_enable;
+static int cpr_enable;
+static struct cpr_regulator *the_cpr;
+
+module_param_named(debug_enable, cpr_debug_enable, int, S_IRUGO | S_IWUSR);
+#define cpr_debug(message, ...) \
+ do { \
+ if (cpr_debug_enable) \
+ pr_info(message, ##__VA_ARGS__); \
+ } while (0)
+
+static bool cpr_is_allowed(struct cpr_regulator *cpr_vreg)
+{
+ if (cpr_vreg->cpr_fuse_disable || !cpr_enable)
+ return false;
+ else
+ return true;
+}
+
+static void cpr_write(struct cpr_regulator *cpr_vreg, u32 offset, u32 value)
+{
+ writel_relaxed(value, cpr_vreg->rbcpr_base + offset);
+}
+
+static u32 cpr_read(struct cpr_regulator *cpr_vreg, u32 offset)
+{
+ return readl_relaxed(cpr_vreg->rbcpr_base + offset);
+}
+
+static void cpr_masked_write(struct cpr_regulator *cpr_vreg, u32 offset,
+ u32 mask, u32 value)
+{
+ u32 reg_val;
+
+ reg_val = readl_relaxed(cpr_vreg->rbcpr_base + offset);
+ reg_val &= ~mask;
+ reg_val |= value & mask;
+ writel_relaxed(reg_val, cpr_vreg->rbcpr_base + offset);
+}
+
+static void cpr_irq_clr(struct cpr_regulator *cpr_vreg)
+{
+ cpr_write(cpr_vreg, REG_RBIF_IRQ_CLEAR, CPR_INT_ALL);
+}
+
+static void cpr_irq_clr_nack(struct cpr_regulator *cpr_vreg)
+{
+ cpr_irq_clr(cpr_vreg);
+ cpr_write(cpr_vreg, REG_RBIF_CONT_NACK_CMD, 1);
+}
+
+static void cpr_irq_clr_ack(struct cpr_regulator *cpr_vreg)
+{
+ cpr_irq_clr(cpr_vreg);
+ cpr_write(cpr_vreg, REG_RBIF_CONT_ACK_CMD, 1);
+}
+
+static void cpr_irq_set(struct cpr_regulator *cpr_vreg, u32 int_bits)
+{
+ cpr_write(cpr_vreg, REG_RBIF_IRQ_EN(cpr_vreg->irq_line), int_bits);
+}
+
+static void cpr_ctl_modify(struct cpr_regulator *cpr_vreg, u32 mask, u32 value)
+{
+ cpr_masked_write(cpr_vreg, REG_RBCPR_CTL, mask, value);
+}
+
+static void cpr_ctl_enable(struct cpr_regulator *cpr_vreg)
+{
+ u32 val;
+
+ if (cpr_is_allowed(cpr_vreg))
+ val = RBCPR_CTL_LOOP_EN;
+ else
+ val = 0;
+ cpr_ctl_modify(cpr_vreg, RBCPR_CTL_LOOP_EN, val);
+}
+
+static void cpr_ctl_disable(struct cpr_regulator *cpr_vreg)
+{
+ cpr_ctl_modify(cpr_vreg, RBCPR_CTL_LOOP_EN, 0);
+}
+
+static void cpr_regs_save(struct cpr_regulator *cpr_vreg)
+{
+ int i, offset;
+
+ for (i = 0; i < CPR_NUM_SAVE_REGS; i++) {
+ offset = cpr_vreg->save_regs[i];
+ cpr_vreg->save_reg_val[i] = cpr_read(cpr_vreg, offset);
+ }
+}
+
+static void cpr_regs_restore(struct cpr_regulator *cpr_vreg)
+{
+ int i, offset;
+ u32 val;
+
+ for (i = 0; i < CPR_NUM_SAVE_REGS; i++) {
+ offset = cpr_vreg->save_regs[i];
+ val = cpr_vreg->save_reg_val[i];
+ cpr_write(cpr_vreg, offset, val);
+ }
+}
+
+static void cpr_corner_save(struct cpr_regulator *cpr_vreg, int corner)
+{
+ cpr_vreg->save_ctl[corner] = cpr_read(cpr_vreg, REG_RBCPR_CTL);
+ cpr_vreg->save_irq[corner] =
+ cpr_read(cpr_vreg, REG_RBIF_IRQ_EN(cpr_vreg->irq_line));
+}
+
+static void cpr_corner_restore(struct cpr_regulator *cpr_vreg, int corner)
+{
+ u32 gcnt, ctl, irq, ro_sel;
+
+ ro_sel = cpr_vreg->cpr_fuse_ro_sel[corner];
+ gcnt = cpr_vreg->gcnt | cpr_vreg->cpr_fuse_target_quot[corner];
+ cpr_write(cpr_vreg, REG_RBCPR_GCNT_TARGET(ro_sel), gcnt);
+ ctl = cpr_vreg->save_ctl[corner];
+ cpr_write(cpr_vreg, REG_RBCPR_CTL, ctl);
+ irq = cpr_vreg->save_irq[corner];
+ cpr_irq_set(cpr_vreg, irq);
+ cpr_debug("gcnt = 0x%08x, ctl = 0x%08x, irq = 0x%08x\n",
+ gcnt, ctl, irq);
+}
+
+static void cpr_corner_switch(struct cpr_regulator *cpr_vreg, int corner)
+{
+ if (cpr_vreg->corner == corner)
+ return;
+
+ cpr_corner_restore(cpr_vreg, corner);
+}
+
+/* Module parameter ops */
+static int cpr_enable_param_set(const char *val, const struct kernel_param *kp)
+{
+ int rc;
+ int old_cpr_enable;
+
+ if (!the_cpr) {
+ pr_err("the_cpr = NULL\n");
+ return -ENXIO;
+ }
+
+ mutex_lock(&the_cpr->cpr_mutex);
+
+ old_cpr_enable = cpr_enable;
+ rc = param_set_int(val, kp);
+ if (rc) {
+ pr_err("param_set_int: rc = %d\n", rc);
+ goto _exit;
+ }
+
+ cpr_debug("%d -> %d [corner=%d]\n",
+ old_cpr_enable, cpr_enable, the_cpr->corner);
+
+ if (the_cpr->cpr_fuse_disable) {
+ /* Already disabled */
+ pr_info("CPR disabled by fuse\n");
+ goto _exit;
+ }
+
+ if ((old_cpr_enable != cpr_enable) && the_cpr->corner) {
+ if (cpr_enable) {
+ cpr_ctl_disable(the_cpr);
+ cpr_irq_clr(the_cpr);
+ cpr_corner_restore(the_cpr, the_cpr->corner);
+ cpr_ctl_enable(the_cpr);
+ } else {
+ cpr_ctl_disable(the_cpr);
+ cpr_irq_set(the_cpr, 0);
+ }
+ }
+
+_exit:
+ mutex_unlock(&the_cpr->cpr_mutex);
+ return 0;
+}
+
+static struct kernel_param_ops cpr_enable_ops = {
+ .set = cpr_enable_param_set,
+ .get = param_get_int,
+};
+
+module_param_cb(cpr_enable, &cpr_enable_ops, &cpr_enable, S_IRUGO | S_IWUSR);
+
+static int cpr_apc_set(struct cpr_regulator *cpr_vreg, u32 new_volt)
+{
+ int max_volt, rc;
+
+ max_volt = cpr_vreg->ceiling_max;
+ rc = regulator_set_voltage(cpr_vreg->vdd_apc, new_volt, max_volt);
+ if (rc)
+ pr_err("set: vdd_apc = %d uV: rc=%d\n", new_volt, rc);
+ return rc;
+}
+
+static int cpr_mx_get(struct cpr_regulator *cpr_vreg, int corner, int apc_volt)
+{
+ int vdd_mx;
+
+ switch (cpr_vreg->vdd_mx_vmin_method) {
+ case VDD_MX_VMIN_APC:
+ vdd_mx = apc_volt;
+ break;
+ case VDD_MX_VMIN_APC_CORNER_CEILING:
+ vdd_mx = cpr_vreg->ceiling_volt[corner];
+ break;
+ case VDD_MX_VMIN_APC_SLOW_CORNER_CEILING:
+ vdd_mx = cpr_vreg->pvs_corner_v[APC_PVS_SLOW]
+ [CPR_CORNER_TURBO];
+ break;
+ case VDD_MX_VMIN_MX_VMAX:
+ vdd_mx = cpr_vreg->vdd_mx_vmax;
+ break;
+ default:
+ vdd_mx = 0;
+ break;
+ }
+
+ return vdd_mx;
+}
+
+static int cpr_mx_set(struct cpr_regulator *cpr_vreg, int corner,
+ int vdd_mx_vmin)
+{
+ int rc;
+
+ rc = regulator_set_voltage(cpr_vreg->vdd_mx, vdd_mx_vmin,
+ cpr_vreg->vdd_mx_vmax);
+ cpr_debug("[corner:%d] %d uV\n", corner, vdd_mx_vmin);
+ if (!rc)
+ cpr_vreg->vdd_mx_vmin = vdd_mx_vmin;
+ else
+ pr_err("set: vdd_mx [%d] = %d uV: rc=%d\n",
+ corner, vdd_mx_vmin, rc);
+ return rc;
+}
+
+static int cpr_scale_voltage(struct cpr_regulator *cpr_vreg, int corner,
+ int new_apc_volt, enum voltage_change_dir dir)
+{
+ int rc = 0, vdd_mx_vmin = 0;
+
+ /* No MX scaling if no vdd_mx */
+ if (cpr_vreg->vdd_mx == NULL)
+ dir = NO_CHANGE;
+
+ if (dir != NO_CHANGE) {
+ /* Determine the vdd_mx voltage */
+ vdd_mx_vmin = cpr_mx_get(cpr_vreg, corner, new_apc_volt);
+ }
+
+ if (vdd_mx_vmin && dir == UP) {
+ if (vdd_mx_vmin != cpr_vreg->vdd_mx_vmin)
+ rc = cpr_mx_set(cpr_vreg, corner, vdd_mx_vmin);
+ }
+
+ if (!rc)
+ rc = cpr_apc_set(cpr_vreg, new_apc_volt);
+
+ if (!rc && vdd_mx_vmin && dir == DOWN) {
+ if (vdd_mx_vmin != cpr_vreg->vdd_mx_vmin)
+ rc = cpr_mx_set(cpr_vreg, corner, vdd_mx_vmin);
+ }
+
+ return rc;
+}
+
+static void cpr_scale(struct cpr_regulator *cpr_vreg,
+ enum voltage_change_dir dir)
+{
+ u32 reg_val, error_steps, reg_mask;
+ int last_volt, new_volt, corner;
+
+ corner = cpr_vreg->corner;
+
+ reg_val = cpr_read(cpr_vreg, REG_RBCPR_RESULT_0);
+
+ error_steps = (reg_val >> RBCPR_RESULT0_ERROR_STEPS_SHIFT)
+ & RBCPR_RESULT0_ERROR_STEPS_MASK;
+ last_volt = cpr_vreg->last_volt[corner];
+
+ cpr_debug("last_volt[corner:%d] = %d uV\n", corner, last_volt);
+
+ if (dir == UP) {
+ cpr_debug("Up: cpr status = 0x%08x (error_steps=%d)\n",
+ reg_val, error_steps);
+
+ if (last_volt >= cpr_vreg->ceiling_volt[corner]) {
+ cpr_debug("[corn:%d] @ ceiling: %d >= %d: NACK\n",
+ corner, last_volt,
+ cpr_vreg->ceiling_volt[corner]);
+ cpr_irq_clr_nack(cpr_vreg);
+
+ /* Maximize the UP threshold */
+ reg_mask = RBCPR_CTL_UP_THRESHOLD_MASK <<
+ RBCPR_CTL_UP_THRESHOLD_SHIFT;
+ reg_val = reg_mask;
+ cpr_ctl_modify(cpr_vreg, reg_mask, reg_val);
+ return;
+ }
+
+ if (error_steps > cpr_vreg->vdd_apc_step_up_limit) {
+ cpr_debug("%d is over up-limit(%d): Clamp\n",
+ error_steps,
+ cpr_vreg->vdd_apc_step_up_limit);
+ error_steps = cpr_vreg->vdd_apc_step_up_limit;
+ }
+
+ /* Calculate new voltage */
+ new_volt = last_volt + (error_steps * cpr_vreg->step_volt);
+ if (new_volt > cpr_vreg->ceiling_volt[corner]) {
+ cpr_debug("new_volt(%d) >= ceiling_volt(%d): Clamp\n",
+ new_volt, cpr_vreg->ceiling_volt[corner]);
+ new_volt = cpr_vreg->ceiling_volt[corner];
+ }
+
+ if (cpr_scale_voltage(cpr_vreg, corner, new_volt, dir)) {
+ cpr_irq_clr_nack(cpr_vreg);
+ return;
+ }
+ cpr_vreg->last_volt[corner] = new_volt;
+
+ /* Restore default threshold for DOWN */
+ reg_mask = RBCPR_CTL_DN_THRESHOLD_MASK <<
+ RBCPR_CTL_DN_THRESHOLD_SHIFT;
+ reg_val = cpr_vreg->down_threshold <<
+ RBCPR_CTL_DN_THRESHOLD_SHIFT;
+ /* and disable auto nack down */
+ reg_mask |= RBCPR_CTL_SW_AUTO_CONT_NACK_DN_EN;
+
+ cpr_ctl_modify(cpr_vreg, reg_mask, reg_val);
+
+ /* Re-enable default interrupts */
+ cpr_irq_set(cpr_vreg, CPR_INT_DEFAULT);
+
+ /* Ack */
+ cpr_irq_clr_ack(cpr_vreg);
+
+ cpr_debug("UP: -> new_volt = %d uV\n", new_volt);
+ } else if (dir == DOWN) {
+ cpr_debug("Down: cpr status = 0x%08x (error_steps=%d)\n",
+ reg_val, error_steps);
+
+ if (last_volt <= cpr_vreg->floor_volt[corner]) {
+ cpr_debug("[corn:%d] @ floor: %d <= %d: NACK\n",
+ corner, last_volt,
+ cpr_vreg->floor_volt[corner]);
+ cpr_irq_clr_nack(cpr_vreg);
+
+ /* Maximize the DOWN threshold */
+ reg_mask = RBCPR_CTL_DN_THRESHOLD_MASK <<
+ RBCPR_CTL_DN_THRESHOLD_SHIFT;
+ reg_val = reg_mask;
+
+ /* Enable auto nack down */
+ reg_mask |= RBCPR_CTL_SW_AUTO_CONT_NACK_DN_EN;
+ reg_val |= RBCPR_CTL_SW_AUTO_CONT_NACK_DN_EN;
+
+ cpr_ctl_modify(cpr_vreg, reg_mask, reg_val);
+
+ /* Disable DOWN interrupt */
+ cpr_irq_set(cpr_vreg, CPR_INT_DEFAULT & ~CPR_INT_DOWN);
+
+ return;
+ }
+
+ if (error_steps > cpr_vreg->vdd_apc_step_down_limit) {
+ cpr_debug("%d is over down-limit(%d): Clamp\n",
+ error_steps,
+ cpr_vreg->vdd_apc_step_down_limit);
+ error_steps = cpr_vreg->vdd_apc_step_down_limit;
+ }
+
+ /* Calculte new voltage */
+ new_volt = last_volt - (error_steps * cpr_vreg->step_volt);
+ if (new_volt < cpr_vreg->floor_volt[corner]) {
+ cpr_debug("new_volt(%d) < floor_volt(%d): Clamp\n",
+ new_volt, cpr_vreg->floor_volt[corner]);
+ new_volt = cpr_vreg->floor_volt[corner];
+ }
+
+ if (cpr_scale_voltage(cpr_vreg, corner, new_volt, dir)) {
+ cpr_irq_clr_nack(cpr_vreg);
+ return;
+ }
+ cpr_vreg->last_volt[corner] = new_volt;
+
+ /* Restore default threshold for UP */
+ reg_mask = RBCPR_CTL_UP_THRESHOLD_MASK <<
+ RBCPR_CTL_UP_THRESHOLD_SHIFT;
+ reg_val = cpr_vreg->up_threshold <<
+ RBCPR_CTL_UP_THRESHOLD_SHIFT;
+ cpr_ctl_modify(cpr_vreg, reg_mask, reg_val);
+
+ /* Ack */
+ cpr_irq_clr_ack(cpr_vreg);
+
+ cpr_debug("DOWN: -> new_volt = %d uV\n", new_volt);
+ }
+}
+
+static irqreturn_t cpr_irq_handler(int irq, void *dev)
+{
+ struct cpr_regulator *cpr_vreg = dev;
+ u32 reg_val;
+
+ mutex_lock(&cpr_vreg->cpr_mutex);
+
+ reg_val = cpr_read(cpr_vreg, REG_RBIF_IRQ_STATUS);
+ cpr_debug("IRQ_STATUS = 0x%02X\n", reg_val);
+
+ if (!cpr_is_allowed(cpr_vreg)) {
+ reg_val = cpr_read(cpr_vreg, REG_RBCPR_CTL);
+ pr_err("Interrupt broken? RBCPR_CTL = 0x%02X\n", reg_val);
+ goto _exit;
+ }
+
+ /* Following sequence of handling is as per each IRQ's priority */
+ if (reg_val & CPR_INT_UP) {
+ cpr_scale(cpr_vreg, UP);
+ } else if (reg_val & CPR_INT_DOWN) {
+ cpr_scale(cpr_vreg, DOWN);
+ } else if (reg_val & CPR_INT_MIN) {
+ cpr_irq_clr_nack(cpr_vreg);
+ } else if (reg_val & CPR_INT_MAX) {
+ cpr_irq_clr_nack(cpr_vreg);
+ } else if (reg_val & CPR_INT_MID) {
+ /* RBCPR_CTL_SW_AUTO_CONT_ACK_EN is enabled */
+ cpr_debug("IRQ occured for Mid Flag\n");
+ } else {
+ pr_err("IRQ occured for unknown flag (0x%08x)\n", reg_val);
+ }
+
+ /* Save register values for the corner */
+ cpr_corner_save(cpr_vreg, cpr_vreg->corner);
+
+_exit:
+ mutex_unlock(&cpr_vreg->cpr_mutex);
+ return IRQ_HANDLED;
+}
+
static int cpr_regulator_is_enabled(struct regulator_dev *rdev)
{
struct cpr_regulator *cpr_vreg = rdev_get_drvdata(rdev);
- return cpr_vreg->enabled;
+ return cpr_vreg->vreg_enabled;
}
static int cpr_regulator_enable(struct regulator_dev *rdev)
@@ -78,7 +677,7 @@
rc = regulator_enable(cpr_vreg->vdd_apc);
if (!rc)
- cpr_vreg->enabled = true;
+ cpr_vreg->vreg_enabled = true;
else
pr_err("regulator_enable: vdd_apc: rc=%d\n", rc);
@@ -98,7 +697,7 @@
if (rc)
pr_err("regulator_disable: vdd_mx: rc=%d\n", rc);
else
- cpr_vreg->enabled = false;
+ cpr_vreg->vreg_enabled = false;
} else {
pr_err("regulator_disable: vdd_apc: rc=%d\n", rc);
}
@@ -107,90 +706,44 @@
}
static int cpr_regulator_set_voltage(struct regulator_dev *rdev,
- int min_uV, int max_uV, unsigned *selector)
+ int corner, int corner_max, unsigned *selector)
{
struct cpr_regulator *cpr_vreg = rdev_get_drvdata(rdev);
int rc;
- int vdd_apc_min, vdd_apc_max, vdd_mx_vmin = 0;
- int change_dir = 0;
+ int new_volt;
+ enum voltage_change_dir change_dir = NO_CHANGE;
- if (cpr_vreg->vdd_mx) {
- if (min_uV > cpr_vreg->corner)
- change_dir = 1;
- else if (min_uV < cpr_vreg->corner)
- change_dir = -1;
- }
+ mutex_lock(&cpr_vreg->cpr_mutex);
- vdd_apc_min = cpr_vreg->corner_ceiling[min_uV];
- vdd_apc_max = cpr_vreg->corner_ceiling[CPR_CORNER_SUPER_TURBO];
-
- if (change_dir) {
- /* Determine the vdd_mx voltage */
- switch (cpr_vreg->vdd_mx_vmin_method) {
- case VDD_MX_VMIN_APC:
- vdd_mx_vmin = vdd_apc_min;
- break;
- case VDD_MX_VMIN_APC_CORNER_CEILING:
- vdd_mx_vmin = vdd_apc_min;
- break;
- case VDD_MX_VMIN_APC_SLOW_CORNER_CEILING:
- vdd_mx_vmin = cpr_vreg->pvs_corner_ceiling
- [APC_PVS_SLOW][min_uV];
- break;
- case VDD_MX_VMIN_MX_VMAX:
- default:
- vdd_mx_vmin = cpr_vreg->vdd_mx_vmax;
- break;
- }
- }
-
- if (change_dir > 0) {
- if (vdd_mx_vmin < cpr_vreg->vdd_mx_vmin) {
- /* Check and report the value in case */
- pr_err("Up: but new %d < old %d uV\n", vdd_mx_vmin,
- cpr_vreg->vdd_mx_vmin);
- }
-
- rc = regulator_set_voltage(cpr_vreg->vdd_mx, vdd_mx_vmin,
- cpr_vreg->vdd_mx_vmax);
- if (!rc) {
- cpr_vreg->vdd_mx_vmin = vdd_mx_vmin;
- } else {
- pr_err("set: vdd_mx [%d] = %d uV: rc=%d\n",
- min_uV, vdd_mx_vmin, rc);
- return rc;
- }
- }
-
- rc = regulator_set_voltage(cpr_vreg->vdd_apc,
- vdd_apc_min, vdd_apc_max);
- if (!rc) {
- cpr_vreg->corner = min_uV;
+ if (cpr_is_allowed(cpr_vreg)) {
+ cpr_ctl_disable(cpr_vreg);
+ new_volt = cpr_vreg->last_volt[corner];
} else {
- pr_err("set: vdd_apc [%d] = %d uV: rc=%d\n",
- min_uV, vdd_apc_min, rc);
- return rc;
+ new_volt = cpr_vreg->pvs_corner_v[cpr_vreg->process][corner];
}
- if (change_dir < 0) {
- if (vdd_mx_vmin > cpr_vreg->vdd_mx_vmin) {
- /* Check and report the value in case */
- pr_err("Down: but new %d >= old %d uV\n", vdd_mx_vmin,
- cpr_vreg->vdd_mx_vmin);
- }
+ cpr_debug("[corner:%d] = %d uV\n", corner, new_volt);
- rc = regulator_set_voltage(cpr_vreg->vdd_mx, vdd_mx_vmin,
- cpr_vreg->vdd_mx_vmax);
- if (!rc) {
- cpr_vreg->vdd_mx_vmin = vdd_mx_vmin;
- } else {
- pr_err("set: vdd_mx [%d] = %d uV: rc=%d\n",
- min_uV, vdd_mx_vmin, rc);
- return rc;
- }
+ if (corner > cpr_vreg->corner)
+ change_dir = UP;
+ else if (corner < cpr_vreg->corner)
+ change_dir = DOWN;
+
+ rc = cpr_scale_voltage(cpr_vreg, corner, new_volt, change_dir);
+ if (rc)
+ goto _exit;
+
+ if (cpr_is_allowed(cpr_vreg)) {
+ cpr_irq_clr(cpr_vreg);
+ cpr_corner_switch(cpr_vreg, corner);
+ cpr_ctl_enable(cpr_vreg);
}
- pr_debug("set [corner:%d] = %d uV: rc=%d\n", min_uV, vdd_apc_min, rc);
+ cpr_vreg->corner = corner;
+
+_exit:
+ mutex_unlock(&cpr_vreg->cpr_mutex);
+
return rc;
}
@@ -209,56 +762,202 @@
.get_voltage = cpr_regulator_get_voltage,
};
-static int __init cpr_regulator_pvs_init(struct cpr_regulator *cpr_vreg)
+#ifdef CONFIG_PM
+static int cpr_suspend(struct cpr_regulator *cpr_vreg)
+{
+ cpr_debug("suspend\n");
+
+ cpr_ctl_disable(cpr_vreg);
+ disable_irq(cpr_vreg->cpr_irq);
+
+ cpr_irq_clr(cpr_vreg);
+ cpr_regs_save(cpr_vreg);
+
+ return 0;
+}
+
+static int cpr_resume(struct cpr_regulator *cpr_vreg)
+
+{
+ cpr_debug("resume\n");
+
+ cpr_regs_restore(cpr_vreg);
+ cpr_irq_clr(cpr_vreg);
+
+ enable_irq(cpr_vreg->cpr_irq);
+ cpr_ctl_enable(cpr_vreg);
+
+ return 0;
+}
+
+static int cpr_regulator_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ struct cpr_regulator *cpr_vreg = platform_get_drvdata(pdev);
+
+ if (cpr_is_allowed(cpr_vreg))
+ return cpr_suspend(cpr_vreg);
+ else
+ return 0;
+}
+
+static int cpr_regulator_resume(struct platform_device *pdev)
+{
+ struct cpr_regulator *cpr_vreg = platform_get_drvdata(pdev);
+
+ if (cpr_is_allowed(cpr_vreg))
+ return cpr_resume(cpr_vreg);
+ else
+ return 0;
+}
+#else
+#define cpr_regulator_suspend NULL
+#define cpr_regulator_resume NULL
+#endif
+
+static void cpr_config(struct cpr_regulator *cpr_vreg)
+{
+ int i;
+ u32 val, gcnt;
+
+ /* Disable interrupt and CPR */
+ cpr_write(cpr_vreg, REG_RBIF_IRQ_EN(cpr_vreg->irq_line), 0);
+ cpr_write(cpr_vreg, REG_RBCPR_CTL, 0);
+
+ /* Program the default HW Ceiling, Floor and vlevel */
+ val = ((RBIF_LIMIT_CEILING_DEFAULT & RBIF_LIMIT_CEILING_MASK)
+ << RBIF_LIMIT_CEILING_SHIFT)
+ | (RBIF_LIMIT_FLOOR_DEFAULT & RBIF_LIMIT_FLOOR_MASK);
+ cpr_write(cpr_vreg, REG_RBIF_LIMIT, val);
+ cpr_write(cpr_vreg, REG_RBIF_SW_VLEVEL, RBIF_SW_VLEVEL_DEFAULT);
+
+ /* Clear the target quotient value and gate count of all ROs */
+ for (i = 0; i < CPR_NUM_RING_OSC; i++)
+ cpr_write(cpr_vreg, REG_RBCPR_GCNT_TARGET(i), 0);
+
+ /* Init and save gcnt */
+ gcnt = (cpr_vreg->ref_clk_khz * cpr_vreg->gcnt_time_us) / 1000;
+ gcnt = (gcnt & RBCPR_GCNT_TARGET_GCNT_MASK) <<
+ RBCPR_GCNT_TARGET_GCNT_SHIFT;
+ cpr_vreg->gcnt = gcnt;
+
+ /* Program the step quotient and idle clocks */
+ val = ((cpr_vreg->idle_clocks & RBCPR_STEP_QUOT_IDLE_CLK_MASK)
+ << RBCPR_STEP_QUOT_IDLE_CLK_SHIFT) |
+ (cpr_vreg->step_quotient & RBCPR_STEP_QUOT_STEPQUOT_MASK);
+ cpr_write(cpr_vreg, REG_RBCPR_STEP_QUOT, val);
+
+ /* Program the delay count for the timer */
+ val = (cpr_vreg->ref_clk_khz * cpr_vreg->timer_delay_us) / 1000;
+ cpr_write(cpr_vreg, REG_RBCPR_TIMER_INTERVAL, val);
+ pr_info("Timer count: 0x%0x (for %d us)\n", val,
+ cpr_vreg->timer_delay_us);
+
+ /* Program Consecutive Up & Down */
+ val = ((cpr_vreg->timer_cons_down & RBIF_TIMER_ADJ_CONS_DOWN_MASK)
+ << RBIF_TIMER_ADJ_CONS_DOWN_SHIFT) |
+ (cpr_vreg->timer_cons_up & RBIF_TIMER_ADJ_CONS_UP_MASK);
+ cpr_write(cpr_vreg, REG_RBIF_TIMER_ADJUST, val);
+
+ /* Program the control register */
+ cpr_vreg->up_threshold &= RBCPR_CTL_UP_THRESHOLD_MASK;
+ cpr_vreg->down_threshold &= RBCPR_CTL_DN_THRESHOLD_MASK;
+ val = (cpr_vreg->up_threshold << RBCPR_CTL_UP_THRESHOLD_SHIFT)
+ | (cpr_vreg->down_threshold << RBCPR_CTL_DN_THRESHOLD_SHIFT);
+ val |= RBCPR_CTL_TIMER_EN | RBCPR_CTL_COUNT_MODE;
+ val |= RBCPR_CTL_SW_AUTO_CONT_ACK_EN;
+ cpr_write(cpr_vreg, REG_RBCPR_CTL, val);
+
+ /* Registers to save & restore for suspend */
+ cpr_vreg->save_regs[0] = REG_RBCPR_TIMER_INTERVAL;
+ cpr_vreg->save_regs[1] = REG_RBCPR_STEP_QUOT;
+ cpr_vreg->save_regs[2] = REG_RBIF_TIMER_ADJUST;
+ cpr_vreg->save_regs[3] = REG_RBIF_LIMIT;
+ cpr_vreg->save_regs[4] = REG_RBIF_SW_VLEVEL;
+ cpr_vreg->save_regs[5] = REG_RBIF_IRQ_EN(cpr_vreg->irq_line);
+ cpr_vreg->save_regs[6] = REG_RBCPR_CTL;
+ cpr_vreg->save_regs[7] = REG_RBCPR_GCNT_TARGET
+ (cpr_vreg->cpr_fuse_ro_sel[CPR_CORNER_SVS]);
+ cpr_vreg->save_regs[8] = REG_RBCPR_GCNT_TARGET
+ (cpr_vreg->cpr_fuse_ro_sel[CPR_CORNER_NORMAL]);
+ cpr_vreg->save_regs[9] = REG_RBCPR_GCNT_TARGET
+ (cpr_vreg->cpr_fuse_ro_sel[CPR_CORNER_TURBO]);
+
+ cpr_irq_set(cpr_vreg, CPR_INT_DEFAULT);
+
+ cpr_corner_save(cpr_vreg, CPR_CORNER_SVS);
+ cpr_corner_save(cpr_vreg, CPR_CORNER_NORMAL);
+ cpr_corner_save(cpr_vreg, CPR_CORNER_TURBO);
+}
+
+static int __init cpr_pvs_init(struct cpr_regulator *cpr_vreg)
{
void __iomem *efuse_base;
- u32 efuse_bits;
- int i, bit_pos;
- u32 vmax;
+ u32 efuse_bits, redundant, shift, mask;
+ int i, process;
- efuse_base = ioremap(cpr_vreg->efuse_phys, 4);
+ efuse_base = ioremap(cpr_vreg->pvs_efuse, 4);
if (!efuse_base) {
- pr_err("Unable to map efuse_phys 0x%x\n",
- cpr_vreg->efuse_phys);
+ pr_err("Unable to map pvs_efuse 0x%08x\n",
+ cpr_vreg->pvs_efuse);
return -EINVAL;
}
efuse_bits = readl_relaxed(efuse_base);
/* Construct PVS process # from the efuse bits */
- for (i = 0; i < cpr_vreg->num_efuse_bits; i++) {
- bit_pos = cpr_vreg->efuse_bit_pos[i];
- cpr_vreg->pvs_bin |= (efuse_bits & BIT(bit_pos)) ? BIT(i) : 0;
- }
+ redundant = (efuse_bits >> PVS_FUSE_REDUNDANT_SHIFT)
+ & PVS_FUSE_REDUNDANT_MASK;
+ if (redundant == 2)
+ shift = PVS_FUSE_BINS_REDUNDANT_SHIFT;
+ else
+ shift = PVS_FUSE_BINS_SHIFT;
+ mask = (1 << cpr_vreg->num_efuse_bits) - 1;
+ cpr_vreg->pvs_bin = (efuse_bits >> shift) & mask;
- cpr_vreg->pvs_process = cpr_vreg->pvs_bin_process[cpr_vreg->pvs_bin];
- if (cpr_vreg->pvs_process >= NUM_APC_PVS)
- cpr_vreg->pvs_process = APC_PVS_NO;
-
- /* Use ceiling voltage of Turbo@Slow for all corners of APC_PVS_NO
- but use SuperTurbo@Slow for its SuperTurbo */
- vmax = cpr_vreg->pvs_corner_ceiling[APC_PVS_SLOW][CPR_CORNER_TURBO];
- for (i = CPR_CORNER_SVS; i <= CPR_CORNER_TURBO; i++)
- cpr_vreg->pvs_corner_ceiling[APC_PVS_NO][i] = vmax;
- cpr_vreg->pvs_corner_ceiling[APC_PVS_NO][CPR_CORNER_SUPER_TURBO]
- = cpr_vreg->pvs_corner_ceiling[APC_PVS_SLOW]
- [CPR_CORNER_SUPER_TURBO];
-
- cpr_vreg->corner_ceiling =
- cpr_vreg->pvs_corner_ceiling[cpr_vreg->pvs_process];
+ /* Set ceiling max and use it for APC_PVS_NO */
+ cpr_vreg->ceiling_max =
+ cpr_vreg->pvs_corner_v[APC_PVS_SLOW][CPR_CORNER_TURBO];
iounmap(efuse_base);
- pr_info("PVS Info: efuse_phys=0x%08X, n_bits=%d\n",
- cpr_vreg->efuse_phys, cpr_vreg->num_efuse_bits);
- pr_info("PVS Info: efuse=0x%08X, bin=%d, process=%d\n",
- efuse_bits, cpr_vreg->pvs_bin, cpr_vreg->pvs_process);
+ process = cpr_vreg->pvs_bin_process[cpr_vreg->pvs_bin];
+ pr_info("[0x%08X] = 0x%08X, n_bits=%d, bin=%d (%d) [redundant=%d]\n",
+ cpr_vreg->pvs_efuse, efuse_bits, cpr_vreg->num_efuse_bits,
+ cpr_vreg->pvs_bin, process, redundant);
+ for (i = APC_PVS_SLOW; i < NUM_APC_PVS; i++) {
+ pr_info("[%d] [%d %d %d] uV\n", i,
+ cpr_vreg->pvs_corner_v[i][CPR_CORNER_SVS],
+ cpr_vreg->pvs_corner_v[i][CPR_CORNER_NORMAL],
+ cpr_vreg->pvs_corner_v[i][CPR_CORNER_TURBO]);
+ }
+
+ if (process == APC_PVS_NO || process >= NUM_APC_PVS) {
+ pr_err("Bin=%d (%d) is out of spec. Assume SLOW.\n",
+ cpr_vreg->pvs_bin, process);
+ process = APC_PVS_SLOW;
+ }
+
+ cpr_vreg->process = process;
return 0;
}
-static int __init cpr_regulator_apc_init(struct platform_device *pdev,
- struct cpr_regulator *cpr_vreg)
+#define CPR_PROP_READ_U32(of_node, cpr_property, cpr_config, rc) \
+do { \
+ if (!rc) { \
+ rc = of_property_read_u32(of_node, \
+ "qcom," cpr_property, \
+ cpr_config); \
+ if (rc) { \
+ pr_err("Missing " #cpr_property \
+ ": rc = %d\n", rc); \
+ } \
+ } \
+} while (0)
+
+static int __init cpr_apc_init(struct platform_device *pdev,
+ struct cpr_regulator *cpr_vreg)
{
struct device_node *of_node = pdev->dev.of_node;
int rc;
@@ -308,9 +1007,9 @@
return 0;
}
-static void cpr_regulator_apc_exit(struct cpr_regulator *cpr_vreg)
+static void cpr_apc_exit(struct cpr_regulator *cpr_vreg)
{
- if (cpr_vreg->enabled) {
+ if (cpr_vreg->vreg_enabled) {
regulator_disable(cpr_vreg->vdd_apc);
if (cpr_vreg->vdd_mx)
@@ -318,8 +1017,231 @@
}
}
-static int __init cpr_regulator_parse_dt(struct platform_device *pdev,
- struct cpr_regulator *cpr_vreg)
+static int __init cpr_init_cpr_efuse(struct cpr_regulator *cpr_vreg)
+{
+ void __iomem *efuse_base;
+ u32 ro_sel, val;
+ u64 fuse_bits;
+ int ro_sel_shift[CPR_CORNER_MAX];
+
+ efuse_base = ioremap(cpr_vreg->cpr_fuse_addr, 16);
+ if (!efuse_base) {
+ pr_err("Unable to map cpr_fuse_addr 0x%08x\n",
+ cpr_vreg->cpr_fuse_addr);
+ return -EINVAL;
+ }
+
+ cpr_vreg->cpr_fuse_bits = readll_relaxed(efuse_base);
+ cpr_vreg->cpr_fuse_bits_2 = readll_relaxed(efuse_base + 8);
+
+ iounmap(efuse_base);
+
+ /* Read the control bits of eFuse */
+ cpr_vreg->cpr_fuse_disable = (cpr_vreg->cpr_fuse_bits >>
+ CPR_FUSE_DISABLE_CPR_SHIFT) & 0x01;
+ cpr_vreg->cpr_fuse_local = (cpr_vreg->cpr_fuse_bits >>
+ CPR_FUSE_LOCAL_APPROACH_SHIFT) & 0x01;
+ cpr_vreg->cpr_fuse_redundancy = (cpr_vreg->cpr_fuse_bits >>
+ CPR_FUSE_REDUNDANT_SHIFT) & 0x01;
+
+ pr_info("[0x%08X] = 0x%llx\n", cpr_vreg->cpr_fuse_addr,
+ cpr_vreg->cpr_fuse_bits);
+ pr_info("disable = %d, local = %d, redundancy = %d\n",
+ cpr_vreg->cpr_fuse_disable,
+ cpr_vreg->cpr_fuse_local,
+ cpr_vreg->cpr_fuse_redundancy);
+ pr_info("[0x%08X] = 0x%llx\n", cpr_vreg->cpr_fuse_addr + 8,
+ cpr_vreg->cpr_fuse_bits_2);
+
+ if (cpr_vreg->cpr_fuse_redundancy == 0) {
+ fuse_bits = cpr_vreg->cpr_fuse_bits;
+ ro_sel_shift[CPR_CORNER_SVS] = 54;
+ ro_sel_shift[CPR_CORNER_NORMAL] = 38;
+ ro_sel_shift[CPR_CORNER_TURBO] = 41;
+ } else {
+ fuse_bits = cpr_vreg->cpr_fuse_bits_2;
+ ro_sel_shift[CPR_CORNER_SVS] = 46;
+ ro_sel_shift[CPR_CORNER_NORMAL] = 36;
+ ro_sel_shift[CPR_CORNER_TURBO] = 39;
+ }
+
+ /* SVS */
+ ro_sel = (fuse_bits >> ro_sel_shift[CPR_CORNER_SVS])
+ & CPR_FUSE_RO_SEL_BITS_MASK;
+ val = (fuse_bits >> CPR_FUSE_TARGET_QUOT_SVS_SHIFT)
+ & CPR_FUSE_TARGET_QUOT_BITS_MASK;
+ cpr_vreg->cpr_fuse_target_quot[CPR_CORNER_SVS] = val;
+ cpr_vreg->cpr_fuse_ro_sel[CPR_CORNER_SVS] = ro_sel;
+ pr_info("SVS: ro_sel = %d, target quot = 0x%04x\n", ro_sel, val);
+
+ /* Nominal */
+ ro_sel = (fuse_bits >> ro_sel_shift[CPR_CORNER_NORMAL])
+ & CPR_FUSE_RO_SEL_BITS_MASK;
+ val = (fuse_bits >> CPR_FUSE_TARGET_QUOT_NOMINAL_SHIFT)
+ & CPR_FUSE_TARGET_QUOT_BITS_MASK;
+ cpr_vreg->cpr_fuse_target_quot[CPR_CORNER_NORMAL] = val;
+ cpr_vreg->cpr_fuse_ro_sel[CPR_CORNER_NORMAL] = ro_sel;
+ pr_info("Nominal: ro_sel = %d, target quot = 0x%04x\n", ro_sel, val);
+
+ /* Turbo */
+ ro_sel = (fuse_bits >> ro_sel_shift[CPR_CORNER_TURBO])
+ & CPR_FUSE_RO_SEL_BITS_MASK;
+ val = (fuse_bits >> CPR_FUSE_TARGET_QUOT_TURBO_SHIFT)
+ & CPR_FUSE_TARGET_QUOT_BITS_MASK;
+ cpr_vreg->cpr_fuse_target_quot[CPR_CORNER_TURBO] = val;
+ cpr_vreg->cpr_fuse_ro_sel[CPR_CORNER_TURBO] = ro_sel;
+ pr_info("Turbo: ro_sel = %d, target quot = 0x%04x\n", ro_sel, val);
+
+ if (!cpr_vreg->cpr_fuse_bits) {
+ cpr_vreg->cpr_fuse_disable = 1;
+ pr_err("cpr_fuse_bits = 0: set cpr_fuse_disable = 1\n");
+ }
+
+ return 0;
+}
+
+static int __init cpr_init_cpr_voltages(struct cpr_regulator *cpr_vreg)
+{
+ int i;
+
+ /* Construct CPR voltage limits */
+ for (i = CPR_CORNER_SVS; i < CPR_CORNER_MAX; i++) {
+ cpr_vreg->floor_volt[i] =
+ cpr_vreg->pvs_corner_v[APC_PVS_FAST][i];
+ cpr_vreg->ceiling_volt[i] =
+ cpr_vreg->pvs_corner_v[APC_PVS_SLOW][i];
+ cpr_vreg->last_volt[i] =
+ cpr_vreg->pvs_corner_v[cpr_vreg->process][i];
+ }
+
+ return 0;
+}
+
+static int __init cpr_init_cpr_parameters(struct platform_device *pdev,
+ struct cpr_regulator *cpr_vreg)
+{
+ struct device_node *of_node = pdev->dev.of_node;
+ int rc = 0;
+
+ CPR_PROP_READ_U32(of_node, "cpr-ref-clk",
+ &cpr_vreg->ref_clk_khz, rc);
+ if (rc)
+ return rc;
+ CPR_PROP_READ_U32(of_node, "cpr-timer-delay",
+ &cpr_vreg->timer_delay_us, rc);
+ if (rc)
+ return rc;
+ CPR_PROP_READ_U32(of_node, "cpr-timer-cons-up",
+ &cpr_vreg->timer_cons_up, rc);
+ if (rc)
+ return rc;
+ CPR_PROP_READ_U32(of_node, "cpr-timer-cons-down",
+ &cpr_vreg->timer_cons_down, rc);
+ if (rc)
+ return rc;
+ CPR_PROP_READ_U32(of_node, "cpr-irq-line",
+ &cpr_vreg->irq_line, rc);
+ if (rc)
+ return rc;
+ CPR_PROP_READ_U32(of_node, "cpr-step-quotient",
+ &cpr_vreg->step_quotient, rc);
+ if (rc)
+ return rc;
+ CPR_PROP_READ_U32(of_node, "cpr-up-threshold",
+ &cpr_vreg->up_threshold, rc);
+ if (rc)
+ return rc;
+ CPR_PROP_READ_U32(of_node, "cpr-down-threshold",
+ &cpr_vreg->down_threshold, rc);
+ if (rc)
+ return rc;
+ CPR_PROP_READ_U32(of_node, "cpr-idle-clocks",
+ &cpr_vreg->idle_clocks, rc);
+ if (rc)
+ return rc;
+ CPR_PROP_READ_U32(of_node, "cpr-gcnt-time",
+ &cpr_vreg->gcnt_time_us, rc);
+ if (rc)
+ return rc;
+ CPR_PROP_READ_U32(of_node, "vdd-apc-step-up-limit",
+ &cpr_vreg->vdd_apc_step_up_limit, rc);
+ if (rc)
+ return rc;
+ CPR_PROP_READ_U32(of_node, "vdd-apc-step-down-limit",
+ &cpr_vreg->vdd_apc_step_down_limit, rc);
+ if (rc)
+ return rc;
+ CPR_PROP_READ_U32(of_node, "cpr-apc-volt-step",
+ &cpr_vreg->step_volt, rc);
+ if (rc)
+ return rc;
+
+ /* Init module parameter with the DT value */
+ cpr_vreg->enable = of_property_read_bool(of_node, "qcom,cpr-enable");
+ cpr_enable = (int) cpr_vreg->enable;
+ pr_info("CPR is %s by default.\n",
+ cpr_vreg->enable ? "enabled" : "disabled");
+
+ return rc;
+}
+
+static int __init cpr_init_cpr(struct platform_device *pdev,
+ struct cpr_regulator *cpr_vreg)
+{
+ struct resource *res;
+ int rc = 0;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "cpr_efuse");
+ if (!res || !res->start) {
+ pr_err("cpr_efuse missing: res=%p\n", res);
+ return -EINVAL;
+ }
+ cpr_vreg->cpr_fuse_addr = res->start;
+
+ rc = cpr_init_cpr_efuse(cpr_vreg);
+ if (rc)
+ return rc;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rbcpr");
+ if (!res || !res->start) {
+ pr_err("missing rbcpr address: res=%p\n", res);
+ return -EINVAL;
+ }
+ cpr_vreg->rbcpr_base = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+
+ /* Init all voltage set points of APC regulator for CPR */
+ cpr_init_cpr_voltages(cpr_vreg);
+
+ /* Init CPR configuration parameters */
+ rc = cpr_init_cpr_parameters(pdev, cpr_vreg);
+ if (rc)
+ return rc;
+
+ /* Get and Init interrupt */
+ cpr_vreg->cpr_irq = platform_get_irq(pdev, 0);
+ if (!cpr_vreg->cpr_irq) {
+ pr_err("missing CPR IRQ\n");
+ return -EINVAL;
+ }
+
+ /* Configure CPR HW but keep it disabled */
+ cpr_config(cpr_vreg);
+
+ rc = request_threaded_irq(cpr_vreg->cpr_irq, NULL, cpr_irq_handler,
+ IRQF_TRIGGER_RISING, "cpr", cpr_vreg);
+ if (rc) {
+ pr_err("CPR: request irq failed for IRQ %d\n",
+ cpr_vreg->cpr_irq);
+ return rc;
+ }
+
+ return 0;
+}
+
+static int __init cpr_pvs_parse_dt(struct platform_device *pdev,
+ struct cpr_regulator *cpr_vreg)
{
struct device_node *of_node = pdev->dev.of_node;
struct resource *res;
@@ -327,13 +1249,12 @@
size_t pvs_bins;
/* Parse process voltage parameters */
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
- "efuse_phys");
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pvs_efuse");
if (!res || !res->start) {
- pr_err("efuse_phys missing: res=%p\n", res);
+ pr_err("pvs_efuse missing: res=%p\n", res);
return -EINVAL;
}
- cpr_vreg->efuse_phys = res->start;
+ cpr_vreg->pvs_efuse = res->start;
rc = of_property_read_u32(of_node, "qcom,num-efuse-bits",
&cpr_vreg->num_efuse_bits);
@@ -349,14 +1270,6 @@
return -EINVAL;
}
- rc = of_property_read_u32_array(of_node, "qcom,efuse-bit-pos",
- cpr_vreg->efuse_bit_pos,
- cpr_vreg->num_efuse_bits);
- if (rc < 0) {
- pr_err("efuse-bit-pos missing: rc=%d\n", rc);
- return rc;
- }
-
pvs_bins = 1 << cpr_vreg->num_efuse_bits;
rc = of_property_read_u32_array(of_node, "qcom,pvs-bin-process",
cpr_vreg->pvs_bin_process,
@@ -368,7 +1281,7 @@
rc = of_property_read_u32_array(of_node,
"qcom,pvs-corner-ceiling-slow",
- &cpr_vreg->pvs_corner_ceiling[APC_PVS_SLOW][CPR_CORNER_SVS],
+ &cpr_vreg->pvs_corner_v[APC_PVS_SLOW][CPR_CORNER_SVS],
CPR_CORNER_MAX - CPR_CORNER_SVS);
if (rc < 0) {
pr_err("pvs-corner-ceiling-slow missing: rc=%d\n", rc);
@@ -377,7 +1290,7 @@
rc = of_property_read_u32_array(of_node,
"qcom,pvs-corner-ceiling-nom",
- &cpr_vreg->pvs_corner_ceiling[APC_PVS_NOM][CPR_CORNER_SVS],
+ &cpr_vreg->pvs_corner_v[APC_PVS_NOM][CPR_CORNER_SVS],
CPR_CORNER_MAX - CPR_CORNER_SVS);
if (rc < 0) {
pr_err("pvs-corner-ceiling-norm missing: rc=%d\n", rc);
@@ -386,7 +1299,7 @@
rc = of_property_read_u32_array(of_node,
"qcom,pvs-corner-ceiling-fast",
- &cpr_vreg->pvs_corner_ceiling[APC_PVS_FAST][CPR_CORNER_SVS],
+ &cpr_vreg->pvs_corner_v[APC_PVS_FAST][CPR_CORNER_SVS],
CPR_CORNER_MAX - CPR_CORNER_SVS);
if (rc < 0) {
pr_err("pvs-corner-ceiling-fast missing: rc=%d\n", rc);
@@ -426,25 +1339,33 @@
return -ENOMEM;
}
- rc = cpr_regulator_parse_dt(pdev, cpr_vreg);
+ rc = cpr_pvs_parse_dt(pdev, cpr_vreg);
if (rc) {
pr_err("Wrong DT parameter specified: rc=%d\n", rc);
return rc;
}
- rc = cpr_regulator_pvs_init(cpr_vreg);
+ rc = cpr_pvs_init(cpr_vreg);
if (rc) {
pr_err("Initialize PVS wrong: rc=%d\n", rc);
return rc;
}
- rc = cpr_regulator_apc_init(pdev, cpr_vreg);
+ rc = cpr_apc_init(pdev, cpr_vreg);
if (rc) {
if (rc != -EPROBE_DEFER)
pr_err("Initialize APC wrong: rc=%d\n", rc);
return rc;
}
+ rc = cpr_init_cpr(pdev, cpr_vreg);
+ if (rc) {
+ pr_err("Initialize CPR failed: rc=%d\n", rc);
+ return rc;
+ }
+
+ mutex_init(&cpr_vreg->cpr_mutex);
+
rdesc = &cpr_vreg->rdesc;
rdesc->owner = THIS_MODULE;
rdesc->type = REGULATOR_VOLTAGE;
@@ -457,17 +1378,12 @@
rc = PTR_ERR(cpr_vreg->rdev);
pr_err("regulator_register failed: rc=%d\n", rc);
- cpr_regulator_apc_exit(cpr_vreg);
+ cpr_apc_exit(cpr_vreg);
return rc;
}
platform_set_drvdata(pdev, cpr_vreg);
-
- pr_info("PVS [%d %d %d %d] uV\n",
- cpr_vreg->corner_ceiling[CPR_CORNER_SVS],
- cpr_vreg->corner_ceiling[CPR_CORNER_NORMAL],
- cpr_vreg->corner_ceiling[CPR_CORNER_TURBO],
- cpr_vreg->corner_ceiling[CPR_CORNER_SUPER_TURBO]);
+ the_cpr = cpr_vreg;
return 0;
}
@@ -478,7 +1394,13 @@
cpr_vreg = platform_get_drvdata(pdev);
if (cpr_vreg) {
- cpr_regulator_apc_exit(cpr_vreg);
+ /* Disable CPR */
+ if (cpr_is_allowed(cpr_vreg)) {
+ cpr_ctl_disable(cpr_vreg);
+ cpr_irq_set(cpr_vreg, 0);
+ }
+
+ cpr_apc_exit(cpr_vreg);
regulator_unregister(cpr_vreg->rdev);
}
@@ -498,6 +1420,8 @@
},
.probe = cpr_regulator_probe,
.remove = __devexit_p(cpr_regulator_remove),
+ .suspend = cpr_regulator_suspend,
+ .resume = cpr_regulator_resume,
};
/**
diff --git a/arch/arm/mach-msm/cpufreq.c b/arch/arm/mach-msm/cpufreq.c
index d5084e4..2e70c83 100644
--- a/arch/arm/mach-msm/cpufreq.c
+++ b/arch/arm/mach-msm/cpufreq.c
@@ -402,7 +402,8 @@
per_cpu(cpufreq_suspend, cpu).device_suspended = 0;
}
- msm_cpufreq_wq = create_workqueue("msm-cpufreq");
+ msm_cpufreq_wq = alloc_workqueue("msm-cpufreq",
+ WQ_MEM_RECLAIM | WQ_HIGHPRI, 1);
register_hotcpu_notifier(&msm_cpufreq_cpu_notifier);
return cpufreq_register_driver(&msm_cpufreq_driver);
diff --git a/arch/arm/mach-msm/devices-8064.c b/arch/arm/mach-msm/devices-8064.c
index 2f44566..14fe79d 100644
--- a/arch/arm/mach-msm/devices-8064.c
+++ b/arch/arm/mach-msm/devices-8064.c
@@ -904,6 +904,7 @@
.gpios = tspp_gpios,
.tsif_pclk = "iface_clk",
.tsif_ref_clk = "ref_clk",
+ .tsif_vreg_present = 0,
};
struct platform_device msm_8064_device_tspp = {
diff --git a/arch/arm/mach-msm/include/mach/msm_ipc_router.h b/arch/arm/mach-msm/include/mach/msm_ipc_router.h
index c68c783..894379e 100644
--- a/arch/arm/mach-msm/include/mach/msm_ipc_router.h
+++ b/arch/arm/mach-msm/include/mach/msm_ipc_router.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2013, 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,6 +31,11 @@
MSM_IPC_ROUTER_WRITE_DONE,
};
+struct comm_mode_info {
+ int mode;
+ void *xprt_info;
+};
+
struct msm_ipc_port {
struct list_head list;
@@ -39,6 +44,7 @@
uint32_t type;
unsigned flags;
spinlock_t port_lock;
+ struct comm_mode_info mode_info;
struct list_head incomplete;
struct mutex incomplete_lock;
diff --git a/arch/arm/mach-msm/include/mach/msm_smsm.h b/arch/arm/mach-msm/include/mach/msm_smsm.h
index 81a6399..5173c12 100644
--- a/arch/arm/mach-msm/include/mach/msm_smsm.h
+++ b/arch/arm/mach-msm/include/mach/msm_smsm.h
@@ -353,7 +353,7 @@
}
static inline phys_addr_t smem_virt_to_phys(void *smem_address)
{
- return NULL;
+ return (phys_addr_t) NULL;
}
#endif
#endif
diff --git a/arch/arm/mach-msm/include/mach/msm_tspp.h b/arch/arm/mach-msm/include/mach/msm_tspp.h
index 696d4ef..ddc99f3 100644
--- a/arch/arm/mach-msm/include/mach/msm_tspp.h
+++ b/arch/arm/mach-msm/include/mach/msm_tspp.h
@@ -20,6 +20,7 @@
const struct msm_gpio *gpios;
const char *tsif_pclk;
const char *tsif_ref_clk;
+ int tsif_vreg_present;
};
struct tspp_data_descriptor {
diff --git a/arch/arm/mach-msm/include/mach/qseecomi.h b/arch/arm/mach-msm/include/mach/qseecomi.h
index e889242..3a997be 100644
--- a/arch/arm/mach-msm/include/mach/qseecomi.h
+++ b/arch/arm/mach-msm/include/mach/qseecomi.h
@@ -67,9 +67,9 @@
};
enum qseecom_pipe_type {
- QSEOS_PIPE_ENC = 0,
- QSEOS_PIPE_ENC_XTS,
- QSEOS_PIPE_AUTH,
+ QSEOS_PIPE_ENC = 0x1,
+ QSEOS_PIPE_ENC_XTS = 0x2,
+ QSEOS_PIPE_AUTH = 0x4,
QSEOS_PIPE_ENUM_FILL = 0x7FFFFFFF
};
diff --git a/arch/arm/mach-msm/include/mach/socinfo.h b/arch/arm/mach-msm/include/mach/socinfo.h
index 64531f0..dc8cbaa 100644
--- a/arch/arm/mach-msm/include/mach/socinfo.h
+++ b/arch/arm/mach-msm/include/mach/socinfo.h
@@ -53,6 +53,8 @@
of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,apq8084")
#define early_machine_is_msmkrypton() \
of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,msmkrypton")
+#define early_machine_is_fsm9900() \
+ of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,fsm9900")
#else
#define of_board_is_sim() 0
#define of_board_is_rumi() 0
@@ -70,6 +72,7 @@
#define early_machine_is_mpq8092() 0
#define early_machine_is_apq8084() 0
#define early_machine_is_msmkrypton() 0
+#define early_machine_is_fsm9900() 0
#endif
#define PLATFORM_SUBTYPE_MDM 1
@@ -110,6 +113,7 @@
MSM_CPU_8625Q,
MSM_CPU_8084,
MSM_CPU_KRYPTON,
+ FSM_CPU_9900,
};
enum pmic_model {
diff --git a/arch/arm/mach-msm/include/mach/subsystem_restart.h b/arch/arm/mach-msm/include/mach/subsystem_restart.h
index 893db0b..35b1f76 100644
--- a/arch/arm/mach-msm/include/mach/subsystem_restart.h
+++ b/arch/arm/mach-msm/include/mach/subsystem_restart.h
@@ -41,7 +41,8 @@
* @powerup: Start a subsystem
* @crash_shutdown: Shutdown a subsystem when the system crashes (can't sleep)
* @ramdump: Collect a ramdump of the subsystem
- * @is_loadable: Indicate if subsystem firmware is loadable via pil framework
+ * @is_not_loadable: Indicate if subsystem firmware is not loadable via pil
+ * framework
*/
struct subsys_desc {
const char *name;
@@ -57,7 +58,7 @@
void (*crash_shutdown)(const struct subsys_desc *desc);
int (*ramdump)(int, const struct subsys_desc *desc);
unsigned int err_ready_irq;
- int is_loadable;
+ int is_not_loadable;
};
#if defined(CONFIG_MSM_SUBSYSTEM_RESTART)
diff --git a/arch/arm/mach-msm/ipc_router.c b/arch/arm/mach-msm/ipc_router.c
index c31c969..a328b2b 100644
--- a/arch/arm/mach-msm/ipc_router.c
+++ b/arch/arm/mach-msm/ipc_router.c
@@ -1245,6 +1245,67 @@
return 0;
}
+static int msm_ipc_router_send_remove_client(struct comm_mode_info *mode_info,
+ uint32_t node_id, uint32_t port_id)
+{
+ union rr_control_msg msg;
+ struct msm_ipc_router_xprt_info *tmp_xprt_info;
+ int mode;
+ void *xprt_info;
+ int rc = 0;
+
+ if (!mode_info) {
+ pr_err("%s: NULL mode_info\n", __func__);
+ return -EINVAL;
+ }
+ mode = mode_info->mode;
+ xprt_info = mode_info->xprt_info;
+
+ msg.cmd = IPC_ROUTER_CTRL_CMD_REMOVE_CLIENT;
+ msg.cli.node_id = node_id;
+ msg.cli.port_id = port_id;
+
+ if ((mode == SINGLE_LINK_MODE) && xprt_info) {
+ mutex_lock(&xprt_info_list_lock);
+ list_for_each_entry(tmp_xprt_info, &xprt_info_list, list) {
+ if (tmp_xprt_info != xprt_info)
+ continue;
+ msm_ipc_router_send_control_msg(tmp_xprt_info, &msg);
+ break;
+ }
+ mutex_unlock(&xprt_info_list_lock);
+ } else if ((mode == SINGLE_LINK_MODE) && !xprt_info) {
+ broadcast_ctl_msg_locally(&msg);
+ } else if (mode == MULTI_LINK_MODE) {
+ broadcast_ctl_msg(&msg);
+ broadcast_ctl_msg_locally(&msg);
+ } else if (mode != NULL_MODE) {
+ pr_err("%s: Invalid mode(%d) + xprt_inf(%p) for %08x:%08x\n",
+ __func__, mode, xprt_info, node_id, port_id);
+ rc = -EINVAL;
+ }
+ return rc;
+}
+
+static void update_comm_mode_info(struct comm_mode_info *mode_info,
+ struct msm_ipc_router_xprt_info *xprt_info)
+{
+ if (!mode_info) {
+ pr_err("%s: NULL mode_info\n", __func__);
+ return;
+ }
+
+ if (mode_info->mode == NULL_MODE) {
+ mode_info->xprt_info = xprt_info;
+ mode_info->mode = SINGLE_LINK_MODE;
+ } else if (mode_info->mode == SINGLE_LINK_MODE &&
+ mode_info->xprt_info != xprt_info) {
+ mode_info->mode = MULTI_LINK_MODE;
+ }
+
+ return;
+}
+
static void reset_remote_port_info(uint32_t node_id, uint32_t port_id)
{
struct msm_ipc_router_remote_port *rport_ptr;
@@ -1915,6 +1976,7 @@
broadcast_ctl_msg(&ctl);
spin_lock_irqsave(&port_ptr->port_lock, flags);
port_ptr->type = SERVER_PORT;
+ port_ptr->mode_info.mode = MULTI_LINK_MODE;
port_ptr->port_name.service = server->name.service;
port_ptr->port_name.instance = server->name.instance;
spin_unlock_irqrestore(&port_ptr->port_lock, flags);
@@ -2026,6 +2088,7 @@
ret_len = pkt->length;
wake_up(&port_ptr->port_rx_wait_q);
mutex_unlock(&port_ptr->port_rx_q_lock);
+ update_comm_mode_info(&src->mode_info, NULL);
mutex_unlock(&local_ports_lock);
return ret_len;
@@ -2118,6 +2181,7 @@
pr_err("%s: Write on XPRT failed\n", __func__);
return ret;
}
+ update_comm_mode_info(&src->mode_info, xprt_info);
RAW_HDR("[w rr_h] "
"ver=%i,type=%s,src_nid=%08x,src_port_id=%08x,"
@@ -2407,13 +2471,11 @@
* Server port could have been a client port earlier.
* Send REMOVE_CLIENT message in either case.
*/
- msg.cmd = IPC_ROUTER_CTRL_CMD_REMOVE_CLIENT;
- msg.cli.node_id = port_ptr->this_port.node_id;
- msg.cli.port_id = port_ptr->this_port.port_id;
RR("x REMOVE_CLIENT id=%d:%08x\n",
- msg.cli.node_id, msg.cli.port_id);
- broadcast_ctl_msg(&msg);
- broadcast_ctl_msg_locally(&msg);
+ port_ptr->this_port.node_id, port_ptr->this_port.port_id);
+ msm_ipc_router_send_remove_client(&port_ptr->mode_info,
+ port_ptr->this_port.node_id,
+ port_ptr->this_port.port_id);
} else if (port_ptr->type == CONTROL_PORT) {
mutex_lock(&control_ports_lock);
list_del(&port_ptr->list);
diff --git a/arch/arm/mach-msm/ipc_router.h b/arch/arm/mach-msm/ipc_router.h
index cafcdd2..32832dd 100644
--- a/arch/arm/mach-msm/ipc_router.h
+++ b/arch/arm/mach-msm/ipc_router.h
@@ -62,6 +62,12 @@
IRSC_PORT,
};
+enum {
+ NULL_MODE,
+ SINGLE_LINK_MODE,
+ MULTI_LINK_MODE,
+};
+
union rr_control_msg {
uint32_t cmd;
struct {
diff --git a/arch/arm/mach-msm/krait-regulator.c b/arch/arm/mach-msm/krait-regulator.c
index 9014f37..7c1b8d6 100644
--- a/arch/arm/mach-msm/krait-regulator.c
+++ b/arch/arm/mach-msm/krait-regulator.c
@@ -92,6 +92,16 @@
#define MDD_CONFIG_CTL 0x00000000
#define MDD_MODE 0x00000010
+#define PHASE_SCALING_REF 4
+
+/* bit definitions for phase scaling eFuses */
+#define PHASE_SCALING_EFUSE_VERSION_POS 26
+#define PHASE_SCALING_EFUSE_VERSION_MASK KRAIT_MASK(27, 26)
+#define PHASE_SCALING_EFUSE_VERSION_SET 1
+
+#define PHASE_SCALING_EFUSE_VALUE_POS 16
+#define PHASE_SCALING_EFUSE_VALUE_MASK KRAIT_MASK(18, 16)
+
/* bit definitions for APC_PWR_GATE_CTL */
#define BHS_CNT_BIT_POS 24
#define BHS_CNT_MASK KRAIT_MASK(31, 24)
@@ -149,6 +159,10 @@
* @manage_phases: begin phase control
* @pfm_threshold: the sum of coefficients below which PFM can be
* enabled
+ * @efuse_phase_scaling_factor: Phase scaling factor read out of an eFuse. When
+ * calculating the appropriate phase count to use,
+ * coeff2 is multiplied by this factor and then
+ * divided by PHASE_SCALING_REF.
*/
struct pmic_gang_vreg {
const char *name;
@@ -163,6 +177,7 @@
void __iomem *apcs_gcc_base;
bool manage_phases;
int pfm_threshold;
+ int efuse_phase_scaling_factor;
};
static struct pmic_gang_vreg *the_gang;
@@ -202,6 +217,12 @@
static u32 version;
+static int use_efuse_phase_scaling_factor;
+module_param_named(
+ use_phase_scaling_efuse, use_efuse_phase_scaling_factor, int,
+ S_IRUSR | S_IWUSR
+);
+
static int is_between(int left, int right, int value)
{
if (left >= right && left >= value && value >= right)
@@ -304,7 +325,7 @@
}
#define COEFF2_UV_THRESHOLD 850000
-static int get_coeff2(int krait_uV)
+static int get_coeff2(int krait_uV, int phase_scaling_factor)
{
int coeff2 = 0;
int krait_mV = krait_uV / 1000;
@@ -314,6 +335,8 @@
else
coeff2 = (892564 * krait_mV) / 1000 - 449543;
+ coeff2 = coeff2 * phase_scaling_factor / PHASE_SCALING_REF;
+
return coeff2;
}
@@ -330,6 +353,10 @@
int coeff_total = 0;
struct krait_power_vreg *kvreg;
struct pmic_gang_vreg *pvreg = from->pvreg;
+ int phase_scaling_factor = PHASE_SCALING_REF;
+
+ if (use_efuse_phase_scaling_factor)
+ phase_scaling_factor = pvreg->efuse_phase_scaling_factor;
list_for_each_entry(kvreg, &pvreg->krait_power_vregs, link) {
if (!kvreg->online)
@@ -340,12 +367,14 @@
get_coeff1(kvreg->uV - kvreg->ldo_delta_uV,
kvreg->uV, kvreg->load);
kvreg->coeff2 =
- get_coeff2(kvreg->uV - kvreg->ldo_delta_uV);
+ get_coeff2(kvreg->uV - kvreg->ldo_delta_uV,
+ phase_scaling_factor);
} else {
kvreg->coeff1 =
get_coeff1(pvreg->pmic_vmax_uV,
kvreg->uV, kvreg->load);
- kvreg->coeff2 = get_coeff2(pvreg->pmic_vmax_uV);
+ kvreg->coeff2 = get_coeff2(pvreg->pmic_vmax_uV,
+ phase_scaling_factor);
}
coeff_total += kvreg->coeff1 + kvreg->coeff2;
}
@@ -1226,6 +1255,65 @@
.resume = boot_cpu_mdd_on,
};
+static int __devinit krait_pdn_phase_scaling_init(struct pmic_gang_vreg *pvreg,
+ struct platform_device *pdev)
+{
+ struct resource *res;
+ void __iomem *efuse;
+ u32 efuse_data, efuse_version;
+ bool scaling_factor_valid, use_efuse;
+
+ use_efuse = of_property_read_bool(pdev->dev.of_node,
+ "qcom,use-phase-scaling-factor");
+ /*
+ * Allow usage of the eFuse phase scaling factor if it is enabled in
+ * either device tree or by module parameter.
+ */
+ use_efuse_phase_scaling_factor = use_efuse_phase_scaling_factor
+ || use_efuse;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "phase-scaling-efuse");
+ if (!res || !res->start) {
+ pr_err("phase scaling eFuse address is missing\n");
+ return -EINVAL;
+ }
+
+ efuse = ioremap(res->start, 8);
+ if (!efuse) {
+ pr_err("could not map phase scaling eFuse address\n");
+ return -EINVAL;
+ }
+
+ efuse_data = readl_relaxed(efuse);
+ efuse_version = readl_relaxed(efuse + 4);
+
+ iounmap(efuse);
+
+ scaling_factor_valid
+ = ((efuse_version & PHASE_SCALING_EFUSE_VERSION_MASK) >>
+ PHASE_SCALING_EFUSE_VERSION_POS)
+ == PHASE_SCALING_EFUSE_VERSION_SET;
+
+ if (scaling_factor_valid)
+ pvreg->efuse_phase_scaling_factor
+ = ((efuse_data & PHASE_SCALING_EFUSE_VALUE_MASK)
+ >> PHASE_SCALING_EFUSE_VALUE_POS) + 1;
+ else
+ pvreg->efuse_phase_scaling_factor = PHASE_SCALING_REF;
+
+ pr_info("eFuse phase scaling factor = %d/%d%s\n",
+ pvreg->efuse_phase_scaling_factor, PHASE_SCALING_REF,
+ scaling_factor_valid ? "" : " (eFuse not blown)");
+ pr_info("initial phase scaling factor = %d/%d%s\n",
+ use_efuse_phase_scaling_factor
+ ? pvreg->efuse_phase_scaling_factor : PHASE_SCALING_REF,
+ PHASE_SCALING_REF,
+ use_efuse_phase_scaling_factor ? "" : " (ignoring eFuse)");
+
+ return 0;
+}
+
static int __devinit krait_pdn_probe(struct platform_device *pdev)
{
int rc;
@@ -1269,6 +1357,10 @@
if (pvreg->apcs_gcc_base == NULL)
return -ENOMEM;
+ rc = krait_pdn_phase_scaling_init(pvreg, pdev);
+ if (rc)
+ return rc;
+
pvreg->name = "pmic_gang";
pvreg->pmic_vmax_uV = PMIC_VOLTAGE_MIN;
pvreg->pmic_phase_count = -EINVAL;
diff --git a/arch/arm/mach-msm/lpm_resources.c b/arch/arm/mach-msm/lpm_resources.c
index f0e5ebd..624a27c 100644
--- a/arch/arm/mach-msm/lpm_resources.c
+++ b/arch/arm/mach-msm/lpm_resources.c
@@ -429,7 +429,7 @@
trace_lpm_resources(rs->sleep_value, rs->name);
}
-static void msm_lpm_set_l2_mode(int sleep_mode, int notify_rpm)
+static void msm_lpm_set_l2_mode(int sleep_mode)
{
int lpm, rc;
@@ -453,7 +453,7 @@
break;
}
- rc = msm_spm_l2_set_low_power_mode(lpm, notify_rpm);
+ rc = msm_spm_l2_set_low_power_mode(lpm, true);
if (rc < 0)
pr_err("%s: Failed to set L2 low power mode %d",
@@ -474,7 +474,7 @@
{
struct msm_lpm_resource *rs = &msm_lpm_l2;
- msm_lpm_set_l2_mode(rs->sleep_value, notify_rpm);
+ msm_lpm_set_l2_mode(rs->sleep_value);
}
int msm_lpm_get_l2_cache_value(struct device_node *node,
@@ -786,7 +786,7 @@
msm_mpm_exit_sleep(from_idle);
if (msm_lpm_l2.valid)
- msm_lpm_set_l2_mode(msm_lpm_l2.rs_data.default_value, false);
+ msm_lpm_set_l2_mode(msm_lpm_l2.rs_data.default_value);
}
static int msm_lpm_cpu_callback(struct notifier_block *cpu_nb,
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 5002a7d..eddf017 100644
--- a/arch/arm/mach-msm/msm_bus/msm_bus_arb.c
+++ b/arch/arm/mach-msm/msm_bus/msm_bus_arb.c
@@ -247,6 +247,11 @@
struct msm_bus_fabric_device *gwfab =
msm_bus_get_fabric_device(fabnodeinfo->
info->node_info->priv_id);
+ if (!gwfab) {
+ MSM_BUS_ERR("Err: No gateway found\n");
+ return -ENXIO;
+ }
+
if (!gwfab->visited) {
MSM_BUS_DBG("VISITED ID: %d\n",
gwfab->id);
@@ -320,6 +325,12 @@
struct msm_bus_fabric_device *fabdev = msm_bus_get_fabric_device
(GET_FABID(curr));
+ if (!fabdev) {
+ MSM_BUS_ERR("Bus device for bus ID: %d not found!\n",
+ GET_FABID(curr));
+ return -ENXIO;
+ }
+
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);
@@ -525,6 +536,11 @@
goto err;
}
srcfab = msm_bus_get_fabric_device(GET_FABID(src));
+ if (!srcfab) {
+ MSM_BUS_ERR("Fabric not found\n");
+ goto err;
+ }
+
srcfab->visited = true;
pnode[i] = getpath(src, dest);
bus_for_each_dev(&msm_bus_type, NULL, NULL, clearvisitedflag);
@@ -661,6 +677,12 @@
struct msm_bus_fabric_device *fabdev;
int index, next_pnode;
fabdev = msm_bus_get_fabric_device(GET_FABID(curr));
+ if (!fabdev) {
+ MSM_BUS_ERR("Fabric not found for: %d\n",
+ (GET_FABID(curr)));
+ return -ENXIO;
+ }
+
index = GET_INDEX(pnode);
info = fabdev->algo->find_node(fabdev, curr);
if (!info) {
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 cd6693e..f05b381 100644
--- a/arch/arm/mach-msm/msm_bus/msm_bus_bimc.c
+++ b/arch/arm/mach-msm/msm_bus/msm_bus_bimc.c
@@ -1769,8 +1769,13 @@
}
}
+ if (fab_pdata->virt) {
+ MSM_BUS_DBG("Don't get memory regions for virtual fabric\n");
+ goto skip_mem;
+ }
+
bimc_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!bimc_mem && !fab_pdata->virt) {
+ if (!bimc_mem) {
MSM_BUS_ERR("Cannot get BIMC Base address\n");
kfree(binfo);
return NULL;
@@ -1792,6 +1797,7 @@
return NULL;
}
+skip_mem:
fab_pdata->hw_data = (void *)binfo;
return (void *)binfo;
}
diff --git a/arch/arm/mach-msm/msm_bus/msm_bus_config.c b/arch/arm/mach-msm/msm_bus/msm_bus_config.c
index c6fa250..858b15e 100644
--- a/arch/arm/mach-msm/msm_bus/msm_bus_config.c
+++ b/arch/arm/mach-msm/msm_bus/msm_bus_config.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2013, 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
@@ -39,7 +39,7 @@
MSM_BUS_DBG("master_port: %d iid: %d fabid%d\n",
master_port, priv_id, GET_FABID(priv_id));
fabdev = msm_bus_get_fabric_device(GET_FABID(priv_id));
- if (IS_ERR(fabdev)) {
+ if (IS_ERR_OR_NULL(fabdev)) {
MSM_BUS_ERR("Fabric device not found for mport: %d\n",
master_port);
return -ENODEV;
@@ -65,7 +65,7 @@
MSM_BUS_DBG("master_port: %d iid: %d fabid: %d\n",
master_port, priv_id, GET_FABID(priv_id));
fabdev = msm_bus_get_fabric_device(GET_FABID(priv_id));
- if (IS_ERR(fabdev)) {
+ if (IS_ERR_OR_NULL(fabdev)) {
MSM_BUS_ERR("Fabric device not found for mport: %d\n",
master_port);
return -ENODEV;
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 af3537c..4e25637 100644
--- a/arch/arm/mach-msm/msm_bus/msm_bus_of.c
+++ b/arch/arm/mach-msm/msm_bus/msm_bus_of.c
@@ -103,6 +103,11 @@
}
vec_arr = of_get_property(of_node, "qcom,msm-bus,vectors-KBps", &len);
+ if (vec_arr == NULL) {
+ pr_err("Error: Vector array not found\n");
+ goto err;
+ }
+
if (len != num_usecases * num_paths * sizeof(uint32_t) * 4) {
pr_err("Error: Length-error on getting vectors\n");
goto err;
@@ -432,7 +437,7 @@
struct msm_bus_fabric_registration
*msm_bus_of_get_fab_data(struct platform_device *pdev)
{
- struct device_node *of_node = pdev->dev.of_node;
+ struct device_node *of_node;
struct msm_bus_fabric_registration *pdata;
bool mem_err = false;
int ret = 0;
@@ -443,6 +448,7 @@
return NULL;
}
+ of_node = pdev->dev.of_node;
pdata = devm_kzalloc(&pdev->dev,
sizeof(struct msm_bus_fabric_registration), GFP_KERNEL);
if (!pdata) {
diff --git a/arch/arm/mach-msm/msm_dcvs.c b/arch/arm/mach-msm/msm_dcvs.c
index 9e0be63..1a919fc 100644
--- a/arch/arm/mach-msm/msm_dcvs.c
+++ b/arch/arm/mach-msm/msm_dcvs.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2013, 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
@@ -1026,6 +1026,9 @@
uint32_t ret1;
uint32_t ret2;
+ if (!msm_dcvs_enabled)
+ return ret;
+
offset = get_core_offset(type, type_core_num);
if (offset < 0)
return ret;
@@ -1277,6 +1280,9 @@
struct kobject *module_kobj = NULL;
int ret = 0;
+ if (!msm_dcvs_enabled)
+ return ret;
+
module_kobj = kset_find_obj(module_kset, KBUILD_MODNAME);
if (!module_kobj) {
pr_err("%s: cannot find kobject for module %s\n",
@@ -1343,6 +1349,7 @@
ret = msm_dcvs_scm_init(SZ_32K);
if (ret) {
__err("Unable to initialize DCVS err=%d\n", ret);
+ msm_dcvs_enabled = 0;
goto done;
}
diff --git a/arch/arm/mach-msm/pil-q6v5-lpass.c b/arch/arm/mach-msm/pil-q6v5-lpass.c
index 04c1be3..6e8e79e 100644
--- a/arch/arm/mach-msm/pil-q6v5-lpass.c
+++ b/arch/arm/mach-msm/pil-q6v5-lpass.c
@@ -409,6 +409,12 @@
drv->err_fatal_irq = ret;
ret = gpio_to_irq(of_get_named_gpio(pdev->dev.of_node,
+ "qcom,gpio-err-ready", 0));
+ if (ret < 0)
+ return ret;
+ drv->subsys_desc.err_ready_irq = ret;
+
+ ret = gpio_to_irq(of_get_named_gpio(pdev->dev.of_node,
"qcom,gpio-proxy-unvote", 0));
if (ret < 0)
return ret;
diff --git a/arch/arm/mach-msm/pil-q6v5-mss.c b/arch/arm/mach-msm/pil-q6v5-mss.c
index e993282..979458e 100644
--- a/arch/arm/mach-msm/pil-q6v5-mss.c
+++ b/arch/arm/mach-msm/pil-q6v5-mss.c
@@ -104,13 +104,15 @@
static int pil_mss_power_up(struct q6v5_data *drv)
{
- int ret;
+ int ret = 0;
struct device *dev = drv->desc.dev;
u32 regval;
- ret = regulator_enable(drv->vreg);
- if (ret)
- dev_err(dev, "Failed to enable modem regulator.\n");
+ if (drv->vreg) {
+ ret = regulator_enable(drv->vreg);
+ if (ret)
+ dev_err(dev, "Failed to enable modem regulator.\n");
+ }
if (drv->cxrail_bhs) {
regval = readl_relaxed(drv->cxrail_bhs);
@@ -134,7 +136,10 @@
writel_relaxed(regval, drv->cxrail_bhs);
}
- return regulator_disable(drv->vreg);
+ if (drv->vreg)
+ return regulator_disable(drv->vreg);
+
+ return 0;
}
static int pil_mss_enable_clks(struct q6v5_data *drv)
@@ -501,7 +506,7 @@
{
struct mba_data *drv = subsys_to_drv(subsys);
- if (!subsys->is_loadable)
+ if (subsys->is_not_loadable)
return 0;
pil_shutdown(&drv->desc);
pil_shutdown(&drv->q6->desc);
@@ -513,7 +518,7 @@
struct mba_data *drv = subsys_to_drv(subsys);
int ret;
- if (!subsys->is_loadable)
+ if (subsys->is_not_loadable)
return 0;
/*
* At this time, the modem is shutdown. Therefore this function cannot
@@ -600,7 +605,7 @@
int ret;
struct mba_data *drv = subsys_to_drv(desc);
- if (!desc->is_loadable)
+ if (desc->is_not_loadable)
return 0;
ret = pil_boot(&drv->q6->desc);
@@ -623,7 +628,7 @@
{
struct mba_data *drv = subsys_to_drv(desc);
- if (!desc->is_loadable)
+ if (desc->is_not_loadable)
return;
pil_shutdown(&drv->desc);
@@ -724,6 +729,7 @@
struct q6v5_data *q6;
struct pil_desc *q6_desc, *mba_desc;
struct resource *res;
+ struct property *prop;
int ret;
int clk_ready = of_get_named_gpio(pdev->dev.of_node,
@@ -761,31 +767,36 @@
if (!q6->restart_reg)
return -ENOMEM;
- q6->vreg = devm_regulator_get(&pdev->dev, "vdd_mss");
- if (IS_ERR(q6->vreg))
- return PTR_ERR(q6->vreg);
+ q6->vreg = NULL;
+
+ prop = of_find_property(pdev->dev.of_node, "vdd_mss-supply", NULL);
+ if (prop) {
+ q6->vreg = devm_regulator_get(&pdev->dev, "vdd_mss");
+ if (IS_ERR(q6->vreg))
+ return PTR_ERR(q6->vreg);
+
+ ret = regulator_set_voltage(q6->vreg, VDD_MSS_UV,
+ MAX_VDD_MSS_UV);
+ if (ret)
+ dev_err(&pdev->dev, "Failed to set vreg voltage.\n");
+
+ ret = regulator_set_optimum_mode(q6->vreg, 100000);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to set vreg mode.\n");
+ return ret;
+ }
+ }
q6->vreg_mx = devm_regulator_get(&pdev->dev, "vdd_mx");
if (IS_ERR(q6->vreg_mx))
return PTR_ERR(q6->vreg_mx);
- ret = regulator_set_voltage(q6->vreg, VDD_MSS_UV, MAX_VDD_MSS_UV);
- if (ret)
- dev_err(&pdev->dev, "Failed to set regulator's voltage.\n");
-
- ret = regulator_set_optimum_mode(q6->vreg, 100000);
- if (ret < 0) {
- dev_err(&pdev->dev, "Failed to set regulator's mode.\n");
- return ret;
- }
-
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
"cxrail_bhs_reg");
if (res)
q6->cxrail_bhs = devm_ioremap(&pdev->dev, res->start,
resource_size(res));
-
q6->ahb_clk = devm_clk_get(&pdev->dev, "iface_clk");
if (IS_ERR(q6->ahb_clk))
return PTR_ERR(q6->ahb_clk);
@@ -825,17 +836,18 @@
static int __devinit pil_mss_driver_probe(struct platform_device *pdev)
{
struct mba_data *drv;
- int ret, err_fatal_gpio, is_loadable;
+ int ret, err_fatal_gpio, is_not_loadable;
drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
if (!drv)
return -ENOMEM;
platform_set_drvdata(pdev, drv);
- is_loadable = of_property_read_bool(pdev->dev.of_node,
- "qcom,is-loadable");
- if (is_loadable) {
- drv->subsys_desc.is_loadable = 1;
+ is_not_loadable = of_property_read_bool(pdev->dev.of_node,
+ "qcom,is-not-loadable");
+ if (is_not_loadable) {
+ drv->subsys_desc.is_not_loadable = 1;
+ } else {
ret = pil_mss_loadable_init(drv, pdev);
if (ret)
return ret;
diff --git a/arch/arm/mach-msm/pm-8x60.c b/arch/arm/mach-msm/pm-8x60.c
index 4fca346..afb2b84 100644
--- a/arch/arm/mach-msm/pm-8x60.c
+++ b/arch/arm/mach-msm/pm-8x60.c
@@ -671,7 +671,7 @@
u64 modified_time_ns = modified_time_us * NSEC_PER_USEC;
ktime_t modified_ktime = ns_to_ktime(modified_time_ns);
pm_hrtimer.function = pm_hrtimer_cb;
- hrtimer_start(&pm_hrtimer, modified_ktime, HRTIMER_MODE_ABS);
+ hrtimer_start(&pm_hrtimer, modified_ktime, HRTIMER_MODE_REL);
}
/******************************************************************************
@@ -1248,24 +1248,19 @@
static void setup_broadcast_timer(void *arg)
{
- unsigned long reason = (unsigned long)arg;
int cpu = smp_processor_id();
- reason = reason ?
- CLOCK_EVT_NOTIFY_BROADCAST_ON : CLOCK_EVT_NOTIFY_BROADCAST_OFF;
-
- clockevents_notify(reason, &cpu);
+ clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ON, &cpu);
}
static int setup_broadcast_cpuhp_notify(struct notifier_block *n,
unsigned long action, void *hcpu)
{
- int hotcpu = (unsigned long)hcpu;
+ int cpu = (unsigned long)hcpu;
switch (action & ~CPU_TASKS_FROZEN) {
case CPU_ONLINE:
- smp_call_function_single(hotcpu, setup_broadcast_timer,
- (void *)true, 1);
+ smp_call_function_single(cpu, setup_broadcast_timer, NULL, 1);
break;
}
@@ -1288,14 +1283,11 @@
msm_pm_mode_sysfs_add();
msm_pm_add_stats(enable_stats, ARRAY_SIZE(enable_stats));
suspend_set_ops(&msm_pm_ops);
- hrtimer_init(&pm_hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
+ hrtimer_init(&pm_hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
msm_cpuidle_init();
if (msm_pm_pc_reset_timer) {
- get_cpu();
- smp_call_function_many(cpu_online_mask, setup_broadcast_timer,
- (void *)true, 1);
- put_cpu();
+ on_each_cpu(setup_broadcast_timer, NULL, 1);
register_cpu_notifier(&setup_broadcast_notifier);
}
diff --git a/arch/arm/mach-msm/qdsp6v2/audio_acdb.c b/arch/arm/mach-msm/qdsp6v2/audio_acdb.c
index ea22c12..b5ccc31 100644
--- a/arch/arm/mach-msm/qdsp6v2/audio_acdb.c
+++ b/arch/arm/mach-msm/qdsp6v2/audio_acdb.c
@@ -786,7 +786,7 @@
goto done;
}
- if (size <= 0) {
+ if ((size <= 0) || (size > sizeof(data))) {
pr_err("%s: Invalid size sent to driver: %d\n",
__func__, size);
result = -EFAULT;
diff --git a/arch/arm/mach-msm/qdsp6v2/msm_audio_ion.c b/arch/arm/mach-msm/qdsp6v2/msm_audio_ion.c
index c9bc3d7..5303009 100644
--- a/arch/arm/mach-msm/qdsp6v2/msm_audio_ion.c
+++ b/arch/arm/mach-msm/qdsp6v2/msm_audio_ion.c
@@ -22,10 +22,15 @@
#include <linux/of_device.h>
#include <linux/msm_audio_ion.h>
+#include <linux/iommu.h>
+#include <mach/iommu_domains.h>
+
struct msm_audio_ion_private {
bool smmu_enabled;
- /*u32 group_id;*/
- /*u32 domain_id;*/
+ bool audioheap_enabled;
+ struct iommu_group *group;
+ u32 domain_id;
+ struct iommu_domain *domain;
};
static struct msm_audio_ion_private msm_audio_ion_data = {0,};
@@ -49,10 +54,22 @@
goto err;
}
- *handle = ion_alloc(*client, bufsz, SZ_4K, (0x1<<ION_AUDIO_HEAP_ID), 0);
+ *handle = ion_alloc(*client, bufsz, SZ_4K,
+ ION_HEAP(ION_AUDIO_HEAP_ID), 0);
if (IS_ERR_OR_NULL((void *) (*handle))) {
- pr_err("%s: ION memory allocation for AUDIO failed\n",
- __func__);
+ pr_debug("system heap is used");
+ msm_audio_ion_data.audioheap_enabled = 0;
+ *handle = ion_alloc(*client, bufsz, SZ_4K,
+ ION_HEAP(ION_SYSTEM_HEAP_ID), 0);
+
+ } else {
+ pr_debug("audio heap is used");
+ msm_audio_ion_data.audioheap_enabled = 1;
+ }
+
+ if (IS_ERR_OR_NULL((void *) (*handle))) {
+ pr_err("%s: ION memory allocation for AUDIO failed rc=%d, smmu_enabled=%d\n",
+ __func__, rc, msm_audio_ion_data.smmu_enabled);
goto err_ion_client;
}
@@ -63,15 +80,17 @@
goto err_ion_handle;
}
- /*Need to add condition SMMU enable or not */
*vaddr = ion_map_kernel(*client, *handle);
if (IS_ERR_OR_NULL((void *)*vaddr)) {
pr_err("%s: ION memory mapping for AUDIO failed\n", __func__);
goto err_ion_handle;
}
+ pr_debug("%s: mapped address = %p, size=%d\n", __func__, *vaddr, bufsz);
- if (bufsz != 0)
+ if (bufsz != 0) {
+ pr_debug("%s: memset to 0 %p %d\n", __func__, *vaddr, bufsz);
memset((void *)*vaddr, 0, bufsz);
+ }
return 0;
@@ -81,7 +100,6 @@
msm_audio_ion_client_destroy(*client);
err:
return -EINVAL;
-
}
int msm_audio_ion_import(const char *name, struct ion_client **client,
@@ -125,13 +143,6 @@
goto err_ion_handle;
}
- /*Need to add condition SMMU enable or not */
- *vaddr = ion_map_kernel(*client, *handle);
- if (IS_ERR_OR_NULL((void *)*vaddr)) {
- pr_err("%s: ION memory mapping for AUDIO failed\n", __func__);
- goto err_ion_handle;
- }
-
if (bufsz != 0)
memset((void *)*vaddr, 0, bufsz);
@@ -142,12 +153,20 @@
msm_audio_ion_client_destroy(*client);
err:
return -EINVAL;
-
}
int msm_audio_ion_free(struct ion_client *client, struct ion_handle *handle)
{
- /* add condition for SMMU enabled */
+ if (msm_audio_ion_data.smmu_enabled) {
+ /* Need to populate book kept infomation */
+ pr_debug("client=%p, domain=%p, domain_id=%d, group=%p",
+ client, msm_audio_ion_data.domain,
+ msm_audio_ion_data.domain_id, msm_audio_ion_data.group);
+
+ ion_unmap_iommu(client, handle,
+ msm_audio_ion_data.domain_id, 0);
+ }
+
ion_unmap_kernel(client, handle);
ion_free(client, handle);
@@ -155,6 +174,91 @@
return 0;
}
+int msm_audio_ion_mmap(struct audio_buffer *ab,
+ struct vm_area_struct *vma)
+{
+ struct sg_table *table;
+ unsigned long addr = vma->vm_start;
+ unsigned long offset = vma->vm_pgoff * PAGE_SIZE;
+ struct scatterlist *sg;
+ unsigned int i;
+ struct page *page;
+ int ret;
+
+ pr_debug("%s\n", __func__);
+
+ table = ion_sg_table(ab->client, ab->handle);
+
+ if (IS_ERR(table)) {
+ pr_err("%s: Unable to get sg_table from ion: %ld\n",
+ __func__, PTR_ERR(table));
+ return PTR_ERR(table);
+ } else if (!table) {
+ pr_err("%s: sg_list is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ /* uncached */
+ vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
+
+ /* We need to check if a page is associated with this sg list because:
+ * If the allocation came from a carveout we currently don't have
+ * pages associated with carved out memory. This might change in the
+ * future and we can remove this check and the else statement.
+ */
+ page = sg_page(table->sgl);
+ if (page) {
+ pr_debug("%s: page is NOT null\n", __func__);
+ for_each_sg(table->sgl, sg, table->nents, i) {
+ unsigned long remainder = vma->vm_end - addr;
+ unsigned long len = sg_dma_len(sg);
+
+ page = sg_page(sg);
+
+ if (offset >= sg_dma_len(sg)) {
+ offset -= sg_dma_len(sg);
+ continue;
+ } else if (offset) {
+ page += offset / PAGE_SIZE;
+ len = sg_dma_len(sg) - offset;
+ offset = 0;
+ }
+ len = min(len, remainder);
+ pr_debug("vma=%p, addr=%x len=%ld vm_start=%x vm_end=%x vm_page_prot=%ld\n",
+ vma, (unsigned int)addr, len,
+ (unsigned int)vma->vm_start,
+ (unsigned int)vma->vm_end,
+ (unsigned long int)vma->vm_page_prot);
+ remap_pfn_range(vma, addr, page_to_pfn(page), len,
+ vma->vm_page_prot);
+ addr += len;
+ if (addr >= vma->vm_end)
+ return 0;
+ }
+ } else {
+ ion_phys_addr_t phys_addr;
+ size_t phys_len;
+ pr_debug("%s: page is NULL\n", __func__);
+
+ ret = ion_phys(ab->client, ab->handle, &phys_addr, &phys_len);
+ if (ret) {
+ pr_err("%s: Unable to get phys address from ION buffer: %d\n"
+ , __func__ , ret);
+ return ret;
+ }
+ pr_debug("phys=%x len=%d\n", (unsigned int)phys_addr, phys_len);
+ pr_debug("vma=%p, vm_start=%x vm_end=%x vm_pgoff=%ld vm_page_prot=%ld\n",
+ vma, (unsigned int)vma->vm_start,
+ (unsigned int)vma->vm_end, vma->vm_pgoff,
+ (unsigned long int)vma->vm_page_prot);
+ ret = remap_pfn_range(vma, vma->vm_start,
+ __phys_to_pfn(phys_addr) + vma->vm_pgoff,
+ vma->vm_end - vma->vm_start,
+ vma->vm_page_prot);
+ }
+ return 0;
+}
+
bool msm_audio_ion_is_smmu_available(void)
{
@@ -165,18 +269,17 @@
struct ion_client *msm_audio_ion_client_create(unsigned int heap_mask,
const char *name)
{
- pr_debug("%s: smmu_enabled = %d\n", __func__,
- msm_audio_ion_data.smmu_enabled);
-
-
- return msm_ion_client_create(heap_mask, name);
+ struct ion_client *pclient = NULL;
+ /*IOMMU group and domain are moved to probe()*/
+ pclient = msm_ion_client_create(heap_mask, name);
+ return pclient;
}
void msm_audio_ion_client_destroy(struct ion_client *client)
{
- pr_debug("%s: smmu_enabled = %d\n", __func__,
- msm_audio_ion_data.smmu_enabled);
+ pr_debug("%s: client = %p smmu_enabled = %d\n", __func__,
+ client, msm_audio_ion_data.smmu_enabled);
ion_client_destroy(client);
}
@@ -192,9 +295,9 @@
bufsz should be 0 and fd shouldn't be 0 as of now
*/
*handle = ion_import_dma_buf(client, fd);
- pr_err("%s: DMA Buf name=%s, fd=%d handle=%p\n", __func__,
+ pr_debug("%s: DMA Buf name=%s, fd=%d handle=%p\n", __func__,
name, fd, *handle);
- if (IS_ERR_OR_NULL((void *) (*handle))) {
+ if (IS_ERR_OR_NULL((void *)(*handle))) {
pr_err("%s: ion import dma buffer failed\n",
__func__);
goto err_ion_handle;
@@ -245,6 +348,41 @@
return 0;
}
+int msm_audio_ion_cache_operations(struct audio_buffer *abuff, int cache_op)
+{
+ unsigned long ionflag = 0;
+ int rc = 0;
+ int msm_cache_ops = 0;
+
+ if (!abuff) {
+ pr_err("Invalid params: %p, %p\n", __func__, abuff);
+ return -EINVAL;
+ }
+ rc = ion_handle_get_flags(abuff->client, abuff->handle,
+ &ionflag);
+ if (rc) {
+ pr_err("ion_handle_get_flags failed: %d\n", rc);
+ goto cache_op_failed;
+ }
+
+ /* has to be CACHED */
+ if (ION_IS_CACHED(ionflag)) {
+ /* ION_IOC_INV_CACHES or ION_IOC_CLEAN_CACHES */
+ msm_cache_ops = cache_op;
+ rc = msm_ion_do_cache_op(abuff->client,
+ abuff->handle,
+ (unsigned long *) abuff->data,
+ (unsigned long)abuff->size,
+ msm_cache_ops);
+ if (rc) {
+ pr_err("cache operation failed %d\n", rc);
+ goto cache_op_failed;
+ }
+ }
+cache_op_failed:
+ return rc;
+}
+
static int msm_audio_ion_get_phys(struct ion_client *client,
struct ion_handle *handle,
@@ -255,18 +393,25 @@
msm_audio_ion_data.smmu_enabled);
if (msm_audio_ion_data.smmu_enabled) {
- /* SMMU enabled case ion_map_iommu()*/
+ rc = ion_map_iommu(client, handle, msm_audio_ion_data.domain_id,
+ 0 /*partition_num*/, SZ_4K /*align*/, 0/*iova_length*/,
+ addr, (unsigned long *)len,
+ 0, 0);
+ if (rc) {
+ pr_err("%s: ION map iommu failed %d\n", __func__, rc);
+ return rc;
+ }
+ pr_debug("client=%p, domain=%p, domain_id=%d, group=%p",
+ client, msm_audio_ion_data.domain,
+ msm_audio_ion_data.domain_id, msm_audio_ion_data.group);
} else {
/* SMMU is disabled*/
rc = ion_phys(client, handle, addr, len);
}
- pr_debug("%s: addr= 0x%p, len= %d\n", __func__, addr, *len);
+ pr_debug("phys=%x, len=%d, rc=%d\n", (unsigned int)*addr, *len, rc);
return rc;
}
-
-
-
static int msm_audio_ion_probe(struct platform_device *pdev)
{
int rc = 0;
@@ -283,13 +428,53 @@
msm_audio_ion_dt);
msm_audio_ion_data.smmu_enabled = smmu_enabled;
+ if (smmu_enabled) {
+ msm_audio_ion_data.group = iommu_group_find("lpass_audio");
+ if (!msm_audio_ion_data.group) {
+ pr_debug("Failed to find group lpass_audio deferred\n");
+ goto fail_group;
+ }
+ msm_audio_ion_data.domain =
+ iommu_group_get_iommudata(msm_audio_ion_data.group);
+ if (IS_ERR_OR_NULL(msm_audio_ion_data.domain)) {
+ pr_err("Failed to get domain data for group %p",
+ msm_audio_ion_data.group);
+ goto fail_group;
+ }
+ msm_audio_ion_data.domain_id =
+ msm_find_domain_no(msm_audio_ion_data.domain);
+ if (msm_audio_ion_data.domain_id < 0) {
+ pr_err("Failed to get domain index for domain %p",
+ msm_audio_ion_data.domain);
+ goto fail_group;
+ }
+ pr_debug("domain=%p, domain_id=%d, group=%p",
+ msm_audio_ion_data.domain,
+ msm_audio_ion_data.domain_id, msm_audio_ion_data.group);
+
+ /* iommu_attach_group() will make AXI clock ON. For future PL
+ this will require to be called in once per session */
+ rc = iommu_attach_group(msm_audio_ion_data.domain,
+ msm_audio_ion_data.group);
+ if (rc) {
+ pr_err("%s:ION attach group failed %d\n", __func__, rc);
+ return rc;
+ }
+
+ }
+
pr_debug("%s: SMMU-Enabled = %d\n", __func__, smmu_enabled);
return rc;
+
+fail_group:
+ return -EPROBE_DEFER;
}
static int msm_audio_ion_remove(struct platform_device *pdev)
{
- pr_debug("%s: msm audio ion is unloaded\n", __func__);
+ pr_debug("%s: msm audio ion is unloaded, domain=%p, group=%p\n",
+ __func__, msm_audio_ion_data.domain, msm_audio_ion_data.group);
+ iommu_detach_group(msm_audio_ion_data.domain, msm_audio_ion_data.group);
return 0;
}
diff --git a/arch/arm/mach-msm/rpm-smd.c b/arch/arm/mach-msm/rpm-smd.c
index 6ed80f6..1eb66f4 100644
--- a/arch/arm/mach-msm/rpm-smd.c
+++ b/arch/arm/mach-msm/rpm-smd.c
@@ -1349,7 +1349,8 @@
smd_disable_read_intr(msm_rpm_data.ch_info);
if (!standalone) {
- msm_rpm_smd_wq = create_singlethread_workqueue("rpm-smd");
+ msm_rpm_smd_wq = alloc_workqueue("rpm-smd",
+ WQ_UNBOUND | WQ_MEM_RECLAIM | WQ_HIGHPRI, 1);
if (!msm_rpm_smd_wq)
return -EINVAL;
queue_work(msm_rpm_smd_wq, &msm_rpm_data.work);
diff --git a/arch/arm/mach-msm/socinfo.c b/arch/arm/mach-msm/socinfo.c
index 12a3ceb..abc63ef 100644
--- a/arch/arm/mach-msm/socinfo.c
+++ b/arch/arm/mach-msm/socinfo.c
@@ -279,6 +279,8 @@
/* 8974 IDs */
[126] = MSM_CPU_8974,
[184] = MSM_CPU_8974,
+ [185] = MSM_CPU_8974,
+ [186] = MSM_CPU_8974,
/* 8625 IDs */
[127] = MSM_CPU_8625,
@@ -359,6 +361,9 @@
/* krypton IDs */
[187] = MSM_CPU_KRYPTON,
+ /* FSM9900 ID */
+ [188] = FSM_CPU_9900,
+
/* 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_devices.c b/arch/arm/mach-msm/spm_devices.c
index 8e94b3a..fc05fce 100644
--- a/arch/arm/mach-msm/spm_devices.c
+++ b/arch/arm/mach-msm/spm_devices.c
@@ -392,7 +392,7 @@
};
struct mode_of of_l2_modes[] = {
- {"qcom,saw2-spm-cmd-ret", MSM_SPM_L2_MODE_RETENTION, 0},
+ {"qcom,saw2-spm-cmd-ret", MSM_SPM_L2_MODE_RETENTION, 1},
{"qcom,saw2-spm-cmd-gdhs", MSM_SPM_L2_MODE_GDHS, 1},
{"qcom,saw2-spm-cmd-pc", MSM_SPM_L2_MODE_POWER_COLLAPSE, 1},
};
diff --git a/arch/arm/mach-msm/subsystem_restart.c b/arch/arm/mach-msm/subsystem_restart.c
index 9479d46c..c971896 100644
--- a/arch/arm/mach-msm/subsystem_restart.c
+++ b/arch/arm/mach-msm/subsystem_restart.c
@@ -495,7 +495,7 @@
if (ret)
return ret;
- if (!subsys->desc->is_loadable)
+ if (subsys->desc->is_not_loadable)
return 0;
ret = wait_for_err_ready(subsys);
@@ -938,6 +938,10 @@
struct subsys_device *subsys_dev = subsys;
pr_info("Error ready interrupt occured for %s\n",
subsys_dev->desc->name);
+
+ if (subsys_dev->desc->is_not_loadable)
+ return IRQ_HANDLED;
+
complete(&subsys_dev->err_ready);
return IRQ_HANDLED;
}
diff --git a/drivers/char/diag/diagfwd_cntl.c b/drivers/char/diag/diagfwd_cntl.c
index c3ff7dc..2776c58 100644
--- a/drivers/char/diag/diagfwd_cntl.c
+++ b/drivers/char/diag/diagfwd_cntl.c
@@ -69,7 +69,7 @@
type = *(uint32_t *)(buf);
data_len = *(uint32_t *)(buf + 4);
if (type < DIAG_CTRL_MSG_REG ||
- type > DIAG_CTRL_MSG_F3_MASK_V2) {
+ type > DIAG_CTRL_MSG_LAST) {
pr_alert("diag: In %s, Invalid Msg type %d proc %d",
__func__, type, smd_info->peripheral);
break;
diff --git a/drivers/char/diag/diagfwd_cntl.h b/drivers/char/diag/diagfwd_cntl.h
index 9c2c691..f58ab24 100644
--- a/drivers/char/diag/diagfwd_cntl.h
+++ b/drivers/char/diag/diagfwd_cntl.h
@@ -29,6 +29,12 @@
#define DIAG_CTRL_MSG_EVENT_MASK_V2 10
/* Send Diag F3 mask */
#define DIAG_CTRL_MSG_F3_MASK_V2 11
+#define DIAG_CTRL_MSG_NUM_PRESETS 12
+#define DIAG_CTRL_MSG_SET_PRESET_ID 13
+#define DIAG_CTRL_MSG_LOG_MASK_WITH_PRESET_ID 14
+#define DIAG_CTRL_MSG_EVENT_MASK_WITH_PRESET_ID 15
+#define DIAG_CTRL_MSG_F3_MASK_WITH_PRESET_ID 16
+#define DIAG_CTRL_MSG_LAST DIAG_CTRL_MSG_F3_MASK_WITH_PRESET_ID
/* Denotes that we support sending/receiving the feature mask */
#define F_DIAG_INT_FEATURE_MASK 0x01
diff --git a/drivers/coresight/Kconfig b/drivers/coresight/Kconfig
index 5e00570..7ec83dd 100644
--- a/drivers/coresight/Kconfig
+++ b/drivers/coresight/Kconfig
@@ -105,6 +105,16 @@
If unsure, say 'N' here to avoid potential power and performance
penalty.
+config CORESIGHT_HWEVENT
+ bool "CoreSight Hardware Event driver"
+ depends on CORESIGHT_STM
+ select CORESIGHT_CSR
+ help
+ This driver provides support for monitoring and tracing CoreSight
+ Hardware Event across STM interface. It configures Coresight
+ Hardware Event mux control registers to select hardware events
+ based on user input.
+
config CORESIGHT_ETM
bool "CoreSight Embedded Trace Macrocell driver"
help
diff --git a/drivers/coresight/Makefile b/drivers/coresight/Makefile
index 0595064..0e2e2d9 100644
--- a/drivers/coresight/Makefile
+++ b/drivers/coresight/Makefile
@@ -11,5 +11,6 @@
obj-$(CONFIG_CORESIGHT_FUNNEL) += coresight-funnel.o
obj-$(CONFIG_CORESIGHT_REPLICATOR) += coresight-replicator.o
obj-$(CONFIG_CORESIGHT_STM) += coresight-stm.o
+obj-$(CONFIG_CORESIGHT_HWEVENT) += coresight-hwevent.o
obj-$(CONFIG_CORESIGHT_ETM) += coresight-etm.o coresight-etm-cp14.o
obj-$(CONFIG_CORESIGHT_EVENT) += coresight-event.o
diff --git a/drivers/coresight/coresight-csr.c b/drivers/coresight/coresight-csr.c
index 1c2ab25..8195184 100644
--- a/drivers/coresight/coresight-csr.c
+++ b/drivers/coresight/coresight-csr.c
@@ -73,6 +73,7 @@
struct csr_drvdata {
void __iomem *base;
+ phys_addr_t pbase;
struct device *dev;
struct coresight_device *csdev;
uint32_t blksize;
@@ -134,6 +135,30 @@
}
EXPORT_SYMBOL(msm_qdss_csr_disable_flush);
+int coresight_csr_hwctrl_set(phys_addr_t addr, uint32_t val)
+{
+ struct csr_drvdata *drvdata = csrdrvdata;
+ int ret = 0;
+
+ CSR_UNLOCK(drvdata);
+
+ if (addr == (drvdata->pbase + CSR_STMEXTHWCTRL0))
+ csr_writel(drvdata, val, CSR_STMEXTHWCTRL0);
+ else if (addr == (drvdata->pbase + CSR_STMEXTHWCTRL1))
+ csr_writel(drvdata, val, CSR_STMEXTHWCTRL1);
+ else if (addr == (drvdata->pbase + CSR_STMEXTHWCTRL2))
+ csr_writel(drvdata, val, CSR_STMEXTHWCTRL2);
+ else if (addr == (drvdata->pbase + CSR_STMEXTHWCTRL3))
+ csr_writel(drvdata, val, CSR_STMEXTHWCTRL3);
+ else
+ ret = -EINVAL;
+
+ CSR_LOCK(drvdata);
+
+ return ret;
+}
+EXPORT_SYMBOL(coresight_csr_hwctrl_set);
+
static int __devinit csr_probe(struct platform_device *pdev)
{
int ret;
@@ -161,6 +186,7 @@
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "csr-base");
if (!res)
return -ENODEV;
+ drvdata->pbase = res->start;
drvdata->base = devm_ioremap(dev, res->start, resource_size(res));
if (!drvdata->base)
diff --git a/drivers/coresight/coresight-hwevent.c b/drivers/coresight/coresight-hwevent.c
new file mode 100644
index 0000000..777484d
--- /dev/null
+++ b/drivers/coresight/coresight-hwevent.c
@@ -0,0 +1,341 @@
+/* Copyright (c) 2013, 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/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/clk.h>
+#include <linux/of_coresight.h>
+#include <linux/coresight.h>
+#include <linux/of.h>
+
+#include "coresight-priv.h"
+
+struct hwevent_mux {
+ phys_addr_t start;
+ phys_addr_t end;
+};
+
+struct hwevent_drvdata {
+ struct device *dev;
+ struct coresight_device *csdev;
+ struct clk *clk;
+ struct mutex mutex;
+ int nr_hclk;
+ struct clk **hclk;
+ int nr_hmux;
+ struct hwevent_mux *hmux;
+ bool enable;
+};
+
+static int hwevent_enable(struct hwevent_drvdata *drvdata)
+{
+ int ret, i;
+
+ mutex_lock(&drvdata->mutex);
+
+ if (drvdata->enable)
+ goto out;
+
+ ret = clk_prepare_enable(drvdata->clk);
+ if (ret)
+ goto err0;
+ for (i = 0; i < drvdata->nr_hclk; i++) {
+ ret = clk_prepare_enable(drvdata->hclk[i]);
+ if (ret)
+ goto err1;
+ }
+ drvdata->enable = true;
+ dev_info(drvdata->dev, "Hardware Event driver enabled\n");
+out:
+ mutex_unlock(&drvdata->mutex);
+ return 0;
+err1:
+ clk_disable_unprepare(drvdata->clk);
+ for (i--; i >= 0; i--)
+ clk_disable_unprepare(drvdata->hclk[i]);
+err0:
+ mutex_unlock(&drvdata->mutex);
+ return ret;
+}
+
+static void hwevent_disable(struct hwevent_drvdata *drvdata)
+{
+ int i;
+
+ mutex_lock(&drvdata->mutex);
+
+ if (!drvdata->enable)
+ goto out;
+
+ drvdata->enable = false;
+ clk_disable_unprepare(drvdata->clk);
+ for (i = 0; i < drvdata->nr_hclk; i++)
+ clk_disable_unprepare(drvdata->hclk[i]);
+ dev_info(drvdata->dev, "Hardware Event driver disabled\n");
+out:
+ mutex_unlock(&drvdata->mutex);
+}
+
+static ssize_t hwevent_show_enable(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct hwevent_drvdata *drvdata = dev_get_drvdata(dev->parent);
+ unsigned long val = drvdata->enable;
+
+ return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
+}
+
+static ssize_t hwevent_store_enable(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct hwevent_drvdata *drvdata = dev_get_drvdata(dev->parent);
+ unsigned long val;
+ int ret = 0;
+
+ if (sscanf(buf, "%lx", &val) != 1)
+ return -EINVAL;
+
+ if (val)
+ ret = hwevent_enable(drvdata);
+ else
+ hwevent_disable(drvdata);
+
+ if (ret)
+ return ret;
+ return size;
+}
+static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR, hwevent_show_enable,
+ hwevent_store_enable);
+
+static ssize_t hwevent_store_setreg(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct hwevent_drvdata *drvdata = dev_get_drvdata(dev->parent);
+ void *hwereg;
+ phys_addr_t addr;
+ uint32_t val;
+ int ret, i;
+
+ if (sscanf(buf, "%x %x", &addr, &val) != 2)
+ return -EINVAL;
+
+ mutex_lock(&drvdata->mutex);
+
+ if (!drvdata->enable) {
+ dev_err(dev, "Hardware Event driver not enabled\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ for (i = 0; i < drvdata->nr_hmux; i++) {
+ if ((addr >= drvdata->hmux[i].start) &&
+ (addr < drvdata->hmux[i].end)) {
+ hwereg = devm_ioremap(dev,
+ drvdata->hmux[i].start,
+ drvdata->hmux[i].end -
+ drvdata->hmux[i].start);
+ if (!hwereg) {
+ dev_err(dev, "unable to map address 0x%x\n",
+ addr);
+ ret = -ENOMEM;
+ goto err;
+ }
+ writel_relaxed(val, hwereg + addr -
+ drvdata->hmux[i].start);
+ /* Ensure writes to hwevent control registers
+ are completed before unmapping the address
+ */
+ mb();
+ devm_iounmap(dev, hwereg);
+ break;
+ }
+ }
+
+ if (i == drvdata->nr_hmux) {
+ ret = coresight_csr_hwctrl_set(addr, val);
+ if (ret) {
+ dev_err(dev, "invalid mux control register address\n");
+ ret = -EINVAL;
+ goto err;
+ }
+ }
+
+ mutex_unlock(&drvdata->mutex);
+ return size;
+err:
+ mutex_unlock(&drvdata->mutex);
+ return ret;
+}
+static DEVICE_ATTR(setreg, S_IWUSR, NULL, hwevent_store_setreg);
+
+static struct attribute *hwevent_attrs[] = {
+ &dev_attr_enable.attr,
+ &dev_attr_setreg.attr,
+ NULL,
+};
+
+static struct attribute_group hwevent_attr_grp = {
+ .attrs = hwevent_attrs,
+};
+
+static const struct attribute_group *hwevent_attr_grps[] = {
+ &hwevent_attr_grp,
+ NULL,
+};
+
+static int __devinit hwevent_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct hwevent_drvdata *drvdata;
+ struct coresight_desc *desc;
+ struct coresight_platform_data *pdata;
+ struct resource *res;
+ int ret, i;
+ const char *hmux_name, *hclk_name;
+
+ if (pdev->dev.of_node) {
+ pdata = of_get_coresight_platform_data(dev, pdev->dev.of_node);
+ if (IS_ERR(pdata))
+ return PTR_ERR(pdata);
+ pdev->dev.platform_data = pdata;
+ }
+
+ drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
+ if (!drvdata)
+ return -ENOMEM;
+ drvdata->dev = &pdev->dev;
+ platform_set_drvdata(pdev, drvdata);
+
+ if (pdev->dev.of_node)
+ drvdata->nr_hmux = of_property_count_strings(pdev->dev.of_node,
+ "reg-names");
+
+ if (drvdata->nr_hmux > 0) {
+ drvdata->hmux = devm_kzalloc(dev, drvdata->nr_hmux *
+ sizeof(*drvdata->hmux),
+ GFP_KERNEL);
+ if (!drvdata->hmux)
+ return -ENOMEM;
+ for (i = 0; i < drvdata->nr_hmux; i++) {
+ ret = of_property_read_string_index(pdev->dev.of_node,
+ "reg-names", i,
+ &hmux_name);
+ if (ret)
+ return ret;
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ hmux_name);
+ if (!res)
+ return -ENODEV;
+ drvdata->hmux[i].start = res->start;
+ drvdata->hmux[i].end = res->end;
+ }
+ } else if (drvdata->nr_hmux < 0) {
+ return drvdata->nr_hmux;
+ } else {
+ /* return error if reg-names in dt node is empty string */
+ return -ENODEV;
+ }
+
+ mutex_init(&drvdata->mutex);
+
+ drvdata->clk = devm_clk_get(dev, "core_clk");
+ if (IS_ERR(drvdata->clk))
+ return PTR_ERR(drvdata->clk);
+
+ ret = clk_set_rate(drvdata->clk, CORESIGHT_CLK_RATE_TRACE);
+ if (ret)
+ return ret;
+
+ if (pdev->dev.of_node)
+ drvdata->nr_hclk = of_property_count_strings(pdev->dev.of_node,
+ "qcom,hwevent-clks");
+ if (drvdata->nr_hclk > 0) {
+ drvdata->hclk = devm_kzalloc(dev, drvdata->nr_hclk *
+ sizeof(*drvdata->hclk),
+ GFP_KERNEL);
+ if (!drvdata->hclk)
+ return -ENOMEM;
+ for (i = 0; i < drvdata->nr_hclk; i++) {
+ ret = of_property_read_string_index(pdev->dev.of_node,
+ "qcom,hwevent-clks",
+ i, &hclk_name);
+ if (ret)
+ return ret;
+ drvdata->hclk[i] = devm_clk_get(dev, hclk_name);
+ if (IS_ERR(drvdata->hclk[i]))
+ return PTR_ERR(drvdata->hclk[i]);
+ }
+ }
+
+ desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
+ if (!desc)
+ return -ENOMEM;
+
+ desc->type = CORESIGHT_DEV_TYPE_NONE;
+ desc->pdata = pdev->dev.platform_data;
+ desc->dev = &pdev->dev;
+ desc->groups = hwevent_attr_grps;
+ desc->owner = THIS_MODULE;
+ drvdata->csdev = coresight_register(desc);
+ if (IS_ERR(drvdata->csdev))
+ return PTR_ERR(drvdata->csdev);
+
+ dev_info(dev, "Hardware Event driver initialized\n");
+ return 0;
+}
+
+static int __devexit hwevent_remove(struct platform_device *pdev)
+{
+ struct hwevent_drvdata *drvdata = platform_get_drvdata(pdev);
+
+ coresight_unregister(drvdata->csdev);
+ return 0;
+}
+
+static struct of_device_id hwevent_match[] = {
+ {.compatible = "qcom,coresight-hwevent"},
+ {}
+};
+
+static struct platform_driver hwevent_driver = {
+ .probe = hwevent_probe,
+ .remove = __devexit_p(hwevent_remove),
+ .driver = {
+ .name = "coresight-hwevent",
+ .owner = THIS_MODULE,
+ .of_match_table = hwevent_match,
+ },
+};
+
+static int __init hwevent_init(void)
+{
+ return platform_driver_register(&hwevent_driver);
+}
+module_init(hwevent_init);
+
+static void __exit hwevent_exit(void)
+{
+ platform_driver_unregister(&hwevent_driver);
+}
+module_exit(hwevent_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("CoreSight Hardware Event driver");
diff --git a/drivers/coresight/coresight-priv.h b/drivers/coresight/coresight-priv.h
index 258ff09..f208185 100644
--- a/drivers/coresight/coresight-priv.h
+++ b/drivers/coresight/coresight-priv.h
@@ -40,10 +40,13 @@
extern void msm_qdss_csr_enable_bam_to_usb(void);
extern void msm_qdss_csr_disable_bam_to_usb(void);
extern void msm_qdss_csr_disable_flush(void);
+extern int coresight_csr_hwctrl_set(phys_addr_t addr, uint32_t val);
#else
static inline void msm_qdss_csr_enable_bam_to_usb(void) {}
static inline void msm_qdss_csr_disable_bam_to_usb(void) {}
static inline void msm_qdss_csr_disable_flush(void) {}
+static inline int coresight_csr_hwctrl_set(phys_addr_t addr,
+ uint32_t val) { return -ENOSYS; }
#endif
#ifdef CONFIG_CORESIGHT_ETM
extern unsigned int etm_readl_cp14(uint32_t off);
diff --git a/drivers/coresight/coresight-tpiu.c b/drivers/coresight/coresight-tpiu.c
index 7ea71d3..53df0f9 100644
--- a/drivers/coresight/coresight-tpiu.c
+++ b/drivers/coresight/coresight-tpiu.c
@@ -218,6 +218,24 @@
return 0;
}
+static int tpiu_reg_set_optimum_mode(struct regulator *reg,
+ unsigned int reg_hpm)
+{
+ if (regulator_count_voltages(reg) <= 0)
+ return 0;
+
+ return regulator_set_optimum_mode(reg, reg_hpm);
+}
+
+static int tpiu_reg_set_voltage(struct regulator *reg, unsigned int reg_low,
+ unsigned int reg_high)
+{
+ if (regulator_count_voltages(reg) <= 0)
+ return 0;
+
+ return regulator_set_voltage(reg, reg_low, reg_high);
+}
+
static int __tpiu_enable_to_sdc(struct tpiu_drvdata *drvdata)
{
int ret;
@@ -225,11 +243,11 @@
if (!drvdata->reg)
return -EINVAL;
- ret = regulator_set_optimum_mode(drvdata->reg, drvdata->reg_hpm);
+ ret = tpiu_reg_set_optimum_mode(drvdata->reg, drvdata->reg_hpm);
if (ret < 0)
return ret;
- ret = regulator_set_voltage(drvdata->reg, drvdata->reg_low,
- drvdata->reg_high);
+ ret = tpiu_reg_set_voltage(drvdata->reg, drvdata->reg_low,
+ drvdata->reg_high);
if (ret)
goto err0;
ret = regulator_enable(drvdata->reg);
@@ -248,9 +266,9 @@
return 0;
err1:
- regulator_set_voltage(drvdata->reg, 0, drvdata->reg_high);
+ tpiu_reg_set_voltage(drvdata->reg, 0, drvdata->reg_high);
err0:
- regulator_set_optimum_mode(drvdata->reg, 0);
+ tpiu_reg_set_optimum_mode(drvdata->reg, 0);
return ret;
}
@@ -325,8 +343,8 @@
msm_tlmm_misc_reg_write(TLMM_ETM_MODE_REG, 0);
regulator_disable(drvdata->reg);
- regulator_set_optimum_mode(drvdata->reg, 0);
- regulator_set_voltage(drvdata->reg, 0, drvdata->reg_high);
+ tpiu_reg_set_optimum_mode(drvdata->reg, 0);
+ tpiu_reg_set_voltage(drvdata->reg, 0, drvdata->reg_high);
}
static void tpiu_disable(struct coresight_device *csdev)
@@ -530,8 +548,7 @@
prop = of_get_property(node, "qcom,vdd-voltage-level", &len);
if (!prop || (len != (2 * sizeof(__be32)))) {
- of_node_put(reg_node);
- return -EINVAL;
+ dev_err(dev, "sdc voltage levels not specified\n");
} else {
drvdata->reg_low = be32_to_cpup(&prop[0]);
drvdata->reg_high = be32_to_cpup(&prop[1]);
@@ -539,8 +556,7 @@
prop = of_get_property(node, "qcom,vdd-current-level", &len);
if (!prop || (len != (2 * sizeof(__be32)))) {
- of_node_put(reg_node);
- return -EINVAL;
+ dev_err(dev, "sdc current levels not specified\n");
} else {
drvdata->reg_lpm = be32_to_cpup(&prop[0]);
drvdata->reg_hpm = be32_to_cpup(&prop[1]);
@@ -615,6 +631,8 @@
for (i = 0; i < drvdata->seta_gpiocnt; i++)
drvdata->seta_cfgs[i].dir = seta_cfgs[i];
+
+ devm_kfree(dev, seta_cfgs);
} else {
dev_err(dev, "seta gpios not specified\n");
}
@@ -681,6 +699,8 @@
for (i = 0; i < drvdata->setb_gpiocnt; i++)
drvdata->setb_cfgs[i].dir = setb_cfgs[i];
+
+ devm_kfree(dev, setb_cfgs);
} else {
dev_err(dev, "setb gpios not specified\n");
}
diff --git a/drivers/crypto/msm/qce40.c b/drivers/crypto/msm/qce40.c
index 5249917..1dcd3bc 100644
--- a/drivers/crypto/msm/qce40.c
+++ b/drivers/crypto/msm/qce40.c
@@ -412,6 +412,9 @@
/* write seg size */
*((uint32_t *)(pce_dev->ce_dm.buffer.seg_size)) = sreq->size;
+ /* clear status */
+ *((uint32_t *)(pce_dev->ce_dm.buffer.status)) = 0;
+
_ce_setup_hash_cmdrptrlist(pce_dev, sreq);
return 0;
@@ -685,6 +688,9 @@
*((uint32_t *)(buffer->seg_size)) = totallen_in;
+ /* clear status */
+ *((uint32_t *)(pce_dev->ce_dm.buffer.status)) = 0;
+
_ce_setup_cipher_cmdrptrlist(pce_dev, creq);
return 0;
};
@@ -708,13 +714,19 @@
/* check MAC */
if (pce_dev->mode == QCE_MODE_CCM) {
- uint32_t result;
+ int32_t result = 0;
result =
(uint32_t)(*((uint32_t *)pce_dev->ce_dm.buffer.status));
result &= (1 << CRYPTO_MAC_FAILED);
result |= (pce_dev->ce_dm.chan_ce_in_status |
pce_dev->ce_dm.chan_ce_out_status);
+ if (pce_dev->ce_dm.chan_ce_in_status |
+ pce_dev->ce_dm.chan_ce_out_status)
+ result = -ENXIO;
+ else if (result & (1 << CRYPTO_MAC_FAILED))
+ result = -EBADMSG;
+
pce_dev->qce_cb(areq, pce_dev->ce_dm.buffer.auth_result, NULL,
result);
}
@@ -1617,16 +1629,16 @@
pscmd++;
- /* SET SEG SIZE REGISTER and OCB COMMAND LIST */
- pce_dev->ce_dm.cmdlist.set_seg_size_ocb = pscmd;
- pscmd->cmd = CMD_LC | CMD_MODE_SINGLE | CMD_OCB;
+ /* SET SEG SIZE REGISTER LIST */
+ pce_dev->ce_dm.cmdlist.set_seg_size = pscmd;
+ pscmd->cmd = CMD_LC | CMD_MODE_SINGLE;
pscmd->dst = (unsigned) (CRYPTO_SEG_SIZE_REG + pce_dev->phy_iobase);
pscmd->len = CRYPTO_REG_SIZE;
pscmd->src = GET_PHYS_ADDR(pce_dev->ce_dm.buffer.seg_size);
pscmd++;
- /* OCU COMMAND LIST */
+ /* Get status and OCU COMMAND LIST */
pce_dev->ce_dm.cmdlist.get_status_ocu = pscmd;
pscmd->cmd = CMD_LC | CMD_MODE_SINGLE | CMD_OCU;
pscmd->src = (unsigned) (CRYPTO_STATUS_REG + pce_dev->phy_iobase);
@@ -1634,7 +1646,7 @@
pscmd->dst = GET_PHYS_ADDR(pce_dev->ce_dm.buffer.status);
pscmd++;
- /* CLEAR STATUS COMMAND LIST */
+ /* CLEAR STATUS and OCU COMMAND LIST */
pce_dev->ce_dm.cmdlist.clear_status = pscmd;
pscmd->cmd = CMD_LC | CMD_MODE_SINGLE | CMD_OCU;
pscmd->dst = (unsigned) (CRYPTO_STATUS_REG + pce_dev->phy_iobase);
@@ -1642,6 +1654,14 @@
pscmd->src = GET_PHYS_ADDR(pce_dev->ce_dm.buffer.status);
pscmd++;
+ /* CLEAR STATUS and OCB COMMAND LIST */
+ pce_dev->ce_dm.cmdlist.clear_status_ocb = pscmd;
+ pscmd->cmd = CMD_LC | CMD_MODE_SINGLE | CMD_OCB;
+ pscmd->dst = (unsigned) (CRYPTO_STATUS_REG + pce_dev->phy_iobase);
+ pscmd->len = CRYPTO_REG_SIZE;
+ pscmd->src = GET_PHYS_ADDR(pce_dev->ce_dm.buffer.status);
+ pscmd++;
+
/* SET GO_PROC REGISTERS COMMAND LIST */
pce_dev->ce_dm.cmdlist.set_go_proc = pscmd;
pscmd->cmd = CMD_LC | CMD_MODE_SINGLE;
@@ -1715,7 +1735,8 @@
cmd_ptr_vaddr = (uint32_t *) ALIGN(((unsigned int) cmd_ptr_vaddr), 16);
cmdptrlist->cipher_aes_128_cbc_ctr = QCE_SET_CMD_PTR(cmd_ptr_vaddr);
- *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size_ocb);
+ *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->clear_status_ocb);
+ *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_cfg);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_aes_128_key);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_aes_iv);
@@ -1727,7 +1748,8 @@
cmd_ptr_vaddr = (uint32_t *) ALIGN(((unsigned int) cmd_ptr_vaddr), 16);
cmdptrlist->cipher_aes_256_cbc_ctr = QCE_SET_CMD_PTR(cmd_ptr_vaddr);
- *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size_ocb);
+ *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->clear_status_ocb);
+ *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_cfg);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_aes_256_key);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_aes_iv);
@@ -1739,7 +1761,8 @@
cmd_ptr_vaddr = (uint32_t *) ALIGN(((unsigned int) cmd_ptr_vaddr), 16);
cmdptrlist->cipher_aes_128_ecb = QCE_SET_CMD_PTR(cmd_ptr_vaddr);
- *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size_ocb);
+ *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->clear_status_ocb);
+ *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_cfg);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_aes_128_key);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->reset_auth_cfg);
@@ -1750,7 +1773,8 @@
cmd_ptr_vaddr = (uint32_t *)ALIGN(((unsigned int) cmd_ptr_vaddr), 16);
cmdptrlist->cipher_aes_256_ecb = QCE_SET_CMD_PTR(cmd_ptr_vaddr);
- *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size_ocb);
+ *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->clear_status_ocb);
+ *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_cfg);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_aes_256_key);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->reset_auth_cfg);
@@ -1761,7 +1785,8 @@
cmd_ptr_vaddr = (uint32_t *)ALIGN(((unsigned int) cmd_ptr_vaddr), 16);
cmdptrlist->cipher_aes_128_xts = QCE_SET_CMD_PTR(cmd_ptr_vaddr);
- *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size_ocb);
+ *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->clear_status_ocb);
+ *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_cfg);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_aes_128_key);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_aes_128_xts_key);
@@ -1775,7 +1800,8 @@
cmd_ptr_vaddr = (uint32_t *) ALIGN(((unsigned int) cmd_ptr_vaddr), 16);
cmdptrlist->cipher_aes_256_xts = QCE_SET_CMD_PTR(cmd_ptr_vaddr);
- *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size_ocb);
+ *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->clear_status_ocb);
+ *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_cfg);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_aes_256_key);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_aes_256_xts_key);
@@ -1789,7 +1815,8 @@
cmd_ptr_vaddr = (uint32_t *)ALIGN(((unsigned int) cmd_ptr_vaddr), 16);
cmdptrlist->cipher_des_cbc = QCE_SET_CMD_PTR(cmd_ptr_vaddr);
- *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size_ocb);
+ *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->clear_status_ocb);
+ *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_cfg);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_des_key);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_des_iv);
@@ -1800,7 +1827,8 @@
cmd_ptr_vaddr = (uint32_t *)ALIGN(((unsigned int) cmd_ptr_vaddr), 16);
cmdptrlist->cipher_des_ecb = QCE_SET_CMD_PTR(cmd_ptr_vaddr);
- *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size_ocb);
+ *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->clear_status_ocb);
+ *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_cfg);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_des_key);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->reset_auth_cfg);
@@ -1810,7 +1838,8 @@
cmd_ptr_vaddr = (uint32_t *) ALIGN(((unsigned int) cmd_ptr_vaddr), 16);
cmdptrlist->cipher_3des_cbc = QCE_SET_CMD_PTR(cmd_ptr_vaddr);
- *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size_ocb);
+ *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->clear_status_ocb);
+ *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_cfg);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_3des_key);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_des_iv);
@@ -1821,7 +1850,8 @@
cmd_ptr_vaddr = (uint32_t *) ALIGN(((unsigned int) cmd_ptr_vaddr), 16);
cmdptrlist->cipher_3des_ecb = QCE_SET_CMD_PTR(cmd_ptr_vaddr);
- *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size_ocb);
+ *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->clear_status_ocb);
+ *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_cfg);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_cipher_3des_key);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->reset_auth_cfg);
@@ -1861,7 +1891,8 @@
cmd_ptr_vaddr = (uint32_t *) ALIGN(((unsigned int) cmd_ptr_vaddr), 16);
cmdptrlist->auth_sha1 = QCE_SET_CMD_PTR(cmd_ptr_vaddr);
- *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size_ocb);
+ *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->clear_status_ocb);
+ *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->reset_cipher_cfg);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_auth_cfg);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_auth_iv_20);
@@ -1879,7 +1910,8 @@
cmd_ptr_vaddr = (uint32_t *) ALIGN(((unsigned int) cmd_ptr_vaddr), 16);
cmdptrlist->auth_sha256 = QCE_SET_CMD_PTR(cmd_ptr_vaddr);
- *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size_ocb);
+ *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->clear_status_ocb);
+ *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->reset_cipher_cfg);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_auth_cfg);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_auth_iv_32);
@@ -1897,7 +1929,8 @@
cmd_ptr_vaddr = (uint32_t *) ALIGN(((unsigned int) cmd_ptr_vaddr), 16);
cmdptrlist->auth_sha1_hmac = QCE_SET_CMD_PTR(cmd_ptr_vaddr);
- *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size_ocb);
+ *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->clear_status_ocb);
+ *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->reset_cipher_cfg);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_auth_key_512);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_auth_cfg);
@@ -1916,7 +1949,8 @@
cmd_ptr_vaddr = (uint32_t *) ALIGN(((unsigned int) cmd_ptr_vaddr), 16);
cmdptrlist->auth_sha256_hmac = QCE_SET_CMD_PTR(cmd_ptr_vaddr);
- *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size_ocb);
+ *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->clear_status_ocb);
+ *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->reset_cipher_cfg);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_auth_key_512);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_auth_cfg);
@@ -1935,7 +1969,8 @@
cmd_ptr_vaddr = (uint32_t *) ALIGN(((unsigned int) cmd_ptr_vaddr), 16);
cmdptrlist->auth_aes_128_cmac = QCE_SET_CMD_PTR(cmd_ptr_vaddr);
- *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size_ocb);
+ *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->clear_status_ocb);
+ *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->reset_cipher_cfg);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->reset_auth_iv);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->reset_auth_key);
@@ -1953,7 +1988,8 @@
cmd_ptr_vaddr = (uint32_t *) ALIGN(((unsigned int) cmd_ptr_vaddr), 16);
cmdptrlist->auth_aes_256_cmac = QCE_SET_CMD_PTR(cmd_ptr_vaddr);
- *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size_ocb);
+ *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->clear_status_ocb);
+ *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->reset_cipher_cfg);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->reset_auth_iv);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->reset_auth_key);
@@ -1988,7 +2024,8 @@
cmd_ptr_vaddr = (uint32_t *) ALIGN(((unsigned int) cmd_ptr_vaddr), 16);
cmdptrlist->aead_aes_128_ccm = QCE_SET_CMD_PTR(cmd_ptr_vaddr);
- *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size_ocb);
+ *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->clear_status_ocb);
+ *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->reset_auth_iv);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->reset_auth_key);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->reset_auth_byte_count);
@@ -2005,7 +2042,8 @@
cmd_ptr_vaddr = (uint32_t *) ALIGN(((unsigned int) cmd_ptr_vaddr), 16);
cmdptrlist->aead_aes_256_ccm = QCE_SET_CMD_PTR(cmd_ptr_vaddr);
- *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size_ocb);
+ *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->clear_status_ocb);
+ *cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->set_seg_size);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->reset_auth_iv);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->reset_auth_key);
*cmd_ptr_vaddr++ = QCE_SET_CMD_PTR(cmdlist->reset_auth_byte_count);
diff --git a/drivers/crypto/msm/qce40.h b/drivers/crypto/msm/qce40.h
index 0d19106..179250c 100644
--- a/drivers/crypto/msm/qce40.h
+++ b/drivers/crypto/msm/qce40.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013, 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
@@ -118,6 +118,7 @@
struct ce_cmdlists {
dmov_s *get_hw_version;
dmov_s *clear_status;
+ dmov_s *clear_status_ocb;
dmov_s *get_status_ocu;
dmov_s *set_cipher_cfg;
@@ -162,7 +163,7 @@
dmov_s *reset_auth_cfg;
dmov_s *reset_auth_byte_count;
- dmov_s *set_seg_size_ocb;
+ dmov_s *set_seg_size;
dmov_s *get_status_wait;
dmov_s *set_go_proc;
diff --git a/drivers/crypto/msm/qcrypto.c b/drivers/crypto/msm/qcrypto.c
index d1cb933..a4bb2f1 100644
--- a/drivers/crypto/msm/qcrypto.c
+++ b/drivers/crypto/msm/qcrypto.c
@@ -56,6 +56,7 @@
u32 aead_ccm_aes_dec;
u32 aead_op_success;
u32 aead_op_fail;
+ u32 aead_bad_msg;
u32 ablk_cipher_aes_enc;
u32 ablk_cipher_aes_dec;
u32 ablk_cipher_des_enc;
@@ -684,6 +685,9 @@
" AEAD operation fail : %d\n",
pstat->aead_op_fail);
len += snprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
+ " AEAD bad message : %d\n",
+ pstat->aead_bad_msg);
+ len += snprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
" SHA1 digest : %d\n",
pstat->sha1_digest);
len += snprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
@@ -1036,12 +1040,6 @@
kzfree(rctx->assoc);
areq->assoc = rctx->assoc_sg;
areq->assoclen = rctx->assoclen;
- if (ret) {
- if (ret == 0x2000000)
- ret = -EBADMSG;
- else
- ret = -ENXIO;
- }
} else {
if (ret == 0) {
if (rctx->dir == QCE_ENCRYPT) {
@@ -1070,11 +1068,15 @@
memcpy(ctx->iv, iv, crypto_aead_ivsize(aead));
}
- if (ret)
+ if (ret == (-EBADMSG))
+ pstat->aead_bad_msg++;
+ else if (ret)
pstat->aead_op_fail++;
else
pstat->aead_op_success++;
+ cp->res = ret;
+
if (cp->platform_support.ce_shared)
schedule_work(&cp->unlock_ce_ws);
tasklet_schedule(&cp->done_tasklet);
diff --git a/drivers/crypto/msm/qcryptohw_50.h b/drivers/crypto/msm/qcryptohw_50.h
index 2210dc8..1be2702 100644
--- a/drivers/crypto/msm/qcryptohw_50.h
+++ b/drivers/crypto/msm/qcryptohw_50.h
@@ -379,7 +379,7 @@
#define CRYPTO_FIRST 17
#define CRYPTO_LAST 16
-#define CRYPTO_AUTH_POS 15 /* bit 15 .. 14*/
+#define CRYPTO_AUTH_POS 14 /* bit 15 .. 14*/
#define CRYPTO_AUTH_POS_MASK (0x3 << CRYPTO_AUTH_POS)
#define CRYPTO_AUTH_POS_BEFORE 0
#define CRYPTO_AUTH_POS_AFTER 1
diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c
index e6c345b..a4f60f9 100644
--- a/drivers/gpu/msm/adreno.c
+++ b/drivers/gpu/msm/adreno.c
@@ -101,13 +101,6 @@
.iomemname = KGSL_3D0_REG_MEMORY,
.shadermemname = KGSL_3D0_SHADER_MEMORY,
.ftbl = &adreno_functable,
-#ifdef CONFIG_HAS_EARLYSUSPEND
- .display_off = {
- .level = EARLY_SUSPEND_LEVEL_STOP_DRAWING,
- .suspend = kgsl_early_suspend_driver,
- .resume = kgsl_late_resume_driver,
- },
-#endif
},
.gmem_base = 0,
.gmem_size = SZ_256K,
@@ -813,7 +806,8 @@
struct adreno_ringbuffer *rb = &adreno_dev->ringbuffer;
if (!adreno_dev->drawctxt_active ||
- KGSL_STATE_ACTIVE != device->state) {
+ KGSL_STATE_ACTIVE != device->state ||
+ !device->active_cnt) {
kgsl_mmu_device_setstate(&device->mmu, flags);
return;
}
@@ -1719,6 +1713,7 @@
{
int status = -EINVAL;
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+ unsigned int state = device->state;
kgsl_cffdump_open(device);
@@ -1780,8 +1775,11 @@
kgsl_mmu_stop(&device->mmu);
error_clk_off:
- if (KGSL_STATE_DUMP_AND_FT != device->state)
+ if (KGSL_STATE_DUMP_AND_FT != device->state) {
kgsl_pwrctrl_disable(device);
+ /* set the state back to original state */
+ kgsl_pwrctrl_set_state(device, state);
+ }
return status;
}
@@ -2773,9 +2771,6 @@
unsigned long wait;
unsigned long timeout = jiffies + msecs_to_jiffies(ADRENO_IDLE_TIMEOUT);
- if (!(rb->flags & KGSL_FLAGS_STARTED))
- return 0;
-
/*
* The first time into the loop, wait for 100 msecs and kick wptr again
* to ensure that the hardware has updated correctly. After that, kick
diff --git a/drivers/gpu/msm/adreno_snapshot.c b/drivers/gpu/msm/adreno_snapshot.c
index 967e4ab..144c3d6 100644
--- a/drivers/gpu/msm/adreno_snapshot.c
+++ b/drivers/gpu/msm/adreno_snapshot.c
@@ -822,7 +822,8 @@
int remain, void *priv)
{
struct kgsl_snapshot_replay_mem_list *header = snapshot;
- struct kgsl_process_private *private;
+ struct kgsl_process_private *private = NULL;
+ struct kgsl_process_private *tmp_private;
unsigned int ptbase;
struct rb_node *node;
struct kgsl_mem_entry *entry = NULL;
@@ -831,10 +832,12 @@
ptbase = kgsl_mmu_get_current_ptbase(&device->mmu);
mutex_lock(&kgsl_driver.process_mutex);
- list_for_each_entry(private, &kgsl_driver.process_list, list) {
- if (kgsl_mmu_pt_equal(&device->mmu, private->pagetable,
- ptbase))
+ list_for_each_entry(tmp_private, &kgsl_driver.process_list, list) {
+ if (kgsl_mmu_pt_equal(&device->mmu, tmp_private->pagetable,
+ ptbase)) {
+ private = tmp_private;
break;
+ }
}
mutex_unlock(&kgsl_driver.process_mutex);
if (!private) {
diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c
index 7cd9943..992f88d 100644
--- a/drivers/gpu/msm/kgsl.c
+++ b/drivers/gpu/msm/kgsl.c
@@ -308,16 +308,28 @@
static void kgsl_mem_entry_detach_process(struct kgsl_mem_entry *entry)
{
+ bool had_gpuaddr = false;
+
if (entry == NULL)
return;
+ /*
+ * Unmap the entry first so that there isn't a period of
+ * time where kgsl doesn't know about the address range
+ * but it is still present in the pagetable. Unmapping will
+ * clear the gpuaddr field, so remember if we had a mapping,
+ * and an rbtree entry for later.
+ */
+ had_gpuaddr = entry->memdesc.gpuaddr != 0;
+ kgsl_mmu_unmap(entry->memdesc.pagetable, &entry->memdesc);
+
spin_lock(&entry->priv->mem_lock);
if (entry->id != 0)
idr_remove(&entry->priv->mem_idr, entry->id);
entry->id = 0;
- if (entry->memdesc.gpuaddr != 0)
+ if (had_gpuaddr)
rb_erase(&entry->node, &entry->priv->mem_rb);
spin_unlock(&entry->priv->mem_lock);
@@ -325,7 +337,6 @@
entry->priv->stats[entry->memtype].cur -= entry->memdesc.size;
entry->priv = NULL;
- kgsl_mmu_unmap(entry->memdesc.pagetable, &entry->memdesc);
kgsl_mem_entry_put(entry);
}
@@ -614,20 +625,6 @@
};
EXPORT_SYMBOL(kgsl_pm_ops);
-void kgsl_early_suspend_driver(struct early_suspend *h)
-{
- struct kgsl_device *device = container_of(h,
- struct kgsl_device, display_off);
- KGSL_PWR_WARN(device, "early suspend start\n");
- mutex_lock(&device->mutex);
- device->pwrctrl.restore_slumber = true;
- kgsl_pwrctrl_request_state(device, KGSL_STATE_SLUMBER);
- kgsl_pwrctrl_sleep(device);
- mutex_unlock(&device->mutex);
- KGSL_PWR_WARN(device, "early suspend end\n");
-}
-EXPORT_SYMBOL(kgsl_early_suspend_driver);
-
int kgsl_suspend_driver(struct platform_device *pdev,
pm_message_t state)
{
@@ -643,101 +640,44 @@
}
EXPORT_SYMBOL(kgsl_resume_driver);
-void kgsl_late_resume_driver(struct early_suspend *h)
+/**
+ * kgsl_destroy_process_private() - Cleanup function to free process private
+ * @kref: - Pointer to object being destroyed's kref struct
+ * Free struct object and all other resources attached to it.
+ * Since the function can be used when not all resources inside process
+ * private have been allocated, there is a check to (before each resource
+ * cleanup) see if the struct member being cleaned is in fact allocated or not.
+ * If the value is not NULL, resource is freed.
+ */
+static void kgsl_destroy_process_private(struct kref *kref)
{
- struct kgsl_device *device = container_of(h,
- struct kgsl_device, display_off);
- KGSL_PWR_WARN(device, "late resume start\n");
- mutex_lock(&device->mutex);
- device->pwrctrl.restore_slumber = false;
- if (device->pwrscale.policy == NULL)
- kgsl_pwrctrl_pwrlevel_change(device, KGSL_PWRLEVEL_TURBO);
- if (kgsl_pwrctrl_wake(device) != 0)
- return;
- /*
- * We don't have a way to go directly from
- * a deeper sleep state to NAP, which is
- * the desired state here.
- */
- kgsl_pwrctrl_request_state(device, KGSL_STATE_NAP);
- kgsl_pwrctrl_sleep(device);
- mutex_unlock(&device->mutex);
- KGSL_PWR_WARN(device, "late resume end\n");
-}
-EXPORT_SYMBOL(kgsl_late_resume_driver);
-/* file operations */
-static struct kgsl_process_private *
-kgsl_get_process_private(struct kgsl_device_private *cur_dev_priv)
-{
- struct kgsl_process_private *private;
-
- mutex_lock(&kgsl_driver.process_mutex);
- list_for_each_entry(private, &kgsl_driver.process_list, list) {
- if (private->pid == task_tgid_nr(current)) {
- private->refcnt++;
- goto out;
- }
- }
-
- /* no existing process private found for this dev_priv, create one */
- private = kzalloc(sizeof(struct kgsl_process_private), GFP_KERNEL);
- if (private == NULL) {
- KGSL_DRV_ERR(cur_dev_priv->device, "kzalloc(%d) failed\n",
- sizeof(struct kgsl_process_private));
- goto out;
- }
-
- spin_lock_init(&private->mem_lock);
- private->refcnt = 1;
- private->pid = task_tgid_nr(current);
- private->mem_rb = RB_ROOT;
-
- idr_init(&private->mem_idr);
-
- if (kgsl_mmu_enabled())
- {
- unsigned long pt_name;
- struct kgsl_mmu *mmu = &cur_dev_priv->device->mmu;
-
- pt_name = task_tgid_nr(current);
- private->pagetable = kgsl_mmu_getpagetable(mmu, pt_name);
- if (private->pagetable == NULL) {
- kfree(private);
- private = NULL;
- goto out;
- }
- }
-
- list_add(&private->list, &kgsl_driver.process_list);
-
- kgsl_process_init_sysfs(private);
- kgsl_process_init_debugfs(private);
-
-out:
- mutex_unlock(&kgsl_driver.process_mutex);
- return private;
-}
-
-static void
-kgsl_put_process_private(struct kgsl_device *device,
- struct kgsl_process_private *private)
-{
struct kgsl_mem_entry *entry = NULL;
int next = 0;
- if (!private)
+
+ struct kgsl_process_private *private = container_of(kref,
+ struct kgsl_process_private, refcount);
+
+ /*
+ * Remove this process from global process list
+ * We do not acquire a lock first as it is expected that
+ * kgsl_destroy_process_private() is only going to be called
+ * through kref_put() which is only called after acquiring
+ * the lock.
+ */
+ if (!private) {
+ KGSL_CORE_ERR("Cannot destroy null process private\n");
+ mutex_unlock(&kgsl_driver.process_mutex);
return;
-
- mutex_lock(&kgsl_driver.process_mutex);
-
- if (--private->refcnt)
- goto unlock;
-
- kgsl_process_uninit_sysfs(private);
- debugfs_remove_recursive(private->debug_root);
-
+ }
list_del(&private->list);
+ mutex_unlock(&kgsl_driver.process_mutex);
+
+ if (private->kobj.parent)
+ kgsl_process_uninit_sysfs(private);
+ if (private->debug_root)
+ debugfs_remove_recursive(private->debug_root);
while (1) {
rcu_read_lock();
@@ -755,9 +695,110 @@
}
kgsl_mmu_putpagetable(private->pagetable);
idr_destroy(&private->mem_idr);
+
kfree(private);
-unlock:
+ return;
+}
+
+static void
+kgsl_put_process_private(struct kgsl_device *device,
+ struct kgsl_process_private *private)
+{
+ mutex_lock(&kgsl_driver.process_mutex);
+
+ /*
+ * kref_put() returns 1 when the refcnt has reached 0 and the destroy
+ * function is called. Mutex is released in the destroy function if
+ * its called, so only release mutex if kref_put() return 0
+ */
+ if (!kref_put(&private->refcount, kgsl_destroy_process_private))
+ mutex_unlock(&kgsl_driver.process_mutex);
+ return;
+}
+
+/**
+ * find_process_private() - Helper function to search for process private
+ * @cur_dev_priv: Pointer to device private structure which contains pointers
+ * to device and process_private structs.
+ * Returns: Pointer to the found/newly created private struct
+ */
+static struct kgsl_process_private *
+kgsl_find_process_private(struct kgsl_device_private *cur_dev_priv)
+{
+ struct kgsl_process_private *private;
+
+ /* Search in the process list */
+ mutex_lock(&kgsl_driver.process_mutex);
+ list_for_each_entry(private, &kgsl_driver.process_list, list) {
+ if (private->pid == task_tgid_nr(current)) {
+ kref_get(&private->refcount);
+ goto done;
+ }
+ }
+
+ /* no existing process private found for this dev_priv, create one */
+ private = kzalloc(sizeof(struct kgsl_process_private), GFP_KERNEL);
+ if (private == NULL) {
+ KGSL_DRV_ERR(cur_dev_priv->device, "kzalloc(%d) failed\n",
+ sizeof(struct kgsl_process_private));
+ goto done;
+ }
+
+ kref_init(&private->refcount);
+
+ private->pid = task_tgid_nr(current);
+ spin_lock_init(&private->mem_lock);
+ mutex_init(&private->process_private_mutex);
+ /* Add the newly created process struct obj to the process list */
+ list_add(&private->list, &kgsl_driver.process_list);
+done:
mutex_unlock(&kgsl_driver.process_mutex);
+ return private;
+}
+
+/**
+ * kgsl_get_process_private() - Used to find the process private structure
+ * @cur_dev_priv: Current device pointer
+ * Finds or creates a new porcess private structire and initializes its members
+ * Returns: Pointer to the private process struct obj found/created or
+ * NULL if pagetable creation for this process private obj failed.
+ */
+static struct kgsl_process_private *
+kgsl_get_process_private(struct kgsl_device_private *cur_dev_priv)
+{
+ struct kgsl_process_private *private;
+
+ private = kgsl_find_process_private(cur_dev_priv);
+
+ mutex_lock(&private->process_private_mutex);
+
+ if (!private->mem_rb.rb_node) {
+ private->mem_rb = RB_ROOT;
+ idr_init(&private->mem_idr);
+ }
+
+ if ((!private->pagetable) && kgsl_mmu_enabled()) {
+ unsigned long pt_name;
+ struct kgsl_mmu *mmu = &cur_dev_priv->device->mmu;
+
+ pt_name = task_tgid_nr(current);
+ private->pagetable = kgsl_mmu_getpagetable(mmu, pt_name);
+ if (private->pagetable == NULL) {
+ mutex_unlock(&private->process_private_mutex);
+ kgsl_put_process_private(cur_dev_priv->device,
+ private);
+ return NULL;
+ }
+ }
+
+ if (!private->kobj.parent)
+ kgsl_process_init_sysfs(private);
+ if (!private->debug_root)
+ kgsl_process_init_debugfs(private);
+
+ mutex_unlock(&private->process_private_mutex);
+
+ return private;
}
static int kgsl_release(struct inode *inodep, struct file *filep)
@@ -868,7 +909,12 @@
result = device->ftbl->start(device);
if (result)
goto err_freedevpriv;
-
+ /*
+ * Make sure the gates are open, so they don't block until
+ * we start suspend or FT.
+ */
+ complete_all(&device->ft_gate);
+ complete_all(&device->hwaccess_gate);
kgsl_pwrctrl_set_state(device, KGSL_STATE_ACTIVE);
kgsl_active_count_put(device);
}
@@ -1346,6 +1392,12 @@
"invalid gpuaddr %08x\n", gpuaddr);
return -EINVAL;
}
+ if (entry->memdesc.priv & KGSL_MEMDESC_FREE_PENDING) {
+ kgsl_mem_entry_put(entry);
+ return -EBUSY;
+ }
+ entry->memdesc.priv |= KGSL_MEMDESC_FREE_PENDING;
+
trace_kgsl_mem_timestamp_queue(device, entry, context_id,
kgsl_readtimestamp(device, context,
KGSL_TIMESTAMP_RETIRED),
@@ -1445,6 +1497,11 @@
return -EINVAL;
}
+ if (entry->memdesc.priv & KGSL_MEMDESC_FREE_PENDING) {
+ kgsl_mem_entry_put(entry);
+ return -EBUSY;
+ }
+
trace_kgsl_mem_free(entry);
kgsl_memfree_hist_set_event(entry->priv->pid,
@@ -1470,6 +1527,12 @@
KGSL_MEM_INFO(dev_priv->device, "invalid id %d\n", param->id);
return -EINVAL;
}
+
+ if (entry->memdesc.priv & KGSL_MEMDESC_FREE_PENDING) {
+ kgsl_mem_entry_put(entry);
+ return -EBUSY;
+ }
+
trace_kgsl_mem_free(entry);
kgsl_memfree_hist_set_event(entry->priv->pid,
diff --git a/drivers/gpu/msm/kgsl.h b/drivers/gpu/msm/kgsl.h
index abe9100..c7cbaf8 100644
--- a/drivers/gpu/msm/kgsl.h
+++ b/drivers/gpu/msm/kgsl.h
@@ -46,7 +46,11 @@
#define KGSL_PAGETABLE_ENTRY_SIZE 4
/* Pagetable Virtual Address base */
+#ifndef CONFIG_MSM_KGSL_CFF_DUMP
#define KGSL_PAGETABLE_BASE 0x10000000
+#else
+#define KGSL_PAGETABLE_BASE 0xE0000000
+#endif
/* Extra accounting entries needed in the pagetable */
#define KGSL_PT_EXTRA_ENTRIES 16
@@ -151,6 +155,8 @@
#define KGSL_MEMDESC_GLOBAL BIT(1)
/* The memdesc is frozen during a snapshot */
#define KGSL_MEMDESC_FROZEN BIT(2)
+/* The memdesc is scheduled to be freed on a timestamp */
+#define KGSL_MEMDESC_FREE_PENDING BIT(3)
/* shared memory allocation */
struct kgsl_memdesc {
@@ -220,11 +226,8 @@
extern const struct dev_pm_ops kgsl_pm_ops;
-struct early_suspend;
int kgsl_suspend_driver(struct platform_device *pdev, pm_message_t state);
int kgsl_resume_driver(struct platform_device *pdev);
-void kgsl_early_suspend_driver(struct early_suspend *h);
-void kgsl_late_resume_driver(struct early_suspend *h);
void kgsl_trace_regwrite(struct kgsl_device *device, unsigned int offset,
unsigned int value);
@@ -251,6 +254,10 @@
static inline int kgsl_gpuaddr_in_memdesc(const struct kgsl_memdesc *memdesc,
unsigned int gpuaddr, unsigned int size)
{
+ /* set a minimum size to search for */
+ if (!size)
+ size = 1;
+
/* don't overflow */
if ((gpuaddr + size) < gpuaddr)
return 0;
diff --git a/drivers/gpu/msm/kgsl_cffdump.c b/drivers/gpu/msm/kgsl_cffdump.c
index ef2a19a..c3bdf80 100644
--- a/drivers/gpu/msm/kgsl_cffdump.c
+++ b/drivers/gpu/msm/kgsl_cffdump.c
@@ -360,6 +360,8 @@
void kgsl_cffdump_open(struct kgsl_device *device)
{
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+ if (!kgsl_cff_dump_enable)
+ return;
if (KGSL_MMU_TYPE_IOMMU == kgsl_mmu_get_mmutype()) {
kgsl_cffdump_memory_base(device->id,
@@ -379,24 +381,33 @@
void kgsl_cffdump_memory_base(enum kgsl_deviceid device_id, unsigned int base,
unsigned int range, unsigned gmemsize)
{
+ if (!kgsl_cff_dump_enable)
+ return;
cffdump_printline(device_id, CFF_OP_MEMORY_BASE, base,
range, gmemsize, 0, 0);
}
void kgsl_cffdump_hang(enum kgsl_deviceid device_id)
{
+ if (!kgsl_cff_dump_enable)
+ return;
cffdump_printline(device_id, CFF_OP_HANG, 0, 0, 0, 0, 0);
}
void kgsl_cffdump_close(enum kgsl_deviceid device_id)
{
+ if (!kgsl_cff_dump_enable)
+ return;
cffdump_printline(device_id, CFF_OP_EOF, 0, 0, 0, 0, 0);
}
+
void kgsl_cffdump_user_event(unsigned int cff_opcode, unsigned int op1,
unsigned int op2, unsigned int op3,
unsigned int op4, unsigned int op5)
{
+ if (!kgsl_cff_dump_enable)
+ return;
cffdump_printline(-1, cff_opcode, op1, op2, op3, op4, op5);
}
diff --git a/drivers/gpu/msm/kgsl_device.h b/drivers/gpu/msm/kgsl_device.h
index ac82820..e80721a 100644
--- a/drivers/gpu/msm/kgsl_device.h
+++ b/drivers/gpu/msm/kgsl_device.h
@@ -15,7 +15,6 @@
#include <linux/idr.h>
#include <linux/pm_qos.h>
-#include <linux/earlysuspend.h>
#include "kgsl.h"
#include "kgsl_mmu.h"
@@ -190,7 +189,6 @@
struct completion ft_gate;
struct dentry *d_debugfs;
struct idr context_idr;
- struct early_suspend display_off;
void *snapshot; /* Pointer to the snapshot memory region */
int snapshot_maxsize; /* Max size of the snapshot region */
@@ -280,6 +278,12 @@
unsigned int refcnt;
pid_t pid;
spinlock_t mem_lock;
+
+ /* General refcount for process private struct obj */
+ struct kref refcount;
+ /* Mutex to synchronize access to each process_private struct obj */
+ struct mutex process_private_mutex;
+
struct rb_root mem_rb;
struct idr mem_idr;
struct kgsl_pagetable *pagetable;
diff --git a/drivers/gpu/msm/kgsl_events.c b/drivers/gpu/msm/kgsl_events.c
index d872783..3b01e54 100644
--- a/drivers/gpu/msm/kgsl_events.c
+++ b/drivers/gpu/msm/kgsl_events.c
@@ -143,6 +143,15 @@
cur = kgsl_readtimestamp(device, context, KGSL_TIMESTAMP_RETIRED);
id = context->id;
+ /*
+ * Increment the refcount to avoid freeing the context while
+ * cancelling its events
+ */
+ kgsl_context_get(context);
+
+ /* Remove ourselves from the master pending list */
+ list_del_init(&context->events_list);
+
list_for_each_entry_safe(event, event_tmp, &context->events, list) {
/*
* "cancel" the events by calling their callback.
@@ -165,9 +174,7 @@
kgsl_active_count_put(device);
}
-
- /* Remove ourselves from the master pending list */
- list_del_init(&context->events_list);
+ kgsl_context_put(context);
}
/**
@@ -313,12 +320,18 @@
events_list) {
/*
+ * Increment the refcount to make sure that the list_del_init
+ * is called with a valid context's list
+ */
+ kgsl_context_get(context);
+ /*
* If kgsl_timestamp_expired_context returns 0 then it no longer
* has any pending events and can be removed from the list
*/
if (kgsl_process_context_events(device, context) == 0)
list_del_init(&context->events_list);
+ kgsl_context_put(context);
}
mutex_unlock(&device->mutex);
diff --git a/drivers/gpu/msm/kgsl_iommu.c b/drivers/gpu/msm/kgsl_iommu.c
index 3057723..513fb90 100644
--- a/drivers/gpu/msm/kgsl_iommu.c
+++ b/drivers/gpu/msm/kgsl_iommu.c
@@ -1345,13 +1345,27 @@
* we're better off with extra room.
*/
if (mmu->pt_per_process) {
+#ifndef CONFIG_MSM_KGSL_CFF_DUMP
mmu->pt_base = PAGE_OFFSET;
mmu->pt_size = KGSL_IOMMU_GLOBAL_MEM_BASE
- kgsl_mmu_get_base_addr(mmu) - SZ_1M;
mmu->use_cpu_map = true;
+#else
+ mmu->pt_base = KGSL_PAGETABLE_BASE;
+ mmu->pt_size = KGSL_IOMMU_GLOBAL_MEM_BASE +
+ KGSL_IOMMU_GLOBAL_MEM_SIZE -
+ KGSL_PAGETABLE_BASE;
+ mmu->use_cpu_map = false;
+#endif
} else {
mmu->pt_base = KGSL_PAGETABLE_BASE;
+#ifndef CONFIG_MSM_KGSL_CFF_DUMP
mmu->pt_size = SZ_2G;
+#else
+ mmu->pt_size = KGSL_IOMMU_GLOBAL_MEM_BASE +
+ KGSL_IOMMU_GLOBAL_MEM_SIZE -
+ KGSL_PAGETABLE_BASE;
+#endif
mmu->use_cpu_map = false;
}
diff --git a/drivers/gpu/msm/kgsl_mmu.h b/drivers/gpu/msm/kgsl_mmu.h
index ef5b0f4..02cde94 100644
--- a/drivers/gpu/msm/kgsl_mmu.h
+++ b/drivers/gpu/msm/kgsl_mmu.h
@@ -410,7 +410,7 @@
*/
static inline int kgsl_mmu_use_cpu_map(struct kgsl_mmu *mmu)
{
- return mmu->pt_per_process;
+ return mmu->use_cpu_map;
}
/*
diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c
index b124257..e071650 100644
--- a/drivers/gpu/msm/kgsl_pwrctrl.c
+++ b/drivers/gpu/msm/kgsl_pwrctrl.c
@@ -1021,7 +1021,6 @@
pwr->pm_qos_latency = 501;
pm_runtime_enable(device->parentdev);
- register_early_suspend(&device->display_off);
return result;
clk_err:
@@ -1041,7 +1040,6 @@
KGSL_PWR_INFO(device, "close device %d\n", device->id);
pm_runtime_disable(device->parentdev);
- unregister_early_suspend(&device->display_off);
clk_put(pwr->ebi1_clk);
@@ -1151,8 +1149,7 @@
KGSL_PWR_INFO(device, "idle timer expired device %d\n", device->id);
if (device->requested_state != KGSL_STATE_SUSPEND) {
- if (device->pwrctrl.restore_slumber ||
- device->pwrctrl.strtstp_sleepwake)
+ if (device->pwrctrl.strtstp_sleepwake)
kgsl_pwrctrl_request_state(device, KGSL_STATE_SLUMBER);
else
kgsl_pwrctrl_request_state(device, KGSL_STATE_SLEEP);
@@ -1448,16 +1445,11 @@
BUG_ON(!mutex_is_locked(&device->mutex));
if (device->active_cnt == 0) {
- if (device->requested_state == KGSL_STATE_SUSPEND ||
- device->state == KGSL_STATE_SUSPEND) {
- mutex_unlock(&device->mutex);
- wait_for_completion(&device->hwaccess_gate);
- mutex_lock(&device->mutex);
- } else if (device->state == KGSL_STATE_DUMP_AND_FT) {
- mutex_unlock(&device->mutex);
- wait_for_completion(&device->ft_gate);
- mutex_lock(&device->mutex);
- }
+ mutex_unlock(&device->mutex);
+ wait_for_completion(&device->hwaccess_gate);
+ wait_for_completion(&device->ft_gate);
+ mutex_lock(&device->mutex);
+
ret = kgsl_pwrctrl_wake(device);
}
if (ret == 0)
diff --git a/drivers/gpu/msm/kgsl_pwrctrl.h b/drivers/gpu/msm/kgsl_pwrctrl.h
index b3e8702..2b986c8 100644
--- a/drivers/gpu/msm/kgsl_pwrctrl.h
+++ b/drivers/gpu/msm/kgsl_pwrctrl.h
@@ -58,7 +58,6 @@
* @nap_allowed - true if the device supports naps
* @idle_needed - true if the device needs a idle before clock change
* @irq_name - resource name for the IRQ
- * @restore_slumber - Flag to indicate that we are in a suspend/restore sequence
* @clk_stats - structure of clock statistics
* @pm_qos_req_dma - the power management quality of service structure
* @pm_qos_latency - allowed CPU latency in microseconds
@@ -86,7 +85,6 @@
unsigned int idle_needed;
const char *irq_name;
s64 time;
- unsigned int restore_slumber;
struct kgsl_clk_stats clk_stats;
struct pm_qos_request pm_qos_req_dma;
unsigned int pm_qos_latency;
diff --git a/drivers/gpu/msm/kgsl_snapshot.c b/drivers/gpu/msm/kgsl_snapshot.c
index 4165690..f09c623 100644
--- a/drivers/gpu/msm/kgsl_snapshot.c
+++ b/drivers/gpu/msm/kgsl_snapshot.c
@@ -347,6 +347,9 @@
int offset;
int ret = -EINVAL;
+ if (!gpuaddr)
+ return 0;
+
entry = kgsl_get_mem_entry(device, ptbase, gpuaddr, size);
if (entry == NULL) {
diff --git a/drivers/hwmon/qpnp-adc-current.c b/drivers/hwmon/qpnp-adc-current.c
index 275291f..f6ab3c2 100644
--- a/drivers/hwmon/qpnp-adc-current.c
+++ b/drivers/hwmon/qpnp-adc-current.c
@@ -95,6 +95,7 @@
#define QPNP_IADC_LSB_OFFSET 0xF3
#define QPNP_IADC_NOMINAL_RSENSE 0xF4
#define QPNP_IADC_ATE_GAIN_CALIB_OFFSET 0xF5
+#define QPNP_INT_TEST_VAL 0xE1
#define QPNP_IADC_ADC_CH_SEL_CTL 0x48
#define QPNP_IADC_ADC_CHX_SEL_SHIFT 3
@@ -126,16 +127,24 @@
#define QPNP_RSENSE_MSB_SIGN_CHECK 0x80
#define QPNP_ADC_COMPLETION_TIMEOUT HZ
+struct qpnp_iadc_comp {
+ bool ext_rsense;
+ u8 id;
+ u8 sys_gain;
+ u8 revision;
+};
+
struct qpnp_iadc_drv {
struct qpnp_adc_drv *adc;
int32_t rsense;
bool external_rsense;
struct device *iadc_hwmon;
bool iadc_initialized;
- int64_t die_temp_calib_offset;
+ int64_t die_temp;
struct delayed_work iadc_work;
struct mutex iadc_vadc_lock;
bool iadc_mode_sel;
+ struct qpnp_iadc_comp iadc_comp;
struct sensor_device_attribute sens_attr[0];
};
@@ -293,6 +302,104 @@
return 0;
}
+static int32_t qpnp_iadc_comp(int64_t *result, struct qpnp_iadc_comp comp,
+ int64_t die_temp)
+{
+ int64_t temp_var = 0, sign_coeff = 0, sys_gain_coeff = 0, old;
+
+ old = *result;
+ *result = *result * 1000000;
+
+ if (comp.revision == QPNP_IADC_VER_3_1) {
+ /* revision 3.1 */
+ if (comp.sys_gain > 127)
+ sys_gain_coeff = -QPNP_COEFF_6 * (comp.sys_gain - 128);
+ else
+ sys_gain_coeff = QPNP_COEFF_6 * comp.sys_gain;
+ } else if (comp.revision != QPNP_IADC_VER_3_0) {
+ /* unsupported revision, do not compensate */
+ *result = old;
+ return 0;
+ }
+
+ if (!comp.ext_rsense) {
+ /* internal rsense */
+ switch (comp.id) {
+ case COMP_ID_TSMC:
+ temp_var = ((QPNP_COEFF_2 * die_temp) -
+ QPNP_COEFF_3_TYPEB);
+ break;
+ case COMP_ID_GF:
+ default:
+ temp_var = ((QPNP_COEFF_2 * die_temp) -
+ QPNP_COEFF_3_TYPEA);
+ break;
+ }
+ temp_var = div64_s64(temp_var, QPNP_COEFF_4);
+ if (comp.revision == QPNP_IADC_VER_3_0)
+ temp_var = QPNP_COEFF_1 * (1000000 - temp_var);
+ else if (comp.revision == QPNP_IADC_VER_3_1)
+ temp_var = 1000000 * (1000000 - temp_var);
+ *result = div64_s64(*result * 1000000, temp_var);
+ }
+
+ sign_coeff = *result < 0 ? QPNP_COEFF_7 : QPNP_COEFF_5;
+ if (comp.ext_rsense) {
+ /* external rsense and current charging */
+ temp_var = div64_s64((-sign_coeff * die_temp) + QPNP_COEFF_8,
+ QPNP_COEFF_4);
+ temp_var = 1000000000 - temp_var;
+ if (comp.revision == QPNP_IADC_VER_3_1) {
+ sys_gain_coeff = (1000000 +
+ div64_s64(sys_gain_coeff, QPNP_COEFF_4));
+ temp_var = div64_s64(temp_var * sys_gain_coeff,
+ 1000000000);
+ }
+ *result = div64_s64(*result, temp_var);
+ }
+ pr_debug("%lld compensated into %lld\n", old, *result);
+
+ return 0;
+}
+
+int32_t qpnp_iadc_comp_result(int64_t *result)
+{
+ struct qpnp_iadc_drv *iadc = qpnp_iadc;
+
+ return qpnp_iadc_comp(result, iadc->iadc_comp, iadc->die_temp);
+}
+EXPORT_SYMBOL(qpnp_iadc_comp_result);
+
+static int32_t qpnp_iadc_comp_info(void)
+{
+ struct qpnp_iadc_drv *iadc = qpnp_iadc;
+ int rc = 0;
+
+ rc = qpnp_iadc_read_reg(QPNP_INT_TEST_VAL, &iadc->iadc_comp.id);
+ if (rc < 0) {
+ pr_err("qpnp adc comp id failed with %d\n", rc);
+ return rc;
+ }
+
+ rc = qpnp_iadc_read_reg(QPNP_IADC_REVISION2, &iadc->iadc_comp.revision);
+ if (rc < 0) {
+ pr_err("qpnp adc revision read failed with %d\n", rc);
+ return rc;
+ }
+
+ rc = qpnp_iadc_read_reg(QPNP_IADC_ATE_GAIN_CALIB_OFFSET,
+ &iadc->iadc_comp.sys_gain);
+ if (rc < 0)
+ pr_err("full scale read failed with %d\n", rc);
+
+ pr_debug("fab id = %u, revision = %u, sys gain = %u, external_rsense = %d\n",
+ iadc->iadc_comp.id,
+ iadc->iadc_comp.revision,
+ iadc->iadc_comp.sys_gain,
+ iadc->iadc_comp.ext_rsense);
+ return rc;
+}
+
static int32_t qpnp_iadc_configure(enum qpnp_iadc_channels channel,
uint16_t *raw_code, uint32_t mode_sel)
{
@@ -583,12 +690,12 @@
return rc;
die_temp_offset = result_pmic_therm.physical -
- iadc->die_temp_calib_offset;
+ iadc->die_temp;
if (die_temp_offset < 0)
die_temp_offset = -die_temp_offset;
if (die_temp_offset > QPNP_IADC_DIE_TEMP_CALIB_OFFSET) {
- iadc->die_temp_calib_offset =
+ iadc->die_temp =
result_pmic_therm.physical;
rc = qpnp_iadc_calibrate_for_trim();
if (rc)
@@ -640,12 +747,17 @@
(iadc->adc->calib.gain_raw - iadc->adc->calib.offset_raw);
result_current = result->result_uv;
result_current *= QPNP_IADC_NANO_VOLTS_FACTOR;
+ /* Intentional fall through. Process the result w/o comp */
do_div(result_current, rsense_u_ohms);
if (sign) {
result->result_uv = -result->result_uv;
result_current = -result_current;
}
+ rc = qpnp_iadc_comp_result(&result_current);
+ if (rc < 0)
+ pr_err("Error during compensating the IADC\n");
+ rc = 0;
result->result_ua = (int32_t) result_current;
fail:
@@ -868,6 +980,11 @@
mutex_init(&iadc->iadc_vadc_lock);
INIT_DELAYED_WORK(&iadc->iadc_work, qpnp_iadc_work);
+ rc = qpnp_iadc_comp_info();
+ if (rc) {
+ dev_err(&spmi->dev, "abstracting IADC comp info failed!\n");
+ goto fail;
+ }
iadc->iadc_initialized = true;
rc = qpnp_iadc_calibrate_for_trim();
diff --git a/drivers/hwmon/qpnp-adc-voltage.c b/drivers/hwmon/qpnp-adc-voltage.c
index edb1d66..b3b3b5e 100644
--- a/drivers/hwmon/qpnp-adc-voltage.c
+++ b/drivers/hwmon/qpnp-adc-voltage.c
@@ -83,6 +83,7 @@
#define QPNP_VADC_M1_LOW_THR_MSB 0x6a
#define QPNP_VADC_M1_HIGH_THR_LSB 0x6b
#define QPNP_VADC_M1_HIGH_THR_MSB 0x6c
+#define QPNP_INT_TEST_VAL 0xE1
#define QPNP_VADC_DATA0 0x60
#define QPNP_VADC_DATA1 0x61
@@ -100,7 +101,8 @@
bool vadc_initialized;
int max_channels_available;
bool vadc_iadc_sync_lock;
- struct sensor_device_attribute sens_attr[0];
+ u8 id;
+ struct sensor_device_attribute sens_attr[0];
};
struct qpnp_vadc_drv *qpnp_vadc;
@@ -432,6 +434,62 @@
return 0;
}
+static int32_t qpnp_vbat_sns_comp(int64_t *result, u8 id, int64_t die_temp)
+{
+ int64_t temp_var = 0;
+ int64_t old = *result;
+
+ if (die_temp < 25000)
+ return 0;
+
+ switch (id) {
+ case COMP_ID_TSMC:
+ temp_var = (((die_temp *
+ (-QPNP_VBAT_SNS_COEFF_1_TYPEB))
+ + QPNP_VBAT_SNS_COEFF_2_TYPEB));
+ break;
+ default:
+ case COMP_ID_GF:
+ temp_var = (((die_temp *
+ (-QPNP_VBAT_SNS_COEFF_1_TYPEA))
+ + QPNP_VBAT_SNS_COEFF_2_TYPEA));
+ break;
+ }
+
+ temp_var = div64_s64(temp_var, QPNP_VBAT_SNS_COEFF_3);
+
+ temp_var = 1000000 + temp_var;
+
+ *result = *result * temp_var;
+
+ *result = div64_s64(*result, 1000000);
+ pr_debug("%lld compensated into %lld\n", old, *result);
+
+ return 0;
+}
+
+int32_t qpnp_vbat_sns_comp_result(int64_t *result)
+{
+ struct qpnp_vadc_drv *vadc = qpnp_vadc;
+ struct qpnp_vadc_result die_temp_result;
+ int rc = 0;
+
+ rc = qpnp_vadc_conv_seq_request(ADC_SEQ_NONE,
+ DIE_TEMP, &die_temp_result);
+ if (rc < 0) {
+ pr_err("Error reading die_temp\n");
+ return rc;
+ }
+
+ rc = qpnp_vbat_sns_comp(result, vadc->id,
+ die_temp_result.physical);
+ if (rc < 0)
+ pr_err("Error with vbat compensation\n");
+
+ return rc;
+}
+EXPORT_SYMBOL(qpnp_vbat_sns_comp_result);
+
static void qpnp_vadc_625mv_channel_sel(uint32_t *ref_channel_sel)
{
struct qpnp_vadc_drv *vadc = qpnp_vadc;
@@ -790,7 +848,34 @@
int32_t qpnp_vadc_read(enum qpnp_vadc_channels channel,
struct qpnp_vadc_result *result)
{
- return qpnp_vadc_conv_seq_request(ADC_SEQ_NONE,
+ struct qpnp_vadc_drv *vadc = qpnp_vadc;
+ enum qpnp_vadc_channels;
+ struct qpnp_vadc_result die_temp_result;
+ int rc = 0;
+
+ if (channel == VBAT_SNS) {
+ rc = qpnp_vadc_conv_seq_request(ADC_SEQ_NONE,
+ channel, result);
+ if (rc < 0) {
+ pr_err("Error reading vbatt\n");
+ return rc;
+ }
+
+ rc = qpnp_vadc_conv_seq_request(ADC_SEQ_NONE,
+ DIE_TEMP, &die_temp_result);
+ if (rc < 0) {
+ pr_err("Error reading die_temp\n");
+ return rc;
+ }
+
+ rc = qpnp_vbat_sns_comp(&result->physical, vadc->id,
+ die_temp_result.physical);
+ if (rc < 0)
+ pr_err("Error with vbat compensation\n");
+
+ return 0;
+ } else
+ return qpnp_vadc_conv_seq_request(ADC_SEQ_NONE,
channel, result);
}
EXPORT_SYMBOL(qpnp_vadc_read);
@@ -970,6 +1055,7 @@
struct device_node *node = spmi->dev.of_node;
struct device_node *child;
int rc, count_adc_channel_list = 0;
+ u8 fab_id = 0;
if (!node)
return -EINVAL;
@@ -1033,6 +1119,13 @@
vadc->vadc_hwmon = hwmon_device_register(&vadc->adc->spmi->dev);
vadc->vadc_init_calib = false;
vadc->max_channels_available = count_adc_channel_list;
+ rc = qpnp_vadc_read_reg(QPNP_INT_TEST_VAL, &fab_id);
+ if (rc < 0) {
+ pr_err("qpnp adc comp id failed with %d\n", rc);
+ return rc;
+ }
+ vadc->id = fab_id;
+
vadc->vadc_initialized = true;
vadc->vadc_iadc_sync_lock = false;
diff --git a/drivers/input/touchscreen/gen_vkeys.c b/drivers/input/touchscreen/gen_vkeys.c
index fcda6c9..efddf61 100644
--- a/drivers/input/touchscreen/gen_vkeys.c
+++ b/drivers/input/touchscreen/gen_vkeys.c
@@ -24,6 +24,8 @@
#define HEIGHT_SCALE_NUM 8
#define HEIGHT_SCALE_DENOM 10
+#define VKEY_Y_OFFSET_DEFAULT 0
+
/* numerator and denomenator for border equations */
#define BORDER_ADJUST_NUM 3
#define BORDER_ADJUST_DENOM 4
@@ -59,7 +61,7 @@
{
struct device_node *np = dev->of_node;
struct property *prop;
- int rc;
+ int rc, val;
rc = of_property_read_string(np, "label", &pdata->name);
if (rc) {
@@ -105,6 +107,15 @@
return -EINVAL;
}
}
+
+ pdata->y_offset = VKEY_Y_OFFSET_DEFAULT;
+ rc = of_property_read_u32(np, "qcom,y-offset", &val);
+ if (!rc)
+ pdata->y_offset = val;
+ else if (rc != -EINVAL) {
+ dev_err(dev, "Failed to read y position offset\n");
+ return rc;
+ }
return 0;
}
@@ -147,7 +158,7 @@
width = ((pdata->disp_maxx - (border * (pdata->num_keys - 1)))
/ pdata->num_keys);
height = (pdata->panel_maxy - pdata->disp_maxy);
- center_y = pdata->disp_maxy + (height / 2);
+ center_y = pdata->disp_maxy + (height / 2) + pdata->y_offset;
height = height * HEIGHT_SCALE_NUM / HEIGHT_SCALE_DENOM;
x2 -= border * BORDER_ADJUST_NUM / BORDER_ADJUST_DENOM;
diff --git a/drivers/input/touchscreen/synaptics_fw_update.c b/drivers/input/touchscreen/synaptics_fw_update.c
index dbe378c..d51481f 100644
--- a/drivers/input/touchscreen/synaptics_fw_update.c
+++ b/drivers/input/touchscreen/synaptics_fw_update.c
@@ -107,6 +107,9 @@
struct kobject *kobj, struct bin_attribute *attributes,
char *buf, loff_t pos, size_t count);
+static ssize_t fwu_sysfs_force_reflash_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count);
+
static ssize_t fwu_sysfs_do_reflash_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count);
@@ -301,6 +304,9 @@
};
static struct device_attribute attrs[] = {
+ __ATTR(forceflash, S_IWUGO,
+ synaptics_rmi4_show_error,
+ fwu_sysfs_force_reflash_store),
__ATTR(doreflash, S_IWUGO,
synaptics_rmi4_show_error,
fwu_sysfs_do_reflash_store),
@@ -1462,6 +1468,40 @@
return count;
}
+static ssize_t fwu_sysfs_force_reflash_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int retval;
+ unsigned int input;
+ struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
+
+ if (sscanf(buf, "%u", &input) != 1) {
+ retval = -EINVAL;
+ goto exit;
+ }
+
+ if (input != 1) {
+ retval = -EINVAL;
+ goto exit;
+ }
+
+ fwu->force_update = true;
+ retval = synaptics_fw_updater(fwu->ext_data_source);
+ if (retval < 0) {
+ dev_err(&rmi4_data->i2c_client->dev,
+ "%s: Failed to do reflash\n",
+ __func__);
+ goto exit;
+ }
+
+ retval = count;
+
+exit:
+ kfree(fwu->ext_data_source);
+ fwu->ext_data_source = NULL;
+ return retval;
+}
+
static ssize_t fwu_sysfs_do_reflash_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
diff --git a/drivers/input/touchscreen/synaptics_i2c_rmi4.c b/drivers/input/touchscreen/synaptics_i2c_rmi4.c
index 426c7e7..ed53c41 100644
--- a/drivers/input/touchscreen/synaptics_i2c_rmi4.c
+++ b/drivers/input/touchscreen/synaptics_i2c_rmi4.c
@@ -107,13 +107,17 @@
static int synaptics_rmi4_suspend(struct device *dev);
static int synaptics_rmi4_resume(struct device *dev);
-#ifdef CONFIG_HAS_EARLYSUSPEND
+
static ssize_t synaptics_rmi4_full_pm_cycle_show(struct device *dev,
struct device_attribute *attr, char *buf);
static ssize_t synaptics_rmi4_full_pm_cycle_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count);
+#if defined(CONFIG_FB)
+static int fb_notifier_callback(struct notifier_block *self,
+ unsigned long event, void *data);
+#elif defined(CONFIG_HAS_EARLYSUSPEND)
static void synaptics_rmi4_early_suspend(struct early_suspend *h);
static void synaptics_rmi4_late_resume(struct early_suspend *h);
@@ -229,7 +233,7 @@
};
static struct device_attribute attrs[] = {
-#ifdef CONFIG_HAS_EARLYSUSPEND
+#ifdef CONFIG_PM
__ATTR(full_pm_cycle, (S_IRUGO | S_IWUGO),
synaptics_rmi4_full_pm_cycle_show,
synaptics_rmi4_full_pm_cycle_store),
@@ -260,8 +264,7 @@
static bool exp_fn_inited;
static struct mutex exp_fn_list_mutex;
static struct list_head exp_fn_list;
-
-#ifdef CONFIG_HAS_EARLYSUSPEND
+#ifdef CONFIG_PM
static ssize_t synaptics_rmi4_full_pm_cycle_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -284,6 +287,36 @@
return count;
}
+
+#ifdef CONFIG_FB
+static void configure_sleep(struct synaptics_rmi4_data *rmi4_data)
+{
+ int retval = 0;
+
+ rmi4_data->fb_notif.notifier_call = fb_notifier_callback;
+
+ retval = fb_register_client(&rmi4_data->fb_notif);
+ if (retval)
+ dev_err(&rmi4_data->i2c_client->dev,
+ "Unable to register fb_notifier: %d\n", retval);
+ return;
+}
+#elif defined CONFIG_HAS_EARLYSUSPEND
+static void configure_sleep(struct synaptics_rmi4_data *rmi4_data)
+{
+ rmi4_data->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
+ rmi4_data->early_suspend.suspend = synaptics_rmi4_early_suspend;
+ rmi4_data->early_suspend.resume = synaptics_rmi4_late_resume;
+ register_early_suspend(&rmi4_data->early_suspend);
+
+ return;
+}
+#else
+static void configure_sleep(struct synaptics_rmi4_data *rmi4_data)
+{
+ return;
+}
+#endif
#endif
static ssize_t synaptics_rmi4_f01_reset_store(struct device *dev,
@@ -2203,12 +2236,7 @@
goto err_register_input;
}
-#ifdef CONFIG_HAS_EARLYSUSPEND
- rmi4_data->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
- rmi4_data->early_suspend.suspend = synaptics_rmi4_early_suspend;
- rmi4_data->early_suspend.resume = synaptics_rmi4_late_resume;
- register_early_suspend(&rmi4_data->early_suspend);
-#endif
+ configure_sleep(rmi4_data);
if (!exp_fn_inited) {
mutex_init(&exp_fn_list_mutex);
@@ -2446,7 +2474,27 @@
return;
}
-#ifdef CONFIG_HAS_EARLYSUSPEND
+#if defined(CONFIG_FB)
+static int fb_notifier_callback(struct notifier_block *self,
+ unsigned long event, void *data)
+{
+ struct fb_event *evdata = data;
+ int *blank;
+ struct synaptics_rmi4_data *rmi4_data =
+ container_of(self, struct synaptics_rmi4_data, fb_notif);
+
+ if (evdata && evdata->data && event == FB_EVENT_BLANK &&
+ rmi4_data && rmi4_data->i2c_client) {
+ blank = evdata->data;
+ if (*blank == FB_BLANK_UNBLANK)
+ synaptics_rmi4_resume(&(rmi4_data->input_dev->dev));
+ else if (*blank == FB_BLANK_POWERDOWN)
+ synaptics_rmi4_suspend(&(rmi4_data->input_dev->dev));
+ }
+
+ return 0;
+}
+#elif defined(CONFIG_HAS_EARLYSUSPEND)
/**
* synaptics_rmi4_early_suspend()
*
@@ -2629,10 +2677,15 @@
return 0;
}
+#if (!defined(CONFIG_FB) && !defined(CONFIG_HAS_EARLYSUSPEND))
static const struct dev_pm_ops synaptics_rmi4_dev_pm_ops = {
.suspend = synaptics_rmi4_suspend,
.resume = synaptics_rmi4_resume,
};
+#else
+static const struct dev_pm_ops synaptics_rmi4_dev_pm_ops = {
+};
+#endif
#endif
static const struct i2c_device_id synaptics_rmi4_id_table[] = {
diff --git a/drivers/input/touchscreen/synaptics_i2c_rmi4.h b/drivers/input/touchscreen/synaptics_i2c_rmi4.h
index 681822a..681b95c 100644
--- a/drivers/input/touchscreen/synaptics_i2c_rmi4.h
+++ b/drivers/input/touchscreen/synaptics_i2c_rmi4.h
@@ -27,7 +27,11 @@
#define SYNAPTICS_DSX_DRIVER_VERSION 0x1005
#include <linux/version.h>
-#ifdef CONFIG_HAS_EARLYSUSPEND
+
+#ifdef CONFIG_FB
+#include <linux/notifier.h>
+#include <linux/fb.h>
+#elif defined CONFIG_HAS_EARLYSUSPEND
#include <linux/earlysuspend.h>
#endif
@@ -227,6 +231,13 @@
unsigned char *data, unsigned short length);
int (*irq_enable)(struct synaptics_rmi4_data *rmi4_data, bool enable);
int (*reset_device)(struct synaptics_rmi4_data *rmi4_data);
+#ifdef CONFIG_FB
+ struct notifier_block fb_notif;
+#else
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ struct early_suspend early_suspend;
+#endif
+#endif
};
enum exp_fn {
diff --git a/drivers/iommu/msm_iommu-v0.c b/drivers/iommu/msm_iommu-v0.c
index b1960c6..b92ec7f 100644
--- a/drivers/iommu/msm_iommu-v0.c
+++ b/drivers/iommu/msm_iommu-v0.c
@@ -148,12 +148,26 @@
if (ret)
clk_disable_unprepare(drvdata->pclk);
}
+
+ if (ret)
+ goto fail;
+
+ if (drvdata->aclk) {
+ ret = clk_prepare_enable(drvdata->aclk);
+ if (ret) {
+ clk_disable_unprepare(drvdata->clk);
+ clk_disable_unprepare(drvdata->pclk);
+ }
+ }
+
fail:
return ret;
}
static void __disable_clocks(struct msm_iommu_drvdata *drvdata)
{
+ if (drvdata->aclk)
+ clk_disable_unprepare(drvdata->aclk);
if (drvdata->clk)
clk_disable_unprepare(drvdata->clk);
clk_disable_unprepare(drvdata->pclk);
diff --git a/drivers/iommu/msm_iommu_dev-v0.c b/drivers/iommu/msm_iommu_dev-v0.c
index 7ae0b21..059216e 100644
--- a/drivers/iommu/msm_iommu_dev-v0.c
+++ b/drivers/iommu/msm_iommu_dev-v0.c
@@ -129,7 +129,8 @@
}
static int msm_iommu_parse_dt(struct platform_device *pdev,
- struct msm_iommu_drvdata *drvdata)
+ struct msm_iommu_drvdata *drvdata,
+ int *needs_alt_core_clk)
{
#ifdef CONFIG_OF_DEVICE
struct device_node *child;
@@ -169,6 +170,10 @@
pr_err("%s: Missing property label\n", __func__);
return -EINVAL;
}
+
+ *needs_alt_core_clk = of_property_read_bool(pdev->dev.of_node,
+ "qcom,needs-alt-core-clk");
+
drvdata->sec_id = -1;
drvdata->ttbr_split = 0;
#endif
@@ -176,7 +181,8 @@
}
static int __get_clocks(struct platform_device *pdev,
- struct msm_iommu_drvdata *drvdata)
+ struct msm_iommu_drvdata *drvdata,
+ int needs_alt_core_clk)
{
int ret = 0;
@@ -199,6 +205,18 @@
} else {
drvdata->clk = NULL;
}
+
+ if (needs_alt_core_clk) {
+ drvdata->aclk = devm_clk_get(&pdev->dev, "alt_core_clk");
+ if (IS_ERR(drvdata->aclk))
+ return PTR_ERR(drvdata->aclk);
+ }
+
+ if (drvdata->aclk && clk_get_rate(drvdata->aclk) == 0) {
+ ret = clk_round_rate(drvdata->aclk, 1000);
+ clk_set_rate(drvdata->aclk, ret);
+ }
+
return 0;
fail:
return ret;
@@ -206,35 +224,13 @@
static void __put_clocks(struct msm_iommu_drvdata *drvdata)
{
+ if (drvdata->aclk)
+ clk_put(drvdata->aclk);
if (drvdata->clk)
clk_put(drvdata->clk);
clk_put(drvdata->pclk);
}
-static int __enable_clocks(struct msm_iommu_drvdata *drvdata)
-{
- int ret;
-
- ret = clk_prepare_enable(drvdata->pclk);
- if (ret)
- goto fail;
-
- if (drvdata->clk) {
- ret = clk_prepare_enable(drvdata->clk);
- if (ret)
- clk_disable_unprepare(drvdata->pclk);
- }
-fail:
- return ret;
-}
-
-static void __disable_clocks(struct msm_iommu_drvdata *drvdata)
-{
- if (drvdata->clk)
- clk_disable_unprepare(drvdata->clk);
- clk_disable_unprepare(drvdata->pclk);
-}
-
/*
* Do a basic check of the IOMMU by performing an ATS operation
* on context bank 0.
@@ -326,6 +322,7 @@
struct msm_iommu_drvdata *drvdata;
struct msm_iommu_dev *iommu_dev = pdev->dev.platform_data;
int ret;
+ int needs_alt_core_clk = 0;
drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), GFP_KERNEL);
@@ -335,7 +332,7 @@
}
if (pdev->dev.of_node) {
- ret = msm_iommu_parse_dt(pdev, drvdata);
+ ret = msm_iommu_parse_dt(pdev, drvdata, &needs_alt_core_clk);
if (ret)
goto fail;
} else if (pdev->dev.platform_data) {
@@ -382,12 +379,12 @@
drvdata->dev = &pdev->dev;
- ret = __get_clocks(pdev, drvdata);
+ ret = __get_clocks(pdev, drvdata, needs_alt_core_clk);
if (ret)
goto fail;
- __enable_clocks(drvdata);
+ iommu_access_ops_v0.iommu_clk_on(drvdata);
msm_iommu_reset(drvdata->base, drvdata->glb_base, drvdata->ncb);
@@ -401,7 +398,7 @@
msm_iommu_add_drv(drvdata);
platform_set_drvdata(pdev, drvdata);
- __disable_clocks(drvdata);
+ iommu_access_ops_v0.iommu_clk_off(drvdata);
pmon_info = msm_iommu_pm_alloc(&pdev->dev);
if (pmon_info != NULL) {
@@ -430,7 +427,7 @@
return 0;
fail_clk:
- __disable_clocks(drvdata);
+ iommu_access_ops_v0.iommu_clk_off(drvdata);
__put_clocks(drvdata);
fail:
return ret;
@@ -618,9 +615,9 @@
goto fail;
}
- __enable_clocks(drvdata);
+ iommu_access_ops_v0.iommu_clk_on(drvdata);
__program_m2v_tables(drvdata, ctx_drvdata);
- __disable_clocks(drvdata);
+ iommu_access_ops_v0.iommu_clk_off(drvdata);
dev_info(&pdev->dev, "context %s using bank %d\n", ctx_drvdata->name,
ctx_drvdata->num);
diff --git a/drivers/leds/leds-qpnp.c b/drivers/leds/leds-qpnp.c
index 5d5ff43..0233e18 100644
--- a/drivers/leds/leds-qpnp.c
+++ b/drivers/leds/leds-qpnp.c
@@ -93,6 +93,7 @@
#define FLASH_LED_STROBE_CTRL(base) (base + 0x47)
#define FLASH_LED_UNLOCK_SECURE(base) (base + 0xD0)
#define FLASH_LED_TORCH(base) (base + 0xE4)
+#define FLASH_FAULT_DETECT(base) (base + 0x51)
#define FLASH_MAX_LEVEL 0x4F
#define FLASH_NO_MASK 0x00
@@ -106,10 +107,9 @@
#define FLASH_TMR_MASK 0x03
#define FLASH_TMR_WATCHDOG 0x03
#define FLASH_TMR_SAFETY 0x00
-
+#define FLASH_FAULT_DETECT_MASK 0X80
#define FLASH_HW_VREG_OK 0x80
#define FLASH_VREG_MASK 0xC0
-
#define FLASH_STARTUP_DLY_MASK 0x02
#define FLASH_ENABLE_ALL 0xE0
@@ -120,6 +120,7 @@
#define FLASH_ENABLE_LED_0 0x40
#define FLASH_ENABLE_LED_1 0x20
#define FLASH_INIT_MASK 0xE0
+#define FLASH_SELFCHECK_ENABLE 0x80
#define FLASH_STROBE_SW 0xC0
#define FLASH_STROBE_HW 0xC4
@@ -130,7 +131,7 @@
#define FLASH_CURRENT_PRGM_MIN 1
#define FLASH_CURRENT_PRGM_SHIFT 1
#define FLASH_CURRENT_MAX 0x4F
-#define FLASH_CURRENT_TORCH 0x0F
+#define FLASH_CURRENT_TORCH 0x07
#define FLASH_DURATION_200ms 0x13
#define FLASH_CLAMP_200mA 0x0F
@@ -171,6 +172,8 @@
#define LED_MPP_SINK_MASK 0x07
#define LED_MPP_MODE_MASK 0x7F
#define LED_MPP_EN_MASK 0x80
+#define LED_MPP_SRC_MASK 0x0F
+#define LED_MPP_MODE_CTRL_MASK 0x70
#define LED_MPP_MODE_SINK (0x06 << 4)
#define LED_MPP_MODE_ENABLE 0x01
@@ -551,7 +554,8 @@
return rc;
}
- val = led->mpp_cfg->source_sel | led->mpp_cfg->mode_ctrl;
+ val = (led->mpp_cfg->source_sel & LED_MPP_SRC_MASK) |
+ (led->mpp_cfg->mode_ctrl & LED_MPP_MODE_CTRL_MASK);
rc = qpnp_led_masked_write(led,
LED_MPP_MODE_CTRL(led->base), LED_MPP_MODE_MASK,
@@ -632,18 +636,10 @@
return rc;
}
- qpnp_led_masked_write(led, FLASH_MAX_CURR(led->base),
- FLASH_CURRENT_MASK, FLASH_CURRENT_TORCH);
- if (rc) {
- dev_err(&led->spmi_dev->dev,
- "Max current reg write failed(%d)\n",
- rc);
- return rc;
- }
-
rc = qpnp_led_masked_write(led,
led->flash_cfg->current_addr,
- FLASH_CURRENT_MASK, FLASH_CURRENT_TORCH);
+ FLASH_CURRENT_MASK,
+ led->flash_cfg->current_prgm);
if (rc) {
dev_err(&led->spmi_dev->dev,
"Current reg write failed(%d)\n", rc);
@@ -652,7 +648,8 @@
rc = qpnp_led_masked_write(led,
led->flash_cfg->second_addr,
- FLASH_CURRENT_MASK, FLASH_CURRENT_TORCH);
+ FLASH_CURRENT_MASK,
+ led->flash_cfg->current_prgm);
if (rc) {
dev_err(&led->spmi_dev->dev,
"2nd Current reg write failed(%d)\n",
@@ -660,6 +657,16 @@
return rc;
}
+ qpnp_led_masked_write(led, FLASH_MAX_CURR(led->base),
+ FLASH_CURRENT_MASK,
+ led->max_current);
+ if (rc) {
+ dev_err(&led->spmi_dev->dev,
+ "Max current reg write failed(%d)\n",
+ rc);
+ return rc;
+ }
+
rc = qpnp_led_masked_write(led,
FLASH_ENABLE_CONTROL(led->base),
FLASH_ENABLE_MODULE_MASK, FLASH_ENABLE_MODULE);
@@ -669,9 +676,22 @@
return rc;
}
} else {
+ /* Set flash safety timer */
rc = qpnp_led_masked_write(led,
- FLASH_MAX_CURR(led->base),
- FLASH_CURRENT_MASK, FLASH_CURRENT_MAX);
+ FLASH_SAFETY_TIMER(led->base),
+ FLASH_SAFETY_TIMER_MASK,
+ led->flash_cfg->duration);
+ if (rc) {
+ dev_err(&led->spmi_dev->dev,
+ "Safety timer reg write failed(%d)\n",
+ rc);
+ return rc;
+ }
+
+ /* Set max current */
+ rc = qpnp_led_masked_write(led,
+ FLASH_MAX_CURR(led->base), FLASH_CURRENT_MASK,
+ FLASH_MAX_LEVEL);
if (rc) {
dev_err(&led->spmi_dev->dev,
"Max current reg write failed(%d)\n",
@@ -679,6 +699,18 @@
return rc;
}
+ /* Set clamp current */
+ rc = qpnp_led_masked_write(led,
+ FLASH_CLAMP_CURR(led->base),
+ FLASH_CURRENT_MASK,
+ led->flash_cfg->clamp_curr);
+ if (rc) {
+ dev_err(&led->spmi_dev->dev,
+ "Clamp current reg write failed(%d)\n",
+ rc);
+ return rc;
+ }
+
/* Write 0x80 to MODULE_ENABLE before writing 0xE0
* in order to avoid reg value goes from 0x00 to
* 0xE0. This causes a hardware bug.
@@ -714,16 +746,6 @@
}
rc = qpnp_led_masked_write(led,
- FLASH_CLAMP_CURR(led->base),
- FLASH_CURRENT_MASK, FLASH_CURRENT_TORCH);
- if (rc) {
- dev_err(&led->spmi_dev->dev,
- "Clamp Current reg write failed(%d)\n",
- rc);
- return rc;
- }
-
- rc = qpnp_led_masked_write(led,
FLASH_ENABLE_CONTROL(led->base),
FLASH_ENABLE_MASK, FLASH_ENABLE_ALL);
if (rc) {
@@ -762,25 +784,16 @@
if (rc) {
dev_err(&led->spmi_dev->dev,
"Secure reg write failed(%d)\n", rc);
- }
-
- rc = qpnp_led_masked_write(led,
- FLASH_LED_TORCH(led->base),
- FLASH_TORCH_MASK, FLASH_LED_TORCH_DISABLE);
- if (rc) {
- dev_err(&led->spmi_dev->dev,
- "Torch reg write failed(%d)\n", rc);
return rc;
}
rc = qpnp_led_masked_write(led,
- FLASH_SAFETY_TIMER(led->base),
- FLASH_SAFETY_TIMER_MASK,
- led->flash_cfg->duration);
+ FLASH_LED_TORCH(led->base),
+ FLASH_TORCH_MASK,
+ FLASH_LED_TORCH_DISABLE);
if (rc) {
dev_err(&led->spmi_dev->dev,
- "Safety timer reg write failed(%d)\n",
- rc);
+ "Torch reg write failed(%d)\n", rc);
return rc;
}
}
@@ -936,6 +949,7 @@
if (rc < 0)
dev_err(&led->spmi_dev->dev,
"MPP set brightness failed (%d)\n", rc);
+ break;
case QPNP_ID_KPDBL:
rc = qpnp_kpdbl_set(led);
if (rc < 0)
@@ -1208,52 +1222,7 @@
"LED %d flash write failed(%d)\n", led->id, rc);
return rc;
}
- rc = qpnp_led_masked_write(led, FLASH_ENABLE_CONTROL(led->base),
- FLASH_INIT_MASK, FLASH_ENABLE_MODULE);
- if (rc) {
- dev_err(&led->spmi_dev->dev,
- "Enable reg write failed(%d)\n", rc);
- return rc;
- }
- /* Set flash safety timer */
- rc = qpnp_led_masked_write(led, FLASH_SAFETY_TIMER(led->base),
- FLASH_SAFETY_TIMER_MASK, led->flash_cfg->duration);
- if (rc) {
- dev_err(&led->spmi_dev->dev,
- "Safety timer reg write failed(%d)\n", rc);
- return rc;
- }
-
- /* Set max current */
- rc = qpnp_led_masked_write(led, FLASH_MAX_CURR(led->base),
- FLASH_CURRENT_MASK, FLASH_MAX_LEVEL);
- if (rc) {
- dev_err(&led->spmi_dev->dev,
- "Max current reg write failed(%d)\n", rc);
- return rc;
- }
- /* Set clamp current */
- rc = qpnp_led_masked_write(led, FLASH_CLAMP_CURR(led->base),
- FLASH_CURRENT_MASK, led->flash_cfg->clamp_curr);
- if (rc) {
- dev_err(&led->spmi_dev->dev,
- "Clamp current reg write failed(%d)\n", rc);
- return rc;
- }
-
- /* Set timer control - safety or watchdog */
- if (led->flash_cfg->safety_timer)
- rc = qpnp_led_masked_write(led, FLASH_LED_TMR_CTRL(led->base),
- FLASH_TMR_MASK, FLASH_TMR_SAFETY);
- else
- rc = qpnp_led_masked_write(led, FLASH_LED_TMR_CTRL(led->base),
- FLASH_TMR_MASK, FLASH_TMR_WATCHDOG);
- if (rc) {
- dev_err(&led->spmi_dev->dev,
- "LED timer ctrl reg write failed(%d)\n", rc);
- return rc;
- }
/* Set headroom */
rc = qpnp_led_masked_write(led, FLASH_HEADROOM(led->base),
FLASH_HEADROOM_MASK, led->flash_cfg->headroom);
@@ -1263,6 +1232,47 @@
return rc;
}
+ /* Set startup delay */
+ rc = qpnp_led_masked_write(led,
+ FLASH_STARTUP_DELAY(led->base), FLASH_STARTUP_DLY_MASK,
+ led->flash_cfg->startup_dly);
+ if (rc) {
+ dev_err(&led->spmi_dev->dev,
+ "Startup delay reg write failed(%d)\n", rc);
+ return rc;
+ }
+
+ /* Set timer control - safety or watchdog */
+ if (led->flash_cfg->safety_timer) {
+ rc = qpnp_led_masked_write(led,
+ FLASH_LED_TMR_CTRL(led->base),
+ FLASH_TMR_MASK, FLASH_TMR_SAFETY);
+ if (rc) {
+ dev_err(&led->spmi_dev->dev,
+ "LED timer ctrl reg write failed(%d)\n",
+ rc);
+ return rc;
+ }
+ }
+
+ /* Set Vreg force */
+ rc = qpnp_led_masked_write(led, FLASH_VREG_OK_FORCE(led->base),
+ FLASH_VREG_MASK, FLASH_HW_VREG_OK);
+ if (rc) {
+ dev_err(&led->spmi_dev->dev,
+ "Vreg OK reg write failed(%d)\n", rc);
+ return rc;
+ }
+
+ /* Set self fault check */
+ rc = qpnp_led_masked_write(led, FLASH_FAULT_DETECT(led->base),
+ FLASH_FAULT_DETECT_MASK, FLASH_SELFCHECK_ENABLE);
+ if (rc) {
+ dev_err(&led->spmi_dev->dev,
+ "Fault detect reg write failed(%d)\n", rc);
+ return rc;
+ }
+
/* Set mask enable */
rc = qpnp_led_masked_write(led, FLASH_MASK_ENABLE(led->base),
FLASH_MASK_REG_MASK, FLASH_MASK_1);
@@ -1272,32 +1282,7 @@
return rc;
}
- /* Set startup delay */
- rc = qpnp_led_masked_write(led, FLASH_STARTUP_DELAY(led->base),
- FLASH_STARTUP_DLY_MASK, led->flash_cfg->startup_dly);
- if (rc) {
- dev_err(&led->spmi_dev->dev,
- "Startup delay reg write failed(%d)\n", rc);
- return rc;
- }
-
- rc = qpnp_led_masked_write(led, FLASH_VREG_OK_FORCE(led->base),
- FLASH_VREG_MASK, FLASH_HW_VREG_OK);
- if (rc) {
- dev_err(&led->spmi_dev->dev,
- "Vreg OK reg write failed(%d)\n", rc);
- return rc;
- }
-
- /* Set led current and disable module */
- rc = qpnp_led_masked_write(led, led->flash_cfg->current_addr,
- FLASH_CURRENT_MASK, led->flash_cfg->current_prgm);
- if (rc) {
- dev_err(&led->spmi_dev->dev,
- "Current reg write failed(%d)\n", rc);
- return rc;
- }
-
+ /* Disable flash LED module */
rc = qpnp_led_masked_write(led, FLASH_ENABLE_CONTROL(led->base),
FLASH_ENABLE_MODULE_MASK, FLASH_DISABLE_ALL);
if (rc) {
@@ -1306,7 +1291,6 @@
return rc;
}
- led->flash_cfg->torch_enable = false;
led->flash_cfg->strobe_type = 0;
/* dump flash registers */
@@ -1679,6 +1663,9 @@
led->flash_cfg->safety_timer =
of_property_read_bool(node, "qcom,safety-timer");
+ led->flash_cfg->torch_enable =
+ of_property_read_bool(node, "qcom,torch-enable");
+
return 0;
}
diff --git a/drivers/media/dvb/dvb-core/dmxdev.c b/drivers/media/dvb/dvb-core/dmxdev.c
index 7347b37..2a750a6 100644
--- a/drivers/media/dvb/dvb-core/dmxdev.c
+++ b/drivers/media/dvb/dvb-core/dmxdev.c
@@ -1796,19 +1796,10 @@
return 0;
}
-static void dvb_dmxdev_ts_insertion_timer(unsigned long data)
-{
- struct ts_insertion_buffer *ts_buffer =
- (struct ts_insertion_buffer *)data;
-
- if (ts_buffer && !ts_buffer->abort)
- schedule_work(&ts_buffer->work);
-}
-
static void dvb_dmxdev_ts_insertion_work(struct work_struct *worker)
{
struct ts_insertion_buffer *ts_buffer =
- container_of(worker, struct ts_insertion_buffer, work);
+ container_of(worker, struct ts_insertion_buffer, dwork.work);
struct dmxdev_feed *feed;
size_t free_bytes;
struct dmx_ts_feed *ts;
@@ -1832,8 +1823,8 @@
ts->ts_insertion_insert_buffer(ts,
ts_buffer->buffer, ts_buffer->size);
- if (ts_buffer->repetition_time)
- mod_timer(&ts_buffer->timer, jiffies +
+ if (ts_buffer->repetition_time && !ts_buffer->abort)
+ schedule_delayed_work(&ts_buffer->dwork,
msecs_to_jiffies(ts_buffer->repetition_time));
}
@@ -1854,7 +1845,7 @@
}
ts_buffer->abort = 0;
- schedule_work(&ts_buffer->work);
+ schedule_delayed_work(&ts_buffer->dwork, 0);
}
static void dvb_dmxdev_cancel_ts_insertion(
@@ -1873,16 +1864,10 @@
return;
}
- /*
- * Work should be stopped first as it might re-trigger the timer
- * until it is stopped. Timer would not re-schedule the work
- * due to the abort flag.
- */
ts_buffer->abort = 1;
mutex_unlock(&ts_buffer->dmxdevfilter->mutex);
- cancel_work_sync(&ts_buffer->work);
- del_timer_sync(&ts_buffer->timer);
+ cancel_delayed_work_sync(&ts_buffer->dwork);
mutex_lock(&ts_buffer->dmxdevfilter->mutex);
}
@@ -1928,11 +1913,7 @@
ts_buffer->identifier = params->identifier;
ts_buffer->repetition_time = params->repetition_time;
ts_buffer->dmxdevfilter = dmxdevfilter;
- init_timer(&ts_buffer->timer);
- ts_buffer->timer.function = dvb_dmxdev_ts_insertion_timer;
- ts_buffer->timer.data = (unsigned long)ts_buffer;
- ts_buffer->timer.expires = 0xffffffffL;
- INIT_WORK(&ts_buffer->work, dvb_dmxdev_ts_insertion_work);
+ INIT_DELAYED_WORK(&ts_buffer->dwork, dvb_dmxdev_ts_insertion_work);
first_buffer = list_empty(&dmxdevfilter->insertion_buffers);
list_add_tail(&ts_buffer->next, &dmxdevfilter->insertion_buffers);
diff --git a/drivers/media/dvb/dvb-core/dmxdev.h b/drivers/media/dvb/dvb-core/dmxdev.h
index d8cd982..49e5e1b 100644
--- a/drivers/media/dvb/dvb-core/dmxdev.h
+++ b/drivers/media/dvb/dvb-core/dmxdev.h
@@ -117,7 +117,7 @@
#define DMX_MIN_INSERTION_REPETITION_TIME 25 /* in msec */
struct ts_insertion_buffer {
/* work scheduled for insertion of this buffer */
- struct work_struct work;
+ struct delayed_work dwork;
struct list_head next;
@@ -133,9 +133,6 @@
/* repetition time for the buffer insertion */
u32 repetition_time;
- /* timer used for insertion of the buffer */
- struct timer_list timer;
-
/* the recording filter to which this buffer belongs */
struct dmxdev_filter *dmxdevfilter;
diff --git a/drivers/media/dvb/dvb-core/dvb_demux.c b/drivers/media/dvb/dvb-core/dvb_demux.c
index 939d591..9844c64 100644
--- a/drivers/media/dvb/dvb-core/dvb_demux.c
+++ b/drivers/media/dvb/dvb-core/dvb_demux.c
@@ -437,6 +437,27 @@
}
EXPORT_SYMBOL(dvb_dmx_video_pattern_search);
+static int dvb_dmx_check_pes_end(struct dvb_demux_feed *feed)
+{
+ struct dmx_data_ready data;
+
+ if (!feed->pusi_seen)
+ return 0;
+
+ data.status = DMX_OK_PES_END;
+ data.data_length = 0;
+ data.pes_end.start_gap = 0;
+ data.pes_end.actual_length = feed->peslen;
+ data.pes_end.disc_indicator_set = 0;
+ data.pes_end.pes_length_mismatch = 0;
+ data.pes_end.stc = 0;
+ data.pes_end.tei_counter = feed->pes_tei_counter;
+ data.pes_end.cont_err_counter = feed->pes_cont_err_counter;
+ data.pes_end.ts_packets_num = feed->pes_ts_packets_num;
+
+ return feed->data_ready_cb.ts(&feed->feed.ts, &data);
+}
+
static inline int dvb_dmx_swfilter_payload(struct dvb_demux_feed *feed,
const u8 *buf)
{
@@ -444,7 +465,6 @@
int p;
int ccok;
u8 cc;
- struct dmx_data_ready data;
if (count == 0)
return -1;
@@ -462,24 +482,7 @@
/* PUSI ? */
if (buf[1] & 0x40) {
- if (feed->pusi_seen) {
- /* We had seen PUSI before, this means
- * that previous PES can be closed now.
- */
- data.status = DMX_OK_PES_END;
- data.data_length = 0;
- data.pes_end.start_gap = 0;
- data.pes_end.actual_length = feed->peslen;
- data.pes_end.disc_indicator_set = 0;
- data.pes_end.pes_length_mismatch = 0;
- data.pes_end.stc = 0;
- data.pes_end.tei_counter = feed->pes_tei_counter;
- data.pes_end.cont_err_counter =
- feed->pes_cont_err_counter;
- data.pes_end.ts_packets_num = feed->pes_ts_packets_num;
- feed->data_ready_cb.ts(&feed->feed.ts, &data);
- }
-
+ dvb_dmx_check_pes_end(feed);
feed->pusi_seen = 1;
feed->peslen = 0;
feed->pes_tei_counter = 0;
@@ -2250,7 +2253,9 @@
struct dvb_demux_feed *feed = (struct dvb_demux_feed *)ts_feed;
struct dmx_data_ready data;
struct dvb_demux *dvbdmx = feed->demux;
- int ret;
+ int ret = 0;
+ int secure_non_rec = feed->secure_mode.is_secured &&
+ !dvb_dmx_is_rec_feed(feed);
mutex_lock(&dvbdmx->mutex);
@@ -2259,13 +2264,14 @@
return -EINVAL;
}
- /* Decoder feeds are handled by plug-in */
- if (feed->ts_type & TS_DECODER) {
+ /* Decoder & non-recording secure feeds are handled by plug-in */
+ if ((feed->ts_type & TS_DECODER) || secure_non_rec) {
if (feed->demux->oob_command)
ret = feed->demux->oob_command(feed, cmd);
- else
- ret = 0;
+ }
+ if (!(feed->ts_type & (TS_PAYLOAD_ONLY | TS_PACKET)) ||
+ secure_non_rec) {
mutex_unlock(&dvbdmx->mutex);
return ret;
}
@@ -2274,44 +2280,9 @@
switch (cmd->type) {
case DMX_OOB_CMD_EOS:
- if (feed->ts_type & TS_PAYLOAD_ONLY) {
- if (feed->secure_mode.is_secured) {
- /* Secure feeds are handled by plug-in */
- if (feed->demux->oob_command)
- ret = feed->demux->oob_command(feed,
- cmd);
- else
- ret = 0;
- break;
- }
+ if (feed->ts_type & TS_PAYLOAD_ONLY)
+ dvb_dmx_check_pes_end(feed);
- /* Close last PES on non-secure feeds */
- if (feed->pusi_seen) {
- data.status = DMX_OK_PES_END;
- data.pes_end.start_gap = 0;
- data.pes_end.actual_length =
- feed->peslen;
- data.pes_end.disc_indicator_set = 0;
- data.pes_end.pes_length_mismatch = 0;
- data.pes_end.stc = 0;
- data.pes_end.tei_counter =
- feed->pes_tei_counter;
- data.pes_end.cont_err_counter =
- feed->pes_cont_err_counter;
- data.pes_end.ts_packets_num =
- feed->pes_ts_packets_num;
-
- feed->peslen = 0;
- feed->pes_tei_counter = 0;
- feed->pes_ts_packets_num = 0;
- feed->pes_cont_err_counter = 0;
-
- ret = feed->data_ready_cb.ts(&feed->feed.ts,
- &data);
- if (ret)
- break;
- }
- }
data.status = DMX_OK_EOS;
ret = feed->data_ready_cb.ts(&feed->feed.ts, &data);
break;
diff --git a/drivers/media/platform/msm/camera_v2/Kconfig b/drivers/media/platform/msm/camera_v2/Kconfig
index d9552e2..52864ae 100644
--- a/drivers/media/platform/msm/camera_v2/Kconfig
+++ b/drivers/media/platform/msm/camera_v2/Kconfig
@@ -153,3 +153,8 @@
This module serves as the common driver
for the JPEG 1.0 encoder and decoder.
+config MSM_GEMINI
+ tristate "Qualcomm MSM Gemini JPEG engine support"
+ depends on MSMB_CAMERA && (ARCH_MSM7X30 || ARCH_MSM8X60 || ARCH_MSM8960)
+ ---help---
+ Enables support for the Gemini JPEG encoder engine for 8x60.
diff --git a/drivers/media/platform/msm/camera_v2/Makefile b/drivers/media/platform/msm/camera_v2/Makefile
index a1c5ea5..02eb3dd 100644
--- a/drivers/media/platform/msm/camera_v2/Makefile
+++ b/drivers/media/platform/msm/camera_v2/Makefile
@@ -16,3 +16,4 @@
obj-$(CONFIG_MSMB_JPEG) += jpeg_10/
obj-$(CONFIG_MSMB_CAMERA) += msm_buf_mgr/
obj-$(CONFIG_MSMB_CAMERA) += pproc/
+obj-$(CONFIG_MSMB_CAMERA) += gemini/
diff --git a/drivers/media/platform/msm/camera_v2/gemini/Makefile b/drivers/media/platform/msm/camera_v2/gemini/Makefile
new file mode 100644
index 0000000..74d7294
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/gemini/Makefile
@@ -0,0 +1,5 @@
+GCC_VERSION := $(shell $(CONFIG_SHELL) $(PWD)/scripts/gcc-version.sh $(CROSS_COMPILE)gcc)
+ccflags-y += -Idrivers/media/video/msm
+ccflags-y += -Idrivers/media/platform/msm/camera_v2/sensor/io
+
+obj-$(CONFIG_MSM_GEMINI) += msm_gemini_dev.o msm_gemini_sync.o msm_gemini_core.o msm_gemini_hw.o msm_gemini_platform.o
diff --git a/drivers/media/platform/msm/camera_v2/gemini/msm_gemini_common.h b/drivers/media/platform/msm/camera_v2/gemini/msm_gemini_common.h
new file mode 100644
index 0000000..eefad6d
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/gemini/msm_gemini_common.h
@@ -0,0 +1,39 @@
+/* Copyright (c) 2010,2013, 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_GEMINI_COMMON_H
+#define MSM_GEMINI_COMMON_H
+
+#define MSM_GEMINI_DEBUG
+#ifdef MSM_GEMINI_DEBUG
+#define GMN_DBG(fmt, args...) pr_debug(fmt, ##args)
+#else
+#define GMN_DBG(fmt, args...) do { } while (0)
+#endif
+
+#define GMN_PR_ERR pr_err
+
+enum GEMINI_MODE {
+ GEMINI_MODE_DISABLE,
+ GEMINI_MODE_OFFLINE,
+ GEMINI_MODE_REALTIME,
+ GEMINI_MODE_REALTIME_ROTATION
+};
+
+enum GEMINI_ROTATION {
+ GEMINI_ROTATION_0,
+ GEMINI_ROTATION_90,
+ GEMINI_ROTATION_180,
+ GEMINI_ROTATION_270
+};
+
+#endif /* MSM_GEMINI_COMMON_H */
diff --git a/drivers/media/platform/msm/camera_v2/gemini/msm_gemini_core.c b/drivers/media/platform/msm/camera_v2/gemini/msm_gemini_core.c
new file mode 100644
index 0000000..88fa9e7
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/gemini/msm_gemini_core.c
@@ -0,0 +1,250 @@
+/* Copyright (c) 2010-2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/sched.h>
+#include "msm_gemini_hw.h"
+#include "msm_gemini_core.h"
+#include "msm_gemini_platform.h"
+#include "msm_gemini_common.h"
+
+static struct msm_gemini_hw_pingpong fe_pingpong_buf;
+static struct msm_gemini_hw_pingpong we_pingpong_buf;
+static int we_pingpong_index;
+static int reset_done_ack;
+static spinlock_t reset_lock;
+static wait_queue_head_t reset_wait;
+
+int msm_gemini_core_reset(uint8_t op_mode, void *base, int size)
+{
+ unsigned long flags;
+ int rc = 0;
+ int tm = 500;
+ memset(&fe_pingpong_buf, 0, sizeof(fe_pingpong_buf));
+ fe_pingpong_buf.is_fe = 1;
+ we_pingpong_index = 0;
+ memset(&we_pingpong_buf, 0, sizeof(we_pingpong_buf));
+ spin_lock_irqsave(&reset_lock, flags);
+ reset_done_ack = 0;
+ msm_gemini_hw_reset(base, size);
+ spin_unlock_irqrestore(&reset_lock, flags);
+ rc = wait_event_interruptible_timeout(
+ reset_wait,
+ reset_done_ack,
+ msecs_to_jiffies(tm));
+
+ if (!reset_done_ack) {
+ GMN_DBG("%s: reset ACK failed %d", __func__, rc);
+ return -EBUSY;
+ }
+
+ GMN_DBG("%s: reset_done_ack rc %d", __func__, rc);
+ spin_lock_irqsave(&reset_lock, flags);
+ reset_done_ack = 0;
+ spin_unlock_irqrestore(&reset_lock, flags);
+
+ if (op_mode == MSM_GEMINI_MODE_REALTIME_ENCODE) {
+ /* Nothing needed for fe buffer cfg, config we only */
+ msm_gemini_hw_we_buffer_cfg(1);
+ } else {
+ /* Nothing needed for fe buffer cfg, config we only */
+ msm_gemini_hw_we_buffer_cfg(0);
+ }
+
+ /* @todo wait for reset done irq */
+
+ return 0;
+}
+
+void msm_gemini_core_release(int release_buf)
+{
+ int i = 0;
+ for (i = 0; i < 2; i++) {
+ if (we_pingpong_buf.buf_status[i] && release_buf)
+ msm_gemini_platform_p2v(we_pingpong_buf.buf[i].file,
+ &we_pingpong_buf.buf[i].handle);
+ we_pingpong_buf.buf_status[i] = 0;
+ }
+}
+
+void msm_gemini_core_init(void)
+{
+ init_waitqueue_head(&reset_wait);
+ spin_lock_init(&reset_lock);
+}
+
+int msm_gemini_core_fe_start(void)
+{
+ msm_gemini_hw_fe_start();
+ return 0;
+}
+
+/* fetch engine */
+int msm_gemini_core_fe_buf_update(struct msm_gemini_core_buf *buf)
+{
+ GMN_DBG("%s:%d] 0x%08x %d 0x%08x %d\n", __func__, __LINE__,
+ (int) buf->y_buffer_addr, buf->y_len,
+ (int) buf->cbcr_buffer_addr, buf->cbcr_len);
+ return msm_gemini_hw_pingpong_update(&fe_pingpong_buf, buf);
+}
+
+void *msm_gemini_core_fe_pingpong_irq(int gemini_irq_status, void *context)
+{
+ return msm_gemini_hw_pingpong_irq(&fe_pingpong_buf);
+}
+
+/* write engine */
+int msm_gemini_core_we_buf_update(struct msm_gemini_core_buf *buf)
+{
+ int rc;
+ GMN_DBG("%s:%d] 0x%08x 0x%08x %d\n", __func__, __LINE__,
+ (int) buf->y_buffer_addr, (int) buf->cbcr_buffer_addr,
+ buf->y_len);
+ we_pingpong_buf.buf_status[we_pingpong_index] = 0;
+ we_pingpong_index = (we_pingpong_index + 1)%2;
+ rc = msm_gemini_hw_pingpong_update(&we_pingpong_buf, buf);
+ return 0;
+}
+
+int msm_gemini_core_we_buf_reset(struct msm_gemini_hw_buf *buf)
+{
+ int i;
+ for (i = 0; i < 2; i++) {
+ if (we_pingpong_buf.buf[i].y_buffer_addr
+ == buf->y_buffer_addr)
+ we_pingpong_buf.buf_status[i] = 0;
+ }
+ return 0;
+}
+
+void *msm_gemini_core_we_pingpong_irq(int gemini_irq_status, void *context)
+{
+ GMN_DBG("%s:%d]\n", __func__, __LINE__);
+
+ return msm_gemini_hw_pingpong_irq(&we_pingpong_buf);
+}
+
+void *msm_gemini_core_framedone_irq(int gemini_irq_status, void *context)
+{
+ struct msm_gemini_hw_buf *buf_p;
+
+ GMN_DBG("%s:%d]\n", __func__, __LINE__);
+
+ buf_p = msm_gemini_hw_pingpong_active_buffer(&we_pingpong_buf);
+ if (buf_p) {
+ buf_p->framedone_len = msm_gemini_hw_encode_output_size();
+ GMN_DBG("%s:%d] framedone_len %d\n", __func__, __LINE__,
+ buf_p->framedone_len);
+ }
+
+ return buf_p;
+}
+
+void *msm_gemini_core_reset_ack_irq(int gemini_irq_status, void *context)
+{
+ /* @todo return the status back to msm_gemini_core_reset */
+ GMN_DBG("%s:%d]\n", __func__, __LINE__);
+ return NULL;
+}
+
+void *msm_gemini_core_err_irq(int gemini_irq_status, void *context)
+{
+ GMN_PR_ERR("%s:%d]\n", __func__, gemini_irq_status);
+ return NULL;
+}
+
+static int (*msm_gemini_irq_handler) (int, void *, void *);
+
+irqreturn_t msm_gemini_core_irq(int irq_num, void *context)
+{
+ void *data = NULL;
+ unsigned long flags;
+ int gemini_irq_status;
+
+ GMN_DBG("%s:%d] irq_num = %d\n", __func__, __LINE__, irq_num);
+
+ spin_lock_irqsave(&reset_lock, flags);
+ reset_done_ack = 1;
+ spin_unlock_irqrestore(&reset_lock, flags);
+ gemini_irq_status = msm_gemini_hw_irq_get_status();
+
+ GMN_DBG("%s:%d] gemini_irq_status = %0x\n", __func__, __LINE__,
+ gemini_irq_status);
+
+ /* For reset and framedone IRQs, clear all bits */
+ if (gemini_irq_status & 0x400) {
+ wake_up(&reset_wait);
+ msm_gemini_hw_irq_clear(HWIO_JPEG_IRQ_CLEAR_RMSK,
+ JPEG_IRQ_CLEAR_ALL);
+ } else if (gemini_irq_status & 0x1) {
+ msm_gemini_hw_irq_clear(HWIO_JPEG_IRQ_CLEAR_RMSK,
+ JPEG_IRQ_CLEAR_ALL);
+ } else {
+ msm_gemini_hw_irq_clear(HWIO_JPEG_IRQ_CLEAR_RMSK,
+ gemini_irq_status);
+ }
+
+ if (msm_gemini_hw_irq_is_frame_done(gemini_irq_status)) {
+ data = msm_gemini_core_framedone_irq(gemini_irq_status,
+ context);
+ if (msm_gemini_irq_handler)
+ msm_gemini_irq_handler(
+ MSM_GEMINI_HW_MASK_COMP_FRAMEDONE,
+ context, data);
+ }
+
+ if (msm_gemini_hw_irq_is_fe_pingpong(gemini_irq_status)) {
+ data = msm_gemini_core_fe_pingpong_irq(gemini_irq_status,
+ context);
+ if (msm_gemini_irq_handler)
+ msm_gemini_irq_handler(MSM_GEMINI_HW_MASK_COMP_FE,
+ context, data);
+ }
+
+ if (msm_gemini_hw_irq_is_we_pingpong(gemini_irq_status) &&
+ !msm_gemini_hw_irq_is_frame_done(gemini_irq_status)) {
+ data = msm_gemini_core_we_pingpong_irq(gemini_irq_status,
+ context);
+ if (msm_gemini_irq_handler)
+ msm_gemini_irq_handler(MSM_GEMINI_HW_MASK_COMP_WE,
+ context, data);
+ }
+
+ if (msm_gemini_hw_irq_is_reset_ack(gemini_irq_status)) {
+ data = msm_gemini_core_reset_ack_irq(gemini_irq_status,
+ context);
+ if (msm_gemini_irq_handler)
+ msm_gemini_irq_handler(
+ MSM_GEMINI_HW_MASK_COMP_RESET_ACK,
+ context, data);
+ }
+
+ /* Unexpected/unintended HW interrupt */
+ if (msm_gemini_hw_irq_is_err(gemini_irq_status)) {
+ data = msm_gemini_core_err_irq(gemini_irq_status, context);
+ if (msm_gemini_irq_handler)
+ msm_gemini_irq_handler(MSM_GEMINI_HW_MASK_COMP_ERR,
+ context, data);
+ }
+
+ return IRQ_HANDLED;
+}
+
+void msm_gemini_core_irq_install(int (*irq_handler) (int, void *, void *))
+{
+ msm_gemini_irq_handler = irq_handler;
+}
+
+void msm_gemini_core_irq_remove(void)
+{
+ msm_gemini_irq_handler = NULL;
+}
diff --git a/drivers/media/platform/msm/camera_v2/gemini/msm_gemini_core.h b/drivers/media/platform/msm/camera_v2/gemini/msm_gemini_core.h
new file mode 100644
index 0000000..3aac25a
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/gemini/msm_gemini_core.h
@@ -0,0 +1,35 @@
+/* Copyright (c) 2010,2013, 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_GEMINI_CORE_H
+#define MSM_GEMINI_CORE_H
+
+#include <linux/interrupt.h>
+#include "msm_gemini_hw.h"
+
+#define msm_gemini_core_buf msm_gemini_hw_buf
+
+irqreturn_t msm_gemini_core_irq(int irq_num, void *context);
+
+void msm_gemini_core_irq_install(int (*irq_handler) (int, void *, void *));
+void msm_gemini_core_irq_remove(void);
+
+int msm_gemini_core_fe_buf_update(struct msm_gemini_core_buf *buf);
+int msm_gemini_core_we_buf_update(struct msm_gemini_core_buf *buf);
+int msm_gemini_core_we_buf_reset(struct msm_gemini_hw_buf *buf);
+
+int msm_gemini_core_reset(uint8_t op_mode, void *base, int size);
+int msm_gemini_core_fe_start(void);
+
+void msm_gemini_core_release(int);
+void msm_gemini_core_init(void);
+#endif /* MSM_GEMINI_CORE_H */
diff --git a/drivers/media/platform/msm/camera_v2/gemini/msm_gemini_dev.c b/drivers/media/platform/msm/camera_v2/gemini/msm_gemini_dev.c
new file mode 100644
index 0000000..13c1e11
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/gemini/msm_gemini_dev.c
@@ -0,0 +1,265 @@
+/* Copyright (c) 2010-2011,2013 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/uaccess.h>
+#include <media/msm_gemini.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+#include <mach/board.h>
+#include "../msm.h"
+#include "msm_gemini_sync.h"
+#include "msm_gemini_common.h"
+
+#define MSM_GEMINI_NAME "gemini"
+#define MSM_GEMINI_DRV_NAME "msm_gemini"
+
+static int msm_gemini_open(struct inode *inode, struct file *filp)
+{
+ int rc;
+
+ struct msm_gemini_device *pgmn_dev = container_of(inode->i_cdev,
+ struct msm_gemini_device, cdev);
+ filp->private_data = pgmn_dev;
+
+ GMN_DBG("%s:%d]\n", __func__, __LINE__);
+
+ rc = __msm_gemini_open(pgmn_dev);
+
+ GMN_DBG("%s:%d] %s open_count = %d\n", __func__, __LINE__,
+ filp->f_path.dentry->d_name.name, pgmn_dev->open_count);
+
+ return rc;
+}
+
+static int msm_gemini_release(struct inode *inode, struct file *filp)
+{
+ int rc;
+
+ struct msm_gemini_device *pgmn_dev = filp->private_data;
+
+ GMN_DBG("%s:%d]\n", __func__, __LINE__);
+
+ rc = __msm_gemini_release(pgmn_dev);
+
+ GMN_DBG("%s:%d] %s open_count = %d\n", __func__, __LINE__,
+ filp->f_path.dentry->d_name.name, pgmn_dev->open_count);
+ return rc;
+}
+
+static long msm_gemini_ioctl(struct file *filp, unsigned int cmd,
+ unsigned long arg)
+{
+ int rc;
+ struct msm_gemini_device *pgmn_dev = filp->private_data;
+
+ GMN_DBG("%s:%d] cmd=%d pgmn_dev=0x%x arg=0x%x\n", __func__,
+ __LINE__, _IOC_NR(cmd), (uint32_t)pgmn_dev, (uint32_t)arg);
+
+ rc = __msm_gemini_ioctl(pgmn_dev, cmd, arg);
+
+ GMN_DBG("%s:%d]\n", __func__, __LINE__);
+ return rc;
+}
+
+static const struct file_operations msm_gemini_fops = {
+ .owner = THIS_MODULE,
+ .open = msm_gemini_open,
+ .release = msm_gemini_release,
+ .unlocked_ioctl = msm_gemini_ioctl,
+};
+
+static struct class *msm_gemini_class;
+static dev_t msm_gemini_devno;
+static struct msm_gemini_device *msm_gemini_device_p;
+
+int msm_gemini_subdev_init(struct v4l2_subdev *gemini_sd)
+{
+ int rc;
+ struct msm_gemini_device *pgmn_dev =
+ (struct msm_gemini_device *)gemini_sd->host_priv;
+
+ GMN_DBG("%s:%d: gemini_sd=0x%x pgmn_dev=0x%x\n",
+ __func__, __LINE__, (uint32_t)gemini_sd, (uint32_t)pgmn_dev);
+ rc = __msm_gemini_open(pgmn_dev);
+ GMN_DBG("%s:%d: rc=%d\n",
+ __func__, __LINE__, rc);
+ return rc;
+}
+
+static long msm_gemini_subdev_ioctl(struct v4l2_subdev *sd,
+ unsigned int cmd, void *arg)
+{
+ long rc;
+ struct msm_gemini_device *pgmn_dev =
+ (struct msm_gemini_device *)sd->host_priv;
+
+ GMN_DBG("%s: cmd=%d\n", __func__, cmd);
+
+ GMN_DBG("%s: pgmn_dev 0x%x", __func__, (uint32_t)pgmn_dev);
+
+ GMN_DBG("%s: Calling __msm_gemini_ioctl\n", __func__);
+
+ rc = __msm_gemini_ioctl(pgmn_dev, cmd, (unsigned long)arg);
+ GMN_DBG("%s: X\n", __func__);
+ return rc;
+}
+
+void msm_gemini_subdev_release(struct v4l2_subdev *gemini_sd)
+{
+ int rc;
+ struct msm_gemini_device *pgmn_dev =
+ (struct msm_gemini_device *)gemini_sd->host_priv;
+ GMN_DBG("%s:pgmn_dev=0x%x", __func__, (uint32_t)pgmn_dev);
+ rc = __msm_gemini_release(pgmn_dev);
+ GMN_DBG("%s:rc=%d", __func__, rc);
+}
+
+static const struct v4l2_subdev_core_ops msm_gemini_subdev_core_ops = {
+ .ioctl = msm_gemini_subdev_ioctl,
+};
+
+static const struct v4l2_subdev_ops msm_gemini_subdev_ops = {
+ .core = &msm_gemini_subdev_core_ops,
+};
+
+static int msm_gemini_init(struct platform_device *pdev)
+{
+ int rc = -1;
+ struct device *dev;
+
+ GMN_DBG("%s:\n", __func__);
+ msm_gemini_device_p = __msm_gemini_init(pdev);
+ if (msm_gemini_device_p == NULL) {
+ GMN_PR_ERR("%s: initialization failed\n", __func__);
+ goto fail;
+ }
+
+ v4l2_subdev_init(&msm_gemini_device_p->subdev, &msm_gemini_subdev_ops);
+ v4l2_set_subdev_hostdata(&msm_gemini_device_p->subdev,
+ msm_gemini_device_p);
+ GMN_DBG("%s: msm_gemini_device_p 0x%x", __func__,
+ (uint32_t)msm_gemini_device_p);
+ GMN_DBG("%s:gemini: platform_set_drvdata\n", __func__);
+ platform_set_drvdata(pdev, &msm_gemini_device_p->subdev);
+
+ rc = alloc_chrdev_region(&msm_gemini_devno, 0, 1, MSM_GEMINI_NAME);
+ if (rc < 0) {
+ GMN_PR_ERR("%s: failed to allocate chrdev\n", __func__);
+ goto fail_1;
+ }
+
+ if (!msm_gemini_class) {
+ msm_gemini_class = class_create(THIS_MODULE, MSM_GEMINI_NAME);
+ if (IS_ERR(msm_gemini_class)) {
+ rc = PTR_ERR(msm_gemini_class);
+ GMN_PR_ERR("%s: create device class failed\n",
+ __func__);
+ goto fail_2;
+ }
+ }
+
+ dev = device_create(msm_gemini_class, NULL,
+ MKDEV(MAJOR(msm_gemini_devno), MINOR(msm_gemini_devno)), NULL,
+ "%s%d", MSM_GEMINI_NAME, 0);
+
+ if (IS_ERR(dev)) {
+ GMN_PR_ERR("%s: error creating device\n", __func__);
+ rc = -ENODEV;
+ goto fail_3;
+ }
+
+ cdev_init(&msm_gemini_device_p->cdev, &msm_gemini_fops);
+ msm_gemini_device_p->cdev.owner = THIS_MODULE;
+ msm_gemini_device_p->cdev.ops =
+ (const struct file_operations *) &msm_gemini_fops;
+ rc = cdev_add(&msm_gemini_device_p->cdev, msm_gemini_devno, 1);
+ if (rc < 0) {
+ GMN_PR_ERR("%s: error adding cdev\n", __func__);
+ rc = -ENODEV;
+ goto fail_4;
+ }
+
+ GMN_DBG("%s %s: success\n", __func__, MSM_GEMINI_NAME);
+
+ return rc;
+
+fail_4:
+ device_destroy(msm_gemini_class, msm_gemini_devno);
+
+fail_3:
+ class_destroy(msm_gemini_class);
+
+fail_2:
+ unregister_chrdev_region(msm_gemini_devno, 1);
+
+fail_1:
+ __msm_gemini_exit(msm_gemini_device_p);
+
+fail:
+ return rc;
+}
+
+static void msm_gemini_exit(void)
+{
+ cdev_del(&msm_gemini_device_p->cdev);
+ device_destroy(msm_gemini_class, msm_gemini_devno);
+ class_destroy(msm_gemini_class);
+ unregister_chrdev_region(msm_gemini_devno, 1);
+
+ __msm_gemini_exit(msm_gemini_device_p);
+}
+
+static int __msm_gemini_probe(struct platform_device *pdev)
+{
+ return msm_gemini_init(pdev);
+}
+
+static int __msm_gemini_remove(struct platform_device *pdev)
+{
+ msm_gemini_exit();
+ return 0;
+}
+
+static struct platform_driver msm_gemini_driver = {
+ .probe = __msm_gemini_probe,
+ .remove = __msm_gemini_remove,
+ .driver = {
+ .name = MSM_GEMINI_DRV_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init msm_gemini_driver_init(void)
+{
+ int rc;
+ rc = platform_driver_register(&msm_gemini_driver);
+ return rc;
+}
+
+static void __exit msm_gemini_driver_exit(void)
+{
+ platform_driver_unregister(&msm_gemini_driver);
+}
+
+MODULE_DESCRIPTION("MSM Gemini JPEG driver");
+MODULE_VERSION("msm gemini 0.1");
+
+module_init(msm_gemini_driver_init);
+module_exit(msm_gemini_driver_exit);
+
diff --git a/drivers/media/platform/msm/camera_v2/gemini/msm_gemini_hw.c b/drivers/media/platform/msm/camera_v2/gemini/msm_gemini_hw.c
new file mode 100644
index 0000000..96470fd
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/gemini/msm_gemini_hw.c
@@ -0,0 +1,520 @@
+/* Copyright (c) 2010,2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include "msm_gemini_hw.h"
+#include "msm_gemini_common.h"
+
+
+static void *gemini_region_base;
+static uint32_t gemini_region_size;
+
+int msm_gemini_hw_pingpong_update(struct msm_gemini_hw_pingpong *pingpong_hw,
+ struct msm_gemini_hw_buf *buf)
+{
+ int buf_free_index = -1;
+
+ if (!pingpong_hw->buf_status[0])
+ buf_free_index = 0;
+ else if (!pingpong_hw->buf_status[1])
+ buf_free_index = 1;
+ else {
+ GMN_PR_ERR("%s:%d: pingpong buffer busy\n", __func__, __LINE__);
+ return -EBUSY;
+ }
+
+ pingpong_hw->buf[buf_free_index] = *buf;
+ pingpong_hw->buf_status[buf_free_index] = 1;
+
+ if (pingpong_hw->is_fe)
+ msm_gemini_hw_fe_buffer_update(
+ &pingpong_hw->buf[buf_free_index], buf_free_index);
+ else
+ msm_gemini_hw_we_buffer_update(
+ &pingpong_hw->buf[buf_free_index], buf_free_index);
+ return 0;
+}
+
+void *msm_gemini_hw_pingpong_irq(struct msm_gemini_hw_pingpong *pingpong_hw)
+{
+ struct msm_gemini_hw_buf *buf_p = NULL;
+
+ if (pingpong_hw->buf_status[pingpong_hw->buf_active_index]) {
+ buf_p = &pingpong_hw->buf[pingpong_hw->buf_active_index];
+ pingpong_hw->buf_status[pingpong_hw->buf_active_index] = 0;
+ }
+
+ pingpong_hw->buf_active_index = !pingpong_hw->buf_active_index;
+
+ return (void *) buf_p;
+}
+
+void *msm_gemini_hw_pingpong_active_buffer(
+ struct msm_gemini_hw_pingpong *pingpong_hw)
+{
+ struct msm_gemini_hw_buf *buf_p = NULL;
+
+ if (pingpong_hw->buf_status[pingpong_hw->buf_active_index])
+ buf_p = &pingpong_hw->buf[pingpong_hw->buf_active_index];
+
+ return (void *) buf_p;
+}
+
+struct msm_gemini_hw_cmd hw_cmd_irq_get_status[] = {
+ /* type, repeat n times, offset, mask, data or pdata */
+ {MSM_GEMINI_HW_CMD_TYPE_READ, 1, HWIO_JPEG_IRQ_STATUS_ADDR,
+ HWIO_JPEG_IRQ_STATUS_RMSK, {0} },
+};
+
+int msm_gemini_hw_irq_get_status(void)
+{
+ uint32_t n_irq_status = 0;
+ n_irq_status = msm_gemini_hw_read(&hw_cmd_irq_get_status[0]);
+ return n_irq_status;
+}
+
+struct msm_gemini_hw_cmd hw_cmd_encode_output_size[] = {
+ /* type, repeat n times, offset, mask, data or pdata */
+ {MSM_GEMINI_HW_CMD_TYPE_READ, 1,
+ HWIO_JPEG_STATUS_ENCODE_OUTPUT_SIZE_ADDR,
+ HWIO_JPEG_STATUS_ENCODE_OUTPUT_SIZE_RMSK, {0} },
+};
+
+long msm_gemini_hw_encode_output_size(void)
+{
+ long encode_output_size;
+
+ encode_output_size = msm_gemini_hw_read(&hw_cmd_encode_output_size[0]);
+
+ return encode_output_size;
+}
+
+struct msm_gemini_hw_cmd hw_cmd_irq_clear[] = {
+ /* type, repeat n times, offset, mask, data or pdata */
+ {MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_IRQ_CLEAR_ADDR,
+ HWIO_JPEG_IRQ_CLEAR_RMSK, {JPEG_IRQ_CLEAR_ALL} },
+};
+
+void msm_gemini_hw_irq_clear(uint32_t mask, uint32_t data)
+{
+ GMN_DBG("%s:%d] mask %0x data %0x", __func__, __LINE__, mask, data);
+ hw_cmd_irq_clear[0].mask = mask;
+ hw_cmd_irq_clear[0].data = data;
+ msm_gemini_hw_write(&hw_cmd_irq_clear[0]);
+}
+
+struct msm_gemini_hw_cmd hw_cmd_fe_ping_update[] = {
+ /* type, repeat n times, offset, mask, data or pdata */
+ {MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_FE_BUFFER_CFG_ADDR,
+ HWIO_JPEG_FE_BUFFER_CFG_RMSK, {0} },
+ {MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_FE_Y_PING_ADDR_ADDR,
+ HWIO_JPEG_FE_Y_PING_ADDR_RMSK, {0} },
+ {MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_FE_CBCR_PING_ADDR_ADDR,
+ HWIO_JPEG_FE_CBCR_PING_ADDR_RMSK, {0} },
+ {MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_FE_CMD_ADDR,
+ HWIO_JPEG_FE_CMD_RMSK, {JPEG_FE_CMD_BUFFERRELOAD} },
+};
+
+struct msm_gemini_hw_cmd hw_cmd_fe_pong_update[] = {
+ /* type, repeat n times, offset, mask, data or pdata */
+ {MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_FE_BUFFER_CFG_ADDR,
+ HWIO_JPEG_FE_BUFFER_CFG_RMSK, {0} },
+ {MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_FE_Y_PONG_ADDR_ADDR,
+ HWIO_JPEG_FE_Y_PONG_ADDR_RMSK, {0} },
+ {MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_FE_CBCR_PONG_ADDR_ADDR,
+ HWIO_JPEG_FE_CBCR_PONG_ADDR_RMSK, {0} },
+ {MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_FE_CMD_ADDR,
+ HWIO_JPEG_FE_CMD_RMSK, {JPEG_FE_CMD_BUFFERRELOAD} },
+};
+
+void msm_gemini_hw_fe_buffer_update(struct msm_gemini_hw_buf *p_input,
+ uint8_t pingpong_index)
+{
+ uint32_t n_reg_val = 0;
+
+ struct msm_gemini_hw_cmd *hw_cmd_p;
+
+ if (pingpong_index == 0) {
+ hw_cmd_p = &hw_cmd_fe_ping_update[0];
+ n_reg_val = ((((p_input->num_of_mcu_rows - 1) <<
+ HWIO_JPEG_FE_BUFFER_CFG_CBCR_MCU_ROWS_SHFT) &
+ HWIO_JPEG_FE_BUFFER_CFG_CBCR_MCU_ROWS_BMSK) |
+ (((p_input->num_of_mcu_rows - 1) <<
+ HWIO_JPEG_FE_BUFFER_CFG_Y_MCU_ROWS_SHFT) &
+ HWIO_JPEG_FE_BUFFER_CFG_Y_MCU_ROWS_BMSK));
+ hw_cmd_p->data = n_reg_val;
+ msm_gemini_hw_write(hw_cmd_p++);
+
+ n_reg_val = ((p_input->y_buffer_addr <<
+ HWIO_JPEG_FE_Y_PING_ADDR_FE_Y_PING_START_ADDR_SHFT) &
+ HWIO_JPEG_FE_Y_PING_ADDR_FE_Y_PING_START_ADDR_BMSK);
+ hw_cmd_p->data = n_reg_val;
+ msm_gemini_hw_write(hw_cmd_p++);
+
+ n_reg_val = ((p_input->cbcr_buffer_addr<<
+ HWIO_JPEG_FE_CBCR_PING_ADDR_FE_CBCR_PING_START_ADDR_SHFT) &
+ HWIO_JPEG_FE_CBCR_PING_ADDR_FE_CBCR_PING_START_ADDR_BMSK);
+ hw_cmd_p->data = n_reg_val;
+ msm_gemini_hw_write(hw_cmd_p++);
+
+ msm_gemini_hw_write(hw_cmd_p);
+ } else if (pingpong_index == 1) {
+ hw_cmd_p = &hw_cmd_fe_pong_update[0];
+ n_reg_val = ((((p_input->num_of_mcu_rows - 1) <<
+ HWIO_JPEG_FE_BUFFER_CFG_CBCR_MCU_ROWS_SHFT) &
+ HWIO_JPEG_FE_BUFFER_CFG_CBCR_MCU_ROWS_BMSK) |
+ (((p_input->num_of_mcu_rows - 1) <<
+ HWIO_JPEG_FE_BUFFER_CFG_Y_MCU_ROWS_SHFT) &
+ HWIO_JPEG_FE_BUFFER_CFG_Y_MCU_ROWS_BMSK));
+ hw_cmd_p->data = n_reg_val;
+ msm_gemini_hw_write(hw_cmd_p++);
+
+ n_reg_val = ((p_input->y_buffer_addr <<
+ HWIO_JPEG_FE_Y_PONG_ADDR_FE_Y_PONG_START_ADDR_SHFT) &
+ HWIO_JPEG_FE_Y_PONG_ADDR_FE_Y_PONG_START_ADDR_BMSK);
+ hw_cmd_p->data = n_reg_val;
+ msm_gemini_hw_write(hw_cmd_p++);
+
+ n_reg_val = ((p_input->cbcr_buffer_addr<<
+ HWIO_JPEG_FE_CBCR_PONG_ADDR_FE_CBCR_PONG_START_ADDR_SHFT) &
+ HWIO_JPEG_FE_CBCR_PONG_ADDR_FE_CBCR_PONG_START_ADDR_BMSK);
+ hw_cmd_p->data = n_reg_val;
+ msm_gemini_hw_write(hw_cmd_p++);
+
+ msm_gemini_hw_write(hw_cmd_p);
+ } else {
+ /* shall not get to here */
+ }
+
+ return;
+}
+
+struct msm_gemini_hw_cmd hw_cmd_fe_start[] = {
+ /* type, repeat n times, offset, mask, data or pdata */
+ {MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_FE_CMD_ADDR,
+ HWIO_JPEG_FE_CMD_RMSK, {JPEG_OFFLINE_CMD_START} },
+};
+
+void msm_gemini_hw_fe_start(void)
+{
+ msm_gemini_hw_write(&hw_cmd_fe_start[0]);
+
+ return;
+}
+
+struct msm_gemini_hw_cmd hw_cmd_we_buffer_cfg[] = {
+ /* type, repeat n times, offset, mask, data or pdata */
+ {MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_WE_Y_THRESHOLD_ADDR,
+ HWIO_JPEG_WE_Y_THRESHOLD_RMSK, {0} },
+ {MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_WE_Y_UB_CFG_ADDR,
+ HWIO_JPEG_WE_Y_UB_CFG_RMSK, {JPEG_WE_YUB_ENCODE} },
+ {MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_WE_CBCR_THRESHOLD_ADDR,
+ HWIO_JPEG_WE_CBCR_THRESHOLD_RMSK, {0} },
+};
+
+/*
+ * first dimension is WE_ASSERT_STALL_TH and WE_DEASSERT_STALL_TH
+ * second dimension is for offline and real-time settings
+ */
+static const uint32_t GEMINI_WE_Y_THRESHOLD[2][2] = {
+ { 0x00000190, 0x000001ff },
+ { 0x0000016a, 0x000001ff }
+};
+
+/*
+ * first dimension is WE_ASSERT_STALL_TH and WE_DEASSERT_STALL_TH
+ * second dimension is for offline and real-time settings
+ */
+static const uint32_t GEMINI_WE_CBCR_THRESHOLD[2][2] = {
+ { 0x00000190, 0x000001ff },
+ { 0x0000016a, 0x000001ff }
+};
+
+void msm_gemini_hw_we_buffer_cfg(uint8_t is_realtime)
+{
+ uint32_t n_reg_val = 0;
+
+ struct msm_gemini_hw_cmd *hw_cmd_p = &hw_cmd_we_buffer_cfg[0];
+
+ n_reg_val = (((GEMINI_WE_Y_THRESHOLD[1][is_realtime] <<
+ HWIO_JPEG_WE_Y_THRESHOLD_WE_DEASSERT_STALL_TH_SHFT) &
+ HWIO_JPEG_WE_Y_THRESHOLD_WE_DEASSERT_STALL_TH_BMSK) |
+ ((GEMINI_WE_Y_THRESHOLD[0][is_realtime] <<
+ HWIO_JPEG_WE_Y_THRESHOLD_WE_ASSERT_STALL_TH_SHFT) &
+ HWIO_JPEG_WE_Y_THRESHOLD_WE_ASSERT_STALL_TH_BMSK));
+ hw_cmd_p->data = n_reg_val;
+ msm_gemini_hw_write(hw_cmd_p++);
+
+ msm_gemini_hw_write(hw_cmd_p++);
+
+ /* @todo maybe not for realtime? */
+ n_reg_val = (((GEMINI_WE_CBCR_THRESHOLD[1][is_realtime] <<
+ HWIO_JPEG_WE_CBCR_THRESHOLD_WE_DEASSERT_STALL_TH_SHFT) &
+ HWIO_JPEG_WE_CBCR_THRESHOLD_WE_DEASSERT_STALL_TH_BMSK) |
+ ((GEMINI_WE_CBCR_THRESHOLD[0][is_realtime] <<
+ HWIO_JPEG_WE_CBCR_THRESHOLD_WE_ASSERT_STALL_TH_SHFT) &
+ HWIO_JPEG_WE_CBCR_THRESHOLD_WE_ASSERT_STALL_TH_BMSK));
+ hw_cmd_p->data = n_reg_val;
+ msm_gemini_hw_write(hw_cmd_p);
+
+ return;
+}
+
+struct msm_gemini_hw_cmd hw_cmd_we_ping_update[] = {
+ /* type, repeat n times, offset, mask, data or pdata */
+ {MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_WE_Y_PING_BUFFER_CFG_ADDR,
+ HWIO_JPEG_WE_Y_PING_BUFFER_CFG_RMSK, {0} },
+ {MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_WE_Y_PING_ADDR_ADDR,
+ HWIO_JPEG_WE_Y_PING_ADDR_RMSK, {0} },
+};
+
+struct msm_gemini_hw_cmd hw_cmd_we_pong_update[] = {
+ /* type, repeat n times, offset, mask, data or pdata */
+ {MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_WE_Y_PONG_BUFFER_CFG_ADDR,
+ HWIO_JPEG_WE_Y_PONG_BUFFER_CFG_RMSK, {0} },
+ {MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_WE_Y_PONG_ADDR_ADDR,
+ HWIO_JPEG_WE_Y_PONG_ADDR_RMSK, {0} },
+};
+
+void msm_gemini_hw_we_buffer_update(struct msm_gemini_hw_buf *p_input,
+ uint8_t pingpong_index)
+{
+ uint32_t n_reg_val = 0;
+
+ struct msm_gemini_hw_cmd *hw_cmd_p;
+
+ GMN_DBG("%s:%d] pingpong index %d", __func__, __LINE__,
+ pingpong_index);
+ if (pingpong_index == 0) {
+ hw_cmd_p = &hw_cmd_we_ping_update[0];
+
+ n_reg_val = ((p_input->y_len <<
+ HWIO_JPEG_WE_Y_PING_BUFFER_CFG_WE_BUFFER_LENGTH_SHFT) &
+ HWIO_JPEG_WE_Y_PING_BUFFER_CFG_WE_BUFFER_LENGTH_BMSK);
+ hw_cmd_p->data = n_reg_val;
+ msm_gemini_hw_write(hw_cmd_p++);
+
+ n_reg_val = p_input->y_buffer_addr;
+ hw_cmd_p->data = n_reg_val;
+ msm_gemini_hw_write(hw_cmd_p++);
+ } else if (pingpong_index == 1) {
+ hw_cmd_p = &hw_cmd_we_pong_update[0];
+
+ n_reg_val = ((p_input->y_len <<
+ HWIO_JPEG_WE_Y_PONG_BUFFER_CFG_WE_BUFFER_LENGTH_SHFT) &
+ HWIO_JPEG_WE_Y_PONG_BUFFER_CFG_WE_BUFFER_LENGTH_BMSK);
+ hw_cmd_p->data = n_reg_val;
+ msm_gemini_hw_write(hw_cmd_p++);
+
+ n_reg_val = p_input->y_buffer_addr;
+ hw_cmd_p->data = n_reg_val;
+ msm_gemini_hw_write(hw_cmd_p++);
+ } else {
+ /* shall not get to here */
+ }
+
+ return;
+}
+
+struct msm_gemini_hw_cmd hw_cmd_reset[] = {
+ /* type, repeat n times, offset, mask, data or pdata */
+ {MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_IRQ_MASK_ADDR,
+ HWIO_JPEG_IRQ_MASK_RMSK, {JPEG_IRQ_DISABLE_ALL} },
+ {MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_IRQ_CLEAR_ADDR,
+ HWIO_JPEG_IRQ_MASK_RMSK, {JPEG_IRQ_CLEAR_ALL} },
+ {MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_IRQ_MASK_ADDR,
+ HWIO_JPEG_IRQ_MASK_RMSK, {JPEG_IRQ_ALLSOURCES_ENABLE} },
+ {MSM_GEMINI_HW_CMD_TYPE_WRITE, 1, HWIO_JPEG_RESET_CMD_ADDR,
+ HWIO_JPEG_RESET_CMD_RMSK, {JPEG_RESET_DEFAULT} },
+};
+
+void msm_gemini_hw_init(void *base, int size)
+{
+ gemini_region_base = base;
+ gemini_region_size = size;
+}
+
+void msm_gemini_hw_reset(void *base, int size)
+{
+ struct msm_gemini_hw_cmd *hw_cmd_p;
+
+ hw_cmd_p = &hw_cmd_reset[0];
+
+ msm_gemini_hw_write(hw_cmd_p++);
+ msm_gemini_hw_write(hw_cmd_p++);
+ msm_gemini_hw_write(hw_cmd_p++);
+ msm_gemini_hw_write(hw_cmd_p);
+}
+
+uint32_t msm_gemini_hw_read(struct msm_gemini_hw_cmd *hw_cmd_p)
+{
+ uint32_t *paddr;
+ uint32_t data;
+
+ paddr = gemini_region_base + hw_cmd_p->offset;
+
+ data = readl_relaxed(paddr);
+ data &= hw_cmd_p->mask;
+
+ GMN_DBG("%s:%d] type-%d n-%d offset-0x%4x mask-0x%8x data-0x%8x\n",
+ __func__, __LINE__, hw_cmd_p->type, hw_cmd_p->n,
+ hw_cmd_p->offset, hw_cmd_p->mask, data);
+ return data;
+}
+
+void msm_gemini_hw_write(struct msm_gemini_hw_cmd *hw_cmd_p)
+{
+ uint32_t *paddr;
+ uint32_t old_data, new_data;
+
+ /* type, repeat n times, offset, mask, data or pdata */
+ GMN_DBG("%s:%d] type-%d n-%d offset-0x%4x mask-0x%8x data-0x%8x\n",
+ __func__, __LINE__, hw_cmd_p->type, hw_cmd_p->n,
+ hw_cmd_p->offset, hw_cmd_p->mask, hw_cmd_p->data);
+
+ paddr = gemini_region_base + hw_cmd_p->offset;
+
+ if (hw_cmd_p->mask == 0xffffffff) {
+ old_data = 0;
+ } else {
+ old_data = readl_relaxed(paddr);
+ old_data &= ~hw_cmd_p->mask;
+ }
+
+ new_data = hw_cmd_p->data & hw_cmd_p->mask;
+ new_data |= old_data;
+ writel_relaxed(new_data, paddr);
+}
+
+int msm_gemini_hw_wait(struct msm_gemini_hw_cmd *hw_cmd_p, int m_us)
+{
+ int tm = hw_cmd_p->n;
+ uint32_t data;
+ uint32_t wait_data = hw_cmd_p->data & hw_cmd_p->mask;
+
+ data = msm_gemini_hw_read(hw_cmd_p);
+ if (data != wait_data) {
+ while (tm) {
+ udelay(m_us);
+ data = msm_gemini_hw_read(hw_cmd_p);
+ if (data == wait_data)
+ break;
+ tm--;
+ }
+ }
+ hw_cmd_p->data = data;
+ return tm;
+}
+
+void msm_gemini_hw_delay(struct msm_gemini_hw_cmd *hw_cmd_p, int m_us)
+{
+ int tm = hw_cmd_p->n;
+ while (tm) {
+ udelay(m_us);
+ tm--;
+ }
+}
+
+int msm_gemini_hw_exec_cmds(struct msm_gemini_hw_cmd *hw_cmd_p, int m_cmds)
+{
+ int is_copy_to_user = -1;
+ uint32_t data;
+
+ while (m_cmds--) {
+ if (hw_cmd_p->offset > gemini_region_size) {
+ GMN_PR_ERR("%s:%d] %d exceed hw region %d\n", __func__,
+ __LINE__, hw_cmd_p->offset, gemini_region_size);
+ return -EFAULT;
+ }
+
+ switch (hw_cmd_p->type) {
+ case MSM_GEMINI_HW_CMD_TYPE_READ:
+ hw_cmd_p->data = msm_gemini_hw_read(hw_cmd_p);
+ is_copy_to_user = 1;
+ break;
+
+ case MSM_GEMINI_HW_CMD_TYPE_WRITE:
+ msm_gemini_hw_write(hw_cmd_p);
+ break;
+
+ case MSM_GEMINI_HW_CMD_TYPE_WRITE_OR:
+ data = msm_gemini_hw_read(hw_cmd_p);
+ hw_cmd_p->data = (hw_cmd_p->data & hw_cmd_p->mask) |
+ data;
+ msm_gemini_hw_write(hw_cmd_p);
+ break;
+
+ case MSM_GEMINI_HW_CMD_TYPE_UWAIT:
+ msm_gemini_hw_wait(hw_cmd_p, 1);
+ break;
+
+ case MSM_GEMINI_HW_CMD_TYPE_MWAIT:
+ msm_gemini_hw_wait(hw_cmd_p, 1000);
+ break;
+
+ case MSM_GEMINI_HW_CMD_TYPE_UDELAY:
+ /* Userspace driver provided delay duration */
+ msm_gemini_hw_delay(hw_cmd_p, 1);
+ break;
+
+ case MSM_GEMINI_HW_CMD_TYPE_MDELAY:
+ /* Userspace driver provided delay duration */
+ msm_gemini_hw_delay(hw_cmd_p, 1000);
+ break;
+
+ default:
+ GMN_PR_ERR("wrong hw command type\n");
+ break;
+ }
+
+ hw_cmd_p++;
+ }
+ return is_copy_to_user;
+}
+
+#ifdef MSM_GMN_DBG_DUMP
+void msm_gemini_io_dump(int size)
+{
+ char line_str[128], *p_str;
+ void __iomem *addr = gemini_region_base;
+ int i;
+ u32 *p = (u32 *) addr;
+ u32 data;
+ pr_info("%s: %p %d reg_size %d\n", __func__, addr, size,
+ gemini_region_size);
+ line_str[0] = '\0';
+ p_str = line_str;
+ for (i = 0; i < size/4; i++) {
+ if (i % 4 == 0) {
+ snprintf(p_str, 12, "%08x: ", (u32) p);
+ p_str += 10;
+ }
+ data = readl_relaxed(p++);
+ snprintf(p_str, 12, "%08x ", data);
+ p_str += 9;
+ if ((i + 1) % 4 == 0) {
+ pr_info("%s\n", line_str);
+ line_str[0] = '\0';
+ p_str = line_str;
+ }
+ }
+ if (line_str[0] != '\0')
+ pr_info("%s\n", line_str);
+}
+#else
+void msm_gemini_io_dump(int size)
+{
+
+}
+#endif
diff --git a/drivers/media/platform/msm/camera_v2/gemini/msm_gemini_hw.h b/drivers/media/platform/msm/camera_v2/gemini/msm_gemini_hw.h
new file mode 100644
index 0000000..84eed72
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/gemini/msm_gemini_hw.h
@@ -0,0 +1,104 @@
+/* Copyright (c) 2010-2013, 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_GEMINI_HW_H
+#define MSM_GEMINI_HW_H
+
+#include <linux/msm_ion.h>
+#include <media/msm_gemini.h>
+#include <mach/iommu_domains.h>
+#include "msm_gemini_hw_reg.h"
+
+struct msm_gemini_hw_buf {
+ struct msm_gemini_buf vbuf;
+ struct file *file;
+ uint32_t framedone_len;
+ uint32_t y_buffer_addr;
+ uint32_t y_len;
+ uint32_t cbcr_buffer_addr;
+ uint32_t cbcr_len;
+ uint32_t num_of_mcu_rows;
+ struct ion_handle *handle;
+};
+
+struct msm_gemini_hw_pingpong {
+ uint8_t is_fe; /* 1: fe; 0: we */
+ struct msm_gemini_hw_buf buf[2];
+ int buf_status[2];
+ int buf_active_index;
+};
+
+int msm_gemini_hw_pingpong_update(struct msm_gemini_hw_pingpong *pingpong_hw,
+ struct msm_gemini_hw_buf *buf);
+void *msm_gemini_hw_pingpong_irq(struct msm_gemini_hw_pingpong *pingpong_hw);
+void *msm_gemini_hw_pingpong_active_buffer(struct msm_gemini_hw_pingpong
+ *pingpong_hw);
+
+void msm_gemini_hw_irq_clear(uint32_t, uint32_t);
+int msm_gemini_hw_irq_get_status(void);
+long msm_gemini_hw_encode_output_size(void);
+#define MSM_GEMINI_HW_MASK_COMP_FRAMEDONE \
+ MSM_GEMINI_HW_IRQ_STATUS_FRAMEDONE_MASK
+#define MSM_GEMINI_HW_MASK_COMP_FE \
+ MSM_GEMINI_HW_IRQ_STATUS_FE_RD_DONE_MASK
+#define MSM_GEMINI_HW_MASK_COMP_WE \
+ (MSM_GEMINI_HW_IRQ_STATUS_WE_Y_PINGPONG_MASK | \
+ MSM_GEMINI_HW_IRQ_STATUS_WE_CBCR_PINGPONG_MASK)
+#define MSM_GEMINI_HW_MASK_COMP_RESET_ACK \
+ MSM_GEMINI_HW_IRQ_STATUS_RESET_ACK_MASK
+#define MSM_GEMINI_HW_MASK_COMP_ERR \
+ (MSM_GEMINI_HW_IRQ_STATUS_FE_RTOVF_MASK | \
+ MSM_GEMINI_HW_IRQ_STATUS_FE_VFE_OVERFLOW_MASK | \
+ MSM_GEMINI_HW_IRQ_STATUS_WE_Y_BUFFER_OVERFLOW_MASK | \
+ MSM_GEMINI_HW_IRQ_STATUS_WE_CBCR_BUFFER_OVERFLOW_MASK | \
+ MSM_GEMINI_HW_IRQ_STATUS_WE_CH0_DATAFIFO_OVERFLOW_MASK | \
+ MSM_GEMINI_HW_IRQ_STATUS_WE_CH1_DATAFIFO_OVERFLOW_MASK | \
+ MSM_GEMINI_HW_IRQ_STATUS_BUS_ERROR_MASK | \
+ MSM_GEMINI_HW_IRQ_STATUS_VIOLATION_MASK)
+
+#define msm_gemini_hw_irq_is_frame_done(gemini_irq_status) \
+ (gemini_irq_status & MSM_GEMINI_HW_MASK_COMP_FRAMEDONE)
+#define msm_gemini_hw_irq_is_fe_pingpong(gemini_irq_status) \
+ (gemini_irq_status & MSM_GEMINI_HW_MASK_COMP_FE)
+#define msm_gemini_hw_irq_is_we_pingpong(gemini_irq_status) \
+ (gemini_irq_status & MSM_GEMINI_HW_MASK_COMP_WE)
+#define msm_gemini_hw_irq_is_reset_ack(gemini_irq_status) \
+ (gemini_irq_status & MSM_GEMINI_HW_MASK_COMP_RESET_ACK)
+#define msm_gemini_hw_irq_is_err(gemini_irq_status) \
+ (gemini_irq_status & MSM_GEMINI_HW_MASK_COMP_ERR)
+
+void msm_gemini_hw_fe_buffer_update(struct msm_gemini_hw_buf *p_input,
+ uint8_t pingpong_index);
+void msm_gemini_hw_we_buffer_update(struct msm_gemini_hw_buf *p_input,
+ uint8_t pingpong_index);
+
+void msm_gemini_hw_we_buffer_cfg(uint8_t is_realtime);
+
+void msm_gemini_hw_fe_start(void);
+void msm_gemini_hw_clk_cfg(void);
+
+void msm_gemini_hw_reset(void *base, int size);
+void msm_gemini_hw_irq_cfg(void);
+void msm_gemini_hw_init(void *base, int size);
+
+uint32_t msm_gemini_hw_read(struct msm_gemini_hw_cmd *hw_cmd_p);
+void msm_gemini_hw_write(struct msm_gemini_hw_cmd *hw_cmd_p);
+int msm_gemini_hw_wait(struct msm_gemini_hw_cmd *hw_cmd_p, int m_us);
+void msm_gemini_hw_delay(struct msm_gemini_hw_cmd *hw_cmd_p, int m_us);
+int msm_gemini_hw_exec_cmds(struct msm_gemini_hw_cmd *hw_cmd_p, int m_cmds);
+void msm_gemini_io_dump(int size);
+
+#define MSM_GEMINI_PIPELINE_CLK_128MHZ 128 /* 8MP 128MHz */
+#define MSM_GEMINI_PIPELINE_CLK_140MHZ 140 /* 9MP 140MHz */
+#define MSM_GEMINI_PIPELINE_CLK_200MHZ 153 /* 12MP 153MHz */
+
+#endif /* MSM_GEMINI_HW_H */
diff --git a/drivers/media/platform/msm/camera_v2/gemini/msm_gemini_hw_reg.h b/drivers/media/platform/msm/camera_v2/gemini/msm_gemini_hw_reg.h
new file mode 100644
index 0000000..4f05650
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/gemini/msm_gemini_hw_reg.h
@@ -0,0 +1,176 @@
+/* Copyright (c) 2010,2013, 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_GEMINI_HW_REG_H
+#define MSM_GEMINI_HW_REG_H
+
+#define GEMINI_REG_BASE 0
+
+#define MSM_GEMINI_HW_IRQ_MASK_ADDR 0x00000014
+#define MSM_GEMINI_HW_IRQ_MASK_RMSK 0xffffffff
+#define MSM_GEMINI_HW_IRQ_MASK_SHFT 0
+#define MSM_GEMINI_HW_IRQ_DISABLE 0
+#define MSM_GEMINI_HW_IRQ_ENABLE 0xffffffff
+
+#define MSM_GEMINI_HW_IRQ_CLEAR_ADDR 0x00000018
+#define MSM_GEMINI_HW_IRQ_CLEAR_RMSK 0xffffffff
+#define MSM_GEMINI_HW_IRQ_CLEAR_SHFT 0
+#define MSM_GEMINI_HW_IRQ_CLEAR 0xffffffff
+
+#define MSM_GEMINI_HW_IRQ_STATUS_FRAMEDONE_MASK 0x00000001
+#define MSM_GEMINI_HW_IRQ_STATUS_FRAMEDONE_SHIFT 0x00000000
+
+#define MSM_GEMINI_HW_IRQ_STATUS_FE_RD_DONE_MASK 0x00000002
+#define MSM_GEMINI_HW_IRQ_STATUS_FE_RD_DONE_SHIFT 0x00000001
+
+#define MSM_GEMINI_HW_IRQ_STATUS_FE_RTOVF_MASK 0x00000004
+#define MSM_GEMINI_HW_IRQ_STATUS_FE_RTOVF_SHIFT 0x00000002
+
+#define MSM_GEMINI_HW_IRQ_STATUS_FE_VFE_OVERFLOW_MASK 0x00000008
+#define MSM_GEMINI_HW_IRQ_STATUS_FE_VFE_OVERFLOW_SHIFT 0x00000003
+
+#define MSM_GEMINI_HW_IRQ_STATUS_WE_Y_PINGPONG_MASK 0x00000010
+#define MSM_GEMINI_HW_IRQ_STATUS_WE_Y_PINGPONG_SHIFT 0x00000004
+
+#define MSM_GEMINI_HW_IRQ_STATUS_WE_CBCR_PINGPONG_MASK 0x00000020
+#define MSM_GEMINI_HW_IRQ_STATUS_WE_CBCR_PINGPONG_SHIFT 0x00000005
+
+#define MSM_GEMINI_HW_IRQ_STATUS_WE_Y_BUFFER_OVERFLOW_MASK 0x00000040
+#define MSM_GEMINI_HW_IRQ_STATUS_WE_Y_BUFFER_OVERFLOW_SHIFT 0x00000006
+
+#define MSM_GEMINI_HW_IRQ_STATUS_WE_CBCR_BUFFER_OVERFLOW_MASK 0x00000080
+#define MSM_GEMINI_HW_IRQ_STATUS_WE_CBCR_BUFFER_OVERFLOW_SHIFT 0x00000007
+
+#define MSM_GEMINI_HW_IRQ_STATUS_WE_CH0_DATAFIFO_OVERFLOW_MASK 0x00000100
+#define MSM_GEMINI_HW_IRQ_STATUS_WE_CH0_DATAFIFO_OVERFLOW_SHIFT 0x00000008
+
+#define MSM_GEMINI_HW_IRQ_STATUS_WE_CH1_DATAFIFO_OVERFLOW_MASK 0x00000200
+#define MSM_GEMINI_HW_IRQ_STATUS_WE_CH1_DATAFIFO_OVERFLOW_SHIFT 0x00000009
+
+#define MSM_GEMINI_HW_IRQ_STATUS_RESET_ACK_MASK 0x00000400
+#define MSM_GEMINI_HW_IRQ_STATUS_RESET_ACK_SHIFT 0x0000000a
+
+#define MSM_GEMINI_HW_IRQ_STATUS_BUS_ERROR_MASK 0x00000800
+#define MSM_GEMINI_HW_IRQ_STATUS_BUS_ERROR_SHIFT 0x0000000b
+
+#define MSM_GEMINI_HW_IRQ_STATUS_VIOLATION_MASK 0x00001000
+#define MSM_GEMINI_HW_IRQ_STATUS_VIOLATION_SHIFT 0x0000000c
+
+#define JPEG_BUS_CMD_HALT_REQ 0x00000001
+
+#define JPEG_REALTIME_CMD_STOP_FB 0x00000000
+#define JPEG_REALTIME_CMD_STOP_IM 0x00000003
+#define JPEG_REALTIME_CMD_START 0x00000001
+
+#define JPEG_OFFLINE_CMD_START 0x00000003
+
+#define JPEG_DMI_CFG_DISABLE 0x00000000
+#define JPEG_DMI_ADDR_START 0x00000000
+
+#define JPEG_FE_CMD_BUFFERRELOAD 0x00000001
+
+#define JPEG_WE_YUB_ENCODE 0x01ff0000
+
+#define JPEG_RESET_DEFAULT 0x0004ffff /* cfff? */
+
+#define JPEG_IRQ_DISABLE_ALL 0x00000000
+#define JPEG_IRQ_CLEAR_ALL 0xffffffff
+#define JPEG_IRQ_ALLSOURCES_ENABLE 0xffffffff
+
+#define HWIO_JPEG_FE_BUFFER_CFG_ADDR (GEMINI_REG_BASE + 0x00000080)
+#define HWIO_JPEG_FE_BUFFER_CFG_RMSK 0x1fff1fff
+
+#define HWIO_JPEG_FE_Y_PING_ADDR_ADDR (GEMINI_REG_BASE + 0x00000084)
+#define HWIO_JPEG_FE_Y_PING_ADDR_RMSK 0xffffffff
+
+#define HWIO_JPEG_FE_Y_PONG_ADDR_ADDR (GEMINI_REG_BASE + 0x00000088)
+#define HWIO_JPEG_FE_Y_PONG_ADDR_RMSK 0xffffffff
+
+#define HWIO_JPEG_FE_CBCR_PING_ADDR_ADDR (GEMINI_REG_BASE + 0x0000008c)
+#define HWIO_JPEG_FE_CBCR_PING_ADDR_RMSK 0xffffffff
+
+#define HWIO_JPEG_FE_CBCR_PONG_ADDR_ADDR (GEMINI_REG_BASE + 0x00000090)
+#define HWIO_JPEG_FE_CBCR_PONG_ADDR_RMSK 0xffffffff
+
+#define HWIO_JPEG_FE_CMD_ADDR (GEMINI_REG_BASE + 0x00000094)
+#define HWIO_JPEG_FE_CMD_RMSK 0x3
+
+#define HWIO_JPEG_FE_BUFFER_CFG_CBCR_MCU_ROWS_BMSK 0x1fff0000
+#define HWIO_JPEG_FE_BUFFER_CFG_CBCR_MCU_ROWS_SHFT 0x10
+#define HWIO_JPEG_FE_BUFFER_CFG_Y_MCU_ROWS_BMSK 0x1fff
+#define HWIO_JPEG_FE_BUFFER_CFG_Y_MCU_ROWS_SHFT 0
+
+#define HWIO_JPEG_FE_Y_PING_ADDR_FE_Y_PING_START_ADDR_BMSK 0xffffffff
+#define HWIO_JPEG_FE_Y_PING_ADDR_FE_Y_PING_START_ADDR_SHFT 0
+
+#define HWIO_JPEG_FE_CBCR_PING_ADDR_FE_CBCR_PING_START_ADDR_BMSK 0xffffffff
+#define HWIO_JPEG_FE_CBCR_PING_ADDR_FE_CBCR_PING_START_ADDR_SHFT 0
+
+#define HWIO_JPEG_FE_Y_PONG_ADDR_FE_Y_PONG_START_ADDR_BMSK 0xffffffff
+#define HWIO_JPEG_FE_Y_PONG_ADDR_FE_Y_PONG_START_ADDR_SHFT 0
+
+#define HWIO_JPEG_FE_CBCR_PONG_ADDR_FE_CBCR_PONG_START_ADDR_BMSK 0xffffffff
+#define HWIO_JPEG_FE_CBCR_PONG_ADDR_FE_CBCR_PONG_START_ADDR_SHFT 0
+
+#define HWIO_JPEG_WE_Y_THRESHOLD_ADDR (GEMINI_REG_BASE + 0x000000c0)
+#define HWIO_JPEG_WE_Y_THRESHOLD_RMSK 0x1ff01ff
+
+#define HWIO_JPEG_WE_CBCR_THRESHOLD_ADDR (GEMINI_REG_BASE + 0x000000c4)
+#define HWIO_JPEG_WE_CBCR_THRESHOLD_RMSK 0x1ff01ff
+
+#define HWIO_JPEG_WE_Y_UB_CFG_ADDR (GEMINI_REG_BASE + 0x000000e8)
+#define HWIO_JPEG_WE_Y_UB_CFG_RMSK 0x1ff01ff
+
+#define HWIO_JPEG_WE_Y_THRESHOLD_WE_DEASSERT_STALL_TH_BMSK 0x1ff0000
+#define HWIO_JPEG_WE_Y_THRESHOLD_WE_DEASSERT_STALL_TH_SHFT 0x10
+#define HWIO_JPEG_WE_Y_THRESHOLD_WE_ASSERT_STALL_TH_BMSK 0x1ff
+#define HWIO_JPEG_WE_Y_THRESHOLD_WE_ASSERT_STALL_TH_SHFT 0
+
+#define HWIO_JPEG_WE_CBCR_THRESHOLD_WE_DEASSERT_STALL_TH_BMSK 0x1ff0000
+#define HWIO_JPEG_WE_CBCR_THRESHOLD_WE_DEASSERT_STALL_TH_SHFT 0x10
+#define HWIO_JPEG_WE_CBCR_THRESHOLD_WE_ASSERT_STALL_TH_BMSK 0x1ff
+#define HWIO_JPEG_WE_CBCR_THRESHOLD_WE_ASSERT_STALL_TH_SHFT 0
+
+#define HWIO_JPEG_WE_Y_PING_BUFFER_CFG_ADDR (GEMINI_REG_BASE + 0x000000c8)
+#define HWIO_JPEG_WE_Y_PING_BUFFER_CFG_RMSK 0x7fffff
+
+#define HWIO_JPEG_WE_Y_PING_ADDR_ADDR (GEMINI_REG_BASE + 0x000000d8)
+#define HWIO_JPEG_WE_Y_PING_ADDR_RMSK 0xfffffff8
+
+#define HWIO_JPEG_WE_Y_PONG_BUFFER_CFG_ADDR (GEMINI_REG_BASE + 0x000000cc)
+#define HWIO_JPEG_WE_Y_PONG_BUFFER_CFG_RMSK 0x7fffff
+
+#define HWIO_JPEG_WE_Y_PONG_ADDR_ADDR (GEMINI_REG_BASE + 0x000000dc)
+#define HWIO_JPEG_WE_Y_PONG_ADDR_RMSK 0xfffffff8
+
+#define HWIO_JPEG_WE_Y_PING_BUFFER_CFG_WE_BUFFER_LENGTH_BMSK 0x7fffff
+#define HWIO_JPEG_WE_Y_PING_BUFFER_CFG_WE_BUFFER_LENGTH_SHFT 0
+
+#define HWIO_JPEG_WE_Y_PONG_BUFFER_CFG_WE_BUFFER_LENGTH_BMSK 0x7fffff
+#define HWIO_JPEG_WE_Y_PONG_BUFFER_CFG_WE_BUFFER_LENGTH_SHFT 0
+
+#define HWIO_JPEG_IRQ_MASK_ADDR (GEMINI_REG_BASE + 0x00000014)
+#define HWIO_JPEG_IRQ_MASK_RMSK 0xffffffff
+
+#define HWIO_JPEG_IRQ_CLEAR_ADDR (GEMINI_REG_BASE + 0x00000018)
+#define HWIO_JPEG_IRQ_CLEAR_RMSK 0xffffffff
+
+#define HWIO_JPEG_RESET_CMD_ADDR (GEMINI_REG_BASE + 0x00000004)
+#define HWIO_JPEG_RESET_CMD_RMSK 0xe004ffff
+
+#define HWIO_JPEG_IRQ_STATUS_ADDR (GEMINI_REG_BASE + 0x0000001c)
+#define HWIO_JPEG_IRQ_STATUS_RMSK 0xffffffff
+
+#define HWIO_JPEG_STATUS_ENCODE_OUTPUT_SIZE_ADDR (GEMINI_REG_BASE + 0x00000034)
+#define HWIO_JPEG_STATUS_ENCODE_OUTPUT_SIZE_RMSK 0xffffffff
+
+#endif /* MSM_GEMINI_HW_REG_H */
diff --git a/drivers/media/platform/msm/camera_v2/gemini/msm_gemini_platform.c b/drivers/media/platform/msm/camera_v2/gemini/msm_gemini_platform.c
new file mode 100644
index 0000000..f442068
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/gemini/msm_gemini_platform.c
@@ -0,0 +1,283 @@
+/* Copyright (c) 2010-2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/pm_qos.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/android_pmem.h>
+#include <mach/clk.h>
+#include <mach/camera2.h>
+#include <mach/iommu_domains.h>
+#include "msm_gemini_platform.h"
+#include "msm_gemini_sync.h"
+#include "msm_gemini_common.h"
+#include "msm_gemini_hw.h"
+#include "msm_camera_io_util.h"
+
+/* AXI rate in KHz */
+#define MSM_SYSTEM_BUS_RATE 160000
+struct ion_client *gemini_client;
+
+
+#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
+void msm_gemini_platform_p2v(struct file *file,
+ struct ion_handle **ionhandle)
+{
+ ion_unmap_iommu(gemini_client, *ionhandle, CAMERA_DOMAIN, GEN_POOL);
+ ion_free(gemini_client, *ionhandle);
+ *ionhandle = NULL;
+}
+#else
+void msm_gemini_platform_p2v(struct file *file,
+ struct ion_handle **ionhandle)
+{
+
+}
+#endif
+
+
+#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
+uint32_t msm_gemini_platform_v2p(int fd, uint32_t len, struct file **file_p,
+ struct ion_handle **ionhandle)
+{
+ unsigned long paddr;
+ unsigned long size;
+ int rc;
+
+ *ionhandle = ion_import_dma_buf(gemini_client, fd);
+ if (IS_ERR_OR_NULL(*ionhandle))
+ return 0;
+
+ rc = ion_map_iommu(gemini_client, *ionhandle, CAMERA_DOMAIN, GEN_POOL,
+ SZ_4K, 0, &paddr, (unsigned long *)&size, 0, 0);
+ if (rc < 0) {
+ GMN_PR_ERR("%s: get_pmem_file fd %d error %d\n", __func__, fd,
+ rc);
+ goto error1;
+ }
+ /* validate user input */
+ if (len > size) {
+ GMN_PR_ERR("%s: invalid offset + len\n", __func__);
+ goto error1;
+ }
+
+ return paddr;
+error1:
+ ion_free(gemini_client, *ionhandle);
+
+ return 0;
+}
+#else
+uint32_t msm_gemini_platform_v2p(int fd, uint32_t len, struct file **file_p,
+ struct ion_handle **ionhandle)
+{
+ return 0;
+}
+#endif
+
+static struct msm_cam_clk_info gemini_8x_clk_info[] = {
+ {"core_clk", 228571000, 0},
+ {"iface_clk", -1, 0},
+};
+
+static struct msm_cam_clk_info gemini_7x_clk_info[] = {
+ {"core_clk", 153600000, 0},
+ {"iface_clk", -1, 0},
+};
+
+static struct msm_cam_clk_info gemini_imem_clk_info[] = {
+ {"mem_clk", -1, 0},
+};
+
+#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
+static struct ion_client *msm_gemini_ion_client_create(unsigned int heap_mask,
+ const char *name)
+{
+ return msm_ion_client_create(heap_mask, name);
+}
+#else
+static struct ion_client *msm_gemini_ion_client_create(unsigned int heap_mask,
+ const char *name)
+{
+ return NULL;
+}
+#endif
+
+#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
+void msm_gemini_ion_client_destroy(struct ion_client *client)
+{
+ ion_client_destroy(client);
+}
+#else
+void msm_gemini_ion_client_destroy(struct ion_client *client)
+{
+
+}
+#endif
+
+int msm_gemini_platform_init(struct platform_device *pdev,
+ struct resource **mem,
+ void **base,
+ int *irq,
+ irqreturn_t (*handler) (int, void *),
+ void *context)
+{
+ int rc = -1;
+ int gemini_irq;
+ struct resource *gemini_mem, *gemini_io, *gemini_irq_res;
+ void *gemini_base;
+ struct msm_gemini_device *pgmn_dev =
+ (struct msm_gemini_device *) context;
+
+ gemini_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!gemini_mem) {
+ GMN_PR_ERR("%s: no mem resource!\n", __func__);
+ return -ENODEV;
+ }
+
+ gemini_irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!gemini_irq_res) {
+ GMN_PR_ERR("no irq resource!\n");
+ return -ENODEV;
+ }
+ gemini_irq = gemini_irq_res->start;
+
+ gemini_io = request_mem_region(gemini_mem->start,
+ resource_size(gemini_mem), pdev->name);
+ if (!gemini_io) {
+ GMN_PR_ERR("%s: region already claimed\n", __func__);
+ return -EBUSY;
+ }
+
+ gemini_base = ioremap(gemini_mem->start, resource_size(gemini_mem));
+ if (!gemini_base) {
+ rc = -ENOMEM;
+ GMN_PR_ERR("%s: ioremap failed\n", __func__);
+ goto fail1;
+ }
+ pgmn_dev->hw_version = GEMINI_8X60;
+ rc = msm_cam_clk_enable(&pgmn_dev->pdev->dev, gemini_8x_clk_info,
+ pgmn_dev->gemini_clk, ARRAY_SIZE(gemini_8x_clk_info), 1);
+ if (rc < 0) {
+ pgmn_dev->hw_version = GEMINI_7X;
+ rc = msm_cam_clk_enable(&pgmn_dev->pdev->dev,
+ gemini_7x_clk_info, pgmn_dev->gemini_clk,
+ ARRAY_SIZE(gemini_7x_clk_info), 1);
+ if (rc < 0) {
+ GMN_PR_ERR("%s: clk failed rc = %d\n", __func__, rc);
+ goto fail2;
+ }
+ } else {
+ rc = msm_cam_clk_enable(&pgmn_dev->pdev->dev,
+ gemini_imem_clk_info, &pgmn_dev->gemini_clk[2],
+ ARRAY_SIZE(gemini_imem_clk_info), 1);
+ if (!rc)
+ pgmn_dev->hw_version = GEMINI_8960;
+ }
+
+ if (pgmn_dev->hw_version != GEMINI_7X) {
+ if (pgmn_dev->gemini_fs == NULL) {
+ pgmn_dev->gemini_fs =
+ regulator_get(&pgmn_dev->pdev->dev, "vdd");
+ if (IS_ERR(pgmn_dev->gemini_fs)) {
+ GMN_PR_ERR("%s: regulator_get failed %ld\n",
+ __func__, PTR_ERR(pgmn_dev->gemini_fs));
+ pgmn_dev->gemini_fs = NULL;
+ goto gemini_fs_failed;
+ } else if (regulator_enable(pgmn_dev->gemini_fs)) {
+ GMN_PR_ERR("%s: regulator_enable failed\n",
+ __func__);
+ regulator_put(pgmn_dev->gemini_fs);
+ pgmn_dev->gemini_fs = NULL;
+ goto gemini_fs_failed;
+ }
+ }
+ }
+
+ msm_gemini_hw_init(gemini_base, resource_size(gemini_mem));
+ rc = request_irq(gemini_irq, handler, IRQF_TRIGGER_RISING, "gemini",
+ context);
+ if (rc) {
+ GMN_PR_ERR("%s: request_irq failed, %d\n", __func__,
+ gemini_irq);
+ goto fail3;
+ }
+
+ *mem = gemini_mem;
+ *base = gemini_base;
+ *irq = gemini_irq;
+
+ gemini_client = msm_gemini_ion_client_create(-1, "camera/gemini");
+
+ GMN_DBG("%s:%d] success\n", __func__, __LINE__);
+
+ return rc;
+
+fail3:
+ if (pgmn_dev->hw_version != GEMINI_7X) {
+ regulator_disable(pgmn_dev->gemini_fs);
+ regulator_put(pgmn_dev->gemini_fs);
+ pgmn_dev->gemini_fs = NULL;
+ }
+gemini_fs_failed:
+ if (pgmn_dev->hw_version == GEMINI_8960)
+ msm_cam_clk_enable(&pgmn_dev->pdev->dev, gemini_imem_clk_info,
+ &pgmn_dev->gemini_clk[2], ARRAY_SIZE(gemini_imem_clk_info), 0);
+ if (pgmn_dev->hw_version != GEMINI_7X)
+ msm_cam_clk_enable(&pgmn_dev->pdev->dev, gemini_8x_clk_info,
+ pgmn_dev->gemini_clk, ARRAY_SIZE(gemini_8x_clk_info), 0);
+ else
+ msm_cam_clk_enable(&pgmn_dev->pdev->dev, gemini_7x_clk_info,
+ pgmn_dev->gemini_clk, ARRAY_SIZE(gemini_7x_clk_info), 0);
+fail2:
+ iounmap(gemini_base);
+fail1:
+ release_mem_region(gemini_mem->start, resource_size(gemini_mem));
+ GMN_DBG("%s:%d] fail\n", __func__, __LINE__);
+ return rc;
+}
+
+int msm_gemini_platform_release(struct resource *mem, void *base, int irq,
+ void *context)
+{
+ int result = 0;
+ struct msm_gemini_device *pgmn_dev =
+ (struct msm_gemini_device *) context;
+
+ free_irq(irq, context);
+
+ if (pgmn_dev->hw_version != GEMINI_7X) {
+ regulator_disable(pgmn_dev->gemini_fs);
+ regulator_put(pgmn_dev->gemini_fs);
+ pgmn_dev->gemini_fs = NULL;
+ }
+
+ if (pgmn_dev->hw_version == GEMINI_8960)
+ msm_cam_clk_enable(&pgmn_dev->pdev->dev, gemini_imem_clk_info,
+ &pgmn_dev->gemini_clk[2], ARRAY_SIZE(gemini_imem_clk_info), 0);
+ if (pgmn_dev->hw_version != GEMINI_7X)
+ msm_cam_clk_enable(&pgmn_dev->pdev->dev, gemini_8x_clk_info,
+ pgmn_dev->gemini_clk, ARRAY_SIZE(gemini_8x_clk_info), 0);
+ else
+ msm_cam_clk_enable(&pgmn_dev->pdev->dev, gemini_7x_clk_info,
+ pgmn_dev->gemini_clk, ARRAY_SIZE(gemini_7x_clk_info), 0);
+
+ iounmap(base);
+ release_mem_region(mem->start, resource_size(mem));
+
+ msm_gemini_ion_client_destroy(gemini_client);
+
+ GMN_DBG("%s:%d] success\n", __func__, __LINE__);
+ return result;
+}
+
diff --git a/drivers/media/platform/msm/camera_v2/gemini/msm_gemini_platform.h b/drivers/media/platform/msm/camera_v2/gemini/msm_gemini_platform.h
new file mode 100644
index 0000000..a071df9
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/gemini/msm_gemini_platform.h
@@ -0,0 +1,37 @@
+/* Copyright (c) 2010-2013, 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_GEMINI_PLATFORM_H
+#define MSM_GEMINI_PLATFORM_H
+
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/msm_ion.h>
+#include <linux/iommu.h>
+void msm_gemini_platform_p2v(struct file *file,
+ struct ion_handle **ionhandle);
+uint32_t msm_gemini_platform_v2p(int fd, uint32_t len, struct file **file,
+ struct ion_handle **ionhandle);
+
+int msm_gemini_platform_clk_enable(void);
+int msm_gemini_platform_clk_disable(void);
+
+int msm_gemini_platform_init(struct platform_device *pdev,
+ struct resource **mem,
+ void **base,
+ int *irq,
+ irqreturn_t (*handler) (int, void *),
+ void *context);
+int msm_gemini_platform_release(struct resource *mem, void *base, int irq,
+ void *context);
+
+#endif /* MSM_GEMINI_PLATFORM_H */
diff --git a/drivers/media/platform/msm/camera_v2/gemini/msm_gemini_sync.c b/drivers/media/platform/msm/camera_v2/gemini/msm_gemini_sync.c
new file mode 100644
index 0000000..8f84a2c
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/gemini/msm_gemini_sync.c
@@ -0,0 +1,1081 @@
+/* Copyright (c) 2010-2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/list.h>
+#include <linux/uaccess.h>
+#include <linux/slab.h>
+#include <media/msm_gemini.h>
+#include <mach/msm_bus.h>
+#include <mach/msm_bus_board.h>
+#include "msm_gemini_sync.h"
+#include "msm_gemini_core.h"
+#include "msm_gemini_platform.h"
+#include "msm_gemini_common.h"
+
+static int release_buf;
+
+/* size is based on 4k page size */
+static const int g_max_out_size = 0x7ff000;
+
+/*************** queue helper ****************/
+static inline void msm_gemini_q_init(char const *name, struct msm_gemini_q *q_p)
+{
+ GMN_DBG("%s:%d] %s\n", __func__, __LINE__, name);
+ q_p->name = name;
+ spin_lock_init(&q_p->lck);
+ INIT_LIST_HEAD(&q_p->q);
+ init_waitqueue_head(&q_p->wait);
+ q_p->unblck = 0;
+}
+
+static inline void *msm_gemini_q_out(struct msm_gemini_q *q_p)
+{
+ unsigned long flags;
+ struct msm_gemini_q_entry *q_entry_p = NULL;
+ void *data = NULL;
+
+ GMN_DBG("%s:%d] %s\n", __func__, __LINE__, q_p->name);
+ spin_lock_irqsave(&q_p->lck, flags);
+ if (!list_empty(&q_p->q)) {
+ q_entry_p = list_first_entry(&q_p->q, struct msm_gemini_q_entry,
+ list);
+ list_del_init(&q_entry_p->list);
+ }
+ spin_unlock_irqrestore(&q_p->lck, flags);
+
+ if (q_entry_p) {
+ data = q_entry_p->data;
+ kfree(q_entry_p);
+ } else {
+ GMN_DBG("%s:%d] %s no entry\n", __func__, __LINE__,
+ q_p->name);
+ }
+
+ return data;
+}
+
+static inline int msm_gemini_q_in(struct msm_gemini_q *q_p, void *data)
+{
+ unsigned long flags;
+
+ struct msm_gemini_q_entry *q_entry_p;
+
+ GMN_DBG("%s:%d] %s\n", __func__, __LINE__, q_p->name);
+
+ q_entry_p = kmalloc(sizeof(struct msm_gemini_q_entry), GFP_ATOMIC);
+ if (!q_entry_p) {
+ GMN_PR_ERR("%s: no mem\n", __func__);
+ return -ENOMEM;
+ }
+ q_entry_p->data = data;
+
+ spin_lock_irqsave(&q_p->lck, flags);
+ list_add_tail(&q_entry_p->list, &q_p->q);
+ spin_unlock_irqrestore(&q_p->lck, flags);
+
+ return 0;
+}
+
+static inline int msm_gemini_q_in_buf(struct msm_gemini_q *q_p,
+ struct msm_gemini_core_buf *buf)
+{
+ struct msm_gemini_core_buf *buf_p;
+
+ GMN_DBG("%s:%d]\n", __func__, __LINE__);
+ buf_p = kmalloc(sizeof(struct msm_gemini_core_buf), GFP_ATOMIC);
+ if (!buf_p) {
+ GMN_PR_ERR("%s: no mem\n", __func__);
+ return -ENOMEM;
+ }
+
+ memcpy(buf_p, buf, sizeof(struct msm_gemini_core_buf));
+
+ msm_gemini_q_in(q_p, buf_p);
+ return 0;
+}
+
+static inline int msm_gemini_q_wait(struct msm_gemini_q *q_p)
+{
+ int tm = MAX_SCHEDULE_TIMEOUT;
+ int rc;
+
+ GMN_DBG("%s:%d] %s wait\n", __func__, __LINE__, q_p->name);
+ rc = wait_event_interruptible_timeout(q_p->wait,
+ (!list_empty_careful(&q_p->q) || q_p->unblck),
+ msecs_to_jiffies(tm));
+ GMN_DBG("%s:%d] %s wait done\n", __func__, __LINE__, q_p->name);
+ if (list_empty_careful(&q_p->q)) {
+ if (rc == 0) {
+ rc = -ETIMEDOUT;
+ GMN_PR_ERR("%s:%d] %s timeout\n", __func__, __LINE__,
+ q_p->name);
+ } else if (q_p->unblck) {
+ GMN_DBG("%s:%d] %s unblock is true\n", __func__,
+ __LINE__, q_p->name);
+ q_p->unblck = 0;
+ rc = -ECANCELED;
+ } else if (rc < 0) {
+ GMN_PR_ERR("%s:%d] %s rc %d\n", __func__, __LINE__,
+ q_p->name, rc);
+ }
+ }
+ return rc;
+}
+
+static inline int msm_gemini_q_wakeup(struct msm_gemini_q *q_p)
+{
+ GMN_DBG("%s:%d] %s\n", __func__, __LINE__, q_p->name);
+ wake_up(&q_p->wait);
+ return 0;
+}
+
+static inline int msm_gemini_q_unblock(struct msm_gemini_q *q_p)
+{
+ GMN_DBG("%s:%d] %s\n", __func__, __LINE__, q_p->name);
+ q_p->unblck = 1;
+ wake_up(&q_p->wait);
+ return 0;
+}
+
+static inline void msm_gemini_outbuf_q_cleanup(struct msm_gemini_q *q_p)
+{
+ struct msm_gemini_core_buf *buf_p;
+ GMN_DBG("%s:%d] %s\n", __func__, __LINE__, q_p->name);
+ do {
+ buf_p = msm_gemini_q_out(q_p);
+ if (buf_p) {
+ msm_gemini_platform_p2v(buf_p->file,
+ &buf_p->handle);
+ GMN_DBG("%s:%d] %s\n", __func__, __LINE__, q_p->name);
+ kfree(buf_p);
+ }
+ } while (buf_p);
+ q_p->unblck = 0;
+}
+
+static inline void msm_gemini_q_cleanup(struct msm_gemini_q *q_p)
+{
+ void *data;
+ GMN_DBG("%s:%d] %s\n", __func__, __LINE__, q_p->name);
+ do {
+ data = msm_gemini_q_out(q_p);
+ if (data) {
+ GMN_DBG("%s:%d] %s\n", __func__, __LINE__, q_p->name);
+ kfree(data);
+ }
+ } while (data);
+ q_p->unblck = 0;
+}
+
+/*************** event queue ****************/
+
+int msm_gemini_framedone_irq(struct msm_gemini_device *pgmn_dev,
+ struct msm_gemini_core_buf *buf_in)
+{
+ int rc = 0;
+
+ GMN_DBG("%s:%d] buf_in %p", __func__, __LINE__, buf_in);
+
+ if (buf_in) {
+ buf_in->vbuf.framedone_len = buf_in->framedone_len;
+ buf_in->vbuf.type = MSM_GEMINI_EVT_FRAMEDONE;
+ GMN_DBG("%s:%d] 0x%08x %d framedone_len %d\n",
+ __func__, __LINE__,
+ (int) buf_in->y_buffer_addr, buf_in->y_len,
+ buf_in->vbuf.framedone_len);
+ rc = msm_gemini_q_in_buf(&pgmn_dev->evt_q, buf_in);
+ } else {
+ GMN_DBG("%s:%d] no output return buffer\n",
+ __func__, __LINE__);
+ rc = -1;
+ }
+
+ if (buf_in)
+ rc = msm_gemini_q_wakeup(&pgmn_dev->evt_q);
+
+ return rc;
+}
+
+int msm_gemini_evt_get(struct msm_gemini_device *pgmn_dev,
+ void __user *to)
+{
+ struct msm_gemini_core_buf *buf_p;
+ struct msm_gemini_ctrl_cmd ctrl_cmd;
+
+ GMN_DBG("%s:%d] Enter\n", __func__, __LINE__);
+
+ msm_gemini_q_wait(&pgmn_dev->evt_q);
+ buf_p = msm_gemini_q_out(&pgmn_dev->evt_q);
+
+ if (!buf_p) {
+ GMN_DBG("%s:%d] no buffer\n", __func__, __LINE__);
+ return -EAGAIN;
+ }
+
+ memset(&ctrl_cmd, 0, sizeof(struct msm_gemini_ctrl_cmd));
+ ctrl_cmd.type = buf_p->vbuf.type;
+ kfree(buf_p);
+
+ GMN_DBG("%s:%d] 0x%08x %d\n", __func__, __LINE__,
+ (int) ctrl_cmd.value, ctrl_cmd.len);
+
+ if (copy_to_user(to, &ctrl_cmd, sizeof(ctrl_cmd))) {
+ GMN_PR_ERR("%s:%d]\n", __func__, __LINE__);
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+int msm_gemini_evt_get_unblock(struct msm_gemini_device *pgmn_dev)
+{
+ GMN_DBG("%s:%d] Enter\n", __func__, __LINE__);
+ msm_gemini_q_unblock(&pgmn_dev->evt_q);
+ return 0;
+}
+
+void msm_gemini_reset_ack_irq(struct msm_gemini_device *pgmn_dev)
+{
+ GMN_DBG("%s:%d]\n", __func__, __LINE__);
+}
+
+void msm_gemini_err_irq(struct msm_gemini_device *pgmn_dev,
+ int event)
+{
+ int rc = 0;
+ struct msm_gemini_core_buf buf;
+
+ GMN_DBG("%s:%d] error: %d\n", __func__, __LINE__, event);
+
+ buf.vbuf.type = MSM_GEMINI_EVT_ERR;
+ rc = msm_gemini_q_in_buf(&pgmn_dev->evt_q, &buf);
+ if (!rc)
+ rc = msm_gemini_q_wakeup(&pgmn_dev->evt_q);
+
+ if (!rc)
+ GMN_PR_ERR("%s:%d] err err\n", __func__, __LINE__);
+
+ return;
+}
+
+/*************** output queue ****************/
+
+int msm_gemini_get_out_buffer(struct msm_gemini_device *pgmn_dev,
+ struct msm_gemini_hw_buf *p_outbuf)
+{
+ int buf_size = 0;
+ int bytes_remaining = 0;
+ if (pgmn_dev->out_offset >= pgmn_dev->out_buf.y_len) {
+ GMN_PR_ERR("%s:%d] no more buffers", __func__, __LINE__);
+ return -EINVAL;
+ }
+ bytes_remaining = pgmn_dev->out_buf.y_len - pgmn_dev->out_offset;
+ buf_size = min(bytes_remaining, pgmn_dev->max_out_size);
+
+ pgmn_dev->out_frag_cnt++;
+ GMN_DBG("%s:%d] buf_size[%d] %d", __func__, __LINE__,
+ pgmn_dev->out_frag_cnt, buf_size);
+ p_outbuf->y_len = buf_size;
+ p_outbuf->y_buffer_addr = pgmn_dev->out_buf.y_buffer_addr +
+ pgmn_dev->out_offset;
+ pgmn_dev->out_offset += buf_size;
+ return 0;
+}
+
+int msm_gemini_outmode_single_we_pingpong_irq(
+ struct msm_gemini_device *pgmn_dev,
+ struct msm_gemini_core_buf *buf_in)
+{
+ int rc = 0;
+ struct msm_gemini_core_buf out_buf;
+ int frame_done = buf_in &&
+ buf_in->vbuf.type == MSM_GEMINI_EVT_FRAMEDONE;
+ GMN_DBG("%s:%d] framedone %d", __func__, __LINE__, frame_done);
+ if (!pgmn_dev->out_buf_set) {
+ GMN_PR_ERR("%s:%d] output buffer not set",
+ __func__, __LINE__);
+ return -EFAULT;
+ }
+ if (frame_done) {
+ /* send the buffer back */
+ pgmn_dev->out_buf.vbuf.framedone_len = buf_in->framedone_len;
+ pgmn_dev->out_buf.vbuf.type = MSM_GEMINI_EVT_FRAMEDONE;
+ rc = msm_gemini_q_in_buf(&pgmn_dev->output_rtn_q,
+ &pgmn_dev->out_buf);
+ if (rc) {
+ GMN_PR_ERR("%s:%d] cannot queue the output buffer",
+ __func__, __LINE__);
+ return -EFAULT;
+ }
+ rc = msm_gemini_q_wakeup(&pgmn_dev->output_rtn_q);
+ /*
+ * reset the output buffer since the ownership is
+ * transferred to the rtn queue
+ */
+ if (!rc)
+ pgmn_dev->out_buf_set = 0;
+ } else {
+ /* configure ping/pong */
+ rc = msm_gemini_get_out_buffer(pgmn_dev, &out_buf);
+ if (rc)
+ msm_gemini_core_we_buf_reset(&out_buf);
+ else
+ msm_gemini_core_we_buf_update(&out_buf);
+ }
+ return rc;
+}
+
+int msm_gemini_we_pingpong_irq(struct msm_gemini_device *pgmn_dev,
+ struct msm_gemini_core_buf *buf_in)
+{
+ int rc = 0;
+ struct msm_gemini_core_buf *buf_out;
+
+ GMN_DBG("%s:%d] Enter mode %d", __func__, __LINE__,
+ pgmn_dev->out_mode);
+
+ if (pgmn_dev->out_mode == MSM_GMN_OUTMODE_SINGLE)
+ return msm_gemini_outmode_single_we_pingpong_irq(pgmn_dev,
+ buf_in);
+
+ if (buf_in) {
+ GMN_DBG("%s:%d] 0x%08x %d\n", __func__, __LINE__,
+ (int) buf_in->y_buffer_addr, buf_in->y_len);
+ rc = msm_gemini_q_in_buf(&pgmn_dev->output_rtn_q, buf_in);
+ } else {
+ GMN_DBG("%s:%d] no output return buffer\n", __func__,
+ __LINE__);
+ rc = -1;
+ return rc;
+ }
+
+ buf_out = msm_gemini_q_out(&pgmn_dev->output_buf_q);
+
+ if (buf_out) {
+ rc = msm_gemini_core_we_buf_update(buf_out);
+ kfree(buf_out);
+ } else {
+ msm_gemini_core_we_buf_reset(buf_in);
+ GMN_DBG("%s:%d] no output buffer\n", __func__, __LINE__);
+ rc = -2;
+ }
+
+ if (buf_in)
+ rc = msm_gemini_q_wakeup(&pgmn_dev->output_rtn_q);
+
+ return rc;
+}
+
+int msm_gemini_output_get(struct msm_gemini_device *pgmn_dev, void __user *to)
+{
+ struct msm_gemini_core_buf *buf_p;
+ struct msm_gemini_buf buf_cmd;
+
+ GMN_DBG("%s:%d] Enter\n", __func__, __LINE__);
+
+ msm_gemini_q_wait(&pgmn_dev->output_rtn_q);
+ buf_p = msm_gemini_q_out(&pgmn_dev->output_rtn_q);
+
+ if (!buf_p) {
+ GMN_DBG("%s:%d] no output buffer return\n",
+ __func__, __LINE__);
+ return -EAGAIN;
+ }
+
+ buf_cmd = buf_p->vbuf;
+ msm_gemini_platform_p2v(buf_p->file, &buf_p->handle);
+ kfree(buf_p);
+
+ GMN_DBG("%s:%d] 0x%08x %d\n", __func__, __LINE__,
+ (int) buf_cmd.vaddr, buf_cmd.y_len);
+
+ if (copy_to_user(to, &buf_cmd, sizeof(buf_cmd))) {
+ GMN_PR_ERR("%s:%d]", __func__, __LINE__);
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+int msm_gemini_output_get_unblock(struct msm_gemini_device *pgmn_dev)
+{
+ GMN_DBG("%s:%d] Enter\n", __func__, __LINE__);
+ msm_gemini_q_unblock(&pgmn_dev->output_rtn_q);
+ return 0;
+}
+
+int msm_gemini_set_output_buf(struct msm_gemini_device *pgmn_dev,
+ void __user *arg)
+{
+ struct msm_gemini_buf buf_cmd;
+
+ if (pgmn_dev->out_buf_set) {
+ GMN_PR_ERR("%s:%d] outbuffer buffer already provided",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ if (copy_from_user(&buf_cmd, arg, sizeof(struct msm_gemini_buf))) {
+ GMN_PR_ERR("%s:%d] failed\n", __func__, __LINE__);
+ return -EFAULT;
+ }
+
+ GMN_DBG("%s:%d] output addr 0x%08x len %d", __func__, __LINE__,
+ (int) buf_cmd.vaddr,
+ buf_cmd.y_len);
+
+ pgmn_dev->out_buf.y_buffer_addr = msm_gemini_platform_v2p(
+ buf_cmd.fd,
+ buf_cmd.y_len,
+ &pgmn_dev->out_buf.file,
+ &pgmn_dev->out_buf.handle);
+ if (!pgmn_dev->out_buf.y_buffer_addr) {
+ GMN_PR_ERR("%s:%d] cannot map the output address",
+ __func__, __LINE__);
+ return -EFAULT;
+ }
+ pgmn_dev->out_buf.y_len = buf_cmd.y_len;
+ pgmn_dev->out_buf.vbuf = buf_cmd;
+ pgmn_dev->out_buf_set = 1;
+
+ return 0;
+}
+
+int msm_gemini_output_buf_enqueue(struct msm_gemini_device *pgmn_dev,
+ void __user *arg)
+{
+ struct msm_gemini_buf buf_cmd;
+ struct msm_gemini_core_buf *buf_p;
+
+ GMN_DBG("%s:%d] Enter\n", __func__, __LINE__);
+ if (copy_from_user(&buf_cmd, arg, sizeof(struct msm_gemini_buf))) {
+ GMN_PR_ERR("%s:%d] failed\n", __func__, __LINE__);
+ return -EFAULT;
+ }
+
+ buf_p = kmalloc(sizeof(struct msm_gemini_core_buf), GFP_ATOMIC);
+ if (!buf_p) {
+ GMN_PR_ERR("%s:%d] no mem\n", __func__, __LINE__);
+ return -ENOMEM;
+ }
+
+ GMN_DBG("%s:%d] 0x%08x %d\n", __func__, __LINE__, (int) buf_cmd.vaddr,
+ buf_cmd.y_len);
+
+ buf_p->y_buffer_addr = msm_gemini_platform_v2p(buf_cmd.fd,
+ buf_cmd.y_len, &buf_p->file, &buf_p->handle);
+ if (!buf_p->y_buffer_addr) {
+ GMN_PR_ERR("%s:%d] v2p wrong\n", __func__, __LINE__);
+ kfree(buf_p);
+ return -ENOMEM;
+ }
+ buf_p->y_len = buf_cmd.y_len;
+ buf_p->vbuf = buf_cmd;
+
+ msm_gemini_q_in(&pgmn_dev->output_buf_q, buf_p);
+ return 0;
+}
+
+/*************** input queue ****************/
+
+int msm_gemini_fe_pingpong_irq(struct msm_gemini_device *pgmn_dev,
+ struct msm_gemini_core_buf *buf_in)
+{
+ struct msm_gemini_core_buf *buf_out;
+ int rc = 0;
+
+ GMN_DBG("%s:%d] Enter\n", __func__, __LINE__);
+ if (buf_in) {
+ GMN_DBG("%s:%d] 0x%08x %d\n", __func__, __LINE__,
+ (int) buf_in->y_buffer_addr, buf_in->y_len);
+ rc = msm_gemini_q_in_buf(&pgmn_dev->input_rtn_q, buf_in);
+ } else {
+ GMN_DBG("%s:%d] no input return buffer\n", __func__,
+ __LINE__);
+ rc = -1;
+ }
+
+ buf_out = msm_gemini_q_out(&pgmn_dev->input_buf_q);
+
+ if (buf_out) {
+ rc = msm_gemini_core_fe_buf_update(buf_out);
+ kfree(buf_out);
+ msm_gemini_core_fe_start();
+ } else {
+ GMN_DBG("%s:%d] no input buffer\n", __func__, __LINE__);
+ rc = -2;
+ }
+
+ if (buf_in)
+ rc = msm_gemini_q_wakeup(&pgmn_dev->input_rtn_q);
+
+ return rc;
+}
+
+int msm_gemini_input_get(struct msm_gemini_device *pgmn_dev, void __user *to)
+{
+ struct msm_gemini_core_buf *buf_p;
+ struct msm_gemini_buf buf_cmd;
+
+ GMN_DBG("%s:%d] Enter\n", __func__, __LINE__);
+ msm_gemini_q_wait(&pgmn_dev->input_rtn_q);
+ buf_p = msm_gemini_q_out(&pgmn_dev->input_rtn_q);
+
+ if (!buf_p) {
+ GMN_DBG("%s:%d] no input buffer return\n",
+ __func__, __LINE__);
+ return -EAGAIN;
+ }
+
+ buf_cmd = buf_p->vbuf;
+ if (pgmn_dev->op_mode == MSM_GEMINI_MODE_OFFLINE_ENCODE ||
+ pgmn_dev->op_mode == MSM_GEMINI_MODE_OFFLINE_ROTATION) {
+ msm_gemini_platform_p2v(buf_p->file, &buf_p->handle);
+ }
+ kfree(buf_p);
+
+ GMN_DBG("%s:%d] 0x%08x %d\n", __func__, __LINE__,
+ (int) buf_cmd.vaddr, buf_cmd.y_len);
+
+ if (copy_to_user(to, &buf_cmd, sizeof(buf_cmd))) {
+ GMN_PR_ERR("%s:%d]\n", __func__, __LINE__);
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+int msm_gemini_input_get_unblock(struct msm_gemini_device *pgmn_dev)
+{
+ GMN_DBG("%s:%d] Enter\n", __func__, __LINE__);
+ msm_gemini_q_unblock(&pgmn_dev->input_rtn_q);
+ return 0;
+}
+
+int msm_gemini_input_buf_enqueue(struct msm_gemini_device *pgmn_dev,
+ void __user *arg)
+{
+ struct msm_gemini_core_buf *buf_p;
+ struct msm_gemini_buf buf_cmd;
+ int rc = 0;
+ struct msm_bus_scale_pdata *p_bus_scale_data = NULL;
+
+ if (copy_from_user(&buf_cmd, arg, sizeof(struct msm_gemini_buf))) {
+ GMN_PR_ERR("%s:%d] failed\n", __func__, __LINE__);
+ return -EFAULT;
+ }
+
+ buf_p = kmalloc(sizeof(struct msm_gemini_core_buf), GFP_ATOMIC);
+ if (!buf_p) {
+ GMN_PR_ERR("%s:%d] no mem\n", __func__, __LINE__);
+ return -EFAULT;
+ }
+
+ GMN_DBG("%s:%d] 0x%08x %d\n", __func__, __LINE__,
+ (int) buf_cmd.vaddr, buf_cmd.y_len);
+
+ if (pgmn_dev->op_mode == MSM_GEMINI_MODE_REALTIME_ENCODE) {
+ rc = msm_iommu_map_contig_buffer(
+ (unsigned long)buf_cmd.y_off, CAMERA_DOMAIN, GEN_POOL,
+ ((buf_cmd.y_len + buf_cmd.cbcr_len + 4095) & (~4095)),
+ SZ_4K, IOMMU_WRITE | IOMMU_READ,
+ (unsigned long *)&buf_p->y_buffer_addr);
+ if (rc < 0) {
+ GMN_PR_ERR("%s iommu mapping failed with error %d\n",
+ __func__, rc);
+ kfree(buf_p);
+ return rc;
+ }
+ } else {
+ buf_p->y_buffer_addr = msm_gemini_platform_v2p(buf_cmd.fd,
+ buf_cmd.y_len + buf_cmd.cbcr_len, &buf_p->file,
+ &buf_p->handle) + buf_cmd.offset + buf_cmd.y_off;
+ }
+ buf_p->y_len = buf_cmd.y_len;
+
+ buf_p->cbcr_buffer_addr = buf_p->y_buffer_addr + buf_cmd.y_len +
+ buf_cmd.cbcr_off;
+ buf_p->cbcr_len = buf_cmd.cbcr_len;
+ buf_p->num_of_mcu_rows = buf_cmd.num_of_mcu_rows;
+ GMN_DBG("%s: y_addr=%x,y_len=%x,cbcr_addr=%x,cbcr_len=%x\n", __func__,
+ buf_p->y_buffer_addr, buf_p->y_len, buf_p->cbcr_buffer_addr,
+ buf_p->cbcr_len);
+
+ if (!buf_p->y_buffer_addr || !buf_p->cbcr_buffer_addr) {
+ GMN_PR_ERR("%s:%d] v2p wrong\n", __func__, __LINE__);
+ kfree(buf_p);
+ return -EINVAL;
+ }
+ buf_p->vbuf = buf_cmd;
+ buf_p->vbuf.type = MSM_GEMINI_EVT_RESET;
+
+ /* Set bus vectors */
+ p_bus_scale_data = (struct msm_bus_scale_pdata *)
+ pgmn_dev->pdev->dev.platform_data;
+ if (pgmn_dev->bus_perf_client &&
+ (MSM_GMN_OUTMODE_SINGLE == pgmn_dev->out_mode)) {
+ int rc;
+ struct msm_bus_paths *path = &(p_bus_scale_data->usecase[1]);
+ GMN_DBG("%s:%d] Update bus bandwidth", __func__, __LINE__);
+ if (pgmn_dev->op_mode & MSM_GEMINI_MODE_OFFLINE_ENCODE) {
+ path->vectors[0].ab = (buf_p->y_len + buf_p->cbcr_len) *
+ 15 * 2;
+ path->vectors[0].ib = path->vectors[0].ab;
+ path->vectors[1].ab = 0;
+ path->vectors[1].ib = 0;
+ }
+ rc = msm_bus_scale_client_update_request(
+ pgmn_dev->bus_perf_client, 1);
+ if (rc < 0) {
+ GMN_PR_ERR("%s:%d] update_request fails %d",
+ __func__, __LINE__, rc);
+ }
+ }
+
+ msm_gemini_q_in(&pgmn_dev->input_buf_q, buf_p);
+
+ return 0;
+}
+
+int msm_gemini_irq(int event, void *context, void *data)
+{
+ struct msm_gemini_device *pgmn_dev =
+ (struct msm_gemini_device *) context;
+
+ switch (event) {
+ case MSM_GEMINI_HW_MASK_COMP_FRAMEDONE:
+ msm_gemini_framedone_irq(pgmn_dev, data);
+ msm_gemini_we_pingpong_irq(pgmn_dev, data);
+ break;
+
+ case MSM_GEMINI_HW_MASK_COMP_FE:
+ msm_gemini_fe_pingpong_irq(pgmn_dev, data);
+ break;
+
+ case MSM_GEMINI_HW_MASK_COMP_WE:
+ msm_gemini_we_pingpong_irq(pgmn_dev, data);
+ break;
+
+ case MSM_GEMINI_HW_MASK_COMP_RESET_ACK:
+ msm_gemini_reset_ack_irq(pgmn_dev);
+ break;
+
+ case MSM_GEMINI_HW_MASK_COMP_ERR:
+ default:
+ msm_gemini_err_irq(pgmn_dev, event);
+ break;
+ }
+
+ return 0;
+}
+
+int __msm_gemini_open(struct msm_gemini_device *pgmn_dev)
+{
+ int rc;
+ struct msm_bus_scale_pdata *p_bus_scale_data =
+ (struct msm_bus_scale_pdata *)pgmn_dev->pdev->dev.
+ platform_data;
+
+ mutex_lock(&pgmn_dev->lock);
+ if (pgmn_dev->open_count) {
+ /* only open once */
+ GMN_PR_ERR("%s:%d] busy\n", __func__, __LINE__);
+ mutex_unlock(&pgmn_dev->lock);
+ return -EBUSY;
+ }
+ pgmn_dev->open_count++;
+ mutex_unlock(&pgmn_dev->lock);
+
+ msm_gemini_core_irq_install(msm_gemini_irq);
+
+
+ rc = msm_gemini_platform_init(pgmn_dev->pdev,
+ &pgmn_dev->mem, &pgmn_dev->base,
+ &pgmn_dev->irq, msm_gemini_core_irq, pgmn_dev);
+ if (rc) {
+ GMN_PR_ERR("%s:%d] platform_init fail %d\n", __func__,
+ __LINE__, rc);
+ return rc;
+ }
+
+ GMN_DBG("%s:%d] platform resources - mem %p, base %p, irq %d\n",
+ __func__, __LINE__,
+ pgmn_dev->mem, pgmn_dev->base, pgmn_dev->irq);
+
+ msm_gemini_q_cleanup(&pgmn_dev->evt_q);
+ msm_gemini_q_cleanup(&pgmn_dev->output_rtn_q);
+ msm_gemini_outbuf_q_cleanup(&pgmn_dev->output_buf_q);
+ msm_gemini_q_cleanup(&pgmn_dev->input_rtn_q);
+ msm_gemini_q_cleanup(&pgmn_dev->input_buf_q);
+ msm_gemini_core_init();
+ pgmn_dev->out_mode = MSM_GMN_OUTMODE_FRAGMENTED;
+ pgmn_dev->out_buf_set = 0;
+ pgmn_dev->out_offset = 0;
+ pgmn_dev->max_out_size = g_max_out_size;
+ pgmn_dev->out_frag_cnt = 0;
+ pgmn_dev->bus_perf_client = 0;
+
+ if (p_bus_scale_data) {
+ GMN_DBG("%s:%d] register bus client", __func__, __LINE__);
+ pgmn_dev->bus_perf_client =
+ msm_bus_scale_register_client(p_bus_scale_data);
+ if (!pgmn_dev->bus_perf_client) {
+ GMN_PR_ERR("%s:%d] bus client register failed",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ }
+ GMN_DBG("%s:%d] success\n", __func__, __LINE__);
+ return rc;
+}
+
+int __msm_gemini_release(struct msm_gemini_device *pgmn_dev)
+{
+ GMN_DBG("%s:%d] Enter\n", __func__, __LINE__);
+ mutex_lock(&pgmn_dev->lock);
+ if (!pgmn_dev->open_count) {
+ GMN_PR_ERR("%s: not opened\n", __func__);
+ mutex_unlock(&pgmn_dev->lock);
+ return -EINVAL;
+ }
+ pgmn_dev->open_count--;
+ mutex_unlock(&pgmn_dev->lock);
+
+ if (pgmn_dev->out_mode == MSM_GMN_OUTMODE_FRAGMENTED) {
+ msm_gemini_core_release(release_buf);
+ } else if (pgmn_dev->out_buf_set) {
+ msm_gemini_platform_p2v(pgmn_dev->out_buf.file,
+ &pgmn_dev->out_buf.handle);
+ }
+ msm_gemini_q_cleanup(&pgmn_dev->evt_q);
+ msm_gemini_q_cleanup(&pgmn_dev->output_rtn_q);
+ msm_gemini_outbuf_q_cleanup(&pgmn_dev->output_buf_q);
+ msm_gemini_q_cleanup(&pgmn_dev->input_rtn_q);
+ msm_gemini_outbuf_q_cleanup(&pgmn_dev->input_buf_q);
+
+ if (pgmn_dev->bus_perf_client) {
+ msm_bus_scale_unregister_client(pgmn_dev->bus_perf_client);
+ pgmn_dev->bus_perf_client = 0;
+ }
+
+ if (pgmn_dev->open_count)
+ GMN_PR_ERR("%s: multiple opens\n", __func__);
+
+ msm_gemini_platform_release(pgmn_dev->mem, pgmn_dev->base,
+ pgmn_dev->irq, pgmn_dev);
+
+ return 0;
+}
+
+int msm_gemini_ioctl_hw_cmd(struct msm_gemini_device *pgmn_dev,
+ void * __user arg)
+{
+ struct msm_gemini_hw_cmd hw_cmd;
+ int is_copy_to_user;
+
+ if (copy_from_user(&hw_cmd, arg, sizeof(struct msm_gemini_hw_cmd))) {
+ GMN_PR_ERR("%s:%d] failed\n", __func__, __LINE__);
+ return -EFAULT;
+ }
+
+ is_copy_to_user = msm_gemini_hw_exec_cmds(&hw_cmd, 1);
+ GMN_DBG("%s:%d] type %d, n %d, offset %d, mask %x, data %x, pdata %x\n",
+ __func__, __LINE__, hw_cmd.type, hw_cmd.n, hw_cmd.offset,
+ hw_cmd.mask, hw_cmd.data, (int) hw_cmd.pdata);
+
+ if (is_copy_to_user >= 0) {
+ if (copy_to_user(arg, &hw_cmd, sizeof(hw_cmd))) {
+ GMN_PR_ERR("%s:%d] failed\n", __func__, __LINE__);
+ return -EFAULT;
+ }
+ }
+
+ return 0;
+}
+
+static int msm_gemini_ioctl_hw_cmds(struct msm_gemini_device *pgmn_dev,
+ void * __user arg)
+{
+ int is_copy_to_user;
+ int len;
+ uint32_t m;
+ struct msm_gemini_hw_cmds *hw_cmds_p;
+ struct msm_gemini_hw_cmd *hw_cmd_p;
+
+ if (copy_from_user(&m, arg, sizeof(m))) {
+ GMN_PR_ERR("%s:%d] failed\n", __func__, __LINE__);
+ return -EFAULT;
+ }
+
+ len = sizeof(struct msm_gemini_hw_cmds) +
+ sizeof(struct msm_gemini_hw_cmd) * (m - 1);
+ hw_cmds_p = kmalloc(len, GFP_KERNEL);
+ if (!hw_cmds_p) {
+ GMN_PR_ERR("%s:%d] no mem %d\n", __func__, __LINE__, len);
+ return -EFAULT;
+ }
+
+ if (copy_from_user(hw_cmds_p, arg, len)) {
+ GMN_PR_ERR("%s:%d] failed\n", __func__, __LINE__);
+ kfree(hw_cmds_p);
+ return -EFAULT;
+ }
+
+ hw_cmd_p = (struct msm_gemini_hw_cmd *) &(hw_cmds_p->hw_cmd);
+
+ is_copy_to_user = msm_gemini_hw_exec_cmds(hw_cmd_p, m);
+
+ if (is_copy_to_user >= 0) {
+ if (copy_to_user(arg, hw_cmds_p, len)) {
+ GMN_PR_ERR("%s:%d] failed\n", __func__, __LINE__);
+ kfree(hw_cmds_p);
+ return -EFAULT;
+ }
+ }
+ kfree(hw_cmds_p);
+ return 0;
+}
+
+static int msm_gemini_start(struct msm_gemini_device *pgmn_dev,
+ void * __user arg)
+{
+ struct msm_gemini_core_buf *buf_out;
+ struct msm_gemini_core_buf *buf_out_free[2] = {NULL, NULL};
+ int i, rc;
+
+ GMN_DBG("%s:%d] Enter\n", __func__, __LINE__);
+
+ release_buf = 1;
+ for (i = 0; i < 2; i++) {
+ buf_out = msm_gemini_q_out(&pgmn_dev->input_buf_q);
+
+ if (buf_out) {
+ msm_gemini_core_fe_buf_update(buf_out);
+ kfree(buf_out);
+ } else {
+ GMN_DBG("%s:%d] no input buffer\n", __func__, __LINE__);
+ break;
+ }
+ }
+
+ if (pgmn_dev->out_mode == MSM_GMN_OUTMODE_FRAGMENTED) {
+ for (i = 0; i < 2; i++) {
+ buf_out_free[i] =
+ msm_gemini_q_out(&pgmn_dev->output_buf_q);
+
+ if (buf_out_free[i]) {
+ msm_gemini_core_we_buf_update(buf_out_free[i]);
+ } else if (i == 1) {
+ /* set the pong to same address as ping */
+ buf_out_free[0]->y_len >>= 1;
+ buf_out_free[0]->y_buffer_addr +=
+ buf_out_free[0]->y_len;
+ msm_gemini_core_we_buf_update(buf_out_free[0]);
+ /*
+ * since ping and pong are same buf
+ * release only once
+ */
+ release_buf = 0;
+ } else {
+ GMN_DBG("%s:%d] no output buffer\n",
+ __func__, __LINE__);
+ break;
+ }
+ }
+ for (i = 0; i < 2; i++)
+ kfree(buf_out_free[i]);
+ } else {
+ struct msm_gemini_core_buf out_buf;
+ /*
+ * Since the same buffer is fragmented, p2v need not be
+ * called for all the buffers
+ */
+ release_buf = 0;
+ if (!pgmn_dev->out_buf_set) {
+ GMN_PR_ERR("%s:%d] output buffer not set",
+ __func__, __LINE__);
+ return -EFAULT;
+ }
+ /* configure ping */
+ rc = msm_gemini_get_out_buffer(pgmn_dev, &out_buf);
+ if (rc) {
+ GMN_PR_ERR("%s:%d] no output buffer for ping",
+ __func__, __LINE__);
+ return rc;
+ }
+ msm_gemini_core_we_buf_update(&out_buf);
+ /* configure pong */
+ rc = msm_gemini_get_out_buffer(pgmn_dev, &out_buf);
+ if (rc) {
+ GMN_DBG("%s:%d] no output buffer for pong",
+ __func__, __LINE__);
+ /* fall through to configure same buffer */
+ }
+ msm_gemini_core_we_buf_update(&out_buf);
+ msm_gemini_io_dump(0x150);
+ }
+
+ rc = msm_gemini_ioctl_hw_cmds(pgmn_dev, arg);
+ GMN_DBG("%s:%d]\n", __func__, __LINE__);
+ return rc;
+}
+
+static int msm_gemini_ioctl_reset(struct msm_gemini_device *pgmn_dev,
+ void * __user arg)
+{
+ int rc;
+ struct msm_gemini_ctrl_cmd ctrl_cmd;
+
+ GMN_DBG("%s:%d] Enter\n", __func__, __LINE__);
+ if (copy_from_user(&ctrl_cmd, arg, sizeof(ctrl_cmd))) {
+ GMN_PR_ERR("%s:%d] failed\n", __func__, __LINE__);
+ return -EFAULT;
+ }
+
+ pgmn_dev->op_mode = ctrl_cmd.type;
+
+ rc = msm_gemini_core_reset(pgmn_dev->op_mode, pgmn_dev->base,
+ resource_size(pgmn_dev->mem));
+ return rc;
+}
+
+static int msm_gemini_ioctl_set_outmode(struct msm_gemini_device *pgmn_dev,
+ void * __user arg)
+{
+ int rc = 0;
+ enum msm_gmn_out_mode mode;
+
+ if (copy_from_user(&mode, arg, sizeof(mode))) {
+ GMN_PR_ERR("%s:%d] failed\n", __func__, __LINE__);
+ return -EFAULT;
+ }
+ GMN_DBG("%s:%d] mode %d", __func__, __LINE__, mode);
+
+ if ((mode == MSM_GMN_OUTMODE_FRAGMENTED)
+ || (mode == MSM_GMN_OUTMODE_SINGLE))
+ pgmn_dev->out_mode = mode;
+ return rc;
+}
+
+long __msm_gemini_ioctl(struct msm_gemini_device *pgmn_dev,
+ unsigned int cmd, unsigned long arg)
+{
+ int rc = 0;
+ switch (cmd) {
+ case MSM_GMN_IOCTL_GET_HW_VERSION:
+ GMN_DBG("%s:%d] VERSION 1\n", __func__, __LINE__);
+ rc = msm_gemini_ioctl_hw_cmd(pgmn_dev, (void __user *) arg);
+ break;
+
+ case MSM_GMN_IOCTL_RESET:
+ rc = msm_gemini_ioctl_reset(pgmn_dev, (void __user *) arg);
+ break;
+
+ case MSM_GMN_IOCTL_STOP:
+ rc = msm_gemini_ioctl_hw_cmds(pgmn_dev, (void __user *) arg);
+ break;
+
+ case MSM_GMN_IOCTL_START:
+ rc = msm_gemini_start(pgmn_dev, (void __user *) arg);
+ break;
+
+ case MSM_GMN_IOCTL_INPUT_BUF_ENQUEUE:
+ rc = msm_gemini_input_buf_enqueue(pgmn_dev,
+ (void __user *) arg);
+ break;
+
+ case MSM_GMN_IOCTL_INPUT_GET:
+ rc = msm_gemini_input_get(pgmn_dev, (void __user *) arg);
+ break;
+
+ case MSM_GMN_IOCTL_INPUT_GET_UNBLOCK:
+ rc = msm_gemini_input_get_unblock(pgmn_dev);
+ break;
+
+ case MSM_GMN_IOCTL_OUTPUT_BUF_ENQUEUE:
+ if (pgmn_dev->out_mode == MSM_GMN_OUTMODE_FRAGMENTED)
+ rc = msm_gemini_output_buf_enqueue(pgmn_dev,
+ (void __user *) arg);
+ else
+ rc = msm_gemini_set_output_buf(pgmn_dev,
+ (void __user *) arg);
+ break;
+
+ case MSM_GMN_IOCTL_OUTPUT_GET:
+ rc = msm_gemini_output_get(pgmn_dev, (void __user *) arg);
+ break;
+
+ case MSM_GMN_IOCTL_OUTPUT_GET_UNBLOCK:
+ rc = msm_gemini_output_get_unblock(pgmn_dev);
+ break;
+
+ case MSM_GMN_IOCTL_EVT_GET:
+ rc = msm_gemini_evt_get(pgmn_dev, (void __user *) arg);
+ break;
+
+ case MSM_GMN_IOCTL_EVT_GET_UNBLOCK:
+ rc = msm_gemini_evt_get_unblock(pgmn_dev);
+ break;
+
+ case MSM_GMN_IOCTL_HW_CMD:
+ rc = msm_gemini_ioctl_hw_cmd(pgmn_dev, (void __user *) arg);
+ break;
+
+ case MSM_GMN_IOCTL_HW_CMDS:
+ rc = msm_gemini_ioctl_hw_cmds(pgmn_dev, (void __user *) arg);
+ break;
+
+ case MSM_GMN_IOCTL_SET_MODE:
+ rc = msm_gemini_ioctl_set_outmode(pgmn_dev, (void __user *)arg);
+ break;
+
+ default:
+ GMN_PR_ERR("%s:%d] cmd = %d not supported\n",
+ __func__, __LINE__, _IOC_NR(cmd));
+ rc = -EINVAL;
+ break;
+ }
+ return rc;
+}
+
+struct msm_gemini_device *__msm_gemini_init(struct platform_device *pdev)
+{
+ struct msm_gemini_device *pgmn_dev;
+
+ pgmn_dev = kzalloc(sizeof(struct msm_gemini_device), GFP_ATOMIC);
+ if (!pgmn_dev) {
+ GMN_PR_ERR("%s:%d]no mem\n", __func__, __LINE__);
+ return NULL;
+ }
+
+ mutex_init(&pgmn_dev->lock);
+
+ pgmn_dev->pdev = pdev;
+
+ msm_gemini_q_init("evt_q", &pgmn_dev->evt_q);
+ msm_gemini_q_init("output_rtn_q", &pgmn_dev->output_rtn_q);
+ msm_gemini_q_init("output_buf_q", &pgmn_dev->output_buf_q);
+ msm_gemini_q_init("input_rtn_q", &pgmn_dev->input_rtn_q);
+ msm_gemini_q_init("input_buf_q", &pgmn_dev->input_buf_q);
+
+ return pgmn_dev;
+}
+
+int __msm_gemini_exit(struct msm_gemini_device *pgmn_dev)
+{
+ mutex_destroy(&pgmn_dev->lock);
+ kfree(pgmn_dev);
+ return 0;
+}
+
diff --git a/drivers/media/platform/msm/camera_v2/gemini/msm_gemini_sync.h b/drivers/media/platform/msm/camera_v2/gemini/msm_gemini_sync.h
new file mode 100644
index 0000000..6982a78
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/gemini/msm_gemini_sync.h
@@ -0,0 +1,98 @@
+/* Copyright (c) 2010,2013, 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_GEMINI_SYNC_H
+#define MSM_GEMINI_SYNC_H
+
+#include <linux/fs.h>
+#include <linux/list.h>
+#include <linux/cdev.h>
+#include <linux/platform_device.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+#include "msm_gemini_core.h"
+
+#define GEMINI_7X 0x1
+#define GEMINI_8X60 (0x1 << 1)
+#define GEMINI_8960 (0x1 << 2)
+
+struct msm_gemini_q {
+ char const *name;
+ struct list_head q;
+ spinlock_t lck;
+ wait_queue_head_t wait;
+ int unblck;
+};
+
+struct msm_gemini_q_entry {
+ struct list_head list;
+ void *data;
+};
+
+struct msm_gemini_device {
+ struct platform_device *pdev;
+ struct resource *mem;
+ int irq;
+ void *base;
+ struct clk *gemini_clk[3];
+ struct regulator *gemini_fs;
+ uint32_t hw_version;
+
+ struct device *device;
+ struct cdev cdev;
+ struct mutex lock;
+ char open_count;
+ uint8_t op_mode;
+
+ /* event queue including frame done & err indications
+ */
+ struct msm_gemini_q evt_q;
+
+ /* output return queue
+ */
+ struct msm_gemini_q output_rtn_q;
+
+ /* output buf queue
+ */
+ struct msm_gemini_q output_buf_q;
+
+ /* input return queue
+ */
+ struct msm_gemini_q input_rtn_q;
+
+ /* input buf queue
+ */
+ struct msm_gemini_q input_buf_q;
+
+ struct v4l2_subdev subdev;
+ enum msm_gmn_out_mode out_mode;
+
+ /*single out mode parameters*/
+ struct msm_gemini_hw_buf out_buf;
+ int out_offset;
+ int out_buf_set;
+ int max_out_size;
+ int out_frag_cnt;
+
+ uint32_t bus_perf_client;
+};
+
+int __msm_gemini_open(struct msm_gemini_device *pgmn_dev);
+int __msm_gemini_release(struct msm_gemini_device *pgmn_dev);
+
+long __msm_gemini_ioctl(struct msm_gemini_device *pgmn_dev,
+ unsigned int cmd, unsigned long arg);
+
+struct msm_gemini_device *__msm_gemini_init(struct platform_device *pdev);
+int __msm_gemini_exit(struct msm_gemini_device *pgmn_dev);
+
+#endif /* MSM_GEMINI_SYNC_H */
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c
index 731056b..756cb41 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c
@@ -520,26 +520,37 @@
comp_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x40);
comp_mask &= ~(0x7F << (comp_mask_index * 8));
comp_mask |= (axi_data->composite_info[comp_mask_index].
- stream_composite_mask << (comp_mask_index * 8));
+ stream_composite_mask << (comp_mask_index * 8));
+ if (stream_info->plane_offset[0])
+ comp_mask |= (axi_data->composite_info[comp_mask_index].
+ stream_composite_mask << 24);
msm_camera_io_w(comp_mask, vfe_dev->vfe_base + 0x40);
irq_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x28);
irq_mask |= 1 << (comp_mask_index + 25);
+ if (stream_info->plane_offset[0] && (comp_mask >> 24))
+ irq_mask |= BIT(28);
msm_camera_io_w(irq_mask, vfe_dev->vfe_base + 0x28);
}
static void msm_vfe40_axi_clear_comp_mask(struct vfe_device *vfe_dev,
struct msm_vfe_axi_stream *stream_info)
{
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
uint32_t comp_mask, comp_mask_index = stream_info->comp_mask_index;
uint32_t irq_mask;
comp_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x40);
comp_mask &= ~(0x7F << (comp_mask_index * 8));
+ if (stream_info->plane_offset[0])
+ comp_mask &= ~(axi_data->composite_info[comp_mask_index].
+ stream_composite_mask << 24);
msm_camera_io_w(comp_mask, vfe_dev->vfe_base + 0x40);
irq_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x28);
irq_mask &= ~(1 << (comp_mask_index + 25));
+ if (stream_info->plane_offset[0] && !(comp_mask >> 24))
+ irq_mask &= ~BIT(28);
msm_camera_io_w(irq_mask, vfe_dev->vfe_base + 0x28);
}
@@ -1219,7 +1230,7 @@
static struct msm_vfe_axi_hardware_info msm_vfe40_axi_hw_info = {
.num_wm = 4,
- .num_comp_mask = 4,
+ .num_comp_mask = 3,
.num_rdi = 3,
.num_rdi_master = 3,
.min_wm_ub = 64,
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c
index 613ad86..dd8db03 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c
@@ -230,12 +230,12 @@
return rc;
}
-static int msm_isp_set_clk_rate(struct vfe_device *vfe_dev, uint32_t rate)
+static int msm_isp_set_clk_rate(struct vfe_device *vfe_dev, long *rate)
{
int rc = 0;
int clk_idx = vfe_dev->hw_info->vfe_clk_idx;
long round_rate =
- clk_round_rate(vfe_dev->vfe_clk[clk_idx], rate);
+ clk_round_rate(vfe_dev->vfe_clk[clk_idx], *rate);
if (round_rate < 0) {
pr_err("%s: Invalid vfe clock rate\n", __func__);
return round_rate;
@@ -246,6 +246,7 @@
pr_err("%s: Vfe set rate error\n", __func__);
return rc;
}
+ *rate = round_rate;
return 0;
}
@@ -266,7 +267,7 @@
input_cfg->d.pix_cfg.camif_cfg.pixels_per_line;
rc = msm_isp_set_clk_rate(vfe_dev,
- vfe_dev->axi_data.src_info[VFE_PIX_0].pixel_clock);
+ &vfe_dev->axi_data.src_info[VFE_PIX_0].pixel_clock);
if (rc < 0) {
pr_err("%s: clock set rate failed\n", __func__);
return rc;
diff --git a/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_platform.c b/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_platform.c
index 59b9746..509567c 100644
--- a/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_platform.c
+++ b/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_platform.c
@@ -70,7 +70,7 @@
}
static struct msm_cam_clk_info jpeg_8x_clk_info[] = {
- {"core_clk", 228570000},
+ {"core_clk", JPEG_CLK_RATE},
{"iface_clk", -1},
{"bus_clk0", -1},
{"camss_top_ahb_clk", -1},
@@ -135,8 +135,8 @@
{
.src = MSM_BUS_MASTER_JPEG,
.dst = MSM_BUS_SLAVE_EBI_CH0,
- .ab = 1027648000,
- .ib = 1105920000,
+ .ab = JPEG_CLK_RATE * 2.5,
+ .ib = JPEG_CLK_RATE * 2.5,
},
};
diff --git a/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_platform.h b/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_platform.h
index cd80d2e..a14b8ee 100644
--- a/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_platform.h
+++ b/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_platform.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -19,6 +19,7 @@
#include <linux/iommu.h>
#include <mach/iommu.h>
#include "msm_jpeg_sync.h"
+#define JPEG_CLK_RATE 266670000
void msm_jpeg_platform_p2v(struct msm_jpeg_device *pgmn_dev, struct file *file,
struct ion_handle **ionhandle, int domain_num);
diff --git a/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cam_cci_hwreg.h b/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cam_cci_hwreg.h
index 642df76..591c464 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cam_cci_hwreg.h
+++ b/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cam_cci_hwreg.h
@@ -43,10 +43,6 @@
#define CCI_IRQ_MASK_0_RMSK 0x7fff7ff7
#define CCI_IRQ_CLEAR_0_ADDR 0x00000c08
#define CCI_IRQ_STATUS_0_ADDR 0x00000c0c
-#define CCI_IRQ_STATUS_0_I2C_M1_Q1_NACK_ERR_BMSK 0x40000000
-#define CCI_IRQ_STATUS_0_I2C_M1_Q0_NACK_ERR_BMSK 0x20000000
-#define CCI_IRQ_STATUS_0_I2C_M0_Q1_NACK_ERR_BMSK 0x10000000
-#define CCI_IRQ_STATUS_0_I2C_M0_Q0_NACK_ERR_BMSK 0x8000000
#define CCI_IRQ_STATUS_0_I2C_M1_Q0Q1_HALT_ACK_BMSK 0x4000000
#define CCI_IRQ_STATUS_0_I2C_M0_Q0Q1_HALT_ACK_BMSK 0x2000000
#define CCI_IRQ_STATUS_0_RST_DONE_ACK_BMSK 0x1000000
@@ -55,6 +51,8 @@
#define CCI_IRQ_STATUS_0_I2C_M1_RD_DONE_BMSK 0x1000
#define CCI_IRQ_STATUS_0_I2C_M0_Q1_REPORT_BMSK 0x100
#define CCI_IRQ_STATUS_0_I2C_M0_Q0_REPORT_BMSK 0x10
+#define CCI_IRQ_STATUS_0_I2C_M0_ERROR_BMSK 0x18000EE6
+#define CCI_IRQ_STATUS_0_I2C_M1_ERROR_BMSK 0x60EE6000
#define CCI_IRQ_STATUS_0_I2C_M0_RD_DONE_BMSK 0x1
#define CCI_IRQ_GLOBAL_CLEAR_CMD_ADDR 0x00000c00
#endif /* __MSM_CAM_CCI_HWREG__ */
diff --git a/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c b/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c
index 9300ce0..3409b3e 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c
@@ -271,6 +271,21 @@
master = c_ctrl->cci_info->cci_i2c_master;
read_cfg = &c_ctrl->cfg.cci_i2c_read_cfg;
mutex_lock(&cci_dev->cci_master_info[master].mutex);
+
+ /*
+ * Call validate queue to make sure queue is empty before starting.
+ * If this call fails, don't proceed with i2c_read call. This is to
+ * avoid overflow / underflow of queue
+ */
+ rc = msm_cci_validate_queue(cci_dev,
+ cci_dev->cci_i2c_queue_info[master][queue].max_queue_size - 1,
+ master, queue);
+ if (rc < 0) {
+ pr_err("%s:%d Initial validataion failed rc %d\n", __func__,
+ __LINE__, rc);
+ goto ERROR;
+ }
+
CDBG("%s master %d, queue %d\n", __func__, master, queue);
CDBG("%s set param sid 0x%x retries %d id_map %d\n", __func__,
c_ctrl->cci_info->sid, c_ctrl->cci_info->retries,
@@ -450,6 +465,21 @@
c_ctrl->cci_info->sid, c_ctrl->cci_info->retries,
c_ctrl->cci_info->id_map);
mutex_lock(&cci_dev->cci_master_info[master].mutex);
+
+ /*
+ * Call validate queue to make sure queue is empty before starting.
+ * If this call fails, don't proceed with i2c_write call. This is to
+ * avoid overflow / underflow of queue
+ */
+ rc = msm_cci_validate_queue(cci_dev,
+ cci_dev->cci_i2c_queue_info[master][queue].max_queue_size - 1,
+ master, queue);
+ if (rc < 0) {
+ pr_err("%s:%d Initial validataion failed rc %d\n", __func__,
+ __LINE__, rc);
+ goto ERROR;
+ }
+
val = CCI_I2C_SET_PARAM_CMD | c_ctrl->cci_info->sid << 4 |
c_ctrl->cci_info->retries << 16 |
c_ctrl->cci_info->id_map << 18;
@@ -694,16 +724,6 @@
(irq & CCI_IRQ_STATUS_0_I2C_M1_Q1_REPORT_BMSK)) {
cci_dev->cci_master_info[MASTER_1].status = 0;
complete(&cci_dev->cci_master_info[MASTER_1].reset_complete);
- } else if ((irq & CCI_IRQ_STATUS_0_I2C_M0_Q0_NACK_ERR_BMSK) ||
- (irq & CCI_IRQ_STATUS_0_I2C_M0_Q1_NACK_ERR_BMSK)) {
- cci_dev->cci_master_info[MASTER_0].status = -EINVAL;
- msm_camera_io_w(CCI_M0_HALT_REQ_RMSK,
- cci_dev->base + CCI_HALT_REQ_ADDR);
- } else if ((irq & CCI_IRQ_STATUS_0_I2C_M1_Q0_NACK_ERR_BMSK) ||
- (irq & CCI_IRQ_STATUS_0_I2C_M1_Q1_NACK_ERR_BMSK)) {
- cci_dev->cci_master_info[MASTER_1].status = -EINVAL;
- msm_camera_io_w(CCI_M1_HALT_REQ_RMSK,
- cci_dev->base + CCI_HALT_REQ_ADDR);
} else if (irq & CCI_IRQ_STATUS_0_I2C_M0_Q0Q1_HALT_ACK_BMSK) {
cci_dev->cci_master_info[MASTER_0].reset_pending = TRUE;
msm_camera_io_w(CCI_M0_RESET_RMSK,
@@ -712,6 +732,16 @@
cci_dev->cci_master_info[MASTER_1].reset_pending = TRUE;
msm_camera_io_w(CCI_M1_RESET_RMSK,
cci_dev->base + CCI_RESET_CMD_ADDR);
+ } else if (irq & CCI_IRQ_STATUS_0_I2C_M0_ERROR_BMSK) {
+ pr_err("%s:%d MASTER_0 error %x\n", __func__, __LINE__, irq);
+ cci_dev->cci_master_info[MASTER_0].status = -EINVAL;
+ msm_camera_io_w(CCI_M0_HALT_REQ_RMSK,
+ cci_dev->base + CCI_HALT_REQ_ADDR);
+ } else if (irq & CCI_IRQ_STATUS_0_I2C_M1_ERROR_BMSK) {
+ pr_err("%s:%d MASTER_1 error %x\n", __func__, __LINE__, irq);
+ cci_dev->cci_master_info[MASTER_1].status = -EINVAL;
+ msm_camera_io_w(CCI_M1_HALT_REQ_RMSK,
+ cci_dev->base + CCI_HALT_REQ_ADDR);
} else {
pr_err("%s unhandled irq 0x%x\n", __func__, irq);
cci_dev->cci_master_info[MASTER_0].status = 0;
diff --git a/drivers/media/platform/msm/camera_v2/sensor/eeprom/msm_eeprom.c b/drivers/media/platform/msm/camera_v2/sensor/eeprom/msm_eeprom.c
index 47e672d..3dd3a4e 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/eeprom/msm_eeprom.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/eeprom/msm_eeprom.c
@@ -42,13 +42,13 @@
e_ctrl->eboard_info->eeprom_name,
sizeof(cdata->cfg.eeprom_name));
break;
- case CFG_EEPROM_GET_DATA:
- CDBG("%s E CFG_EEPROM_GET_DATA\n", __func__);
+ case CFG_EEPROM_GET_CAL_DATA:
+ CDBG("%s E CFG_EEPROM_GET_CAL_DATA\n", __func__);
cdata->cfg.get_data.num_bytes =
e_ctrl->num_bytes;
break;
- case CFG_EEPROM_READ_DATA:
- CDBG("%s E CFG_EEPROM_READ_DATA\n", __func__);
+ case CFG_EEPROM_READ_CAL_DATA:
+ CDBG("%s E CFG_EEPROM_READ_CAL_DATA\n", __func__);
rc = copy_to_user(cdata->cfg.read_data.dbuffer,
e_ctrl->memory_data,
cdata->cfg.read_data.num_bytes);
diff --git a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c
index b56378a..5b385a0 100644
--- a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c
+++ b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c
@@ -4772,6 +4772,12 @@
mutex_lock(&mpq_demux->mutex);
mpq_feed = feed->priv;
+ if (!dvb_dmx_is_video_feed(feed) && !dvb_dmx_is_pcr_feed(feed) &&
+ !feed->secure_mode.is_secured) {
+ mutex_unlock(&mpq_demux->mutex);
+ return 0;
+ }
+
event.data_length = 0;
switch (cmd->type) {
diff --git a/drivers/media/platform/msm/vidc/hfi_packetization.c b/drivers/media/platform/msm/vidc/hfi_packetization.c
index 7fc8810..42d4f95 100644
--- a/drivers/media/platform/msm/vidc/hfi_packetization.c
+++ b/drivers/media/platform/msm/vidc/hfi_packetization.c
@@ -45,6 +45,22 @@
[ilog2(HAL_H264_CABAC_MODEL_2)] = HFI_H264_CABAC_MODEL_2,
};
+static int color_format[] = {
+ [ilog2(HAL_COLOR_FORMAT_MONOCHROME)] = HFI_COLOR_FORMAT_MONOCHROME,
+ [ilog2(HAL_COLOR_FORMAT_NV12)] = HFI_COLOR_FORMAT_NV12,
+ [ilog2(HAL_COLOR_FORMAT_NV21)] = HFI_COLOR_FORMAT_NV21,
+ [ilog2(HAL_COLOR_FORMAT_NV12_4x4TILE)] = HFI_COLOR_FORMAT_NV12_4x4TILE,
+ [ilog2(HAL_COLOR_FORMAT_NV21_4x4TILE)] = HFI_COLOR_FORMAT_NV21_4x4TILE,
+ [ilog2(HAL_COLOR_FORMAT_YUYV)] = HFI_COLOR_FORMAT_YUYV,
+ [ilog2(HAL_COLOR_FORMAT_YVYU)] = HFI_COLOR_FORMAT_YVYU,
+ [ilog2(HAL_COLOR_FORMAT_UYVY)] = HFI_COLOR_FORMAT_UYVY,
+ [ilog2(HAL_COLOR_FORMAT_VYUY)] = HFI_COLOR_FORMAT_VYUY,
+ [ilog2(HAL_COLOR_FORMAT_RGB565)] = HFI_COLOR_FORMAT_RGB565,
+ [ilog2(HAL_COLOR_FORMAT_BGR565)] = HFI_COLOR_FORMAT_BGR565,
+ [ilog2(HAL_COLOR_FORMAT_RGB888)] = HFI_COLOR_FORMAT_RGB888,
+ [ilog2(HAL_COLOR_FORMAT_BGR888)] = HFI_COLOR_FORMAT_BGR888,
+};
+
static inline int hal_to_hfi_type(int property, int hal_type)
{
if (hal_type && (roundup_pow_of_two(hal_type) != hal_type)) {
@@ -66,6 +82,9 @@
case HAL_PARAM_VENC_H264_ENTROPY_CABAC_MODEL:
return (hal_type >= ARRAY_SIZE(cabac_model)) ?
-ENOTSUPP : cabac_model[hal_type];
+ case HAL_PARAM_UNCOMPRESSED_FORMAT_SELECT:
+ return (hal_type >= ARRAY_SIZE(color_format)) ?
+ -ENOTSUPP : color_format[hal_type];
default:
return -ENOTSUPP;
}
@@ -316,6 +335,9 @@
case HAL_EXTRADATA_ASPECT_RATIO:
ret = HFI_PROPERTY_PARAM_INDEX_EXTRADATA;
break;
+ case HAL_EXTRADATA_MPEG2_SEQDISP:
+ ret = HFI_PROPERTY_PARAM_VDEC_MPEG2_SEQDISP_EXTRADATA;
+ break;
default:
dprintk(VIDC_WARN, "Extradata index not found: %d\n", index);
break;
@@ -629,11 +651,13 @@
hfi->buffer_type = buffer_type;
else
return -EINVAL;
- hfi->format = prop->format;
+ hfi->format = hal_to_hfi_type(
+ HAL_PARAM_UNCOMPRESSED_FORMAT_SELECT,
+ prop->format);
pkt->size += sizeof(u32) +
sizeof(struct hfi_uncompressed_format_select);
break;
- }
+ }
case HAL_PARAM_UNCOMPRESSED_PLANE_ACTUAL_CONSTRAINTS_INFO:
break;
case HAL_PARAM_UNCOMPRESSED_PLANE_ACTUAL_INFO:
diff --git a/drivers/media/platform/msm/vidc/hfi_response_handler.c b/drivers/media/platform/msm/vidc/hfi_response_handler.c
index 43a3dad..19f5dcd 100644
--- a/drivers/media/platform/msm/vidc/hfi_response_handler.c
+++ b/drivers/media/platform/msm/vidc/hfi_response_handler.c
@@ -1018,6 +1018,41 @@
callback(SESSION_END_DONE, &cmd_done);
}
+static void hfi_process_session_abort_done(
+ msm_vidc_callback callback, u32 device_id,
+ struct hfi_msg_sys_session_abort_done_packet *pkt)
+{
+ struct msm_vidc_cb_cmd_done cmd_done;
+ struct hal_session *sess_close;
+
+ dprintk(VIDC_DBG, "RECEIVED:SESSION_ABORT_DONE");
+
+ if (!pkt || pkt->size !=
+ sizeof(struct hfi_msg_sys_session_abort_done_packet)) {
+ dprintk(VIDC_ERR, "%s: bad packet/packet size: %d",
+ __func__, pkt ? pkt->size : 0);
+ return;
+ }
+ memset(&cmd_done, 0, sizeof(struct msm_vidc_cb_cmd_done));
+ cmd_done.device_id = device_id;
+ cmd_done.session_id =
+ ((struct hal_session *) pkt->session_id)->session_id;
+ cmd_done.status = hfi_map_err_status((u32)pkt->error_type);
+ cmd_done.data = NULL;
+ cmd_done.size = 0;
+
+ sess_close = (struct hal_session *)pkt->session_id;
+ if (!sess_close) {
+ dprintk(VIDC_ERR, "%s: invalid session pointer\n", __func__);
+ return;
+ }
+ dprintk(VIDC_ERR, "deleted the session: 0x%x",
+ sess_close->session_id);
+ list_del(&sess_close->list);
+ kfree(sess_close);
+ callback(SESSION_ABORT_DONE, &cmd_done);
+}
+
static void hfi_process_session_get_seq_hdr_done(
msm_vidc_callback callback, u32 device_id,
struct hfi_msg_session_get_sequence_header_done_packet *pkt)
@@ -1134,6 +1169,10 @@
hfi_msg_session_release_buffers_done_packet*)
msg_hdr);
break;
+ case HFI_MSG_SYS_SESSION_ABORT_DONE:
+ hfi_process_session_abort_done(callback, device_id, (struct
+ hfi_msg_sys_session_abort_done_packet*) msg_hdr);
+ break;
default:
dprintk(VIDC_ERR, "UNKNOWN_MSG_TYPE : %d", msg_hdr->packet);
break;
diff --git a/drivers/media/platform/msm/vidc/msm_vdec.c b/drivers/media/platform/msm/vidc/msm_vdec.c
index 1611a09..511a478 100644
--- a/drivers/media/platform/msm/vidc/msm_vdec.c
+++ b/drivers/media/platform/msm/vidc/msm_vdec.c
@@ -66,6 +66,7 @@
"Extradata input crop",
"Extradata digital zoom",
"Extradata aspect ratio",
+ "Extradata mpeg2 seqdisp",
};
static struct msm_vidc_ctrl msm_vdec_ctrls[] = {
@@ -203,7 +204,7 @@
.name = "Extradata Type",
.type = V4L2_CTRL_TYPE_MENU,
.minimum = V4L2_MPEG_VIDC_EXTRADATA_NONE,
- .maximum = V4L2_MPEG_VIDC_INDEX_EXTRADATA_ASPECT_RATIO,
+ .maximum = V4L2_MPEG_VIDC_EXTRADATA_MPEG2_SEQDISP,
.default_value = V4L2_MPEG_VIDC_EXTRADATA_NONE,
.menu_skip_mask = ~(
(1 << V4L2_MPEG_VIDC_EXTRADATA_NONE) |
@@ -223,7 +224,8 @@
(1 << V4L2_MPEG_VIDC_EXTRADATA_METADATA_FILLER) |
(1 << V4L2_MPEG_VIDC_INDEX_EXTRADATA_INPUT_CROP) |
(1 << V4L2_MPEG_VIDC_INDEX_EXTRADATA_DIGITAL_ZOOM) |
- (1 << V4L2_MPEG_VIDC_INDEX_EXTRADATA_ASPECT_RATIO)
+ (1 << V4L2_MPEG_VIDC_INDEX_EXTRADATA_ASPECT_RATIO) |
+ (1 << V4L2_MPEG_VIDC_EXTRADATA_MPEG2_SEQDISP)
),
.qmenu = mpeg_video_vidc_extradata,
.step = 0,
@@ -697,32 +699,43 @@
}
int msm_vdec_s_parm(struct msm_vidc_inst *inst, struct v4l2_streamparm *a)
{
- u32 us_per_frame = 0;
- int rc = 0;
+ u64 us_per_frame = 0;
+ int rc = 0, fps = 0, rem = 0;
if (a->parm.output.timeperframe.denominator) {
switch (a->type) {
- case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
- us_per_frame = a->parm.output.timeperframe.numerator/
- a->parm.output.timeperframe.denominator;
- break;
case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
- us_per_frame = a->parm.capture.timeperframe.numerator/
- a->parm.capture.timeperframe.denominator;
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+ us_per_frame = a->parm.output.timeperframe.numerator *
+ (u64)USEC_PER_SEC;
+ do_div(us_per_frame, a->parm.output.\
+ timeperframe.denominator);
break;
default:
dprintk(VIDC_ERR,
- "Scale clocks : Unknown buffer type\n");
+ "Scale clocks : Unknown buffer type %d\n",
+ a->type);
break;
}
}
+
if (!us_per_frame) {
dprintk(VIDC_ERR,
- "Failed to scale clocks : time between frames is 0\n");
+ "Failed to scale clocks : time between frames is 0\n");
rc = -EINVAL;
goto exit;
}
- inst->prop.fps = (u8) (USEC_PER_SEC / us_per_frame);
- if (inst->prop.fps) {
+
+ fps = USEC_PER_SEC;
+ rem = do_div(fps, us_per_frame);
+ if (rem) {
+ /* Effectively fps = ceil((float)USEC_PER_SEC/us_per_frame) */
+ fps++;
+ }
+
+ if (inst->prop.fps != fps) {
+ dprintk(VIDC_PROF, "reported fps changed for %p: %d->%d\n",
+ inst, inst->prop.fps, fps);
+ inst->prop.fps = fps;
msm_comm_scale_clocks_and_bus(inst);
}
exit:
@@ -1121,11 +1134,24 @@
int rc = 0;
struct v4l2_event dqevent = {0};
struct msm_vidc_core *core = inst->core;
+
+ if (!dec || !inst || !inst->core) {
+ dprintk(VIDC_ERR, "%s invalid params", __func__);
+ return -EINVAL;
+ }
switch (dec->cmd) {
case V4L2_DEC_QCOM_CMD_FLUSH:
rc = msm_comm_flush(inst, dec->flags);
break;
case V4L2_DEC_CMD_STOP:
+ if (core->state != VIDC_CORE_INVALID &&
+ inst->state == MSM_VIDC_CORE_INVALID) {
+ rc = msm_comm_recover_from_session_error(inst);
+ if (rc)
+ dprintk(VIDC_ERR,
+ "Failed to recover from session_error: %d\n",
+ rc);
+ }
rc = msm_comm_release_scratch_buffers(inst);
if (rc)
dprintk(VIDC_ERR,
@@ -1191,7 +1217,6 @@
inst->capability.width.min = MIN_SUPPORTED_WIDTH;
inst->capability.width.max = DEFAULT_WIDTH;
inst->prop.fps = 30;
- inst->prop.prev_time_stamp = 0;
return rc;
}
diff --git a/drivers/media/platform/msm/vidc/msm_venc.c b/drivers/media/platform/msm/vidc/msm_venc.c
index 66a3b6e..e03edd7 100644
--- a/drivers/media/platform/msm/vidc/msm_venc.c
+++ b/drivers/media/platform/msm/vidc/msm_venc.c
@@ -687,7 +687,7 @@
static u32 get_frame_size_nv21(int plane, u32 height, u32 width)
{
- return height * width * 2;
+ return VENUS_BUFFER_SIZE(COLOR_FMT_NV21, width, height);
}
static u32 get_frame_size_compressed(int plane, u32 height, u32 width)
@@ -1970,7 +1970,7 @@
{
u32 property_id = 0, us_per_frame = 0;
void *pdata;
- int rc = 0;
+ int rc = 0, fps = 0, rem = 0;
struct hal_frame_rate frame_rate;
struct hfi_device *hdev;
@@ -1978,32 +1978,45 @@
dprintk(VIDC_ERR, "%s invalid parameters", __func__);
return -EINVAL;
}
- hdev = inst->core->device;
+ hdev = inst->core->device;
property_id = HAL_CONFIG_FRAME_RATE;
+
if (a->parm.output.timeperframe.denominator) {
switch (a->type) {
- case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
us_per_frame = a->parm.output.timeperframe.numerator *
- USEC_PER_SEC / a->parm.output.\
- timeperframe.denominator;
+ (u64)USEC_PER_SEC;
+ do_div(us_per_frame, a->parm.output.\
+ timeperframe.denominator);
break;
default:
dprintk(VIDC_ERR,
- "Scale clocks : Unknown buffer type\n");
+ "Scale clocks : Unknown buffer type %d\n",
+ a->type);
break;
}
}
if (!us_per_frame) {
dprintk(VIDC_ERR,
- "Failed to scale clocks : time between frames is 0\n");
+ "Failed to scale clocks : time between frames is 0\n");
rc = -EINVAL;
goto exit;
}
- inst->prop.fps = (u8) (USEC_PER_SEC / us_per_frame);
- if (inst->prop.fps) {
+
+ fps = USEC_PER_SEC;
+ rem = do_div(fps, us_per_frame);
+ if (rem) {
+ /* Effectively fps = ceil((float)USEC_PER_SEC/us_per_frame) */
+ fps++;
+ }
+
+ if (inst->prop.fps != fps) {
+ dprintk(VIDC_PROF, "reported fps changed for %p: %d->%d\n",
+ inst, inst->prop.fps, fps);
+ inst->prop.fps = fps;
frame_rate.frame_rate = inst->prop.fps * (0x1<<16);
frame_rate.buffer_type = HAL_BUFFER_OUTPUT;
pdata = &frame_rate;
@@ -2022,7 +2035,6 @@
int msm_venc_s_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f)
{
struct msm_vidc_format *fmt = NULL;
- struct hal_frame_size frame_sz;
int rc = 0;
int i;
struct hfi_device *hdev;
@@ -2050,6 +2062,9 @@
goto exit;
}
} else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ struct hal_uncompressed_format_select hal_fmt = {0};
+ struct hal_frame_size frame_sz;
+
inst->prop.width = f->fmt.pix_mp.width;
inst->prop.height = f->fmt.pix_mp.height;
rc = msm_vidc_check_session_supported(inst);
@@ -2088,6 +2103,29 @@
rc = -EINVAL;
goto exit;
}
+
+ switch (fmt->fourcc) {
+ case V4L2_PIX_FMT_NV12:
+ hal_fmt.format = HAL_COLOR_FORMAT_NV12;
+ break;
+ case V4L2_PIX_FMT_NV21:
+ hal_fmt.format = HAL_COLOR_FORMAT_NV21;
+ break;
+ default:
+ /* we really shouldn't be here */
+ rc = -ENOTSUPP;
+ goto exit;
+ }
+
+ hal_fmt.buffer_type = HAL_BUFFER_INPUT;
+ rc = call_hfi_op(hdev, session_set_property, (void *)
+ inst->session, HAL_PARAM_UNCOMPRESSED_FORMAT_SELECT,
+ &hal_fmt);
+ if (rc) {
+ dprintk(VIDC_ERR,
+ "Failed to set input color format\n");
+ goto exit;
+ }
}
if (fmt) {
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.c b/drivers/media/platform/msm/vidc/msm_vidc_common.c
index eb5a03c..91fcdb6 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_common.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_common.c
@@ -47,8 +47,6 @@
__mbs;\
})
-#define TIME_DIFF_THRESHOLD 200
-
static int msm_comm_get_load(struct msm_vidc_core *core,
enum session_type type)
{
@@ -63,8 +61,29 @@
if (inst->session_type == type &&
inst->state >= MSM_VIDC_OPEN_DONE &&
inst->state < MSM_VIDC_STOP_DONE) {
- num_mbs_per_sec += NUM_MBS_PER_SEC(inst->prop.height,
- inst->prop.width, inst->prop.fps);
+ int stride, scanlines, rc;
+ struct hfi_device *hdev;
+
+ hdev = inst->core->device;
+ if (!hdev) {
+ dprintk(VIDC_ERR,
+ "No hdev (probably in bad state)\n");
+ return -EINVAL;
+ }
+
+ rc = call_hfi_op(hdev, get_stride_scanline,
+ COLOR_FMT_NV12,
+ inst->prop.width, inst->prop.height,
+ &stride, &scanlines);
+ if (rc) {
+ dprintk(VIDC_WARN,
+ "Failed to determine stride/scan when getting load. Perf. might be affected\n");
+ stride = inst->prop.width;
+ scanlines = inst->prop.height;
+ }
+
+ num_mbs_per_sec += NUM_MBS_PER_SEC(stride, scanlines,
+ inst->prop.fps);
}
mutex_unlock(&inst->lock);
}
@@ -315,6 +334,7 @@
msecs_to_jiffies(HW_RESPONSE_TIMEOUT));
if (!rc) {
dprintk(VIDC_ERR, "Wait interrupted or timeout: %d\n", rc);
+ msm_comm_recover_from_session_error(inst);
rc = -EIO;
} else {
rc = 0;
@@ -345,7 +365,7 @@
{
struct msm_vidc_cb_cmd_done *response = data;
struct msm_vidc_inst *inst;
- if (response) {
+ if (response && !response->status) {
struct vidc_hal_session_init_done *session_init_done =
(struct vidc_hal_session_init_done *) response->data;
inst = (struct msm_vidc_inst *)response->session_id;
@@ -638,56 +658,6 @@
}
}
-static void msm_comm_update_clocks(struct msm_vidc_inst *inst,
- u64 cur_time_stamp)
-{
- u32 new_time_diff = 0, cur_time_diff = 0;
- u8 updated_fps = 0;
- struct v4l2_ctrl *ctrl = NULL;
- u32 output_order = 0;
-
- if (inst->session_type == MSM_VIDC_ENCODER)
- goto exit;
- if (cur_time_stamp >= LLONG_MAX) {
- dprintk(VIDC_DBG,
- "Clock scaling failed : Timestamp invalid\n");
- goto exit;
- }
- ctrl = v4l2_ctrl_find(&inst->ctrl_handler,
- V4L2_CID_MPEG_VIDC_VIDEO_OUTPUT_ORDER);
- if (!ctrl) {
- dprintk(VIDC_WARN, "Unable to find output order control\n");
- dprintk(VIDC_WARN,
- "Performance might be impacted for higher fps clips\n");
- goto exit;
- }
- output_order = v4l2_ctrl_g_ctrl(ctrl);
- if (output_order == V4L2_MPEG_VIDC_VIDEO_OUTPUT_ORDER_DISPLAY) {
- new_time_diff =
- (u32)(cur_time_stamp - inst->prop.prev_time_stamp);
- inst->prop.prev_time_stamp = cur_time_stamp;
- if (!new_time_diff)
- goto exit;
- if (inst->prop.fps)
- cur_time_diff = USEC_PER_SEC / inst->prop.fps;
- cur_time_diff = cur_time_diff > new_time_diff ?
- cur_time_diff - new_time_diff :
- new_time_diff - cur_time_diff;
- if (cur_time_diff > TIME_DIFF_THRESHOLD) {
- updated_fps = (u8) (USEC_PER_SEC / new_time_diff);
- if (updated_fps && (updated_fps != inst->prop.fps)) {
- inst->prop.fps = updated_fps;
- dprintk(VIDC_DBG,
- "Updating clocks: Decoding fps = %d\n",
- inst->prop.fps);
- msm_comm_scale_clocks_and_bus(inst);
- }
- }
- }
-exit:
- return;
-}
-
static void handle_fbd(enum command_response cmd, void *data)
{
struct msm_vidc_cb_data_done *response = data;
@@ -717,7 +687,6 @@
fill_buf_done->timestamp_lo;
vb->v4l2_buf.timestamp =
ns_to_timeval(time_usec * NSEC_PER_USEC);
- msm_comm_update_clocks(inst, time_usec);
}
vb->v4l2_buf.flags = 0;
@@ -726,13 +695,17 @@
if (fill_buf_done->flags1 & HAL_BUFFERFLAG_CODECCONFIG)
vb->v4l2_buf.flags &= ~V4L2_QCOM_BUF_FLAG_CODECCONFIG;
if (fill_buf_done->flags1 & HAL_BUFFERFLAG_SYNCFRAME)
- vb->v4l2_buf.flags |= V4L2_BUF_FLAG_KEYFRAME;
+ vb->v4l2_buf.flags |= V4L2_QCOM_BUF_FLAG_IDRFRAME;
if (fill_buf_done->flags1 & HAL_BUFFERFLAG_EOSEQ)
vb->v4l2_buf.flags |= V4L2_QCOM_BUF_FLAG_EOSEQ;
if (fill_buf_done->flags1 & HAL_BUFFERFLAG_DECODEONLY)
vb->v4l2_buf.flags |= V4L2_QCOM_BUF_FLAG_DECODEONLY;
+ if (fill_buf_done->flags1 & HAL_BUFFERFLAG_DATACORRUPT)
+ vb->v4l2_buf.flags |= V4L2_QCOM_BUF_DATA_CORRUPT;
switch (fill_buf_done->picture_type) {
case HAL_PICTURE_IDR:
+ vb->v4l2_buf.flags |= V4L2_QCOM_BUF_FLAG_IDRFRAME;
+ break;
case HAL_PICTURE_I:
vb->v4l2_buf.flags |= V4L2_BUF_FLAG_KEYFRAME;
break;
@@ -861,6 +834,7 @@
handle_release_res_done(cmd, data);
break;
case SESSION_END_DONE:
+ case SESSION_ABORT_DONE:
handle_session_close(cmd, data);
break;
case VIDC_EVENT_CHANGE:
@@ -1278,7 +1252,8 @@
}
mutex_unlock(&temp->lock);
}
-
+ inst->state = MSM_VIDC_CORE_INVALID;
+ msm_comm_recover_from_session_error(inst);
return -ENOMEM;
}
@@ -1930,6 +1905,8 @@
if (!rc) {
dprintk(VIDC_ERR,
"Wait interrupted or timeout: %d\n", rc);
+ inst->state = MSM_VIDC_CORE_INVALID;
+ msm_comm_recover_from_session_error(inst);
rc = -EIO;
goto exit;
}
@@ -1990,6 +1967,13 @@
mutex_unlock(&inst->lock);
rc = wait_for_sess_signal_receipt(inst,
SESSION_RELEASE_BUFFER_DONE);
+ if (rc) {
+ mutex_lock(&inst->sync_lock);
+ inst->state = MSM_VIDC_CORE_INVALID;
+ mutex_unlock(&inst->sync_lock);
+ msm_comm_recover_from_session_error(
+ inst);
+ }
mutex_lock(&inst->lock);
}
list_del(&buf->list);
@@ -2054,6 +2038,13 @@
mutex_unlock(&inst->lock);
rc = wait_for_sess_signal_receipt(inst,
SESSION_RELEASE_BUFFER_DONE);
+ if (rc) {
+ mutex_lock(&inst->sync_lock);
+ inst->state = MSM_VIDC_CORE_INVALID;
+ mutex_unlock(&inst->sync_lock);
+ msm_comm_recover_from_session_error(
+ inst);
+ }
mutex_lock(&inst->lock);
}
list_del(&buf->list);
@@ -2262,7 +2253,10 @@
kfree(temp);
}
}
- rc = call_hfi_op(hdev, session_flush, inst->session,
+ /*Do not send flush in case of session_error */
+ if (!(inst->state == MSM_VIDC_CORE_INVALID &&
+ core->state != VIDC_CORE_INVALID))
+ rc = call_hfi_op(hdev, session_flush, inst->session,
HAL_FLUSH_ALL);
}
mutex_unlock(&inst->sync_lock);
@@ -2323,6 +2317,9 @@
case V4L2_MPEG_VIDC_INDEX_EXTRADATA_ASPECT_RATIO:
ret = HAL_EXTRADATA_ASPECT_RATIO;
break;
+ case V4L2_MPEG_VIDC_EXTRADATA_MPEG2_SEQDISP:
+ ret = HAL_EXTRADATA_MPEG2_SEQDISP;
+ break;
default:
dprintk(VIDC_WARN, "Extradata not found: %d\n", index);
break;
@@ -2386,38 +2383,27 @@
struct msm_vidc_core_capability *capability;
int rc = 0;
struct v4l2_event dqevent;
+ struct hfi_device *hdev;
- if (!inst) {
+ if (!inst || !inst->core || !inst->core->device) {
dprintk(VIDC_WARN, "%s: Invalid parameter\n", __func__);
return -EINVAL;
}
capability = &inst->capability;
+ hdev = inst->core->device;
if (inst->capability.capability_set) {
- if (msm_vp8_low_tier &&
- inst->core->hfi_type == VIDC_HFI_VENUS &&
- inst->fmts[OUTPUT_PORT]->fourcc == V4L2_PIX_FMT_VP8) {
- capability->width.max = DEFAULT_WIDTH;
- capability->height.max = DEFAULT_HEIGHT;
- }
- if (inst->prop.width < capability->width.min ||
- inst->prop.width > capability->width.max ||
- (inst->prop.width % capability->width.step_size != 0)) {
- dprintk(VIDC_ERR,
- "Unsupported width = %d range min(%u) - max(%u) step_size(%u)",
- inst->prop.width, capability->width.min,
- capability->width.max, capability->width.step_size);
- rc = -ENOTSUPP;
- }
+ rc = call_hfi_op(hdev, capability_check,
+ inst->fmts[OUTPUT_PORT]->fourcc,
+ inst->prop.width, &capability->width.max,
+ &capability->height.max);
- if (inst->prop.height < capability->height.min ||
- inst->prop.height > capability->height.max ||
- (inst->prop.height %
- capability->height.step_size != 0)) {
+ if (!rc && (inst->prop.height * inst->prop.width >
+ capability->width.max * capability->height.max)) {
dprintk(VIDC_ERR,
- "Unsupported height = %d range min(%u) - max(%u) step_size(%u)",
- inst->prop.height, capability->height.min,
- capability->height.max, capability->height.step_size);
+ "Unsupported WxH = (%u)x(%u), Max supported is - (%u)x(%u)",
+ inst->prop.width, inst->prop.height,
+ capability->width.max, capability->height.max);
rc = -ENOTSUPP;
}
}
@@ -2432,3 +2418,57 @@
}
return rc;
}
+
+static void msm_comm_generate_sys_error(struct msm_vidc_inst *inst)
+{
+ struct msm_vidc_core *core;
+ enum command_response cmd = SYS_ERROR;
+ struct msm_vidc_cb_cmd_done response = {0};
+ if (!inst || !inst->core) {
+ dprintk(VIDC_ERR, "%s: invalid input parameters", __func__);
+ return;
+ }
+ core = inst->core;
+ response.device_id = (u32) core->id;
+ handle_sys_error(cmd, (void *) &response);
+
+}
+int msm_comm_recover_from_session_error(struct msm_vidc_inst *inst)
+{
+ struct hfi_device *hdev;
+ int rc = 0;
+
+ if (!inst || !inst->core || !inst->core->device) {
+ dprintk(VIDC_ERR, "%s: invalid input parameters", __func__);
+ return -EINVAL;
+ }
+ if (inst->state < MSM_VIDC_OPEN_DONE) {
+ dprintk(VIDC_WARN,
+ "No corresponding FW session. No need to send Abort");
+ inst->state = MSM_VIDC_CORE_INVALID;
+ return rc;
+ }
+ hdev = inst->core->device;
+
+ init_completion(&inst->completions[SESSION_MSG_INDEX
+ (SESSION_ABORT_DONE)]);
+
+ /* We have received session_error. Send session_abort to firmware
+ * to clean up and release the session
+ */
+ rc = call_hfi_op(hdev, session_abort, (void *) inst->session);
+ if (rc) {
+ dprintk(VIDC_ERR, "session_abort failed rc: %d\n", rc);
+ return rc;
+ }
+ rc = wait_for_completion_timeout(
+ &inst->completions[SESSION_MSG_INDEX(SESSION_ABORT_DONE)],
+ msecs_to_jiffies(HW_RESPONSE_TIMEOUT));
+ if (!rc) {
+ dprintk(VIDC_ERR, "%s: Wait interrupted or timeout: %d\n",
+ __func__, rc);
+ msm_comm_generate_sys_error(inst);
+ } else
+ change_inst_state(inst, MSM_VIDC_CLOSE_DONE);
+ return rc;
+}
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.h b/drivers/media/platform/msm/vidc/msm_vidc_common.h
index 862dfab..c018345 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_common.h
+++ b/drivers/media/platform/msm/vidc/msm_vidc_common.h
@@ -48,3 +48,4 @@
V4L2_CTRL_DRIVER_PRIV(idx))
#endif
+int msm_comm_recover_from_session_error(struct msm_vidc_inst *inst);
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_internal.h b/drivers/media/platform/msm/vidc/msm_vidc_internal.h
index e5696be..d9a2332 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_internal.h
+++ b/drivers/media/platform/msm/vidc/msm_vidc_internal.h
@@ -130,7 +130,6 @@
u32 height;
u32 fps;
u32 bitrate;
- u64 prev_time_stamp;
};
struct buf_queue {
diff --git a/drivers/media/platform/msm/vidc/venus_hfi.c b/drivers/media/platform/msm/vidc/venus_hfi.c
index b96a6dc..74733c2 100644
--- a/drivers/media/platform/msm/vidc/venus_hfi.c
+++ b/drivers/media/platform/msm/vidc/venus_hfi.c
@@ -217,9 +217,12 @@
packet + ((packet_size_in_words - new_write_idx) << 2),
new_write_idx << 2);
}
+ /* Memory barrier to make sure packet is written before updating the
+ * write index */
+ mb();
queue->qhdr_write_idx = new_write_idx;
*rx_req_is_set = (1 == queue->qhdr_rx_req) ? 1 : 0;
- /*Memory barrier to make sure data is written before an
+ /*Memory barrier to make sure write index is updated before an
* interupt is raised on venus.*/
mb();
dprintk(VIDC_DBG, "Out : ");
@@ -2846,6 +2849,28 @@
return 0;
}
+int venus_hfi_capability_check(u32 fourcc, u32 width,
+ u32 *max_width, u32 *max_height)
+{
+ int rc = 0;
+ if (!max_width || !max_height) {
+ dprintk(VIDC_ERR, "%s - invalid parameter\n", __func__);
+ return -EINVAL;
+ }
+
+ if (msm_vp8_low_tier && fourcc == V4L2_PIX_FMT_VP8) {
+ *max_width = DEFAULT_WIDTH;
+ *max_height = DEFAULT_HEIGHT;
+ }
+ if (width > *max_width) {
+ dprintk(VIDC_ERR,
+ "Unsupported width = %u supported max width = %u",
+ width, *max_width);
+ rc = -ENOTSUPP;
+ }
+ return rc;
+}
+
static void *venus_hfi_add_device(u32 device_id,
struct msm_vidc_platform_resources *res,
hfi_cmd_response_callback callback)
@@ -2985,6 +3010,7 @@
hdev->unload_fw = venus_hfi_unload_fw;
hdev->get_fw_info = venus_hfi_get_fw_info;
hdev->get_stride_scanline = venus_hfi_get_stride_scanline;
+ hdev->capability_check = venus_hfi_capability_check;
}
int venus_hfi_initialize(struct hfi_device *hdev, u32 device_id,
diff --git a/drivers/media/platform/msm/vidc/vidc_hfi.h b/drivers/media/platform/msm/vidc/vidc_hfi.h
index 1311752..bb72da7 100644
--- a/drivers/media/platform/msm/vidc/vidc_hfi.h
+++ b/drivers/media/platform/msm/vidc/vidc_hfi.h
@@ -75,7 +75,8 @@
#define HFI_EXTRADATA_PANSCAN_WINDOW 0x00000008
#define HFI_EXTRADATA_RECOVERY_POINT_SEI 0x00000009
#define HFI_EXTRADATA_CLOSED_CAPTION_UD 0x0000000A
-#define HFI_EXTRADATA_AFD_UD 0x0000000B
+#define HFI_EXTRADATA_AFD_UD 0x0000000B
+#define HFI_EXTRADATA_MPEG2_SEQDISP 0x0000000D
#define HFI_EXTRADATA_MULTISLICE_INFO 0x7F100000
#define HFI_EXTRADATA_NUM_CONCEALED_MB 0x7F100001
#define HFI_EXTRADATA_INDEX 0x7F100002
@@ -191,6 +192,8 @@
(HFI_PROPERTY_PARAM_VDEC_OX_START + 0x014)
#define HFI_PROPERTY_PARAM_VDEC_AVC_SESSION_SELECT \
(HFI_PROPERTY_PARAM_VDEC_OX_START + 0x015)
+#define HFI_PROPERTY_PARAM_VDEC_MPEG2_SEQDISP_EXTRADATA \
+ (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x016)
#define HFI_PROPERTY_CONFIG_VDEC_OX_START \
(HFI_DOMAIN_BASE_VDEC + HFI_ARCH_OX_OFFSET + 0x0000)
diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_api.h b/drivers/media/platform/msm/vidc/vidc_hfi_api.h
index 3fbfec4..01395e5 100644
--- a/drivers/media/platform/msm/vidc/vidc_hfi_api.h
+++ b/drivers/media/platform/msm/vidc/vidc_hfi_api.h
@@ -95,6 +95,7 @@
HAL_EXTRADATA_NUM_CONCEALED_MB,
HAL_EXTRADATA_METADATA_FILLER,
HAL_EXTRADATA_ASPECT_RATIO,
+ HAL_EXTRADATA_MPEG2_SEQDISP
};
enum hal_property {
@@ -390,20 +391,20 @@
};
enum hal_uncompressed_format {
- HAL_COLOR_FORMAT_MONOCHROME,
- HAL_COLOR_FORMAT_NV12,
- HAL_COLOR_FORMAT_NV21,
- HAL_COLOR_FORMAT_NV12_4x4TILE,
- HAL_COLOR_FORMAT_NV21_4x4TILE,
- HAL_COLOR_FORMAT_YUYV,
- HAL_COLOR_FORMAT_YVYU,
- HAL_COLOR_FORMAT_UYVY,
- HAL_COLOR_FORMAT_VYUY,
- HAL_COLOR_FORMAT_RGB565,
- HAL_COLOR_FORMAT_BGR565,
- HAL_COLOR_FORMAT_RGB888,
- HAL_COLOR_FORMAT_BGR888,
- HAL_UNUSED_COLOR = 0x10000000,
+ HAL_COLOR_FORMAT_MONOCHROME = 0x00000001,
+ HAL_COLOR_FORMAT_NV12 = 0x00000002,
+ HAL_COLOR_FORMAT_NV21 = 0x00000004,
+ HAL_COLOR_FORMAT_NV12_4x4TILE = 0x00000008,
+ HAL_COLOR_FORMAT_NV21_4x4TILE = 0x00000010,
+ HAL_COLOR_FORMAT_YUYV = 0x00000020,
+ HAL_COLOR_FORMAT_YVYU = 0x00000040,
+ HAL_COLOR_FORMAT_UYVY = 0x00000080,
+ HAL_COLOR_FORMAT_VYUY = 0x00000100,
+ HAL_COLOR_FORMAT_RGB565 = 0x00000200,
+ HAL_COLOR_FORMAT_BGR565 = 0x00000400,
+ HAL_COLOR_FORMAT_RGB888 = 0x00000800,
+ HAL_COLOR_FORMAT_BGR888 = 0x00001000,
+ HAL_UNUSED_COLOR = 0x10000000,
};
enum hal_ssr_trigger_type {
@@ -482,7 +483,7 @@
HAL_PICTURE_I = 0x01,
HAL_PICTURE_P = 0x02,
HAL_PICTURE_B = 0x04,
- HAL_PICTURE_IDR = 0x7F001000,
+ HAL_PICTURE_IDR = 0x08,
HAL_FRAME_NOTCODED = 0x7F002000,
HAL_FRAME_YUV = 0x7F004000,
HAL_UNUSED_PICT = 0x10000000,
@@ -1020,7 +1021,7 @@
};
#define call_hfi_op(q, op, args...) \
- (((q)->op) ? ((q)->op(args)) : 0)
+ (((q) && (q)->op) ? ((q)->op(args)) : 0)
struct hfi_device {
void *hfi_device_data;
@@ -1074,6 +1075,8 @@
int (*get_fw_info)(void *dev, enum fw_info info);
int (*get_stride_scanline)(int color_fmt, int width,
int height, int *stride, int *scanlines);
+ int (*capability_check)(u32 fourcc, u32 width,
+ u32 *max_width, u32 *max_height);
};
typedef void (*hfi_cmd_response_callback) (enum command_response cmd,
diff --git a/drivers/mfd/wcd9xxx-core.c b/drivers/mfd/wcd9xxx-core.c
index 046faac..130ff48 100644
--- a/drivers/mfd/wcd9xxx-core.c
+++ b/drivers/mfd/wcd9xxx-core.c
@@ -70,8 +70,7 @@
static int wcd9xxx_read(struct wcd9xxx *wcd9xxx, unsigned short reg,
int bytes, void *dest, bool interface_reg)
{
- int ret;
- u8 *buf = dest;
+ int i, ret;
if (bytes <= 0) {
dev_err(wcd9xxx->dev, "Invalid byte read length %d\n", bytes);
@@ -82,9 +81,11 @@
if (ret < 0) {
dev_err(wcd9xxx->dev, "Codec read failed\n");
return ret;
- } else
- dev_dbg(wcd9xxx->dev, "Read 0x%02x from 0x%x\n",
- *buf, reg);
+ } else {
+ for (i = 0; i < bytes; i++)
+ dev_dbg(wcd9xxx->dev, "Read 0x%02x from 0x%x\n",
+ ((u8 *)dest)[i], reg + i);
+ }
return 0;
}
@@ -107,15 +108,16 @@
static int wcd9xxx_write(struct wcd9xxx *wcd9xxx, unsigned short reg,
int bytes, void *src, bool interface_reg)
{
- u8 *buf = src;
+ int i;
if (bytes <= 0) {
pr_err("%s: Error, invalid write length\n", __func__);
return -EINVAL;
}
- dev_dbg(wcd9xxx->dev, "Write %02x to 0x%x\n",
- *buf, reg);
+ for (i = 0; i < bytes; i++)
+ dev_dbg(wcd9xxx->dev, "Write %02x to 0x%x\n", ((u8 *)src)[i],
+ reg + i);
return wcd9xxx->write_dev(wcd9xxx, reg, bytes, src, interface_reg);
}
@@ -1147,6 +1149,10 @@
(of_property_read_bool(dev->of_node, "qcom,cdc-micbias4-ext-cap") ?
MICBIAS_EXT_BYP_CAP : MICBIAS_NO_EXT_BYP_CAP);
+ micbias->bias2_is_headset_only =
+ of_property_read_bool(dev->of_node,
+ "qcom,cdc-micbias2-headset-only");
+
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);
@@ -1162,6 +1168,8 @@
dev_dbg(dev, "bias3_ext_cap %d bias4_ext_cap %d\n",
micbias->bias3_cap_mode, micbias->bias4_cap_mode);
+ dev_dbg(dev, "bias2_is_headset_only %d\n",
+ micbias->bias2_is_headset_only);
return 0;
}
diff --git a/drivers/misc/qseecom.c b/drivers/misc/qseecom.c
index fcadc30..fa28d6a 100644
--- a/drivers/misc/qseecom.c
+++ b/drivers/misc/qseecom.c
@@ -1918,6 +1918,10 @@
qclk = &qseecom.ce_drv;
mutex_lock(&clk_access_lock);
+
+ if (qclk->clk_access_cnt == ULONG_MAX)
+ goto err;
+
if (qclk->clk_access_cnt > 0) {
qclk->clk_access_cnt++;
mutex_unlock(&clk_access_lock);
@@ -1965,6 +1969,12 @@
qclk = &qseecom.ce_drv;
mutex_lock(&clk_access_lock);
+
+ if (qclk->clk_access_cnt == 0) {
+ mutex_unlock(&clk_access_lock);
+ return;
+ }
+
if (qclk->clk_access_cnt == 1) {
if (qclk->ce_clk != NULL)
clk_disable_unprepare(qclk->ce_clk);
@@ -2495,8 +2505,8 @@
ireq.pipe = set_key_para->pipe;
ireq.flags = set_key_para->flags;
- /* set PIPE_ENC */
- ireq.pipe_type = QSEOS_PIPE_ENC;
+ /* set both PIPE_ENC and PIPE_ENC_XTS*/
+ ireq.pipe_type = QSEOS_PIPE_ENC|QSEOS_PIPE_ENC_XTS;
if (set_key_para->set_clear_key_flag ==
QSEECOM_SET_CE_KEY_CMD)
@@ -2513,17 +2523,6 @@
return ret;
}
- /* set PIPE_ENC_XTS */
- ireq.pipe_type = QSEOS_PIPE_ENC_XTS;
- ret = scm_call(SCM_SVC_TZSCHEDULER, 1,
- &ireq, sizeof(struct qseecom_key_select_ireq),
- &resp, sizeof(struct qseecom_command_scm_resp));
- if (ret) {
- pr_err("scm call to set QSEOS_PIPE_ENC_XTS key failed : %d\n",
- ret);
- return ret;
- }
-
switch (resp.result) {
case QSEOS_RESULT_SUCCESS:
break;
diff --git a/drivers/misc/tspp.c b/drivers/misc/tspp.c
index 73cae32..501da4c8 100644
--- a/drivers/misc/tspp.c
+++ b/drivers/misc/tspp.c
@@ -33,6 +33,7 @@
#include <linux/wait.h> /* wait() macros, sleeping */
#include <linux/tspp.h> /* tspp functions */
#include <linux/bitops.h> /* BIT() macro */
+#include <linux/regulator/consumer.h>
#include <mach/sps.h> /* BAM stuff */
#include <mach/gpio.h>
#include <linux/wakelock.h> /* Locking functions */
@@ -40,6 +41,7 @@
#include <linux/jiffies.h> /* Jiffies counter */
#include <mach/dma.h>
#include <mach/msm_tspp.h>
+#include <mach/rpm-regulator-smd.h>
#include <linux/debugfs.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
@@ -447,6 +449,8 @@
/* clocks */
struct clk *tsif_pclk;
struct clk *tsif_ref_clk;
+ /* regulators */
+ struct regulator *tsif_vreg;
/* data */
struct tspp_pid_filter_table *filters[TSPP_FILTER_TABLES];
struct tspp_channel channels[TSPP_NUM_CHANNELS];
@@ -763,13 +767,31 @@
/*** Clock functions ***/
static int tspp_clock_start(struct tspp_device *device)
{
+ int rc;
+
if (device == NULL) {
pr_err("tspp: Can't start clocks, invalid device\n");
return -EINVAL;
}
+ if (device->tsif_vreg) {
+ rc = regulator_set_voltage(device->tsif_vreg,
+ RPM_REGULATOR_CORNER_SUPER_TURBO,
+ RPM_REGULATOR_CORNER_SUPER_TURBO);
+ if (rc) {
+ pr_err("Unable to set CX voltage.\n");
+ return rc;
+ }
+ }
+
if (device->tsif_pclk && clk_prepare_enable(device->tsif_pclk) != 0) {
pr_err("tspp: Can't start pclk");
+
+ if (device->tsif_vreg) {
+ regulator_set_voltage(device->tsif_vreg,
+ RPM_REGULATOR_CORNER_SVS_SOC,
+ RPM_REGULATOR_CORNER_SUPER_TURBO);
+ }
return -EBUSY;
}
@@ -777,6 +799,11 @@
clk_prepare_enable(device->tsif_ref_clk) != 0) {
pr_err("tspp: Can't start ref clk");
clk_disable_unprepare(device->tsif_pclk);
+ if (device->tsif_vreg) {
+ regulator_set_voltage(device->tsif_vreg,
+ RPM_REGULATOR_CORNER_SVS_SOC,
+ RPM_REGULATOR_CORNER_SUPER_TURBO);
+ }
return -EBUSY;
}
@@ -785,6 +812,8 @@
static void tspp_clock_stop(struct tspp_device *device)
{
+ int rc;
+
if (device == NULL) {
pr_err("tspp: Can't stop clocks, invalid device\n");
return;
@@ -795,6 +824,14 @@
if (device->tsif_ref_clk)
clk_disable_unprepare(device->tsif_ref_clk);
+
+ if (device->tsif_vreg) {
+ rc = regulator_set_voltage(device->tsif_vreg,
+ RPM_REGULATOR_CORNER_SVS_SOC,
+ RPM_REGULATOR_CORNER_SUPER_TURBO);
+ if (rc)
+ pr_err("Unable to set CX voltage.\n");
+ }
}
/*** TSIF functions ***/
@@ -2667,6 +2704,7 @@
struct device_node *node = pdev->dev.of_node;
struct msm_tspp_platform_data *data;
struct msm_gpio *gpios;
+ struct property *prop;
int i, rc;
int gpio;
u32 gpio_func;
@@ -2691,6 +2729,11 @@
return NULL;
}
+ data->tsif_vreg_present = 0;
+ prop = of_find_property(node, "vdd_cx-supply", NULL);
+ if (prop)
+ data->tsif_vreg_present = 1;
+
data->num_gpios = of_gpio_count(node);
if (data->num_gpios == 0) {
pr_err("tspp: Could not find GPIO definitions\n");
@@ -2843,6 +2886,31 @@
device->pdev = pdev;
platform_set_drvdata(pdev, device);
+ /* map regulators */
+ if (data->tsif_vreg_present) {
+ device->tsif_vreg = devm_regulator_get(&pdev->dev, "vdd_cx");
+ if (IS_ERR(device->tsif_vreg)) {
+ rc = PTR_ERR(device->tsif_vreg);
+ device->tsif_vreg = NULL;
+ goto err_regultaor;
+ }
+
+ /* Set an initial voltage and enable the regulator */
+ rc = regulator_set_voltage(device->tsif_vreg,
+ RPM_REGULATOR_CORNER_SVS_SOC,
+ RPM_REGULATOR_CORNER_SUPER_TURBO);
+ if (rc) {
+ dev_err(&pdev->dev, "Unable to set CX voltage.\n");
+ goto err_regultaor;
+ }
+
+ rc = regulator_enable(device->tsif_vreg);
+ if (rc) {
+ dev_err(&pdev->dev, "Unable to enable CX regulator.\n");
+ goto err_regultaor;
+ }
+ }
+
/* map clocks */
if (data->tsif_pclk) {
device->tsif_pclk = clk_get(&pdev->dev, data->tsif_pclk);
@@ -3032,6 +3100,9 @@
if (device->tsif_pclk)
clk_put(device->tsif_pclk);
err_pclock:
+ if (device->tsif_vreg)
+ regulator_disable(device->tsif_vreg);
+err_regultaor:
kfree(device);
out:
@@ -3081,6 +3152,9 @@
if (device->tsif_pclk)
clk_put(device->tsif_pclk);
+ if (device->tsif_vreg)
+ regulator_disable(device->tsif_vreg);
+
pm_runtime_disable(&pdev->dev);
kfree(device);
diff --git a/drivers/net/ethernet/msm/ecm_ipa.c b/drivers/net/ethernet/msm/ecm_ipa.c
index 726e9a6..be9058b 100644
--- a/drivers/net/ethernet/msm/ecm_ipa.c
+++ b/drivers/net/ethernet/msm/ecm_ipa.c
@@ -47,6 +47,29 @@
#define ECM_IPA_LOG_EXIT() pr_debug("end\n")
/**
+ * enum ecm_ipa_state - specify the current driver internal state.
+ *
+ * The driver internal state changes due to its API usage.
+ * The driver saves its internal state to guard from caller illegal
+ * call sequence.
+ * LOADED is the first state which is the default one.
+ * INITIALIZED is the driver state once it finished registering
+ * the network device
+ * CONNECTED is the driver state once the USB pipes were connected to IPA
+ * UP is the driver state when it allows Linux network stack start
+ * data transfer
+ */
+enum ecm_ipa_mode {
+ ECM_IPA_LOADED,
+ ECM_IPA_INITIALIZED,
+ ECM_IPA_CONNECTED,
+ ECM_IPA_UP,
+};
+
+#define ECM_IPA_MODE(ecm_ipa_ctx) \
+ pr_debug("Driver mode changed - %d", ecm_ipa_ctx->mode);
+
+/**
* struct ecm_ipa_dev - main driver context parameters
* @net: network interface struct implemented by this driver
* @directory: debugfs directory for various debuging switches
@@ -62,6 +85,7 @@
* @outstanding_high: number of outstanding packets allowed
* @outstanding_low: number of outstanding packets which shall cause
* to netdev queue start (after stopped due to outstanding_high reached)
+ * @mode: current mode of ecm_ipa driver
*/
struct ecm_ipa_dev {
struct net_device *net;
@@ -77,34 +101,29 @@
atomic_t outstanding_pkts;
u8 outstanding_high;
u8 outstanding_low;
+ enum ecm_ipa_mode mode;
};
-/**
- * struct ecm_ipa_ctx - saved pointer for the ecm_ipa network device
- * which allow ecm_ipa to be a singleton
- */
-static struct ecm_ipa_dev *ecm_ipa_ctx;
-
static int ecm_ipa_open(struct net_device *net);
static void ecm_ipa_packet_receive_notify(void *priv,
enum ipa_dp_evt_type evt, unsigned long data);
static void ecm_ipa_tx_complete_notify(void *priv,
enum ipa_dp_evt_type evt, unsigned long data);
static int ecm_ipa_stop(struct net_device *net);
-static int ecm_ipa_rules_cfg(struct ecm_ipa_dev *dev,
+static int ecm_ipa_rules_cfg(struct ecm_ipa_dev *ecm_ipa_ctx,
const void *dst_mac, const void *src_mac);
-static void ecm_ipa_rules_destroy(struct ecm_ipa_dev *dev);
+static void ecm_ipa_rules_destroy(struct ecm_ipa_dev *ecm_ipa_ctx);
static int ecm_ipa_register_properties(void);
static void ecm_ipa_deregister_properties(void);
static void ecm_ipa_rm_notify(void *user_data, enum ipa_rm_event event,
unsigned long data);
-static int ecm_ipa_create_rm_resource(struct ecm_ipa_dev *dev);
-static void ecm_ipa_destory_rm_resource(struct ecm_ipa_dev *dev);
+static int ecm_ipa_create_rm_resource(struct ecm_ipa_dev *ecm_ipa_ctx);
+static void ecm_ipa_destory_rm_resource(struct ecm_ipa_dev *ecm_ipa_ctx);
static bool rx_filter(struct sk_buff *skb);
static bool tx_filter(struct sk_buff *skb);
-static bool rm_enabled(struct ecm_ipa_dev *dev);
-static int resource_request(struct ecm_ipa_dev *dev);
-static void resource_release(struct ecm_ipa_dev *dev);
+static bool rm_enabled(struct ecm_ipa_dev *ecm_ipa_ctx);
+static int resource_request(struct ecm_ipa_dev *ecm_ipa_ctx);
+static void resource_release(struct ecm_ipa_dev *ecm_ipa_ctx);
static netdev_tx_t ecm_ipa_start_xmit(struct sk_buff *skb,
struct net_device *net);
static int ecm_ipa_debugfs_atomic_open(struct inode *inode, struct file *file);
@@ -117,12 +136,14 @@
char __user *ubuf, size_t count, loff_t *ppos);
static ssize_t ecm_ipa_debugfs_atomic_read(struct file *file,
char __user *ubuf, size_t count, loff_t *ppos);
-static int ecm_ipa_debugfs_init(struct ecm_ipa_dev *dev);
-static void ecm_ipa_debugfs_destroy(struct ecm_ipa_dev *dev);
+static int ecm_ipa_debugfs_init(struct ecm_ipa_dev *ecm_ipa_ctx);
+static void ecm_ipa_debugfs_destroy(struct ecm_ipa_dev *ecm_ipa_ctx);
static int ecm_ipa_ep_registers_cfg(u32 usb_to_ipa_hdl, u32 ipa_to_usb_hdl);
static int ecm_ipa_ep_registers_dma_cfg(u32 usb_to_ipa_hdl);
static int ecm_ipa_set_device_ethernet_addr(u8 *dev_ethaddr,
u8 device_ethaddr[]);
+static bool ecm_ipa_state_validate(enum ecm_ipa_mode current_mode,
+ enum ecm_ipa_mode new_mode);
static int ecm_ipa_init_module(void);
static void ecm_ipa_cleanup_module(void);
@@ -134,7 +155,7 @@
};
const struct file_operations ecm_ipa_debugfs_dma_ops = {
- .open = ecm_ipa_debugfs_dma_open,
+ .open = ecm_ipa_debugfs_dma_open,
.read = ecm_ipa_debugfs_enable_read,
.write = ecm_ipa_debugfs_enable_write_dma,
};
@@ -146,7 +167,7 @@
/**
* ecm_ipa_init() - create network device and initializes internal
- * data structures
+ * data structures
* @params: in/out parameters required for ecm_ipa initialization
*
* Shall be called prior to pipe connection.
@@ -170,7 +191,7 @@
{
int result = 0;
struct net_device *net;
- struct ecm_ipa_dev *dev;
+ struct ecm_ipa_dev *ecm_ipa_ctx;
ECM_IPA_LOG_ENTRY();
pr_debug("%s initializing\n", DRIVER_NAME);
@@ -188,26 +209,25 @@
}
pr_debug("network device was successfully allocated\n");
- dev = netdev_priv(net);
- memset(dev, 0, sizeof(*dev));
- dev->net = net;
- ecm_ipa_ctx = dev;
- dev->tx_enable = true;
- dev->rx_enable = true;
- dev->rm_enable = true;
- dev->outstanding_high = DEFAULT_OUTSTANDING_HIGH;
- dev->outstanding_low = DEFAULT_OUTSTANDING_LOW;
- atomic_set(&dev->outstanding_pkts, 0);
+ ecm_ipa_ctx = netdev_priv(net);
+ memset(ecm_ipa_ctx, 0, sizeof(*ecm_ipa_ctx));
+ ecm_ipa_ctx->net = net;
+ ecm_ipa_ctx->tx_enable = true;
+ ecm_ipa_ctx->rx_enable = true;
+ ecm_ipa_ctx->rm_enable = true;
+ ecm_ipa_ctx->outstanding_high = DEFAULT_OUTSTANDING_HIGH;
+ ecm_ipa_ctx->outstanding_low = DEFAULT_OUTSTANDING_LOW;
+ atomic_set(&ecm_ipa_ctx->outstanding_pkts, 0);
snprintf(net->name, sizeof(net->name), "%s%%d", "ecm");
net->netdev_ops = &ecm_ipa_netdev_ops;
pr_debug("internal data structures were intialized and defaults set\n");
- result = ecm_ipa_debugfs_init(dev);
+ result = ecm_ipa_debugfs_init(ecm_ipa_ctx);
if (result)
goto fail_debugfs;
pr_debug("debugfs entries were created\n");
- result = ecm_ipa_create_rm_resource(dev);
+ result = ecm_ipa_create_rm_resource(ecm_ipa_ctx);
if (result) {
ECM_IPA_ERROR("fail on RM create\n");
goto fail_create_rm;
@@ -222,7 +242,7 @@
}
pr_debug("Device Ethernet address set %pM\n", net->dev_addr);
- result = ecm_ipa_rules_cfg(dev, params->host_ethaddr,
+ result = ecm_ipa_rules_cfg(ecm_ipa_ctx, params->host_ethaddr,
params->device_ethaddr);
if (result) {
ECM_IPA_ERROR("fail on ipa rules set\n");
@@ -249,7 +269,10 @@
params->ecm_ipa_rx_dp_notify = ecm_ipa_packet_receive_notify;
params->ecm_ipa_tx_dp_notify = ecm_ipa_tx_complete_notify;
- params->private = (void *)dev;
+ params->private = (void *)ecm_ipa_ctx;
+ ecm_ipa_ctx->mode = ECM_IPA_INITIALIZED;
+ ECM_IPA_MODE(ecm_ipa_ctx);
+
ECM_IPA_LOG_EXIT();
return 0;
@@ -257,12 +280,12 @@
fail_register_netdev:
ecm_ipa_deregister_properties();
fail_register_tx:
- ecm_ipa_rules_destroy(dev);
+ ecm_ipa_rules_destroy(ecm_ipa_ctx);
fail_set_device_ethernet:
fail_rules_cfg:
- ecm_ipa_destory_rm_resource(dev);
+ ecm_ipa_destory_rm_resource(ecm_ipa_ctx);
fail_create_rm:
- ecm_ipa_debugfs_destroy(dev);
+ ecm_ipa_debugfs_destroy(ecm_ipa_ctx);
fail_debugfs:
free_netdev(net);
fail_alloc_etherdev:
@@ -270,17 +293,38 @@
}
EXPORT_SYMBOL(ecm_ipa_init);
-
+/**
+ * ecm_ipa_connect() - notify ecm_ipa for IPA<->USB pipes connection
+ * @usb_to_ipa_hdl: handle of IPA driver client for USB->IPA
+ * @ipa_to_usb_hdl: handle of IPA driver client for IPA->USB
+ * @priv: same value that was set by ecm_ipa_init(), this
+ * parameter holds the network device pointer.
+ *
+ * Once USB driver finishes the pipe connection between IPA core
+ * and USB core this method shall be called in order to
+ * allow ecm_ipa complete the data path configurations.
+ * Detailed description:
+ * - configure the IPA end-points register
+ * - notify the Linux kernel for "carrier_on"
+ * After this function is done the driver state changes to "Connected".
+ * This API is expected to be called after ecm_ipa_init() or
+ * after a call to ecm_ipa_disconnect.
+ */
int ecm_ipa_connect(u32 usb_to_ipa_hdl, u32 ipa_to_usb_hdl,
void *priv)
{
- struct ecm_ipa_dev *dev = priv;
+ struct ecm_ipa_dev *ecm_ipa_ctx = priv;
ECM_IPA_LOG_ENTRY();
NULL_CHECK(priv);
pr_debug("usb_to_ipa_hdl = %d, ipa_to_usb_hdl = %d, priv=0x%p\n",
usb_to_ipa_hdl, ipa_to_usb_hdl, priv);
+ if (!ecm_ipa_state_validate(ecm_ipa_ctx->mode, ECM_IPA_CONNECTED)) {
+ ECM_IPA_ERROR("can't call connect before driver init\n");
+ return -EPERM;
+ }
+
if (!usb_to_ipa_hdl || usb_to_ipa_hdl >= IPA_CLIENT_MAX) {
ECM_IPA_ERROR("usb_to_ipa_hdl(%d) is not a valid ipa handle\n",
usb_to_ipa_hdl);
@@ -291,29 +335,48 @@
ipa_to_usb_hdl);
return -EINVAL;
}
- dev->ipa_to_usb_hdl = ipa_to_usb_hdl;
- dev->usb_to_ipa_hdl = usb_to_ipa_hdl;
+ ecm_ipa_ctx->ipa_to_usb_hdl = ipa_to_usb_hdl;
+ ecm_ipa_ctx->usb_to_ipa_hdl = usb_to_ipa_hdl;
ecm_ipa_ep_registers_cfg(usb_to_ipa_hdl, ipa_to_usb_hdl);
pr_debug("end-point configured\n");
- netif_carrier_on(dev->net);
- if (!netif_carrier_ok(dev->net)) {
+ netif_carrier_on(ecm_ipa_ctx->net);
+ if (!netif_carrier_ok(ecm_ipa_ctx->net)) {
ECM_IPA_ERROR("netif_carrier_ok error\n");
return -EBUSY;
}
pr_debug("carrier_on notified, ecm_ipa is operational\n");
+ ecm_ipa_ctx->mode = ECM_IPA_CONNECTED;
+ ECM_IPA_MODE(ecm_ipa_ctx);
+
ECM_IPA_LOG_EXIT();
+
return 0;
}
EXPORT_SYMBOL(ecm_ipa_connect);
+/**
+ * ecm_ipa_open() - notify Linux network stack to start sending packets
+ * @net: the network interface supplied by the network stack
+ *
+ * Linux uses this API to notify the driver that the network interface
+ * transitions to the up state.
+ * The driver will instruct the Linux network stack to start
+ * delivering data packets.
+ */
static int ecm_ipa_open(struct net_device *net)
{
- struct ecm_ipa_dev *dev;
+ struct ecm_ipa_dev *ecm_ipa_ctx;
+
ECM_IPA_LOG_ENTRY();
- dev = netdev_priv(net);
+ ecm_ipa_ctx = netdev_priv(net);
+
+ if (!ecm_ipa_state_validate(ecm_ipa_ctx->mode, ECM_IPA_UP)) {
+ ECM_IPA_ERROR("can't bring driver up before cable connect\n");
+ return -EPERM;
+ }
if (!netif_carrier_ok(net))
pr_debug("carrier is not ON yet - continuing\n");
@@ -321,49 +384,68 @@
netif_start_queue(net);
pr_debug("queue started\n");
+ ecm_ipa_ctx->mode = ECM_IPA_UP;
+ ECM_IPA_MODE(ecm_ipa_ctx);
+
ECM_IPA_LOG_EXIT();
+
return 0;
}
/**
* ecm_ipa_start_xmit() - send data from APPs to USB core via IPA core
- * @skb: packet received from Linux stack
+ * @skb: packet received from Linux network stack
* @net: the network device being used to send this packet
*
* Several conditions needed in order to send the packet to IPA:
- * - we are in a valid state were the queue is not stopped
+ * - Transmit queue for the network driver is currently
+ * in "send" state
+ * - The driver internal state is in "UP" state.
* - Filter Tx switch is turned off
- * - The resources required for actual Tx are all up
+ * - The IPA resource manager state for the driver producer client
+ * is "Granted" which implies that all the resources in the dependency
+ * graph are valid for data flow.
+ * - outstanding high boundary did not reach.
*
+ * In case all of the above conditions are met, the network driver will
+ * send the packet by using the IPA API for Tx.
+ * In case the outstanding packet high boundary is reached, the driver will
+ * stop the send queue until enough packet were proceeded by the IPA core.
*/
static netdev_tx_t ecm_ipa_start_xmit(struct sk_buff *skb,
struct net_device *net)
{
int ret;
netdev_tx_t status = NETDEV_TX_BUSY;
- struct ecm_ipa_dev *dev = netdev_priv(net);
+ struct ecm_ipa_dev *ecm_ipa_ctx = netdev_priv(net);
if (unlikely(netif_queue_stopped(net))) {
ECM_IPA_ERROR("interface queue is stopped\n");
goto out;
}
+ if (unlikely(ecm_ipa_ctx->mode != ECM_IPA_UP)) {
+ ECM_IPA_ERROR("can't send without network interface up\n");
+ return -NETDEV_TX_BUSY;
+ }
+
if (unlikely(tx_filter(skb))) {
dev_kfree_skb_any(skb);
pr_debug("packet got filtered out on Tx path\n");
status = NETDEV_TX_OK;
goto out;
}
- ret = resource_request(dev);
+ ret = resource_request(ecm_ipa_ctx);
if (ret) {
pr_debug("Waiting to resource\n");
netif_stop_queue(net);
goto resource_busy;
}
- if (atomic_read(&dev->outstanding_pkts) >= dev->outstanding_high) {
+ if (atomic_read(&ecm_ipa_ctx->outstanding_pkts) >=
+ ecm_ipa_ctx->outstanding_high) {
pr_debug("Outstanding high boundary reached (%d)- stopping queue\n",
- dev->outstanding_high);
+ ecm_ipa_ctx->outstanding_high);
netif_stop_queue(net);
status = -NETDEV_TX_BUSY;
goto out;
@@ -375,7 +457,7 @@
goto fail_tx_packet;
}
- atomic_inc(&dev->outstanding_pkts);
+ atomic_inc(&ecm_ipa_ctx->outstanding_pkts);
net->stats.tx_packets++;
net->stats.tx_bytes += skb->len;
status = NETDEV_TX_OK;
@@ -383,7 +465,7 @@
fail_tx_packet:
out:
- resource_release(dev);
+ resource_release(ecm_ipa_ctx);
resource_busy:
return status;
}
@@ -395,14 +477,15 @@
* @evt: event type
* @data: data provided with event
*
- * IPA will pass a packet with skb->data pointing to Ethernet packet frame
+ * IPA will pass a packet to the Linux network stack with skb->data pointing
+ * to Ethernet packet frame.
*/
static void ecm_ipa_packet_receive_notify(void *priv,
enum ipa_dp_evt_type evt,
unsigned long data)
{
struct sk_buff *skb = (struct sk_buff *)data;
- struct ecm_ipa_dev *dev = priv;
+ struct ecm_ipa_dev *ecm_ipa_ctx = priv;
int result;
if (evt != IPA_RECEIVE) {
@@ -410,8 +493,8 @@
return;
}
- skb->dev = dev->net;
- skb->protocol = eth_type_trans(skb, dev->net);
+ skb->dev = ecm_ipa_ctx->net;
+ skb->protocol = eth_type_trans(skb, ecm_ipa_ctx->net);
if (rx_filter(skb)) {
pr_debug("packet got filtered out on Rx path\n");
dev_kfree_skb_any(skb);
@@ -421,92 +504,138 @@
result = netif_rx(skb);
if (result)
ECM_IPA_ERROR("fail on netif_rx\n");
- dev->net->stats.rx_packets++;
- dev->net->stats.rx_bytes += skb->len;
+ ecm_ipa_ctx->net->stats.rx_packets++;
+ ecm_ipa_ctx->net->stats.rx_bytes += skb->len;
return;
}
+/** ecm_ipa_stop() - called when network device transitions to the down
+ * state.
+ * @net: the network device being stopped.
+ *
+ * This API is used by Linux network stack to notify the network driver that
+ * its state was changed to "down"
+ * The driver will stop the "send" queue and change its internal
+ * state to "Connected".
+ */
static int ecm_ipa_stop(struct net_device *net)
{
+ struct ecm_ipa_dev *ecm_ipa_ctx = netdev_priv(net);
+
ECM_IPA_LOG_ENTRY();
- pr_debug("stopping net device\n");
+
+ if (!ecm_ipa_state_validate(ecm_ipa_ctx->mode, ECM_IPA_CONNECTED)) {
+ ECM_IPA_ERROR("can't do network interface down without up\n");
+ return -EPERM;
+ }
+
netif_stop_queue(net);
+ pr_debug("network device stopped\n");
+
+ ecm_ipa_ctx->mode = ECM_IPA_CONNECTED;
+ ECM_IPA_MODE(ecm_ipa_ctx);
+
ECM_IPA_LOG_EXIT();
return 0;
}
+/** ecm_ipa_disconnect() - called when the USB cable is unplugged.
+ * @priv: same value that was set by ecm_ipa_init(), this
+ * parameter holds the network device pointer.
+ *
+ * Once the USB cable is unplugged the USB driver will notify the network
+ * interface driver.
+ * The internal driver state will returned to its initialized state and
+ * Linux network stack will be informed for carrier off and the send queue
+ * will be stopped.
+ */
int ecm_ipa_disconnect(void *priv)
{
- struct ecm_ipa_dev *dev = priv;
+ struct ecm_ipa_dev *ecm_ipa_ctx = priv;
+
ECM_IPA_LOG_ENTRY();
- NULL_CHECK(dev);
+ NULL_CHECK(ecm_ipa_ctx);
pr_debug("priv=0x%p\n", priv);
- netif_carrier_off(dev->net);
+
+ if (!ecm_ipa_state_validate(ecm_ipa_ctx->mode, ECM_IPA_INITIALIZED)) {
+ ECM_IPA_ERROR("can't disconnect without connect first\n");
+ return -EPERM;
+ }
+
+ netif_carrier_off(ecm_ipa_ctx->net);
+ pr_debug("carrier_off notifcation was sent\n");
+
+ netif_stop_queue(ecm_ipa_ctx->net);
+ pr_debug("queue stopped\n");
+
+ ecm_ipa_ctx->mode = ECM_IPA_INITIALIZED;
+ ECM_IPA_MODE(ecm_ipa_ctx);
+
ECM_IPA_LOG_EXIT();
+
return 0;
}
EXPORT_SYMBOL(ecm_ipa_disconnect);
/**
- * ecm_ipa_cleanup() - destroys all
- * ecm information
- * @priv: main driver context parameters
+ * ecm_ipa_cleanup() - unregister the network interface driver and free
+ * internal data structs.
+ * @priv: same value that was set by ecm_ipa_init(), this
+ * parameter holds the network device pointer.
*
+ * This function shall be called once the network interface is not
+ * needed anymore, e.g: when the USB composition does not support ECM.
+ * This function shall be called after the pipes were disconnected.
+ * Detailed description:
+ * - delete the driver dependency defined for IPA resource manager and
+ * destroy the producer resource.
+ * - remove the debugfs entries
+ * - deregister the network interface from Linux network stack
+ * - free all internal data structs
*/
void ecm_ipa_cleanup(void *priv)
{
- struct ecm_ipa_dev *dev = priv;
+ struct ecm_ipa_dev *ecm_ipa_ctx = priv;
+
ECM_IPA_LOG_ENTRY();
+
pr_debug("priv=0x%p\n", priv);
- if (!dev) {
- ECM_IPA_ERROR("dev NULL pointer\n");
+
+ if (!ecm_ipa_ctx) {
+ ECM_IPA_ERROR("ecm_ipa_ctx NULL pointer\n");
return;
}
- ecm_ipa_destory_rm_resource(dev);
- ecm_ipa_debugfs_destroy(dev);
+ if (!ecm_ipa_state_validate(ecm_ipa_ctx->mode, ECM_IPA_LOADED))
+ ECM_IPA_ERROR("can't clean driver without cable disconnect\n");
- unregister_netdev(dev->net);
- free_netdev(dev->net);
+
+ ecm_ipa_destory_rm_resource(ecm_ipa_ctx);
+ ecm_ipa_debugfs_destroy(ecm_ipa_ctx);
+
+ unregister_netdev(ecm_ipa_ctx->net);
+ free_netdev(ecm_ipa_ctx->net);
pr_debug("cleanup done\n");
ecm_ipa_ctx = NULL;
ECM_IPA_LOG_EXIT();
+
return ;
}
EXPORT_SYMBOL(ecm_ipa_cleanup);
/**
- * @ecm_ipa_rx_dp_notify: supplied callback to be called by the IPA
- * driver upon data packets received from USB pipe into IPA core.
- * @ecm_ipa_rt_dp_notify: supplied callback to be called by the IPA
- * driver upon exception packets sent from IPA pipe into USB core.
- * @priv: should be passed later on to ecm_ipa_configure, hold the network
- * structure allocated for STD ECM interface.
- *
- * Shall be called prior to pipe connection.
- * The out parameters (the callbacks) shall be supplied to ipa_connect.
- * Detailed description:
- * - set the callbacks to be used by the caller upon ipa_connect
- * - allocate the network device
- * - set the priv argument with a reference to the network device
- *
- * Returns negative errno, or zero on success
- */
-
-
-/**
* ecm_ipa_rules_cfg() - set header insertion and register Tx/Rx properties
* Headers will be commited to HW
- * @dev: main driver context parameters
+ * @ecm_ipa_ctx: main driver context parameters
* @dst_mac: destination MAC address
* @src_mac: source MAC address
*
* Returns negative errno, or zero on success
*/
-static int ecm_ipa_rules_cfg(struct ecm_ipa_dev *dev,
+static int ecm_ipa_rules_cfg(struct ecm_ipa_dev *ecm_ipa_ctx,
const void *dst_mac, const void *src_mac)
{
struct ipa_ioc_add_hdr *hdrs;
@@ -558,8 +687,8 @@
result = ipv6_hdr->status;
goto out_free_mem;
}
- dev->eth_ipv4_hdr_hdl = ipv4_hdr->hdr_hdl;
- dev->eth_ipv6_hdr_hdl = ipv6_hdr->hdr_hdl;
+ ecm_ipa_ctx->eth_ipv4_hdr_hdl = ipv4_hdr->hdr_hdl;
+ ecm_ipa_ctx->eth_ipv6_hdr_hdl = ipv6_hdr->hdr_hdl;
ECM_IPA_LOG_EXIT();
out_free_mem:
kfree(hdrs);
@@ -567,7 +696,14 @@
return result;
}
-static void ecm_ipa_rules_destroy(struct ecm_ipa_dev *dev)
+/**
+ * ecm_ipa_rules_destroy() - remove the IPA core configuration done for
+ * the driver data path.
+ * @ecm_ipa_ctx: the driver context
+ *
+ * Revert the work done on ecm_ipa_rules_cfg.
+ */
+static void ecm_ipa_rules_destroy(struct ecm_ipa_dev *ecm_ipa_ctx)
{
struct ipa_ioc_del_hdr *del_hdr;
struct ipa_hdr_del *ipv4;
@@ -580,9 +716,9 @@
del_hdr->commit = 1;
del_hdr->num_hdls = 2;
ipv4 = &del_hdr->hdl[0];
- ipv4->hdl = dev->eth_ipv4_hdr_hdl;
+ ipv4->hdl = ecm_ipa_ctx->eth_ipv4_hdr_hdl;
ipv6 = &del_hdr->hdl[1];
- ipv6->hdl = dev->eth_ipv6_hdr_hdl;
+ ipv6->hdl = ecm_ipa_ctx->eth_ipv6_hdr_hdl;
result = ipa_del_hdr(del_hdr);
if (result || ipv4->status || ipv6->status)
ECM_IPA_ERROR("ipa_del_hdr failed");
@@ -676,29 +812,25 @@
static void ecm_ipa_rm_notify(void *user_data, enum ipa_rm_event event,
unsigned long data)
{
- struct ecm_ipa_dev *dev = user_data;
+ struct ecm_ipa_dev *ecm_ipa_ctx = user_data;
ECM_IPA_LOG_ENTRY();
if (event == IPA_RM_RESOURCE_GRANTED &&
- netif_queue_stopped(dev->net)) {
+ netif_queue_stopped(ecm_ipa_ctx->net)) {
pr_debug("Resource Granted - waking queue\n");
- netif_wake_queue(dev->net);
+ netif_wake_queue(ecm_ipa_ctx->net);
} else {
pr_debug("Resource released\n");
}
ECM_IPA_LOG_EXIT();
}
-static int ecm_ipa_create_rm_resource(struct ecm_ipa_dev *dev)
+static int ecm_ipa_create_rm_resource(struct ecm_ipa_dev *ecm_ipa_ctx)
{
struct ipa_rm_create_params create_params = {0};
int result;
ECM_IPA_LOG_ENTRY();
- if (!dev->rm_enable) {
- pr_debug("RM feature not used\n");
- return 0;
- }
create_params.name = IPA_RM_RESOURCE_STD_ECM_PROD;
- create_params.reg_params.user_data = dev;
+ create_params.reg_params.user_data = ecm_ipa_ctx;
create_params.reg_params.notify_cb = ecm_ipa_rm_notify;
result = ipa_rm_create_resource(&create_params);
if (result) {
@@ -730,14 +862,12 @@
return result;
}
-static void ecm_ipa_destory_rm_resource(struct ecm_ipa_dev *dev)
+static void ecm_ipa_destory_rm_resource(struct ecm_ipa_dev *ecm_ipa_ctx)
{
int result;
ECM_IPA_LOG_ENTRY();
- if (!dev->rm_enable)
- return;
ipa_rm_delete_dependency(IPA_RM_RESOURCE_STD_ECM_PROD,
IPA_RM_RESOURCE_USB_CONS);
ipa_rm_inactivity_timer_destroy(IPA_RM_RESOURCE_STD_ECM_PROD);
@@ -750,26 +880,26 @@
static bool rx_filter(struct sk_buff *skb)
{
- struct ecm_ipa_dev *dev = netdev_priv(skb->dev);
- return !dev->rx_enable;
+ struct ecm_ipa_dev *ecm_ipa_ctx = netdev_priv(skb->dev);
+ return !ecm_ipa_ctx->rx_enable;
}
static bool tx_filter(struct sk_buff *skb)
{
- struct ecm_ipa_dev *dev = netdev_priv(skb->dev);
- return !dev->tx_enable;
+ struct ecm_ipa_dev *ecm_ipa_ctx = netdev_priv(skb->dev);
+ return !ecm_ipa_ctx->tx_enable;
}
-static bool rm_enabled(struct ecm_ipa_dev *dev)
+static bool rm_enabled(struct ecm_ipa_dev *ecm_ipa_ctx)
{
- return dev->rm_enable;
+ return ecm_ipa_ctx->rm_enable;
}
-static int resource_request(struct ecm_ipa_dev *dev)
+static int resource_request(struct ecm_ipa_dev *ecm_ipa_ctx)
{
int result = 0;
- if (!rm_enabled(dev))
+ if (!rm_enabled(ecm_ipa_ctx))
goto out;
result = ipa_rm_inactivity_timer_request_resource(
IPA_RM_RESOURCE_STD_ECM_PROD);
@@ -777,9 +907,9 @@
return result;
}
-static void resource_release(struct ecm_ipa_dev *dev)
+static void resource_release(struct ecm_ipa_dev *ecm_ipa_ctx)
{
- if (!rm_enabled(dev))
+ if (!rm_enabled(ecm_ipa_ctx))
goto out;
ipa_rm_inactivity_timer_release_resource(IPA_RM_RESOURCE_STD_ECM_PROD);
out:
@@ -801,22 +931,23 @@
unsigned long data)
{
struct sk_buff *skb = (struct sk_buff *)data;
- struct ecm_ipa_dev *dev = priv;
+ struct ecm_ipa_dev *ecm_ipa_ctx = priv;
- if (!dev) {
- ECM_IPA_ERROR("dev is NULL pointer\n");
+ if (!ecm_ipa_ctx) {
+ ECM_IPA_ERROR("ecm_ipa_ctx is NULL pointer\n");
return;
}
if (evt != IPA_WRITE_DONE) {
ECM_IPA_ERROR("unsupported event on Tx callback\n");
return;
}
- atomic_dec(&dev->outstanding_pkts);
- if (netif_queue_stopped(dev->net) &&
- atomic_read(&dev->outstanding_pkts) < (dev->outstanding_low)) {
+ atomic_dec(&ecm_ipa_ctx->outstanding_pkts);
+ if (netif_queue_stopped(ecm_ipa_ctx->net) &&
+ atomic_read(&ecm_ipa_ctx->outstanding_pkts) <
+ (ecm_ipa_ctx->outstanding_low)) {
pr_debug("Outstanding low boundary reached (%d) - waking up queue\n",
- dev->outstanding_low);
- netif_wake_queue(dev->net);
+ ecm_ipa_ctx->outstanding_low);
+ netif_wake_queue(ecm_ipa_ctx->net);
}
dev_kfree_skb_any(skb);
@@ -825,9 +956,9 @@
static int ecm_ipa_debugfs_atomic_open(struct inode *inode, struct file *file)
{
- struct ecm_ipa_dev *dev = inode->i_private;
+ struct ecm_ipa_dev *ecm_ipa_ctx = inode->i_private;
ECM_IPA_LOG_ENTRY();
- file->private_data = &(dev->outstanding_pkts);
+ file->private_data = &(ecm_ipa_ctx->outstanding_pkts);
ECM_IPA_LOG_EXIT();
return 0;
}
@@ -835,25 +966,25 @@
static ssize_t ecm_ipa_debugfs_enable_write_dma(struct file *file,
const char __user *buf, size_t count, loff_t *ppos)
{
- struct ecm_ipa_dev *dev = file->private_data;
+ struct ecm_ipa_dev *ecm_ipa_ctx = file->private_data;
int result;
ECM_IPA_LOG_ENTRY();
- file->private_data = &dev->dma_enable;
+ file->private_data = &ecm_ipa_ctx->dma_enable;
result = ecm_ipa_debugfs_enable_write(file, buf, count, ppos);
- if (dev->dma_enable)
- ecm_ipa_ep_registers_dma_cfg(dev->usb_to_ipa_hdl);
+ if (ecm_ipa_ctx->dma_enable)
+ ecm_ipa_ep_registers_dma_cfg(ecm_ipa_ctx->usb_to_ipa_hdl);
else
- ecm_ipa_ep_registers_cfg(dev->usb_to_ipa_hdl,
- dev->usb_to_ipa_hdl);
+ ecm_ipa_ep_registers_cfg(ecm_ipa_ctx->usb_to_ipa_hdl,
+ ecm_ipa_ctx->usb_to_ipa_hdl);
ECM_IPA_LOG_EXIT();
return result;
}
static int ecm_ipa_debugfs_dma_open(struct inode *inode, struct file *file)
{
- struct ecm_ipa_dev *dev = inode->i_private;
+ struct ecm_ipa_dev *ecm_ipa_ctx = inode->i_private;
ECM_IPA_LOG_ENTRY();
- file->private_data = dev;
+ file->private_data = ecm_ipa_ctx;
ECM_IPA_LOG_EXIT();
return 0;
}
@@ -915,7 +1046,7 @@
}
-static int ecm_ipa_debugfs_init(struct ecm_ipa_dev *dev)
+static int ecm_ipa_debugfs_init(struct ecm_ipa_dev *ecm_ipa_ctx)
{
const mode_t flags_read_write = S_IRUGO | S_IWUGO;
const mode_t flags_read_only = S_IRUGO;
@@ -923,68 +1054,71 @@
ECM_IPA_LOG_ENTRY();
- if (!dev)
+ if (!ecm_ipa_ctx)
return -EINVAL;
- dev->directory = debugfs_create_dir("ecm_ipa", NULL);
- if (!dev->directory) {
+ ecm_ipa_ctx->directory = debugfs_create_dir("ecm_ipa", NULL);
+ if (!ecm_ipa_ctx->directory) {
ECM_IPA_ERROR("could not create debugfs directory entry\n");
goto fail_directory;
}
file = debugfs_create_bool("tx_enable", flags_read_write,
- dev->directory, &dev->tx_enable);
+ ecm_ipa_ctx->directory, &ecm_ipa_ctx->tx_enable);
if (!file) {
ECM_IPA_ERROR("could not create debugfs tx file\n");
goto fail_file;
}
file = debugfs_create_bool("rx_enable", flags_read_write,
- dev->directory, &dev->rx_enable);
+ ecm_ipa_ctx->directory, &ecm_ipa_ctx->rx_enable);
if (!file) {
ECM_IPA_ERROR("could not create debugfs rx file\n");
goto fail_file;
}
file = debugfs_create_bool("rm_enable", flags_read_write,
- dev->directory, &dev->rm_enable);
+ ecm_ipa_ctx->directory, &ecm_ipa_ctx->rm_enable);
if (!file) {
ECM_IPA_ERROR("could not create debugfs rm file\n");
goto fail_file;
}
file = debugfs_create_u8("outstanding_high", flags_read_write,
- dev->directory, &dev->outstanding_high);
+ ecm_ipa_ctx->directory, &ecm_ipa_ctx->outstanding_high);
if (!file) {
ECM_IPA_ERROR("could not create outstanding_high file\n");
goto fail_file;
}
file = debugfs_create_u8("outstanding_low", flags_read_write,
- dev->directory, &dev->outstanding_low);
+ ecm_ipa_ctx->directory, &ecm_ipa_ctx->outstanding_low);
if (!file) {
ECM_IPA_ERROR("could not create outstanding_low file\n");
goto fail_file;
}
file = debugfs_create_file("dma_enable", flags_read_write,
- dev->directory, dev, &ecm_ipa_debugfs_dma_ops);
+ ecm_ipa_ctx->directory,
+ ecm_ipa_ctx, &ecm_ipa_debugfs_dma_ops);
if (!file) {
ECM_IPA_ERROR("could not create debugfs dma file\n");
goto fail_file;
}
file = debugfs_create_file("outstanding", flags_read_only,
- dev->directory, dev, &ecm_ipa_debugfs_atomic_ops);
+ ecm_ipa_ctx->directory,
+ ecm_ipa_ctx, &ecm_ipa_debugfs_atomic_ops);
if (!file) {
ECM_IPA_ERROR("could not create outstanding file\n");
goto fail_file;
}
ECM_IPA_LOG_EXIT();
+
return 0;
fail_file:
- debugfs_remove_recursive(dev->directory);
+ debugfs_remove_recursive(ecm_ipa_ctx->directory);
fail_directory:
return -EFAULT;
}
-static void ecm_ipa_debugfs_destroy(struct ecm_ipa_dev *dev)
+static void ecm_ipa_debugfs_destroy(struct ecm_ipa_dev *ecm_ipa_ctx)
{
- debugfs_remove_recursive(dev->directory);
+ debugfs_remove_recursive(ecm_ipa_ctx->directory);
}
/**
@@ -1090,6 +1224,48 @@
return 0;
}
+/** ecm_ipa_state_validate - check if a state transition is allowed
+ *
+ * Allowed transition:
+ * LOADED->INITIALIZED: ecm_ipa_init()
+ * INITIALIZED->CONNECTED: ecm_ipa_connect()
+ * CONNECTED->INITIALIZED: ecm_ipa_disconnect()
+ * CONNECTED->UP: ecm_ipa_open()
+ * UP->CONNECTED: ecm_ipa_stop()
+ * UP->INITIALIZED: ecm_ipa_disconnect()
+ * INITIALIZED-> LOADED
+ */
+static bool ecm_ipa_state_validate(enum ecm_ipa_mode current_mode,
+ enum ecm_ipa_mode new_mode)
+{
+ bool result;
+
+ switch (current_mode) {
+ case ECM_IPA_LOADED:
+ result = (new_mode == ECM_IPA_INITIALIZED);
+ break;
+ case ECM_IPA_INITIALIZED:
+ result = (new_mode == ECM_IPA_CONNECTED ||
+ new_mode == ECM_IPA_LOADED);
+ break;
+ case ECM_IPA_CONNECTED:
+ result = (new_mode == ECM_IPA_INITIALIZED ||
+ new_mode == ECM_IPA_UP);
+ break;
+ case ECM_IPA_UP:
+ result = (new_mode == ECM_IPA_CONNECTED ||
+ new_mode == ECM_IPA_INITIALIZED);
+ break;
+ default:
+ result = false;
+ break;
+ }
+
+ pr_debug("state transition (%d->%d)- %s\n", current_mode,
+ new_mode , result ? "Allowed" : "Forbidden");
+ return result;
+}
+
/**
* ecm_ipa_init_module() - module initialization
*
diff --git a/drivers/net/wireless/wcnss/wcnss_vreg.c b/drivers/net/wireless/wcnss/wcnss_vreg.c
index 7e6cd4f..59a6b68 100644
--- a/drivers/net/wireless/wcnss/wcnss_vreg.c
+++ b/drivers/net/wireless/wcnss/wcnss_vreg.c
@@ -172,16 +172,13 @@
}
/* NV bit is set to indicate that platform driver is capable
- * of doing NV download. SSR should not set NV bit; during
- * SSR NV bin is downloaded by WLAN driver.
+ * of doing NV download.
*/
- if (!wcnss_cold_boot_done()) {
- pr_debug("wcnss: Indicate NV bin download\n");
- spare_reg = msm_wcnss_base + spare_offset;
- reg = readl_relaxed(spare_reg);
- reg |= NVBIN_DLND_BIT;
- writel_relaxed(reg, spare_reg);
- }
+ pr_debug("wcnss: Indicate NV bin download\n");
+ spare_reg = msm_wcnss_base + spare_offset;
+ reg = readl_relaxed(spare_reg);
+ reg |= NVBIN_DLND_BIT;
+ writel_relaxed(reg, spare_reg);
pmu_conf_reg = msm_wcnss_base + pmu_offset;
writel_relaxed(0, pmu_conf_reg);
diff --git a/drivers/net/wireless/wcnss/wcnss_wlan.c b/drivers/net/wireless/wcnss/wcnss_wlan.c
index 1a175c9..f837a06 100644
--- a/drivers/net/wireless/wcnss/wcnss_wlan.c
+++ b/drivers/net/wireless/wcnss/wcnss_wlan.c
@@ -28,6 +28,9 @@
#include <linux/of_gpio.h>
#include <linux/clk.h>
#include <linux/ratelimit.h>
+#include <linux/kthread.h>
+#include <linux/wait.h>
+#include <linux/uaccess.h>
#include <mach/msm_smd.h>
#include <mach/msm_iomap.h>
@@ -48,6 +51,10 @@
module_param(has_48mhz_xo, int, S_IWUSR | S_IRUGO);
MODULE_PARM_DESC(has_48mhz_xo, "Is an external 48 MHz XO present");
+static int has_calibrated_data = WCNSS_CONFIG_UNSPECIFIED;
+module_param(has_calibrated_data, int, S_IWUSR | S_IRUGO);
+MODULE_PARM_DESC(has_calibrated_data, "whether calibrated data file available");
+
static int do_not_cancel_vote = WCNSS_CONFIG_UNSPECIFIED;
module_param(do_not_cancel_vote, int, S_IWUSR | S_IRUGO);
MODULE_PARM_DESC(do_not_cancel_vote, "Do not cancel votes for wcnss");
@@ -92,20 +99,27 @@
#define CCU_PRONTO_LAST_ADDR2_OFFSET 0x14
#define WCNSS_CTRL_CHANNEL "WCNSS_CTRL"
-#define WCNSS_MAX_FRAME_SIZE 500
+#define WCNSS_MAX_FRAME_SIZE (4*1024)
#define WCNSS_VERSION_LEN 30
/* message types */
#define WCNSS_CTRL_MSG_START 0x01000000
-#define WCNSS_VERSION_REQ (WCNSS_CTRL_MSG_START + 0)
-#define WCNSS_VERSION_RSP (WCNSS_CTRL_MSG_START + 1)
-#define WCNSS_NVBIN_DNLD_REQ (WCNSS_CTRL_MSG_START + 2)
-#define WCNSS_NVBIN_DNLD_RSP (WCNSS_CTRL_MSG_START + 3)
+#define WCNSS_VERSION_REQ (WCNSS_CTRL_MSG_START + 0)
+#define WCNSS_VERSION_RSP (WCNSS_CTRL_MSG_START + 1)
+#define WCNSS_NVBIN_DNLD_REQ (WCNSS_CTRL_MSG_START + 2)
+#define WCNSS_NVBIN_DNLD_RSP (WCNSS_CTRL_MSG_START + 3)
+#define WCNSS_CALDATA_UPLD_REQ (WCNSS_CTRL_MSG_START + 4)
+#define WCNSS_CALDATA_UPLD_RSP (WCNSS_CTRL_MSG_START + 5)
+#define WCNSS_CALDATA_DNLD_REQ (WCNSS_CTRL_MSG_START + 6)
+#define WCNSS_CALDATA_DNLD_RSP (WCNSS_CTRL_MSG_START + 7)
#define VALID_VERSION(version) \
((strncmp(version, "INVALID", WCNSS_VERSION_LEN)) ? 1 : 0)
+#define FW_CALDATA_CAPABLE() \
+ ((penv->fw_major >= 1) && (penv->fw_minor >= 5) ? 1 : 0)
+
struct smd_msg_hdr {
unsigned int msg_type;
unsigned int msg_len;
@@ -126,6 +140,13 @@
* header, so NV fragment size as next multiple of 1Kb is 3Kb.
*/
#define NV_FRAGMENT_SIZE 3072
+#define MAX_CALIBRATED_DATA_SIZE (64*1024)
+#define LAST_FRAGMENT (1 << 0)
+#define MESSAGE_TO_FOLLOW (1 << 1)
+#define CAN_RECEIVE_CALDATA (1 << 15)
+#define WCNSS_RESP_SUCCESS 1
+#define WCNSS_RESP_FAIL 0
+
/* Macro to find the total number fragments of the NV bin Image */
#define TOTALFRAGMENTS(x) (((x % NV_FRAGMENT_SIZE) == 0) ? \
@@ -144,11 +165,14 @@
unsigned short frag_number;
/*
- * When set to 1 it indicates that no more fragments will
- * be sent. Receiver shall send back response message after
- * the last fragment.
+ * bit 0: When set to 1 it indicates that no more fragments will
+ * be sent.
+ * bit 1: When set, a new message will be followed by this message
+ * bit 2- bit 14: Reserved
+ * bit 15: when set, it indicates that the sender is capable of
+ * receiving Calibrated data.
*/
- unsigned short is_last_fragment;
+ unsigned short msg_flags;
/* NV Image size (number of bytes) */
unsigned int nvbin_buffer_size;
@@ -160,6 +184,7 @@
*/
};
+
struct nvbin_dnld_req_msg {
/*
* Note: The length specified in nvbin_dnld_req_msg messages
@@ -170,6 +195,39 @@
struct nvbin_dnld_req_params dnld_req_params;
};
+struct cal_data_params {
+
+ /* The total size of the calibrated data, including all the
+ * fragments.
+ */
+ unsigned int total_size;
+ unsigned short frag_number;
+ /*
+ * bit 0: When set to 1 it indicates that no more fragments will
+ * be sent.
+ * bit 1: When set, a new message will be followed by this message
+ * bit 2- bit 15: Reserved
+ */
+ unsigned short msg_flags;
+ /*
+ * fragment size
+ */
+ unsigned int frag_size;
+ /*
+ * Following the frag_size, frag_size of fragmented
+ * data will be followed.
+ */
+};
+
+struct cal_data_msg {
+ /*
+ * The length specified in cal_data_msg should be
+ * hdr.msg_len = sizeof(cal_data_msg) + frag_size
+ */
+ struct smd_msg_hdr hdr;
+ struct cal_data_params cal_params;
+};
+
static struct {
struct platform_device *pdev;
void *pil;
@@ -180,9 +238,10 @@
const struct dev_pm_ops *pm_ops;
int triggered;
int smd_channel_ready;
- int cold_boot_done;
smd_channel_t *smd_ch;
unsigned char wcnss_version[WCNSS_VERSION_LEN];
+ unsigned char fw_major;
+ unsigned char fw_minor;
unsigned int serial_number;
int thermal_mitigation;
enum wcnss_hw_type wcnss_hw_type;
@@ -198,6 +257,20 @@
void __iomem *pronto_a2xb_base;
void __iomem *pronto_ccpu_base;
void __iomem *fiq_reg;
+ int ssr_boot;
+ int nv_downloaded;
+ unsigned char *fw_cal_data;
+ unsigned char *user_cal_data;
+ int fw_cal_rcvd;
+ int fw_cal_exp_frag;
+ int fw_cal_available;
+ int user_cal_read;
+ int user_cal_available;
+ int user_cal_rcvd;
+ int user_cal_exp_size;
+ int device_opened;
+ struct mutex dev_lock;
+ wait_queue_head_t read_wait;
} *penv = NULL;
static ssize_t wcnss_serial_number_show(struct device *dev,
@@ -459,21 +532,26 @@
switch (event) {
case SMD_EVENT_DATA:
len = smd_read_avail(penv->smd_ch);
- if (len < 0)
+ if (len < 0) {
pr_err("wcnss: failed to read from smd %d\n", len);
+ return;
+ }
schedule_work(&penv->wcnssctrl_rx_work);
break;
case SMD_EVENT_OPEN:
pr_debug("wcnss: opening WCNSS SMD channel :%s",
WCNSS_CTRL_CHANNEL);
- if (!VALID_VERSION(penv->wcnss_version))
- schedule_work(&penv->wcnssctrl_version_work);
+ schedule_work(&penv->wcnssctrl_version_work);
+
break;
case SMD_EVENT_CLOSE:
pr_debug("wcnss: closing WCNSS SMD channel :%s",
WCNSS_CTRL_CHANNEL);
+ /* This SMD is closed only during SSR */
+ penv->ssr_boot = true;
+ penv->nv_downloaded = 0;
break;
default:
@@ -658,6 +736,15 @@
}
EXPORT_SYMBOL(wcnss_get_wlan_config);
+int wcnss_device_ready(void)
+{
+ if (penv && penv->pdev && penv->nv_downloaded)
+ return 1;
+ return 0;
+}
+EXPORT_SYMBOL(wcnss_device_ready);
+
+
struct resource *wcnss_wlan_get_memory_map(struct device *dev)
{
if (penv && dev && (dev == &penv->pdev->dev) && penv->smd_channel_ready)
@@ -837,15 +924,13 @@
}
EXPORT_SYMBOL(wcnss_hardware_type);
-int wcnss_cold_boot_done(void)
+int fw_cal_data_available(void)
{
if (penv)
- return penv->cold_boot_done;
+ return penv->fw_cal_available;
else
return -ENODEV;
}
-EXPORT_SYMBOL(wcnss_cold_boot_done);
-
static int wcnss_smd_tx(void *data, int len)
{
@@ -864,14 +949,145 @@
return ret;
}
+static unsigned char wcnss_fw_status(void)
+{
+ int len = 0;
+ int rc = 0;
+
+ unsigned char fw_status = 0xFF;
+
+ len = smd_read_avail(penv->smd_ch);
+ if (len < 1) {
+ pr_err("%s: invalid firmware status", __func__);
+ return fw_status;
+ }
+
+ rc = smd_read(penv->smd_ch, &fw_status, 1);
+ if (rc < 0) {
+ pr_err("%s: incomplete data read from smd\n", __func__);
+ return fw_status;
+ }
+ return fw_status;
+}
+
+static void wcnss_send_cal_rsp(unsigned char fw_status)
+{
+ struct smd_msg_hdr *rsphdr;
+ unsigned char *msg = NULL;
+ int rc;
+
+ msg = kmalloc((sizeof(struct smd_msg_hdr) + 1), GFP_KERNEL);
+ if (NULL == msg) {
+ pr_err("wcnss: %s: failed to get memory\n", __func__);
+ return;
+ }
+
+ rsphdr = (struct smd_msg_hdr *)msg;
+ rsphdr->msg_type = WCNSS_CALDATA_UPLD_RSP;
+ rsphdr->msg_len = sizeof(struct smd_msg_hdr) + 1;
+ memcpy(msg+sizeof(struct smd_msg_hdr), &fw_status, 1);
+
+ rc = wcnss_smd_tx(msg, rsphdr->msg_len);
+ if (rc < 0)
+ pr_err("wcnss: smd tx failed\n");
+}
+
+/* Collect calibrated data from WCNSS */
+void extract_cal_data(int len)
+{
+ int rc;
+ struct cal_data_params calhdr;
+ unsigned char fw_status = WCNSS_RESP_FAIL;
+
+ if (len < sizeof(struct cal_data_params)) {
+ pr_err("wcnss: incomplete cal header length\n");
+ return;
+ }
+
+ rc = smd_read(penv->smd_ch, (unsigned char *)&calhdr,
+ sizeof(struct cal_data_params));
+ if (rc < sizeof(struct cal_data_params)) {
+ pr_err("wcnss: incomplete cal header read from smd\n");
+ return;
+ }
+
+ if (penv->fw_cal_exp_frag != calhdr.frag_number) {
+ pr_err("wcnss: Invalid frgament");
+ goto exit;
+ }
+
+ if (calhdr.frag_size > WCNSS_MAX_FRAME_SIZE) {
+ pr_err("wcnss: Invalid fragment size");
+ goto exit;
+ }
+
+ if (0 == calhdr.frag_number) {
+ if (calhdr.total_size > MAX_CALIBRATED_DATA_SIZE) {
+ pr_err("wcnss: Invalid cal data size %d",
+ calhdr.total_size);
+ goto exit;
+ }
+ kfree(penv->fw_cal_data);
+ penv->fw_cal_rcvd = 0;
+ penv->fw_cal_data = kmalloc(calhdr.total_size,
+ GFP_KERNEL);
+ if (penv->fw_cal_data == NULL) {
+ smd_read(penv->smd_ch, NULL, calhdr.frag_size);
+ goto exit;
+ }
+ }
+
+ mutex_lock(&penv->dev_lock);
+ if (penv->fw_cal_rcvd + calhdr.frag_size >
+ MAX_CALIBRATED_DATA_SIZE) {
+ pr_err("calibrated data size is more than expected %d",
+ penv->fw_cal_rcvd + calhdr.frag_size);
+ penv->fw_cal_exp_frag = 0;
+ penv->fw_cal_rcvd = 0;
+ smd_read(penv->smd_ch, NULL, calhdr.frag_size);
+ goto unlock_exit;
+ }
+
+ rc = smd_read(penv->smd_ch, penv->fw_cal_data + penv->fw_cal_rcvd,
+ calhdr.frag_size);
+ if (rc < calhdr.frag_size)
+ goto unlock_exit;
+
+ penv->fw_cal_exp_frag++;
+ penv->fw_cal_rcvd += calhdr.frag_size;
+
+ if (calhdr.msg_flags & LAST_FRAGMENT) {
+ penv->fw_cal_exp_frag = 0;
+ penv->fw_cal_available = true;
+ pr_info("wcnss: cal data collection completed\n");
+ }
+ mutex_unlock(&penv->dev_lock);
+ wake_up(&penv->read_wait);
+
+ if (penv->fw_cal_available) {
+ fw_status = WCNSS_RESP_SUCCESS;
+ wcnss_send_cal_rsp(fw_status);
+ }
+ return;
+
+unlock_exit:
+ mutex_unlock(&penv->dev_lock);
+
+exit:
+ wcnss_send_cal_rsp(fw_status);
+ return;
+}
+
+
static void wcnssctrl_rx_handler(struct work_struct *worker)
{
int len = 0;
int rc = 0;
- unsigned char buf[WCNSS_MAX_FRAME_SIZE];
+ unsigned char buf[sizeof(struct wcnss_version)];
struct smd_msg_hdr *phdr;
struct wcnss_version *pversion;
int hw_type;
+ unsigned char fw_status = 0;
len = smd_read_avail(penv->smd_ch);
if (len > WCNSS_MAX_FRAME_SIZE) {
@@ -882,23 +1098,33 @@
if (len <= 0)
return;
- rc = smd_read(penv->smd_ch, buf, len);
- if (rc < len) {
- pr_err("wcnss: incomplete data read from smd\n");
+ rc = smd_read(penv->smd_ch, buf, sizeof(struct smd_msg_hdr));
+ if (rc < sizeof(struct smd_msg_hdr)) {
+ pr_err("wcnss: incomplete header read from smd\n");
return;
}
+ len -= sizeof(struct smd_msg_hdr);
phdr = (struct smd_msg_hdr *)buf;
switch (phdr->msg_type) {
case WCNSS_VERSION_RSP:
- pversion = (struct wcnss_version *)buf;
- if (len != sizeof(struct wcnss_version)) {
+ if (len != sizeof(struct wcnss_version)
+ - sizeof(struct smd_msg_hdr)) {
pr_err("wcnss: invalid version data from wcnss %d\n",
- len);
+ len);
return;
}
+ rc = smd_read(penv->smd_ch, buf+sizeof(struct smd_msg_hdr),
+ len);
+ if (rc < len) {
+ pr_err("wcnss: incomplete data read from smd\n");
+ return;
+ }
+ pversion = (struct wcnss_version *)buf;
+ penv->fw_major = pversion->major;
+ penv->fw_minor = pversion->minor;
snprintf(penv->wcnss_version, WCNSS_VERSION_LEN,
"%02x%02x%02x%02x", pversion->major, pversion->minor,
pversion->version, pversion->revision);
@@ -930,7 +1156,22 @@
break;
case WCNSS_NVBIN_DNLD_RSP:
- pr_info("wcnss: received WCNSS_NVBIN_DNLD_RSP from ccpu\n");
+ penv->nv_downloaded = true;
+ fw_status = wcnss_fw_status();
+ pr_debug("wcnss: received WCNSS_NVBIN_DNLD_RSP from ccpu %u\n",
+ fw_status);
+ break;
+
+ case WCNSS_CALDATA_DNLD_RSP:
+ penv->nv_downloaded = true;
+ fw_status = wcnss_fw_status();
+ pr_debug("wcnss: received WCNSS_CALDATA_DNLD_RSP from ccpu %u\n",
+ fw_status);
+ break;
+
+ case WCNSS_CALDATA_UPLD_REQ:
+ penv->fw_cal_available = 0;
+ extract_cal_data(len);
break;
default:
@@ -953,7 +1194,8 @@
return;
}
-static void wcnss_nvbin_dnld_req(struct work_struct *worker)
+
+static void wcnss_nvbin_dnld(void)
{
int ret = 0;
struct nvbin_dnld_req_msg *dnld_req_msg;
@@ -970,8 +1212,8 @@
ret = request_firmware(&nv, NVBIN_FILE, dev);
if (ret || !nv || !nv->data || !nv->size) {
- pr_err("wcnss: wcnss_nvbin_dnld_req: request_firmware failed for %s\n",
- NVBIN_FILE);
+ pr_err("wcnss: %s: request_firmware failed for %s\n",
+ __func__, NVBIN_FILE);
return;
}
@@ -992,13 +1234,14 @@
NV_FRAGMENT_SIZE), GFP_KERNEL);
if (NULL == outbuffer) {
- pr_err("wcnss: wcnss_nvbin_dnld_req: failed to get buffer\n");
+ pr_err("wcnss: %s: failed to get buffer\n", __func__);
goto err_free_nv;
}
dnld_req_msg = (struct nvbin_dnld_req_msg *)outbuffer;
dnld_req_msg->hdr.msg_type = WCNSS_NVBIN_DNLD_REQ;
+ dnld_req_msg->dnld_req_params.msg_flags = 0;
for (count = 0; count < total_fragments; count++) {
dnld_req_msg->dnld_req_params.frag_number = count;
@@ -1009,10 +1252,14 @@
if (!cur_frag_size)
cur_frag_size = NV_FRAGMENT_SIZE;
- dnld_req_msg->dnld_req_params.is_last_fragment = 1;
+ dnld_req_msg->dnld_req_params.msg_flags |=
+ LAST_FRAGMENT;
+ dnld_req_msg->dnld_req_params.msg_flags |=
+ CAN_RECEIVE_CALDATA;
} else {
cur_frag_size = NV_FRAGMENT_SIZE;
- dnld_req_msg->dnld_req_params.is_last_fragment = 0;
+ dnld_req_msg->dnld_req_params.msg_flags &=
+ ~LAST_FRAGMENT;
}
dnld_req_msg->dnld_req_params.nvbin_buffer_size =
@@ -1030,7 +1277,8 @@
retry_count = 0;
while ((ret == -ENOSPC) && (retry_count <= 3)) {
- pr_debug("wcnss: wcnss_nvbin_dnld_req: smd tx failed, ENOSPC\n");
+ pr_debug("wcnss: %s: smd tx failed, ENOSPC\n",
+ __func__);
pr_debug("fragment: %d, len: %d, TotFragments: %d, retry_count: %d\n",
count, dnld_req_msg->hdr.msg_len,
total_fragments, retry_count);
@@ -1043,7 +1291,7 @@
}
if (ret < 0) {
- pr_err("wcnss: wcnss_nvbin_dnld_req: smd tx failed\n");
+ pr_err("wcnss: %s: smd tx failed\n", __func__);
pr_err("fragment %d, len: %d, TotFragments: %d, retry_count: %d\n",
count, dnld_req_msg->hdr.msg_len,
total_fragments, retry_count);
@@ -1062,6 +1310,138 @@
return;
}
+
+static void wcnss_caldata_dnld(const void *cal_data,
+ unsigned int cal_data_size, bool msg_to_follow)
+{
+ int ret = 0;
+ struct cal_data_msg *cal_msg;
+ unsigned short total_fragments = 0;
+ unsigned short count = 0;
+ unsigned short retry_count = 0;
+ unsigned short cur_frag_size = 0;
+ unsigned char *outbuffer = NULL;
+
+ total_fragments = TOTALFRAGMENTS(cal_data_size);
+
+ outbuffer = kmalloc((sizeof(struct cal_data_msg) +
+ NV_FRAGMENT_SIZE), GFP_KERNEL);
+
+ if (NULL == outbuffer) {
+ pr_err("wcnss: %s: failed to get buffer\n", __func__);
+ return;
+ }
+
+ cal_msg = (struct cal_data_msg *)outbuffer;
+
+ cal_msg->hdr.msg_type = WCNSS_CALDATA_DNLD_REQ;
+ cal_msg->cal_params.msg_flags = 0;
+
+ for (count = 0; count < total_fragments; count++) {
+ cal_msg->cal_params.frag_number = count;
+
+ if (count == (total_fragments - 1)) {
+ cur_frag_size = cal_data_size % NV_FRAGMENT_SIZE;
+ if (!cur_frag_size)
+ cur_frag_size = NV_FRAGMENT_SIZE;
+
+ cal_msg->cal_params.msg_flags
+ |= LAST_FRAGMENT;
+ if (msg_to_follow)
+ cal_msg->cal_params.msg_flags |=
+ MESSAGE_TO_FOLLOW;
+ } else {
+ cur_frag_size = NV_FRAGMENT_SIZE;
+ cal_msg->cal_params.msg_flags &=
+ ~LAST_FRAGMENT;
+ }
+
+ cal_msg->cal_params.total_size = cal_data_size;
+ cal_msg->cal_params.frag_size =
+ cur_frag_size;
+
+ cal_msg->hdr.msg_len =
+ sizeof(struct cal_data_msg) + cur_frag_size;
+
+ memcpy((outbuffer + sizeof(struct cal_data_msg)),
+ (cal_data + count * NV_FRAGMENT_SIZE),
+ cur_frag_size);
+
+ ret = wcnss_smd_tx(outbuffer, cal_msg->hdr.msg_len);
+
+ retry_count = 0;
+ while ((ret == -ENOSPC) && (retry_count <= 3)) {
+ pr_debug("wcnss: %s: smd tx failed, ENOSPC\n",
+ __func__);
+ pr_debug("fragment: %d, len: %d, TotFragments: %d, retry_count: %d\n",
+ count, cal_msg->hdr.msg_len,
+ total_fragments, retry_count);
+
+ /* wait and try again */
+ msleep(20);
+ retry_count++;
+ ret = wcnss_smd_tx(outbuffer,
+ cal_msg->hdr.msg_len);
+ }
+
+ if (ret < 0) {
+ pr_err("wcnss: %s: smd tx failed\n", __func__);
+ pr_err("fragment %d, len: %d, TotFragments: %d, retry_count: %d\n",
+ count, cal_msg->hdr.msg_len,
+ total_fragments, retry_count);
+ goto err_dnld;
+ }
+ }
+
+
+err_dnld:
+ /* free buffer */
+ kfree(outbuffer);
+
+ return;
+}
+
+
+static void wcnss_nvbin_dnld_main(struct work_struct *worker)
+{
+ int retry = 0;
+
+ if (!FW_CALDATA_CAPABLE())
+ goto nv_download;
+
+ if (!penv->fw_cal_available && WCNSS_CONFIG_UNSPECIFIED
+ != has_calibrated_data && !penv->user_cal_available) {
+ while (!penv->user_cal_available && retry++ < 5)
+ msleep(500);
+ }
+
+ /* only cal data is sent during ssr (if available) */
+ if (penv->fw_cal_available && penv->ssr_boot) {
+ pr_info_ratelimited("wcnss: cal download during SSR, using fw cal");
+ wcnss_caldata_dnld(penv->fw_cal_data, penv->fw_cal_rcvd, false);
+ return;
+
+ } else if (penv->user_cal_available && penv->ssr_boot) {
+ pr_info_ratelimited("wcnss: cal download during SSR, using user cal");
+ wcnss_caldata_dnld(penv->user_cal_data,
+ penv->user_cal_rcvd, false);
+ return;
+
+ } else if (penv->user_cal_available) {
+ pr_info_ratelimited("wcnss: cal download during cold boot, using user cal");
+ wcnss_caldata_dnld(penv->user_cal_data,
+ penv->user_cal_rcvd, true);
+ }
+
+nv_download:
+ pr_info_ratelimited("wcnss: NV download");
+ wcnss_nvbin_dnld();
+
+ return;
+}
+
+
+
static int
wcnss_trigger_config(struct platform_device *pdev)
{
@@ -1146,7 +1526,7 @@
}
INIT_WORK(&penv->wcnssctrl_rx_work, wcnssctrl_rx_handler);
INIT_WORK(&penv->wcnssctrl_version_work, wcnss_send_version_req);
- INIT_WORK(&penv->wcnssctrl_nvbin_dnld_work, wcnss_nvbin_dnld_req);
+ INIT_WORK(&penv->wcnssctrl_nvbin_dnld_work, wcnss_nvbin_dnld_main);
wake_lock_init(&penv->wcnss_wake_lock, WAKE_LOCK_SUSPEND, "wcnss");
@@ -1201,7 +1581,6 @@
goto fail_ioremap3;
}
}
- penv->cold_boot_done = 1;
return 0;
@@ -1229,20 +1608,128 @@
return ret;
}
-#ifndef MODULE
static int wcnss_node_open(struct inode *inode, struct file *file)
{
struct platform_device *pdev;
- pr_info(DEVICE " triggered by userspace\n");
+ /* first open is only to trigger WCNSS platform driver */
+ if (!penv->triggered) {
+ pr_info(DEVICE " triggered by userspace\n");
+ pdev = penv->pdev;
+ return wcnss_trigger_config(pdev);
- pdev = penv->pdev;
- return wcnss_trigger_config(pdev);
+ } else if (penv->device_opened) {
+ pr_info(DEVICE " already opened\n");
+ return -EBUSY;
+ }
+
+ mutex_lock(&penv->dev_lock);
+ penv->user_cal_rcvd = 0;
+ penv->user_cal_read = 0;
+ penv->user_cal_available = false;
+ penv->user_cal_data = NULL;
+ penv->device_opened = 1;
+ mutex_unlock(&penv->dev_lock);
+
+ return 0;
}
+static ssize_t wcnss_wlan_read(struct file *fp, char __user
+ *buffer, size_t count, loff_t *position)
+{
+ int rc = 0;
+
+ if (!penv->device_opened)
+ return -EFAULT;
+
+ rc = wait_event_interruptible(penv->read_wait, penv->fw_cal_rcvd
+ > penv->user_cal_read || penv->fw_cal_available);
+
+ if (rc < 0)
+ return rc;
+
+ mutex_lock(&penv->dev_lock);
+
+ if (penv->fw_cal_available && penv->fw_cal_rcvd
+ == penv->user_cal_read) {
+ rc = 0;
+ goto exit;
+ }
+
+ if (count > penv->fw_cal_rcvd - penv->user_cal_read)
+ count = penv->fw_cal_rcvd - penv->user_cal_read;
+
+ rc = copy_to_user(buffer, penv->fw_cal_data +
+ penv->user_cal_read, count);
+ if (rc == 0) {
+ penv->user_cal_read += count;
+ rc = count;
+ }
+
+exit:
+ mutex_unlock(&penv->dev_lock);
+ return rc;
+}
+
+/* first (valid) write to this device should be 4 bytes cal file size */
+static ssize_t wcnss_wlan_write(struct file *fp, const char __user
+ *user_buffer, size_t count, loff_t *position)
+{
+ int rc = 0;
+ int size = 0;
+
+ if (!penv->device_opened || penv->user_cal_available)
+ return -EFAULT;
+
+ if (penv->user_cal_rcvd == 0 && count >= 4
+ && !penv->user_cal_data) {
+ rc = copy_from_user((void *)&size, user_buffer, 4);
+ if (size > MAX_CALIBRATED_DATA_SIZE) {
+ pr_err(DEVICE " invalid size to write %d\n", size);
+ return -EFAULT;
+ }
+
+ rc += count;
+ count -= 4;
+ penv->user_cal_exp_size = size;
+ penv->user_cal_data = kmalloc(size, GFP_KERNEL);
+ if (penv->user_cal_data == NULL) {
+ pr_err(DEVICE " no memory to write\n");
+ return -ENOMEM;
+ }
+ if (0 == count)
+ goto exit;
+
+ } else if (penv->user_cal_rcvd == 0 && count < 4)
+ return -EFAULT;
+
+ if (MAX_CALIBRATED_DATA_SIZE < count + penv->user_cal_rcvd) {
+ pr_err(DEVICE " invalid size to write %d\n", count +
+ penv->user_cal_rcvd);
+ rc = -ENOMEM;
+ goto exit;
+ }
+ rc = copy_from_user((void *)penv->user_cal_data +
+ penv->user_cal_rcvd, user_buffer, count);
+ if (0 == rc) {
+ penv->user_cal_rcvd += count;
+ rc += count;
+ }
+ if (penv->user_cal_rcvd == penv->user_cal_exp_size) {
+ penv->user_cal_available = true;
+ pr_info_ratelimited("wcnss: user cal written");
+ }
+
+exit:
+ return rc;
+}
+
+
static const struct file_operations wcnss_node_fops = {
.owner = THIS_MODULE,
.open = wcnss_node_open,
+ .read = wcnss_wlan_read,
+ .write = wcnss_wlan_write,
};
static struct miscdevice wcnss_misc = {
@@ -1250,8 +1737,6 @@
.name = DEVICE,
.fops = &wcnss_node_fops,
};
-#endif /* ifndef MODULE */
-
static int __devinit
wcnss_wlan_probe(struct platform_device *pdev)
@@ -1279,18 +1764,8 @@
return -ENOENT;
}
-
-#ifdef MODULE
-
- /* Since we were built as a module, we are running because
- * the module was loaded, therefore we assume userspace
- * applications are available to service PIL, so we can
- * trigger the WCNSS configuration now
- */
- pr_info(DEVICE " probed in MODULE mode\n");
- return wcnss_trigger_config(pdev);
-
-#else
+ mutex_init(&penv->dev_lock);
+ init_waitqueue_head(&penv->read_wait);
/* Since we were built into the kernel we'll be called as part
* of kernel initialization. We don't know if userspace
@@ -1303,7 +1778,6 @@
pr_info(DEVICE " probed in built-in mode\n");
return misc_register(&wcnss_misc);
-#endif
}
static int __devexit
diff --git a/drivers/platform/msm/ipa/a2_service.c b/drivers/platform/msm/ipa/a2_service.c
index 26353ec..fa71efc 100644
--- a/drivers/platform/msm/ipa/a2_service.c
+++ b/drivers/platform/msm/ipa/a2_service.c
@@ -92,11 +92,13 @@
bool bam_connect_in_progress;
int a2_mux_send_power_vote_on_init_once;
int a2_mux_sw_bridge_is_connected;
+ bool a2_mux_dl_wakeup;
u32 a2_device_handle;
struct mutex wakeup_lock;
struct completion ul_wakeup_ack_completion;
struct completion bam_connection_completion;
struct completion request_resource_completion;
+ struct completion dl_wakeup_completion;
rwlock_t ul_wakeup_lock;
int wait_for_ack;
struct wake_lock bam_wakelock;
@@ -431,6 +433,7 @@
static void kickoff_ul_wakeup_func(struct work_struct *work)
{
bool is_connected;
+ int ret;
ul_wakeup();
write_lock(&a2_mux_ctx->ul_wakeup_lock);
@@ -440,8 +443,19 @@
write_unlock(&a2_mux_ctx->ul_wakeup_lock);
if (is_connected)
ipa_rm_notify_completion(IPA_RM_RESOURCE_GRANTED,
- IPA_RM_RESOURCE_A2_CONS);
- else
+ IPA_RM_RESOURCE_A2_CONS);
+ INIT_COMPLETION(a2_mux_ctx->dl_wakeup_completion);
+ if (!a2_mux_ctx->a2_mux_dl_wakeup) {
+ ret = wait_for_completion_timeout(
+ &a2_mux_ctx->dl_wakeup_completion,
+ A2_MUX_COMPLETION_TIMEOUT);
+ if (unlikely(ret == 0)) {
+ IPAERR("%s timeout A2 PROD\n", __func__);
+ BUG();
+ return;
+ }
+ }
+ if (!is_connected)
msm_bam_dmux_kickoff_ul_power_down();
}
@@ -468,6 +482,8 @@
}
}
toggle_apps_ack();
+ a2_mux_ctx->a2_mux_dl_wakeup = true;
+ complete_all(&a2_mux_ctx->dl_wakeup_completion);
}
static void ipa_embedded_notify(void *priv,
@@ -635,6 +651,7 @@
(void) ipa_rm_release_resource(IPA_RM_RESOURCE_A2_PROD);
if (a2_mux_ctx->disconnect_ack)
toggle_apps_ack();
+ a2_mux_ctx->a2_mux_dl_wakeup = false;
a2_mux_ctx->a2_mux_sw_bridge_is_connected = 0;
complete_all(&a2_mux_ctx->bam_connection_completion);
return 0;
@@ -1467,6 +1484,7 @@
init_completion(&a2_mux_ctx->ul_wakeup_ack_completion);
init_completion(&a2_mux_ctx->bam_connection_completion);
init_completion(&a2_mux_ctx->request_resource_completion);
+ init_completion(&a2_mux_ctx->dl_wakeup_completion);
wake_lock_init(&a2_mux_ctx->bam_wakelock,
WAKE_LOCK_SUSPEND, "a2_mux_wakelock");
a2_mux_ctx->a2_mux_initialized = 1;
diff --git a/drivers/platform/msm/ipa/ipa_dp.c b/drivers/platform/msm/ipa/ipa_dp.c
index 67728c2..1b6181f 100644
--- a/drivers/platform/msm/ipa/ipa_dp.c
+++ b/drivers/platform/msm/ipa/ipa_dp.c
@@ -187,6 +187,7 @@
int inactive_cycles = 0;
int cnt;
+ ipa_inc_client_enable_clks();
do {
cnt = ipa_handle_tx_core(sys, true, true);
if (cnt == 0) {
@@ -199,6 +200,7 @@
} while (inactive_cycles <= POLLING_INACTIVITY_TX);
ipa_tx_switch_to_intr_mode(sys);
+ ipa_dec_client_disable_clks();
}
static void ipa_wq_handle_tx(struct work_struct *work)
@@ -230,7 +232,7 @@
struct ipa_tx_pkt_wrapper *tx_pkt;
unsigned long irq_flags;
int result;
- u16 sps_flags = SPS_IOVEC_FLAG_EOT | SPS_IOVEC_FLAG_INT;
+ u16 sps_flags = SPS_IOVEC_FLAG_EOT;
dma_addr_t dma_address;
u16 len;
u32 mem_flag = GFP_ATOMIC;
@@ -467,8 +469,7 @@
}
if (i == (num_desc - 1)) {
- iovec->flags |= (SPS_IOVEC_FLAG_EOT |
- SPS_IOVEC_FLAG_INT);
+ iovec->flags |= SPS_IOVEC_FLAG_EOT;
/* "mark" the last desc */
tx_pkt->cnt = IPA_LAST_DESC_CNT;
}
@@ -914,6 +915,7 @@
int inactive_cycles = 0;
int cnt;
+ ipa_inc_client_enable_clks();
do {
cnt = ipa_handle_rx_core(sys, true, true);
if (cnt == 0) {
@@ -926,6 +928,7 @@
} while (inactive_cycles <= POLLING_INACTIVITY_RX);
ipa_rx_switch_to_intr_mode(sys);
+ ipa_dec_client_disable_clks();
}
static void switch_to_intr_rx_work_func(struct work_struct *work)
@@ -1014,11 +1017,8 @@
ipa_ctx->ep[ipa_ep_idx].connect.dest_pipe_index =
ipa_ctx->a5_pipe_index++;
ipa_ctx->ep[ipa_ep_idx].connect.src_pipe_index = ipa_ep_idx;
- ipa_ctx->ep[ipa_ep_idx].connect.options =
- SPS_O_AUTO_ENABLE | SPS_O_EOT | SPS_O_ACK_TRANSFERS |
+ ipa_ctx->ep[ipa_ep_idx].connect.options = SPS_O_ACK_TRANSFERS |
SPS_O_NO_DISABLE;
- if (ipa_ctx->polling_mode)
- ipa_ctx->ep[ipa_ep_idx].connect.options |= SPS_O_POLL;
} else {
ipa_ctx->ep[ipa_ep_idx].connect.mode = SPS_MODE_DEST;
ipa_ctx->ep[ipa_ep_idx].connect.source = SPS_DEV_HANDLE_MEM;
@@ -1027,13 +1027,16 @@
ipa_ctx->ep[ipa_ep_idx].connect.src_pipe_index =
ipa_ctx->a5_pipe_index++;
ipa_ctx->ep[ipa_ep_idx].connect.dest_pipe_index = ipa_ep_idx;
- ipa_ctx->ep[ipa_ep_idx].connect.options =
- SPS_O_AUTO_ENABLE | SPS_O_EOT;
- if (ipa_ctx->polling_mode)
+ if (sys_in->client == IPA_CLIENT_A5_LAN_WAN_PROD)
ipa_ctx->ep[ipa_ep_idx].connect.options |=
- SPS_O_ACK_TRANSFERS | SPS_O_POLL;
+ SPS_O_ACK_TRANSFERS;
}
+ ipa_ctx->ep[ipa_ep_idx].connect.options |= (SPS_O_AUTO_ENABLE |
+ SPS_O_EOT);
+ if (ipa_ctx->polling_mode)
+ ipa_ctx->ep[ipa_ep_idx].connect.options |= SPS_O_POLL;
+
ipa_ctx->ep[ipa_ep_idx].connect.desc.size = sys_in->desc_fifo_sz;
ipa_ctx->ep[ipa_ep_idx].connect.desc.base =
dma_alloc_coherent(NULL, ipa_ctx->ep[ipa_ep_idx].connect.desc.size,
@@ -1342,7 +1345,7 @@
ret = sps_transfer_one(sys->ep->ep_hdl, rx_pkt->dma_address,
IPA_RX_SKB_SIZE, rx_pkt,
- SPS_IOVEC_FLAG_INT);
+ 0);
if (ret) {
IPAERR("sps_transfer_one failed %d\n", ret);
diff --git a/drivers/platform/msm/ipa/ipa_rm_resource.c b/drivers/platform/msm/ipa/ipa_rm_resource.c
index 0fc4826..8655d89 100644
--- a/drivers/platform/msm/ipa/ipa_rm_resource.c
+++ b/drivers/platform/msm/ipa/ipa_rm_resource.c
@@ -336,27 +336,28 @@
int ipa_rm_resource_delete(struct ipa_rm_resource *resource)
{
struct ipa_rm_resource *consumer, *producer;
- int i, result = 0;
+ int peers_index, result = 0, list_size;
- IPADBG("IPA RM: %s ENTER\n", __func__);
-
+ IPADBG("ipa_rm_resource_delete ENTER with resource %d\n",
+ resource->name);
if (!resource) {
IPADBG("ipa_rm_resource_delete ENTER with invalid param\n");
return -EINVAL;
}
if (resource->type == IPA_RM_PRODUCER) {
if (resource->peers_list) {
- for (i = IPA_RM_RESOURCE_PROD_MAX;
- i < IPA_RM_RESOURCE_MAX;
- ++i) {
+ list_size = ipa_rm_peers_list_get_size(
+ resource->peers_list);
+ for (peers_index = 0;
+ peers_index < list_size;
+ peers_index++) {
consumer = ipa_rm_peers_list_get_resource(
- i,
+ peers_index,
resource->peers_list);
- if (consumer) {
+ if (consumer)
ipa_rm_resource_delete_dependency(
resource,
consumer);
- }
}
ipa_rm_peers_list_delete(resource->peers_list);
}
@@ -365,9 +366,13 @@
kfree((struct ipa_rm_resource_prod *) resource);
} else if (resource->type == IPA_RM_CONSUMER) {
if (resource->peers_list) {
- for (i = 0; i < IPA_RM_RESOURCE_PROD_MAX; ++i) {
+ list_size = ipa_rm_peers_list_get_size(
+ resource->peers_list);
+ for (peers_index = 0;
+ peers_index < list_size;
+ peers_index++){
producer = ipa_rm_peers_list_get_resource(
- i,
+ peers_index,
resource->peers_list);
if (producer)
ipa_rm_resource_delete_dependency(
diff --git a/drivers/platform/msm/ipa/teth_bridge.c b/drivers/platform/msm/ipa/teth_bridge.c
index 9062ab8..add9522 100644
--- a/drivers/platform/msm/ipa/teth_bridge.c
+++ b/drivers/platform/msm/ipa/teth_bridge.c
@@ -128,8 +128,10 @@
* @teth_wq: dedicated workqueue, used for setting up the HW bridge and for
* sending packets using the SW bridge when the system is waking up from power
* collapse
- * @a2_ipa_hdr_len: A2 to IPA header length, used to configure the A2 endpoint
- * for header removal
+ * @a2_ipa_hdr_len: A2 to IPA header length, used for configuring the A2
+ * endpoint for header removal
+ * @ipa_a2_hdr_len: IPA to A2 header length, used for configuring the A2
+ * endpoint for header removal
* @hdr_del: array to store the headers handles in order to delete them later
* @routing_del: array of routing rules handles, one array for IPv4 and one for
* IPv6
@@ -160,6 +162,7 @@
struct stats stats;
struct workqueue_struct *teth_wq;
u16 a2_ipa_hdr_len;
+ u16 ipa_a2_hdr_len;
struct ipa_ioc_del_hdr *hdr_del;
struct ipa_ioc_del_rt_rule *routing_del[TETH_IP_FAMILIES];
struct ipa_ioc_del_flt_rule *filtering_del[TETH_IP_FAMILIES];
@@ -287,6 +290,7 @@
}
hdr_cfg.hdr_len = ipa_a2_hdr_len;
+ teth_ctx->ipa_a2_hdr_len = ipa_a2_hdr_len;
res = ipa_cfg_ep_hdr(teth_ctx->ipa_a2_pipe_hdl, &hdr_cfg);
if (res) {
TETH_ERR("Header insertion config for IPA->A2 pipe failed\n");
@@ -1225,6 +1229,28 @@
enum ipa_rm_event event,
unsigned long data)
{
+ switch (event) {
+ case IPA_RM_RESOURCE_GRANTED:
+ complete(&teth_ctx->is_bridge_prod_up);
+ break;
+
+ case IPA_RM_RESOURCE_RELEASED:
+ complete(&teth_ctx->is_bridge_prod_down);
+ break;
+
+ default:
+ TETH_ERR("Unsupported notification!\n");
+ WARN_ON(1);
+ break;
+ }
+
+ return;
+}
+
+static void a2_prod_notify_cb(void *notify_cb_data,
+ enum ipa_rm_event event,
+ unsigned long data)
+{
int res;
struct ipa_ep_cfg ipa_ep_cfg;
@@ -1243,15 +1269,15 @@
/* Reset the various endpoints configuration */
memset(&ipa_ep_cfg, 0, sizeof(ipa_ep_cfg));
+ ipa_ep_cfg.hdr.hdr_len = teth_ctx->ipa_a2_hdr_len;
ipa_cfg_ep(teth_ctx->ipa_a2_pipe_hdl, &ipa_ep_cfg);
+ memset(&ipa_ep_cfg, 0, sizeof(ipa_ep_cfg));
ipa_ep_cfg.hdr.hdr_len = teth_ctx->a2_ipa_hdr_len;
ipa_cfg_ep(teth_ctx->a2_ipa_pipe_hdl, &ipa_ep_cfg);
- complete(&teth_ctx->is_bridge_prod_up);
break;
case IPA_RM_RESOURCE_RELEASED:
- complete(&teth_ctx->is_bridge_prod_down);
break;
default:
@@ -1283,6 +1309,7 @@
int teth_bridge_init(ipa_notify_cb *usb_notify_cb_ptr, void **private_data_ptr)
{
int res = 0;
+ struct ipa_rm_register_params a2_prod_reg_params;
TETH_DBG_FUNC_ENTRY();
if (usb_notify_cb_ptr == NULL) {
@@ -1323,10 +1350,22 @@
goto fail_add_dependency_3;
}
+ /* Register for A2_PROD resource notifications */
+ a2_prod_reg_params.user_data = NULL;
+ a2_prod_reg_params.notify_cb = a2_prod_notify_cb;
+ res = ipa_rm_register(IPA_RM_RESOURCE_A2_PROD, &a2_prod_reg_params);
+ if (res) {
+ TETH_ERR("ipa_rm_register() failed\n");
+ goto fail_add_dependency_4;
+ }
+
/* Return 0 as EINPROGRESS is a valid return value at this point */
res = 0;
goto bail;
+fail_add_dependency_4:
+ ipa_rm_delete_dependency(IPA_RM_RESOURCE_A2_PROD,
+ IPA_RM_RESOURCE_USB_CONS);
fail_add_dependency_3:
ipa_rm_delete_dependency(IPA_RM_RESOURCE_USB_PROD,
IPA_RM_RESOURCE_A2_CONS);
@@ -1368,6 +1407,7 @@
teth_ctx->comp_hw_bridge_in_progress = false;
memset(&teth_ctx->stats, 0, sizeof(teth_ctx->stats));
teth_ctx->a2_ipa_hdr_len = 0;
+ teth_ctx->ipa_a2_hdr_len = 0;
memset(teth_ctx->hdr_del,
0,
sizeof(struct ipa_ioc_del_hdr) + TETH_TOTAL_HDR_ENTRIES *
@@ -1402,6 +1442,7 @@
int teth_bridge_disconnect(void)
{
int res;
+ struct ipa_rm_register_params a2_prod_reg_params;
TETH_DBG_FUNC_ENTRY();
if (!teth_ctx->is_connected) {
@@ -1478,6 +1519,13 @@
TETH_ERR(
"Failed deleting ipa_rm dependency BRIDGE_PROD <-> A2_CONS\n");
+ /* Deregister from A2_PROD notifications */
+ a2_prod_reg_params.user_data = NULL;
+ a2_prod_reg_params.notify_cb = a2_prod_notify_cb;
+ res = ipa_rm_deregister(IPA_RM_RESOURCE_A2_PROD, &a2_prod_reg_params);
+ if (res)
+ TETH_ERR("Failed deregistering from A2_prod notifications.\n");
+
teth_ctx->is_connected = false;
bail:
TETH_DBG_FUNC_EXIT();
diff --git a/drivers/platform/msm/qpnp-power-on.c b/drivers/platform/msm/qpnp-power-on.c
index 1907adc..a85e31c 100644
--- a/drivers/platform/msm/qpnp-power-on.c
+++ b/drivers/platform/msm/qpnp-power-on.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2013, 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
@@ -24,47 +24,51 @@
#include <linux/log2.h>
/* PON common register addresses */
-#define QPNP_PON_RT_STS(base) (base + 0x10)
-#define QPNP_PON_PULL_CTL(base) (base + 0x70)
-#define QPNP_PON_DBC_CTL(base) (base + 0x71)
+#define QPNP_PON_RT_STS(base) (base + 0x10)
+#define QPNP_PON_PULL_CTL(base) (base + 0x70)
+#define QPNP_PON_DBC_CTL(base) (base + 0x71)
/* PON/RESET sources register addresses */
-#define QPNP_PON_KPDPWR_S1_TIMER(base) (base + 0x40)
-#define QPNP_PON_KPDPWR_S2_TIMER(base) (base + 0x41)
-#define QPNP_PON_KPDPWR_S2_CNTL(base) (base + 0x42)
-#define QPNP_PON_RESIN_S1_TIMER(base) (base + 0x44)
-#define QPNP_PON_RESIN_S2_TIMER(base) (base + 0x45)
-#define QPNP_PON_RESIN_S2_CNTL(base) (base + 0x46)
-#define QPNP_PON_PS_HOLD_RST_CTL(base) (base + 0x5A)
+#define QPNP_PON_WARM_RESET_REASON1(base) (base + 0xA)
+#define QPNP_PON_WARM_RESET_REASON2(base) (base + 0xB)
+#define QPNP_PON_KPDPWR_S1_TIMER(base) (base + 0x40)
+#define QPNP_PON_KPDPWR_S2_TIMER(base) (base + 0x41)
+#define QPNP_PON_KPDPWR_S2_CNTL(base) (base + 0x42)
+#define QPNP_PON_RESIN_S1_TIMER(base) (base + 0x44)
+#define QPNP_PON_RESIN_S2_TIMER(base) (base + 0x45)
+#define QPNP_PON_RESIN_S2_CNTL(base) (base + 0x46)
+#define QPNP_PON_PS_HOLD_RST_CTL(base) (base + 0x5A)
-#define QPNP_PON_RESIN_PULL_UP BIT(0)
-#define QPNP_PON_KPDPWR_PULL_UP BIT(1)
-#define QPNP_PON_CBLPWR_PULL_UP BIT(2)
-#define QPNP_PON_S2_CNTL_EN BIT(7)
-#define QPNP_PON_S2_RESET_ENABLE BIT(7)
-#define QPNP_PON_DELAY_BIT_SHIFT 6
+#define QPNP_PON_WARM_RESET_TFT BIT(4)
-#define QPNP_PON_S1_TIMER_MASK (0xF)
-#define QPNP_PON_S2_TIMER_MASK (0x7)
-#define QPNP_PON_S2_CNTL_TYPE_MASK (0xF)
+#define QPNP_PON_RESIN_PULL_UP BIT(0)
+#define QPNP_PON_KPDPWR_PULL_UP BIT(1)
+#define QPNP_PON_CBLPWR_PULL_UP BIT(2)
+#define QPNP_PON_S2_CNTL_EN BIT(7)
+#define QPNP_PON_S2_RESET_ENABLE BIT(7)
+#define QPNP_PON_DELAY_BIT_SHIFT 6
-#define QPNP_PON_DBC_DELAY_MASK (0x7)
-#define QPNP_PON_KPDPWR_N_SET BIT(0)
-#define QPNP_PON_RESIN_N_SET BIT(1)
-#define QPNP_PON_CBLPWR_N_SET BIT(2)
-#define QPNP_PON_RESIN_BARK_N_SET BIT(4)
+#define QPNP_PON_S1_TIMER_MASK (0xF)
+#define QPNP_PON_S2_TIMER_MASK (0x7)
+#define QPNP_PON_S2_CNTL_TYPE_MASK (0xF)
-#define QPNP_PON_RESET_EN BIT(7)
-#define QPNP_PON_WARM_RESET BIT(0)
-#define QPNP_PON_SHUTDOWN BIT(2)
+#define QPNP_PON_DBC_DELAY_MASK (0x7)
+#define QPNP_PON_KPDPWR_N_SET BIT(0)
+#define QPNP_PON_RESIN_N_SET BIT(1)
+#define QPNP_PON_CBLPWR_N_SET BIT(2)
+#define QPNP_PON_RESIN_BARK_N_SET BIT(4)
+
+#define QPNP_PON_RESET_EN BIT(7)
+#define QPNP_PON_WARM_RESET BIT(0)
+#define QPNP_PON_SHUTDOWN BIT(2)
/* Ranges */
-#define QPNP_PON_S1_TIMER_MAX 10256
-#define QPNP_PON_S2_TIMER_MAX 2000
-#define QPNP_PON_RESET_TYPE_MAX 0xF
-#define PON_S1_COUNT_MAX 0xF
+#define QPNP_PON_S1_TIMER_MAX 10256
+#define QPNP_PON_S2_TIMER_MAX 2000
+#define QPNP_PON_RESET_TYPE_MAX 0xF
+#define PON_S1_COUNT_MAX 0xF
-#define QPNP_KEY_STATUS_DELAY msecs_to_jiffies(250)
+#define QPNP_KEY_STATUS_DELAY msecs_to_jiffies(250)
enum pon_type {
PON_KPDPWR,
@@ -175,6 +179,50 @@
}
EXPORT_SYMBOL(qpnp_pon_system_pwr_off);
+/**
+ * qpnp_pon_is_warm_reset - Checks if the PMIC went through a warm reset.
+ *
+ * Returns > 0 for warm resets, 0 for not warm reset, < 0 for errors
+ *
+ * Note that this function will only return the warm vs not-warm reset status
+ * of the PMIC that is configured as the system-reset device.
+ */
+int qpnp_pon_is_warm_reset(void)
+{
+ struct qpnp_pon *pon = sys_reset_dev;
+ int rc;
+ u8 reg;
+
+ if (!pon)
+ return -EPROBE_DEFER;
+
+ rc = spmi_ext_register_readl(pon->spmi->ctrl, pon->spmi->sid,
+ QPNP_PON_WARM_RESET_REASON1(pon->base), ®, 1);
+ if (rc) {
+ dev_err(&pon->spmi->dev,
+ "Unable to read addr=%x, rc(%d)\n",
+ QPNP_PON_WARM_RESET_REASON1(pon->base), rc);
+ return rc;
+ }
+
+ if (reg)
+ return 1;
+
+ rc = spmi_ext_register_readl(pon->spmi->ctrl, pon->spmi->sid,
+ QPNP_PON_WARM_RESET_REASON2(pon->base), ®, 1);
+ if (rc) {
+ dev_err(&pon->spmi->dev,
+ "Unable to read addr=%x, rc(%d)\n",
+ QPNP_PON_WARM_RESET_REASON2(pon->base), rc);
+ return rc;
+ }
+ if (reg & QPNP_PON_WARM_RESET_TFT)
+ return 1;
+
+ return 0;
+}
+EXPORT_SYMBOL(qpnp_pon_is_warm_reset);
+
static struct qpnp_pon_config *
qpnp_get_cfg(struct qpnp_pon *pon, u32 pon_type)
{
diff --git a/drivers/platform/msm/usb_bam.c b/drivers/platform/msm/usb_bam.c
index 2d1b763..64e8d7a 100644
--- a/drivers/platform/msm/usb_bam.c
+++ b/drivers/platform/msm/usb_bam.c
@@ -1744,10 +1744,12 @@
spin_lock(&usb_bam_lock);
- ctx.inactivity_timer_ms[bam] = timer_d;
/* Apply new timer setting if bam has running pipes */
- if (ctx.pipes_enabled_per_bam[bam] > 0)
- usb_bam_set_inactivity_timer(bam);
+ if (ctx.inactivity_timer_ms[bam] != timer_d) {
+ ctx.inactivity_timer_ms[bam] = timer_d;
+ if (ctx.pipes_enabled_per_bam[bam] > 0)
+ usb_bam_set_inactivity_timer(bam);
+ }
spin_unlock(&usb_bam_lock);
}
diff --git a/drivers/power/pm8921-bms.c b/drivers/power/pm8921-bms.c
index c09373a..9b3973b 100644
--- a/drivers/power/pm8921-bms.c
+++ b/drivers/power/pm8921-bms.c
@@ -177,6 +177,9 @@
int vbatt_cutoff_count;
int low_voltage_detect;
int vbatt_cutoff_retries;
+ bool first_report_after_suspend;
+ bool soc_updated_on_resume;
+ int last_soc_at_suspend;
};
/*
@@ -2387,10 +2390,11 @@
rbatt, fcc_uah, unusable_charge_uah, cc_uah);
pr_debug("calculated SOC = %d\n", new_calculated_soc);
- if (new_calculated_soc != calculated_soc)
+ if (new_calculated_soc != calculated_soc) {
+ calculated_soc = new_calculated_soc;
update_power_supply(chip);
+ }
- calculated_soc = new_calculated_soc;
firsttime = 0;
get_current_time(&chip->last_recalc_time);
@@ -2495,15 +2499,31 @@
/* last_soc < soc ... scale and catch up */
if (last_soc != -EINVAL && last_soc < soc && soc != 100)
- soc = scale_soc_while_chg(chip, delta_time_us,
- soc, last_soc);
+ soc = scale_soc_while_chg(chip, delta_time_us, soc, last_soc);
- /* restrict soc to 1% change */
if (last_soc != -EINVAL) {
- if (soc < last_soc && soc != 0)
+ if (chip->first_report_after_suspend) {
+ chip->first_report_after_suspend = false;
+ if (chip->soc_updated_on_resume) {
+ /* coming here after a long suspend */
+ chip->soc_updated_on_resume = false;
+ if (last_soc < soc)
+ /* if soc has falsely increased during
+ * suspend, set the soc_at_suspend
+ */
+ soc = chip->last_soc_at_suspend;
+ } else {
+ /*
+ * suspended for a short time
+ * report the last_soc before suspend
+ */
+ soc = chip->last_soc_at_suspend;
+ }
+ } else if (soc < last_soc && soc != 0) {
soc = last_soc - 1;
- if (soc > last_soc && soc != 100)
+ } else if (soc > last_soc && soc != 100) {
soc = last_soc + 1;
+ }
}
last_soc = bound_soc(soc);
@@ -3560,12 +3580,11 @@
static int pm8921_bms_suspend(struct device *dev)
{
- /*
- * set the last reported soc to invalid, so that
- * next time we resume we don't want to restrict
- * the decrease of soc by only 1%
- */
- last_soc = -EINVAL;
+ struct pm8921_bms_chip *chip = dev_get_drvdata(dev);
+
+ cancel_delayed_work_sync(&chip->calculate_soc_delayed_work);
+
+ chip->last_soc_at_suspend = last_soc;
return 0;
}
@@ -3575,22 +3594,30 @@
int rc;
unsigned long time_since_last_recalc;
unsigned long tm_now_sec;
+ struct pm8921_bms_chip *chip = dev_get_drvdata(dev);
rc = get_current_time(&tm_now_sec);
if (rc) {
pr_err("Could not read current time: %d\n", rc);
return 0;
}
- if (tm_now_sec > the_chip->last_recalc_time) {
+
+ if (tm_now_sec > chip->last_recalc_time) {
time_since_last_recalc = tm_now_sec -
- the_chip->last_recalc_time;
+ chip->last_recalc_time;
pr_debug("Time since last recalc: %lu\n",
time_since_last_recalc);
- if (time_since_last_recalc >= the_chip->soc_calc_period) {
- the_chip->last_recalc_time = tm_now_sec;
- recalculate_soc(the_chip);
+ if ((time_since_last_recalc * 1000) >=
+ chip->soc_calc_period) {
+ chip->last_recalc_time = tm_now_sec;
+ recalculate_soc(chip);
+ chip->soc_updated_on_resume = true;
}
}
+ chip->first_report_after_suspend = true;
+ update_power_supply(chip);
+ schedule_delayed_work(&chip->calculate_soc_delayed_work,
+ msecs_to_jiffies(chip->soc_calc_period));
return 0;
}
diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c
index 63fb0a5..5cdb666 100644
--- a/drivers/power/power_supply_sysfs.c
+++ b/drivers/power/power_supply_sysfs.c
@@ -176,6 +176,7 @@
POWER_SUPPLY_ATTR(type),
POWER_SUPPLY_ATTR(scope),
POWER_SUPPLY_ATTR(system_temp_level),
+ POWER_SUPPLY_ATTR(resistance),
/* Properties of type `const char *' */
POWER_SUPPLY_ATTR(model_name),
POWER_SUPPLY_ATTR(manufacturer),
diff --git a/drivers/power/qpnp-bms.c b/drivers/power/qpnp-bms.c
index 866b921..eac8953 100644
--- a/drivers/power/qpnp-bms.c
+++ b/drivers/power/qpnp-bms.c
@@ -24,6 +24,7 @@
#include <linux/rtc.h>
#include <linux/delay.h>
#include <linux/qpnp/qpnp-adc.h>
+#include <linux/qpnp/power-on.h>
#include <linux/mfd/pm8xxx/batterydata-lib.h>
/* BMS Register Offsets */
@@ -116,7 +117,9 @@
u8 revision1;
u8 revision2;
int battery_present;
+ int battery_status;
bool new_battery;
+ bool done_charging;
bool last_soc_invalid;
/* platform data */
int r_sense_uohm;
@@ -127,7 +130,7 @@
int adjust_soc_low_threshold;
int chg_term_ua;
enum battery_type batt_type;
- unsigned int fcc;
+ unsigned int fcc_mah;
struct single_row_lut *fcc_temp_lut;
struct single_row_lut *fcc_sf_lut;
struct pc_temp_ocv_lut *pc_temp_ocv_lut;
@@ -135,13 +138,16 @@
struct sf_lut *rbatt_sf_lut;
int default_rbatt_mohm;
int rbatt_capacitive_mohm;
+ int rbatt_mohm;
struct delayed_work calculate_soc_delayed_work;
struct work_struct recalc_work;
struct mutex bms_output_lock;
struct mutex last_ocv_uv_mutex;
+ struct mutex vbat_monitor_mutex;
struct mutex soc_invalidation_mutex;
+ struct mutex last_soc_mutex;
bool use_external_rsense;
bool use_ocv_thresholds;
@@ -152,22 +158,24 @@
int shutdown_iavg_ma;
struct wake_lock low_voltage_wake_lock;
- bool low_voltage_wake_lock_held;
int low_voltage_threshold;
int low_soc_calc_threshold;
int low_soc_calculate_soc_ms;
int calculate_soc_ms;
struct wake_lock soc_wake_lock;
+ struct wake_lock cv_wake_lock;
uint16_t ocv_reading_at_100;
uint16_t prev_last_good_ocv_raw;
int last_ocv_uv;
int last_ocv_temp;
int last_cc_uah;
+ unsigned long last_soc_change_sec;
unsigned long tm_sec;
+ unsigned long report_tm_sec;
bool first_time_calc_soc;
bool first_time_calc_uuc;
- int pon_ocv_uv;
+ int64_t software_cc_uah;
int iavg_samples_ma[IAVG_SAMPLES];
int iavg_index;
@@ -176,15 +184,17 @@
int last_soc;
int last_soc_est;
int last_soc_unbound;
-
- int charge_time_us;
- int catch_up_time_us;
+ bool was_charging_at_sleep;
+ int charge_start_tm_sec;
+ int catch_up_time_sec;
struct single_row_lut *adjusted_fcc_temp_lut;
+ struct qpnp_adc_tm_btm_param vbat_monitor_params;
+ struct qpnp_adc_tm_btm_param die_temp_monitor_params;
+ int temperature_margin;
unsigned int vadc_v0625;
unsigned int vadc_v1250;
- int ibat_max_ua;
int prev_uuc_iavg_ma;
int prev_pc_unusable;
int ibat_at_cv_ua;
@@ -193,6 +203,7 @@
int calculated_soc;
int prev_voltage_based_soc;
bool use_voltage_soc;
+ bool in_cv_range;
int prev_batt_terminal_uv;
int high_ocv_correction_limit_uv;
@@ -215,10 +226,9 @@
};
static enum power_supply_property msm_bms_power_props[] = {
- POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_CAPACITY,
POWER_SUPPLY_PROP_CURRENT_NOW,
- POWER_SUPPLY_PROP_CURRENT_MAX,
+ POWER_SUPPLY_PROP_RESISTANCE,
POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
};
@@ -364,12 +374,17 @@
static inline int convert_vbatt_raw_to_uv(struct qpnp_bms_chip *chip,
uint16_t reading)
{
- int uv;
+ int64_t uv;
+ int rc;
uv = vadc_reading_to_uv(reading);
- pr_debug("%u raw converted into %d uv\n", reading, uv);
+ pr_debug("%u raw converted into %lld uv\n", reading, uv);
uv = adjust_vbatt_reading(chip, uv);
- pr_debug("adjusted into %d uv\n", uv);
+ pr_debug("adjusted into %lld uv\n", uv);
+ rc = qpnp_vbat_sns_comp_result(&uv);
+ if (rc)
+ pr_debug("could not compensate vbatt\n");
+ pr_debug("compensated into %lld uv\n", uv);
return uv;
}
@@ -429,7 +444,8 @@
static int get_battery_current(struct qpnp_bms_chip *chip, int *result_ua)
{
- int vsense_uv = 0;
+ int rc, vsense_uv = 0;
+ int64_t temp_current;
if (chip->r_sense_uohm == 0) {
pr_err("r_sense is zero\n");
@@ -444,8 +460,15 @@
pr_debug("vsense_uv=%duV\n", vsense_uv);
/* cast for signed division */
- *result_ua = div_s64((vsense_uv * 1000000LL), (int)chip->r_sense_uohm);
- pr_debug("ibat=%duA\n", *result_ua);
+ temp_current = div_s64((vsense_uv * 1000000LL),
+ (int)chip->r_sense_uohm);
+
+ rc = qpnp_iadc_comp_result(&temp_current);
+ if (rc)
+ pr_debug("error compensation failed: %d\n", rc);
+
+ *result_ua = temp_current;
+ pr_debug("err compensated ibat=%duA\n", *result_ua);
return 0;
}
@@ -533,6 +556,7 @@
raw->last_good_ocv_raw);
chip->last_ocv_uv = raw->last_good_ocv_uv;
chip->last_ocv_temp = batt_temp;
+ chip->software_cc_uah = 0;
pr_debug("last_good_ocv_uv = %d\n", raw->last_good_ocv_uv);
}
@@ -564,7 +588,7 @@
pr_err("cc reenable failed: %d\n", rc);
}
-static bool is_battery_charging(struct qpnp_bms_chip *chip)
+static int get_battery_status(struct qpnp_bms_chip *chip)
{
union power_supply_propval ret = {0,};
@@ -574,7 +598,30 @@
/* if battery has been registered, use the status property */
chip->batt_psy->get_property(chip->batt_psy,
POWER_SUPPLY_PROP_STATUS, &ret);
- return ret.intval == POWER_SUPPLY_STATUS_CHARGING;
+ return ret.intval;
+ }
+
+ /* Default to false if the battery power supply is not registered. */
+ pr_debug("battery power supply is not registered\n");
+ return POWER_SUPPLY_STATUS_UNKNOWN;
+}
+
+static bool is_battery_charging(struct qpnp_bms_chip *chip)
+{
+ return get_battery_status(chip) == POWER_SUPPLY_STATUS_CHARGING;
+}
+
+static bool is_battery_present(struct qpnp_bms_chip *chip)
+{
+ union power_supply_propval ret = {0,};
+
+ if (chip->batt_psy == NULL)
+ chip->batt_psy = power_supply_get_by_name("battery");
+ if (chip->batt_psy) {
+ /* if battery has been registered, use the status property */
+ chip->batt_psy->get_property(chip->batt_psy,
+ POWER_SUPPLY_PROP_PRESENT, &ret);
+ return ret.intval;
}
/* Default to false if the battery power supply is not registered. */
@@ -584,20 +631,7 @@
static bool is_battery_full(struct qpnp_bms_chip *chip)
{
- union power_supply_propval ret = {0,};
-
- if (chip->batt_psy == NULL)
- chip->batt_psy = power_supply_get_by_name("battery");
- if (chip->batt_psy) {
- /* if battery has been registered, use the status property */
- chip->batt_psy->get_property(chip->batt_psy,
- POWER_SUPPLY_PROP_STATUS, &ret);
- return ret.intval == POWER_SUPPLY_STATUS_FULL;
- }
-
- /* Default to true if the battery power supply is not registered. */
- pr_debug("battery power supply is not registered\n");
- return true;
+ return get_battery_status(chip) == POWER_SUPPLY_STATUS_FULL;
}
static int get_simultaneous_batt_v_and_i(struct qpnp_bms_chip *chip,
@@ -661,24 +695,29 @@
static void reset_for_new_battery(struct qpnp_bms_chip *chip, int batt_temp)
{
chip->last_ocv_uv = estimate_ocv(chip);
+ mutex_lock(&chip->last_soc_mutex);
chip->last_soc = -EINVAL;
+ chip->last_soc_invalid = true;
+ mutex_unlock(&chip->last_soc_mutex);
chip->soc_at_cv = -EINVAL;
chip->shutdown_soc_invalid = true;
chip->shutdown_soc = 0;
chip->shutdown_iavg_ma = 0;
chip->prev_pc_unusable = -EINVAL;
reset_cc(chip);
+ chip->software_cc_uah = 0;
chip->last_cc_uah = INT_MIN;
chip->last_ocv_temp = batt_temp;
- chip->last_soc_invalid = true;
chip->prev_batt_terminal_uv = 0;
}
#define OCV_RAW_UNINITIALIZED 0xFFFF
+#define MIN_OCV_UV 2000000
static int read_soc_params_raw(struct qpnp_bms_chip *chip,
struct raw_soc_params *raw,
int batt_temp)
{
+ bool warm_reset = false;
int rc;
mutex_lock(&chip->bms_output_lock);
@@ -711,13 +750,37 @@
if (chip->prev_last_good_ocv_raw == OCV_RAW_UNINITIALIZED) {
convert_and_store_ocv(chip, raw, batt_temp);
- pr_debug("PON_OCV_UV = %d\n", chip->last_ocv_uv);
+ pr_debug("PON_OCV_UV = %d, cc = %llx\n",
+ chip->last_ocv_uv, raw->cc);
+ warm_reset = qpnp_pon_is_warm_reset();
+ if (raw->last_good_ocv_uv < MIN_OCV_UV
+ || warm_reset > 0) {
+ pr_debug("OCV is stale or bad, estimating new OCV.\n");
+ chip->last_ocv_uv = estimate_ocv(chip);
+ raw->last_good_ocv_uv = chip->last_ocv_uv;
+ reset_cc(chip);
+ pr_debug("New PON_OCV_UV = %d, cc = %llx\n",
+ chip->last_ocv_uv, raw->cc);
+ }
} else if (chip->new_battery) {
/* if a new battery was inserted, estimate the ocv */
reset_for_new_battery(chip, batt_temp);
raw->cc = 0;
raw->last_good_ocv_uv = chip->last_ocv_uv;
chip->new_battery = false;
+ } else if (chip->done_charging) {
+ chip->done_charging = false;
+ /* if we just finished charging, reset CC and fake 100% */
+ chip->ocv_reading_at_100 = raw->last_good_ocv_raw;
+ chip->last_ocv_uv = chip->max_voltage_uv;
+ raw->last_good_ocv_uv = chip->max_voltage_uv;
+ raw->cc = 0;
+ reset_cc(chip);
+ chip->last_ocv_temp = batt_temp;
+ chip->software_cc_uah = 0;
+ chip->last_cc_uah = INT_MIN;
+ pr_debug("EOC Battery full ocv_reading = 0x%x\n",
+ chip->ocv_reading_at_100);
} else if (chip->prev_last_good_ocv_raw != raw->last_good_ocv_raw) {
convert_and_store_ocv(chip, raw, batt_temp);
/* forget the old cc value upon ocv */
@@ -726,20 +789,10 @@
raw->last_good_ocv_uv = chip->last_ocv_uv;
}
- /* fake a high OCV if done charging */
- if (chip->ocv_reading_at_100 != raw->last_good_ocv_raw) {
+ /* stop faking a high OCV if we get a new OCV */
+ if (chip->ocv_reading_at_100 != raw->last_good_ocv_raw)
chip->ocv_reading_at_100 = OCV_RAW_UNINITIALIZED;
- } else {
- /*
- * force 100% ocv by selecting the highest voltage the
- * battery could ever reach
- */
- raw->last_good_ocv_uv = chip->max_voltage_uv;
- chip->last_ocv_uv = chip->max_voltage_uv;
- chip->last_ocv_temp = batt_temp;
- reset_cc(chip);
- raw->cc = 0;
- }
+
pr_debug("last_good_ocv_raw= 0x%x, last_good_ocv_uv= %duV\n",
raw->last_good_ocv_raw, raw->last_good_ocv_uv);
pr_debug("cc_raw= 0x%llx\n", raw->cc);
@@ -818,11 +871,25 @@
*/
static int calculate_cc(struct qpnp_bms_chip *chip, int64_t cc)
{
- int64_t cc_voltage_uv, cc_pvh, cc_uah;
struct qpnp_iadc_calib calibration;
+ struct qpnp_vadc_result result;
+ int64_t cc_voltage_uv, cc_pvh, cc_uah;
+ int ibat_ua, rc;
+
+ rc = qpnp_vadc_read(DIE_TEMP, &result);
+ if (rc) {
+ pr_err("could not read pmic die temperature: %d\n", rc);
+ return chip->software_cc_uah;
+ }
+
+ rc = get_battery_current(chip, &ibat_ua);
+ if (rc) {
+ pr_err("could not read battery current: %d\n", rc);
+ return chip->software_cc_uah;
+ }
qpnp_iadc_get_gain_and_offset(&calibration);
- pr_debug("cc = %lld\n", cc);
+ pr_debug("cc = %lld, die_temp = %lld\n", cc, result.physical);
cc_voltage_uv = cc_reading_to_uv(cc);
cc_voltage_uv = cc_adjust_for_gain(cc_voltage_uv,
calibration.gain_raw
@@ -831,9 +898,12 @@
cc_pvh = cc_uv_to_pvh(cc_voltage_uv);
pr_debug("cc_pvh = %lld pvh\n", cc_pvh);
cc_uah = div_s64(cc_pvh, chip->r_sense_uohm);
- /* cc_raw had 4 bits of extra precision.
- By now it should be within 32 bit range */
- return (int)cc_uah;
+ rc = qpnp_iadc_comp_result(&cc_uah);
+ if (rc)
+ pr_debug("error compensation failed: %d\n", rc);
+ chip->software_cc_uah += cc_uah;
+ reset_cc(chip);
+ return (int)chip->software_cc_uah;
}
static int get_rbatt(struct qpnp_bms_chip *chip,
@@ -842,7 +912,6 @@
int rbatt_mohm, scalefactor;
rbatt_mohm = chip->default_rbatt_mohm;
- pr_debug("rbatt before scaling = %d\n", rbatt_mohm);
if (chip->rbatt_sf_lut == NULL) {
pr_debug("RBATT = %d\n", rbatt_mohm);
return rbatt_mohm;
@@ -851,18 +920,10 @@
batt_temp = batt_temp / 10;
scalefactor = interpolate_scalingfactor(chip->rbatt_sf_lut,
batt_temp, soc_rbatt_mohm);
- pr_debug("rbatt sf = %d for batt_temp = %d, soc_rbatt = %d\n",
- scalefactor, batt_temp, soc_rbatt_mohm);
rbatt_mohm = (rbatt_mohm * scalefactor) / 100;
rbatt_mohm += chip->r_conn_mohm;
- pr_debug("adding r_conn_mohm = %d rbatt = %d\n",
- chip->r_conn_mohm, rbatt_mohm);
rbatt_mohm += chip->rbatt_capacitive_mohm;
- pr_debug("adding rbatt_capacitive_mohm = %d rbatt = %d\n",
- chip->rbatt_capacitive_mohm, rbatt_mohm);
-
- pr_debug("RBATT = %d\n", rbatt_mohm);
return rbatt_mohm;
}
@@ -915,9 +976,6 @@
+ (chip->v_cutoff_uv);
delta_uv = ocv_mv * 1000 - unusable_uv;
- pr_debug("soc = %d ocv = %d rbat = %d u_uv = %d delta_v = %d\n",
- i, ocv_mv, rbatt_mohm, unusable_uv, delta_uv);
-
if (delta_uv > 0)
break;
@@ -933,10 +991,10 @@
pc_unusable = calculate_pc(chip, unusable_uv, batt_temp);
uuc_uah = (params->fcc_uah * pc_unusable) / 100;
- pr_debug("For uuc_iavg_ma = %d, unusable_rbatt = %d unusable_uv = %d unusable_pc = %d uuc = %d\n",
+ pr_debug("For uuc_iavg_ma = %d, unusable_rbatt = %d unusable_uv = %d unusable_pc = %d rbatt_pc = %d uuc = %d\n",
uuc_iavg_ma,
uuc_rbatt_mohm, unusable_uv,
- pc_unusable, uuc_uah);
+ pc_unusable, i, uuc_uah);
*ret_pc_unusable = pc_unusable;
return uuc_uah;
}
@@ -1112,8 +1170,7 @@
if (rtc == NULL) {
pr_err("%s: unable to open rtc device (%s)\n",
__FILE__, CONFIG_RTC_HCTOSYS_DEVICE);
- rc = -EINVAL;
- goto close_time;
+ return -EINVAL;
}
rc = rtc_read_time(rtc, &tm);
@@ -1136,21 +1193,22 @@
return rc;
}
-static int calculate_delta_time(struct qpnp_bms_chip *chip, int *delta_time_s)
+static int calculate_delta_time(unsigned long *time_stamp, int *delta_time_s)
{
unsigned long now_tm_sec = 0;
/* default to delta time = 0 if anything fails */
*delta_time_s = 0;
- get_current_time(&now_tm_sec);
+ if (get_current_time(&now_tm_sec)) {
+ pr_err("RTC read failed\n");
+ return 0;
+ }
- *delta_time_s = (now_tm_sec - chip->tm_sec);
- pr_debug("tm_sec = %ld, now_tm_sec = %ld delta_s = %d\n",
- chip->tm_sec, now_tm_sec, *delta_time_s);
+ *delta_time_s = (now_tm_sec - *time_stamp);
/* remember this time */
- chip->tm_sec = now_tm_sec;
+ *time_stamp = now_tm_sec;
return 0;
}
@@ -1161,7 +1219,9 @@
{
int soc_rbatt;
- calculate_delta_time(chip, ¶ms->delta_time_s);
+ calculate_delta_time(&chip->tm_sec, ¶ms->delta_time_s);
+ pr_debug("tm_sec = %ld, delta_s = %d\n",
+ chip->tm_sec, params->delta_time_s);
params->fcc_uah = calculate_fcc(chip, batt_temp);
pr_debug("FCC = %uuAh batt_temp = %d\n", params->fcc_uah, batt_temp);
@@ -1180,6 +1240,13 @@
if (soc_rbatt < 0)
soc_rbatt = 0;
params->rbatt_mohm = get_rbatt(chip, soc_rbatt, batt_temp);
+ pr_debug("rbatt_mohm = %d\n", params->rbatt_mohm);
+
+ if (params->rbatt_mohm != chip->rbatt_mohm
+ && chip->bms_psy.name != NULL) {
+ chip->rbatt_mohm = params->rbatt_mohm;
+ power_supply_changed(&chip->bms_psy);
+ }
calculate_iavg(chip, params->cc_uah, ¶ms->iavg_ua,
params->delta_time_s);
@@ -1246,9 +1313,12 @@
ocv_est_uv = vbat_uv + (ibat_ua * chip->r_conn_mohm) / 1000;
pr_debug("forcing ocv to be %d due to bms reset mode\n", ocv_est_uv);
chip->last_ocv_uv = ocv_est_uv;
+ mutex_lock(&chip->last_soc_mutex);
chip->last_soc = -EINVAL;
chip->last_soc_invalid = true;
+ mutex_unlock(&chip->last_soc_mutex);
reset_cc(chip);
+ chip->software_cc_uah = 0;
chip->last_cc_uah = INT_MIN;
stop_ocv_updates(chip);
@@ -1289,25 +1359,26 @@
module_param_cb(bms_reset, &bms_reset_ops, &bms_reset, 0644);
+#define VBATT_ERROR_MARGIN 20000
static int charging_adjustments(struct qpnp_bms_chip *chip,
struct soc_params *params, int soc,
int vbat_uv, int ibat_ua, int batt_temp)
{
- int chg_soc;
- int batt_terminal_uv = vbat_uv + (ibat_ua * chip->r_conn_mohm) / 1000;
+ int chg_soc, batt_terminal_uv;
+
+ batt_terminal_uv = vbat_uv + VBATT_ERROR_MARGIN
+ + (ibat_ua * chip->r_conn_mohm) / 1000;
if (chip->soc_at_cv == -EINVAL) {
- /* In constant current charging return the calc soc */
- if (batt_terminal_uv <= chip->max_voltage_uv)
- pr_debug("CC CHG SOC %d\n", soc);
-
- /* Note the CC to CV point */
if (batt_terminal_uv >= chip->max_voltage_uv) {
chip->soc_at_cv = soc;
chip->prev_chg_soc = soc;
chip->ibat_at_cv_ua = ibat_ua;
pr_debug("CC_TO_CV ibat_ua = %d CHG SOC %d\n",
ibat_ua, soc);
+ } else {
+ /* In constant current charging return the calc soc */
+ pr_debug("CC CHG SOC %d\n", soc);
}
chip->prev_batt_terminal_uv = batt_terminal_uv;
@@ -1361,18 +1432,40 @@
* a wakelock untill soc = 0%
*/
if (vbat_uv <= chip->low_voltage_threshold
- && !chip->low_voltage_wake_lock_held) {
+ && !wake_lock_active(&chip->low_voltage_wake_lock)) {
pr_debug("voltage = %d low holding wakelock\n", vbat_uv);
wake_lock(&chip->low_voltage_wake_lock);
- chip->low_voltage_wake_lock_held = 1;
} else if (vbat_uv > chip->low_voltage_threshold
- && chip->low_voltage_wake_lock_held) {
+ && wake_lock_active(&chip->low_voltage_wake_lock)) {
pr_debug("voltage = %d releasing wakelock\n", vbat_uv);
- chip->low_voltage_wake_lock_held = 0;
wake_unlock(&chip->low_voltage_wake_lock);
}
}
+#define VBATT_ERROR_MARGIN 20000
+static void cv_voltage_check(struct qpnp_bms_chip *chip, int vbat_uv)
+{
+ /*
+ * if battery is very low (v_cutoff voltage + 20mv) hold
+ * a wakelock untill soc = 0%
+ */
+ if (wake_lock_active(&chip->cv_wake_lock)) {
+ if (chip->soc_at_cv != -EINVAL) {
+ pr_debug("hit CV, releasing cv wakelock\n");
+ wake_unlock(&chip->cv_wake_lock);
+ } else if (!is_battery_charging(chip)) {
+ pr_debug("charging stopped, releasing cv wakelock\n");
+ wake_unlock(&chip->cv_wake_lock);
+ }
+ } else if (vbat_uv > chip->max_voltage_uv - VBATT_ERROR_MARGIN
+ && chip->soc_at_cv == -EINVAL
+ && is_battery_charging(chip)
+ && !wake_lock_active(&chip->cv_wake_lock)) {
+ pr_debug("voltage = %d holding cv wakelock\n", vbat_uv);
+ wake_lock(&chip->cv_wake_lock);
+ }
+}
+
#define NO_ADJUST_HIGH_SOC_THRESHOLD 90
static int adjust_soc(struct qpnp_bms_chip *chip, struct soc_params *params,
int soc, int batt_temp)
@@ -1396,14 +1489,12 @@
}
very_low_voltage_check(chip, vbat_uv);
+ cv_voltage_check(chip, vbat_uv);
delta_ocv_uv_limit = DIV_ROUND_CLOSEST(ibat_ua, 1000);
ocv_est_uv = vbat_uv + (ibat_ua * params->rbatt_mohm)/1000;
- chip->ibat_max_ua = (ocv_est_uv - chip->v_cutoff_uv) * 1000
- / (params->rbatt_mohm);
-
pc_est = calculate_pc(chip, ocv_est_uv, batt_temp);
soc_est = div_s64((s64)params->fcc_uah * pc_est - params->uuc_uah*100,
(s64)params->fcc_uah - params->uuc_uah);
@@ -1534,10 +1625,6 @@
pr_debug("clamping soc to 1, vbat (%d) > cutoff (%d)\n",
vbat_uv, chip->v_cutoff_uv);
return 1;
- } else if (soc > 0 && vbat_uv < chip->v_cutoff_uv) {
- pr_debug("forcing soc to 0, vbat (%d) < cutoff (%d)\n",
- vbat_uv, chip->v_cutoff_uv);
- return 0;
} else {
pr_debug("not clamping, using soc = %d, vbat = %d and cutoff = %d\n",
soc, vbat_uv, chip->v_cutoff_uv);
@@ -1545,6 +1632,7 @@
}
}
+#define SLEEP_RECALC_INTERVAL 3
static int calculate_state_of_charge(struct qpnp_bms_chip *chip,
struct raw_soc_params *raw,
int batt_temp)
@@ -1553,7 +1641,7 @@
int shutdown_soc, new_calculated_soc, remaining_usable_charge_uah;
struct soc_params params;
- if (!chip->battery_present) {
+ if (!is_battery_present(chip)) {
pr_debug("battery gone, reporting 100\n");
new_calculated_soc = 100;
goto done_calculating;
@@ -1625,7 +1713,6 @@
soc, shutdown_soc);
find_ocv_for_soc(chip, ¶ms, batt_temp,
shutdown_soc, &new_ocv_uv);
- chip->pon_ocv_uv = chip->last_ocv_uv;
chip->last_ocv_uv = new_ocv_uv;
remaining_usable_charge_uah = params.ocv_charge_uah
@@ -1656,13 +1743,30 @@
}
chip->calculated_soc = new_calculated_soc;
+ pr_debug("CC based calculated SOC = %d\n", chip->calculated_soc);
+ mutex_lock(&chip->last_soc_mutex);
if (chip->last_soc_invalid) {
chip->last_soc_invalid = false;
chip->last_soc = -EINVAL;
}
- pr_debug("CC based calculated SOC = %d\n", chip->calculated_soc);
- chip->first_time_calc_soc = 0;
+ /*
+ * Check if more than a long time has passed since the last
+ * calculation (more than n times compared to the soc recalculation
+ * rate, where n is defined by SLEEP_RECALC_INTERVAL). If this is true,
+ * then the system must have gone through a long sleep, and SoC can be
+ * allowed to become unbounded by the last reported SoC
+ */
+ if (params.delta_time_s * 1000 >
+ chip->calculate_soc_ms * SLEEP_RECALC_INTERVAL
+ && !chip->first_time_calc_soc) {
+ chip->last_soc_unbound = true;
+ chip->last_soc_change_sec = chip->last_recalc_time;
+ pr_debug("last_soc unbound because elapsed time = %d\n",
+ params.delta_time_s);
+ }
+ mutex_unlock(&chip->last_soc_mutex);
get_current_time(&chip->last_recalc_time);
+ chip->first_time_calc_soc = 0;
return chip->calculated_soc;
}
@@ -1700,10 +1804,15 @@
struct qpnp_vadc_result result;
struct raw_soc_params raw;
- wake_lock(&chip->soc_wake_lock);
+ if (!wake_lock_active(&chip->soc_wake_lock))
+ wake_lock(&chip->soc_wake_lock);
+ mutex_lock(&chip->vbat_monitor_mutex);
+ qpnp_adc_tm_channel_measure(&chip->vbat_monitor_params);
+ mutex_unlock(&chip->vbat_monitor_mutex);
if (chip->use_voltage_soc) {
soc = calculate_soc_from_voltage(chip);
} else {
+ qpnp_iadc_calibrate_for_trim();
rc = qpnp_vadc_read(LR_MUX1_BATT_THERM, &result);
if (rc) {
pr_err("error reading vadc LR_MUX1_BATT_THERM = %d, rc = %d\n",
@@ -1742,7 +1851,7 @@
int soc = recalculate_soc(chip);
if (soc < chip->low_soc_calc_threshold
- || chip->low_voltage_wake_lock_held)
+ || wake_lock_active(&chip->low_voltage_wake_lock))
schedule_delayed_work(&chip->calculate_soc_delayed_work,
round_jiffies_relative(msecs_to_jiffies
(chip->low_soc_calculate_soc_ms)));
@@ -1778,32 +1887,20 @@
#define SOC_CATCHUP_SEC_MAX 600
#define SOC_CATCHUP_SEC_PER_PERCENT 60
#define MAX_CATCHUP_SOC (SOC_CATCHUP_SEC_MAX/SOC_CATCHUP_SEC_PER_PERCENT)
-static int scale_soc_while_chg(struct qpnp_bms_chip *chip,
- int delta_time_us, int new_soc, int prev_soc)
+static int scale_soc_while_chg(struct qpnp_bms_chip *chip, int chg_time_sec,
+ int catch_up_sec, int new_soc, int prev_soc)
{
- int chg_time_sec;
- int catch_up_sec;
int scaled_soc;
int numerator;
/*
- * The device must be charging for reporting a higher soc, if
- * not ignore this soc and continue reporting the prev_soc.
- * Also don't report a high value immediately slowly scale the
+ * Don't report a high value immediately slowly scale the
* value from prev_soc to the new soc based on a charge time
* weighted average
*/
-
- /* if not charging, return last soc */
- if (!is_battery_charging(chip))
- return prev_soc;
-
- chg_time_sec = DIV_ROUND_UP(chip->charge_time_us, USEC_PER_SEC);
- catch_up_sec = DIV_ROUND_UP(chip->catch_up_time_us, USEC_PER_SEC);
+ pr_debug("cts = %d catch_up_sec = %d\n", chg_time_sec, catch_up_sec);
if (catch_up_sec == 0)
return new_soc;
- pr_debug("cts= %d catch_up_sec = %d\n", chg_time_sec, catch_up_sec);
-
/*
* if charging for more than catch_up time, simply return
* new soc
@@ -1836,14 +1933,17 @@
return chip->prev_voltage_based_soc;
}
+#define SOC_CHANGE_PER_SEC 20
static int report_cc_based_soc(struct qpnp_bms_chip *chip)
{
- int soc;
- int delta_time_us;
+ int soc, soc_change;
+ int time_since_last_change_sec, charge_time_sec = 0;
+ unsigned long last_change_sec;
struct timespec now;
struct qpnp_vadc_result result;
int batt_temp;
int rc;
+ bool charging, charging_since_last_report;
soc = chip->calculated_soc;
@@ -1858,68 +1958,84 @@
result.measurement);
batt_temp = (int)result.physical;
- do_posix_clock_monotonic_gettime(&now);
- if (chip->t_soc_queried.tv_sec != 0) {
- delta_time_us
- = (now.tv_sec - chip->t_soc_queried.tv_sec) * USEC_PER_SEC
- + (now.tv_nsec - chip->t_soc_queried.tv_nsec) / 1000;
- } else {
- /* calculation for the first time */
- delta_time_us = 0;
- }
+ mutex_lock(&chip->last_soc_mutex);
+ last_change_sec = chip->last_soc_change_sec;
+ calculate_delta_time(&last_change_sec, &time_since_last_change_sec);
+ charging = is_battery_charging(chip);
+ charging_since_last_report = charging || (chip->last_soc_unbound
+ && chip->was_charging_at_sleep);
/*
* account for charge time - limit it to SOC_CATCHUP_SEC to
* avoid overflows when charging continues for extended periods
*/
- if (is_battery_charging(chip)) {
- if (chip->charge_time_us == 0) {
+ if (charging) {
+ if (chip->charge_start_tm_sec == 0) {
/*
* calculating soc for the first time
* after start of chg. Initialize catchup time
*/
if (abs(soc - chip->last_soc) < MAX_CATCHUP_SOC)
- chip->catch_up_time_us =
+ chip->catch_up_time_sec =
(soc - chip->last_soc)
- * SOC_CATCHUP_SEC_PER_PERCENT
- * USEC_PER_SEC;
+ * SOC_CATCHUP_SEC_PER_PERCENT;
else
- chip->catch_up_time_us =
- SOC_CATCHUP_SEC_MAX * USEC_PER_SEC;
+ chip->catch_up_time_sec = SOC_CATCHUP_SEC_MAX;
- if (chip->catch_up_time_us < 0)
- chip->catch_up_time_us = 0;
+ if (chip->catch_up_time_sec < 0)
+ chip->catch_up_time_sec = 0;
+ chip->charge_start_tm_sec = last_change_sec;
}
- /* add charge time */
- if (chip->charge_time_us < SOC_CATCHUP_SEC_MAX * USEC_PER_SEC)
- chip->charge_time_us += delta_time_us;
+ charge_time_sec = min(SOC_CATCHUP_SEC_MAX, (int)last_change_sec
+ - chip->charge_start_tm_sec);
/* end catchup if calculated soc and last soc are same */
if (chip->last_soc == soc)
- chip->catch_up_time_us = 0;
+ chip->catch_up_time_sec = 0;
}
- /* last_soc < soc ... scale and catch up */
- if (chip->last_soc != -EINVAL && chip->last_soc < soc && soc != 100)
- soc = scale_soc_while_chg(chip, delta_time_us,
- soc, chip->last_soc);
+ if (chip->last_soc != -EINVAL) {
+ /* last_soc < soc ... if we have not been charging at all
+ * since the last time this was called, report previous SoC.
+ * Otherwise, scale and catch up.
+ */
+ if (chip->last_soc < soc && !charging_since_last_report)
+ soc = chip->last_soc;
+ else if (chip->last_soc < soc && soc != 100)
+ soc = scale_soc_while_chg(chip, charge_time_sec,
+ chip->catch_up_time_sec,
+ soc, chip->last_soc);
- if (chip->last_soc_unbound)
- chip->last_soc_unbound = false;
- else if (chip->last_soc != -EINVAL) {
+ soc_change = min((int)abs(chip->last_soc - soc),
+ time_since_last_change_sec / SOC_CHANGE_PER_SEC);
+ if (chip->last_soc_unbound) {
+ chip->last_soc_unbound = false;
+ } else {
+ /*
+ * if soc have not been unbound by resume,
+ * only change reported SoC by 1.
+ */
+ soc_change = min(1, soc_change);
+ }
+
if (soc < chip->last_soc && soc != 0)
- soc = chip->last_soc - 1;
+ soc = chip->last_soc - soc_change;
if (soc > chip->last_soc && soc != 100)
- soc = chip->last_soc + 1;
+ soc = chip->last_soc + soc_change;
}
- pr_debug("last_soc = %d, calculated_soc = %d, soc = %d\n",
- chip->last_soc, chip->calculated_soc, soc);
+ if (chip->last_soc != soc)
+ chip->last_soc_change_sec = last_change_sec;
+
+ pr_debug("last_soc = %d, calculated_soc = %d, soc = %d, time since last change = %d\n",
+ chip->last_soc, chip->calculated_soc,
+ soc, time_since_last_change_sec);
chip->last_soc = bound_soc(soc);
backup_soc_and_iavg(chip, batt_temp, chip->last_soc);
pr_debug("Reported SOC = %d\n", chip->last_soc);
chip->t_soc_queried = now;
+ mutex_unlock(&chip->last_soc_mutex);
return soc;
}
@@ -1935,16 +2051,281 @@
return report_cc_based_soc(chip);
}
+static void configure_vbat_monitor_low(struct qpnp_bms_chip *chip)
+{
+ mutex_lock(&chip->vbat_monitor_mutex);
+ if (chip->vbat_monitor_params.state_request
+ == ADC_TM_HIGH_LOW_THR_ENABLE) {
+ /*
+ * Battery is now around or below v_cutoff
+ */
+ pr_debug("battery entered cutoff range\n");
+ if (!wake_lock_active(&chip->low_voltage_wake_lock)) {
+ pr_debug("voltage low, holding wakelock\n");
+ wake_lock(&chip->low_voltage_wake_lock);
+ cancel_delayed_work_sync(
+ &chip->calculate_soc_delayed_work);
+ schedule_delayed_work(
+ &chip->calculate_soc_delayed_work, 0);
+ }
+ chip->vbat_monitor_params.state_request =
+ ADC_TM_HIGH_THR_ENABLE;
+ chip->vbat_monitor_params.high_thr =
+ (chip->low_voltage_threshold + VBATT_ERROR_MARGIN);
+ pr_debug("set low thr to %d and high to %d\n",
+ chip->vbat_monitor_params.low_thr,
+ chip->vbat_monitor_params.high_thr);
+ chip->vbat_monitor_params.low_thr = 0;
+ } else if (chip->vbat_monitor_params.state_request
+ == ADC_TM_LOW_THR_ENABLE) {
+ /*
+ * Battery is in normal operation range.
+ */
+ pr_debug("battery entered normal range\n");
+ if (wake_lock_active(&chip->cv_wake_lock)) {
+ wake_unlock(&chip->cv_wake_lock);
+ pr_debug("releasing cv wake lock\n");
+ }
+ chip->in_cv_range = false;
+ chip->vbat_monitor_params.state_request =
+ ADC_TM_HIGH_LOW_THR_ENABLE;
+ chip->vbat_monitor_params.high_thr = chip->max_voltage_uv
+ - VBATT_ERROR_MARGIN;
+ chip->vbat_monitor_params.low_thr =
+ chip->low_voltage_threshold;
+ pr_debug("set low thr to %d and high to %d\n",
+ chip->vbat_monitor_params.low_thr,
+ chip->vbat_monitor_params.high_thr);
+ }
+ qpnp_adc_tm_channel_measure(&chip->vbat_monitor_params);
+ mutex_unlock(&chip->vbat_monitor_mutex);
+}
+
+#define CV_LOW_THRESHOLD_HYST_UV 100000
+static void configure_vbat_monitor_high(struct qpnp_bms_chip *chip)
+{
+ mutex_lock(&chip->vbat_monitor_mutex);
+ if (chip->vbat_monitor_params.state_request
+ == ADC_TM_HIGH_LOW_THR_ENABLE) {
+ /*
+ * Battery is around vddmax
+ */
+ pr_debug("battery entered vddmax range\n");
+ chip->in_cv_range = true;
+ if (!wake_lock_active(&chip->cv_wake_lock)) {
+ wake_lock(&chip->cv_wake_lock);
+ pr_debug("holding cv wake lock\n");
+ }
+ schedule_work(&chip->recalc_work);
+ chip->vbat_monitor_params.state_request =
+ ADC_TM_LOW_THR_ENABLE;
+ chip->vbat_monitor_params.low_thr =
+ (chip->max_voltage_uv - CV_LOW_THRESHOLD_HYST_UV);
+ chip->vbat_monitor_params.high_thr = chip->max_voltage_uv * 2;
+ pr_debug("set low thr to %d and high to %d\n",
+ chip->vbat_monitor_params.low_thr,
+ chip->vbat_monitor_params.high_thr);
+ } else if (chip->vbat_monitor_params.state_request
+ == ADC_TM_HIGH_THR_ENABLE) {
+ /*
+ * Battery is in normal operation range.
+ */
+ pr_debug("battery entered normal range\n");
+ if (wake_lock_active(&chip->low_voltage_wake_lock)) {
+ pr_debug("voltage high, releasing wakelock\n");
+ wake_unlock(&chip->low_voltage_wake_lock);
+ }
+ chip->vbat_monitor_params.state_request =
+ ADC_TM_HIGH_LOW_THR_ENABLE;
+ chip->vbat_monitor_params.high_thr =
+ chip->max_voltage_uv - VBATT_ERROR_MARGIN;
+ chip->vbat_monitor_params.low_thr =
+ chip->low_voltage_threshold;
+ pr_debug("set low thr to %d and high to %d\n",
+ chip->vbat_monitor_params.low_thr,
+ chip->vbat_monitor_params.high_thr);
+ }
+ qpnp_adc_tm_channel_measure(&chip->vbat_monitor_params);
+ mutex_unlock(&chip->vbat_monitor_mutex);
+}
+
+static void btm_notify_vbat(enum qpnp_tm_state state, void *ctx)
+{
+ struct qpnp_bms_chip *chip = ctx;
+ int vbat_uv;
+ struct qpnp_vadc_result result;
+ int rc;
+
+ rc = qpnp_vadc_read(VBAT_SNS, &result);
+ pr_debug("vbat = %lld, raw = 0x%x\n", result.physical, result.adc_code);
+
+ get_battery_voltage(&vbat_uv);
+ pr_debug("vbat is at %d, state is at %d\n", vbat_uv, state);
+
+ if (state == ADC_TM_LOW_STATE) {
+ pr_debug("low voltage btm notification triggered\n");
+ if (vbat_uv - VBATT_ERROR_MARGIN
+ < chip->vbat_monitor_params.low_thr) {
+ configure_vbat_monitor_low(chip);
+ } else {
+ pr_debug("faulty btm trigger, discarding\n");
+ qpnp_adc_tm_channel_measure(
+ &chip->vbat_monitor_params);
+ }
+ } else if (state == ADC_TM_HIGH_STATE) {
+ pr_debug("high voltage btm notification triggered\n");
+ if (vbat_uv + VBATT_ERROR_MARGIN
+ > chip->vbat_monitor_params.high_thr) {
+ configure_vbat_monitor_high(chip);
+ } else {
+ pr_debug("faulty btm trigger, discarding\n");
+ qpnp_adc_tm_channel_measure(
+ &chip->vbat_monitor_params);
+ }
+ } else {
+ pr_debug("unknown voltage notification state: %d\n", state);
+ }
+ power_supply_changed(&chip->bms_psy);
+}
+
+static int reset_vbat_monitoring(struct qpnp_bms_chip *chip)
+{
+ int rc;
+
+ chip->vbat_monitor_params.state_request = ADC_TM_HIGH_LOW_THR_DISABLE;
+ rc = qpnp_adc_tm_channel_measure(&chip->vbat_monitor_params);
+ if (rc) {
+ pr_err("tm measure failed: %d\n", rc);
+ return rc;
+ }
+ mutex_lock(&chip->vbat_monitor_mutex);
+ if (wake_lock_active(&chip->low_voltage_wake_lock)) {
+ pr_debug("battery removed, releasing wakelock\n");
+ wake_unlock(&chip->low_voltage_wake_lock);
+ }
+ if (chip->in_cv_range) {
+ pr_debug("battery removed, removing in_cv_range state\n");
+ chip->in_cv_range = false;
+ }
+ mutex_unlock(&chip->vbat_monitor_mutex);
+ return 0;
+}
+
+static int setup_vbat_monitoring(struct qpnp_bms_chip *chip)
+{
+ int rc;
+
+ rc = qpnp_adc_tm_is_ready();
+ if (rc) {
+ pr_info("adc tm is not ready yet: %d, defer probe\n", rc);
+ return -EPROBE_DEFER;
+ }
+
+ if (!is_battery_present(chip)) {
+ pr_debug("no battery inserted, do not setup vbat monitoring\n");
+ return 0;
+ }
+
+ chip->vbat_monitor_params.low_thr = chip->low_voltage_threshold;
+ chip->vbat_monitor_params.high_thr = chip->max_voltage_uv
+ - VBATT_ERROR_MARGIN;
+ chip->vbat_monitor_params.state_request = ADC_TM_HIGH_LOW_THR_ENABLE;
+ chip->vbat_monitor_params.channel = VBAT_SNS;
+ chip->vbat_monitor_params.btm_ctx = (void *)chip;
+ chip->vbat_monitor_params.timer_interval = ADC_MEAS1_INTERVAL_1S;
+ chip->vbat_monitor_params.threshold_notification = &btm_notify_vbat;
+ pr_debug("set low thr to %d and high to %d\n",
+ chip->vbat_monitor_params.low_thr,
+ chip->vbat_monitor_params.high_thr);
+ rc = qpnp_adc_tm_channel_measure(&chip->vbat_monitor_params);
+ if (rc) {
+ pr_err("tm setup failed: %d\n", rc);
+ return rc;
+ }
+ pr_debug("setup complete\n");
+ return 0;
+}
+
+static void charging_began(struct qpnp_bms_chip *chip)
+{
+ mutex_lock(&chip->last_soc_mutex);
+ chip->charge_start_tm_sec = 0;
+ chip->catch_up_time_sec = 0;
+ mutex_unlock(&chip->last_soc_mutex);
+
+ mutex_lock(&chip->last_ocv_uv_mutex);
+ chip->soc_at_cv = -EINVAL;
+ chip->prev_chg_soc = -EINVAL;
+ mutex_unlock(&chip->last_ocv_uv_mutex);
+}
+
+static void charging_ended(struct qpnp_bms_chip *chip)
+{
+ mutex_lock(&chip->last_soc_mutex);
+ chip->charge_start_tm_sec = 0;
+ chip->catch_up_time_sec = 0;
+ mutex_unlock(&chip->last_soc_mutex);
+
+ mutex_lock(&chip->last_ocv_uv_mutex);
+ chip->soc_at_cv = -EINVAL;
+ chip->prev_chg_soc = -EINVAL;
+ if (get_battery_status(chip) == POWER_SUPPLY_STATUS_FULL)
+ chip->done_charging = true;
+ mutex_unlock(&chip->last_ocv_uv_mutex);
+}
+
+static void battery_status_check(struct qpnp_bms_chip *chip)
+{
+ int status = get_battery_status(chip);
+
+ if (chip->battery_status != status) {
+ if (status == POWER_SUPPLY_STATUS_CHARGING) {
+ pr_debug("charging started\n");
+ charging_began(chip);
+ } else if (chip->battery_status
+ == POWER_SUPPLY_STATUS_CHARGING) {
+ pr_debug("charging ended\n");
+ charging_ended(chip);
+ }
+ chip->battery_status = status;
+ /* a new battery was inserted or removed, so force a soc
+ * recalculation to update the SoC */
+ schedule_work(&chip->recalc_work);
+ }
+}
+
+static void battery_insertion_check(struct qpnp_bms_chip *chip)
+{
+ bool present = is_battery_present(chip);
+
+ mutex_lock(&chip->vbat_monitor_mutex);
+ if (chip->battery_present != present) {
+ if (chip->battery_present != -EINVAL) {
+ if (present) {
+ setup_vbat_monitoring(chip);
+ chip->new_battery = true;
+ } else {
+ reset_vbat_monitoring(chip);
+ }
+ }
+ chip->battery_present = present;
+ /* a new battery was inserted or removed, so force a soc
+ * recalculation to update the SoC */
+ schedule_work(&chip->recalc_work);
+ }
+ mutex_unlock(&chip->vbat_monitor_mutex);
+}
+
/* Returns capacity as a SoC percentage between 0 and 100 */
static int get_prop_bms_capacity(struct qpnp_bms_chip *chip)
{
return report_state_of_charge(chip);
}
-/* Returns estimated max current that the battery can supply in uA */
-static int get_prop_bms_current_max(struct qpnp_bms_chip *chip)
+/* Returns estimated battery resistance */
+static int get_prop_bms_batt_resistance(struct qpnp_bms_chip *chip)
{
- return chip->ibat_max_ua;
+ return chip->rbatt_mohm * 1000;
}
/* Returns instantaneous current in uA */
@@ -1963,28 +2344,16 @@
/* Returns full charge design in uAh */
static int get_prop_bms_charge_full_design(struct qpnp_bms_chip *chip)
{
- return chip->fcc;
-}
-
-static int get_prop_bms_present(struct qpnp_bms_chip *chip)
-{
- return chip->battery_present;
-}
-
-static void set_prop_bms_present(struct qpnp_bms_chip *chip, int present)
-{
- if (chip->battery_present != present) {
- chip->battery_present = present;
- if (present)
- chip->new_battery = true;
- /* a new battery was inserted or removed, so force a soc
- * recalculation to update the SoC */
- schedule_work(&chip->recalc_work);
- }
+ return chip->fcc_mah * 1000;
}
static void qpnp_bms_external_power_changed(struct power_supply *psy)
{
+ struct qpnp_bms_chip *chip = container_of(psy, struct qpnp_bms_chip,
+ bms_psy);
+
+ battery_insertion_check(chip);
+ battery_status_check(chip);
}
static int qpnp_bms_power_get_property(struct power_supply *psy,
@@ -2001,32 +2370,12 @@
case POWER_SUPPLY_PROP_CURRENT_NOW:
val->intval = get_prop_bms_current_now(chip);
break;
- case POWER_SUPPLY_PROP_CURRENT_MAX:
- val->intval = get_prop_bms_current_max(chip);
+ case POWER_SUPPLY_PROP_RESISTANCE:
+ val->intval = get_prop_bms_batt_resistance(chip);
break;
case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
val->intval = get_prop_bms_charge_full_design(chip);
break;
- case POWER_SUPPLY_PROP_PRESENT:
- val->intval = get_prop_bms_present(chip);
- break;
- default:
- return -EINVAL;
- }
- return 0;
-}
-
-static int qpnp_bms_power_set_property(struct power_supply *psy,
- enum power_supply_property psp,
- const union power_supply_propval *val)
-{
- struct qpnp_bms_chip *chip = container_of(psy, struct qpnp_bms_chip,
- bms_psy);
-
- switch (psp) {
- case POWER_SUPPLY_PROP_PRESENT:
- set_prop_bms_present(chip, val->intval);
- break;
default:
return -EINVAL;
}
@@ -2175,7 +2524,7 @@
}
}
- chip->fcc = batt_data->fcc;
+ chip->fcc_mah = batt_data->fcc;
chip->fcc_temp_lut = batt_data->fcc_temp_lut;
chip->fcc_sf_lut = batt_data->fcc_sf_lut;
chip->pc_temp_ocv_lut = batt_data->pc_temp_ocv_lut;
@@ -2245,6 +2594,7 @@
SPMI_PROP_READ(ocv_low_threshold_uv,
"ocv-voltage-low-threshold-uv", rc);
SPMI_PROP_READ(low_voltage_threshold, "low-voltage-threshold", rc);
+ SPMI_PROP_READ(temperature_margin, "tm-temp-margin", rc);
if (chip->adjust_soc_low_threshold >= 45)
chip->adjust_soc_low_threshold = 45;
@@ -2271,6 +2621,8 @@
chip->calculated_soc = -EINVAL;
chip->last_soc = -EINVAL;
chip->last_soc_est = -EINVAL;
+ chip->battery_present = -EINVAL;
+ chip->battery_status = POWER_SUPPLY_STATUS_UNKNOWN;
chip->last_cc_uah = INT_MIN;
chip->ocv_reading_at_100 = OCV_RAW_UNINITIALIZED;
chip->prev_last_good_ocv_raw = OCV_RAW_UNINITIALIZED;
@@ -2384,6 +2736,7 @@
return rc;
}
reset_cc(chip);
+ chip->software_cc_uah = 0;
}
} else {
pr_debug("Internal rsense selected\n");
@@ -2400,6 +2753,7 @@
return rc;
}
reset_cc(chip);
+ chip->software_cc_uah = 0;
}
rc = qpnp_iadc_get_rsense(&rds_rsense_nohm);
@@ -2415,10 +2769,68 @@
return 0;
}
+static int refresh_die_temp_monitor(struct qpnp_bms_chip *chip)
+{
+ struct qpnp_vadc_result result;
+ int rc;
+
+ rc = qpnp_vadc_read(DIE_TEMP, &result);
+
+ pr_debug("low = %lld, high = %lld\n",
+ result.physical - chip->temperature_margin,
+ result.physical + chip->temperature_margin);
+ chip->die_temp_monitor_params.high_temp = result.physical
+ + chip->temperature_margin;
+ chip->die_temp_monitor_params.low_temp = result.physical
+ - chip->temperature_margin;
+ chip->die_temp_monitor_params.state_request =
+ ADC_TM_HIGH_LOW_THR_ENABLE;
+ return qpnp_adc_tm_channel_measure(&chip->die_temp_monitor_params);
+}
+
+static void btm_notify_die_temp(enum qpnp_tm_state state, void *ctx)
+{
+ struct qpnp_bms_chip *chip = ctx;
+ struct qpnp_vadc_result result;
+ int rc;
+
+ rc = qpnp_vadc_read(DIE_TEMP, &result);
+
+ if (state == ADC_TM_LOW_STATE)
+ pr_debug("low state triggered\n");
+ else if (state == ADC_TM_HIGH_STATE)
+ pr_debug("high state triggered\n");
+ pr_debug("die temp = %lld, raw = 0x%x\n",
+ result.physical, result.adc_code);
+ schedule_work(&chip->recalc_work);
+ refresh_die_temp_monitor(chip);
+}
+
+static int setup_die_temp_monitoring(struct qpnp_bms_chip *chip)
+{
+ int rc = qpnp_adc_tm_is_ready();
+ if (rc) {
+ pr_info("adc tm is not ready yet: %d, defer probe\n", rc);
+ return -EPROBE_DEFER;
+ }
+ chip->die_temp_monitor_params.channel = DIE_TEMP;
+ chip->die_temp_monitor_params.btm_ctx = (void *)chip;
+ chip->die_temp_monitor_params.timer_interval = ADC_MEAS1_INTERVAL_1S;
+ chip->die_temp_monitor_params.threshold_notification =
+ &btm_notify_die_temp;
+ refresh_die_temp_monitor(chip);
+ if (rc) {
+ pr_err("tm setup failed: %d\n", rc);
+ return rc;
+ }
+ pr_debug("setup complete\n");
+ return 0;
+}
+
static int __devinit qpnp_bms_probe(struct spmi_device *spmi)
{
struct qpnp_bms_chip *chip;
- union power_supply_propval retval = {0,};
+ bool warm_reset;
int rc, vbatt;
chip = kzalloc(sizeof *chip, GFP_KERNEL);
@@ -2431,15 +2843,22 @@
rc = qpnp_vadc_is_ready();
if (rc) {
pr_info("vadc not ready: %d, deferring probe\n", rc);
+ rc = -EPROBE_DEFER;
goto error_read;
}
rc = qpnp_iadc_is_ready();
if (rc) {
pr_info("iadc not ready: %d, deferring probe\n", rc);
+ rc = -EPROBE_DEFER;
goto error_read;
}
+ warm_reset = qpnp_pon_is_warm_reset();
+ rc = warm_reset;
+ if (rc < 0)
+ goto error_read;
+
rc = register_spmi(chip, spmi);
if (rc) {
pr_err("error registering spmi resource %d\n", rc);
@@ -2494,12 +2913,16 @@
mutex_init(&chip->bms_output_lock);
mutex_init(&chip->last_ocv_uv_mutex);
+ mutex_init(&chip->vbat_monitor_mutex);
mutex_init(&chip->soc_invalidation_mutex);
+ mutex_init(&chip->last_soc_mutex);
wake_lock_init(&chip->soc_wake_lock, WAKE_LOCK_SUSPEND,
"qpnp_soc_lock");
wake_lock_init(&chip->low_voltage_wake_lock, WAKE_LOCK_SUSPEND,
"qpnp_low_voltage_lock");
+ wake_lock_init(&chip->cv_wake_lock, WAKE_LOCK_SUSPEND,
+ "qpnp_cv_lock");
INIT_DELAYED_WORK(&chip->calculate_soc_delayed_work,
calculate_soc_work);
INIT_WORK(&chip->recalc_work, recalculate_work);
@@ -2509,15 +2932,16 @@
dev_set_drvdata(&spmi->dev, chip);
device_init_wakeup(&spmi->dev, 1);
- if (!chip->batt_psy)
- chip->batt_psy = power_supply_get_by_name("battery");
- if (chip->batt_psy) {
- chip->batt_psy->get_property(chip->batt_psy,
- POWER_SUPPLY_PROP_PRESENT, &retval);
- chip->battery_present = retval.intval;
- pr_debug("present = %d\n", chip->battery_present);
- } else {
- chip->battery_present = 1;
+ rc = setup_vbat_monitoring(chip);
+ if (rc < 0) {
+ pr_err("failed to set up voltage notifications: %d\n", rc);
+ goto error_setup;
+ }
+
+ rc = setup_die_temp_monitoring(chip);
+ if (rc < 0) {
+ pr_err("failed to set up die temp notifications: %d\n", rc);
+ goto error_setup;
}
calculate_soc_work(&(chip->calculate_soc_delayed_work.work));
@@ -2528,7 +2952,6 @@
chip->bms_psy.properties = msm_bms_power_props;
chip->bms_psy.num_properties = ARRAY_SIZE(msm_bms_power_props);
chip->bms_psy.get_property = qpnp_bms_power_get_property;
- chip->bms_psy.set_property = qpnp_bms_power_set_property;
chip->bms_psy.external_power_changed =
qpnp_bms_external_power_changed;
chip->bms_psy.supplied_to = qpnp_bms_supplicants;
@@ -2549,16 +2972,18 @@
goto unregister_dc;
}
- pr_info("probe success: soc =%d vbatt = %d ocv = %d r_sense_uohm = %u\n",
- get_prop_bms_capacity(chip),
- vbatt, chip->last_ocv_uv, chip->r_sense_uohm);
+ pr_info("probe success: soc =%d vbatt = %d ocv = %d r_sense_uohm = %u warm_reset = %d\n",
+ get_prop_bms_capacity(chip), vbatt, chip->last_ocv_uv,
+ chip->r_sense_uohm, warm_reset);
return 0;
unregister_dc:
+ power_supply_unregister(&chip->bms_psy);
+error_setup:
+ dev_set_drvdata(&spmi->dev, NULL);
wake_lock_destroy(&chip->soc_wake_lock);
wake_lock_destroy(&chip->low_voltage_wake_lock);
- power_supply_unregister(&chip->bms_psy);
- dev_set_drvdata(&spmi->dev, NULL);
+ wake_lock_destroy(&chip->cv_wake_lock);
error_resource:
error_read:
kfree(chip);
@@ -2575,10 +3000,20 @@
return 0;
}
+static int bms_suspend(struct device *dev)
+{
+ struct qpnp_bms_chip *chip = dev_get_drvdata(dev);
+
+ cancel_delayed_work_sync(&chip->calculate_soc_delayed_work);
+ chip->was_charging_at_sleep = is_battery_charging(chip);
+ return 0;
+}
+
static int bms_resume(struct device *dev)
{
int rc;
- unsigned long soc_calc_period;
+ int soc_calc_period;
+ int time_until_next_recalc;
unsigned long time_since_last_recalc;
unsigned long tm_now_sec;
struct qpnp_bms_chip *chip = dev_get_drvdata(dev);
@@ -2587,11 +3022,6 @@
if (rc) {
pr_err("Could not read current time: %d\n", rc);
} else if (tm_now_sec > chip->last_recalc_time) {
- /*
- * unbind the last soc so that the next
- * recalculation is not limited to changing by 1%
- */
- chip->last_soc_unbound = true;
time_since_last_recalc = tm_now_sec - chip->last_recalc_time;
pr_debug("Time since last recalc: %lu\n",
time_since_last_recalc);
@@ -2600,16 +3030,23 @@
else
soc_calc_period = chip->calculate_soc_ms;
- if (time_since_last_recalc >= soc_calc_period) {
- chip->last_recalc_time = tm_now_sec;
- recalculate_soc(chip);
- }
+ time_until_next_recalc = max(0, soc_calc_period
+ - (int)(time_since_last_recalc * 1000));
+
+ if (!wake_lock_active(&chip->soc_wake_lock)
+ && time_until_next_recalc == 0)
+ wake_lock(&chip->soc_wake_lock);
+
+ schedule_delayed_work(&chip->calculate_soc_delayed_work,
+ round_jiffies_relative(msecs_to_jiffies
+ (time_until_next_recalc)));
}
return 0;
}
static const struct dev_pm_ops qpnp_bms_pm_ops = {
.resume = bms_resume,
+ .suspend = bms_suspend,
};
static struct spmi_driver qpnp_bms_driver = {
diff --git a/drivers/power/qpnp-charger.c b/drivers/power/qpnp-charger.c
index 993b2cb..3d8a75d 100644
--- a/drivers/power/qpnp-charger.c
+++ b/drivers/power/qpnp-charger.c
@@ -81,6 +81,7 @@
#define CHGR_BAT_IF_VCP 0x42
#define CHGR_BAT_IF_BATFET_CTRL1 0x90
#define CHGR_MISC_BOOT_DONE 0x42
+#define CHGR_BUCK_COMPARATOR_OVRIDE_1 0xEB
#define CHGR_BUCK_COMPARATOR_OVRIDE_3 0xED
#define CHGR_BUCK_BCK_VBAT_REG_MODE 0x74
#define MISC_REVISION2 0x01
@@ -242,6 +243,7 @@
unsigned int chg_fastchg_irq;
unsigned int chg_trklchg_irq;
unsigned int chg_failed_irq;
+ unsigned int chg_vbatdet_lo_irq;
unsigned int batt_pres_irq;
bool bat_is_cool;
bool bat_is_warm;
@@ -260,7 +262,7 @@
unsigned int warm_bat_mv;
unsigned int cool_bat_mv;
unsigned int resume_delta_mv;
- unsigned int term_current;
+ int term_current;
unsigned int maxinput_usb_ma;
unsigned int maxinput_dc_ma;
unsigned int warm_bat_decidegc;
@@ -280,6 +282,8 @@
struct qpnp_adc_tm_btm_param adc_param;
struct work_struct adc_measure_work;
struct delayed_work arb_stop_work;
+ struct delayed_work eoc_work;
+ struct wake_lock eoc_wake_lock;
};
static struct of_device_id qpnp_charger_match_table[] = {
@@ -557,6 +561,53 @@
disable ? CHGR_ON_BAT_FORCE_BIT : 0, 1);
}
+#define COMPATATOR_OVERRIDE_0 0x80
+static int
+qpnp_chg_toggle_chg_done_logic(struct qpnp_chg_chip *chip, int enable)
+{
+ int rc;
+
+ pr_debug("toggle: %d\n", enable);
+
+ rc = qpnp_chg_masked_write(chip,
+ chip->buck_base + SEC_ACCESS, 0xA5, 0xA5, 1);
+ if (rc) {
+ pr_debug("failed to write sec access rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = qpnp_chg_masked_write(chip,
+ chip->buck_base + CHGR_BUCK_COMPARATOR_OVRIDE_1,
+ 0xC0, enable ? 0x00 : COMPATATOR_OVERRIDE_0, 1);
+ if (rc) {
+ pr_debug("failed to toggle chg done override rc=%d\n", rc);
+ return rc;
+ }
+
+ return rc;
+}
+
+#define QPNP_CHG_VBATDET_MIN_MV 3240
+#define QPNP_CHG_VBATDET_MAX_MV 5780
+#define QPNP_CHG_VBATDET_STEP_MV 20
+static int
+qpnp_chg_vbatdet_set(struct qpnp_chg_chip *chip, int vbatdet_mv)
+{
+ u8 temp;
+
+ if (vbatdet_mv < QPNP_CHG_VBATDET_MIN_MV
+ || vbatdet_mv > QPNP_CHG_VBATDET_MAX_MV) {
+ pr_err("bad mV=%d asked to set\n", vbatdet_mv);
+ return -EINVAL;
+ }
+ temp = (vbatdet_mv - QPNP_CHG_VBATDET_MIN_MV)
+ / QPNP_CHG_VBATDET_STEP_MV;
+
+ pr_debug("voltage=%d setting %02x\n", vbatdet_mv, temp);
+ return qpnp_chg_write(chip, &temp,
+ chip->chgr_base + CHGR_VBAT_DET, 1);
+}
+
static void
qpnp_arb_stop_work(struct work_struct *work)
{
@@ -564,7 +615,8 @@
struct qpnp_chg_chip *chip = container_of(dwork,
struct qpnp_chg_chip, arb_stop_work);
- qpnp_chg_charge_en(chip, !chip->charging_disabled);
+ if (!chip->chg_done)
+ qpnp_chg_charge_en(chip, !chip->charging_disabled);
qpnp_chg_force_run_on_batt(chip, chip->charging_disabled);
}
@@ -578,6 +630,36 @@
pr_err("request ADC error\n");
}
+#define EOC_CHECK_PERIOD_MS 10000
+static irqreturn_t
+qpnp_chg_vbatdet_lo_irq_handler(int irq, void *_chip)
+{
+ struct qpnp_chg_chip *chip = _chip;
+ u8 chg_sts = 0;
+ int rc;
+
+ pr_debug("vbatdet-lo triggered\n");
+
+ rc = qpnp_chg_read(chip, &chg_sts, INT_RT_STS(chip->chgr_base), 1);
+ if (rc)
+ pr_err("failed to read chg_sts rc=%d\n", rc);
+
+ pr_debug("chg_done chg_sts: 0x%x triggered\n", chg_sts);
+ if (!chip->charging_disabled && (chg_sts & FAST_CHG_ON_IRQ)) {
+ schedule_delayed_work(&chip->eoc_work,
+ msecs_to_jiffies(EOC_CHECK_PERIOD_MS));
+ wake_lock(&chip->eoc_wake_lock);
+ disable_irq_nosync(chip->chg_vbatdet_lo_irq);
+ } else {
+ qpnp_chg_charge_en(chip, !chip->charging_disabled);
+ }
+
+ power_supply_changed(chip->usb_psy);
+ power_supply_changed(&chip->dc_psy);
+ power_supply_changed(&chip->batt_psy);
+ return IRQ_HANDLED;
+}
+
#define ARB_STOP_WORK_MS 1000
static irqreturn_t
qpnp_chg_usb_chg_gone_irq_handler(int irq, void *_chip)
@@ -587,7 +669,7 @@
pr_debug("chg_gone triggered\n");
if (qpnp_chg_is_usb_chg_plugged_in(chip)) {
qpnp_chg_charge_en(chip, 0);
- qpnp_chg_force_run_on_batt(chip, chip->charging_disabled);
+ qpnp_chg_force_run_on_batt(chip, 1);
schedule_delayed_work(&chip->arb_stop_work,
msecs_to_jiffies(ARB_STOP_WORK_MS));
}
@@ -613,11 +695,15 @@
if (chip->usb_present ^ usb_present) {
chip->usb_present = usb_present;
- if (!usb_present)
+ if (!usb_present) {
qpnp_chg_usb_suspend_enable(chip, 1);
+ chip->chg_done = false;
+ } else {
+ schedule_delayed_work(&chip->eoc_work,
+ msecs_to_jiffies(EOC_CHECK_PERIOD_MS));
+ }
- power_supply_set_present(chip->usb_psy,
- chip->usb_present);
+ power_supply_set_present(chip->usb_psy, chip->usb_present);
}
return IRQ_HANDLED;
@@ -635,6 +721,7 @@
if (chip->batt_present ^ batt_present) {
chip->batt_present = batt_present;
power_supply_changed(&chip->batt_psy);
+ power_supply_changed(chip->usb_psy);
if (chip->cool_bat_decidegc && chip->warm_bat_decidegc
&& batt_present) {
@@ -642,9 +729,6 @@
}
}
- if (chip->bms_psy)
- power_supply_set_present(chip->bms_psy, batt_present);
-
return IRQ_HANDLED;
}
@@ -659,7 +743,13 @@
if (chip->dc_present ^ dc_present) {
chip->dc_present = dc_present;
+ if (!dc_present)
+ chip->chg_done = false;
+ else
+ schedule_delayed_work(&chip->eoc_work,
+ msecs_to_jiffies(EOC_CHECK_PERIOD_MS));
power_supply_changed(&chip->dc_psy);
+ power_supply_changed(&chip->batt_psy);
}
return IRQ_HANDLED;
@@ -672,6 +762,8 @@
struct qpnp_chg_chip *chip = _chip;
int rc;
+ pr_debug("chg_failed triggered\n");
+
rc = qpnp_chg_masked_write(chip,
chip->chgr_base + CHGR_CHG_FAILED,
CHGR_CHG_FAILED_BIT,
@@ -679,6 +771,9 @@
if (rc)
pr_err("Failed to write chg_fail clear bit!\n");
+ power_supply_changed(&chip->batt_psy);
+ power_supply_changed(chip->usb_psy);
+ power_supply_changed(&chip->dc_psy);
return IRQ_HANDLED;
}
@@ -701,9 +796,11 @@
struct qpnp_chg_chip *chip = _chip;
pr_debug("FAST_CHG IRQ triggered\n");
-
chip->chg_done = false;
power_supply_changed(&chip->batt_psy);
+ power_supply_changed(chip->usb_psy);
+ power_supply_changed(&chip->dc_psy);
+ enable_irq(chip->chg_vbatdet_lo_irq);
return IRQ_HANDLED;
}
@@ -818,7 +915,6 @@
POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
POWER_SUPPLY_PROP_CAPACITY,
- POWER_SUPPLY_PROP_CURRENT_MAX,
POWER_SUPPLY_PROP_CURRENT_NOW,
POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
POWER_SUPPLY_PROP_TEMP,
@@ -829,6 +925,10 @@
"battery",
};
+static char *pm_batt_supplied_to[] = {
+ "bms",
+};
+
#define USB_WALL_THRESHOLD_MA 500
static int
qpnp_power_get_property_mains(struct power_supply *psy,
@@ -941,11 +1041,12 @@
int rc;
u8 chgr_sts;
- if (chip->chg_done)
+ if ((qpnp_chg_is_usb_chg_plugged_in(chip) ||
+ qpnp_chg_is_dc_chg_plugged_in(chip)) && chip->chg_done) {
return POWER_SUPPLY_STATUS_FULL;
+ }
- rc = qpnp_chg_read(chip, &chgr_sts,
- INT_RT_STS(chip->chgr_base), 1);
+ rc = qpnp_chg_read(chip, &chgr_sts, INT_RT_STS(chip->chgr_base), 1);
if (rc) {
pr_err("failed to read interrupt sts %d\n", rc);
return POWER_SUPPLY_CHARGE_TYPE_NONE;
@@ -958,21 +1059,6 @@
return POWER_SUPPLY_STATUS_DISCHARGING;
}
-static int
-get_prop_current_max(struct qpnp_chg_chip *chip)
-{
- union power_supply_propval ret = {0,};
-
- if (chip->bms_psy) {
- chip->bms_psy->get_property(chip->bms_psy,
- POWER_SUPPLY_PROP_CURRENT_MAX, &ret);
- return ret.intval;
- } else {
- pr_debug("No BMS supply registered return 0\n");
- }
-
- return 0;
-}
static int
get_prop_current_now(struct qpnp_chg_chip *chip)
@@ -1151,9 +1237,6 @@
case POWER_SUPPLY_PROP_CAPACITY:
val->intval = get_prop_capacity(chip);
break;
- case POWER_SUPPLY_PROP_CURRENT_MAX:
- val->intval = get_prop_current_max(chip);
- break;
case POWER_SUPPLY_PROP_CURRENT_NOW:
val->intval = get_prop_current_now(chip);
break;
@@ -1282,27 +1365,7 @@
temp = (minutes - 1)/QPNP_CHG_TCHG_STEP;
return qpnp_chg_masked_write(chip, chip->chgr_base + CHGR_TCHG_MAX,
- QPNP_CHG_I_MASK, temp, 1);
-}
-#define QPNP_CHG_VBATDET_MIN_MV 3240
-#define QPNP_CHG_VBATDET_MAX_MV 5780
-#define QPNP_CHG_VBATDET_STEP_MV 20
-static int
-qpnp_chg_vbatdet_set(struct qpnp_chg_chip *chip, int vbatdet_mv)
-{
- u8 temp;
-
- if (vbatdet_mv < QPNP_CHG_VBATDET_MIN_MV
- || vbatdet_mv > QPNP_CHG_VBATDET_MAX_MV) {
- pr_err("bad mV=%d asked to set\n", vbatdet_mv);
- return -EINVAL;
- }
- temp = (vbatdet_mv - QPNP_CHG_VBATDET_MIN_MV)
- / QPNP_CHG_VBATDET_STEP_MV;
-
- pr_debug("voltage=%d setting %02x\n", vbatdet_mv, temp);
- return qpnp_chg_write(chip, &temp,
- chip->chgr_base + CHGR_VBAT_DET, 1);
+ QPNP_CHG_TCHG_MASK, temp, 1);
}
#define QPNP_CHG_V_MIN_MV 3240
@@ -1404,6 +1467,88 @@
}
}
+#define CONSECUTIVE_COUNT 3
+static void
+qpnp_eoc_work(struct work_struct *work)
+{
+ struct delayed_work *dwork = to_delayed_work(work);
+ struct qpnp_chg_chip *chip = container_of(dwork,
+ struct qpnp_chg_chip, eoc_work);
+ static int count;
+ int ibat_ma, vbat_mv, rc = 0;
+ u8 batt_sts = 0, buck_sts = 0, chg_sts = 0;
+
+ wake_lock(&chip->eoc_wake_lock);
+ qpnp_chg_charge_en(chip, !chip->charging_disabled);
+
+ rc = qpnp_chg_read(chip, &batt_sts, INT_RT_STS(chip->bat_if_base), 1);
+ if (rc) {
+ pr_err("failed to read batt_if rc=%d\n", rc);
+ return;
+ }
+
+ rc = qpnp_chg_read(chip, &buck_sts, INT_RT_STS(chip->buck_base), 1);
+ if (rc) {
+ pr_err("failed to read buck rc=%d\n", rc);
+ return;
+ }
+
+ rc = qpnp_chg_read(chip, &chg_sts, INT_RT_STS(chip->chgr_base), 1);
+ if (rc) {
+ pr_err("failed to read chg_sts rc=%d\n", rc);
+ return;
+ }
+
+ pr_debug("chgr: 0x%x, bat_if: 0x%x, buck: 0x%x\n",
+ chg_sts, batt_sts, buck_sts);
+
+ if (!qpnp_chg_is_usb_chg_plugged_in(chip) &&
+ !qpnp_chg_is_dc_chg_plugged_in(chip)) {
+ pr_debug("no chg connected, stopping\n");
+ goto stop_eoc;
+ }
+
+ if ((batt_sts & BAT_FET_ON_IRQ) && (chg_sts & FAST_CHG_ON_IRQ
+ || chg_sts & TRKL_CHG_ON_IRQ)) {
+ ibat_ma = get_prop_current_now(chip) / 1000;
+ vbat_mv = get_prop_battery_voltage_now(chip) / 1000;
+ pr_debug("ibat_ma: %d term_current =%d\n",
+ ibat_ma, chip->term_current);
+ if (ibat_ma > chip->term_current) {
+ pr_debug("charging but increase in current demand\n");
+ count = 0;
+ } else if ((ibat_ma * -1) < chip->term_current) {
+ if (count == CONSECUTIVE_COUNT) {
+ pr_info("End of Charging\n");
+ qpnp_chg_charge_en(chip, 0);
+ chip->chg_done = true;
+ power_supply_changed(&chip->batt_psy);
+ enable_irq(chip->chg_vbatdet_lo_irq);
+ goto stop_eoc;
+ } else {
+ count += 1;
+ pr_debug("EOC count = %d\n", count);
+ }
+ } else if ((!(chg_sts & VBAT_DET_LOW_IRQ)) && (vbat_mv <
+ (chip->max_voltage_mv - chip->resume_delta_mv))) {
+ pr_debug("woke up too early\n");
+ enable_irq(chip->chg_vbatdet_lo_irq);
+ goto stop_eoc;
+ }
+ } else {
+ pr_debug("not charging\n");
+ goto stop_eoc;
+ }
+
+ schedule_delayed_work(&chip->eoc_work,
+ msecs_to_jiffies(EOC_CHECK_PERIOD_MS));
+ return;
+
+stop_eoc:
+ count = 0;
+ wake_unlock(&chip->eoc_wake_lock);
+}
+
#define HYSTERISIS_DECIDEGC 20
static void
qpnp_chg_adc_notification(enum qpnp_tm_state state, void *ctx)
@@ -1565,6 +1710,13 @@
return rc;
}
+ chip->chg_vbatdet_lo_irq = spmi_get_irq_byname(spmi,
+ spmi_resource, "vbat-det-lo");
+ if (chip->chg_vbatdet_lo_irq < 0) {
+ pr_err("Unable to get fast-chg-on irq\n");
+ return rc;
+ }
+
rc |= devm_request_irq(chip->dev, chip->chg_failed_irq,
qpnp_chg_chgr_chg_failed_irq_handler,
IRQF_TRIGGER_RISING, "chg-failed", chip);
@@ -1593,9 +1745,23 @@
chip->chg_trklchg_irq, rc);
return rc;
}
+
+ rc |= devm_request_irq(chip->dev,
+ chip->chg_vbatdet_lo_irq,
+ qpnp_chg_vbatdet_lo_irq_handler,
+ IRQF_TRIGGER_RISING,
+ "vbat-det-lo", chip);
+ if (rc < 0) {
+ pr_err("Can't request %d vbat-det-lo: %d\n",
+ chip->chg_vbatdet_lo_irq, rc);
+ return rc;
+ }
+
enable_irq_wake(chip->chg_fastchg_irq);
enable_irq_wake(chip->chg_trklchg_irq);
enable_irq_wake(chip->chg_failed_irq);
+ disable_irq_nosync(chip->chg_vbatdet_lo_irq);
+ enable_irq_wake(chip->chg_vbatdet_lo_irq);
break;
case SMBB_BAT_IF_SUBTYPE:
@@ -1744,12 +1910,16 @@
/* HACK: use analog EOC */
rc = qpnp_chg_masked_write(chip, chip->chgr_base +
CHGR_IBAT_TERM_CHGR,
- 0x80, 0x00, 1);
+ 0xFF, 0x08, 1);
break;
case SMBB_BUCK_SUBTYPE:
case SMBBP_BUCK_SUBTYPE:
case SMBCL_BUCK_SUBTYPE:
+ rc = qpnp_chg_toggle_chg_done_logic(chip, 0);
+ if (rc)
+ return rc;
+
rc = qpnp_chg_masked_write(chip,
chip->chgr_base + CHGR_BUCK_BCK_VBAT_REG_MODE,
BUCK_VBAT_REG_NODE_SEL_BIT,
@@ -1775,8 +1945,7 @@
case SMBB_USB_CHGPTH_SUBTYPE:
case SMBBP_USB_CHGPTH_SUBTYPE:
case SMBCL_USB_CHGPTH_SUBTYPE:
- chip->usb_present = qpnp_chg_is_usb_chg_plugged_in(chip);
- if (chip->usb_present) {
+ if (qpnp_chg_is_usb_chg_plugged_in(chip)) {
rc = qpnp_chg_masked_write(chip,
chip->usb_chgpth_base + CHGR_USB_ENUM_T_STOP,
ENUM_T_STOP_BIT,
@@ -1814,11 +1983,15 @@
case SMBBP_BOOST_SUBTYPE:
break;
case SMBB_MISC_SUBTYPE:
- chip->type = SMBB;
case SMBBP_MISC_SUBTYPE:
- chip->type = SMBBP;
case SMBCL_MISC_SUBTYPE:
- chip->type = SMBCL;
+ if (subtype == SMBB_MISC_SUBTYPE)
+ chip->type = SMBB;
+ else if (subtype == SMBBP_MISC_SUBTYPE)
+ chip->type = SMBBP;
+ else if (subtype == SMBCL_MISC_SUBTYPE)
+ chip->type = SMBCL;
+
pr_debug("Setting BOOT_DONE\n");
rc = qpnp_chg_masked_write(chip,
chip->misc_base + CHGR_MISC_BOOT_DONE,
@@ -1939,7 +2112,6 @@
struct qpnp_chg_chip *chip;
struct resource *resource;
struct spmi_resource *spmi_resource;
- bool present;
int rc = 0;
chip = kzalloc(sizeof *chip, GFP_KERNEL);
@@ -2096,14 +2268,6 @@
if (rc)
goto fail_chg_enable;
- /* if bms exists, notify it of the presence of the battery */
- if (!chip->bms_psy)
- chip->bms_psy = power_supply_get_by_name("bms");
- if (chip->bms_psy) {
- present = get_prop_batt_present(chip);
- power_supply_set_present(chip->bms_psy, present);
- }
-
chip->batt_psy.name = "battery";
chip->batt_psy.type = POWER_SUPPLY_TYPE_BATTERY;
chip->batt_psy.properties = msm_batt_power_props;
@@ -2115,6 +2279,9 @@
qpnp_batt_property_is_writeable;
chip->batt_psy.external_power_changed =
qpnp_batt_external_power_changed;
+ chip->batt_psy.supplied_to = pm_batt_supplied_to;
+ chip->batt_psy.num_supplicants =
+ ARRAY_SIZE(pm_batt_supplied_to);
rc = power_supply_register(chip->dev, &chip->batt_psy);
if (rc < 0) {
@@ -2125,6 +2292,9 @@
qpnp_bat_if_adc_measure_work);
}
+ wake_lock_init(&chip->eoc_wake_lock,
+ WAKE_LOCK_SUSPEND, "qpnp-chg-eoc-lock");
+ INIT_DELAYED_WORK(&chip->eoc_work, qpnp_eoc_work);
INIT_DELAYED_WORK(&chip->arb_stop_work, qpnp_arb_stop_work);
if (chip->dc_chgpth_base) {
@@ -2146,9 +2316,6 @@
/* Turn on appropriate workaround flags */
qpnp_chg_setup_flags(chip);
- power_supply_set_present(chip->usb_psy,
- qpnp_chg_is_usb_chg_plugged_in(chip));
-
if (chip->maxinput_dc_ma && chip->dc_chgpth_base) {
rc = qpnp_chg_idcmax_set(chip, chip->maxinput_dc_ma);
if (rc) {
@@ -2185,6 +2352,15 @@
goto unregister_batt;
}
+ qpnp_chg_usb_usbin_valid_irq_handler(USBIN_VALID_IRQ, chip);
+ power_supply_set_present(chip->usb_psy,
+ qpnp_chg_is_usb_chg_plugged_in(chip));
+
+ /* Set USB psy online to avoid userspace from shutting down if battery
+ * capacity is at zero and no chargers online. */
+ if (qpnp_chg_is_usb_chg_plugged_in(chip))
+ power_supply_set_online(chip->usb_psy, 1);
+
pr_info("success chg_dis = %d, usb = %d, dc = %d b_health = %d batt_present = %d\n",
chip->charging_disabled,
qpnp_chg_is_usb_chg_plugged_in(chip),
@@ -2212,6 +2388,7 @@
qpnp_adc_tm_disable_chan_meas(&chip->adc_param);
}
cancel_work_sync(&chip->adc_measure_work);
+ cancel_delayed_work_sync(&chip->eoc_work);
dev_set_drvdata(&spmi->dev, NULL);
kfree(chip);
diff --git a/drivers/regulator/qpnp-regulator.c b/drivers/regulator/qpnp-regulator.c
index 2d10f89..c9d0500 100644
--- a/drivers/regulator/qpnp-regulator.c
+++ b/drivers/regulator/qpnp-regulator.c
@@ -514,8 +514,10 @@
{
struct qpnp_regulator *vreg = rdev_get_drvdata(rdev);
- if (vreg->ocp_irq)
+ if (vreg->ocp_irq) {
+ vreg->ocp_count = 0;
vreg->vs_enable_time = ktime_get();
+ }
return qpnp_regulator_common_enable(rdev);
}
diff --git a/drivers/rtc/qpnp-rtc.c b/drivers/rtc/qpnp-rtc.c
index 6d8985e..e0bffb9 100644
--- a/drivers/rtc/qpnp-rtc.c
+++ b/drivers/rtc/qpnp-rtc.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-13, 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
@@ -503,12 +503,17 @@
}
}
- rtc_dd->rtc_ctrl_reg = BIT_RTC_ENABLE;
- rc = qpnp_write_wrapper(rtc_dd, &rtc_dd->rtc_ctrl_reg,
+ rc = qpnp_read_wrapper(rtc_dd, &rtc_dd->rtc_ctrl_reg,
rtc_dd->rtc_base + REG_OFFSET_RTC_CTRL, 1);
if (rc) {
dev_err(&spmi->dev,
- "Write to RTC control reg failed\n");
+ "Read from RTC control reg failed\n");
+ goto fail_rtc_enable;
+ }
+
+ if (!(rtc_dd->rtc_ctrl_reg & BIT_RTC_ENABLE)) {
+ dev_err(&spmi->dev,
+ "RTC h/w disabled, rtc not registered\n");
goto fail_rtc_enable;
}
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index e12b9b4..c8fdc6b 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -1384,17 +1384,17 @@
* The result is saved with the response so that
* the ufs_core layer will handle it.
*/
- result |= DID_OK << 16;
+ result = DID_OK << 16;
ufshcd_copy_query_response(hba, lrbp);
break;
case UPIU_TRANSACTION_REJECT_UPIU:
/* TODO: handle Reject UPIU Response */
- result |= DID_ERROR << 16;
+ result = DID_ERROR << 16;
dev_err(hba->dev,
"Reject UPIU not fully implemented\n");
break;
default:
- result |= DID_ERROR << 16;
+ result = DID_ERROR << 16;
dev_err(hba->dev,
"Unexpected request response code = %x\n",
result);
diff --git a/drivers/slimbus/slim-msm-ngd.c b/drivers/slimbus/slim-msm-ngd.c
index a0179cb..86ae8db 100644
--- a/drivers/slimbus/slim-msm-ngd.c
+++ b/drivers/slimbus/slim-msm-ngd.c
@@ -471,6 +471,8 @@
DECLARE_COMPLETION_ONSTACK(done);
u8 wbuf[SLIM_RX_MSGQ_BUF_LEN];
+ *clkgear = ctrl->clkgear;
+ *subfrmc = 0;
txn.mt = SLIM_MSG_MT_DEST_REFERRED_USER;
txn.dt = SLIM_MSG_DEST_LOGICALADDR;
txn.la = SLIM_LA_MGR;
@@ -479,6 +481,34 @@
txn.wbuf = wbuf;
txn.rbuf = NULL;
+ if (ctrl->sched.msgsl != ctrl->sched.pending_msgsl) {
+ pr_debug("slim reserve BW for messaging: req: %d",
+ ctrl->sched.pending_msgsl);
+ txn.mc = SLIM_USR_MC_REQ_BW;
+ wbuf[txn.len++] = ((sb->laddr & 0x1f) |
+ ((u8)(ctrl->sched.pending_msgsl & 0x7) << 5));
+ wbuf[txn.len++] = (u8)(ctrl->sched.pending_msgsl >> 3);
+ ret = ngd_get_tid(ctrl, &txn, &wbuf[txn.len++], &done);
+ if (ret)
+ return ret;
+ 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_define, pending) {
struct slim_ich *slc;
slc = &ctrl->chans[pch->chan];
@@ -1039,8 +1069,6 @@
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);
diff --git a/drivers/spi/spi_qsd.c b/drivers/spi/spi_qsd.c
index b89f608..c5aa7e5 100644
--- a/drivers/spi/spi_qsd.c
+++ b/drivers/spi/spi_qsd.c
@@ -2280,7 +2280,7 @@
bam_props.phys_addr = dd->bam.phys_addr;
bam_props.virt_addr = dd->bam.base;
bam_props.irq = dd->bam.irq;
- bam_props.manage = SPS_BAM_MGR_LOCAL;
+ bam_props.manage = SPS_BAM_MGR_DEVICE_REMOTE;
bam_props.summing_threshold = 0x10;
rc = sps_register_bam_device(&bam_props, &bam_handle);
diff --git a/drivers/thermal/msm8974-tsens.c b/drivers/thermal/msm8974-tsens.c
index 991cf2e..f01a078 100644
--- a/drivers/thermal/msm8974-tsens.c
+++ b/drivers/thermal/msm8974-tsens.c
@@ -199,8 +199,8 @@
#define TSENS0_8X10_POINT1_SHIFT 16
#define TSENS0_8X10_POINT2_SHIFT 22
#define TSENS1_8X10_POINT2_SHIFT 6
-#define TSENS_8X10_BASE0_MASK 0xf
-#define TSENS_8X10_BASE1_MASK 0xf0
+#define TSENS_8X10_BASE0_MASK 0xff
+#define TSENS_8X10_BASE1_MASK 0xff00
#define TSENS0_8X10_POINT1_MASK 0x3f0000
#define TSENS0_8X10_POINT2_MASK 0xfc00000
#define TSENS_8X10_TSENS_CAL_SEL 0x70000000
diff --git a/drivers/thermal/qpnp-adc-tm.c b/drivers/thermal/qpnp-adc-tm.c
index 26a12d0..f3b29c9 100644
--- a/drivers/thermal/qpnp-adc-tm.c
+++ b/drivers/thermal/qpnp-adc-tm.c
@@ -91,8 +91,8 @@
#define QPNP_M2_ADC_CH_SEL_CTL 0x70
#define QPNP_M2_LOW_THR_LSB 0x71
#define QPNP_M2_LOW_THR_MSB 0x72
-#define QPNP_M2_HIGH_THR_LSB 0x7b
-#define QPNP_M2_HIGH_THR_MSB 0x7c
+#define QPNP_M2_HIGH_THR_LSB 0x73
+#define QPNP_M2_HIGH_THR_MSB 0x74
#define QPNP_M3_ADC_CH_SEL_CTL 0x78
#define QPNP_M3_LOW_THR_LSB 0x79
#define QPNP_M3_LOW_THR_MSB 0x7a
diff --git a/drivers/tty/serial/msm_serial_hs.c b/drivers/tty/serial/msm_serial_hs.c
index f695870..890a897 100644
--- a/drivers/tty/serial/msm_serial_hs.c
+++ b/drivers/tty/serial/msm_serial_hs.c
@@ -2739,7 +2739,7 @@
/* SPS driver wll handle the UART BAM IRQ */
bam.irq = (u32)msm_uport->bam_irq;
- bam.manage = SPS_BAM_MGR_LOCAL;
+ bam.manage = SPS_BAM_MGR_DEVICE_REMOTE;
pr_debug("msm_serial_hs: bam physical base=0x%x\n",
(u32)bam.phys_addr);
diff --git a/drivers/usb/gadget/ci13xxx_udc.c b/drivers/usb/gadget/ci13xxx_udc.c
index 01d8be1..38f08fc 100644
--- a/drivers/usb/gadget/ci13xxx_udc.c
+++ b/drivers/usb/gadget/ci13xxx_udc.c
@@ -338,14 +338,17 @@
*/
static int hw_device_reset(struct ci13xxx *udc)
{
+ int delay_count = 25; /* 250 usec */
+
/* should flush & stop before reset */
hw_cwrite(CAP_ENDPTFLUSH, ~0, ~0);
hw_cwrite(CAP_USBCMD, USBCMD_RS, 0);
hw_cwrite(CAP_USBCMD, USBCMD_RST, USBCMD_RST);
- while (hw_cread(CAP_USBCMD, USBCMD_RST))
- udelay(10); /* not RTOS friendly */
-
+ while (delay_count-- && hw_cread(CAP_USBCMD, USBCMD_RST))
+ udelay(10);
+ if (delay_count < 0)
+ pr_err("USB controller reset failed\n");
if (udc->udc_driver->notify_event)
udc->udc_driver->notify_event(udc,
@@ -814,6 +817,8 @@
*/
static int hw_usb_reset(void)
{
+ int delay_count = 10; /* 100 usec delay */
+
hw_usb_set_address(0);
/* ESS flushes only at end?!? */
@@ -826,8 +831,10 @@
hw_cwrite(CAP_ENDPTCOMPLETE, 0, 0); /* writes its content */
/* wait until all bits cleared */
- while (hw_cread(CAP_ENDPTPRIME, ~0))
- udelay(10); /* not RTOS friendly */
+ while (delay_count-- && hw_cread(CAP_ENDPTPRIME, ~0))
+ udelay(10);
+ if (delay_count < 0)
+ pr_err("ENDPTPRIME is not cleared during bus reset\n");
/* reset all endpoints ? */
diff --git a/drivers/usb/gadget/f_qc_ecm.c b/drivers/usb/gadget/f_qc_ecm.c
index a395d15..5e68296 100644
--- a/drivers/usb/gadget/f_qc_ecm.c
+++ b/drivers/usb/gadget/f_qc_ecm.c
@@ -599,7 +599,7 @@
DBG(cdev, "activate ecm\n");
if (ecm->xport != USB_GADGET_XPORT_BAM2BAM_IPA) {
net = gether_qc_connect_name(&ecm->port,
- "ecm0");
+ "ecm0", true);
if (IS_ERR(net))
return PTR_ERR(net);
}
diff --git a/drivers/usb/gadget/f_qc_rndis.c b/drivers/usb/gadget/f_qc_rndis.c
index 8b01176..baea664 100644
--- a/drivers/usb/gadget/f_qc_rndis.c
+++ b/drivers/usb/gadget/f_qc_rndis.c
@@ -725,7 +725,7 @@
rndis->port.cdc_filter = 0;
DBG(cdev, "RNDIS RX/TX early activation ...\n");
- net = gether_qc_connect_name(&rndis->port, "rndis0");
+ net = gether_qc_connect_name(&rndis->port, "rndis0", false);
if (IS_ERR(net))
return PTR_ERR(net);
diff --git a/drivers/usb/gadget/u_qc_ether.c b/drivers/usb/gadget/u_qc_ether.c
index e10ec25..044da47 100644
--- a/drivers/usb/gadget/u_qc_ether.c
+++ b/drivers/usb/gadget/u_qc_ether.c
@@ -354,12 +354,13 @@
* current device speed, and any framing wrapper(s) set up.
* @netname: name for network device (for example, "usb")
* Context: irqs blocked
+ * @netif_enable: if true, net interface will be turned on
*
* This is called to let the network layer know the connection
* is active ("carrier detect").
*/
struct net_device *gether_qc_connect_name(struct qc_gether *link,
- const char *netname)
+ const char *netname, bool netif_enable)
{
struct net_device *net_dev;
struct eth_qc_dev *dev;
@@ -390,9 +391,11 @@
}
spin_unlock(&dev->lock);
- netif_carrier_on(dev->net);
- if (netif_running(dev->net))
- netif_wake_queue(dev->net);
+ if (netif_enable) {
+ netif_carrier_on(dev->net);
+ if (netif_running(dev->net))
+ netif_wake_queue(dev->net);
+ }
return dev->net;
}
diff --git a/drivers/usb/gadget/u_qc_ether.h b/drivers/usb/gadget/u_qc_ether.h
index 25562da..5d9f738 100644
--- a/drivers/usb/gadget/u_qc_ether.h
+++ b/drivers/usb/gadget/u_qc_ether.h
@@ -82,7 +82,7 @@
/* connect/disconnect is handled by individual functions */
struct net_device *gether_qc_connect_name(struct qc_gether *link,
- const char *netname);
+ const char *netname, bool netif_enable);
void gether_qc_disconnect_name(struct qc_gether *link, const char *netname);
/* each configuration may bind one instance of an ethernet link */
diff --git a/drivers/video/msm/mdss/mdp3.c b/drivers/video/msm/mdss/mdp3.c
index f672bd4..290e317 100644
--- a/drivers/video/msm/mdss/mdp3.c
+++ b/drivers/video/msm/mdss/mdp3.c
@@ -32,6 +32,8 @@
#include <linux/spinlock.h>
#include <linux/semaphore.h>
#include <linux/uaccess.h>
+#include <linux/file.h>
+#include <linux/msm_kgsl.h>
#include <mach/board.h>
#include <mach/clk.h>
@@ -125,7 +127,7 @@
pr_debug("mdp3_irq_handler irq=%d\n", mdp_interrupt);
spin_lock(&mdata->irq_lock);
- mdp_interrupt &= mdata->irqMask;
+ mdp_interrupt &= mdata->irq_mask;
while (mdp_interrupt && i < MDP3_MAX_INTR) {
if ((mdp_interrupt & 0x1) && mdata->callbacks[i].cb)
@@ -145,14 +147,15 @@
pr_debug("mdp3_irq_enable type=%d\n", type);
spin_lock_irqsave(&mdp3_res->irq_lock, flag);
- if (mdp3_res->irqMask & BIT(type)) {
+ mdp3_res->irq_ref_count[type] += 1;
+ if (mdp3_res->irq_ref_count[type] > 1) {
pr_debug("interrupt %d already enabled\n", type);
spin_unlock_irqrestore(&mdp3_res->irq_lock, flag);
return;
}
- irqEnabled = mdp3_res->irqMask;
- mdp3_res->irqMask |= BIT(type);
- MDP3_REG_WRITE(MDP3_REG_INTR_ENABLE, mdp3_res->irqMask);
+ irqEnabled = mdp3_res->irq_mask;
+ mdp3_res->irq_mask |= BIT(type);
+ MDP3_REG_WRITE(MDP3_REG_INTR_ENABLE, mdp3_res->irq_mask);
if (!irqEnabled)
enable_irq(mdp3_res->irq);
spin_unlock_irqrestore(&mdp3_res->irq_lock, flag);
@@ -163,26 +166,33 @@
unsigned long flag;
spin_lock_irqsave(&mdp3_res->irq_lock, flag);
- if (mdp3_res->irqMask & BIT(type)) {
- mdp3_res->irqMask &= ~BIT(type);
- MDP3_REG_WRITE(MDP3_REG_INTR_ENABLE, mdp3_res->irqMask);
- if (!mdp3_res->irqMask)
- disable_irq(mdp3_res->irq);
- } else {
+ if (mdp3_res->irq_ref_count[type] <= 0) {
pr_debug("interrupt %d not enabled\n", type);
+ spin_unlock_irqrestore(&mdp3_res->irq_lock, flag);
+ return;
+ }
+ mdp3_res->irq_ref_count[type] -= 1;
+ if (mdp3_res->irq_ref_count[type] == 0) {
+ mdp3_res->irq_mask &= ~BIT(type);
+ MDP3_REG_WRITE(MDP3_REG_INTR_ENABLE, mdp3_res->irq_mask);
+ if (!mdp3_res->irq_mask)
+ disable_irq(mdp3_res->irq);
}
spin_unlock_irqrestore(&mdp3_res->irq_lock, flag);
}
void mdp3_irq_disable_nosync(int type)
{
- if (mdp3_res->irqMask & BIT(type)) {
- mdp3_res->irqMask &= ~BIT(type);
- MDP3_REG_WRITE(MDP3_REG_INTR_ENABLE, mdp3_res->irqMask);
- if (!mdp3_res->irqMask)
- disable_irq_nosync(mdp3_res->irq);
- } else {
+ if (mdp3_res->irq_ref_count[type] <= 0) {
pr_debug("interrupt %d not enabled\n", type);
+ return;
+ }
+ mdp3_res->irq_ref_count[type] -= 1;
+ if (mdp3_res->irq_ref_count[type] == 0) {
+ mdp3_res->irq_mask &= ~BIT(type);
+ MDP3_REG_WRITE(MDP3_REG_INTR_ENABLE, mdp3_res->irq_mask);
+ if (!mdp3_res->irq_mask)
+ disable_irq_nosync(mdp3_res->irq);
}
}
@@ -688,6 +698,108 @@
return 0;
}
+int mdp3_put_img(struct mdp3_img_data *data)
+{
+ struct ion_client *iclient = mdp3_res->ion_client;
+ int dom = (mdp3_res->domains + MDP3_IOMMU_DOMAIN)->domain_idx;
+
+ if (!data->srcp_file) {
+ pr_debug("No img to put\n");
+ return 0;
+ }
+ if (data->flags & MDP_BLIT_SRC_GEM) {
+ pr_debug("memory source MDP_BLIT_SRC_GEM\n");
+ } else if (data->flags & MDP_MEMORY_ID_TYPE_FB) {
+ pr_debug("fb mem buf=0x%x\n", data->addr);
+ fput_light(data->srcp_file, data->p_need);
+ data->srcp_file = NULL;
+ } else {
+ ion_unmap_iommu(iclient, data->srcp_ihdl, dom, 0);
+ ion_free(iclient, data->srcp_ihdl);
+ data->srcp_ihdl = NULL;
+ }
+ return 0;
+}
+
+int mdp3_get_img(struct msmfb_data *img, struct mdp3_img_data *data)
+{
+ struct file *file;
+ int ret = -EINVAL;
+ int fb_num;
+ unsigned long *start, *len;
+ struct ion_client *iclient = mdp3_res->ion_client;
+ int dom = (mdp3_res->domains + MDP3_IOMMU_DOMAIN)->domain_idx;
+
+ start = (unsigned long *) &data->addr;
+ len = (unsigned long *) &data->len;
+ data->flags |= img->flags;
+ data->p_need = 0;
+
+ if (img->flags & MDP_BLIT_SRC_GEM) {
+ data->srcp_file = NULL;
+ ret = kgsl_gem_obj_addr(img->memory_id, (int) img->priv,
+ &data->addr, &data->len);
+ if (!ret)
+ goto done;
+ }
+ if (img->flags & MDP_MEMORY_ID_TYPE_FB) {
+ file = fget_light(img->memory_id, &data->p_need);
+ if (file == NULL) {
+ pr_err("invalid framebuffer file (%d)\n",
+ img->memory_id);
+ return -EINVAL;
+ }
+ if (MAJOR(file->f_dentry->d_inode->i_rdev) == FB_MAJOR) {
+ fb_num = MINOR(file->f_dentry->d_inode->i_rdev);
+ ret = mdss_fb_get_phys_info(start, len, fb_num);
+ if (ret) {
+ pr_err("mdss_fb_get_phys_info() failed\n");
+ fput_light(file, data->p_need);
+ file = NULL;
+ }
+ } else {
+ pr_err("invalid FB_MAJOR\n");
+ fput_light(file, data->p_need);
+ file = NULL;
+ ret = -EINVAL;
+ }
+ data->srcp_file = file;
+ if (!ret)
+ goto done;
+ }
+ if (iclient) {
+ data->srcp_ihdl = ion_import_dma_buf(iclient, img->memory_id);
+ if (IS_ERR_OR_NULL(data->srcp_ihdl)) {
+ pr_err("error on ion_import_fd\n");
+ ret = PTR_ERR(data->srcp_ihdl);
+ data->srcp_ihdl = NULL;
+ return ret;
+ }
+
+ ret = ion_map_iommu(iclient, data->srcp_ihdl, dom,
+ 0, SZ_4K, 0, start, len, 0, 0);
+
+ if (IS_ERR_VALUE(ret)) {
+ ion_free(iclient, data->srcp_ihdl);
+ pr_err("failed to map ion handle (%d)\n", ret);
+ return ret;
+ }
+ }
+done:
+ if (!ret && (img->offset < data->len)) {
+ data->addr += img->offset;
+ data->len -= img->offset;
+
+ pr_debug("mem=%d ihdl=%p buf=0x%x len=0x%x\n", img->memory_id,
+ data->srcp_ihdl, data->addr, data->len);
+ } else {
+ mdp3_put_img(data);
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
static int mdp3_init(struct msm_fb_data_type *mfd)
{
return mdp3_ctrl_init(mfd);
diff --git a/drivers/video/msm/mdss/mdp3.h b/drivers/video/msm/mdss/mdp3.h
index 5774e5a..7e395e1 100644
--- a/drivers/video/msm/mdss/mdp3.h
+++ b/drivers/video/msm/mdss/mdp3.h
@@ -23,6 +23,7 @@
#include <mach/iommu_domains.h>
#include "mdp3_dma.h"
+#include "mdss_fb.h"
enum {
MDP3_CLK_AHB,
@@ -100,12 +101,22 @@
struct mdp3_intf intf[MDP3_DMA_OUTPUT_SEL_MAX];
spinlock_t irq_lock;
- u32 irqMask;
+ u32 irq_ref_count[MDP3_MAX_INTR];
+ u32 irq_mask;
struct mdp3_intr_cb callbacks[MDP3_MAX_INTR];
struct early_suspend suspend_handler;
};
+struct mdp3_img_data {
+ u32 addr;
+ u32 len;
+ u32 flags;
+ int p_need;
+ struct file *srcp_file;
+ struct ion_handle *srcp_ihdl;
+};
+
extern struct mdp3_hw_resource *mdp3_res;
struct mdp3_dma *mdp3_get_dma_pipe(int capability);
@@ -117,6 +128,8 @@
int mdp3_clk_set_rate(int clk_type, unsigned long clk_rate);
int mdp3_clk_enable(int enable);
int mdp3_bus_scale_set_quota(int client, u64 ab_quota, u64 ib_quota);
+int mdp3_put_img(struct mdp3_img_data *data);
+int mdp3_get_img(struct msmfb_data *img, struct mdp3_img_data *data);
#define MDP3_REG_WRITE(addr, val) writel_relaxed(val, mdp3_res->mdp_base + addr)
#define MDP3_REG_READ(addr) readl_relaxed(mdp3_res->mdp_base + addr)
diff --git a/drivers/video/msm/mdss/mdp3_ctrl.c b/drivers/video/msm/mdss/mdp3_ctrl.c
index 929e5f8..f5ac5e9 100644
--- a/drivers/video/msm/mdss/mdp3_ctrl.c
+++ b/drivers/video/msm/mdss/mdp3_ctrl.c
@@ -26,37 +26,108 @@
#define MDP_VSYNC_CLK_RATE 19200000
#define VSYNC_PERIOD 16
+static void mdp3_ctrl_pan_display(struct msm_fb_data_type *mfd);
+
+static void mdp3_bufq_init(struct mdp3_buffer_queue *bufq)
+{
+ bufq->count = 0;
+ bufq->push_idx = 0;
+ bufq->pop_idx = 0;
+}
+
+static void mdp3_bufq_deinit(struct mdp3_buffer_queue *bufq)
+{
+ int count = bufq->count;
+
+ if (!count)
+ return;
+
+ while (count--) {
+ struct mdp3_img_data *data = &bufq->img_data[bufq->pop_idx];
+ bufq->pop_idx = (bufq->pop_idx + 1) % MDP3_MAX_BUF_QUEUE;
+ mdp3_put_img(data);
+ }
+ bufq->count = 0;
+ bufq->push_idx = 0;
+ bufq->pop_idx = 0;
+}
+
+static int mdp3_bufq_push(struct mdp3_buffer_queue *bufq,
+ struct mdp3_img_data *data)
+{
+ if (bufq->count >= MDP3_MAX_BUF_QUEUE) {
+ pr_err("bufq full\n");
+ return -EPERM;
+ }
+
+ bufq->img_data[bufq->push_idx] = *data;
+ bufq->push_idx = (bufq->push_idx + 1) % MDP3_MAX_BUF_QUEUE;
+ bufq->count++;
+ return 0;
+}
+
+static struct mdp3_img_data *mdp3_bufq_pop(struct mdp3_buffer_queue *bufq)
+{
+ struct mdp3_img_data *data;
+ if (bufq->count == 0)
+ return NULL;
+
+ data = &bufq->img_data[bufq->pop_idx];
+ bufq->count--;
+ bufq->pop_idx = (bufq->pop_idx + 1) % MDP3_MAX_BUF_QUEUE;
+ return data;
+}
+
+static int mdp3_bufq_count(struct mdp3_buffer_queue *bufq)
+{
+ return bufq->count;
+}
+
void vsync_notify_handler(void *arg)
{
struct mdp3_session_data *session = (struct mdp3_session_data *)arg;
+ spin_lock(&session->vsync_lock);
+ session->vsync_time = ktime_get();
complete(&session->vsync_comp);
+ spin_unlock(&session->vsync_lock);
}
static int mdp3_ctrl_vsync_enable(struct msm_fb_data_type *mfd, int enable)
{
struct mdp3_session_data *mdp3_session;
struct mdp3_vsync_notification vsync_client;
+ struct mdp3_vsync_notification *arg = NULL;
+ unsigned long flag;
+ pr_debug("mdp3_ctrl_vsync_enable =%d\n", enable);
mdp3_session = (struct mdp3_session_data *)mfd->mdp.private1;
if (!mdp3_session || !mdp3_session->panel || !mdp3_session->dma ||
!mdp3_session->intf)
return -ENODEV;
- vsync_client.handler = vsync_notify_handler;
- vsync_client.arg = mdp3_session;
-
- mutex_lock(&mdp3_session->lock);
if (!mdp3_session->status) {
pr_debug("fb%d is not on yet", mfd->index);
- mutex_unlock(&mdp3_session->lock);
return -EINVAL;
}
+ if (enable) {
+ vsync_client.handler = vsync_notify_handler;
+ vsync_client.arg = mdp3_session;
+ arg = &vsync_client;
+ }
- mdp3_session->dma->vsync_enable(mdp3_session->dma, &vsync_client);
+ mutex_lock(&mdp3_session->lock);
+ mdp3_session->dma->vsync_enable(mdp3_session->dma, arg);
mutex_unlock(&mdp3_session->lock);
+ spin_lock_irqsave(&mdp3_session->vsync_lock, flag);
+ if (enable)
+ INIT_COMPLETION(mdp3_session->vsync_comp);
+ else
+ complete_all(&mdp3_session->vsync_comp);
+ spin_unlock_irqrestore(&mdp3_session->vsync_lock, flag);
return 0;
}
+
static ssize_t mdp3_vsync_show_event(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -64,8 +135,8 @@
struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)fbi->par;
struct mdp3_session_data *mdp3_session = NULL;
u64 vsync_ticks;
- ktime_t vsync_time;
int rc;
+ unsigned long flag;
if (!mfd || !mfd->mdp.private1)
return 0;
@@ -78,11 +149,11 @@
if (rc <= 0) {
pr_warn("vsync wait on fb%d interrupted (%d)\n",
mfd->index, rc);
- return -EBUSY;
}
- vsync_time = mdp3_session->dma->get_vsync_time(mdp3_session->dma);
- vsync_ticks = ktime_to_ns(vsync_time);
+ spin_lock_irqsave(&mdp3_session->vsync_lock, flag);
+ vsync_ticks = ktime_to_ns(mdp3_session->vsync_time);
+ spin_unlock_irqrestore(&mdp3_session->vsync_lock, flag);
pr_debug("fb%d vsync=%llu", mfd->index, vsync_ticks);
rc = snprintf(buf, PAGE_SIZE, "VSYNC=%llu", vsync_ticks);
@@ -395,6 +466,148 @@
return 0;
}
+static int mdp3_overlay_get(struct msm_fb_data_type *mfd,
+ struct mdp_overlay *req)
+{
+ int rc = 0;
+ struct mdp3_session_data *mdp3_session = mfd->mdp.private1;
+
+ mutex_lock(&mdp3_session->lock);
+
+ if (mdp3_session->overlay.id == req->id)
+ *req = mdp3_session->overlay;
+ else
+ rc = -EINVAL;
+
+ mutex_unlock(&mdp3_session->lock);
+
+ return rc;
+}
+
+static int mdp3_overlay_set(struct msm_fb_data_type *mfd,
+ struct mdp_overlay *req)
+{
+ int rc = 0;
+ struct mdp3_session_data *mdp3_session = mfd->mdp.private1;
+
+ mutex_lock(&mdp3_session->lock);
+
+ if (mdp3_session->overlay.id == req->id) {
+ mdp3_session->overlay = *req;
+ if (req->id == MSMFB_NEW_REQUEST) {
+ mdp3_session->overlay.id = 1;
+ req->id = 1;
+ }
+ } else {
+ rc = -EINVAL;
+ }
+ mutex_unlock(&mdp3_session->lock);
+
+ return rc;
+}
+
+static int mdp3_overlay_unset(struct msm_fb_data_type *mfd, int ndx)
+{
+ int rc = 0;
+ struct mdp3_session_data *mdp3_session = mfd->mdp.private1;
+
+ mdp3_ctrl_pan_display(mfd);
+
+ mutex_lock(&mdp3_session->lock);
+
+ if (mdp3_session->overlay.id == ndx && ndx == 1) {
+ mdp3_session->overlay.id = MSMFB_NEW_REQUEST;
+ mdp3_bufq_deinit(&mdp3_session->bufq_in);
+ mdp3_bufq_deinit(&mdp3_session->bufq_out);
+ } else {
+ rc = -EINVAL;
+ }
+
+ mutex_unlock(&mdp3_session->lock);
+
+ return rc;
+}
+
+static int mdp3_overlay_queue_buffer(struct msm_fb_data_type *mfd,
+ struct msmfb_overlay_data *req)
+{
+ int rc;
+ struct mdp3_session_data *mdp3_session = mfd->mdp.private1;
+ struct msmfb_data *img = &req->data;
+ struct mdp3_img_data data;
+
+ rc = mdp3_get_img(img, &data);
+ if (rc) {
+ pr_err("fail to get overlay buffer\n");
+ return rc;
+ }
+
+ rc = mdp3_bufq_push(&mdp3_session->bufq_in, &data);
+ if (rc) {
+ pr_err("fail to queue the overlay buffer, buffer drop\n");
+ mdp3_put_img(&data);
+ return rc;
+ }
+ return 0;
+}
+
+static int mdp3_overlay_play(struct msm_fb_data_type *mfd,
+ struct msmfb_overlay_data *req)
+{
+ struct mdp3_session_data *mdp3_session = mfd->mdp.private1;
+ int rc = 0;
+
+ pr_debug("mdp3_overlay_play req id=%x mem_id=%d\n",
+ req->id, req->data.memory_id);
+
+ mutex_lock(&mdp3_session->lock);
+
+ if (mfd->panel_power_on)
+ rc = mdp3_overlay_queue_buffer(mfd, req);
+ else
+ rc = -EPERM;
+
+ mutex_unlock(&mdp3_session->lock);
+
+ return rc;
+}
+
+static int mdp3_ctrl_display_commit_kickoff(struct msm_fb_data_type *mfd)
+{
+ struct mdp3_session_data *mdp3_session;
+ struct mdp3_img_data *data;
+ int rc = 0;
+
+ if (!mfd || !mfd->mdp.private1)
+ return -EINVAL;
+
+ mdp3_session = mfd->mdp.private1;
+ if (!mdp3_session || !mdp3_session->dma)
+ return -EINVAL;
+
+ if (!mdp3_session->status) {
+ pr_err("%s, display off!\n", __func__);
+ return -EPERM;
+ }
+
+ mutex_lock(&mdp3_session->lock);
+
+ data = mdp3_bufq_pop(&mdp3_session->bufq_in);
+ if (data) {
+ mdp3_session->dma->update(mdp3_session->dma,
+ (void *)data->addr);
+ mdp3_bufq_push(&mdp3_session->bufq_out, data);
+ }
+
+ if (mdp3_bufq_count(&mdp3_session->bufq_out) > 1) {
+ data = mdp3_bufq_pop(&mdp3_session->bufq_out);
+ mdp3_put_img(data);
+ }
+
+ mutex_unlock(&mdp3_session->lock);
+ return rc;
+}
+
static void mdp3_ctrl_pan_display(struct msm_fb_data_type *mfd)
{
struct fb_info *fbi;
@@ -445,6 +658,9 @@
break;
case metadata_op_get_caps:
metadata->data.caps.mdp_rev = 304;
+ metadata->data.caps.rgb_pipes = 0;
+ metadata->data.caps.vig_pipes = 0;
+ metadata->data.caps.dma_pipes = 1;
break;
default:
pr_warn("Unsupported request to MDP META IOCTL.\n");
@@ -460,6 +676,8 @@
int rc = -EINVAL;
struct mdp3_session_data *mdp3_session;
struct msmfb_metadata metadata;
+ struct mdp_overlay req;
+ struct msmfb_overlay_data ov_data;
int val;
pr_debug("mdp3_ctrl_ioctl_handler\n");
@@ -478,8 +696,6 @@
case MSMFB_OVERLAY_VSYNC_CTRL:
if (!copy_from_user(&val, argp, sizeof(val))) {
rc = mdp3_ctrl_vsync_enable(mfd, val);
- if (!val)
- init_completion(&mdp3_session->vsync_comp);
} else {
pr_err("MSMFB_OVERLAY_VSYNC_CTRL failed\n");
rc = -EFAULT;
@@ -493,10 +709,42 @@
if (!rc)
rc = copy_to_user(argp, &metadata, sizeof(metadata));
break;
+ case MSMFB_OVERLAY_GET:
+ rc = copy_from_user(&req, argp, sizeof(req));
+ if (!rc) {
+ rc = mdp3_overlay_get(mfd, &req);
+
+ if (!IS_ERR_VALUE(rc))
+ rc = copy_to_user(argp, &req, sizeof(req));
+ }
+ if (rc)
+ pr_err("OVERLAY_GET failed (%d)\n", rc);
+ break;
+ case MSMFB_OVERLAY_SET:
+ rc = copy_from_user(&req, argp, sizeof(req));
+ if (!rc) {
+ rc = mdp3_overlay_set(mfd, &req);
+
+ if (!IS_ERR_VALUE(rc))
+ rc = copy_to_user(argp, &req, sizeof(req));
+ }
+ if (rc)
+ pr_err("OVERLAY_SET failed (%d)\n", rc);
+ break;
+ case MSMFB_OVERLAY_UNSET:
+ if (!IS_ERR_VALUE(copy_from_user(&val, argp, sizeof(val))))
+ rc = mdp3_overlay_unset(mfd, val);
+ break;
+ case MSMFB_OVERLAY_PLAY:
+ rc = copy_from_user(&ov_data, argp, sizeof(ov_data));
+ if (!rc)
+ rc = mdp3_overlay_play(mfd, &ov_data);
+ if (rc)
+ pr_err("OVERLAY_PLAY failed (%d)\n", rc);
+ break;
default:
break;
}
-
return rc;
}
@@ -515,7 +763,7 @@
mdp3_interface->cursor_update = NULL;
mdp3_interface->dma_fnc = mdp3_ctrl_pan_display;
mdp3_interface->ioctl_handler = mdp3_ctrl_ioctl_handler;
- mdp3_interface->kickoff_fnc = NULL;
+ mdp3_interface->kickoff_fnc = mdp3_ctrl_display_commit_kickoff;
mdp3_session = kmalloc(sizeof(struct mdp3_session_data), GFP_KERNEL);
if (!mdp3_session) {
@@ -525,6 +773,7 @@
memset(mdp3_session, 0, sizeof(struct mdp3_session_data));
mutex_init(&mdp3_session->lock);
init_completion(&mdp3_session->vsync_comp);
+ spin_lock_init(&mdp3_session->vsync_lock);
mdp3_session->dma = mdp3_get_dma_pipe(MDP3_DMA_CAP_ALL);
if (!mdp3_session->dma) {
rc = -ENODEV;
@@ -540,6 +789,9 @@
mdp3_session->panel = dev_get_platdata(&mfd->pdev->dev);
mdp3_session->status = 0;
+ mdp3_session->overlay.id = MSMFB_NEW_REQUEST;
+ mdp3_bufq_init(&mdp3_session->bufq_in);
+ mdp3_bufq_init(&mdp3_session->bufq_out);
mfd->mdp.private1 = mdp3_session;
diff --git a/drivers/video/msm/mdss/mdp3_ctrl.h b/drivers/video/msm/mdss/mdp3_ctrl.h
index d42ece7..fb3bd36 100644
--- a/drivers/video/msm/mdss/mdp3_ctrl.h
+++ b/drivers/video/msm/mdss/mdp3_ctrl.h
@@ -18,10 +18,20 @@
#include <linux/mutex.h>
#include <linux/completion.h>
+#include "mdp3.h"
#include "mdp3_dma.h"
#include "mdss_fb.h"
#include "mdss_panel.h"
+#define MDP3_MAX_BUF_QUEUE 8
+
+struct mdp3_buffer_queue {
+ struct mdp3_img_data img_data[MDP3_MAX_BUF_QUEUE];
+ int count;
+ int push_idx;
+ int pop_idx;
+};
+
struct mdp3_session_data {
struct mutex lock;
int status;
@@ -29,7 +39,12 @@
struct mdss_panel_data *panel;
struct mdp3_intf *intf;
struct msm_fb_data_type *mfd;
+ ktime_t vsync_time;
+ spinlock_t vsync_lock;
struct completion vsync_comp;
+ struct mdp_overlay overlay;
+ struct mdp3_buffer_queue bufq_in;
+ struct mdp3_buffer_queue bufq_out;
};
int mdp3_ctrl_init(struct msm_fb_data_type *mfd);
diff --git a/drivers/video/msm/mdss/mdp3_dma.c b/drivers/video/msm/mdss/mdp3_dma.c
index 69e3d7e..a09f503 100644
--- a/drivers/video/msm/mdss/mdp3_dma.c
+++ b/drivers/video/msm/mdss/mdp3_dma.c
@@ -20,17 +20,6 @@
#define DMA_STOP_POLL_SLEEP_US 1000
#define DMA_STOP_POLL_TIMEOUT_US 16000
-static ktime_t mdp3_get_vsync_time(struct mdp3_dma *dma)
-{
- unsigned long flag;
- ktime_t time;
-
- spin_lock_irqsave(&dma->dma_lock, flag);
- time = dma->vsync_time;
- spin_unlock_irqrestore(&dma->dma_lock, flag);
- return time;
-}
-
static void mdp3_vsync_intr_handler(int type, void *arg)
{
struct mdp3_dma *dma = (struct mdp3_dma *)arg;
@@ -41,13 +30,11 @@
vsync_client = dma->vsync_client;
if (!vsync_client.handler)
dma->cb_type &= ~MDP3_DMA_CALLBACK_TYPE_VSYNC;
- dma->vsync_time = ktime_get();
complete(&dma->vsync_comp);
+ spin_unlock(&dma->dma_lock);
if (vsync_client.handler)
vsync_client.handler(vsync_client.arg);
- spin_unlock(&dma->dma_lock);
-
- if (!vsync_client.handler)
+ else
mdp3_irq_disable_nosync(type);
}
@@ -186,7 +173,7 @@
updated = 1;
}
} else {
- if (!dma->vsync_client.handler) {
+ if (dma->vsync_client.handler) {
dma->vsync_client.handler = NULL;
dma->vsync_client.arg = NULL;
updated = 1;
@@ -696,7 +683,6 @@
dma->histo_intr_enable = mdp3_dmap_histo_intr_enable;
dma->histo_intr_clear = mdp3_dmap_histo_intr_clear;
dma->vsync_enable = mdp3_dma_vsync_enable;
- dma->get_vsync_time = mdp3_get_vsync_time;
dma->start = mdp3_dma_start;
dma->stop = mdp3_dma_stop;
break;
@@ -717,7 +703,6 @@
dma->histo_intr_enable = NULL;
dma->histo_intr_clear = NULL;
dma->vsync_enable = mdp3_dma_vsync_enable;
- dma->get_vsync_time = mdp3_get_vsync_time;
dma->start = mdp3_dma_start;
dma->stop = mdp3_dma_stop;
break;
diff --git a/drivers/video/msm/mdss/mdp3_dma.h b/drivers/video/msm/mdss/mdp3_dma.h
index 2fb8427..cef749b 100644
--- a/drivers/video/msm/mdss/mdp3_dma.h
+++ b/drivers/video/msm/mdss/mdp3_dma.h
@@ -231,7 +231,6 @@
spinlock_t dma_lock;
struct completion vsync_comp;
struct completion dma_comp;
- ktime_t vsync_time;
struct mdp3_vsync_notification vsync_client;
u32 cb_type;
@@ -275,9 +274,6 @@
void (*vsync_enable)(struct mdp3_dma *dma,
struct mdp3_vsync_notification *vsync_client);
-
- ktime_t (*get_vsync_time)(struct mdp3_dma *dma);
-
};
struct mdp3_video_intf_cfg {
diff --git a/drivers/video/msm/mdss/mdss_dsi.c b/drivers/video/msm/mdss/mdss_dsi.c
index 5d56df4..7c9046c 100644
--- a/drivers/video/msm/mdss/mdss_dsi.c
+++ b/drivers/video/msm/mdss/mdss_dsi.c
@@ -431,6 +431,7 @@
{
int ret = 0;
struct mipi_panel_info *mipi;
+ struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL;
pr_info("%s:%d DSI on for continuous splash.\n", __func__, __LINE__);
@@ -439,7 +440,16 @@
return -EINVAL;
}
- mipi = &pdata->panel_info.mipi;
+ mipi = &pdata->panel_info.mipi;
+
+ ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata,
+ panel_data);
+
+ pr_debug("%s+: ctrl=%p ndx=%d\n", __func__,
+ ctrl_pdata, ctrl_pdata->ndx);
+
+ WARN(ctrl_pdata->panel_state != UNKNOWN_STATE,
+ "incorrect panel state=%d\n", ctrl_pdata->panel_state);
ret = mdss_dsi_panel_power_on(pdata, 1);
if (ret) {
@@ -453,6 +463,8 @@
mdss_dsi_op_mode_config(mipi->mode, pdata);
+ ctrl_pdata->panel_state = PANEL_ON;
+
pr_debug("%s-:End\n", __func__);
return ret;
}
@@ -598,12 +610,15 @@
panel_data);
mipi = &pdata->panel_info.mipi;
- ret = ctrl_pdata->on(pdata);
- if (ret) {
- pr_err("%s: unable to initialize the panel\n", __func__);
- return ret;
+ if (ctrl_pdata->panel_state != PANEL_ON) {
+ ret = ctrl_pdata->on(pdata);
+ if (ret) {
+ pr_err("%s: unable to initialize the panel\n",
+ __func__);
+ return ret;
+ }
+ ctrl_pdata->panel_state = PANEL_ON;
}
-
mdss_dsi_op_mode_config(mipi->mode, pdata);
pr_debug("%s-:\n", __func__);
@@ -628,12 +643,14 @@
mdss_dsi_op_mode_config(DSI_CMD_MODE, pdata);
- ret = ctrl_pdata->off(pdata);
- if (ret) {
- pr_err("%s: Panel OFF failed\n", __func__);
- return ret;
+ if (ctrl_pdata->panel_state == PANEL_ON) {
+ ret = ctrl_pdata->off(pdata);
+ if (ret) {
+ pr_err("%s: Panel OFF failed\n", __func__);
+ return ret;
+ }
+ ctrl_pdata->panel_state = PANEL_OFF;
}
-
pr_debug("%s-:End\n", __func__);
return ret;
}
@@ -1092,6 +1109,10 @@
ctrl_pdata->panel_data.panel_info.panel_power_on = 1;
}
+ ctrl_pdata->pclk_rate = dsi_pclk_rate;
+ ctrl_pdata->byte_clk_rate = panel_data->panel_info.clk_rate / 8;
+ pr_debug("%s: pclk=%d, bclk=%d\n", __func__,
+ ctrl_pdata->pclk_rate, ctrl_pdata->byte_clk_rate);
if (ctrl_pdata->panel_data.panel_info.cont_splash_enabled) {
mdss_dsi_prepare_clocks(ctrl_pdata);
@@ -1111,11 +1132,6 @@
ctrl_pdata->on = panel_data->on;
ctrl_pdata->off = panel_data->off;
- ctrl_pdata->pclk_rate = dsi_pclk_rate;
- ctrl_pdata->byte_clk_rate = panel_data->panel_info.clk_rate / 8;
- pr_debug("%s: pclk=%d, bclk=%d\n", __func__,
- ctrl_pdata->pclk_rate, ctrl_pdata->byte_clk_rate);
-
if (panel_data->panel_info.pdest == DISPLAY_1) {
mdss_debug_register_base("dsi0",
ctrl_pdata->ctrl_base, ctrl_pdata->reg_size);
@@ -1126,6 +1142,7 @@
ctrl_pdata->ndx = 1;
}
+ ctrl_pdata->panel_state = UNKNOWN_STATE;
pr_debug("%s: Panal data initialized\n", __func__);
return 0;
}
diff --git a/drivers/video/msm/mdss/mdss_dsi.h b/drivers/video/msm/mdss/mdss_dsi.h
index 197ff7a..9a32dc5 100644
--- a/drivers/video/msm/mdss/mdss_dsi.h
+++ b/drivers/video/msm/mdss/mdss_dsi.h
@@ -89,6 +89,12 @@
DSI_HS_MODE,
};
+enum dsi_panel_state {
+ UNKNOWN_STATE,
+ PANEL_ON,
+ PANEL_OFF,
+};
+
#define DSI_NON_BURST_SYNCH_PULSE 0
#define DSI_NON_BURST_SYNCH_EVENT 1
#define DSI_BURST_MODE 2
@@ -283,6 +289,7 @@
struct clk *byte_clk;
struct clk *esc_clk;
struct clk *pixel_clk;
+ u8 panel_state;
int irq_cnt;
int mdss_dsi_clk_on;
int rst_gpio;
diff --git a/drivers/video/msm/mdss/mdss_fb.c b/drivers/video/msm/mdss/mdss_fb.c
index 28f61f9..e2d8cf6 100644
--- a/drivers/video/msm/mdss/mdss_fb.c
+++ b/drivers/video/msm/mdss/mdss_fb.c
@@ -561,6 +561,13 @@
pdata->set_backlight(pdata, temp);
mfd->bl_level = bkl_lvl;
bl_level_old = temp;
+
+ if (mfd->mdp.update_ad_input) {
+ mutex_unlock(&mfd->bl_lock);
+ /* Will trigger ad_setup which will grab bl_lock */
+ mfd->mdp.update_ad_input(mfd);
+ mutex_lock(&mfd->bl_lock);
+ }
}
}
diff --git a/drivers/video/msm/mdss/mdss_fb.h b/drivers/video/msm/mdss/mdss_fb.h
index 5682f0b..05fdec4 100644
--- a/drivers/video/msm/mdss/mdss_fb.h
+++ b/drivers/video/msm/mdss/mdss_fb.h
@@ -68,6 +68,7 @@
int (*lut_update)(struct msm_fb_data_type *mfd, struct fb_cmap *cmap);
int (*do_histogram)(struct msm_fb_data_type *mfd,
struct mdp_histogram *hist);
+ int (*update_ad_input)(struct msm_fb_data_type *mfd);
int (*panel_register_done)(struct mdss_panel_data *pdata);
u32 (*fb_stride)(u32 fb_index, u32 xres, int bpp);
void *private1;
diff --git a/drivers/video/msm/mdss/mdss_hdmi_edid.c b/drivers/video/msm/mdss/mdss_hdmi_edid.c
index e87f028..1876057 100644
--- a/drivers/video/msm/mdss/mdss_hdmi_edid.c
+++ b/drivers/video/msm/mdss/mdss_hdmi_edid.c
@@ -119,6 +119,12 @@
89909, 119880, 148352, 119880, false},
{HDMI_VFRMT_1280x720p120_16_9, 1280, 720, false, 1650, 370, 750, 30,
90000, 120000, 148500, 120000, false},
+ {HDMI_VFRMT_1280x1024p60_5_4, 1280, 1024, false, 1688, 408, 1066, 42,
+ 63981, 60020, 108000, 60000, false},
+
+ /* All 1024 H Active */
+ {HDMI_VFRMT_1024x768p60_4_3, 1024, 768, false, 1344, 320, 806, 38,
+ 48363, 60004, 65000, 60000, false},
/* All 1440 H Active */
{HDMI_VFRMT_1440x576i50_4_3, 1440, 576, true, 1728, 288, 625, 24,
@@ -1016,7 +1022,7 @@
static void hdmi_edid_get_display_mode(struct hdmi_edid_ctrl *edid_ctrl,
const u8 *data_buf, u32 num_of_cea_blocks)
{
- u8 i = 0;
+ u8 i = 0, offset = 0, std_blk = 0;
u32 video_format = HDMI_VFRMT_640x480p60_4_3;
u32 has480p = false;
u8 len;
@@ -1175,6 +1181,72 @@
}
}
+ std_blk = 0;
+ offset = 0;
+ while (std_blk < 8) {
+ if ((edid_blk0[0x26 + offset] == 0x81) &&
+ (edid_blk0[0x26 + offset + 1] == 0x80)) {
+ pr_debug("%s: 108MHz: off=[%x] stdblk=[%x]\n",
+ __func__, offset, std_blk);
+ hdmi_edid_add_sink_video_format(sink_data,
+ HDMI_VFRMT_1280x1024p60_5_4);
+ }
+ if ((edid_blk0[0x26 + offset] == 0x61) &&
+ (edid_blk0[0x26 + offset + 1] == 0x40)) {
+ pr_debug("%s: 65MHz: off=[%x] stdblk=[%x]\n",
+ __func__, offset, std_blk);
+ hdmi_edid_add_sink_video_format(sink_data,
+ HDMI_VFRMT_1024x768p60_4_3);
+ break;
+ } else {
+ offset += 2;
+ }
+ std_blk++;
+ }
+ /* check if the EDID revision is 4 (version 1.4) */
+ if (edid_blk0[0x13] == 4) {
+ u8 start = 0x36;
+ i = 0;
+ /* Check each of 4 - 18 bytes descriptors */
+ while (i < 4) {
+ u8 iter = start;
+ u32 header_1 = 0;
+ u8 header_2 = 0;
+ header_1 = edid_blk0[iter++];
+ header_1 = header_1 << 8 | edid_blk0[iter++];
+ header_1 = header_1 << 8 | edid_blk0[iter++];
+ header_1 = header_1 << 8 | edid_blk0[iter++];
+ header_2 = edid_blk0[iter];
+ if (header_1 == 0x000000F7 &&
+ header_2 == 0x00) {
+ iter++;
+ /* VESA DMT Standard Version (0x0A)*/
+ iter++;
+ /* First set of supported formats */
+ iter++;
+ /* Second set of supported formats */
+ if (edid_blk0[iter] & 0x02) {
+ pr_debug("%s: DMT 1280x1024@60\n",
+ __func__);
+ hdmi_edid_add_sink_video_format(
+ sink_data,
+ HDMI_VFRMT_1280x1024p60_5_4);
+ break;
+ }
+ }
+ i++;
+ start += 0x12;
+ }
+ }
+
+ /* Established Timing I and II */
+ if (edid_blk0[0x24] & BIT(3)) {
+ pr_debug("%s: 65MHz: off=[%x] stdblk=[%x]\n",
+ __func__, offset, std_blk);
+ hdmi_edid_add_sink_video_format(sink_data,
+ HDMI_VFRMT_1024x768p60_4_3);
+ }
+
hdmi_edid_get_extended_video_formats(edid_ctrl, data_buf+0x80);
/* mandaroty 3d format */
diff --git a/drivers/video/msm/mdss/mdss_hdmi_tx.c b/drivers/video/msm/mdss/mdss_hdmi_tx.c
index 42cc356..12552bc 100644
--- a/drivers/video/msm/mdss/mdss_hdmi_tx.c
+++ b/drivers/video/msm/mdss/mdss_hdmi_tx.c
@@ -190,6 +190,14 @@
{20480, 247500} } },
};
+static bool is_cea_format(int mode)
+{
+ if ((mode > 0) && (mode < HDMI_EVFRMT_END))
+ return true;
+ else
+ return false;
+}
+
const char *hdmi_tx_pm_name(enum hdmi_tx_power_module_type module)
{
switch (module) {
@@ -2100,7 +2108,8 @@
return rc;
}
- if (!hdmi_tx_is_dvi_mode(hdmi_ctrl)) {
+ if (!hdmi_tx_is_dvi_mode(hdmi_ctrl) &&
+ is_cea_format(hdmi_ctrl->video_resolution)) {
rc = hdmi_tx_audio_setup(hdmi_ctrl);
if (rc) {
DEV_ERR("%s: hdmi_msm_audio_setup failed. rc=%d\n",
diff --git a/drivers/video/msm/mdss/mdss_hdmi_util.c b/drivers/video/msm/mdss/mdss_hdmi_util.c
index 0c8b0f8..53dfc71 100644
--- a/drivers/video/msm/mdss/mdss_hdmi_util.c
+++ b/drivers/video/msm/mdss/mdss_hdmi_util.c
@@ -109,8 +109,8 @@
hdmi_supported_video_mode_lut, MSM_HDMI_MODES_XTND);
/* Add any other specific DVI timings (DVI modes, etc.) */
- MSM_HDMI_MODES_SET_TIMING(hdmi_supported_video_mode_lut,
- HDMI_VFRMT_2560x1600p60_16_9);
+ MSM_HDMI_MODES_SET_SUPP_TIMINGS(
+ hdmi_supported_video_mode_lut, MSM_HDMI_MODES_DVI);
} /* hdmi_setup_video_mode_lut */
const char *hdmi_get_single_video_3d_fmt_2string(u32 format)
diff --git a/drivers/video/msm/mdss/mdss_mdp.h b/drivers/video/msm/mdss/mdss_mdp.h
index a650522..f3b5acec 100644
--- a/drivers/video/msm/mdss/mdss_mdp.h
+++ b/drivers/video/msm/mdss/mdss_mdp.h
@@ -465,7 +465,7 @@
u32 *copyback);
int mdss_mdp_igc_lut_config(struct mdss_mdp_ctl *ctl,
struct mdp_igc_lut_data *config,
- u32 *copyback);
+ u32 *copyback, u32 copy_from_kernel);
int mdss_mdp_argc_config(struct mdss_mdp_ctl *ctl,
struct mdp_pgc_lut_data *config,
u32 *copyback);
@@ -486,11 +486,10 @@
struct mdp_histogram_data *hist);
void mdss_mdp_hist_intr_done(u32 isr);
-int mdss_ad_init_checks(struct msm_fb_data_type *mfd);
int mdss_mdp_ad_config(struct msm_fb_data_type *mfd,
struct mdss_ad_init_cfg *init_cfg);
int mdss_mdp_ad_input(struct msm_fb_data_type *mfd,
- struct mdss_ad_input *input);
+ struct mdss_ad_input *input, int wait);
int mdss_mdp_ad_addr_setup(struct mdss_data_type *mdata, u32 *ad_off);
struct mdss_mdp_pipe *mdss_mdp_pipe_alloc(struct mdss_mdp_mixer *mixer,
@@ -536,6 +535,7 @@
u32 mdss_mdp_fb_stride(u32 fb_index, u32 xres, int bpp);
int mdss_panel_register_done(struct mdss_panel_data *pdata);
+int mdss_mdp_limited_lut_igc_config(struct mdss_mdp_ctl *ctl);
#define mfd_to_mdp5_data(mfd) (mfd->mdp.private1)
#define mfd_to_mdata(mfd) (((struct mdss_overlay_private *)\
diff --git a/drivers/video/msm/mdss/mdss_mdp_ctl.c b/drivers/video/msm/mdss/mdss_mdp_ctl.c
index b2147c3..e346082 100644
--- a/drivers/video/msm/mdss/mdss_mdp_ctl.c
+++ b/drivers/video/msm/mdss/mdss_mdp_ctl.c
@@ -248,13 +248,14 @@
return ret;
}
-static struct mdss_mdp_ctl *mdss_mdp_ctl_alloc(struct mdss_data_type *mdata)
+static struct mdss_mdp_ctl *mdss_mdp_ctl_alloc(struct mdss_data_type *mdata,
+ u32 off)
{
struct mdss_mdp_ctl *ctl = NULL;
- int cnum;
+ u32 cnum;
mutex_lock(&mdss_mdp_ctl_lock);
- for (cnum = 0; cnum < mdata->nctl; cnum++) {
+ for (cnum = off; cnum < mdata->nctl; cnum++) {
ctl = mdata->ctl_off + cnum;
if (ctl->ref_cnt == 0) {
ctl->ref_cnt++;
@@ -391,7 +392,7 @@
struct mdss_mdp_ctl *ctl = NULL;
struct mdss_mdp_mixer *mixer = NULL;
- ctl = mdss_mdp_ctl_alloc(mdss_res);
+ ctl = mdss_mdp_ctl_alloc(mdss_res, mdss_res->nmixers_intf);
if (!ctl)
return NULL;
@@ -669,7 +670,7 @@
int ret = 0;
struct mdss_data_type *mdata = mfd_to_mdata(mfd);
- ctl = mdss_mdp_ctl_alloc(mdata);
+ ctl = mdss_mdp_ctl_alloc(mdata, MDSS_MDP_CTL0);
if (!ctl) {
pr_err("unable to allocate ctl\n");
return ERR_PTR(-ENOMEM);
@@ -711,6 +712,9 @@
ctl->intf_type = MDSS_INTF_HDMI;
ctl->opmode = MDSS_MDP_CTL_OP_VIDEO_MODE;
ctl->start_fnc = mdss_mdp_video_start;
+ ret = mdss_mdp_limited_lut_igc_config(ctl);
+ if (ret)
+ pr_err("Unable to config IGC LUT data");
break;
case WRITEBACK_PANEL:
ctl->intf_num = MDSS_MDP_NO_INTF;
diff --git a/drivers/video/msm/mdss/mdss_mdp_overlay.c b/drivers/video/msm/mdss/mdss_mdp_overlay.c
index 56fd163..c96464a 100644
--- a/drivers/video/msm/mdss/mdss_mdp_overlay.c
+++ b/drivers/video/msm/mdss/mdss_mdp_overlay.c
@@ -65,11 +65,19 @@
{
u32 xres, yres;
u32 min_src_size, min_dst_size;
+ int content_secure;
struct mdss_data_type *mdata = mfd_to_mdata(mfd);
+ struct mdss_mdp_ctl *ctl = mfd_to_ctl(mfd);
xres = mfd->fbi->var.xres;
yres = mfd->fbi->var.yres;
+ content_secure = (req->flags & MDP_SECURE_OVERLAY_SESSION);
+ if (!ctl->is_secure && content_secure &&
+ (mfd->panel.type == WRITEBACK_PANEL)) {
+ pr_debug("return due to security concerns\n");
+ return -EPERM;
+ }
if (mdata->mdp_rev >= MDSS_MDP_HW_REV_102) {
min_src_size = fmt->is_yuv ? 2 : 1;
min_dst_size = 1;
@@ -688,6 +696,7 @@
{
struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
struct mdss_mdp_pipe *pipe;
+ struct mdss_mdp_ctl *ctl = mfd_to_ctl(mfd);
int ret;
mutex_lock(&mdp5_data->ov_lock);
@@ -696,6 +705,9 @@
struct mdss_mdp_data *buf;
if (pipe->back_buf.num_planes) {
buf = &pipe->back_buf;
+ } else if (ctl->play_cnt == 0) {
+ pipe->params_changed++;
+ buf = &pipe->front_buf;
} else if (!pipe->params_changed) {
continue;
} else if (pipe->front_buf.num_planes) {
@@ -1467,6 +1479,7 @@
int ret;
struct msmfb_mdp_pp mdp_pp;
u32 copyback = 0;
+ u32 copy_from_kernel = 0;
struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
ret = copy_from_user(&mdp_pp, argp, sizeof(mdp_pp));
@@ -1493,7 +1506,7 @@
mdp5_data->ctl,
(struct mdp_igc_lut_data *)
&mdp_pp.data.lut_cfg_data.data,
- ©back);
+ ©back, copy_from_kernel);
break;
case mdp_lut_pgc:
@@ -1535,7 +1548,7 @@
ret = mdss_mdp_ad_config(mfd, &mdp_pp.data.ad_init_cfg);
break;
case mdp_op_ad_input:
- ret = mdss_mdp_ad_input(mfd, &mdp_pp.data.ad_input);
+ ret = mdss_mdp_ad_input(mfd, &mdp_pp.data.ad_input, 1);
if (ret > 0) {
ret = 0;
copyback = 1;
@@ -1860,7 +1873,19 @@
if (!mdp5_data->ctl->power_on)
return 0;
- mdss_mdp_overlay_release_all(mfd);
+ if (!mfd->ref_cnt) {
+ mdss_mdp_overlay_release_all(mfd);
+ } else {
+ int need_cleanup;
+ mutex_lock(&mfd->lock);
+ need_cleanup = !list_empty(&mdp5_data->pipes_cleanup);
+ mutex_unlock(&mfd->lock);
+
+ if (need_cleanup) {
+ pr_debug("cleaning up some pipes\n");
+ mdss_mdp_overlay_kickoff(mfd);
+ }
+ }
rc = mdss_mdp_ctl_stop(mdp5_data->ctl);
if (rc == 0) {
diff --git a/drivers/video/msm/mdss/mdss_mdp_pipe.c b/drivers/video/msm/mdss/mdss_mdp_pipe.c
index 0f65530..4f9ab81 100644
--- a/drivers/video/msm/mdss/mdss_mdp_pipe.c
+++ b/drivers/video/msm/mdss/mdss_mdp_pipe.c
@@ -22,6 +22,7 @@
#define SMP_MB_SIZE (mdss_res->smp_mb_size)
#define SMP_MB_CNT (mdss_res->smp_mb_cnt)
#define SMP_ENTRIES_PER_MB (SMP_MB_SIZE / 16)
+#define MAX_BPP 4
static DEFINE_MUTEX(mdss_mdp_sspp_lock);
static DEFINE_MUTEX(mdss_mdp_smp_lock);
@@ -152,6 +153,9 @@
ps.num_planes = 2;
ps.ystride[0] = pipe->src.w >> pipe->horz_deci;
ps.ystride[1] = pipe->src.h >> pipe->vert_deci;
+ } else if (pipe->src_fmt->fetch_planes == MDSS_MDP_PLANE_INTERLEAVED) {
+ ps.ystride[0] = max(pipe->mixer->width, pipe->src.w) * MAX_BPP;
+ ps.num_planes = 1;
} else {
rc = mdss_mdp_get_plane_sizes(pipe->src_fmt->format,
pipe->src.w, pipe->src.h, &ps, 0);
diff --git a/drivers/video/msm/mdss/mdss_mdp_pp.c b/drivers/video/msm/mdss/mdss_mdp_pp.c
index 4e7cc37..fcc1c1a 100644
--- a/drivers/video/msm/mdss/mdss_mdp_pp.c
+++ b/drivers/video/msm/mdss/mdss_mdp_pp.c
@@ -93,6 +93,72 @@
static u32 dither_depth_map[9] = {
0, 0, 0, 0, 0, 1, 2, 3, 3};
+static u32 igc_limited[IGC_LUT_ENTRIES] = {
+ 16777472, 17826064, 18874656, 19923248,
+ 19923248, 20971840, 22020432, 23069024,
+ 24117616, 25166208, 26214800, 26214800,
+ 27263392, 28311984, 29360576, 30409168,
+ 31457760, 32506352, 32506352, 33554944,
+ 34603536, 35652128, 36700720, 37749312,
+ 38797904, 38797904, 39846496, 40895088,
+ 41943680, 42992272, 44040864, 45089456,
+ 45089456, 46138048, 47186640, 48235232,
+ 49283824, 50332416, 51381008, 51381008,
+ 52429600, 53478192, 54526784, 55575376,
+ 56623968, 57672560, 58721152, 58721152,
+ 59769744, 60818336, 61866928, 62915520,
+ 63964112, 65012704, 65012704, 66061296,
+ 67109888, 68158480, 69207072, 70255664,
+ 71304256, 71304256, 72352848, 73401440,
+ 74450032, 75498624, 76547216, 77595808,
+ 77595808, 78644400, 79692992, 80741584,
+ 81790176, 82838768, 83887360, 83887360,
+ 84935952, 85984544, 87033136, 88081728,
+ 89130320, 90178912, 90178912, 91227504,
+ 92276096, 93324688, 94373280, 95421872,
+ 96470464, 96470464, 97519056, 98567648,
+ 99616240, 100664832, 101713424, 102762016,
+ 102762016, 103810608, 104859200, 105907792,
+ 106956384, 108004976, 109053568, 109053568,
+ 110102160, 111150752, 112199344, 113247936,
+ 114296528, 115345120, 115345120, 116393712,
+ 117442304, 118490896, 119539488, 120588080,
+ 121636672, 121636672, 122685264, 123733856,
+ 124782448, 125831040, 126879632, 127928224,
+ 127928224, 128976816, 130025408, 131074000,
+ 132122592, 133171184, 134219776, 135268368,
+ 135268368, 136316960, 137365552, 138414144,
+ 139462736, 140511328, 141559920, 141559920,
+ 142608512, 143657104, 144705696, 145754288,
+ 146802880, 147851472, 147851472, 148900064,
+ 149948656, 150997248, 152045840, 153094432,
+ 154143024, 154143024, 155191616, 156240208,
+ 157288800, 158337392, 159385984, 160434576,
+ 160434576, 161483168, 162531760, 163580352,
+ 164628944, 165677536, 166726128, 166726128,
+ 167774720, 168823312, 169871904, 170920496,
+ 171969088, 173017680, 173017680, 174066272,
+ 175114864, 176163456, 177212048, 178260640,
+ 179309232, 179309232, 180357824, 181406416,
+ 182455008, 183503600, 184552192, 185600784,
+ 185600784, 186649376, 187697968, 188746560,
+ 189795152, 190843744, 191892336, 191892336,
+ 192940928, 193989520, 195038112, 196086704,
+ 197135296, 198183888, 198183888, 199232480,
+ 200281072, 201329664, 202378256, 203426848,
+ 204475440, 204475440, 205524032, 206572624,
+ 207621216, 208669808, 209718400, 210766992,
+ 211815584, 211815584, 212864176, 213912768,
+ 214961360, 216009952, 217058544, 218107136,
+ 218107136, 219155728, 220204320, 221252912,
+ 222301504, 223350096, 224398688, 224398688,
+ 225447280, 226495872, 227544464, 228593056,
+ 229641648, 230690240, 230690240, 231738832,
+ 232787424, 233836016, 234884608, 235933200,
+ 236981792, 236981792, 238030384, 239078976,
+ 240127568, 241176160, 242224752, 243273344,
+ 243273344, 244321936, 245370528, 246419120};
+
#define GAMUT_T0_SIZE 125
#define GAMUT_T1_SIZE 100
#define GAMUT_T2_SIZE 80
@@ -218,6 +284,9 @@
static void pp_sharp_config(char __iomem *offset,
struct pp_sts_type *pp_sts,
struct mdp_sharp_cfg *sharp_config);
+static int mdss_ad_init_checks(struct msm_fb_data_type *mfd);
+static struct mdss_ad_info *mdss_mdp_get_ad(struct msm_fb_data_type *mfd);
+static int pp_update_ad_input(struct msm_fb_data_type *mfd);
static void pp_ad_vsync_handler(struct mdss_mdp_ctl *ctl, ktime_t t);
static void pp_ad_cfg_write(struct mdss_ad_info *ad);
static void pp_ad_init_write(struct mdss_ad_info *ad);
@@ -1543,9 +1612,30 @@
MDSS_MDP_REG_WRITE(offset, (cfg->c2_data[i] & 0xFFF) | data);
}
+int mdss_mdp_limited_lut_igc_config(struct mdss_mdp_ctl *ctl)
+{
+ int ret = 0;
+ u32 copyback = 0;
+ u32 copy_from_kernel = 1;
+ struct mdp_igc_lut_data config;
+
+ if (!ctl)
+ return -EINVAL;
+
+ config.len = IGC_LUT_ENTRIES;
+ config.ops = MDP_PP_OPS_WRITE | MDP_PP_OPS_ENABLE;
+ config.block = (ctl->mfd->index) + MDP_LOGICAL_BLOCK_DISP_0;
+ config.c0_c1_data = igc_limited;
+ config.c2_data = igc_limited;
+
+ ret = mdss_mdp_igc_lut_config(ctl, &config, ©back,
+ copy_from_kernel);
+ return ret;
+}
+
int mdss_mdp_igc_lut_config(struct mdss_mdp_ctl *ctl,
struct mdp_igc_lut_data *config,
- u32 *copyback)
+ u32 *copyback, u32 copy_from_kernel)
{
int ret = 0;
u32 tbl_idx, igc_offset, disp_num, dspp_num = 0;
@@ -1597,15 +1687,25 @@
*copyback = 1;
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
} else {
- if (copy_from_user(&mdss_pp_res->igc_lut_c0c1[disp_num][0],
- config->c0_c1_data, config->len * sizeof(u32))) {
- ret = -EFAULT;
- goto igc_config_exit;
- }
- if (copy_from_user(&mdss_pp_res->igc_lut_c2[disp_num][0],
- config->c2_data, config->len * sizeof(u32))) {
- ret = -EFAULT;
- goto igc_config_exit;
+ if (copy_from_kernel) {
+ memcpy(&mdss_pp_res->igc_lut_c0c1[disp_num][0],
+ config->c0_c1_data, config->len * sizeof(u32));
+ memcpy(&mdss_pp_res->igc_lut_c2[disp_num][0],
+ config->c2_data, config->len * sizeof(u32));
+ } else {
+ if (copy_from_user(
+ &mdss_pp_res->igc_lut_c0c1[disp_num][0],
+ config->c0_c1_data,
+ config->len * sizeof(u32))) {
+ ret = -EFAULT;
+ goto igc_config_exit;
+ }
+ if (copy_from_user(
+ &mdss_pp_res->igc_lut_c2[disp_num][0],
+ config->c2_data, config->len * sizeof(u32))) {
+ ret = -EFAULT;
+ goto igc_config_exit;
+ }
}
mdss_pp_res->igc_disp_cfg[disp_num] = *config;
mdss_pp_res->igc_disp_cfg[disp_num].c0_c1_data =
@@ -2583,7 +2683,7 @@
}
#define MDSS_AD_MAX_MIXERS 1
-int mdss_ad_init_checks(struct msm_fb_data_type *mfd)
+static int mdss_ad_init_checks(struct msm_fb_data_type *mfd)
{
u32 mixer_id[MDSS_MDP_INTF_MAX_LAYERMIXER];
u32 mixer_num;
@@ -2621,23 +2721,47 @@
return mixer_id[0];
}
+static struct mdss_ad_info *mdss_mdp_get_ad(struct msm_fb_data_type *mfd)
+{
+ int ad_num;
+ struct mdss_data_type *mdata;
+ struct mdss_ad_info *ad = NULL;
+ mdata = mfd_to_mdata(mfd);
+
+ ad_num = mdss_ad_init_checks(mfd);
+ if (ad_num >= 0)
+ ad = &mdata->ad_cfgs[ad_num];
+ return ad;
+}
+
+static int pp_update_ad_input(struct msm_fb_data_type *mfd)
+{
+ struct mdss_ad_info *ad;
+ struct mdss_ad_input input;
+
+ ad = mdss_mdp_get_ad(mfd);
+ if (!ad)
+ return -EINVAL;
+
+ pr_debug("backlight level changed, trigger update to AD");
+ input.mode = ad->cfg.mode;
+ if (MDSS_AD_MODE_DATA_MATCH(ad->cfg.mode, MDSS_AD_INPUT_AMBIENT))
+ input.in.amb_light = ad->ad_data;
+ else
+ input.in.strength = ad->ad_data;
+ /* call to ad_input will trigger backlight read */
+ return mdss_mdp_ad_input(mfd, &input, 0);
+}
+
int mdss_mdp_ad_config(struct msm_fb_data_type *mfd,
struct mdss_ad_init_cfg *init_cfg)
{
- int ad_num;
struct mdss_ad_info *ad;
- struct mdss_data_type *mdata;
struct mdss_mdp_ctl *ctl;
- struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
- ctl = mdp5_data->ctl;
-
- ad_num = mdss_ad_init_checks(mfd);
- if (ad_num < 0)
- return ad_num;
-
- mdata = mdss_mdp_get_mdata();
- ad = &mdata->ad_cfgs[ad_num];
+ ad = mdss_mdp_get_ad(mfd);
+ if (!ad)
+ return -EINVAL;
mutex_lock(&ad->lock);
if (init_cfg->ops & MDP_PP_AD_INIT) {
@@ -2663,26 +2787,20 @@
ad->mfd = mfd;
}
mutex_unlock(&ad->lock);
+ ctl = mfd_to_ctl(mfd);
mdss_mdp_pp_setup(ctl);
return 0;
}
int mdss_mdp_ad_input(struct msm_fb_data_type *mfd,
- struct mdss_ad_input *input) {
- int ad_num, ret = 0;
+ struct mdss_ad_input *input, int wait) {
+ int ret = 0;
struct mdss_ad_info *ad;
- struct mdss_data_type *mdata;
struct mdss_mdp_ctl *ctl;
- struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
- ctl = mdp5_data->ctl;
-
- ad_num = mdss_ad_init_checks(mfd);
- if (ad_num < 0)
- return ad_num;
-
- mdata = mdss_mdp_get_mdata();
- ad = &mdata->ad_cfgs[ad_num];
+ ad = mdss_mdp_get_ad(mfd);
+ if (!ad)
+ return -EINVAL;
mutex_lock(&ad->lock);
if (!PP_AD_STATE_IS_INITCFG(ad->state) &&
@@ -2727,16 +2845,21 @@
error:
mutex_unlock(&ad->lock);
if (!ret) {
- mutex_lock(&ad->lock);
- init_completion(&ad->comp);
- mutex_unlock(&ad->lock);
+ if (wait) {
+ mutex_lock(&ad->lock);
+ init_completion(&ad->comp);
+ mutex_unlock(&ad->lock);
+ }
+ ctl = mfd_to_ctl(mfd);
mdss_mdp_pp_setup(ctl);
- ret = wait_for_completion_interruptible_timeout(&ad->comp,
- HIST_WAIT_TIMEOUT(1));
- if (ret == 0)
- ret = -ETIMEDOUT;
- else if (ret > 0)
- input->output = ad->last_str;
+ if (wait) {
+ ret = wait_for_completion_interruptible_timeout(
+ &ad->comp, HIST_WAIT_TIMEOUT(1));
+ if (ret == 0)
+ ret = -ETIMEDOUT;
+ else if (ret > 0)
+ input->output = ad->last_str;
+ }
}
return ret;
}
@@ -2861,19 +2984,16 @@
#define MDSS_PP_AD_BYPASS_DEF 0x101
static int mdss_mdp_ad_setup(struct msm_fb_data_type *mfd)
{
- int ad_num, ret = 0;
+ int ret = 0;
struct mdss_ad_info *ad;
- struct mdss_data_type *mdata;
struct mdss_mdp_ctl *ctl = mfd_to_ctl(mfd);
char __iomem *base;
u32 bypass = MDSS_PP_AD_BYPASS_DEF;
- ad_num = mdss_ad_init_checks(mfd);
- if (ad_num < 0)
- return ad_num;
+ ad = mdss_mdp_get_ad(mfd);
+ if (!ad)
+ return -EINVAL;
- mdata = mdss_mdp_get_mdata();
- ad = &mdata->ad_cfgs[ad_num];
base = ad->base;
mutex_lock(&ad->lock);
@@ -2924,10 +3044,17 @@
bypass = 0;
ret = 1;
ad->state |= PP_AD_STATE_RUN;
+ mutex_lock(&mfd->bl_lock);
+ mfd->mdp.update_ad_input = pp_update_ad_input;
+ mutex_unlock(&mfd->bl_lock);
+
} else {
if (ad->state & PP_AD_STATE_RUN) {
ret = 1;
ad->sts |= PP_AD_STS_DIRTY_VSYNC;
+ mutex_lock(&mfd->bl_lock);
+ mfd->mdp.update_ad_input = NULL;
+ mutex_unlock(&mfd->bl_lock);
}
ad->state &= ~PP_AD_STATE_RUN;
}
diff --git a/include/linux/diagchar.h b/include/linux/diagchar.h
index 2f77d29..73b94af 100644
--- a/include/linux/diagchar.h
+++ b/include/linux/diagchar.h
@@ -286,7 +286,7 @@
MSG_LVL_LOW,
MSG_LVL_LOW,
MSG_LVL_LOW,
- MSG_LVL_HIGH,
+ MSG_LVL_LOW,
MSG_LVL_LOW,
MSG_LVL_LOW,
MSG_LVL_LOW|MSG_LVL_MED|MSG_LVL_HIGH|MSG_LVL_ERROR|MSG_LVL_FATAL,
@@ -725,7 +725,7 @@
/* LOG CODES */
#define LOG_0 0x0
-#define LOG_1 0x17F4
+#define LOG_1 0x17FA
#define LOG_2 0x0
#define LOG_3 0x0
#define LOG_4 0x4910
diff --git a/include/linux/input/gen_vkeys.h b/include/linux/input/gen_vkeys.h
index ce29351..a58158d 100644
--- a/include/linux/input/gen_vkeys.h
+++ b/include/linux/input/gen_vkeys.h
@@ -19,5 +19,6 @@
int panel_maxy;
int *keycodes;
int num_keys;
+ int y_offset;
};
#endif
diff --git a/include/linux/mfd/wcd9xxx/pdata.h b/include/linux/mfd/wcd9xxx/pdata.h
index c6e4ab3..b7ba6fb 100644
--- a/include/linux/mfd/wcd9xxx/pdata.h
+++ b/include/linux/mfd/wcd9xxx/pdata.h
@@ -126,6 +126,7 @@
u8 bias2_cap_mode;
u8 bias3_cap_mode;
u8 bias4_cap_mode;
+ bool bias2_is_headset_only;
};
struct wcd9xxx_ocp_setting {
diff --git a/include/linux/mfd/wcd9xxx/wcd9304_registers.h b/include/linux/mfd/wcd9xxx/wcd9304_registers.h
index a7f9e4a..8e5e23a 100644
--- a/include/linux/mfd/wcd9xxx/wcd9304_registers.h
+++ b/include/linux/mfd/wcd9xxx/wcd9304_registers.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2013, 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
@@ -473,17 +473,17 @@
#define SITAR_A_CDC_TX3_DMIC_CTL (0x235)
#define SITAR_A_CDC_TX3_DMIC_CTL__POR (0x00000000)
-#define SITAR_A_CDC_TX4_VOL_CTL_TIMER (0x239)
+#define SITAR_A_CDC_TX4_VOL_CTL_TIMER (0x238)
#define SITAR_A_CDC_TX4_VOL_CTL_TIMER__POR (0x00000000)
-#define SITAR_A_CDC_TX4_VOL_CTL_GAIN (0x23A)
+#define SITAR_A_CDC_TX4_VOL_CTL_GAIN (0x239)
#define SITAR_A_CDC_TX4_VOL_CTL_GAIN__POR (0x00000000)
-#define SITAR_A_CDC_TX4_VOL_CTL_CFG (0x23B)
+#define SITAR_A_CDC_TX4_VOL_CTL_CFG (0x23A)
#define SITAR_A_CDC_TX4_VOL_CTL_CFG__POR (0x00000000)
-#define SITAR_A_CDC_TX4_MUX_CTL (0x23C)
+#define SITAR_A_CDC_TX4_MUX_CTL (0x23B)
#define SITAR_A_CDC_TX4_MUX_CTL__POR (0x00000008)
-#define SITAR_A_CDC_TX4_CLK_FS_CTL (0x23D)
+#define SITAR_A_CDC_TX4_CLK_FS_CTL (0x23C)
#define SITAR_A_CDC_TX4_CLK_FS_CTL__POR (0x00000003)
-#define SITAR_A_CDC_TX4_DMIC_CTL (0x23E)
+#define SITAR_A_CDC_TX4_DMIC_CTL (0x23D)
#define SITAR_A_CDC_TX4_DMIC_CTL__POR (0x00000000)
#define SITAR_A_CDC_TX5_VOL_CTL_TIMER (0x240)
diff --git a/include/linux/msm_audio_ion.h b/include/linux/msm_audio_ion.h
index 83e5dff..38b27bf 100644
--- a/include/linux/msm_audio_ion.h
+++ b/include/linux/msm_audio_ion.h
@@ -13,7 +13,12 @@
#ifndef _LINUX_MSM_AUDIO_ION_H
#define _LINUX_MSM_AUDIO_ION_H
-
+#ifdef CONFIG_SND_SOC_QDSP6V2
+#include <sound/q6asm-v2.h>
+#else
+#include <sound/q6asm.h>
+#endif
+#include <sound/pcm.h>
#include <linux/msm_ion.h>
@@ -26,9 +31,11 @@
unsigned long *ionflag, size_t bufsz,
ion_phys_addr_t *paddr, size_t *pa_len, void **vaddr);
int msm_audio_ion_free(struct ion_client *client, struct ion_handle *handle);
-
+int msm_audio_ion_mmap(struct audio_buffer *substream,
+ struct vm_area_struct *vma);
bool msm_audio_ion_is_smmu_available(void);
+int msm_audio_ion_cache_operations(struct audio_buffer *abuff, int cache_op);
#ifdef CONFIG_SND_SOC_QDSP6V2
struct ion_client *msm_audio_ion_client_create(unsigned int heap_mask,
diff --git a/include/linux/msm_kgsl.h b/include/linux/msm_kgsl.h
index 2ad040e..e8de769 100644
--- a/include/linux/msm_kgsl.h
+++ b/include/linux/msm_kgsl.h
@@ -769,7 +769,7 @@
struct kgsl_perfcounter_read_group {
unsigned int groupid;
unsigned int countable;
- uint64_t value;
+ unsigned long long value;
};
struct kgsl_perfcounter_read {
diff --git a/include/linux/msm_mdp.h b/include/linux/msm_mdp.h
index 61e9cb1..e4df414 100644
--- a/include/linux/msm_mdp.h
+++ b/include/linux/msm_mdp.h
@@ -716,7 +716,7 @@
} data;
};
-#define MDP_MAX_FENCE_FD 10
+#define MDP_MAX_FENCE_FD 32
#define MDP_BUF_SYNC_FLAG_WAIT 1
struct mdp_buf_sync {
diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h
index 6e30ca2..1de9aaa 100644
--- a/include/linux/power_supply.h
+++ b/include/linux/power_supply.h
@@ -127,6 +127,7 @@
POWER_SUPPLY_PROP_TYPE, /* use power_supply.type instead */
POWER_SUPPLY_PROP_SCOPE,
POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL,
+ POWER_SUPPLY_PROP_RESISTANCE,
/* Properties of type `const char *' */
POWER_SUPPLY_PROP_MODEL_NAME,
POWER_SUPPLY_PROP_MANUFACTURER,
diff --git a/include/linux/qpnp/power-on.h b/include/linux/qpnp/power-on.h
index 85dbce9..6394988 100644
--- a/include/linux/qpnp/power-on.h
+++ b/include/linux/qpnp/power-on.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2013, 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
@@ -17,8 +17,10 @@
#ifdef CONFIG_QPNP_POWER_ON
int qpnp_pon_system_pwr_off(bool reset);
+int qpnp_pon_is_warm_reset(void);
#else
static int qpnp_pon_system_pwr_off(bool reset) { return -ENODEV; }
+static inline int qpnp_pon_is_warm_reset(void) { return -ENODEV; }
#endif
#endif
diff --git a/include/linux/qpnp/qpnp-adc.h b/include/linux/qpnp/qpnp-adc.h
index 13d0b80..dfb156f 100644
--- a/include/linux/qpnp/qpnp-adc.h
+++ b/include/linux/qpnp/qpnp-adc.h
@@ -602,6 +602,34 @@
QPNP_ADC_TM_CH_SELECT_NONE
};
+enum qpnp_comp_scheme_type {
+ COMP_ID_GF = 0,
+ COMP_ID_SMIC,
+ COMP_ID_TSMC,
+ COMP_ID_NUM,
+};
+
+enum qpnp_iadc_rev {
+ QPNP_IADC_VER_3_0 = 0x1,
+ QPNP_IADC_VER_3_1 = 0x3,
+};
+
+#define QPNP_VBAT_SNS_COEFF_1_TYPEA 3000
+#define QPNP_VBAT_SNS_COEFF_2_TYPEA 45810000
+#define QPNP_VBAT_SNS_COEFF_3 100000
+#define QPNP_VBAT_SNS_COEFF_1_TYPEB 3500
+#define QPNP_VBAT_SNS_COEFF_2_TYPEB 80000000
+
+#define QPNP_COEFF_1 969000
+#define QPNP_COEFF_2 34
+#define QPNP_COEFF_3_TYPEA 1700000
+#define QPNP_COEFF_3_TYPEB 1000000
+#define QPNP_COEFF_4 100
+#define QPNP_COEFF_5 15000
+#define QPNP_COEFF_6 100000
+#define QPNP_COEFF_7 21700
+#define QPNP_COEFF_8 100000000
+
/**
* struct qpnp_adc_tm_config - Represent ADC Thermal Monitor configuration.
* @channel: ADC channel for which thermal monitoring is requested.
@@ -1252,6 +1280,11 @@
*/
int32_t qpnp_vadc_iadc_sync_complete_request(
enum qpnp_vadc_channels channel, struct qpnp_vadc_result *result);
+/**
+ * qpnp_vadc_sns_comp_result() - Compensate vbatt readings based on temperature
+ * @result: Voltage in uV that needs compensation.
+ */
+int32_t qpnp_vbat_sns_comp_result(int64_t *result);
#else
static inline int32_t qpnp_vadc_read(uint32_t channel,
struct qpnp_vadc_result *result)
@@ -1336,6 +1369,8 @@
enum qpnp_vadc_channels channel,
struct qpnp_vadc_result *result)
{ return -ENXIO; }
+static inline int32_t qpnp_vbat_sns_comp_result(int64_t *result)
+{ return -ENXIO; }
#endif
/* Public API */
@@ -1389,6 +1424,7 @@
* @result: 0 on success.
*/
int32_t qpnp_iadc_calibrate_for_trim(void);
+int32_t qpnp_iadc_comp_result(int64_t *result);
#else
static inline int32_t qpnp_iadc_read(enum qpnp_iadc_channels channel,
struct qpnp_iadc_result *result)
@@ -1406,6 +1442,8 @@
{ return -ENXIO; }
static inline int32_t qpnp_iadc_calibrate_for_trim(void)
{ return -ENXIO; }
+static inline int32_t qpnp_iadc_comp_result(int64_t *result, int32_t sign)
+{ return -ENXIO; }
#endif
/* Public API */
diff --git a/include/linux/regulator/cpr-regulator.h b/include/linux/regulator/cpr-regulator.h
index b6fc091..6387913 100644
--- a/include/linux/regulator/cpr-regulator.h
+++ b/include/linux/regulator/cpr-regulator.h
@@ -35,7 +35,6 @@
CPR_CORNER_SVS = 1,
CPR_CORNER_NORMAL,
CPR_CORNER_TURBO,
- CPR_CORNER_SUPER_TURBO,
CPR_CORNER_MAX,
};
diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h
index 4bce95c..323f1e7 100644
--- a/include/linux/videodev2.h
+++ b/include/linux/videodev2.h
@@ -700,7 +700,9 @@
#define V4L2_QCOM_BUF_FLAG_CODECCONFIG 0x4000
#define V4L2_QCOM_BUF_FLAG_EOSEQ 0x8000
#define V4L2_QCOM_BUF_TIMESTAMP_INVALID 0x10000
+#define V4L2_QCOM_BUF_FLAG_IDRFRAME 0x20000 /* Image is a IDR-frame */
#define V4L2_QCOM_BUF_FLAG_DECODEONLY 0x40000
+#define V4L2_QCOM_BUF_DATA_CORRUPT 0x80000
/*
* O V E R L A Y P R E V I E W
@@ -1838,6 +1840,7 @@
V4L2_MPEG_VIDC_INDEX_EXTRADATA_INPUT_CROP,
V4L2_MPEG_VIDC_INDEX_EXTRADATA_DIGITAL_ZOOM,
V4L2_MPEG_VIDC_INDEX_EXTRADATA_ASPECT_RATIO,
+ V4L2_MPEG_VIDC_EXTRADATA_MPEG2_SEQDISP
};
#define V4L2_CID_MPEG_VIDC_VIDEO_H264_VUI_TIMING_INFO \
(V4L2_CID_MPEG_MSM_VIDC_BASE + 23)
diff --git a/include/linux/wcnss_wlan.h b/include/linux/wcnss_wlan.h
index 2319c48..da56df6 100644
--- a/include/linux/wcnss_wlan.h
+++ b/include/linux/wcnss_wlan.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2013, 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
@@ -33,6 +33,7 @@
#define WCNSS_WLAN_IRQ_INVALID -1
#define HAVE_WCNSS_SUSPEND_RESUME_NOTIFY 1
#define HAVE_WCNSS_RESET_INTR 1
+#define HAVE_WCNSS_CAL_DOWNLOAD 1
struct device *wcnss_wlan_get_device(void);
struct resource *wcnss_wlan_get_memory_map(struct device *dev);
@@ -65,7 +66,7 @@
void wcnss_resume_notify(void);
void wcnss_riva_log_debug_regs(void);
void wcnss_pronto_log_debug_regs(void);
-int wcnss_cold_boot_done(void);
+int wcnss_device_ready(void);
#define wcnss_wlan_get_drvdata(dev) dev_get_drvdata(dev)
#define wcnss_wlan_set_drvdata(dev, data) dev_set_drvdata((dev), (data))
diff --git a/include/media/msm_cam_sensor.h b/include/media/msm_cam_sensor.h
index 992649f..47cf184 100644
--- a/include/media/msm_cam_sensor.h
+++ b/include/media/msm_cam_sensor.h
@@ -289,8 +289,8 @@
enum eeprom_cfg_type_t {
CFG_EEPROM_GET_INFO,
- CFG_EEPROM_GET_DATA,
- CFG_EEPROM_READ_DATA,
+ CFG_EEPROM_GET_CAL_DATA,
+ CFG_EEPROM_READ_CAL_DATA,
CFG_EEPROM_WRITE_DATA,
};
struct eeprom_get_t {
diff --git a/include/media/msm_media_info.h b/include/media/msm_media_info.h
index 993a4ab..65831db 100644
--- a/include/media/msm_media_info.h
+++ b/include/media/msm_media_info.h
@@ -7,6 +7,7 @@
enum color_fmts {
COLOR_FMT_NV12,
+ COLOR_FMT_NV21,
};
static inline unsigned int VENUS_Y_STRIDE(int color_fmt, int width)
@@ -16,6 +17,7 @@
goto invalid_input;
switch (color_fmt) {
+ case COLOR_FMT_NV21:
case COLOR_FMT_NV12:
alignment = 128;
stride = MSM_MEDIA_ALIGN(width, alignment);
@@ -34,6 +36,7 @@
goto invalid_input;
switch (color_fmt) {
+ case COLOR_FMT_NV21:
case COLOR_FMT_NV12:
alignment = 128;
stride = MSM_MEDIA_ALIGN(width, alignment);
@@ -52,6 +55,7 @@
goto invalid_input;
switch (color_fmt) {
+ case COLOR_FMT_NV21:
case COLOR_FMT_NV12:
alignment = 32;
sclines = MSM_MEDIA_ALIGN(height, alignment);
@@ -70,6 +74,7 @@
goto invalid_input;
switch (color_fmt) {
+ case COLOR_FMT_NV21:
case COLOR_FMT_NV12:
alignment = 16;
sclines = MSM_MEDIA_ALIGN(((height + 1) >> 1), alignment);
@@ -96,6 +101,7 @@
y_sclines = VENUS_Y_SCANLINES(color_fmt, height);
uv_sclines = VENUS_UV_SCANLINES(color_fmt, height);
switch (color_fmt) {
+ case COLOR_FMT_NV21:
case COLOR_FMT_NV12:
uv_alignment = 4096;
y_plane = y_stride * y_sclines;
diff --git a/include/media/msm_vidc.h b/include/media/msm_vidc.h
index f632ad6..2164275 100644
--- a/include/media/msm_vidc.h
+++ b/include/media/msm_vidc.h
@@ -73,6 +73,16 @@
unsigned int aspect_height;
};
+struct msm_vidc_mpeg2_seqdisp_payload {
+ unsigned int video_format;
+ bool color_descp;
+ unsigned int color_primaries;
+ unsigned int transfer_char;
+ unsigned int matrix_coeffs;
+ unsigned int disp_width;
+ unsigned int disp_height;
+};
+
struct msm_vidc_panscan_window {
unsigned int panscan_height_offset;
unsigned int panscan_width_offset;
@@ -94,6 +104,7 @@
EXTRADATA_FRAME_RATE = 0x00000007,
EXTRADATA_PANSCAN_WINDOW = 0x00000008,
EXTRADATA_RECOVERY_POINT_SEI = 0x00000009,
+ EXTRADATA_MPEG2_SEQDISP = 0x0000000D,
EXTRADATA_MULTISLICE_INFO = 0x7F100000,
EXTRADATA_NUM_CONCEALED_MB = 0x7F100001,
EXTRADATA_INDEX = 0x7F100002,
diff --git a/include/sound/apr_audio-v2.h b/include/sound/apr_audio-v2.h
index 01e30d0..0d3b5a0 100644
--- a/include/sound/apr_audio-v2.h
+++ b/include/sound/apr_audio-v2.h
@@ -2237,7 +2237,7 @@
*/
#define ADSP_MEMORY_MAP_PHYSICAL_MEMORY 0
-
+#define NULL_COPP_TOPOLOGY 0x00010312
#define DEFAULT_COPP_TOPOLOGY 0x00010be3
#define DEFAULT_POPP_TOPOLOGY 0x00010be4
#define VPM_TX_SM_ECNS_COPP_TOPOLOGY 0x00010F71
diff --git a/include/sound/q6adm-v2.h b/include/sound/q6adm-v2.h
index 4bea1e1..449694e 100644
--- a/include/sound/q6adm-v2.h
+++ b/include/sound/q6adm-v2.h
@@ -47,10 +47,10 @@
int adm_memory_unmap_regions(int port_id, uint32_t *buf_add, uint32_t *bufsz,
uint32_t bufcnt);
-int adm_close(int port);
+int adm_close(int port, bool perf_mode);
int adm_matrix_map(int session_id, int path, int num_copps,
- unsigned int *port_id, int copp_id);
+ unsigned int *port_id, int copp_id, bool perf_mode);
int adm_connect_afe_port(int mode, int session_id, int port_id);
diff --git a/include/sound/q6asm-v2.h b/include/sound/q6asm-v2.h
index 0dd14e6..2138689 100644
--- a/include/sound/q6asm-v2.h
+++ b/include/sound/q6asm-v2.h
@@ -70,7 +70,6 @@
#define COMPRESSED_IO 0x0040
#define NT_MODE 0x0400
-
#define NO_TIMESTAMP 0xFF00
#define SET_TIMESTAMP 0x0000
@@ -79,6 +78,19 @@
#define SESSION_MAX 0x08
+/* payload structure bytes */
+#define READDONE_IDX_STATUS 0
+#define READDONE_IDX_BUFADD_LSW 1
+#define READDONE_IDX_BUFADD_MSW 2
+#define READDONE_IDX_MEMMAP_HDL 3
+#define READDONE_IDX_SIZE 4
+#define READDONE_IDX_OFFSET 5
+#define READDONE_IDX_LSW_TS 6
+#define READDONE_IDX_MSW_TS 7
+#define READDONE_IDX_FLAGS 8
+#define READDONE_IDX_NUMFRAMES 9
+#define READDONE_IDX_SEQ_ID 10
+
#define SOFT_PAUSE_PERIOD 30 /* ramp up/down for 30ms */
#define SOFT_PAUSE_STEP 2000 /* Step value 2ms or 2000us */
enum {
@@ -153,6 +165,8 @@
wait_queue_head_t cmd_wait;
wait_queue_head_t time_wait;
bool perf_mode;
+ /* audio cache operations fptr*/
+ int (*fptr_cache_ops)(struct audio_buffer *abuff, int cache_op);
};
void q6asm_audio_client_free(struct audio_client *ac);
diff --git a/include/trace/events/workqueue.h b/include/trace/events/workqueue.h
index 7d49729..82f61f4 100644
--- a/include/trace/events/workqueue.h
+++ b/include/trace/events/workqueue.h
@@ -54,7 +54,7 @@
__entry->function = work->func;
__entry->workqueue = cwq->wq;
__entry->req_cpu = req_cpu;
- __entry->cpu = cwq->gcwq->cpu;
+ __entry->cpu = cwq->pool->gcwq->cpu;
),
TP_printk("work struct=%p function=%pf workqueue=%p req_cpu=%u cpu=%u",
diff --git a/include/video/msm_hdmi_modes.h b/include/video/msm_hdmi_modes.h
index a15272b..ced6acb 100644
--- a/include/video/msm_hdmi_modes.h
+++ b/include/video/msm_hdmi_modes.h
@@ -122,9 +122,10 @@
#define HDMI_EVFRMT_END HDMI_VFRMT_4096x2160p24_16_9
/* VESA DMT TIMINGS */
-#define HDMI_VFRMT_2560x1600p60_16_9 (HDMI_EVFRMT_END + 1)
+#define HDMI_VFRMT_1024x768p60_4_3 (HDMI_EVFRMT_END + 1)
#define HDMI_VFRMT_1280x1024p60_5_4 (HDMI_EVFRMT_END + 2)
-#define VESA_DMT_VFRMT_END HDMI_VFRMT_1280x1024p60_5_4
+#define HDMI_VFRMT_2560x1600p60_16_9 (HDMI_EVFRMT_END + 3)
+#define VESA_DMT_VFRMT_END HDMI_VFRMT_2560x1600p60_16_9
#define HDMI_VFRMT_MAX (VESA_DMT_VFRMT_END + 1)
#define HDMI_VFRMT_FORCE_32BIT 0x7FFFFFFF
@@ -183,6 +184,9 @@
#define HDMI_VFRMT_1920x1080p30_16_9_TIMING \
{HDMI_VFRMT_1920x1080p30_16_9, 1920, 88, 44, 148, false, \
1080, 4, 5, 36, false, 74250, 30000, false, true}
+#define HDMI_VFRMT_1024x768p60_4_3_TIMING \
+ {HDMI_VFRMT_1024x768p60_4_3, 1024, 24, 136, 160, false, \
+ 768, 2, 6, 29, false, 65000, 60000, false, true}
#define HDMI_VFRMT_1280x1024p60_5_4_TIMING \
{HDMI_VFRMT_1280x1024p60_5_4, 1280, 48, 112, 248, false, \
1024, 1, 3, 38, false, 108000, 60000, false, true}
@@ -249,6 +253,7 @@
}
if (type & MSM_HDMI_MODES_DVI) {
+ MSM_HDMI_MODES_SET_TIMING(lut, HDMI_VFRMT_1024x768p60_4_3);
MSM_HDMI_MODES_SET_TIMING(lut, HDMI_VFRMT_1280x1024p60_5_4);
MSM_HDMI_MODES_SET_TIMING(lut, HDMI_VFRMT_2560x1600p60_16_9);
}
@@ -326,8 +331,9 @@
case HDMI_VFRMT_3840x2160p25_16_9: return "3840x2160 p25 16/9";
case HDMI_VFRMT_3840x2160p24_16_9: return "3840x2160 p24 16/9";
case HDMI_VFRMT_4096x2160p24_16_9: return "4096x2160 p24 16/9";
+ case HDMI_VFRMT_1024x768p60_4_3: return "1024x768 p60 4/3";
+ case HDMI_VFRMT_1280x1024p60_5_4: return "1280x1024 p60 5/4";
case HDMI_VFRMT_2560x1600p60_16_9: return "2560x1600 p60 16/9";
- case HDMI_VFRMT_1280x1024p60_5_4: return "1280x1042 p60 5/4";
default: return "???";
}
}
diff --git a/kernel/timer.c b/kernel/timer.c
index 24c5d20..cf7217a 100644
--- a/kernel/timer.c
+++ b/kernel/timer.c
@@ -1679,12 +1679,12 @@
boot_done = 1;
base = &boot_tvec_bases;
}
+ spin_lock_init(&base->lock);
tvec_base_done[cpu] = 1;
} else {
base = per_cpu(tvec_bases, cpu);
}
- spin_lock_init(&base->lock);
for (j = 0; j < TVN_SIZE; j++) {
INIT_LIST_HEAD(base->tv5.vec + j);
diff --git a/kernel/workqueue.c b/kernel/workqueue.c
index 5abf42f..f1a6e9e 100644
--- a/kernel/workqueue.c
+++ b/kernel/workqueue.c
@@ -46,11 +46,12 @@
enum {
/* global_cwq flags */
- GCWQ_MANAGE_WORKERS = 1 << 0, /* need to manage workers */
- GCWQ_MANAGING_WORKERS = 1 << 1, /* managing workers */
- GCWQ_DISASSOCIATED = 1 << 2, /* cpu can't serve workers */
- GCWQ_FREEZING = 1 << 3, /* freeze in progress */
- GCWQ_HIGHPRI_PENDING = 1 << 4, /* highpri works on queue */
+ GCWQ_DISASSOCIATED = 1 << 0, /* cpu can't serve workers */
+ GCWQ_FREEZING = 1 << 1, /* freeze in progress */
+
+ /* pool flags */
+ POOL_MANAGE_WORKERS = 1 << 0, /* need to manage workers */
+ POOL_MANAGING_WORKERS = 1 << 1, /* managing workers */
/* worker flags */
WORKER_STARTED = 1 << 0, /* started */
@@ -72,6 +73,8 @@
TRUSTEE_RELEASE = 3, /* release workers */
TRUSTEE_DONE = 4, /* trustee is done */
+ NR_WORKER_POOLS = 2, /* # worker pools per gcwq */
+
BUSY_WORKER_HASH_ORDER = 6, /* 64 pointers */
BUSY_WORKER_HASH_SIZE = 1 << BUSY_WORKER_HASH_ORDER,
BUSY_WORKER_HASH_MASK = BUSY_WORKER_HASH_SIZE - 1,
@@ -91,6 +94,7 @@
* all cpus. Give -20.
*/
RESCUER_NICE_LEVEL = -20,
+ HIGHPRI_NICE_LEVEL = -20,
};
/*
@@ -115,6 +119,7 @@
*/
struct global_cwq;
+struct worker_pool;
/*
* The poor guys doing the actual heavy lifting. All on-duty workers
@@ -131,7 +136,7 @@
struct cpu_workqueue_struct *current_cwq; /* L: current_work's cwq */
struct list_head scheduled; /* L: scheduled works */
struct task_struct *task; /* I: worker task */
- struct global_cwq *gcwq; /* I: the associated gcwq */
+ struct worker_pool *pool; /* I: the associated pool */
/* 64 bytes boundary on 64bit, 32 on 32bit */
unsigned long last_active; /* L: last active timestamp */
unsigned int flags; /* X: flags */
@@ -139,6 +144,22 @@
struct work_struct rebind_work; /* L: rebind worker to cpu */
};
+struct worker_pool {
+ struct global_cwq *gcwq; /* I: the owning gcwq */
+ unsigned int flags; /* X: flags */
+
+ struct list_head worklist; /* L: list of pending works */
+ int nr_workers; /* L: total number of workers */
+ int nr_idle; /* L: currently idle ones */
+
+ struct list_head idle_list; /* X: list of idle workers */
+ struct timer_list idle_timer; /* L: worker idle timeout */
+ struct timer_list mayday_timer; /* L: SOS timer for workers */
+
+ struct ida worker_ida; /* L: for worker IDs */
+ struct worker *first_idle; /* L: first idle worker */
+};
+
/*
* Global per-cpu workqueue. There's one and only one for each cpu
* and all works are queued and processed here regardless of their
@@ -146,27 +167,18 @@
*/
struct global_cwq {
spinlock_t lock; /* the gcwq lock */
- struct list_head worklist; /* L: list of pending works */
unsigned int cpu; /* I: the associated cpu */
unsigned int flags; /* L: GCWQ_* flags */
- int nr_workers; /* L: total number of workers */
- int nr_idle; /* L: currently idle ones */
-
- /* workers are chained either in the idle_list or busy_hash */
- struct list_head idle_list; /* X: list of idle workers */
+ /* workers are chained either in busy_hash or pool idle_list */
struct hlist_head busy_hash[BUSY_WORKER_HASH_SIZE];
/* L: hash of busy workers */
- struct timer_list idle_timer; /* L: worker idle timeout */
- struct timer_list mayday_timer; /* L: SOS timer for dworkers */
-
- struct ida worker_ida; /* L: for worker IDs */
+ struct worker_pool pools[2]; /* normal and highpri pools */
struct task_struct *trustee; /* L: for gcwq shutdown */
unsigned int trustee_state; /* L: trustee state */
wait_queue_head_t trustee_wait; /* trustee wait */
- struct worker *first_idle; /* L: first idle worker */
} ____cacheline_aligned_in_smp;
/*
@@ -175,7 +187,7 @@
* aligned at two's power of the number of flag bits.
*/
struct cpu_workqueue_struct {
- struct global_cwq *gcwq; /* I: the associated gcwq */
+ struct worker_pool *pool; /* I: the associated pool */
struct workqueue_struct *wq; /* I: the owning workqueue */
int work_color; /* L: current color */
int flush_color; /* L: flushing color */
@@ -264,6 +276,10 @@
#define CREATE_TRACE_POINTS
#include <trace/events/workqueue.h>
+#define for_each_worker_pool(pool, gcwq) \
+ for ((pool) = &(gcwq)->pools[0]; \
+ (pool) < &(gcwq)->pools[NR_WORKER_POOLS]; (pool)++)
+
#define for_each_busy_worker(worker, i, pos, gcwq) \
for (i = 0; i < BUSY_WORKER_HASH_SIZE; i++) \
hlist_for_each_entry(worker, pos, &gcwq->busy_hash[i], hentry)
@@ -444,7 +460,7 @@
* try_to_wake_up(). Put it in a separate cacheline.
*/
static DEFINE_PER_CPU(struct global_cwq, global_cwq);
-static DEFINE_PER_CPU_SHARED_ALIGNED(atomic_t, gcwq_nr_running);
+static DEFINE_PER_CPU_SHARED_ALIGNED(atomic_t, pool_nr_running[NR_WORKER_POOLS]);
/*
* Global cpu workqueue and nr_running counter for unbound gcwq. The
@@ -452,10 +468,17 @@
* workers have WORKER_UNBOUND set.
*/
static struct global_cwq unbound_global_cwq;
-static atomic_t unbound_gcwq_nr_running = ATOMIC_INIT(0); /* always 0 */
+static atomic_t unbound_pool_nr_running[NR_WORKER_POOLS] = {
+ [0 ... NR_WORKER_POOLS - 1] = ATOMIC_INIT(0), /* always 0 */
+};
static int worker_thread(void *__worker);
+static int worker_pool_pri(struct worker_pool *pool)
+{
+ return pool - pool->gcwq->pools;
+}
+
static struct global_cwq *get_gcwq(unsigned int cpu)
{
if (cpu != WORK_CPU_UNBOUND)
@@ -464,12 +487,15 @@
return &unbound_global_cwq;
}
-static atomic_t *get_gcwq_nr_running(unsigned int cpu)
+static atomic_t *get_pool_nr_running(struct worker_pool *pool)
{
+ int cpu = pool->gcwq->cpu;
+ int idx = worker_pool_pri(pool);
+
if (cpu != WORK_CPU_UNBOUND)
- return &per_cpu(gcwq_nr_running, cpu);
+ return &per_cpu(pool_nr_running, cpu)[idx];
else
- return &unbound_gcwq_nr_running;
+ return &unbound_pool_nr_running[idx];
}
static struct cpu_workqueue_struct *get_cwq(unsigned int cpu,
@@ -555,7 +581,7 @@
if (data & WORK_STRUCT_CWQ)
return ((struct cpu_workqueue_struct *)
- (data & WORK_STRUCT_WQ_DATA_MASK))->gcwq;
+ (data & WORK_STRUCT_WQ_DATA_MASK))->pool->gcwq;
cpu = data >> WORK_STRUCT_FLAG_BITS;
if (cpu == WORK_CPU_NONE)
@@ -566,60 +592,62 @@
}
/*
- * Policy functions. These define the policies on how the global
- * worker pool is managed. Unless noted otherwise, these functions
- * assume that they're being called with gcwq->lock held.
+ * Policy functions. These define the policies on how the global worker
+ * pools are managed. Unless noted otherwise, these functions assume that
+ * they're being called with gcwq->lock held.
*/
-static bool __need_more_worker(struct global_cwq *gcwq)
+static bool __need_more_worker(struct worker_pool *pool)
{
- return !atomic_read(get_gcwq_nr_running(gcwq->cpu)) ||
- gcwq->flags & GCWQ_HIGHPRI_PENDING;
+ return !atomic_read(get_pool_nr_running(pool));
}
/*
* Need to wake up a worker? Called from anything but currently
* running workers.
+ *
+ * Note that, because unbound workers never contribute to nr_running, this
+ * function will always return %true for unbound gcwq as long as the
+ * worklist isn't empty.
*/
-static bool need_more_worker(struct global_cwq *gcwq)
+static bool need_more_worker(struct worker_pool *pool)
{
- return !list_empty(&gcwq->worklist) && __need_more_worker(gcwq);
+ return !list_empty(&pool->worklist) && __need_more_worker(pool);
}
/* Can I start working? Called from busy but !running workers. */
-static bool may_start_working(struct global_cwq *gcwq)
+static bool may_start_working(struct worker_pool *pool)
{
- return gcwq->nr_idle;
+ return pool->nr_idle;
}
/* Do I need to keep working? Called from currently running workers. */
-static bool keep_working(struct global_cwq *gcwq)
+static bool keep_working(struct worker_pool *pool)
{
- atomic_t *nr_running = get_gcwq_nr_running(gcwq->cpu);
+ atomic_t *nr_running = get_pool_nr_running(pool);
- return !list_empty(&gcwq->worklist) &&
- (atomic_read(nr_running) <= 1 ||
- gcwq->flags & GCWQ_HIGHPRI_PENDING);
+ return !list_empty(&pool->worklist) && atomic_read(nr_running) <= 1;
}
/* Do we need a new worker? Called from manager. */
-static bool need_to_create_worker(struct global_cwq *gcwq)
+static bool need_to_create_worker(struct worker_pool *pool)
{
- return need_more_worker(gcwq) && !may_start_working(gcwq);
+ return need_more_worker(pool) && !may_start_working(pool);
}
/* Do I need to be the manager? */
-static bool need_to_manage_workers(struct global_cwq *gcwq)
+static bool need_to_manage_workers(struct worker_pool *pool)
{
- return need_to_create_worker(gcwq) || gcwq->flags & GCWQ_MANAGE_WORKERS;
+ return need_to_create_worker(pool) ||
+ (pool->flags & POOL_MANAGE_WORKERS);
}
/* Do we have too many workers and should some go away? */
-static bool too_many_workers(struct global_cwq *gcwq)
+static bool too_many_workers(struct worker_pool *pool)
{
- bool managing = gcwq->flags & GCWQ_MANAGING_WORKERS;
- int nr_idle = gcwq->nr_idle + managing; /* manager is considered idle */
- int nr_busy = gcwq->nr_workers - nr_idle;
+ bool managing = pool->flags & POOL_MANAGING_WORKERS;
+ int nr_idle = pool->nr_idle + managing; /* manager is considered idle */
+ int nr_busy = pool->nr_workers - nr_idle;
return nr_idle > 2 && (nr_idle - 2) * MAX_IDLE_WORKERS_RATIO >= nr_busy;
}
@@ -629,26 +657,26 @@
*/
/* Return the first worker. Safe with preemption disabled */
-static struct worker *first_worker(struct global_cwq *gcwq)
+static struct worker *first_worker(struct worker_pool *pool)
{
- if (unlikely(list_empty(&gcwq->idle_list)))
+ if (unlikely(list_empty(&pool->idle_list)))
return NULL;
- return list_first_entry(&gcwq->idle_list, struct worker, entry);
+ return list_first_entry(&pool->idle_list, struct worker, entry);
}
/**
* wake_up_worker - wake up an idle worker
- * @gcwq: gcwq to wake worker for
+ * @pool: worker pool to wake worker from
*
- * Wake up the first idle worker of @gcwq.
+ * Wake up the first idle worker of @pool.
*
* CONTEXT:
* spin_lock_irq(gcwq->lock).
*/
-static void wake_up_worker(struct global_cwq *gcwq)
+static void wake_up_worker(struct worker_pool *pool)
{
- struct worker *worker = first_worker(gcwq);
+ struct worker *worker = first_worker(pool);
if (likely(worker))
wake_up_process(worker->task);
@@ -670,7 +698,7 @@
struct worker *worker = kthread_data(task);
if (!(worker->flags & WORKER_NOT_RUNNING))
- atomic_inc(get_gcwq_nr_running(cpu));
+ atomic_inc(get_pool_nr_running(worker->pool));
}
/**
@@ -692,8 +720,8 @@
unsigned int cpu)
{
struct worker *worker = kthread_data(task), *to_wakeup = NULL;
- struct global_cwq *gcwq = get_gcwq(cpu);
- atomic_t *nr_running = get_gcwq_nr_running(cpu);
+ struct worker_pool *pool = worker->pool;
+ atomic_t *nr_running = get_pool_nr_running(pool);
if (worker->flags & WORKER_NOT_RUNNING)
return NULL;
@@ -712,8 +740,8 @@
* could be manipulating idle_list, so dereferencing idle_list
* without gcwq lock is safe.
*/
- if (atomic_dec_and_test(nr_running) && !list_empty(&gcwq->worklist))
- to_wakeup = first_worker(gcwq);
+ if (atomic_dec_and_test(nr_running) && !list_empty(&pool->worklist))
+ to_wakeup = first_worker(pool);
return to_wakeup ? to_wakeup->task : NULL;
}
@@ -733,7 +761,7 @@
static inline void worker_set_flags(struct worker *worker, unsigned int flags,
bool wakeup)
{
- struct global_cwq *gcwq = worker->gcwq;
+ struct worker_pool *pool = worker->pool;
WARN_ON_ONCE(worker->task != current);
@@ -744,12 +772,12 @@
*/
if ((flags & WORKER_NOT_RUNNING) &&
!(worker->flags & WORKER_NOT_RUNNING)) {
- atomic_t *nr_running = get_gcwq_nr_running(gcwq->cpu);
+ atomic_t *nr_running = get_pool_nr_running(pool);
if (wakeup) {
if (atomic_dec_and_test(nr_running) &&
- !list_empty(&gcwq->worklist))
- wake_up_worker(gcwq);
+ !list_empty(&pool->worklist))
+ wake_up_worker(pool);
} else
atomic_dec(nr_running);
}
@@ -769,7 +797,7 @@
*/
static inline void worker_clr_flags(struct worker *worker, unsigned int flags)
{
- struct global_cwq *gcwq = worker->gcwq;
+ struct worker_pool *pool = worker->pool;
unsigned int oflags = worker->flags;
WARN_ON_ONCE(worker->task != current);
@@ -783,7 +811,7 @@
*/
if ((flags & WORKER_NOT_RUNNING) && (oflags & WORKER_NOT_RUNNING))
if (!(worker->flags & WORKER_NOT_RUNNING))
- atomic_inc(get_gcwq_nr_running(gcwq->cpu));
+ atomic_inc(get_pool_nr_running(pool));
}
/**
@@ -867,43 +895,6 @@
}
/**
- * gcwq_determine_ins_pos - find insertion position
- * @gcwq: gcwq of interest
- * @cwq: cwq a work is being queued for
- *
- * A work for @cwq is about to be queued on @gcwq, determine insertion
- * position for the work. If @cwq is for HIGHPRI wq, the work is
- * queued at the head of the queue but in FIFO order with respect to
- * other HIGHPRI works; otherwise, at the end of the queue. This
- * function also sets GCWQ_HIGHPRI_PENDING flag to hint @gcwq that
- * there are HIGHPRI works pending.
- *
- * CONTEXT:
- * spin_lock_irq(gcwq->lock).
- *
- * RETURNS:
- * Pointer to inserstion position.
- */
-static inline struct list_head *gcwq_determine_ins_pos(struct global_cwq *gcwq,
- struct cpu_workqueue_struct *cwq)
-{
- struct work_struct *twork;
-
- if (likely(!(cwq->wq->flags & WQ_HIGHPRI)))
- return &gcwq->worklist;
-
- list_for_each_entry(twork, &gcwq->worklist, entry) {
- struct cpu_workqueue_struct *tcwq = get_work_cwq(twork);
-
- if (!(tcwq->wq->flags & WQ_HIGHPRI))
- break;
- }
-
- gcwq->flags |= GCWQ_HIGHPRI_PENDING;
- return &twork->entry;
-}
-
-/**
* insert_work - insert a work into gcwq
* @cwq: cwq @work belongs to
* @work: work to insert
@@ -920,7 +911,7 @@
struct work_struct *work, struct list_head *head,
unsigned int extra_flags)
{
- struct global_cwq *gcwq = cwq->gcwq;
+ struct worker_pool *pool = cwq->pool;
/* we own @work, set data and link */
set_work_cwq(work, cwq, extra_flags);
@@ -940,8 +931,8 @@
*/
smp_mb();
- if (__need_more_worker(gcwq))
- wake_up_worker(gcwq);
+ if (__need_more_worker(pool))
+ wake_up_worker(pool);
}
/*
@@ -1040,7 +1031,7 @@
if (likely(cwq->nr_active < cwq->max_active)) {
trace_workqueue_activate_work(work);
cwq->nr_active++;
- worklist = gcwq_determine_ins_pos(gcwq, cwq);
+ worklist = &cwq->pool->worklist;
} else {
work_flags |= WORK_STRUCT_DELAYED;
worklist = &cwq->delayed_works;
@@ -1189,7 +1180,8 @@
*/
static void worker_enter_idle(struct worker *worker)
{
- struct global_cwq *gcwq = worker->gcwq;
+ struct worker_pool *pool = worker->pool;
+ struct global_cwq *gcwq = pool->gcwq;
BUG_ON(worker->flags & WORKER_IDLE);
BUG_ON(!list_empty(&worker->entry) &&
@@ -1197,22 +1189,27 @@
/* can't use worker_set_flags(), also called from start_worker() */
worker->flags |= WORKER_IDLE;
- gcwq->nr_idle++;
+ pool->nr_idle++;
worker->last_active = jiffies;
/* idle_list is LIFO */
- list_add(&worker->entry, &gcwq->idle_list);
+ list_add(&worker->entry, &pool->idle_list);
if (likely(!(worker->flags & WORKER_ROGUE))) {
- if (too_many_workers(gcwq) && !timer_pending(&gcwq->idle_timer))
- mod_timer(&gcwq->idle_timer,
+ if (too_many_workers(pool) && !timer_pending(&pool->idle_timer))
+ mod_timer(&pool->idle_timer,
jiffies + IDLE_WORKER_TIMEOUT);
} else
wake_up_all(&gcwq->trustee_wait);
- /* sanity check nr_running */
- WARN_ON_ONCE(gcwq->nr_workers == gcwq->nr_idle &&
- atomic_read(get_gcwq_nr_running(gcwq->cpu)));
+ /*
+ * Sanity check nr_running. Because trustee releases gcwq->lock
+ * between setting %WORKER_ROGUE and zapping nr_running, the
+ * warning may trigger spuriously. Check iff trustee is idle.
+ */
+ WARN_ON_ONCE(gcwq->trustee_state == TRUSTEE_DONE &&
+ pool->nr_workers == pool->nr_idle &&
+ atomic_read(get_pool_nr_running(pool)));
}
/**
@@ -1226,11 +1223,11 @@
*/
static void worker_leave_idle(struct worker *worker)
{
- struct global_cwq *gcwq = worker->gcwq;
+ struct worker_pool *pool = worker->pool;
BUG_ON(!(worker->flags & WORKER_IDLE));
worker_clr_flags(worker, WORKER_IDLE);
- gcwq->nr_idle--;
+ pool->nr_idle--;
list_del_init(&worker->entry);
}
@@ -1267,7 +1264,7 @@
static bool worker_maybe_bind_and_lock(struct worker *worker)
__acquires(&gcwq->lock)
{
- struct global_cwq *gcwq = worker->gcwq;
+ struct global_cwq *gcwq = worker->pool->gcwq;
struct task_struct *task = worker->task;
while (true) {
@@ -1309,7 +1306,7 @@
static void worker_rebind_fn(struct work_struct *work)
{
struct worker *worker = container_of(work, struct worker, rebind_work);
- struct global_cwq *gcwq = worker->gcwq;
+ struct global_cwq *gcwq = worker->pool->gcwq;
if (worker_maybe_bind_and_lock(worker))
worker_clr_flags(worker, WORKER_REBIND);
@@ -1334,10 +1331,10 @@
/**
* create_worker - create a new workqueue worker
- * @gcwq: gcwq the new worker will belong to
+ * @pool: pool the new worker will belong to
* @bind: whether to set affinity to @cpu or not
*
- * Create a new worker which is bound to @gcwq. The returned worker
+ * Create a new worker which is bound to @pool. The returned worker
* can be started by calling start_worker() or destroyed using
* destroy_worker().
*
@@ -1347,16 +1344,18 @@
* RETURNS:
* Pointer to the newly created worker.
*/
-static struct worker *create_worker(struct global_cwq *gcwq, bool bind)
+static struct worker *create_worker(struct worker_pool *pool, bool bind)
{
+ struct global_cwq *gcwq = pool->gcwq;
bool on_unbound_cpu = gcwq->cpu == WORK_CPU_UNBOUND;
+ const char *pri = worker_pool_pri(pool) ? "H" : "";
struct worker *worker = NULL;
int id = -1;
spin_lock_irq(&gcwq->lock);
- while (ida_get_new(&gcwq->worker_ida, &id)) {
+ while (ida_get_new(&pool->worker_ida, &id)) {
spin_unlock_irq(&gcwq->lock);
- if (!ida_pre_get(&gcwq->worker_ida, GFP_KERNEL))
+ if (!ida_pre_get(&pool->worker_ida, GFP_KERNEL))
goto fail;
spin_lock_irq(&gcwq->lock);
}
@@ -1366,20 +1365,22 @@
if (!worker)
goto fail;
- worker->gcwq = gcwq;
+ worker->pool = pool;
worker->id = id;
if (!on_unbound_cpu)
worker->task = kthread_create_on_node(worker_thread,
- worker,
- cpu_to_node(gcwq->cpu),
- "kworker/%u:%d", gcwq->cpu, id);
+ worker, cpu_to_node(gcwq->cpu),
+ "kworker/%u:%d%s", gcwq->cpu, id, pri);
else
worker->task = kthread_create(worker_thread, worker,
- "kworker/u:%d", id);
+ "kworker/u:%d%s", id, pri);
if (IS_ERR(worker->task))
goto fail;
+ if (worker_pool_pri(pool))
+ set_user_nice(worker->task, HIGHPRI_NICE_LEVEL);
+
/*
* A rogue worker will become a regular one if CPU comes
* online later on. Make sure every worker has
@@ -1397,7 +1398,7 @@
fail:
if (id >= 0) {
spin_lock_irq(&gcwq->lock);
- ida_remove(&gcwq->worker_ida, id);
+ ida_remove(&pool->worker_ida, id);
spin_unlock_irq(&gcwq->lock);
}
kfree(worker);
@@ -1416,7 +1417,7 @@
static void start_worker(struct worker *worker)
{
worker->flags |= WORKER_STARTED;
- worker->gcwq->nr_workers++;
+ worker->pool->nr_workers++;
worker_enter_idle(worker);
wake_up_process(worker->task);
}
@@ -1432,7 +1433,8 @@
*/
static void destroy_worker(struct worker *worker)
{
- struct global_cwq *gcwq = worker->gcwq;
+ struct worker_pool *pool = worker->pool;
+ struct global_cwq *gcwq = pool->gcwq;
int id = worker->id;
/* sanity check frenzy */
@@ -1440,9 +1442,9 @@
BUG_ON(!list_empty(&worker->scheduled));
if (worker->flags & WORKER_STARTED)
- gcwq->nr_workers--;
+ pool->nr_workers--;
if (worker->flags & WORKER_IDLE)
- gcwq->nr_idle--;
+ pool->nr_idle--;
list_del_init(&worker->entry);
worker->flags |= WORKER_DIE;
@@ -1453,29 +1455,30 @@
kfree(worker);
spin_lock_irq(&gcwq->lock);
- ida_remove(&gcwq->worker_ida, id);
+ ida_remove(&pool->worker_ida, id);
}
-static void idle_worker_timeout(unsigned long __gcwq)
+static void idle_worker_timeout(unsigned long __pool)
{
- struct global_cwq *gcwq = (void *)__gcwq;
+ struct worker_pool *pool = (void *)__pool;
+ struct global_cwq *gcwq = pool->gcwq;
spin_lock_irq(&gcwq->lock);
- if (too_many_workers(gcwq)) {
+ if (too_many_workers(pool)) {
struct worker *worker;
unsigned long expires;
/* idle_list is kept in LIFO order, check the last one */
- worker = list_entry(gcwq->idle_list.prev, struct worker, entry);
+ worker = list_entry(pool->idle_list.prev, struct worker, entry);
expires = worker->last_active + IDLE_WORKER_TIMEOUT;
if (time_before(jiffies, expires))
- mod_timer(&gcwq->idle_timer, expires);
+ mod_timer(&pool->idle_timer, expires);
else {
/* it's been idle for too long, wake up manager */
- gcwq->flags |= GCWQ_MANAGE_WORKERS;
- wake_up_worker(gcwq);
+ pool->flags |= POOL_MANAGE_WORKERS;
+ wake_up_worker(pool);
}
}
@@ -1492,7 +1495,7 @@
return false;
/* mayday mayday mayday */
- cpu = cwq->gcwq->cpu;
+ cpu = cwq->pool->gcwq->cpu;
/* WORK_CPU_UNBOUND can't be set in cpumask, use cpu 0 instead */
if (cpu == WORK_CPU_UNBOUND)
cpu = 0;
@@ -1501,37 +1504,38 @@
return true;
}
-static void gcwq_mayday_timeout(unsigned long __gcwq)
+static void gcwq_mayday_timeout(unsigned long __pool)
{
- struct global_cwq *gcwq = (void *)__gcwq;
+ struct worker_pool *pool = (void *)__pool;
+ struct global_cwq *gcwq = pool->gcwq;
struct work_struct *work;
spin_lock_irq(&gcwq->lock);
- if (need_to_create_worker(gcwq)) {
+ if (need_to_create_worker(pool)) {
/*
* We've been trying to create a new worker but
* haven't been successful. We might be hitting an
* allocation deadlock. Send distress signals to
* rescuers.
*/
- list_for_each_entry(work, &gcwq->worklist, entry)
+ list_for_each_entry(work, &pool->worklist, entry)
send_mayday(work);
}
spin_unlock_irq(&gcwq->lock);
- mod_timer(&gcwq->mayday_timer, jiffies + MAYDAY_INTERVAL);
+ mod_timer(&pool->mayday_timer, jiffies + MAYDAY_INTERVAL);
}
/**
* maybe_create_worker - create a new worker if necessary
- * @gcwq: gcwq to create a new worker for
+ * @pool: pool to create a new worker for
*
- * Create a new worker for @gcwq if necessary. @gcwq is guaranteed to
+ * Create a new worker for @pool if necessary. @pool is guaranteed to
* have at least one idle worker on return from this function. If
* creating a new worker takes longer than MAYDAY_INTERVAL, mayday is
- * sent to all rescuers with works scheduled on @gcwq to resolve
+ * sent to all rescuers with works scheduled on @pool to resolve
* possible allocation deadlock.
*
* On return, need_to_create_worker() is guaranteed to be false and
@@ -1546,52 +1550,54 @@
* false if no action was taken and gcwq->lock stayed locked, true
* otherwise.
*/
-static bool maybe_create_worker(struct global_cwq *gcwq)
+static bool maybe_create_worker(struct worker_pool *pool)
__releases(&gcwq->lock)
__acquires(&gcwq->lock)
{
- if (!need_to_create_worker(gcwq))
+ struct global_cwq *gcwq = pool->gcwq;
+
+ if (!need_to_create_worker(pool))
return false;
restart:
spin_unlock_irq(&gcwq->lock);
/* if we don't make progress in MAYDAY_INITIAL_TIMEOUT, call for help */
- mod_timer(&gcwq->mayday_timer, jiffies + MAYDAY_INITIAL_TIMEOUT);
+ mod_timer(&pool->mayday_timer, jiffies + MAYDAY_INITIAL_TIMEOUT);
while (true) {
struct worker *worker;
- worker = create_worker(gcwq, true);
+ worker = create_worker(pool, true);
if (worker) {
- del_timer_sync(&gcwq->mayday_timer);
+ del_timer_sync(&pool->mayday_timer);
spin_lock_irq(&gcwq->lock);
start_worker(worker);
- BUG_ON(need_to_create_worker(gcwq));
+ BUG_ON(need_to_create_worker(pool));
return true;
}
- if (!need_to_create_worker(gcwq))
+ if (!need_to_create_worker(pool))
break;
__set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(CREATE_COOLDOWN);
- if (!need_to_create_worker(gcwq))
+ if (!need_to_create_worker(pool))
break;
}
- del_timer_sync(&gcwq->mayday_timer);
+ del_timer_sync(&pool->mayday_timer);
spin_lock_irq(&gcwq->lock);
- if (need_to_create_worker(gcwq))
+ if (need_to_create_worker(pool))
goto restart;
return true;
}
/**
* maybe_destroy_worker - destroy workers which have been idle for a while
- * @gcwq: gcwq to destroy workers for
+ * @pool: pool to destroy workers for
*
- * Destroy @gcwq workers which have been idle for longer than
+ * Destroy @pool workers which have been idle for longer than
* IDLE_WORKER_TIMEOUT.
*
* LOCKING:
@@ -1602,19 +1608,19 @@
* false if no action was taken and gcwq->lock stayed locked, true
* otherwise.
*/
-static bool maybe_destroy_workers(struct global_cwq *gcwq)
+static bool maybe_destroy_workers(struct worker_pool *pool)
{
bool ret = false;
- while (too_many_workers(gcwq)) {
+ while (too_many_workers(pool)) {
struct worker *worker;
unsigned long expires;
- worker = list_entry(gcwq->idle_list.prev, struct worker, entry);
+ worker = list_entry(pool->idle_list.prev, struct worker, entry);
expires = worker->last_active + IDLE_WORKER_TIMEOUT;
if (time_before(jiffies, expires)) {
- mod_timer(&gcwq->idle_timer, expires);
+ mod_timer(&pool->idle_timer, expires);
break;
}
@@ -1647,23 +1653,24 @@
*/
static bool manage_workers(struct worker *worker)
{
- struct global_cwq *gcwq = worker->gcwq;
+ struct worker_pool *pool = worker->pool;
+ struct global_cwq *gcwq = pool->gcwq;
bool ret = false;
- if (gcwq->flags & GCWQ_MANAGING_WORKERS)
+ if (pool->flags & POOL_MANAGING_WORKERS)
return ret;
- gcwq->flags &= ~GCWQ_MANAGE_WORKERS;
- gcwq->flags |= GCWQ_MANAGING_WORKERS;
+ pool->flags &= ~POOL_MANAGE_WORKERS;
+ pool->flags |= POOL_MANAGING_WORKERS;
/*
* Destroy and then create so that may_start_working() is true
* on return.
*/
- ret |= maybe_destroy_workers(gcwq);
- ret |= maybe_create_worker(gcwq);
+ ret |= maybe_destroy_workers(pool);
+ ret |= maybe_create_worker(pool);
- gcwq->flags &= ~GCWQ_MANAGING_WORKERS;
+ pool->flags &= ~POOL_MANAGING_WORKERS;
/*
* The trustee might be waiting to take over the manager
@@ -1720,10 +1727,9 @@
{
struct work_struct *work = list_first_entry(&cwq->delayed_works,
struct work_struct, entry);
- struct list_head *pos = gcwq_determine_ins_pos(cwq->gcwq, cwq);
trace_workqueue_activate_work(work);
- move_linked_works(work, pos, NULL);
+ move_linked_works(work, &cwq->pool->worklist, NULL);
__clear_bit(WORK_STRUCT_DELAYED_BIT, work_data_bits(work));
cwq->nr_active++;
}
@@ -1796,7 +1802,8 @@
__acquires(&gcwq->lock)
{
struct cpu_workqueue_struct *cwq = get_work_cwq(work);
- struct global_cwq *gcwq = cwq->gcwq;
+ struct worker_pool *pool = worker->pool;
+ struct global_cwq *gcwq = pool->gcwq;
struct hlist_head *bwh = busy_worker_head(gcwq, work);
bool cpu_intensive = cwq->wq->flags & WQ_CPU_INTENSIVE;
work_func_t f = work->func;
@@ -1836,27 +1843,19 @@
list_del_init(&work->entry);
/*
- * If HIGHPRI_PENDING, check the next work, and, if HIGHPRI,
- * wake up another worker; otherwise, clear HIGHPRI_PENDING.
- */
- if (unlikely(gcwq->flags & GCWQ_HIGHPRI_PENDING)) {
- struct work_struct *nwork = list_first_entry(&gcwq->worklist,
- struct work_struct, entry);
-
- if (!list_empty(&gcwq->worklist) &&
- get_work_cwq(nwork)->wq->flags & WQ_HIGHPRI)
- wake_up_worker(gcwq);
- else
- gcwq->flags &= ~GCWQ_HIGHPRI_PENDING;
- }
-
- /*
* CPU intensive works don't participate in concurrency
* management. They're the scheduler's responsibility.
*/
if (unlikely(cpu_intensive))
worker_set_flags(worker, WORKER_CPU_INTENSIVE, true);
+ /*
+ * Unbound gcwq isn't concurrency managed and work items should be
+ * executed ASAP. Wake up another worker if necessary.
+ */
+ if ((worker->flags & WORKER_UNBOUND) && need_more_worker(pool))
+ wake_up_worker(pool);
+
spin_unlock_irq(&gcwq->lock);
work_clear_pending(work);
@@ -1929,7 +1928,8 @@
static int worker_thread(void *__worker)
{
struct worker *worker = __worker;
- struct global_cwq *gcwq = worker->gcwq;
+ struct worker_pool *pool = worker->pool;
+ struct global_cwq *gcwq = pool->gcwq;
/* tell the scheduler that this is a workqueue worker */
worker->task->flags |= PF_WQ_WORKER;
@@ -1946,11 +1946,11 @@
worker_leave_idle(worker);
recheck:
/* no more worker necessary? */
- if (!need_more_worker(gcwq))
+ if (!need_more_worker(pool))
goto sleep;
/* do we need to manage? */
- if (unlikely(!may_start_working(gcwq)) && manage_workers(worker))
+ if (unlikely(!may_start_working(pool)) && manage_workers(worker))
goto recheck;
/*
@@ -1969,7 +1969,7 @@
do {
struct work_struct *work =
- list_first_entry(&gcwq->worklist,
+ list_first_entry(&pool->worklist,
struct work_struct, entry);
if (likely(!(*work_data_bits(work) & WORK_STRUCT_LINKED))) {
@@ -1981,11 +1981,11 @@
move_linked_works(work, &worker->scheduled, NULL);
process_scheduled_works(worker);
}
- } while (keep_working(gcwq));
+ } while (keep_working(pool));
worker_set_flags(worker, WORKER_PREP, false);
sleep:
- if (unlikely(need_to_manage_workers(gcwq)) && manage_workers(worker))
+ if (unlikely(need_to_manage_workers(pool)) && manage_workers(worker))
goto recheck;
/*
@@ -2043,14 +2043,15 @@
for_each_mayday_cpu(cpu, wq->mayday_mask) {
unsigned int tcpu = is_unbound ? WORK_CPU_UNBOUND : cpu;
struct cpu_workqueue_struct *cwq = get_cwq(tcpu, wq);
- struct global_cwq *gcwq = cwq->gcwq;
+ struct worker_pool *pool = cwq->pool;
+ struct global_cwq *gcwq = pool->gcwq;
struct work_struct *work, *n;
__set_current_state(TASK_RUNNING);
mayday_clear_cpu(cpu, wq->mayday_mask);
/* migrate to the target cpu if possible */
- rescuer->gcwq = gcwq;
+ rescuer->pool = pool;
worker_maybe_bind_and_lock(rescuer);
/*
@@ -2058,7 +2059,7 @@
* process'em.
*/
BUG_ON(!list_empty(&rescuer->scheduled));
- list_for_each_entry_safe(work, n, &gcwq->worklist, entry)
+ list_for_each_entry_safe(work, n, &pool->worklist, entry)
if (get_work_cwq(work) == cwq)
move_linked_works(work, scheduled, &n);
@@ -2069,8 +2070,8 @@
* regular worker; otherwise, we end up with 0 concurrency
* and stalling the execution.
*/
- if (keep_working(gcwq))
- wake_up_worker(gcwq);
+ if (keep_working(pool))
+ wake_up_worker(pool);
spin_unlock_irq(&gcwq->lock);
}
@@ -2195,7 +2196,7 @@
for_each_cwq_cpu(cpu, wq) {
struct cpu_workqueue_struct *cwq = get_cwq(cpu, wq);
- struct global_cwq *gcwq = cwq->gcwq;
+ struct global_cwq *gcwq = cwq->pool->gcwq;
spin_lock_irq(&gcwq->lock);
@@ -2411,9 +2412,9 @@
struct cpu_workqueue_struct *cwq = get_cwq(cpu, wq);
bool drained;
- spin_lock_irq(&cwq->gcwq->lock);
+ spin_lock_irq(&cwq->pool->gcwq->lock);
drained = !cwq->nr_active && list_empty(&cwq->delayed_works);
- spin_unlock_irq(&cwq->gcwq->lock);
+ spin_unlock_irq(&cwq->pool->gcwq->lock);
if (drained)
continue;
@@ -2453,7 +2454,7 @@
*/
smp_rmb();
cwq = get_work_cwq(work);
- if (unlikely(!cwq || gcwq != cwq->gcwq))
+ if (unlikely(!cwq || gcwq != cwq->pool->gcwq))
goto already_gone;
} else if (wait_executing) {
worker = find_worker_executing_work(gcwq, work);
@@ -2971,13 +2972,6 @@
if (flags & WQ_MEM_RECLAIM)
flags |= WQ_RESCUER;
- /*
- * Unbound workqueues aren't concurrency managed and should be
- * dispatched to workers immediately.
- */
- if (flags & WQ_UNBOUND)
- flags |= WQ_HIGHPRI;
-
max_active = max_active ?: WQ_DFL_ACTIVE;
max_active = wq_clamp_max_active(max_active, flags, wq->name);
@@ -2998,9 +2992,10 @@
for_each_cwq_cpu(cpu, wq) {
struct cpu_workqueue_struct *cwq = get_cwq(cpu, wq);
struct global_cwq *gcwq = get_gcwq(cpu);
+ int pool_idx = (bool)(flags & WQ_HIGHPRI);
BUG_ON((unsigned long)cwq & WORK_STRUCT_FLAG_MASK);
- cwq->gcwq = gcwq;
+ cwq->pool = &gcwq->pools[pool_idx];
cwq->wq = wq;
cwq->flush_color = -1;
cwq->max_active = max_active;
@@ -3304,9 +3299,30 @@
__ret1 < 0 ? -1 : 0; \
})
+static bool gcwq_is_managing_workers(struct global_cwq *gcwq)
+{
+ struct worker_pool *pool;
+
+ for_each_worker_pool(pool, gcwq)
+ if (pool->flags & POOL_MANAGING_WORKERS)
+ return true;
+ return false;
+}
+
+static bool gcwq_has_idle_workers(struct global_cwq *gcwq)
+{
+ struct worker_pool *pool;
+
+ for_each_worker_pool(pool, gcwq)
+ if (!list_empty(&pool->idle_list))
+ return true;
+ return false;
+}
+
static int __cpuinit trustee_thread(void *__gcwq)
{
struct global_cwq *gcwq = __gcwq;
+ struct worker_pool *pool;
struct worker *worker;
struct work_struct *work;
struct hlist_node *pos;
@@ -3322,13 +3338,15 @@
* cancelled.
*/
BUG_ON(gcwq->cpu != smp_processor_id());
- rc = trustee_wait_event(!(gcwq->flags & GCWQ_MANAGING_WORKERS));
+ rc = trustee_wait_event(!gcwq_is_managing_workers(gcwq));
BUG_ON(rc < 0);
- gcwq->flags |= GCWQ_MANAGING_WORKERS;
+ for_each_worker_pool(pool, gcwq) {
+ pool->flags |= POOL_MANAGING_WORKERS;
- list_for_each_entry(worker, &gcwq->idle_list, entry)
- worker->flags |= WORKER_ROGUE;
+ list_for_each_entry(worker, &pool->idle_list, entry)
+ worker->flags |= WORKER_ROGUE;
+ }
for_each_busy_worker(worker, i, pos, gcwq)
worker->flags |= WORKER_ROGUE;
@@ -3349,10 +3367,12 @@
* keep_working() are always true as long as the worklist is
* not empty.
*/
- atomic_set(get_gcwq_nr_running(gcwq->cpu), 0);
+ for_each_worker_pool(pool, gcwq)
+ atomic_set(get_pool_nr_running(pool), 0);
spin_unlock_irq(&gcwq->lock);
- del_timer_sync(&gcwq->idle_timer);
+ for_each_worker_pool(pool, gcwq)
+ del_timer_sync(&pool->idle_timer);
spin_lock_irq(&gcwq->lock);
/*
@@ -3374,29 +3394,38 @@
* may be frozen works in freezable cwqs. Don't declare
* completion while frozen.
*/
- while (gcwq->nr_workers != gcwq->nr_idle ||
- gcwq->flags & GCWQ_FREEZING ||
- gcwq->trustee_state == TRUSTEE_IN_CHARGE) {
- int nr_works = 0;
+ while (true) {
+ bool busy = false;
- list_for_each_entry(work, &gcwq->worklist, entry) {
- send_mayday(work);
- nr_works++;
- }
+ for_each_worker_pool(pool, gcwq)
+ busy |= pool->nr_workers != pool->nr_idle;
- list_for_each_entry(worker, &gcwq->idle_list, entry) {
- if (!nr_works--)
- break;
- wake_up_process(worker->task);
- }
+ if (!busy && !(gcwq->flags & GCWQ_FREEZING) &&
+ gcwq->trustee_state != TRUSTEE_IN_CHARGE)
+ break;
- if (need_to_create_worker(gcwq)) {
- spin_unlock_irq(&gcwq->lock);
- worker = create_worker(gcwq, false);
- spin_lock_irq(&gcwq->lock);
- if (worker) {
- worker->flags |= WORKER_ROGUE;
- start_worker(worker);
+ for_each_worker_pool(pool, gcwq) {
+ int nr_works = 0;
+
+ list_for_each_entry(work, &pool->worklist, entry) {
+ send_mayday(work);
+ nr_works++;
+ }
+
+ list_for_each_entry(worker, &pool->idle_list, entry) {
+ if (!nr_works--)
+ break;
+ wake_up_process(worker->task);
+ }
+
+ if (need_to_create_worker(pool)) {
+ spin_unlock_irq(&gcwq->lock);
+ worker = create_worker(pool, false);
+ spin_lock_irq(&gcwq->lock);
+ if (worker) {
+ worker->flags |= WORKER_ROGUE;
+ start_worker(worker);
+ }
}
}
@@ -3411,11 +3440,18 @@
* all workers till we're canceled.
*/
do {
- rc = trustee_wait_event(!list_empty(&gcwq->idle_list));
- while (!list_empty(&gcwq->idle_list))
- destroy_worker(list_first_entry(&gcwq->idle_list,
- struct worker, entry));
- } while (gcwq->nr_workers && rc >= 0);
+ rc = trustee_wait_event(gcwq_has_idle_workers(gcwq));
+
+ i = 0;
+ for_each_worker_pool(pool, gcwq) {
+ while (!list_empty(&pool->idle_list)) {
+ worker = list_first_entry(&pool->idle_list,
+ struct worker, entry);
+ destroy_worker(worker);
+ }
+ i |= pool->nr_workers;
+ }
+ } while (i && rc >= 0);
/*
* At this point, either draining has completed and no worker
@@ -3424,7 +3460,8 @@
* Tell the remaining busy ones to rebind once it finishes the
* currently scheduled works by scheduling the rebind_work.
*/
- WARN_ON(!list_empty(&gcwq->idle_list));
+ for_each_worker_pool(pool, gcwq)
+ WARN_ON(!list_empty(&pool->idle_list));
for_each_busy_worker(worker, i, pos, gcwq) {
struct work_struct *rebind_work = &worker->rebind_work;
@@ -3449,7 +3486,8 @@
}
/* relinquish manager role */
- gcwq->flags &= ~GCWQ_MANAGING_WORKERS;
+ for_each_worker_pool(pool, gcwq)
+ pool->flags &= ~POOL_MANAGING_WORKERS;
/* notify completion */
gcwq->trustee = NULL;
@@ -3491,8 +3529,10 @@
unsigned int cpu = (unsigned long)hcpu;
struct global_cwq *gcwq = get_gcwq(cpu);
struct task_struct *new_trustee = NULL;
- struct worker *uninitialized_var(new_worker);
+ struct worker *new_workers[NR_WORKER_POOLS] = { };
+ struct worker_pool *pool;
unsigned long flags;
+ int i;
action &= ~CPU_TASKS_FROZEN;
@@ -3505,12 +3545,12 @@
kthread_bind(new_trustee, cpu);
/* fall through */
case CPU_UP_PREPARE:
- BUG_ON(gcwq->first_idle);
- new_worker = create_worker(gcwq, false);
- if (!new_worker) {
- if (new_trustee)
- kthread_stop(new_trustee);
- return NOTIFY_BAD;
+ i = 0;
+ for_each_worker_pool(pool, gcwq) {
+ BUG_ON(pool->first_idle);
+ new_workers[i] = create_worker(pool, false);
+ if (!new_workers[i++])
+ goto err_destroy;
}
}
@@ -3527,8 +3567,11 @@
wait_trustee_state(gcwq, TRUSTEE_IN_CHARGE);
/* fall through */
case CPU_UP_PREPARE:
- BUG_ON(gcwq->first_idle);
- gcwq->first_idle = new_worker;
+ i = 0;
+ for_each_worker_pool(pool, gcwq) {
+ BUG_ON(pool->first_idle);
+ pool->first_idle = new_workers[i++];
+ }
break;
case CPU_DYING:
@@ -3545,8 +3588,10 @@
gcwq->trustee_state = TRUSTEE_BUTCHER;
/* fall through */
case CPU_UP_CANCELED:
- destroy_worker(gcwq->first_idle);
- gcwq->first_idle = NULL;
+ for_each_worker_pool(pool, gcwq) {
+ destroy_worker(pool->first_idle);
+ pool->first_idle = NULL;
+ }
break;
case CPU_DOWN_FAILED:
@@ -3563,18 +3608,32 @@
* Put the first_idle in and request a real manager to
* take a look.
*/
- spin_unlock_irq(&gcwq->lock);
- kthread_bind(gcwq->first_idle->task, cpu);
- spin_lock_irq(&gcwq->lock);
- gcwq->flags |= GCWQ_MANAGE_WORKERS;
- start_worker(gcwq->first_idle);
- gcwq->first_idle = NULL;
+ for_each_worker_pool(pool, gcwq) {
+ spin_unlock_irq(&gcwq->lock);
+ kthread_bind(pool->first_idle->task, cpu);
+ spin_lock_irq(&gcwq->lock);
+ pool->flags |= POOL_MANAGE_WORKERS;
+ start_worker(pool->first_idle);
+ pool->first_idle = NULL;
+ }
break;
}
spin_unlock_irqrestore(&gcwq->lock, flags);
return notifier_from_errno(0);
+
+err_destroy:
+ if (new_trustee)
+ kthread_stop(new_trustee);
+
+ spin_lock_irqsave(&gcwq->lock, flags);
+ for (i = 0; i < NR_WORKER_POOLS; i++)
+ if (new_workers[i])
+ destroy_worker(new_workers[i]);
+ spin_unlock_irqrestore(&gcwq->lock, flags);
+
+ return NOTIFY_BAD;
}
#ifdef CONFIG_SMP
@@ -3733,6 +3792,7 @@
for_each_gcwq_cpu(cpu) {
struct global_cwq *gcwq = get_gcwq(cpu);
+ struct worker_pool *pool;
struct workqueue_struct *wq;
spin_lock_irq(&gcwq->lock);
@@ -3754,7 +3814,8 @@
cwq_activate_first_delayed(cwq);
}
- wake_up_worker(gcwq);
+ for_each_worker_pool(pool, gcwq)
+ wake_up_worker(pool);
spin_unlock_irq(&gcwq->lock);
}
@@ -3775,24 +3836,29 @@
/* initialize gcwqs */
for_each_gcwq_cpu(cpu) {
struct global_cwq *gcwq = get_gcwq(cpu);
+ struct worker_pool *pool;
spin_lock_init(&gcwq->lock);
- INIT_LIST_HEAD(&gcwq->worklist);
gcwq->cpu = cpu;
gcwq->flags |= GCWQ_DISASSOCIATED;
- INIT_LIST_HEAD(&gcwq->idle_list);
for (i = 0; i < BUSY_WORKER_HASH_SIZE; i++)
INIT_HLIST_HEAD(&gcwq->busy_hash[i]);
- init_timer_deferrable(&gcwq->idle_timer);
- gcwq->idle_timer.function = idle_worker_timeout;
- gcwq->idle_timer.data = (unsigned long)gcwq;
+ for_each_worker_pool(pool, gcwq) {
+ pool->gcwq = gcwq;
+ INIT_LIST_HEAD(&pool->worklist);
+ INIT_LIST_HEAD(&pool->idle_list);
- setup_timer(&gcwq->mayday_timer, gcwq_mayday_timeout,
- (unsigned long)gcwq);
+ init_timer_deferrable(&pool->idle_timer);
+ pool->idle_timer.function = idle_worker_timeout;
+ pool->idle_timer.data = (unsigned long)pool;
- ida_init(&gcwq->worker_ida);
+ setup_timer(&pool->mayday_timer, gcwq_mayday_timeout,
+ (unsigned long)pool);
+
+ ida_init(&pool->worker_ida);
+ }
gcwq->trustee_state = TRUSTEE_DONE;
init_waitqueue_head(&gcwq->trustee_wait);
@@ -3801,15 +3867,20 @@
/* create the initial worker */
for_each_online_gcwq_cpu(cpu) {
struct global_cwq *gcwq = get_gcwq(cpu);
- struct worker *worker;
+ struct worker_pool *pool;
if (cpu != WORK_CPU_UNBOUND)
gcwq->flags &= ~GCWQ_DISASSOCIATED;
- worker = create_worker(gcwq, true);
- BUG_ON(!worker);
- spin_lock_irq(&gcwq->lock);
- start_worker(worker);
- spin_unlock_irq(&gcwq->lock);
+
+ for_each_worker_pool(pool, gcwq) {
+ struct worker *worker;
+
+ worker = create_worker(pool, true);
+ BUG_ON(!worker);
+ spin_lock_irq(&gcwq->lock);
+ start_worker(worker);
+ spin_unlock_irq(&gcwq->lock);
+ }
}
system_wq = alloc_workqueue("events", 0, 0);
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c
index 59debb7..2356791 100644
--- a/net/netlink/af_netlink.c
+++ b/net/netlink/af_netlink.c
@@ -137,6 +137,8 @@
static DEFINE_RWLOCK(nl_table_lock);
static atomic_t nl_table_users = ATOMIC_INIT(0);
+#define nl_deref_protected(X) rcu_dereference_protected(X, lockdep_is_held(&nl_table_lock));
+
static ATOMIC_NOTIFIER_HEAD(netlink_chain);
static inline u32 netlink_group_mask(u32 group)
@@ -330,6 +332,11 @@
struct hlist_node *node;
unsigned long mask;
unsigned int i;
+ struct listeners *listeners;
+
+ listeners = nl_deref_protected(tbl->listeners);
+ if (!listeners)
+ return;
for (i = 0; i < NLGRPLONGS(tbl->groups); i++) {
mask = 0;
@@ -337,7 +344,7 @@
if (i < NLGRPLONGS(nlk_sk(sk)->ngroups))
mask |= nlk_sk(sk)->groups[i];
}
- tbl->listeners->masks[i] = mask;
+ listeners->masks[i] = mask;
}
/* this function is only called with the netlink table "grabbed", which
* makes sure updates are visible before bind or setsockopt return. */
@@ -518,7 +525,11 @@
if (netlink_is_kernel(sk)) {
BUG_ON(nl_table[sk->sk_protocol].registered == 0);
if (--nl_table[sk->sk_protocol].registered == 0) {
- kfree(nl_table[sk->sk_protocol].listeners);
+ struct listeners *old;
+
+ old = nl_deref_protected(nl_table[sk->sk_protocol].listeners);
+ RCU_INIT_POINTER(nl_table[sk->sk_protocol].listeners, NULL);
+ kfree_rcu(old, rcu);
nl_table[sk->sk_protocol].module = NULL;
nl_table[sk->sk_protocol].registered = 0;
}
@@ -948,7 +959,7 @@
rcu_read_lock();
listeners = rcu_dereference(nl_table[sk->sk_protocol].listeners);
- if (group - 1 < nl_table[sk->sk_protocol].groups)
+ if (listeners && group - 1 < nl_table[sk->sk_protocol].groups)
res = test_bit(group - 1, listeners->masks);
rcu_read_unlock();
@@ -1579,7 +1590,7 @@
new = kzalloc(sizeof(*new) + NLGRPSZ(groups), GFP_ATOMIC);
if (!new)
return -ENOMEM;
- old = rcu_dereference_protected(tbl->listeners, 1);
+ old = nl_deref_protected(tbl->listeners);
memcpy(new->masks, old->masks, NLGRPSZ(tbl->groups));
rcu_assign_pointer(tbl->listeners, new);
diff --git a/sound/soc/codecs/wcd9320.c b/sound/soc/codecs/wcd9320.c
index 845f1a2..5d4f9e6 100644
--- a/sound/soc/codecs/wcd9320.c
+++ b/sound/soc/codecs/wcd9320.c
@@ -488,6 +488,8 @@
bool spkr_pa_widget_on;
struct regulator *spkdrv_reg;
+ bool mbhc_started;
+
struct afe_param_cdc_slimbus_slave_cfg slimbus_slave_cfg;
/* resmgr module */
@@ -2648,7 +2650,9 @@
else if (strnstr(w->name, internal3_text, 30))
snd_soc_update_bits(codec, micb_int_reg, 0x3, 0x3);
- if (micb_ctl_reg == TAIKO_A_MICB_2_CTL)
+ if (taiko->mbhc_started &&
+ taiko->resmgr.pdata->micbias.bias2_is_headset_only &&
+ micb_ctl_reg == TAIKO_A_MICB_2_CTL)
wcd9xxx_resmgr_add_cond_update_bits(&taiko->resmgr,
WCD9XXX_COND_HPH_MIC,
micb_ctl_reg, w->shift,
@@ -2663,7 +2667,9 @@
wcd9xxx_resmgr_notifier_call(&taiko->resmgr, e_post_on);
break;
case SND_SOC_DAPM_POST_PMD:
- if (micb_ctl_reg == TAIKO_A_MICB_2_CTL)
+ if (taiko->mbhc_started &&
+ taiko->resmgr.pdata->micbias.bias2_is_headset_only &&
+ micb_ctl_reg == TAIKO_A_MICB_2_CTL)
wcd9xxx_resmgr_rm_cond_update_bits(&taiko->resmgr,
WCD9XXX_COND_HPH_MIC,
micb_ctl_reg, 7, false);
@@ -5960,8 +5966,12 @@
int taiko_hs_detect(struct snd_soc_codec *codec,
struct wcd9xxx_mbhc_config *mbhc_cfg)
{
+ int rc;
struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
- return wcd9xxx_mbhc_start(&taiko->mbhc, mbhc_cfg);
+ rc = wcd9xxx_mbhc_start(&taiko->mbhc, mbhc_cfg);
+ if (!rc)
+ taiko->mbhc_started = true;
+ return rc;
}
EXPORT_SYMBOL_GPL(taiko_hs_detect);
@@ -6017,13 +6027,20 @@
taiko_init_slim_slave_cfg(codec);
taiko_slim_interface_init_reg(codec);
- wcd9xxx_mbhc_deinit(&taiko->mbhc);
- ret = wcd9xxx_mbhc_init(&taiko->mbhc, &taiko->resmgr, codec,
- WCD9XXX_MBHC_VERSION_TAIKO);
- if (ret)
- pr_err("%s: mbhc init failed %d\n", __func__, ret);
- else
- wcd9xxx_mbhc_start(&taiko->mbhc, taiko->mbhc.mbhc_cfg);
+ if (taiko->mbhc_started) {
+ wcd9xxx_mbhc_deinit(&taiko->mbhc);
+ taiko->mbhc_started = false;
+ ret = wcd9xxx_mbhc_init(&taiko->mbhc, &taiko->resmgr, codec,
+ WCD9XXX_MBHC_VERSION_TAIKO);
+ if (ret) {
+ pr_err("%s: mbhc init failed %d\n", __func__, ret);
+ } else {
+ ret = wcd9xxx_mbhc_start(&taiko->mbhc,
+ taiko->mbhc.mbhc_cfg);
+ if (!ret)
+ taiko->mbhc_started = true;
+ }
+ }
mutex_unlock(&codec->mutex);
return ret;
}
diff --git a/sound/soc/msm/mdm9615.c b/sound/soc/msm/mdm9615.c
index b140b5b..c3967dc 100644
--- a/sound/soc/msm/mdm9615.c
+++ b/sound/soc/msm/mdm9615.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2013, 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
@@ -2336,6 +2336,9 @@
{
int ret;
+ /* Set GPIO headset detection by default */
+ hs_detect_use_gpio = true;
+
if (!cpu_is_msm9615()) {
pr_err("%s: Not the right machine type\n", __func__);
return -ENODEV ;
@@ -2413,8 +2416,6 @@
sif_virt_addr = ioremap(LPASS_SIF_MUX_ADDR, 4);
secpcm_portslc_virt_addr = ioremap(SEC_PCM_PORT_SLC_ADDR, 4);
- hs_detect_use_gpio = true;
-
return ret;
}
module_init(mdm9615_audio_init);
diff --git a/sound/soc/msm/msm8226.c b/sound/soc/msm/msm8226.c
index 08731f6..86e3d75 100644
--- a/sound/soc/msm/msm8226.c
+++ b/sound/soc/msm/msm8226.c
@@ -26,6 +26,7 @@
#include <mach/socinfo.h>
#include <qdsp6v2/msm-pcm-routing-v2.h>
#include "../codecs/wcd9306.h"
+#include <linux/io.h>
#define DRV_NAME "msm8226-asoc-tapan"
@@ -35,15 +36,28 @@
#define BTSCO_RATE_8KHZ 8000
#define BTSCO_RATE_16KHZ 16000
-#define GPIO_AUX_PCM_DOUT 43
-#define GPIO_AUX_PCM_DIN 44
-#define GPIO_AUX_PCM_SYNC 45
-#define GPIO_AUX_PCM_CLK 46
-
#define WCD9XXX_MBHC_DEF_BUTTONS 8
#define WCD9XXX_MBHC_DEF_RLOADS 5
#define TAPAN_EXT_CLK_RATE 9600000
+#define NUM_OF_AUXPCM_GPIOS 4
+
+static int msm8226_auxpcm_rate = 8000;
+static atomic_t auxpcm_rsc_ref;
+static const char *const auxpcm_rate_text[] = {"rate_8000", "rate_16000"};
+static const struct soc_enum msm8226_auxpcm_enum[] = {
+ SOC_ENUM_SINGLE_EXT(2, auxpcm_rate_text),
+};
+
+#define LPAIF_OFFSET 0xFE000000
+#define LPAIF_PRI_MODE_MUXSEL (LPAIF_OFFSET + 0x2B000)
+#define LPAIF_SEC_MODE_MUXSEL (LPAIF_OFFSET + 0x2C000)
+#define LPAIF_TER_MODE_MUXSEL (LPAIF_OFFSET + 0x2D000)
+#define LPAIF_QUAD_MODE_MUXSEL (LPAIF_OFFSET + 0x2E000)
+
+#define I2S_PCM_SEL 1
+#define I2S_PCM_SEL_OFFSET 1
+
void *def_tapan_mbhc_cal(void);
static int msm_snd_enable_codec_ext_clk(struct snd_soc_codec *codec, int enable,
bool dapm);
@@ -62,11 +76,34 @@
.swap_gnd_mic = NULL,
};
+struct msm_auxpcm_gpio {
+ unsigned gpio_no;
+ const char *gpio_name;
+};
+
+struct msm_auxpcm_ctrl {
+ struct msm_auxpcm_gpio *pin_data;
+ u32 cnt;
+};
+
struct msm8226_asoc_mach_data {
int mclk_gpio;
u32 mclk_freq;
+ struct msm_auxpcm_ctrl *auxpcm_ctrl;
};
+#define GPIO_NAME_INDEX 0
+#define DT_PARSE_INDEX 1
+
+static char *msm_auxpcm_gpio_name[][2] = {
+ {"PRIM_AUXPCM_CLK", "qcom,prim-auxpcm-gpio-clk"},
+ {"PRIM_AUXPCM_SYNC", "qcom,prim-auxpcm-gpio-sync"},
+ {"PRIM_AUXPCM_DIN", "qcom,prim-auxpcm-gpio-din"},
+ {"PRIM_AUXPCM_DOUT", "qcom,prim-auxpcm-gpio-dout"},
+};
+
+void *lpaif_pri_muxsel_virt_addr;
+
/* Shared channel numbers for Slimbus ports that connect APQ to MDM. */
enum {
SLIM_1_RX_1 = 145, /* BT-SCO and USB TX */
@@ -292,6 +329,45 @@
return 0;
}
+static int msm8226_auxpcm_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = msm8226_auxpcm_rate;
+ return 0;
+}
+
+static int msm8226_auxpcm_rate_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 0:
+ msm8226_auxpcm_rate = 8000;
+ break;
+ case 1:
+ msm8226_auxpcm_rate = 16000;
+ break;
+ default:
+ msm8226_auxpcm_rate = 8000;
+ break;
+ }
+ return 0;
+}
+
+static int msm_auxpcm_be_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate =
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+
+ struct snd_interval *channels =
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+
+ rate->min = rate->max = msm8226_auxpcm_rate;
+ channels->min = channels->max = 1;
+
+ return 0;
+}
+
static int msm_proxy_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
struct snd_pcm_hw_params *params)
{
@@ -304,6 +380,118 @@
return 0;
}
+static int msm_aux_pcm_get_gpios(struct msm_auxpcm_ctrl *auxpcm_ctrl)
+{
+ struct msm_auxpcm_gpio *pin_data = NULL;
+ int ret = 0;
+ int i;
+ int j;
+
+ pin_data = auxpcm_ctrl->pin_data;
+ if (!pin_data) {
+ pr_err("%s: Invalid control data for AUXPCM\n", __func__);
+ ret = -EINVAL;
+ goto err;
+ }
+ for (i = 0; i < auxpcm_ctrl->cnt; i++, pin_data++) {
+ ret = gpio_request(pin_data->gpio_no,
+ pin_data->gpio_name);
+ pr_debug("%s: gpio = %d, gpio name = %s\n"
+ "ret = %d\n", __func__,
+ pin_data->gpio_no,
+ pin_data->gpio_name,
+ ret);
+ if (ret) {
+ pr_err("%s: Failed to request gpio %d\n",
+ __func__, pin_data->gpio_no);
+ /* Release all GPIOs on failure */
+ if (i > 0) {
+ for (j = i; j >= 0; j--)
+ gpio_free(pin_data->gpio_no);
+ }
+ goto err;
+ }
+ }
+err:
+ return ret;
+}
+
+static int msm_aux_pcm_free_gpios(struct msm_auxpcm_ctrl *auxpcm_ctrl)
+{
+ struct msm_auxpcm_gpio *pin_data = NULL;
+ int i;
+ int ret = 0;
+
+ if (auxpcm_ctrl == NULL || auxpcm_ctrl->pin_data == NULL) {
+ pr_err("%s: Invalid control data for AUXPCM\n", __func__);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ pin_data = auxpcm_ctrl->pin_data;
+ for (i = 0; i < auxpcm_ctrl->cnt; i++, pin_data++) {
+ gpio_free(pin_data->gpio_no);
+ pr_debug("%s: gpio = %d, gpio_name = %s\n",
+ __func__, pin_data->gpio_no,
+ pin_data->gpio_name);
+ }
+err:
+ return ret;
+}
+
+static int msm_auxpcm_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_card *card = rtd->card;
+ struct msm8226_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card);
+ struct msm_auxpcm_ctrl *auxpcm_ctrl = NULL;
+ int ret = 0;
+
+ pr_debug("%s(): substream = %s, auxpcm_rsc_ref counter = %d\n",
+ __func__, substream->name, atomic_read(&auxpcm_rsc_ref));
+
+ auxpcm_ctrl = pdata->auxpcm_ctrl;
+
+ if (auxpcm_ctrl == NULL || auxpcm_ctrl->pin_data == NULL ||
+ lpaif_pri_muxsel_virt_addr == NULL) {
+ pr_err("%s: Invalid control data for AUXPCM\n", __func__);
+ ret = -EINVAL;
+ goto err;
+ }
+ if (atomic_inc_return(&auxpcm_rsc_ref) == 1) {
+ iowrite32(I2S_PCM_SEL << I2S_PCM_SEL_OFFSET,
+ lpaif_pri_muxsel_virt_addr);
+ ret = msm_aux_pcm_get_gpios(auxpcm_ctrl);
+ }
+ if (ret < 0) {
+ pr_err("%s: Aux PCM GPIO request failed\n", __func__);
+ return -EINVAL;
+ }
+err:
+ return ret;
+}
+
+static void msm_auxpcm_shutdown(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_card *card = rtd->card;
+ struct msm8226_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card);
+ struct msm_auxpcm_ctrl *auxpcm_ctrl = NULL;
+
+ pr_debug("%s(): substream = %s, auxpcm_rsc_ref counter = %d\n",
+ __func__, substream->name, atomic_read(&auxpcm_rsc_ref));
+
+ auxpcm_ctrl = pdata->auxpcm_ctrl;
+
+ if (atomic_dec_return(&auxpcm_rsc_ref) == 0)
+ msm_aux_pcm_free_gpios(auxpcm_ctrl);
+}
+
+static struct snd_soc_ops msm_auxpcm_be_ops = {
+ .startup = msm_auxpcm_startup,
+ .shutdown = msm_auxpcm_shutdown,
+};
+
static int msm_slim_0_rx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
struct snd_pcm_hw_params *params)
{
@@ -358,6 +546,8 @@
msm_slim_0_rx_ch_get, msm_slim_0_rx_ch_put),
SOC_ENUM_EXT("SLIM_0_TX Channels", msm_snd_enum[1],
msm_slim_0_tx_ch_get, msm_slim_0_tx_ch_put),
+ SOC_ENUM_EXT("AUX PCM SampleRate", msm8226_auxpcm_enum[0],
+ msm8226_auxpcm_rate_get, msm8226_auxpcm_rate_put),
};
static int msm_audrx_init(struct snd_soc_pcm_runtime *rtd)
@@ -893,6 +1083,35 @@
.codec_dai_name = "snd-soc-dummy-dai",
.codec_name = "snd-soc-dummy",
},
+ /* AUX PCM Backend DAI Links */
+ {
+ .name = LPASS_BE_AUXPCM_RX,
+ .stream_name = "AUX PCM Playback",
+ .cpu_dai_name = "msm-dai-q6.4106",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .be_id = MSM_BACKEND_DAI_AUXPCM_RX,
+ .be_hw_params_fixup = msm_auxpcm_be_params_fixup,
+ .ops = &msm_auxpcm_be_ops,
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1
+ /* this dainlink has playback support */
+ },
+ {
+ .name = LPASS_BE_AUXPCM_TX,
+ .stream_name = "AUX PCM Capture",
+ .cpu_dai_name = "msm-dai-q6.4107",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .be_id = MSM_BACKEND_DAI_AUXPCM_TX,
+ .be_hw_params_fixup = msm_auxpcm_be_params_fixup,
+ .ops = &msm_auxpcm_be_ops,
+ .ignore_suspend = 1
+ },
/* Backend DAI Links */
{
.name = LPASS_BE_SLIMBUS_0_RX,
@@ -1053,6 +1272,70 @@
.num_links = ARRAY_SIZE(msm8226_dai),
};
+static int msm8226_dtparse_auxpcm(struct platform_device *pdev,
+ struct msm_auxpcm_ctrl **auxpcm_ctrl,
+ char *msm_auxpcm_gpio_name[][2])
+{
+ int ret = 0;
+ int i = 0;
+ struct msm_auxpcm_gpio *pin_data = NULL;
+ struct msm_auxpcm_ctrl *ctrl;
+ unsigned int gpio_no[NUM_OF_AUXPCM_GPIOS];
+ enum of_gpio_flags flags = OF_GPIO_ACTIVE_LOW;
+ int auxpcm_cnt = 0;
+
+ pin_data = devm_kzalloc(&pdev->dev, (ARRAY_SIZE(gpio_no) *
+ sizeof(struct msm_auxpcm_gpio)),
+ GFP_KERNEL);
+ if (!pin_data) {
+ dev_err(&pdev->dev, "No memory for gpio\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(gpio_no); i++) {
+ gpio_no[i] = of_get_named_gpio_flags(pdev->dev.of_node,
+ msm_auxpcm_gpio_name[i][DT_PARSE_INDEX],
+ 0, &flags);
+
+ if (gpio_no[i] > 0) {
+ pin_data[i].gpio_name =
+ msm_auxpcm_gpio_name[auxpcm_cnt][GPIO_NAME_INDEX];
+ pin_data[i].gpio_no = gpio_no[i];
+ dev_dbg(&pdev->dev, "%s:GPIO gpio[%s] =\n"
+ "0x%x\n", __func__,
+ pin_data[i].gpio_name,
+ pin_data[i].gpio_no);
+ auxpcm_cnt++;
+ } else {
+ dev_err(&pdev->dev, "%s:Invalid AUXPCM GPIO[%s]= %x\n",
+ __func__,
+ msm_auxpcm_gpio_name[i][GPIO_NAME_INDEX],
+ gpio_no[i]);
+ ret = -ENODEV;
+ goto err;
+ }
+ }
+
+ ctrl = devm_kzalloc(&pdev->dev,
+ sizeof(struct msm_auxpcm_ctrl), GFP_KERNEL);
+ if (!ctrl) {
+ dev_err(&pdev->dev, "No memory for gpio\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ ctrl->pin_data = pin_data;
+ ctrl->cnt = auxpcm_cnt;
+ *auxpcm_ctrl = ctrl;
+ return ret;
+
+err:
+ if (pin_data)
+ devm_kfree(&pdev->dev, pin_data);
+ return ret;
+}
+
static int msm8226_prepare_codec_mclk(struct snd_soc_card *card)
{
struct msm8226_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card);
@@ -1074,6 +1357,7 @@
struct snd_soc_card *card = &snd_soc_card_msm8226;
struct msm8226_asoc_mach_data *pdata;
int ret;
+ const char *auxpcm_pri_gpio_set = NULL;
if (!pdev->dev.of_node) {
dev_err(&pdev->dev, "No platform supplied from device tree\n");
@@ -1088,6 +1372,15 @@
goto err;
}
+ /* Parse AUXPCM info from DT */
+ ret = msm8226_dtparse_auxpcm(pdev, &pdata->auxpcm_ctrl,
+ msm_auxpcm_gpio_name);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "%s: Auxpcm pin data parse failed\n", __func__);
+ goto err;
+ }
+
card->dev = &pdev->dev;
platform_set_drvdata(pdev, card);
snd_soc_card_set_drvdata(card, pdata);
@@ -1146,6 +1439,9 @@
}
}
+ mbhc_cfg.gpio_level_insert = of_property_read_bool(pdev->dev.of_node,
+ "qcom,headset-jack-type-NO");
+
ret = msm8226_prepare_codec_mclk(card);
if (ret)
goto err_vdd_spkr;
@@ -1158,6 +1454,30 @@
}
mutex_init(&cdc_mclk_mutex);
+ ret = of_property_read_string(pdev->dev.of_node,
+ "qcom,prim-auxpcm-gpio-set", &auxpcm_pri_gpio_set);
+ if (ret) {
+ dev_err(&pdev->dev, "Looking up %s property in node %s failed",
+ "qcom,prim-auxpcm-gpio-set",
+ pdev->dev.of_node->full_name);
+ goto err_vdd_spkr;
+ }
+ if (!strcmp(auxpcm_pri_gpio_set, "prim-gpio-prim")) {
+ lpaif_pri_muxsel_virt_addr = ioremap(LPAIF_PRI_MODE_MUXSEL, 4);
+ } else if (!strcmp(auxpcm_pri_gpio_set, "prim-gpio-tert")) {
+ lpaif_pri_muxsel_virt_addr = ioremap(LPAIF_TER_MODE_MUXSEL, 4);
+ } else {
+ dev_err(&pdev->dev, "Invalid value %s for AUXPCM GPIO set\n",
+ auxpcm_pri_gpio_set);
+ ret = -EINVAL;
+ goto err_vdd_spkr;
+ }
+ if (lpaif_pri_muxsel_virt_addr == NULL) {
+ pr_err("%s Pri muxsel virt addr is null\n", __func__);
+ ret = -EINVAL;
+ goto err_vdd_spkr;
+ }
+
return 0;
err_vdd_spkr:
diff --git a/sound/soc/msm/msm8974.c b/sound/soc/msm/msm8974.c
index 51334f2..945840d 100644
--- a/sound/soc/msm/msm8974.c
+++ b/sound/soc/msm/msm8974.c
@@ -1260,6 +1260,22 @@
return 0;
}
+static int msm_slim_4_tx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+
+ pr_debug("%s()\n", __func__);
+ rate->min = rate->max = 48000;
+ channels->min = channels->max = 2;
+
+ return 0;
+}
+
static int msm_slim_5_tx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
struct snd_pcm_hw_params *params)
{
@@ -2195,7 +2211,7 @@
.codec_name = "taiko_codec",
.codec_dai_name = "taiko_vifeedback",
.be_id = MSM_BACKEND_DAI_SLIMBUS_4_TX,
- .be_hw_params_fixup = msm_slim_0_tx_be_hw_params_fixup,
+ .be_hw_params_fixup = msm_slim_4_tx_be_hw_params_fixup,
.ops = &msm8974_be_ops,
.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
.ignore_suspend = 1,
@@ -2388,7 +2404,7 @@
{
struct msm8974_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card);
int ret;
- if (pdata->us_euro_gpio) {
+ if (pdata->us_euro_gpio >= 0) {
dev_dbg(card->dev, "%s : us_euro gpio request %d", __func__,
pdata->us_euro_gpio);
ret = gpio_request(pdata->us_euro_gpio, "TAIKO_CODEC_US_EURO");
@@ -2524,7 +2540,7 @@
pdata->us_euro_gpio = of_get_named_gpio(pdev->dev.of_node,
"qcom,us-euro-gpios", 0);
if (pdata->us_euro_gpio < 0) {
- dev_err(&pdev->dev, "Looking up %s property in node %s failed",
+ dev_info(&pdev->dev, "property %s not detected in node %s",
"qcom,us-euro-gpios",
pdev->dev.of_node->full_name);
} else {
diff --git a/sound/soc/msm/qdsp6v2/audio_ocmem.c b/sound/soc/msm/qdsp6v2/audio_ocmem.c
index 08d7277..bedaba0 100644
--- a/sound/soc/msm/qdsp6v2/audio_ocmem.c
+++ b/sound/soc/msm/qdsp6v2/audio_ocmem.c
@@ -243,7 +243,7 @@
struct ocmem_buf *buf = NULL;
struct avcs_cmd_rsp_get_low_power_segments_info_t *lp_segptr;
- pr_debug("%s\n", __func__);
+ pr_debug("%s, %p\n", __func__, &audio_ocmem_lcl);
atomic_set(&audio_ocmem_lcl.audio_state, OCMEM_STATE_DEFAULT);
if (audio_ocmem_lcl.lp_memseg_ptr == NULL) {
/* Retrieve low power segments */
@@ -329,6 +329,7 @@
if (ret) {
pr_err("%s: ocmem_map failed\n", __func__);
atomic_set(&audio_ocmem_lcl.audio_state, OCMEM_STATE_MAP_FAIL);
+ goto fail_cmd1;
}
wait_event_interruptible(audio_ocmem_lcl.audio_wait,
@@ -372,7 +373,7 @@
pr_err("%s: ocmem_unmap failed, state[%d]\n",
__func__,
atomic_read(&audio_ocmem_lcl.audio_state));
- goto fail_cmd;
+ goto fail_cmd1;
}
wait_event_interruptible(audio_ocmem_lcl.audio_wait,
@@ -384,7 +385,7 @@
pr_err("%s: ocmem_shrink failed, state[%d]\n",
__func__,
atomic_read(&audio_ocmem_lcl.audio_state));
- goto fail_cmd;
+ goto fail_cmd1;
}
atomic_set(&audio_ocmem_lcl.audio_cond, 1);
clear_bit_pos(audio_ocmem_lcl.audio_state,
@@ -405,7 +406,7 @@
pr_err("%s: ocmem_map failed, state[%d]\n",
__func__,
atomic_read(&audio_ocmem_lcl.audio_state));
- goto fail_cmd;
+ goto fail_cmd1;
}
wait_event_interruptible(audio_ocmem_lcl.audio_wait,
(atomic_read(&audio_ocmem_lcl.audio_state) &
@@ -428,7 +429,7 @@
pr_err("%s: ocmem_unmap failed, state[0x%x]\n",
__func__,
atomic_read(&audio_ocmem_lcl.audio_state));
- goto fail_cmd;
+ goto fail_cmd1;
}
wait_event_interruptible(
audio_ocmem_lcl.audio_wait,
@@ -446,14 +447,16 @@
pr_err("%s: ocmem_shrink failed, state[0x%x]\n",
__func__,
atomic_read(&audio_ocmem_lcl.audio_state));
- goto fail_cmd;
+ goto fail_cmd1;
}
clear_bit_pos(audio_ocmem_lcl.audio_state,
OCMEM_STATE_SHRINK);
}
- pr_debug("%s: calling ocmem free\n", __func__);
+ pr_debug("%s: calling ocmem free, state:0x%x\n",
+ __func__,
+ atomic_read(&audio_ocmem_lcl.audio_state));
ret = ocmem_free(OCMEM_LP_AUDIO, audio_ocmem_lcl.buf);
if (ret == -EAGAIN) {
pr_debug("%s: received EAGAIN\n", __func__);
@@ -466,7 +469,7 @@
pr_err("%s: ocmem_shrink failed, state[0x%x]\n",
__func__,
atomic_read(&audio_ocmem_lcl.audio_state));
- goto fail_cmd;
+ goto fail_cmd1;
}
pr_debug("calling free after EAGAIN");
ret = ocmem_free(OCMEM_LP_AUDIO,
@@ -474,19 +477,19 @@
if (ret) {
pr_err("%s: ocmem_free failed\n",
__func__);
- goto fail_cmd;
+ goto fail_cmd2;
}
} else {
pr_debug("%s: shrink callback already processed\n",
__func__);
- goto fail_cmd;
+ goto fail_cmd1;
}
} else if (ret) {
pr_err("%s: ocmem_free failed, state[0x%x], ret:%d\n",
__func__,
atomic_read(&audio_ocmem_lcl.audio_state),
ret);
- goto fail_cmd;
+ goto fail_cmd2;
}
pr_debug("%s: ocmem_free success\n", __func__);
/* Fall through */
@@ -508,6 +511,14 @@
mutex_unlock(&audio_ocmem_lcl.state_process_lock);
}
ret = 0;
+ goto fail_cmd;
+
+fail_cmd1:
+ ret = ocmem_free(OCMEM_LP_AUDIO, audio_ocmem_lcl.buf);
+ if (ret)
+ pr_err("%s: ocmem_free failed\n", __func__);
+fail_cmd2:
+ mutex_unlock(&audio_ocmem_lcl.state_process_lock);
fail_cmd:
pr_debug("%s: exit\n", __func__);
audio_ocmem_lcl.audio_ocmem_running = false;
diff --git a/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c
index 9359ed7..ec5359c 100644
--- a/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c
@@ -29,6 +29,7 @@
#include <sound/pcm_params.h>
#include <asm/dma.h>
#include <linux/dma-mapping.h>
+#include <linux/msm_audio_ion.h>
#include <sound/timer.h>
@@ -39,7 +40,10 @@
#define COMPRE_CAPTURE_NUM_PERIODS 16
/* Allocate the worst case frame size for compressed audio */
#define COMPRE_CAPTURE_HEADER_SIZE (sizeof(struct snd_compr_audio_info))
-#define COMPRE_CAPTURE_MAX_FRAME_SIZE (6144)
+/* Changing period size to 4032. 4032 will make sure COMPRE_CAPTURE_PERIOD_SIZE
+ * is 4096 with meta data size of 64 and MAX_NUM_FRAMES_PER_BUFFER 1
+ */
+#define COMPRE_CAPTURE_MAX_FRAME_SIZE (4032)
#define COMPRE_CAPTURE_PERIOD_SIZE ((COMPRE_CAPTURE_MAX_FRAME_SIZE + \
COMPRE_CAPTURE_HEADER_SIZE) * \
MAX_NUM_FRAMES_PER_BUFFER)
@@ -226,7 +230,7 @@
prtd->pcm_irq_pos);
memcpy(prtd->audio_client->port[OUT].buf->data +
- prtd->pcm_irq_pos, (ptrmem + 2),
+ prtd->pcm_irq_pos, (ptrmem + READDONE_IDX_SIZE),
COMPRE_CAPTURE_HEADER_SIZE);
pr_debug("buf = %p, updated data = 0x%X, *data = %p\n",
prtd->audio_client->port[OUT].buf,
@@ -235,9 +239,10 @@
prtd->audio_client->port[OUT].buf->data);
if (!atomic_read(&prtd->start))
break;
- pr_debug("frame size=%d, buffer = 0x%X\n", ptrmem[2],
- ptrmem[1]);
- if (ptrmem[2] > COMPRE_CAPTURE_MAX_FRAME_SIZE) {
+ pr_debug("frame size=%d, buffer = 0x%X\n",
+ ptrmem[READDONE_IDX_SIZE],
+ ptrmem[READDONE_IDX_BUFADD_LSW]);
+ if (ptrmem[READDONE_IDX_SIZE] > COMPRE_CAPTURE_MAX_FRAME_SIZE) {
pr_err("Frame length exceeded the max length");
break;
}
@@ -546,7 +551,7 @@
{
pr_debug("%s\n", __func__);
/* MP3 Block */
- compr->info.compr_cap.num_codecs = 4;
+ compr->info.compr_cap.num_codecs = 5;
compr->info.compr_cap.min_fragment_size = runtime->hw.period_bytes_min;
compr->info.compr_cap.max_fragment_size = runtime->hw.period_bytes_max;
compr->info.compr_cap.min_fragments = runtime->hw.periods_min;
@@ -555,6 +560,7 @@
compr->info.compr_cap.codecs[1] = SND_AUDIOCODEC_AAC;
compr->info.compr_cap.codecs[2] = SND_AUDIOCODEC_AC3;
compr->info.compr_cap.codecs[3] = SND_AUDIOCODEC_EAC3;
+ compr->info.compr_cap.codecs[4] = SND_AUDIOCODEC_AMRWB;
/* Add new codecs here */
}
@@ -723,25 +729,14 @@
static int msm_compr_mmap(struct snd_pcm_substream *substream,
struct vm_area_struct *vma)
{
- int result = 0;
struct snd_pcm_runtime *runtime = substream->runtime;
- struct compr_audio *compr = runtime->private_data;
- struct msm_audio *prtd = &compr->prtd;
-
- pr_debug("%s\n", __func__);
+ struct msm_audio *prtd = runtime->private_data;
+ struct audio_client *ac = prtd->audio_client;
+ struct audio_port_data *apd = ac->port;
+ struct audio_buffer *ab = &(apd[IN].buf[0]);
prtd->mmap_flag = 1;
- runtime->render_flag = SNDRV_NON_DMA_MODE;
- if (runtime->dma_addr && runtime->dma_bytes) {
- vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
- result = remap_pfn_range(vma, vma->vm_start,
- runtime->dma_addr >> PAGE_SHIFT,
- runtime->dma_bytes,
- vma->vm_page_prot);
- } else {
- pr_err("Physical address or size of buf is NULL");
- return -EINVAL;
- }
- return result;
+
+ return msm_audio_ion_mmap(ab, vma);
}
static int msm_compr_hw_params(struct snd_pcm_substream *substream,
diff --git a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c
index 04b090a..04a0a84 100644
--- a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c
@@ -1358,16 +1358,25 @@
SOC_ENUM_EXT("PRI MI2S RX Format", mi2s_config_enum[0],
msm_dai_q6_mi2s_format_get,
msm_dai_q6_mi2s_format_put),
- SOC_ENUM_EXT("SEC RX Format", mi2s_config_enum[0],
+ SOC_ENUM_EXT("SEC MI2S RX Format", mi2s_config_enum[0],
+ msm_dai_q6_mi2s_format_get,
+ msm_dai_q6_mi2s_format_put),
+ SOC_ENUM_EXT("TERT MI2S RX Format", mi2s_config_enum[0],
+ msm_dai_q6_mi2s_format_get,
+ msm_dai_q6_mi2s_format_put),
+ SOC_ENUM_EXT("QUAT MI2S RX Format", mi2s_config_enum[0],
msm_dai_q6_mi2s_format_get,
msm_dai_q6_mi2s_format_put),
SOC_ENUM_EXT("PRI MI2S TX Format", mi2s_config_enum[0],
msm_dai_q6_mi2s_format_get,
msm_dai_q6_mi2s_format_put),
- SOC_ENUM_EXT("SEC MI2S RX Format", mi2s_config_enum[0],
+ SOC_ENUM_EXT("SEC MI2S TX Format", mi2s_config_enum[0],
msm_dai_q6_mi2s_format_get,
msm_dai_q6_mi2s_format_put),
- SOC_ENUM_EXT("SEC MI2S TX Format", mi2s_config_enum[0],
+ SOC_ENUM_EXT("TERT MI2S TX Format", mi2s_config_enum[0],
+ msm_dai_q6_mi2s_format_get,
+ msm_dai_q6_mi2s_format_put),
+ SOC_ENUM_EXT("QUAT MI2S TX Format", mi2s_config_enum[0],
msm_dai_q6_mi2s_format_get,
msm_dai_q6_mi2s_format_put),
};
@@ -1384,6 +1393,10 @@
if (!strncmp(dai->name, "msm-dai-q6-mi2s.0", 17))
ctrl = &mi2s_config_controls[0];
if (!strncmp(dai->name, "msm-dai-q6-mi2s.1", 17))
+ ctrl = &mi2s_config_controls[1];
+ if (!strncmp(dai->name, "msm-dai-q6-mi2s.2", 17))
+ ctrl = &mi2s_config_controls[2];
+ if (!strncmp(dai->name, "msm-dai-q6-mi2s.3", 17))
ctrl = &mi2s_config_controls[3];
}
@@ -1402,9 +1415,13 @@
ctrl = NULL;
if (mi2s_dai_data->tx_dai.mi2s_dai_data.port_config.i2s.channel_mode) {
if (!strncmp(dai->name, "msm-dai-q6-mi2s.0", 17))
- ctrl = &mi2s_config_controls[2];
- if (!strncmp(dai->name, "msm-dai-q6-mi2s.1", 17))
ctrl = &mi2s_config_controls[4];
+ if (!strncmp(dai->name, "msm-dai-q6-mi2s.1", 17))
+ ctrl = &mi2s_config_controls[5];
+ if (!strncmp(dai->name, "msm-dai-q6-mi2s.2", 17))
+ ctrl = &mi2s_config_controls[6];
+ if (!strncmp(dai->name, "msm-dai-q6-mi2s.3", 17))
+ ctrl = &mi2s_config_controls[7];
}
if (ctrl) {
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-lpa-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-lpa-v2.c
index 27b3f56..7055c57 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-lpa-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-lpa-v2.c
@@ -29,6 +29,8 @@
#include <linux/dma-mapping.h>
#include <linux/of_device.h>
+#include <linux/msm_audio_ion.h>
+
#include <sound/compress_params.h>
#include <sound/compress_offload.h>
#include <sound/compress_driver.h>
@@ -421,24 +423,14 @@
static int msm_pcm_mmap(struct snd_pcm_substream *substream,
struct vm_area_struct *vma)
{
- int result = 0;
struct snd_pcm_runtime *runtime = substream->runtime;
struct msm_audio *prtd = runtime->private_data;
-
- pr_debug("%s\n", __func__);
+ struct audio_client *ac = prtd->audio_client;
+ struct audio_port_data *apd = ac->port;
+ struct audio_buffer *ab = &(apd[IN].buf[0]);
prtd->mmap_flag = 1;
- if (runtime->dma_addr && runtime->dma_bytes) {
- vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
- result = remap_pfn_range(vma, vma->vm_start,
- runtime->dma_addr >> PAGE_SHIFT,
- runtime->dma_bytes,
- vma->vm_page_prot);
- } else {
- pr_err("Physical address or size of buf is NULL");
- return -EINVAL;
- }
- return result;
+ return msm_audio_ion_mmap(ab, vma);
}
static int msm_pcm_hw_params(struct snd_pcm_substream *substream,
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c
index 9fbf749..f4ca5b8 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c
@@ -27,6 +27,7 @@
#include <sound/control.h>
#include <asm/dma.h>
#include <linux/dma-mapping.h>
+#include <linux/msm_audio_ion.h>
#include <linux/of_device.h>
#include <sound/pcm_params.h>
@@ -628,25 +629,14 @@
static int msm_pcm_mmap(struct snd_pcm_substream *substream,
struct vm_area_struct *vma)
{
- int result = 0;
struct snd_pcm_runtime *runtime = substream->runtime;
struct msm_audio *prtd = runtime->private_data;
-
- pr_debug("%s\n", __func__);
+ struct audio_client *ac = prtd->audio_client;
+ struct audio_port_data *apd = ac->port;
+ struct audio_buffer *ab = &(apd[IN].buf[0]);
prtd->mmap_flag = 1;
- if (runtime->dma_addr && runtime->dma_bytes) {
- vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
- result = remap_pfn_range(vma, vma->vm_start,
- runtime->dma_addr >> PAGE_SHIFT,
- runtime->dma_bytes,
- vma->vm_page_prot);
- } else {
- pr_err("Physical address or size of buf is NULL");
- return -EINVAL;
- }
-
- return result;
+ return msm_audio_ion_mmap(ab, vma);
}
static int msm_pcm_hw_params(struct snd_pcm_substream *substream,
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c
index fa476ee..643f280 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c
@@ -44,7 +44,7 @@
unsigned int sample_rate;
unsigned int channel;
unsigned int format;
- bool perf_mode;
+ unsigned long perf_mode;
};
#define INVALID_SESSION -1
@@ -270,7 +270,7 @@
}
static void msm_pcm_routing_build_matrix(int fedai_id, int dspst_id,
- int path_type)
+ int path_type, bool perf_mode)
{
int i, port_type;
struct route_payload payload;
@@ -290,7 +290,7 @@
if (payload.num_copps)
adm_matrix_map(dspst_id, path_type,
- payload.num_copps, payload.copp_ids, 0);
+ payload.num_copps, payload.copp_ids, 0, perf_mode);
}
void msm_pcm_routing_reg_psthr_stream(int fedai_id, int dspst_id,
@@ -365,8 +365,8 @@
msm_send_eq_values(fedai_id);
topology = get_topology(path_type);
for (i = 0; i < MSM_BACKEND_DAI_MAX; i++) {
- if (test_bit(fedai_id, &msm_bedais[i].fe_sessions))
- msm_bedais[i].perf_mode = perf_mode;
+ if (test_bit(fedai_id, &msm_bedais[i].fe_sessions) && perf_mode)
+ set_bit(fedai_id, &msm_bedais[i].perf_mode);
if (!is_be_dai_extproc(i) &&
(afe_get_port_type(msm_bedais[i].port_id) == port_type) &&
(msm_bedais[i].active) &&
@@ -385,7 +385,8 @@
path_type,
msm_bedais[i].sample_rate,
msm_bedais[i].channel,
- topology, msm_bedais[i].perf_mode,
+ topology,
+ test_bit(fedai_id, &msm_bedais[i].perf_mode),
bits_per_sample);
else
adm_open(msm_bedais[i].port_id,
@@ -408,7 +409,7 @@
}
if (payload.num_copps)
adm_matrix_map(dspst_id, path_type,
- payload.num_copps, payload.copp_ids, 0);
+ payload.num_copps, payload.copp_ids, 0, perf_mode);
mutex_unlock(&routing_lock);
}
@@ -440,7 +441,8 @@
(afe_get_port_type(msm_bedais[i].port_id) == port_type) &&
(msm_bedais[i].active) &&
(test_bit(fedai_id, &msm_bedais[i].fe_sessions))) {
- adm_close(msm_bedais[i].port_id);
+ adm_close(msm_bedais[i].port_id,
+ test_bit(fedai_id, &msm_bedais[i].perf_mode));
if (DOLBY_ADM_COPP_TOPOLOGY_ID == topology)
dolby_dap_deinit(msm_bedais[i].port_id);
}
@@ -473,6 +475,7 @@
int session_type, path_type, port_id, topology;
u32 channels;
uint16_t bits_per_sample = 16;
+ bool perf_mode = false;
pr_debug("%s: reg %x val %x set %x\n", __func__, reg, val, set);
@@ -507,12 +510,14 @@
if ((session_type == SESSION_TYPE_RX) &&
(channels > 0)) {
+ perf_mode = test_bit(val,
+ &msm_bedais[reg].perf_mode);
adm_multi_ch_copp_open(msm_bedais[reg].port_id,
path_type,
msm_bedais[reg].sample_rate,
channels,
topology,
- msm_bedais[reg].perf_mode,
+ perf_mode,
bits_per_sample);
} else
adm_open(msm_bedais[reg].port_id,
@@ -521,7 +526,8 @@
topology, false, bits_per_sample);
msm_pcm_routing_build_matrix(val,
- fe_dai_map[val][session_type], path_type);
+ fe_dai_map[val][session_type], path_type,
+ perf_mode);
port_id = srs_port_id = msm_bedais[reg].port_id;
srs_send_params(srs_port_id, 1, 0);
if (DOLBY_ADM_COPP_TOPOLOGY_ID == topology)
@@ -536,11 +542,13 @@
clear_bit(val, &msm_bedais[reg].fe_sessions);
if (msm_bedais[reg].active && fe_dai_map[val][session_type] !=
INVALID_SESSION) {
- adm_close(msm_bedais[reg].port_id);
+ perf_mode = test_bit(val, &msm_bedais[reg].perf_mode);
+ adm_close(msm_bedais[reg].port_id, perf_mode);
if (DOLBY_ADM_COPP_TOPOLOGY_ID == topology)
dolby_dap_deinit(msm_bedais[reg].port_id);
msm_pcm_routing_build_matrix(val,
- fe_dai_map[val][session_type], path_type);
+ fe_dai_map[val][session_type], path_type,
+ perf_mode);
}
}
if ((msm_bedais[reg].port_id == VOICE_RECORD_RX)
@@ -1411,6 +1419,21 @@
msm_routing_put_audio_mixer),
};
+static const struct snd_kcontrol_new tertiary_mi2s_rx_mixer_controls[] = {
+ SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_TERTIARY_MI2S_RX ,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
static const struct snd_kcontrol_new secondary_mi2s_rx_mixer_controls[] = {
SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_SECONDARY_MI2S_RX ,
MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
@@ -1595,6 +1618,9 @@
SOC_SINGLE_EXT("QUAT_MI2S_TX", MSM_BACKEND_DAI_QUATERNARY_MI2S_TX,
MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("TERT_MI2S_TX", MSM_BACKEND_DAI_TERTIARY_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
SOC_SINGLE_EXT("SEC_MI2S_TX", MSM_BACKEND_DAI_SECONDARY_MI2S_TX,
MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
@@ -2596,6 +2622,8 @@
SND_SOC_DAPM_AIF_OUT("MI2S_RX", "MI2S Playback", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_OUT("QUAT_MI2S_RX", "Quaternary MI2S Playback",
0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("TERT_MI2S_RX", "Tertiary MI2S Playback",
+ 0, 0, 0, 0),
SND_SOC_DAPM_AIF_OUT("SEC_MI2S_RX", "Secondary MI2S Playback",
0, 0, 0, 0),
SND_SOC_DAPM_AIF_OUT("PRI_MI2S_RX", "Primary MI2S Playback",
@@ -2606,6 +2634,8 @@
0, 0, 0, 0),
SND_SOC_DAPM_AIF_IN("PRI_MI2S_TX", "Primary MI2S Capture",
0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("TERT_MI2S_TX", "Tertiary MI2S Capture",
+ 0, 0, 0, 0),
SND_SOC_DAPM_AIF_IN("SEC_MI2S_TX", "Secondary MI2S Capture",
0, 0, 0, 0),
SND_SOC_DAPM_AIF_IN("SLIMBUS_0_TX", "Slimbus Capture", 0, 0, 0, 0),
@@ -2681,6 +2711,9 @@
SND_SOC_DAPM_MIXER("QUAT_MI2S_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
quaternary_mi2s_rx_mixer_controls,
ARRAY_SIZE(quaternary_mi2s_rx_mixer_controls)),
+ SND_SOC_DAPM_MIXER("TERT_MI2S_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+ tertiary_mi2s_rx_mixer_controls,
+ ARRAY_SIZE(tertiary_mi2s_rx_mixer_controls)),
SND_SOC_DAPM_MIXER("SEC_MI2S_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
secondary_mi2s_rx_mixer_controls,
ARRAY_SIZE(secondary_mi2s_rx_mixer_controls)),
@@ -2872,6 +2905,11 @@
{"QUAT_MI2S_RX Audio Mixer", "MultiMedia5", "MM_DL5"},
{"QUAT_MI2S_RX", NULL, "QUAT_MI2S_RX Audio Mixer"},
+ {"TERT_MI2S_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"TERT_MI2S_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"TERT_MI2S_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"TERT_MI2S_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
+ {"TERT_MI2S_RX", NULL, "TERT_MI2S_RX Audio Mixer"},
{"SEC_MI2S_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
{"SEC_MI2S_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
@@ -2895,6 +2933,7 @@
{"MultiMedia2 Mixer", "MI2S_TX", "MI2S_TX"},
{"MultiMedia5 Mixer", "MI2S_TX", "MI2S_TX"},
{"MultiMedia1 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+ {"MultiMedia1 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"},
{"MultiMedia1 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
{"MultiMedia1 Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
{"MultiMedia5 Mixer", "AUX_PCM_TX", "AUX_PCM_TX"},
@@ -3093,7 +3132,7 @@
{"MI2S_UL_HL", NULL, "MI2S_TX"},
{"PCM_RX_DL_HL", "Switch", "SLIM0_DL_HL"},
{"PCM_RX", NULL, "PCM_RX_DL_HL"},
- {"MI2S_UL_HL", NULL, "MI2S_TX"},
+ {"MI2S_UL_HL", NULL, "TERT_MI2S_TX"},
{"SEC_I2S_RX", NULL, "SEC_I2S_DL_HL"},
{"PRI_MI2S_UL_HL", NULL, "PRI_MI2S_TX"},
{"SEC_MI2S_RX", NULL, "SEC_MI2S_DL_HL"},
@@ -3163,6 +3202,7 @@
{"BE_OUT", NULL, "HDMI"},
{"BE_OUT", NULL, "MI2S_RX"},
{"BE_OUT", NULL, "QUAT_MI2S_RX"},
+ {"BE_OUT", NULL, "TERT_MI2S_RX"},
{"BE_OUT", NULL, "SEC_MI2S_RX"},
{"BE_OUT", NULL, "PRI_MI2S_RX"},
{"BE_OUT", NULL, "INT_BT_SCO_RX"},
@@ -3183,6 +3223,7 @@
{"MI2S_TX", NULL, "BE_IN"},
{"QUAT_MI2S_TX", NULL, "BE_IN"},
{"PRI_MI2S_TX", NULL, "BE_IN"},
+ {"TERT_MI2S_TX", NULL, "BE_IN"},
{"SEC_MI2S_TX", NULL, "BE_IN"},
{"SLIMBUS_0_TX", NULL, "BE_IN" },
{"SLIMBUS_1_TX", NULL, "BE_IN" },
@@ -3243,8 +3284,10 @@
topology = get_topology(path_type);
for_each_set_bit(i, &bedai->fe_sessions, MSM_FRONTEND_DAI_MM_SIZE) {
if (fe_dai_map[i][session_type] != INVALID_SESSION) {
- adm_close(bedai->port_id);
+ adm_close(bedai->port_id,
+ test_bit(i, &(bedai->perf_mode)));
srs_port_id = -1;
+ clear_bit(i, &(bedai->perf_mode));
if (DOLBY_ADM_COPP_TOPOLOGY_ID == topology)
dolby_dap_deinit(bedai->port_id);
}
@@ -3253,7 +3296,6 @@
bedai->active = 0;
bedai->sample_rate = 0;
bedai->channel = 0;
- bedai->perf_mode = false;
mutex_unlock(&routing_lock);
return 0;
@@ -3268,6 +3310,7 @@
u32 channels;
bool playback, capture;
uint16_t bits_per_sample = 16;
+ bool perf_mode = false;
if (be_id >= MSM_BACKEND_DAI_MAX) {
pr_err("%s: unexpected be_id %d\n", __func__, be_id);
@@ -3306,11 +3349,13 @@
bits_per_sample = 24;
if ((playback) && (channels > 0)) {
+ perf_mode = test_bit(i, &(bedai->perf_mode));
adm_multi_ch_copp_open(bedai->port_id,
path_type,
bedai->sample_rate,
channels,
- topology, bedai->perf_mode,
+ topology,
+ perf_mode,
bits_per_sample);
} else if (capture) {
adm_open(bedai->port_id,
@@ -3322,7 +3367,8 @@
}
msm_pcm_routing_build_matrix(i,
- fe_dai_map[i][session_type], path_type);
+ fe_dai_map[i][session_type], path_type,
+ perf_mode);
port_id = srs_port_id = bedai->port_id;
srs_send_params(srs_port_id, 1, 0);
if (DOLBY_ADM_COPP_TOPOLOGY_ID == topology)
diff --git a/sound/soc/msm/qdsp6v2/q6adm.c b/sound/soc/msm/qdsp6v2/q6adm.c
index 1bd3eac..29c06cb 100644
--- a/sound/soc/msm/qdsp6v2/q6adm.c
+++ b/sound/soc/msm/qdsp6v2/q6adm.c
@@ -39,6 +39,9 @@
void *apr;
atomic_t copp_id[AFE_MAX_PORTS];
atomic_t copp_cnt[AFE_MAX_PORTS];
+ atomic_t copp_low_latency_id[AFE_MAX_PORTS];
+ atomic_t copp_low_latency_cnt[AFE_MAX_PORTS];
+ atomic_t copp_perf_mode[AFE_MAX_PORTS];
atomic_t copp_stat[AFE_MAX_PORTS];
wait_queue_head_t wait[AFE_MAX_PORTS];
@@ -445,7 +448,12 @@
for (i = 0; i < AFE_MAX_PORTS; i++) {
atomic_set(&this_adm.copp_id[i],
RESET_COPP_ID);
+ atomic_set(&this_adm.copp_low_latency_id[i],
+ RESET_COPP_ID);
atomic_set(&this_adm.copp_cnt[i], 0);
+ atomic_set(&this_adm.copp_low_latency_cnt[i],
+ 0);
+ atomic_set(&this_adm.copp_perf_mode[i], 0);
atomic_set(&this_adm.copp_stat[i], 0);
}
this_adm.apr = NULL;
@@ -545,7 +553,13 @@
wake_up(&this_adm.wait[index]);
break;
}
- atomic_set(&this_adm.copp_id[index], open->copp_id);
+ if (atomic_read(&this_adm.copp_perf_mode[index])) {
+ atomic_set(&this_adm.copp_low_latency_id[index],
+ open->copp_id);
+ } else {
+ atomic_set(&this_adm.copp_id[index],
+ open->copp_id);
+ }
atomic_set(&this_adm.copp_stat[index], 1);
pr_debug("%s: coppid rxed=%d\n", __func__,
open->copp_id);
@@ -892,8 +906,8 @@
int index;
int tmp_port = q6audio_get_port_id(port_id);
- pr_debug("%s: port %#x path:%d rate:%d mode:%d\n", __func__,
- port_id, path, rate, channel_mode);
+ pr_debug("%s: port %#x path:%d rate:%d mode:%d perf_mode:%d\n",
+ __func__, port_id, path, rate, channel_mode, perf_mode);
port_id = q6audio_convert_virtual_to_portid(port_id);
@@ -916,11 +930,19 @@
rtac_set_adm_handle(this_adm.apr);
}
- send_adm_custom_topology(port_id);
+ if (!perf_mode) {
+ atomic_set(&this_adm.copp_perf_mode[index], 0);
+ send_adm_custom_topology(port_id);
+ } else {
+ atomic_set(&this_adm.copp_perf_mode[index], 1);
+ }
/* Create a COPP if port id are not enabled */
- if (atomic_read(&this_adm.copp_cnt[index]) == 0) {
-
+ if ((!perf_mode && (atomic_read(&this_adm.copp_cnt[index]) == 0)) ||
+ (perf_mode &&
+ (atomic_read(&this_adm.copp_low_latency_cnt[index]) == 0))) {
+ pr_debug("%s:opening ADM: perf_mode: %d\n", __func__,
+ perf_mode);
open.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
open.hdr.pkt_size = sizeof(open);
@@ -950,6 +972,9 @@
(open.topology_id == VPM_TX_DM_FLUENCE_COPP_TOPOLOGY))
rate = 16000;
+ if (perf_mode)
+ open.topology_id = NULL_COPP_TOPOLOGY;
+
open.dev_num_channel = channel_mode & 0x00FF;
open.bit_width = bits_per_sample;
open.sample_rate = rate;
@@ -1026,7 +1051,15 @@
goto fail_cmd;
}
}
- atomic_inc(&this_adm.copp_cnt[index]);
+ if (perf_mode) {
+ atomic_inc(&this_adm.copp_low_latency_cnt[index]);
+ pr_debug("%s: index: %d coppid: %d", __func__, index,
+ atomic_read(&this_adm.copp_low_latency_id[index]));
+ } else {
+ atomic_inc(&this_adm.copp_cnt[index]);
+ pr_debug("%s: index: %d coppid: %d", __func__, index,
+ atomic_read(&this_adm.copp_id[index]));
+ }
return 0;
fail_cmd:
@@ -1046,7 +1079,7 @@
}
int adm_matrix_map(int session_id, int path, int num_copps,
- unsigned int *port_id, int copp_id)
+ unsigned int *port_id, int copp_id, bool perf_mode)
{
struct adm_cmd_matrix_map_routings_v5 *route;
struct adm_session_map_node_v5 *node;
@@ -1085,7 +1118,12 @@
route->hdr.src_port = copp_id;
route->hdr.dest_svc = APR_SVC_ADM;
route->hdr.dest_domain = APR_DOMAIN_ADSP;
- route->hdr.dest_port = atomic_read(&this_adm.copp_id[index]);
+ if (perf_mode) {
+ route->hdr.dest_port =
+ atomic_read(&this_adm.copp_low_latency_id[index]);
+ } else {
+ route->hdr.dest_port = atomic_read(&this_adm.copp_id[index]);
+ }
route->hdr.token = copp_id;
route->hdr.opcode = ADM_CMD_MATRIX_MAP_ROUTINGS_V5;
route->num_sessions = 1;
@@ -1117,9 +1155,14 @@
tmp = q6audio_get_port_index(port_id[i]);
- if (tmp >= 0 && tmp < AFE_MAX_PORTS)
- copps_list[i] =
+ if (tmp >= 0 && tmp < AFE_MAX_PORTS) {
+ if (perf_mode)
+ copps_list[i] =
+ atomic_read(&this_adm.copp_low_latency_id[tmp]);
+ else
+ copps_list[i] =
atomic_read(&this_adm.copp_id[tmp]);
+ }
else
continue;
pr_debug("%s: port_id[%#x]: %d, index: %d act coppid[0x%x]\n",
@@ -1144,21 +1187,42 @@
ret = -EINVAL;
goto fail_cmd;
}
- for (i = 0; i < num_copps; i++)
- send_adm_cal(port_id[i], path);
+ if (perf_mode) {
+ for (i = 0; i < num_copps; i++) {
+ int tmp;
- for (i = 0; i < num_copps; i++) {
- int tmp;
- tmp = afe_get_port_index(port_id[i]);
- if (tmp >= 0 && tmp < AFE_MAX_PORTS)
- rtac_add_adm_device(port_id[i],
- atomic_read(&this_adm.copp_id[tmp]),
- path, session_id);
- else
- pr_debug("%s: Invalid port index %d",
- __func__, tmp);
+ tmp = afe_get_port_index(port_id[i]);
+ if (tmp >= 0 && tmp < AFE_MAX_PORTS) {
+ rtac_add_adm_device(port_id[i], atomic_read(
+ &this_adm.copp_low_latency_id[tmp]),
+ path, session_id);
+ pr_debug("%s, copp_id: %d\n", __func__,
+ atomic_read(
+ &this_adm.copp_low_latency_id[tmp]));
+ } else {
+ pr_debug("%s: Invalid port index %d",
+ __func__, tmp);
+ }
+ }
+ } else {
+ for (i = 0; i < num_copps; i++)
+ send_adm_cal(port_id[i], path);
+
+ for (i = 0; i < num_copps; i++) {
+ int tmp;
+ tmp = afe_get_port_index(port_id[i]);
+ if (tmp >= 0 && tmp < AFE_MAX_PORTS) {
+ rtac_add_adm_device(port_id[i],
+ atomic_read(&this_adm.copp_id[tmp]),
+ path, session_id);
+ pr_debug("%s, copp_id: %d\n", __func__,
+ atomic_read(&this_adm.copp_id[tmp]));
+ } else {
+ pr_debug("%s: Invalid port index %d",
+ __func__, tmp);
+ }
+ }
}
-
fail_cmd:
kfree(matrix_map);
return ret;
@@ -1319,7 +1383,7 @@
return atomic_read(&this_adm.copp_id[port_index]);
}
-int adm_close(int port_id)
+int adm_close(int port_id, bool perf_mode)
{
struct apr_hdr close;
@@ -1332,16 +1396,30 @@
if (q6audio_validate_port(port_id) < 0)
return -EINVAL;
- pr_debug("%s port_id=%#x index %d\n", __func__, port_id, index);
+ pr_debug("%s port_id=%#x index %d perf_mode: %d\n", __func__, port_id,
+ index, perf_mode);
- if (!(atomic_read(&this_adm.copp_cnt[index]))) {
- pr_err("%s: copp count for port[%#x]is 0\n", __func__, port_id);
-
- goto fail_cmd;
+ if (perf_mode) {
+ if (!(atomic_read(&this_adm.copp_low_latency_cnt[index]))) {
+ pr_err("%s: copp count for port[%#x]is 0\n", __func__,
+ port_id);
+ goto fail_cmd;
+ }
+ atomic_dec(&this_adm.copp_low_latency_cnt[index]);
+ } else {
+ if (!(atomic_read(&this_adm.copp_cnt[index]))) {
+ pr_err("%s: copp count for port[%#x]is 0\n", __func__,
+ port_id);
+ goto fail_cmd;
+ }
+ atomic_dec(&this_adm.copp_cnt[index]);
}
- atomic_dec(&this_adm.copp_cnt[index]);
- if (!(atomic_read(&this_adm.copp_cnt[index]))) {
+ if ((!perf_mode && !(atomic_read(&this_adm.copp_cnt[index]))) ||
+ (perf_mode &&
+ !(atomic_read(&this_adm.copp_low_latency_cnt[index])))) {
+ pr_debug("%s:Closing ADM: perf_mode: %d\n", __func__,
+ perf_mode);
close.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
close.pkt_size = sizeof(close);
@@ -1350,19 +1428,33 @@
close.src_port = port_id;
close.dest_svc = APR_SVC_ADM;
close.dest_domain = APR_DOMAIN_ADSP;
- close.dest_port = atomic_read(&this_adm.copp_id[index]);
+ if (perf_mode)
+ close.dest_port =
+ atomic_read(&this_adm.copp_low_latency_id[index]);
+ else
+ close.dest_port = atomic_read(&this_adm.copp_id[index]);
close.token = port_id;
close.opcode = ADM_CMD_DEVICE_CLOSE_V5;
- atomic_set(&this_adm.copp_id[index], RESET_COPP_ID);
atomic_set(&this_adm.copp_stat[index], 0);
-
- pr_debug("%s:coppid %d portid=%#x index=%d coppcnt=%d\n",
+ if (perf_mode) {
+ pr_debug("%s:coppid %d portid=%#x index=%d coppcnt=%d\n",
+ __func__,
+ atomic_read(&this_adm.copp_low_latency_id[index]),
+ port_id, index,
+ atomic_read(&this_adm.copp_low_latency_cnt[index]));
+ atomic_set(&this_adm.copp_low_latency_id[index],
+ RESET_COPP_ID);
+ } else {
+ pr_debug("%s:coppid %d portid=%#x index=%d coppcnt=%d\n",
__func__,
atomic_read(&this_adm.copp_id[index]),
port_id, index,
atomic_read(&this_adm.copp_cnt[index]));
+ atomic_set(&this_adm.copp_id[index],
+ RESET_COPP_ID);
+ }
ret = apr_send_pkt(this_adm.apr, (uint32_t *)&close);
if (ret < 0) {
@@ -1380,7 +1472,10 @@
ret = -EINVAL;
goto fail_cmd;
}
-
+ }
+ if (!atomic_read(&this_adm.copp_cnt[index]) &&
+ !atomic_read(&this_adm.copp_low_latency_cnt[index])) {
+ pr_debug("%s: remove adm device from rtac\n", __func__);
rtac_remove_adm_device(port_id);
}
@@ -1396,8 +1491,11 @@
for (i = 0; i < AFE_MAX_PORTS; i++) {
atomic_set(&this_adm.copp_id[i], RESET_COPP_ID);
+ atomic_set(&this_adm.copp_low_latency_id[i], RESET_COPP_ID);
atomic_set(&this_adm.copp_cnt[i], 0);
+ atomic_set(&this_adm.copp_low_latency_cnt[i], 0);
atomic_set(&this_adm.copp_stat[i], 0);
+ atomic_set(&this_adm.copp_perf_mode[i], 0);
init_waitqueue_head(&this_adm.wait[i]);
}
return 0;
diff --git a/sound/soc/msm/qdsp6v2/q6afe.c b/sound/soc/msm/qdsp6v2/q6afe.c
index 5b4244e..ce5e816 100644
--- a/sound/soc/msm/qdsp6v2/q6afe.c
+++ b/sound/soc/msm/qdsp6v2/q6afe.c
@@ -1341,6 +1341,10 @@
return IDX_AFE_PORT_ID_SECONDARY_MI2S_RX;
case AFE_PORT_ID_SECONDARY_MI2S_TX:
return IDX_AFE_PORT_ID_SECONDARY_MI2S_TX;
+ case AFE_PORT_ID_TERTIARY_MI2S_RX:
+ return IDX_AFE_PORT_ID_TERTIARY_MI2S_RX;
+ case AFE_PORT_ID_TERTIARY_MI2S_TX:
+ return IDX_AFE_PORT_ID_TERTIARY_MI2S_TX;
default: return -EINVAL;
}
diff --git a/sound/soc/msm/qdsp6v2/q6asm.c b/sound/soc/msm/qdsp6v2/q6asm.c
index 59d4de2..c2fd2d7 100644
--- a/sound/soc/msm/qdsp6v2/q6asm.c
+++ b/sound/soc/msm/qdsp6v2/q6asm.c
@@ -45,17 +45,6 @@
#define TRUE 0x01
#define FALSE 0x00
-#define READDONE_IDX_STATUS 0
-#define READDONE_IDX_BUFADD_LSW 1
-#define READDONE_IDX_BUFADD_MSW 2
-#define READDONE_IDX_MEMMAP_HDL 3
-#define READDONE_IDX_SIZE 4
-#define READDONE_IDX_OFFSET 5
-#define READDONE_IDX_LSW_TS 6
-#define READDONE_IDX_MSW_TS 7
-#define READDONE_IDX_FLAGS 8
-#define READDONE_IDX_NUMFRAMES 9
-#define READDONE_IDX_SEQ_ID 10
/* TODO, combine them together */
static DEFINE_MUTEX(session_lock);
@@ -342,6 +331,7 @@
mutex_unlock(&session_lock);
ac->session = 0;
ac->perf_mode = 0;
+ ac->fptr_cache_ops = NULL;
return;
}
@@ -621,6 +611,7 @@
ac->priv = priv;
ac->io_mode = SYNC_IO_MODE;
ac->perf_mode = false;
+ ac->fptr_cache_ops = NULL;
ac->apr = apr_register("ADSP", "ASM", \
(apr_fn)q6asm_callback,\
((ac->session) << 8 | 0x0001),\
@@ -3371,6 +3362,7 @@
struct list_head *ptr, *next;
u32 lbuf_addr_lsw;
u32 liomode;
+ u32 io_compressed;
if (!ac || ac->apr == NULL) {
pr_err("%s: APR handle NULL\n", __func__);
@@ -3387,12 +3379,15 @@
read.buf_size = param->len;
read.seq_id = param->uid;
liomode = (NT_MODE | ASYNC_IO_MODE);
+ io_compressed = (ASYNC_IO_MODE | COMPRESSED_IO);
if (ac->io_mode == liomode)
lbuf_addr_lsw = (read.buf_addr_lsw - 32);
+ else if (ac->io_mode == io_compressed)
+ lbuf_addr_lsw = (read.buf_addr_lsw - 64);
else
lbuf_addr_lsw = read.buf_addr_lsw;
- list_for_each_safe(ptr, next, &ac->port[IN].mem_map_handle) {
+ list_for_each_safe(ptr, next, &ac->port[OUT].mem_map_handle) {
buf_node = list_entry(ptr, struct asm_buffer_node, list);
if (buf_node->buf_addr_lsw == lbuf_addr_lsw) {
read.mem_map_handle = buf_node->mmap_hdl;
diff --git a/sound/soc/msm/qdsp6v2/q6audio-v2.c b/sound/soc/msm/qdsp6v2/q6audio-v2.c
index dae1ebd..bc7ad4d 100644
--- a/sound/soc/msm/qdsp6v2/q6audio-v2.c
+++ b/sound/soc/msm/qdsp6v2/q6audio-v2.c
@@ -70,6 +70,10 @@
return IDX_AFE_PORT_ID_SECONDARY_MI2S_RX;
case AFE_PORT_ID_SECONDARY_MI2S_TX:
return IDX_AFE_PORT_ID_SECONDARY_MI2S_TX;
+ case AFE_PORT_ID_TERTIARY_MI2S_RX:
+ return IDX_AFE_PORT_ID_TERTIARY_MI2S_RX;
+ case AFE_PORT_ID_TERTIARY_MI2S_TX:
+ return IDX_AFE_PORT_ID_TERTIARY_MI2S_TX;
default: return -EINVAL;
}
@@ -126,7 +130,10 @@
return AFE_PORT_ID_SECONDARY_MI2S_RX;
case AFE_PORT_ID_SECONDARY_MI2S_TX:
return AFE_PORT_ID_SECONDARY_MI2S_TX;
-
+ case AFE_PORT_ID_TERTIARY_MI2S_RX:
+ return AFE_PORT_ID_TERTIARY_MI2S_RX;
+ case AFE_PORT_ID_TERTIARY_MI2S_TX:
+ return AFE_PORT_ID_TERTIARY_MI2S_TX;
default:
pr_warn("%s: Invalid port_id %d\n", __func__, port_id);
return -EINVAL;
diff --git a/sound/soc/msm/qdsp6v2/q6core.c b/sound/soc/msm/qdsp6v2/q6core.c
index 42cbcd1..5fec0c1 100644
--- a/sound/soc/msm/qdsp6v2/q6core.c
+++ b/sound/soc/msm/qdsp6v2/q6core.c
@@ -29,7 +29,7 @@
struct apr_svc *core_handle_q;
wait_queue_head_t bus_bw_req_wait;
u32 bus_bw_resp_received;
- struct avcs_cmd_rsp_get_low_power_segments_info_t *lp_ocm_payload;
+ struct avcs_cmd_rsp_get_low_power_segments_info_t lp_ocm_payload;
};
static struct q6core_str q6core_lcl;
@@ -74,19 +74,19 @@
pr_info("%s: cmd = AVCS_CMDRSP_GET_LOW_POWER_SEGMENTS_INFO num_segments = 0x%x\n",
__func__, payload1[0]);
nseg = payload1[0];
- q6core_lcl.lp_ocm_payload->num_segments = nseg;
- q6core_lcl.lp_ocm_payload->bandwidth = payload1[1];
+ q6core_lcl.lp_ocm_payload.num_segments = nseg;
+ q6core_lcl.lp_ocm_payload.bandwidth = payload1[1];
for (i = 0, j = 2; i < nseg; i++) {
- q6core_lcl.lp_ocm_payload->mem_segment[i].type =
+ q6core_lcl.lp_ocm_payload.mem_segment[i].type =
(payload1[j] & 0xffff);
- q6core_lcl.lp_ocm_payload->mem_segment[i].category =
+ q6core_lcl.lp_ocm_payload.mem_segment[i].category =
((payload1[j++] >> 16) & 0xffff);
- q6core_lcl.lp_ocm_payload->mem_segment[i].size =
+ q6core_lcl.lp_ocm_payload.mem_segment[i].size =
payload1[j++];
- q6core_lcl.lp_ocm_payload->
+ q6core_lcl.lp_ocm_payload.
mem_segment[i].start_address_lsw =
payload1[j++];
- q6core_lcl.lp_ocm_payload->
+ q6core_lcl.lp_ocm_payload.
mem_segment[i].start_address_msw =
payload1[j++];
}
@@ -152,7 +152,6 @@
struct avcs_cmd_rsp_get_low_power_segments_info_t **lp_memseg)
{
struct avcs_cmd_get_low_power_segments_info lp_ocm_cmd;
- u8 *cptr = NULL;
int ret = 0;
pr_debug("%s: ", __func__);
@@ -163,16 +162,6 @@
return -ENODEV;
}
- cptr = kzalloc(
- sizeof(struct avcs_cmd_rsp_get_low_power_segments_info_t),
- GFP_KERNEL);
- if (!cptr) {
- pr_err("%s: Failed to allocate memory for low power segment struct\n",
- __func__);
- return -ENOMEM;
- }
- q6core_lcl.lp_ocm_payload =
- (struct avcs_cmd_rsp_get_low_power_segments_info_t *) cptr;
lp_ocm_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
@@ -201,7 +190,7 @@
goto fail_cmd;
}
- *lp_memseg = q6core_lcl.lp_ocm_payload;
+ *lp_memseg = &q6core_lcl.lp_ocm_payload;
return 0;
fail_cmd:
@@ -215,14 +204,6 @@
q6core_lcl.bus_bw_resp_received = 0;
q6core_lcl.core_handle_q = NULL;
- q6core_lcl.lp_ocm_payload = kzalloc(
- sizeof(struct avcs_cmd_rsp_get_low_power_segments_info_t), GFP_KERNEL);
-
- if (!q6core_lcl.lp_ocm_payload) {
- pr_err("%s: Failed to allocate memory for low power segment struct\n",
- __func__);
- return -ENOMEM;
- }
return 0;
}
@@ -230,7 +211,7 @@
static void __exit core_exit(void)
{
- kfree(q6core_lcl.lp_ocm_payload);
+
}
module_exit(core_exit);
MODULE_DESCRIPTION("ADSP core driver");