Merge "mmc: core: fix the issue with clock scaling in HS400 mode"
diff --git a/Documentation/devicetree/bindings/arm/msm/cpr-regulator.txt b/Documentation/devicetree/bindings/arm/msm/cpr-regulator.txt
index aa24dc6..bba4cf7 100644
--- a/Documentation/devicetree/bindings/arm/msm/cpr-regulator.txt
+++ b/Documentation/devicetree/bindings/arm/msm/cpr-regulator.txt
@@ -251,6 +251,8 @@
corresponds to 'scaling' in this equation:
quot_adjust = (freq_turbo - freq_corner) * scaling / 1000.
This property is required if qcom,cpr-speed-bin-max-corners is present.
+- mem-acc-supply: Regulator to vote for the memory accelerator configuration.
+ Not Present: memory accelerator configuration not supported.
Example:
apc_vreg_corner: regulator@f9018000 {
diff --git a/Documentation/devicetree/bindings/arm/msm/msm_bus.txt b/Documentation/devicetree/bindings/arm/msm/msm_bus.txt
index 9f74225..e6e54bf 100644
--- a/Documentation/devicetree/bindings/arm/msm/msm_bus.txt
+++ b/Documentation/devicetree/bindings/arm/msm/msm_bus.txt
@@ -100,8 +100,8 @@
(Can be Fixed/Limiter/Bypass/Regulator)
qcom,bimc,bw: Bandwidth limit for a BIMC master using dual modes.
This bandwidth is used to calculate Grant count and
- other parameters used in Limiter and Regular mode
- for static BKE configuration. It is defined in KBps.
+ other parameters used in Limiter and Regular mode.
+ for static BKE configuration. It is defined in KBytes/s.
qcom,bimc,gp: Grant Period for configuring a master in limiter
mode. This is an integer value in nano-seconds.
qcom,bimc,thmp: Medium threshold percentage for BIMC masters.
@@ -111,11 +111,12 @@
1 and 100.
qcom,thresh: Beyond this threshold frequency, the mode usage is
switched from mode specified by property qcom,mode
- to the one specified by qcom,mode-thresh. In case the
- requested IB value falls below this threshold, the mode
- is switched back to qcom,mode. Frequency is specified in
- KBps.
-
+ to the one specified by qcom,mode-thresh. These thresholds
+ can be setup in increasing order of thresholds, so the
+ requested IB is evaluated at each threshold level before
+ making the decision to switch QoS modes and applying the
+ corresponding qcom,bimc,bw limitig bw as needed.
+ This is specified in KBytes/s.
diff --git a/Documentation/devicetree/bindings/crypto/msm/qcedev.txt b/Documentation/devicetree/bindings/crypto/msm/qcedev.txt
index 3ca9080..fc94be4 100644
--- a/Documentation/devicetree/bindings/crypto/msm/qcedev.txt
+++ b/Documentation/devicetree/bindings/crypto/msm/qcedev.txt
@@ -12,6 +12,7 @@
- qcom,msm_bus,active-only: Boolean flag for context of request (actve/dual)
- qcom,msm_bus,num_paths: The paths for source and destination ports
- qcom,msm_bus,vectors: Vectors for bus topology.
+ - qcom,ce-device: Device number.
Optional properties:
- qcom,ce-hw-shared : optional, indicates if the hardware is shared between EE.
@@ -27,6 +28,7 @@
interrupts = <0 235 0>;
qcom,bam-pipe-pair = <0>;
qcom,ce-hw-instance = <1>;
+ qcom,ce-device = <0>;
qcom,ce-hw-shared;
qcom,msm-bus,name = "qcedev-noc";
qcom,msm-bus,num-cases = <2>;
diff --git a/Documentation/devicetree/bindings/crypto/msm/qcrypto.txt b/Documentation/devicetree/bindings/crypto/msm/qcrypto.txt
index 1bb4659..6561b6a 100644
--- a/Documentation/devicetree/bindings/crypto/msm/qcrypto.txt
+++ b/Documentation/devicetree/bindings/crypto/msm/qcrypto.txt
@@ -12,6 +12,7 @@
- qcom,msm_bus,active-only: Boolean flag for context of request (actve/dual)
- qcom,msm_bus,num_paths: The paths for source and destination ports
- qcom,msm_bus,vectors: Vectors for bus topology.
+ - qcom,ce-device: Device number.
Optional properties:
- qcom,ce-hw-shared : optional, indicates if the hardware is shared between EE.
@@ -34,6 +35,7 @@
interrupts = <0 235 0>;
qcom,bam-pipe-pair = <1>;
qcom,ce-hw-instance = <1>;
+ qcom,ce-device = <0>;
qcom,ce-hw-shared;
qcom,msm-bus,name = "qcrypto-noc";
qcom,msm-bus,num-cases = <2>;
diff --git a/Documentation/devicetree/bindings/fb/mdss-dsi-ctrl.txt b/Documentation/devicetree/bindings/fb/mdss-dsi-ctrl.txt
index e8b02cf..b88c3ce 100644
--- a/Documentation/devicetree/bindings/fb/mdss-dsi-ctrl.txt
+++ b/Documentation/devicetree/bindings/fb/mdss-dsi-ctrl.txt
@@ -6,8 +6,12 @@
Required properties:
- compatible: Must be "qcom,mdss-dsi-ctrl"
- cell-index: Specifies the controller used among the two controllers.
-- reg: Offset and length of the register regions(s) for the device.
+- reg: Base address and length of the different register
+ regions(s) required for DSI device functionality.
- reg-names: A list of strings that map in order to the list of regs.
+ "dsi_ctrl" - MDSS DSI controller register region
+ "dsi_phy" - MDSS DSI PHY register region
+ "mmss_misc_phys" - Register region for MMSS DSI clamps
- vdd-supply: Phandle for vdd regulator device node.
- vddio-supply: Phandle for vdd-io regulator device node.
- vdda-supply: Phandle for vreg regulator device node.
@@ -53,9 +57,10 @@
compatible = "qcom,mdss-dsi-ctrl";
label = "MDSS DSI CTRL->0";
cell-index = <0>;
- reg = <0xfd922800 0x600>,
+ reg = <0xfd922800 0x1f8>,
+ <0xfd922b00 0x2b0>,
<0xfd828000 0x108>;
- reg-names = "dsi_phys", "mmss_misc_phys";
+ reg-names = "dsi_ctrl", "dsi_phy", "mmss_misc_phys";
vdd-supply = <&pm8226_l15>;
vddio-supply = <&pm8226_l8>;
vdda-supply = <&pm8226_l4>;
diff --git a/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt b/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt
index 02d6df9..f6b4923 100644
--- a/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt
+++ b/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt
@@ -257,6 +257,37 @@
- qcom,mdss-dsi-rx-eot-ignore: Boolean used to enable ignoring end of transmission packets.
- qcom,mdss-dsi-tx-eot-append: Boolean used to enable appending end of transmission packets.
- qcom,ulps-enabled: Boolean to enable support for Ultra Low Power State (ULPS) mode.
+- qcom,mdss-tear-check-disable: Boolean to disable mdp tear check. Tear check is enabled by default to avoid
+ tearing. Other tear-check properties are ignored if this property is present.
+ The below tear check configuration properties can be individually tuned if
+ tear check is enabled.
+- qcom,mdss-tear-check-sync-cfg-height: Specifies the vertical total number of lines.
+ The default value is 0xfff0.
+- qcom,mdss-tear-check-sync-init-val: Specifies the init value at which the read pointer gets loaded
+ at vsync edge. The reader pointer refers to the line number of
+ panel buffer that is currently being updated.
+ The default value is panel height.
+- qcom,mdss-tear-check-sync-threshold-start:
+ Allows the first ROI line write to an panel when read pointer is
+ between the range of ROI start line and ROI start line plus this
+ setting.
+ The default value is 4.
+- qcom,mdss-tear-check-sync-threshold-continue:
+ The minimum number of lines the write pointer needs to be
+ above the read pointer so that it is safe to write to the panel.
+ (This check is not done for the first ROI line write of an update)
+ The default value is 4.
+- qcom,mdss-tear-check-start-pos: Specify the y position from which the start_threshold value is
+ added and write is kicked off if the read pointer falls within that
+ region.
+ The default value is panel height.
+- qcom,mdss-tear-check-rd-ptr-trigger-intr:
+ Specify the read pointer value at which an interrupt has to be
+ generated.
+ The default value is panel height + 1.
+- qcom,mdss-tear-check-frame-rate: Specify the value to be a real frame rate(fps) x 100 factor to tune the
+ timing of TE simulation with more precision.
+ The default value is 6000 with 60 fps.
Note, if a given optional qcom,* binding is not present, then the driver will configure
the default values specified.
@@ -354,5 +385,12 @@
mdss-dsi-rx-eot-ignore;
mdss-dsi-tx-eot-append;
qcom,ulps-enabled;
+ qcom,mdss-tear-check-sync-cfg-height = <0xfff0>;
+ qcom,mdss-tear-check-sync-init-val = <1280>;
+ qcom,mdss-tear-check-sync-threshold-start = <4>;
+ qcom,mdss-tear-check-sync-threshold-continue = <4>;
+ qcom,mdss-tear-check-start-pos = <1280>;
+ qcom,mdss-tear-check-rd-ptr-trigger-intr = <1281>;
+ qcom,mdss-tear-check-frame-rate = <6000>;
};
};
diff --git a/Documentation/devicetree/bindings/fb/mdss-mdp.txt b/Documentation/devicetree/bindings/fb/mdss-mdp.txt
index c2b963f..bd11551 100644
--- a/Documentation/devicetree/bindings/fb/mdss-mdp.txt
+++ b/Documentation/devicetree/bindings/fb/mdss-mdp.txt
@@ -27,6 +27,14 @@
to the respective VIG pipes. Number of xin ids
defined should match the number of offsets
defined in property: qcom,mdss-pipe-vig-off
+- qcom,mdss-pipe-vig-clk-ctrl-off: Array of offsets describing clk control
+ offsets for dynamic clock gating. 1st value
+ in the array represents offset of the control
+ register. 2nd value represents bit offset within
+ control register and 3rd value represents bit
+ offset within status register. Number of tuples
+ defined should match the number of offsets
+ defined in property: qcom,mdss-pipe-vig-off
- qcom,mdss-pipe-rgb-off: Array of offsets for MDP source surface pipes of
type RGB, the offsets are calculated from
register "mdp_phys" defined in reg property.
@@ -42,6 +50,14 @@
to the respective RGB pipes. Number of xin ids
defined should match the number of offsets
defined in property: qcom,mdss-pipe-rgb-off
+- qcom,mdss-pipe-rgb-clk-ctrl-off: Array of offsets describing clk control
+ offsets for dynamic clock gating. 1st value
+ in the array represents offset of the control
+ register. 2nd value represents bit offset within
+ control register and 3rd value represents bit
+ offset within status register. Number of tuples
+ defined should match the number of offsets
+ defined in property: qcom,mdss-pipe-rgb-off
- qcom,mdss-pipe-dma-off: Array of offsets for MDP source surface pipes of
type DMA, the offsets are calculated from
register "mdp_phys" defined in reg property.
@@ -57,6 +73,14 @@
to the respective DMA pipes. Number of xin ids
defined should match the number of offsets
defined in property: qcom,mdss-pipe-dma-off
+- qcom,mdss-pipe-dma-clk-ctrl-off: Array of offsets describing clk control
+ offsets for dynamic clock gating. 1st value
+ in the array represents offset of the control
+ register. 2nd value represents bit offset within
+ control register and 3rd value represents bit
+ offset within status register. Number of tuples
+ defined should match the number of offsets
+ defined in property: qcom,mdss-pipe-dma-off
- qcom,mdss-smp-data: Array of shared memory pool data. There should
be only two values in this property. The first
value corresponds to the number of smp blocks
@@ -222,6 +246,8 @@
- qcom,mdss-has-wfd-blk: Boolean property to indicate the presence of dedicated
writeback wfd block in MDSS as opposed to writeback
block that is shared between rotator and wfd.
+- qcom,mdss-no-lut-read: Boolean property to indicate reading of LUT is
+ not supported.
- qcom,mdss-smp-mb-per-pipe: Maximum number of shared memory pool blocks
restricted for a source surface pipe. If this
property is not specified, no such restriction
@@ -249,6 +275,12 @@
used to reduce the pending writes limit dynamically
and can be tuned to match performance requirements
depending upon system state.
+- qcom,mdss-clk-levels: This array indicates the mdp core clock level selection
+ array. Core clock is calculated for each frame and
+ hence depending upon calculated value, clock rate
+ will be rounded up to the next level according to
+ this table. Order of entries need to be ordered in
+ ascending order.
Fudge Factors: Fudge factors are used to boost demand for
resources like bus bandswidth, clk rate etc. to
@@ -264,6 +296,10 @@
FUDGE(x, a, b) = ((x * a) / b)
- qcom,mdss-ib-factor: This fudge factor is applied to calculated ib
values in default conditions.
+- qcom,mdss-ib-factor-overlap: This fudge factor is applied to calculated ib
+ values when the overlap bandwidth is the
+ predominant value compared to prefill bandwidth
+ value.
- qcom,mdss-clk-factor: This fudge factor is applied to calculated mdp
clk rate in default conditions.
- qcom,mdss-highest-bank-bit: Property to indicate tile format as opposed to usual
@@ -333,6 +369,9 @@
qcom,mdss-ib-factor = <3 2>; /* 1.5 times */
qcom,mdss-clk-factor = <5 4>; /* 1.25 times */
+ /* Clock levels */
+ qcom,mdss-clk-levels = <92310000, 177780000, 200000000>;
+
qcom,max-clk-rate = <320000000>;
qcom,vbif-settings = <0x0004 0x00000001>,
<0x00D8 0x00000707>;
@@ -363,6 +402,20 @@
qcom,mdss-has-bwc;
qcom,mdss-has-decimation;
qcom,mdss-has-wfd-blk;
+ qcom,mdss-no-lut-read;
+
+ qcom,mdss-pipe-vig-clk-ctrl-offsets = <0x3AC 0 0>,
+ <0x3B4 0 0>,
+ <0x3BC 0 0>,
+ <0x3C4 0 0>;
+
+ qcom,mdss-pipe-rgb-clk-ctrl-offsets = <0x3AC 4 8>,
+ <0x3B4 4 8>,
+ <0x3BC 4 8>,
+ <0x3C4 4 8>;
+
+ qcom,mdss-pipe-dma-clk-ctrl-offsets = <0x3AC 8 12>,
+ <0x3B4 8 12>;
qcom,mdss-ctl-off = <0x00000600 0x00000700 0x00000800
0x00000900 0x0000A00>;
diff --git a/Documentation/devicetree/bindings/hwmon/qpnp-adc-current.txt b/Documentation/devicetree/bindings/hwmon/qpnp-adc-current.txt
index d0cad52..3e6d060 100644
--- a/Documentation/devicetree/bindings/hwmon/qpnp-adc-current.txt
+++ b/Documentation/devicetree/bindings/hwmon/qpnp-adc-current.txt
@@ -29,10 +29,15 @@
- qcom,use-default-rds-trim : Add this property to check if certain conditions are to be checked
reading the SMBB_BAT_IF_CNST_RDS, IADC_RDS trim register and
manufacturer type. Check the driver for conditions that each of the type.
- 0 : Select this type to read the IADC and SMBB trim register and
+ 0 : Select the TypeA to read the IADC and SMBB trim register and
apply the default RSENSE if conditions are met.
- 1 : Select this type to read the IADC, SMBB trim register and
+ 1 : Select the TypeB to read the IADC, SMBB trim register and
manufacturer type and apply the default RSENSE if conditions are met.
+ 2 : Select the TypeC to read the IADC, SMBB trim register and
+ apply the default RSENSE if conditions are met.
+- qcom,pmic-revid : Phandle pointing to the revision peripheral node. Use it to query the
+ PMIC type and revision for applying the appropriate temperature
+ compensation parameters.
Channel node
NOTE: Atleast one Channel node is required.
diff --git a/Documentation/devicetree/bindings/hwmon/qpnp-adc-voltage.txt b/Documentation/devicetree/bindings/hwmon/qpnp-adc-voltage.txt
index dd0c440..83403ba 100644
--- a/Documentation/devicetree/bindings/hwmon/qpnp-adc-voltage.txt
+++ b/Documentation/devicetree/bindings/hwmon/qpnp-adc-voltage.txt
@@ -22,6 +22,9 @@
Optional properties:
- qcom,vadc-poll-eoc: Use polling instead of interrupts for End of Conversion completion.
+- qcom,pmic-revid : Phandle pointing to the revision peripheral node. Use it to query the
+ PMIC type and revision for applying the appropriate temperature
+ compensation parameters.
Client required property:
- qcom,<consumer name>-vadc : The phandle to the corresponding vadc device.
diff --git a/Documentation/devicetree/bindings/memory.txt b/Documentation/devicetree/bindings/memory.txt
index e98ee05..32f6a24 100644
--- a/Documentation/devicetree/bindings/memory.txt
+++ b/Documentation/devicetree/bindings/memory.txt
@@ -36,6 +36,7 @@
reg = <(baseaddr) (size)>;
(linux,contiguous-region);
(linux,default-contiguous-region);
+ (linux,memory-limit);
label = (unique_name);
};
@@ -48,6 +49,11 @@
linux,default-contiguous-region: property indicating that the region
is the default region for all contiguous memory
allocations, Linux specific (optional)
+linux,memory-limit: property specifying an upper bound on the physical address
+ of the region if the region is placed dynamically. If no limit
+ is specificed, the region may be placed anywhere in the physical
+ address space. 0 may be used to specify lowmem (i.e. the region
+ will be placed in the direct mapped lowmem region)
label: an internal name used for automatically associating the
cma region with a given device. The label is optional;
if the label is not given the client is responsible for
diff --git a/Documentation/devicetree/bindings/mmc/sdhci-msm.txt b/Documentation/devicetree/bindings/mmc/sdhci-msm.txt
index d502f78..8c41926 100644
--- a/Documentation/devicetree/bindings/mmc/sdhci-msm.txt
+++ b/Documentation/devicetree/bindings/mmc/sdhci-msm.txt
@@ -55,6 +55,9 @@
no default value that the driver assumes if this property
is not specified. So if this property is not specified,
then SDHC driver will not vote for PM QOS.
+ - qcom,dat1-mpm-int: specifies MPM interrupt number (e.g. sdhc_2 node below)
+ corresponding to DAT1 line of SDHC (used only if slot has dedicated
+ DAT1 MSM pin (not GPIO))
In the following, <supply> can be vdd (flash core voltage) or vdd-io (I/O voltage).
- qcom,<supply>-always-on - specifies whether supply should be kept "on" always.
@@ -165,4 +168,5 @@
<81 512 106496 212992>, /* 208 MB/s */
<81 512 2147483647 4294967295>; /* Max. bandwidth */
qcom,bus-bw-vectors-bps = <0 13631488 27262976 54525952 109051904 218103808 4294967295>;
+ qcom,dat1-mpm-int = <44>;
};
diff --git a/Documentation/devicetree/bindings/power/smb358-charger.txt b/Documentation/devicetree/bindings/power/smb358-charger.txt
new file mode 100644
index 0000000..9d5dc6a
--- /dev/null
+++ b/Documentation/devicetree/bindings/power/smb358-charger.txt
@@ -0,0 +1,71 @@
+Summit smb358 battery charger
+
+SMB358 is a single-cell battery charger. It can charge
+the battery and power the system via the USB/AC adapter input.
+
+The smb358 interface is via I2C bus.
+
+Required Properties:
+- compatible Must be "qcom,smb358-charger".
+- reg The device 7-bit I2C address.
+- interrupt-parent parent of interrupt.
+- interrupts This indicates the IRQ number of the GPIO
+ connected to the STAT pin.
+- qcom,irq-gpio GPIO which receives interrupts from STAT output.
+- qcom,bms-psy-name This is a string and it points to the bms
+ power supply name.
+- qcom,float-voltage-mv Float Voltage in mV - the maximum voltage up to which
+ the battery is charged. Supported range 3500mV to 4500mV
+- qcom,vcc-i2c-supply Power source required to power up i2c bus.
+- qcom,chg-vadc Corresponding VADC device's phandle.
+
+Optional Properties:
+
+- qcom,fastchg-current-max-ma Fast Charging current in mA. Supported range is
+ from 200mA to 2000mA.
+- qcom,chg-valid-gpio GPIO which indicates the charger presence.
+ This GPIO is connected to the SYSOK pin.
+- qcom,chg-autonomous-mode This is a bool property and it indicates that the
+ charger is configured for autonomous operation and
+ does not require any software configuration.
+- qcom,disable-apsd This is a bool property which disables automatic
+ power source detection (APSD). If this is set
+ charger detection is done by DCIN UV irq.
+- qcom,charger-disabled This is a bool property which disables charging.
+- qcom,using-pmic-therm This property indicates thermal pin connected to pmic or smb.
+- qcom,iterm-ma Specifies the termination current to indicate end-of-charge.
+ Possible values in mA - 30, 40, 60, 80, 100, 125, 150, 200.
+- qcom,iterm-disabled Disables the termination current feature. This is a bool
+ property.
+- qcom,recharge-mv Recharge threshold in mV - the offset from the float-volatge
+ as which the charger restarts charging. Possible
+ values are 50mV to 300mV.
+- qcom,recharge-disabled Boolean value which disables the auto-recharge.
+- qcom,chg-adc_tm phandle to the corresponding VADC device to read the ADC channels.
+- qcom,cold-bat-decidegc Cold battery temperature in decidegC.
+- qcom,hot-bat-decidegc Hot battery temperature in decidegC.
+- qcom,bat-present-decidegc This is a property indicating battery present temperature, if
+ higher than it, battery should exist. Default value is negative,
+ if this property is 200, it stands for -200 decidegC.
+
+Example:
+ i2c@f9967000 {
+ smb358-charger@1b {
+ compatible = "qcom,smb358-charger";
+ reg = <0x1b>;
+ interrupt-parent = <&msmgpio>;
+ interrupts = <17 0x0>;
+ qcom,irq-gpio = <&msmgpio 17 0x00>;
+ qcom,vcc-i2c-supply = <&pm8226_lvs1>;
+ qcom,float-voltage-mv = <4350>;
+ qcom,disable-apsd;
+ qcom,fastchg-current-max-ma = <1500>;
+ qcom,bms-psy-name = "bms";
+ qcom,chg-vadc = <&pm8226_vadc>;
+ qcom,chg-adc_tm = <&pm8226_adc_tm>;
+ qcom,hot-bat-decidegc = <500>;
+ qcom,cold-bat-decidegc = <0>;
+ qcom,bat-present-decidegc = <200>;
+ };
+ };
+
diff --git a/Documentation/devicetree/bindings/qseecom/qseecom.txt b/Documentation/devicetree/bindings/qseecom/qseecom.txt
index 9e582e2..c0426a7 100644
--- a/Documentation/devicetree/bindings/qseecom/qseecom.txt
+++ b/Documentation/devicetree/bindings/qseecom/qseecom.txt
@@ -4,6 +4,7 @@
- compatible : Should be "qcom,qseecom"
- reg : should contain memory region address reserved for loading secure apps.
- qcom,disk-encrypt-pipe-pair : indicates what CE HW pipe pair is used for disk encryption
+- qcom,file-encrypt-pipe-pair : indicates what CE HW pipe pair is used for file encryption
- qcom,hlos-ce-hw-instance : indicates what CE HW is used by HLOS crypto driver
- qcom,qsee-ce-hw-instance : indicates what CE HW is used by secure domain (TZ) crypto driver
- qcom, msm_bus,name: Should be "qseecom-noc"
@@ -12,7 +13,9 @@
- qcom, msm_bus,vectors: Vectors for bus topology.
Optional properties:
- - qcom,support-bus-scaling : optional, indicates if driver support scaling the bus for crypto operation.
+ - qcom,support-bus-scaling : indicates if driver support scaling the bus for crypto operation.
+ - qcom,support-fde : indicates if driver support key managing for full disk encryption feature.
+ - qcom,support-pfe : indicates if driver support key managing for per file encryption feature.
Example:
qcom,qseecom@fe806000 {
@@ -20,8 +23,11 @@
reg = <0x7f00000 0x500000>;
reg-names = "secapp-region";
qcom,disk-encrypt-pipe-pair = <2>;
+ qcom,file-encrypt-pipe-pair = <0>;
qcom,hlos-ce-hw-instance = <1>;
qcom,qsee-ce-hw-instance = <0>;
+ qcom,support-fde;
+ qcom,support-pfe;
qcom,msm_bus,name = "qseecom-noc";
qcom,msm_bus,num_cases = <4>;
qcom,msm_bus,active_only = <0>;
diff --git a/Documentation/devicetree/bindings/regulator/mem-acc-regulator.txt b/Documentation/devicetree/bindings/regulator/mem-acc-regulator.txt
new file mode 100644
index 0000000..8eaa3a1
--- /dev/null
+++ b/Documentation/devicetree/bindings/regulator/mem-acc-regulator.txt
@@ -0,0 +1,52 @@
+Qualcomm Technologies Memory Accelerator
+
+Memory accelerator configures the power-mode (corner) for the
+accelerator.
+
+Required properties:
+- compatible: Must be "qcom,mem-acc-regulator"
+- regulator-name: A string used to describe the regulator
+- 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
+ should be 4 for SUPER_TURBO or 3 for TURBO
+- qcom,corner-acc-map Array which maps the APC (application processor)
+ corner value to the accelerator corner.
+ [0] maps APC SVS corner (1) to accelerator SVS corner
+ [1] maps APC NOMINAL corner (2) to accelerator NOMINAL corner
+ [2] maps APC TURBO corner (3) to accelerator TURBO corner
+
+Optional properties:
+- reg: Register addresses for acc-en and acc-sel-l1 acc-sel-l2 control.
+- reg-names: Register names. Must be "acc-sel-l1", "acc-sel-l2", "acc-en".
+ A given mem-acc-regulator driver must have "acc-sel-l1" or
+ "acc-sel-l2" reg-names property and related register address
+ property.
+- qcom,acc-en-bit-pos Array which specifies bit positions in the
+ 'acc-en' register. Setting these bits forces the
+ the acclerator to use the corner value specified
+ in the 'acc-sel-l1' and 'acc-sel-l2' register.
+- qcom,acc-sel-l1-bit-pos Array which specifies bit positions in the
+ 'acc-sel-l1' register. Each element in this array
+ is the LSB of a 2-bit value. This 2-bit value
+ specifies the corner value used by the
+ accelerator for L1 cache.
+- qcom,acc-sel-l2-bit-pos Array which specifies bit positions in the
+ 'acc-sel-l2' register. Each element in this array
+ is the LSB of a 2-bit value. This 2-bit value
+ specifies the corner value used by the
+ accelerator for L2 cache.
+
+mem_acc_vreg_corner: regulator@fd4aa044 {
+ compatible = "qcom,mem-acc-regulator";
+ reg = <0xfd4aa048 0x1>, <0xfd4aa044 0x1>, <0xfd4af000 0x1>;
+ reg-names = "acc-en", "acc-sel-l1" , "acc-sel-l2";
+ regulator-name = "mem_acc_corner";
+ regulator-min-microvolt = <1>;
+ regulator-max-microvolt = <3>;
+
+ qcom,acc-en-bit-pos = <0>;
+ qcom,acc-sel-l1-bit-pos = <0>;
+ qcom,acc-sel-l2-bit-pos = <0>;
+ qcom,corner-acc-map = <0 1 3>;
+};
diff --git a/Documentation/devicetree/bindings/slimbus/slim-msm-ctrl.txt b/Documentation/devicetree/bindings/slimbus/slim-msm-ctrl.txt
index e199e55..d25b456 100644
--- a/Documentation/devicetree/bindings/slimbus/slim-msm-ctrl.txt
+++ b/Documentation/devicetree/bindings/slimbus/slim-msm-ctrl.txt
@@ -54,11 +54,11 @@
are to be used, so that application processor can query
logical address of the ported generic device to be used.
Other than PC, fields of EA are same across platforms.
- -qcom,slim-mdm: This value indicates presence of slimbus component on
+ - qcom,slim-mdm: This value provides the identifier of slimbus component on
external mdm. This property enables the slimbus driver to
- receive subsytem restart notification from mdm and follow
- appropriate steps to ensure communication on the bus can be
- resumed after mdm-restart.
+ register and receive subsytem restart notification from mdm
+ and follow appropriate steps to ensure communication on the bus
+ can be resumed after mdm-restart.
Example:
slim@fe12f000 {
cell-index = <1>;
diff --git a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
index e672a14..401a2ec 100644
--- a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
+++ b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
@@ -140,6 +140,7 @@
FM Rx and TX port ID values from 12292 to 12293
incall record Rx and TX port ID values from 32771 to 32772
inCall Music Delivery port ID is 32773
+ incall Music 2 Delivery port ID is 32770
* msm-auxpcm
@@ -376,6 +377,11 @@
compatible = "qcom,msm-dai-q6-dev";
qcom,msm-dai-q6-dev-id = <32773>;
};
+
+ qcom,msm-dai-q6-incall-music-2-rx {
+ compatible = "qcom,msm-dai-q6-dev";
+ qcom,msm-dai-q6-dev-id = <32770>;
+ };
};
qcom,msm-pri-auxpcm {
diff --git a/Documentation/devicetree/bindings/spi/spi_qsd.txt b/Documentation/devicetree/bindings/spi/spi_qsd.txt
index 1504dc0..da71e19 100644
--- a/Documentation/devicetree/bindings/spi/spi_qsd.txt
+++ b/Documentation/devicetree/bindings/spi/spi_qsd.txt
@@ -33,6 +33,9 @@
When this entry is not present, voting is done by the runtime-pm callbacks.
- qcom,master-id : Master endpoint number used for voting on clocks using the
bus-scaling driver.
+ - qcom,rt-priority : whether spi message queue is set to run as a realtime task.
+ With this spi transaction message pump with high (realtime) priority to reduce
+ the transfer latency on the bus by minimising the delay between a transfer request
Optional properties which are required for support of BAM-mode:
- qcom,ver-reg-exists : Boolean. When present, allows driver to verify if HW
@@ -92,4 +95,5 @@
qcom,bam-producer-pipe-index = <13>;
qcom,ver-reg-exists;
qcom,master-id = <86>;
+ qcom,rt-priority;
};
diff --git a/Documentation/devicetree/bindings/usb/msm-hsusb.txt b/Documentation/devicetree/bindings/usb/msm-hsusb.txt
index 7d3d435..979819f 100644
--- a/Documentation/devicetree/bindings/usb/msm-hsusb.txt
+++ b/Documentation/devicetree/bindings/usb/msm-hsusb.txt
@@ -159,6 +159,7 @@
and no peripheral connected over dock during low power mode, fourth value represents
minimum value to vote when USB is operational, fifth item represents maximum value
to vote for USB is operational.
+- qcom,usb2-enable-uicc: If present, usb2 port will be used for uicc card connection.
Example MSM HSUSB EHCI controller device node :
ehci: qcom,ehci-host@f9a55000 {
@@ -172,6 +173,7 @@
qcom,usb2-enable-hsphy2;
qcom,usb2-power-budget = <500>;
qcom,vdd-voltage-level = <1 2 3 5 7>;
+ qcom,usb2-enable-uicc;
};
ANDROID USB:
@@ -194,12 +196,15 @@
turnaround timeout is observed on enabling streaming. Hence it is required
to see these errors and number of erros on enabling this at USB level to make
final decision to enable this feature or not.
+- qcom,android-usb-uicc-nluns : Number of mass storage LUNs required for
+ the UICC card.
Example Android USB device node :
android_usb@fc42b0c8 {
compatible = "qcom,android-usb";
reg = <0xfc42b0c8 0xc8>;
qcom,android-usb-swfi-latency = <1>;
qcom,streaming-func = "rndis","mtp";
+ qcom,android-usb-uicc-nluns = <1>;
};
diff --git a/Documentation/devicetree/bindings/usb/msm-ssusb.txt b/Documentation/devicetree/bindings/usb/msm-ssusb.txt
index bfa9abe..d2f03ea 100644
--- a/Documentation/devicetree/bindings/usb/msm-ssusb.txt
+++ b/Documentation/devicetree/bindings/usb/msm-ssusb.txt
@@ -60,6 +60,8 @@
deemphasis value to be used for overriding into SSPHY register.
- qcom,usbin-vadc: Corresponding vadc device's phandle to read usbin voltage using VADC.
This will be used to get value of usb power supply's VOLTAGE_NOW property,
+- qcom,utmi-clk-rate: Indicates refclk frequency (in Hz) to the core. If not
+ specified, default of 19.2MHz is assumed.
Sub nodes:
- Sub node for "DWC3- USB3 controller".
diff --git a/Documentation/vm/ksm.txt b/Documentation/vm/ksm.txt
index b392e49..8e54981 100644
--- a/Documentation/vm/ksm.txt
+++ b/Documentation/vm/ksm.txt
@@ -72,6 +72,13 @@
pages_unshared - how many pages unique but repeatedly checked for merging
pages_volatile - how many pages changing too fast to be placed in a tree
full_scans - how many times all mergeable areas have been scanned
+deferred_timer - whether to use deferred timers or not
+ e.g. "echo 1 > /sys/kernel/mm/ksm/deferred_timer"
+ Default: 0 (means, we are not using deferred timers. Users
+ might want to set deferred_timer option if they donot want
+ ksm thread to wakeup CPU to carryout ksm activities thus
+ gaining on battery while compromising slightly on memory
+ that could have been saved.)
A high ratio of pages_sharing to pages_shared indicates good sharing, but
a high ratio of pages_unshared to pages_sharing indicates wasted effort.
diff --git a/arch/arm/Kconfig.debug b/arch/arm/Kconfig.debug
index 344b57e..969ddcb 100644
--- a/arch/arm/Kconfig.debug
+++ b/arch/arm/Kconfig.debug
@@ -63,6 +63,29 @@
8 - SIGSEGV faults
16 - SIGBUS faults
+config FORCE_PAGES
+ bool "Force lowmem to be mapped with 4K pages"
+ help
+ There are some advanced debug features that can only be done when
+ memory is mapped with pages instead of sections. Enable this option
+ to always map lowmem pages with pages. This may have a performance
+ cost due to increased TLB pressure.
+
+ If unsure say N.
+
+config FREE_PAGES_RDONLY
+ bool "Set pages as read only while on the buddy list"
+ select FORCE_PAGES
+ select PAGE_POISONING
+ help
+ Pages are always mapped in the kernel. This means that anyone
+ can write to the page if they have the address. Enable this option
+ to mark pages as read only to trigger a fault if any code attempts
+ to write to a page on the buddy list. This may have a performance
+ impact.
+
+ If unsure, say N.
+
# These options are only for real kernel hackers who want to get their hands dirty.
config DEBUG_LL
bool "Kernel low-level debugging functions (read help!)"
diff --git a/arch/arm/boot/dts/apq8074-v1.dtsi b/arch/arm/boot/dts/apq8074-v1.dtsi
index 59e7f7f..be7dee8 100644
--- a/arch/arm/boot/dts/apq8074-v1.dtsi
+++ b/arch/arm/boot/dts/apq8074-v1.dtsi
@@ -27,15 +27,17 @@
qcom,disk-encrypt-pipe-pair = <2>;
qcom,hlos-ce-hw-instance = <1>;
qcom,qsee-ce-hw-instance = <0>;
+ qcom,support-bus-scaling;
qcom,msm-bus,name = "qseecom-noc";
qcom,msm-bus,num-cases = <4>;
qcom,msm-bus,active-only = <0>;
qcom,msm-bus,num-paths = <1>;
+ qcom,support-fde;
qcom,msm-bus,vectors-KBps =
<55 512 0 0>,
- <55 512 3936000 393600>,
- <55 512 3936000 393600>,
- <55 512 3936000 393600>;
+ <55 512 0 0>,
+ <55 512 120000 1200000>,
+ <55 512 393600 3936000>;
};
};
diff --git a/arch/arm/boot/dts/apq8074-v2.0-1.dtsi b/arch/arm/boot/dts/apq8074-v2.0-1.dtsi
index 3575c92..064a28c 100644
--- a/arch/arm/boot/dts/apq8074-v2.0-1.dtsi
+++ b/arch/arm/boot/dts/apq8074-v2.0-1.dtsi
@@ -31,11 +31,13 @@
qcom,msm-bus,num-cases = <4>;
qcom,msm-bus,active-only = <0>;
qcom,msm-bus,num-paths = <1>;
+ qcom,support-fde;
+ qcom,support-bus-scaling;
qcom,msm-bus,vectors-KBps =
<55 512 0 0>,
- <55 512 3936000 393600>,
- <55 512 3936000 393600>,
- <55 512 3936000 393600>;
+ <55 512 0 0>,
+ <55 512 120000 1200000>,
+ <55 512 393600 3936000>;
};
sound {
diff --git a/arch/arm/boot/dts/apq8074-v2.2.dtsi b/arch/arm/boot/dts/apq8074-v2.2.dtsi
index 18f00c5..010d574 100644
--- a/arch/arm/boot/dts/apq8074-v2.2.dtsi
+++ b/arch/arm/boot/dts/apq8074-v2.2.dtsi
@@ -31,11 +31,13 @@
qcom,msm-bus,num-cases = <4>;
qcom,msm-bus,active-only = <0>;
qcom,msm-bus,num-paths = <1>;
+ qcom,support-fde;
+ qcom,support-bus-scaling;
qcom,msm-bus,vectors-KBps =
<55 512 0 0>,
- <55 512 3936000 393600>,
- <55 512 3936000 393600>,
- <55 512 3936000 393600>;
+ <55 512 0 0>,
+ <55 512 120000 1200000>,
+ <55 512 393600 3936000>;
};
sound {
diff --git a/arch/arm/boot/dts/apq8084-camera-sensor-cdp.dtsi b/arch/arm/boot/dts/apq8084-camera-sensor-cdp.dtsi
index 5577d16..173ea63 100644
--- a/arch/arm/boot/dts/apq8084-camera-sensor-cdp.dtsi
+++ b/arch/arm/boot/dts/apq8084-camera-sensor-cdp.dtsi
@@ -104,6 +104,7 @@
qcom,csiphy-sd-index = <0>;
qcom,csid-sd-index = <0>;
qcom,actuator-src = <&actuator0>;
+ qcom,mount-angle = <90>;
cam_vdig-supply = <&pma8084_l27>;
cam_vio-supply = <&pma8084_lvs4>;
cam_vana-supply = <&pma8084_l17>;
@@ -125,6 +126,8 @@
qcom,gpio-req-tbl-label = "CAMIF_MCLK",
"CAM_RESET1",
"CAM_STANDBY";
+ qcom,sensor-position = <0>;
+ qcom,sensor-mode = <1>;
qcom,cci-master = <0>;
status = "ok";
};
@@ -153,6 +156,8 @@
qcom,gpio-req-tbl-flags = <1 0>;
qcom,gpio-req-tbl-label = "CAMIF_MCLK2",
"CAM_XSHUTDOWN";
+ qcom,sensor-position = <1>;
+ qcom,sensor-mode = <1>;
qcom,cci-master = <0>;
status = "ok";
};
diff --git a/arch/arm/boot/dts/apq8084-camera-sensor-mtp.dtsi b/arch/arm/boot/dts/apq8084-camera-sensor-mtp.dtsi
index 02d8b59..bfbbf54 100644
--- a/arch/arm/boot/dts/apq8084-camera-sensor-mtp.dtsi
+++ b/arch/arm/boot/dts/apq8084-camera-sensor-mtp.dtsi
@@ -126,6 +126,8 @@
qcom,gpio-req-tbl-label = "CAMIF_MCLK",
"CAM_RESET1",
"CAM_STANDBY";
+ qcom,sensor-position = <0>;
+ qcom,sensor-mode = <1>;
qcom,cci-master = <0>;
status = "ok";
};
@@ -153,6 +155,8 @@
qcom,gpio-req-tbl-flags = <1 0>;
qcom,gpio-req-tbl-label = "CAMIF_MCLK2",
"CAM_XSHUTDOWN";
+ qcom,sensor-position = <1>;
+ qcom,sensor-mode = <1>;
qcom,cci-master = <0>;
status = "ok";
};
diff --git a/arch/arm/boot/dts/apq8084-mdss.dtsi b/arch/arm/boot/dts/apq8084-mdss.dtsi
index 329ab32..665eedc 100644
--- a/arch/arm/boot/dts/apq8084-mdss.dtsi
+++ b/arch/arm/boot/dts/apq8084-mdss.dtsi
@@ -54,6 +54,17 @@
qcom,mdss-pipe-rgb-xin-id = <1 5 9 13>;
qcom,mdss-pipe-dma-xin-id = <2 10>;
+ qcom,mdss-pipe-vig-clk-ctrl-offsets = <0x3AC 0 0>,
+ <0x3B4 0 0>,
+ <0x3BC 0 0>,
+ <0x3C4 0 0>;
+ qcom,mdss-pipe-rgb-clk-ctrl-offsets = <0x3AC 4 8>,
+ <0x3B4 4 8>,
+ <0x3BC 4 8>,
+ <0x3C4 4 8>;
+ qcom,mdss-pipe-dma-clk-ctrl-offsets = <0x3AC 8 12>,
+ <0x3B4 8 12>;
+
qcom,mdss-smp-data = <44 8192>;
qcom,mdss-ctl-off = <0x00000600 0x00000700 0x00000800
diff --git a/arch/arm/boot/dts/dsi-panel-generic-720p-cmd.dtsi b/arch/arm/boot/dts/dsi-panel-generic-720p-cmd.dtsi
index 14d4c35..214c7f8 100644
--- a/arch/arm/boot/dts/dsi-panel-generic-720p-cmd.dtsi
+++ b/arch/arm/boot/dts/dsi-panel-generic-720p-cmd.dtsi
@@ -75,5 +75,6 @@
qcom,mdss-dsi-mdp-trigger = "none";
qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_pwm";
qcom,mdss-dsi-reset-sequence = <1 20>, <0 200>, <1 20>;
+ qcom,partial-update-enabled;
};
};
diff --git a/arch/arm/boot/dts/dsi-panel-jdi-1080p-video.dtsi b/arch/arm/boot/dts/dsi-panel-jdi-1080p-video.dtsi
index 917c637..17b39fc 100644
--- a/arch/arm/boot/dts/dsi-panel-jdi-1080p-video.dtsi
+++ b/arch/arm/boot/dts/dsi-panel-jdi-1080p-video.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2014, 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
@@ -30,8 +30,8 @@
qcom,mdss-dsi-h-back-porch = <64>;
qcom,mdss-dsi-h-pulse-width = <16>;
qcom,mdss-dsi-h-sync-skew = <0>;
- qcom,mdss-dsi-v-back-porch = <4>;
- qcom,mdss-dsi-v-front-porch = <3>;
+ qcom,mdss-dsi-v-back-porch = <16>;
+ qcom,mdss-dsi-v-front-porch = <4>;
qcom,mdss-dsi-v-pulse-width = <1>;
qcom,mdss-dsi-h-left-border = <0>;
qcom,mdss-dsi-h-right-border = <0>;
@@ -57,7 +57,7 @@
qcom,mdss-dsi-lane-1-state;
qcom,mdss-dsi-lane-2-state;
qcom,mdss-dsi-lane-3-state;
- qcom,mdss-dsi-panel-timings = [e1 37 25 00 67 6b 2a 3a 59 03 04 00];
+ qcom,mdss-dsi-panel-timings = [e7 36 24 00 66 6a 2a 3a 2d 03 04 00];
qcom,mdss-dsi-t-clk-post = <0x04>;
qcom,mdss-dsi-t-clk-pre = <0x1b>;
qcom,mdss-dsi-bl-min-level = <1>;
diff --git a/arch/arm/boot/dts/dsi-panel-jdi-720p-cmd.dtsi b/arch/arm/boot/dts/dsi-panel-jdi-720p-cmd.dtsi
new file mode 100755
index 0000000..d465a6f
--- /dev/null
+++ b/arch/arm/boot/dts/dsi-panel-jdi-720p-cmd.dtsi
@@ -0,0 +1,89 @@
+/* Copyright (c) 2014, 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.
+ */
+
+/*---------------------------------------------------------------------------
+ * This file is autogenerated file using gcdb parser. Please do not edit it.
+ * Update input XML file to add a new entry or update variable in this file
+ * VERSION = "1.0"
+ *---------------------------------------------------------------------------*/
+&mdss_mdp {
+ dsi_jdi_720p_cmd: qcom,mdss_dsi_jdi_720p_cmd {
+ qcom,mdss-dsi-panel-name = "JDI 720p command mode dsi panel";
+ qcom,mdss-dsi-panel-controller = <&mdss_dsi0>;
+ qcom,mdss-dsi-panel-type = "dsi_cmd_mode";
+ qcom,mdss-dsi-panel-destination = "display_1";
+ qcom,mdss-dsi-panel-framerate = <50>;
+ qcom,mdss-dsi-virtual-channel-id = <0>;
+ qcom,mdss-dsi-stream = <0>;
+ qcom,mdss-dsi-panel-width = <720>;
+ qcom,mdss-dsi-panel-height = <1280>;
+ qcom,mdss-dsi-h-front-porch = <130>;
+ qcom,mdss-dsi-h-back-porch = <130>;
+ qcom,mdss-dsi-h-pulse-width = <8>;
+ qcom,mdss-dsi-h-sync-skew = <0>;
+ qcom,mdss-dsi-v-back-porch = <32>;
+ qcom,mdss-dsi-v-front-porch = <32>;
+ qcom,mdss-dsi-v-pulse-width = <4>;
+ qcom,mdss-dsi-h-left-border = <0>;
+ qcom,mdss-dsi-h-right-border = <0>;
+ qcom,mdss-dsi-v-top-border = <0>;
+ qcom,mdss-dsi-v-bottom-border = <0>;
+ qcom,mdss-dsi-bpp = <24>;
+ qcom,mdss-dsi-underflow-color = <0xff>;
+ qcom,mdss-dsi-border-color = <0>;
+ qcom,mdss-dsi-on-command = [05 01 00 00 78 00 02 28 00
+ 05 01 00 00 78 00 02 10 00
+ 05 01 00 00 64 00 02 01 00
+ 05 01 00 00 64 00 02 11 00
+ 05 01 00 00 64 00 02 29 00
+ 15 01 00 00 0a 00 02 35 00
+ 39 01 00 00 0a 00 04 ff 12 82 01
+ 15 01 00 00 0a 00 02 00 80
+ 15 01 00 00 0a 00 02 ff 12
+ 15 01 00 00 0a 00 02 00 81
+ 15 01 00 00 0a 00 02 ff 82
+ 15 01 00 00 0a 00 02 00 b4
+ 15 01 00 00 0a 00 02 c0 40
+ 39 01 00 00 0a 00 08 00 80 ff 00 00 81 ff 00
+ 39 01 00 00 0a 00 06 00 00 ff 00 00 00];
+ qcom,mdss-dsi-off-command = [05 01 00 00 0a 00 01 34
+ 05 01 00 00 0a 00 02 28 00
+ 05 01 00 00 78 00 02 10 00];
+ qcom,mdss-dsi-on-command-state = "dsi_lp_mode";
+ qcom,mdss-dsi-off-command-state = "dsi_hs_mode";
+ qcom,mdss-dsi-h-sync-pulse = <0>;
+ qcom,mdss-dsi-traffic-mode = "non_burst_sync_event";
+ qcom,mdss-dsi-pan-enable-dynamic-fps;
+ qcom,mdss-dsi-fps-update = "dfps_suspend_resume_mode";
+ qcom,mdss-dsi-bllp-eof-power-mode;
+ qcom,mdss-dsi-bllp-power-mode;
+ qcom,mdss-dsi-lane-0-state;
+ qcom,mdss-dsi-lane-1-state;
+ qcom,mdss-dsi-lane-2-state;
+ qcom,mdss-dsi-lane-3-state;
+ qcom,mdss-dsi-te-pin-select = <1>;
+ qcom,mdss-dsi-te-v-sync-rd-ptr-irq-line = <0x2c>;
+ qcom,mdss-dsi-te-dcs-command = <1>;
+ qcom,mdss-dsi-te-check-enable;
+ qcom,mdss-dsi-te-using-te-pin;
+ qcom,mdss-dsi-panel-timings = [6e 26 1b 00 35 34
+ 20 28 17 03 04 00];
+ qcom,mdss-dsi-t-clk-post = <0x20>;
+ qcom,mdss-dsi-t-clk-pre = <0x2a>;
+ qcom,mdss-dsi-bl-min-level = <1>;
+ qcom,mdss-dsi-bl-max-level = <4095>;
+ qcom,mdss-dsi-dma-trigger = "trigger_sw";
+ qcom,mdss-dsi-mdp-trigger = "none";
+ qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+ qcom,mdss-dsi-reset-sequence = <1 20>, <0 200>, <1 20>;
+ };
+};
diff --git a/arch/arm/boot/dts/dsi-panel-nt35590-720p-video.dtsi b/arch/arm/boot/dts/dsi-panel-nt35590-720p-video.dtsi
index 8d28996..9a18a31 100644
--- a/arch/arm/boot/dts/dsi-panel-nt35590-720p-video.dtsi
+++ b/arch/arm/boot/dts/dsi-panel-nt35590-720p-video.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014, 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
@@ -30,7 +30,7 @@
qcom,mdss-dsi-h-back-porch = <164>;
qcom,mdss-dsi-h-pulse-width = <8>;
qcom,mdss-dsi-h-sync-skew = <0>;
- qcom,mdss-dsi-v-back-porch = <1>;
+ qcom,mdss-dsi-v-back-porch = <11>;
qcom,mdss-dsi-v-front-porch = <6>;
qcom,mdss-dsi-v-pulse-width = <1>;
qcom,mdss-dsi-h-left-border = <0>;
@@ -504,7 +504,9 @@
29 01 00 00 00 00 02 6A 60
29 01 00 00 00 00 02 FF 00
29 01 00 00 78 00 02 29 00
- 29 01 00 00 78 00 02 53 2C];
+ 29 01 00 00 78 00 02 53 2C
+ 29 01 00 00 00 00 02 FF 00
+ 29 01 00 00 00 00 06 3B 03 06 03 02 02];
qcom,mdss-dsi-off-command = [05 01 00 00 32 00 02 28 00
05 01 00 00 78 00 02 10 00];
qcom,mdss-dsi-on-command-state = "dsi_lp_mode";
diff --git a/arch/arm/boot/dts/dsi-panel-truly-wvga-cmd.dtsi b/arch/arm/boot/dts/dsi-panel-truly-wvga-cmd.dtsi
index 3106cd4..e6aaa41 100644
--- a/arch/arm/boot/dts/dsi-panel-truly-wvga-cmd.dtsi
+++ b/arch/arm/boot/dts/dsi-panel-truly-wvga-cmd.dtsi
@@ -167,5 +167,9 @@
qcom,mdss-dsi-reset-sequence = <1 20>, <0 2>, <1 20>;
qcom,mdss-pan-physical-width-dimension = <52>;
qcom,mdss-pan-physical-height-dimension = <86>;
+ qcom,mdss-tear-check-rd-ptr-trigger-intr = <0x5>;
+ qcom,mdss-tear-check-sync-threshold-start = <0x2>;
+ qcom,mdss-tear-check-sync-threshold-continue = <0x4>;
+ qcom,mdss-tear-check-start-pos = <0x18>;
};
};
diff --git a/arch/arm/boot/dts/msm-pm8110.dtsi b/arch/arm/boot/dts/msm-pm8110.dtsi
index 9adbf81..5ccecfa 100644
--- a/arch/arm/boot/dts/msm-pm8110.dtsi
+++ b/arch/arm/boot/dts/msm-pm8110.dtsi
@@ -22,7 +22,7 @@
#address-cells = <1>;
#size-cells = <1>;
- qcom,revid@100 {
+ pm8110_revid: qcom,revid@100 {
compatible = "qcom,qpnp-revid";
reg = <0x100 0x100>;
};
@@ -219,6 +219,7 @@
qcom,adc-bit-resolution = <15>;
qcom,adc-vdd-reference = <1800>;
qcom,vadc-poll-eoc;
+ qcom,pmic-revid = <&pm8110_revid>;
chan@8 {
label = "die_temp";
@@ -268,6 +269,7 @@
qcom,iadc-vadc = <&pm8110_vadc>;
qcom,iadc-poll-eoc;
qcom,use-default-rds-trim = <1>;
+ qcom,pmic-revid = <&pm8110_revid>;
chan@0 {
label = "internal_rsense";
@@ -337,6 +339,10 @@
qcom,bms-iadc = <&pm8110_iadc>;
qcom,bms-adc_tm = <&pm8110_adc_tm>;
+ qcom,batt-pres-status@1208 {
+ reg = <0x1208 0x1>;
+ };
+
qcom,bms-iadc@3800 {
reg = <0x3800 0x100>;
};
diff --git a/arch/arm/boot/dts/msm-pm8226.dtsi b/arch/arm/boot/dts/msm-pm8226.dtsi
index 41897da..6e1b985 100644
--- a/arch/arm/boot/dts/msm-pm8226.dtsi
+++ b/arch/arm/boot/dts/msm-pm8226.dtsi
@@ -210,6 +210,10 @@
qcom,bms-iadc = <&pm8226_iadc>;
qcom,bms-adc_tm = <&pm8226_adc_tm>;
+ qcom,batt-pres-status@1208 {
+ reg = <0x1208 0x1>;
+ };
+
qcom,bms-iadc@3800 {
reg = <0x3800 0x100>;
};
@@ -364,6 +368,7 @@
qcom,adc-bit-resolution = <15>;
qcom,adc-vdd-reference = <1800>;
qcom,vadc-poll-eoc;
+ qcom,pmic-revid = <&pm8226_revid>;
chan@8 {
label = "die_temp";
@@ -423,7 +428,8 @@
qcom,adc-vdd-reference = <1800>;
qcom,iadc-vadc = <&pm8226_vadc>;
qcom,iadc-poll-eoc;
- qcom,use-default-rds-trim = <0>;
+ qcom,use-default-rds-trim = <2>;
+ qcom,pmic-revid = <&pm8226_revid>;
chan@0 {
label = "internal_rsense";
diff --git a/arch/arm/boot/dts/msm-pm8941.dtsi b/arch/arm/boot/dts/msm-pm8941.dtsi
index 94a4e83..d8046c5 100644
--- a/arch/arm/boot/dts/msm-pm8941.dtsi
+++ b/arch/arm/boot/dts/msm-pm8941.dtsi
@@ -27,7 +27,7 @@
reg = <0x900 0x100>;
};
- qcom,revid@100 {
+ pm8941_revid: qcom,revid@100 {
compatible = "qcom,qpnp-revid";
reg = <0x100 0x100>;
};
@@ -141,6 +141,10 @@
qcom,bms-iadc = <&pm8941_iadc>;
qcom,bms-adc_tm = <&pm8941_adc_tm>;
+ qcom,batt-pres-status@1208 {
+ reg = <0x1208 0x1>;
+ };
+
qcom,bms-iadc@3800 {
reg = <0x3800 0x100>;
};
@@ -577,6 +581,7 @@
qcom,adc-bit-resolution = <15>;
qcom,adc-vdd-reference = <1800>;
qcom,vadc-poll-eoc;
+ qcom,pmic-revid = <&pm8941_revid>;
chan@0 {
label = "usb_in";
@@ -824,6 +829,7 @@
qcom,iadc-vadc = <&pm8941_vadc>;
qcom,iadc-poll-eoc;
qcom,use-default-rds-trim = <0>;
+ qcom,pmic-revid = <&pm8941_revid>;
chan@0 {
label = "internal_rsense";
diff --git a/arch/arm/boot/dts/msm-pma8084.dtsi b/arch/arm/boot/dts/msm-pma8084.dtsi
index c070443..93f05c4 100644
--- a/arch/arm/boot/dts/msm-pma8084.dtsi
+++ b/arch/arm/boot/dts/msm-pma8084.dtsi
@@ -837,5 +837,53 @@
status = "disabled";
};
};
+
+ pma8084_lpg1: pwm@b100 {
+ compatible = "qcom,qpnp-pwm";
+ reg = <0xb100 0x100>,
+ <0xb042 0x7e>;
+ reg-names = "qpnp-lpg-channel-base", "qpnp-lpg-lut-base";
+ qcom,channel-id = <0>;
+ };
+
+ pma8084_lpg2: pwm@b200 {
+ compatible = "qcom,qpnp-pwm";
+ reg = <0xb200 0x100>,
+ <0xb042 0x7e>;
+ reg-names = "qpnp-lpg-channel-base", "qpnp-lpg-lut-base";
+ qcom,channel-id = <1>;
+ };
+
+ pma8084_lpg3: pwm@b300 {
+ compatible = "qcom,qpnp-pwm";
+ reg = <0xb300 0x100>,
+ <0xb042 0x7e>;
+ reg-names = "qpnp-lpg-channel-base", "qpnp-lpg-lut-base";
+ qcom,channel-id = <2>;
+ };
+
+ pma8084_lpg4: pwm@b400 {
+ compatible = "qcom,qpnp-pwm";
+ reg = <0xb400 0x100>,
+ <0xb042 0x7e>;
+ reg-names = "qpnp-lpg-channel-base", "qpnp-lpg-lut-base";
+ qcom,channel-id = <3>;
+ };
+
+ pma8084_lpg5: pwm@b500 {
+ compatible = "qcom,qpnp-pwm";
+ reg = <0xb500 0x100>,
+ <0xb042 0x7e>;
+ reg-names = "qpnp-lpg-channel-base", "qpnp-lpg-lut-base";
+ qcom,channel-id = <4>;
+ };
+
+ pma8084_lpg6: pwm@b600 {
+ compatible = "qcom,qpnp-pwm";
+ reg = <0xb600 0x100>,
+ <0xb042 0x7e>;
+ reg-names = "qpnp-lpg-channel-base", "qpnp-lpg-lut-base";
+ qcom,channel-id = <5>;
+ };
};
};
diff --git a/arch/arm/boot/dts/msm8226-720p-cdp.dtsi b/arch/arm/boot/dts/msm8226-720p-cdp.dtsi
index 00e90f3..66f9b18 100644
--- a/arch/arm/boot/dts/msm8226-720p-cdp.dtsi
+++ b/arch/arm/boot/dts/msm8226-720p-cdp.dtsi
@@ -35,6 +35,20 @@
};
};
+ i2c@f9925000 { /* BLSP1 QUP3 */
+ nfc-nci@0e {
+ compatible = "qcom,nfc-nci";
+ reg = <0x0e>;
+ qcom,irq-gpio = <&msmgpio 21 0x00>;
+ qcom,dis-gpio = <&msmgpio 20 0x00>;
+ qcom,clk-src = "BBCLK2";
+ interrupt-parent = <&msmgpio>;
+ interrupts = <21 0>;
+ qcom,clk-gpio = <&pm8226_gpios 3 0>;
+ };
+ };
+
+
gpio_keys {
compatible = "gpio-keys";
input-name = "gpio-keys";
@@ -372,6 +386,11 @@
};
gpio@c200 { /* GPIO 3 */
+ qcom,mode = <0>; /* QPNP_PIN_MODE_DIG_IN */
+ qcom,pull = <5>; /* QPNP_PIN_PULL_NO */
+ qcom,vin-sel = <2>; /* QPNP_PIN_VIN2 */
+ qcom,src-sel = <2>; /* QPNP_PIN_SEL_FUNC_1 */
+ qcom,master-en = <1>;
};
gpio@c300 { /* GPIO 4 */
diff --git a/arch/arm/boot/dts/msm8226-bus.dtsi b/arch/arm/boot/dts/msm8226-bus.dtsi
index 74b4a30..8abe795 100644
--- a/arch/arm/boot/dts/msm8226-bus.dtsi
+++ b/arch/arm/boot/dts/msm8226-bus.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2014, 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
@@ -22,6 +22,7 @@
qcom,qos-freq = <4800>;
qcom,hw-sel = "NoC";
qcom,rpm-en;
+ qcom,nfab = <6>;
mas-gfx3d {
cell-id = <26>;
@@ -261,6 +262,7 @@
qcom,qos-freq = <4800>;
qcom,hw-sel = "NoC";
qcom,rpm-en;
+ qcom,nfab = <6>;
mas-lpass-ahb {
cell-id = <52>;
@@ -473,6 +475,7 @@
qcom,ntieredslaves = <0>;
qcom,hw-sel = "NoC";
qcom,rpm-en;
+ qcom,nfab = <6>;
mas-pnoc-cfg {
cell-id = <88>;
@@ -678,6 +681,7 @@
qcom,ntieredslaves = <0>;
qcom,hw-sel = "NoC";
qcom,rpm-en;
+ qcom,nfab = <6>;
mas-rpm-inst {
cell-id = <72>;
@@ -998,9 +1002,10 @@
qcom,fabclk-dual = "mem_clk";
qcom,fabclk-active = "mem_a_clk";
qcom,ntieredslaves = <0>;
- qcom,qos-freq = <4800>;
+ qcom,qos-freq = <19200>;
qcom,hw-sel = "BIMC";
qcom,rpm-en;
+ qcom,nfab = <6>;
mas-ampss-m0 {
cell-id = <1>;
@@ -1008,12 +1013,18 @@
qcom,masterp = <0>;
qcom,tier = <2>;
qcom,hw-sel = "BIMC";
- qcom,mode = "Fixed";
+ qcom,mode = "Limiter";
qcom,qport = <0>;
qcom,ws = <10000>;
qcom,mas-hw-id = <0>;
qcom,prio-rd = <0>;
qcom,prio-wr = <0>;
+ qcom,mode-thresh = "Fixed";
+ qcom,thresh = <1800000>;
+ qcom,dual-conf;
+ qcom,bimc,bw = <450000>;
+ qcom,bimc,gp = <5000>;
+ qcom,bimc,thmp = <50>;
};
mas-mss-proc {
@@ -1082,6 +1093,7 @@
qcom,hw-sel = "NoC";
qcom,rpm-en;
qcom,virt;
+ qcom,nfab = <6>;
mas-v-ocmem-gfx3d {
cell-id = <89>;
diff --git a/arch/arm/boot/dts/msm8226-camera-sensor-cdp.dtsi b/arch/arm/boot/dts/msm8226-camera-sensor-cdp.dtsi
index 4170255..05a3f07 100644
--- a/arch/arm/boot/dts/msm8226-camera-sensor-cdp.dtsi
+++ b/arch/arm/boot/dts/msm8226-camera-sensor-cdp.dtsi
@@ -198,6 +198,7 @@
reg = <0x0>;
qcom,csiphy-sd-index = <0>;
qcom,csid-sd-index = <0>;
+ qcom,mount-angle = <90>;
qcom,actuator-src = <&actuator0>;
qcom,led-flash-src = <&led_flash0>;
cam_vdig-supply = <&pm8226_l5>;
@@ -220,6 +221,8 @@
qcom,gpio-req-tbl-label = "CAMIF_MCLK",
"CAM_RESET1",
"CAM_STANDBY";
+ qcom,sensor-position = <0>;
+ qcom,sensor-mode = <0>;
qcom,cci-master = <0>;
status = "ok";
};
@@ -230,6 +233,7 @@
reg = <0x1>;
qcom,csiphy-sd-index = <1>;
qcom,csid-sd-index = <1>;
+ qcom,mount-angle = <0>;
cam_vdig-supply = <&pm8226_l5>;
cam_vana-supply = <&pm8226_l19>;
cam_vio-supply = <&pm8226_lvs1>;
@@ -248,6 +252,8 @@
qcom,gpio-req-tbl-label = "CAMIF_MCLK",
"CAM_RESET",
"CAM_STANDBY";
+ qcom,sensor-position = <1>;
+ qcom,sensor-mode = <1>;
qcom,cci-master = <0>;
status = "ok";
};
diff --git a/arch/arm/boot/dts/msm8226-camera-sensor-mtp.dtsi b/arch/arm/boot/dts/msm8226-camera-sensor-mtp.dtsi
index 97e7dff..a6af2c2 100644
--- a/arch/arm/boot/dts/msm8226-camera-sensor-mtp.dtsi
+++ b/arch/arm/boot/dts/msm8226-camera-sensor-mtp.dtsi
@@ -112,7 +112,7 @@
reg = <0x0>;
qcom,csiphy-sd-index = <0>;
qcom,csid-sd-index = <0>;
- qcom,mount-angle = <0>;
+ qcom,mount-angle = <270>;
qcom,actuator-src = <&actuator0>;
qcom,led-flash-src = <&led_flash0>;
cam_vdig-supply = <&pm8226_l5>;
@@ -136,6 +136,8 @@
qcom,gpio-req-tbl-label = "CAMIF_MCLK",
"CAM_RESET1",
"CAM_STANDBY";
+ qcom,sensor-position = <0>;
+ qcom,sensor-mode = <0>;
qcom,cci-master = <0>;
status = "ok";
};
@@ -166,6 +168,8 @@
qcom,gpio-req-tbl-label = "CAMIF_MCLK",
"CAM_RESET",
"CAM_STANDBY";
+ qcom,sensor-position = <1>;
+ qcom,sensor-mode = <0>;
qcom,cci-master = <0>;
status = "ok";
};
diff --git a/arch/arm/boot/dts/msm8226-camera-sensor-qrd.dtsi b/arch/arm/boot/dts/msm8226-camera-sensor-qrd.dtsi
index a553918..dab92ff 100644
--- a/arch/arm/boot/dts/msm8226-camera-sensor-qrd.dtsi
+++ b/arch/arm/boot/dts/msm8226-camera-sensor-qrd.dtsi
@@ -448,7 +448,7 @@
reg = <0x0>;
qcom,csiphy-sd-index = <0>;
qcom,csid-sd-index = <0>;
- qcom,mount-angle = <270>;
+ qcom,mount-angle = <90>;
qcom,actuator-src = <&actuator0>;
qcom,eeprom-src = <&eeprom0>;
qcom,led-flash-src = <&led_flash0>;
@@ -479,6 +479,8 @@
"CAM_STANDBY",
"CAM_VDIG",
"CAM_AF_PWDM";
+ qcom,sensor-position = <0>;
+ qcom,sensor-mode = <0>;
qcom,cci-master = <0>;
status = "ok";
};
@@ -516,6 +518,8 @@
qcom,gpio-set-tbl-num = <1 1>;
qcom,gpio-set-tbl-flags = <0 2>;
qcom,gpio-set-tbl-delay = <1000 4000>;
+ qcom,sensor-position = <1>;
+ qcom,sensor-mode = <0>;
qcom,cci-master = <0>;
status = "ok";
};
diff --git a/arch/arm/boot/dts/msm8226-mdss.dtsi b/arch/arm/boot/dts/msm8226-mdss.dtsi
index 2176d39..cedec5f 100644
--- a/arch/arm/boot/dts/msm8226-mdss.dtsi
+++ b/arch/arm/boot/dts/msm8226-mdss.dtsi
@@ -19,7 +19,7 @@
interrupts = <0 72 0>;
vdd-supply = <&gdsc_mdss>;
- qcom,max-bandwidth-low-kbps = <1660000>;
+ qcom,max-bandwidth-low-kbps = <1100000>;
qcom,max-bandwidth-high-kbps = <1660000>;
/* Bus Scale Settings */
@@ -32,10 +32,14 @@
<22 512 0 6400000>;
/* Fudge factors */
- qcom,mdss-ab-factor = <2 1>; /* 2 times */
- qcom,mdss-ib-factor = <6 5>; /* 1.2 times */
+ qcom,mdss-ab-factor = <1 1>; /* 1 times */
+ qcom,mdss-ib-factor = <2 1>; /* 2 times */
qcom,mdss-clk-factor = <5 4>; /* 1.25 times */
+ /* Clock levels */
+ qcom,mdss-clk-levels = <92310000 100000000
+ 133330000 177780000 200000000>;
+
qcom,max-clk-rate = <200000000>;
qcom,mdss-pipe-vig-off = <0x00001200>;
qcom,mdss-pipe-rgb-off = <0x00001E00>;
@@ -48,8 +52,14 @@
qcom,mdss-pipe-rgb-xin-id = <1>;
qcom,mdss-pipe-dma-xin-id = <2>;
+ qcom,mdss-pipe-vig-clk-ctrl-offsets = <0x3AC 0 0>;
+ qcom,mdss-pipe-rgb-clk-ctrl-offsets = <0x3AC 4 8>;
+ qcom,mdss-pipe-dma-clk-ctrl-offsets = <0x3AC 8 12>;
+
qcom,mdss-smp-data = <7 4096>;
+ qcom,mdss-no-lut-read;
+
qcom,mdss-ctl-off = <0x00000600 0x00000700>;
qcom,mdss-mixer-intf-off = <0x00003200>;
qcom,mdss-mixer-wb-off = <0x00003E00>;
@@ -66,7 +76,13 @@
<0x0D8 0x00000707>,
<0x124 0x00000003>;
qcom,mdp-settings = <0x02E0 0x000000A5>,
- <0x02E4 0x00000055>;
+ <0x02E4 0x00000055>,
+ <0x03AC 0xC0000CCC>,
+ <0x03B4 0xC0000000>,
+ <0x03BC 0x00C00C00>,
+ <0x04A8 0x0CCCC000>,
+ <0x04B0 0xC0000000>,
+ <0x04B8 0xC0000000>;
/* buffer parameters to calculate prefill bandwidth */
qcom,mdss-prefill-outstanding-buffer-bytes = <1024>;
@@ -95,9 +111,10 @@
compatible = "qcom,mdss-dsi-ctrl";
label = "MDSS DSI CTRL->0";
cell-index = <0>;
- reg = <0xfd922800 0x600>,
+ reg = <0xfd922800 0x1f8>,
+ <0xfd922b00 0x2b0>,
<0xfd828000 0x108>;
- reg-names = "dsi_phys", "mmss_misc_phys";
+ reg-names = "dsi_ctrl", "dsi_phy", "mmss_misc_phys";
qcom,mdss-fb-map = <&mdss_fb0>;
qcom,mdss-mdp = <&mdss_mdp>;
vdd-supply = <&pm8226_l15>;
diff --git a/arch/arm/boot/dts/msm8226-v2.dtsi b/arch/arm/boot/dts/msm8226-v2.dtsi
index 14fe237..787c9fb 100644
--- a/arch/arm/boot/dts/msm8226-v2.dtsi
+++ b/arch/arm/boot/dts/msm8226-v2.dtsi
@@ -80,11 +80,18 @@
< 0 0>,
< 384000000 2>,
< 787200000 4>,
+ < 998400000 5>,
+ <1094400000 6>,
<1190400000 7>;
qcom,speed2-bin-v2 =
< 0 0>,
< 384000000 2>,
< 787200000 4>,
+ < 998400000 5>,
+ <1094400000 6>,
+ <1190400000 7>,
+ <1305600000 8>,
+ <1344000000 9>,
<1401600000 10>;
qcom,speed1-bin-v2 =
< 0 0>,
diff --git a/arch/arm/boot/dts/msm8226.dtsi b/arch/arm/boot/dts/msm8226.dtsi
index 4117d9d..8d2414c 100644
--- a/arch/arm/boot/dts/msm8226.dtsi
+++ b/arch/arm/boot/dts/msm8226.dtsi
@@ -300,6 +300,7 @@
reg = <0xfe8050c8 0xc8>;
qcom,android-usb-swfi-latency = <1>;
qcom,streaming-func = "rndis";
+ qcom,android-usb-uicc-nluns = <1>;
};
smsc_hub: hsic_hub {
@@ -775,6 +776,7 @@
qcom,bus-width = <8>;
+ qcom,cpu-dma-latency-us = <701>;
qcom,msm-bus,name = "sdcc1";
qcom,msm-bus,num-cases = <8>;
qcom,msm-bus,num-paths = <1>;
@@ -802,6 +804,7 @@
qcom,bus-width = <8>;
+ qcom,cpu-dma-latency-us = <701>;
qcom,msm-bus,name = "sdhc1";
qcom,msm-bus,num-cases = <8>;
qcom,msm-bus,num-paths = <1>;
@@ -832,6 +835,7 @@
qcom,bus-width = <4>;
+ qcom,cpu-dma-latency-us = <701>;
qcom,msm-bus,name = "sdcc2";
qcom,msm-bus,num-cases = <8>;
qcom,msm-bus,num-paths = <1>;
@@ -859,6 +863,7 @@
qcom,bus-width = <4>;
+ qcom,cpu-dma-latency-us = <701>;
qcom,msm-bus,name = "sdhc2";
qcom,msm-bus,num-cases = <8>;
qcom,msm-bus,num-paths = <1>;
@@ -885,6 +890,7 @@
reg-names = "core_mem", "dml_mem", "bam_mem";
qcom,bus-width = <4>;
+ qcom,cpu-dma-latency-us = <701>;
gpios = <&msmgpio 44 0>, /* CLK */
<&msmgpio 43 0>, /* CMD */
<&msmgpio 42 0>, /* DATA0 */
@@ -928,6 +934,7 @@
reg-names = "hc_mem", "core_mem";
qcom,bus-width = <4>;
+ qcom,cpu-dma-latency-us = <701>;
gpios = <&msmgpio 44 0>, /* CLK */
<&msmgpio 43 0>, /* CMD */
<&msmgpio 42 0>, /* DATA0 */
@@ -1054,7 +1061,7 @@
< 300000 1525 >,
< 384000 1525 >,
< 600000 1525 >,
- < 787200 3051 >,
+ < 787200 1525 >,
< 998400 4066 >,
< 1094400 4066 >,
< 1190400 4066 >,
@@ -1329,6 +1336,7 @@
qcom,msm-bus,num-cases = <4>;
qcom,msm-bus,active-only = <0>;
qcom,msm-bus,num-paths = <1>;
+ qcom,support-fde;
qcom,msm-bus,vectors-KBps =
<55 512 0 0>,
<55 512 0 0>,
@@ -1344,6 +1352,7 @@
interrupts = <0 207 0>;
qcom,bam-pipe-pair = <2>;
qcom,ce-hw-instance = <0>;
+ qcom,ce-device = <0>;
qcom,ce-hw-shared;
qcom,msm-bus,name = "qcrypto-noc";
qcom,msm-bus,num-cases = <2>;
@@ -1362,6 +1371,7 @@
interrupts = <0 207 0>;
qcom,bam-pipe-pair = <1>;
qcom,ce-hw-instance = <0>;
+ qcom,ce-device = <0>;
qcom,ce-hw-shared;
qcom,msm-bus,name = "qcedev-noc";
qcom,msm-bus,num-cases = <2>;
@@ -1377,6 +1387,12 @@
qcom,irq-is-percpu;
interrupts = <1 7 0xf00>;
};
+
+ bimc_sharedmem {
+ compatible = "qcom,sharedmem-uio";
+ reg = <0xfc380000 0x00100000>;
+ reg-names = "bimc";
+ };
};
&gdsc_venus {
diff --git a/arch/arm/boot/dts/msm8610-camera-sensor-cdp-mtp.dtsi b/arch/arm/boot/dts/msm8610-camera-sensor-cdp-mtp.dtsi
index 7f4197f..1d47a94 100644
--- a/arch/arm/boot/dts/msm8610-camera-sensor-cdp-mtp.dtsi
+++ b/arch/arm/boot/dts/msm8610-camera-sensor-cdp-mtp.dtsi
@@ -192,8 +192,8 @@
reg = <0x6d>;
qcom,slave-id = <0x20 0x0 0x9724>;
qcom,csiphy-sd-index = <1>;
- qcom,csid-sd-index = <0>;
- qcom,mount-angle = <90>;
+ qcom,csid-sd-index = <1>;
+ qcom,mount-angle = <270>;
qcom,sensor-name = "ov9724";
cam_vdig-supply = <&pm8110_l4>;
cam_vana-supply = <&pm8110_l19>;
@@ -204,7 +204,7 @@
qcom,cam-vreg-max-voltage = <1200000 0 2850000>;
qcom,cam-vreg-op-mode = <200000 0 80000>;
qcom,gpio-no-mux = <0>;
- gpios = <&msmgpio 14 0>,
+ gpios = <&msmgpio 13 0>,
<&msmgpio 15 0>,
<&msmgpio 8 0>;
qcom,gpio-reset = <1>;
@@ -218,7 +218,7 @@
qcom,gpio-set-tbl-flags = <0 2>;
qcom,gpio-set-tbl-delay = <1000 4000>;
qcom,csi-lane-assign = <0xe4>;
- qcom,csi-lane-mask = <0x1>;
+ qcom,csi-lane-mask = <0x3>;
qcom,sensor-position = <1>;
qcom,sensor-mode = <1>;
qcom,cci-master = <0>;
@@ -291,6 +291,9 @@
qcom,gpio-req-tbl-label = "CAMIF_MCLK",
"CAM_RESET1",
"CAM_STANDBY";
+ qcom,sensor-position = <0>;
+ qcom,sensor-mode = <0>;
+ qcom,cci-master = <0>;
status = "ok";
};
@@ -320,6 +323,9 @@
qcom,gpio-req-tbl-label = "CAMIF_MCLK",
"CAM_RESET",
"CAM_STANDBY";
+ qcom,sensor-position = <1>;
+ qcom,sensor-mode = <0>;
+ qcom,cci-master = <0>;
status = "ok";
};
diff --git a/arch/arm/boot/dts/msm8610-cdp.dtsi b/arch/arm/boot/dts/msm8610-cdp.dtsi
index 04eca14..9344e89 100644
--- a/arch/arm/boot/dts/msm8610-cdp.dtsi
+++ b/arch/arm/boot/dts/msm8610-cdp.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2014, 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,15 +39,15 @@
/* Object 6, Instance = 0 */
00 00 00 00 00 00
/* Object 38, Instance = 0 */
- 1D 03 00 1E 07 0D 00 00
+ 1D 05 00 1D 0B 0D 00 00
/* Object 7, Instance = 0 */
- 20 08 32
+ 20 0A 32
/* Object 8, Instance = 0 */
- 19 00 14 14 FF 00 FF 00 00 00
+ 19 00 14 14 FF 00 05 00 32 19
/* Object 9, Instance = 0 */
83 00 00 13 0B 00 20 32 01 03
- 00 32 05 30 0A 05 0A 00 70 03
- FC 01 04 2F F8 DC 00 00 40 00
+ 00 0A 05 30 0A 05 0A 00 70 03
+ FC 01 04 2F 08 24 00 00 40 00
00 0A 00 00 02
/* Object 18, Instance = 0 */
00 00
@@ -59,16 +59,16 @@
00 00 00 00 00 00 00 00 00 00
00
/* Object 42, Instance = 0 */
- 00 00 00 00 00 00 00 00
+ 03 19 1E 14 80 05 00 00
/* Object 46, Instance = 0 */
04 03 08 10 00 00 00 00 00
/* Object 47, Instance = 0 */
00 00 00 00 00 00 00 00 00 00
/* Object 48, Instance = 0 */
- 00 00 00 00 00 00 00 00 00 00
- 00 00 00 00 00 00 00 00 00 00
- 00 00 00 00 00 00 00 00 00 00
- 00 00 00 00 00 00 00 00 00 00
+ 1D C0 22 00 00 00 00 00 00 00
+ 20 19 00 06 06 00 00 64 04 40
+ 00 00 00 05 00 2A 00 00 00 19
+ 34 0C 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00
00 00 00 00
/* Object 55, Instance = 0 */
diff --git a/arch/arm/boot/dts/msm8610-mdss.dtsi b/arch/arm/boot/dts/msm8610-mdss.dtsi
index 929659e..70d53e9 100644
--- a/arch/arm/boot/dts/msm8610-mdss.dtsi
+++ b/arch/arm/boot/dts/msm8610-mdss.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -16,6 +16,7 @@
reg = <0xfd900000 0x100000>;
reg-names = "mdp_phys";
interrupts = <0 72 0>;
+ vdd-cx-supply = <&pm8110_s1_corner>;
mdss_fb0: qcom,mdss_fb_primary {
cell-index = <0>;
diff --git a/arch/arm/boot/dts/msm8610-mtp.dtsi b/arch/arm/boot/dts/msm8610-mtp.dtsi
index aad838e..d88aba6 100644
--- a/arch/arm/boot/dts/msm8610-mtp.dtsi
+++ b/arch/arm/boot/dts/msm8610-mtp.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-14, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2014, 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,15 +39,15 @@
/* Object 6, Instance = 0 */
00 00 00 00 00 00
/* Object 38, Instance = 0 */
- 1D 05 01 0D 01 0E 00 00
+ 1D 05 00 1D 0B 0D 00 00
/* Object 7, Instance = 0 */
- 20 08 32
+ 20 0A 32
/* Object 8, Instance = 0 */
- 19 00 14 14 FF 00 FF 00 00 00
+ 19 00 14 14 FF 00 05 00 32 19
/* Object 9, Instance = 0 */
83 00 00 13 0B 00 20 32 01 03
- 00 32 05 30 05 05 0A 00 70 03
- FC 01 04 2F F8 DC 00 00 40 00
+ 00 0A 05 30 0A 05 0A 00 70 03
+ FC 01 04 2F 08 24 00 00 40 00
00 0A 00 00 02
/* Object 18, Instance = 0 */
00 00
@@ -59,16 +59,16 @@
00 00 00 00 00 00 00 00 00 00
00
/* Object 42, Instance = 0 */
- 00 00 00 00 00 00 00 00
+ 03 19 1E 14 80 05 00 00
/* Object 46, Instance = 0 */
04 03 08 10 00 00 00 00 00
/* Object 47, Instance = 0 */
00 00 00 00 00 00 00 00 00 00
/* Object 48, Instance = 0 */
- 00 00 00 00 00 00 00 00 00 00
- 00 00 00 00 00 00 00 00 00 00
- 00 00 00 00 00 00 00 00 00 00
- 00 00 00 00 00 00 00 00 00 00
+ 1D C0 22 00 00 00 00 00 00 00
+ 20 19 00 06 06 00 00 64 04 40
+ 00 00 00 05 00 2A 00 00 00 19
+ 34 0C 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00
00 00 00 00
/* Object 55, Instance = 0 */
diff --git a/arch/arm/boot/dts/msm8610-qrd-skuaa.dtsi b/arch/arm/boot/dts/msm8610-qrd-skuaa.dtsi
index 53abb95..d731ce0 100644
--- a/arch/arm/boot/dts/msm8610-qrd-skuaa.dtsi
+++ b/arch/arm/boot/dts/msm8610-qrd-skuaa.dtsi
@@ -96,3 +96,14 @@
qcom,master-en = <1>;
};
};
+
+&sdhc_2 {
+ qcom,nonremovable;
+
+ interrupts = <0 1>;
+ interrupt-map = <0 &intc 0 125 0
+ 1 &intc 0 221 0>;
+ interrupt-names = "hc_irq", "pwr_irq";
+ /delete-property/ cd-gpios;
+};
+
diff --git a/arch/arm/boot/dts/msm8610-qrd-skuab.dtsi b/arch/arm/boot/dts/msm8610-qrd-skuab.dtsi
index 1554575..336553a 100644
--- a/arch/arm/boot/dts/msm8610-qrd-skuab.dtsi
+++ b/arch/arm/boot/dts/msm8610-qrd-skuab.dtsi
@@ -183,3 +183,14 @@
&dsi_otm8018b_fwvga_vid {
qcom,cont-splash-enabled;
};
+
+&sdhc_2 {
+ qcom,nonremovable;
+
+ interrupts = <0 1>;
+ interrupt-map = <0 &intc 0 125 0
+ 1 &intc 0 221 0>;
+ interrupt-names = "hc_irq", "pwr_irq";
+ /delete-property/ cd-gpios;
+};
+
diff --git a/arch/arm/boot/dts/msm8610-v2-mtp.dts b/arch/arm/boot/dts/msm8610-v2-mtp.dts
index 77f5276..debfc23 100644
--- a/arch/arm/boot/dts/msm8610-v2-mtp.dts
+++ b/arch/arm/boot/dts/msm8610-v2-mtp.dts
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2014, 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
@@ -22,4 +22,6 @@
qcom,board-id = <8 0>;
};
-
+&sdhc_2 {
+ qcom,pad-drv-on = <0x5 0x4 0x4>; /* 12mA, 10mA, 10mA */
+};
diff --git a/arch/arm/boot/dts/msm8610.dtsi b/arch/arm/boot/dts/msm8610.dtsi
index 43cd7c6..90dae06 100644
--- a/arch/arm/boot/dts/msm8610.dtsi
+++ b/arch/arm/boot/dts/msm8610.dtsi
@@ -256,6 +256,7 @@
reg = <0xfe8050c8 0xc8>;
qcom,android-usb-swfi-latency = <1>;
qcom,streaming-func = "rndis";
+ qcom,android-usb-uicc-nluns = <1>;
};
rmtfs_sharedmem {
@@ -305,6 +306,7 @@
qcom,bus-width = <8>;
qcom,nonremovable;
qcom,bus-speed-mode = "HS200_1p8v", "DDR_1p8v";
+ qcom,cpu-dma-latency-us = <701>;
status = "disabled";
};
@@ -338,6 +340,7 @@
qcom,xpc;
qcom,bus-speed-mode = "SDR12", "SDR25", "SDR50", "DDR50", "SDR104";
qcom,current-limit = <800>;
+ qcom,cpu-dma-latency-us = <701>;
status = "disabled";
};
@@ -351,6 +354,7 @@
interrupt-names = "hc_irq", "pwr_irq";
qcom,bus-width = <8>;
+ qcom,cpu-dma-latency-us = <701>;
status = "disabled";
};
@@ -363,6 +367,7 @@
interrupt-names = "hc_irq", "pwr_irq";
qcom,bus-width = <4>;
+ qcom,cpu-dma-latency-us = <701>;
status = "disabled";
};
@@ -588,6 +593,7 @@
qcom,i2c-src-freq = <19200000>;
qcom,sda-gpio = <&msmgpio 10 0>;
qcom,scl-gpio = <&msmgpio 11 0>;
+ qcom,clk-ctl-xfer;
qcom,master-id = <86>;
};
@@ -925,15 +931,16 @@
qcom,disk-encrypt-pipe-pair = <2>;
qcom,hlos-ce-hw-instance = <0>;
qcom,qsee-ce-hw-instance = <0>;
+ qcom,support-bus-scaling;
qcom,msm-bus,name = "qseecom-noc";
qcom,msm-bus,num-cases = <4>;
qcom,msm-bus,active-only = <0>;
qcom,msm-bus,num-paths = <1>;
qcom,msm-bus,vectors-KBps =
<55 512 0 0>,
- <55 512 3936000 393600>,
- <55 512 3936000 393600>,
- <55 512 3936000 393600>;
+ <55 512 0 0>,
+ <55 512 120000 1200000>,
+ <55 512 393600 3936000>;
};
qcom,msm-rng@f9bff000 {
@@ -1001,6 +1008,7 @@
interrupts = <0 207 0>;
qcom,bam-pipe-pair = <2>;
qcom,ce-hw-instance = <1>;
+ qcom,ce-device = <0>;
qcom,ce-hw-shared;
qcom,msm-bus,name = "qcrypto-noc";
qcom,msm-bus,num-cases = <2>;
@@ -1019,6 +1027,7 @@
interrupts = <0 207 0>;
qcom,bam-pipe-pair = <1>;
qcom,ce-hw-instance = <1>;
+ qcom,ce-device = <0>;
qcom,ce-hw-shared;
qcom,msm-bus,name = "qcedev-noc";
qcom,msm-bus,num-cases = <2>;
@@ -1034,6 +1043,12 @@
qcom,irq-is-percpu;
interrupts = <1 7 0xf00>;
};
+
+ bimc_sharedmem {
+ compatible = "qcom,sharedmem-uio";
+ reg = <0xfc380000 0x00100000>;
+ reg-names = "bimc";
+ };
};
&gdsc_vfe {
diff --git a/arch/arm/boot/dts/msm8612-qrd-camera-sensor.dtsi b/arch/arm/boot/dts/msm8612-qrd-camera-sensor.dtsi
index 4d50b36..551e007 100644
--- a/arch/arm/boot/dts/msm8612-qrd-camera-sensor.dtsi
+++ b/arch/arm/boot/dts/msm8612-qrd-camera-sensor.dtsi
@@ -121,6 +121,8 @@
qcom,gpio-req-tbl-label = "CAMIF_MCLK",
"CAM_RESET1",
"CAM_STANDBY";
+ qcom,sensor-position = <0>;
+ qcom,sensor-mode = <0>;
qcom,cci-master = <0>;
status = "ok";
};
@@ -149,6 +151,8 @@
qcom,gpio-req-tbl-label = "CAMIF_MCLK",
"CAM_RESET",
"CAM_STANDBY";
+ qcom,sensor-position = <1>;
+ qcom,sensor-mode = <0>;
qcom,cci-master = <0>;
status = "ok";
};
diff --git a/arch/arm/boot/dts/msm8926-camera-sensor-qrd.dtsi b/arch/arm/boot/dts/msm8926-camera-sensor-qrd.dtsi
index 8e053a9..6af862f 100644
--- a/arch/arm/boot/dts/msm8926-camera-sensor-qrd.dtsi
+++ b/arch/arm/boot/dts/msm8926-camera-sensor-qrd.dtsi
@@ -263,6 +263,8 @@
"CAM_RESET1",
"CAM_STANDBY",
"CAM_AF_PWDM";
+ qcom,sensor-position = <0>;
+ qcom,sensor-mode = <0>;
qcom,cci-master = <0>;
status = "ok";
};
@@ -296,6 +298,8 @@
"CAM_RESET",
"CAM_STANDBY",
"CAM_VDIG";
+ qcom,sensor-position = <1>;
+ qcom,sensor-mode = <0>;
qcom,cci-master = <0>;
status = "ok";
};
diff --git a/arch/arm/boot/dts/msm8926-qrd-skug.dtsi b/arch/arm/boot/dts/msm8926-qrd-skug.dtsi
index c93ea12..dabadda 100644
--- a/arch/arm/boot/dts/msm8926-qrd-skug.dtsi
+++ b/arch/arm/boot/dts/msm8926-qrd-skug.dtsi
@@ -98,3 +98,13 @@
&dsi_ssd2080m_720_vid {
qcom,cont-splash-enabled;
};
+
+&sdhc_2 {
+ qcom,nonremovable;
+
+ interrupts = <0 1>;
+ interrupt-map = <0 &intc 0 125 0
+ 1 &intc 0 221 0>;
+ interrupt-names = "hc_irq", "pwr_irq";
+ /delete-property/ cd-gpios;
+};
diff --git a/arch/arm/boot/dts/msm8926-v1-1080p-mtp.dts b/arch/arm/boot/dts/msm8926-v1-1080p-mtp.dts
index 0812c54..d94b40c9 100644
--- a/arch/arm/boot/dts/msm8926-v1-1080p-mtp.dts
+++ b/arch/arm/boot/dts/msm8926-v1-1080p-mtp.dts
@@ -21,3 +21,7 @@
compatible = "qcom,msm8926-mtp", "qcom,msm8926", "qcom,mtp";
qcom,board-id = <8 2>;
};
+
+&sdhc_2 {
+ qcom,pad-drv-on = <0x7 0x4 0x4>; /* 16mA, 10mA, 10mA */
+};
diff --git a/arch/arm/boot/dts/msm8926-v1-720p-mtp.dts b/arch/arm/boot/dts/msm8926-v1-720p-mtp.dts
index 7b49930..74fe06f 100644
--- a/arch/arm/boot/dts/msm8926-v1-720p-mtp.dts
+++ b/arch/arm/boot/dts/msm8926-v1-720p-mtp.dts
@@ -21,3 +21,7 @@
compatible = "qcom,msm8926-mtp", "qcom,msm8926", "qcom,mtp";
qcom,board-id = <8 0>;
};
+
+&sdhc_2 {
+ qcom,pad-drv-on = <0x7 0x4 0x4>; /* 16mA, 10mA, 10mA */
+};
diff --git a/arch/arm/boot/dts/msm8926-v2.dtsi b/arch/arm/boot/dts/msm8926-v2.dtsi
index 1322573..c443ba6 100644
--- a/arch/arm/boot/dts/msm8926-v2.dtsi
+++ b/arch/arm/boot/dts/msm8926-v2.dtsi
@@ -66,6 +66,22 @@
};
};
+&soc {
+ mem_acc_vreg_corner: regulator@fd4aa044 {
+ compatible = "qcom,mem-acc-regulator";
+ reg = <0xfd4aa048 0x4>, <0xfd4aa044 0x4>, <0xfd4af000 0x4>;
+ reg-names = "acc-en", "acc-sel-l1", "acc-sel-l2";
+ regulator-name = "mem_acc_corner";
+ regulator-min-microvolt = <1>;
+ regulator-max-microvolt = <3>;
+
+ qcom,acc-en-bit-pos = <0>;
+ qcom,acc-sel-l1-bit-pos = <0>;
+ qcom,acc-sel-l2-bit-pos = <0>;
+ qcom,corner-acc-map = <0 1 3>;
+ };
+};
+
&pm8226_l3 {
regulator-max-microvolt = <1287500>;
};
@@ -90,4 +106,5 @@
qcom,vdd-mx-vmax = <1287500>;
qcom,vdd-mx-vmin-method = <4>;
qcom,vdd-mx-corner-map = <1050000 1150000 1280000>;
+ mem-acc-supply = <&mem_acc_vreg_corner>;
};
diff --git a/arch/arm/boot/dts/msm8926.dtsi b/arch/arm/boot/dts/msm8926.dtsi
index e866286..1919022 100644
--- a/arch/arm/boot/dts/msm8926.dtsi
+++ b/arch/arm/boot/dts/msm8926.dtsi
@@ -22,6 +22,11 @@
/ {
model = "Qualcomm MSM 8926";
compatible = "qcom,msm8926";
+
+};
+
+&qsecom_mem {
+ linux,memory-limit = <0x0>;
};
&soc {
@@ -48,11 +53,18 @@
< 0 0>,
< 384000000 2>,
< 787200000 4>,
+ < 998400000 5>,
+ <1094400000 6>,
<1190400000 7>;
qcom,speed2-bin-v1 =
< 0 0>,
< 384000000 2>,
< 787200000 4>,
+ < 998400000 5>,
+ <1094400000 6>,
+ <1190400000 7>,
+ <1305600000 8>,
+ <1344000000 9>,
<1401600000 10>;
qcom,speed1-bin-v1 =
< 0 0>,
diff --git a/arch/arm/boot/dts/msm8974-bus.dtsi b/arch/arm/boot/dts/msm8974-bus.dtsi
index b33b2b5..ba8b27e 100644
--- a/arch/arm/boot/dts/msm8974-bus.dtsi
+++ b/arch/arm/boot/dts/msm8974-bus.dtsi
@@ -1175,9 +1175,9 @@
qcom,prio-rd = <0>;
qcom,prio-wr = <0>;
qcom,mode-thresh = "Fixed";
- qcom,thresh = <2000000>;
+ qcom,thresh = <2000000 2456000>;
qcom,dual-conf;
- qcom,bimc,bw = <300000>;
+ qcom,bimc,bw = <300000 450000>;
qcom,bimc,gp = <5000>;
qcom,bimc,thmp = <50>;
};
@@ -1195,9 +1195,9 @@
qcom,prio-rd = <0>;
qcom,prio-wr = <0>;
qcom,mode-thresh = "Fixed";
- qcom,thresh = <2000000>;
+ qcom,thresh = <2000000 2456000>;
qcom,dual-conf;
- qcom,bimc,bw = <300000>;
+ qcom,bimc,bw = <300000 450000>;
qcom,bimc,gp = <5000>;
qcom,bimc,thmp = <50>;
};
diff --git a/arch/arm/boot/dts/msm8974-camera-sensor-cdp.dtsi b/arch/arm/boot/dts/msm8974-camera-sensor-cdp.dtsi
index bdc3bef..2e58167 100644
--- a/arch/arm/boot/dts/msm8974-camera-sensor-cdp.dtsi
+++ b/arch/arm/boot/dts/msm8974-camera-sensor-cdp.dtsi
@@ -27,130 +27,13 @@
qcom,cci-master = <0>;
};
- qcom,camera@6e {
- compatible = "qcom,s5k3l1yx";
- reg = <0x6e>;
- qcom,slave-id = <0x6e 0x0 0x3121>;
- qcom,csiphy-sd-index = <0>;
- qcom,csid-sd-index = <0>;
- qcom,actuator-src = <&actuator0>;
- qcom,mount-angle = <90>;
- qcom,sensor-name = "s5k3l1yx";
- cam_vdig-supply = <&pm8941_l3>;
- cam_vana-supply = <&pm8941_l17>;
- cam_vio-supply = <&pm8941_lvs3>;
- cam_vaf-supply = <&pm8941_l23>;
- qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana",
- "cam_vaf";
- qcom,cam-vreg-type = <0 1 0 0>;
- qcom,cam-vreg-min-voltage = <1225000 0 2850000 3000000>;
- qcom,cam-vreg-max-voltage = <1225000 0 2850000 3000000>;
- qcom,cam-vreg-op-mode = <105000 0 80000 100000>;
- qcom,gpio-no-mux = <0>;
- gpios = <&msmgpio 15 0>,
- <&msmgpio 90 0>,
- <&msmgpio 89 0>;
- qcom,gpio-reset = <1>;
- qcom,gpio-standby = <2>;
- qcom,gpio-req-tbl-num = <0 1 2>;
- qcom,gpio-req-tbl-flags = <1 0 0>;
- qcom,gpio-req-tbl-label = "CAMIF_MCLK",
- "CAM_RESET1",
- "CAM_STANDBY";
- qcom,gpio-set-tbl-num = <1 1>;
- qcom,gpio-set-tbl-flags = <0 2>;
- qcom,gpio-set-tbl-delay = <1000 30000>;
- qcom,csi-lane-assign = <0x4320>;
- qcom,csi-lane-mask = <0x1F>;
- qcom,sensor-position = <0>;
- qcom,sensor-mode = <1>;
- qcom,cci-master = <0>;
- status = "ok";
- };
-
- qcom,camera@20 {
- compatible = "qcom,imx135";
- reg = <0x20>;
- qcom,slave-id = <0x20 0x0016 0x0135>;
- qcom,csiphy-sd-index = <0>;
- qcom,csid-sd-index = <0>;
- qcom,mount-angle = <90>;
- qcom,sensor-name = "imx135";
- qcom,actuator-src = <&actuator1>;
- cam_vdig-supply = <&pm8941_l3>;
- cam_vana-supply = <&pm8941_l17>;
- cam_vio-supply = <&pm8941_lvs3>;
- cam_vaf-supply = <&pm8941_l23>;
- qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana",
- "cam_vaf";
- qcom,cam-vreg-type = <0 1 0 0>;
- qcom,cam-vreg-min-voltage = <1225000 0 2850000 3000000>;
- qcom,cam-vreg-max-voltage = <1225000 0 2850000 3000000>;
- qcom,cam-vreg-op-mode = <105000 0 80000 100000>;
- qcom,gpio-no-mux = <0>;
- gpios = <&msmgpio 15 0>,
- <&msmgpio 90 0>,
- <&msmgpio 89 0>;
- qcom,gpio-reset = <1>;
- qcom,gpio-standby = <2>;
- qcom,gpio-req-tbl-num = <0 1 2>;
- qcom,gpio-req-tbl-flags = <1 0 0>;
- qcom,gpio-req-tbl-label = "CAMIF_MCLK",
- "CAM_RESET1",
- "CAM_STANDBY";
- qcom,gpio-set-tbl-num = <1 1>;
- qcom,gpio-set-tbl-flags = <0 2>;
- qcom,gpio-set-tbl-delay = <1000 30000>;
- qcom,csi-lane-assign = <0x4320>;
- qcom,csi-lane-mask = <0x1F>;
- qcom,sensor-position = <0>;
- qcom,sensor-mode = <0>;
- qcom,sensor-type = <0>;
- qcom,cci-master = <0>;
- status = "ok";
- };
-
- qcom,camera@6c {
- compatible = "qcom,ov2720";
- reg = <0x6c>;
- qcom,slave-id = <0x6c 0x300A 0x2720>;
- qcom,csiphy-sd-index = <2>;
- qcom,csid-sd-index = <2>;
- qcom,mount-angle = <90>;
- qcom,sensor-name = "ov2720";
- cam_vdig-supply = <&pm8941_l3>;
- cam_vana-supply = <&pm8941_l17>;
- cam_vio-supply = <&pm8941_lvs3>;
- qcom,cam-vreg-name = "cam_vdig", "cam_vana", "cam_vio";
- qcom,cam-vreg-type = <0 0 1>;
- qcom,cam-vreg-min-voltage = <1225000 2850000 0>;
- qcom,cam-vreg-max-voltage = <1225000 2850000 0>;
- qcom,cam-vreg-op-mode = <105000 80000 0>;
- qcom,gpio-no-mux = <0>;
- gpios = <&msmgpio 17 0>,
- <&msmgpio 18 0>;
- qcom,gpio-reset = <1>;
- qcom,gpio-req-tbl-num = <0 1>;
- qcom,gpio-req-tbl-flags = <1 0>;
- qcom,gpio-req-tbl-label = "CAMIF_MCLK",
- "CAM_RESET1";
- qcom,gpio-set-tbl-num = <1 1>;
- qcom,gpio-set-tbl-flags = <0 2>;
- qcom,gpio-set-tbl-delay = <1000 4000>;
- qcom,csi-lane-assign = <0x4320>;
- qcom,csi-lane-mask = <0x7>;
- qcom,sensor-position = <1>;
- qcom,sensor-mode = <1>;
- qcom,cci-master = <0>;
- status = "ok";
- };
qcom,camera@90 {
compatible = "qcom,mt9m114";
reg = <0x90>;
qcom,slave-id = <0x90 0x0 0x2481>;
qcom,csiphy-sd-index = <1>;
- qcom,csid-sd-index = <0>;
+ qcom,csid-sd-index = <1>;
qcom,mount-angle = <0>;
qcom,sensor-name = "mt9m114";
cam_vdig-supply = <&pm8941_l3>;
@@ -178,15 +61,15 @@
qcom,sensor-mode = <1>;
qcom,cci-master = <0>;
};
+
qcom,camera@0 {
cell-index = <0>;
compatible = "qcom,camera";
reg = <0x0>;
qcom,csiphy-sd-index = <0>;
qcom,csid-sd-index = <0>;
+ qcom,mount-angle = <90>;
qcom,actuator-src = <&actuator0>;
- qcom,vdd-cx-supply = <&pm8841_s2>;
- qcom,vdd-cx-name = "qcom,vdd-cx";
cam_vdig-supply = <&pm8941_l3>;
cam_vana-supply = <&pm8941_l17>;
cam_vio-supply = <&pm8941_lvs3>;
@@ -205,9 +88,11 @@
qcom,gpio-standby = <2>;
qcom,gpio-req-tbl-num = <0 1 2>;
qcom,gpio-req-tbl-flags = <1 0 0>;
- qcom,gpio-req-tbl-label = "CAMIF_MCLK",
- "CAM_RESET1",
- "CAM_STANDBY";
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK0",
+ "CAM_RESET0",
+ "CAM_STANDBY0";
+ qcom,sensor-position = <0>;
+ qcom,sensor-mode = <0>;
qcom,cci-master = <0>;
status = "ok";
};
@@ -218,8 +103,7 @@
reg = <0x1>;
qcom,csiphy-sd-index = <1>;
qcom,csid-sd-index = <0>;
- qcom,vdd-cx-supply = <&pm8841_s2>;
- qcom,vdd-cx-name = "qcom,vdd-cx";
+ qcom,mount-angle = <90>;
cam_vdig-supply = <&pm8941_l3>;
cam_vana-supply = <&pm8941_l17>;
cam_vio-supply = <&pm8941_lvs3>;
@@ -237,9 +121,11 @@
qcom,gpio-standby = <2>;
qcom,gpio-req-tbl-num = <0 1 2>;
qcom,gpio-req-tbl-flags = <1 0 0>;
- qcom,gpio-req-tbl-label = "CAMIF_MCLK",
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK1",
"CAM_RESET1",
- "CAM_STANDBY";
+ "CAM_STANDBY1";
+ qcom,sensor-position = <0>;
+ qcom,sensor-mode = <0>;
qcom,cci-master = <0>;
status = "ok";
};
@@ -247,27 +133,31 @@
qcom,camera@2 {
cell-index = <2>;
compatible = "qcom,camera";
- reg = <0x2>;
+ reg = <0x02>;
qcom,csiphy-sd-index = <2>;
qcom,csid-sd-index = <2>;
- qcom,vdd-cx-supply = <&pm8841_s2>;
- qcom,vdd-cx-name = "qcom,vdd-cx";
+ qcom,mount-angle = <90>;
cam_vdig-supply = <&pm8941_l3>;
cam_vana-supply = <&pm8941_l17>;
cam_vio-supply = <&pm8941_lvs3>;
- qcom,cam-vreg-name = "cam_vdig", "cam_vana", "cam_vio";
- qcom,cam-vreg-type = <0 0 1>;
- qcom,cam-vreg-min-voltage = <1225000 2850000 0>;
- qcom,cam-vreg-max-voltage = <1225000 2850000 0>;
- qcom,cam-vreg-op-mode = <105000 80000 0>;
+ qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana";
+ qcom,cam-vreg-type = <0 1 0>;
+ qcom,cam-vreg-min-voltage = <1225000 0 2850000>;
+ qcom,cam-vreg-max-voltage = <1225000 0 2850000>;
+ qcom,cam-vreg-op-mode = <105000 0 80000>;
qcom,gpio-no-mux = <0>;
gpios = <&msmgpio 17 0>,
- <&msmgpio 18 0>;
+ <&msmgpio 18 0>,
+ <&msmgpio 28 0>;
qcom,gpio-reset = <1>;
- qcom,gpio-req-tbl-num = <0 1>;
- qcom,gpio-req-tbl-flags = <1 0>;
- qcom,gpio-req-tbl-label = "CAMIF_MCLK",
- "CAM_RESET1";
+ qcom,gpio-standby = <2>;
+ qcom,gpio-req-tbl-num = <0 1 2>;
+ qcom,gpio-req-tbl-flags = <1 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK2",
+ "CAM_RESET2",
+ "CAM_STANDBY2";
+ qcom,sensor-position = <1>;
+ qcom,sensor-mode = <0>;
qcom,cci-master = <0>;
status = "ok";
};
diff --git a/arch/arm/boot/dts/msm8974-camera-sensor-dragonboard.dtsi b/arch/arm/boot/dts/msm8974-camera-sensor-dragonboard.dtsi
index 43b0d75..7ca986d 100644
--- a/arch/arm/boot/dts/msm8974-camera-sensor-dragonboard.dtsi
+++ b/arch/arm/boot/dts/msm8974-camera-sensor-dragonboard.dtsi
@@ -247,6 +247,8 @@
qcom,gpio-req-tbl-label = "CAMIF_MCLK",
"CAM_RESET1",
"CAM_STANDBY";
+ qcom,sensor-position = <1>;
+ qcom,sensor-mode = <0>;
qcom,cci-master = <0>;
status = "ok";
};
@@ -257,6 +259,7 @@
reg = <0x2>;
qcom,csiphy-sd-index = <2>;
qcom,csid-sd-index = <2>;
+ qcom,mount-angle = <180>;
qcom,vdd-cx-supply = <&pm8841_s2>;
qcom,vdd-cx-name = "qcom,vdd-cx";
cam_vdig-supply = <&pm8941_l3>;
@@ -278,6 +281,8 @@
qcom,gpio-req-tbl-flags = <1 0 0>;
qcom,gpio-req-tbl-label = "CAMIF_MCLK",
"CAM_RESET1";
+ qcom,sensor-position = <1>;
+ qcom,sensor-mode = <0>;
qcom,cci-master = <0>;
status = "ok";
};
diff --git a/arch/arm/boot/dts/msm8974-camera-sensor-fluid.dtsi b/arch/arm/boot/dts/msm8974-camera-sensor-fluid.dtsi
index 529d3ba..ecf5098 100644
--- a/arch/arm/boot/dts/msm8974-camera-sensor-fluid.dtsi
+++ b/arch/arm/boot/dts/msm8974-camera-sensor-fluid.dtsi
@@ -221,6 +221,8 @@
qcom,gpio-req-tbl-label = "CAMIF_MCLK",
"CAM_RESET1",
"CAM_STANDBY";
+ qcom,sensor-position = <0>;
+ qcom,sensor-mode = <0>;
qcom,cci-master = <0>;
status = "ok";
};
@@ -251,6 +253,8 @@
qcom,gpio-req-tbl-flags = <1 0>;
qcom,gpio-req-tbl-label = "CAMIF_MCLK",
"CAM_RESET1";
+ qcom,sensor-position = <1>;
+ qcom,sensor-mode = <0>;
qcom,cci-master = <1>;
status = "ok";
};
@@ -261,6 +265,7 @@
reg = <0x2>;
qcom,csiphy-sd-index = <2>;
qcom,csid-sd-index = <2>;
+ qcom,mount-angle = <90>;
qcom,vdd-cx-supply = <&pm8841_s2>;
qcom,vdd-cx-name = "qcom,vdd-cx";
cam_vdig-supply = <&pm8941_l3>;
diff --git a/arch/arm/boot/dts/msm8974-camera-sensor-liquid.dtsi b/arch/arm/boot/dts/msm8974-camera-sensor-liquid.dtsi
index 854e8f7..a22355a 100644
--- a/arch/arm/boot/dts/msm8974-camera-sensor-liquid.dtsi
+++ b/arch/arm/boot/dts/msm8974-camera-sensor-liquid.dtsi
@@ -28,124 +28,6 @@
qcom,cci-master = <0>;
};
- qcom,camera@6e {
- compatible = "qcom,s5k3l1yx";
- reg = <0x6e>;
- qcom,slave-id = <0x6e 0x0 0x3121>;
- qcom,csiphy-sd-index = <0>;
- qcom,csid-sd-index = <0>;
- qcom,mount-angle = <0>;
- qcom,actuator-src = <&actuator0>;
- qcom,sensor-name = "s5k3l1yx";
- cam_vdig-supply = <&pm8941_l3>;
- cam_vana-supply = <&pm8941_l17>;
- cam_vio-supply = <&pm8941_lvs2>;
- cam_vaf-supply = <&pm8941_l23>;
- qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana",
- "cam_vaf";
- qcom,cam-vreg-type = <0 1 0 0>;
- qcom,cam-vreg-min-voltage = <1225000 0 2850000 3000000>;
- qcom,cam-vreg-max-voltage = <1225000 0 2850000 3000000>;
- qcom,cam-vreg-op-mode = <105000 0 80000 100000>;
- qcom,gpio-no-mux = <0>;
- gpios = <&msmgpio 15 0>,
- <&msmgpio 90 0>,
- <&msmgpio 89 0>;
- qcom,gpio-reset = <1>;
- qcom,gpio-standby = <2>;
- qcom,gpio-req-tbl-num = <0 1 2>;
- qcom,gpio-req-tbl-flags = <1 0 0>;
- qcom,gpio-req-tbl-label = "CAMIF_MCLK",
- "CAM_RESET1",
- "CAM_STANDBY";
- qcom,gpio-set-tbl-num = <1 1>;
- qcom,gpio-set-tbl-flags = <0 2>;
- qcom,gpio-set-tbl-delay = <1000 30000>;
- qcom,csi-lane-assign = <0x4320>;
- qcom,csi-lane-mask = <0x1F>;
- qcom,sensor-position = <0>;
- qcom,sensor-mode = <1>;
- qcom,cci-master = <0>;
- status = "ok";
- };
-
- qcom,camera@20 {
- compatible = "qcom,imx135";
- reg = <0x20>;
- qcom,slave-id = <0x20 0x0016 0x0135>;
- qcom,csiphy-sd-index = <0>;
- qcom,csid-sd-index = <0>;
- qcom,mount-angle = <0>;
- qcom,sensor-name = "imx135";
- qcom,actuator-src = <&actuator1>;
- cam_vdig-supply = <&pm8941_l3>;
- cam_vana-supply = <&pm8941_l17>;
- cam_vio-supply = <&pm8941_lvs2>;
- cam_vaf-supply = <&pm8941_l23>;
- qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana",
- "cam_vaf";
- qcom,cam-vreg-type = <0 1 0 0>;
- qcom,cam-vreg-min-voltage = <1225000 0 2850000 3000000>;
- qcom,cam-vreg-max-voltage = <1225000 0 2850000 3000000>;
- qcom,cam-vreg-op-mode = <105000 0 80000 100000>;
- qcom,gpio-no-mux = <0>;
- gpios = <&msmgpio 15 0>,
- <&msmgpio 90 0>,
- <&msmgpio 89 0>;
- qcom,gpio-reset = <1>;
- qcom,gpio-standby = <2>;
- qcom,gpio-req-tbl-num = <0 1 2>;
- qcom,gpio-req-tbl-flags = <1 0 0>;
- qcom,gpio-req-tbl-label = "CAMIF_MCLK",
- "CAM_RESET1",
- "CAM_STANDBY";
- qcom,gpio-set-tbl-num = <1 1>;
- qcom,gpio-set-tbl-flags = <0 2>;
- qcom,gpio-set-tbl-delay = <1000 30000>;
- qcom,csi-lane-assign = <0x4320>;
- qcom,csi-lane-mask = <0x1F>;
- qcom,sensor-position = <0>;
- qcom,sensor-mode = <0>;
- qcom,sensor-type = <0>;
- qcom,cci-master = <0>;
- status = "ok";
- };
-
- qcom,camera@6c {
- compatible = "qcom,ov2720";
- reg = <0x6c>;
- qcom,slave-id = <0x6c 0x300A 0x2720>;
- qcom,csiphy-sd-index = <2>;
- qcom,csid-sd-index = <0>;
- qcom,mount-angle = <180>;
- qcom,sensor-name = "ov2720";
- cam_vdig-supply = <&pm8941_l3>;
- cam_vana-supply = <&pm8941_l17>;
- cam_vio-supply = <&pm8941_lvs2>;
- qcom,cam-vreg-name = "cam_vdig", "cam_vana", "cam_vio";
- qcom,cam-vreg-type = <0 0 1>;
- qcom,cam-vreg-min-voltage = <1225000 2850000 0>;
- qcom,cam-vreg-max-voltage = <1225000 2850000 0>;
- qcom,cam-vreg-op-mode = <105000 80000 0>;
- qcom,gpio-no-mux = <0>;
- gpios = <&msmgpio 17 0>,
- <&msmgpio 18 0>;
- qcom,gpio-reset = <1>;
- qcom,gpio-req-tbl-num = <0 1>;
- qcom,gpio-req-tbl-flags = <1 0>;
- qcom,gpio-req-tbl-label = "CAMIF_MCLK",
- "CAM_RESET1";
- qcom,gpio-set-tbl-num = <1 1>;
- qcom,gpio-set-tbl-flags = <0 2>;
- qcom,gpio-set-tbl-delay = <1000 4000>;
- qcom,csi-lane-assign = <0x4320>;
- qcom,csi-lane-mask = <0x7>;
- qcom,sensor-position = <1>;
- qcom,sensor-mode = <1>;
- qcom,cci-master = <0>;
- status = "ok";
- };
-
qcom,camera@90 {
compatible = "qcom,mt9m114";
reg = <0x90>;
@@ -179,16 +61,15 @@
qcom,sensor-mode = <1>;
qcom,cci-master = <0>;
};
+
qcom,camera@0 {
cell-index = <0>;
compatible = "qcom,camera";
reg = <0x0>;
qcom,csiphy-sd-index = <0>;
qcom,csid-sd-index = <0>;
- qcom,mount-angle = <0>;
+ qcom,mount-angle = <90>;
qcom,actuator-src = <&actuator0>;
- qcom,vdd-cx-supply = <&pm8841_s2>;
- qcom,vdd-cx-name = "qcom,vdd-cx";
cam_vdig-supply = <&pm8941_l3>;
cam_vana-supply = <&pm8941_l17>;
cam_vio-supply = <&pm8941_lvs2>;
@@ -199,6 +80,7 @@
qcom,cam-vreg-min-voltage = <1225000 0 2850000 3000000>;
qcom,cam-vreg-max-voltage = <1225000 0 2850000 3000000>;
qcom,cam-vreg-op-mode = <105000 0 80000 100000>;
+ qcom,gpio-no-mux = <0>;
gpios = <&msmgpio 15 0>,
<&msmgpio 90 0>,
<&msmgpio 89 0>;
@@ -206,9 +88,11 @@
qcom,gpio-standby = <2>;
qcom,gpio-req-tbl-num = <0 1 2>;
qcom,gpio-req-tbl-flags = <1 0 0>;
- qcom,gpio-req-tbl-label = "CAMIF_MCLK",
- "CAM_RESET1",
- "CAM_STANDBY";
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK0",
+ "CAM_RESET0",
+ "CAM_STANDBY0";
+ qcom,sensor-position = <0>;
+ qcom,sensor-mode = <0>;
qcom,cci-master = <0>;
status = "ok";
};
@@ -218,10 +102,8 @@
compatible = "qcom,camera";
reg = <0x1>;
qcom,csiphy-sd-index = <1>;
- qcom,csid-sd-index = <1>;
- qcom,mount-angle = <180>;
- qcom,vdd-cx-supply = <&pm8841_s2>;
- qcom,vdd-cx-name = "qcom,vdd-cx";
+ qcom,csid-sd-index = <0>;
+ qcom,mount-angle = <90>;
cam_vdig-supply = <&pm8941_l3>;
cam_vana-supply = <&pm8941_l17>;
cam_vio-supply = <&pm8941_lvs2>;
@@ -230,14 +112,18 @@
qcom,cam-vreg-min-voltage = <1225000 2850000 0>;
qcom,cam-vreg-max-voltage = <1225000 2850000 0>;
qcom,cam-vreg-op-mode = <105000 80000 0>;
- qcom,gpio-no-mux = <0>;
- gpios = <&msmgpio 17 0>,
- <&msmgpio 18 0>;
+ gpios = <&msmgpio 16 0>,
+ <&msmgpio 92 0>,
+ <&msmgpio 91 0>;
qcom,gpio-reset = <1>;
- qcom,gpio-req-tbl-num = <0 1>;
- qcom,gpio-req-tbl-flags = <1 0>;
- qcom,gpio-req-tbl-label = "CAMIF_MCLK",
- "CAM_RESET1";
+ qcom,gpio-standby = <2>;
+ qcom,gpio-req-tbl-num = <0 1 2>;
+ qcom,gpio-req-tbl-flags = <1 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK1",
+ "CAM_RESET1",
+ "CAM_STANDBY1";
+ qcom,sensor-position = <0>;
+ qcom,sensor-mode = <0>;
qcom,cci-master = <0>;
status = "ok";
};
@@ -245,11 +131,10 @@
qcom,camera@2 {
cell-index = <2>;
compatible = "qcom,camera";
- reg = <0x2>;
+ reg = <0x02>;
qcom,csiphy-sd-index = <2>;
qcom,csid-sd-index = <2>;
- qcom,vdd-cx-supply = <&pm8841_s2>;
- qcom,vdd-cx-name = "qcom,vdd-cx";
+ qcom,mount-angle = <90>;
cam_vdig-supply = <&pm8941_l3>;
cam_vana-supply = <&pm8941_l17>;
cam_vio-supply = <&pm8941_lvs3>;
@@ -260,6 +145,7 @@
qcom,cam-vreg-min-voltage = <1225000 0 2850000 3000000>;
qcom,cam-vreg-max-voltage = <1225000 0 2850000 3000000>;
qcom,cam-vreg-op-mode = <105000 0 80000 100000>;
+ qcom,gpio-no-mux = <0>;
gpios = <&msmgpio 17 0>,
<&msmgpio 18 0>,
<&msmgpio 28 0>;
@@ -267,9 +153,11 @@
qcom,gpio-standby = <2>;
qcom,gpio-req-tbl-num = <0 1 2>;
qcom,gpio-req-tbl-flags = <1 0 0>;
- qcom,gpio-req-tbl-label = "CAMIF_MCLK",
- "CAM_RESET1",
- "CAM_STANDBY";
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK2",
+ "CAM_RESET2",
+ "CAM_STANDBY2";
+ qcom,sensor-position = <1>;
+ qcom,sensor-mode = <0>;
qcom,cci-master = <0>;
status = "ok";
};
diff --git a/arch/arm/boot/dts/msm8974-camera-sensor-mtp.dtsi b/arch/arm/boot/dts/msm8974-camera-sensor-mtp.dtsi
index 59e1a7c..c3c0d47 100644
--- a/arch/arm/boot/dts/msm8974-camera-sensor-mtp.dtsi
+++ b/arch/arm/boot/dts/msm8974-camera-sensor-mtp.dtsi
@@ -37,127 +37,6 @@
qcom,cci-master = <0>;
};
- qcom,camera@6e {
- compatible = "qcom,s5k3l1yx";
- reg = <0x6e>;
- qcom,slave-id = <0x6e 0x0 0x3121>;
- qcom,csiphy-sd-index = <0>;
- qcom,csid-sd-index = <0>;
- qcom,actuator-src = <&actuator0>;
- qcom,led-flash-src = <&led_flash0>;
- qcom,mount-angle = <90>;
- qcom,sensor-name = "s5k3l1yx";
- cam_vdig-supply = <&pm8941_l3>;
- cam_vana-supply = <&pm8941_l17>;
- cam_vio-supply = <&pm8941_lvs3>;
- cam_vaf-supply = <&pm8941_l23>;
- qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana",
- "cam_vaf";
- qcom,cam-vreg-type = <0 1 0 0>;
- qcom,cam-vreg-min-voltage = <1225000 0 2850000 3000000>;
- qcom,cam-vreg-max-voltage = <1225000 0 2850000 3000000>;
- qcom,cam-vreg-op-mode = <105000 0 80000 100000>;
- qcom,gpio-no-mux = <0>;
- gpios = <&msmgpio 15 0>,
- <&msmgpio 90 0>,
- <&msmgpio 89 0>;
- qcom,gpio-reset = <1>;
- qcom,gpio-standby = <2>;
- qcom,gpio-req-tbl-num = <0 1 2>;
- qcom,gpio-req-tbl-flags = <1 0 0>;
- qcom,gpio-req-tbl-label = "CAMIF_MCLK",
- "CAM_RESET1",
- "CAM_STANDBY";
- qcom,gpio-set-tbl-num = <1 1>;
- qcom,gpio-set-tbl-flags = <0 2>;
- qcom,gpio-set-tbl-delay = <1000 30000>;
- qcom,csi-lane-assign = <0x4320>;
- qcom,csi-lane-mask = <0x1F>;
- qcom,sensor-position = <0>;
- qcom,sensor-mode = <1>;
- qcom,cci-master = <0>;
- status = "ok";
- };
-
- qcom,camera@20 {
- compatible = "qcom,imx135";
- reg = <0x20>;
- qcom,slave-id = <0x20 0x0016 0x0135>;
- qcom,csiphy-sd-index = <0>;
- qcom,csid-sd-index = <0>;
- qcom,mount-angle = <90>;
- qcom,sensor-name = "imx135";
- qcom,actuator-src = <&actuator1>;
- qcom,led-flash-src = <&led_flash0>;
- cam_vdig-supply = <&pm8941_l3>;
- cam_vana-supply = <&pm8941_l17>;
- cam_vio-supply = <&pm8941_lvs3>;
- cam_vaf-supply = <&pm8941_l23>;
- qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana",
- "cam_vaf";
- qcom,cam-vreg-type = <0 1 0 0>;
- qcom,cam-vreg-min-voltage = <1225000 0 2850000 3000000>;
- qcom,cam-vreg-max-voltage = <1225000 0 2850000 3000000>;
- qcom,cam-vreg-op-mode = <105000 0 80000 100000>;
- qcom,gpio-no-mux = <0>;
- gpios = <&msmgpio 15 0>,
- <&msmgpio 90 0>,
- <&msmgpio 89 0>;
- qcom,gpio-reset = <1>;
- qcom,gpio-standby = <2>;
- qcom,gpio-req-tbl-num = <0 1 2>;
- qcom,gpio-req-tbl-flags = <1 0 0>;
- qcom,gpio-req-tbl-label = "CAMIF_MCLK",
- "CAM_RESET1",
- "CAM_STANDBY";
- qcom,gpio-set-tbl-num = <1 1>;
- qcom,gpio-set-tbl-flags = <0 2>;
- qcom,gpio-set-tbl-delay = <1000 30000>;
- qcom,csi-lane-assign = <0x4320>;
- qcom,csi-lane-mask = <0x1F>;
- qcom,sensor-position = <0>;
- qcom,sensor-mode = <0>;
- qcom,sensor-type = <0>;
- qcom,cci-master = <0>;
- status = "ok";
- };
-
-
- qcom,camera@6c {
- compatible = "qcom,ov2720";
- reg = <0x6c>;
- qcom,slave-id = <0x6c 0x300A 0x2720>;
- qcom,csiphy-sd-index = <2>;
- qcom,csid-sd-index = <2>;
- qcom,mount-angle = <90>;
- qcom,sensor-name = "ov2720";
- cam_vdig-supply = <&pm8941_l3>;
- cam_vana-supply = <&pm8941_l17>;
- cam_vio-supply = <&pm8941_lvs3>;
- qcom,cam-vreg-name = "cam_vdig", "cam_vana", "cam_vio";
- qcom,cam-vreg-type = <0 0 1>;
- qcom,cam-vreg-min-voltage = <1225000 2850000 0>;
- qcom,cam-vreg-max-voltage = <1225000 2850000 0>;
- qcom,cam-vreg-op-mode = <105000 80000 0>;
- qcom,gpio-no-mux = <0>;
- gpios = <&msmgpio 17 0>,
- <&msmgpio 18 0>;
- qcom,gpio-reset = <1>;
- qcom,gpio-req-tbl-num = <0 1>;
- qcom,gpio-req-tbl-flags = <1 0>;
- qcom,gpio-req-tbl-label = "CAMIF_MCLK",
- "CAM_RESET1";
- qcom,gpio-set-tbl-num = <1 1>;
- qcom,gpio-set-tbl-flags = <0 2>;
- qcom,gpio-set-tbl-delay = <1000 4000>;
- qcom,csi-lane-assign = <0x4320>;
- qcom,csi-lane-mask = <0x7>;
- qcom,sensor-position = <1>;
- qcom,sensor-mode = <1>;
- qcom,cci-master = <1>;
- status = "ok";
- };
-
qcom,camera@90 {
compatible = "qcom,mt9m114";
reg = <0x90>;
@@ -191,6 +70,7 @@
qcom,sensor-mode = <1>;
qcom,cci-master = <0>;
};
+
qcom,camera@0 {
cell-index = <0>;
compatible = "qcom,camera";
@@ -200,8 +80,6 @@
qcom,mount-angle = <90>;
qcom,actuator-src = <&actuator0>;
qcom,led-flash-src = <&led_flash0>;
- qcom,vdd-cx-supply = <&pm8841_s2>;
- qcom,vdd-cx-name = "qcom,vdd-cx";
cam_vdig-supply = <&pm8941_l3>;
cam_vana-supply = <&pm8941_l17>;
cam_vio-supply = <&pm8941_lvs3>;
@@ -212,6 +90,7 @@
qcom,cam-vreg-min-voltage = <1225000 0 2850000 3000000>;
qcom,cam-vreg-max-voltage = <1225000 0 2850000 3000000>;
qcom,cam-vreg-op-mode = <105000 0 80000 100000>;
+ qcom,gpio-no-mux = <0>;
gpios = <&msmgpio 15 0>,
<&msmgpio 90 0>,
<&msmgpio 89 0>;
@@ -219,9 +98,11 @@
qcom,gpio-standby = <2>;
qcom,gpio-req-tbl-num = <0 1 2>;
qcom,gpio-req-tbl-flags = <1 0 0>;
- qcom,gpio-req-tbl-label = "CAMIF_MCLK",
- "CAM_RESET1",
- "CAM_STANDBY";
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK0",
+ "CAM_RESET0",
+ "CAM_STANDBY0";
+ qcom,sensor-position = <0>;
+ qcom,sensor-mode = <0>;
qcom,cci-master = <0>;
status = "ok";
};
@@ -231,10 +112,8 @@
compatible = "qcom,camera";
reg = <0x1>;
qcom,csiphy-sd-index = <1>;
- qcom,csid-sd-index = <1>;
+ qcom,csid-sd-index = <0>;
qcom,mount-angle = <90>;
- qcom,vdd-cx-supply = <&pm8841_s2>;
- qcom,vdd-cx-name = "qcom,vdd-cx";
cam_vdig-supply = <&pm8941_l3>;
cam_vana-supply = <&pm8941_l17>;
cam_vio-supply = <&pm8941_lvs3>;
@@ -244,13 +123,18 @@
qcom,cam-vreg-min-voltage = <1225000 0 2850000 3000000>;
qcom,cam-vreg-max-voltage = <1225000 0 2850000 3000000>;
qcom,cam-vreg-op-mode = <105000 0 80000 100000>;
- gpios = <&msmgpio 17 0>,
- <&msmgpio 18 0>;
+ gpios = <&msmgpio 16 0>,
+ <&msmgpio 92 0>,
+ <&msmgpio 91 0>;
qcom,gpio-reset = <1>;
- qcom,gpio-req-tbl-num = <0 1>;
- qcom,gpio-req-tbl-flags = <1 0>;
- qcom,gpio-req-tbl-label = "CAMIF_MCLK",
- "CAM_RESET1";
+ qcom,gpio-standby = <2>;
+ qcom,gpio-req-tbl-num = <0 1 2>;
+ qcom,gpio-req-tbl-flags = <1 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK1",
+ "CAM_RESET1",
+ "CAM_STANDBY1";
+ qcom,sensor-position = <0>;
+ qcom,sensor-mode = <0>;
qcom,cci-master = <0>;
status = "ok";
};
@@ -272,14 +156,18 @@
qcom,cam-vreg-op-mode = <105000 80000 0>;
qcom,gpio-no-mux = <0>;
gpios = <&msmgpio 17 0>,
- <&msmgpio 18 0>;
+ <&msmgpio 18 0>,
+ <&msmgpio 28 0>;
qcom,gpio-reset = <1>;
- qcom,gpio-req-tbl-num = <0 1>;
- qcom,gpio-req-tbl-flags = <1 0>;
- qcom,gpio-req-tbl-label = "CAMIF_MCLK",
- "CAM_RESET1";
+ qcom,gpio-standby = <2>;
+ qcom,gpio-req-tbl-num = <0 1 2>;
+ qcom,gpio-req-tbl-flags = <1 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK2",
+ "CAM_RESET2",
+ "CAM_STANDBY2";
+ qcom,sensor-position = <1>;
+ qcom,sensor-mode = <0>;
qcom,cci-master = <1>;
status = "ok";
};
-
};
diff --git a/arch/arm/boot/dts/msm8974-liquid.dtsi b/arch/arm/boot/dts/msm8974-liquid.dtsi
index 942aba4..5d1034b 100644
--- a/arch/arm/boot/dts/msm8974-liquid.dtsi
+++ b/arch/arm/boot/dts/msm8974-liquid.dtsi
@@ -318,7 +318,7 @@
compatible = "qca,ar3002";
qca,bt-reset-gpio = <&pm8941_gpios 34 0>;
qca,bt-chip-pwd-supply = <&ath_chip_pwd_l>;
- qca,bt-vdd-io-supply = <&pm8941_l10>;
+ qca,bt-vdd-io-supply = <&pm8941_s3>;
qca,bt-vdd-pa-supply = <&pm8941_l19>;
};
@@ -380,34 +380,47 @@
qcom,prim-auxpcm-gpio-set = "prim-gpio-tert";
};
- hsic_host: hsic@f9a00000 {
- compatible = "qcom,hsic-host";
- reg = <0xf9a00000 0x400>;
- #address-cells = <0>;
- interrupt-parent = <&hsic_host>;
- interrupts = <0 1 2>;
- #interrupt-cells = <1>;
- interrupt-map-mask = <0xffffffff>;
- interrupt-map = <0 &intc 0 136 0
- 1 &intc 0 148 0
- 2 &msmgpio 144 0x8>;
- interrupt-names = "core_irq", "async_irq", "wakeup";
- hsic_vdd_dig-supply = <&pm8841_s2_corner>;
- HSIC_GDSC-supply = <&gdsc_usb_hsic>;
- hsic,strobe-gpio = <&msmgpio 144 0x00>;
- hsic,data-gpio = <&msmgpio 145 0x00>;
- hsic,ignore-cal-pad-config;
- hsic,strobe-pad-offset = <0x2050>;
- hsic,data-pad-offset = <0x2054>;
- qcom,phy-susp-sof-workaround;
- hsic,vdd-voltage-level = <1 5 7>;
+ hsic_hub {
+ compatible = "qcom,hsic-smsc-hub";
+ smsc,model-id = <3503>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+ smsc,reset-gpio = <&pm8941_gpios 8 0x00>;
+ smsc,refclk-gpio = <&pm8941_gpios 16 0x00>;
+ smsc,int-gpio = <&msmgpio 50 0x00>;
+ hub_int-supply = <&pm8941_l10>;
+ hub_vbus-supply = <&ext_5v>;
- qcom,msm-bus,name = "hsic";
- qcom,msm-bus,num-cases = <2>;
- qcom,msm-bus,num-paths = <1>;
- qcom,msm-bus,vectors-KBps =
- <85 512 0 0>,
- <85 512 40000 160000>;
+ hsic_host: hsic@f9a00000 {
+ compatible = "qcom,hsic-host";
+ reg = <0xf9a00000 0x400>;
+ #address-cells = <0>;
+ interrupt-parent = <&hsic_host>;
+ interrupts = <0 1 2>;
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0xffffffff>;
+ interrupt-map = <0 &intc 0 136 0
+ 1 &intc 0 148 0
+ 2 &msmgpio 144 0x8>;
+ interrupt-names = "core_irq", "async_irq", "wakeup";
+ hsic_vdd_dig-supply = <&pm8841_s2_corner>;
+ HSIC_GDSC-supply = <&gdsc_usb_hsic>;
+ hsic,strobe-gpio = <&msmgpio 144 0x00>;
+ hsic,data-gpio = <&msmgpio 145 0x00>;
+ hsic,ignore-cal-pad-config;
+ hsic,strobe-pad-offset = <0x2050>;
+ hsic,data-pad-offset = <0x2054>;
+ qcom,phy-susp-sof-workaround;
+ hsic,vdd-voltage-level = <1 5 7>;
+
+ qcom,msm-bus,name = "hsic";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <85 512 0 0>,
+ <85 512 40000 160000>;
+ };
};
wlan0: qca,wlan {
@@ -433,37 +446,6 @@
qca,wifi-chip-pwd-supply = <&ath_chip_pwd_l>;
};
- qcom,pronto@fb21b000 {
- status = "disabled";
- };
-
- qcom,iris-fm {
- status = "disabled";
- };
-
- qcom,wcnss-wlan@fb000000 {
- status = "disabled";
- };
-
- qcom,smd-wcnss {
- status = "disabled";
- };
-
- qcom,smsm-wcnss {
- status = "disabled";
- };
-};
-
-&pm8941_l19 {
- regulator-min-microvolt = <3300000>;
- regulator-max-microvolt = <3300000>;
- qcom,init-voltage = <3300000>;
-};
-
-&pm8941_l10 {
- regulator-min-microvolt = <1800000>;
- regulator-max-microvolt = <1800000>;
- qcom,init-voltage = <1800000>;
};
&mdss_fb0 {
diff --git a/arch/arm/boot/dts/msm8974-mdss.dtsi b/arch/arm/boot/dts/msm8974-mdss.dtsi
index 0e72446..3fef47a 100644
--- a/arch/arm/boot/dts/msm8974-mdss.dtsi
+++ b/arch/arm/boot/dts/msm8974-mdss.dtsi
@@ -35,6 +35,7 @@
qcom,mdss-ab-factor = <2 1>; /* 2 times */
qcom,mdss-ib-factor = <6 5>; /* 1.2 times */
qcom,mdss-clk-factor = <5 4>; /* 1.25 times */
+ qcom,mdss-ib-factor-overlap = <7 4>; /* 1.75 times */
qcom,max-clk-rate = <320000000>;
qcom,mdss-pipe-vig-off = <0x00001200 0x00001600
@@ -50,6 +51,15 @@
qcom,mdss-pipe-rgb-xin-id = <1 5 9>;
qcom,mdss-pipe-dma-xin-id = <2 10>;
+ qcom,mdss-pipe-vig-clk-ctrl-offsets = <0x3AC 0 0>,
+ <0x3B4 0 0>,
+ <0x3BC 0 0>;
+ qcom,mdss-pipe-rgb-clk-ctrl-offsets = <0x3AC 4 8>,
+ <0x3B4 4 8>,
+ <0x3BC 4 8>;
+ qcom,mdss-pipe-dma-clk-ctrl-offsets = <0x3AC 8 12>,
+ <0x3B4 8 12>;
+
qcom,mdss-smp-data = <22 4096>;
qcom,mdss-ctl-off = <0x00000600 0x00000700 0x00000800
@@ -113,9 +123,10 @@
compatible = "qcom,mdss-dsi-ctrl";
label = "MDSS DSI CTRL->0";
cell-index = <0>;
- reg = <0xfd922800 0x600>,
+ reg = <0xfd922800 0x1f8>,
+ <0xfd922b00 0x2b0>,
<0xfdf30000 0x108>;
- reg-names = "dsi_phys", "mmss_misc_phys";
+ reg-names = "dsi_ctrl", "dsi_phy", "mmss_misc_phys";
vdd-supply = <&pm8941_l22>;
vddio-supply = <&pm8941_l12>;
vdda-supply = <&pm8941_l2>;
@@ -171,9 +182,10 @@
compatible = "qcom,mdss-dsi-ctrl";
label = "MDSS DSI CTRL->1";
cell-index = <1>;
- reg = <0xfd922e00 0x600>,
+ reg = <0xfd922e00 0x1f8>,
+ <0xfd923100 0x2b0>,
<0xfdf30000 0x108>;
- reg-names = "dsi_phys", "mmss_misc_phys";
+ reg-names = "dsi_ctrl", "dsi_phy", "mmss_misc_phys";
vdd-supply = <&pm8941_l22>;
vddio-supply = <&pm8941_l12>;
vdda-supply = <&pm8941_l2>;
diff --git a/arch/arm/boot/dts/msm8974-v2.dtsi b/arch/arm/boot/dts/msm8974-v2.dtsi
index 7e102fe..fd4221f 100644
--- a/arch/arm/boot/dts/msm8974-v2.dtsi
+++ b/arch/arm/boot/dts/msm8974-v2.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2014, 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
@@ -198,17 +198,17 @@
&cci {
- qcom,camera@6e {
+ qcom,camera@0 {
qcom,vdd-cx-supply = <&pm8841_s2>;
qcom,vdd-cx-name = "qcom,vdd-cx";
};
- qcom,camera@20 {
+ qcom,camera@1 {
qcom,vdd-cx-supply = <&pm8841_s2>;
qcom,vdd-cx-name = "qcom,vdd-cx";
};
- qcom,camera@6c {
+ qcom,camera@2 {
qcom,vdd-cx-supply = <&pm8841_s2>;
qcom,vdd-cx-name = "qcom,vdd-cx";
};
diff --git a/arch/arm/boot/dts/msm8974.dtsi b/arch/arm/boot/dts/msm8974.dtsi
index da5474d..63a37f5 100644
--- a/arch/arm/boot/dts/msm8974.dtsi
+++ b/arch/arm/boot/dts/msm8974.dtsi
@@ -335,7 +335,7 @@
qcom,bus-speed-mode = "HS200_1p8v", "DDR_1p8v";
qcom,msm-bus,name = "sdcc1";
- qcom,msm-bus,num-cases = <8>;
+ qcom,msm-bus,num-cases = <9>;
qcom,msm-bus,num-paths = <1>;
qcom,msm-bus,vectors-KBps = <78 512 0 0>, /* No vote */
<78 512 1600 3200>, /* 400 KB/s*/
@@ -344,8 +344,11 @@
<78 512 200000 400000>, /* 50 MB/s */
<78 512 400000 800000>, /* 100 MB/s */
<78 512 800000 1600000>, /* 200 MB/s */
+ <78 512 800000 1600000>, /* 400 MB/s */
<78 512 2048000 4096000>; /* Max. bandwidth */
- qcom,bus-bw-vectors-bps = <0 400000 20000000 25000000 50000000 100000000 200000000 4294967295>;
+ qcom,bus-bw-vectors-bps = <0 400000 20000000 25000000 50000000
+ 100000000 200000000 400000000
+ 4294967295>;
qcom,dat1-mpm-int = <42>;
status = "disable";
};
@@ -498,7 +501,7 @@
qcom,cpu-dma-latency-us = <200>;
qcom,msm-bus,name = "sdhc1";
- qcom,msm-bus,num-cases = <8>;
+ qcom,msm-bus,num-cases = <9>;
qcom,msm-bus,num-paths = <1>;
qcom,msm-bus,vectors-KBps = <78 512 0 0>, /* No vote */
<78 512 1600 3200>, /* 400 KB/s*/
@@ -507,8 +510,12 @@
<78 512 200000 400000>, /* 50 MB/s */
<78 512 400000 800000>, /* 100 MB/s */
<78 512 800000 1600000>, /* 200 MB/s */
+ <78 512 800000 1600000>, /* 400 MB/s */
<78 512 2048000 4096000>; /* Max. bandwidth */
- qcom,bus-bw-vectors-bps = <0 400000 20000000 25000000 50000000 100000000 200000000 4294967295>;
+ qcom,bus-bw-vectors-bps = <0 400000 20000000 25000000 50000000
+ 100000000 200000000 400000000
+ 4294967295>;
+ qcom,dat1-mpm-int = <42>;
status = "disable";
};
@@ -535,6 +542,7 @@
<81 512 800000 1600000>, /* 200 MB/s */
<81 512 2048000 4096000>; /* Max. bandwidth */
qcom,bus-bw-vectors-bps = <0 400000 20000000 25000000 50000000 100000000 200000000 4294967295>;
+ qcom,dat1-mpm-int = <44>;
status = "disable";
};
@@ -542,8 +550,17 @@
compatible = "qcom,sdhci-msm";
reg = <0xf9864900 0x11c>, <0xf9864000 0x800>;
reg-names = "hc_mem", "core_mem";
- interrupts = <0 127 0>, <0 224 0>;
- interrupt-names = "hc_irq", "pwr_irq";
+
+ #address-cells = <0>;
+ interrupt-parent = <&sdhc_3>;
+ interrupts = <0 1 2>;
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0xffffffff>;
+ interrupt-map = <0 &intc 0 127 0
+ 1 &intc 0 224 0
+ 2 &msmgpio 37 0x8>;
+ interrupt-names = "hc_irq", "pwr_irq", "sdiowakeup_irq";
+
gpios = <&msmgpio 40 0>, /* CLK */
<&msmgpio 39 0>, /* CMD */
<&msmgpio 38 0>, /* DATA0 */
@@ -575,8 +592,17 @@
compatible = "qcom,sdhci-msm";
reg = <0xf98e4900 0x11c>, <0xf98e4000 0x800>;
reg-names = "hc_mem", "core_mem";
- interrupts = <0 129 0>, <0 227 0>;
- interrupt-names = "hc_irq", "pwr_irq";
+
+ #address-cells = <0>;
+ interrupt-parent = <&sdhc_4>;
+ interrupts = <0 1 2>;
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0xffffffff>;
+ interrupt-map = <0 &intc 0 129 0
+ 1 &intc 0 227 0
+ 2 &msmgpio 95 0x8>;
+ interrupt-names = "hc_irq", "pwr_irq", "sdiowakeup_irq";
+
gpios = <&msmgpio 93 0>, /* CLK */
<&msmgpio 91 0>, /* CMD */
<&msmgpio 96 0>, /* DATA0 */
@@ -1823,6 +1849,11 @@
compatible = "qcom,msm-dai-q6-dev";
qcom,msm-dai-q6-dev-id = <32773>;
};
+
+ qcom,msm-dai-q6-incall-music-2-rx {
+ compatible = "qcom,msm-dai-q6-dev";
+ qcom,msm-dai-q6-dev-id = <32770>;
+ };
};
qcom,msm-pri-auxpcm {
@@ -2012,12 +2043,15 @@
reg = <0x7b00000 0x500000>;
reg-names = "secapp-region";
qcom,disk-encrypt-pipe-pair = <2>;
+ qcom,file-encrypt-pipe-pair = <0>;
qcom,hlos-ce-hw-instance = <1>;
qcom,qsee-ce-hw-instance = <0>;
qcom,support-bus-scaling;
qcom,msm-bus,name = "qseecom-noc";
qcom,msm-bus,num-cases = <4>;
qcom,msm-bus,num-paths = <1>;
+ qcom,support-fde;
+ qcom,support-pfe;
qcom,msm-bus,vectors-KBps =
<55 512 0 0>,
<55 512 0 0>,
@@ -2114,11 +2148,12 @@
qcom,qcedev@fd440000 {
compatible = "qcom,qcedev";
reg = <0xfd440000 0x20000>,
- <0xfd444000 0x8000>;
+ <0xfd444000 0x1b000>;
reg-names = "crypto-base","crypto-bam-base";
interrupts = <0 236 0>;
qcom,bam-pipe-pair = <1>;
qcom,ce-hw-instance = <1>;
+ qcom,ce-device = <0>;
qcom,msm-bus,name = "qcedev-noc";
qcom,msm-bus,num-cases = <2>;
qcom,msm-bus,num-paths = <1>;
@@ -2127,14 +2162,15 @@
<56 512 3936000 393600>;
};
- qcom,qcrypto@fd444000 {
+ qcom,qcrypto@fd440000 {
compatible = "qcom,qcrypto";
reg = <0xfd440000 0x20000>,
- <0xfd444000 0x8000>;
+ <0xfd444000 0x1b000>;
reg-names = "crypto-base","crypto-bam-base";
interrupts = <0 236 0>;
qcom,bam-pipe-pair = <2>;
qcom,ce-hw-instance = <1>;
+ qcom,ce-device = <0>;
qcom,clk-mgmt-sus-res;
qcom,msm-bus,name = "qcrypto-noc";
qcom,msm-bus,num-cases = <2>;
@@ -2147,6 +2183,27 @@
<56 512 3936000 393600>;
};
+ qcom,qcrypto1@fd440000 {
+ compatible = "qcom,qcrypto";
+ reg = <0xfd440000 0x20000>,
+ <0xfd444000 0x1b000>;
+ reg-names = "crypto-base","crypto-bam-base";
+ interrupts = <0 236 0>;
+ qcom,bam-pipe-pair = <0>;
+ qcom,ce-hw-instance = <1>;
+ qcom,ce-device = <1>;
+ qcom,clk-mgmt-sus-res;
+ qcom,msm-bus,name = "qcrypto-noc";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,use-sw-aes-cbc-ecb-ctr-algo;
+ qcom,use-sw-aes-xts-algo;
+ qcom,use-sw-ahash-algo;
+ qcom,msm-bus,vectors-KBps =
+ <56 512 0 0>,
+ <56 512 3936000 393600>;
+ };
+
qcom,usbbam@f9304000 {
compatible = "qcom,usb-bam-msm";
reg = <0xf9304000 0x5000>,
@@ -2397,6 +2454,12 @@
interrupts = <0 1 0>;
};
+ bimc_sharedmem {
+ compatible = "qcom,sharedmem-uio";
+ reg = <0xfc380000 0x00100000>;
+ reg-names = "bimc";
+ };
+
qcom,smdtty {
compatible = "qcom,smdtty";
diff --git a/arch/arm/boot/dts/msm8974pro-ab-pm8941-cdp.dts b/arch/arm/boot/dts/msm8974pro-ab-pm8941-cdp.dts
index 5a01945..b9f2574 100644
--- a/arch/arm/boot/dts/msm8974pro-ab-pm8941-cdp.dts
+++ b/arch/arm/boot/dts/msm8974pro-ab-pm8941-cdp.dts
@@ -25,3 +25,8 @@
qcom,pad-pull-on = <0x0 0x3 0x3 0x1>; /* no-pull, pull-up, pull-up, pull-down */
qcom,pad-pull-off = <0x0 0x3 0x3 0x1>; /* no-pull, pull-up, pull-up, pull-down */
};
+
+&ehci {
+ hsusb_vdd_dig-supply = <&pm8841_s2_corner>;
+ qcom,vdd-voltage-level = <1 2 3 5 7>;
+};
diff --git a/arch/arm/boot/dts/msm8974pro-ab-pm8941-fluid-hbtp.dts b/arch/arm/boot/dts/msm8974pro-ab-pm8941-fluid-hbtp.dts
index 6ba8b5e..feefded 100644
--- a/arch/arm/boot/dts/msm8974pro-ab-pm8941-fluid-hbtp.dts
+++ b/arch/arm/boot/dts/msm8974pro-ab-pm8941-fluid-hbtp.dts
@@ -14,6 +14,7 @@
/include/ "msm8974pro-ab-pm8941.dtsi"
/include/ "msm8974-fluid.dtsi"
+/include/ "dsi-panel-jdi-720p-cmd.dtsi"
/ {
model = "Qualcomm MSM 8974Pro-AA/AB FLUID";
@@ -22,6 +23,10 @@
};
&soc {
+ qcom,mdss_dsi@fd922800{
+ qcom,dsi-pref-prim-pan = <&dsi_jdi_720p_cmd>;
+ };
+
spi@f9966000 { /* BLSP2 QUP4 */
/* Leave the SPI bus for QDSP to use */
status = "disabled";
diff --git a/arch/arm/boot/dts/msm8974pro-ab-pm8941-mtp.dts b/arch/arm/boot/dts/msm8974pro-ab-pm8941-mtp.dts
index f80551e..99c4ec7 100644
--- a/arch/arm/boot/dts/msm8974pro-ab-pm8941-mtp.dts
+++ b/arch/arm/boot/dts/msm8974pro-ab-pm8941-mtp.dts
@@ -26,3 +26,10 @@
qcom,pad-pull-off = <0x0 0x3 0x3 0x1>; /* no-pull, pull-up, pull-up, pull-down */
qcom,pad-drv-on = <0x4 0x4 0x4>; /* 10mA, 10mA, 10mA */
};
+
+&ehci {
+ status = "ok";
+ qcom,usb2-enable-uicc;
+ hsusb_vdd_dig-supply = <&pm8841_s2_corner>;
+ qcom,vdd-voltage-level = <1 2 3 5 7>;
+};
diff --git a/arch/arm/boot/dts/msm8974pro-ac-pm8941-mtp.dts b/arch/arm/boot/dts/msm8974pro-ac-pm8941-mtp.dts
index 76d0121..a25b8ca 100644
--- a/arch/arm/boot/dts/msm8974pro-ac-pm8941-mtp.dts
+++ b/arch/arm/boot/dts/msm8974pro-ac-pm8941-mtp.dts
@@ -25,3 +25,8 @@
qcom,pad-pull-on = <0x0 0x3 0x3 0x1>; /* no-pull, pull-up, pull-up, pull-down */
qcom,pad-pull-off = <0x0 0x3 0x3 0x1>; /* no-pull, pull-up, pull-up, pull-down */
};
+
+&ehci {
+ status = "ok";
+ qcom,usb2-enable-uicc;
+};
diff --git a/arch/arm/boot/dts/msm8974pro-ac-pma8084-pm8941-mtp.dts b/arch/arm/boot/dts/msm8974pro-ac-pma8084-pm8941-mtp.dts
index 8a4ad45..0fe8813 100644
--- a/arch/arm/boot/dts/msm8974pro-ac-pma8084-pm8941-mtp.dts
+++ b/arch/arm/boot/dts/msm8974pro-ac-pma8084-pm8941-mtp.dts
@@ -120,3 +120,8 @@
qcom,thermal-node;
};
};
+
+&ehci {
+ status = "ok";
+ qcom,usb2-enable-uicc;
+};
diff --git a/arch/arm/boot/dts/msm8974pro-ac-pma8084-pm8941.dtsi b/arch/arm/boot/dts/msm8974pro-ac-pma8084-pm8941.dtsi
index bc7ecd2..df04f82 100644
--- a/arch/arm/boot/dts/msm8974pro-ac-pma8084-pm8941.dtsi
+++ b/arch/arm/boot/dts/msm8974pro-ac-pma8084-pm8941.dtsi
@@ -74,6 +74,54 @@
qcom,auto-mode-enable = <0>;
status = "okay";
};
+
+ pwm@b100 {
+ status = "disabled";
+ };
+
+ pwm@b200 {
+ status = "disabled";
+ };
+
+ pwm@b300 {
+ status = "disabled";
+ };
+
+ pwm@b400 {
+ status = "disabled";
+ };
+
+ pwm@b500 {
+ status = "disabled";
+ };
+
+ pwm@b600 {
+ status = "disabled";
+ };
+
+ pwm@b700 {
+ status = "disabled";
+ };
+
+ pwm@b800 {
+ status = "disabled";
+ };
+
+ pwm@e400 {
+ status = "disabled";
+ };
+
+ pwm@e500 {
+ status = "disabled";
+ };
+
+ pwm@e600 {
+ status = "disabled";
+ };
+
+ pwm@e700 {
+ status = "disabled";
+ };
};
&pma8084_mvs1 {
@@ -103,6 +151,7 @@
qcom,misc-ref = <&pm8941_misc>;
dwc_usb3-adc_tm = <&pm8941_adc_tm>;
qcom,usbin-vadc = <&pm8941_vadc>;
+ qcom,utmi-clk-rate = <24000000>;
interrupt-map-mask = <0x0 0xffffffff>;
interrupt-map = <0x0 0 &intc 0 133 0
0x0 1 &spmi_bus 0x0 0x2 0x9 0x0>;
@@ -240,6 +289,11 @@
vadc@3100 {
interrupts = <0x2 0x31 0x0>;
interrupt-names = "eoc-int-en-set";
+ /delete-node/ chan@b3;
+ /delete-node/ chan@b4;
+ /delete-node/ chan@b5;
+ /delete-node/ chan@b7;
+ /delete-node/ chan@b8;
};
iadc@3600 {
@@ -254,5 +308,9 @@
interrupt-names = "eoc-int-en-set",
"high-thr-en-set",
"low-thr-en-set";
+ /delete-node/ chan@b3;
+ /delete-node/ chan@b4;
+ /delete-node/ chan@b5;
+ /delete-node/ chan@b7;
};
};
diff --git a/arch/arm/boot/dts/msm8974pro-pm8941.dtsi b/arch/arm/boot/dts/msm8974pro-pm8941.dtsi
index 9c2be1a..b502078 100644
--- a/arch/arm/boot/dts/msm8974pro-pm8941.dtsi
+++ b/arch/arm/boot/dts/msm8974pro-pm8941.dtsi
@@ -68,6 +68,12 @@
&usb3 {
qcom,usbin-vadc = <&pm8941_vadc>;
+ qcom,utmi-clk-rate = <24000000>;
+};
+
+&ehci {
+ hsusb_vdd_dig-supply = <&pm8841_s2_corner>;
+ qcom,vdd-voltage-level = <1 2 3 5 7>;
};
&krait_regulator_pmic {
diff --git a/arch/arm/boot/dts/msm8974pro-pma8084-mtp.dtsi b/arch/arm/boot/dts/msm8974pro-pma8084-mtp.dtsi
index 680674d..12ed7d4 100644
--- a/arch/arm/boot/dts/msm8974pro-pma8084-mtp.dtsi
+++ b/arch/arm/boot/dts/msm8974pro-pma8084-mtp.dtsi
@@ -200,9 +200,12 @@
};
gpio@c700 { /* GPIO 8 */
- /* Unused */
- qcom,mode = <0>; /* Digital input */
- qcom,pull = <0>; /* Pull up 30 uA */
+ qcom,mode = <1>; /* Digital output */
+ qcom,output-type = <0>; /* CMOS logic */
+ qcom,invert = <0>; /* Do not invert the output */
+ qcom,vin-sel = <2>; /* PMA8084 S4 = 1.8V */
+ qcom,src-sel = <2>; /* Special function 1=LPG 3 */
+ qcom,out-strength = <3>; /* High drive Strength*/
qcom,master-en = <1>;
};
diff --git a/arch/arm/boot/dts/msm8974pro-pma8084.dtsi b/arch/arm/boot/dts/msm8974pro-pma8084.dtsi
index c06ebf8..28ddda2 100644
--- a/arch/arm/boot/dts/msm8974pro-pma8084.dtsi
+++ b/arch/arm/boot/dts/msm8974pro-pma8084.dtsi
@@ -118,6 +118,8 @@
HSUSB_VDDCX-supply = <&pma8084_s2>;
HSUSB_1p8-supply = <&pma8084_l6>;
HSUSB_3p3-supply = <&pma8084_l24>;
+ hsusb_vdd_dig-supply = <&pma8084_s2_corner>;
+ qcom,vdd-voltage-level = <1 2 3 5 7>;
};
qcom,gdsc@fd8c4024 {
@@ -210,3 +212,7 @@
status = "ok";
};
};
+
+&dsi_generic_720p_cmd {
+ qcom,mdss-dsi-bl-pmic-bank-select = <3>;
+};
diff --git a/arch/arm/boot/dts/msm8974pro.dtsi b/arch/arm/boot/dts/msm8974pro.dtsi
index ae0547f..d398f72 100644
--- a/arch/arm/boot/dts/msm8974pro.dtsi
+++ b/arch/arm/boot/dts/msm8974pro.dtsi
@@ -1723,7 +1723,7 @@
<3240000 1600000>,
<4048000 1600000>,
<4264000 1600000>;
- qcom,max-hw-load = <1281600>; /* max(4k X 2304 @ 24, 4k X 2160 @ 30) + 1080p @ 30 */
+ qcom,max-hw-load = <1216800>; /* 3840 X 2160 @ 30 fps + 1920 X 1088 @ 30 fps */
qcom,buffer-type-tz-usage-table = <0x241 0x1>,
<0x106 0x2>,
<0x480 0x3>;
diff --git a/arch/arm/boot/dts/msmsamarium-camera-sensor-cdp-interposer.dtsi b/arch/arm/boot/dts/msmsamarium-camera-sensor-cdp-interposer.dtsi
index 81640f8..0c25060 100644
--- a/arch/arm/boot/dts/msmsamarium-camera-sensor-cdp-interposer.dtsi
+++ b/arch/arm/boot/dts/msmsamarium-camera-sensor-cdp-interposer.dtsi
@@ -135,6 +135,8 @@
qcom,gpio-req-tbl-label = "CAMIF_MCLK",
"CAM_RESET1",
"CAM_STANDBY";
+ qcom,sensor-position = <0>;
+ qcom,sensor-mode = <0>;
qcom,cci-master = <0>;
status = "ok";
};
@@ -165,6 +167,8 @@
qcom,gpio-req-tbl-flags = <1 0>;
qcom,gpio-req-tbl-label = "CAMIF_MCLK2",
"CAM_XSHUTDOWN";
+ qcom,sensor-position = <1>;
+ qcom,sensor-mode = <0>;
qcom,cci-master = <0>;
status = "ok";
};
diff --git a/arch/arm/boot/dts/msmsamarium-camera-sensor-cdp.dtsi b/arch/arm/boot/dts/msmsamarium-camera-sensor-cdp.dtsi
index 27f4a99..637356b 100644
--- a/arch/arm/boot/dts/msmsamarium-camera-sensor-cdp.dtsi
+++ b/arch/arm/boot/dts/msmsamarium-camera-sensor-cdp.dtsi
@@ -136,6 +136,8 @@
qcom,gpio-req-tbl-label = "CAMIF_MCLK",
"CAM_RESET1",
"CAM_STANDBY";
+ qcom,sensor-position = <0>;
+ qcom,sensor-mode = <0>;
qcom,cci-master = <0>;
status = "ok";
};
@@ -165,6 +167,8 @@
qcom,gpio-req-tbl-flags = <1 0>;
qcom,gpio-req-tbl-label = "CAMIF_MCLK2",
"CAM_XSHUTDOWN";
+ qcom,sensor-position = <1>;
+ qcom,sensor-mode = <0>;
qcom,cci-master = <0>;
status = "ok";
};
diff --git a/arch/arm/common/cpaccess.c b/arch/arm/common/cpaccess.c
index cca0b39..6fd54ef 100644
--- a/arch/arm/common/cpaccess.c
+++ b/arch/arm/common/cpaccess.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2010-2014, 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
@@ -84,9 +84,10 @@
*/
static void do_write_il2(void *ret)
{
- *(unsigned long *)ret =
- set_get_l2_indirect_reg(per_cpu(cp_param.il2index, cpu),
+ set_l2_indirect_reg(per_cpu(cp_param.il2index, cpu),
per_cpu(cp_param.write_value, cpu));
+ *(unsigned long *)ret =
+ get_l2_indirect_reg(per_cpu(cp_param.il2index, cpu));
}
/*
diff --git a/arch/arm/configs/apq8084_defconfig b/arch/arm/configs/apq8084_defconfig
index 823e4f8..2f5e776 100644
--- a/arch/arm/configs/apq8084_defconfig
+++ b/arch/arm/configs/apq8084_defconfig
@@ -365,6 +365,7 @@
CONFIG_QPNP_CLKDIV=y
CONFIG_MSM_IOMMU_V1=y
CONFIG_IOMMU_PGTABLES_L2=y
+CONFIG_MSM_IOMMU_VBIF_CHECK=y
CONFIG_IOMMU_NON_SECURE=y
CONFIG_EXT2_FS=y
CONFIG_EXT2_FS_XATTR=y
diff --git a/arch/arm/configs/msm8226-perf_defconfig b/arch/arm/configs/msm8226-perf_defconfig
index 3dd9c55..dd355af 100644
--- a/arch/arm/configs/msm8226-perf_defconfig
+++ b/arch/arm/configs/msm8226-perf_defconfig
@@ -272,8 +272,8 @@
CONFIG_INPUT_MISC=y
CONFIG_INPUT_UINPUT=y
CONFIG_INPUT_GPIO=m
-CONFIG_SERIAL_MSM_HSL=y
-CONFIG_SERIAL_MSM_HSL_CONSOLE=y
+#CONFIG_SERIAL_MSM_HSL is not set
+#CONFIG_SERIAL_MSM_HSL_CONSOLE is not set
CONFIG_DIAG_CHAR=y
CONFIG_HW_RANDOM=y
CONFIG_HW_RANDOM_MSM=y
@@ -304,6 +304,7 @@
CONFIG_WCD9306_CODEC=y
CONFIG_REGULATOR_FIXED_VOLTAGE=y
CONFIG_REGULATOR_FAN53555=y
+CONFIG_REGULATOR_MEM_ACC=y
CONFIG_REGULATOR_ONSEMI_NCP6335D=y
CONFIG_REGULATOR_STUB=y
CONFIG_REGULATOR_QPNP=y
@@ -396,6 +397,7 @@
CONFIG_MMC_PARANOID_SD_INIT=y
CONFIG_MMC_BLOCK_MINORS=32
CONFIG_MMC_TEST=m
+CONFIG_MMC_BLOCK_DEFERRED_RESUME=y
CONFIG_MMC_BLOCK_TEST=m
CONFIG_MMC_SDHCI=y
CONFIG_MMC_SDHCI_PLTFM=y
@@ -427,6 +429,7 @@
CONFIG_QPNP_VIBRATOR=y
CONFIG_QPNP_REVID=y
CONFIG_MSM_IOMMU_V1=y
+CONFIG_MSM_IOMMU_VBIF_CHECK=y
CONFIG_EXT2_FS=y
CONFIG_EXT2_FS_XATTR=y
CONFIG_EXT3_FS=y
diff --git a/arch/arm/configs/msm8226_defconfig b/arch/arm/configs/msm8226_defconfig
index 38171e6..5f008d5 100644
--- a/arch/arm/configs/msm8226_defconfig
+++ b/arch/arm/configs/msm8226_defconfig
@@ -306,6 +306,7 @@
CONFIG_WCD9306_CODEC=y
CONFIG_REGULATOR_FIXED_VOLTAGE=y
CONFIG_REGULATOR_FAN53555=y
+CONFIG_REGULATOR_MEM_ACC=y
CONFIG_REGULATOR_ONSEMI_NCP6335D=y
CONFIG_REGULATOR_STUB=y
CONFIG_REGULATOR_QPNP=y
@@ -333,6 +334,7 @@
CONFIG_MSMB_JPEG=y
CONFIG_MSM_VIDC_V4L2=y
CONFIG_MSM_WFD=y
+CONFIG_MSM_COMMON_LOG=y
# CONFIG_MEDIA_TUNER_SIMPLE is not set
# CONFIG_MEDIA_TUNER_TDA8290 is not set
# CONFIG_MEDIA_TUNER_TDA827X is not set
@@ -420,6 +422,7 @@
CONFIG_MMC_PARANOID_SD_INIT=y
CONFIG_MMC_BLOCK_MINORS=32
CONFIG_MMC_TEST=m
+CONFIG_MMC_BLOCK_DEFERRED_RESUME=y
CONFIG_MMC_BLOCK_TEST=m
CONFIG_MMC_SDHCI=y
CONFIG_MMC_SDHCI_PLTFM=y
@@ -451,6 +454,7 @@
CONFIG_QPNP_VIBRATOR=y
CONFIG_QPNP_REVID=y
CONFIG_MSM_IOMMU_V1=y
+CONFIG_MSM_IOMMU_VBIF_CHECK=y
CONFIG_CORESIGHT=y
CONFIG_CORESIGHT_FUSE=y
CONFIG_CORESIGHT_TMC=y
@@ -464,7 +468,7 @@
CONFIG_CORESIGHT_MODEM_ETM=y
CONFIG_CORESIGHT_WCN_ETM=y
CONFIG_CORESIGHT_RPM_ETM=y
-CONFIG_CORESIGHT_EVENT=m
+CONFIG_CORESIGHT_EVENT=y
CONFIG_EXT2_FS=y
CONFIG_EXT2_FS_XATTR=y
CONFIG_EXT3_FS=y
@@ -483,6 +487,7 @@
CONFIG_SCHEDSTATS=y
CONFIG_TIMER_STATS=y
CONFIG_DEBUG_KMEMLEAK=y
+CONFIG_DEBUG_KMEMLEAK_EARLY_LOG_SIZE=4000
CONFIG_DEBUG_KMEMLEAK_DEFAULT_OFF=y
CONFIG_DEBUG_SPINLOCK=y
CONFIG_DEBUG_MUTEXES=y
diff --git a/arch/arm/configs/msm8610-perf_defconfig b/arch/arm/configs/msm8610-perf_defconfig
index ede654d..55bc185 100644
--- a/arch/arm/configs/msm8610-perf_defconfig
+++ b/arch/arm/configs/msm8610-perf_defconfig
@@ -251,8 +251,8 @@
CONFIG_INPUT_UINPUT=y
CONFIG_INPUT_GPIO=m
# CONFIG_INPUT_MOUSEDEV is not set
-CONFIG_SERIAL_MSM_HSL=y
-CONFIG_SERIAL_MSM_HSL_CONSOLE=y
+#CONFIG_SERIAL_MSM_HSL is not set
+#CONFIG_SERIAL_MSM_HSL_CONSOLE is not set
CONFIG_DIAG_CHAR=y
CONFIG_HW_RANDOM=y
CONFIG_HW_RANDOM_MSM=y
@@ -345,6 +345,7 @@
CONFIG_USB=y
CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
CONFIG_USB_SUSPEND=y
+CONFIG_USB_MON=y
CONFIG_USB_ICE40_HCD=m
CONFIG_USB_CCID_BRIDGE=y
CONFIG_USB_STORAGE=y
@@ -361,6 +362,7 @@
CONFIG_MMC_PARANOID_SD_INIT=y
CONFIG_MMC_BLOCK_MINORS=32
CONFIG_MMC_TEST=m
+CONFIG_MMC_BLOCK_DEFERRED_RESUME=y
CONFIG_MMC_BLOCK_TEST=y
CONFIG_MMC_SDHCI=y
CONFIG_MMC_SDHCI_PLTFM=y
diff --git a/arch/arm/configs/msm8610_defconfig b/arch/arm/configs/msm8610_defconfig
index 4f60013..6874b28 100644
--- a/arch/arm/configs/msm8610_defconfig
+++ b/arch/arm/configs/msm8610_defconfig
@@ -315,6 +315,7 @@
CONFIG_MSMB_JPEG=y
CONFIG_MSM_VIDC_V4L2=y
CONFIG_MSM_WFD=y
+CONFIG_MSM_COMMON_LOG=y
# CONFIG_MEDIA_TUNER_SIMPLE is not set
# CONFIG_MEDIA_TUNER_TDA8290 is not set
# CONFIG_MEDIA_TUNER_TDA827X is not set
@@ -367,6 +368,7 @@
CONFIG_USB=y
CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
CONFIG_USB_SUSPEND=y
+CONFIG_USB_MON=y
CONFIG_USB_ICE40_HCD=m
CONFIG_USB_CCID_BRIDGE=y
CONFIG_USB_STORAGE=y
@@ -383,6 +385,7 @@
CONFIG_MMC_PARANOID_SD_INIT=y
CONFIG_MMC_BLOCK_MINORS=32
CONFIG_MMC_TEST=m
+CONFIG_MMC_BLOCK_DEFERRED_RESUME=y
CONFIG_MMC_BLOCK_TEST=y
CONFIG_MMC_SDHCI=y
CONFIG_MMC_SDHCI_PLTFM=y
@@ -430,7 +433,7 @@
CONFIG_CORESIGHT_MODEM_ETM=y
CONFIG_CORESIGHT_WCN_ETM=y
CONFIG_CORESIGHT_RPM_ETM=y
-CONFIG_CORESIGHT_EVENT=m
+CONFIG_CORESIGHT_EVENT=y
CONFIG_SENSORS=y
CONFIG_EXT2_FS=y
CONFIG_EXT2_FS_XATTR=y
@@ -450,6 +453,7 @@
CONFIG_SCHEDSTATS=y
CONFIG_TIMER_STATS=y
CONFIG_DEBUG_KMEMLEAK=y
+CONFIG_DEBUG_KMEMLEAK_EARLY_LOG_SIZE=4000
CONFIG_DEBUG_KMEMLEAK_DEFAULT_OFF=y
CONFIG_DEBUG_SPINLOCK=y
CONFIG_DEBUG_MUTEXES=y
diff --git a/arch/arm/configs/msm8974-perf_defconfig b/arch/arm/configs/msm8974-perf_defconfig
index 915b1c3..478e766 100755
--- a/arch/arm/configs/msm8974-perf_defconfig
+++ b/arch/arm/configs/msm8974-perf_defconfig
@@ -298,8 +298,8 @@
CONFIG_INPUT_HBTP_INPUT=y
CONFIG_INPUT_UINPUT=y
CONFIG_SERIAL_MSM_HS=y
-CONFIG_SERIAL_MSM_HSL=y
-CONFIG_SERIAL_MSM_HSL_CONSOLE=y
+#CONFIG_SERIAL_MSM_HSL is not set
+#CONFIG_SERIAL_MSM_HSL_CONSOLE is not set
CONFIG_DIAG_CHAR=y
CONFIG_HW_RANDOM=y
CONFIG_HW_RANDOM_MSM=y
@@ -343,10 +343,6 @@
CONFIG_VIDEO_V4L2_SUBDEV_API=y
CONFIG_DVB_CORE=m
# CONFIG_MSM_CAMERA is not set
-CONFIG_MT9M114=y
-CONFIG_OV2720=y
-CONFIG_IMX135=y
-CONFIG_IMX132=y
CONFIG_MSM_CAMERA_SENSOR=y
CONFIG_MSM_EEPROM=y
CONFIG_MSM_CPP=y
@@ -355,7 +351,6 @@
CONFIG_MSM_CSIPHY=y
CONFIG_MSM_CSID=y
CONFIG_MSM_ISPIF=y
-CONFIG_S5K3L1YX=y
CONFIG_MSMB_CAMERA=y
CONFIG_MSMB_JPEG=y
CONFIG_MSM_VIDC_V4L2=y
@@ -401,6 +396,7 @@
CONFIG_USB_EHCI_MSM=y
CONFIG_USB_EHCI_MSM_HSIC=y
CONFIG_USB_ACM=y
+CONFIG_USB_CCID_BRIDGE=y
CONFIG_USB_STORAGE=y
CONFIG_USB_STORAGE_DATAFAB=y
CONFIG_USB_STORAGE_FREECOM=y
@@ -463,6 +459,7 @@
CONFIG_QPNP_COINCELL=y
CONFIG_MSM_IOMMU_V1=y
CONFIG_IOMMU_PGTABLES_L2=y
+CONFIG_MSM_IOMMU_VBIF_CHECK=y
CONFIG_MOBICORE_SUPPORT=m
CONFIG_MOBICORE_API=m
CONFIG_BIF=y
diff --git a/arch/arm/configs/msm8974_defconfig b/arch/arm/configs/msm8974_defconfig
index e72e5ff..c5c16c2 100755
--- a/arch/arm/configs/msm8974_defconfig
+++ b/arch/arm/configs/msm8974_defconfig
@@ -124,6 +124,7 @@
CONFIG_PACKET=y
CONFIG_UNIX=y
CONFIG_XFRM_USER=y
+CONFIG_NET_KEY=y
CONFIG_INET=y
CONFIG_IP_ADVANCED_ROUTER=y
CONFIG_IP_MULTIPLE_TABLES=y
@@ -132,6 +133,7 @@
CONFIG_IP_PNP_DHCP=y
CONFIG_INET_AH=y
CONFIG_INET_ESP=y
+CONFIG_INET_IPCOMP=y
# CONFIG_INET_XFRM_MODE_BEET is not set
# CONFIG_INET_LRO is not set
CONFIG_IPV6=y
@@ -222,6 +224,9 @@
CONFIG_IP6_NF_RAW=y
CONFIG_BRIDGE_NF_EBTABLES=y
CONFIG_BRIDGE_EBT_BROUTE=y
+CONFIG_L2TP=y
+CONFIG_L2TP_DEBUGFS=y
+CONFIG_L2TP_V3=y
CONFIG_BRIDGE=y
CONFIG_NET_SCHED=y
CONFIG_NET_SCH_HTB=y
@@ -283,6 +288,18 @@
CONFIG_KS8851=m
# CONFIG_MSM_RMNET is not set
CONFIG_MSM_RMNET_BAM=y
+CONFIG_PPP=y
+CONFIG_PPP_BSDCOMP=y
+CONFIG_PPP_DEFLATE=y
+CONFIG_PPP_FILTER=y
+CONFIG_PPP_MPPE=y
+CONFIG_PPP_MULTILINK=y
+CONFIG_PPPOE=y
+CONFIG_PPPOL2TP=y
+CONFIG_PPPOLAC=y
+CONFIG_PPPOPNS=y
+CONFIG_PPP_ASYNC=y
+CONFIG_PPP_SYNC_TTY=y
CONFIG_SLIP=y
CONFIG_SLIP_COMPRESSED=y
CONFIG_SLIP_MODE_SLIP6=y
@@ -350,10 +367,6 @@
CONFIG_VIDEO_V4L2_SUBDEV_API=y
CONFIG_DVB_CORE=m
# CONFIG_MSM_CAMERA is not set
-CONFIG_MT9M114=y
-CONFIG_OV2720=y
-CONFIG_IMX135=y
-CONFIG_IMX132=y
CONFIG_MSM_CAMERA_SENSOR=y
CONFIG_MSM_EEPROM=y
CONFIG_MSM_CPP=y
@@ -362,7 +375,6 @@
CONFIG_MSM_CSIPHY=y
CONFIG_MSM_CSID=y
CONFIG_MSM_ISPIF=y
-CONFIG_S5K3L1YX=y
CONFIG_MSMB_CAMERA=y
CONFIG_MSMB_JPEG=y
CONFIG_MSM_VIDC_V4L2=y
@@ -408,6 +420,7 @@
CONFIG_USB_EHCI_MSM=y
CONFIG_USB_EHCI_MSM_HSIC=y
CONFIG_USB_ACM=y
+CONFIG_USB_CCID_BRIDGE=y
CONFIG_USB_STORAGE=y
CONFIG_USB_STORAGE_DATAFAB=y
CONFIG_USB_STORAGE_FREECOM=y
@@ -471,6 +484,7 @@
CONFIG_MSM_IOMMU_V1=y
CONFIG_MSM_IOMMU_PMON=y
CONFIG_IOMMU_PGTABLES_L2=y
+CONFIG_MSM_IOMMU_VBIF_CHECK=y
CONFIG_MOBICORE_SUPPORT=m
CONFIG_MOBICORE_API=m
CONFIG_CORESIGHT=y
@@ -487,7 +501,7 @@
CONFIG_CORESIGHT_MODEM_ETM=y
CONFIG_CORESIGHT_WCN_ETM=y
CONFIG_CORESIGHT_RPM_ETM=y
-CONFIG_CORESIGHT_EVENT=m
+CONFIG_CORESIGHT_EVENT=y
CONFIG_BIF=y
CONFIG_BIF_QPNP=y
CONFIG_EXT2_FS=y
@@ -510,6 +524,7 @@
CONFIG_SCHEDSTATS=y
CONFIG_TIMER_STATS=y
CONFIG_DEBUG_KMEMLEAK=y
+CONFIG_DEBUG_KMEMLEAK_EARLY_LOG_SIZE=4000
CONFIG_DEBUG_KMEMLEAK_DEFAULT_OFF=y
CONFIG_DEBUG_SPINLOCK=y
CONFIG_DEBUG_MUTEXES=y
diff --git a/arch/arm/configs/msm9625_defconfig b/arch/arm/configs/msm9625_defconfig
index d6811df..59057f0 100644
--- a/arch/arm/configs/msm9625_defconfig
+++ b/arch/arm/configs/msm9625_defconfig
@@ -298,7 +298,7 @@
CONFIG_CORESIGHT_AUDIO_ETM=y
CONFIG_CORESIGHT_MODEM_ETM=y
CONFIG_CORESIGHT_RPM_ETM=y
-CONFIG_CORESIGHT_EVENT=m
+CONFIG_CORESIGHT_EVENT=y
CONFIG_EXT3_FS=y
CONFIG_VFAT_FS=y
CONFIG_TMPFS=y
diff --git a/arch/arm/include/asm/cacheflush.h b/arch/arm/include/asm/cacheflush.h
index 0e8f4916..0fd3191 100644
--- a/arch/arm/include/asm/cacheflush.h
+++ b/arch/arm/include/asm/cacheflush.h
@@ -385,4 +385,12 @@
int set_memory_x(unsigned long addr, int numpages);
int set_memory_nx(unsigned long addr, int numpages);
+#ifdef CONFIG_FREE_PAGES_RDONLY
+#define mark_addr_rdonly(a) set_memory_ro((unsigned long)a, 1);
+#define mark_addr_rdwrite(a) set_memory_rw((unsigned long)a, 1);
+#else
+#define mark_addr_rdonly(a)
+#define mark_addr_rdwrite(a)
+#endif
+
#endif
diff --git a/arch/arm/kernel/arch_timer.c b/arch/arm/kernel/arch_timer.c
index 8c0a923..5a1ed75 100644
--- a/arch/arm/kernel/arch_timer.c
+++ b/arch/arm/kernel/arch_timer.c
@@ -493,8 +493,8 @@
clockevents_config_and_register(clk, arch_timer_rate,
0xf, 0x7fffffff);
- err = request_irq(arch_timer_spi, arch_timer_handler_mem, 0,
- "arch_timer", clk);
+ err = request_irq(arch_timer_spi, arch_timer_handler_mem,
+ IRQF_TIMER, "arch_timer", clk);
return err;
}
diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c
index c5fa883..4d24305 100644
--- a/arch/arm/kernel/process.c
+++ b/arch/arm/kernel/process.c
@@ -14,6 +14,7 @@
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/mm.h>
+#include <linux/vmalloc.h>
#include <linux/stddef.h>
#include <linux/unistd.h>
#include <linux/user.h>
@@ -378,7 +379,12 @@
printk("%04lx ", (unsigned long)p & 0xffff);
for (j = 0; j < 8; j++) {
u32 data;
- if (probe_kernel_address(p, data)) {
+ /*
+ * vmalloc addresses may point to
+ * memory-mapped peripherals
+ */
+ if (is_vmalloc_addr(p) ||
+ probe_kernel_address(p, data)) {
printk(" ********");
} else {
printk(" %08x", data);
diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c
index 5bca467..4a6e6db 100644
--- a/arch/arm/kernel/smp.c
+++ b/arch/arm/kernel/smp.c
@@ -696,6 +696,7 @@
void smp_send_reschedule(int cpu)
{
+ BUG_ON(cpu_is_offline(cpu));
smp_cross_call(cpumask_of(cpu), IPI_RESCHEDULE);
}
diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig
index b8444c1..6b551cd 100644
--- a/arch/arm/mach-msm/Kconfig
+++ b/arch/arm/mach-msm/Kconfig
@@ -3005,6 +3005,13 @@
help
Select if BLSP based UART Core v.14 or higher is present.
+config MSM_COMMON_LOG
+ bool "Use to export symbols for parsing memory dump"
+ help
+ Use this to export symbols to a memory dump table. This table
+ can be used by post analysis tools to extract information from
+ memory when device crashes.
+
config MSM_BOOT_STATS
bool "Use MSM boot stats reporting"
help
diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile
index 1c07e7d..5f7f0d9 100644
--- a/arch/arm/mach-msm/Makefile
+++ b/arch/arm/mach-msm/Makefile
@@ -100,6 +100,7 @@
obj-$(CONFIG_MACH_TROUT) += board-trout-rfkill.o
obj-$(CONFIG_MSM_BAM_DMUX) += bam_dmux.o
obj-$(CONFIG_MSM_SMD_LOGGING) += smem_log.o
+obj-$(CONFIG_MSM_COMMON_LOG) += common_log.o
obj-$(CONFIG_MSM_IPC_LOGGING) += ipc_logging.o
ifdef CONFIG_DEBUG_FS
obj-$(CONFIG_MSM_IPC_LOGGING) += ipc_logging_debug.o
diff --git a/arch/arm/mach-msm/batterydata-lib.c b/arch/arm/mach-msm/batterydata-lib.c
index 42a59d1..7ac7a45 100644
--- a/arch/arm/mach-msm/batterydata-lib.c
+++ b/arch/arm/mach-msm/batterydata-lib.c
@@ -34,32 +34,33 @@
return 0;
}
-static int interpolate_single_lut(struct single_row_lut *lut, int x)
+static int interpolate_single_lut_scaled(struct single_row_lut *lut,
+ int x, int scale)
{
int i, result;
- if (x < lut->x[0]) {
+ if (x < lut->x[0] * scale) {
pr_debug("x %d less than known range return y = %d lut = %pS\n",
x, lut->y[0], lut);
return lut->y[0];
}
- if (x > lut->x[lut->cols - 1]) {
+ if (x > lut->x[lut->cols - 1] * scale) {
pr_debug("x %d more than known range return y = %d lut = %pS\n",
x, lut->y[lut->cols - 1], lut);
return lut->y[lut->cols - 1];
}
for (i = 0; i < lut->cols; i++)
- if (x <= lut->x[i])
+ if (x <= lut->x[i] * scale)
break;
- if (x == lut->x[i]) {
+ if (x == lut->x[i] * scale) {
result = lut->y[i];
} else {
result = linear_interpolate(
lut->y[i - 1],
- lut->x[i - 1],
+ lut->x[i - 1] * scale,
lut->y[i],
- lut->x[i],
+ lut->x[i] * scale,
x);
}
return result;
@@ -67,9 +68,9 @@
int interpolate_fcc(struct single_row_lut *fcc_temp_lut, int batt_temp)
{
- /* batt_temp is in tenths of degC - convert it to degC for lookups */
- batt_temp = batt_temp/10;
- return interpolate_single_lut(fcc_temp_lut, batt_temp);
+ return interpolate_single_lut_scaled(fcc_temp_lut,
+ batt_temp,
+ DEGC_SCALE);
}
int interpolate_scalingfactor_fcc(struct single_row_lut *fcc_sf_lut,
@@ -80,7 +81,7 @@
* that case return 100%
*/
if (fcc_sf_lut)
- return interpolate_single_lut(fcc_sf_lut, cycles);
+ return interpolate_single_lut_scaled(fcc_sf_lut, cycles, 1);
else
return 100;
}
@@ -104,34 +105,34 @@
pr_debug("pc %d greater than known pc ranges for sfd\n", pc);
row1 = 0;
row2 = 0;
- }
- if (pc < sf_lut->percent[rows - 1]) {
+ } else if (pc < sf_lut->percent[rows - 1]) {
pr_debug("pc %d less than known pc ranges for sf\n", pc);
row1 = rows - 1;
row2 = rows - 1;
- }
- for (i = 0; i < rows; i++) {
- if (pc == sf_lut->percent[i]) {
- row1 = i;
- row2 = i;
- break;
- }
- if (pc > sf_lut->percent[i]) {
- row1 = i - 1;
- row2 = i;
- break;
+ } else {
+ for (i = 0; i < rows; i++) {
+ if (pc == sf_lut->percent[i]) {
+ row1 = i;
+ row2 = i;
+ break;
+ }
+ if (pc > sf_lut->percent[i]) {
+ row1 = i - 1;
+ row2 = i;
+ break;
+ }
}
}
- if (row_entry < sf_lut->row_entries[0])
- row_entry = sf_lut->row_entries[0];
- if (row_entry > sf_lut->row_entries[cols - 1])
- row_entry = sf_lut->row_entries[cols - 1];
+ if (row_entry < sf_lut->row_entries[0] * DEGC_SCALE)
+ row_entry = sf_lut->row_entries[0] * DEGC_SCALE;
+ if (row_entry > sf_lut->row_entries[cols - 1] * DEGC_SCALE)
+ row_entry = sf_lut->row_entries[cols - 1] * DEGC_SCALE;
for (i = 0; i < cols; i++)
- if (row_entry <= sf_lut->row_entries[i])
+ if (row_entry <= sf_lut->row_entries[i] * DEGC_SCALE)
break;
- if (row_entry == sf_lut->row_entries[i]) {
+ if (row_entry == sf_lut->row_entries[i] * DEGC_SCALE) {
scalefactor = linear_interpolate(
sf_lut->sf[row1][i],
sf_lut->percent[row1],
@@ -143,16 +144,16 @@
scalefactorrow1 = linear_interpolate(
sf_lut->sf[row1][i - 1],
- sf_lut->row_entries[i - 1],
+ sf_lut->row_entries[i - 1] * DEGC_SCALE,
sf_lut->sf[row1][i],
- sf_lut->row_entries[i],
+ sf_lut->row_entries[i] * DEGC_SCALE,
row_entry);
scalefactorrow2 = linear_interpolate(
sf_lut->sf[row2][i - 1],
- sf_lut->row_entries[i - 1],
+ sf_lut->row_entries[i - 1] * DEGC_SCALE,
sf_lut->sf[row2][i],
- sf_lut->row_entries[i],
+ sf_lut->row_entries[i] * DEGC_SCALE,
row_entry);
scalefactor = linear_interpolate(
@@ -167,7 +168,7 @@
/* get ocv given a soc -- reverse lookup */
int interpolate_ocv(struct pc_temp_ocv_lut *pc_temp_ocv,
- int batt_temp_degc, int pc)
+ int batt_temp, int pc)
{
int i, ocvrow1, ocvrow2, ocv, rows, cols;
int row1 = 0;
@@ -179,34 +180,34 @@
pr_debug("pc %d greater than known pc ranges for sfd\n", pc);
row1 = 0;
row2 = 0;
- }
- if (pc < pc_temp_ocv->percent[rows - 1]) {
+ } else if (pc < pc_temp_ocv->percent[rows - 1]) {
pr_debug("pc %d less than known pc ranges for sf\n", pc);
row1 = rows - 1;
row2 = rows - 1;
- }
- for (i = 0; i < rows; i++) {
- if (pc == pc_temp_ocv->percent[i]) {
- row1 = i;
- row2 = i;
- break;
- }
- if (pc > pc_temp_ocv->percent[i]) {
- row1 = i - 1;
- row2 = i;
- break;
+ } else {
+ for (i = 0; i < rows; i++) {
+ if (pc == pc_temp_ocv->percent[i]) {
+ row1 = i;
+ row2 = i;
+ break;
+ }
+ if (pc > pc_temp_ocv->percent[i]) {
+ row1 = i - 1;
+ row2 = i;
+ break;
+ }
}
}
- if (batt_temp_degc < pc_temp_ocv->temp[0])
- batt_temp_degc = pc_temp_ocv->temp[0];
- if (batt_temp_degc > pc_temp_ocv->temp[cols - 1])
- batt_temp_degc = pc_temp_ocv->temp[cols - 1];
+ if (batt_temp < pc_temp_ocv->temp[0] * DEGC_SCALE)
+ batt_temp = pc_temp_ocv->temp[0] * DEGC_SCALE;
+ if (batt_temp > pc_temp_ocv->temp[cols - 1] * DEGC_SCALE)
+ batt_temp = pc_temp_ocv->temp[cols - 1] * DEGC_SCALE;
for (i = 0; i < cols; i++)
- if (batt_temp_degc <= pc_temp_ocv->temp[i])
+ if (batt_temp <= pc_temp_ocv->temp[i] * DEGC_SCALE)
break;
- if (batt_temp_degc == pc_temp_ocv->temp[i]) {
+ if (batt_temp == pc_temp_ocv->temp[i] * DEGC_SCALE) {
ocv = linear_interpolate(
pc_temp_ocv->ocv[row1][i],
pc_temp_ocv->percent[row1],
@@ -218,17 +219,17 @@
ocvrow1 = linear_interpolate(
pc_temp_ocv->ocv[row1][i - 1],
- pc_temp_ocv->temp[i - 1],
+ pc_temp_ocv->temp[i - 1] * DEGC_SCALE,
pc_temp_ocv->ocv[row1][i],
- pc_temp_ocv->temp[i],
- batt_temp_degc);
+ pc_temp_ocv->temp[i] * DEGC_SCALE,
+ batt_temp);
ocvrow2 = linear_interpolate(
pc_temp_ocv->ocv[row2][i - 1],
- pc_temp_ocv->temp[i - 1],
+ pc_temp_ocv->temp[i - 1] * DEGC_SCALE,
pc_temp_ocv->ocv[row2][i],
- pc_temp_ocv->temp[i],
- batt_temp_degc);
+ pc_temp_ocv->temp[i] * DEGC_SCALE,
+ batt_temp);
ocv = linear_interpolate(
ocvrow1,
@@ -241,26 +242,26 @@
}
int interpolate_pc(struct pc_temp_ocv_lut *pc_temp_ocv,
- int batt_temp_degc, int ocv)
+ int batt_temp, int ocv)
{
int i, j, pcj, pcj_minus_one, pc;
int rows = pc_temp_ocv->rows;
int cols = pc_temp_ocv->cols;
- if (batt_temp_degc < pc_temp_ocv->temp[0]) {
- pr_debug("batt_temp %d < known temp range\n", batt_temp_degc);
- batt_temp_degc = pc_temp_ocv->temp[0];
+ if (batt_temp < pc_temp_ocv->temp[0] * DEGC_SCALE) {
+ pr_debug("batt_temp %d < known temp range\n", batt_temp);
+ batt_temp = pc_temp_ocv->temp[0] * DEGC_SCALE;
}
- if (batt_temp_degc > pc_temp_ocv->temp[cols - 1]) {
- pr_debug("batt_temp %d > known temp range\n", batt_temp_degc);
- batt_temp_degc = pc_temp_ocv->temp[cols - 1];
+ if (batt_temp > pc_temp_ocv->temp[cols - 1] * DEGC_SCALE) {
+ pr_debug("batt_temp %d > known temp range\n", batt_temp);
+ batt_temp = pc_temp_ocv->temp[cols - 1] * DEGC_SCALE;
}
for (j = 0; j < cols; j++)
- if (batt_temp_degc <= pc_temp_ocv->temp[j])
+ if (batt_temp <= pc_temp_ocv->temp[j] * DEGC_SCALE)
break;
- if (batt_temp_degc == pc_temp_ocv->temp[j]) {
+ if (batt_temp == pc_temp_ocv->temp[j] * DEGC_SCALE) {
/* found an exact match for temp in the table */
if (ocv >= pc_temp_ocv->ocv[0][j])
return pc_temp_ocv->percent[0];
@@ -282,7 +283,7 @@
}
/*
- * batt_temp_degc is within temperature for
+ * batt_temp is within temperature for
* column j-1 and j
*/
if (ocv >= pc_temp_ocv->ocv[0][j])
@@ -318,10 +319,10 @@
if (pcj && pcj_minus_one) {
pc = linear_interpolate(
pcj_minus_one,
- pc_temp_ocv->temp[j-1],
+ pc_temp_ocv->temp[j-1] * DEGC_SCALE,
pcj,
- pc_temp_ocv->temp[j],
- batt_temp_degc);
+ pc_temp_ocv->temp[j] * DEGC_SCALE,
+ batt_temp);
return pc;
}
}
@@ -333,6 +334,6 @@
return pcj_minus_one;
pr_debug("%d ocv wasn't found for temp %d in the LUT returning 100%%\n",
- ocv, batt_temp_degc);
+ ocv, batt_temp);
return 100;
}
diff --git a/arch/arm/mach-msm/board-8974.c b/arch/arm/mach-msm/board-8974.c
index 7b13bbc..a923846 100644
--- a/arch/arm/mach-msm/board-8974.c
+++ b/arch/arm/mach-msm/board-8974.c
@@ -149,8 +149,6 @@
"msm-tsens", NULL),
OF_DEV_AUXDATA("qcom,qcedev", 0xFD440000, \
"qcedev.0", NULL),
- OF_DEV_AUXDATA("qcom,qcrypto", 0xFD440000, \
- "qcrypto.0", NULL),
OF_DEV_AUXDATA("qcom,hsic-host", 0xF9A00000, \
"msm_hsic_host", NULL),
OF_DEV_AUXDATA("qcom,hsic-smsc-hub", 0, "msm_smsc_hub",
diff --git a/arch/arm/mach-msm/clock-8226.c b/arch/arm/mach-msm/clock-8226.c
index d1f8666..e16bcb1 100644
--- a/arch/arm/mach-msm/clock-8226.c
+++ b/arch/arm/mach-msm/clock-8226.c
@@ -3146,8 +3146,11 @@
CLK_LOOKUP("bus_clk", gcc_mss_q6_bimc_axi_clk.c, "fc880000.qcom,mss"),
CLK_LOOKUP("iface_clk", gcc_mss_cfg_ahb_clk.c, "fc880000.qcom,mss"),
CLK_LOOKUP("mem_clk", gcc_boot_rom_ahb_clk.c, "fc880000.qcom,mss"),
+
/* NFC */
- CLK_LOOKUP("ref_clk", cxo_d1_a_pin.c, "2-000e"),
+ CLK_LOOKUP("ref_clk", cxo_d1_a_pin.c, ""),
+ CLK_LOOKUP("ref_clk", cxo_d1_pin.c, "2-000e"),
+
/* PIL-PRONTO */
CLK_LOOKUP("xo", cxo_pil_pronto_clk.c, "fb21b000.qcom,pronto"),
@@ -3407,13 +3410,13 @@
/* MM sensor clocks */
CLK_LOOKUP("cam_src_clk", mclk0_clk_src.c, "6f.qcom,camera"),
- CLK_LOOKUP("cam_src_clk", mclk0_clk_src.c, "90.qcom,camera"),
+ CLK_LOOKUP("cam_src_clk", mclk1_clk_src.c, "90.qcom,camera"),
CLK_LOOKUP("cam_src_clk", mclk0_clk_src.c, "6d.qcom,camera"),
CLK_LOOKUP("cam_src_clk", mclk0_clk_src.c, "6a.qcom,camera"),
CLK_LOOKUP("cam_src_clk", mclk0_clk_src.c, "6c.qcom,camera"),
CLK_LOOKUP("cam_src_clk", mclk0_clk_src.c, "20.qcom,camera"),
CLK_LOOKUP("cam_clk", camss_mclk0_clk.c, "6f.qcom,camera"),
- CLK_LOOKUP("cam_clk", camss_mclk0_clk.c, "90.qcom,camera"),
+ CLK_LOOKUP("cam_clk", camss_mclk1_clk.c, "90.qcom,camera"),
CLK_LOOKUP("cam_clk", camss_mclk0_clk.c, "6d.qcom,camera"),
CLK_LOOKUP("cam_clk", camss_mclk0_clk.c, "6a.qcom,camera"),
CLK_LOOKUP("cam_clk", camss_mclk0_clk.c, "6c.qcom,camera"),
@@ -3494,8 +3497,36 @@
"fda08400.qcom,csid"),
/* ISPIF clocks */
+
CLK_LOOKUP("ispif_ahb_clk", camss_ispif_ahb_clk.c,
- "fda0a000.qcom,ispif"),
+ "fda0a000.qcom,ispif"),
+ CLK_LOOKUP("camss_top_ahb_clk", camss_top_ahb_clk.c,
+ "fda0a000.qcom,ispif"),
+ CLK_LOOKUP("csi0_ahb_clk", camss_csi0_ahb_clk.c,
+ "fda0a000.qcom,ispif"),
+ CLK_LOOKUP("csi0_src_clk", csi0_clk_src.c,
+ "fda0a000.qcom,ispif"),
+ CLK_LOOKUP("csi0_phy_clk", camss_csi0phy_clk.c,
+ "fda0a000.qcom,ispif"),
+ CLK_LOOKUP("csi0_clk", camss_csi0_clk.c,
+ "fda0a000.qcom,ispif"),
+ CLK_LOOKUP("csi0_pix_clk", camss_csi0pix_clk.c,
+ "fda0a000.qcom,ispif"),
+ CLK_LOOKUP("csi0_rdi_clk", camss_csi0rdi_clk.c,
+ "fda0a000.qcom,ispif"),
+
+ CLK_LOOKUP("csi1_ahb_clk", camss_csi1_ahb_clk.c,
+ "fda0a000.qcom,ispif"),
+ CLK_LOOKUP("csi1_src_clk", csi1_clk_src.c,
+ "fda0a000.qcom,ispif"),
+ CLK_LOOKUP("csi1_phy_clk", camss_csi1phy_clk.c,
+ "fda0a000.qcom,ispif"),
+ CLK_LOOKUP("csi1_clk", camss_csi1_clk.c,
+ "fda0a000.qcom,ispif"),
+ CLK_LOOKUP("csi1_pix_clk", camss_csi1pix_clk.c,
+ "fda0a000.qcom,ispif"),
+ CLK_LOOKUP("csi1_rdi_clk", camss_csi1rdi_clk.c,
+ "fda0a000.qcom,ispif"),
CLK_LOOKUP("camss_vfe_vfe_clk", camss_vfe_vfe0_clk.c,
"fda0a000.qcom,ispif"),
CLK_LOOKUP("camss_csi_vfe_clk", camss_csi_vfe0_clk.c,
diff --git a/arch/arm/mach-msm/clock-8974.c b/arch/arm/mach-msm/clock-8974.c
old mode 100755
new mode 100644
index 1771090..1847bf4
--- a/arch/arm/mach-msm/clock-8974.c
+++ b/arch/arm/mach-msm/clock-8974.c
@@ -1653,6 +1653,7 @@
};
static struct clk_freq_tbl ftbl_gcc_usb30_mock_utmi_clk[] = {
+ F(48000000, gpll0, 12.5, 0, 0),
F(60000000, gpll0, 10, 0, 0),
F_END
};
@@ -4870,13 +4871,7 @@
CLK_LOOKUP("gpll4", gpll4_clk_src.c, ""),
CLK_LOOKUP("sleep_clk", gcc_sdcc1_cdccal_sleep_clk.c, "msm_sdcc.1"),
CLK_LOOKUP("cal_clk", gcc_sdcc1_cdccal_ff_clk.c, "msm_sdcc.1"),
- CLK_LOOKUP("cam_src_clk", mclk0_clk_src.c, "6e.qcom,camera"),
- CLK_LOOKUP("cam_src_clk", mclk0_clk_src.c, "20.qcom,camera"),
- CLK_LOOKUP("cam_src_clk", mclk2_clk_src.c, "6c.qcom,camera"),
CLK_LOOKUP("cam_src_clk", mclk1_clk_src.c, "90.qcom,camera"),
- CLK_LOOKUP("cam_clk", camss_mclk0_clk.c, "6e.qcom,camera"),
- CLK_LOOKUP("cam_clk", camss_mclk0_clk.c, "20.qcom,camera"),
- CLK_LOOKUP("cam_clk", camss_mclk2_clk.c, "6c.qcom,camera"),
CLK_LOOKUP("cam_clk", camss_mclk1_clk.c, "90.qcom,camera"),
CLK_LOOKUP("cam_src_clk", mclk0_clk_src.c, "0.qcom,camera"),
CLK_LOOKUP("cam_src_clk", mclk1_clk_src.c, "1.qcom,camera"),
@@ -4887,20 +4882,14 @@
};
static struct clk_lookup msm_clocks_8974_only[] __initdata = {
- CLK_LOOKUP("cam_src_clk", mmss_gp0_clk_src.c, "6e.qcom,camera"),
- CLK_LOOKUP("cam_src_clk", mmss_gp0_clk_src.c, "20.qcom,camera"),
- CLK_LOOKUP("cam_src_clk", gp1_clk_src.c, "6c.qcom,camera"),
CLK_LOOKUP("cam_src_clk", mmss_gp1_clk_src.c, "90.qcom,camera"),
- CLK_LOOKUP("cam_clk", camss_gp0_clk.c, "6e.qcom,camera"),
- CLK_LOOKUP("cam_clk", camss_gp0_clk.c, "20.qcom,camera"),
- CLK_LOOKUP("cam_clk", gcc_gp1_clk.c, "6c.qcom,camera"),
CLK_LOOKUP("cam_clk", camss_gp1_clk.c, "90.qcom,camera"),
CLK_LOOKUP("cam_src_clk", mmss_gp0_clk_src.c, "0.qcom,camera"),
- CLK_LOOKUP("cam_src_clk", gp1_clk_src.c, "2.qcom,camera"),
CLK_LOOKUP("cam_src_clk", mmss_gp1_clk_src.c, "1.qcom,camera"),
+ CLK_LOOKUP("cam_src_clk", gp1_clk_src.c, "2.qcom,camera"),
CLK_LOOKUP("cam_clk", camss_gp0_clk.c, "0.qcom,camera"),
- CLK_LOOKUP("cam_clk", gcc_gp1_clk.c, "2.qcom,camera"),
CLK_LOOKUP("cam_clk", camss_gp1_clk.c, "1.qcom,camera"),
+ CLK_LOOKUP("cam_clk", gcc_gp1_clk.c, "2.qcom,camera"),
};
static struct clk_lookup msm_clocks_8974_common[] __initdata = {
@@ -4987,10 +4976,16 @@
CLK_LOOKUP("bus_clk", gcc_ce2_axi_clk.c, "qcedev.0"),
CLK_LOOKUP("core_clk_src", ce2_clk_src.c, "qcedev.0"),
- CLK_LOOKUP("core_clk", gcc_ce2_clk.c, "qcrypto.0"),
- CLK_LOOKUP("iface_clk", gcc_ce2_ahb_clk.c, "qcrypto.0"),
- CLK_LOOKUP("bus_clk", gcc_ce2_axi_clk.c, "qcrypto.0"),
- CLK_LOOKUP("core_clk_src", ce2_clk_src.c, "qcrypto.0"),
+
+ CLK_LOOKUP("core_clk", gcc_ce2_clk.c, "fd440000.qcom,qcrypto"),
+ CLK_LOOKUP("iface_clk", gcc_ce2_ahb_clk.c, "fd440000.qcom,qcrypto"),
+ CLK_LOOKUP("bus_clk", gcc_ce2_axi_clk.c, "fd440000.qcom,qcrypto"),
+ CLK_LOOKUP("core_clk_src", ce2_clk_src.c, "fd440000.qcom,qcrypto"),
+
+ CLK_LOOKUP("core_clk", gcc_ce2_clk.c, "fd440000.qcom,qcrypto1"),
+ CLK_LOOKUP("iface_clk", gcc_ce2_ahb_clk.c, "fd440000.qcom,qcrypto1"),
+ CLK_LOOKUP("bus_clk", gcc_ce2_axi_clk.c, "fd440000.qcom,qcrypto1"),
+ CLK_LOOKUP("core_clk_src", ce2_clk_src.c, "fd440000.qcom,qcrypto1"),
CLK_LOOKUP("core_clk", gcc_ce1_clk.c, "qseecom"),
CLK_LOOKUP("iface_clk", gcc_ce1_ahb_clk.c, "qseecom"),
@@ -5035,6 +5030,7 @@
CLK_LOOKUP("mem_clk", gcc_usb30_master_clk.c, "usb_bam"),
CLK_LOOKUP("mem_iface_clk", gcc_sys_noc_usb3_axi_clk.c, "usb_bam"),
CLK_LOOKUP("core_clk", gcc_usb30_master_clk.c, "msm_dwc3"),
+ CLK_LOOKUP("utmi_clk_src", usb30_mock_utmi_clk_src.c, "msm_dwc3"),
CLK_LOOKUP("utmi_clk", gcc_usb30_mock_utmi_clk.c, "msm_dwc3"),
CLK_LOOKUP("iface_clk", gcc_sys_noc_usb3_axi_clk.c, "msm_dwc3"),
CLK_LOOKUP("iface_clk", gcc_sys_noc_usb3_axi_clk.c, "msm_usb3"),
@@ -5766,6 +5762,8 @@
mdp_clk_src.c.fmax[VDD_DIG_NOMINAL] = 240000000;
+ gcc_usb30_mock_utmi_clk.max_div = 3;
+
/* The parent of each of the QUP I2C clocks is an RCG on V2 */
for (i = 0; i < ARRAY_SIZE(qup_i2c_clks); i++)
qup_i2c_clks[i][0]->parent = qup_i2c_clks[i][1];
diff --git a/arch/arm/mach-msm/clock-local2.h b/arch/arm/mach-msm/clock-local2.h
index 35fb6fa..fb0402d 100644
--- a/arch/arm/mach-msm/clock-local2.h
+++ b/arch/arm/mach-msm/clock-local2.h
@@ -103,7 +103,7 @@
const u32 bcr_reg;
int has_sibling;
u32 cur_div;
- const u32 max_div;
+ u32 max_div;
const u32 halt_check;
void *const __iomem *base;
};
diff --git a/arch/arm/mach-msm/clock-mdss-8974.c b/arch/arm/mach-msm/clock-mdss-8974.c
index 1fc7f1d..b63008f 100644
--- a/arch/arm/mach-msm/clock-mdss-8974.c
+++ b/arch/arm/mach-msm/clock-mdss-8974.c
@@ -1877,7 +1877,7 @@
if (vco_rate == 810000000) {
DSS_REG_W(mdss_edp_base, 0x0c, 0x18);
/* UNIPHY_PLL_LKDET_CFG2 */
- DSS_REG_W(mdss_edp_base, 0x64, 0x05);
+ DSS_REG_W(mdss_edp_base, 0x64, 0x0d);
/* UNIPHY_PLL_REFCLK_CFG */
DSS_REG_W(mdss_edp_base, 0x00, 0x00);
/* UNIPHY_PLL_SDM_CFG0 */
@@ -1899,7 +1899,7 @@
/* UNIPHY_PLL_SSC_CFG3 */
DSS_REG_W(mdss_edp_base, 0x58, 0x00);
/* UNIPHY_PLL_CAL_CFG0 */
- DSS_REG_W(mdss_edp_base, 0x6c, 0x0a);
+ DSS_REG_W(mdss_edp_base, 0x6c, 0x12);
/* UNIPHY_PLL_CAL_CFG2 */
DSS_REG_W(mdss_edp_base, 0x74, 0x01);
/* UNIPHY_PLL_CAL_CFG6 */
@@ -1924,7 +1924,7 @@
DSS_REG_W(mdss_edp_base, 0x28, 0x00);
} else if (vco_rate == 1350000000) {
/* UNIPHY_PLL_LKDET_CFG2 */
- DSS_REG_W(mdss_edp_base, 0x64, 0x05);
+ DSS_REG_W(mdss_edp_base, 0x64, 0x0d);
/* UNIPHY_PLL_REFCLK_CFG */
DSS_REG_W(mdss_edp_base, 0x00, 0x01);
/* UNIPHY_PLL_SDM_CFG0 */
@@ -1946,7 +1946,7 @@
/* UNIPHY_PLL_SSC_CFG3 */
DSS_REG_W(mdss_edp_base, 0x58, 0x00);
/* UNIPHY_PLL_CAL_CFG0 */
- DSS_REG_W(mdss_edp_base, 0x6c, 0x0a);
+ DSS_REG_W(mdss_edp_base, 0x6c, 0x12);
/* UNIPHY_PLL_CAL_CFG2 */
DSS_REG_W(mdss_edp_base, 0x74, 0x01);
/* UNIPHY_PLL_CAL_CFG6 */
diff --git a/arch/arm/mach-msm/common_log.c b/arch/arm/mach-msm/common_log.c
new file mode 100644
index 0000000..9491d9a
--- /dev/null
+++ b/arch/arm/mach-msm/common_log.c
@@ -0,0 +1,44 @@
+/* Copyright (c) 2014, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/kallsyms.h>
+#include <mach/msm_memory_dump.h>
+
+static void __init common_log_register(void)
+{
+ struct msm_client_dump dump;
+ char **log_bufp;
+ uint32_t *log_buf_lenp;
+
+ log_bufp = (char **)kallsyms_lookup_name("log_buf");
+ log_buf_lenp = (uint32_t *)kallsyms_lookup_name("log_buf_len");
+ if (!log_bufp || !log_buf_lenp) {
+ pr_err("common_log_register: Symbol log_buf not found!\n");
+ return;
+ }
+ dump.id = MSM_LOG_BUF;
+ dump.start_addr = virt_to_phys(*log_bufp);
+ dump.end_addr = virt_to_phys(*log_bufp + *log_buf_lenp);
+ if (msm_dump_table_register(&dump))
+ pr_err("common_log_register: Could not register log_bug.\n");
+}
+
+static int __init msm_common_log_init(void)
+{
+ common_log_register();
+ return 0;
+}
+late_initcall(msm_common_log_init);
diff --git a/arch/arm/mach-msm/cpr-regulator.c b/arch/arm/mach-msm/cpr-regulator.c
index d952f82..e50f865 100644
--- a/arch/arm/mach-msm/cpr-regulator.c
+++ b/arch/arm/mach-msm/cpr-regulator.c
@@ -96,6 +96,8 @@
/* RBCPR Result status Register */
#define REG_RBCPR_RESULT_0 0xA0
+#define RBCPR_RESULT0_BUSY_SHIFT 19
+#define RBCPR_RESULT0_BUSY_MASK BIT(RBCPR_RESULT0_BUSY_SHIFT)
#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)
@@ -181,6 +183,9 @@
int vdd_mx_vmin;
int vdd_mx_corner_map[CPR_FUSE_CORNER_MAX];
+ /* mem-acc regulator */
+ struct regulator *mem_acc_vreg;
+
/* CPR parameters */
u64 cpr_fuse_bits;
bool cpr_fuse_disable;
@@ -388,6 +393,22 @@
cpr_ctl_modify(cpr_vreg, RBCPR_CTL_LOOP_EN, 0);
}
+static bool cpr_ctl_is_enabled(struct cpr_regulator *cpr_vreg)
+{
+ u32 reg_val;
+
+ reg_val = cpr_read(cpr_vreg, REG_RBCPR_CTL);
+ return reg_val & RBCPR_CTL_LOOP_EN;
+}
+
+static bool cpr_ctl_is_busy(struct cpr_regulator *cpr_vreg)
+{
+ u32 reg_val;
+
+ reg_val = cpr_read(cpr_vreg, REG_RBCPR_RESULT_0);
+ return reg_val & RBCPR_RESULT0_BUSY_MASK;
+}
+
static void cpr_corner_save(struct cpr_regulator *cpr_vreg, int corner)
{
cpr_vreg->save_ctl[corner] = cpr_read(cpr_vreg, REG_RBCPR_CTL);
@@ -539,15 +560,15 @@
int new_apc_volt, enum voltage_change_dir dir)
{
int rc = 0, vdd_mx_vmin = 0;
+ int fuse_corner = cpr_vreg->corner_map[corner];
- /* 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 */
+ /* Determine the vdd_mx voltage */
+ if (dir != NO_CHANGE && cpr_vreg->vdd_mx != NULL)
vdd_mx_vmin = cpr_mx_get(cpr_vreg, corner, new_apc_volt);
- }
+
+ if (cpr_vreg->mem_acc_vreg && dir == DOWN)
+ rc = regulator_set_voltage(cpr_vreg->mem_acc_vreg,
+ fuse_corner, fuse_corner);
if (vdd_mx_vmin && dir == UP) {
if (vdd_mx_vmin != cpr_vreg->vdd_mx_vmin)
@@ -557,6 +578,10 @@
if (!rc)
rc = cpr_apc_set(cpr_vreg, new_apc_volt);
+ if (!rc && cpr_vreg->mem_acc_vreg && dir == UP)
+ rc = regulator_set_voltage(cpr_vreg->mem_acc_vreg,
+ fuse_corner, fuse_corner);
+
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);
@@ -732,7 +757,13 @@
cpr_debug_irq("IRQ_STATUS = 0x%02X\n", reg_val);
- if (!cpr_is_allowed(cpr_vreg)) {
+ if (!cpr_ctl_is_enabled(cpr_vreg)) {
+ cpr_debug_irq("CPR is disabled\n");
+ goto _exit;
+ } else if (cpr_ctl_is_busy(cpr_vreg)) {
+ cpr_debug_irq("CPR measurement is not ready\n");
+ goto _exit;
+ } else 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;
@@ -897,7 +928,6 @@
mutex_lock(&cpr_vreg->cpr_mutex);
cpr_ctl_disable(cpr_vreg);
- disable_irq(cpr_vreg->cpr_irq);
cpr_irq_clr(cpr_vreg);
@@ -917,7 +947,6 @@
cpr_vreg->is_cpr_suspended = false;
cpr_irq_clr(cpr_vreg);
- enable_irq(cpr_vreg->cpr_irq);
cpr_ctl_enable(cpr_vreg, cpr_vreg->corner);
mutex_unlock(&cpr_vreg->cpr_mutex);
@@ -1992,6 +2021,25 @@
return 0;
}
+static int cpr_mem_acc_init(struct platform_device *pdev,
+ struct cpr_regulator *cpr_vreg)
+{
+ int rc;
+
+ if (of_property_read_bool(pdev->dev.of_node, "mem-acc-supply")) {
+ cpr_vreg->mem_acc_vreg = devm_regulator_get(&pdev->dev,
+ "mem-acc");
+ if (IS_ERR_OR_NULL(cpr_vreg->mem_acc_vreg)) {
+ rc = PTR_RET(cpr_vreg->mem_acc_vreg);
+ if (rc != -EPROBE_DEFER)
+ pr_err("devm_regulator_get: mem-acc: rc=%d\n",
+ rc);
+ return rc;
+ }
+ }
+ return 0;
+}
+
static int __devinit cpr_regulator_probe(struct platform_device *pdev)
{
struct cpr_regulator *cpr_vreg;
@@ -2022,6 +2070,12 @@
return -ENOMEM;
}
+ rc = cpr_mem_acc_init(pdev, cpr_vreg);
+ if (rc) {
+ pr_err("mem_acc intialization error rc=%d\n", rc);
+ return rc;
+ }
+
rc = cpr_efuse_init(pdev, cpr_vreg);
if (rc) {
pr_err("Wrong eFuse address specified: rc=%d\n", rc);
diff --git a/arch/arm/mach-msm/include/mach/iommu.h b/arch/arm/mach-msm/include/mach/iommu.h
index c4db727..0f69a7b 100644
--- a/arch/arm/mach-msm/include/mach/iommu.h
+++ b/arch/arm/mach-msm/include/mach/iommu.h
@@ -86,6 +86,7 @@
* struct msm_iommu_drvdata - A single IOMMU hardware instance
* @base: IOMMU config port base address (VA)
* @glb_base: IOMMU config port base address for global register space (VA)
+ * @phys_base: IOMMU physical base address.
* @ncb The number of contexts on this IOMMU
* @irq: Interrupt number
* @clk: The bus clock for this IOMMU hardware instance
@@ -108,6 +109,7 @@
*/
struct msm_iommu_drvdata {
void __iomem *base;
+ phys_addr_t phys_base;
void __iomem *glb_base;
int ncb;
int ttbr_split;
diff --git a/arch/arm/mach-msm/include/mach/iommu_hw-v1.h b/arch/arm/mach-msm/include/mach/iommu_hw-v1.h
index 04cd441..4509092 100644
--- a/arch/arm/mach-msm/include/mach/iommu_hw-v1.h
+++ b/arch/arm/mach-msm/include/mach/iommu_hw-v1.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -16,17 +16,19 @@
#define CTX_SHIFT 12
#define CTX_OFFSET 0x8000
-#define GET_GLOBAL_REG(reg, base) (readl_relaxed((base) + (reg)))
-#define GET_CTX_REG(reg, base, ctx) \
- (readl_relaxed((base) + CTX_OFFSET + (reg) + ((ctx) << CTX_SHIFT)))
-#define GET_CTX_REG_L(reg, base, ctx) \
- (readll_relaxed((base) + CTX_OFFSET + (reg) + ((ctx) << CTX_SHIFT)))
+#define CTX_REG(reg, base, ctx) \
+ ((base) + CTX_OFFSET + (reg) + ((ctx) << CTX_SHIFT))
+#define GLB_REG(reg, base) \
+ ((base) + (reg))
+
+#define GET_GLOBAL_REG(reg, base) (readl_relaxed(GLB_REG(reg, base)))
+#define GET_CTX_REG(reg, base, ctx) (readl_relaxed(CTX_REG(reg, base, ctx)))
+#define GET_CTX_REG_L(reg, base, ctx) (readll_relaxed(CTX_REG(reg, base, ctx)))
#define SET_GLOBAL_REG(reg, base, val) writel_relaxed((val), ((base) + (reg)))
#define SET_CTX_REG(reg, base, ctx, val) \
- writel_relaxed((val), \
- ((base) + CTX_OFFSET + (reg) + ((ctx) << CTX_SHIFT)))
+ writel_relaxed((val), (CTX_REG(reg, base, ctx)))
/* Wrappers for numbered registers */
#define SET_GLOBAL_REG_N(b, n, r, v) SET_GLOBAL_REG((b), ((r) + (n << 2)), (v))
@@ -150,6 +152,11 @@
SET_GLOBAL_FIELD(b, MICRO_MMU_CTRL, HALT_REQ, v)
#define GET_MICRO_MMU_CTRL_IDLE(b) \
GET_GLOBAL_FIELD(b, MICRO_MMU_CTRL, IDLE)
+#define SET_MICRO_MMU_CTRL_RESERVED(b, v) \
+ SET_GLOBAL_FIELD(b, MICRO_MMU_CTRL, RESERVED, v)
+
+#define MMU_CTRL_IDLE (MICRO_MMU_CTRL_IDLE_MASK << MICRO_MMU_CTRL_IDLE_SHIFT)
+
#define SET_PREDICTIONDIS0(b, v) SET_GLOBAL_REG(PREDICTIONDIS0, (b), (v))
#define SET_PREDICTIONDIS1(b, v) SET_GLOBAL_REG(PREDICTIONDIS1, (b), (v))
#define SET_S1L1BFBLP0(b, v) SET_GLOBAL_REG(S1L1BFBLP0, (b), (v))
diff --git a/arch/arm/mach-msm/include/mach/msm-krait-l2-accessors.h b/arch/arm/mach-msm/include/mach/msm-krait-l2-accessors.h
index 45d000b..5e927cf 100644
--- a/arch/arm/mach-msm/include/mach/msm-krait-l2-accessors.h
+++ b/arch/arm/mach-msm/include/mach/msm-krait-l2-accessors.h
@@ -1,8 +1,5 @@
-#ifndef __ASM_ARCH_MSM_MSM_KRAIT_L2_ACCESSORS_H
-#define __ASM_ARCH_MSM_MSM_KRAIT_L2_ACCESSORS_H
-
/*
- * Copyright (c) 2011-2013 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-2014 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
@@ -14,6 +11,9 @@
* GNU General Public License for more details.
*/
+#ifndef __ASM_ARCH_MSM_MSM_KRAIT_L2_ACCESSORS_H
+#define __ASM_ARCH_MSM_MSM_KRAIT_L2_ACCESSORS_H
+
#define MAX_L2_PERIOD ((1ULL << 32) - 1)
#define MAX_KRAIT_L2_CTRS 10
@@ -66,17 +66,12 @@
#ifdef CONFIG_ARCH_MSM_KRAIT
extern void set_l2_indirect_reg(u32 reg_addr, u32 val);
extern u32 get_l2_indirect_reg(u32 reg_addr);
-extern u32 set_get_l2_indirect_reg(u32 reg_addr, u32 val);
#else
static inline void set_l2_indirect_reg(u32 reg_addr, u32 val) {}
static inline u32 get_l2_indirect_reg(u32 reg_addr)
{
return 0;
}
-static inline u32 set_get_l2_indirect_reg(u32 reg_addr, u32 val)
-{
- return 0;
-}
#endif
#endif
diff --git a/arch/arm/mach-msm/include/mach/msm_memory_dump.h b/arch/arm/mach-msm/include/mach/msm_memory_dump.h
index 89df485..13f54bf 100644
--- a/arch/arm/mach-msm/include/mach/msm_memory_dump.h
+++ b/arch/arm/mach-msm/include/mach/msm_memory_dump.h
@@ -27,6 +27,7 @@
MSM_ETM3_REG,
MSM_TMC0_REG, /* TMC_ETR */
MSM_TMC1_REG, /* TMC_ETF */
+ MSM_LOG_BUF,
MAX_NUM_CLIENTS,
};
diff --git a/arch/arm/mach-msm/include/mach/msm_rtb.h b/arch/arm/mach-msm/include/mach/msm_rtb.h
index 19d171e..b33e8b6 100644
--- a/arch/arm/mach-msm/include/mach/msm_rtb.h
+++ b/arch/arm/mach-msm/include/mach/msm_rtb.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012, 2014 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
@@ -25,6 +25,8 @@
LOGK_HOTPLUG = 4,
LOGK_CTXID = 5,
LOGK_TIMESTAMP = 6,
+ LOGK_L2CPREAD = 7,
+ LOGK_L2CPWRITE = 8,
};
#define LOGTYPE_NOPC 0x80
diff --git a/arch/arm/mach-msm/include/mach/msm_smem.h b/arch/arm/mach-msm/include/mach/msm_smem.h
index 19f9c0e..670efe6 100644
--- a/arch/arm/mach-msm/include/mach/msm_smem.h
+++ b/arch/arm/mach-msm/include/mach/msm_smem.h
@@ -40,6 +40,17 @@
#define SMEM_NUM_SMD_STREAM_CHANNELS 64
+/**
+ * OVERFLOW_ADD_UNSIGNED() - check for unsigned overflow
+ *
+ * @type: type to check for overflow
+ * @a: left value to use
+ * @b: right value to use
+ * @returns: true if a + b will result in overflow; false otherwise
+ */
+#define OVERFLOW_ADD_UNSIGNED(type, a, b) \
+ (((type)~0 - (a)) < (b) ? true : false)
+
enum {
/* fixed items */
SMEM_PROC_COMM = 0,
diff --git a/arch/arm/mach-msm/include/mach/msm_spi.h b/arch/arm/mach-msm/include/mach/msm_spi.h
index 608927c..52d88a1 100644
--- a/arch/arm/mach-msm/include/mach/msm_spi.h
+++ b/arch/arm/mach-msm/include/mach/msm_spi.h
@@ -22,6 +22,9 @@
* runtime pm (optimizes for power).
* @master_id master id number of the controller's wrapper (BLSP or GSBI).
* When zero, clock path voting is disabled.
+ * @rt when set, spi will pump transaction messages with high (realtime)
+ * priority to reduce the transfer latency on the bus by minimising
+ * the delay between a transfer request.
*/
struct msm_spi_platform_data {
u32 max_clock_speed;
@@ -37,4 +40,5 @@
bool use_bam;
u32 bam_consumer_pipe_index;
u32 bam_producer_pipe_index;
+ bool rt_priority;
};
diff --git a/arch/arm/mach-msm/include/mach/ocmem_priv.h b/arch/arm/mach-msm/include/mach/ocmem_priv.h
index 00aedb6..c94fa0e 100644
--- a/arch/arm/mach-msm/include/mach/ocmem_priv.h
+++ b/arch/arm/mach-msm/include/mach/ocmem_priv.h
@@ -206,7 +206,7 @@
int check_notifier(int);
const char *get_name(int);
int get_tz_id(int);
-int ocmem_enable_sec_program(int);
+int ocmem_restore_sec_program(int);
int ocmem_enable_dump(enum ocmem_client, unsigned long, unsigned long);
int ocmem_disable_dump(enum ocmem_client, unsigned long, unsigned long);
int check_id(int);
diff --git a/arch/arm/mach-msm/include/mach/qcrypto.h b/arch/arm/mach-msm/include/mach/qcrypto.h
index bb8048f..f9608b3 100644
--- a/arch/arm/mach-msm/include/mach/qcrypto.h
+++ b/arch/arm/mach-msm/include/mach/qcrypto.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2014, 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,6 +24,11 @@
#define QCRYPTO_CTX_XTS_DU_SIZE_512B 0x00000100
#define QCRYPTO_CTX_XTS_DU_SIZE_1KB 0x00000200
+
+int qcrypto_cipher_set_device(struct ablkcipher_request *req, unsigned int dev);
+int qcrypto_ahash_set_device(struct ahash_request *req, unsigned int dev);
+int qcrypto_aead_set_device(struct aead_request *req, unsigned int dev);
+
int qcrypto_cipher_set_flag(struct ablkcipher_request *req, unsigned int flags);
int qcrypto_ahash_set_flag(struct ahash_request *req, unsigned int flags);
int qcrypto_aead_set_flag(struct aead_request *req, unsigned int flags);
diff --git a/arch/arm/mach-msm/include/mach/qdsp6v2/apr.h b/arch/arm/mach-msm/include/mach/qdsp6v2/apr.h
index c21f6e5..9f4acca 100644
--- a/arch/arm/mach-msm/include/mach/qdsp6v2/apr.h
+++ b/arch/arm/mach-msm/include/mach/qdsp6v2/apr.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2010-2014, 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
@@ -94,7 +94,7 @@
#define APR_SVC_SRD 0x7
/* APR Port IDs */
-#define APR_MAX_PORTS 0x40
+#define APR_MAX_PORTS 0x80
#define APR_NAME_MAX 0x40
diff --git a/arch/arm/mach-msm/krait-regulator-pmic.c b/arch/arm/mach-msm/krait-regulator-pmic.c
index 5081e7b..2f4185e 100644
--- a/arch/arm/mach-msm/krait-regulator-pmic.c
+++ b/arch/arm/mach-msm/krait-regulator-pmic.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2014, 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
@@ -58,7 +58,8 @@
#define REG_GANG_CTL2 0xC1
#define GANG_EN_BIT BIT(7)
-#define REG_PWM_CL 0x60
+#define REG_PWM_CL 0x60
+#define REG_SEC_ACCESS 0xD0
struct krait_vreg_pmic_chip {
struct spmi_device *spmi;
@@ -89,11 +90,21 @@
return 0;
}
-static int write_byte(struct spmi_device *spmi, u16 addr, u8 *val)
+static int write_secure_byte(struct spmi_device *spmi, u16 base,
+ u16 addr, u8 *val)
{
int rc;
+ u8 sec_val = 0xA5;
- rc = spmi_ext_register_writel(spmi->ctrl, spmi->sid, addr, val, 1);
+ rc = spmi_ext_register_writel(spmi->ctrl, spmi->sid,
+ base + REG_SEC_ACCESS, &sec_val, 1);
+ if (rc) {
+ pr_err("SPMI write failed [%d,0x%04x] val = 0x%02x rc=%d\n",
+ spmi->sid, base + REG_SEC_ACCESS, sec_val, rc);
+ return rc;
+ }
+ rc = spmi_ext_register_writel(spmi->ctrl, spmi->sid,
+ base + addr, val, 1);
if (rc) {
pr_err("SPMI write failed [%d,0x%04x] val = 0x%02x rc=%d\n",
spmi->sid, addr, *val, rc);
@@ -127,7 +138,7 @@
bool krait_pmic_is_ready(void)
{
if (the_chip == NULL) {
- pr_debug("kait_regulator_pmic not ready yet\n");
+ pr_debug("krait_regulator_pmic not ready yet\n");
return false;
}
return true;
@@ -149,7 +160,7 @@
int rc;
if (the_chip == NULL) {
- pr_debug("kait_regulator_pmic not ready yet\n");
+ pr_debug("krait_regulator_pmic not ready yet\n");
return -ENXIO;
}
@@ -157,8 +168,8 @@
return 0;
setpoint = (I_PFM_MA - IOFFSET_MA) / ISTEP_MA;
- rc = write_byte(the_chip->spmi,
- the_chip->ps_base + REG_PWM_CL, &setpoint);
+ rc = write_secure_byte(the_chip->spmi,
+ the_chip->ps_base, REG_PWM_CL, &setpoint);
pr_debug("wrote 0x%02x->[%d 0x%04x] rc = %d\n", setpoint,
the_chip->spmi->sid,
the_chip->ps_base + REG_PWM_CL, rc);
@@ -180,7 +191,7 @@
int rc;
if (the_chip == NULL) {
- pr_debug("kait_regulator_pmic not ready yet\n");
+ pr_debug("krait_regulator_pmic not ready yet\n");
return -ENXIO;
}
@@ -190,8 +201,8 @@
udelay(50);
setpoint = (I_PWM_MA - IOFFSET_MA) / ISTEP_MA;
- rc = write_byte(the_chip->spmi,
- the_chip->ps_base + REG_PWM_CL, &setpoint);
+ rc = write_secure_byte(the_chip->spmi,
+ the_chip->ps_base, REG_PWM_CL, &setpoint);
pr_debug("wrote 0x%02x->[%d 0x%04x] rc = %d\n", setpoint,
the_chip->spmi->sid,
the_chip->ps_base + REG_PWM_CL, rc);
diff --git a/arch/arm/mach-msm/krait-regulator.c b/arch/arm/mach-msm/krait-regulator.c
index a291b90..b0a1a7c 100644
--- a/arch/arm/mach-msm/krait-regulator.c
+++ b/arch/arm/mach-msm/krait-regulator.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2014, 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
@@ -708,8 +708,10 @@
static int switch_to_using_ldo(struct krait_power_vreg *kvreg)
{
- if (kvreg->mode == LDO_MODE
- && get_krait_ldo_uv(kvreg) == kvreg->uV - kvreg->ldo_delta_uV)
+ int uV = kvreg->uV - kvreg->ldo_delta_uV;
+ int ldo_uV = DIV_ROUND_UP(uV, KRAIT_LDO_STEP) * KRAIT_LDO_STEP;
+
+ if (kvreg->mode == LDO_MODE && get_krait_ldo_uv(kvreg) == ldo_uV)
return 0;
return smp_call_function_single(kvreg->cpu_num,
diff --git a/arch/arm/mach-msm/mpm-of.c b/arch/arm/mach-msm/mpm-of.c
index 1fbd077..f1bf64f 100644
--- a/arch/arm/mach-msm/mpm-of.c
+++ b/arch/arm/mach-msm/mpm-of.c
@@ -531,6 +531,7 @@
void msm_mpm_exit_sleep(bool from_idle)
{
unsigned long pending;
+ uint32_t *enabled_intr;
int i;
int k;
@@ -539,12 +540,16 @@
return;
}
+ enabled_intr = from_idle ? msm_mpm_enabled_irq :
+ msm_mpm_wake_irq;
+
for (i = 0; i < MSM_MPM_REG_WIDTH; i++) {
pending = msm_mpm_read(MSM_MPM_REG_STATUS, i);
+ pending &= enabled_intr[i];
if (MSM_MPM_DEBUG_PENDING_IRQ & msm_mpm_debug_mask)
- pr_info("%s: pending.%d: 0x%08lx", __func__,
- i, pending);
+ pr_info("%s: enabled_intr pending.%d: 0x%08x 0x%08lx\n",
+ __func__, i, enabled_intr[i], pending);
k = find_first_bit(&pending, 32);
while (k < 32) {
@@ -678,7 +683,8 @@
return -ENXIO;
}
ret = devm_request_irq(&pdev->dev, dev->mpm_ipc_irq, msm_mpm_irq,
- IRQF_TRIGGER_RISING, pdev->name, msm_mpm_irq);
+ IRQF_TRIGGER_RISING | IRQF_NO_SUSPEND, pdev->name,
+ msm_mpm_irq);
if (ret) {
pr_info("%s(): request_irq failed errno: %d\n", __func__, ret);
diff --git a/arch/arm/mach-msm/msm-krait-l2-accessors.c b/arch/arm/mach-msm/msm-krait-l2-accessors.c
index 7498e7f..0faf112 100644
--- a/arch/arm/mach-msm/msm-krait-l2-accessors.c
+++ b/arch/arm/mach-msm/msm-krait-l2-accessors.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-2012, 2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -13,38 +13,19 @@
#include <linux/spinlock.h>
#include <linux/module.h>
+#include <mach/msm_rtb.h>
#include <asm/mach-types.h>
#include <asm/cputype.h>
DEFINE_RAW_SPINLOCK(l2_access_lock);
-u32 set_get_l2_indirect_reg(u32 reg_addr, u32 val)
-{
- unsigned long flags;
- u32 ret_val;
-
- raw_spin_lock_irqsave(&l2_access_lock, flags);
- mb();
- asm volatile ("mcr p15, 3, %[l2cpselr], c15, c0, 6\n\t"
- "isb\n\t"
- "mcr p15, 3, %[l2cpdr], c15, c0, 7\n\t"
- "isb\n\t"
- "mrc p15, 3, %[l2cpdr_read], c15, c0, 7\n\t"
- : [l2cpdr_read]"=r" (ret_val)
- : [l2cpselr]"r" (reg_addr), [l2cpdr]"r" (val)
- );
- raw_spin_unlock_irqrestore(&l2_access_lock, flags);
-
- return ret_val;
-}
-EXPORT_SYMBOL(set_get_l2_indirect_reg);
-
void set_l2_indirect_reg(u32 reg_addr, u32 val)
{
unsigned long flags;
raw_spin_lock_irqsave(&l2_access_lock, flags);
mb();
+ uncached_logk(LOGK_L2CPWRITE, (void *)reg_addr);
asm volatile ("mcr p15, 3, %[l2cpselr], c15, c0, 6\n\t"
"isb\n\t"
"mcr p15, 3, %[l2cpdr], c15, c0, 7\n\t"
@@ -62,6 +43,7 @@
unsigned long flags;
raw_spin_lock_irqsave(&l2_access_lock, flags);
+ uncached_logk(LOGK_L2CPREAD, (void *)reg_addr);
asm volatile ("mcr p15, 3, %[l2cpselr], c15, c0, 6\n\t"
"isb\n\t"
"mrc p15, 3, %[l2cpdr], c15, c0, 7\n\t"
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 5747f79..8cb3cf3 100644
--- a/arch/arm/mach-msm/msm_bus/msm_bus_bimc.c
+++ b/arch/arm/mach-msm/msm_bus/msm_bus_bimc.c
@@ -1828,6 +1828,53 @@
set_qos_mode(baddr, mas_index, 0, 0, 0);
}
+static void bimc_set_static_qos_bw(struct msm_bus_bimc_info *binfo,
+ int mport, struct msm_bus_bimc_qos_bw *qbw)
+{
+ int32_t bw_mbps, thh = 0, thm, thl, gc;
+ int32_t gp;
+ u64 temp;
+
+ if (binfo->qos_freq == 0) {
+ MSM_BUS_DBG("Zero QoS Frequency\n");
+ return;
+ }
+
+ if (!(qbw->bw && qbw->ws)) {
+ MSM_BUS_DBG("No QoS Bandwidth or Window size\n");
+ return;
+ }
+
+ /* Convert bandwidth to MBPS */
+ temp = qbw->bw;
+ bimc_div(&temp, 1000000);
+ bw_mbps = temp;
+
+ /* Grant period in clock cycles
+ * Grant period from bandwidth structure
+ * is in nano seconds, QoS freq is in KHz.
+ * Divide by 1000 to get clock cycles */
+ gp = (binfo->qos_freq * qbw->gp) / (1000 * NSEC_PER_USEC);
+
+ /* Grant count = BW in MBps * Grant period
+ * in micro seconds */
+ gc = bw_mbps * (qbw->gp / NSEC_PER_USEC);
+
+ /* Medium threshold = -((Medium Threshold percentage *
+ * Grant count) / 100) */
+ thm = -((qbw->thmp * gc) / 100);
+ qbw->thm = thm;
+
+ /* Low threshold = -(Grant count) */
+ thl = -gc;
+ qbw->thl = thl;
+
+ MSM_BUS_DBG("%s: BKE parameters: gp %d, gc %d, thm %d thl %d thh %d",
+ __func__, gp, gc, thm, thl, thh);
+
+ set_qos_bw_regs(binfo->base, mport, thh, thm, thl, gp, gc);
+}
+
static void msm_bus_bimc_config_master(
struct msm_bus_fabric_registration *fab_pdata,
struct msm_bus_inode_info *info,
@@ -1835,6 +1882,7 @@
{
int mode, i, ports;
struct msm_bus_bimc_info *binfo;
+ uint64_t bw = 0;
binfo = (struct msm_bus_bimc_info *)fab_pdata->hw_data;
ports = info->node_info->num_mports;
@@ -1843,11 +1891,17 @@
* Here check the details of dual configuration.
* Take actions based on different modes.
* Check for threshold if limiter mode, etc.
- */
- if (req_clk > info->node_info->th)
- mode = info->node_info->mode_thresh;
- else
+ */
+
+ if (req_clk <= info->node_info->th[0]) {
mode = info->node_info->mode;
+ bw = info->node_info->bimc_bw[0];
+ } else if ((info->node_info->num_thresh > 1) &&
+ (req_clk <= info->node_info->th[1])) {
+ mode = info->node_info->mode;
+ bw = info->node_info->bimc_bw[1];
+ } else
+ mode = info->node_info->mode_thresh;
switch (mode) {
case BIMC_QOS_MODE_BYPASS:
@@ -1858,9 +1912,24 @@
break;
case BIMC_QOS_MODE_REGULATOR:
case BIMC_QOS_MODE_LIMITER:
- for (i = 0; i < ports; i++)
+ for (i = 0; i < ports; i++) {
+ /* If not in fixed mode, update bandwidth */
+ if ((info->node_info->cur_lim_bw != bw)
+ && (mode != BIMC_QOS_MODE_FIXED)) {
+ struct msm_bus_bimc_qos_bw qbw;
+ qbw.ws = info->node_info->ws;
+ qbw.bw = bw;
+ qbw.gp = info->node_info->bimc_gp;
+ qbw.thmp = info->node_info->bimc_thmp;
+ bimc_set_static_qos_bw(binfo,
+ info->node_info->qport[i], &qbw);
+ info->node_info->cur_lim_bw = bw;
+ MSM_BUS_DBG("%s: Qos is %d reqclk %llu bw %llu",
+ __func__, mode, req_clk, bw);
+ }
bke_switch(binfo->base, info->node_info->qport[i],
BKE_ON, mode);
+ }
break;
default:
break;
@@ -1964,52 +2033,6 @@
return 0;
}
-static void bimc_set_static_qos_bw(struct msm_bus_bimc_info *binfo,
- int mport, struct msm_bus_bimc_qos_bw *qbw)
-{
- int32_t bw_mbps, thh = 0, thm, thl, gc;
- int32_t gp;
- u64 temp;
-
- if (binfo->qos_freq == 0) {
- MSM_BUS_DBG("Zero QoS Frequency\n");
- return;
- }
-
- if (!(qbw->bw && qbw->ws)) {
- MSM_BUS_DBG("No QoS Bandwidth or Window size\n");
- return;
- }
-
- /* Convert bandwidth to MBPS */
- temp = qbw->bw;
- bimc_div(&temp, 1000000);
- bw_mbps = temp;
-
- /* Grant period in clock cycles
- * Grant period from bandwidth structure
- * is in nano seconds, QoS freq is in KHz.
- * Divide by 1000 to get clock cycles */
- gp = (binfo->qos_freq * qbw->gp) / (1000 * NSEC_PER_USEC);
-
- /* Grant count = BW in MBps * Grant period
- * in micro seconds */
- gc = bw_mbps * (qbw->gp / NSEC_PER_USEC);
-
- /* Medium threshold = -((Medium Threshold percentage *
- * Grant count) / 100) */
- thm = -((qbw->thmp * gc) / 100);
- qbw->thm = thm;
-
- /* Low threshold = -(Grant count) */
- thl = -gc;
- qbw->thl = thl;
-
- MSM_BUS_DBG("%s: BKE parameters: gp %d, gc %d, thm %d thl %d thh %d",
- __func__, gp, gc, thm, thl, thh);
-
- set_qos_bw_regs(binfo->base, mport, thh, thm, thl, gp, gc);
-}
static void bimc_init_mas_reg(struct msm_bus_bimc_info *binfo,
struct msm_bus_inode_info *info,
@@ -2048,7 +2071,7 @@
if (mode != BIMC_QOS_MODE_FIXED) {
struct msm_bus_bimc_qos_bw qbw;
qbw.ws = info->node_info->ws;
- qbw.bw = info->node_info->bimc_bw;
+ qbw.bw = info->node_info->bimc_bw[0];
qbw.gp = info->node_info->bimc_gp;
qbw.thmp = info->node_info->bimc_thmp;
bimc_set_static_qos_bw(binfo,
@@ -2081,9 +2104,11 @@
* If the master supports dual configuration,
* configure registers for both modes
*/
- if (info->node_info->dual_conf)
+ if (info->node_info->dual_conf) {
bimc_init_mas_reg(binfo, info, qmode,
info->node_info->mode_thresh);
+ info->node_info->cur_lim_bw = 0;
+ }
bimc_init_mas_reg(binfo, info, qmode, info->node_info->mode);
return 0;
diff --git a/arch/arm/mach-msm/msm_bus/msm_bus_core.h b/arch/arm/mach-msm/msm_bus/msm_bus_core.h
index 7e4a513..557bcca 100644
--- a/arch/arm/mach-msm/msm_bus/msm_bus_core.h
+++ b/arch/arm/mach-msm/msm_bus/msm_bus_core.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2014 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
@@ -82,10 +82,12 @@
unsigned int prio_wr;
unsigned int prio1;
unsigned int prio0;
- u64 th;
+ unsigned int num_thresh;
+ u64 *th;
+ u64 cur_lim_bw;
unsigned int mode_thresh;
bool dual_conf;
- u64 bimc_bw;
+ u64 *bimc_bw;
u32 bimc_gp;
u32 bimc_thmp;
const char *name;
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 52195c7..f857920 100644
--- a/arch/arm/mach-msm/msm_bus/msm_bus_of.c
+++ b/arch/arm/mach-msm/msm_bus/msm_bus_of.c
@@ -271,6 +271,54 @@
return NULL;
}
+static u64 *get_th_params(struct platform_device *pdev,
+ const struct device_node *node, const char *prop,
+ int *nports)
+{
+ int size = 0, ret;
+ u64 *ret_arr = NULL;
+ int *arr = NULL;
+ int i;
+
+ if (of_get_property(node, prop, &size)) {
+ *nports = size / sizeof(int);
+ } else {
+ pr_debug("Property %s not available\n", prop);
+ *nports = 0;
+ return NULL;
+ }
+
+ ret_arr = devm_kzalloc(&pdev->dev, (*nports * sizeof(u64)),
+ GFP_KERNEL);
+ arr = kzalloc(size, GFP_KERNEL);
+ if ((size > 0) && (ZERO_OR_NULL_PTR(arr)
+ || ZERO_OR_NULL_PTR(ret_arr))) {
+ pr_err("Error: Failed to alloc mem for %s\n", prop);
+ return NULL;
+ }
+
+ ret = of_property_read_u32_array(node, prop, (u32 *)arr, *nports);
+ if (ret) {
+ pr_err("Error in reading property: %s\n", prop);
+ goto err;
+ }
+
+ for (i = 0; i < *nports; i++)
+ ret_arr[i] = (uint64_t)KBTOB(arr[i]);
+
+ MSM_BUS_DBG("%s: num entries %d prop %s", __func__, *nports, prop);
+
+ for (i = 0; i < *nports; i++)
+ MSM_BUS_DBG("Th %d val %llu", i, ret_arr[i]);
+
+ kfree(arr);
+ return ret_arr;
+err:
+ devm_kfree(&pdev->dev, arr);
+ devm_kfree(&pdev->dev, ret_arr);
+ return NULL;
+}
+
static struct msm_bus_node_info *get_nodes(struct device_node *of_node,
struct platform_device *pdev,
struct msm_bus_fabric_registration *pdata)
@@ -278,7 +326,7 @@
struct msm_bus_node_info *info;
struct device_node *child_node = NULL;
int i = 0, ret;
- u32 temp;
+ int num_bw = 0;
for_each_child_of_node(of_node, child_node) {
i++;
@@ -353,36 +401,29 @@
of_property_read_u32(child_node, "qcom,buswidth",
&info[i].buswidth);
of_property_read_u32(child_node, "qcom,ws", &info[i].ws);
- ret = of_property_read_u32(child_node, "qcom,thresh",
- &temp);
- if (!ret)
- info[i].th = (uint64_t)KBTOB(temp);
- ret = of_property_read_u32(child_node, "qcom,bimc,bw",
- &temp);
- if (!ret)
- info[i].bimc_bw = (uint64_t)KBTOB(temp);
+ info[i].dual_conf =
+ of_property_read_bool(child_node, "qcom,dual-conf");
+
+
+ info[i].th = get_th_params(pdev, child_node, "qcom,thresh",
+ &info[i].num_thresh);
+
+ info[i].bimc_bw = get_th_params(pdev, child_node,
+ "qcom,bimc,bw", &num_bw);
+
+ if (num_bw != info[i].num_thresh) {
+ pr_err("%s:num_bw %d must equal num_thresh %d",
+ __func__, num_bw, info[i].num_thresh);
+ pr_err("%s:Err setting up dual conf for %s",
+ __func__, info[i].name);
+ goto err;
+ }
of_property_read_u32(child_node, "qcom,bimc,gp",
&info[i].bimc_gp);
of_property_read_u32(child_node, "qcom,bimc,thmp",
&info[i].bimc_thmp);
- ret = of_property_read_string(child_node, "qcom,mode",
- &sel_str);
- if (ret)
- info[i].mode = 0;
- else {
- ret = get_num(mode_sel_name, sel_str);
- if (ret < 0) {
- pr_err("Unknown mode :%s\n", sel_str);
- goto err;
- }
-
- info[i].mode = ret;
- }
-
- info[i].dual_conf =
- of_property_read_bool(child_node, "qcom,dual-conf");
ret = of_property_read_string(child_node, "qcom,mode-thresh",
&sel_str);
@@ -397,7 +438,21 @@
info[i].mode_thresh = ret;
MSM_BUS_DBG("AXI: THreshold mode set: %d\n",
- info[i].mode_thresh);
+ info[i].mode_thresh);
+ }
+
+ ret = of_property_read_string(child_node, "qcom,mode",
+ &sel_str);
+ if (ret)
+ info[i].mode = 0;
+ else {
+ ret = get_num(mode_sel_name, sel_str);
+ if (ret < 0) {
+ pr_err("Unknown mode :%s\n", sel_str);
+ goto err;
+ }
+
+ info[i].mode = ret;
}
ret = of_property_read_string(child_node, "qcom,perm-mode",
diff --git a/arch/arm/mach-msm/msm_rq_stats.c b/arch/arm/mach-msm/msm_rq_stats.c
index c99f0ec..af4a6d2 100644
--- a/arch/arm/mach-msm/msm_rq_stats.c
+++ b/arch/arm/mach-msm/msm_rq_stats.c
@@ -30,7 +30,6 @@
#include <linux/kernel_stat.h>
#include <linux/tick.h>
#include <asm/smp_plat.h>
-#include "acpuclock.h"
#include <linux/suspend.h>
#define MAX_LONG_SIZE 24
@@ -199,7 +198,7 @@
switch (val) {
case CPU_ONLINE:
if (!this_cpu->cur_freq)
- this_cpu->cur_freq = acpuclk_get_rate(cpu);
+ this_cpu->cur_freq = cpufreq_quick_get(cpu);
case CPU_ONLINE_FROZEN:
this_cpu->avg_load_maxfreq = 0;
}
@@ -402,7 +401,7 @@
cpufreq_get_policy(&cpu_policy, i);
pcpu->policy_max = cpu_policy.cpuinfo.max_freq;
if (cpu_online(i))
- pcpu->cur_freq = acpuclk_get_rate(i);
+ pcpu->cur_freq = cpufreq_quick_get(i);
cpumask_copy(pcpu->related_cpus, cpu_policy.cpus);
}
freq_transition.notifier_call = cpufreq_transition_handler;
diff --git a/arch/arm/mach-msm/ocmem.c b/arch/arm/mach-msm/ocmem.c
index 99e54b7..4a8cc39 100644
--- a/arch/arm/mach-msm/ocmem.c
+++ b/arch/arm/mach-msm/ocmem.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2014, 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
@@ -788,6 +788,7 @@
struct device *dev = &pdev->dev;
struct clk *ocmem_core_clk = NULL;
struct clk *ocmem_iface_clk = NULL;
+ int rc;
if (!pdev->dev.of_node) {
dev_info(dev, "Missing Configuration in Device Tree\n");
@@ -830,10 +831,23 @@
platform_set_drvdata(pdev, ocmem_pdata);
+ rc = ocmem_enable_core_clock();
+ if (rc < 0)
+ goto core_clk_fail;
+
+ rc = ocmem_enable_iface_clock();
+ if (rc < 0)
+ goto iface_clk_fail;
+
/* Parameter to be updated based on TZ */
/* Allow the OCMEM CSR to be programmed */
- if (ocmem_enable_sec_program(OCMEM_SECURE_DEV_ID))
+ if (ocmem_restore_sec_program(OCMEM_SECURE_DEV_ID)) {
+ ocmem_disable_iface_clock();
+ ocmem_disable_core_clock();
return -EBUSY;
+ }
+ ocmem_disable_iface_clock();
+ ocmem_disable_core_clock();
if (ocmem_debugfs_init(pdev))
dev_err(dev, "ocmem: No debugfs node available\n");
@@ -860,6 +874,11 @@
dev_dbg(dev, "initialized successfully\n");
return 0;
+
+iface_clk_fail:
+ ocmem_disable_core_clock();
+core_clk_fail:
+ return rc;
}
static int __devexit msm_ocmem_remove(struct platform_device *pdev)
diff --git a/arch/arm/mach-msm/ocmem_core.c b/arch/arm/mach-msm/ocmem_core.c
index f753391..9c2afb9 100644
--- a/arch/arm/mach-msm/ocmem_core.c
+++ b/arch/arm/mach-msm/ocmem_core.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2014, 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
@@ -583,7 +583,7 @@
return 0;
}
-int ocmem_enable_sec_program(int sec_id)
+int ocmem_restore_sec_program(int sec_id)
{
return 0;
}
@@ -702,7 +702,7 @@
return rc;
}
-int ocmem_enable_sec_program(int sec_id)
+int ocmem_restore_sec_program(int sec_id)
{
int rc, scm_ret = 0;
struct msm_scm_sec_cfg {
@@ -953,7 +953,6 @@
unsigned end_m = num_banks;
unsigned long region_offset = 0;
struct ocmem_hw_region *region;
- int rc = 0;
if (offset < 0)
return -EINVAL;
@@ -974,14 +973,6 @@
(region_end >= num_regions))
return -EINVAL;
- rc = ocmem_enable_core_clock();
-
- if (rc < 0) {
- pr_err("ocmem: Power transistion request for client %s (id: %d) failed\n",
- get_name(id), id);
- return rc;
- }
-
mutex_lock(®ion_ctrl_lock);
for (i = region_start; i <= region_end; i++) {
@@ -1035,11 +1026,10 @@
}
mutex_unlock(®ion_ctrl_lock);
- ocmem_disable_core_clock();
+
return 0;
invalid_transition:
mutex_unlock(®ion_ctrl_lock);
- ocmem_disable_core_clock();
pr_err("ocmem_core: Invalid state transition detected for %d\n", id);
pr_err("ocmem_core: Offset %lx Len %lx curr_state %x new_state %x\n",
offset, len, curr_state, new_state);
@@ -1122,9 +1112,37 @@
static int ocmem_power_show(struct seq_file *f, void *dummy)
{
+ int rc = 0;
+
+ rc = ocmem_enable_core_clock();
+
+ if (rc < 0)
+ goto core_clock_fail;
+
+ rc = ocmem_enable_iface_clock();
+
+ if (rc < 0)
+ goto iface_clock_fail;
+
+ rc = ocmem_restore_sec_program(OCMEM_SECURE_DEV_ID);
+ if (rc < 0) {
+ pr_err("ocmem: Failed to restore security programming\n");
+ goto restore_config_fail;
+ }
ocmem_power_show_sw_state(f, dummy);
ocmem_power_show_hw_state(f, dummy);
+
+ ocmem_disable_iface_clock();
+ ocmem_disable_core_clock();
+
return 0;
+
+restore_config_fail:
+ ocmem_disable_iface_clock();
+iface_clock_fail:
+ ocmem_disable_core_clock();
+core_clock_fail:
+ return -EINVAL;
}
static int ocmem_power_open(struct inode *inode, struct file *file)
diff --git a/arch/arm/mach-msm/ocmem_sched.c b/arch/arm/mach-msm/ocmem_sched.c
index cbf7933..78279ed 100644
--- a/arch/arm/mach-msm/ocmem_sched.c
+++ b/arch/arm/mach-msm/ocmem_sched.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2014, 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
@@ -634,17 +634,11 @@
{
int rc = 0;
- rc = ocmem_enable_core_clock();
+ rc = ocmem_restore_sec_program(OCMEM_SECURE_DEV_ID);
- if (rc < 0)
- goto core_clock_fail;
-
-
- if (is_iface_access(req->owner)) {
- rc = ocmem_enable_iface_clock();
-
- if (rc < 0)
- goto iface_clock_fail;
+ if (rc < 0) {
+ pr_err("ocmem: Failed to restore security programming\n");
+ goto lock_failed;
}
rc = ocmem_lock(req->owner, phys_to_offset(req->req_start), req->req_sz,
@@ -670,11 +664,6 @@
process_map_fail:
ocmem_unlock(req->owner, phys_to_offset(req->req_start), req->req_sz);
lock_failed:
- if (is_iface_access(req->owner))
- ocmem_disable_iface_clock();
-iface_clock_fail:
- ocmem_disable_core_clock();
-core_clock_fail:
pr_err("ocmem: Failed to map ocmem request\n");
return rc;
}
@@ -698,9 +687,6 @@
goto unlock_failed;
}
- if (is_iface_access(req->owner))
- ocmem_disable_iface_clock();
- ocmem_disable_core_clock();
pr_debug("ocmem: Unmapped request %p\n", req);
return 0;
@@ -1345,9 +1331,21 @@
if (rc < 0)
return -EINVAL;
+ rc = ocmem_enable_core_clock();
+
+ if (rc < 0)
+ goto core_clock_fail;
+
+ if (is_iface_access(req->owner)) {
+ rc = ocmem_enable_iface_clock();
+
+ if (rc < 0)
+ goto iface_clock_fail;
+ }
+
rc = process_map(req, req->req_start, req->req_end);
if (rc < 0)
- return -EINVAL;
+ goto map_error;
offset = phys_to_offset(req->req_start);
@@ -1366,7 +1364,14 @@
BUG();
}
return 0;
+
power_ctl_error:
+map_error:
+if (is_iface_access(req->owner))
+ ocmem_disable_iface_clock();
+iface_clock_fail:
+ ocmem_disable_core_clock();
+core_clock_fail:
return -EINVAL;
}
@@ -1536,6 +1541,10 @@
}
}
+ if (is_iface_access(req->owner))
+ ocmem_disable_iface_clock();
+ ocmem_disable_core_clock();
+
rc = do_free(req);
if (rc < 0) {
pr_err("ocmem: Failed to free %p\n", req);
@@ -1556,6 +1565,9 @@
pr_err("Failed to switch OFF memory macros\n");
goto free_fail;
}
+ if (is_iface_access(req->owner))
+ ocmem_disable_iface_clock();
+ ocmem_disable_core_clock();
}
/* free the allocation */
@@ -1658,6 +1670,10 @@
rc = process_unmap(req, req->req_start, req->req_end);
if (rc < 0)
return -EINVAL;
+
+ if (is_iface_access(req->owner))
+ ocmem_disable_iface_clock();
+ ocmem_disable_core_clock();
} else
return -EINVAL;
@@ -1768,6 +1784,9 @@
rc = process_unmap(req, req->req_start, req->req_end);
if (rc < 0)
goto shrink_fail;
+ if (is_iface_access(req->owner))
+ ocmem_disable_iface_clock();
+ ocmem_disable_core_clock();
}
rc = do_free(req);
if (rc < 0)
@@ -2104,7 +2123,6 @@
down_write(&req->rw_sem);
- mutex_lock(&allocation_mutex);
retry_allocate:
/* Take the scheduler mutex */
@@ -2114,12 +2132,14 @@
if (rc == OP_EVICT) {
+ mutex_lock(&allocation_mutex);
ret = run_evict(req);
if (ret == 0) {
rc = sched_restore(req);
if (rc < 0) {
pr_err("Failed to restore for req %p\n", req);
+ mutex_unlock(&allocation_mutex);
goto err_allocate_fail;
}
req->edata = NULL;
@@ -2127,14 +2147,14 @@
pr_debug("Attempting to re-allocate req %p\n", req);
req->req_start = 0x0;
req->req_end = 0x0;
+ mutex_unlock(&allocation_mutex);
goto retry_allocate;
} else {
+ mutex_unlock(&allocation_mutex);
goto err_allocate_fail;
}
}
- mutex_unlock(&allocation_mutex);
-
if (rc == OP_FAIL) {
inc_ocmem_stat(zone_of(req), NR_ALLOCATION_FAILS);
goto err_allocate_fail;
@@ -2159,7 +2179,6 @@
up_write(&req->rw_sem);
return 0;
err_allocate_fail:
- mutex_unlock(&allocation_mutex);
up_write(&req->rw_sem);
return -EINVAL;
}
@@ -2254,6 +2273,17 @@
if (req->req_sz != 0) {
+ rc = ocmem_enable_core_clock();
+
+ if (rc < 0)
+ goto core_clock_fail;
+
+ if (is_iface_access(req->owner)) {
+ rc = ocmem_enable_iface_clock();
+
+ if (rc < 0)
+ goto iface_clock_fail;
+ }
rc = process_map(req, req->req_start, req->req_end);
if (rc < 0)
goto map_error;
@@ -2275,6 +2305,11 @@
map_error:
handle->req = NULL;
do_free(req);
+ if (is_iface_access(req->owner))
+ ocmem_disable_iface_clock();
+iface_clock_fail:
+ ocmem_disable_core_clock();
+core_clock_fail:
do_allocate_error:
ocmem_destroy_req(req);
return -EINVAL;
@@ -2303,6 +2338,17 @@
inc_ocmem_stat(zone_of(req), NR_ASYNC_ALLOCATIONS);
if (req->req_sz != 0) {
+ rc = ocmem_enable_core_clock();
+
+ if (rc < 0)
+ goto core_clock_fail;
+
+ if (is_iface_access(req->owner)) {
+ rc = ocmem_enable_iface_clock();
+
+ if (rc < 0)
+ goto iface_clock_fail;
+ }
rc = process_map(req, req->req_start, req->req_end);
if (rc < 0)
@@ -2333,6 +2379,11 @@
map_error:
handle->req = NULL;
do_free(req);
+ if (is_iface_access(req->owner))
+ ocmem_disable_iface_clock();
+iface_clock_fail:
+ ocmem_disable_core_clock();
+core_clock_fail:
do_allocate_error:
ocmem_destroy_req(req);
return -EINVAL;
diff --git a/arch/arm/mach-msm/pil-pronto.c b/arch/arm/mach-msm/pil-pronto.c
index a7fc204..a91331e 100644
--- a/arch/arm/mach-msm/pil-pronto.c
+++ b/arch/arm/mach-msm/pil-pronto.c
@@ -62,6 +62,9 @@
#define PRONTO_PMU_CCPU_BOOT_REMAP_ADDR 0x2004
+#define PRONTO_PMU_SPARE 0x1088
+#define PRONTO_PMU_SPARE_SSR_BIT BIT(23)
+
#define CLK_CTL_WCNSS_RESTART_BIT BIT(0)
#define AXI_HALTREQ 0x0
@@ -376,7 +379,15 @@
struct pronto_data *drv = subsys_to_drv(subsys);
struct platform_device *pdev = wcnss_get_platform_device();
struct wcnss_wlan_config *pwlanconfig = wcnss_get_wlan_config();
- int ret = -1;
+ void __iomem *base = drv->base;
+ u32 reg;
+ int ret = -1;
+
+ if (base) {
+ reg = readl_relaxed(base + PRONTO_PMU_SPARE);
+ reg |= PRONTO_PMU_SPARE_SSR_BIT;
+ writel_relaxed(reg, base + PRONTO_PMU_SPARE);
+ }
if (pdev && pwlanconfig)
ret = wcnss_wlan_power(&pdev->dev, pwlanconfig,
diff --git a/arch/arm/mach-msm/pm-boot.c b/arch/arm/mach-msm/pm-boot.c
index 3559510..0d4840f 100644
--- a/arch/arm/mach-msm/pm-boot.c
+++ b/arch/arm/mach-msm/pm-boot.c
@@ -343,4 +343,4 @@
{
return platform_driver_register(&msm_pm_boot_driver);
}
-module_init(msm_pm_boot_module_init);
+postcore_initcall(msm_pm_boot_module_init);
diff --git a/arch/arm/mach-msm/qdsp6v2/ultrasound/usf.c b/arch/arm/mach-msm/qdsp6v2/ultrasound/usf.c
index cf69e17..192aaf9 100644
--- a/arch/arm/mach-msm/qdsp6v2/ultrasound/usf.c
+++ b/arch/arm/mach-msm/qdsp6v2/ultrasound/usf.c
@@ -70,6 +70,7 @@
USF_OPENED_STATE,
USF_CONFIGURED_STATE,
USF_WORK_STATE,
+ USF_ADSP_RESTART_STATE,
USF_ERROR_STATE
};
@@ -406,6 +407,13 @@
case Q6USM_EVENT_WRITE_DONE:
wake_up(&usf_xx->wait);
break;
+
+ case RESET_EVENTS:
+ pr_err("%s: received RESET_EVENTS\n", __func__);
+ usf_xx->usf_state = USF_ADSP_RESTART_STATE;
+ wake_up(&usf_xx->wait);
+ break;
+
default:
break;
}
@@ -445,6 +453,12 @@
}
break;
+ case RESET_EVENTS:
+ pr_err("%s: received RESET_EVENTS\n", __func__);
+ usf_xx->usf_state = USF_ADSP_RESTART_STATE;
+ wake_up(&usf_xx->wait);
+ break;
+
default:
break;
}
@@ -865,7 +879,9 @@
if (detect_info.detect_timeout == USF_INFINITIVE_TIMEOUT) {
rc = wait_event_interruptible(usf_xx->wait,
(usf_xx->us_detect_type !=
- USF_US_DETECT_UNDEF));
+ USF_US_DETECT_UNDEF) ||
+ (usf_xx->usf_state ==
+ USF_ADSP_RESTART_STATE));
} else {
if (detect_info.detect_timeout == USF_DEFAULT_TIMEOUT)
timeout = USF_TIMEOUT_JIFFIES;
@@ -874,8 +890,14 @@
}
rc = wait_event_interruptible_timeout(usf_xx->wait,
(usf_xx->us_detect_type !=
- USF_US_DETECT_UNDEF),
- timeout);
+ USF_US_DETECT_UNDEF) ||
+ (usf_xx->usf_state ==
+ USF_ADSP_RESTART_STATE), timeout);
+
+ /* In the case of aDSP restart, "no US" is assumed */
+ if (usf_xx->usf_state == USF_ADSP_RESTART_STATE) {
+ rc = -EFAULT;
+ }
/* In the case of timeout, "no US" is assumed */
if (rc < 0)
pr_err("%s: Getting US detection failed rc[%d]\n",
@@ -1336,7 +1358,8 @@
case US_STOP_TX: {
usf_xx = &usf->usf_tx;
- if (usf_xx->usf_state == USF_WORK_STATE)
+ if ((usf_xx->usf_state == USF_WORK_STATE)
+ || (usf_xx->usf_state == USF_ADSP_RESTART_STATE))
rc = usf_stop_tx(usf);
else {
pr_err("%s: stop_tx: wrong state[%d]\n",
@@ -1349,7 +1372,8 @@
case US_STOP_RX: {
usf_xx = &usf->usf_rx;
- if (usf_xx->usf_state == USF_WORK_STATE)
+ if ((usf_xx->usf_state == USF_WORK_STATE)
+ || (usf_xx->usf_state == USF_ADSP_RESTART_STATE))
usf_disable(usf_xx);
else {
pr_err("%s: stop_rx: wrong state[%d]\n",
diff --git a/arch/arm/mach-msm/qdsp6v2/ultrasound/version_b/q6usm_b.c b/arch/arm/mach-msm/qdsp6v2/ultrasound/version_b/q6usm_b.c
index 51a51c5..af3c1f5 100644
--- a/arch/arm/mach-msm/qdsp6v2/ultrasound/version_b/q6usm_b.c
+++ b/arch/arm/mach-msm/qdsp6v2/ultrasound/version_b/q6usm_b.c
@@ -488,6 +488,24 @@
}
switch (data->opcode) {
+ case RESET_EVENTS: {
+ pr_err("%s: Reset event is received: %d %d\n",
+ __func__,
+ data->reset_event,
+ data->reset_proc);
+
+ opcode = RESET_EVENTS;
+
+ apr_reset(this_mmap.apr);
+ this_mmap.apr = NULL;
+
+ apr_reset(usc->apr);
+ usc->apr = NULL;
+
+ break;
+ }
+
+
case USM_DATA_EVENT_READ_DONE: {
struct us_port_data *port = &usc->port[OUT];
diff --git a/arch/arm/mach-msm/rpm_log.c b/arch/arm/mach-msm/rpm_log.c
old mode 100644
new mode 100755
index 58e8588..1809cea
--- a/arch/arm/mach-msm/rpm_log.c
+++ b/arch/arm/mach-msm/rpm_log.c
@@ -52,6 +52,7 @@
char *data;
u32 len;
u32 pos;
+ struct mutex mutex;
u32 max_len;
u32 read_idx;
struct msm_rpm_log_platform_data *pdata;
@@ -218,6 +219,7 @@
if (!access_ok(VERIFY_WRITE, bufu, count))
return -EFAULT;
+ mutex_lock(&buf->mutex);
/* check for more messages if local buffer empty */
if (buf->pos == buf->len) {
buf->pos = 0;
@@ -226,6 +228,7 @@
}
if ((file->f_flags & O_NONBLOCK) && buf->len == 0)
+ mutex_unlock(&buf->mutex);
return -EAGAIN;
/* loop until new messages arrive */
@@ -241,6 +244,7 @@
remaining = __copy_to_user(bufu, &(buf->data[buf->pos]), out_len);
buf->pos += out_len - remaining;
+ mutex_unlock(&buf->mutex);
return out_len - remaining;
}
@@ -287,6 +291,7 @@
buf->pdata = pdata;
buf->len = 0;
buf->pos = 0;
+ mutex_init(&buf->mutex);
buf->max_len = PRINTED_LENGTH(pdata->log_len);
buf->read_idx = msm_rpm_log_read(pdata, MSM_RPM_LOG_PAGE_INDICES,
MSM_RPM_LOG_HEAD);
diff --git a/arch/arm/mach-msm/smd.c b/arch/arm/mach-msm/smd.c
index 1241e44..32f9b3b 100644
--- a/arch/arm/mach-msm/smd.c
+++ b/arch/arm/mach-msm/smd.c
@@ -1035,12 +1035,19 @@
{
unsigned head = ch->half_ch->get_head(ch->recv);
unsigned tail = ch->half_ch->get_tail(ch->recv);
- *ptr = (void *) (ch->recv_data + tail);
+ unsigned fifo_size = ch->fifo_size;
+ BUG_ON(fifo_size >= SZ_1M);
+ BUG_ON(head >= fifo_size);
+ BUG_ON(tail >= fifo_size);
+ BUG_ON(OVERFLOW_ADD_UNSIGNED(uintptr_t, (uintptr_t)ch->recv_data,
+ tail));
+
+ *ptr = (void *) (ch->recv_data + tail);
if (tail <= head)
return head - tail;
else
- return ch->fifo_size - tail;
+ return fifo_size - tail;
}
static int read_intr_blocked(struct smd_channel *ch)
@@ -1140,16 +1147,23 @@
{
unsigned head = ch->half_ch->get_head(ch->send);
unsigned tail = ch->half_ch->get_tail(ch->send);
- *ptr = (void *) (ch->send_data + head);
+ unsigned fifo_size = ch->fifo_size;
+ BUG_ON(fifo_size >= SZ_1M);
+ BUG_ON(head >= fifo_size);
+ BUG_ON(tail >= fifo_size);
+ BUG_ON(OVERFLOW_ADD_UNSIGNED(uintptr_t, (uintptr_t)ch->send_data,
+ head));
+
+ *ptr = (void *) (ch->send_data + head);
if (head < tail) {
return tail - head - SMD_FIFO_FULL_RESERVE;
} else {
if (tail < SMD_FIFO_FULL_RESERVE)
- return ch->fifo_size + tail - head
+ return fifo_size + tail - head
- SMD_FIFO_FULL_RESERVE;
else
- return ch->fifo_size - head;
+ return fifo_size - head;
}
}
diff --git a/arch/arm/mach-msm/smd_tty.c b/arch/arm/mach-msm/smd_tty.c
index 9cb26e1..e1c5162 100644
--- a/arch/arm/mach-msm/smd_tty.c
+++ b/arch/arm/mach-msm/smd_tty.c
@@ -28,6 +28,7 @@
#include <linux/tty.h>
#include <linux/tty_driver.h>
#include <linux/tty_flip.h>
+#include <linux/suspend.h>
#include <mach/msm_smd.h>
#include <mach/subsystem_restart.h>
@@ -40,6 +41,7 @@
#define MAX_SMD_TTYS 37
#define MAX_TTY_BUF_SIZE 2048
#define TTY_PUSH_WS_DELAY 500
+#define TTY_PUSH_WS_POST_SUSPEND_DELAY 100
#define MAX_RA_WAKE_LOCK_NAME_LEN 32
#define SMD_TTY_PROBE_WAIT_TIMEOUT 3000
#define SMD_TTY_LOG_PAGES 2
@@ -63,6 +65,10 @@
static struct delayed_work smd_tty_probe_work;
static int smd_tty_probe_done;
+static bool smd_tty_in_suspend;
+static bool smd_tty_read_in_suspend;
+static struct wakeup_source read_in_suspend_ws;
+
/**
* struct smd_tty_info - context for an individual SMD TTY device
*
@@ -297,6 +303,9 @@
*/
__pm_wakeup_event(&info->pending_ws, TTY_PUSH_WS_DELAY);
+ if (smd_tty_in_suspend)
+ smd_tty_read_in_suspend = true;
+
tty_flip_buffer_push(tty);
}
@@ -696,6 +705,32 @@
return -ENODEV;
}
+static int smd_tty_pm_notifier(struct notifier_block *nb,
+ unsigned long event, void *unused)
+{
+ switch (event) {
+ case PM_SUSPEND_PREPARE:
+ smd_tty_read_in_suspend = false;
+ smd_tty_in_suspend = true;
+ break;
+
+ case PM_POST_SUSPEND:
+ smd_tty_in_suspend = false;
+ if (smd_tty_read_in_suspend) {
+ smd_tty_read_in_suspend = false;
+ __pm_wakeup_event(&read_in_suspend_ws,
+ TTY_PUSH_WS_POST_SUSPEND_DELAY);
+ }
+ break;
+ }
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block smd_tty_pm_nb = {
+ .notifier_call = smd_tty_pm_notifier,
+ .priority = 0,
+};
+
/**
* smd_tty_log_init()- Init function for IPC logging
*
@@ -836,6 +871,11 @@
}
}
INIT_DELAYED_WORK(&loopback_work, loopback_probe_worker);
+
+ ret = register_pm_notifier(&smd_tty_pm_nb);
+ if (ret)
+ pr_err("%s: power state notif error %d\n", __func__, ret);
+
return 0;
out:
@@ -918,6 +958,10 @@
}
INIT_DELAYED_WORK(&loopback_work, loopback_probe_worker);
+ ret = register_pm_notifier(&smd_tty_pm_nb);
+ if (ret)
+ pr_err("%s: power state notif error %d\n", __func__, ret);
+
return 0;
error:
@@ -1002,6 +1046,7 @@
schedule_delayed_work(&smd_tty_probe_work,
msecs_to_jiffies(SMD_TTY_PROBE_WAIT_TIMEOUT));
+ wakeup_source_init(&read_in_suspend_ws, "SMDTTY_READ_IN_SUSPEND");
return 0;
}
diff --git a/arch/arm/mach-msm/smem.c b/arch/arm/mach-msm/smem.c
index f41240a..3c7cbeb 100644
--- a/arch/arm/mach-msm/smem.c
+++ b/arch/arm/mach-msm/smem.c
@@ -27,17 +27,6 @@
#include "smem_private.h"
-/**
- * OVERFLOW_ADD_UNSIGNED() - check for unsigned overflow
- *
- * @type: type to check for overflow
- * @a: left value to use
- * @b: right value to use
- * @returns: true if a + b will result in overflow; false otherwise
- */
-#define OVERFLOW_ADD_UNSIGNED(type, a, b) \
- (((type)~0 - (a)) < (b) ? true : false)
-
#define MODEM_SBL_VERSION_INDEX 7
#define SMEM_VERSION_INFO_SIZE (32 * 4)
#define SMEM_VERSION 0x000B
diff --git a/arch/arm/mach-msm/socinfo.c b/arch/arm/mach-msm/socinfo.c
index 8e7adba..795749b 100644
--- a/arch/arm/mach-msm/socinfo.c
+++ b/arch/arm/mach-msm/socinfo.c
@@ -176,6 +176,14 @@
uint32_t pmic_die_revision_2;
};
+
+struct socinfo_v9 {
+ struct socinfo_v8 v8;
+
+ /* only valid when format==9*/
+ uint32_t foundry_id;
+};
+
static union {
struct socinfo_v1 v1;
struct socinfo_v2 v2;
@@ -185,6 +193,7 @@
struct socinfo_v6 v6;
struct socinfo_v7 v7;
struct socinfo_v8 v8;
+ struct socinfo_v9 v9;
} *socinfo;
static struct msm_soc_info cpu_of_id[] = {
@@ -522,6 +531,14 @@
: 0;
}
+static uint32_t socinfo_get_foundry_id(void)
+{
+ return socinfo ?
+ (socinfo->v1.format >= 9 ? socinfo->v9.foundry_id : 0)
+ : 0;
+}
+
+
enum pmic_model socinfo_get_pmic_model(void)
{
return socinfo ?
@@ -876,6 +893,15 @@
}
static ssize_t
+msm_get_foundry_id(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ socinfo_get_foundry_id());
+}
+
+static ssize_t
msm_get_pmic_model(struct device *dev,
struct device_attribute *attr,
char *buf)
@@ -1115,6 +1141,10 @@
__ATTR(platform_subtype_id, S_IRUGO,
msm_get_platform_subtype_id, NULL);
+static struct device_attribute msm_soc_attr_foundry_id =
+ __ATTR(foundry_id, S_IRUGO,
+ msm_get_foundry_id, NULL);
+
static struct device_attribute msm_soc_attr_pmic_model =
__ATTR(pmic_model, S_IRUGO,
msm_get_pmic_model, NULL);
@@ -1199,6 +1229,9 @@
device_create_file(msm_soc_device, &select_image);
switch (legacy_format) {
+ case 9:
+ device_create_file(msm_soc_device,
+ &msm_soc_attr_foundry_id);
case 8:
case 7:
device_create_file(msm_soc_device,
@@ -1414,6 +1447,23 @@
socinfo->v7.pmic_model,
socinfo->v7.pmic_die_revision);
break;
+ case 9:
+ pr_info("%s: v%u, id=%u, ver=%u.%u, raw_id=%u, raw_ver=%u ,"
+ "hw_plat=%u, hw_plat_ver=%u\n accessory_chip=%u,"
+ "hw_plat_subtype=%u, pmic_model=%u, pmic_die_revision=%u,"
+ "foundry_id=%u\n", __func__,
+ socinfo->v1.format,
+ socinfo->v1.id,
+ SOCINFO_VERSION_MAJOR(socinfo->v1.version),
+ SOCINFO_VERSION_MINOR(socinfo->v1.version),
+ socinfo->v2.raw_id, socinfo->v2.raw_version,
+ socinfo->v3.hw_platform, socinfo->v4.platform_version,
+ socinfo->v5.accessory_chip,
+ socinfo->v6.hw_platform_subtype,
+ socinfo->v7.pmic_model,
+ socinfo->v7.pmic_die_revision,
+ socinfo->v9.foundry_id);
+ break;
default:
pr_err("%s: Unknown format found\n", __func__);
break;
@@ -1422,7 +1472,10 @@
int __init socinfo_init(void)
{
- socinfo = smem_alloc(SMEM_HW_SW_BUILD_ID, sizeof(struct socinfo_v8));
+ socinfo = smem_alloc(SMEM_HW_SW_BUILD_ID, sizeof(struct socinfo_v9));
+ if (!socinfo)
+ socinfo = smem_alloc(SMEM_HW_SW_BUILD_ID,
+ sizeof(struct socinfo_v8));
if (!socinfo)
socinfo = smem_alloc(SMEM_HW_SW_BUILD_ID,
diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c
index 4ff4b3e..935bb9a 100644
--- a/arch/arm/mm/dma-mapping.c
+++ b/arch/arm/mm/dma-mapping.c
@@ -308,7 +308,7 @@
static int __init atomic_pool_init(void)
{
struct dma_pool *pool = &atomic_pool;
- pgprot_t prot = pgprot_dmacoherent(pgprot_kernel);
+ pgprot_t prot = pgprot_dmacoherent(PAGE_KERNEL);
unsigned long nr_pages = pool->size >> PAGE_SHIFT;
unsigned long *bitmap;
struct page *page;
@@ -550,7 +550,7 @@
void *cpu_addr, size_t size)
{
if (!PageHighMem(page))
- __dma_remap(page, size, pgprot_kernel, false);
+ __dma_remap(page, size, PAGE_KERNEL, false);
else
__dma_free_remap(cpu_addr, size, true);
dma_release_from_contiguous(dev, page, size >> PAGE_SHIFT);
@@ -657,7 +657,7 @@
void *arm_dma_alloc(struct device *dev, size_t size, dma_addr_t *handle,
gfp_t gfp, struct dma_attrs *attrs)
{
- pgprot_t prot = __get_dma_pgprot(attrs, pgprot_kernel);
+ pgprot_t prot = __get_dma_pgprot(attrs, PAGE_KERNEL);
void *memory;
bool no_kernel_mapping = dma_get_attr(DMA_ATTR_NO_KERNEL_MAPPING,
attrs);
@@ -1151,7 +1151,7 @@
static void *arm_iommu_alloc_attrs(struct device *dev, size_t size,
dma_addr_t *handle, gfp_t gfp, struct dma_attrs *attrs)
{
- pgprot_t prot = __get_dma_pgprot(attrs, pgprot_kernel);
+ pgprot_t prot = __get_dma_pgprot(attrs, PAGE_KERNEL);
struct page **pages;
void *addr = NULL;
diff --git a/arch/arm/mm/fault.c b/arch/arm/mm/fault.c
index 235c386..698c93e 100644
--- a/arch/arm/mm/fault.c
+++ b/arch/arm/mm/fault.c
@@ -675,6 +675,8 @@
if (!inf->fn(addr, fsr & ~FSR_LNX_PF, regs))
return;
+ trace_unhandled_abort(regs, addr, fsr);
+
printk(KERN_ALERT "Unhandled fault: %s (0x%03x) at 0x%08lx\n",
inf->name, fsr, addr);
@@ -707,6 +709,8 @@
if (!inf->fn(addr, ifsr | FSR_LNX_PF, regs))
return;
+ trace_unhandled_abort(regs, addr, ifsr);
+
printk(KERN_ALERT "Unhandled prefetch abort: %s (0x%03x) at 0x%08lx\n",
inf->name, ifsr, addr);
diff --git a/arch/arm/mm/mmu.c b/arch/arm/mm/mmu.c
index 669b10e..3d52735 100644
--- a/arch/arm/mm/mmu.c
+++ b/arch/arm/mm/mmu.c
@@ -307,7 +307,8 @@
.domain = DOMAIN_KERNEL,
},
[MT_MEMORY_DMA_READY] = {
- .prot_pte = L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_DIRTY,
+ .prot_pte = L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_DIRTY |
+ L_PTE_XN,
.prot_l1 = PMD_TYPE_TABLE,
.domain = DOMAIN_KERNEL,
},
@@ -343,11 +344,13 @@
unsigned long size = PAGE_SIZE*numpages; \
unsigned end = start + size; \
\
- if (start < MODULES_VADDR || start >= MODULES_END) \
- return -EINVAL;\
+ if (!IS_ENABLED(CONFIG_FORCE_PAGES)) { \
+ if (start < MODULES_VADDR || start >= MODULES_END) \
+ return -EINVAL;\
\
- if (end < MODULES_VADDR || end >= MODULES_END) \
- return -EINVAL; \
+ if (end < MODULES_VADDR || end >= MODULES_END) \
+ return -EINVAL; \
+ } \
\
apply_to_page_range(&init_mm, start, size, callback, NULL); \
flush_tlb_kernel_range(start, end); \
@@ -1506,6 +1509,100 @@
}
}
+#ifdef CONFIG_FORCE_PAGES
+/*
+ * remap a PMD into pages
+ * We split a single pmd here none of this two pmd nonsense
+ */
+static noinline void split_pmd(pmd_t *pmd, unsigned long addr,
+ unsigned long end, unsigned long pfn,
+ const struct mem_type *type)
+{
+ pte_t *pte, *start_pte;
+
+ start_pte = early_alloc(PTE_HWTABLE_OFF + PTE_HWTABLE_SIZE);
+
+ pte = start_pte;
+
+ do {
+ set_pte_ext(pte, pfn_pte(pfn, type->prot_pte), 0);
+ pfn++;
+ } while (pte++, addr += PAGE_SIZE, addr != end);
+
+ *pmd = __pmd((__pa(start_pte) + PTE_HWTABLE_OFF) | type->prot_l1);
+ mb();
+ flush_pmd_entry(pmd);
+ flush_tlb_all();
+}
+
+/*
+ * It's significantly easier to remap as pages later after all memory is
+ * mapped. Everything is sections so all we have to do is split
+ */
+static void __init remap_pages(void)
+{
+ struct memblock_region *reg;
+
+ for_each_memblock(memory, reg) {
+ phys_addr_t phys_start = reg->base;
+ phys_addr_t phys_end = reg->base + reg->size;
+ unsigned long addr = (unsigned long)__va(phys_start);
+ unsigned long end = (unsigned long)__va(phys_end);
+ pmd_t *pmd = NULL;
+ unsigned long next;
+ unsigned long pfn = __phys_to_pfn(phys_start);
+ bool fixup = false;
+ unsigned long saved_start = addr;
+
+ if (phys_end > arm_lowmem_limit)
+ end = (unsigned long)__va(arm_lowmem_limit);
+ if (phys_start >= phys_end)
+ break;
+
+ pmd = pmd_offset(
+ pud_offset(pgd_offset(&init_mm, addr), addr), addr);
+
+#ifndef CONFIG_ARM_LPAE
+ if (addr & SECTION_SIZE) {
+ fixup = true;
+ pmd_empty_section_gap((addr - SECTION_SIZE) & PMD_MASK);
+ pmd++;
+ }
+
+ if (end & SECTION_SIZE)
+ pmd_empty_section_gap(end);
+#endif
+
+ do {
+ next = addr + SECTION_SIZE;
+
+ if (pmd_none(*pmd) || pmd_bad(*pmd))
+ split_pmd(pmd, addr, next, pfn,
+ &mem_types[MT_MEMORY]);
+ pmd++;
+ pfn += SECTION_SIZE >> PAGE_SHIFT;
+
+ } while (addr = next, addr < end);
+
+ if (fixup) {
+ /*
+ * Put a faulting page table here to avoid detecting no
+ * pmd when accessing an odd section boundary. This
+ * needs to be faulting to help catch errors and avoid
+ * speculation
+ */
+ pmd = pmd_off_k(saved_start);
+ pmd[0] = pmd[1] & ~1;
+ }
+ }
+}
+#else
+static void __init remap_pages(void)
+{
+
+}
+#endif
+
/*
* paging_init() sets up the page tables, initialises the zone memory
* maps, and sets up the zero page, bad page and bad page tables.
@@ -1520,6 +1617,7 @@
prepare_page_table();
map_lowmem();
dma_contiguous_remap();
+ remap_pages();
devicemaps_init(mdesc);
kmap_init();
diff --git a/drivers/base/dma-contiguous.c b/drivers/base/dma-contiguous.c
index 606383a..2adcbbc 100644
--- a/drivers/base/dma-contiguous.c
+++ b/drivers/base/dma-contiguous.c
@@ -47,6 +47,7 @@
unsigned long base_pfn;
unsigned long count;
unsigned long *bitmap;
+ struct mutex lock;
};
static DEFINE_MUTEX(cma_mutex);
@@ -191,6 +192,7 @@
ret = cma_activate_area(base_pfn, count);
if (ret)
goto error;
+ mutex_init(&cma->lock);
pr_debug("%s: returned %p\n", __func__, (void *)cma);
return cma;
@@ -212,6 +214,7 @@
unsigned long len;
__be32 *prop;
char *name;
+ phys_addr_t limit = MEMBLOCK_ALLOC_ANYWHERE;
if (!of_get_flat_dt_prop(node, "linux,contiguous-region", NULL))
return 0;
@@ -225,9 +228,13 @@
name = of_get_flat_dt_prop(node, "label", NULL);
- pr_info("Found %s, memory base %lx, size %ld MiB\n", uname,
- (unsigned long)base, (unsigned long)size / SZ_1M);
- dma_contiguous_reserve_area(size, &base, MEMBLOCK_ALLOC_ANYWHERE, name);
+ prop = of_get_flat_dt_prop(node, "linux,memory-limit", NULL);
+ if (prop)
+ limit = be32_to_cpu(prop[0]);
+
+ pr_info("Found %s, memory base %lx, size %ld MiB, limit %pa\n", uname,
+ (unsigned long)base, (unsigned long)size / SZ_1M, &limit);
+ dma_contiguous_reserve_area(size, &base, limit, name);
return 0;
}
@@ -454,6 +461,13 @@
return cma->base_pfn << PAGE_SHIFT;
}
+static void clear_cma_bitmap(struct cma *cma, unsigned long pfn, int count)
+{
+ mutex_lock(&cma->lock);
+ bitmap_clear(cma->bitmap, pfn - cma->base_pfn, count);
+ mutex_unlock(&cma->lock);
+}
+
/**
* dma_alloc_from_contiguous() - allocate pages from contiguous area
* @dev: Pointer to device for which the allocation is performed.
@@ -488,23 +502,36 @@
mask = (1 << align) - 1;
- mutex_lock(&cma_mutex);
for (;;) {
+ mutex_lock(&cma->lock);
pageno = bitmap_find_next_zero_area(cma->bitmap, cma->count,
start, count, mask);
- if (pageno >= cma->count)
+ if (pageno >= cma->count) {
+ mutex_unlock(&cma->lock);
break;
+ }
+ bitmap_set(cma->bitmap, pageno, count);
+ /*
+ * It's safe to drop the lock here. We've marked this region for
+ * our exclusive use. If the migration fails we will take the
+ * lock again and unmark it.
+ */
+ mutex_unlock(&cma->lock);
pfn = cma->base_pfn + pageno;
+ mutex_lock(&cma_mutex);
ret = alloc_contig_range(pfn, pfn + count, MIGRATE_CMA);
+ mutex_unlock(&cma_mutex);
if (ret == 0) {
- bitmap_set(cma->bitmap, pageno, count);
page = pfn_to_page(pfn);
break;
} else if (ret != -EBUSY) {
+ pfn = 0;
+ clear_cma_bitmap(cma, pfn, count);
break;
}
+ clear_cma_bitmap(cma, pfn, count);
tries++;
trace_dma_alloc_contiguous_retry(tries);
@@ -514,7 +541,6 @@
start = pageno + mask + 1;
}
- mutex_unlock(&cma_mutex);
pr_debug("%s(): returned %p\n", __func__, page);
return page;
}
@@ -548,9 +574,7 @@
VM_BUG_ON(pfn + count > cma->base_pfn + cma->count);
free_contig_range(pfn, count);
- mutex_lock(&cma_mutex);
- bitmap_clear(cma->bitmap, pfn - cma->base_pfn, count);
- mutex_unlock(&cma_mutex);
+ clear_cma_bitmap(cma, pfn, count);
return true;
}
diff --git a/drivers/char/diag/diagchar.h b/drivers/char/diag/diagchar.h
index 99647a7..95d90b3 100644
--- a/drivers/char/diag/diagchar.h
+++ b/drivers/char/diag/diagchar.h
@@ -241,6 +241,7 @@
int in_busy_1;
int in_busy_2;
+ spinlock_t in_busy_lock;
unsigned char *buf_in_1;
unsigned char *buf_in_2;
@@ -383,6 +384,7 @@
struct work_struct diag_usb_disconnect_work;
#endif
struct workqueue_struct *diag_wq;
+ struct workqueue_struct *diag_usb_wq;
struct work_struct diag_drain_work;
struct workqueue_struct *diag_cntl_wq;
uint8_t *msg_masks;
diff --git a/drivers/char/diag/diagchar_core.c b/drivers/char/diag/diagchar_core.c
index 0e475c9..ba13ec1 100644
--- a/drivers/char/diag/diagchar_core.c
+++ b/drivers/char/diag/diagchar_core.c
@@ -1171,6 +1171,7 @@
int remote_token;
int exit_stat;
int clear_read_wakelock;
+ unsigned long flags;
for (i = 0; i < driver->num_clients; i++)
if (driver->client_map[i].pid == current->tgid)
@@ -1250,7 +1251,10 @@
process_lock_on_copy(&data->nrt_lock);
clear_read_wakelock++;
}
+ spin_lock_irqsave(&data->in_busy_lock, flags);
data->in_busy_1 = 0;
+ spin_unlock_irqrestore(&data->in_busy_lock,
+ flags);
}
if (data->in_busy_2 == 1) {
num_data++;
@@ -1265,7 +1269,10 @@
process_lock_on_copy(&data->nrt_lock);
clear_read_wakelock++;
}
+ spin_lock_irqsave(&data->in_busy_lock, flags);
data->in_busy_2 = 0;
+ spin_unlock_irqrestore(&data->in_busy_lock,
+ flags);
}
}
if (driver->supports_separate_cmdrsp) {
diff --git a/drivers/char/diag/diagfwd.c b/drivers/char/diag/diagfwd.c
index 0bbb012..f7e720f 100644
--- a/drivers/char/diag/diagfwd.c
+++ b/drivers/char/diag/diagfwd.c
@@ -73,6 +73,8 @@
struct diag_send_desc_type send = { NULL, NULL, DIAG_STATE_START, 0 };
struct diag_hdlc_dest_type enc = { NULL, NULL, 0 };
struct diag_smd_info *data = &(driver->smd_data[MODEM_DATA]);
+ int err;
+ unsigned long flags;
if (buf_length > APPS_BUF_SIZE) {
pr_err("diag: In %s, invalid len %d, permissible len %d\n",
@@ -85,6 +87,7 @@
send.last = (void *)(driver->apps_rsp_buf + buf_length);
send.terminate = 1;
if (!data->in_busy_1) {
+ spin_lock_irqsave(&data->in_busy_lock, flags);
enc.dest = data->buf_in_1;
enc.dest_last = (void *)(data->buf_in_1 + APPS_BUF_SIZE - 1);
diag_hdlc_encode(&send, &enc);
@@ -92,9 +95,15 @@
data->write_ptr_1->length = (int)(enc.dest -
(void *)(data->buf_in_1));
data->in_busy_1 = 1;
- diag_device_write(data->buf_in_1, data->peripheral,
+ err = diag_device_write(data->buf_in_1, data->peripheral,
data->write_ptr_1);
+ if (err) {
+ pr_err("diag: In %s, Unable to write to device, err: %d\n",
+ __func__, err);
+ data->in_busy_1 = 0;
+ }
memset(driver->apps_rsp_buf, '\0', APPS_BUF_SIZE);
+ spin_unlock_irqrestore(&data->in_busy_lock, flags);
}
}
@@ -462,6 +471,7 @@
struct diag_request *write_ptr_modem = NULL;
int *in_busy_ptr = 0;
int err = 0;
+ unsigned long flags;
/*
* Do not process data on command channel if the
@@ -489,16 +499,16 @@
}
if (write_ptr_modem) {
+ spin_lock_irqsave(&smd_info->in_busy_lock, flags);
write_ptr_modem->length = total_recd;
*in_busy_ptr = 1;
err = diag_device_write(buf, smd_info->peripheral,
write_ptr_modem);
if (err) {
- /* Free up the buffer for future use */
- *in_busy_ptr = 0;
pr_err_ratelimited("diag: In %s, diag_device_write error: %d\n",
__func__, err);
}
+ spin_unlock_irqrestore(&smd_info->in_busy_lock, flags);
}
} else {
/* The data is raw and needs to be hdlc encoded */
@@ -526,6 +536,8 @@
success = diag_add_hdlc_encoding(smd_info, buf,
total_recd, write_buf,
&write_length);
+ spin_lock_irqsave(&smd_info->in_busy_lock,
+ flags);
if (success) {
write_ptr_modem->length = write_length;
*in_busy_ptr = 1;
@@ -533,15 +545,12 @@
smd_info->peripheral,
write_ptr_modem);
if (err) {
- /*
- * Free up the buffer for
- * future use
- */
- *in_busy_ptr = 0;
pr_err_ratelimited("diag: In %s, diag_device_write error: %d\n",
__func__, err);
}
}
+ spin_unlock_irqrestore(&smd_info->in_busy_lock,
+ flags);
}
}
}
@@ -814,6 +823,43 @@
diag_smd_send_req(smd_info);
}
+#ifdef CONFIG_DIAG_OVER_USB
+static int diag_write_to_usb(struct usb_diag_ch *ch,
+ struct diag_request *write_ptr)
+{
+ int err = 0;
+ uint8_t retry_count, max_retries;
+
+ if (!ch || !write_ptr)
+ return -EIO;
+
+ retry_count = 0;
+ max_retries = 3;
+
+ while (retry_count < max_retries) {
+ retry_count++;
+ /* If USB is not connected, don't try to write */
+ if (!driver->usb_connected) {
+ err = -ENODEV;
+ break;
+ }
+ err = usb_diag_write(ch, write_ptr);
+ if (err == -EAGAIN) {
+ /*
+ * USB is not configured. Wait for sometime and
+ * try again. The value 10000 was chosen empirically
+ * as an optimum value for USB to be configured.
+ */
+ usleep_range(10000, 10100);
+ continue;
+ } else {
+ break;
+ }
+ }
+ return err;
+}
+#endif
+
int diag_device_write(void *buf, int data_type, struct diag_request *write_ptr)
{
int i, err = 0, index;
@@ -919,7 +965,7 @@
if (driver->write_ptr_svc) {
driver->write_ptr_svc->length = driver->used;
driver->write_ptr_svc->buf = buf;
- err = usb_diag_write(driver->legacy_ch,
+ err = diag_write_to_usb(driver->legacy_ch,
driver->write_ptr_svc);
/* Free the buffer if write failed */
if (err) {
@@ -941,7 +987,7 @@
" USB: ", 16, 1, DUMP_PREFIX_ADDRESS,
buf, write_ptr->length, 1);
#endif /* DIAG DEBUG */
- err = usb_diag_write(driver->legacy_ch, write_ptr);
+ err = diag_write_to_usb(driver->legacy_ch, write_ptr);
}
#ifdef CONFIG_DIAG_SDIO_PIPE
else if (data_type == SDIO_DATA) {
@@ -1817,10 +1863,14 @@
void diag_reset_smd_data(int queue)
{
int i;
+ unsigned long flags;
for (i = 0; i < NUM_SMD_DATA_CHANNELS; i++) {
+ spin_lock_irqsave(&driver->smd_data[i].in_busy_lock, flags);
driver->smd_data[i].in_busy_1 = 0;
driver->smd_data[i].in_busy_2 = 0;
+ spin_unlock_irqrestore(&driver->smd_data[i].in_busy_lock,
+ flags);
if (queue)
/* Poll SMD data channels to check for data */
queue_work(driver->smd_data[i].wq,
@@ -1829,8 +1879,12 @@
if (driver->supports_separate_cmdrsp) {
for (i = 0; i < NUM_SMD_CMD_CHANNELS; i++) {
+ spin_lock_irqsave(&driver->smd_cmd[i].in_busy_lock,
+ flags);
driver->smd_cmd[i].in_busy_1 = 0;
driver->smd_cmd[i].in_busy_2 = 0;
+ spin_unlock_irqrestore(&driver->smd_cmd[i].in_busy_lock,
+ flags);
if (queue)
/* Poll SMD data channels to check for data */
queue_work(driver->diag_wq,
@@ -1868,11 +1922,10 @@
N_LEGACY_WRITE_CMD : N_LEGACY_WRITE),
N_LEGACY_READ);
if (err)
- printk(KERN_ERR "diag: unable to alloc USB req on legacy ch");
-
+ goto exit;
driver->usb_connected = 1;
diag_reset_smd_data(RESET_AND_QUEUE);
- for (i = 0; i < NUM_SMD_DATA_CHANNELS; i++) {
+ for (i = 0; i < NUM_SMD_CONTROL_CHANNELS; i++) {
/* Poll SMD CNTL channels to check for data */
diag_smd_notify(&(driver->smd_cntl[i]), SMD_EVENT_DATA);
}
@@ -1889,26 +1942,40 @@
printk(KERN_INFO "diag: No USB MDM ch");
}
#endif
+
return 0;
+exit:
+ pr_err("diag: unable to alloc USB req on legacy ch, err: %d", err);
+ return err;
}
int diagfwd_disconnect(void)
{
int i;
+ unsigned long flags;
+ struct diag_smd_info *smd_info = NULL;
printk(KERN_DEBUG "diag: USB disconnected\n");
driver->usb_connected = 0;
driver->debug_flag = 1;
if (driver->logging_mode == USB_MODE) {
for (i = 0; i < NUM_SMD_DATA_CHANNELS; i++) {
- driver->smd_data[i].in_busy_1 = 1;
- driver->smd_data[i].in_busy_2 = 1;
+ smd_info = &driver->smd_data[i];
+ spin_lock_irqsave(&smd_info->in_busy_lock, flags);
+ smd_info->in_busy_1 = 1;
+ smd_info->in_busy_2 = 1;
+ spin_unlock_irqrestore(&smd_info->in_busy_lock, flags);
}
if (driver->supports_separate_cmdrsp) {
for (i = 0; i < NUM_SMD_CMD_CHANNELS; i++) {
- driver->smd_cmd[i].in_busy_1 = 1;
- driver->smd_cmd[i].in_busy_2 = 1;
+ smd_info = &driver->smd_cmd[i];
+ spin_lock_irqsave(&smd_info->in_busy_lock,
+ flags);
+ smd_info->in_busy_1 = 1;
+ smd_info->in_busy_2 = 1;
+ spin_unlock_irqrestore(&smd_info->in_busy_lock,
+ flags);
}
}
}
@@ -1928,7 +1995,9 @@
{
int i;
int found_it = 0;
+ unsigned long flags;
+ spin_lock_irqsave(&data->in_busy_lock, flags);
for (i = 0; i < num_channels; i++) {
if (buf == (void *)data[i].buf_in_1) {
data[i].in_busy_1 = 0;
@@ -1940,6 +2009,7 @@
break;
}
}
+ spin_unlock_irqrestore(&data->in_busy_lock, flags);
if (found_it) {
if (data[i].type == SMD_DATA_TYPE)
@@ -2053,11 +2123,11 @@
{
switch (event) {
case USB_DIAG_CONNECT:
- queue_work(driver->diag_wq,
+ queue_work(driver->diag_usb_wq,
&driver->diag_usb_connect_work);
break;
case USB_DIAG_DISCONNECT:
- queue_work(driver->diag_wq,
+ queue_work(driver->diag_usb_wq,
&driver->diag_usb_disconnect_work);
break;
case USB_DIAG_READ_DONE:
@@ -2071,7 +2141,6 @@
break;
}
}
-
#endif /* DIAG OVER USB */
void diag_smd_notify(void *ctxt, unsigned event)
@@ -2283,6 +2352,7 @@
smd_info->type = type;
smd_info->encode_hdlc = 0;
mutex_init(&smd_info->smd_ch_mutex);
+ spin_lock_init(&smd_info->in_busy_lock);
switch (peripheral) {
case MODEM_DATA:
@@ -2596,6 +2666,7 @@
kmemleak_not_leak(driver->apps_rsp_buf);
}
driver->diag_wq = create_singlethread_workqueue("diag_wq");
+ driver->diag_usb_wq = create_singlethread_workqueue("diag_usb_wq");
#ifdef CONFIG_DIAG_OVER_USB
INIT_WORK(&(driver->diag_usb_connect_work),
diag_usb_connect_work_fn);
@@ -2643,6 +2714,8 @@
kfree(driver->user_space_data_buf);
if (driver->diag_wq)
destroy_workqueue(driver->diag_wq);
+ if (driver->diag_usb_wq)
+ destroy_workqueue(driver->diag_usb_wq);
}
void diagfwd_exit(void)
@@ -2680,4 +2753,5 @@
kfree(driver->apps_rsp_buf);
kfree(driver->user_space_data_buf);
destroy_workqueue(driver->diag_wq);
+ destroy_workqueue(driver->diag_usb_wq);
}
diff --git a/drivers/coresight/coresight-event.c b/drivers/coresight/coresight-event.c
index aa8bfc5..95cadc2 100644
--- a/drivers/coresight/coresight-event.c
+++ b/drivers/coresight/coresight-event.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
@@ -10,9 +10,6 @@
* GNU General Public License for more details.
*/
-/*
- * DLKM to register a callback with a ftrace event
- */
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
@@ -23,49 +20,101 @@
#include <trace/events/exception.h>
-static void abort_coresight_tracing(void *ignore, struct task_struct *task,\
- unsigned long addr, unsigned int fsr)
+static int event_abort_enable;
+static int event_abort_set(const char *val, struct kernel_param *kp);
+module_param_call(event_abort_enable, event_abort_set, param_get_int,
+ &event_abort_enable, 0644);
+
+static void event_abort_user_fault(void *ignore,
+ struct task_struct *task,
+ unsigned long addr,
+ unsigned int fsr)
{
coresight_abort();
- pr_debug("control_trace: task_name: %s, addr: %lu, fsr:%u",\
+ pr_debug("coresight_event: task_name: %s, addr: %lu, fsr:%u",
(char *)task->comm, addr, fsr);
}
-static void abort_tracing_undef_instr(void *ignore, struct pt_regs *regs,\
- void *pc)
+static void event_abort_undef_instr(void *ignore,
+ struct pt_regs *regs,
+ void *pc)
{
if (user_mode(regs)) {
coresight_abort();
- pr_debug("control_trace: pc: %p", pc);
+ pr_debug("coresight_event: pc: %p", pc);
}
}
-static int __init control_trace_init(void)
+static void event_abort_unhandled_abort(void *ignore,
+ struct pt_regs *regs,
+ unsigned long addr,
+ unsigned int fsr)
{
- int ret_user_fault, ret_undef_instr;
- ret_user_fault = register_trace_user_fault(abort_coresight_tracing,\
- NULL);
- ret_undef_instr = register_trace_undef_instr(abort_tracing_undef_instr,\
- NULL);
- if (ret_user_fault != 0 || ret_undef_instr != 0) {
- pr_info("control_trace: Module Not Registered\n");
- return (ret_user_fault < 0 ?\
- ret_user_fault : ret_undef_instr);
+ if (user_mode(regs)) {
+ coresight_abort();
+ pr_debug("coresight_event: addr: %lu, fsr:%u", addr, fsr);
}
- pr_info("control_trace: Module Registered\n");
+}
+
+static int event_abort_register(void)
+{
+ int ret;
+
+ ret = register_trace_user_fault(event_abort_user_fault, NULL);
+ if (ret)
+ goto err_usr_fault;
+ ret = register_trace_undef_instr(event_abort_undef_instr, NULL);
+ if (ret)
+ goto err_undef_instr;
+ ret = register_trace_unhandled_abort(event_abort_unhandled_abort, NULL);
+ if (ret)
+ goto err_unhandled_abort;
+
+ return 0;
+
+err_unhandled_abort:
+ unregister_trace_undef_instr(event_abort_undef_instr, NULL);
+err_undef_instr:
+ unregister_trace_user_fault(event_abort_user_fault, NULL);
+err_usr_fault:
+ return ret;
+}
+
+static void event_abort_unregister(void)
+{
+ unregister_trace_user_fault(event_abort_user_fault, NULL);
+ unregister_trace_undef_instr(event_abort_undef_instr, NULL);
+ unregister_trace_unhandled_abort(event_abort_unhandled_abort, NULL);
+}
+
+static int event_abort_set(const char *val, struct kernel_param *kp)
+{
+ int ret;
+
+ ret = param_set_int(val, kp);
+ if (ret) {
+ pr_err("coresight_event: error setting value %d\n", ret);
+ return ret;
+ }
+
+ if (event_abort_enable)
+ ret = event_abort_register();
+ else
+ event_abort_unregister();
+
+ return ret;
+}
+
+static int __init event_init(void)
+{
return 0;
}
+module_init(event_init);
-module_init(control_trace_init);
-
-static void __exit control_trace_exit(void)
+static void __exit event_exit(void)
{
- unregister_trace_user_fault(abort_coresight_tracing, NULL);
- unregister_trace_undef_instr(abort_tracing_undef_instr, NULL);
- pr_info("control_trace: Module Removed\n");
}
-
-module_exit(control_trace_exit);
+module_exit(event_exit);
MODULE_LICENSE("GPL v2");
-MODULE_DESCRIPTION("Kernel Module to abort tracing");
+MODULE_DESCRIPTION("Coresight Event driver to abort tracing");
diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c
index e96d577..3a964a5 100644
--- a/drivers/cpufreq/cpufreq_interactive.c
+++ b/drivers/cpufreq/cpufreq_interactive.c
@@ -1250,6 +1250,8 @@
pcpu->hispeed_validate_time =
pcpu->floor_validate_time;
down_write(&pcpu->enable_sem);
+ del_timer_sync(&pcpu->cpu_timer);
+ del_timer_sync(&pcpu->cpu_slack_timer);
cpufreq_interactive_timer_start(j);
pcpu->governor_enabled = 1;
up_write(&pcpu->enable_sem);
diff --git a/drivers/crypto/msm/ota_crypto.c b/drivers/crypto/msm/ota_crypto.c
index 5051a3a..6ecf5b2 100644
--- a/drivers/crypto/msm/ota_crypto.c
+++ b/drivers/crypto/msm/ota_crypto.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2010-2014, 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
@@ -674,50 +674,50 @@
struct ota_qce_dev *p;
pstat = &_qcota_stat;
- len = snprintf(_debug_read_buf, DEBUG_MAX_RW_BUF - 1,
+ len = scnprintf(_debug_read_buf, DEBUG_MAX_RW_BUF - 1,
"\nQualcomm OTA crypto accelerator Statistics:\n");
- len += snprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
+ len += scnprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
" F8 request : %d\n",
pstat->f8_req);
- len += snprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
+ len += scnprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
" F8 operation success : %d\n",
pstat->f8_op_success);
- len += snprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
+ len += scnprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
" F8 operation fail : %d\n",
pstat->f8_op_fail);
- len += snprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
+ len += scnprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
" F8 MP request : %d\n",
pstat->f8_mp_req);
- len += snprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
+ len += scnprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
" F8 MP operation success: %d\n",
pstat->f8_mp_op_success);
- len += snprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
+ len += scnprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
" F8 MP operation fail : %d\n",
pstat->f8_mp_op_fail);
- len += snprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
+ len += scnprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
" F9 request : %d\n",
pstat->f9_req);
- len += snprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
+ len += scnprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
" F9 operation success : %d\n",
pstat->f9_op_success);
- len += snprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
+ len += scnprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
" F9 operation fail : %d\n",
pstat->f9_op_fail);
spin_lock_irqsave(&podev->lock, flags);
list_for_each_entry(p, &podev->qce_dev, qlist) {
- len += snprintf(
+ len += scnprintf(
_debug_read_buf + len,
DEBUG_MAX_RW_BUF - len - 1,
" Engine %d Req : %d\n",
p->unit,
p->totalReq
);
- len += snprintf(
+ len += scnprintf(
_debug_read_buf + len,
DEBUG_MAX_RW_BUF - len - 1,
" Engine %d Req Error : %d\n",
diff --git a/drivers/crypto/msm/qce.c b/drivers/crypto/msm/qce.c
index 8037187..d11a831 100644
--- a/drivers/crypto/msm/qce.c
+++ b/drivers/crypto/msm/qce.c
@@ -1,6 +1,6 @@
/* Qualcomm Crypto Engine driver.
*
- * Copyright (c) 2010-2013, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2010-2014, 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
@@ -422,7 +422,7 @@
{
unsigned n;
- n = len / sizeof(uint32_t) ;
+ n = len / sizeof(uint32_t);
for (; n > 0; n--) {
*iv = ((*b << 24) & 0xff000000) |
(((*(b+1)) << 16) & 0xff0000) |
@@ -436,12 +436,12 @@
if (n == 3) {
*iv = ((*b << 24) & 0xff000000) |
(((*(b+1)) << 16) & 0xff0000) |
- (((*(b+2)) << 8) & 0xff00) ;
+ (((*(b+2)) << 8) & 0xff00);
} else if (n == 2) {
*iv = ((*b << 24) & 0xff000000) |
- (((*(b+1)) << 16) & 0xff0000) ;
+ (((*(b+1)) << 16) & 0xff0000);
} else if (n == 1) {
- *iv = ((*b << 24) & 0xff000000) ;
+ *iv = ((*b << 24) & 0xff000000);
}
}
@@ -1819,7 +1819,7 @@
}
};
-static int _ce_f9_setup(struct qce_device *pce_dev, struct qce_f9_req * req)
+static int _ce_f9_setup(struct qce_device *pce_dev, struct qce_f9_req *req)
{
uint32_t cfg;
uint32_t ikey[OTA_KEY_SIZE/sizeof(uint32_t)];
@@ -1846,7 +1846,7 @@
if (req->algorithm == QCE_OTA_ALGO_KASUMI)
cfg |= (CRYPTO_AUTH_SIZE_UIA1 << CRYPTO_AUTH_SIZE);
else
- cfg |= (CRYPTO_AUTH_SIZE_UIA2 << CRYPTO_AUTH_SIZE) ;
+ cfg |= (CRYPTO_AUTH_SIZE_UIA2 << CRYPTO_AUTH_SIZE);
if (req->direction == QCE_OTA_DIR_DOWNLINK)
cfg |= 1 << CRYPTO_F9_DIRECTION;
@@ -1885,7 +1885,7 @@
if (req->algorithm == QCE_OTA_ALGO_KASUMI)
cfg |= (CRYPTO_ENCR_KEY_SZ_UEA1 << CRYPTO_ENCR_KEY_SZ);
else
- cfg |= (CRYPTO_ENCR_KEY_SZ_UEA2 << CRYPTO_ENCR_KEY_SZ) ;
+ cfg |= (CRYPTO_ENCR_KEY_SZ_UEA2 << CRYPTO_ENCR_KEY_SZ);
if (key_stream_mode)
cfg |= 1 << CRYPTO_F8_KEYSTREAM_ENABLE;
if (req->direction == QCE_OTA_DIR_DOWNLINK)
@@ -1932,6 +1932,9 @@
return 0;
};
+struct qce_pm_table qce_pm_table = {NULL, NULL};
+EXPORT_SYMBOL(qce_pm_table);
+
int qce_aead_req(void *handle, struct qce_req *q_req)
{
struct qce_device *pce_dev = (struct qce_device *) handle;
@@ -2555,7 +2558,7 @@
rc = _ce_f8_setup(pce_dev, req, false, num_pkt, cipher_start,
cipher_size);
if (rc)
- goto bad ;
+ goto bad;
/* setup for callback, and issue command to adm */
pce_dev->areq = cookie;
diff --git a/drivers/crypto/msm/qce.h b/drivers/crypto/msm/qce.h
index 73438d0..6ba5063 100644
--- a/drivers/crypto/msm/qce.h
+++ b/drivers/crypto/msm/qce.h
@@ -123,6 +123,7 @@
bool use_sw_hmac_algo;
bool use_sw_aes_ccm_algo;
bool clk_mgmt_sus_res;
+ unsigned int ce_device;
};
/* Sha operation parameters */
@@ -165,6 +166,13 @@
unsigned int flags;
};
+struct qce_pm_table {
+ int (*suspend)(void *handle);
+ int (*resume)(void *handle);
+};
+
+extern struct qce_pm_table qce_pm_table;
+
void *qce_open(struct platform_device *pdev, int *rc);
int qce_close(void *handle);
int qce_aead_req(void *handle, struct qce_req *req);
diff --git a/drivers/crypto/msm/qce50.c b/drivers/crypto/msm/qce50.c
index 62fd948..ad7dd31 100644
--- a/drivers/crypto/msm/qce50.c
+++ b/drivers/crypto/msm/qce50.c
@@ -127,7 +127,7 @@
{
unsigned n;
- n = len / sizeof(uint32_t) ;
+ n = len / sizeof(uint32_t);
for (; n > 0; n--) {
*iv = ((*b << 24) & 0xff000000) |
(((*(b+1)) << 16) & 0xff0000) |
@@ -141,12 +141,12 @@
if (n == 3) {
*iv = ((*b << 24) & 0xff000000) |
(((*(b+1)) << 16) & 0xff0000) |
- (((*(b+2)) << 8) & 0xff00) ;
+ (((*(b+2)) << 8) & 0xff00);
} else if (n == 2) {
*iv = ((*b << 24) & 0xff000000) |
- (((*(b+1)) << 16) & 0xff0000) ;
+ (((*(b+1)) << 16) & 0xff0000);
} else if (n == 1) {
- *iv = ((*b << 24) & 0xff000000) ;
+ *iv = ((*b << 24) & 0xff000000);
}
}
@@ -223,12 +223,14 @@
pce_dev->ce_sps.ce_burst_size = MAX_CE_BAM_BURST_SIZE;
dev_info(pce_dev->pdev,
+ "CE device = 0x%x\n, "
"IO base, CE = 0x%x\n, "
"Consumer (IN) PIPE %d, "
"Producer (OUT) PIPE %d\n"
"IO base BAM = 0x%x\n"
"BAM IRQ %d\n"
"Engines Availability = 0x%x\n",
+ (uint32_t) pce_dev->ce_sps.ce_device,
(uint32_t) pce_dev->iobase,
pce_dev->ce_sps.dest_pipe_index,
pce_dev->ce_sps.src_pipe_index,
@@ -2643,7 +2645,7 @@
struct sps_bam_props bam = {0};
struct bam_registration_info *pbam = NULL;
struct bam_registration_info *p;
- uint32_t bam_cfg = 0 ;
+ uint32_t bam_cfg = 0;
mutex_lock(&bam_register_lock);
@@ -2953,7 +2955,7 @@
(*cmd_ptr)->mask = 0xFFFFFFFF;
if (populate != NULL)
*populate = *cmd_ptr;
- (*cmd_ptr)++ ;
+ (*cmd_ptr)++;
}
static int _setup_cipher_aes_cmdlistptrs(struct qce_device *pdev,
@@ -4403,6 +4405,65 @@
return rc;
}
+static int _qce_suspend(void *handle)
+{
+ struct qce_device *pce_dev = (struct qce_device *)handle;
+ struct sps_pipe *sps_pipe_info;
+
+ if (handle == NULL)
+ return -ENODEV;
+
+ qce_enable_clk(pce_dev);
+
+ sps_pipe_info = pce_dev->ce_sps.consumer.pipe;
+ sps_disconnect(sps_pipe_info);
+
+ sps_pipe_info = pce_dev->ce_sps.producer.pipe;
+ sps_disconnect(sps_pipe_info);
+
+ qce_disable_clk(pce_dev);
+ return 0;
+}
+
+static int _qce_resume(void *handle)
+{
+ struct qce_device *pce_dev = (struct qce_device *)handle;
+ struct sps_pipe *sps_pipe_info;
+ struct sps_connect *sps_connect_info;
+ int rc;
+
+ if (handle == NULL)
+ return -ENODEV;
+
+ qce_enable_clk(pce_dev);
+
+ sps_pipe_info = pce_dev->ce_sps.consumer.pipe;
+ sps_connect_info = &pce_dev->ce_sps.consumer.connect;
+ memset(sps_connect_info->desc.base, 0x00, sps_connect_info->desc.size);
+ rc = sps_connect(sps_pipe_info, sps_connect_info);
+ if (rc) {
+ pr_err("sps_connect() fail pipe_handle=0x%x, rc = %d\n",
+ (u32)sps_pipe_info, rc);
+ return rc;
+ }
+ sps_pipe_info = pce_dev->ce_sps.producer.pipe;
+ sps_connect_info = &pce_dev->ce_sps.producer.connect;
+ memset(sps_connect_info->desc.base, 0x00, sps_connect_info->desc.size);
+ rc = sps_connect(sps_pipe_info, sps_connect_info);
+ if (rc)
+ pr_err("sps_connect() fail pipe_handle=0x%x, rc = %d\n",
+ (u32)sps_pipe_info, rc);
+
+ pce_dev->ce_sps.out_transfer.user = pce_dev->ce_sps.producer.pipe;
+ pce_dev->ce_sps.in_transfer.user = pce_dev->ce_sps.consumer.pipe;
+
+ qce_disable_clk(pce_dev);
+ return rc;
+}
+
+struct qce_pm_table qce_pm_table = {_qce_suspend, _qce_resume};
+EXPORT_SYMBOL(qce_pm_table);
+
int qce_aead_req(void *handle, struct qce_req *q_req)
{
struct qce_device *pce_dev;
@@ -5116,6 +5177,15 @@
} else {
pr_warn("bam_pipe_pair=0x%x", pce_dev->ce_sps.pipe_pair_index);
}
+ if (of_property_read_u32((&pdev->dev)->of_node,
+ "qcom,ce-device",
+ &pce_dev->ce_sps.ce_device)) {
+ pr_err("Fail to get CE device information.\n");
+ return -EINVAL;
+ } else {
+ pr_warn("ce-device =0x%x", pce_dev->ce_sps.ce_device);
+ }
+
pce_dev->ce_sps.dest_pipe_index = 2 * pce_dev->ce_sps.pipe_pair_index;
pce_dev->ce_sps.src_pipe_index = pce_dev->ce_sps.dest_pipe_index + 1;
@@ -5453,9 +5523,11 @@
pce_dev->use_sw_hmac_algo;
ce_support->use_sw_aes_ccm_algo =
pce_dev->use_sw_aes_ccm_algo;
+ ce_support->ce_device = pce_dev->ce_sps.ce_device;
return 0;
}
EXPORT_SYMBOL(qce_hw_support);
+
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Crypto Engine driver");
diff --git a/drivers/crypto/msm/qce50.h b/drivers/crypto/msm/qce50.h
index fc387aa..78bfdee 100644
--- a/drivers/crypto/msm/qce50.h
+++ b/drivers/crypto/msm/qce50.h
@@ -176,6 +176,7 @@
struct sps_event_notify notify;
struct scatterlist *src;
struct scatterlist *dst;
+ uint32_t ce_device;
unsigned int pipe_pair_index;
unsigned int src_pipe_index;
unsigned int dest_pipe_index;
diff --git a/drivers/crypto/msm/qcrypto.c b/drivers/crypto/msm/qcrypto.c
index 48dc6ec..c247189 100644
--- a/drivers/crypto/msm/qcrypto.c
+++ b/drivers/crypto/msm/qcrypto.c
@@ -58,63 +58,83 @@
#define QCRYPTO_HIGH_BANDWIDTH_TIMEOUT 1000
+enum qcrypto_bus_state {
+ BUS_NO_BANDWIDTH = 0,
+ BUS_HAS_BANDWIDTH,
+ BUS_BANDWIDTH_RELEASING,
+ BUS_BANDWIDTH_ALLOCATING,
+ BUS_SUSPENDED,
+};
+
struct crypto_stat {
- u32 aead_sha1_aes_enc;
- u32 aead_sha1_aes_dec;
- u32 aead_sha1_des_enc;
- u32 aead_sha1_des_dec;
- u32 aead_sha1_3des_enc;
- u32 aead_sha1_3des_dec;
- u32 aead_ccm_aes_enc;
- u32 aead_ccm_aes_dec;
- u32 aead_rfc4309_ccm_aes_enc;
- u32 aead_rfc4309_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;
- u32 ablk_cipher_des_dec;
- u32 ablk_cipher_3des_enc;
- u32 ablk_cipher_3des_dec;
- u32 ablk_cipher_op_success;
- u32 ablk_cipher_op_fail;
- u32 sha1_digest;
- u32 sha256_digest;
- u32 sha_op_success;
- u32 sha_op_fail;
- u32 sha1_hmac_digest;
- u32 sha256_hmac_digest;
- u32 sha_hmac_op_success;
- u32 sha_hmac_op_fail;
+ u64 aead_sha1_aes_enc;
+ u64 aead_sha1_aes_dec;
+ u64 aead_sha1_des_enc;
+ u64 aead_sha1_des_dec;
+ u64 aead_sha1_3des_enc;
+ u64 aead_sha1_3des_dec;
+ u64 aead_ccm_aes_enc;
+ u64 aead_ccm_aes_dec;
+ u64 aead_rfc4309_ccm_aes_enc;
+ u64 aead_rfc4309_ccm_aes_dec;
+ u64 aead_op_success;
+ u64 aead_op_fail;
+ u64 aead_bad_msg;
+ u64 ablk_cipher_aes_enc;
+ u64 ablk_cipher_aes_dec;
+ u64 ablk_cipher_des_enc;
+ u64 ablk_cipher_des_dec;
+ u64 ablk_cipher_3des_enc;
+ u64 ablk_cipher_3des_dec;
+ u64 ablk_cipher_op_success;
+ u64 ablk_cipher_op_fail;
+ u64 sha1_digest;
+ u64 sha256_digest;
+ u64 sha_op_success;
+ u64 sha_op_fail;
+ u64 sha1_hmac_digest;
+ u64 sha256_hmac_digest;
+ u64 sha_hmac_op_success;
+ u64 sha_hmac_op_fail;
};
static struct crypto_stat _qcrypto_stat;
static struct dentry *_debug_dent;
static char _debug_read_buf[DEBUG_MAX_RW_BUF];
+static bool _qcrypto_init_assign;
struct crypto_priv;
struct crypto_engine {
struct list_head elist;
void *qce; /* qce handle */
struct platform_device *pdev; /* platform device */
struct crypto_async_request *req; /* current active request */
+ struct qcrypto_resp_ctx *arsp; /* rsp associcated with req */
+ int res; /* execution result */
struct crypto_priv *pcp;
struct tasklet_struct done_tasklet;
uint32_t bus_scale_handle;
struct crypto_queue req_queue; /*
* request queue for those requests
- * that have this engine assgined
+ * that have this engine assigned
* waiting to be executed
*/
- u32 total_req;
- u32 err_req;
+ u64 total_req;
+ u64 err_req;
u32 unit;
- int res; /* execution result */
+ u32 ce_device;
unsigned int signature;
- uint32_t high_bw_req_count;
- bool high_bw_req;
- struct timer_list bw_scale_down_timer;
- struct work_struct low_bw_req_ws;
+
+ enum qcrypto_bus_state bw_state;
+ bool high_bw_req;
+ struct timer_list bw_reaper_timer;
+ struct work_struct bw_reaper_ws;
+ struct work_struct bw_allocate_ws;
+
+ /* engine execution sequence number */
+ u32 active_seq;
+ /* last QCRYPTO_HIGH_BANDWIDTH_TIMEOUT active_seq */
+ u32 last_active_seq;
+
+ bool check_flag;
};
struct crypto_priv {
@@ -124,7 +144,7 @@
/* CE features/algorithms supported by HW engine*/
struct ce_hw_support ce_support;
- /* the lock protects queue and req*/
+ /* the lock protects crypto queue and req */
spinlock_t lock;
/* list of registered algorithms */
@@ -138,7 +158,13 @@
struct list_head engine_list; /* list of qcrypto engines */
int32_t total_units; /* total units of engines */
struct mutex engine_lock;
+
struct crypto_engine *next_engine; /* next assign engine */
+ struct crypto_queue req_queue; /*
+ * request queue for those requests
+ * that waiting for an available
+ * engine.
+ */
};
static struct crypto_priv qcrypto_dev;
static struct crypto_engine *_qcrypto_static_assign_engine(
@@ -173,6 +199,29 @@
#endif
}
+static struct crypto_engine *_qrypto_find_pengine_device(struct crypto_priv *cp,
+ unsigned int device)
+{
+ struct crypto_engine *entry = NULL;
+ unsigned long flags;
+
+ spin_lock_irqsave(&cp->lock, flags);
+ list_for_each_entry(entry, &cp->engine_list, elist) {
+ if (entry->ce_device == device)
+ break;
+ }
+ spin_unlock_irqrestore(&cp->lock, flags);
+
+ if (((entry != NULL) && (entry->ce_device != device)) ||
+ (entry == NULL)) {
+ pr_err("Device node for CE device %d NOT FOUND!!\n",
+ device);
+ return NULL;
+ }
+
+ return entry;
+}
+
static void qcrypto_unlock_ce(struct work_struct *work)
{
int response = 0;
@@ -237,6 +286,11 @@
#define QCRYPTO_CCM4309_NONCE_LEN 3
struct qcrypto_cipher_ctx {
+ struct list_head rsp_queue; /* response queue */
+ struct crypto_engine *pengine; /* fixed engine assigned to this tfm */
+ struct crypto_priv *cp;
+ unsigned int flags;
+
u8 auth_key[QCRYPTO_MAX_KEY_SIZE];
u8 iv[QCRYPTO_MAX_IV_LENGTH];
@@ -246,13 +300,18 @@
unsigned int authsize;
unsigned int auth_key_len;
- struct crypto_priv *cp;
- unsigned int flags;
- struct crypto_engine *pengine; /* fixed engine assigned */
u8 ccm4309_nonce[QCRYPTO_CCM4309_NONCE_LEN];
};
+struct qcrypto_resp_ctx {
+ struct list_head list;
+ struct crypto_async_request *async_req; /* async req */
+ int res; /* execution result */
+};
+
struct qcrypto_cipher_req_ctx {
+ struct qcrypto_resp_ctx rsp_entry;/* rsp entry. */
+ struct crypto_engine *pengine; /* engine assigned to this request */
u8 *iv;
u8 rfc4309_iv[QCRYPTO_MAX_IV_LENGTH];
unsigned int ivsize;
@@ -277,6 +336,8 @@
#define SHA_MAX_STATE_SIZE (SHA256_DIGEST_SIZE / sizeof(u32))
#define SHA_MAX_DIGEST_SIZE SHA256_DIGEST_SIZE
+#define MSM_QCRYPTO_REQ_QUEUE_LENGTH 50
+
static uint8_t _std_init_vector_sha1_uint8[] = {
0x67, 0x45, 0x23, 0x01, 0xEF, 0xCD, 0xAB, 0x89,
0x98, 0xBA, 0xDC, 0xFE, 0x10, 0x32, 0x54, 0x76,
@@ -292,18 +353,21 @@
};
struct qcrypto_sha_ctx {
+ struct list_head rsp_queue; /* response queue */
+ struct crypto_engine *pengine; /* fixed engine assigned to this tfm */
+ struct crypto_priv *cp;
+ unsigned int flags;
enum qce_hash_alg_enum alg;
uint32_t diglen;
uint32_t authkey_in_len;
uint8_t authkey[SHA_MAX_BLOCK_SIZE];
struct ahash_request *ahash_req;
struct completion ahash_req_complete;
- struct crypto_priv *cp;
- unsigned int flags;
- struct crypto_engine *pengine; /* fixed engine assigned */
};
struct qcrypto_sha_req_ctx {
+ struct qcrypto_resp_ctx rsp_entry;/* rsp entry. */
+ struct crypto_engine *pengine; /* engine assigned to this request */
struct scatterlist *src;
uint32_t nbytes;
@@ -386,7 +450,7 @@
{
int ret = 0;
- if (high_bw_req && pengine->high_bw_req == false) {
+ if (high_bw_req) {
pm_stay_awake(&pengine->pdev->dev);
ret = qce_enable_clk(pengine->qce);
if (ret) {
@@ -401,8 +465,10 @@
qce_disable_clk(pengine->qce);
goto clk_err;
}
- pengine->high_bw_req = true;
- } else if (high_bw_req == false && pengine->high_bw_req == true) {
+
+
+ } else {
+
ret = msm_bus_scale_client_update_request(
pengine->bus_scale_handle, 0);
if (ret) {
@@ -420,7 +486,6 @@
__func__);
goto clk_err;
}
- pengine->high_bw_req = false;
pm_relax(&pengine->pdev->dev);
}
return;
@@ -430,55 +495,108 @@
}
-static void qcrypto_bw_scale_down_timer_callback(unsigned long data)
+static void qcrypto_bw_reaper_timer_callback(unsigned long data)
{
struct crypto_engine *pengine = (struct crypto_engine *)data;
- schedule_work(&pengine->low_bw_req_ws);
+ schedule_work(&pengine->bw_reaper_ws);
return;
}
static void qcrypto_bw_set_timeout(struct crypto_engine *pengine)
{
- del_timer_sync(&(pengine->bw_scale_down_timer));
- pengine->bw_scale_down_timer.data =
+ pengine->bw_reaper_timer.data =
(unsigned long)(pengine);
- pengine->bw_scale_down_timer.expires = jiffies +
+ pengine->bw_reaper_timer.expires = jiffies +
msecs_to_jiffies(QCRYPTO_HIGH_BANDWIDTH_TIMEOUT);
- add_timer(&(pengine->bw_scale_down_timer));
+ add_timer(&(pengine->bw_reaper_timer));
}
-static void qcrypto_ce_bw_scaling_req(struct crypto_engine *pengine,
- bool high_bw_req)
+static void qcrypto_ce_bw_allocate_req(struct crypto_engine *pengine)
{
- mutex_lock(&pengine->pcp->engine_lock);
- if (high_bw_req) {
- if (pengine->high_bw_req_count == 0)
- qcrypto_ce_set_bus(pengine, true);
- pengine->high_bw_req_count++;
- } else {
- pengine->high_bw_req_count--;
- if (pengine->high_bw_req_count == 0)
- qcrypto_bw_set_timeout(pengine);
- }
- mutex_unlock(&pengine->pcp->engine_lock);
-}
-
-static void qcrypto_low_bw_req_work(struct work_struct *work)
-{
- struct crypto_engine *pengine = container_of(work,
- struct crypto_engine, low_bw_req_ws);
-
- mutex_lock(&pengine->pcp->engine_lock);
- if (pengine->high_bw_req_count == 0)
- qcrypto_ce_set_bus(pengine, false);
- mutex_unlock(&pengine->pcp->engine_lock);
+ schedule_work(&pengine->bw_allocate_ws);
}
static int _start_qcrypto_process(struct crypto_priv *cp,
struct crypto_engine *pengine);
+static void qcrypto_bw_allocate_work(struct work_struct *work)
+{
+ struct crypto_engine *pengine = container_of(work,
+ struct crypto_engine, bw_allocate_ws);
+ unsigned long flags;
+ struct crypto_priv *cp = pengine->pcp;
+
+ spin_lock_irqsave(&cp->lock, flags);
+ pengine->bw_state = BUS_BANDWIDTH_ALLOCATING;
+ spin_unlock_irqrestore(&cp->lock, flags);
+
+ qcrypto_ce_set_bus(pengine, true);
+
+ spin_lock_irqsave(&cp->lock, flags);
+ pengine->bw_state = BUS_HAS_BANDWIDTH;
+ pengine->high_bw_req = false;
+ pengine->active_seq++;
+ pengine->check_flag = true;
+ spin_unlock_irqrestore(&cp->lock, flags);
+ _start_qcrypto_process(cp, pengine);
+};
+
+static void qcrypto_bw_reaper_work(struct work_struct *work)
+{
+ struct crypto_engine *pengine = container_of(work,
+ struct crypto_engine, bw_reaper_ws);
+ struct crypto_priv *cp = pengine->pcp;
+ unsigned long flags;
+ u32 active_seq;
+ bool restart = false;
+
+ spin_lock_irqsave(&cp->lock, flags);
+ active_seq = pengine->active_seq;
+ if (pengine->bw_state == BUS_HAS_BANDWIDTH &&
+ (active_seq == pengine->last_active_seq)) {
+
+ /* check if engine is stuck */
+ if (pengine->req) {
+ if (pengine->check_flag)
+ dev_err(&pengine->pdev->dev,
+ "The engine appears to be stuck seq %d req %p.\n",
+ active_seq, pengine->req);
+ pengine->check_flag = false;
+ goto ret;
+ }
+ if (cp->platform_support.bus_scale_table == NULL)
+ goto ret;
+ pengine->bw_state = BUS_BANDWIDTH_RELEASING;
+ spin_unlock_irqrestore(&cp->lock, flags);
+
+ qcrypto_ce_set_bus(pengine, false);
+
+ spin_lock_irqsave(&cp->lock, flags);
+
+ if (pengine->high_bw_req == true) {
+ /* we got request while we are disabling clock */
+ pengine->bw_state = BUS_BANDWIDTH_ALLOCATING;
+ spin_unlock_irqrestore(&cp->lock, flags);
+
+ qcrypto_ce_set_bus(pengine, true);
+
+ spin_lock_irqsave(&cp->lock, flags);
+ pengine->bw_state = BUS_HAS_BANDWIDTH;
+ pengine->high_bw_req = false;
+ restart = true;
+ } else
+ pengine->bw_state = BUS_NO_BANDWIDTH;
+ }
+ret:
+ pengine->last_active_seq = active_seq;
+ spin_unlock_irqrestore(&cp->lock, flags);
+ if (restart)
+ _start_qcrypto_process(cp, pengine);
+ qcrypto_bw_set_timeout(pengine);
+}
+
static int qcrypto_count_sg(struct scatterlist *sg, int nbytes)
{
int i;
@@ -573,11 +691,13 @@
/* random first IV */
get_random_bytes(ctx->iv, QCRYPTO_MAX_IV_LENGTH);
- ctx->pengine = _qcrypto_static_assign_engine(ctx->cp);
- if (ctx->pengine == NULL)
- return -ENODEV;
- if (ctx->cp->platform_support.bus_scale_table != NULL)
- qcrypto_ce_bw_scaling_req(ctx->pengine, true);
+ if (_qcrypto_init_assign) {
+ ctx->pengine = _qcrypto_static_assign_engine(ctx->cp);
+ if (ctx->pengine == NULL)
+ return -ENODEV;
+ } else
+ ctx->pengine = NULL;
+ INIT_LIST_HEAD(&ctx->rsp_queue);
return 0;
};
@@ -595,11 +715,13 @@
sha_ctx->cp = q_alg->cp;
sha_ctx->flags = 0;
sha_ctx->ahash_req = NULL;
- sha_ctx->pengine = _qcrypto_static_assign_engine(sha_ctx->cp);
- if (sha_ctx->pengine == NULL)
- return -ENODEV;
- if (sha_ctx->cp->platform_support.bus_scale_table != NULL)
- qcrypto_ce_bw_scaling_req(sha_ctx->pengine, true);
+ if (_qcrypto_init_assign) {
+ sha_ctx->pengine = _qcrypto_static_assign_engine(sha_ctx->cp);
+ if (sha_ctx->pengine == NULL)
+ return -ENODEV;
+ } else
+ sha_ctx->pengine = NULL;
+ INIT_LIST_HEAD(&sha_ctx->rsp_queue);
return 0;
};
@@ -607,13 +729,12 @@
{
struct qcrypto_sha_ctx *sha_ctx = crypto_tfm_ctx(tfm);
+ if (!list_empty(&sha_ctx->rsp_queue))
+ pr_err("_qcrypto_ahash_cra_exit: requests still outstanding");
if (sha_ctx->ahash_req != NULL) {
ahash_request_free(sha_ctx->ahash_req);
sha_ctx->ahash_req = NULL;
}
- if (sha_ctx->pengine &&
- sha_ctx->cp->platform_support.bus_scale_table != NULL)
- qcrypto_ce_bw_scaling_req(sha_ctx->pengine, false);
};
@@ -662,16 +783,16 @@
{
struct qcrypto_cipher_ctx *ctx = crypto_tfm_ctx(tfm);
- if (ctx->pengine && ctx->cp->platform_support.bus_scale_table != NULL)
- qcrypto_ce_bw_scaling_req(ctx->pengine, false);
+ if (!list_empty(&ctx->rsp_queue))
+ pr_err("_qcrypto__cra_ablkcipher_exit: requests still outstanding");
};
static void _qcrypto_cra_aead_exit(struct crypto_tfm *tfm)
{
struct qcrypto_cipher_ctx *ctx = crypto_tfm_ctx(tfm);
- if (ctx->pengine && ctx->cp->platform_support.bus_scale_table != NULL)
- qcrypto_ce_bw_scaling_req(ctx->pengine, false);
+ if (!list_empty(&ctx->rsp_queue))
+ pr_err("_qcrypto__cra_aead_exit: requests still outstanding");
};
static int _disp_stats(int id)
@@ -684,117 +805,117 @@
pstat = &_qcrypto_stat;
len = scnprintf(_debug_read_buf, DEBUG_MAX_RW_BUF - 1,
- "\nQualcomm crypto accelerator %d Statistics:\n",
+ "\nQualcomm crypto accelerator %d Statistics\n",
id + 1);
len += scnprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
- " ABLK AES CIPHER encryption : %d\n",
+ " ABLK AES CIPHER encryption : %llu\n",
pstat->ablk_cipher_aes_enc);
len += scnprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
- " ABLK AES CIPHER decryption : %d\n",
+ " ABLK AES CIPHER decryption : %llu\n",
pstat->ablk_cipher_aes_dec);
len += scnprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
- " ABLK DES CIPHER encryption : %d\n",
+ " ABLK DES CIPHER encryption : %llu\n",
pstat->ablk_cipher_des_enc);
len += scnprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
- " ABLK DES CIPHER decryption : %d\n",
+ " ABLK DES CIPHER decryption : %llu\n",
pstat->ablk_cipher_des_dec);
len += scnprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
- " ABLK 3DES CIPHER encryption : %d\n",
+ " ABLK 3DES CIPHER encryption : %llu\n",
pstat->ablk_cipher_3des_enc);
len += scnprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
- " ABLK 3DES CIPHER decryption : %d\n",
+ " ABLK 3DES CIPHER decryption : %llu\n",
pstat->ablk_cipher_3des_dec);
len += scnprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
- " ABLK CIPHER operation success: %d\n",
+ " ABLK CIPHER operation success : %llu\n",
pstat->ablk_cipher_op_success);
len += scnprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
- " ABLK CIPHER operation fail : %d\n",
+ " ABLK CIPHER operation fail : %llu\n",
pstat->ablk_cipher_op_fail);
len += scnprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
- " AEAD SHA1-AES encryption : %d\n",
+ " AEAD SHA1-AES encryption : %llu\n",
pstat->aead_sha1_aes_enc);
len += scnprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
- " AEAD SHA1-AES decryption : %d\n",
+ " AEAD SHA1-AES decryption : %llu\n",
pstat->aead_sha1_aes_dec);
len += scnprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
- " AEAD SHA1-DES encryption : %d\n",
+ " AEAD SHA1-DES encryption : %llu\n",
pstat->aead_sha1_des_enc);
len += scnprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
- " AEAD SHA1-DES decryption : %d\n",
+ " AEAD SHA1-DES decryption : %llu\n",
pstat->aead_sha1_des_dec);
len += scnprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
- " AEAD SHA1-3DES encryption : %d\n",
+ " AEAD SHA1-3DES encryption : %llu\n",
pstat->aead_sha1_3des_enc);
len += scnprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
- " AEAD SHA1-3DES decryption : %d\n",
+ " AEAD SHA1-3DES decryption : %llu\n",
pstat->aead_sha1_3des_dec);
len += scnprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
- " AEAD CCM-AES encryption : %d\n",
+ " AEAD CCM-AES encryption : %llu\n",
pstat->aead_ccm_aes_enc);
len += scnprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
- " AEAD CCM-AES decryption : %d\n",
+ " AEAD CCM-AES decryption : %llu\n",
pstat->aead_ccm_aes_dec);
len += scnprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
- " AEAD RFC4309-CCM-AES encryption : %d\n",
+ " AEAD RFC4309-CCM-AES encryption : %llu\n",
pstat->aead_rfc4309_ccm_aes_enc);
len += scnprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
- " AEAD RFC4309-CCM-AES decryption : %d\n",
+ " AEAD RFC4309-CCM-AES decryption : %llu\n",
pstat->aead_rfc4309_ccm_aes_dec);
len += scnprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
- " AEAD operation success : %d\n",
+ " AEAD operation success : %llu\n",
pstat->aead_op_success);
len += scnprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
- " AEAD operation fail : %d\n",
+ " AEAD operation fail : %llu\n",
pstat->aead_op_fail);
len += scnprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
- " AEAD bad message : %d\n",
+ " AEAD bad message : %llu\n",
pstat->aead_bad_msg);
len += scnprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
- " SHA1 digest : %d\n",
+ " SHA1 digest : %llu\n",
pstat->sha1_digest);
len += scnprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
- " SHA256 digest : %d\n",
+ " SHA256 digest : %llu\n",
pstat->sha256_digest);
len += scnprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
- " SHA operation fail : %d\n",
+ " SHA operation fail : %llu\n",
pstat->sha_op_fail);
len += scnprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
- " SHA operation success : %d\n",
+ " SHA operation success : %llu\n",
pstat->sha_op_success);
len += scnprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
- " SHA1 HMAC digest : %d\n",
+ " SHA1 HMAC digest : %llu\n",
pstat->sha1_hmac_digest);
len += scnprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
- " SHA256 HMAC digest : %d\n",
+ " SHA256 HMAC digest : %llu\n",
pstat->sha256_hmac_digest);
len += scnprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
- " SHA HMAC operation fail : %d\n",
+ " SHA HMAC operation fail : %llu\n",
pstat->sha_hmac_op_fail);
len += scnprintf(_debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
- " SHA HMAC operation success : %d\n",
+ " SHA HMAC operation success : %llu\n",
pstat->sha_hmac_op_success);
spin_lock_irqsave(&cp->lock, flags);
list_for_each_entry(pe, &cp->engine_list, elist) {
len += snprintf(
_debug_read_buf + len,
DEBUG_MAX_RW_BUF - len - 1,
- " Engine %d Req : %d\n",
+ " Engine %4d Req : %llu\n",
pe->unit,
pe->total_req
);
len += snprintf(
_debug_read_buf + len,
DEBUG_MAX_RW_BUF - len - 1,
- " Engine %d Req Error : %d\n",
+ " Engine %4d Req Error : %llu\n",
pe->unit,
pe->err_req
);
@@ -821,9 +942,9 @@
cp->total_units--;
tasklet_kill(&pengine->done_tasklet);
- cancel_work_sync(&pengine->low_bw_req_ws);
- del_timer_sync(&pengine->bw_scale_down_timer);
- device_init_wakeup(&pengine->pdev->dev, false);
+ cancel_work_sync(&pengine->bw_reaper_ws);
+ cancel_work_sync(&pengine->bw_allocate_ws);
+ del_timer_sync(&pengine->bw_reaper_timer);
if (pengine->bus_scale_handle != 0)
msm_bus_scale_unregister_client(pengine->bus_scale_handle);
@@ -996,27 +1117,73 @@
return 0;
};
+static void _qcrypto_tfm_complete(struct crypto_priv *cp, u32 type,
+ void *tfm_ctx)
+{
+ unsigned long flags;
+ struct qcrypto_resp_ctx *arsp;
+ struct list_head *plist;
+ struct crypto_async_request *areq;
+
+ switch (type) {
+ case CRYPTO_ALG_TYPE_AHASH:
+ plist = &((struct qcrypto_sha_ctx *) tfm_ctx)->rsp_queue;
+ break;
+ case CRYPTO_ALG_TYPE_ABLKCIPHER:
+ case CRYPTO_ALG_TYPE_AEAD:
+ default:
+ plist = &((struct qcrypto_cipher_ctx *) tfm_ctx)->rsp_queue;
+ break;
+ }
+again:
+ spin_lock_irqsave(&cp->lock, flags);
+ if (list_empty(plist)) {
+ arsp = NULL; /* nothing to do */
+ } else {
+ arsp = list_first_entry(plist,
+ struct qcrypto_resp_ctx, list);
+ if (arsp->res == -EINPROGRESS)
+ arsp = NULL; /* still in progress */
+ else
+ list_del(&arsp->list); /* request is complete */
+ }
+ spin_unlock_irqrestore(&cp->lock, flags);
+ if (arsp) {
+ areq = arsp->async_req;
+ areq->complete(areq, arsp->res);
+ goto again;
+ }
+}
+
static void req_done(unsigned long data)
{
struct crypto_async_request *areq;
struct crypto_engine *pengine = (struct crypto_engine *)data;
struct crypto_priv *cp;
unsigned long flags;
+ struct qcrypto_resp_ctx *arsp;
int res;
+ u32 type = 0;
+ void *tfm_ctx = NULL;
cp = pengine->pcp;
spin_lock_irqsave(&cp->lock, flags);
areq = pengine->req;
- pengine->req = NULL;
- res = pengine->res;
- spin_unlock_irqrestore(&cp->lock, flags);
- if (areq)
- areq->complete(areq, res);
- if (res)
- pengine->err_req++;
- _start_qcrypto_process(cp, pengine);
-};
+ arsp = pengine->arsp;
+ res = pengine->res;
+ pengine->req = NULL;
+ pengine->arsp = NULL;
+ if (areq) {
+ type = crypto_tfm_alg_type(areq->tfm);
+ tfm_ctx = crypto_tfm_ctx(areq->tfm);
+ arsp->res = res;
+ }
+ spin_unlock_irqrestore(&cp->lock, flags);
+ _start_qcrypto_process(cp, pengine);
+ if (areq)
+ _qcrypto_tfm_complete(cp, type, tfm_ctx);
+}
static void _qce_ahash_complete(void *cookie, unsigned char *digest,
unsigned char *authdata, int ret)
@@ -1033,7 +1200,7 @@
pstat = &_qcrypto_stat;
- pengine = sha_ctx->pengine;
+ pengine = rctx->pengine;
#ifdef QCRYPTO_DEBUG
dev_info(&pengine->pdev->dev, "_qce_ahash_complete: %p ret %d\n",
areq, ret);
@@ -1079,10 +1246,12 @@
struct qcrypto_cipher_ctx *ctx = crypto_tfm_ctx(areq->base.tfm);
struct crypto_priv *cp = ctx->cp;
struct crypto_stat *pstat;
+ struct qcrypto_cipher_req_ctx *rctx;
struct crypto_engine *pengine;
pstat = &_qcrypto_stat;
- pengine = ctx->pengine;
+ rctx = ablkcipher_request_ctx(areq);
+ pengine = rctx->pengine;
#ifdef QCRYPTO_DEBUG
dev_info(&pengine->pdev->dev, "_qce_ablk_cipher_complete: %p ret %d\n",
areq, ret);
@@ -1134,8 +1303,8 @@
struct crypto_engine *pengine;
pstat = &_qcrypto_stat;
- pengine = ctx->pengine;
rctx = aead_request_ctx(areq);
+ pengine = rctx->pengine;
if (rctx->mode == QCE_MODE_CCM) {
if (cp->ce_support.aligned_only) {
@@ -1339,6 +1508,7 @@
req = container_of(async_req, struct ablkcipher_request, base);
cipher_ctx = crypto_tfm_ctx(async_req->tfm);
rctx = ablkcipher_request_ctx(req);
+ rctx->pengine = pengine;
tfm = crypto_ablkcipher_reqtfm(req);
if (pengine->pcp->ce_support.aligned_only) {
uint32_t bytes = 0;
@@ -1402,6 +1572,7 @@
struct ahash_request, base);
rctx = ahash_request_ctx(req);
sha_ctx = crypto_tfm_ctx(async_req->tfm);
+ rctx->pengine = pengine;
sreq.qce_cb = _qce_ahash_complete;
sreq.digest = &rctx->digest[0];
@@ -1457,6 +1628,7 @@
struct crypto_aead *aead = crypto_aead_reqtfm(req);
rctx = aead_request_ctx(req);
+ rctx->pengine = pengine;
cipher_ctx = crypto_tfm_ctx(async_req->tfm);
qreq.op = QCE_REQ_AEAD;
@@ -1671,28 +1843,95 @@
struct crypto_engine *pengine)
{
struct crypto_async_request *async_req = NULL;
- struct crypto_async_request *backlog = NULL;
+ struct crypto_async_request *backlog_eng = NULL;
+ struct crypto_async_request *backlog_cp = NULL;
unsigned long flags;
u32 type;
int ret = 0;
struct crypto_stat *pstat;
+ void *tfm_ctx;
+ struct qcrypto_cipher_req_ctx *cipher_rctx;
+ struct qcrypto_sha_req_ctx *ahash_rctx;
+ struct ablkcipher_request *ablkcipher_req;
+ struct ahash_request *ahash_req;
+ struct aead_request *aead_req;
+ struct qcrypto_resp_ctx *arsp;
pstat = &_qcrypto_stat;
again:
spin_lock_irqsave(&cp->lock, flags);
- if (pengine->req == NULL) {
- backlog = crypto_get_backlog(&pengine->req_queue);
- async_req = crypto_dequeue_request(&pengine->req_queue);
- pengine->req = async_req;
+ if (pengine->req) {
+ spin_unlock_irqrestore(&cp->lock, flags);
+ return 0;
}
- spin_unlock_irqrestore(&cp->lock, flags);
- if (!async_req)
- return ret;
- if (backlog)
- backlog->complete(backlog, -EINPROGRESS);
- type = crypto_tfm_alg_type(async_req->tfm);
+ backlog_eng = crypto_get_backlog(&pengine->req_queue);
+
+ /* try to get request from request queue of the engine first */
+ async_req = crypto_dequeue_request(&pengine->req_queue);
+ if (!async_req) {
+ /*
+ * if no request from the engine,
+ * try to get from request queue of driver
+ */
+ backlog_cp = crypto_get_backlog(&cp->req_queue);
+ async_req = crypto_dequeue_request(&cp->req_queue);
+ if (!async_req) {
+ spin_unlock_irqrestore(&cp->lock, flags);
+ return 0;
+ }
+ }
+
+ /* add associated rsp entry to tfm response queue */
+ type = crypto_tfm_alg_type(async_req->tfm);
+ tfm_ctx = crypto_tfm_ctx(async_req->tfm);
+ switch (type) {
+ case CRYPTO_ALG_TYPE_AHASH:
+ ahash_req = container_of(async_req,
+ struct ahash_request, base);
+ ahash_rctx = ahash_request_ctx(ahash_req);
+ arsp = &ahash_rctx->rsp_entry;
+ list_add_tail(
+ &arsp->list,
+ &((struct qcrypto_sha_ctx *)tfm_ctx)
+ ->rsp_queue);
+ break;
+ case CRYPTO_ALG_TYPE_ABLKCIPHER:
+ ablkcipher_req = container_of(async_req,
+ struct ablkcipher_request, base);
+ cipher_rctx = ablkcipher_request_ctx(ablkcipher_req);
+ arsp = &cipher_rctx->rsp_entry;
+ list_add_tail(
+ &arsp->list,
+ &((struct qcrypto_sha_ctx *)tfm_ctx)
+ ->rsp_queue);
+ break;
+ case CRYPTO_ALG_TYPE_AEAD:
+ default:
+ aead_req = container_of(async_req,
+ struct aead_request, base);
+ cipher_rctx = aead_request_ctx(aead_req);
+ arsp = &cipher_rctx->rsp_entry;
+ list_add_tail(
+ &arsp->list,
+ &((struct qcrypto_sha_ctx *)tfm_ctx)
+ ->rsp_queue);
+ break;
+ }
+
+ arsp->res = -EINPROGRESS;
+ arsp->async_req = async_req;
+ pengine->req = async_req;
+ pengine->arsp = arsp;
+ pengine->active_seq++;
+ pengine->check_flag = true;
+
+ spin_unlock_irqrestore(&cp->lock, flags);
+ if (backlog_eng)
+ backlog_eng->complete(backlog_eng, -EINPROGRESS);
+ if (backlog_cp)
+ backlog_cp->complete(backlog_cp, -EINPROGRESS);
switch (type) {
case CRYPTO_ALG_TYPE_ABLKCIPHER:
ret = _qcrypto_process_ablkcipher(pengine, async_req);
@@ -1708,9 +1947,11 @@
};
pengine->total_req++;
if (ret) {
+ arsp->res = ret;
pengine->err_req++;
spin_lock_irqsave(&cp->lock, flags);
pengine->req = NULL;
+ pengine->arsp = NULL;
spin_unlock_irqrestore(&cp->lock, flags);
if (type == CRYPTO_ALG_TYPE_ABLKCIPHER)
@@ -1721,11 +1962,22 @@
else
pstat->aead_op_fail++;
- async_req->complete(async_req, ret);
+ _qcrypto_tfm_complete(cp, type, tfm_ctx);
goto again;
};
return ret;
-};
+}
+
+static struct crypto_engine *_avail_eng(struct crypto_priv *cp)
+{
+ struct crypto_engine *pe = NULL;
+
+ list_for_each_entry(pe, &cp->engine_list, elist) {
+ if (pe->req == NULL)
+ return pe;
+ }
+ return NULL;
+}
static int _qcrypto_queue_req(struct crypto_priv *cp,
struct crypto_engine *pengine,
@@ -1741,10 +1993,40 @@
}
spin_lock_irqsave(&cp->lock, flags);
- ret = crypto_enqueue_request(&pengine->req_queue, req);
- spin_unlock_irqrestore(&cp->lock, flags);
- _start_qcrypto_process(cp, pengine);
+ if (pengine) {
+ ret = crypto_enqueue_request(&pengine->req_queue, req);
+ } else {
+ ret = crypto_enqueue_request(&cp->req_queue, req);
+ pengine = _avail_eng(cp);
+ }
+ if (pengine) {
+ switch (pengine->bw_state) {
+ case BUS_NO_BANDWIDTH:
+ if (pengine->high_bw_req == false) {
+ qcrypto_ce_bw_allocate_req(pengine);
+ pengine->high_bw_req = true;
+ }
+ pengine = NULL;
+ break;
+ case BUS_HAS_BANDWIDTH:
+ break;
+ case BUS_BANDWIDTH_RELEASING:
+ pengine->high_bw_req = true;
+ pengine = NULL;
+ break;
+ case BUS_BANDWIDTH_ALLOCATING:
+ pengine = NULL;
+ break;
+ case BUS_SUSPENDED:
+ default:
+ pengine = NULL;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&cp->lock, flags);
+ if (pengine)
+ _start_qcrypto_process(cp, pengine);
return ret;
}
@@ -3339,13 +3621,59 @@
static int _qcrypto_prefix_alg_cra_name(char cra_name[], unsigned int size)
{
char new_cra_name[CRYPTO_MAX_ALG_NAME] = "qcom-";
- if (CRYPTO_MAX_ALG_NAME < size + 5)
+ if (size >= CRYPTO_MAX_ALG_NAME - strlen("qcom-"))
return -EINVAL;
strlcat(new_cra_name, cra_name, CRYPTO_MAX_ALG_NAME);
strlcpy(cra_name, new_cra_name, CRYPTO_MAX_ALG_NAME);
return 0;
}
+
+int qcrypto_cipher_set_device(struct ablkcipher_request *req, unsigned int dev)
+{
+ struct qcrypto_cipher_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
+ struct crypto_priv *cp = ctx->cp;
+ struct crypto_engine *pengine = NULL;
+
+ pengine = _qrypto_find_pengine_device(cp, dev);
+ if (pengine == NULL)
+ return -ENODEV;
+ ctx->pengine = pengine;
+
+ return 0;
+};
+EXPORT_SYMBOL(qcrypto_cipher_set_device);
+
+int qcrypto_aead_set_device(struct aead_request *req, unsigned int dev)
+{
+ struct qcrypto_cipher_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
+ struct crypto_priv *cp = ctx->cp;
+ struct crypto_engine *pengine = NULL;
+
+ pengine = _qrypto_find_pengine_device(cp, dev);
+ if (pengine == NULL)
+ return -ENODEV;
+ ctx->pengine = pengine;
+
+ return 0;
+};
+EXPORT_SYMBOL(qcrypto_aead_set_device);
+
+int qcrypto_ahash_set_device(struct ahash_request *req, unsigned int dev)
+{
+ struct qcrypto_sha_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
+ struct crypto_priv *cp = ctx->cp;
+ struct crypto_engine *pengine = NULL;
+
+ pengine = _qrypto_find_pengine_device(cp, dev);
+ if (pengine == NULL)
+ return -ENODEV;
+ ctx->pengine = pengine;
+
+ return 0;
+};
+EXPORT_SYMBOL(qcrypto_ahash_set_device);
+
int qcrypto_cipher_set_flag(struct ablkcipher_request *req, unsigned int flags)
{
struct qcrypto_cipher_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
@@ -3927,18 +4255,21 @@
pengine->pcp = cp;
pengine->pdev = pdev;
pengine->req = NULL;
+ pengine->signature = 0xdeadbeef;
- pengine->high_bw_req_count = 0;
+ init_timer(&(pengine->bw_reaper_timer));
+ INIT_WORK(&pengine->bw_reaper_ws, qcrypto_bw_reaper_work);
+ pengine->bw_reaper_timer.function =
+ qcrypto_bw_reaper_timer_callback;
+ INIT_WORK(&pengine->bw_allocate_ws, qcrypto_bw_allocate_work);
pengine->high_bw_req = false;
- init_timer(&(pengine->bw_scale_down_timer));
- INIT_WORK(&pengine->low_bw_req_ws, qcrypto_low_bw_req_work);
- pengine->bw_scale_down_timer.function =
- qcrypto_bw_scale_down_timer_callback;
-
- device_init_wakeup(&pengine->pdev->dev, true);
+ pengine->active_seq = 0;
+ pengine->last_active_seq = 0;
+ pengine->check_flag = false;
+ qcrypto_bw_set_timeout(pengine);
tasklet_init(&pengine->done_tasklet, req_done, (unsigned long)pengine);
- crypto_init_queue(&pengine->req_queue, 50);
+ crypto_init_queue(&pengine->req_queue, MSM_QCRYPTO_REQ_QUEUE_LENGTH);
mutex_lock(&cp->engine_lock);
cp->total_units++;
@@ -3961,6 +4292,9 @@
msm_bus_cl_get_pdata(pdev);
if (!cp->platform_support.bus_scale_table)
pr_warn("bus_scale_table is NULL\n");
+
+ pengine->ce_device = cp->ce_support.ce_device;
+
} else {
platform_support =
(struct msm_ce_hw_support *)pdev->dev.platform_data;
@@ -3973,7 +4307,9 @@
platform_support->bus_scale_table;
cp->platform_support.sha_hmac = platform_support->sha_hmac;
}
+
pengine->bus_scale_handle = 0;
+
if (cp->platform_support.bus_scale_table != NULL) {
pengine->bus_scale_handle =
msm_bus_scale_register_client(
@@ -3985,6 +4321,9 @@
rc = -ENOMEM;
goto err;
}
+ pengine->bw_state = BUS_NO_BANDWIDTH;
+ } else {
+ pengine->bw_state = BUS_HAS_BANDWIDTH;
}
if (cp->total_units != 1) {
@@ -4010,6 +4349,7 @@
dev_err(&pdev->dev,
"The algorithm name %s is too long.\n",
q_alg->cipher_alg.cra_name);
+ kfree(q_alg);
goto err;
}
}
@@ -4043,6 +4383,7 @@
dev_err(&pdev->dev,
"The algorithm name %s is too long.\n",
q_alg->cipher_alg.cra_name);
+ kfree(q_alg);
goto err;
}
}
@@ -4079,6 +4420,7 @@
dev_err(&pdev->dev,
"The algorithm name %s is too long.\n",
q_alg->sha_alg.halg.base.cra_name);
+ kfree(q_alg);
goto err;
}
}
@@ -4115,6 +4457,7 @@
dev_err(&pdev->dev,
"The algorithm name %s is too long.\n",
q_alg->cipher_alg.cra_name);
+ kfree(q_alg);
goto err;
}
}
@@ -4153,6 +4496,7 @@
dev_err(&pdev->dev,
"The algorithm name %s is too long.\n",
q_alg->sha_alg.halg.base.cra_name);
+ kfree(q_alg);
goto err;
}
}
@@ -4189,6 +4533,7 @@
dev_err(&pdev->dev,
"The algorithm name %s is too long.\n",
q_alg->cipher_alg.cra_name);
+ kfree(q_alg);
goto err;
}
}
@@ -4245,12 +4590,12 @@
return rc;
};
-
static int _qcrypto_suspend(struct platform_device *pdev, pm_message_t state)
{
int ret = 0;
struct crypto_engine *pengine;
struct crypto_priv *cp;
+ unsigned long flags;
pengine = platform_get_drvdata(pdev);
if (!pengine)
@@ -4263,42 +4608,39 @@
cp = pengine->pcp;
if (!cp->ce_support.clk_mgmt_sus_res)
return 0;
-
- mutex_lock(&cp->engine_lock);
-
- if (pengine->high_bw_req) {
- del_timer_sync(&(pengine->bw_scale_down_timer));
- ret = msm_bus_scale_client_update_request(
- pengine->bus_scale_handle, 0);
- if (ret) {
- dev_err(&pdev->dev, "%s Unable to set to low bandwidth\n",
- __func__);
- mutex_unlock(&cp->engine_lock);
- return ret;
- }
- ret = qce_disable_clk(pengine->qce);
- if (ret) {
- pr_err("%s Unable disable clk\n", __func__);
- ret = msm_bus_scale_client_update_request(
- pengine->bus_scale_handle, 1);
- if (ret)
- dev_err(&pdev->dev,
- "%s Unable to set to high bandwidth\n",
- __func__);
- mutex_unlock(&cp->engine_lock);
- return ret;
- }
+ spin_lock_irqsave(&cp->lock, flags);
+ switch (pengine->bw_state) {
+ case BUS_NO_BANDWIDTH:
+ if (pengine->high_bw_req == false)
+ pengine->bw_state = BUS_SUSPENDED;
+ else
+ ret = -EBUSY;
+ break;
+ case BUS_HAS_BANDWIDTH:
+ case BUS_BANDWIDTH_RELEASING:
+ case BUS_BANDWIDTH_ALLOCATING:
+ case BUS_SUSPENDED:
+ default:
+ ret = -EBUSY;
+ break;
}
- mutex_unlock(&cp->engine_lock);
- return 0;
+ spin_unlock_irqrestore(&cp->lock, flags);
+ if (ret)
+ return ret;
+ else {
+ if (qce_pm_table.suspend)
+ qce_pm_table.suspend(pengine->qce);
+ return 0;
+ }
}
static int _qcrypto_resume(struct platform_device *pdev)
{
- int ret = 0;
struct crypto_engine *pengine;
struct crypto_priv *cp;
+ unsigned long flags;
+ bool restart = false;
pengine = platform_get_drvdata(pdev);
@@ -4309,33 +4651,26 @@
if (!cp->ce_support.clk_mgmt_sus_res)
return 0;
- mutex_lock(&cp->engine_lock);
- if (pengine->high_bw_req) {
- ret = qce_enable_clk(pengine->qce);
- if (ret) {
- dev_err(&pdev->dev, "%s Unable to enable clk\n",
- __func__);
- mutex_unlock(&cp->engine_lock);
- return ret;
- }
- ret = msm_bus_scale_client_update_request(
- pengine->bus_scale_handle, 1);
- if (ret) {
- dev_err(&pdev->dev,
- "%s Unable to set to high bandwidth\n",
- __func__);
- qce_disable_clk(pengine->qce);
- mutex_unlock(&cp->engine_lock);
- return ret;
- }
- pengine->bw_scale_down_timer.data =
- (unsigned long)(pengine);
- pengine->bw_scale_down_timer.expires = jiffies +
- msecs_to_jiffies(QCRYPTO_HIGH_BANDWIDTH_TIMEOUT);
- add_timer(&(pengine->bw_scale_down_timer));
- }
+ spin_lock_irqsave(&cp->lock, flags);
+ if (pengine->bw_state == BUS_SUSPENDED) {
+ pengine->bw_state = BUS_BANDWIDTH_ALLOCATING;
+ spin_unlock_irqrestore(&cp->lock, flags);
- mutex_unlock(&cp->engine_lock);
+ if (qce_pm_table.resume)
+ qce_pm_table.resume(pengine->qce);
+
+ qcrypto_ce_set_bus(pengine, true);
+
+ spin_lock_irqsave(&cp->lock, flags);
+ pengine->bw_state = BUS_HAS_BANDWIDTH;
+ pengine->high_bw_req = false;
+ restart = true;
+ pengine->active_seq++;
+ pengine->check_flag = true;
+ }
+ spin_unlock_irqrestore(&cp->lock, flags);
+ if (restart)
+ _start_qcrypto_process(cp, pengine);
return 0;
}
@@ -4450,6 +4785,7 @@
pcp->ce_lock_count = 0;
pcp->platform_support.bus_scale_table = NULL;
pcp->next_engine = NULL;
+ crypto_init_queue(&pcp->req_queue, MSM_QCRYPTO_REQ_QUEUE_LENGTH);
return platform_driver_register(&_qualcomm_crypto);
}
diff --git a/drivers/devfreq/governor_cpubw_hwmon.c b/drivers/devfreq/governor_cpubw_hwmon.c
index e7d373b..aa7ff69 100644
--- a/drivers/devfreq/governor_cpubw_hwmon.c
+++ b/drivers/devfreq/governor_cpubw_hwmon.c
@@ -246,8 +246,9 @@
new_bw /= 100;
}
- *ab = roundup(mbps, bw_step);
- *freq = (mbps * 100) / io_percent;
+ prev_ab = new_bw;
+ *ab = roundup(new_bw, bw_step);
+ *freq = (new_bw * 100) / io_percent;
}
#define TOO_SOON_US (1 * USEC_PER_MSEC)
@@ -340,7 +341,6 @@
mbps = measure_bw_and_set_irq();
compute_bw(mbps, freq, df->data);
- prev_ab = *(unsigned long *) df->data;
return 0;
}
diff --git a/drivers/gpio/gpio-msm-common.c b/drivers/gpio/gpio-msm-common.c
index 3d18809..4a69cd5 100644
--- a/drivers/gpio/gpio-msm-common.c
+++ b/drivers/gpio/gpio-msm-common.c
@@ -112,16 +112,11 @@
* @wake_irqs: a bitmap for tracking which interrupt lines are enabled
* as wakeup sources. When the device is suspended, interrupts which are
* not wakeup sources are disabled.
- *
- * @dual_edge_irqs: a bitmap used to track which irqs are configured
- * as dual-edge, as this is not supported by the hardware and requires
- * some special handling in the driver.
*/
struct msm_gpio_dev {
struct gpio_chip gpio_chip;
unsigned long *enabled_irqs;
unsigned long *wake_irqs;
- unsigned long *dual_edge_irqs;
struct irq_domain *domain;
};
@@ -219,64 +214,11 @@
},
};
-static void switch_mpm_config(struct irq_data *d, unsigned val)
-{
- /* switch the configuration in the mpm as well */
- if (!msm_gpio_irq_extn.irq_set_type)
- return;
-
- if (val)
- msm_gpio_irq_extn.irq_set_type(d, IRQF_TRIGGER_FALLING);
- else
- msm_gpio_irq_extn.irq_set_type(d, IRQF_TRIGGER_RISING);
-}
-
-/* For dual-edge interrupts in software, since the hardware has no
- * such support:
- *
- * At appropriate moments, this function may be called to flip the polarity
- * settings of both-edge irq lines to try and catch the next edge.
- *
- * The attempt is considered successful if:
- * - the status bit goes high, indicating that an edge was caught, or
- * - the input value of the gpio doesn't change during the attempt.
- * If the value changes twice during the process, that would cause the first
- * test to fail but would force the second, as two opposite
- * transitions would cause a detection no matter the polarity setting.
- *
- * The do-loop tries to sledge-hammer closed the timing hole between
- * the initial value-read and the polarity-write - if the line value changes
- * during that window, an interrupt is lost, the new polarity setting is
- * incorrect, and the first success test will fail, causing a retry.
- *
- * Algorithm comes from Google's msmgpio driver, see mach-msm/gpio.c.
- */
-static void msm_gpio_update_dual_edge_pos(struct irq_data *d, unsigned gpio)
-{
- int loop_limit = 100;
- unsigned val, val2, intstat;
-
- do {
- val = __msm_gpio_get_inout(gpio);
- __msm_gpio_set_polarity(gpio, val);
- val2 = __msm_gpio_get_inout(gpio);
- intstat = __msm_gpio_get_intr_status(gpio);
- if (intstat || val == val2) {
- switch_mpm_config(d, val);
- return;
- }
- } while (loop_limit-- > 0);
- pr_err("%s: dual-edge irq failed to stabilize, %#08x != %#08x\n",
- __func__, val, val2);
-}
-
static void msm_gpio_irq_ack(struct irq_data *d)
{
int gpio = msm_irq_to_gpio(&msm_gpio.gpio_chip, d->irq);
__msm_gpio_set_intr_status(gpio);
- if (test_bit(gpio, msm_gpio.dual_edge_irqs))
- msm_gpio_update_dual_edge_pos(d, gpio);
mb();
}
@@ -327,29 +269,18 @@
spin_lock_irqsave(&tlmm_lock, irq_flags);
- if (flow_type & IRQ_TYPE_EDGE_BOTH) {
+ if (flow_type & IRQ_TYPE_EDGE_BOTH)
__irq_set_handler_locked(d->irq, handle_edge_irq);
- if ((flow_type & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH)
- __set_bit(gpio, msm_gpio.dual_edge_irqs);
- else
- __clear_bit(gpio, msm_gpio.dual_edge_irqs);
- } else {
+ else
__irq_set_handler_locked(d->irq, handle_level_irq);
- __clear_bit(gpio, msm_gpio.dual_edge_irqs);
- }
__msm_gpio_set_intr_cfg_type(gpio, flow_type);
- if ((flow_type & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH)
- msm_gpio_update_dual_edge_pos(d, gpio);
-
mb();
spin_unlock_irqrestore(&tlmm_lock, irq_flags);
- if ((flow_type & IRQ_TYPE_EDGE_BOTH) != IRQ_TYPE_EDGE_BOTH) {
- if (msm_gpio_irq_extn.irq_set_type)
- msm_gpio_irq_extn.irq_set_type(d, flow_type);
- }
+ if (msm_gpio_irq_extn.irq_set_type)
+ msm_gpio_irq_extn.irq_set_type(d, flow_type);
return 0;
}
@@ -609,17 +540,9 @@
, __func__);
return -ENOMEM;
}
- msm_gpio.dual_edge_irqs = devm_kzalloc(&pdev->dev, sizeof(unsigned long)
- * BITS_TO_LONGS(ngpio), GFP_KERNEL);
- if (!msm_gpio.dual_edge_irqs) {
- dev_err(&pdev->dev, "%s failed to allocated dual_edge_irqs bitmap\n"
- , __func__);
- return -ENOMEM;
- }
bitmap_zero(msm_gpio.enabled_irqs, ngpio);
bitmap_zero(msm_gpio.wake_irqs, ngpio);
- bitmap_zero(msm_gpio.dual_edge_irqs, ngpio);
ret = gpiochip_add(&msm_gpio.gpio_chip);
if (ret < 0)
return ret;
diff --git a/drivers/gpu/ion/ion_cma_secure_heap.c b/drivers/gpu/ion/ion_cma_secure_heap.c
index 0aef596..da68d05 100644
--- a/drivers/gpu/ion/ion_cma_secure_heap.c
+++ b/drivers/gpu/ion/ion_cma_secure_heap.c
@@ -444,6 +444,7 @@
ret = ion_secure_cma_alloc_from_pool(sheap, &info->phys, len);
if (ret) {
+retry:
ret = ion_secure_cma_add_to_pool(sheap, len);
if (ret) {
mutex_unlock(&sheap->alloc_lock);
@@ -453,10 +454,9 @@
ret = ion_secure_cma_alloc_from_pool(sheap, &info->phys, len);
if (ret) {
/*
- * We just added memory to the pool, we shouldn't be
- * failing to get memory
+ * Lost the race with the shrinker, try again
*/
- BUG();
+ goto retry;
}
}
mutex_unlock(&sheap->alloc_lock);
diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c
index 3196911..f230033 100644
--- a/drivers/gpu/msm/adreno.c
+++ b/drivers/gpu/msm/adreno.c
@@ -272,7 +272,7 @@
struct adreno_device, input_work);
struct kgsl_device *device = &adreno_dev->dev;
- mutex_lock(&device->mutex);
+ kgsl_mutex_lock(&device->mutex, &device->mutex_owner);
device->flags |= KGSL_FLAG_WAKE_ON_TOUCH;
@@ -290,7 +290,7 @@
*/
mod_timer(&device->idle_timer,
jiffies + msecs_to_jiffies(_wake_timeout));
- mutex_unlock(&device->mutex);
+ kgsl_mutex_unlock(&device->mutex, &device->mutex_owner);
}
/*
@@ -1137,9 +1137,7 @@
uint32_t flags)
{
phys_addr_t pt_val;
- unsigned int link[230];
- unsigned int *cmds = &link[0];
- int sizedwords = 0;
+ unsigned int *link = NULL, *cmds;
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
int num_iommu_units;
struct kgsl_context *context;
@@ -1160,6 +1158,14 @@
}
adreno_ctx = ADRENO_CONTEXT(context);
+ link = kmalloc(PAGE_SIZE, GFP_KERNEL);
+ if (link == NULL) {
+ result = -ENOMEM;
+ goto done;
+ }
+
+ cmds = link;
+
result = kgsl_mmu_enable_clk(&device->mmu, KGSL_IOMMU_CONTEXT_USER);
if (result)
@@ -1179,17 +1185,11 @@
cmds += _adreno_iommu_setstate_v1(device, cmds, pt_val,
num_iommu_units, flags);
- sizedwords += (cmds - &link[0]);
- if (sizedwords == 0) {
- KGSL_DRV_ERR(device, "no commands generated\n");
- BUG();
- }
/* invalidate all base pointers */
*cmds++ = cp_type3_packet(CP_INVALIDATE_STATE, 1);
*cmds++ = 0x7fff;
- sizedwords += 2;
- if (sizedwords > (ARRAY_SIZE(link))) {
+ if ((unsigned int) (cmds - link) > (PAGE_SIZE / sizeof(unsigned int))) {
KGSL_DRV_ERR(device, "Temp command buffer overflow\n");
BUG();
}
@@ -1198,7 +1198,8 @@
* use the global timestamp for iommu clock disablement
*/
result = adreno_ringbuffer_issuecmds(device, adreno_ctx,
- KGSL_CMD_FLAGS_PMODE, &link[0], sizedwords);
+ KGSL_CMD_FLAGS_PMODE, link,
+ (unsigned int)(cmds - link));
/*
* On error disable the IOMMU clock right away otherwise turn it off
@@ -1212,6 +1213,7 @@
KGSL_IOMMU_CONTEXT_USER);
done:
+ kfree(link);
kgsl_context_put(context);
return result;
}
@@ -1960,6 +1962,10 @@
for (i = 6; i < FT_DETECT_REGS_COUNT; i++)
ft_detect_regs[i] = 0;
+ /* turn on hang interrupt for a330v2 by default */
+ if (adreno_is_a330v2(adreno_dev))
+ set_bit(ADRENO_DEVICE_HANG_INTR, &adreno_dev->priv);
+
ret = adreno_perfcounter_init(device);
if (ret)
goto done;
@@ -2099,7 +2105,7 @@
/* Nice ourselves to be higher priority but not too high priority */
set_user_nice(current, _wake_nice);
- mutex_lock(&device->mutex);
+ kgsl_mutex_lock(&device->mutex, &device->mutex_owner);
/*
* If adreno start is already called, no need to call it again
* it can lead to unpredictable behavior if we try to start
@@ -2119,7 +2125,7 @@
_status = _adreno_start(adreno_dev);
else
_status = 0;
- mutex_unlock(&device->mutex);
+ kgsl_mutex_unlock(&device->mutex, &device->mutex_owner);
}
/**
@@ -2144,9 +2150,9 @@
* higher priority work queue and wait for it to finish
*/
queue_work(adreno_wq, &adreno_dev->start_work);
- mutex_unlock(&device->mutex);
+ kgsl_mutex_unlock(&device->mutex, &device->mutex_owner);
flush_work(&adreno_dev->start_work);
- mutex_lock(&device->mutex);
+ kgsl_mutex_lock(&device->mutex, &device->mutex_owner);
return _status;
}
@@ -2537,6 +2543,88 @@
return snprintf(buf, PAGE_SIZE, "%d\n", _wake_timeout);
}
+/**
+ * _ft_hang_intr_status_store - Routine to enable/disable h/w hang interrupt
+ * @dev: device ptr
+ * @attr: Device attribute
+ * @buf: value to write
+ * @count: size of the value to write
+ */
+static ssize_t _ft_hang_intr_status_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned int new_setting, old_setting;
+ struct kgsl_device *device = kgsl_device_from_dev(dev);
+ struct adreno_device *adreno_dev;
+ int ret;
+ if (device == NULL)
+ return 0;
+ adreno_dev = ADRENO_DEVICE(device);
+
+ mutex_lock(&device->mutex);
+ ret = _ft_sysfs_store(buf, count, &new_setting);
+ if (ret != count)
+ goto done;
+ if (new_setting)
+ new_setting = 1;
+ old_setting =
+ (test_bit(ADRENO_DEVICE_HANG_INTR, &adreno_dev->priv) ? 1 : 0);
+ if (new_setting != old_setting) {
+ if (new_setting)
+ set_bit(ADRENO_DEVICE_HANG_INTR, &adreno_dev->priv);
+ else
+ clear_bit(ADRENO_DEVICE_HANG_INTR, &adreno_dev->priv);
+ /* Set the new setting based on device state */
+ switch (device->state) {
+ case KGSL_STATE_NAP:
+ case KGSL_STATE_SLEEP:
+ kgsl_pwrctrl_wake(device, 0);
+ case KGSL_STATE_ACTIVE:
+ adreno_dev->gpudev->irq_control(adreno_dev, 1);
+ /*
+ * For following states setting will be picked up on device
+ * start. Still need them in switch statement to differentiate
+ * from default
+ */
+ case KGSL_STATE_SLUMBER:
+ case KGSL_STATE_SUSPEND:
+ break;
+ default:
+ ret = -EACCES;
+ /* reset back to old setting on error */
+ if (new_setting)
+ clear_bit(ADRENO_DEVICE_HANG_INTR,
+ &adreno_dev->priv);
+ else
+ set_bit(ADRENO_DEVICE_HANG_INTR,
+ &adreno_dev->priv);
+ goto done;
+ }
+ }
+done:
+ mutex_unlock(&device->mutex);
+ return ret;
+}
+
+/**
+ * _ft_hang_intr_status_show() - Routine to read hardware hang interrupt
+ * enablement
+ * @dev: device ptr
+ * @attr: Device attribute
+ * @buf: value read
+ */
+static ssize_t _ft_hang_intr_status_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct adreno_device *adreno_dev = _get_adreno_dev(dev);
+ if (adreno_dev == NULL)
+ return 0;
+ return snprintf(buf, PAGE_SIZE, "%d\n",
+ test_bit(ADRENO_DEVICE_HANG_INTR, &adreno_dev->priv) ? 1 : 0);
+}
+
#define FT_DEVICE_ATTR(name) \
DEVICE_ATTR(name, 0644, _ ## name ## _show, _ ## name ## _store);
@@ -2544,6 +2632,7 @@
FT_DEVICE_ATTR(ft_pagefault_policy);
FT_DEVICE_ATTR(ft_fast_hang_detect);
FT_DEVICE_ATTR(ft_long_ib_detect);
+FT_DEVICE_ATTR(ft_hang_intr_status);
static DEVICE_INT_ATTR(wake_nice, 0644, _wake_nice);
static FT_DEVICE_ATTR(wake_timeout);
@@ -2555,6 +2644,7 @@
&dev_attr_ft_long_ib_detect,
&dev_attr_wake_nice.attr,
&dev_attr_wake_timeout,
+ &dev_attr_ft_hang_intr_status,
NULL,
};
diff --git a/drivers/gpu/msm/adreno.h b/drivers/gpu/msm/adreno.h
index 9092a03..3d9206b 100644
--- a/drivers/gpu/msm/adreno.h
+++ b/drivers/gpu/msm/adreno.h
@@ -217,6 +217,7 @@
ADRENO_DEVICE_PWRON_FIXUP = 1,
ADRENO_DEVICE_INITIALIZED = 2,
ADRENO_DEVICE_STARTED = 3,
+ ADRENO_DEVICE_HANG_INTR = 4,
};
#define PERFCOUNTER_FLAG_NONE 0x0
@@ -415,7 +416,8 @@
#define KGSL_FT_DISABLE 4
#define KGSL_FT_TEMP_DISABLE 5
#define KGSL_FT_THROTTLE 6
-#define KGSL_FT_DEFAULT_POLICY (BIT(KGSL_FT_REPLAY) + BIT(KGSL_FT_SKIPIB) \
+#define KGSL_FT_SKIPCMD 7
+#define KGSL_FT_DEFAULT_POLICY (BIT(KGSL_FT_REPLAY) + BIT(KGSL_FT_SKIPCMD) \
+ BIT(KGSL_FT_THROTTLE))
/* This internal bit is used to skip the PM dump on replayed command batches */
@@ -435,7 +437,8 @@
{ BIT(KGSL_FT_SKIPFRAME), "skipframe" }, \
{ BIT(KGSL_FT_DISABLE), "disable" }, \
{ BIT(KGSL_FT_TEMP_DISABLE), "temp" }, \
- { BIT(KGSL_FT_THROTTLE), "throttle"}
+ { BIT(KGSL_FT_THROTTLE), "throttle"}, \
+ { BIT(KGSL_FT_SKIPCMD), "skipcmd" }
extern struct adreno_gpudev adreno_a2xx_gpudev;
extern struct adreno_gpudev adreno_a3xx_gpudev;
@@ -515,6 +518,10 @@
int adreno_ft_init_sysfs(struct kgsl_device *device);
void adreno_ft_uninit_sysfs(struct kgsl_device *device);
+void adreno_fault_skipcmd_detached(struct kgsl_device *device,
+ struct adreno_context *drawctxt,
+ struct kgsl_cmdbatch *cmdbatch);
+
int adreno_perfcounter_get_groupid(struct adreno_device *adreno_dev,
const char *name);
@@ -717,6 +724,11 @@
*cmds++ = val;
*cmds++ = 0xFFFFFFFF;
*cmds++ = 0xFFFFFFFF;
+
+ /* WAIT_REG_MEM turns back on protected mode - push it off */
+ *cmds++ = cp_type3_packet(CP_SET_PROTECTED_MODE, 1);
+ *cmds++ = 0;
+
cmds += __adreno_add_idle_indirect_cmds(cmds, nop_gpuaddr);
return cmds - start;
}
diff --git a/drivers/gpu/msm/adreno_a3xx.c b/drivers/gpu/msm/adreno_a3xx.c
index 47ba854..2025d73 100644
--- a/drivers/gpu/msm/adreno_a3xx.c
+++ b/drivers/gpu/msm/adreno_a3xx.c
@@ -3039,6 +3039,25 @@
return 0;
}
+static void a3xx_fatal_err_callback(struct adreno_device *adreno_dev, int bit)
+{
+ struct kgsl_device *device = &adreno_dev->dev;
+ const char *err = "";
+
+ switch (bit) {
+ case A3XX_INT_MISC_HANG_DETECT:
+ err = "MISC: GPU hang detected\n";
+ break;
+ default:
+ return;
+ }
+ KGSL_DRV_CRIT(device, "%s\n", err);
+ kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_OFF);
+
+ /* Trigger a fault in the dispatcher - this will effect a restart */
+ adreno_dispatcher_irq_fault(device);
+}
+
static void a3xx_err_callback(struct adreno_device *adreno_dev, int bit)
{
struct kgsl_device *device = &adreno_dev->dev;
@@ -3063,7 +3082,7 @@
/* Clear the error */
kgsl_regwrite(device, A3XX_RBBM_AHB_CMD, (1 << 3));
- goto done;
+ return;
}
case A3XX_INT_RBBM_REG_TIMEOUT:
err = "RBBM: AHB register timeout";
@@ -3100,14 +3119,10 @@
"CP | Protected mode error| %s | addr=%x\n",
reg & (1 << 24) ? "WRITE" : "READ",
(reg & 0x1FFFF) >> 2);
- goto done;
}
case A3XX_INT_CP_AHB_ERROR_HALT:
err = "ringbuffer AHB error interrupt";
break;
- case A3XX_INT_MISC_HANG_DETECT:
- err = "MISC: GPU hang detected";
- break;
case A3XX_INT_UCHE_OOB_ACCESS:
err = "UCHE: Out of bounds access";
break;
@@ -3115,11 +3130,6 @@
return;
}
KGSL_DRV_CRIT(device, "%s\n", err);
- kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_OFF);
-
-done:
- /* Trigger a fault in the dispatcher - this will effect a restart */
- adreno_dispatcher_irq_fault(device);
}
static void a3xx_cp_callback(struct adreno_device *adreno_dev, int irq)
@@ -3561,7 +3571,7 @@
A3XX_IRQ_CALLBACK(a3xx_err_callback), /* 21 - CP_AHB_ERROR_FAULT */
A3XX_IRQ_CALLBACK(NULL), /* 22 - Unused */
A3XX_IRQ_CALLBACK(NULL), /* 23 - Unused */
- A3XX_IRQ_CALLBACK(NULL), /* 24 - MISC_HANG_DETECT */
+ A3XX_IRQ_CALLBACK(a3xx_fatal_err_callback),/* 24 - MISC_HANG_DETECT */
A3XX_IRQ_CALLBACK(a3xx_err_callback), /* 25 - UCHE_OOB_ACCESS */
/* 26 to 31 - Unused */
};
@@ -3602,7 +3612,9 @@
struct kgsl_device *device = &adreno_dev->dev;
if (state)
- kgsl_regwrite(device, A3XX_RBBM_INT_0_MASK, A3XX_INT_MASK);
+ kgsl_regwrite(device, A3XX_RBBM_INT_0_MASK, A3XX_INT_MASK |
+ (test_bit(ADRENO_DEVICE_HANG_INTR, &adreno_dev->priv) ?
+ (1 << A3XX_INT_MISC_HANG_DETECT) : 0));
else
kgsl_regwrite(device, A3XX_RBBM_INT_0_MASK, 0);
}
@@ -4138,6 +4150,7 @@
/* CP registers */
adreno_set_protected_registers(device, &index, 0x1C0, 5);
+ adreno_set_protected_registers(device, &index, 0x1EC, 1);
adreno_set_protected_registers(device, &index, 0x1F6, 1);
adreno_set_protected_registers(device, &index, 0x1F8, 2);
adreno_set_protected_registers(device, &index, 0x45E, 2);
@@ -4193,9 +4206,12 @@
/* Turn on hang detection - this spews a lot of useful information
* into the RBBM registers on a hang */
-
- kgsl_regwrite(device, A3XX_RBBM_INTERFACE_HANG_INT_CTL,
- (1 << 16) | 0xFFF);
+ if (adreno_is_a330v2(adreno_dev))
+ kgsl_regwrite(device, A3XX_RBBM_INTERFACE_HANG_INT_CTL,
+ (1 << 31) | 0xFFFF);
+ else
+ kgsl_regwrite(device, A3XX_RBBM_INTERFACE_HANG_INT_CTL,
+ (1 << 16) | 0xFFF);
/* Enable 64-byte cacheline size. HW Default is 32-byte (0x000000E0). */
kgsl_regwrite(device, A3XX_UCHE_CACHE_MODE_CONTROL_REG, 0x00000001);
@@ -4235,7 +4251,7 @@
*/
int a3xx_coresight_enable(struct kgsl_device *device)
{
- mutex_lock(&device->mutex);
+ kgsl_mutex_lock(&device->mutex, &device->mutex_owner);
if (!kgsl_active_count_get(device)) {
kgsl_regwrite(device, A3XX_RBBM_DEBUG_BUS_CTL, 0x0001093F);
kgsl_regwrite(device, A3XX_RBBM_DEBUG_BUS_STB_CTL0,
@@ -4256,7 +4272,7 @@
0x00000001);
kgsl_active_count_put(device);
}
- mutex_unlock(&device->mutex);
+ kgsl_mutex_unlock(&device->mutex, &device->mutex_owner);
return 0;
}
@@ -4267,7 +4283,7 @@
*/
void a3xx_coresight_disable(struct kgsl_device *device)
{
- mutex_lock(&device->mutex);
+ kgsl_mutex_lock(&device->mutex, &device->mutex_owner);
if (!kgsl_active_count_get(device)) {
kgsl_regwrite(device, A3XX_RBBM_DEBUG_BUS_CTL, 0x0);
kgsl_regwrite(device, A3XX_RBBM_DEBUG_BUS_STB_CTL0, 0x0);
@@ -4280,18 +4296,18 @@
kgsl_regwrite(device, A3XX_RBBM_EXT_TRACE_CMD, 0x0);
kgsl_active_count_put(device);
}
- mutex_unlock(&device->mutex);
+ kgsl_mutex_unlock(&device->mutex, &device->mutex_owner);
}
static void a3xx_coresight_write_reg(struct kgsl_device *device,
unsigned int wordoffset, unsigned int val)
{
- mutex_lock(&device->mutex);
+ kgsl_mutex_lock(&device->mutex, &device->mutex_owner);
if (!kgsl_active_count_get(device)) {
kgsl_regwrite(device, wordoffset, val);
kgsl_active_count_put(device);
}
- mutex_unlock(&device->mutex);
+ kgsl_mutex_unlock(&device->mutex, &device->mutex_owner);
}
void a3xx_coresight_config_debug_reg(struct kgsl_device *device,
diff --git a/drivers/gpu/msm/adreno_coresight.c b/drivers/gpu/msm/adreno_coresight.c
index d0ba145..d374e23 100644
--- a/drivers/gpu/msm/adreno_coresight.c
+++ b/drivers/gpu/msm/adreno_coresight.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2014, 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
@@ -117,12 +117,12 @@
{
unsigned int regval = 0;
- mutex_lock(&device->mutex);
+ kgsl_mutex_lock(&device->mutex, &device->mutex_owner);
if (!kgsl_active_count_get(device)) {
kgsl_regread(device, offset, ®val);
kgsl_active_count_put(device);
}
- mutex_unlock(&device->mutex);
+ kgsl_mutex_unlock(&device->mutex, &device->mutex_owner);
return snprintf(buf, PAGE_SIZE, "0x%X", regval);
}
diff --git a/drivers/gpu/msm/adreno_dispatch.c b/drivers/gpu/msm/adreno_dispatch.c
index ab9d220..588c243 100644
--- a/drivers/gpu/msm/adreno_dispatch.c
+++ b/drivers/gpu/msm/adreno_dispatch.c
@@ -21,6 +21,7 @@
#include "adreno.h"
#include "adreno_ringbuffer.h"
#include "adreno_trace.h"
+#include "kgsl_sharedmem.h"
#define CMDQUEUE_NEXT(_i, _s) (((_i) + 1) % (_s))
@@ -49,7 +50,7 @@
static unsigned int _cmdbatch_timeout = 2000;
/* Interval for reading and comparing fault detection registers */
-static unsigned int _fault_timer_interval = 50;
+static unsigned int _fault_timer_interval = 200;
/* Local array for the current set of fault detect registers */
static unsigned int fault_detect_regs[FT_DETECT_REGS_COUNT];
@@ -269,7 +270,7 @@
dispatcher->inflight++;
- mutex_lock(&device->mutex);
+ kgsl_mutex_lock(&device->mutex, &device->mutex_owner);
if (dispatcher->inflight == 1 &&
!test_bit(ADRENO_DISPATCHER_POWER, &dispatcher->priv)) {
@@ -277,7 +278,7 @@
ret = kgsl_active_count_get(device);
if (ret) {
dispatcher->inflight--;
- mutex_unlock(&device->mutex);
+ kgsl_mutex_unlock(&device->mutex, &device->mutex_owner);
return ret;
}
@@ -300,7 +301,7 @@
}
}
- mutex_unlock(&device->mutex);
+ kgsl_mutex_unlock(&device->mutex, &device->mutex_owner);
if (ret) {
dispatcher->inflight--;
@@ -770,7 +771,8 @@
* passed in then zero the size which effectively skips it when it is submitted
* in the ringbuffer.
*/
-static void cmdbatch_skip_ib(struct kgsl_cmdbatch *cmdbatch, unsigned int base)
+static void cmdbatch_skip_ib(struct kgsl_cmdbatch *cmdbatch,
+ unsigned int base)
{
int i;
@@ -783,6 +785,48 @@
}
}
+static void cmdbatch_skip_cmd(struct kgsl_cmdbatch *cmdbatch,
+ struct kgsl_cmdbatch **replay, int count)
+{
+ struct adreno_context *drawctxt = ADRENO_CONTEXT(cmdbatch->context);
+ int i;
+
+ /*
+ * SKIPCMD policy: next IB issued for this context is tentative
+ * if it fails we assume that GFT failed and if it succeeds
+ * we mark GFT as a success.
+ *
+ * Find next commandbatch for the faulting context
+ * If commandbatch is found
+ * a) store the current commandbatch fault_policy in context's next
+ * commandbatch fault_policy
+ * b) force preamble for next commandbatch
+ */
+ for (i = 1; i < count; i++) {
+ if (replay[i]->context->id == cmdbatch->context->id) {
+ replay[i]->fault_policy = replay[0]->fault_policy;
+ set_bit(CMDBATCH_FLAG_FORCE_PREAMBLE, &replay[i]->priv);
+ set_bit(KGSL_FT_SKIPCMD, &replay[i]->fault_recovery);
+ break;
+ }
+ }
+
+ /*
+ * If we did not find the next cmd then
+ * a) set a flag for next command issued in this context
+ * b) store the fault_policy, this fault_policy becomes the policy of
+ * next command issued in this context
+ */
+ if ((i == count) && drawctxt) {
+ set_bit(ADRENO_CONTEXT_SKIP_CMD, &drawctxt->priv);
+ drawctxt->fault_policy = replay[0]->fault_policy;
+ }
+
+ /* set the flags to skip this cmdbatch */
+ set_bit(CMDBATCH_FLAG_SKIP, &cmdbatch->priv);
+ cmdbatch->fault_recovery = 0;
+}
+
static void cmdbatch_skip_frame(struct kgsl_cmdbatch *cmdbatch,
struct kgsl_cmdbatch **replay, int count)
{
@@ -852,10 +896,10 @@
drawctxt->state == ADRENO_CONTEXT_STATE_INVALID) {
replay[i] = NULL;
- mutex_lock(&device->mutex);
+ kgsl_mutex_lock(&device->mutex, &device->mutex_owner);
kgsl_cancel_events_timestamp(device, cmd->context,
cmd->timestamp);
- mutex_unlock(&device->mutex);
+ kgsl_mutex_unlock(&device->mutex, &device->mutex_owner);
kgsl_cmdbatch_destroy(cmd);
}
@@ -925,6 +969,18 @@
rptr, wptr, ib1base, ib1sz, ib2base, ib2sz);
}
+void adreno_fault_skipcmd_detached(struct kgsl_device *device,
+ struct adreno_context *drawctxt,
+ struct kgsl_cmdbatch *cmdbatch)
+{
+ if (test_bit(ADRENO_CONTEXT_SKIP_CMD, &drawctxt->priv) &&
+ kgsl_context_detached(&drawctxt->base)) {
+ pr_fault(device, cmdbatch, "gpu %s ctx %d\n",
+ "detached", cmdbatch->context->id);
+ clear_bit(ADRENO_CONTEXT_SKIP_CMD, &drawctxt->priv);
+ }
+}
+
static int dispatcher_do_fault(struct kgsl_device *device)
{
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
@@ -947,6 +1003,12 @@
if (dispatcher->inflight == 0) {
KGSL_DRV_WARN(device,
"dispatcher_do_fault with 0 inflight commands\n");
+ /*
+ * For certain faults like h/w fault the interrupts are
+ * turned off, re-enable here
+ */
+ if (kgsl_pwrctrl_isenabled(device))
+ kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_ON);
return 0;
}
@@ -954,7 +1016,7 @@
del_timer_sync(&dispatcher->timer);
del_timer_sync(&dispatcher->fault_timer);
- mutex_lock(&device->mutex);
+ kgsl_mutex_lock(&device->mutex, &device->mutex_owner);
cmdbatch = dispatcher->cmdqueue[dispatcher->head];
@@ -987,7 +1049,7 @@
kgsl_device_snapshot(device, 1);
}
- mutex_unlock(&device->mutex);
+ kgsl_mutex_unlock(&device->mutex, &device->mutex_owner);
/* Allocate memory to store the inflight commands */
replay = kzalloc(sizeof(*replay) * dispatcher->inflight, GFP_KERNEL);
@@ -1148,6 +1210,16 @@
goto replay;
}
+ /* Skip the faulted command batch submission */
+ if (test_and_clear_bit(KGSL_FT_SKIPCMD, &cmdbatch->fault_policy)) {
+ trace_adreno_cmdbatch_recovery(cmdbatch, BIT(KGSL_FT_SKIPCMD));
+
+ /* Skip faulting command batch */
+ cmdbatch_skip_cmd(cmdbatch, replay, count);
+
+ goto replay;
+ }
+
if (test_and_clear_bit(KGSL_FT_SKIPFRAME, &cmdbatch->fault_policy)) {
trace_adreno_cmdbatch_recovery(cmdbatch,
BIT(KGSL_FT_SKIPFRAME));
@@ -1179,10 +1251,10 @@
dispatcher->head = dispatcher->tail = 0;
/* Reset the GPU */
- mutex_lock(&device->mutex);
+ kgsl_mutex_lock(&device->mutex, &device->mutex_owner);
ret = adreno_reset(device);
- mutex_unlock(&device->mutex);
+ kgsl_mutex_unlock(&device->mutex, &device->mutex_owner);
/* if any other fault got in until reset then ignore */
fault = atomic_xchg(&dispatcher->fault, 0);
@@ -1418,9 +1490,9 @@
* stragglers
*/
if (dispatcher->inflight == 0 && count) {
- mutex_lock(&device->mutex);
+ kgsl_mutex_lock(&device->mutex, &device->mutex_owner);
queue_work(device->work_queue, &device->ts_expired_ws);
- mutex_unlock(&device->mutex);
+ kgsl_mutex_unlock(&device->mutex, &device->mutex_owner);
}
/* Dispatch new commands if we have the room */
@@ -1437,12 +1509,12 @@
mod_timer(&dispatcher->timer, cmdbatch->expires);
/* There are still things in flight - update the idle counts */
- mutex_lock(&device->mutex);
+ kgsl_mutex_lock(&device->mutex, &device->mutex_owner);
kgsl_pwrscale_idle(device);
- mutex_unlock(&device->mutex);
+ kgsl_mutex_unlock(&device->mutex, &device->mutex_owner);
} else {
/* There is nothing left in the pipeline. Shut 'er down boys */
- mutex_lock(&device->mutex);
+ kgsl_mutex_lock(&device->mutex, &device->mutex_owner);
/*
* Stop the fault timer before decrementing the active count to
* avoid reading the hardware registers while we are trying to
@@ -1455,13 +1527,13 @@
clear_bit(ADRENO_DISPATCHER_POWER, &dispatcher->priv);
}
- mutex_unlock(&device->mutex);
+ kgsl_mutex_unlock(&device->mutex, &device->mutex_owner);
}
/* Before leaving update the pwrscale information */
- mutex_lock(&device->mutex);
+ kgsl_mutex_lock(&device->mutex, &device->mutex_owner);
kgsl_pwrscale_idle(device);
- mutex_unlock(&device->mutex);
+ kgsl_mutex_unlock(&device->mutex, &device->mutex_owner);
mutex_unlock(&dispatcher->mutex);
}
@@ -1559,14 +1631,8 @@
*/
void adreno_dispatcher_start(struct kgsl_device *device)
{
- struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
-
complete_all(&device->cmdbatch_gate);
- /* a305b & a305c GPUs are slower than a330 and needs a larger timer */
- if (adreno_is_a305b(adreno_dev) || adreno_is_a305c(adreno_dev))
- _fault_timer_interval = 200;
-
/* Schedule the work loop to get things going */
adreno_dispatcher_schedule(device);
}
diff --git a/drivers/gpu/msm/adreno_drawctxt.c b/drivers/gpu/msm/adreno_drawctxt.c
index 136456a..0778cac 100644
--- a/drivers/gpu/msm/adreno_drawctxt.c
+++ b/drivers/gpu/msm/adreno_drawctxt.c
@@ -171,9 +171,9 @@
drawctxt->state != ADRENO_CONTEXT_STATE_ACTIVE)
return 1;
- mutex_lock(&device->mutex);
+ kgsl_mutex_lock(&device->mutex, &device->mutex_owner);
ret = kgsl_check_timestamp(device, &drawctxt->base, timestamp);
- mutex_unlock(&device->mutex);
+ kgsl_mutex_unlock(&device->mutex, &device->mutex_owner);
return ret;
}
@@ -227,7 +227,7 @@
io = (io_cnt < pwr->pwrlevels[pwr->active_pwrlevel].io_fraction)
? 0 : 1;
- mutex_unlock(&device->mutex);
+ kgsl_mutex_unlock(&device->mutex, &device->mutex_owner);
if (timeout) {
ret = (int) adreno_wait_event_interruptible_timeout(
@@ -245,7 +245,7 @@
io);
}
- mutex_lock(&device->mutex);
+ kgsl_mutex_lock(&device->mutex, &device->mutex_owner);
/* -EDEADLK if the context was invalidated while we were waiting */
if (drawctxt->state == ADRENO_CONTEXT_STATE_INVALID)
@@ -314,7 +314,7 @@
goto done;
}
- mutex_unlock(&device->mutex);
+ kgsl_mutex_unlock(&device->mutex, &device->mutex_owner);
if (timeout) {
ret = (int) wait_event_timeout(drawctxt->waiting,
@@ -330,7 +330,7 @@
_check_global_timestamp(device, drawctxt, timestamp));
}
- mutex_lock(&device->mutex);
+ kgsl_mutex_lock(&device->mutex, &device->mutex_owner);
if (ret)
kgsl_cancel_events_timestamp(device, NULL, timestamp);
@@ -381,10 +381,10 @@
mutex_unlock(&drawctxt->mutex);
- mutex_lock(&device->mutex);
+ kgsl_mutex_lock(&device->mutex, &device->mutex_owner);
kgsl_cancel_events_timestamp(device, context,
cmdbatch->timestamp);
- mutex_unlock(&device->mutex);
+ kgsl_mutex_unlock(&device->mutex, &device->mutex_owner);
kgsl_cmdbatch_destroy(cmdbatch);
mutex_lock(&drawctxt->mutex);
@@ -530,6 +530,13 @@
mutex_unlock(&drawctxt->mutex);
/*
+ * If the context is deteached while we are waiting for
+ * the next command in GFT SKIP CMD, print the context
+ * detached status here.
+ */
+ adreno_fault_skipcmd_detached(device, drawctxt, cmdbatch);
+
+ /*
* Don't hold the drawctxt mutex while the cmdbatch is being
* destroyed because the cmdbatch destroy takes the device
* mutex and the world falls in on itself
diff --git a/drivers/gpu/msm/adreno_drawctxt.h b/drivers/gpu/msm/adreno_drawctxt.h
index 258cf94..8543f39 100644
--- a/drivers/gpu/msm/adreno_drawctxt.h
+++ b/drivers/gpu/msm/adreno_drawctxt.h
@@ -130,6 +130,7 @@
* @waiting: Workqueue structure for contexts waiting for a timestamp or event
* @queued: Number of commands queued in the cmdqueue
* @ops: Context switch functions for this context.
+ * @fault_policy: GFT fault policy set in cmdbatch_skip_cmd();
*/
struct adreno_context {
struct kgsl_context base;
@@ -177,6 +178,7 @@
int queued;
const struct adreno_context_ops *ops;
+ unsigned int fault_policy;
};
/**
@@ -192,6 +194,8 @@
* @ADRENO_CONTEXT_SKIP_EOF - Context skip IBs until the next end of frame
* marker.
* @ADRENO_CONTEXT_FORCE_PREAMBLE - Force the preamble for the next submission.
+ * @ADRENO_CONTEXT_SKIP_CMD - Context's command batch is skipped during
+ fault tolerance.
*/
enum adreno_context_priv {
ADRENO_CONTEXT_FAULT = 0,
@@ -203,6 +207,7 @@
ADRENO_CONTEXT_GPU_HANG_FT,
ADRENO_CONTEXT_SKIP_EOF,
ADRENO_CONTEXT_FORCE_PREAMBLE,
+ ADRENO_CONTEXT_SKIP_CMD,
};
struct kgsl_context *adreno_drawctxt_create(struct kgsl_device_private *,
diff --git a/drivers/gpu/msm/adreno_profile.c b/drivers/gpu/msm/adreno_profile.c
index 26c8b69..38e0af8 100644
--- a/drivers/gpu/msm/adreno_profile.c
+++ b/drivers/gpu/msm/adreno_profile.c
@@ -479,9 +479,9 @@
struct kgsl_device *device = data;
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
- mutex_lock(&device->mutex);
+ kgsl_mutex_lock(&device->mutex, &device->mutex_owner);
*val = adreno_profile_enabled(&adreno_dev->profile);
- mutex_unlock(&device->mutex);
+ kgsl_mutex_unlock(&device->mutex, &device->mutex_owner);
return 0;
}
@@ -492,10 +492,10 @@
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
struct adreno_profile *profile = &adreno_dev->profile;
- mutex_lock(&device->mutex);
+ kgsl_mutex_lock(&device->mutex, &device->mutex_owner);
if (adreno_is_a2xx(adreno_dev)) {
- mutex_unlock(&device->mutex);
+ kgsl_mutex_unlock(&device->mutex, &device->mutex_owner);
return 0;
}
@@ -503,7 +503,7 @@
check_close_profile(profile);
- mutex_unlock(&device->mutex);
+ kgsl_mutex_unlock(&device->mutex, &device->mutex_owner);
return 0;
}
@@ -522,11 +522,11 @@
if (adreno_is_a2xx(adreno_dev))
return -EINVAL;
- mutex_lock(&device->mutex);
+ kgsl_mutex_lock(&device->mutex, &device->mutex_owner);
buf = kmalloc(max_size, GFP_KERNEL);
if (!buf) {
- mutex_unlock(&device->mutex);
+ kgsl_mutex_unlock(&device->mutex, &device->mutex_owner);
return -ENOMEM;
}
@@ -546,7 +546,7 @@
kfree(buf);
- mutex_unlock(&device->mutex);
+ kgsl_mutex_unlock(&device->mutex, &device->mutex_owner);
return size;
}
@@ -679,7 +679,7 @@
if (adreno_is_a2xx(adreno_dev))
return -ENOSPC;
- mutex_lock(&device->mutex);
+ kgsl_mutex_lock(&device->mutex, &device->mutex_owner);
if (adreno_profile_enabled(profile)) {
size = -EINVAL;
@@ -745,7 +745,7 @@
error_put:
kgsl_active_count_put(device);
error_unlock:
- mutex_unlock(&device->mutex);
+ kgsl_mutex_unlock(&device->mutex, &device->mutex_owner);
return size;
}
@@ -883,7 +883,7 @@
* for each perf counter <cntr_reg_off> <start hi & lo> <end hi & low>
*/
- mutex_lock(&device->mutex);
+ kgsl_mutex_lock(&device->mutex, &device->mutex_owner);
while (1) {
/* process any results that are available into the log_buffer */
@@ -905,10 +905,10 @@
}
}
- mutex_unlock(&device->mutex);
+ kgsl_mutex_unlock(&device->mutex, &device->mutex_owner);
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(HZ / 10);
- mutex_lock(&device->mutex);
+ kgsl_mutex_lock(&device->mutex, &device->mutex_owner);
if (signal_pending(current)) {
status = 0;
@@ -917,7 +917,7 @@
}
check_close_profile(profile);
- mutex_unlock(&device->mutex);
+ kgsl_mutex_unlock(&device->mutex, &device->mutex_owner);
return status;
}
@@ -934,7 +934,7 @@
if (adreno_is_a2xx(adreno_dev))
return -EINVAL;
- mutex_lock(&device->mutex);
+ kgsl_mutex_lock(&device->mutex, &device->mutex_owner);
for (i = 0; i < counters->group_count; ++i) {
group = &(counters->groups[i]);
@@ -950,7 +950,7 @@
group->reg_count, used);
}
- mutex_unlock(&device->mutex);
+ kgsl_mutex_unlock(&device->mutex, &device->mutex_owner);
return 0;
}
diff --git a/drivers/gpu/msm/adreno_ringbuffer.c b/drivers/gpu/msm/adreno_ringbuffer.c
index 7bbbc35..57d21f5 100644
--- a/drivers/gpu/msm/adreno_ringbuffer.c
+++ b/drivers/gpu/msm/adreno_ringbuffer.c
@@ -1228,6 +1228,29 @@
/* process any profiling results that are available into the log_buf */
adreno_profile_process_results(device);
+ /*
+ * If SKIP CMD flag is set for current context
+ * a) set SKIPCMD as fault_recovery for current commandbatch
+ * b) store context's commandbatch fault_policy in current
+ * commandbatch fault_policy and clear context's commandbatch
+ * fault_policy
+ * c) force preamble for commandbatch
+ */
+ if (test_bit(ADRENO_CONTEXT_SKIP_CMD, &drawctxt->priv) &&
+ (!test_bit(CMDBATCH_FLAG_SKIP, &cmdbatch->priv))) {
+
+ set_bit(KGSL_FT_SKIPCMD, &cmdbatch->fault_recovery);
+ cmdbatch->fault_policy = drawctxt->fault_policy;
+ set_bit(CMDBATCH_FLAG_FORCE_PREAMBLE, &cmdbatch->priv);
+
+ /* if context is detached print fault recovery */
+ adreno_fault_skipcmd_detached(device, drawctxt, cmdbatch);
+
+ /* clear the drawctxt flags */
+ clear_bit(ADRENO_CONTEXT_SKIP_CMD, &drawctxt->priv);
+ drawctxt->fault_policy = 0;
+ }
+
/*When preamble is enabled, the preamble buffer with state restoration
commands are stored in the first node of the IB chain. We can skip that
if a context switch hasn't occured */
diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c
index 11b7429..a96ab2b 100644
--- a/drivers/gpu/msm/kgsl.c
+++ b/drivers/gpu/msm/kgsl.c
@@ -628,7 +628,7 @@
KGSL_PWR_WARN(device, "suspend start\n");
- mutex_lock(&device->mutex);
+ kgsl_mutex_lock(&device->mutex, &device->mutex_owner);
kgsl_pwrctrl_request_state(device, KGSL_STATE_SUSPEND);
/* Tell the device to drain the submission queue */
@@ -683,7 +683,7 @@
device->ftbl->resume(device);
}
- mutex_unlock(&device->mutex);
+ kgsl_mutex_unlock(&device->mutex, &device->mutex_owner);
KGSL_PWR_WARN(device, "suspend end\n");
return status;
}
@@ -694,7 +694,7 @@
return -EINVAL;
KGSL_PWR_WARN(device, "resume start\n");
- mutex_lock(&device->mutex);
+ kgsl_mutex_lock(&device->mutex, &device->mutex_owner);
if (device->state == KGSL_STATE_SUSPEND) {
kgsl_pwrctrl_set_state(device, KGSL_STATE_SLUMBER);
complete_all(&device->hwaccess_gate);
@@ -718,7 +718,7 @@
if (device->ftbl->resume)
device->ftbl->resume(device);
- mutex_unlock(&device->mutex);
+ kgsl_mutex_unlock(&device->mutex, &device->mutex_owner);
KGSL_PWR_WARN(device, "resume end\n");
return 0;
}
@@ -953,7 +953,7 @@
filep->private_data = NULL;
- mutex_lock(&device->mutex);
+ kgsl_mutex_lock(&device->mutex, &device->mutex_owner);
while (1) {
read_lock(&device->context_lock);
@@ -1005,7 +1005,7 @@
kgsl_cancel_events(device, dev_priv);
result = kgsl_close_device(device);
- mutex_unlock(&device->mutex);
+ kgsl_mutex_unlock(&device->mutex, &device->mutex_owner);
kfree(dev_priv);
@@ -1087,12 +1087,12 @@
dev_priv->device = device;
filep->private_data = dev_priv;
- mutex_lock(&device->mutex);
+ kgsl_mutex_lock(&device->mutex, &device->mutex_owner);
result = kgsl_open_device(device);
if (result)
goto err_freedevpriv;
- mutex_unlock(&device->mutex);
+ kgsl_mutex_unlock(&device->mutex, &device->mutex_owner);
/*
* Get file (per process) private struct. This must be done
@@ -1112,7 +1112,7 @@
return result;
err_stop:
- mutex_lock(&device->mutex);
+ kgsl_mutex_lock(&device->mutex, &device->mutex_owner);
device->open_count--;
if (device->open_count == 0) {
/* make sure power is on to stop the device */
@@ -1122,7 +1122,7 @@
atomic_dec(&device->active_cnt);
}
err_freedevpriv:
- mutex_unlock(&device->mutex);
+ kgsl_mutex_unlock(&device->mutex, &device->mutex_owner);
filep->private_data = NULL;
kfree(dev_priv);
err_pmruntime:
@@ -1789,10 +1789,10 @@
list_add(&event->node, &cmdbatch->synclist);
spin_unlock(&cmdbatch->lock);
- mutex_lock(&device->mutex);
+ kgsl_mutex_lock(&device->mutex, &device->mutex_owner);
ret = kgsl_add_event(device, context->id, sync->timestamp,
kgsl_cmdbatch_sync_func, event, NULL);
- mutex_unlock(&device->mutex);
+ kgsl_mutex_unlock(&device->mutex, &device->mutex_owner);
if (ret) {
spin_lock(&cmdbatch->lock);
@@ -3525,12 +3525,14 @@
}
if (lock)
- mutex_lock(&dev_priv->device->mutex);
+ kgsl_mutex_lock(&dev_priv->device->mutex,
+ &dev_priv->device->mutex_owner);
ret = func(dev_priv, cmd, uptr);
if (lock)
- mutex_unlock(&dev_priv->device->mutex);
+ kgsl_mutex_unlock(&dev_priv->device->mutex,
+ &dev_priv->device->mutex_owner);
/*
* Still copy back on failure, but assume function took
diff --git a/drivers/gpu/msm/kgsl_cffdump.c b/drivers/gpu/msm/kgsl_cffdump.c
index 43bcc30..171435a 100644
--- a/drivers/gpu/msm/kgsl_cffdump.c
+++ b/drivers/gpu/msm/kgsl_cffdump.c
@@ -631,20 +631,20 @@
}
}
if (!device->cff_dump_enable) {
- mutex_lock(&device->mutex);
+ kgsl_mutex_lock(&device->mutex, &device->mutex_owner);
device->cff_dump_enable = 1;
ret = kgsl_open_device(device);
if (!ret)
ret = kgsl_active_count_get(device);
if (ret)
device->cff_dump_enable = 0;
- mutex_unlock(&device->mutex);
+ kgsl_mutex_unlock(&device->mutex, &device->mutex_owner);
}
} else if (device->cff_dump_enable && !val) {
- mutex_lock(&device->mutex);
+ kgsl_mutex_lock(&device->mutex, &device->mutex_owner);
ret = kgsl_close_device(device);
device->cff_dump_enable = 0;
- mutex_unlock(&device->mutex);
+ kgsl_mutex_unlock(&device->mutex, &device->mutex_owner);
}
done:
mutex_unlock(&kgsl_driver.devlock);
diff --git a/drivers/gpu/msm/kgsl_debugfs.c b/drivers/gpu/msm/kgsl_debugfs.c
index 318b10d..c1b3139 100644
--- a/drivers/gpu/msm/kgsl_debugfs.c
+++ b/drivers/gpu/msm/kgsl_debugfs.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2002,2008-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2002,2008-2014, 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
@@ -30,9 +30,9 @@
struct kgsl_device *device = data;
if (val) {
- mutex_lock(&device->mutex);
+ kgsl_mutex_lock(&device->mutex, &device->mutex_owner);
kgsl_postmortem_dump(device, 1);
- mutex_unlock(&device->mutex);
+ kgsl_mutex_unlock(&device->mutex, &device->mutex_owner);
}
return 0;
@@ -260,8 +260,10 @@
kgsl_get_memory_usage(usage, sizeof(usage), m->flags);
- seq_printf(s, "%08x %08lx %8d %5d %5s %10s %16s %5d\n",
- m->gpuaddr, m->useraddr, m->size, entry->id, flags,
+ seq_printf(s, "%pK %pK %8zd %5d %5s %10s %16s %5d\n",
+ (unsigned long *) m->gpuaddr,
+ (unsigned long *) m->useraddr,
+ m->size, entry->id, flags,
memtype_str(entry->memtype), usage, m->sglen);
}
@@ -347,7 +349,7 @@
* So if debugfs is disabled in kernel, return as
* success.
*/
- dentry = debugfs_create_file("mem", 0400, private->debug_root, private,
+ dentry = debugfs_create_file("mem", 0444, private->debug_root, private,
&process_mem_fops);
if (IS_ERR(dentry)) {
diff --git a/drivers/gpu/msm/kgsl_device.h b/drivers/gpu/msm/kgsl_device.h
index fc4b77e..47801a4 100644
--- a/drivers/gpu/msm/kgsl_device.h
+++ b/drivers/gpu/msm/kgsl_device.h
@@ -251,6 +251,7 @@
int open_count;
struct mutex mutex;
+ atomic64_t mutex_owner;
uint32_t state;
uint32_t requested_state;
@@ -745,4 +746,35 @@
return 0;
}
+
+/**
+ * kgsl_mutex_lock() -- try to acquire the mutex if current thread does not
+ * already own it
+ * @mutex: mutex to lock
+ * @owner: current mutex owner
+ */
+
+static inline int kgsl_mutex_lock(struct mutex *mutex, atomic64_t *owner)
+{
+
+ if (atomic64_read(owner) != (long)current) {
+ mutex_lock(mutex);
+ atomic64_set(owner, (long)current);
+ /* Barrier to make sure owner is updated */
+ smp_wmb();
+ return 0;
+ }
+ return 1;
+}
+
+/**
+ * kgsl_mutex_unlock() -- Clear the owner and unlock the mutex
+ * @mutex: mutex to unlock
+ * @owner: current mutex owner
+ */
+static inline void kgsl_mutex_unlock(struct mutex *mutex, atomic64_t *owner)
+{
+ atomic64_set(owner, 0);
+ mutex_unlock(mutex);
+}
#endif /* __KGSL_DEVICE_H */
diff --git a/drivers/gpu/msm/kgsl_events.c b/drivers/gpu/msm/kgsl_events.c
index ccd13d5..6cea80f 100644
--- a/drivers/gpu/msm/kgsl_events.c
+++ b/drivers/gpu/msm/kgsl_events.c
@@ -409,7 +409,7 @@
struct kgsl_context *context, *tmp;
uint32_t timestamp;
- mutex_lock(&device->mutex);
+ kgsl_mutex_lock(&device->mutex, &device->mutex_owner);
timestamp = kgsl_readtimestamp(device, NULL, KGSL_TIMESTAMP_RETIRED);
_retire_events(device, &device->events, timestamp);
@@ -436,6 +436,6 @@
}
}
- mutex_unlock(&device->mutex);
+ kgsl_mutex_unlock(&device->mutex, &device->mutex_owner);
}
EXPORT_SYMBOL(kgsl_process_events);
diff --git a/drivers/gpu/msm/kgsl_iommu.c b/drivers/gpu/msm/kgsl_iommu.c
old mode 100755
new mode 100644
index 69b953f..488e5a8
--- a/drivers/gpu/msm/kgsl_iommu.c
+++ b/drivers/gpu/msm/kgsl_iommu.c
@@ -1048,6 +1048,10 @@
*cmds++ = 0x1;
*cmds++ = 0x1;
+ /* WAIT_REG_MEM turns back on protected mode - push it off */
+ *cmds++ = cp_type3_packet(CP_SET_PROTECTED_MODE, 1);
+ *cmds++ = 0;
+
*cmds++ = cp_type3_packet(CP_MEM_WRITE, 2);
*cmds++ = lock_vars->turn;
*cmds++ = 0;
@@ -1062,11 +1066,19 @@
*cmds++ = 0x1;
*cmds++ = 0x1;
+ /* WAIT_REG_MEM turns back on protected mode - push it off */
+ *cmds++ = cp_type3_packet(CP_SET_PROTECTED_MODE, 1);
+ *cmds++ = 0;
+
*cmds++ = cp_type3_packet(CP_TEST_TWO_MEMS, 3);
*cmds++ = lock_vars->flag[PROC_APPS];
*cmds++ = lock_vars->turn;
*cmds++ = 0;
+ /* TEST_TWO_MEMS turns back on protected mode - push it off */
+ *cmds++ = cp_type3_packet(CP_SET_PROTECTED_MODE, 1);
+ *cmds++ = 0;
+
cmds += adreno_add_idle_cmds(adreno_dev, cmds);
return cmds - start;
@@ -1104,6 +1116,10 @@
*cmds++ = 0x1;
*cmds++ = 0x1;
+ /* WAIT_REG_MEM turns back on protected mode - push it off */
+ *cmds++ = cp_type3_packet(CP_SET_PROTECTED_MODE, 1);
+ *cmds++ = 0;
+
cmds += adreno_add_idle_cmds(adreno_dev, cmds);
return cmds - start;
@@ -1744,16 +1760,42 @@
return status;
}
+static void kgsl_iommu_flush_tlb_pt_current(struct kgsl_pagetable *pt)
+{
+ int lock_taken = 0;
+ struct kgsl_device *device = pt->mmu->device;
+ struct kgsl_iommu *iommu = pt->mmu->priv;
+
+ /*
+ * Check to see if the current thread already holds the device mutex.
+ * If it does not, then take the device mutex which is required for
+ * flushing the tlb
+ */
+ if (!kgsl_mutex_lock(&device->mutex, &device->mutex_owner))
+ lock_taken = 1;
+
+ /*
+ * Flush the tlb only if the iommu device is attached and the pagetable
+ * hasn't been switched yet
+ */
+ if (kgsl_mmu_is_perprocess(pt->mmu) &&
+ iommu->iommu_units[0].dev[KGSL_IOMMU_CONTEXT_USER].attached &&
+ kgsl_iommu_pt_equal(pt->mmu, pt,
+ kgsl_iommu_get_current_ptbase(pt->mmu)))
+ kgsl_iommu_default_setstate(pt->mmu, KGSL_MMUFLAGS_TLBFLUSH);
+
+ if (lock_taken)
+ kgsl_mutex_unlock(&device->mutex, &device->mutex_owner);
+}
+
static int
kgsl_iommu_unmap(struct kgsl_pagetable *pt,
struct kgsl_memdesc *memdesc,
unsigned int *tlb_flags)
{
- int ret = 0, lock_taken = 0;
+ int ret = 0;
unsigned int range = memdesc->size;
struct kgsl_iommu_pt *iommu_pt = pt->priv;
- struct kgsl_device *device = pt->mmu->device;
- struct kgsl_iommu *iommu = pt->mmu->priv;
/* All GPU addresses as assigned are page aligned, but some
functions purturb the gpuaddr with an offset, so apply the
@@ -1775,29 +1817,7 @@
return ret;
}
- /*
- * Check to see if the current thread already holds the device mutex.
- * If it does not, then take the device mutex which is required for
- * flushing the tlb
- */
- if (!mutex_is_locked(&device->mutex) ||
- device->mutex.owner != current) {
- mutex_lock(&device->mutex);
- lock_taken = 1;
- }
-
- /*
- * Flush the tlb only if the iommu device is attached and the pagetable
- * hasn't been switched yet
- */
- if (kgsl_mmu_is_perprocess(pt->mmu) &&
- iommu->iommu_units[0].dev[KGSL_IOMMU_CONTEXT_USER].attached &&
- kgsl_iommu_pt_equal(pt->mmu, pt,
- kgsl_iommu_get_current_ptbase(pt->mmu)))
- kgsl_iommu_default_setstate(pt->mmu, KGSL_MMUFLAGS_TLBFLUSH);
-
- if (lock_taken)
- mutex_unlock(&device->mutex);
+ kgsl_iommu_flush_tlb_pt_current(pt);
return ret;
}
@@ -1839,6 +1859,23 @@
size);
}
}
+
+ /*
+ * IOMMU V1 BFBs pre-fetch data beyond what is being used by the core.
+ * This can include both allocated pages and un-allocated pages.
+ * If an un-allocated page is cached, and later used (if it has been
+ * newly dynamically allocated by SW) the SMMU HW should automatically
+ * re-fetch the pages from memory (rather than using the cached
+ * un-allocated page). This logic is known as the re-fetch logic.
+ * In current chips we suspect this re-fetch logic is broken,
+ * it can result in bad translations which can either cause downstream
+ * bus errors, or upstream cores being hung (because of garbage data
+ * being read) -> causing TLB sync stuck issues. As a result SW must
+ * implement the invalidate+map.
+ */
+ if (!msm_soc_version_supports_iommu_v0())
+ kgsl_iommu_flush_tlb_pt_current(pt);
+
return ret;
}
diff --git a/drivers/gpu/msm/kgsl_mmu.c b/drivers/gpu/msm/kgsl_mmu.c
index d64d0d3..65e607b 100644
--- a/drivers/gpu/msm/kgsl_mmu.c
+++ b/drivers/gpu/msm/kgsl_mmu.c
@@ -401,6 +401,10 @@
status = kgsl_allocate_contiguous(&mmu->setstate_memory, PAGE_SIZE);
if (status)
return status;
+
+ /* Mark the setstate memory as read only */
+ mmu->setstate_memory.flags |= KGSL_MEMFLAGS_GPUREADONLY;
+
kgsl_sharedmem_set(device, &mmu->setstate_memory, 0, 0,
mmu->setstate_memory.size);
diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c
index 656d7e2..563f7ef 100644
--- a/drivers/gpu/msm/kgsl_pwrctrl.c
+++ b/drivers/gpu/msm/kgsl_pwrctrl.c
@@ -78,7 +78,7 @@
};
static void kgsl_pwrctrl_clk(struct kgsl_device *device, int state,
- int requested_state);
+ int requested_state);
static void kgsl_pwrctrl_axi(struct kgsl_device *device, int state);
static void kgsl_pwrctrl_pwrrail(struct kgsl_device *device, int state);
@@ -233,7 +233,7 @@
if (ret)
return ret;
- mutex_lock(&device->mutex);
+ kgsl_mutex_lock(&device->mutex, &device->mutex_owner);
if (level > pwr->num_pwrlevels - 2)
level = pwr->num_pwrlevels - 2;
@@ -249,7 +249,7 @@
*/
if (pwr->thermal_pwrlevel > pwr->active_pwrlevel)
kgsl_pwrctrl_pwrlevel_change(device, pwr->thermal_pwrlevel);
- mutex_unlock(&device->mutex);
+ kgsl_mutex_unlock(&device->mutex, &device->mutex_owner);
return count;
}
@@ -285,7 +285,7 @@
if (ret)
return ret;
- mutex_lock(&device->mutex);
+ kgsl_mutex_lock(&device->mutex, &device->mutex_owner);
/* You can't set a maximum power level lower than the minimum */
if (level > pwr->min_pwrlevel)
@@ -302,7 +302,7 @@
*/
if (max_level > pwr->active_pwrlevel)
kgsl_pwrctrl_pwrlevel_change(device, max_level);
- mutex_unlock(&device->mutex);
+ kgsl_mutex_unlock(&device->mutex, &device->mutex_owner);
return count;
}
@@ -337,7 +337,7 @@
if (ret)
return ret;
- mutex_lock(&device->mutex);
+ kgsl_mutex_lock(&device->mutex, &device->mutex_owner);
if (level > pwr->num_pwrlevels - 2)
level = pwr->num_pwrlevels - 2;
@@ -356,7 +356,7 @@
if (min_level < pwr->active_pwrlevel)
kgsl_pwrctrl_pwrlevel_change(device, min_level);
- mutex_unlock(&device->mutex);
+ kgsl_mutex_unlock(&device->mutex, &device->mutex_owner);
return count;
}
@@ -418,7 +418,7 @@
if (ret)
return ret;
- mutex_lock(&device->mutex);
+ kgsl_mutex_lock(&device->mutex, &device->mutex_owner);
level = _get_nearest_pwrlevel(pwr, val);
if (level < 0)
goto done;
@@ -434,7 +434,7 @@
kgsl_pwrctrl_pwrlevel_change(device, pwr->thermal_pwrlevel);
done:
- mutex_unlock(&device->mutex);
+ kgsl_mutex_unlock(&device->mutex, &device->mutex_owner);
return count;
}
@@ -470,12 +470,12 @@
if (ret)
return ret;
- mutex_lock(&device->mutex);
+ kgsl_mutex_lock(&device->mutex, &device->mutex_owner);
level = _get_nearest_pwrlevel(pwr, val);
if (level >= 0)
kgsl_pwrctrl_pwrlevel_change(device, level);
- mutex_unlock(&device->mutex);
+ kgsl_mutex_unlock(&device->mutex, &device->mutex_owner);
return count;
}
@@ -509,13 +509,13 @@
if (ret)
return ret;
- mutex_lock(&device->mutex);
+ kgsl_mutex_lock(&device->mutex, &device->mutex_owner);
/* Let the timeout be requested in ms, but convert to jiffies. */
val /= div;
pwr->interval_timeout = val;
- mutex_unlock(&device->mutex);
+ kgsl_mutex_unlock(&device->mutex, &device->mutex_owner);
return count;
}
@@ -548,9 +548,9 @@
if (ret)
return ret;
- mutex_lock(&device->mutex);
+ kgsl_mutex_lock(&device->mutex, &device->mutex_owner);
device->pwrctrl.pm_qos_latency = val;
- mutex_unlock(&device->mutex);
+ kgsl_mutex_unlock(&device->mutex, &device->mutex_owner);
return count;
}
@@ -692,9 +692,9 @@
if (ret)
return ret;
- mutex_lock(&device->mutex);
+ kgsl_mutex_lock(&device->mutex, &device->mutex_owner);
__force_on(device, flag, val);
- mutex_unlock(&device->mutex);
+ kgsl_mutex_unlock(&device->mutex, &device->mutex_owner);
return count;
}
@@ -767,9 +767,9 @@
if (ret)
return ret;
- mutex_lock(&device->mutex);
+ kgsl_mutex_lock(&device->mutex, &device->mutex_owner);
device->pwrctrl.bus_control = val ? true : false;
- mutex_unlock(&device->mutex);
+ kgsl_mutex_unlock(&device->mutex, &device->mutex_owner);
return count;
}
@@ -883,7 +883,7 @@
}
}
-static void kgsl_pwrctrl_clk(struct kgsl_device *device, int state,
+void kgsl_pwrctrl_clk(struct kgsl_device *device, int state,
int requested_state)
{
struct kgsl_pwrctrl *pwr = &device->pwrctrl;
@@ -1242,7 +1242,7 @@
if (device == NULL)
return;
- mutex_lock(&device->mutex);
+ kgsl_mutex_lock(&device->mutex, &device->mutex_owner);
kgsl_pwrscale_update(device);
@@ -1266,10 +1266,10 @@
if (!device->pwrctrl.irq_last)
break;
kgsl_pwrctrl_request_state(device, requested_state);
- mutex_unlock(&device->mutex);
+ kgsl_mutex_unlock(&device->mutex, &device->mutex_owner);
udelay(delay);
delay *= 2;
- mutex_lock(&device->mutex);
+ kgsl_mutex_lock(&device->mutex, &device->mutex_owner);
}
@@ -1293,7 +1293,7 @@
}
}
- mutex_unlock(&device->mutex);
+ kgsl_mutex_unlock(&device->mutex, &device->mutex_owner);
}
EXPORT_SYMBOL(kgsl_idle_check);
@@ -1627,9 +1627,9 @@
if ((atomic_read(&device->active_cnt) == 0) &&
(device->state != KGSL_STATE_ACTIVE)) {
- mutex_unlock(&device->mutex);
+ kgsl_mutex_unlock(&device->mutex, &device->mutex_owner);
wait_for_completion(&device->hwaccess_gate);
- mutex_lock(&device->mutex);
+ kgsl_mutex_lock(&device->mutex, &device->mutex_owner);
ret = kgsl_pwrctrl_wake(device, 1);
}
@@ -1719,10 +1719,10 @@
if (atomic_read(&device->active_cnt) > count) {
int ret;
- mutex_unlock(&device->mutex);
+ kgsl_mutex_unlock(&device->mutex, &device->mutex_owner);
ret = wait_event_timeout(device->active_cnt_wq,
_check_active_count(device, count), HZ);
- mutex_lock(&device->mutex);
+ kgsl_mutex_lock(&device->mutex, &device->mutex_owner);
result = ret == 0 ? -ETIMEDOUT : 0;
}
diff --git a/drivers/gpu/msm/kgsl_pwrscale.c b/drivers/gpu/msm/kgsl_pwrscale.c
index 0bb13a4..b0b1474 100644
--- a/drivers/gpu/msm/kgsl_pwrscale.c
+++ b/drivers/gpu/msm/kgsl_pwrscale.c
@@ -197,7 +197,7 @@
pwr = &device->pwrctrl;
- mutex_lock(&device->mutex);
+ kgsl_mutex_lock(&device->mutex, &device->mutex_owner);
cur_freq = kgsl_pwrctrl_active_freq(pwr);
level = pwr->active_pwrlevel;
@@ -247,7 +247,7 @@
*freq = kgsl_pwrctrl_active_freq(pwr);
}
- mutex_unlock(&device->mutex);
+ kgsl_mutex_unlock(&device->mutex, &device->mutex_owner);
return 0;
}
EXPORT_SYMBOL(kgsl_devfreq_target);
@@ -274,7 +274,7 @@
pwrscale = &device->pwrscale;
- mutex_lock(&device->mutex);
+ kgsl_mutex_lock(&device->mutex, &device->mutex_owner);
/* make sure we don't turn on clocks just to read stats */
if (device->state == KGSL_STATE_ACTIVE) {
struct kgsl_power_stats extra;
@@ -302,7 +302,7 @@
trace_kgsl_pwrstats(device, stat->total_time, &pwrscale->accum_stats);
memset(&pwrscale->accum_stats, 0, sizeof(pwrscale->accum_stats));
- mutex_unlock(&device->mutex);
+ kgsl_mutex_unlock(&device->mutex, &device->mutex_owner);
return 0;
}
@@ -325,9 +325,9 @@
if (freq == NULL)
return -EINVAL;
- mutex_lock(&device->mutex);
+ kgsl_mutex_lock(&device->mutex, &device->mutex_owner);
*freq = kgsl_pwrctrl_active_freq(&device->pwrctrl);
- mutex_unlock(&device->mutex);
+ kgsl_mutex_unlock(&device->mutex, &device->mutex_owner);
return 0;
}
diff --git a/drivers/gpu/msm/kgsl_snapshot.c b/drivers/gpu/msm/kgsl_snapshot.c
index 333089a..76f0507 100644
--- a/drivers/gpu/msm/kgsl_snapshot.c
+++ b/drivers/gpu/msm/kgsl_snapshot.c
@@ -660,7 +660,7 @@
return 0;
/* Get the mutex to keep things from changing while we are dumping */
- mutex_lock(&device->mutex);
+ kgsl_mutex_lock(&device->mutex, &device->mutex_owner);
obj_itr_init(&itr, buf, off, count);
@@ -699,7 +699,7 @@
}
done:
- mutex_unlock(&device->mutex);
+ kgsl_mutex_unlock(&device->mutex, &device->mutex_owner);
return itr.write;
}
@@ -731,12 +731,12 @@
size_t count)
{
if (device && count > 0) {
- mutex_lock(&device->mutex);
+ kgsl_mutex_lock(&device->mutex, &device->mutex_owner);
if (!kgsl_active_count_get(device)) {
kgsl_device_snapshot(device, 0);
kgsl_active_count_put(device);
}
- mutex_unlock(&device->mutex);
+ kgsl_mutex_unlock(&device->mutex, &device->mutex_owner);
}
return count;
diff --git a/drivers/gpu/msm/z180.c b/drivers/gpu/msm/z180.c
index 270a7a6..622bd92 100644
--- a/drivers/gpu/msm/z180.c
+++ b/drivers/gpu/msm/z180.c
@@ -404,7 +404,7 @@
unsigned int numibs;
struct kgsl_ibdesc *ibdesc;
- mutex_lock(&device->mutex);
+ kgsl_mutex_lock(&device->mutex, &device->mutex_owner);
result = kgsl_active_count_get(device);
if (result)
@@ -518,7 +518,7 @@
kgsl_active_count_put(device);
error_active_count:
- mutex_unlock(&device->mutex);
+ kgsl_mutex_unlock(&device->mutex, &device->mutex_owner);
return (int)result;
}
@@ -866,9 +866,9 @@
status = kgsl_active_count_get(device);
if (!status) {
- mutex_unlock(&device->mutex);
+ kgsl_mutex_unlock(&device->mutex, &device->mutex_owner);
status = z180_wait(device, context, timestamp, msecs);
- mutex_lock(&device->mutex);
+ kgsl_mutex_lock(&device->mutex, &device->mutex_owner);
kgsl_active_count_put(device);
}
diff --git a/drivers/hwmon/qpnp-adc-common.c b/drivers/hwmon/qpnp-adc-common.c
index 7bc8773..96b058b 100644
--- a/drivers/hwmon/qpnp-adc-common.c
+++ b/drivers/hwmon/qpnp-adc-common.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2014, 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
@@ -814,17 +814,22 @@
return -EINVAL;
scale_voltage = (adc_code -
- chan_properties->adc_graph[CALIB_ABSOLUTE].adc_gnd)
- * chan_properties->adc_graph[CALIB_ABSOLUTE].dx;
+ chan_properties->adc_graph[chan_properties->calib_type].adc_gnd)
+ * chan_properties->adc_graph[chan_properties->calib_type].dx;
if (scale_voltage < 0) {
negative_offset = 1;
scale_voltage = -scale_voltage;
}
do_div(scale_voltage,
- chan_properties->adc_graph[CALIB_ABSOLUTE].dy);
+ chan_properties->adc_graph[chan_properties->calib_type].dy);
if (negative_offset)
scale_voltage = -scale_voltage;
- scale_voltage += chan_properties->adc_graph[CALIB_ABSOLUTE].dx;
+
+ if (chan_properties->calib_type == CALIB_ABSOLUTE)
+ scale_voltage +=
+ chan_properties->adc_graph[chan_properties->calib_type].dx;
+ else
+ scale_voltage *= 1000;
if (scale_voltage < 0) {
if (adc_properties->bipolar) {
@@ -986,6 +991,92 @@
}
EXPORT_SYMBOL(qpnp_vadc_check_result);
+int qpnp_adc_get_revid_version(struct device *dev)
+{
+ struct pmic_revid_data *revid_data;
+ struct device_node *revid_dev_node;
+
+ revid_dev_node = of_parse_phandle(dev->of_node,
+ "qcom,pmic-revid", 0);
+ if (!revid_dev_node) {
+ pr_debug("Missing qcom,pmic-revid property\n");
+ return -EINVAL;
+ }
+
+ revid_data = get_revid_data(revid_dev_node);
+ if (IS_ERR(revid_data)) {
+ pr_debug("revid error rc = %ld\n", PTR_ERR(revid_data));
+ return -EINVAL;
+ }
+
+ if ((revid_data->rev1 == PM8941_V3P1_REV1) &&
+ (revid_data->rev2 == PM8941_V3P1_REV2) &&
+ (revid_data->rev3 == PM8941_V3P1_REV3) &&
+ (revid_data->rev4 == PM8941_V3P1_REV4) &&
+ (revid_data->pmic_type == PM8941_V3P1_TYPE) &&
+ (revid_data->pmic_subtype == PM8941_V3P1_SUBTYPE))
+ return QPNP_REV_ID_8941_3_1;
+ else if ((revid_data->rev1 == PM8941_V3P0_REV1) &&
+ (revid_data->rev2 == PM8941_V3P0_REV2) &&
+ (revid_data->rev3 == PM8941_V3P0_REV3) &&
+ (revid_data->rev4 == PM8941_V3P0_REV4) &&
+ (revid_data->pmic_type == PM8941_V3P0_TYPE) &&
+ (revid_data->pmic_subtype == PM8941_V3P0_SUBTYPE))
+ return QPNP_REV_ID_8941_3_0;
+ else if ((revid_data->rev1 == PM8941_V2P0_REV1) &&
+ (revid_data->rev2 == PM8941_V2P0_REV2) &&
+ (revid_data->rev3 == PM8941_V2P0_REV3) &&
+ (revid_data->rev4 == PM8941_V2P0_REV4) &&
+ (revid_data->pmic_type == PM8941_V2P0_TYPE) &&
+ (revid_data->pmic_subtype == PM8941_V2P0_SUBTYPE))
+ return QPNP_REV_ID_8941_2_0;
+ else if ((revid_data->rev1 == PM8226_V2P2_REV1) &&
+ (revid_data->rev2 == PM8226_V2P2_REV2) &&
+ (revid_data->rev3 == PM8226_V2P2_REV3) &&
+ (revid_data->rev4 == PM8226_V2P2_REV4) &&
+ (revid_data->pmic_type == PM8226_V2P2_TYPE) &&
+ (revid_data->pmic_subtype == PM8226_V2P2_SUBTYPE))
+ return QPNP_REV_ID_8026_2_2;
+ else if ((revid_data->rev1 == PM8226_V2P1_REV1) &&
+ (revid_data->rev2 == PM8226_V2P1_REV2) &&
+ (revid_data->rev3 == PM8226_V2P1_REV3) &&
+ (revid_data->rev4 == PM8226_V2P1_REV4) &&
+ (revid_data->pmic_type == PM8226_V2P1_TYPE) &&
+ (revid_data->pmic_subtype == PM8226_V2P1_SUBTYPE))
+ return QPNP_REV_ID_8026_2_1;
+ else if ((revid_data->rev1 == PM8226_V2P0_REV1) &&
+ (revid_data->rev2 == PM8226_V2P0_REV2) &&
+ (revid_data->rev3 == PM8226_V2P0_REV3) &&
+ (revid_data->rev4 == PM8226_V2P0_REV4) &&
+ (revid_data->pmic_type == PM8226_V2P0_TYPE) &&
+ (revid_data->pmic_subtype == PM8226_V2P0_SUBTYPE))
+ return QPNP_REV_ID_8026_2_0;
+ else if ((revid_data->rev1 == PM8226_V1P0_REV1) &&
+ (revid_data->rev2 == PM8226_V1P0_REV2) &&
+ (revid_data->rev3 == PM8226_V1P0_REV3) &&
+ (revid_data->rev4 == PM8226_V1P0_REV4) &&
+ (revid_data->pmic_type == PM8226_V1P0_TYPE) &&
+ (revid_data->pmic_subtype == PM8226_V1P0_SUBTYPE))
+ return QPNP_REV_ID_8026_1_0;
+ else if ((revid_data->rev1 == PM8110_V1P0_REV1) &&
+ (revid_data->rev2 == PM8110_V1P0_REV2) &&
+ (revid_data->rev3 == PM8110_V1P0_REV3) &&
+ (revid_data->rev4 == PM8110_V1P0_REV4) &&
+ (revid_data->pmic_type == PM8110_V1P0_TYPE) &&
+ (revid_data->pmic_subtype == PM8110_V1P0_SUBTYPE))
+ return QPNP_REV_ID_8110_1_0;
+ else if ((revid_data->rev1 == PM8110_V2P0_REV1) &&
+ (revid_data->rev2 == PM8110_V2P0_REV2) &&
+ (revid_data->rev3 == PM8110_V2P0_REV3) &&
+ (revid_data->rev4 == PM8110_V2P0_REV4) &&
+ (revid_data->pmic_type == PM8110_V2P0_TYPE) &&
+ (revid_data->pmic_subtype == PM8110_V2P0_SUBTYPE))
+ return QPNP_REV_ID_8110_2_0;
+ else
+ return -EINVAL;
+}
+EXPORT_SYMBOL(qpnp_adc_get_revid_version);
+
int32_t qpnp_adc_get_devicetree_data(struct spmi_device *spmi,
struct qpnp_adc_drv *adc_qpnp)
{
@@ -1104,6 +1195,7 @@
adc_channel_list[i].adc_scale_fn = post_scaling;
adc_channel_list[i].hw_settle_time = hw_settle_time;
adc_channel_list[i].fast_avg_setup = fast_avg_setup;
+ adc_channel_list[i].calib_type = calib_type;
i++;
}
diff --git a/drivers/hwmon/qpnp-adc-current.c b/drivers/hwmon/qpnp-adc-current.c
index 44da261..dddbbd0 100644
--- a/drivers/hwmon/qpnp-adc-current.c
+++ b/drivers/hwmon/qpnp-adc-current.c
@@ -130,7 +130,8 @@
#define QPNP_RSENSE_MSB_SIGN_CHECK 0x80
#define QPNP_ADC_COMPLETION_TIMEOUT HZ
#define SMBB_BAT_IF_TRIM_CNST_RDS_MASK 0x7
-#define SMBB_BAT_IF_TRIM_CNST_RDS_MASK_CONST 2
+#define SMBB_BAT_IF_TRIM_CNST_RDS_MASK_CONST_0 0
+#define SMBB_BAT_IF_TRIM_CNST_RDS_MASK_CONST_2 2
#define QPNP_IADC1_USR_TRIM2_ADC_FULLSCALE1_CONST 127
#define QPNP_IADC_RSENSE_DEFAULT_VALUE 7800000
#define QPNP_IADC_RSENSE_DEFAULT_TYPEB_GF 9000000
@@ -172,6 +173,7 @@
enum qpnp_iadc_rsense_rds_workaround {
QPNP_IADC_RDS_DEFAULT_TYPEA,
QPNP_IADC_RDS_DEFAULT_TYPEB,
+ QPNP_IADC_RDS_DEFAULT_TYPEC,
};
static int32_t qpnp_iadc_read_reg(struct qpnp_iadc_chip *iadc,
@@ -340,48 +342,8 @@
return 0;
}
-#define QPNP_IADC_PM8941_3_1_REV2 3
-#define QPNP_IADC_PM8941_3_1_REV3 2
-#define QPNP_IADC_PM8026_1_REV2 1
-#define QPNP_IADC_PM8026_1_REV3 2
-#define QPNP_IADC_PM8026_2_REV2 4
-#define QPNP_IADC_PM8026_2_REV3 2
-#define QPNP_IADC_PM8110_1_REV2 2
-#define QPNP_IADC_PM8110_1_REV3 2
-
-#define QPNP_IADC_REV_ID_8941_3_1 1
-#define QPNP_IADC_REV_ID_8026_1_0 2
-#define QPNP_IADC_REV_ID_8026_2_0 3
-#define QPNP_IADC_REV_ID_8110_1_0 4
-
-static void qpnp_temp_comp_version_check(struct qpnp_iadc_chip *iadc,
- int32_t *version)
-{
- if ((iadc->iadc_comp.revision_dig_major ==
- QPNP_IADC_PM8941_3_1_REV2) &&
- (iadc->iadc_comp.revision_ana_minor ==
- QPNP_IADC_PM8941_3_1_REV3))
- *version = QPNP_IADC_REV_ID_8941_3_1;
- else if ((iadc->iadc_comp.revision_dig_major ==
- QPNP_IADC_PM8026_1_REV2) &&
- (iadc->iadc_comp.revision_ana_minor ==
- QPNP_IADC_PM8026_1_REV3))
- *version = QPNP_IADC_REV_ID_8026_1_0;
- else if ((iadc->iadc_comp.revision_dig_major ==
- QPNP_IADC_PM8026_2_REV2) &&
- (iadc->iadc_comp.revision_ana_minor ==
- QPNP_IADC_PM8026_2_REV3))
- *version = QPNP_IADC_REV_ID_8026_2_0;
- else if ((iadc->iadc_comp.revision_dig_major ==
- QPNP_IADC_PM8110_1_REV2) &&
- (iadc->iadc_comp.revision_ana_minor ==
- QPNP_IADC_PM8110_1_REV3))
- *version = QPNP_IADC_REV_ID_8110_1_0;
- else
- *version = -EINVAL;
-
- return;
-}
+#define QPNP_IADC_PM8026_2_REV2 4
+#define QPNP_IADC_PM8026_2_REV3 2
#define QPNP_COEFF_1 969000
#define QPNP_COEFF_2 32
@@ -408,15 +370,19 @@
#define QPNP_COEFF_22 5000000
#define QPNP_COEFF_23 3722500
#define QPNP_COEFF_24 84
+#define QPNP_COEFF_25 33
+#define QPNP_COEFF_26 22
+#define QPNP_COEFF_27 53
+#define QPNP_COEFF_28 48
static int32_t qpnp_iadc_comp(int64_t *result, struct qpnp_iadc_chip *iadc,
int64_t die_temp)
{
int64_t temp_var = 0, sys_gain_coeff = 0, old;
int32_t coeff_a = 0, coeff_b = 0;
- int32_t version;
+ int version = 0;
- qpnp_temp_comp_version_check(iadc, &version);
+ version = qpnp_adc_get_revid_version(iadc->dev);
if (version == -EINVAL)
return 0;
@@ -431,7 +397,7 @@
iadc->iadc_comp.sys_gain;
switch (version) {
- case QPNP_IADC_REV_ID_8941_3_1:
+ case QPNP_REV_ID_8941_3_1:
switch (iadc->iadc_comp.id) {
case COMP_ID_GF:
if (!iadc->iadc_comp.ext_rsense) {
@@ -470,7 +436,60 @@
break;
}
break;
- case QPNP_IADC_REV_ID_8026_1_0:
+ case QPNP_REV_ID_8026_2_1:
+ case QPNP_REV_ID_8026_2_2:
+ /* pm8026 rev 2.1 and 2.2 */
+ switch (iadc->iadc_comp.id) {
+ case COMP_ID_GF:
+ if (!iadc->iadc_comp.ext_rsense) {
+ /* internal rsense */
+ if (*result < 0) {
+ /* charge */
+ coeff_a = 0;
+ coeff_b = 0;
+ } else {
+ coeff_a = QPNP_COEFF_25;
+ coeff_b = 0;
+ }
+ } else {
+ if (*result < 0) {
+ /* charge */
+ coeff_a = 0;
+ coeff_b = 0;
+ } else {
+ /* discharge */
+ coeff_a = 0;
+ coeff_b = 0;
+ }
+ }
+ break;
+ case COMP_ID_TSMC:
+ default:
+ if (!iadc->iadc_comp.ext_rsense) {
+ /* internal rsense */
+ if (*result < 0) {
+ /* charge */
+ coeff_a = 0;
+ coeff_b = 0;
+ } else {
+ coeff_a = QPNP_COEFF_26;
+ coeff_b = 0;
+ }
+ } else {
+ if (*result < 0) {
+ /* charge */
+ coeff_a = 0;
+ coeff_b = 0;
+ } else {
+ /* discharge */
+ coeff_a = 0;
+ coeff_b = 0;
+ }
+ }
+ break;
+ }
+ break;
+ case QPNP_REV_ID_8026_1_0:
/* pm8026 rev 1.0 */
switch (iadc->iadc_comp.id) {
case COMP_ID_GF:
@@ -522,7 +541,7 @@
break;
}
break;
- case QPNP_IADC_REV_ID_8110_1_0:
+ case QPNP_REV_ID_8110_1_0:
/* pm8110 rev 1.0 */
switch (iadc->iadc_comp.id) {
case COMP_ID_GF:
@@ -554,8 +573,41 @@
break;
}
break;
+ case QPNP_REV_ID_8110_2_0:
+ die_temp -= 25000;
+ /* pm8110 rev 2.0 */
+ switch (iadc->iadc_comp.id) {
+ case COMP_ID_GF:
+ if (!iadc->iadc_comp.ext_rsense) {
+ /* internal rsense */
+ if (*result < 0) {
+ /* charge */
+ coeff_a = 0;
+ coeff_b = 0;
+ } else {
+ coeff_a = QPNP_COEFF_27;
+ coeff_b = 0;
+ }
+ }
+ break;
+ case COMP_ID_SMIC:
+ default:
+ if (!iadc->iadc_comp.ext_rsense) {
+ /* internal rsense */
+ if (*result < 0) {
+ /* charge */
+ coeff_a = 0;
+ coeff_b = 0;
+ } else {
+ coeff_a = QPNP_COEFF_28;
+ coeff_b = 0;
+ }
+ }
+ break;
+ }
+ break;
default:
- case QPNP_IADC_REV_ID_8026_2_0:
+ case QPNP_REV_ID_8026_2_0:
/* pm8026 rev 1.0 */
coeff_a = 0;
coeff_b = 0;
@@ -578,7 +630,8 @@
temp_var = div64_s64(temp_var * sys_gain_coeff, 1000000);
*result = div64_s64(*result * 1000, temp_var);
}
- pr_debug("%lld compensated into %lld\n", old, *result);
+ pr_debug("%lld compensated into %lld, a: %d, b: %d, sys_gain: %lld\n",
+ old, *result, coeff_a, coeff_b, sys_gain_coeff);
return 0;
}
@@ -593,6 +646,7 @@
{
int rc = 0;
u8 trim2_val = 0, smbb_batt_trm_data = 0;
+ u8 smbb_batt_trm_cnst_rds = 0;
if (!iadc->rds_trim_default_check) {
pr_debug("No internal rds trim check needed\n");
@@ -612,11 +666,15 @@
return rc;
}
+ smbb_batt_trm_cnst_rds = smbb_batt_trm_data &
+ SMBB_BAT_IF_TRIM_CNST_RDS_MASK;
+
pr_debug("n_trim:0x%x smb_trm:0x%x\n", trim2_val, smbb_batt_trm_data);
if (iadc->rds_trim_default_type == QPNP_IADC_RDS_DEFAULT_TYPEA) {
- if (((smbb_batt_trm_data & SMBB_BAT_IF_TRIM_CNST_RDS_MASK) ==
- SMBB_BAT_IF_TRIM_CNST_RDS_MASK_CONST) &&
+
+ if ((smbb_batt_trm_cnst_rds ==
+ SMBB_BAT_IF_TRIM_CNST_RDS_MASK_CONST_2) &&
(trim2_val == QPNP_IADC1_USR_TRIM2_ADC_FULLSCALE1_CONST)) {
iadc->rsense_workaround_value =
QPNP_IADC_RSENSE_DEFAULT_VALUE;
@@ -624,15 +682,14 @@
}
} else if (iadc->rds_trim_default_type ==
QPNP_IADC_RDS_DEFAULT_TYPEB) {
- if (((smbb_batt_trm_data & SMBB_BAT_IF_TRIM_CNST_RDS_MASK) >=
- SMBB_BAT_IF_TRIM_CNST_RDS_MASK_CONST) &&
+ if ((smbb_batt_trm_cnst_rds >=
+ SMBB_BAT_IF_TRIM_CNST_RDS_MASK_CONST_2) &&
(trim2_val == QPNP_IADC1_USR_TRIM2_ADC_FULLSCALE1_CONST)) {
iadc->rsense_workaround_value =
QPNP_IADC_RSENSE_DEFAULT_VALUE;
iadc->default_internal_rsense = true;
- } else if (((smbb_batt_trm_data &
- SMBB_BAT_IF_TRIM_CNST_RDS_MASK)
- < SMBB_BAT_IF_TRIM_CNST_RDS_MASK_CONST) &&
+ } else if ((smbb_batt_trm_cnst_rds <
+ SMBB_BAT_IF_TRIM_CNST_RDS_MASK_CONST_2) &&
(trim2_val ==
QPNP_IADC1_USR_TRIM2_ADC_FULLSCALE1_CONST)) {
if (iadc->iadc_comp.id == COMP_ID_GF) {
@@ -645,6 +702,17 @@
iadc->default_internal_rsense = true;
}
}
+ } else if (iadc->rds_trim_default_type == QPNP_IADC_RDS_DEFAULT_TYPEC) {
+
+ if ((smbb_batt_trm_cnst_rds >
+ SMBB_BAT_IF_TRIM_CNST_RDS_MASK_CONST_0) &&
+ (smbb_batt_trm_cnst_rds <=
+ SMBB_BAT_IF_TRIM_CNST_RDS_MASK_CONST_2) &&
+ (trim2_val == QPNP_IADC1_USR_TRIM2_ADC_FULLSCALE1_CONST)) {
+ iadc->rsense_workaround_value =
+ QPNP_IADC_RSENSE_DEFAULT_VALUE;
+ iadc->default_internal_rsense = true;
+ }
}
return 0;
@@ -844,9 +912,10 @@
bool batfet_closed)
{
uint8_t rslt_lsb, rslt_msb;
- int32_t rc = 0;
+ int32_t rc = 0, version = 0;
uint16_t raw_data;
uint32_t mode_sel = 0;
+ bool iadc_offset_ch_batfet_check;
if (qpnp_iadc_is_valid(iadc) < 0)
return -EPROBE_DEFER;
@@ -868,13 +937,22 @@
iadc->adc->calib.gain_raw = raw_data;
/*
- * there is a features in the BMS where if the batfet is opened
- * the BMS reads from INTERNAL_RSENSE (channel 0) actually go to
+ * there is a features on PM8941 in the BMS where if the batfet is
+ * opened the BMS reads from INTERNAL_RSENSE (channel 0) actually go to
* OFFSET_CALIBRATION_CSP_CSN (channel 5). Hence if batfet is opened
* we have to calibrate based on OFFSET_CALIBRATION_CSP_CSN even for
* internal rsense.
*/
- if (!batfet_closed || iadc->external_rsense) {
+ version = qpnp_adc_get_revid_version(iadc->dev);
+ if ((version == QPNP_REV_ID_8941_3_1) ||
+ (version == QPNP_REV_ID_8941_3_0) ||
+ (version == QPNP_REV_ID_8941_2_0))
+ iadc_offset_ch_batfet_check = true;
+ else
+ iadc_offset_ch_batfet_check = false;
+
+ if ((iadc_offset_ch_batfet_check && !batfet_closed) ||
+ (iadc->external_rsense)) {
/* external offset calculation */
rc = qpnp_iadc_configure(iadc, OFFSET_CALIBRATION_CSP_CSN,
&raw_data, mode_sel);
@@ -1141,10 +1219,12 @@
result->result_uv = -result->result_uv;
result_current = -result_current;
}
+ result_current *= -1;
rc = qpnp_iadc_comp_result(iadc, &result_current);
if (rc < 0)
pr_err("Error during compensating the IADC\n");
rc = 0;
+ result_current *= -1;
result->result_ua = (int32_t) result_current;
fail:
diff --git a/drivers/hwmon/qpnp-adc-voltage.c b/drivers/hwmon/qpnp-adc-voltage.c
index d462fb3..ee69840 100644
--- a/drivers/hwmon/qpnp-adc-voltage.c
+++ b/drivers/hwmon/qpnp-adc-voltage.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2014, 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
@@ -512,63 +512,52 @@
#define QPNP_VBAT_COEFF_13 102640000
#define QPNP_VBAT_COEFF_14 22220000
#define QPNP_VBAT_COEFF_15 83060000
-
-#define QPNP_VADC_REV_ID_8941_3_1 1
-#define QPNP_VADC_REV_ID_8026_1_0 2
-#define QPNP_VADC_REV_ID_8026_2_0 3
-
-static void qpnp_temp_comp_version_check(struct qpnp_vadc_chip *vadc,
- int32_t *version)
-{
- if (vadc->revision_dig_major == 3 &&
- vadc->revision_ana_minor == 2)
- *version = QPNP_VADC_REV_ID_8941_3_1;
- else if (vadc->revision_dig_major == 1 &&
- vadc->revision_ana_minor == 2)
- *version = QPNP_VADC_REV_ID_8026_1_0;
- else if (vadc->revision_dig_major == 2 &&
- vadc->revision_ana_minor == 2)
- *version = QPNP_VADC_REV_ID_8026_2_0;
- else
- *version = -EINVAL;
-
- return;
-}
+#define QPNP_VBAT_COEFF_16 2810
+#define QPNP_VBAT_COEFF_17 5260
+#define QPNP_VBAT_COEFF_18 8027
+#define QPNP_VBAT_COEFF_19 2347
+#define QPNP_VBAT_COEFF_20 6043
+#define QPNP_VBAT_COEFF_21 1914
+#define QPNP_VBAT_OFFSET_SMIC 9446
+#define QPNP_VBAT_OFFSET_GF 9441
+#define QPNP_OCV_OFFSET_SMIC 4596
+#define QPNP_OCV_OFFSET_GF 5896
+#define QPNP_VBAT_COEFF_22 6800
+#define QPNP_VBAT_COEFF_23 3500
+#define QPNP_VBAT_COEFF_24 4360
+#define QPNP_VBAT_COEFF_25 8060
static int32_t qpnp_ocv_comp(int64_t *result,
struct qpnp_vadc_chip *vadc, int64_t die_temp)
{
int64_t temp_var = 0;
int64_t old = *result;
- int32_t version;
+ int version;
- qpnp_temp_comp_version_check(vadc, &version);
+ version = qpnp_adc_get_revid_version(vadc->dev);
if (version == -EINVAL)
return 0;
- if (die_temp < 25000)
- return 0;
-
- if (die_temp > 60000)
- die_temp = 60000;
+ if (version == QPNP_REV_ID_8026_2_2) {
+ if (die_temp > 25000)
+ return 0;
+ }
switch (version) {
- case QPNP_VADC_REV_ID_8941_3_1:
+ case QPNP_REV_ID_8941_3_1:
switch (vadc->id) {
case COMP_ID_TSMC:
- temp_var = (((die_temp *
- (-QPNP_VBAT_COEFF_4))
- + QPNP_VBAT_COEFF_5));
+ temp_var = ((die_temp - 25000) *
+ (-QPNP_VBAT_COEFF_4));
break;
default:
case COMP_ID_GF:
- temp_var = (((die_temp *
- (-QPNP_VBAT_COEFF_1))
- + QPNP_VBAT_COEFF_2));
+ temp_var = ((die_temp - 25000) *
+ (-QPNP_VBAT_COEFF_1));
break;
}
break;
- case QPNP_VADC_REV_ID_8026_1_0:
+ case QPNP_REV_ID_8026_1_0:
switch (vadc->id) {
case COMP_ID_TSMC:
temp_var = (((die_temp *
@@ -583,19 +572,56 @@
break;
}
break;
- case QPNP_VADC_REV_ID_8026_2_0:
+ case QPNP_REV_ID_8026_2_0:
+ case QPNP_REV_ID_8026_2_1:
switch (vadc->id) {
case COMP_ID_TSMC:
- temp_var = ((die_temp - 2500) *
+ temp_var = ((die_temp - 25000) *
(-QPNP_VBAT_COEFF_10));
break;
default:
case COMP_ID_GF:
- temp_var = ((die_temp - 2500) *
+ temp_var = ((die_temp - 25000) *
(-QPNP_VBAT_COEFF_8));
break;
}
break;
+ case QPNP_REV_ID_8026_2_2:
+ switch (vadc->id) {
+ case COMP_ID_TSMC:
+ *result -= QPNP_VBAT_COEFF_22;
+ temp_var = (die_temp - 25000) *
+ QPNP_VBAT_COEFF_24;
+ break;
+ default:
+ case COMP_ID_GF:
+ *result -= QPNP_VBAT_COEFF_22;
+ temp_var = (die_temp - 25000) *
+ QPNP_VBAT_COEFF_25;
+ break;
+ }
+ break;
+ case QPNP_REV_ID_8110_2_0:
+ switch (vadc->id) {
+ case COMP_ID_SMIC:
+ *result -= QPNP_OCV_OFFSET_SMIC;
+ if (die_temp < 25000)
+ temp_var = QPNP_VBAT_COEFF_18;
+ else
+ temp_var = QPNP_VBAT_COEFF_19;
+ temp_var = (die_temp - 25000) * temp_var;
+ break;
+ default:
+ case COMP_ID_GF:
+ *result -= QPNP_OCV_OFFSET_GF;
+ if (die_temp < 25000)
+ temp_var = QPNP_VBAT_COEFF_20;
+ else
+ temp_var = QPNP_VBAT_COEFF_21;
+ temp_var = (die_temp - 25000) * temp_var;
+ break;
+ }
+ break;
default:
temp_var = 0;
break;
@@ -618,35 +644,36 @@
{
int64_t temp_var = 0;
int64_t old = *result;
- int32_t version;
+ int version;
- qpnp_temp_comp_version_check(vadc, &version);
+ version = qpnp_adc_get_revid_version(vadc->dev);
if (version == -EINVAL)
return 0;
- if (die_temp < 25000)
- return 0;
-
- /* min(die_temp_c, 60_degC) */
- if (die_temp > 60000)
- die_temp = 60000;
+ if (version != QPNP_REV_ID_8941_3_1) {
+ /* min(die_temp_c, 60_degC) */
+ if (die_temp > 60000)
+ die_temp = 60000;
+ }
switch (version) {
- case QPNP_VADC_REV_ID_8941_3_1:
+ case QPNP_REV_ID_8941_3_1:
switch (vadc->id) {
case COMP_ID_TSMC:
- temp_var = (die_temp *
+ temp_var = ((die_temp - 25000) *
(-QPNP_VBAT_COEFF_1));
break;
default:
case COMP_ID_GF:
- temp_var = (((die_temp *
- (-QPNP_VBAT_COEFF_6))
- + QPNP_VBAT_COEFF_7));
+ /* min(die_temp_c, 60_degC) */
+ if (die_temp > 60000)
+ die_temp = 60000;
+ temp_var = ((die_temp - 25000) *
+ (-QPNP_VBAT_COEFF_1));
break;
}
break;
- case QPNP_VADC_REV_ID_8026_1_0:
+ case QPNP_REV_ID_8026_1_0:
switch (vadc->id) {
case COMP_ID_TSMC:
temp_var = (((die_temp *
@@ -661,19 +688,48 @@
break;
}
break;
- case QPNP_VADC_REV_ID_8026_2_0:
+ case QPNP_REV_ID_8026_2_0:
+ case QPNP_REV_ID_8026_2_1:
switch (vadc->id) {
case COMP_ID_TSMC:
- temp_var = ((die_temp - 2500) *
+ temp_var = ((die_temp - 25000) *
(-QPNP_VBAT_COEFF_11));
break;
default:
case COMP_ID_GF:
- temp_var = ((die_temp - 2500) *
+ temp_var = ((die_temp - 25000) *
(-QPNP_VBAT_COEFF_9));
break;
}
break;
+ case QPNP_REV_ID_8026_2_2:
+ switch (vadc->id) {
+ case COMP_ID_TSMC:
+ *result -= QPNP_VBAT_COEFF_23;
+ temp_var = 0;
+ break;
+ default:
+ case COMP_ID_GF:
+ *result -= QPNP_VBAT_COEFF_23;
+ temp_var = 0;
+ break;
+ }
+ break;
+ case QPNP_REV_ID_8110_2_0:
+ switch (vadc->id) {
+ case COMP_ID_SMIC:
+ *result -= QPNP_VBAT_OFFSET_SMIC;
+ temp_var = ((die_temp - 25000) *
+ (QPNP_VBAT_COEFF_17));
+ break;
+ default:
+ case COMP_ID_GF:
+ *result -= QPNP_VBAT_OFFSET_GF;
+ temp_var = ((die_temp - 25000) *
+ (QPNP_VBAT_COEFF_16));
+ break;
+ }
+ break;
default:
temp_var = 0;
break;
@@ -692,7 +748,7 @@
}
int32_t qpnp_vbat_sns_comp_result(struct qpnp_vadc_chip *vadc,
- int64_t *result)
+ int64_t *result, bool is_pon_ocv)
{
struct qpnp_vadc_result die_temp_result;
int rc = 0;
@@ -708,7 +764,12 @@
return rc;
}
- rc = qpnp_ocv_comp(result, vadc, die_temp_result.physical);
+ if (is_pon_ocv)
+ rc = qpnp_ocv_comp(result, vadc, die_temp_result.physical);
+ else
+ rc = qpnp_vbat_sns_comp(result, vadc,
+ die_temp_result.physical);
+
if (rc < 0)
pr_err("Error with vbat compensation\n");
@@ -1114,6 +1175,8 @@
qpnp_vadc_amux_scaling_ratio[amux_prescaling].num;
vadc->adc->amux_prop->chan_prop->offset_gain_denominator =
qpnp_vadc_amux_scaling_ratio[amux_prescaling].den;
+ vadc->adc->amux_prop->chan_prop->calib_type =
+ vadc->adc->adc_channels[dt_index].calib_type;
scale_type = vadc->adc->adc_channels[dt_index].adc_scale_fn;
if (scale_type >= SCALE_NONE) {
diff --git a/drivers/input/misc/mma8x5x.c b/drivers/input/misc/mma8x5x.c
index a605720..85b4317 100644
--- a/drivers/input/misc/mma8x5x.c
+++ b/drivers/input/misc/mma8x5x.c
@@ -394,9 +394,14 @@
static int mma8x5x_device_set_odr(struct i2c_client *client, u32 delay_ms)
{
+ struct mma8x5x_data *pdata = i2c_get_clientdata(client);
int result;
u8 val;
+ /* set ODR is only required for interrupt mode */
+ if (!pdata->use_int)
+ return 0;
+
result = mma8x5x_delay2odr(delay_ms);
if (result < 0)
goto out;
@@ -503,9 +508,12 @@
{
struct mma8x5x_data *pdata = container_of((struct delayed_work *)work,
struct mma8x5x_data, dwork);
- mma8x5x_report_data(pdata);
- schedule_delayed_work(&pdata->dwork,
- msecs_to_jiffies(pdata->poll_delay));
+
+ if ((pdata->active & MMA_STATE_MASK) == MMA_ACTIVED) {
+ mma8x5x_report_data(pdata);
+ schedule_delayed_work(&pdata->dwork,
+ msecs_to_jiffies(pdata->poll_delay));
+ }
}
static irqreturn_t mma8x5x_interrupt(int vec, void *data)
@@ -570,9 +578,9 @@
dev_err(&client->dev, "change device state failed!");
goto err_failed;
}
-
- schedule_delayed_work(&pdata->dwork,
- msecs_to_jiffies(pdata->poll_delay));
+ if (!pdata->use_int)
+ schedule_delayed_work(&pdata->dwork,
+ msecs_to_jiffies(pdata->poll_delay));
pdata->active = MMA_ACTIVED;
dev_dbg(&client->dev, "%s:mma enable setting active.\n",
@@ -580,8 +588,6 @@
}
} else if (enable == 0) {
if (pdata->active == MMA_ACTIVED) {
- cancel_delayed_work_sync(&pdata->dwork);
-
val = i2c_smbus_read_byte_data(client,
MMA8X5X_CTRL_REG1);
if (val < 0) {
@@ -596,7 +602,10 @@
dev_err(&client->dev, "change device state failed!");
goto err_failed;
}
-
+ /*
+ * Set standby state,
+ * polling work queue will stop after next call.
+ */
pdata->active = MMA_STANDBY;
dev_dbg(&client->dev, "%s:mma enable setting inactive.\n",
__func__);
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 2b98145..dc3bbe0 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -80,7 +80,7 @@
obj-$(CONFIG_TOUCHSCREEN_CY8C_TS) += cy8c_ts.o
obj-$(CONFIG_TOUCHSCREEN_CYTTSP_I2C_QC) += cyttsp-i2c-qc.o
obj-$(CONFIG_TOUCHSCREEN_FT5X06) += ft5x06_ts.o
-obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4) += synaptics_i2c_rmi4.o
obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_RMI4_DEV) += synaptics_rmi_dev.o
obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE) += synaptics_fw_update.o
+obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4) += synaptics_i2c_rmi4.o
obj-$(CONFIG_TOUCHSCREEN_GT9XX) += gt9xx/
diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index cd6989c..be7c3c6 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -1983,6 +1983,7 @@
const char *buf, size_t count)
{
struct mxt_data *data = dev_get_drvdata(dev);
+ struct device *adapter = data->client->adapter->dev.parent;
unsigned long value;
int err = 0;
@@ -2000,7 +2001,7 @@
if (atomic_read(&data->st_enabled) == 0)
break;
- pm_runtime_put(data->client->adapter->dev.parent);
+ pm_runtime_put(adapter);
atomic_set(&data->st_enabled, 0);
mxt_secure_touch_notify(data);
mxt_interrupt(data->client->irq, data);
@@ -2012,7 +2013,7 @@
break;
}
- if (pm_runtime_get(data->client->adapter->dev.parent) < 0) {
+ if (pm_runtime_get_sync(adapter) < 0) {
dev_err(&data->client->dev, "pm_runtime_get failed\n");
err = -EIO;
break;
diff --git a/drivers/input/touchscreen/ft5x06_ts.c b/drivers/input/touchscreen/ft5x06_ts.c
index 4b8a3d4..1da84fa 100644
--- a/drivers/input/touchscreen/ft5x06_ts.c
+++ b/drivers/input/touchscreen/ft5x06_ts.c
@@ -3,7 +3,7 @@
* FocalTech ft5x06 TouchScreen driver.
*
* Copyright (c) 2010 Focal tech Ltd.
- * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@@ -69,7 +69,6 @@
#define FT_REG_THGROUP 0x80
#define FT_REG_ECC 0xCC
#define FT_REG_RESET_FW 0x07
-#define FT_REG_FW_MAJ_VER 0xB1
#define FT_REG_FW_MIN_VER 0xB2
#define FT_REG_FW_SUB_MIN_VER 0xB3
@@ -289,7 +288,7 @@
u8 reg_addr;
int err;
- reg_addr = FT_REG_FW_MAJ_VER;
+ reg_addr = FT_REG_FW_VER;
err = ft5x06_i2c_read(client, ®_addr, 1, &data->fw_ver[0], 1);
if (err < 0)
dev_err(&client->dev, "fw major version read failed");
@@ -877,6 +876,11 @@
u8 fw_file_maj, fw_file_min, fw_file_sub_min;
bool fw_upgrade = false;
+ if (data->suspended) {
+ dev_info(dev, "Device is in suspend state: Exit FW upgrade\n");
+ return -EBUSY;
+ }
+
rc = request_firmware(&fw, data->fw_name, dev);
if (rc < 0) {
dev_err(dev, "Request firmware failed - %s (%d)\n",
@@ -899,17 +903,10 @@
dev_info(dev, "New firmware: %d.%d.%d", fw_file_maj,
fw_file_min, fw_file_sub_min);
- if (force) {
+ if (force)
fw_upgrade = true;
- } else if (data->fw_ver[0] == fw_file_maj) {
- if (data->fw_ver[1] < fw_file_min)
- fw_upgrade = true;
- else if (data->fw_ver[2] < fw_file_sub_min)
- fw_upgrade = true;
- else
- dev_info(dev, "No need to upgrade\n");
- } else
- dev_info(dev, "Firmware versions do not match\n");
+ else if (data->fw_ver[0] < fw_file_maj)
+ fw_upgrade = true;
if (!fw_upgrade) {
dev_info(dev, "Exiting fw upgrade...\n");
diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
index 8e70129..cb1dbd4 100644
--- a/drivers/iommu/Kconfig
+++ b/drivers/iommu/Kconfig
@@ -87,6 +87,16 @@
section mappings and TLB misses should be quite infrequent.
Most people can probably say Y here.
+config MSM_IOMMU_VBIF_CHECK
+ bool "Enable support for VBIF check when IOMMU gets stuck"
+ depends on MSM_IOMMU
+ help
+ Enables an extra check in the IOMMU driver that logs debugging
+ information when TLB sync or iommu halt issue occurs. This helps
+ in debugging such issues.
+
+ If unsure, say N here.
+
config IOMMU_NON_SECURE
bool "Turns on programming of secure SMMU by kernel"
depends on MSM_IOMMU
diff --git a/drivers/iommu/msm_iommu-v1.c b/drivers/iommu/msm_iommu-v1.c
index 84f81bf..50581f8 100644
--- a/drivers/iommu/msm_iommu-v1.c
+++ b/drivers/iommu/msm_iommu-v1.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -16,6 +16,7 @@
#include <linux/platform_device.h>
#include <linux/errno.h>
#include <linux/io.h>
+#include <linux/iopoll.h>
#include <linux/interrupt.h>
#include <linux/list.h>
#include <linux/mutex.h>
@@ -143,13 +144,141 @@
.iommu_lock_release = _iommu_lock_release,
};
-void iommu_halt(const struct msm_iommu_drvdata *iommu_drvdata)
+#ifdef CONFIG_MSM_IOMMU_VBIF_CHECK
+
+#define VBIF_XIN_HALT_CTRL0 0x200
+#define VBIF_XIN_HALT_CTRL1 0x204
+#define VBIF_AXI_HALT_CTRL0 0x208
+#define VBIF_AXI_HALT_CTRL1 0x20C
+
+static void __halt_vbif_xin(void __iomem *vbif_base)
+{
+ pr_err("Halting VBIF_XIN\n");
+ writel_relaxed(0xFFFFFFFF, vbif_base + VBIF_XIN_HALT_CTRL0);
+}
+
+static void __dump_vbif_state(void __iomem *base, void __iomem *vbif_base)
+{
+ unsigned int reg_val;
+
+ reg_val = readl_relaxed(base + MICRO_MMU_CTRL);
+ pr_err("Value of SMMU_IMPLDEF_MICRO_MMU_CTRL = 0x%x\n", reg_val);
+
+ reg_val = readl_relaxed(vbif_base + VBIF_XIN_HALT_CTRL0);
+ pr_err("Value of VBIF_XIN_HALT_CTRL0 = 0x%x\n", reg_val);
+ reg_val = readl_relaxed(vbif_base + VBIF_XIN_HALT_CTRL1);
+ pr_err("Value of VBIF_XIN_HALT_CTRL1 = 0x%x\n", reg_val);
+ reg_val = readl_relaxed(vbif_base + VBIF_AXI_HALT_CTRL0);
+ pr_err("Value of VBIF_AXI_HALT_CTRL0 = 0x%x\n", reg_val);
+ reg_val = readl_relaxed(vbif_base + VBIF_AXI_HALT_CTRL1);
+ pr_err("Value of VBIF_AXI_HALT_CTRL1 = 0x%x\n", reg_val);
+}
+
+static int __check_vbif_state(struct msm_iommu_drvdata const *drvdata)
+{
+ phys_addr_t addr = (phys_addr_t) (drvdata->phys_base
+ - (phys_addr_t) 0x4000);
+ void __iomem *base = ioremap(addr, 0x1000);
+ int ret = 0;
+
+ if (base) {
+ __dump_vbif_state(drvdata->base, base);
+ __halt_vbif_xin(drvdata->base);
+ __dump_vbif_state(drvdata->base, base);
+ iounmap(base);
+ } else {
+ pr_err("%s: Unable to ioremap\n", __func__);
+ ret = -ENOMEM;
+ }
+ return ret;
+}
+
+static void check_halt_state(struct msm_iommu_drvdata const *drvdata)
+{
+ int res;
+ unsigned int val;
+ void __iomem *base = drvdata->base;
+ char const *name = drvdata->name;
+
+ pr_err("Timed out waiting for IOMMU halt to complete for %s\n", name);
+ res = __check_vbif_state(drvdata);
+ if (res)
+ BUG();
+
+ pr_err("Checking if IOMMU halt completed for %s\n", name);
+
+ res = readl_tight_poll_timeout(
+ GLB_REG(MICRO_MMU_CTRL, base), val,
+ (val & MMU_CTRL_IDLE) == MMU_CTRL_IDLE, 5000000);
+
+ if (res) {
+ pr_err("Timed out (again) waiting for IOMMU halt to complete for %s\n",
+ name);
+ } else {
+ pr_err("IOMMU halt completed. VBIF FIFO most likely not getting drained by master\n");
+ }
+ BUG();
+}
+
+static void check_tlb_sync_state(struct msm_iommu_drvdata const *drvdata,
+ int ctx)
+{
+ int res;
+ unsigned int val;
+ void __iomem *base = drvdata->base;
+ char const *name = drvdata->name;
+
+ pr_err("Timed out waiting for TLB SYNC to complete for %s\n", name);
+ res = __check_vbif_state(drvdata);
+ if (res)
+ BUG();
+
+ pr_err("Checking if TLB sync completed for %s\n", name);
+
+ res = readl_tight_poll_timeout(CTX_REG(CB_TLBSTATUS, base, ctx), val,
+ (val & CB_TLBSTATUS_SACTIVE) == 0, 5000000);
+ if (res) {
+ pr_err("Timed out (again) waiting for TLB SYNC to complete for %s\n",
+ name);
+ } else {
+ pr_err("TLB Sync completed. VBIF FIFO most likely not getting drained by master\n");
+ }
+ BUG();
+}
+
+#else
+
+/*
+ * For targets without VBIF or for targets with the VBIF check disabled
+ * we directly just crash to capture the issue
+ */
+static void check_halt_state(struct msm_iommu_drvdata const *drvdata)
+{
+ BUG();
+}
+
+static void check_tlb_sync_state(struct msm_iommu_drvdata const *drvdata,
+ int ctx)
+{
+ BUG();
+}
+
+#endif
+
+void iommu_halt(struct msm_iommu_drvdata const *iommu_drvdata)
{
if (iommu_drvdata->halt_enabled) {
- SET_MICRO_MMU_CTRL_HALT_REQ(iommu_drvdata->base, 1);
+ unsigned int val;
+ void __iomem *base = iommu_drvdata->base;
+ int res;
- while (GET_MICRO_MMU_CTRL_IDLE(iommu_drvdata->base) == 0)
- cpu_relax();
+ SET_MICRO_MMU_CTRL_HALT_REQ(base, 1);
+ res = readl_tight_poll_timeout(
+ GLB_REG(MICRO_MMU_CTRL, base), val,
+ (val & MMU_CTRL_IDLE) == MMU_CTRL_IDLE, 5000000);
+
+ if (res)
+ check_halt_state(iommu_drvdata);
/* Ensure device is idle before continuing */
mb();
}
@@ -173,15 +302,19 @@
}
}
-static void __sync_tlb(void __iomem *base, int ctx)
+static void __sync_tlb(struct msm_iommu_drvdata *iommu_drvdata, int ctx)
{
+ unsigned int val;
+ unsigned int res;
+ void __iomem *base = iommu_drvdata->base;
+
SET_TLBSYNC(base, ctx, 0);
-
- /* No barrier needed due to register proximity */
- while (GET_CB_TLBSTATUS_SACTIVE(base, ctx))
- cpu_relax();
-
/* No barrier needed due to read dependency */
+
+ res = readl_tight_poll_timeout(CTX_REG(CB_TLBSTATUS, base, ctx), val,
+ (val & CB_TLBSTATUS_SACTIVE) == 0, 5000000);
+ if (res)
+ check_tlb_sync_state(iommu_drvdata, ctx);
}
static int __flush_iotlb_va(struct iommu_domain *domain, unsigned int va)
@@ -205,7 +338,7 @@
SET_TLBIVA(iommu_drvdata->base, ctx_drvdata->num,
ctx_drvdata->asid | (va & CB_TLBIVA_VA));
mb();
- __sync_tlb(iommu_drvdata->base, ctx_drvdata->num);
+ __sync_tlb(iommu_drvdata, ctx_drvdata->num);
__disable_clocks(iommu_drvdata);
}
fail:
@@ -232,7 +365,7 @@
SET_TLBIASID(iommu_drvdata->base, ctx_drvdata->num,
ctx_drvdata->asid);
mb();
- __sync_tlb(iommu_drvdata->base, ctx_drvdata->num);
+ __sync_tlb(iommu_drvdata, ctx_drvdata->num);
__disable_clocks(iommu_drvdata);
}
@@ -343,15 +476,14 @@
mb();
}
-static void __release_smg(void __iomem *base, int ctx)
+static void __release_smg(void __iomem *base)
{
int i, smt_size;
smt_size = GET_IDR0_NUMSMRG(base);
- /* Invalidate any SMGs associated with this context */
+ /* Invalidate all SMGs */
for (i = 0; i < smt_size; i++)
- if (GET_SMR_VALID(base, i) &&
- GET_S2CR_CBNDX(base, i) == ctx)
+ if (GET_SMR_VALID(base, i))
SET_SMR_VALID(base, i, 0);
}
@@ -394,17 +526,52 @@
}
}
+
+static int program_m2v_table(struct device *dev, void __iomem *base)
+{
+ struct msm_iommu_ctx_drvdata *ctx_drvdata = dev_get_drvdata(dev);
+ u32 *sids = ctx_drvdata->sids;
+ unsigned int ctx = ctx_drvdata->num;
+ int num = 0, i, smt_size;
+ int len = ctx_drvdata->nsid;
+
+ smt_size = GET_IDR0_NUMSMRG(base);
+ /* Program the M2V tables for this context */
+ for (i = 0; i < len / sizeof(*sids); i++) {
+ for (; num < smt_size; num++)
+ if (GET_SMR_VALID(base, num) == 0)
+ break;
+ BUG_ON(num >= smt_size);
+
+ SET_SMR_VALID(base, num, 1);
+ SET_SMR_MASK(base, num, 0);
+ SET_SMR_ID(base, num, sids[i]);
+
+ SET_S2CR_N(base, num, 0);
+ SET_S2CR_CBNDX(base, num, ctx);
+ SET_S2CR_MEMATTR(base, num, 0x0A);
+ /* Set security bit override to be Non-secure */
+ SET_S2CR_NSCFG(base, num, 3);
+ }
+
+ return 0;
+}
+
+static void program_all_m2v_tables(struct msm_iommu_drvdata *iommu_drvdata)
+{
+ device_for_each_child(iommu_drvdata->dev, iommu_drvdata->base,
+ program_m2v_table);
+}
+
static void __program_context(struct msm_iommu_drvdata *iommu_drvdata,
struct msm_iommu_ctx_drvdata *ctx_drvdata,
- struct msm_iommu_priv *priv, bool is_secure)
+ struct msm_iommu_priv *priv, bool is_secure,
+ bool program_m2v)
{
unsigned int prrr, nmrr;
- unsigned int pn;
- int num = 0, i, smt_size;
+ phys_addr_t pn;
void __iomem *base = iommu_drvdata->base;
unsigned int ctx = ctx_drvdata->num;
- u32 *sids = ctx_drvdata->sids;
- int len = ctx_drvdata->nsid;
phys_addr_t pgtable = __pa(priv->pt.fl_table);
__reset_context(base, ctx);
@@ -445,24 +612,9 @@
}
if (!is_secure) {
- smt_size = GET_IDR0_NUMSMRG(base);
- /* Program the M2V tables for this context */
- for (i = 0; i < len / sizeof(*sids); i++) {
- for (; num < smt_size; num++)
- if (GET_SMR_VALID(base, num) == 0)
- break;
- BUG_ON(num >= smt_size);
+ if (program_m2v)
+ program_all_m2v_tables(iommu_drvdata);
- SET_SMR_VALID(base, num, 1);
- SET_SMR_MASK(base, num, 0);
- SET_SMR_ID(base, num, sids[i]);
-
- SET_S2CR_N(base, num, 0);
- SET_S2CR_CBNDX(base, num, ctx);
- SET_S2CR_MEMATTR(base, num, 0x0A);
- /* Set security bit override to be Non-secure */
- SET_S2CR_NSCFG(base, num, 3);
- }
SET_CBAR_N(base, ctx, 0);
/* Stage 1 Context with Stage 2 bypass */
@@ -534,49 +686,55 @@
struct msm_iommu_drvdata *iommu_drvdata;
struct msm_iommu_ctx_drvdata *ctx_drvdata;
struct msm_iommu_ctx_drvdata *tmp_drvdata;
- int ret;
+ int ret = 0;
int is_secure;
+ bool set_m2v = false;
mutex_lock(&msm_iommu_lock);
priv = domain->priv;
if (!priv || !dev) {
ret = -EINVAL;
- goto fail;
+ goto unlock;
}
iommu_drvdata = dev_get_drvdata(dev->parent);
ctx_drvdata = dev_get_drvdata(dev);
if (!iommu_drvdata || !ctx_drvdata) {
ret = -EINVAL;
- goto fail;
+ goto unlock;
}
+ ++ctx_drvdata->attach_count;
+
+ if (ctx_drvdata->attach_count > 1)
+ goto already_attached;
+
if (!list_empty(&ctx_drvdata->attached_elm)) {
ret = -EBUSY;
- goto fail;
+ goto unlock;
}
list_for_each_entry(tmp_drvdata, &priv->list_attached, attached_elm)
if (tmp_drvdata == ctx_drvdata) {
ret = -EBUSY;
- goto fail;
+ goto unlock;
}
is_secure = iommu_drvdata->sec_id != -1;
ret = __enable_regulators(iommu_drvdata);
if (ret)
- goto fail;
+ goto unlock;
ret = apply_bus_vote(iommu_drvdata, 1);
if (ret)
- goto fail;
+ goto unlock;
ret = __enable_clocks(iommu_drvdata);
if (ret) {
__disable_regulators(iommu_drvdata);
- goto fail;
+ goto unlock;
}
/* We can only do this once */
@@ -591,16 +749,17 @@
if (ret) {
__disable_regulators(iommu_drvdata);
__disable_clocks(iommu_drvdata);
- goto fail;
+ goto unlock;
}
}
program_iommu_bfb_settings(iommu_drvdata->base,
iommu_drvdata->bfb_settings);
+ set_m2v = true;
}
iommu_halt(iommu_drvdata);
- __program_context(iommu_drvdata, ctx_drvdata, priv, is_secure);
+ __program_context(iommu_drvdata, ctx_drvdata, priv, is_secure, set_m2v);
iommu_resume(iommu_drvdata);
@@ -610,11 +769,12 @@
ctx_drvdata->attached_domain = domain;
++iommu_drvdata->ctx_attach_count;
+already_attached:
mutex_unlock(&msm_iommu_lock);
msm_iommu_attached(dev->parent);
return ret;
-fail:
+unlock:
mutex_unlock(&msm_iommu_lock);
return ret;
}
@@ -633,16 +793,22 @@
mutex_lock(&msm_iommu_lock);
priv = domain->priv;
if (!priv || !dev)
- goto fail;
+ goto unlock;
iommu_drvdata = dev_get_drvdata(dev->parent);
ctx_drvdata = dev_get_drvdata(dev);
if (!iommu_drvdata || !ctx_drvdata || !ctx_drvdata->attached_domain)
- goto fail;
+ goto unlock;
+
+ --ctx_drvdata->attach_count;
+ BUG_ON(ctx_drvdata->attach_count < 0);
+
+ if (ctx_drvdata->attach_count > 0)
+ goto unlock;
ret = __enable_clocks(iommu_drvdata);
if (ret)
- goto fail;
+ goto unlock;
is_secure = iommu_drvdata->sec_id != -1;
@@ -652,13 +818,15 @@
iommu_drvdata->asid[ctx_drvdata->asid - 1]--;
ctx_drvdata->asid = -1;
- iommu_halt(iommu_drvdata);
-
__reset_context(iommu_drvdata->base, ctx_drvdata->num);
- if (!is_secure)
- __release_smg(iommu_drvdata->base, ctx_drvdata->num);
- iommu_resume(iommu_drvdata);
+ /*
+ * Only reset the M2V tables on the very last detach */
+ if (!is_secure && iommu_drvdata->ctx_attach_count == 1) {
+ iommu_halt(iommu_drvdata);
+ __release_smg(iommu_drvdata->base);
+ iommu_resume(iommu_drvdata);
+ }
__disable_clocks(iommu_drvdata);
@@ -670,7 +838,7 @@
ctx_drvdata->attached_domain = NULL;
BUG_ON(iommu_drvdata->ctx_attach_count == 0);
--iommu_drvdata->ctx_attach_count;
-fail:
+unlock:
mutex_unlock(&msm_iommu_lock);
}
diff --git a/drivers/iommu/msm_iommu_dev-v1.c b/drivers/iommu/msm_iommu_dev-v1.c
index a9d164e..ff6b58c 100644
--- a/drivers/iommu/msm_iommu_dev-v1.c
+++ b/drivers/iommu/msm_iommu_dev-v1.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2014, 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
@@ -299,6 +299,7 @@
if (!drvdata->base)
return -ENOMEM;
+ drvdata->phys_base = r->start;
drvdata->glb_base = drvdata->base;
if (of_get_property(pdev->dev.of_node, "vdd-supply", NULL)) {
diff --git a/drivers/leds/leds-qpnp.c b/drivers/leds/leds-qpnp.c
index eba5ca8..0659b77 100644
--- a/drivers/leds/leds-qpnp.c
+++ b/drivers/leds/leds-qpnp.c
@@ -484,6 +484,7 @@
};
static int num_kpbl_leds_on;
+static DEFINE_MUTEX(flash_lock);
static int
qpnp_led_masked_write(struct qpnp_led_data *led, u16 addr, u8 mask, u8 val)
@@ -1408,7 +1409,10 @@
{
int rc;
- mutex_lock(&led->lock);
+ if (led->id == QPNP_ID_FLASH1_LED0 || led->id == QPNP_ID_FLASH1_LED1)
+ mutex_lock(&flash_lock);
+ else
+ mutex_lock(&led->lock);
switch (led->id) {
case QPNP_ID_WLED:
@@ -1448,7 +1452,10 @@
dev_err(&led->spmi_dev->dev, "Invalid LED(%d)\n", led->id);
break;
}
- mutex_unlock(&led->lock);
+ if (led->id == QPNP_ID_FLASH1_LED0 || led->id == QPNP_ID_FLASH1_LED1)
+ mutex_unlock(&flash_lock);
+ else
+ mutex_unlock(&led->lock);
}
@@ -3314,7 +3321,9 @@
goto fail_id_check;
}
- mutex_init(&led->lock);
+ if (led->id != QPNP_ID_FLASH1_LED0 &&
+ led->id != QPNP_ID_FLASH1_LED1)
+ mutex_init(&led->lock);
INIT_WORK(&led->work, qpnp_led_work);
rc = qpnp_led_initialize(led);
@@ -3409,7 +3418,9 @@
fail_id_check:
for (i = 0; i < parsed_leds; i++) {
- mutex_destroy(&led_array[i].lock);
+ if (led_array[i].id != QPNP_ID_FLASH1_LED0 &&
+ led_array[i].id != QPNP_ID_FLASH1_LED1)
+ mutex_destroy(&led_array[i].lock);
led_classdev_unregister(&led_array[i].cdev);
}
@@ -3423,7 +3434,10 @@
for (i = 0; i < parsed_leds; i++) {
cancel_work_sync(&led_array[i].work);
- mutex_destroy(&led_array[i].lock);
+ if (led_array[i].id != QPNP_ID_FLASH1_LED0 &&
+ led_array[i].id != QPNP_ID_FLASH1_LED1)
+ mutex_destroy(&led_array[i].lock);
+
led_classdev_unregister(&led_array[i].cdev);
switch (led_array[i].id) {
case QPNP_ID_WLED:
diff --git a/drivers/md/dm-req-crypt.c b/drivers/md/dm-req-crypt.c
index ab21404..87a4ab9 100644
--- a/drivers/md/dm-req-crypt.c
+++ b/drivers/md/dm-req-crypt.c
@@ -45,6 +45,7 @@
#define AES_XTS_IV_LEN 16
#define DM_REQ_CRYPT_ERROR -1
+#define DM_REQ_CRYPT_ERROR_AFTER_PAGE_MALLOC -2
struct req_crypt_result {
struct completion completion;
@@ -105,9 +106,10 @@
atomic_dec(&io->pending);
- if (error < 0)
+ if (error < 0) {
dm_kill_unmapped_request(clone, error);
- else
+ mempool_free(io, req_io_pool);
+ } else
dm_dispatch_request(clone);
}
@@ -158,8 +160,6 @@
struct req_crypt_result result;
struct scatterlist *req_sg_read = NULL;
int err = 0;
- struct req_iterator iter2;
- struct bio_vec *bvec = NULL;
u8 IV[AES_XTS_IV_LEN];
if (io) {
@@ -217,11 +217,12 @@
goto ablkcipher_req_alloc_failure;
}
-
- /* total bytes to copy */
- bvec = NULL;
- rq_for_each_segment(bvec, clone, iter2) {
- total_bytes_in_req = total_bytes_in_req + bvec->bv_len;
+ total_bytes_in_req = clone->__data_len;
+ if (total_bytes_in_req > REQ_DM_512_KB) {
+ DMERR("%s total_bytes_in_req > 512 MB %d",
+ __func__, total_bytes_in_req);
+ err = DM_REQ_CRYPT_ERROR;
+ goto ablkcipher_req_alloc_failure;
}
memset(IV, 0, AES_XTS_IV_LEN);
@@ -263,7 +264,8 @@
kfree(req_sg_read);
submit_request:
- io->error = err;
+ if (io)
+ io->error = err;
req_crypt_dec_pending_decrypt(io);
}
@@ -277,7 +279,8 @@
struct bio *bio_src = NULL;
unsigned int total_sg_len_req_in = 0, total_sg_len_req_out = 0,
total_bytes_in_req = 0, error = DM_MAPIO_REMAPPED, rc = 0;
- struct req_iterator iter;
+ struct req_iterator iter = {0, NULL};
+ struct req_iterator iter1 = {0, NULL};
struct ablkcipher_request *req = NULL;
struct req_crypt_result result;
struct bio_vec *bvec = NULL;
@@ -350,20 +353,28 @@
goto ablkcipher_req_alloc_failure;
}
+ total_bytes_in_req = clone->__data_len;
+ if (total_bytes_in_req > REQ_DM_512_KB) {
+ DMERR("%s total_bytes_in_req > 512 MB %d",
+ __func__, total_bytes_in_req);
+ error = DM_REQ_CRYPT_ERROR;
+ goto ablkcipher_req_alloc_failure;
+ }
rq_for_each_segment(bvec, clone, iter) {
-try_again:
if (bvec->bv_len > remaining_size) {
page = NULL;
- page = mempool_alloc(req_page_pool, gfp_mask);
- if (!page) {
- DMERR("%s Crypt page alloc failed", __func__);
- congestion_wait(BLK_RW_ASYNC, HZ/100);
- goto try_again;
+ while (page == NULL) {
+ page = mempool_alloc(req_page_pool, gfp_mask);
+ if (!page) {
+ DMERR("%s Crypt page alloc failed",
+ __func__);
+ congestion_wait(BLK_RW_ASYNC, HZ/100);
+ }
}
+
bvec->bv_page = page;
bvec->bv_offset = 0;
- total_bytes_in_req = total_bytes_in_req + bvec->bv_len;
remaining_size = PAGE_SIZE - bvec->bv_len;
if (remaining_size < 0)
BUG();
@@ -371,7 +382,6 @@
bvec->bv_page = page;
bvec->bv_offset = PAGE_SIZE - remaining_size;
remaining_size = remaining_size - bvec->bv_len;
- total_bytes_in_req = total_bytes_in_req + bvec->bv_len;
}
}
@@ -379,7 +389,7 @@
if ((total_sg_len_req_out <= 0) ||
(total_sg_len_req_out > MAX_SG_LIST)) {
DMERR("%s Request Error %d", __func__, total_sg_len_req_out);
- error = DM_REQ_CRYPT_ERROR;
+ error = DM_REQ_CRYPT_ERROR_AFTER_PAGE_MALLOC;
goto ablkcipher_req_alloc_failure;
}
@@ -405,13 +415,13 @@
if (result.err) {
DMERR("%s error = %d encrypting the request\n",
__func__, result.err);
- error = DM_REQ_CRYPT_ERROR;
+ error = DM_REQ_CRYPT_ERROR_AFTER_PAGE_MALLOC;
goto ablkcipher_req_alloc_failure;
}
break;
default:
- error = DM_REQ_CRYPT_ERROR;
+ error = DM_REQ_CRYPT_ERROR_AFTER_PAGE_MALLOC;
goto ablkcipher_req_alloc_failure;
}
@@ -428,13 +438,25 @@
if (req)
ablkcipher_request_free(req);
+ if (error == DM_REQ_CRYPT_ERROR_AFTER_PAGE_MALLOC) {
+ bvec = NULL;
+ rq_for_each_segment(bvec, clone, iter1) {
+ if (bvec->bv_offset == 0) {
+ mempool_free(bvec->bv_page, req_page_pool);
+ bvec->bv_page = NULL;
+ } else
+ bvec->bv_page = NULL;
+ }
+ }
+
kfree(req_sg_in);
kfree(req_sg_out);
submit_request:
- io->error = error;
+ if (io)
+ io->error = error;
req_crypt_dec_pending_encrypt(io);
}
@@ -449,7 +471,7 @@
else if (rq_data_dir(io->cloned_request) == READ)
req_cryptd_crypt_read_convert(io);
else
- DMERR("%s received non-write request for Clone %u\n",
+ DMERR("%s received non-read/write request for Clone %u\n",
__func__, (unsigned int)io->cloned_request);
}
@@ -484,6 +506,11 @@
if (bio_sectors(bio) && bdev != bdev->bd_contains) {
struct hd_struct *p = bdev->bd_part;
+ /*
+ * Check for integer overflow, should never happen.
+ */
+ if (p->start_sect > (UINT_MAX - bio->bi_sector))
+ BUG();
bio->bi_sector += p->start_sect;
bio->bi_bdev = bdev->bd_contains;
@@ -543,9 +570,16 @@
union map_info *map_context)
{
struct req_dm_crypt_io *req_io = NULL;
- int error = DM_MAPIO_REMAPPED, copy_bio_sector_to_req = 0;
+ int error = DM_REQ_CRYPT_ERROR, copy_bio_sector_to_req = 0;
struct bio *bio_src = NULL;
+ if ((rq_data_dir(clone) != READ) &&
+ (rq_data_dir(clone) != WRITE)) {
+ error = DM_REQ_CRYPT_ERROR;
+ DMERR("%s Unknown request\n", __func__);
+ goto submit_request;
+ }
+
req_io = mempool_alloc(req_io_pool, GFP_NOWAIT);
if (!req_io) {
DMERR("%s req_io allocation failed\n", __func__);
@@ -598,9 +632,6 @@
req_cryptd_queue_crypt(req_io);
error = DM_MAPIO_SUBMITTED;
goto submit_request;
- } else {
- error = DM_REQ_CRYPT_ERROR;
- DMERR("%s Unknown request\n", __func__);
}
submit_request:
@@ -608,22 +639,24 @@
}
-static int req_crypt_status(struct dm_target *ti, status_type_t type,
- char *result, unsigned maxlen)
-{
- return 0;
-}
-
static void req_crypt_dtr(struct dm_target *ti)
{
- if (req_crypt_queue)
+ if (req_crypt_queue) {
destroy_workqueue(req_crypt_queue);
- if (req_io_pool)
+ req_crypt_queue = NULL;
+ }
+ if (req_io_pool) {
mempool_destroy(req_io_pool);
- if (req_page_pool)
+ req_io_pool = NULL;
+ }
+ if (req_page_pool) {
mempool_destroy(req_page_pool);
- if (tfm)
+ req_page_pool = NULL;
+ }
+ if (tfm) {
crypto_free_ablkcipher(tfm);
+ tfm = NULL;
+ }
}
@@ -635,71 +668,96 @@
{
unsigned long long tmpll;
char dummy;
+ int err = DM_REQ_CRYPT_ERROR;
- if (dm_get_device(ti, argv[3], dm_table_get_mode(ti->table), &dev)) {
- DMERR(" %s Device Lookup failed\n", __func__);
- return DM_REQ_CRYPT_ERROR;
+ if (argc < 5) {
+ DMERR(" %s Not enough args\n", __func__);
+ err = DM_REQ_CRYPT_ERROR;
+ goto ctr_exit;
}
- if (sscanf(argv[4], "%llu%c", &tmpll, &dummy) != 1) {
- DMERR("%s Invalid device sector\n", __func__);
- return DM_REQ_CRYPT_ERROR;
+ if (argv[3]) {
+ if (dm_get_device(ti, argv[3],
+ dm_table_get_mode(ti->table), &dev)) {
+ DMERR(" %s Device Lookup failed\n", __func__);
+ err = DM_REQ_CRYPT_ERROR;
+ goto ctr_exit;
+ }
+ } else {
+ DMERR(" %s Arg[3] invalid\n", __func__);
+ err = DM_REQ_CRYPT_ERROR;
+ goto ctr_exit;
}
+
+ if (argv[4]) {
+ if (sscanf(argv[4], "%llu%c", &tmpll, &dummy) != 1) {
+ DMERR("%s Invalid device sector\n", __func__);
+ err = DM_REQ_CRYPT_ERROR;
+ goto ctr_exit;
+ }
+ } else {
+ DMERR(" %s Arg[4]invalid\n", __func__);
+ err = DM_REQ_CRYPT_ERROR;
+ goto ctr_exit;
+ }
+
start_sector_orig = tmpll;
req_crypt_queue = alloc_workqueue("req_cryptd",
- WQ_HIGHPRI |
- WQ_CPU_INTENSIVE|
- WQ_MEM_RECLAIM,
- 1);
+ WQ_NON_REENTRANT |
+ WQ_HIGHPRI |
+ WQ_CPU_INTENSIVE|
+ WQ_MEM_RECLAIM,
+ 1);
if (!req_crypt_queue) {
DMERR("%s req_crypt_queue not allocated\n", __func__);
- return DM_REQ_CRYPT_ERROR;
+ err = DM_REQ_CRYPT_ERROR;
+ goto ctr_exit;
}
/* Allocate the crypto alloc blk cipher and keep the handle */
tfm = crypto_alloc_ablkcipher("qcom-xts(aes)", 0, 0);
if (IS_ERR(tfm)) {
- DMERR("%s ablkcipher tfm allocation failed : error = %lu\n",
- __func__, PTR_ERR(tfm));
- return DM_REQ_CRYPT_ERROR;
+ DMERR("%s ablkcipher tfm allocation failed : error\n",
+ __func__);
+ err = DM_REQ_CRYPT_ERROR;
+ goto ctr_exit;
}
req_io_pool = mempool_create_slab_pool(MIN_IOS, _req_crypt_io_pool);
if (!req_io_pool) {
DMERR("%s req_io_pool not allocated\n", __func__);
- return DM_REQ_CRYPT_ERROR;
+ err = DM_REQ_CRYPT_ERROR;
+ goto ctr_exit;
}
req_page_pool = mempool_create_page_pool(MIN_POOL_PAGES, 0);
if (!req_page_pool) {
DMERR("%s req_page_pool not allocated\n", __func__);
- return DM_REQ_CRYPT_ERROR;
+ err = DM_REQ_CRYPT_ERROR;
+ goto ctr_exit;
}
-
- return 0;
-}
-
-static void req_crypt_postsuspend(struct dm_target *ti)
-{
-}
-
-static int req_crypt_preresume(struct dm_target *ti)
-{
- return 0;
-}
-
-static void req_crypt_resume(struct dm_target *ti)
-{
-}
-
-/* Message interface
- * key set <key>
- * key wipe
- */
-static int req_crypt_message(struct dm_target *ti, unsigned argc, char **argv)
-{
- return 0;
+ err = 0;
+ctr_exit:
+ if (err != 0) {
+ if (req_crypt_queue) {
+ destroy_workqueue(req_crypt_queue);
+ req_crypt_queue = NULL;
+ }
+ if (req_io_pool) {
+ mempool_destroy(req_io_pool);
+ req_io_pool = NULL;
+ }
+ if (req_page_pool) {
+ mempool_destroy(req_page_pool);
+ req_page_pool = NULL;
+ }
+ if (tfm) {
+ crypto_free_ablkcipher(tfm);
+ tfm = NULL;
+ }
+ }
+ return err;
}
static int req_crypt_iterate_devices(struct dm_target *ti,
@@ -716,11 +774,6 @@
.dtr = req_crypt_dtr,
.map_rq = req_crypt_map,
.rq_end_io = req_crypt_endio,
- .status = req_crypt_status,
- .postsuspend = req_crypt_postsuspend,
- .preresume = req_crypt_preresume,
- .resume = req_crypt_resume,
- .message = req_crypt_message,
.iterate_devices = req_crypt_iterate_devices,
};
@@ -733,8 +786,10 @@
return -ENOMEM;
r = dm_register_target(&req_crypt_target);
- if (r < 0)
+ if (r < 0) {
DMERR("register failed %d", r);
+ kmem_cache_destroy(_req_crypt_io_pool);
+ }
return r;
}
diff --git a/drivers/media/platform/msm/camera_v2/Kconfig b/drivers/media/platform/msm/camera_v2/Kconfig
index 262fb38..9fff025 100644
--- a/drivers/media/platform/msm/camera_v2/Kconfig
+++ b/drivers/media/platform/msm/camera_v2/Kconfig
@@ -101,24 +101,6 @@
of any CID of MSM_CSI22_HEADER can be routed to of pixel
or raw data interface in VFE.
-config S5K3L1YX
- bool "Sensor S5K3L1YX (BAYER 12M)"
- depends on MSMB_CAMERA
- ---help---
- Samsung 12 MP Bayer Sensor with auto focus, uses
- 4 mipi lanes, preview config = 1984 * 1508 at 30 fps,
- snapshot config = 4000 * 3000 at 20 fps,
- hfr video at 60, 90 and 120 fps.
-
-config IMX135
- bool "Sensor IMX135 (BAYER 12M)"
- depends on MSMB_CAMERA
- ---help---
- Sony 12 MP Bayer Sensor with auto focus, uses
- 4 mipi lanes, preview config = 2104 x 1560 at 49 fps,
- snapshot config = 4208 x 3120 at 24 fps,
- Video HDR support.
-
config IMX134
bool "Sensor IMX134 (BAYER 8M)"
depends on MSMB_CAMERA
@@ -128,15 +110,6 @@
HFR @60fps and @120fps
Video HDR support.
-config OV2720
- bool "Sensor OV2720 (BAYER 2M)"
- depends on MSMB_CAMERA
- ---help---
- OmniVision 2 MP Bayer Sensor, supports 2 mipi lanes,
- preview and snapshot config at 1932 * 1092 at 30 fps,
- hfr video at 60, 90 and 120 fps. This sensor driver does
- not support auto focus.
-
config OV9724
bool "Sensor OV9724 (BAYER 2M)"
depends on MSMB_CAMERA
diff --git a/drivers/media/platform/msm/camera_v2/camera/camera.c b/drivers/media/platform/msm/camera_v2/camera/camera.c
index 43cdcbb..170efa8 100644
--- a/drivers/media/platform/msm/camera_v2/camera/camera.c
+++ b/drivers/media/platform/msm/camera_v2/camera/camera.c
@@ -345,20 +345,17 @@
rc = msm_post_event(&event, MSM_POST_EVT_TIMEOUT);
if (rc < 0)
- goto set_fmt_fail;
+ return rc;
rc = camera_check_event_status(&event);
if (rc < 0)
- goto set_fmt_fail;
+ return rc;
+
sp->is_vb2_valid = 1;
}
return rc;
-set_fmt_fail:
- kzfree(sp->vb2_q.drv_priv);
- sp->vb2_q.drv_priv = NULL;
- return rc;
}
static int camera_v4l2_try_fmt_vid_cap_mplane(struct file *filep, void *fh,
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp.c
index 80a0073..d35869c 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp.c
@@ -52,8 +52,82 @@
{}
};
+#define MAX_OVERFLOW_COUNTERS 15
+#define OVERFLOW_LENGTH 512
+#define OVERFLOW_BUFFER_LENGTH 32
static struct msm_isp_buf_mgr vfe_buf_mgr;
+static int msm_isp_enable_debugfs(struct msm_isp_statistics *stats);
+static char *stats_str[MAX_OVERFLOW_COUNTERS] = {
+ "imgmaster0_overflow_cnt",
+ "imgmaster1_overflow_cnt",
+ "imgmaster2_overflow_cnt",
+ "imgmaster3_overflow_cnt",
+ "imgmaster4_overflow_cnt",
+ "imgmaster5_overflow_cnt",
+ "imgmaster6_overflow_cnt",
+ "be_overflow_cnt",
+ "bg_overflow_cnt",
+ "bf_overflow_cnt",
+ "awb_overflow_cnt",
+ "rs_overflow_cnt",
+ "cs_overflow_cnt",
+ "ihist_overflow_cnt",
+ "skinbhist_overflow_cnt",
+};
+static int vfe_debugfs_statistics_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+static ssize_t vfe_debugfs_statistics_read(struct file *t_file, char *t_char,
+ size_t t_size_t, loff_t *t_loff_t)
+{
+ int i;
+ char name[OVERFLOW_LENGTH] = {0};
+ int *ptr;
+ char buffer[OVERFLOW_BUFFER_LENGTH] = {0};
+ struct msm_isp_statistics *stats = (struct msm_isp_statistics *)
+ t_file->private_data;
+ ptr = (int *)(stats);
+ for (i = 0; i < MAX_OVERFLOW_COUNTERS; i++) {
+ strlcat(name, stats_str[i], sizeof(name));
+ strlcat(name, " ", sizeof(name));
+ snprintf(buffer, sizeof(buffer), "%d", ptr[i]);
+ strlcat(name, buffer, sizeof(name));
+ strlcat(name, "\r\n", sizeof(name));
+ }
+ return simple_read_from_buffer(t_char, t_size_t,
+ t_loff_t, name, strlen(name));
+}
+
+static ssize_t vfe_debugfs_statistics_write(struct file *t_file,
+ const char *t_char, size_t t_size_t, loff_t *t_loff_t)
+{
+ struct msm_isp_statistics *stats = (struct msm_isp_statistics *)
+ t_file->private_data;
+ memset(stats, 0, sizeof(struct msm_isp_statistics));
+
+ return sizeof(struct msm_isp_statistics);
+}
+
+static const struct file_operations vfe_debugfs_error = {
+ .open = vfe_debugfs_statistics_open,
+ .read = vfe_debugfs_statistics_read,
+ .write = vfe_debugfs_statistics_write,
+};
+
+static int msm_isp_enable_debugfs(struct msm_isp_statistics *stats)
+{
+ struct dentry *debugfs_base;
+ debugfs_base = debugfs_create_dir("msm_isp", NULL);
+ if (!debugfs_base)
+ return -ENOMEM;
+ if (!debugfs_create_file("stats", S_IRUGO | S_IWUSR, debugfs_base,
+ stats, &vfe_debugfs_error))
+ return -ENOMEM;
+ return 0;
+}
static int __devinit vfe_probe(struct platform_device *pdev)
{
struct vfe_device *vfe_dev;
@@ -73,6 +147,7 @@
};
vfe_dev = kzalloc(sizeof(struct vfe_device), GFP_KERNEL);
+ vfe_dev->stats = kzalloc(sizeof(struct msm_isp_statistics), GFP_KERNEL);
if (!vfe_dev) {
pr_err("%s: no enough memory\n", __func__);
return -ENOMEM;
@@ -144,6 +219,7 @@
kfree(vfe_dev);
return -EINVAL;
}
+ msm_isp_enable_debugfs(vfe_dev->stats);
vfe_dev->buf_mgr->ops->register_ctx(vfe_dev->buf_mgr,
&vfe_dev->iommu_ctx[0], vfe_dev->hw_info->num_iommu_ctx);
vfe_dev->vfe_open_cnt = 0;
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp.h b/drivers/media/platform/msm/camera_v2/isp/msm_isp.h
index 334a293..4700469 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp.h
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp.h
@@ -24,7 +24,6 @@
#include <media/msmb_isp.h>
#include <mach/msm_bus.h>
#include <mach/msm_bus_board.h>
-
#include "msm_buf_mgr.h"
#define MAX_IOMMU_CTX 2
@@ -409,6 +408,24 @@
uint32_t frame_id;
};
+struct msm_isp_statistics {
+ int32_t imagemaster0_overflow;
+ int32_t imagemaster1_overflow;
+ int32_t imagemaster2_overflow;
+ int32_t imagemaster3_overflow;
+ int32_t imagemaster4_overflow;
+ int32_t imagemaster5_overflow;
+ int32_t imagemaster6_overflow;
+ int32_t be_overflow;
+ int32_t bg_overflow;
+ int32_t bf_overflow;
+ int32_t awb_overflow;
+ int32_t rs_overflow;
+ int32_t cs_overflow;
+ int32_t ihist_overflow;
+ int32_t skinbhist_overflow;
+};
+
struct vfe_device {
struct platform_device *pdev;
struct msm_sd_subdev subdev;
@@ -460,6 +477,7 @@
void __iomem *p_avtimer_msw;
void __iomem *p_avtimer_lsw;
uint8_t ignore_error;
+ struct msm_isp_statistics *stats;
};
#endif
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp32.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp32.c
index eb05015..cf76131 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp32.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp32.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2014, 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
@@ -41,6 +41,8 @@
(~(ping_pong >> (idx + VFE32_STATS_PING_PONG_OFFSET)) & 0x1))
#define VFE32_CLK_IDX 0
+#define MSM_ISP32_TOTAL_WM_UB 792
+
static struct msm_cam_clk_info msm_vfe32_1_clk_info[] = {
/*vfe32 clock info for B-family: 8610 */
{"vfe_clk_src", 266670000},
@@ -253,7 +255,6 @@
static void msm_vfe32_process_error_status(struct vfe_device *vfe_dev)
{
uint32_t error_status1 = vfe_dev->error_info.error_mask1;
-
if (error_status1 & BIT(0))
pr_err("%s: camif error status: 0x%x\n",
__func__, vfe_dev->error_info.camif_status);
@@ -273,34 +274,62 @@
pr_err("%s: violation\n", __func__);
msm_vfe32_process_violation_status(vfe_dev);
}
- if (error_status1 & BIT(8))
+ if (error_status1 & BIT(8)) {
+ vfe_dev->stats->imagemaster0_overflow++;
pr_err("%s: image master 0 bus overflow\n", __func__);
- if (error_status1 & BIT(9))
+ }
+ if (error_status1 & BIT(9)) {
+ vfe_dev->stats->imagemaster1_overflow++;
pr_err("%s: image master 1 bus overflow\n", __func__);
- if (error_status1 & BIT(10))
+ }
+ if (error_status1 & BIT(10)) {
+ vfe_dev->stats->imagemaster2_overflow++;
pr_err("%s: image master 2 bus overflow\n", __func__);
- if (error_status1 & BIT(11))
+ }
+ if (error_status1 & BIT(11)) {
+ vfe_dev->stats->imagemaster3_overflow++;
pr_err("%s: image master 3 bus overflow\n", __func__);
- if (error_status1 & BIT(12))
+ }
+ if (error_status1 & BIT(12)) {
+ vfe_dev->stats->imagemaster4_overflow++;
pr_err("%s: image master 4 bus overflow\n", __func__);
- if (error_status1 & BIT(13))
+ }
+ if (error_status1 & BIT(13)) {
+ vfe_dev->stats->imagemaster5_overflow++;
pr_err("%s: image master 5 bus overflow\n", __func__);
- if (error_status1 & BIT(14))
+ }
+ if (error_status1 & BIT(14)) {
+ vfe_dev->stats->imagemaster6_overflow++;
pr_err("%s: image master 6 bus overflow\n", __func__);
- if (error_status1 & BIT(15))
+ }
+ if (error_status1 & BIT(15)) {
+ vfe_dev->stats->bg_overflow++;
pr_err("%s: status ae/bg bus overflow\n", __func__);
- if (error_status1 & BIT(16))
+ }
+ if (error_status1 & BIT(16)) {
+ vfe_dev->stats->bf_overflow++;
pr_err("%s: status af/bf bus overflow\n", __func__);
- if (error_status1 & BIT(17))
+ }
+ if (error_status1 & BIT(17)) {
+ vfe_dev->stats->awb_overflow++;
pr_err("%s: status awb bus overflow\n", __func__);
- if (error_status1 & BIT(18))
+ }
+ if (error_status1 & BIT(18)) {
+ vfe_dev->stats->rs_overflow++;
pr_err("%s: status rs bus overflow\n", __func__);
- if (error_status1 & BIT(19))
+ }
+ if (error_status1 & BIT(19)) {
+ vfe_dev->stats->cs_overflow++;
pr_err("%s: status cs bus overflow\n", __func__);
- if (error_status1 & BIT(20))
+ }
+ if (error_status1 & BIT(20)) {
+ vfe_dev->stats->ihist_overflow++;
pr_err("%s: status ihist bus overflow\n", __func__);
- if (error_status1 & BIT(21))
+ }
+ if (error_status1 & BIT(21)) {
+ vfe_dev->stats->skinbhist_overflow++;
pr_err("%s: status skin bhist bus overflow\n", __func__);
+ }
if (error_status1 & BIT(22))
pr_err("%s: axi error\n", __func__);
}
@@ -375,7 +404,7 @@
rst_val = msm_vfe32_reset_values[reset_type];
init_completion(&vfe_dev->reset_complete);
msm_camera_io_w_mb(rst_val, vfe_dev->vfe_base + 0x4);
- return wait_for_completion_interruptible_timeout(
+ return wait_for_completion_timeout(
&vfe_dev->reset_complete, msecs_to_jiffies(50));
}
@@ -787,7 +816,43 @@
msm_camera_io_w(xbar_reg_cfg, vfe_dev->vfe_base + VFE32_XBAR_BASE(wm));
}
-static void msm_vfe32_cfg_axi_ub(struct vfe_device *vfe_dev)
+static void msm_vfe32_cfg_axi_ub_equal_default(struct vfe_device *vfe_dev)
+{
+ int i;
+ uint32_t ub_offset = 0;
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
+ uint32_t total_image_size = 0;
+ uint32_t num_used_wms = 0;
+ uint32_t prop_size = 0;
+ uint32_t wm_ub_size;
+ uint64_t delta;
+ for (i = 0; i < axi_data->hw_info->num_wm; i++) {
+ if (axi_data->free_wm[i] > 0) {
+ num_used_wms++;
+ total_image_size += axi_data->wm_image_size[i];
+ }
+ }
+ prop_size = MSM_ISP32_TOTAL_WM_UB -
+ axi_data->hw_info->min_wm_ub * num_used_wms;
+ for (i = 0; i < axi_data->hw_info->num_wm; i++) {
+ if (axi_data->free_wm[i]) {
+ delta =
+ (uint64_t)(axi_data->wm_image_size[i] *
+ prop_size);
+ do_div(delta, total_image_size);
+ wm_ub_size = axi_data->hw_info->min_wm_ub +
+ (uint32_t)delta;
+ msm_camera_io_w(ub_offset << 16 |
+ (wm_ub_size - 1), vfe_dev->vfe_base +
+ VFE32_WM_BASE(i) + 0xC);
+ ub_offset += wm_ub_size;
+ } else
+ msm_camera_io_w(0,
+ vfe_dev->vfe_base + VFE32_WM_BASE(i) + 0xC);
+ }
+}
+
+static void msm_vfe32_cfg_axi_ub_equal_slicing(struct vfe_device *vfe_dev)
{
int i;
uint32_t ub_offset = 0;
@@ -809,6 +874,16 @@
}
}
+static void msm_vfe32_cfg_axi_ub(struct vfe_device *vfe_dev)
+{
+ struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
+ axi_data->wm_ub_cfg_policy = MSM_WM_UB_CFG_DEFAULT;
+ if (axi_data->wm_ub_cfg_policy == MSM_WM_UB_EQUAL_SLICING)
+ msm_vfe32_cfg_axi_ub_equal_slicing(vfe_dev);
+ else
+ msm_vfe32_cfg_axi_ub_equal_default(vfe_dev);
+}
+
static void msm_vfe32_update_ping_pong_addr(struct vfe_device *vfe_dev,
uint8_t wm_idx, uint32_t pingpong_status, unsigned long paddr)
{
@@ -1069,6 +1144,7 @@
.num_comp_mask = 3,
.num_rdi = 3,
.num_rdi_master = 3,
+ .min_wm_ub = 64,
};
static struct msm_vfe_stats_hardware_info msm_vfe32_stats_hw_info = {
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 e443e9a..04136d0 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c
@@ -13,7 +13,7 @@
#include <linux/module.h>
#include <mach/iommu.h>
#include <linux/ratelimit.h>
-
+#include <asm/div64.h>
#include "msm_isp40.h"
#include "msm_isp_util.h"
#include "msm_isp_axi_util.h"
@@ -36,8 +36,8 @@
#define VFE40_8x26_VERSION 0x20000013
#define VFE40_8x26V2_VERSION 0x20010014
-#define VFE40_BURST_LEN 3
-#define VFE40_STATS_BURST_LEN 2
+#define VFE40_BURST_LEN 1
+#define VFE40_STATS_BURST_LEN 1
#define VFE40_UB_SIZE 1536
#define VFE40_EQUAL_SLICE_UB 228
#define VFE40_WM_BASE(idx) (0x6C + 0x24 * idx)
@@ -480,44 +480,74 @@
pr_err_ratelimited("%s: violation\n", __func__);
msm_vfe40_process_violation_status(vfe_dev);
}
- if (error_status1 & (1 << 9))
+ if (error_status1 & (1 << 9)) {
+ vfe_dev->stats->imagemaster0_overflow++;
pr_err_ratelimited("%s: image master 0 bus overflow\n",
__func__);
- if (error_status1 & (1 << 10))
+ }
+ if (error_status1 & (1 << 10)) {
+ vfe_dev->stats->imagemaster1_overflow++;
pr_err_ratelimited("%s: image master 1 bus overflow\n",
__func__);
- if (error_status1 & (1 << 11))
+ }
+ if (error_status1 & (1 << 11)) {
+ vfe_dev->stats->imagemaster2_overflow++;
pr_err_ratelimited("%s: image master 2 bus overflow\n",
__func__);
- if (error_status1 & (1 << 12))
+ }
+ if (error_status1 & (1 << 12)) {
+ vfe_dev->stats->imagemaster3_overflow++;
pr_err_ratelimited("%s: image master 3 bus overflow\n",
__func__);
- if (error_status1 & (1 << 13))
+ }
+ if (error_status1 & (1 << 13)) {
+ vfe_dev->stats->imagemaster4_overflow++;
pr_err_ratelimited("%s: image master 4 bus overflow\n",
__func__);
- if (error_status1 & (1 << 14))
+ }
+ if (error_status1 & (1 << 14)) {
+ vfe_dev->stats->imagemaster5_overflow++;
pr_err_ratelimited("%s: image master 5 bus overflow\n",
__func__);
- if (error_status1 & (1 << 15))
+ }
+ if (error_status1 & (1 << 15)) {
+ vfe_dev->stats->imagemaster6_overflow++;
pr_err_ratelimited("%s: image master 6 bus overflow\n",
__func__);
- if (error_status1 & (1 << 16))
+ }
+ if (error_status1 & (1 << 16)) {
+ vfe_dev->stats->be_overflow++;
pr_err_ratelimited("%s: status be bus overflow\n", __func__);
- if (error_status1 & (1 << 17))
+ }
+ if (error_status1 & (1 << 17)) {
+ vfe_dev->stats->bg_overflow++;
pr_err_ratelimited("%s: status bg bus overflow\n", __func__);
- if (error_status1 & (1 << 18))
+ }
+ if (error_status1 & (1 << 18)) {
+ vfe_dev->stats->bf_overflow++;
pr_err_ratelimited("%s: status bf bus overflow\n", __func__);
- if (error_status1 & (1 << 19))
+ }
+ if (error_status1 & (1 << 19)) {
+ vfe_dev->stats->awb_overflow++;
pr_err_ratelimited("%s: status awb bus overflow\n", __func__);
- if (error_status1 & (1 << 20))
+ }
+ if (error_status1 & (1 << 20)) {
+ vfe_dev->stats->imagemaster0_overflow++;
pr_err_ratelimited("%s: status rs bus overflow\n", __func__);
- if (error_status1 & (1 << 21))
+ }
+ if (error_status1 & (1 << 21)) {
+ vfe_dev->stats->cs_overflow++;
pr_err_ratelimited("%s: status cs bus overflow\n", __func__);
- if (error_status1 & (1 << 22))
+ }
+ if (error_status1 & (1 << 22)) {
+ vfe_dev->stats->ihist_overflow++;
pr_err_ratelimited("%s: status ihist bus overflow\n", __func__);
- if (error_status1 & (1 << 23))
+ }
+ if (error_status1 & (1 << 23)) {
+ vfe_dev->stats->skinbhist_overflow++;
pr_err_ratelimited("%s: status skin bhist bus overflow\n",
__func__);
+ }
}
static void msm_vfe40_read_irq_status(struct vfe_device *vfe_dev,
@@ -600,7 +630,7 @@
rst_val = msm_vfe40_reset_values[reset_type];
init_completion(&vfe_dev->reset_complete);
msm_camera_io_w_mb(rst_val, vfe_dev->vfe_base + 0xC);
- return wait_for_completion_interruptible_timeout(
+ return wait_for_completion_timeout(
&vfe_dev->reset_complete, msecs_to_jiffies(50));
}
@@ -1078,7 +1108,6 @@
uint8_t num_used_wms = 0;
uint32_t prop_size = 0;
uint32_t wm_ub_size;
- uint32_t delta;
for (i = 0; i < axi_data->hw_info->num_wm; i++) {
if (axi_data->free_wm[i] > 0) {
@@ -1090,9 +1119,11 @@
axi_data->hw_info->min_wm_ub * num_used_wms;
for (i = 0; i < axi_data->hw_info->num_wm; i++) {
if (axi_data->free_wm[i]) {
- delta =
- (axi_data->wm_image_size[i] *
- prop_size)/total_image_size;
+ uint64_t delta = 0;
+ uint64_t temp = (uint64_t)axi_data->wm_image_size[i] *
+ (uint64_t)prop_size;
+ do_div(temp, total_image_size);
+ delta = temp;
wm_ub_size = axi_data->hw_info->min_wm_ub + delta;
msm_camera_io_w(ub_offset << 16 | (wm_ub_size - 1),
vfe_dev->vfe_base + VFE40_WM_BASE(i) + 0x10);
@@ -1119,7 +1150,7 @@
static void msm_vfe40_cfg_axi_ub(struct vfe_device *vfe_dev)
{
struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
- axi_data->wm_ub_cfg_policy = MSM_WM_UB_EQUAL_SLICING;
+ axi_data->wm_ub_cfg_policy = MSM_WM_UB_CFG_DEFAULT;
if (axi_data->wm_ub_cfg_policy == MSM_WM_UB_EQUAL_SLICING)
msm_vfe40_cfg_axi_ub_equal_slicing(vfe_dev);
else
@@ -1408,8 +1439,8 @@
}
static struct msm_vfe_axi_hardware_info msm_vfe40_axi_hw_info = {
- .num_wm = 5,
- .num_comp_mask = 2,
+ .num_wm = 7,
+ .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_axi_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c
index 206620c..508bcec 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c
@@ -1086,8 +1086,8 @@
if (num_pix_streams > 0)
total_pix_bandwidth = total_pix_bandwidth /
num_pix_streams * (num_pix_streams - 1) +
- axi_data->src_info[VFE_PIX_0].pixel_clock *
- ISP_DEFAULT_FORMAT_FACTOR / ISP_Q2;
+ ((unsigned long)axi_data->src_info[VFE_PIX_0].
+ pixel_clock) * ISP_DEFAULT_FORMAT_FACTOR / ISP_Q2;
total_bandwidth = total_pix_bandwidth + total_rdi_bandwidth;
rc = msm_isp_update_bandwidth(ISP_VFE0 + vfe_dev->pdev->id,
@@ -1109,7 +1109,7 @@
vfe_dev->axi_data.pipeline_update = camif_update;
vfe_dev->axi_data.stream_update = 2;
spin_unlock_irqrestore(&vfe_dev->shared_data_lock, flags);
- rc = wait_for_completion_interruptible_timeout(
+ rc = wait_for_completion_timeout(
&vfe_dev->stream_config_complete,
msecs_to_jiffies(VFE_MAX_CFG_TIMEOUT));
if (rc == 0) {
@@ -1294,7 +1294,15 @@
rc = msm_isp_axi_wait_for_cfg_done(vfe_dev, camif_update);
if (rc < 0) {
pr_err("%s: wait for config done failed\n", __func__);
- return rc;
+ for (i = 0; i < stream_cfg_cmd->num_streams; i++) {
+ stream_info = &axi_data->stream_info[
+ HANDLE_TO_IDX(
+ stream_cfg_cmd->stream_handle[i])];
+ stream_info->state = STOP_PENDING;
+ msm_isp_axi_stream_enable_cfg(
+ vfe_dev, stream_info);
+ stream_info->state = INACTIVE;
+ }
}
}
msm_isp_update_stream_bandwidth(vfe_dev);
@@ -1312,7 +1320,7 @@
if (camif_update == DISABLE_CAMIF_IMMEDIATELY) {
vfe_dev->hw_info->vfe_ops.axi_ops.halt(vfe_dev);
}
- vfe_dev->hw_info->vfe_ops.core_ops.reset_hw(vfe_dev, ISP_RST_SOFT);
+ vfe_dev->hw_info->vfe_ops.core_ops.reset_hw(vfe_dev, ISP_RST_HARD);
vfe_dev->hw_info->vfe_ops.core_ops.init_hw_reg(vfe_dev);
vfe_dev->ignore_error = 0;
}
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c
index 8b83144..6bd7585 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c
@@ -367,7 +367,7 @@
int rc;
init_completion(&vfe_dev->stats_config_complete);
atomic_set(&vfe_dev->stats_data.stats_update, 2);
- rc = wait_for_completion_interruptible_timeout(
+ rc = wait_for_completion_timeout(
&vfe_dev->stats_config_complete,
msecs_to_jiffies(VFE_MAX_CFG_TIMEOUT));
if (rc == 0) {
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 ffe0b9c..cb46e9c 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
@@ -13,6 +13,7 @@
#include <linux/io.h>
#include <media/v4l2-subdev.h>
#include <linux/ratelimit.h>
+#include <asm/div64.h>
#include "msm.h"
#include "msm_isp_util.h"
@@ -24,11 +25,10 @@
static DEFINE_MUTEX(bandwidth_mgr_mutex);
static struct msm_isp_bandwidth_mgr isp_bandwidth_mgr;
-#define MSM_ISP_MIN_AB 300000000
-#define MSM_ISP_MIN_IB 450000000
+#define MSM_ISP_MIN_AB 450000000
+#define MSM_ISP_MIN_IB 900000000
#define VFE40_8974V2_VERSION 0x1001001A
-
static struct msm_bus_vectors msm_isp_init_vectors[] = {
{
.src = MSM_BUS_MASTER_VFE,
@@ -217,7 +217,8 @@
{
uint32_t avtimer_msw_1st = 0, avtimer_lsw = 0;
uint32_t avtimer_msw_2nd = 0;
- uint8_t iter = 0;
+ uint64_t av_timer_tick = 0;
+
if (!vfe_dev->p_avtimer_msw || !vfe_dev->p_avtimer_lsw) {
pr_err("%s: ioremap failed\n", __func__);
return;
@@ -226,15 +227,10 @@
avtimer_msw_1st = msm_camera_io_r(vfe_dev->p_avtimer_msw);
avtimer_lsw = msm_camera_io_r(vfe_dev->p_avtimer_lsw);
avtimer_msw_2nd = msm_camera_io_r(vfe_dev->p_avtimer_msw);
- } while ((avtimer_msw_1st != avtimer_msw_2nd)
- && (iter++ < AVTIMER_ITERATION_CTR));
- /*Just return if the MSW TimeStamps don't converge after
- a few iterations Application needs to handle the zero TS values*/
- if (iter >= AVTIMER_ITERATION_CTR) {
- pr_err("%s: AVTimer MSW TS did not converge !!!\n", __func__);
- return;
- }
- time_stamp->vt_time.tv_sec = avtimer_msw_1st;
+ } while (avtimer_msw_1st != avtimer_msw_2nd);
+ av_timer_tick = ((uint64_t)avtimer_msw_1st << 32) | avtimer_lsw;
+ avtimer_lsw = do_div(av_timer_tick, USEC_PER_SEC);
+ time_stamp->vt_time.tv_sec = (uint32_t)(av_timer_tick);
time_stamp->vt_time.tv_usec = avtimer_lsw;
}
diff --git a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c
index 70042f2..8f99ff6 100755
--- a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c
+++ b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c
@@ -64,6 +64,25 @@
false : true;
}
+static struct msm_cam_clk_info ispif_8626_reset_clk_info[] = {
+ {"ispif_ahb_clk", NO_SET_RATE},
+ {"camss_top_ahb_clk", NO_SET_RATE},
+ {"csi0_ahb_clk", NO_SET_RATE},
+ {"csi0_src_clk", NO_SET_RATE},
+ {"csi0_phy_clk", NO_SET_RATE},
+ {"csi0_clk", NO_SET_RATE},
+ {"csi0_pix_clk", NO_SET_RATE},
+ {"csi0_rdi_clk", NO_SET_RATE},
+ {"csi1_ahb_clk", NO_SET_RATE},
+ {"csi1_src_clk", NO_SET_RATE},
+ {"csi1_phy_clk", NO_SET_RATE},
+ {"csi1_clk", NO_SET_RATE},
+ {"csi1_pix_clk", NO_SET_RATE},
+ {"csi1_rdi_clk", NO_SET_RATE},
+ {"camss_vfe_vfe_clk", NO_SET_RATE},
+ {"camss_csi_vfe_clk", NO_SET_RATE},
+};
+
static struct msm_cam_clk_info ispif_8974_ahb_clk_info[] = {
{"ispif_ahb_clk", -1},
};
@@ -98,13 +117,26 @@
int rc = 0;
long timeout = 0;
struct clk *reset_clk[ARRAY_SIZE(ispif_8974_reset_clk_info)];
+ struct clk *reset_clk1[ARRAY_SIZE(ispif_8626_reset_clk_info)];
+ ispif->clk_idx = 0;
rc = msm_cam_clk_enable(&ispif->pdev->dev,
ispif_8974_reset_clk_info, reset_clk,
ARRAY_SIZE(ispif_8974_reset_clk_info), 1);
if (rc < 0) {
- pr_err("%s: cannot enable clock, error = %d",
- __func__, rc);
+ rc = msm_cam_clk_enable(&ispif->pdev->dev,
+ ispif_8626_reset_clk_info, reset_clk1,
+ ARRAY_SIZE(ispif_8626_reset_clk_info), 1);
+ if (rc < 0){
+ pr_err("%s: cannot enable clock, error = %d",
+ __func__, rc);
+ } else {
+ /* This is set if device is 8x26 */
+ ispif->clk_idx = 2;
+ }
+ } else {
+ /* This is set if device is 8974 */
+ ispif->clk_idx = 1;
}
init_completion(&ispif->reset_complete[VFE0]);
@@ -118,14 +150,22 @@
msm_camera_io_w(ISPIF_RST_CMD_1_MASK,
ispif->base + ISPIF_RST_CMD_1_ADDR);
- timeout = wait_for_completion_interruptible_timeout(
+ timeout = wait_for_completion_timeout(
&ispif->reset_complete[VFE0], msecs_to_jiffies(500));
CDBG("%s: VFE0 done\n", __func__);
+
if (timeout <= 0) {
pr_err("%s: VFE0 reset wait timeout\n", __func__);
- msm_cam_clk_enable(&ispif->pdev->dev,
+ rc = msm_cam_clk_enable(&ispif->pdev->dev,
ispif_8974_reset_clk_info, reset_clk,
ARRAY_SIZE(ispif_8974_reset_clk_info), 0);
+ if (rc < 0){
+ rc = msm_cam_clk_enable(&ispif->pdev->dev,
+ ispif_8626_reset_clk_info, reset_clk1,
+ ARRAY_SIZE(ispif_8626_reset_clk_info), 0);
+ if (rc < 0)
+ pr_err("%s: VFE0 reset wait timeout\n", __func__);
+ }
return -ETIMEDOUT;
}
@@ -143,13 +183,26 @@
}
}
- rc = msm_cam_clk_enable(&ispif->pdev->dev,
- ispif_8974_reset_clk_info, reset_clk,
- ARRAY_SIZE(ispif_8974_reset_clk_info), 0);
- if (rc < 0) {
- pr_err("%s: cannot disable clock, error = %d",
- __func__, rc);
+ if (ispif->clk_idx == 1){
+ rc = msm_cam_clk_enable(&ispif->pdev->dev,
+ ispif_8974_reset_clk_info, reset_clk,
+ ARRAY_SIZE(ispif_8974_reset_clk_info), 0);
+ if (rc < 0) {
+ pr_err("%s: cannot disable clock, error = %d",
+ __func__, rc);
+ }
}
+
+ if (ispif->clk_idx == 2){
+ rc = msm_cam_clk_enable(&ispif->pdev->dev,
+ ispif_8626_reset_clk_info, reset_clk1,
+ ARRAY_SIZE(ispif_8626_reset_clk_info), 0);
+ if (rc < 0) {
+ pr_err("%s: cannot disable clock, error = %d",
+ __func__, rc);
+ }
+ }
+
return rc;
}
@@ -941,11 +994,7 @@
goto error_ahb;
}
- if (of_device_is_compatible(ispif->pdev->dev.of_node,
- "qcom,ispif-v3.0")) {
- /* currently HW reset is implemented for 8974 only */
- msm_ispif_reset_hw(ispif);
- }
+ msm_ispif_reset_hw(ispif);
rc = msm_ispif_reset(ispif);
if (rc == 0) {
diff --git a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.h b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.h
index 45e7354..10dbfb6 100644
--- a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.h
+++ b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.h
@@ -60,5 +60,6 @@
struct clk *ahb_clk;
struct completion reset_complete[VFE_MAX];
uint32_t hw_num_isps;
+ uint32_t clk_idx;
};
#endif
diff --git a/drivers/media/platform/msm/camera_v2/msm.c b/drivers/media/platform/msm/camera_v2/msm.c
index f8f5110..e708d37 100644
--- a/drivers/media/platform/msm/camera_v2/msm.c
+++ b/drivers/media/platform/msm/camera_v2/msm.c
@@ -20,7 +20,6 @@
#include <linux/spinlock.h>
#include <linux/proc_fs.h>
#include <linux/atomic.h>
-#include <linux/wait.h>
#include <linux/videodev2.h>
#include <linux/msm_ion.h>
#include <linux/iommu.h>
@@ -402,7 +401,7 @@
msm_init_queue(&cmd_ack->command_q);
INIT_LIST_HEAD(&cmd_ack->list);
- init_waitqueue_head(&cmd_ack->wait);
+ init_completion(&cmd_ack->wait_complete);
cmd_ack->stream_id = stream_id;
msm_enqueue(&session->command_ack_q, &cmd_ack->list);
@@ -602,7 +601,7 @@
spin_flags);
ret_cmd->event = *(struct v4l2_event *)arg;
msm_enqueue(&cmd_ack->command_q, &ret_cmd->list);
- wake_up(&cmd_ack->wait);
+ complete(&cmd_ack->wait_complete);
spin_unlock_irqrestore(&(session->command_ack_q.lock),
spin_flags);
}
@@ -705,6 +704,9 @@
return -EIO;
}
+ /*re-init wait_complete */
+ INIT_COMPLETION(cmd_ack->wait_complete);
+
v4l2_event_queue(vdev, event);
if (timeout < 0) {
@@ -715,23 +717,18 @@
}
/* should wait on session based condition */
- do {
- rc = wait_event_interruptible_timeout(cmd_ack->wait,
- !list_empty_careful(&cmd_ack->command_q.list),
+ rc = wait_for_completion_timeout(&cmd_ack->wait_complete,
msecs_to_jiffies(timeout));
- if (rc != -ERESTARTSYS)
- break;
- } while (1);
if (list_empty_careful(&cmd_ack->command_q.list)) {
if (!rc) {
pr_err("%s: Timed out\n", __func__);
rc = -ETIMEDOUT;
- }
- if (rc < 0) {
- pr_err("%s: rc = %d\n", __func__, rc);
+ } else {
+ pr_err("%s: Error: No timeout but list empty!",
+ __func__);
mutex_unlock(&session->lock);
- return rc;
+ return -EINVAL;
}
}
diff --git a/drivers/media/platform/msm/camera_v2/msm.h b/drivers/media/platform/msm/camera_v2/msm.h
index 32b7222..c97def3 100644
--- a/drivers/media/platform/msm/camera_v2/msm.h
+++ b/drivers/media/platform/msm/camera_v2/msm.h
@@ -14,6 +14,7 @@
#define _MSM_H
#include <linux/version.h>
+#include <linux/completion.h>
#include <linux/i2c.h>
#include <linux/videodev2.h>
#include <linux/pm_qos.h>
@@ -70,7 +71,7 @@
struct msm_command_ack {
struct list_head list;
struct msm_queue_head command_q;
- wait_queue_head_t wait;
+ struct completion wait_complete;
int stream_id;
};
diff --git a/drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.c b/drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.c
index 0fbaeca..6e9336a 100644
--- a/drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.c
+++ b/drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.c
@@ -20,6 +20,10 @@
int i;
struct msm_v4l2_format_data *data = q->drv_priv;
+ if (!data) {
+ pr_err("%s: drv_priv NULL\n", __func__);
+ return -EINVAL;
+ }
if (data->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
if (WARN_ON(data->num_planes > VIDEO_MAX_PLANES))
return -EINVAL;
diff --git a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c
old mode 100755
new mode 100644
index bba774d..a53ac38
--- a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c
+++ b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c
@@ -51,6 +51,9 @@
#define CPP_CMD_TIMEOUT_MS 300
#define MSM_MICRO_IFACE_CLK_IDX 7
+#define MSM_CPP_NOMINAL_CLOCK 266670000
+#define MSM_CPP_TURBO_CLOCK 320000000
+
struct msm_cpp_timer_data_t {
struct cpp_device *cpp_dev;
struct msm_cpp_frame_info_t *processed_frame;
@@ -1120,9 +1123,6 @@
}
this_frame = cpp_timer.data.processed_frame;
- pr_err("ReInstalling cpp_timer\n");
- setup_timer(&cpp_timer.cpp_timer, cpp_timer_callback,
- (unsigned long)&cpp_timer);
pr_err("Starting timer to fire in %d ms. (jiffies=%lu)\n",
CPP_CMD_TIMEOUT_MS, jiffies);
ret = mod_timer(&cpp_timer.cpp_timer,
@@ -1609,6 +1609,52 @@
}
break;
}
+ case VIDIOC_MSM_CPP_SET_CLOCK: {
+ long clock_rate = 0;
+ if (ioctl_ptr->len == 0) {
+ pr_err("ioctl_ptr->len is 0\n");
+ mutex_unlock(&cpp_dev->mutex);
+ return -EINVAL;
+ }
+
+ if (ioctl_ptr->ioctl_ptr == NULL) {
+ pr_err("ioctl_ptr->ioctl_ptr is NULL\n");
+ mutex_unlock(&cpp_dev->mutex);
+ return -EINVAL;
+ }
+
+ if (ioctl_ptr->len > sizeof(clock_rate)) {
+ pr_err("Not valid ioctl_ptr->len\n");
+ mutex_unlock(&cpp_dev->mutex);
+ return -EINVAL;
+ }
+
+ rc = (copy_from_user(&clock_rate,
+ (void __user *)ioctl_ptr->ioctl_ptr,
+ ioctl_ptr->len) ? -EFAULT : 0);
+ if (rc) {
+ ERR_COPY_FROM_USER();
+ mutex_unlock(&cpp_dev->mutex);
+ return -EINVAL;
+ }
+
+ if (clock_rate > 0) {
+ clock_rate =
+ clk_round_rate(cpp_dev->cpp_clk[4], clock_rate);
+ CPP_DBG("clk:%ld\n", clock_rate);
+ clk_set_rate(cpp_dev->cpp_clk[4], clock_rate);
+ rc = msm_isp_update_bandwidth(ISP_CPP, clock_rate * 4,
+ clock_rate * 6);
+ if (rc < 0) {
+ pr_err("Bandwidth Set Failed!\n");
+ msm_isp_update_bandwidth(ISP_CPP, 0, 0);
+ mutex_unlock(&cpp_dev->mutex);
+ return -EINVAL;
+ }
+ }
+
+ break;
+ }
case MSM_SD_SHUTDOWN: {
mutex_unlock(&cpp_dev->mutex);
while (cpp_dev->cpp_open_cnt != 0)
diff --git a/drivers/media/platform/msm/camera_v2/sensor/Makefile b/drivers/media/platform/msm/camera_v2/sensor/Makefile
index d1ec5d8e..a33cf62 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/Makefile
+++ b/drivers/media/platform/msm/camera_v2/sensor/Makefile
@@ -5,14 +5,11 @@
ccflags-y += -Idrivers/media/platform/msm/camera_v2/sensor/cci
obj-$(CONFIG_MSMB_CAMERA) += cci/ io/ csiphy/ csid/ actuator/ flash/ eeprom/
obj-$(CONFIG_MSM_CAMERA_SENSOR) += msm_sensor_init.o msm_sensor_driver.o msm_sensor.o
-obj-$(CONFIG_S5K3L1YX) += s5k3l1yx.o
-obj-$(CONFIG_IMX135) += imx135.o
obj-$(CONFIG_IMX134) += imx134.o
obj-$(CONFIG_OV8825) += ov8825.o
obj-$(CONFIG_OV8865) += ov8865.o
obj-$(CONFIG_s5k4e1) += s5k4e1.o
obj-$(CONFIG_OV12830) += ov12830.o
-obj-$(CONFIG_OV2720) += ov2720.o
obj-$(CONFIG_OV9724) += ov9724.o
obj-$(CONFIG_HI256) += hi256.o
obj-$(CONFIG_OV5648) += ov5648.o
diff --git a/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c b/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c
index 7805930..8e7dc2c 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c
@@ -156,6 +156,7 @@
settings[i].i2c_operation);
break;
}
+ break;
}
case MSM_ACT_POLL: {
switch (settings[i].data_type) {
@@ -178,6 +179,7 @@
settings[i].i2c_operation);
break;
}
+ break;
}
}
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 e072e53..bf66442 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
@@ -98,7 +98,7 @@
int32_t rc = 0;
msm_camera_io_w(1 << master, cci_dev->base + CCI_HALT_REQ_ADDR);
- rc = wait_for_completion_interruptible_timeout(
+ rc = wait_for_completion_timeout(
&cci_dev->cci_master_info[master].reset_complete, CCI_TIMEOUT);
if (rc < 0) {
pr_err("%s:%d wait failed\n", __func__, __LINE__);
@@ -117,7 +117,7 @@
cci_dev->base + CCI_RESET_CMD_ADDR);
/* wait for reset done irq */
- rc = wait_for_completion_interruptible_timeout(
+ rc = wait_for_completion_timeout(
&cci_dev->cci_master_info[master].reset_complete,
CCI_TIMEOUT);
if (rc <= 0)
@@ -158,10 +158,10 @@
msm_camera_io_w(reg_val, cci_dev->base + CCI_QUEUE_START_ADDR);
CDBG("%s line %d wait_for_completion_interruptible\n",
__func__, __LINE__);
- rc = wait_for_completion_interruptible_timeout(&cci_dev->
+ rc = wait_for_completion_timeout(&cci_dev->
cci_master_info[master].reset_complete, CCI_TIMEOUT);
if (rc <= 0) {
- pr_err("%s: wait_for_completion_interruptible_timeout %d\n",
+ pr_err("%s: wait_for_completion_timeout %d\n",
__func__, __LINE__);
if (rc == 0)
rc = -ETIMEDOUT;
@@ -181,7 +181,7 @@
uint16_t i = 0, j = 0, k = 0, h = 0, len = 0;
int32_t rc = 0;
uint32_t cmd = 0, delay = 0;
- uint8_t data[10];
+ uint8_t data[11];
uint16_t reg_addr = 0;
struct msm_camera_i2c_reg_setting *i2c_msg =
&c_ctrl->cfg.cci_i2c_write_cfg;
@@ -401,12 +401,12 @@
val = 1 << ((master * 2) + queue);
msm_camera_io_w(val, cci_dev->base + CCI_QUEUE_START_ADDR);
- CDBG("%s:%d E wait_for_completion_interruptible_timeout\n", __func__,
+ CDBG("%s:%d E wait_for_completion_timeout\n", __func__,
__LINE__);
- rc = wait_for_completion_interruptible_timeout(&cci_dev->
+ rc = wait_for_completion_timeout(&cci_dev->
cci_master_info[master].reset_complete, CCI_TIMEOUT);
if (rc <= 0) {
- pr_err("%s: wait_for_completion_interruptible_timeout %d\n",
+ pr_err("%s: wait_for_completion_timeout %d\n",
__func__, __LINE__);
if (rc == 0)
rc = -ETIMEDOUT;
@@ -415,7 +415,7 @@
} else {
rc = 0;
}
- CDBG("%s:%d E wait_for_completion_interruptible_timeout\n", __func__,
+ CDBG("%s:%d E wait_for_completion_timeout\n", __func__,
__LINE__);
read_words = msm_camera_io_r(cci_dev->base +
@@ -608,17 +608,17 @@
CDBG("%s:%d E wait_for_completion_interruptible\n",
__func__, __LINE__);
- rc = wait_for_completion_interruptible_timeout(&cci_dev->
+ rc = wait_for_completion_timeout(&cci_dev->
cci_master_info[master].reset_complete, CCI_TIMEOUT);
if (rc <= 0) {
- pr_err("%s: wait_for_completion_interruptible_timeout %d\n",
+ pr_err("%s: wait_for_completion_timeout %d\n",
__func__, __LINE__);
if (rc == 0)
rc = -ETIMEDOUT;
msm_cci_flush_queue(cci_dev, master);
goto ERROR;
} else {
- rc = 0;
+ rc = cci_dev->cci_master_info[master].status;
}
CDBG("%s:%d X wait_for_completion_interruptible\n", __func__,
__LINE__);
@@ -679,7 +679,7 @@
msm_camera_io_w(CCI_M1_RESET_RMSK,
cci_dev->base + CCI_RESET_CMD_ADDR);
/* wait for reset done irq */
- rc = wait_for_completion_interruptible_timeout(
+ rc = wait_for_completion_timeout(
&cci_dev->cci_master_info[master].
reset_complete,
CCI_TIMEOUT);
@@ -713,11 +713,11 @@
cci_dev->cci_master_info[MASTER_0].reset_pending = TRUE;
msm_camera_io_w(CCI_RESET_CMD_RMSK, cci_dev->base + CCI_RESET_CMD_ADDR);
msm_camera_io_w(0x1, cci_dev->base + CCI_RESET_CMD_ADDR);
- rc = wait_for_completion_interruptible_timeout(
+ rc = wait_for_completion_timeout(
&cci_dev->cci_master_info[MASTER_0].reset_complete,
CCI_TIMEOUT);
if (rc <= 0) {
- pr_err("%s: wait_for_completion_interruptible_timeout %d\n",
+ pr_err("%s: wait_for_completion_timeout %d\n",
__func__, __LINE__);
if (rc == 0)
rc = -ETIMEDOUT;
diff --git a/drivers/media/platform/msm/camera_v2/sensor/csid/msm_csid.c b/drivers/media/platform/msm/camera_v2/sensor/csid/msm_csid.c
index 8662657..3596a12 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/csid/msm_csid.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/csid/msm_csid.c
@@ -95,7 +95,7 @@
static void msm_csid_reset(struct csid_device *csid_dev)
{
msm_camera_io_w(CSID_RST_STB_ALL, csid_dev->base + CSID_RST_CMD_ADDR);
- wait_for_completion_interruptible(&csid_dev->reset_complete);
+ wait_for_completion(&csid_dev->reset_complete);
return;
}
diff --git a/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_qup_i2c.c b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_qup_i2c.c
index d5b89b7..6f9aeec 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_qup_i2c.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_qup_i2c.c
@@ -69,7 +69,7 @@
rc = i2c_transfer(dev_client->client->adapter, msg, 1);
if (rc < 0)
S_I2C_DBG("msm_camera_qup_i2c_txdata faild 0x%x\n", saddr);
- return 0;
+ return rc;
}
int32_t msm_camera_qup_i2c_read(struct msm_camera_i2c_client *client,
diff --git a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.c b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.c
index 87ad994..279a7dd 100755
--- a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.c
@@ -169,6 +169,7 @@
if (rc < 0) {
pr_err("%s Default sensor position %d\n", __func__, __LINE__);
sensordata->sensor_info->position = 0;
+ rc = 0;
}
rc = of_property_read_u32(of_node, "qcom,sensor-mode",
@@ -178,6 +179,7 @@
if (rc < 0) {
pr_err("%s Default sensor mode %d\n", __func__, __LINE__);
sensordata->sensor_info->modes_supported = 0;
+ rc = 0;
}
rc = msm_sensor_get_dt_csi_data(of_node, &sensordata->csi_lane_params);
@@ -573,6 +575,10 @@
s_ctrl->sensordata->sensor_info->is_mount_angle_valid;
cdata->cfg.sensor_info.sensor_mount_angle =
s_ctrl->sensordata->sensor_info->sensor_mount_angle;
+ cdata->cfg.sensor_info.position =
+ s_ctrl->sensordata->sensor_info->position;
+ cdata->cfg.sensor_info.modes_supported =
+ s_ctrl->sensordata->sensor_info->modes_supported;
CDBG("%s:%d sensor name %s\n", __func__, __LINE__,
cdata->cfg.sensor_info.sensor_name);
CDBG("%s:%d session id %d\n", __func__, __LINE__,
diff --git a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c
index f5be347..d8b90dd 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c
@@ -27,6 +27,8 @@
#define CDBG(fmt, args...) pr_debug(fmt, ##args)
#endif
+#define SENSOR_MAX_MOUNTANGLE (360)
+
/* Static declaration */
static struct msm_sensor_ctrl_t *g_sctrl[MAX_CAMERAS];
@@ -260,6 +262,58 @@
return rc;
}
+static int32_t msm_sensor_fill_slave_info_init_params(
+ struct msm_camera_sensor_slave_info *slave_info,
+ struct msm_sensor_info_t *sensor_info)
+{
+ struct msm_sensor_init_params *sensor_init_params;
+ if (!slave_info || !sensor_info)
+ return -EINVAL;
+
+ if (!slave_info->is_init_params_valid)
+ return 0;
+
+ sensor_init_params = &slave_info->sensor_init_params;
+ if (INVALID_CAMERA_B != sensor_init_params->position)
+ sensor_info->position =
+ sensor_init_params->position;
+
+ if (SENSOR_MAX_MOUNTANGLE > sensor_init_params->sensor_mount_angle) {
+ sensor_info->sensor_mount_angle =
+ sensor_init_params->sensor_mount_angle;
+ sensor_info->is_mount_angle_valid = 1;
+ }
+
+ if (CAMERA_MODE_INVALID != sensor_init_params->modes_supported)
+ sensor_info->modes_supported =
+ sensor_init_params->modes_supported;
+
+ return 0;
+}
+
+
+static int32_t msm_sensor_validate_slave_info(
+ struct msm_sensor_info_t *sensor_info)
+{
+ if (INVALID_CAMERA_B == sensor_info->position) {
+ sensor_info->position = BACK_CAMERA_B;
+ pr_err("%s Set dafault sensor position%d\n",
+ __func__, __LINE__);
+ }
+ if (CAMERA_MODE_INVALID == sensor_info->modes_supported) {
+ sensor_info->modes_supported = CAMERA_MODE_2D_B;
+ pr_err("%s Set dafault sensor modes_supported%d\n",
+ __func__, __LINE__);
+ }
+ if (SENSOR_MAX_MOUNTANGLE < sensor_info->sensor_mount_angle) {
+ sensor_info->sensor_mount_angle = 0;
+ pr_err("%s Set dafault sensor mount angle%d\n",
+ __func__, __LINE__);
+ sensor_info->is_mount_angle_valid = 1;
+ }
+ return 0;
+}
+
/* static function definition */
int32_t msm_sensor_driver_probe(void *setting)
{
@@ -274,6 +328,7 @@
struct msm_camera_power_ctrl_t *power_info = NULL;
int c, end;
struct msm_sensor_power_setting power_down_setting_t;
+ unsigned long mount_pos = 0;
/* Validate input parameters */
if (!setting) {
@@ -304,6 +359,13 @@
CDBG("size %d", slave_info->power_setting_array.size);
CDBG("size down %d", slave_info->power_setting_array.size_down);
+ if (slave_info->is_init_params_valid) {
+ CDBG("position %d",
+ slave_info->sensor_init_params.position);
+ CDBG("mount %d",
+ slave_info->sensor_init_params.sensor_mount_angle);
+ }
+
/* Validate camera id */
if (slave_info->camera_id >= MAX_CAMERAS) {
pr_err("failed: invalid camera id %d max %d",
@@ -537,6 +599,25 @@
/* Power down */
s_ctrl->func_tbl->sensor_power_down(s_ctrl);
+ rc = msm_sensor_fill_slave_info_init_params(
+ slave_info,
+ s_ctrl->sensordata->sensor_info);
+ if (rc < 0) {
+ pr_err("%s Fill slave info failed", slave_info->sensor_name);
+ goto FREE_CAMERA_INFO;
+ }
+ rc = msm_sensor_validate_slave_info(s_ctrl->sensordata->sensor_info);
+ if (rc < 0) {
+ pr_err("%s Validate slave info failed",
+ slave_info->sensor_name);
+ goto FREE_CAMERA_INFO;
+ }
+ /* Update sensor mount angle and position in media entity flag */
+ mount_pos = s_ctrl->sensordata->sensor_info->position << 16;
+ mount_pos = mount_pos | ((s_ctrl->sensordata->sensor_info->
+ sensor_mount_angle / 90) << 8);
+ s_ctrl->msm_sd.sd.entity.flags = mount_pos | MEDIA_ENT_FL_DEFAULT;
+
/*Save sensor info*/
s_ctrl->sensordata->cam_slave_info = slave_info;
@@ -707,6 +788,22 @@
sensordata->sensor_info->is_mount_angle_valid = 1;
}
+ rc = of_property_read_u32(of_node, "qcom,sensor-position",
+ &sensordata->sensor_info->position);
+ if (rc < 0) {
+ pr_err("%s:%d Invalid sensor position\n", __func__, __LINE__);
+ sensordata->sensor_info->position = INVALID_CAMERA_B;
+ }
+
+ rc = of_property_read_u32(of_node, "qcom,sensor-mode",
+ &sensordata->sensor_info->modes_supported);
+ if (rc < 0) {
+ pr_err("%s:%d Invalid sensor mode supported\n",
+ __func__, __LINE__);
+ sensordata->sensor_info->modes_supported = CAMERA_MODE_INVALID;
+ rc = 0;
+ }
+
/* Get vdd-cx regulator */
/*Optional property, don't return error if absent */
of_property_read_string(of_node, "qcom,vdd-cx-name",
diff --git a/drivers/media/platform/msm/vidc/hfi_packetization.c b/drivers/media/platform/msm/vidc/hfi_packetization.c
index 4b7a3be..a563f68 100644
--- a/drivers/media/platform/msm/vidc/hfi_packetization.c
+++ b/drivers/media/platform/msm/vidc/hfi_packetization.c
@@ -342,12 +342,6 @@
case HAL_EXTRADATA_RECOVERY_POINT_SEI:
ret = HFI_PROPERTY_PARAM_VDEC_RECOVERY_POINT_SEI_EXTRADATA;
break;
- case HAL_EXTRADATA_CLOSED_CAPTION_UD:
- ret = HFI_PROPERTY_PARAM_VDEC_CLOSED_CAPTION_EXTRADATA;
- break;
- case HAL_EXTRADATA_AFD_UD:
- ret = HFI_PROPERTY_PARAM_VDEC_AFD_EXTRADATA;
- break;
case HAL_EXTRADATA_MULTISLICE_INFO:
ret = HFI_PROPERTY_PARAM_VENC_MULTI_SLICE_INFO;
break;
@@ -372,6 +366,9 @@
case HAL_EXTRADATA_METADATA_MBI:
ret = HFI_PROPERTY_PARAM_VENC_MBI_DUMPING;
break;
+ case HAL_EXTRADATA_STREAM_USERDATA:
+ ret = HFI_PROPERTY_PARAM_VDEC_STREAM_USERDATA_EXTRADATA;
+ break;
default:
dprintk(VIDC_WARN, "Extradata index not found: %d\n", index);
break;
@@ -1479,14 +1476,36 @@
pr_err("MARK LTR\n");
break;
}
- case HAL_PARAM_VENC_HIER_P_NUM_FRAMES:
+ case HAL_PARAM_VENC_HIER_P_MAX_ENH_LAYERS:
{
pkt->rg_property_data[0] =
- HFI_PROPERTY_PARAM_VENC_HIER_P_NUM_ENH_LAYER;
+ HFI_PROPERTY_PARAM_VENC_HIER_P_MAX_NUM_ENH_LAYER;
pkt->rg_property_data[1] = *(u32 *)pdata;
pkt->size += sizeof(u32) * 2;
break;
}
+ case HAL_CONFIG_VENC_HIER_P_NUM_FRAMES:
+ {
+ pkt->rg_property_data[0] =
+ HFI_PROPERTY_CONFIG_VENC_HIER_P_ENH_LAYER;
+ pkt->rg_property_data[1] = *(u32 *)pdata;
+ pkt->size += sizeof(u32) * 2;
+ break;
+ }
+ case HAL_PARAM_VENC_ENABLE_INITIAL_QP:
+ {
+ struct hfi_initial_quantization *hfi;
+ struct hal_initial_quantization *quant = pdata;
+ pkt->rg_property_data[0] =
+ HFI_PROPERTY_PARAM_VENC_INITIAL_QP;
+ hfi = (struct hfi_initial_quantization *) &pkt->rg_property_data[1];
+ hfi->init_qp_enable = quant->initqp_enable;
+ hfi->qp_i = quant->qpi;
+ hfi->qp_p = quant->qpp;
+ hfi->qp_b = quant->qpb;
+ pkt->size += sizeof(u32) + sizeof(struct hfi_initial_quantization);
+ break;
+ }
/* FOLLOWING PROPERTIES ARE NOT IMPLEMENTED IN CORE YET */
case HAL_CONFIG_BUFFER_REQUIREMENTS:
case HAL_CONFIG_PRIORITY:
diff --git a/drivers/media/platform/msm/vidc/hfi_response_handler.c b/drivers/media/platform/msm/vidc/hfi_response_handler.c
index f4ad985..c6fb382 100644
--- a/drivers/media/platform/msm/vidc/hfi_response_handler.c
+++ b/drivers/media/platform/msm/vidc/hfi_response_handler.c
@@ -49,7 +49,7 @@
vidc_err = VIDC_ERR_NOT_SUPPORTED;
break;
case HFI_ERR_SYS_MAX_SESSIONS_REACHED:
- vidc_err = VIDC_ERR_MAX_CLIENT;
+ vidc_err = VIDC_ERR_MAX_CLIENTS;
break;
case HFI_ERR_SYS_SESSION_IN_USE:
vidc_err = VIDC_ERR_CLIENT_PRESENT;
@@ -75,6 +75,8 @@
vidc_err = VIDC_ERR_FAIL;
break;
}
+ if (vidc_err != HFI_ERR_NONE)
+ dprintk(VIDC_ERR, "HFI Error: %d\n", vidc_err);
return vidc_err;
}
@@ -108,7 +110,8 @@
struct hfi_frame_size frame_sz;
u8 *data_ptr;
int prop_id;
- dprintk(VIDC_DBG, "RECEIVED:EVENT_NOTIFY");
+ dprintk(VIDC_DBG, "RECEIVED: EVENT_NOTIFY[%u]: %d, 0x%x\n",
+ pkt->session_id, pkt->event_data1, pkt->event_data2);
if (sizeof(struct hfi_msg_event_notify_packet)
> pkt->size) {
dprintk(VIDC_ERR, "hal_process_session_init_done:bad_pkt_size");
@@ -248,27 +251,33 @@
hfi_process_sys_error(callback, device_id);
break;
case HFI_EVENT_SESSION_ERROR:
- dprintk(VIDC_INFO, "HFI_EVENT_SESSION_ERROR");
+ dprintk(VIDC_INFO,
+ "HFI_EVENT_SESSION_ERROR[%u]\n", pkt->session_id);
if (!validate_session_pkt(sessions, sess, session_lock))
hfi_process_session_error(callback, device_id, pkt);
break;
case HFI_EVENT_SESSION_SEQUENCE_CHANGED:
- dprintk(VIDC_INFO, "HFI_EVENT_SESSION_SEQUENCE_CHANGED");
+ dprintk(VIDC_INFO, "HFI_EVENT_SESSION_SEQUENCE_CHANGED[%u]\n",
+ pkt->session_id);
if (!validate_session_pkt(sessions, sess, session_lock))
hfi_process_sess_evt_seq_changed(callback,
device_id, pkt);
break;
case HFI_EVENT_SESSION_PROPERTY_CHANGED:
- dprintk(VIDC_INFO, "HFI_EVENT_SESSION_PROPERTY_CHANGED");
+ dprintk(VIDC_INFO, "HFI_EVENT_SESSION_PROPERTY_CHANGED[%u]\n",
+ pkt->session_id);
break;
case HFI_EVENT_RELEASE_BUFFER_REFERENCE:
- dprintk(VIDC_INFO, "HFI_EVENT_RELEASE_BUFFER_REFERENCE\n");
+ dprintk(VIDC_INFO, "HFI_EVENT_RELEASE_BUFFER_REFERENCE[%u]\n",
+ pkt->session_id);
if (!validate_session_pkt(sessions, sess, session_lock))
hfi_process_evt_release_buffer_ref(callback,
device_id, pkt);
break;
default:
- dprintk(VIDC_WARN, "hal_process_event_notify:unkown_event_id");
+ dprintk(VIDC_WARN,
+ "hal_process_event_notify: unknown_event_id[%u]\n",
+ pkt->session_id);
break;
}
}
@@ -758,7 +767,8 @@
struct msm_vidc_cb_cmd_done cmd_done;
struct buffer_requirements buff_req;
- dprintk(VIDC_DBG, "Received SESSION_PROPERTY_INFO");
+ dprintk(VIDC_DBG, "Received SESSION_PROPERTY_INFO[%u]\n",
+ pkt->session_id);
if (pkt->size < sizeof(struct hfi_msg_session_property_info_packet)) {
dprintk(VIDC_ERR, "hal_process_session_prop_info:bad_pkt_size");
@@ -800,7 +810,8 @@
struct msm_vidc_cb_cmd_done cmd_done;
struct vidc_hal_session_init_done session_init_done;
struct hal_session *sess_close = NULL;
- dprintk(VIDC_DBG, "RECEIVED:SESSION_INIT_DONE");
+ dprintk(VIDC_DBG, "RECEIVED: SESSION_INIT_DONE[%u]\n",
+ pkt->session_id);
if (sizeof(struct hfi_msg_sys_session_init_done_packet)
> pkt->size) {
dprintk(VIDC_ERR, "hal_process_session_init_done:bad_pkt_size");
@@ -839,7 +850,8 @@
struct hfi_msg_session_load_resources_done_packet *pkt)
{
struct msm_vidc_cb_cmd_done cmd_done;
- dprintk(VIDC_DBG, "RECEIVED:SESSION_LOAD_RESOURCES_DONE");
+ dprintk(VIDC_DBG, "RECEIVED: SESSION_LOAD_RESOURCES_DONE[%u]\n",
+ pkt->session_id);
if (sizeof(struct hfi_msg_session_load_resources_done_packet) !=
pkt->size) {
@@ -865,7 +877,8 @@
{
struct msm_vidc_cb_cmd_done cmd_done;
- dprintk(VIDC_DBG, "RECEIVED:SESSION_FLUSH_DONE");
+ dprintk(VIDC_DBG, "RECEIVED: SESSION_FLUSH_DONE[%u]\n",
+ pkt->session_id);
if (sizeof(struct hfi_msg_session_flush_done_packet) != pkt->size) {
dprintk(VIDC_ERR, "hal_process_session_flush_done: "
@@ -889,7 +902,8 @@
{
struct msm_vidc_cb_data_done data_done;
- dprintk(VIDC_DBG, "RECEIVED:SESSION_ETB_DONE");
+ dprintk(VIDC_DBG, "RECEIVED: SESSION_ETB_DONE[%u]\n",
+ pkt->session_id);
if (!pkt || pkt->size <
sizeof(struct hfi_msg_session_empty_buffer_done_packet)) {
@@ -930,7 +944,8 @@
session = (struct hal_session *)
((struct hal_session *) pack->session_id)->session_id;
- dprintk(VIDC_DBG, "RECEIVED:SESSION_FTB_DONE");
+ dprintk(VIDC_DBG, "RECEIVED: SESSION_FTB_DONE[%u]\n",
+ pack->session_id);
memset(&data_done, 0, sizeof(struct msm_vidc_cb_data_done));
@@ -1028,7 +1043,8 @@
{
struct msm_vidc_cb_cmd_done cmd_done;
- dprintk(VIDC_DBG, "RECEIVED:SESSION_START_DONE");
+ dprintk(VIDC_DBG, "RECEIVED: SESSION_START_DONE[%u]\n",
+ pkt->session_id);
if (!pkt || pkt->size !=
sizeof(struct hfi_msg_session_start_done_packet)) {
@@ -1053,7 +1069,8 @@
{
struct msm_vidc_cb_cmd_done cmd_done;
- dprintk(VIDC_DBG, "RECEIVED:SESSION_STOP_DONE");
+ dprintk(VIDC_DBG, "RECEIVED: SESSION_STOP_DONE[%u]\n",
+ pkt->session_id);
if (!pkt || pkt->size !=
sizeof(struct hfi_msg_session_stop_done_packet)) {
@@ -1078,7 +1095,8 @@
{
struct msm_vidc_cb_cmd_done cmd_done;
- dprintk(VIDC_DBG, "RECEIVED:SESSION_RELEASE_RESOURCES_DONE");
+ dprintk(VIDC_DBG, "RECEIVED: SESSION_RELEASE_RESOURCES_DONE[%u]\n",
+ pkt->session_id);
if (!pkt || pkt->size !=
sizeof(struct hfi_msg_session_release_resources_done_packet)) {
@@ -1102,6 +1120,8 @@
struct hfi_msg_session_release_buffers_done_packet *pkt)
{
struct msm_vidc_cb_cmd_done cmd_done;
+ dprintk(VIDC_DBG, "RECEIVED:SESSION_RELEASE_BUFFER_DONE[%u]\n",
+ pkt->session_id);
if (!pkt || pkt->size !=
sizeof(struct
hfi_msg_session_release_buffers_done_packet)) {
@@ -1129,7 +1149,8 @@
{
struct msm_vidc_cb_cmd_done cmd_done;
- dprintk(VIDC_DBG, "RECEIVED:SESSION_END_DONE");
+ dprintk(VIDC_DBG, "RECEIVED: SESSION_END_DONE[%u]\n",
+ pkt->session_id);
if (!pkt || pkt->size !=
sizeof(struct hfi_msg_sys_session_end_done_packet)) {
@@ -1154,8 +1175,8 @@
{
struct msm_vidc_cb_cmd_done cmd_done;
- dprintk(VIDC_DBG, "RECEIVED:SESSION_ABORT_DONE");
-
+ dprintk(VIDC_DBG, "RECEIVED: SESSION_ABORT_DONE[%u]\n",
+ pkt->session_id);
if (!pkt || pkt->size !=
sizeof(struct hfi_msg_sys_session_abort_done_packet)) {
dprintk(VIDC_ERR, "%s: bad packet/packet size: %d",
@@ -1184,6 +1205,8 @@
dprintk(VIDC_ERR, "bad packet/packet size: %d", pkt->size);
return;
}
+ dprintk(VIDC_DBG, "RECEIVED:SESSION_GET_SEQ_HDR_DONE[%u]\n",
+ pkt->session_id);
memset(&data_done, 0, sizeof(struct msm_vidc_cb_data_done));
data_done.device_id = device_id;
data_done.size = sizeof(struct msm_vidc_cb_data_done);
diff --git a/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c b/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c
index 176c612..c638415 100644
--- a/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c
+++ b/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c
@@ -319,7 +319,6 @@
}
INIT_LIST_HEAD(&core->instances);
- mutex_init(&core->sync_lock);
mutex_init(&core->lock);
core->state = VIDC_CORE_UNINIT;
diff --git a/drivers/media/platform/msm/vidc/msm_vdec.c b/drivers/media/platform/msm/vidc/msm_vdec.c
index 66d6878..f84a806 100644
--- a/drivers/media/platform/msm/vidc/msm_vdec.c
+++ b/drivers/media/platform/msm/vidc/msm_vdec.c
@@ -102,7 +102,6 @@
),
.qmenu = mpeg_video_stream_format,
.step = 0,
- .cluster = 0,
},
{
.id = V4L2_CID_MPEG_VIDC_VIDEO_OUTPUT_ORDER,
@@ -117,7 +116,6 @@
),
.qmenu = mpeg_video_output_order,
.step = 0,
- .cluster = 0,
},
{
.id = V4L2_CID_MPEG_VIDC_VIDEO_ENABLE_PICTURE_TYPE,
@@ -129,7 +127,6 @@
.step = 1,
.menu_skip_mask = 0,
.qmenu = NULL,
- .cluster = 0,
},
{
.id = V4L2_CID_MPEG_VIDC_VIDEO_KEEP_ASPECT_RATIO,
@@ -141,7 +138,6 @@
.step = 1,
.menu_skip_mask = 0,
.qmenu = NULL,
- .cluster = 0,
},
{
.id = V4L2_CID_MPEG_VIDC_VIDEO_POST_LOOP_DEBLOCKER_MODE,
@@ -153,7 +149,6 @@
.step = 1,
.menu_skip_mask = 0,
.qmenu = NULL,
- .cluster = 0,
},
{
.id = V4L2_CID_MPEG_VIDC_VIDEO_DIVX_FORMAT,
@@ -169,7 +164,6 @@
),
.qmenu = mpeg_video_vidc_divx_format,
.step = 0,
- .cluster = 0,
},
{
.id = V4L2_CID_MPEG_VIDC_VIDEO_MB_ERROR_MAP_REPORTING,
@@ -181,7 +175,6 @@
.step = 1,
.menu_skip_mask = 0,
.qmenu = NULL,
- .cluster = 0,
},
{
.id = V4L2_CID_MPEG_VIDC_VIDEO_CONTINUE_DATA_TRANSFER,
@@ -193,7 +186,6 @@
.step = 1,
.menu_skip_mask = 0,
.qmenu = NULL,
- .cluster = 0,
},
{
.id = V4L2_CID_MPEG_VIDC_VIDEO_SYNC_FRAME_DECODE,
@@ -213,14 +205,13 @@
.step = 0,
.menu_skip_mask = 0,
.qmenu = NULL,
- .cluster = 0,
},
{
.id = V4L2_CID_MPEG_VIDC_VIDEO_EXTRADATA,
.name = "Extradata Type",
.type = V4L2_CTRL_TYPE_MENU,
.minimum = V4L2_MPEG_VIDC_EXTRADATA_NONE,
- .maximum = V4L2_MPEG_VIDC_EXTRADATA_FRAME_BITS_INFO,
+ .maximum = V4L2_MPEG_VIDC_EXTRADATA_STREAM_USERDATA,
.default_value = V4L2_MPEG_VIDC_EXTRADATA_NONE,
.menu_skip_mask = ~(
(1 << V4L2_MPEG_VIDC_EXTRADATA_NONE) |
@@ -233,8 +224,6 @@
(1 << V4L2_MPEG_VIDC_EXTRADATA_FRAME_RATE) |
(1 << V4L2_MPEG_VIDC_EXTRADATA_PANSCAN_WINDOW) |
(1 << V4L2_MPEG_VIDC_EXTRADATA_RECOVERY_POINT_SEI) |
- (1 << V4L2_MPEG_VIDC_EXTRADATA_CLOSED_CAPTION_UD) |
- (1 << V4L2_MPEG_VIDC_EXTRADATA_AFD_UD) |
(1 << V4L2_MPEG_VIDC_EXTRADATA_MULTISLICE_INFO) |
(1 << V4L2_MPEG_VIDC_EXTRADATA_NUM_CONCEALED_MB) |
(1 << V4L2_MPEG_VIDC_EXTRADATA_METADATA_FILLER) |
@@ -243,7 +232,8 @@
(1 << V4L2_MPEG_VIDC_INDEX_EXTRADATA_ASPECT_RATIO) |
(1 << V4L2_MPEG_VIDC_EXTRADATA_MPEG2_SEQDISP) |
(1 << V4L2_MPEG_VIDC_EXTRADATA_FRAME_QP) |
- (1 << V4L2_MPEG_VIDC_EXTRADATA_FRAME_BITS_INFO)
+ (1 << V4L2_MPEG_VIDC_EXTRADATA_FRAME_BITS_INFO) |
+ (1 << V4L2_MPEG_VIDC_EXTRADATA_STREAM_USERDATA)
),
.qmenu = mpeg_video_vidc_extradata,
.step = 0,
@@ -275,7 +265,6 @@
),
.qmenu = mpeg_vidc_video_alloc_mode_type,
.step = 0,
- .cluster = 0,
},
{
.id = V4L2_CID_MPEG_VIDC_VIDEO_ALLOC_MODE_OUTPUT,
@@ -291,7 +280,6 @@
),
.qmenu = mpeg_vidc_video_alloc_mode_type,
.step = 0,
- .cluster = 0,
},
{
.id = V4L2_CID_MPEG_VIDC_VIDEO_FRAME_ASSEMBLY,
@@ -303,7 +291,6 @@
.step = 1,
.menu_skip_mask = 0,
.qmenu = NULL,
- .cluster = 0,
},
{
.id = V4L2_CID_MPEG_VIDC_VIDEO_STREAM_OUTPUT_MODE,
@@ -319,7 +306,6 @@
.menu_skip_mask = 0,
.step = 1,
.qmenu = NULL,
- .cluster = 0,
},
{
.id = V4L2_CID_MPEG_VIDC_VIDEO_CONCEAL_COLOR,
@@ -329,7 +315,6 @@
.maximum = 0xffffff,
.default_value = DEFAULT_VIDEO_CONCEAL_COLOR_BLACK,
.step = 1,
- .cluster = 0,
},
};
@@ -679,7 +664,7 @@
rc = vb2_reqbufs(&q->vb2_bufq, b);
mutex_unlock(&q->lock);
if (rc)
- dprintk(VIDC_ERR, "Failed to get reqbufs, %d\n", rc);
+ dprintk(VIDC_DBG, "Failed to get reqbufs, %d\n", rc);
return rc;
}
@@ -816,6 +801,26 @@
f->fmt.pix_mp.plane_fmt[0].reserved[0] =
(__u16)inst->prop.height[CAPTURE_PORT];
}
+
+ if (msm_comm_get_stream_output_mode(inst) ==
+ HAL_VIDEO_DECODER_SECONDARY) {
+ if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ f->fmt.pix_mp.height =
+ inst->prop.height[CAPTURE_PORT];
+ f->fmt.pix_mp.width =
+ inst->prop.width[CAPTURE_PORT];
+ } else if (f->type ==
+ V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ f->fmt.pix_mp.height =
+ inst->prop.height[OUTPUT_PORT];
+ f->fmt.pix_mp.width =
+ inst->prop.width[OUTPUT_PORT];
+ f->fmt.pix_mp.plane_fmt[0].bytesperline =
+ (__u16)inst->prop.width[OUTPUT_PORT];
+ f->fmt.pix_mp.plane_fmt[0].reserved[0] =
+ (__u16)inst->prop.height[OUTPUT_PORT];
+ }
+ }
} else {
dprintk(VIDC_ERR,
"Buf type not recognized, type = %d\n",
@@ -865,9 +870,8 @@
dprintk(VIDC_PROF, "reported fps changed for %p: %d->%d\n",
inst, inst->prop.fps, fps);
inst->prop.fps = fps;
- mutex_lock(&inst->core->sync_lock);
+
msm_comm_scale_clocks_and_bus(inst);
- mutex_unlock(&inst->core->sync_lock);
}
exit:
return rc;
@@ -1185,64 +1189,6 @@
return rc;
}
-static int msm_vdec_queue_output_buffers(struct msm_vidc_inst *inst)
-{
- struct internal_buf *binfo;
- struct hfi_device *hdev;
- struct msm_smem *handle;
- struct vidc_frame_data frame_data = {0};
- struct hal_buffer_requirements *output_buf, *extradata_buf;
- int rc = 0;
- hdev = inst->core->device;
-
- output_buf = get_buff_req_buffer(inst, HAL_BUFFER_OUTPUT);
- if (!output_buf) {
- dprintk(VIDC_DBG,
- "This output buffer not required, buffer_type: %x\n",
- HAL_BUFFER_OUTPUT);
- return 0;
- }
- dprintk(VIDC_DBG,
- "output: num = %d, size = %d\n",
- output_buf->buffer_count_actual,
- output_buf->buffer_size);
-
- extradata_buf = get_buff_req_buffer(inst, HAL_BUFFER_EXTRADATA_OUTPUT);
- if (!extradata_buf) {
- dprintk(VIDC_DBG,
- "This extradata buffer not required, buffer_type: %x\n",
- HAL_BUFFER_EXTRADATA_OUTPUT);
- return 0;
- }
-
- hdev = inst->core->device;
-
- mutex_lock(&inst->lock);
- if (!list_empty(&inst->outputbufs)) {
- list_for_each_entry(binfo, &inst->outputbufs, list) {
- if (!binfo) {
- dprintk(VIDC_ERR, "Invalid parameter\n");
- mutex_unlock(&inst->lock);
- return -EINVAL;
- }
- handle = binfo->handle;
- frame_data.alloc_len = output_buf->buffer_size;
- frame_data.filled_len = 0;
- frame_data.offset = 0;
- frame_data.device_addr = handle->device_addr;
- frame_data.flags = 0;
- frame_data.extradata_addr = handle->device_addr +
- output_buf->buffer_size;
- frame_data.buffer_type = HAL_BUFFER_OUTPUT;
- rc = call_hfi_op(hdev, session_ftb,
- (void *) inst->session, &frame_data);
- binfo->buffer_ownership = FIRMWARE;
- }
- }
- mutex_unlock(&inst->lock);
- return 0;
-}
-
static inline int start_streaming(struct msm_vidc_inst *inst)
{
int rc = 0;
@@ -1281,9 +1227,8 @@
goto fail_start;
}
}
- mutex_lock(&inst->core->sync_lock);
+
msm_comm_scale_clocks_and_bus(inst);
- mutex_unlock(&inst->core->sync_lock);
rc = msm_comm_try_state(inst, MSM_VIDC_START_DONE);
if (rc) {
@@ -1293,7 +1238,7 @@
}
if (msm_comm_get_stream_output_mode(inst) ==
HAL_VIDEO_DECODER_SECONDARY) {
- rc = msm_vdec_queue_output_buffers(inst);
+ rc = msm_comm_queue_output_buffers(inst);
if (rc) {
dprintk(VIDC_ERR,
"Failed to queue output buffers: %d\n", rc);
@@ -1390,9 +1335,7 @@
break;
}
- mutex_lock(&inst->core->sync_lock);
msm_comm_scale_clocks_and_bus(inst);
- mutex_unlock(&inst->core->sync_lock);
if (rc)
dprintk(VIDC_ERR,
@@ -1831,21 +1774,18 @@
return v4l2_g_ctrl(&inst->ctrl_handler, ctrl);
}
-static struct v4l2_ctrl **get_cluster(int type, int *size)
+static struct v4l2_ctrl **get_super_cluster(struct msm_vidc_inst *inst,
+ int *size)
{
int c = 0, sz = 0;
struct v4l2_ctrl **cluster = kmalloc(sizeof(struct v4l2_ctrl *) *
NUM_CTRLS, GFP_KERNEL);
- if (type <= 0 || !size || !cluster)
+ if (!size || !cluster || !inst)
return NULL;
- for (c = 0; c < NUM_CTRLS; c++) {
- if (msm_vdec_ctrls[c].cluster & type) {
- cluster[sz] = msm_vdec_ctrls[c].priv;
- ++sz;
- }
- }
+ for (c = 0; c < NUM_CTRLS; c++)
+ cluster[sz++] = inst->ctrls[c];
*size = sz;
return cluster;
@@ -1856,6 +1796,19 @@
int idx = 0;
struct v4l2_ctrl_config ctrl_cfg = {0};
int ret_val = 0;
+ int cluster_size = 0;
+
+ if (!inst) {
+ dprintk(VIDC_ERR, "%s - invalid input\n", __func__);
+ return -EINVAL;
+ }
+
+ inst->ctrls = kzalloc(sizeof(struct v4l2_ctrl *) * NUM_CTRLS,
+ GFP_KERNEL);
+ if (!inst->ctrls) {
+ dprintk(VIDC_ERR, "%s - failed to allocate ctrl\n", __func__);
+ return -ENOMEM;
+ }
ret_val = v4l2_ctrl_handler_init(&inst->ctrl_handler, NUM_CTRLS);
@@ -1909,7 +1862,7 @@
}
- msm_vdec_ctrls[idx].priv = ctrl;
+ inst->ctrls[idx] = ctrl;
}
ret_val = inst->ctrl_handler.error;
if (ret_val)
@@ -1917,41 +1870,29 @@
"Error adding ctrls to ctrl handle, %d\n",
inst->ctrl_handler.error);
- /* Construct clusters */
- for (idx = 1; idx < MSM_VDEC_CTRL_CLUSTER_MAX; ++idx) {
- struct msm_vidc_ctrl_cluster *temp = NULL;
- struct v4l2_ctrl **cluster = NULL;
- int cluster_size = 0;
-
- cluster = get_cluster(idx, &cluster_size);
- if (!cluster || !cluster_size) {
- dprintk(VIDC_WARN, "Failed to setup cluster of type %d",
- idx);
- continue;
- }
-
- v4l2_ctrl_cluster(cluster_size, cluster);
-
- temp = kzalloc(sizeof(*temp), GFP_KERNEL);
- if (!temp) {
- ret_val = -ENOMEM;
- break;
- }
-
- temp->cluster = cluster;
- INIT_LIST_HEAD(&temp->list);
- list_add_tail(&temp->list, &inst->ctrl_clusters);
+ /* Construct a super cluster of all controls */
+ inst->cluster = get_super_cluster(inst, &cluster_size);
+ if (!inst->cluster || !cluster_size) {
+ dprintk(VIDC_WARN,
+ "Failed to setup super cluster\n");
+ return -EINVAL;
}
+
+ v4l2_ctrl_cluster(cluster_size, inst->cluster);
+
return ret_val;
}
int msm_vdec_ctrl_deinit(struct msm_vidc_inst *inst)
{
- struct msm_vidc_ctrl_cluster *curr, *next;
- list_for_each_entry_safe(curr, next, &inst->ctrl_clusters, list) {
- kfree(curr->cluster);
- kfree(curr);
+ if (!inst) {
+ dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
+ return -EINVAL;
}
+
+ kfree(inst->ctrls);
+ kfree(inst->cluster);
v4l2_ctrl_handler_free(&inst->ctrl_handler);
+
return 0;
}
diff --git a/drivers/media/platform/msm/vidc/msm_venc.c b/drivers/media/platform/msm/vidc/msm_venc.c
index 18432dd..d9d65a7 100644
--- a/drivers/media/platform/msm/vidc/msm_venc.c
+++ b/drivers/media/platform/msm/vidc/msm_venc.c
@@ -162,7 +162,6 @@
.step = 1,
.menu_skip_mask = 0,
.qmenu = NULL,
- .cluster = 0,
},
{
.id = V4L2_CID_MPEG_VIDEO_H264_I_PERIOD,
@@ -174,7 +173,6 @@
.step = 1,
.menu_skip_mask = 0,
.qmenu = NULL,
- .cluster = MSM_VENC_CTRL_CLUSTER_INTRA_PERIOD,
},
{
.id = V4L2_CID_MPEG_VIDC_VIDEO_NUM_P_FRAMES,
@@ -186,7 +184,6 @@
.step = 1,
.menu_skip_mask = 0,
.qmenu = NULL,
- .cluster = MSM_VENC_CTRL_CLUSTER_INTRA_PERIOD,
},
{
.id = V4L2_CID_MPEG_VIDC_VIDEO_NUM_B_FRAMES,
@@ -198,7 +195,6 @@
.step = 1,
.menu_skip_mask = 0,
.qmenu = NULL,
- .cluster = MSM_VENC_CTRL_CLUSTER_INTRA_PERIOD,
},
{
.id = V4L2_CID_MPEG_VIDC_VIDEO_REQUEST_IFRAME,
@@ -210,7 +206,6 @@
.step = 0,
.menu_skip_mask = 0,
.qmenu = NULL,
- .cluster = 0,
},
{
.id = V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL,
@@ -228,8 +223,6 @@
(1 << V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_CBR_CFR)
),
.qmenu = mpeg_video_rate_control,
- .cluster = MSM_VENC_CTRL_CLUSTER_BITRATE |
- MSM_VENC_CTRL_CLUSTER_TIMING,
},
{
.id = V4L2_CID_MPEG_VIDEO_BITRATE_MODE,
@@ -244,7 +237,6 @@
(1 << V4L2_MPEG_VIDEO_BITRATE_MODE_CBR)
),
.qmenu = NULL,
- .cluster = MSM_VENC_CTRL_CLUSTER_BITRATE,
},
{
.id = V4L2_CID_MPEG_VIDEO_BITRATE,
@@ -256,7 +248,6 @@
.step = BIT_RATE_STEP,
.menu_skip_mask = 0,
.qmenu = NULL,
- .cluster = MSM_VENC_CTRL_CLUSTER_BITRATE,
},
{
.id = V4L2_CID_MPEG_VIDEO_BITRATE_PEAK,
@@ -268,7 +259,6 @@
.step = BIT_RATE_STEP,
.menu_skip_mask = 0,
.qmenu = NULL,
- .cluster = MSM_VENC_CTRL_CLUSTER_BITRATE,
},
{
.id = V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE,
@@ -282,7 +272,6 @@
(1 << V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC) |
(1 << V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC)
),
- .cluster = MSM_VENC_CTRL_CLUSTER_H264_ENTROPY,
},
{
.id = V4L2_CID_MPEG_VIDC_VIDEO_H264_CABAC_MODEL,
@@ -298,7 +287,6 @@
(1 << V4L2_CID_MPEG_VIDC_VIDEO_H264_CABAC_MODEL_2)
),
.qmenu = h264_video_entropy_cabac_model,
- .cluster = MSM_VENC_CTRL_CLUSTER_H264_ENTROPY,
},
{
.id = V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE,
@@ -309,7 +297,6 @@
.default_value = V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE,
.step = 1,
.menu_skip_mask = 0,
- .cluster = MSM_VENC_CTRL_CLUSTER_MPEG_PROFILE_LEVEL,
},
{
.id = V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL,
@@ -320,7 +307,6 @@
.default_value = V4L2_MPEG_VIDEO_MPEG4_LEVEL_0,
.step = 1,
.menu_skip_mask = 0,
- .cluster = MSM_VENC_CTRL_CLUSTER_MPEG_PROFILE_LEVEL,
},
{
.id = V4L2_CID_MPEG_VIDEO_H264_PROFILE,
@@ -331,7 +317,6 @@
.default_value = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE,
.step = 1,
.menu_skip_mask = 0,
- .cluster = MSM_VENC_CTRL_CLUSTER_H264_PROFILE_LEVEL,
},
{
.id = V4L2_CID_MPEG_VIDEO_H264_LEVEL,
@@ -342,7 +327,6 @@
.default_value = V4L2_MPEG_VIDEO_H264_LEVEL_1_0,
.step = 0,
.menu_skip_mask = 0,
- .cluster = MSM_VENC_CTRL_CLUSTER_H264_PROFILE_LEVEL,
},
{
.id = V4L2_CID_MPEG_VIDC_VIDEO_H263_PROFILE,
@@ -363,7 +347,6 @@
(1 << V4L2_MPEG_VIDC_VIDEO_H263_PROFILE_HIGHLATENCY)
),
.qmenu = h263_profile,
- .cluster = MSM_VENC_CTRL_CLUSTER_H263_PROFILE_LEVEL,
},
{
.id = V4L2_CID_MPEG_VIDC_VIDEO_H263_LEVEL,
@@ -382,7 +365,6 @@
(1 << V4L2_MPEG_VIDC_VIDEO_H263_LEVEL_7_0)
),
.qmenu = h263_level,
- .cluster = MSM_VENC_CTRL_CLUSTER_H263_PROFILE_LEVEL,
},
{
.id = V4L2_CID_MPEG_VIDC_VIDEO_VP8_PROFILE_LEVEL,
@@ -397,7 +379,6 @@
(1 << V4L2_MPEG_VIDC_VIDEO_VP8_VERSION_1)
),
.qmenu = vp8_profile_level,
- .cluster = MSM_VENC_CTRL_CLUSTER_VP8_PROFILE_LEVEL,
},
{
.id = V4L2_CID_MPEG_VIDC_VIDEO_ROTATION,
@@ -414,7 +395,6 @@
(1 << V4L2_CID_MPEG_VIDC_VIDEO_ROTATION_270)
),
.qmenu = mpeg_video_rotation,
- .cluster = MSM_VENC_CTRL_CLUSTER_DEINTERLACE,
},
{
.id = V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP,
@@ -426,7 +406,6 @@
.step = 1,
.menu_skip_mask = 0,
.qmenu = NULL,
- .cluster = MSM_VENC_CTRL_CLUSTER_QP,
},
{
.id = V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP,
@@ -438,7 +417,6 @@
.step = 1,
.menu_skip_mask = 0,
.qmenu = NULL,
- .cluster = MSM_VENC_CTRL_CLUSTER_QP,
},
{
.id = V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP,
@@ -450,7 +428,6 @@
.step = 1,
.menu_skip_mask = 0,
.qmenu = NULL,
- .cluster = MSM_VENC_CTRL_CLUSTER_QP,
},
{
.id = V4L2_CID_MPEG_VIDEO_H264_MIN_QP,
@@ -462,7 +439,6 @@
.step = 1,
.menu_skip_mask = 0,
.qmenu = NULL,
- .cluster = MSM_VENC_CTRL_CLUSTER_QP,
},
{
.id = V4L2_CID_MPEG_VIDEO_H264_MAX_QP,
@@ -474,7 +450,6 @@
.step = 1,
.menu_skip_mask = 0,
.qmenu = NULL,
- .cluster = MSM_VENC_CTRL_CLUSTER_QP,
},
{
.id = V4L2_CID_MPEG_VIDC_VIDEO_VP8_MIN_QP,
@@ -484,7 +459,6 @@
.maximum = 128,
.default_value = 1,
.step = 1,
- .cluster = MSM_VENC_CTRL_CLUSTER_QP,
},
{
.id = V4L2_CID_MPEG_VIDC_VIDEO_VP8_MAX_QP,
@@ -494,7 +468,6 @@
.maximum = 128,
.default_value = 128,
.step = 1,
- .cluster = MSM_VENC_CTRL_CLUSTER_QP,
},
{
.id = V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE,
@@ -510,7 +483,6 @@
(1 << V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES) |
(1 << V4L2_MPEG_VIDEO_MULTI_SLICE_GOB)
),
- .cluster = MSM_VENC_CTRL_CLUSTER_SLICING,
},
{
.id = V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES,
@@ -522,7 +494,6 @@
.step = 1,
.menu_skip_mask = 0,
.qmenu = NULL,
- .cluster = MSM_VENC_CTRL_CLUSTER_SLICING,
},
{
.id = V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB,
@@ -534,7 +505,6 @@
.step = 1,
.menu_skip_mask = 0,
.qmenu = NULL,
- .cluster = MSM_VENC_CTRL_CLUSTER_SLICING,
},
{
.id = V4L2_CID_MPEG_VIDEO_MULTI_SLICE_GOB,
@@ -546,7 +516,6 @@
.step = 1,
.menu_skip_mask = 0,
.qmenu = NULL,
- .cluster = MSM_VENC_CTRL_CLUSTER_SLICING,
},
{
.id = V4L2_CID_MPEG_VIDEO_MULTI_SLICE_DELIVERY_MODE,
@@ -558,7 +527,6 @@
.step = 1,
.menu_skip_mask = 0,
.qmenu = NULL,
- .cluster = MSM_VENC_CTRL_CLUSTER_SLICING,
},
{
.id = V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_MODE,
@@ -576,7 +544,6 @@
(1 << V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_RANDOM)
),
.qmenu = intra_refresh_modes,
- .cluster = MSM_VENC_CTRL_CLUSTER_INTRA_REFRESH,
},
{
.id = V4L2_CID_MPEG_VIDC_VIDEO_AIR_MBS,
@@ -588,7 +555,6 @@
.step = 1,
.menu_skip_mask = 0,
.qmenu = NULL,
- .cluster = MSM_VENC_CTRL_CLUSTER_INTRA_REFRESH,
},
{
.id = V4L2_CID_MPEG_VIDC_VIDEO_AIR_REF,
@@ -600,7 +566,6 @@
.step = 1,
.menu_skip_mask = 0,
.qmenu = NULL,
- .cluster = MSM_VENC_CTRL_CLUSTER_INTRA_REFRESH,
},
{
.id = V4L2_CID_MPEG_VIDC_VIDEO_CIR_MBS,
@@ -612,7 +577,6 @@
.step = 1,
.menu_skip_mask = 0,
.qmenu = NULL,
- .cluster = MSM_VENC_CTRL_CLUSTER_INTRA_REFRESH,
},
{
.id = V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA,
@@ -624,7 +588,6 @@
.step = 1,
.menu_skip_mask = 0,
.qmenu = NULL,
- .cluster = 0,
},
{
.id = V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA,
@@ -636,7 +599,6 @@
.step = 1,
.menu_skip_mask = 0,
.qmenu = NULL,
- .cluster = 0,
},
{
.id = V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE,
@@ -651,7 +613,6 @@
(1 << V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED) |
(1 << L_MODE)
),
- .cluster = 0,
},
{
.id = V4L2_CID_MPEG_VIDEO_HEADER_MODE,
@@ -666,7 +627,6 @@
(1 << V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE) |
(1 << V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_I_FRAME)
),
- .cluster = 0,
},
{
.id = V4L2_CID_MPEG_VIDC_VIDEO_SECURE,
@@ -678,7 +638,6 @@
.step = 0,
.menu_skip_mask = 0,
.qmenu = NULL,
- .cluster = 0,
},
{
.id = V4L2_CID_MPEG_VIDC_VIDEO_EXTRADATA,
@@ -698,8 +657,6 @@
(1 << V4L2_MPEG_VIDC_EXTRADATA_FRAME_RATE) |
(1 << V4L2_MPEG_VIDC_EXTRADATA_PANSCAN_WINDOW) |
(1 << V4L2_MPEG_VIDC_EXTRADATA_RECOVERY_POINT_SEI) |
- (1 << V4L2_MPEG_VIDC_EXTRADATA_CLOSED_CAPTION_UD) |
- (1 << V4L2_MPEG_VIDC_EXTRADATA_AFD_UD) |
(1 << V4L2_MPEG_VIDC_EXTRADATA_MULTISLICE_INFO) |
(1 << V4L2_MPEG_VIDC_EXTRADATA_NUM_CONCEALED_MB) |
(1 << V4L2_MPEG_VIDC_EXTRADATA_METADATA_FILLER) |
@@ -720,7 +677,6 @@
.maximum = V4L2_MPEG_VIDC_VIDEO_H264_VUI_TIMING_INFO_ENABLED,
.default_value =
V4L2_MPEG_VIDC_VIDEO_H264_VUI_TIMING_INFO_DISABLED,
- .cluster = MSM_VENC_CTRL_CLUSTER_TIMING,
},
{
.id = V4L2_CID_MPEG_VIDC_VIDEO_H264_AU_DELIMITER,
@@ -754,7 +710,6 @@
.step = 1,
.menu_skip_mask = 0,
.qmenu = NULL,
- .cluster = MSM_VENC_CTRL_CLUSTER_INTRA_REFRESH,
},
{
.id = V4L2_CID_MPEG_VIDC_VIDEO_DEINTERLACE,
@@ -764,7 +719,6 @@
.maximum = V4L2_CID_MPEG_VIDC_VIDEO_DEINTERLACE_ENABLED,
.default_value = V4L2_CID_MPEG_VIDC_VIDEO_DEINTERLACE_DISABLED,
.step = 1,
- .cluster = MSM_VENC_CTRL_CLUSTER_DEINTERLACE,
},
{
.id = V4L2_CID_MPEG_VIDC_VIDEO_USELTRFRAME,
@@ -775,7 +729,6 @@
.default_value = 0,
.step = 1,
.qmenu = NULL,
- .cluster = 0,
},
{
.id = V4L2_CID_MPEG_VIDC_VIDEO_LTRCOUNT,
@@ -786,7 +739,6 @@
.default_value = 0,
.step = 1,
.qmenu = NULL,
- .cluster = MSM_VENC_CTRL_CLUSTER_USE_LTRFRAME,
},
{
.id = V4L2_CID_MPEG_VIDC_VIDEO_LTRMODE,
@@ -797,7 +749,6 @@
.default_value = V4L2_MPEG_VIDC_VIDEO_LTR_MODE_DISABLE,
.step = 1,
.qmenu = NULL,
- .cluster = MSM_VENC_CTRL_CLUSTER_USE_LTRFRAME,
},
{
.id = V4L2_CID_MPEG_VIDC_VIDEO_MARKLTRFRAME,
@@ -808,7 +759,6 @@
.default_value = 0,
.step = 1,
.qmenu = NULL,
- .cluster = 0,
},
{
.id = V4L2_CID_MPEG_VIDC_VIDEO_HIER_P_NUM_LAYERS,
@@ -819,7 +769,45 @@
.default_value = 0,
.step = 1,
.qmenu = NULL,
- .cluster = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDC_VIDEO_ENABLE_INITIAL_QP,
+ .name = "Enable setting initial QP",
+ .type = V4L2_CTRL_TYPE_BUTTON,
+ .minimum = 0,
+ .maximum = 0,
+ .default_value = 0,
+ .step = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDC_VIDEO_I_FRAME_QP,
+ .name = "Iframe initial QP",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 1,
+ .maximum = 51,
+ .default_value = 1,
+ .step = 1,
+ .qmenu = NULL,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDC_VIDEO_P_FRAME_QP,
+ .name = "Pframe initial QP",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 1,
+ .maximum = 51,
+ .default_value = 1,
+ .step = 1,
+ .qmenu = NULL,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDC_VIDEO_B_FRAME_QP,
+ .name = "Bframe initial QP",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 1,
+ .maximum = 51,
+ .default_value = 1,
+ .step = 1,
+ .qmenu = NULL,
}
};
@@ -919,9 +907,23 @@
}
hdev = inst->core->device;
+ rc = msm_comm_try_state(inst, MSM_VIDC_OPEN_DONE);
+ if (rc) {
+ dprintk(VIDC_ERR, "Failed to open instance\n");
+ return rc;
+ }
+
+ rc = msm_comm_try_get_bufreqs(inst);
+ if (rc) {
+ dprintk(VIDC_ERR,
+ "Failed to get buffer requirements: %d\n", rc);
+ return rc;
+ }
+
switch (q->type) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
*num_planes = 1;
+
buff_req = get_buff_req_buffer(inst, HAL_BUFFER_OUTPUT);
if (buff_req) {
*num_buffers = buff_req->buffer_count_actual =
@@ -949,9 +951,18 @@
inst->fmts[CAPTURE_PORT]->num_planes = *num_planes;
for (i = 0; i < *num_planes; i++) {
+ int extra_idx = EXTRADATA_IDX(*num_planes);
+
sizes[i] = inst->fmts[CAPTURE_PORT]->get_frame_size(
i, inst->prop.height[CAPTURE_PORT],
inst->prop.width[CAPTURE_PORT]);
+
+ if (extra_idx && i == extra_idx &&
+ extra_idx < VIDEO_MAX_PLANES) {
+ buff_req = get_buff_req_buffer(inst,
+ HAL_BUFFER_EXTRADATA_OUTPUT);
+ sizes[i] = buff_req->buffer_size;
+ }
}
property_id = HAL_PARAM_BUFFER_COUNT_ACTUAL;
@@ -961,26 +972,18 @@
property_id, &new_buf_count);
break;
case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
- rc = msm_comm_try_state(inst, MSM_VIDC_OPEN_DONE);
- if (rc) {
- dprintk(VIDC_ERR, "Failed to open instance\n");
- break;
- }
- rc = msm_comm_try_get_bufreqs(inst);
- if (rc) {
- dprintk(VIDC_ERR,
- "Failed to get buffer requirements: %d\n", rc);
- break;
- }
*num_planes = 1;
+
mutex_lock(&inst->lock);
*num_buffers = inst->buff_req.buffer[0].buffer_count_actual =
max(*num_buffers, inst->buff_req.buffer[0].
buffer_count_actual);
mutex_unlock(&inst->lock);
+
property_id = HAL_PARAM_BUFFER_COUNT_ACTUAL;
new_buf_count.buffer_type = HAL_BUFFER_INPUT;
new_buf_count.buffer_count_actual = *num_buffers;
+
rc = call_hfi_op(hdev, session_set_property, inst->session,
property_id, &new_buf_count);
dprintk(VIDC_DBG, "size = %d, alignment = %d, count = %d\n",
@@ -992,6 +995,7 @@
i, inst->prop.height[OUTPUT_PORT],
inst->prop.width[OUTPUT_PORT]);
}
+
break;
default:
dprintk(VIDC_ERR, "Invalid q type = %d\n", q->type);
@@ -1001,11 +1005,53 @@
return rc;
}
+static int msm_venc_enable_hier_p(struct msm_vidc_inst *inst)
+{
+ int num_enh_layers = 0;
+ u32 property_id = 0;
+ struct hfi_device *hdev = NULL;
+ int rc = 0;
+
+ if (!inst || !inst->core || !inst->core->device) {
+ dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
+ return -EINVAL;
+ }
+
+ if (inst->fmts[CAPTURE_PORT]->fourcc != V4L2_PIX_FMT_VP8)
+ return 0;
+
+ num_enh_layers = inst->capability.hier_p.max - 1;
+ if (!num_enh_layers)
+ return 0;
+
+ hdev = inst->core->device;
+ property_id = HAL_PARAM_VENC_HIER_P_MAX_ENH_LAYERS;
+
+ rc = call_hfi_op(hdev, session_set_property,
+ (void *)inst->session, property_id,
+ (void *)&num_enh_layers);
+ if (rc) {
+ dprintk(VIDC_ERR,
+ "%s: failed with error = %d\n", __func__, rc);
+ }
+ return rc;
+}
+
static inline int start_streaming(struct msm_vidc_inst *inst)
{
int rc = 0;
struct vb2_buf_entry *temp;
struct list_head *ptr, *next;
+
+ if (!inst || !inst->core || !inst->core->device) {
+ dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
+ return -EINVAL;
+ }
+
+ rc = msm_venc_enable_hier_p(inst);
+ if (rc)
+ return rc;
+
if (inst->capability.pixelprocess_capabilities &
HAL_VIDEO_ENCODER_SCALING_CAPABILITY)
rc = msm_comm_check_scaling_supported(inst);
@@ -1030,9 +1076,7 @@
goto fail_start;
}
- mutex_lock(&inst->core->sync_lock);
msm_comm_scale_clocks_and_bus(inst);
- mutex_unlock(&inst->core->sync_lock);
rc = msm_comm_try_state(inst, MSM_VIDC_START_DONE);
if (rc) {
@@ -1109,9 +1153,7 @@
break;
}
- mutex_lock(&inst->core->sync_lock);
msm_comm_scale_clocks_and_bus(inst);
- mutex_unlock(&inst->core->sync_lock);
if (rc)
dprintk(VIDC_ERR,
@@ -2102,7 +2144,7 @@
pdata = &markltr;
break;
case V4L2_CID_MPEG_VIDC_VIDEO_HIER_P_NUM_LAYERS:
- property_id = HAL_PARAM_VENC_HIER_P_NUM_FRAMES;
+ property_id = HAL_CONFIG_VENC_HIER_P_NUM_FRAMES;
hier_p_layers = ctrl->val;
if (hier_p_layers > (inst->capability.hier_p.max - 1)) {
dprintk(VIDC_ERR,
@@ -2132,15 +2174,6 @@
return rc;
}
-static struct v4l2_ctrl *get_cluster_from_id(int id)
-{
- int c;
- for (c = 0; c < ARRAY_SIZE(msm_venc_ctrls); ++c)
- if (msm_venc_ctrls[c].id == id)
- return (struct v4l2_ctrl *)msm_venc_ctrls[c].priv;
- return NULL;
-}
-
static int try_set_ext_ctrl(struct msm_vidc_inst *inst,
struct v4l2_ext_controls *ctrl)
{
@@ -2148,24 +2181,16 @@
struct v4l2_ext_control *control;
struct hfi_device *hdev;
struct hal_ltrmode ltrmode;
- struct v4l2_ctrl *cluster;
u32 property_id = 0;
void *pdata = NULL;
struct msm_vidc_core_capability *cap = NULL;
+ struct hal_initial_quantization quant;
if (!inst || !inst->core || !inst->core->device || !ctrl) {
dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
return -EINVAL;
}
- cluster = get_cluster_from_id(ctrl->controls[0].id);
-
- if (!cluster) {
- dprintk(VIDC_ERR, "Invalid Ctrl returned for id: %x\n",
- ctrl->controls[0].id);
- return -EINVAL;
- }
-
hdev = inst->core->device;
cap = &inst->capability;
@@ -2196,6 +2221,26 @@
property_id = HAL_PARAM_VENC_LTRMODE;
pdata = <rmode;
break;
+ case V4L2_CID_MPEG_VIDC_VIDEO_ENABLE_INITIAL_QP:
+ property_id = HAL_PARAM_VENC_ENABLE_INITIAL_QP;
+ quant.initqp_enable = control[i].value;
+ pdata = &quant;
+ break;
+ case V4L2_CID_MPEG_VIDC_VIDEO_I_FRAME_QP:
+ quant.qpi = control[i].value;
+ property_id = HAL_PARAM_VENC_ENABLE_INITIAL_QP;
+ pdata = &quant;
+ break;
+ case V4L2_CID_MPEG_VIDC_VIDEO_P_FRAME_QP:
+ quant.qpp = control[i].value;
+ property_id = HAL_PARAM_VENC_ENABLE_INITIAL_QP;
+ pdata = &quant;
+ break;
+ case V4L2_CID_MPEG_VIDC_VIDEO_B_FRAME_QP:
+ quant.qpb = control[i].value;
+ property_id = HAL_PARAM_VENC_ENABLE_INITIAL_QP;
+ pdata = &quant;
+ break;
default:
dprintk(VIDC_ERR, "Invalid id set: %d\n",
control[i].id);
@@ -2463,9 +2508,8 @@
dprintk(VIDC_WARN,
"Failed to set frame rate %d\n", rc);
}
- mutex_lock(&inst->core->sync_lock);
+
msm_comm_scale_clocks_and_bus(inst);
- mutex_unlock(&inst->core->sync_lock);
}
exit:
return rc;
@@ -2691,7 +2735,7 @@
rc = vb2_reqbufs(&q->vb2_bufq, b);
mutex_unlock(&q->lock);
if (rc)
- dprintk(VIDC_ERR, "Failed to get reqbufs, %d\n", rc);
+ dprintk(VIDC_DBG, "Failed to get reqbufs, %d\n", rc);
return rc;
}
@@ -2898,21 +2942,18 @@
return rc;
}
-static struct v4l2_ctrl **get_cluster(int type, int *size)
+static struct v4l2_ctrl **get_super_cluster(struct msm_vidc_inst *inst,
+ int *size)
{
int c = 0, sz = 0;
struct v4l2_ctrl **cluster = kmalloc(sizeof(struct v4l2_ctrl *) *
NUM_CTRLS, GFP_KERNEL);
- if (type <= 0 || !size || !cluster)
+ if (!size || !cluster || !inst)
return NULL;
- for (c = 0; c < NUM_CTRLS; c++) {
- if (msm_venc_ctrls[c].cluster & type) {
- cluster[sz] = msm_venc_ctrls[c].priv;
- ++sz;
- }
- }
+ for (c = 0; c < NUM_CTRLS; c++)
+ cluster[sz++] = inst->ctrls[c];
*size = sz;
return cluster;
@@ -2920,10 +2961,23 @@
int msm_venc_ctrl_init(struct msm_vidc_inst *inst)
{
-
int idx = 0;
struct v4l2_ctrl_config ctrl_cfg = {0};
int ret_val = 0;
+ int cluster_size = 0;
+
+ if (!inst) {
+ dprintk(VIDC_ERR, "%s - invalid input\n", __func__);
+ return -EINVAL;
+ }
+
+ inst->ctrls = kzalloc(sizeof(struct v4l2_ctrl *) * NUM_CTRLS,
+ GFP_KERNEL);
+ if (!inst->ctrls) {
+ dprintk(VIDC_ERR, "%s - failed to allocate ctrl\n", __func__);
+ return -ENOMEM;
+ }
+
ret_val = v4l2_ctrl_handler_init(&inst->ctrl_handler, NUM_CTRLS);
if (ret_val) {
dprintk(VIDC_ERR, "CTRL ERR: Control handler init failed, %d\n",
@@ -2973,7 +3027,8 @@
"Failed to get ctrl for: idx: %d, %d\n",
idx, msm_venc_ctrls[idx].id);
}
- msm_venc_ctrls[idx].priv = ctrl;
+
+ inst->ctrls[idx] = ctrl;
}
ret_val = inst->ctrl_handler.error;
if (ret_val)
@@ -2981,41 +3036,29 @@
"CTRL ERR: Error adding ctrls to ctrl handle, %d\n",
inst->ctrl_handler.error);
- /* Construct clusters */
- for (idx = 1; idx < MSM_VENC_CTRL_CLUSTER_MAX; ++idx) {
- struct msm_vidc_ctrl_cluster *temp = NULL;
- struct v4l2_ctrl **cluster = NULL;
- int cluster_size = 0;
-
- cluster = get_cluster(idx, &cluster_size);
- if (!cluster || !cluster_size) {
- dprintk(VIDC_WARN, "Failed to setup cluster of type %d",
- idx);
- continue;
- }
- v4l2_ctrl_cluster(cluster_size, cluster);
-
- temp = kzalloc(sizeof(*temp), GFP_KERNEL);
- if (!temp) {
- ret_val = -ENOMEM;
- break;
- }
-
- temp->cluster = cluster;
- INIT_LIST_HEAD(&temp->list);
- list_add_tail(&temp->list, &inst->ctrl_clusters);
+ /* Construct a super cluster of all controls */
+ inst->cluster = get_super_cluster(inst, &cluster_size);
+ if (!inst->cluster || !cluster_size) {
+ dprintk(VIDC_WARN,
+ "Failed to setup super cluster\n");
+ return -EINVAL;
}
+ v4l2_ctrl_cluster(cluster_size, inst->cluster);
+
return ret_val;
}
int msm_venc_ctrl_deinit(struct msm_vidc_inst *inst)
{
- struct msm_vidc_ctrl_cluster *curr, *next;
- list_for_each_entry_safe(curr, next, &inst->ctrl_clusters, list) {
- kfree(curr->cluster);
- kfree(curr);
+ if (!inst) {
+ dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
+ return -EINVAL;
}
+
+ kfree(inst->ctrls);
+ kfree(inst->cluster);
v4l2_ctrl_handler_free(&inst->ctrl_handler);
+
return 0;
}
diff --git a/drivers/media/platform/msm/vidc/msm_vidc.c b/drivers/media/platform/msm/vidc/msm_vidc.c
index 9dbecfb..cfc2eb8 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc.c
@@ -63,8 +63,14 @@
struct poll_table_struct *wait)
{
struct msm_vidc_inst *inst = instance;
- struct vb2_queue *outq = &inst->bufq[OUTPUT_PORT].vb2_bufq;
- struct vb2_queue *capq = &inst->bufq[CAPTURE_PORT].vb2_bufq;
+ struct vb2_queue *outq = NULL;
+ struct vb2_queue *capq = NULL;
+
+ if (!inst)
+ return -EINVAL;
+
+ outq = &inst->bufq[OUTPUT_PORT].vb2_bufq;
+ capq = &inst->bufq[CAPTURE_PORT].vb2_bufq;
poll_wait(filp, &inst->event_handler.wait, wait);
poll_wait(filp, &capq->done_wq, wait);
@@ -78,6 +84,9 @@
struct msm_vidc_inst *inst = instance;
int rc = 0;
+ if (!inst)
+ return -EINVAL;
+
wait_event(inst->kernel_event_queue, (rc = get_poll_flags(inst)));
return rc;
}
@@ -1217,7 +1226,6 @@
INIT_LIST_HEAD(&inst->pendingq);
INIT_LIST_HEAD(&inst->internalbufs);
INIT_LIST_HEAD(&inst->persistbufs);
- INIT_LIST_HEAD(&inst->ctrl_clusters);
INIT_LIST_HEAD(&inst->registered_bufs);
INIT_LIST_HEAD(&inst->outputbufs);
init_waitqueue_head(&inst->kernel_event_queue);
@@ -1268,9 +1276,9 @@
setup_event_queue(inst, &core->vdev[session_type].vdev);
- mutex_lock(&core->sync_lock);
+ mutex_lock(&core->lock);
list_add_tail(&inst->list, &core->instances);
- mutex_unlock(&core->sync_lock);
+ mutex_unlock(&core->lock);
return inst;
fail_init:
vb2_queue_release(&inst->bufq[OUTPUT_PORT].vb2_bufq);
@@ -1293,7 +1301,6 @@
{
struct list_head *ptr, *next;
struct vb2_buf_entry *entry;
- struct internal_buf *buf;
if (inst) {
mutex_lock(&inst->lock);
if (!list_empty(&inst->pendingq)) {
@@ -1305,37 +1312,28 @@
}
}
if (!list_empty(&inst->internalbufs)) {
- list_for_each_safe(ptr, next, &inst->internalbufs) {
- buf = list_entry(ptr, struct internal_buf,
- list);
- list_del(&buf->list);
- mutex_unlock(&inst->lock);
- msm_comm_smem_free(inst, buf->handle);
- kfree(buf);
- mutex_lock(&inst->lock);
- }
+ mutex_unlock(&inst->lock);
+ if (msm_comm_release_scratch_buffers(inst))
+ dprintk(VIDC_ERR,
+ "Failed to release scratch buffers\n");
+
+ mutex_lock(&inst->lock);
}
if (!list_empty(&inst->persistbufs)) {
- list_for_each_safe(ptr, next, &inst->persistbufs) {
- buf = list_entry(ptr, struct internal_buf,
- list);
- list_del(&buf->list);
- mutex_unlock(&inst->lock);
- msm_comm_smem_free(inst, buf->handle);
- kfree(buf);
- mutex_lock(&inst->lock);
- }
+ mutex_unlock(&inst->lock);
+ if (msm_comm_release_persist_buffers(inst))
+ dprintk(VIDC_ERR,
+ "Failed to release persist buffers\n");
+
+ mutex_lock(&inst->lock);
}
if (!list_empty(&inst->outputbufs)) {
- list_for_each_safe(ptr, next, &inst->outputbufs) {
- buf = list_entry(ptr, struct internal_buf,
- list);
- list_del(&buf->list);
- mutex_unlock(&inst->lock);
- msm_comm_smem_free(inst, buf->handle);
- kfree(buf);
- mutex_lock(&inst->lock);
- }
+ mutex_unlock(&inst->lock);
+ if (msm_comm_release_output_buffers(inst))
+ dprintk(VIDC_ERR,
+ "Failed to release output buffers\n");
+
+ mutex_lock(&inst->lock);
}
if (inst->extradata_handle) {
mutex_unlock(&inst->lock);
@@ -1375,13 +1373,13 @@
}
core = inst->core;
- mutex_lock(&core->sync_lock);
+ mutex_lock(&core->lock);
list_for_each_safe(ptr, next, &core->instances) {
temp = list_entry(ptr, struct msm_vidc_inst, list);
if (temp == inst)
list_del(&inst->list);
}
- mutex_unlock(&core->sync_lock);
+ mutex_unlock(&core->lock);
if (inst->session_type == MSM_VIDC_DECODER)
msm_vdec_ctrl_deinit(inst);
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.c b/drivers/media/platform/msm/vidc/msm_vidc_common.c
index 70114de..7429466 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_common.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_common.c
@@ -14,6 +14,7 @@
#include <linux/jiffies.h>
#include <linux/sched.h>
#include <linux/slab.h>
+#include <linux/workqueue.h>
#include <asm/div64.h>
#include <mach/subsystem_restart.h>
@@ -50,9 +51,10 @@
enum session_type type)
{
struct msm_vidc_inst *inst = NULL;
+ bool wants_turbo = false;
+ mutex_lock(&core->lock);
list_for_each_entry(inst, &core->instances, list) {
- bool wants_turbo = false;
mutex_lock(&inst->lock);
if (inst->session_type == type &&
@@ -63,10 +65,12 @@
mutex_unlock(&inst->lock);
if (wants_turbo)
- return true;
+ break;
}
- return false;
+ mutex_unlock(&core->lock);
+
+ return wants_turbo;
}
static bool is_thumbnail_session(struct msm_vidc_inst *inst)
@@ -116,6 +120,7 @@
dprintk(VIDC_ERR, "Invalid args: %p\n", core);
return -EINVAL;
}
+ mutex_lock(&core->lock);
list_for_each_entry(inst, &core->instances, list) {
mutex_lock(&inst->lock);
if (inst->session_type == type &&
@@ -127,6 +132,7 @@
}
mutex_unlock(&inst->lock);
}
+ mutex_unlock(&core->lock);
return num_mbs_per_sec;
}
@@ -390,8 +396,9 @@
&inst->completions[SESSION_MSG_INDEX(cmd)],
msecs_to_jiffies(msm_vidc_hw_rsp_timeout));
if (!rc) {
- dprintk(VIDC_ERR, "Wait interrupted or timeout: %d\n",
- SESSION_MSG_INDEX(cmd));
+ dprintk(VIDC_ERR,
+ "%s: Wait interrupted or timeout[%u]: %d\n",
+ __func__, (u32)inst->session, SESSION_MSG_INDEX(cmd));
msm_comm_recover_from_session_error(inst);
rc = -EIO;
} else {
@@ -439,6 +446,21 @@
mutex_unlock(&inst->lock);
}
+static void msm_comm_generate_max_clients_error(struct msm_vidc_inst *inst)
+{
+ if (!inst) {
+ dprintk(VIDC_ERR, "%s: invalid input parameters\n", __func__);
+ return;
+ }
+ mutex_lock(&inst->sync_lock);
+ inst->session = NULL;
+ inst->state = MSM_VIDC_CORE_INVALID;
+ msm_vidc_queue_v4l2_event(inst, V4L2_EVENT_MSM_VIDC_MAX_CLIENTS);
+ dprintk(VIDC_WARN,
+ "%s: Too many clients\n", __func__);
+ mutex_unlock(&inst->sync_lock);
+}
+
static void handle_session_init_done(enum command_response cmd, void *data)
{
struct msm_vidc_cb_cmd_done *response = data;
@@ -476,7 +498,10 @@
dprintk(VIDC_ERR,
"Session init response from FW : 0x%x",
response->status);
- msm_comm_generate_session_error(inst);
+ if (response->status == VIDC_ERR_MAX_CLIENTS)
+ msm_comm_generate_max_clients_error(inst);
+ else
+ msm_comm_generate_session_error(inst);
}
signal_session_msg_receipt(cmd, inst);
} else {
@@ -587,10 +612,16 @@
} else {
dprintk(VIDC_DBG,
"V4L2_EVENT_SEQ_CHANGED_SUFFICIENT\n");
- inst->prop.height[CAPTURE_PORT] = event_notify->height;
- inst->prop.width[CAPTURE_PORT] = event_notify->width;
- if (!msm_comm_get_stream_output_mode(inst) ==
+ if (msm_comm_get_stream_output_mode(inst) !=
HAL_VIDEO_DECODER_SECONDARY) {
+ dprintk(VIDC_DBG,
+ "event_notify->height = %d event_notify->width = %d\n",
+ event_notify->height,
+ event_notify->width);
+ inst->prop.height[CAPTURE_PORT] =
+ event_notify->height;
+ inst->prop.width[CAPTURE_PORT] =
+ event_notify->width;
inst->prop.height[OUTPUT_PORT] =
event_notify->height;
inst->prop.width[OUTPUT_PORT] =
@@ -711,8 +742,8 @@
return;
}
if (binfo->buffer_ownership != DRIVER) {
- dprintk(VIDC_ERR,
- "Failed : This buffer is with FW 0x%lx\n",
+ dprintk(VIDC_DBG,
+ "This buffer is with FW 0x%lx\n",
binfo->handle->device_addr);
return;
}
@@ -726,16 +757,71 @@
return;
}
+
+int msm_comm_queue_output_buffers(struct msm_vidc_inst *inst)
+{
+ struct internal_buf *binfo;
+ struct hfi_device *hdev;
+ struct msm_smem *handle;
+ struct vidc_frame_data frame_data = {0};
+ struct hal_buffer_requirements *output_buf;
+ int rc = 0;
+
+ if (!inst || !inst->core || !inst->core->device) {
+ dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
+ return -EINVAL;
+ }
+ hdev = inst->core->device;
+ output_buf = get_buff_req_buffer(inst, HAL_BUFFER_OUTPUT);
+ if (!output_buf) {
+ dprintk(VIDC_DBG,
+ "This output buffer not required, buffer_type: %x\n",
+ HAL_BUFFER_OUTPUT);
+ return 0;
+ }
+ dprintk(VIDC_DBG,
+ "output: num = %d, size = %d\n",
+ output_buf->buffer_count_actual,
+ output_buf->buffer_size);
+
+ list_for_each_entry(binfo, &inst->outputbufs, list) {
+ if (binfo->buffer_ownership != DRIVER)
+ continue;
+ handle = binfo->handle;
+ frame_data.alloc_len = output_buf->buffer_size;
+ frame_data.filled_len = 0;
+ frame_data.offset = 0;
+ frame_data.device_addr = handle->device_addr;
+ frame_data.flags = 0;
+ frame_data.extradata_addr = handle->device_addr +
+ output_buf->buffer_size;
+ frame_data.buffer_type = HAL_BUFFER_OUTPUT;
+ rc = call_hfi_op(hdev, session_ftb,
+ (void *) inst->session, &frame_data);
+ binfo->buffer_ownership = FIRMWARE;
+ }
+ return 0;
+}
+
static void handle_session_flush(enum command_response cmd, void *data)
{
struct msm_vidc_cb_cmd_done *response = data;
struct msm_vidc_inst *inst;
+ int rc;
if (response) {
inst = (struct msm_vidc_inst *)response->session_id;
if (msm_comm_get_stream_output_mode(inst) ==
HAL_VIDEO_DECODER_SECONDARY) {
mutex_lock(&inst->lock);
validate_output_buffers(inst);
+ if (!inst->in_reconfig) {
+ rc = msm_comm_queue_output_buffers(inst);
+ if (rc) {
+ dprintk(VIDC_ERR,
+ "Failed to queue output buffers: %d\n",
+ rc);
+ }
+ }
mutex_unlock(&inst->lock);
}
msm_vidc_queue_v4l2_event(inst, V4L2_EVENT_MSM_VIDC_FLUSH_DONE);
@@ -764,95 +850,127 @@
"Failed to get valid response for session error\n");
}
}
+
+struct sys_err_handler_data {
+ struct msm_vidc_core *core;
+ struct delayed_work work;
+};
+
+
+void hw_sys_error_handler(struct work_struct *work)
+{
+ struct msm_vidc_core *core = NULL;
+ struct hfi_device *hdev = NULL;
+ struct sys_err_handler_data *handler = NULL;
+ int rc = 0;
+
+ handler = container_of(work, struct sys_err_handler_data, work.work);
+ if (!handler || !handler->core || !handler->core->device) {
+ dprintk(VIDC_ERR, "%s - invalid work or core handle\n",
+ __func__);
+ goto exit;
+ }
+
+ core = handler->core;
+ hdev = core->device;
+
+ mutex_lock(&core->lock);
+ /*
+ * Restart the firmware to bring out of bad state.
+ */
+ if ((core->state == VIDC_CORE_INVALID) &&
+ hdev->resurrect_fw) {
+ rc = call_hfi_op(hdev, resurrect_fw,
+ hdev->hfi_device_data);
+ if (rc) {
+ dprintk(VIDC_ERR,
+ "%s - resurrect_fw failed: %d\n",
+ __func__, rc);
+ }
+ core->state = VIDC_CORE_LOADED;
+ } else {
+ dprintk(VIDC_DBG,
+ "fw unloaded after sys error, no need to resurrect\n");
+ }
+ mutex_unlock(&core->lock);
+
+exit:
+ /* free sys error handler, allocated in handle_sys_err */
+ kfree(handler);
+}
+
static void handle_sys_error(enum command_response cmd, void *data)
{
struct msm_vidc_cb_cmd_done *response = data;
- struct msm_vidc_inst *inst = NULL ;
struct msm_vidc_core *core = NULL;
+ struct sys_err_handler_data *handler = NULL;
struct hfi_device *hdev = NULL;
+ struct msm_vidc_inst *inst = NULL;
int rc = 0;
subsystem_crashed("venus");
- if (response) {
- core = get_vidc_core(response->device_id);
- dprintk(VIDC_WARN, "SYS_ERROR received for core %p\n", core);
- if (core) {
- mutex_lock(&core->lock);
- core->state = VIDC_CORE_INVALID;
- mutex_unlock(&core->lock);
- mutex_lock(&core->sync_lock);
- list_for_each_entry(inst, &core->instances,
- list) {
- mutex_lock(&inst->lock);
- inst->state = MSM_VIDC_CORE_INVALID;
- if (inst->core)
- hdev = inst->core->device;
- if (hdev && inst->session) {
- dprintk(VIDC_DBG,
- "cleaning up inst: 0x%p", inst);
- rc = call_hfi_op(hdev, session_clean,
- (void *) inst->session);
- if (rc)
- dprintk(VIDC_ERR,
- "Sess clean failed :%p",
- inst);
- }
- inst->session = NULL;
- mutex_unlock(&inst->lock);
- msm_vidc_queue_v4l2_event(inst,
- V4L2_EVENT_MSM_VIDC_SYS_ERROR);
- }
- mutex_unlock(&core->sync_lock);
- } else {
- dprintk(VIDC_ERR,
- "Got SYS_ERR but unable to identify core");
- }
- } else {
+ if (!response) {
dprintk(VIDC_ERR,
"Failed to get valid response for sys error\n");
- }
-}
-
-static void handle_sys_watchdog_timeout(enum command_response cmd, void *data)
-{
- struct msm_vidc_cb_cmd_done *response = data;
- struct msm_vidc_inst *inst;
- struct msm_vidc_core *core = NULL;
- struct hfi_device *hdev = NULL;
- int rc = 0;
- dprintk(VIDC_ERR, "Venus Subsystem crashed\n");
- core = get_vidc_core(response->device_id);
- if (!core) {
- dprintk(VIDC_ERR, "Wrong device_id received\n");
return;
}
- subsystem_crashed("venus");
+
+ core = get_vidc_core(response->device_id);
+ if (!core) {
+ dprintk(VIDC_ERR,
+ "Got SYS_ERR but unable to identify core\n");
+ return;
+ }
+
+ dprintk(VIDC_WARN, "SYS_ERROR %d received for core %p\n", cmd, core);
mutex_lock(&core->lock);
core->state = VIDC_CORE_INVALID;
- mutex_unlock(&core->lock);
- mutex_lock(&core->sync_lock);
- list_for_each_entry(inst, &core->instances, list) {
- if (inst) {
- msm_vidc_queue_v4l2_event(inst,
- V4L2_EVENT_MSM_VIDC_SYS_ERROR);
- mutex_lock(&inst->lock);
- inst->state = MSM_VIDC_CORE_INVALID;
- if (inst->core)
- hdev = inst->core->device;
- if (hdev && inst->session) {
- rc = call_hfi_op(hdev, session_clean,
- (void *) inst->session);
- if (rc)
- dprintk(VIDC_ERR,
- "Sess clean failed :%p",
- inst);
- }
- inst->session = NULL;
- mutex_unlock(&inst->lock);
+ /*
+ * 1. Delete each instance session from hfi list
+ * 2. Notify all clients about hardware error.
+ */
+ list_for_each_entry(inst, &core->instances,
+ list) {
+ mutex_lock(&inst->lock);
+ inst->state = MSM_VIDC_CORE_INVALID;
+ if (inst->core)
+ hdev = inst->core->device;
+ if (hdev && inst->session) {
+ dprintk(VIDC_DBG,
+ "cleaning up inst: 0x%p\n", inst);
+ rc = call_hfi_op(hdev, session_clean,
+ (void *) inst->session);
+ if (rc)
+ dprintk(VIDC_ERR,
+ "Sess clean failed :%p\n",
+ inst);
}
+ inst->session = NULL;
+ mutex_unlock(&inst->lock);
+ msm_vidc_queue_v4l2_event(inst,
+ V4L2_EVENT_MSM_VIDC_SYS_ERROR);
}
- mutex_unlock(&core->sync_lock);
+ mutex_unlock(&core->lock);
+
+
+ handler = kzalloc(sizeof(*handler), GFP_KERNEL);
+ if (!handler) {
+ dprintk(VIDC_ERR,
+ "%s - failed to allocate sys error handler\n",
+ __func__);
+ return;
+ }
+ handler->core = core;
+ INIT_DELAYED_WORK(&handler->work, hw_sys_error_handler);
+
+ /*
+ * Sleep for 5 sec to ensure venus has completed any
+ * pending cache operations. Without this sleep, we see
+ * device reset when firmware is unloaded after a sys
+ * error.
+ */
+ schedule_delayed_work(&handler->work, msecs_to_jiffies(5000));
}
static void handle_session_close(enum command_response cmd, void *data)
@@ -1196,6 +1314,8 @@
vb->v4l2_buf.flags |= V4L2_QCOM_BUF_DATA_CORRUPT;
if (fill_buf_done->flags1 & HAL_BUFFERFLAG_DROP_FRAME)
vb->v4l2_buf.flags |= V4L2_QCOM_BUF_DROP_FRAME;
+ if (fill_buf_done->flags1 & HAL_BUFFERFLAG_MBAFF)
+ vb->v4l2_buf.flags |= V4L2_MSM_BUF_FLAG_MBAFF;
switch (fill_buf_done->picture_type) {
case HAL_PICTURE_IDR:
vb->v4l2_buf.flags |= V4L2_QCOM_BUF_FLAG_IDRFRAME;
@@ -1357,7 +1477,7 @@
handle_seq_hdr_done(cmd, data);
break;
case SYS_WATCHDOG_TIMEOUT:
- handle_sys_watchdog_timeout(cmd, data);
+ handle_sys_error(cmd, data);
break;
case SYS_ERROR:
handle_sys_error(cmd, data);
@@ -1473,8 +1593,8 @@
&core->completions[SYS_MSG_INDEX(RELEASE_RESOURCE_DONE)],
msecs_to_jiffies(msm_vidc_hw_rsp_timeout));
if (!rc) {
- dprintk(VIDC_ERR, "Wait interrupted or timeout: %d\n",
- SYS_MSG_INDEX(RELEASE_RESOURCE_DONE));
+ dprintk(VIDC_ERR, "%s: Wait interrupted or timeout: %d\n",
+ __func__, SYS_MSG_INDEX(RELEASE_RESOURCE_DONE));
rc = -EIO;
}
release_ocmem_failed:
@@ -1485,7 +1605,7 @@
{
struct msm_vidc_core *core = inst->core;
int rc = 0;
- mutex_lock(&core->sync_lock);
+ mutex_lock(&core->lock);
if (core->state >= VIDC_CORE_INIT_DONE) {
dprintk(VIDC_INFO, "Video core: %d is already in state: %d\n",
core->id, core->state);
@@ -1496,21 +1616,19 @@
&core->completions[SYS_MSG_INDEX(SYS_INIT_DONE)],
msecs_to_jiffies(msm_vidc_hw_rsp_timeout));
if (!rc) {
- dprintk(VIDC_ERR, "Wait interrupted or timeout: %d\n",
- SYS_MSG_INDEX(SYS_INIT_DONE));
+ dprintk(VIDC_ERR, "%s: Wait interrupted or timeout: %d\n",
+ __func__, SYS_MSG_INDEX(SYS_INIT_DONE));
rc = -EIO;
goto exit;
} else {
- mutex_lock(&core->lock);
core->state = VIDC_CORE_INIT_DONE;
- mutex_unlock(&core->lock);
}
dprintk(VIDC_DBG, "SYS_INIT_DONE!!!\n");
core_already_inited:
change_inst_state(inst, MSM_VIDC_CORE_INIT_DONE);
rc = 0;
exit:
- mutex_unlock(&core->sync_lock);
+ mutex_unlock(&core->lock);
return rc;
}
@@ -1524,12 +1642,13 @@
return -EINVAL;
hdev = core->device;
- mutex_lock(&core->sync_lock);
+ mutex_lock(&core->lock);
if (core->state >= VIDC_CORE_INIT) {
dprintk(VIDC_INFO, "Video core: %d is already in state: %d\n",
core->id, core->state);
goto core_already_inited;
}
+ mutex_unlock(&core->lock);
rc = msm_comm_scale_bus(core, inst->session_type, DDR_MEM);
if (rc) {
@@ -1537,36 +1656,46 @@
goto fail_scale_bus;
}
- rc = call_hfi_op(hdev, load_fw, hdev->hfi_device_data);
- if (rc) {
- dprintk(VIDC_ERR, "Failed to load video firmware\n");
- goto fail_load_fw;
+ mutex_lock(&core->lock);
+ if (core->state < VIDC_CORE_LOADED) {
+ rc = call_hfi_op(hdev, load_fw, hdev->hfi_device_data);
+ if (rc) {
+ dprintk(VIDC_ERR, "Failed to load video firmware\n");
+ goto fail_load_fw;
+ }
+ core->state = VIDC_CORE_LOADED;
}
+ mutex_unlock(&core->lock);
+
rc = msm_comm_scale_clocks(core);
if (rc) {
dprintk(VIDC_ERR, "Failed to scale clocks: %d\n", rc);
goto fail_core_init;
}
- init_completion(&core->completions[SYS_MSG_INDEX(SYS_INIT_DONE)]);
- rc = call_hfi_op(hdev, core_init, hdev->hfi_device_data);
- if (rc) {
- dprintk(VIDC_ERR, "Failed to init core, id = %d\n", core->id);
- goto fail_core_init;
- }
mutex_lock(&core->lock);
- core->state = VIDC_CORE_INIT;
- mutex_unlock(&core->lock);
+ if (core->state == VIDC_CORE_LOADED) {
+ init_completion(&core->completions
+ [SYS_MSG_INDEX(SYS_INIT_DONE)]);
+ rc = call_hfi_op(hdev, core_init, hdev->hfi_device_data);
+ if (rc) {
+ dprintk(VIDC_ERR, "Failed to init core, id = %d\n",
+ core->id);
+ goto fail_core_init;
+ }
+ core->state = VIDC_CORE_INIT;
+ }
+
core_already_inited:
change_inst_state(inst, MSM_VIDC_CORE_INIT);
- mutex_unlock(&core->sync_lock);
+ mutex_unlock(&core->lock);
return rc;
fail_core_init:
call_hfi_op(hdev, unload_fw, hdev->hfi_device_data);
fail_load_fw:
msm_comm_unvote_buses(core, DDR_MEM);
fail_scale_bus:
- mutex_unlock(&core->sync_lock);
+ mutex_unlock(&core->lock);
return rc;
}
@@ -1584,39 +1713,49 @@
core = inst->core;
hdev = core->device;
- mutex_lock(&core->sync_lock);
+ mutex_lock(&core->lock);
if (core->state == VIDC_CORE_UNINIT) {
dprintk(VIDC_INFO, "Video core: %d is already in state: %d\n",
core->id, core->state);
goto core_already_uninited;
}
+ mutex_unlock(&core->lock);
+
msm_comm_scale_clocks_and_bus(inst);
+
+ mutex_lock(&core->lock);
if (list_empty(&core->instances)) {
- if (core->resources.has_ocmem) {
- if (inst->state != MSM_VIDC_CORE_INVALID)
- msm_comm_unset_ocmem(core);
- call_hfi_op(hdev, free_ocmem, hdev->hfi_device_data);
+ if (core->state > VIDC_CORE_INIT) {
+ if (core->resources.has_ocmem) {
+ if (inst->state != MSM_VIDC_CORE_INVALID)
+ msm_comm_unset_ocmem(core);
+ call_hfi_op(hdev, free_ocmem,
+ hdev->hfi_device_data);
+ }
+ dprintk(VIDC_DBG, "Calling vidc_hal_core_release\n");
+ rc = call_hfi_op(hdev, core_release,
+ hdev->hfi_device_data);
+ if (rc) {
+ dprintk(VIDC_ERR,
+ "Failed to release core, id = %d\n",
+ core->id);
+ goto exit;
+ }
}
- dprintk(VIDC_DBG, "Calling vidc_hal_core_release\n");
- rc = call_hfi_op(hdev, core_release, hdev->hfi_device_data);
- if (rc) {
- dprintk(VIDC_ERR, "Failed to release core, id = %d\n",
- core->id);
- goto exit;
- }
- mutex_lock(&core->lock);
+
core->state = VIDC_CORE_UNINIT;
- mutex_unlock(&core->lock);
+
call_hfi_op(hdev, unload_fw, hdev->hfi_device_data);
if (core->resources.has_ocmem)
msm_comm_unvote_buses(core, DDR_MEM|OCMEM_MEM);
else
msm_comm_unvote_buses(core, DDR_MEM);
}
+
core_already_uninited:
change_inst_state(inst, MSM_VIDC_CORE_UNINIT);
exit:
- mutex_unlock(&core->sync_lock);
+ mutex_unlock(&core->lock);
return rc;
}
@@ -1745,6 +1884,8 @@
struct msm_vidc_inst *temp;
dprintk(VIDC_ERR, "Running instances:\n");
dprintk(VIDC_ERR, "%4s|%4s|%4s|%4s\n", "type", "w", "h", "fps");
+
+ mutex_lock(&core->lock);
list_for_each_entry(temp, &core->instances, list) {
mutex_lock(&temp->lock);
if (temp->state >= MSM_VIDC_OPEN_DONE &&
@@ -1757,6 +1898,7 @@
}
mutex_unlock(&temp->lock);
}
+ mutex_unlock(&core->lock);
}
static int msm_vidc_load_resources(int flipped_state,
@@ -1772,17 +1914,21 @@
dprintk(VIDC_ERR, "%s invalid parameters", __func__);
return -EINVAL;
}
- if (inst->state == MSM_VIDC_CORE_INVALID ||
- inst->core->state == VIDC_CORE_INVALID) {
+
+ if (inst->core->state == VIDC_CORE_INVALID) {
dprintk(VIDC_ERR,
- "Core is in bad state can't do load res");
+ "Core is in bad state can't do load res\n");
return -EINVAL;
}
- mutex_lock(&inst->core->sync_lock);
+ if (inst->state == MSM_VIDC_CORE_INVALID) {
+ dprintk(VIDC_ERR,
+ "Instance is in invalid state can't do load res\n");
+ return -EINVAL;
+ }
+
num_mbs_per_sec = msm_comm_get_load(inst->core, MSM_VIDC_DECODER);
num_mbs_per_sec += msm_comm_get_load(inst->core, MSM_VIDC_ENCODER);
- mutex_unlock(&inst->core->sync_lock);
if (num_mbs_per_sec > inst->core->resources.max_load) {
dprintk(VIDC_ERR, "HW is overloaded, needed: %d max: %d\n",
@@ -1790,7 +1936,7 @@
msm_vidc_print_running_insts(inst->core);
inst->state = MSM_VIDC_CORE_INVALID;
msm_comm_recover_from_session_error(inst);
- return -ENOMEM;
+ return -EBUSY;
}
hdev = inst->core->device;
@@ -1806,16 +1952,14 @@
inst->prop.width[OUTPUT_PORT]);
ocmem_sz = get_ocmem_requirement(
height, width);
- mutex_lock(&inst->core->sync_lock);
rc = msm_comm_scale_bus(inst->core, inst->session_type,
OCMEM_MEM);
- mutex_unlock(&inst->core->sync_lock);
if (!rc) {
- mutex_lock(&inst->core->sync_lock);
+ mutex_lock(&inst->core->lock);
rc = call_hfi_op(hdev, alloc_ocmem,
hdev->hfi_device_data,
ocmem_sz);
- mutex_unlock(&inst->core->sync_lock);
+ mutex_unlock(&inst->core->lock);
if (rc) {
dprintk(VIDC_WARN,
"Failed to allocate OCMEM. Performance will be impacted\n");
@@ -2427,13 +2571,6 @@
mutex_unlock(&inst->sync_lock);
} else {
int64_t time_usec = timeval_to_ns(&vb->v4l2_buf.timestamp);
-
- rc = msm_vidc_check_session_supported(inst);
- if (rc) {
- dprintk(VIDC_ERR,
- "%s: session not supported\n", __func__);
- goto err_no_mem;
- }
do_div(time_usec, NSEC_PER_USEC);
memset(&frame_data, 0 , sizeof(struct vidc_frame_data));
frame_data.alloc_len = vb->v4l2_planes[0].length;
@@ -2577,7 +2714,8 @@
msecs_to_jiffies(msm_vidc_hw_rsp_timeout));
if (!rc) {
dprintk(VIDC_ERR,
- "Wait interrupted or timeout: %d\n",
+ "%s: Wait interrupted or timeout[%u]: %d\n",
+ __func__, (u32)inst->session,
SESSION_MSG_INDEX(SESSION_PROPERTY_INFO));
inst->state = MSM_VIDC_CORE_INVALID;
msm_comm_recover_from_session_error(inst);
@@ -3124,12 +3262,6 @@
case V4L2_MPEG_VIDC_EXTRADATA_RECOVERY_POINT_SEI:
ret = HAL_EXTRADATA_RECOVERY_POINT_SEI;
break;
- case V4L2_MPEG_VIDC_EXTRADATA_CLOSED_CAPTION_UD:
- ret = HAL_EXTRADATA_CLOSED_CAPTION_UD;
- break;
- case V4L2_MPEG_VIDC_EXTRADATA_AFD_UD:
- ret = HAL_EXTRADATA_AFD_UD;
- break;
case V4L2_MPEG_VIDC_EXTRADATA_MULTISLICE_INFO:
ret = HAL_EXTRADATA_MULTISLICE_INFO;
break;
@@ -3157,6 +3289,9 @@
case V4L2_MPEG_VIDC_EXTRADATA_METADATA_MBI:
ret = HAL_EXTRADATA_METADATA_MBI;
break;
+ case V4L2_MPEG_VIDC_EXTRADATA_STREAM_USERDATA:
+ ret = HAL_EXTRADATA_STREAM_USERDATA;
+ break;
default:
dprintk(VIDC_WARN, "Extradata not found: %d\n", index);
break;
@@ -3220,20 +3355,16 @@
int num_mbs_per_sec = 0;
if (inst->state == MSM_VIDC_OPEN_DONE) {
- mutex_lock(&inst->core->sync_lock);
num_mbs_per_sec = msm_comm_get_load(inst->core,
MSM_VIDC_DECODER);
num_mbs_per_sec += msm_comm_get_load(inst->core,
MSM_VIDC_ENCODER);
- mutex_unlock(&inst->core->sync_lock);
if (num_mbs_per_sec > inst->core->resources.max_load) {
dprintk(VIDC_ERR,
"H/w is overloaded. needed: %d max: %d\n",
num_mbs_per_sec,
inst->core->resources.max_load);
- mutex_lock(&inst->sync_lock);
msm_vidc_print_running_insts(inst->core);
- mutex_unlock(&inst->sync_lock);
return -EINVAL;
}
}
@@ -3331,19 +3462,25 @@
capability->height.min);
rc = -ENOTSUPP;
}
- if (!rc) {
- rc = call_hfi_op(hdev, capability_check,
- inst->fmts[OUTPUT_PORT]->fourcc,
+ if (msm_vp8_low_tier &&
+ inst->fmts[OUTPUT_PORT]->fourcc == V4L2_PIX_FMT_VP8) {
+ capability->width.max = DEFAULT_WIDTH;
+ capability->width.max = DEFAULT_HEIGHT;
+ }
+ if (!rc && (inst->prop.width[CAPTURE_PORT] >
+ capability->width.max)) {
+ dprintk(VIDC_ERR,
+ "Unsupported width = %u supported max width = %u\n",
inst->prop.width[CAPTURE_PORT],
- &capability->width.max,
- &capability->height.max);
+ capability->width.max);
+ rc = -ENOTSUPP;
}
if (!rc && (inst->prop.height[CAPTURE_PORT]
* inst->prop.width[CAPTURE_PORT] >
capability->width.max * capability->height.max)) {
dprintk(VIDC_ERR,
- "Unsupported WxH = (%u)x(%u), Max supported is - (%u)x(%u)",
+ "Unsupported WxH = (%u)x(%u), Max supported is - (%u)x(%u)\n",
inst->prop.width[CAPTURE_PORT],
inst->prop.height[CAPTURE_PORT],
capability->width.max, capability->height.max);
@@ -3354,7 +3491,11 @@
mutex_lock(&inst->sync_lock);
inst->state = MSM_VIDC_CORE_INVALID;
mutex_unlock(&inst->sync_lock);
- msm_vidc_queue_v4l2_event(inst, V4L2_EVENT_MSM_VIDC_SYS_ERROR);
+ msm_vidc_queue_v4l2_event(inst,
+ V4L2_EVENT_MSM_VIDC_HW_OVERLOAD);
+ dprintk(VIDC_WARN,
+ "%s: Hardware is overloaded\n", __func__);
+ wake_up(&inst->kernel_event_queue);
}
return rc;
}
@@ -3404,8 +3545,9 @@
&inst->completions[SESSION_MSG_INDEX(SESSION_ABORT_DONE)],
msecs_to_jiffies(msm_vidc_hw_rsp_timeout));
if (!rc) {
- dprintk(VIDC_ERR, "%s: Wait interrupted or timeout: %d\n",
- __func__, SESSION_MSG_INDEX(SESSION_ABORT_DONE));
+ dprintk(VIDC_ERR, "%s: Wait interrupted or timeout[%u]: %d\n",
+ __func__, (u32)inst->session,
+ SESSION_MSG_INDEX(SESSION_ABORT_DONE));
msm_comm_generate_sys_error(inst);
} else
change_inst_state(inst, MSM_VIDC_CLOSE_DONE);
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.h b/drivers/media/platform/msm/vidc/msm_vidc_common.h
index e2f7b61..da71424 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_common.h
+++ b/drivers/media/platform/msm/vidc/msm_vidc_common.h
@@ -32,11 +32,13 @@
int msm_comm_set_scratch_buffers(struct msm_vidc_inst *inst);
int msm_comm_set_persist_buffers(struct msm_vidc_inst *inst);
int msm_comm_set_output_buffers(struct msm_vidc_inst *inst);
+int msm_comm_queue_output_buffers(struct msm_vidc_inst *inst);
int msm_comm_qbuf(struct vb2_buffer *vb);
void msm_comm_scale_clocks_and_bus(struct msm_vidc_inst *inst);
int msm_comm_flush(struct msm_vidc_inst *inst, u32 flags);
int msm_comm_release_scratch_buffers(struct msm_vidc_inst *inst);
int msm_comm_release_persist_buffers(struct msm_vidc_inst *inst);
+int msm_comm_release_output_buffers(struct msm_vidc_inst *inst);
int msm_comm_force_cleanup(struct msm_vidc_inst *inst);
enum hal_extradata_id msm_comm_get_hal_extradata_index(
enum v4l2_mpeg_vidc_extradata index);
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_internal.h b/drivers/media/platform/msm/vidc/msm_vidc_internal.h
index 06181dd..1677e57 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_internal.h
+++ b/drivers/media/platform/msm/vidc/msm_vidc_internal.h
@@ -68,6 +68,7 @@
enum vidc_core_state {
VIDC_CORE_UNINIT = 0,
+ VIDC_CORE_LOADED,
VIDC_CORE_INIT,
VIDC_CORE_INIT_DONE,
VIDC_CORE_INVALID
@@ -198,7 +199,7 @@
struct msm_vidc_core {
struct list_head list;
- struct mutex sync_lock, lock;
+ struct mutex lock;
int id;
void *device;
struct msm_video_device vdev[MSM_VIDC_MAX_DEVICES];
@@ -231,7 +232,7 @@
void *mem_client;
struct v4l2_ctrl_handler ctrl_handler;
struct completion completions[SESSION_MSG_END - SESSION_MSG_START + 1];
- struct list_head ctrl_clusters;
+ struct v4l2_ctrl **cluster;
struct v4l2_fh event_handler;
struct msm_smem *extradata_handle;
wait_queue_head_t kernel_event_queue;
@@ -250,6 +251,7 @@
enum buffer_mode_type buffer_mode_set[MAX_PORT_NUM];
struct list_head registered_bufs;
bool map_output_buffer;
+ struct v4l2_ctrl **ctrls;
};
extern struct msm_vidc_drv *vidc_driver;
@@ -269,8 +271,6 @@
u32 step;
u32 menu_skip_mask;
const char * const *qmenu;
- u32 cluster;
- struct v4l2_ctrl *priv;
};
void handle_cmd_response(enum command_response cmd, void *data);
diff --git a/drivers/media/platform/msm/vidc/q6_hfi.c b/drivers/media/platform/msm/vidc/q6_hfi.c
index 5404af6..486d740 100644
--- a/drivers/media/platform/msm/vidc/q6_hfi.c
+++ b/drivers/media/platform/msm/vidc/q6_hfi.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2014, 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
@@ -146,8 +146,8 @@
struct q6_iface_q_info *q_info;
unsigned long flags = 0;
- if (!pkt) {
- dprintk(VIDC_ERR, "Invalid Params");
+ if (!device || !pkt) {
+ dprintk(VIDC_ERR, "Invalid Params\n");
return -EINVAL;
}
diff --git a/drivers/media/platform/msm/vidc/venus_hfi.c b/drivers/media/platform/msm/vidc/venus_hfi.c
index 008407d..448fe3b 100644
--- a/drivers/media/platform/msm/vidc/venus_hfi.c
+++ b/drivers/media/platform/msm/vidc/venus_hfi.c
@@ -80,11 +80,28 @@
u32 spare; /*reserved for future, should be zero*/
};
+#define VENUS_SET_STATE(__device, __state) {\
+ mutex_lock(&(__device)->write_lock); \
+ mutex_lock(&(__device)->read_lock); \
+ (__device)->state = __state; \
+ mutex_unlock(&(__device)->write_lock); \
+ mutex_unlock(&(__device)->read_lock); }
+
+#define IS_VENUS_IN_VALID_STATE(__device) (\
+ (__device)->state != VENUS_STATE_DEINIT)
+
+static int venus_hfi_power_enable(void *dev);
static inline int venus_hfi_clk_gating_off(struct venus_hfi_device *device);
static int venus_hfi_power_enable(void *dev);
+static inline int venus_hfi_prepare_enable_clks(
+ struct venus_hfi_device *device);
+
+static inline void venus_hfi_disable_unprepare_clks(
+ struct venus_hfi_device *device);
+
static unsigned long venus_hfi_get_clock_rate(struct venus_core_clock *clock,
int num_mbs_per_sec);
@@ -477,7 +494,7 @@
}
base_addr = device->hal_data->register_base_addr;
- if (!device->clocks_enabled) {
+ if (device->clk_state != ENABLED_PREPARED) {
dprintk(VIDC_WARN,
"HFI Write register failed : Clocks are OFF\n");
return;
@@ -517,7 +534,7 @@
}
base_addr = device->hal_data->register_base_addr;
- if (!device->clocks_enabled) {
+ if (device->clk_state != ENABLED_PREPARED) {
dprintk(VIDC_WARN,
"HFI Read register failed : Clocks are OFF\n");
return -EINVAL;
@@ -808,6 +825,203 @@
return rc;
}
+static int venus_hfi_iface_cmdq_write_nolock(struct venus_hfi_device *device,
+ void *pkt);
+
+static int venus_hfi_iface_cmdq_write(struct venus_hfi_device *device,
+ void *pkt)
+{
+ int result = -EPERM;
+ if (!device || !pkt) {
+ dprintk(VIDC_ERR, "Invalid Params");
+ return -EINVAL;
+ }
+ mutex_lock(&device->write_lock);
+ mutex_lock(&device->clk_pwr_lock);
+ result = venus_hfi_iface_cmdq_write_nolock(device, pkt);
+ mutex_unlock(&device->clk_pwr_lock);
+ mutex_unlock(&device->write_lock);
+ return result;
+}
+
+static int venus_hfi_core_set_resource(void *device,
+ struct vidc_resource_hdr *resource_hdr, void *resource_value,
+ int locked)
+{
+ struct hfi_cmd_sys_set_resource_packet *pkt;
+ u8 packet[VIDC_IFACEQ_VAR_SMALL_PKT_SIZE];
+ int rc = 0;
+ struct venus_hfi_device *dev;
+
+ if (!device || !resource_hdr || !resource_value) {
+ dprintk(VIDC_ERR, "set_res: Invalid Params");
+ return -EINVAL;
+ } else {
+ dev = device;
+ }
+
+ pkt = (struct hfi_cmd_sys_set_resource_packet *) packet;
+
+ rc = create_pkt_set_cmd_sys_resource(pkt, resource_hdr,
+ resource_value);
+ if (rc) {
+ dprintk(VIDC_ERR, "set_res: failed to create packet");
+ goto err_create_pkt;
+ }
+ rc = locked ? venus_hfi_iface_cmdq_write(dev, pkt) :
+ venus_hfi_iface_cmdq_write_nolock(dev, pkt);
+ if (rc)
+ rc = -ENOTEMPTY;
+
+err_create_pkt:
+ return rc;
+}
+
+static int venus_hfi_core_release_resource(void *device,
+ struct vidc_resource_hdr *resource_hdr)
+{
+ struct hfi_cmd_sys_release_resource_packet pkt;
+ int rc = 0;
+ struct venus_hfi_device *dev;
+
+ if (!device || !resource_hdr) {
+ dprintk(VIDC_ERR, "Inv-Params in rel_res");
+ return -EINVAL;
+ } else {
+ dev = device;
+ }
+
+ rc = create_pkt_cmd_sys_release_resource(&pkt, resource_hdr);
+ if (rc) {
+ dprintk(VIDC_ERR, "release_res: failed to create packet");
+ goto err_create_pkt;
+ }
+
+ if (venus_hfi_iface_cmdq_write(dev, &pkt))
+ rc = -ENOTEMPTY;
+
+err_create_pkt:
+ return rc;
+}
+
+static int venus_hfi_set_ocmem(void *dev, struct ocmem_buf *ocmem, int locked)
+{
+ struct vidc_resource_hdr rhdr;
+ struct venus_hfi_device *device = dev;
+ int rc = 0;
+ if (!device || !ocmem) {
+ dprintk(VIDC_ERR, "Invalid params, core:%p, ocmem: %p\n",
+ device, ocmem);
+ return -EINVAL;
+ }
+ rhdr.resource_id = VIDC_RESOURCE_OCMEM;
+ rhdr.resource_handle = (u32) &device->resources.ocmem;
+ rhdr.size = ocmem->len;
+ rc = venus_hfi_core_set_resource(device, &rhdr, ocmem, locked);
+ if (rc) {
+ dprintk(VIDC_ERR, "Failed to set OCMEM on driver\n");
+ goto ocmem_set_failed;
+ }
+ dprintk(VIDC_DBG, "OCMEM set, addr = %lx, size: %ld\n",
+ ocmem->addr, ocmem->len);
+ocmem_set_failed:
+ return rc;
+}
+
+static int venus_hfi_unset_ocmem(void *dev)
+{
+ struct vidc_resource_hdr rhdr;
+ struct venus_hfi_device *device = dev;
+ int rc = 0;
+
+ if (!device) {
+ dprintk(VIDC_ERR, "%s Invalid params, device:%p\n",
+ __func__, device);
+ rc = -EINVAL;
+ goto ocmem_unset_failed;
+ }
+ if (!device->resources.ocmem.buf) {
+ dprintk(VIDC_INFO, "%s Trying to free OCMEM which is not set",
+ __func__);
+ rc = -EINVAL;
+ goto ocmem_unset_failed;
+ }
+ rhdr.resource_id = VIDC_RESOURCE_OCMEM;
+ rhdr.resource_handle = (u32) &device->resources.ocmem;
+ rc = venus_hfi_core_release_resource(device, &rhdr);
+ if (rc)
+ dprintk(VIDC_ERR, "Failed to unset OCMEM on driver\n");
+ocmem_unset_failed:
+ return rc;
+}
+
+static int __alloc_ocmem(void *dev, unsigned long size, int locked)
+{
+ int rc = 0;
+ struct ocmem_buf *ocmem_buffer;
+ struct venus_hfi_device *device = dev;
+
+ if (!device || !size) {
+ dprintk(VIDC_ERR, "%s Invalid param, core: %p, size: %lu\n",
+ __func__, device, size);
+ return -EINVAL;
+ }
+ ocmem_buffer = device->resources.ocmem.buf;
+ if (!ocmem_buffer ||
+ ocmem_buffer->len < size) {
+ ocmem_buffer = ocmem_allocate(OCMEM_VIDEO, size);
+ if (IS_ERR_OR_NULL(ocmem_buffer)) {
+ dprintk(VIDC_ERR,
+ "ocmem_allocate_nb failed: %d\n",
+ (u32) ocmem_buffer);
+ rc = -ENOMEM;
+ goto ocmem_alloc_failed;
+ }
+ device->resources.ocmem.buf = ocmem_buffer;
+ rc = venus_hfi_set_ocmem(device, ocmem_buffer, locked);
+ if (rc) {
+ dprintk(VIDC_ERR, "Failed to set ocmem: %d\n", rc);
+ goto ocmem_set_failed;
+ }
+ device->ocmem_size = size;
+ } else
+ dprintk(VIDC_DBG,
+ "OCMEM is enough. reqd: %lu, available: %lu\n",
+ size, ocmem_buffer->len);
+
+ return rc;
+ocmem_set_failed:
+ ocmem_free(OCMEM_VIDEO, device->resources.ocmem.buf);
+ device->resources.ocmem.buf = NULL;
+ocmem_alloc_failed:
+ return rc;
+}
+
+static int venus_hfi_alloc_ocmem(void *dev, unsigned long size)
+{
+ return __alloc_ocmem(dev, size, true);
+}
+
+static int venus_hfi_free_ocmem(void *dev)
+{
+ struct venus_hfi_device *device = dev;
+ int rc = 0;
+
+ if (!device) {
+ dprintk(VIDC_ERR, "%s invalid device handle %p",
+ __func__, device);
+ return -EINVAL;
+ }
+
+ if (device->resources.ocmem.buf) {
+ rc = ocmem_free(OCMEM_VIDEO, device->resources.ocmem.buf);
+ if (rc)
+ dprintk(VIDC_ERR, "Failed to free ocmem\n");
+ device->resources.ocmem.buf = NULL;
+ }
+ return rc;
+}
+
static inline int venus_hfi_tzbsp_set_video_state(enum tzbsp_video_state state)
{
struct tzbsp_video_set_state_req cmd = {0};
@@ -842,8 +1056,6 @@
return rc;
}
-
-/*Calling function is responsible to acquire device->clk_pwr_lock*/
static inline int venus_hfi_clk_enable(struct venus_hfi_device *device)
{
int rc = 0;
@@ -854,7 +1066,9 @@
dprintk(VIDC_ERR, "Invalid params: %p\n", device);
return -EINVAL;
}
- if (device->clocks_enabled) {
+ WARN(!mutex_is_locked(&device->clk_pwr_lock),
+ "Clock/power lock must be acquired");
+ if (device->clk_state == ENABLED_PREPARED) {
dprintk(VIDC_DBG, "Clocks already enabled");
return 0;
}
@@ -869,7 +1083,7 @@
dprintk(VIDC_DBG, "Clock: %s enabled\n", cl->name);
}
}
- device->clocks_enabled = 1;
+ device->clk_state = ENABLED_PREPARED;
++device->clk_cnt;
return 0;
fail_clk_enable:
@@ -878,10 +1092,10 @@
usleep(100);
clk_disable(cl->clk);
}
+ device->clk_state = DISABLED_PREPARED;
return rc;
}
-/*Calling function is responsible to acquire device->clk_pwr_lock*/
static inline void venus_hfi_clk_disable(struct venus_hfi_device *device)
{
int i, rc = 0;
@@ -891,7 +1105,9 @@
dprintk(VIDC_ERR, "Invalid params: %p\n", device);
return;
}
- if (!device->clocks_enabled) {
+ WARN(!mutex_is_locked(&device->clk_pwr_lock),
+ "Clock/power lock must be acquired");
+ if (device->clk_state != ENABLED_PREPARED) {
dprintk(VIDC_DBG, "Clocks already disabled");
return;
}
@@ -909,11 +1125,12 @@
usleep(100);
clk_disable(cl->clk);
}
- device->clocks_enabled = 0;
+ device->clk_state = DISABLED_PREPARED;
--device->clk_cnt;
}
static DECLARE_COMPLETION(pc_prep_done);
+static DECLARE_COMPLETION(release_resources_done);
static int venus_hfi_halt_axi(struct venus_hfi_device *device)
{
@@ -923,9 +1140,11 @@
dprintk(VIDC_ERR, "Invalid input: %p\n", device);
return -EINVAL;
}
+ mutex_lock(&device->clk_pwr_lock);
if (venus_hfi_clk_gating_off(device)) {
dprintk(VIDC_ERR, "Failed to turn off clk gating\n");
- return -EIO;
+ rc = -EIO;
+ goto err_clk_gating_off;
}
/* Halt AXI and AXI OCMEM VBIF Access */
reg = venus_hfi_read_register(device, VENUS_VBIF_AXI_HALT_CTRL0);
@@ -940,6 +1159,8 @@
VENUS_VBIF_AXI_HALT_ACK_TIMEOUT_US);
if (rc)
dprintk(VIDC_WARN, "AXI bus port halt timeout\n");
+err_clk_gating_off:
+ mutex_unlock(&device->clk_pwr_lock);
return rc;
}
@@ -967,7 +1188,7 @@
venus_hfi_clk_disable(device);
return rc;
}
- venus_hfi_clk_disable(device);
+ venus_hfi_disable_unprepare_clks(device);
venus_hfi_iommu_detach(device);
rc = regulator_disable(device->gdsc);
if (rc) {
@@ -979,7 +1200,7 @@
else
venus_hfi_unvote_buses(device, DDR_MEM);
- device->power_enabled = 0;
+ device->power_enabled = false;
--device->pwr_cnt;
dprintk(VIDC_INFO, "entering power collapse\n");
already_disabled:
@@ -1015,7 +1236,11 @@
goto err_iommu_attach;
}
- rc = venus_hfi_clk_enable(device);
+ if (device->clk_state == DISABLED_UNPREPARED)
+ rc = venus_hfi_prepare_enable_clks(device);
+ else if (device->clk_state == DISABLED_PREPARED)
+ rc = venus_hfi_clk_enable(device);
+
if (rc) {
dprintk(VIDC_ERR, "Failed to enable clocks");
goto err_enable_clk;
@@ -1056,11 +1281,22 @@
dprintk(VIDC_ERR, "Failed to reset venus core");
goto err_reset_core;
}
-
- device->power_enabled = 1;
+ /*
+ * write_lock is already acquired at this point, so to avoid
+ * recursive lock in cmdq_write function, call nolock version
+ * of alloc_ocmem
+ */
+ WARN_ON(!mutex_is_locked(&device->write_lock));
+ rc = __alloc_ocmem(device, device->ocmem_size, false);
+ if (rc) {
+ dprintk(VIDC_ERR, "Failed to allocate OCMEM");
+ goto err_alloc_ocmem;
+ }
+ device->power_enabled = true;
++device->pwr_cnt;
dprintk(VIDC_INFO, "resuming from power collapse\n");
return rc;
+err_alloc_ocmem:
err_reset_core:
venus_hfi_tzbsp_set_video_state(TZBSP_VIDEO_STATE_SUSPEND);
err_set_video_state:
@@ -1086,11 +1322,21 @@
dprintk(VIDC_ERR, "Invalid params: %p\n", device);
return -EINVAL;
}
- mutex_lock(&device->clk_pwr_lock);
- if (!device->power_enabled)
- rc = venus_hfi_power_on(device);
- mutex_unlock(&device->clk_pwr_lock);
+ mutex_lock(&device->clk_pwr_lock);
+ if (!device->power_enabled) {
+ rc = venus_hfi_power_on(device);
+ if (rc) {
+ dprintk(VIDC_ERR, "Failed venus power on");
+ goto fail_power_on;
+ }
+ }
+ rc = venus_hfi_clk_gating_off(device);
+ if (rc)
+ dprintk(VIDC_ERR, "%s : Clock enable failed\n", __func__);
+
+fail_power_on:
+ mutex_unlock(&device->clk_pwr_lock);
return rc;
}
@@ -1104,7 +1350,7 @@
dprintk(VIDC_ERR, "Invalid params: %p\n", device);
return -EINVAL;
}
- if (device->clocks_enabled) {
+ if (device->clk_state == ENABLED_PREPARED) {
dprintk(VIDC_DBG, "Clocks are already enabled");
goto already_enabled;
}
@@ -1126,7 +1372,7 @@
VIDC_WRAPPER_INTR_MASK, VIDC_WRAPPER_INTR_MASK_A2HVCODEC_BMSK, 0);
}
already_enabled:
- device->clocks_enabled = 1;
+ device->clk_state = ENABLED_PREPARED;
fail_clk_power_on:
return rc;
}
@@ -1164,36 +1410,49 @@
return rc;
}
-static int venus_hfi_iface_cmdq_write(struct venus_hfi_device *device,
+static int venus_hfi_iface_cmdq_write_nolock(struct venus_hfi_device *device,
void *pkt)
{
u32 rx_req_is_set = 0;
struct vidc_iface_q_info *q_info;
int result = -EPERM;
+
if (!device || !pkt) {
dprintk(VIDC_ERR, "Invalid Params");
return -EINVAL;
}
+ WARN(!mutex_is_locked(&device->write_lock),
+ "Cmd queue write lock must be acquired");
+ if (!IS_VENUS_IN_VALID_STATE(device)) {
+ dprintk(VIDC_ERR, "%s - fw not in init state\n", __func__);
+ result = -EINVAL;
+ goto err_q_null;
+ }
- mutex_lock(&device->write_lock);
q_info = &device->iface_queues[VIDC_IFACEQ_CMDQ_IDX];
if (!q_info) {
dprintk(VIDC_ERR, "cannot write to shared Q's");
goto err_q_null;
}
+
+ if (!q_info->q_array.align_virtual_addr) {
+ dprintk(VIDC_ERR, "cannot write to shared CMD Q's\n");
+ result = -ENODATA;
+ goto err_q_null;
+ }
+
if (!venus_hfi_write_queue(q_info, (u8 *)pkt, &rx_req_is_set)) {
- mutex_lock(&device->clk_pwr_lock);
+ WARN(!mutex_is_locked(&device->clk_pwr_lock),
+ "Clock/power lock must be acquired");
result = venus_hfi_clk_gating_off(device);
if (result) {
dprintk(VIDC_ERR, "%s : Clock enable failed\n",
__func__);
- mutex_unlock(&device->clk_pwr_lock);
goto err_q_write;
}
result = venus_hfi_scale_clocks(device, device->clk_load);
if (result) {
dprintk(VIDC_ERR, "Clock scaling failed\n");
- mutex_unlock(&device->clk_pwr_lock);
goto err_q_write;
}
if (rx_req_is_set)
@@ -1202,13 +1461,11 @@
VIDC_CPU_IC_SOFTINT,
1 << VIDC_CPU_IC_SOFTINT_H2A_SHFT, 0);
result = 0;
- mutex_unlock(&device->clk_pwr_lock);
} else {
dprintk(VIDC_ERR, "venus_hfi_iface_cmdq_write:queue_full");
}
err_q_write:
err_q_null:
- mutex_unlock(&device->write_lock);
return result;
}
@@ -1223,12 +1480,19 @@
return -EINVAL;
}
mutex_lock(&device->read_lock);
+ if (!IS_VENUS_IN_VALID_STATE(device)) {
+ dprintk(VIDC_ERR, "%s - fw not in init state\n", __func__);
+ rc = -EINVAL;
+ goto read_error_null;
+ }
+
if (device->iface_queues[VIDC_IFACEQ_MSGQ_IDX].
q_array.align_virtual_addr == 0) {
dprintk(VIDC_ERR, "cannot read from shared MSG Q's");
rc = -ENODATA;
goto read_error_null;
}
+
q_info = &device->iface_queues[VIDC_IFACEQ_MSGQ_IDX];
if (!venus_hfi_read_queue(q_info, (u8 *)pkt, &tx_req_is_set)) {
mutex_lock(&device->clk_pwr_lock);
@@ -1267,6 +1531,11 @@
return -EINVAL;
}
mutex_lock(&device->read_lock);
+ if (!IS_VENUS_IN_VALID_STATE(device)) {
+ dprintk(VIDC_ERR, "%s - fw not in init state\n", __func__);
+ rc = -EINVAL;
+ goto dbg_error_null;
+ }
if (device->iface_queues[VIDC_IFACEQ_DBGQ_IDX].
q_array.align_virtual_addr == 0) {
dprintk(VIDC_ERR, "cannot read from shared DBG Q's");
@@ -1352,15 +1621,19 @@
device->iface_queues[i].q_array.align_virtual_addr = NULL;
device->iface_queues[i].q_array.align_device_addr = NULL;
}
+ device->iface_q_table.mem_data = NULL;
device->iface_q_table.align_virtual_addr = NULL;
device->iface_q_table.align_device_addr = NULL;
+ device->qdss.mem_data = NULL;
device->qdss.align_virtual_addr = NULL;
device->qdss.align_device_addr = NULL;
+ device->sfr.mem_data = NULL;
device->sfr.align_virtual_addr = NULL;
device->sfr.align_device_addr = NULL;
+ device->mem_addr.mem_data = NULL;
device->mem_addr.align_virtual_addr = NULL;
device->mem_addr.align_device_addr = NULL;
@@ -1587,6 +1860,8 @@
return -ENODEV;
}
+ VENUS_SET_STATE(dev, VENUS_STATE_INIT);
+
dev->intr_status = 0;
INIT_LIST_HEAD(&dev->sess_head);
venus_hfi_set_registers(dev);
@@ -1639,6 +1914,7 @@
return rc;
err_core_init:
+ VENUS_SET_STATE(dev, VENUS_STATE_DEINIT);
disable_irq_nosync(dev->hal_data->irq);
return rc;
}
@@ -1653,6 +1929,7 @@
dprintk(VIDC_ERR, "invalid device");
return -ENODEV;
}
+
if (dev->hal_client) {
mutex_lock(&dev->clk_pwr_lock);
rc = venus_hfi_clk_gating_off(device);
@@ -1669,6 +1946,8 @@
dev->intr_status = 0;
mutex_unlock(&dev->clk_pwr_lock);
}
+ VENUS_SET_STATE(dev, VENUS_STATE_DEINIT);
+
dprintk(VIDC_INFO, "HAL exited\n");
return 0;
}
@@ -1685,6 +1964,8 @@
return -ENOENT;
}
+ WARN(!mutex_is_locked(&dev->write_lock),
+ "Cmdq write lock should be acquired");
q_info = &dev->iface_queues[q_index];
if (!q_info) {
dprintk(VIDC_ERR, "cannot read shared Q's");
@@ -1707,7 +1988,7 @@
dprintk(VIDC_ERR, "Invalid params: %p\n", device);
return;
}
- if (!device->clocks_enabled) {
+ if (device->clk_state != ENABLED_PREPARED) {
dprintk(VIDC_DBG, "Clocks are already disabled");
goto already_disabled;
}
@@ -1724,7 +2005,7 @@
msecs_to_jiffies(msm_vidc_pwr_collapse_delay)))
dprintk(VIDC_DBG, "PM work already scheduled\n");
already_disabled:
- device->clocks_enabled = 0;
+ device->clk_state = DISABLED_PREPARED;
}
static void venus_hfi_core_clear_interrupt(struct venus_hfi_device *device)
@@ -1770,63 +2051,6 @@
mutex_unlock(&device->write_lock);
}
-static int venus_hfi_core_set_resource(void *device,
- struct vidc_resource_hdr *resource_hdr, void *resource_value)
-{
- struct hfi_cmd_sys_set_resource_packet *pkt;
- u8 packet[VIDC_IFACEQ_VAR_SMALL_PKT_SIZE];
- int rc = 0;
- struct venus_hfi_device *dev;
-
- if (!device || !resource_hdr || !resource_value) {
- dprintk(VIDC_ERR, "set_res: Invalid Params");
- return -EINVAL;
- } else {
- dev = device;
- }
-
- pkt = (struct hfi_cmd_sys_set_resource_packet *) packet;
-
- rc = create_pkt_set_cmd_sys_resource(pkt, resource_hdr,
- resource_value);
- if (rc) {
- dprintk(VIDC_ERR, "set_res: failed to create packet");
- goto err_create_pkt;
- }
- if (venus_hfi_iface_cmdq_write(dev, pkt))
- rc = -ENOTEMPTY;
-
-err_create_pkt:
- return rc;
-}
-
-static int venus_hfi_core_release_resource(void *device,
- struct vidc_resource_hdr *resource_hdr)
-{
- struct hfi_cmd_sys_release_resource_packet pkt;
- int rc = 0;
- struct venus_hfi_device *dev;
-
- if (!device || !resource_hdr) {
- dprintk(VIDC_ERR, "Inv-Params in rel_res");
- return -EINVAL;
- } else {
- dev = device;
- }
-
- rc = create_pkt_cmd_sys_release_resource(&pkt, resource_hdr);
- if (rc) {
- dprintk(VIDC_ERR, "release_res: failed to create packet");
- goto err_create_pkt;
- }
-
- if (venus_hfi_iface_cmdq_write(dev, &pkt))
- rc = -ENOTEMPTY;
-
-err_create_pkt:
- return rc;
-}
-
static int venus_hfi_core_ping(void *device)
{
struct hfi_cmd_sys_ping_packet pkt;
@@ -2514,6 +2738,67 @@
return rc;
}
+static int venus_hfi_unset_free_ocmem(struct venus_hfi_device *device)
+{
+ int rc = 0;
+
+ if (!device) {
+ dprintk(VIDC_ERR, "Invalid param: %p\n", device);
+ return -EINVAL;
+ }
+
+ init_completion(&release_resources_done);
+ rc = venus_hfi_unset_ocmem(device);
+ if (rc) {
+ dprintk(VIDC_ERR, "Failed to unset OCMEM during PC %d\n", rc);
+ goto ocmem_unset_failed;
+ }
+ rc = wait_for_completion_timeout(&release_resources_done,
+ msecs_to_jiffies(msm_vidc_hw_rsp_timeout));
+ if (!rc) {
+ dprintk(VIDC_ERR,
+ "Wait interrupted or timeout for RELEASE_RESOURCES: %d\n",
+ rc);
+ rc = -EIO;
+ goto release_resources_failed;
+ }
+
+ rc = venus_hfi_free_ocmem(device);
+ if (rc) {
+ dprintk(VIDC_ERR, "Failed to free OCMEM during PC\n");
+ goto ocmem_free_failed;
+ }
+ return rc;
+
+ocmem_free_failed:
+ venus_hfi_alloc_ocmem(device, device->ocmem_size);
+release_resources_failed:
+ocmem_unset_failed:
+ return rc;
+}
+
+static int venus_hfi_prepare_pc(struct venus_hfi_device *device)
+{
+ int rc = 0;
+ init_completion(&pc_prep_done);
+ rc = venus_hfi_core_pc_prep(device);
+ if (rc) {
+ dprintk(VIDC_ERR, "Failed to prepare venus for power off");
+ goto err_pc_prep;
+ }
+ rc = wait_for_completion_timeout(&pc_prep_done,
+ msecs_to_jiffies(msm_vidc_hw_rsp_timeout));
+ if (!rc) {
+ dprintk(VIDC_ERR,
+ "Wait interrupted or timeout for PC_PREP_DONE: %d\n",
+ rc);
+ rc = -EIO;
+ goto err_pc_prep;
+ }
+ rc = 0;
+err_pc_prep:
+ return rc;
+}
static void venus_hfi_pm_hndlr(struct work_struct *work)
{
@@ -2521,28 +2806,31 @@
struct venus_hfi_device *device = list_first_entry(
&hal_ctxt.dev_head, struct venus_hfi_device, list);
mutex_lock(&device->clk_pwr_lock);
- if (device->clocks_enabled || !device->power_enabled) {
+ if (device->clk_state == ENABLED_PREPARED || !device->power_enabled) {
dprintk(VIDC_DBG,
"Clocks status: %d, Power status: %d, ignore power off\n",
- device->clocks_enabled, device->power_enabled);
+ device->clk_state, device->power_enabled);
goto clks_enabled;
}
mutex_unlock(&device->clk_pwr_lock);
- init_completion(&pc_prep_done);
- rc = venus_hfi_core_pc_prep(device);
+
+ rc = venus_hfi_unset_free_ocmem(device);
if (rc) {
- dprintk(VIDC_ERR, "Failed to prepare venus for power off");
+ dprintk(VIDC_ERR,
+ "Failed to unset and free OCMEM for PC %d\n",
+ rc);
return;
}
- rc = wait_for_completion_timeout(&pc_prep_done,
- msecs_to_jiffies(msm_vidc_hw_rsp_timeout));
- if (!rc) {
- dprintk(VIDC_ERR, "Wait interrupted or timeout: %d", rc);
+
+ rc = venus_hfi_prepare_pc(device);
+ if (rc) {
+ dprintk(VIDC_ERR, "Failed to prepare for PC %d\n", rc);
+ venus_hfi_alloc_ocmem(device, device->ocmem_size);
return;
}
mutex_lock(&device->clk_pwr_lock);
- if (device->clocks_enabled) {
+ if (device->clk_state == ENABLED_PREPARED) {
dprintk(VIDC_ERR,
"Clocks are still enabled after PC_PREP_DONE, ignore power off");
goto clks_enabled;
@@ -2626,26 +2914,35 @@
device->device_id,
(struct vidc_hal_msg_pkt_hdr *) packet,
&device->sess_head, &device->session_lock);
- if (rc == HFI_MSG_EVENT_NOTIFY)
+ if (rc == HFI_MSG_EVENT_NOTIFY) {
venus_hfi_process_msg_event_notify(
device, (void *)packet);
+ } else if (rc == HFI_MSG_SYS_RELEASE_RESOURCE) {
+ dprintk(VIDC_DBG,
+ "Received HFI_MSG_SYS_RELEASE_RESOURCE\n");
+ complete(&release_resources_done);
+ }
}
while (!venus_hfi_iface_dbgq_read(device, packet)) {
struct hfi_msg_sys_debug_packet *pkt =
(struct hfi_msg_sys_debug_packet *) packet;
dprintk(VIDC_FW, "FW-SAYS: %s", pkt->rg_msg_data);
}
- if (rc == HFI_MSG_SYS_IDLE) {
- dprintk(VIDC_DBG, "Received HFI_MSG_SYS_IDLE\n");
+ switch (rc) {
+ case HFI_MSG_SYS_IDLE:
+ dprintk(VIDC_DBG,
+ "Received HFI_MSG_SYS_IDLE\n");
rc = venus_hfi_try_clk_gating(device);
- } else if (rc == HFI_MSG_SYS_PC_PREP_DONE) {
+ break;
+ case HFI_MSG_SYS_PC_PREP_DONE:
dprintk(VIDC_DBG,
"Received HFI_MSG_SYS_PC_PREP_DONE\n");
rc = venus_hfi_try_clk_gating(device);
if (rc)
dprintk(VIDC_ERR,
- "Failed clk gating after PC_PREP_DONE");
+ "Failed clk gating after PC_PREP_DONE\n");
complete(&pc_prep_done);
+ break;
}
} else {
dprintk(VIDC_ERR, "SPURIOUS_INTERRUPT");
@@ -2819,7 +3116,9 @@
clk_put(device->resources.clock[i].clk);
}
}
-static inline void venus_hfi_disable_clks(struct venus_hfi_device *device)
+
+static inline void venus_hfi_disable_unprepare_clks(
+ struct venus_hfi_device *device)
{
int i;
struct venus_core_clock *cl;
@@ -2827,8 +3126,9 @@
dprintk(VIDC_ERR, "Invalid params: %p\n", device);
return;
}
- mutex_lock(&device->clk_pwr_lock);
- if (device->clocks_enabled) {
+
+ WARN_ON(!mutex_is_locked(&device->clk_pwr_lock));
+ if (device->clk_state == ENABLED_PREPARED) {
for (i = VCODEC_CLK; i < VCODEC_MAX_CLKS; i++) {
if (i == VCODEC_OCMEM_CLK && !device->res->has_ocmem)
continue;
@@ -2850,11 +3150,11 @@
cl = &device->resources.clock[i];
clk_unprepare(cl->clk);
}
- device->clocks_enabled = 0;
+ device->clk_state = DISABLED_UNPREPARED;
--device->clk_cnt;
- mutex_unlock(&device->clk_pwr_lock);
}
-static inline int venus_hfi_enable_clks(struct venus_hfi_device *device)
+
+static inline int venus_hfi_prepare_enable_clks(struct venus_hfi_device *device)
{
int i = 0;
struct venus_core_clock *cl;
@@ -2863,7 +3163,12 @@
dprintk(VIDC_ERR, "Invalid params: %p\n", device);
return -EINVAL;
}
- mutex_lock(&device->clk_pwr_lock);
+ WARN_ON(!mutex_is_locked(&device->clk_pwr_lock));
+
+ if (device->clk_state == ENABLED_PREPARED) {
+ dprintk(VIDC_DBG, "Clocks already prepared and enabled\n");
+ return 0;
+ }
for (i = VCODEC_CLK; i < VCODEC_MAX_CLKS; i++) {
if (i == VCODEC_OCMEM_CLK && !device->res->has_ocmem)
continue;
@@ -2876,9 +3181,8 @@
dprintk(VIDC_DBG, "Clock: %s enabled\n", cl->name);
}
}
- device->clocks_enabled = 1;
+ device->clk_state = ENABLED_PREPARED;
++device->clk_cnt;
- mutex_unlock(&device->clk_pwr_lock);
return rc;
fail_clk_enable:
for (; i >= 0; i--) {
@@ -2886,9 +3190,10 @@
usleep(100);
clk_disable_unprepare(cl->clk);
}
- mutex_unlock(&device->clk_pwr_lock);
+ device->clk_state = DISABLED_UNPREPARED;
return rc;
}
+
static int venus_hfi_register_iommu_domains(struct venus_hfi_device *device,
struct msm_vidc_platform_resources *res)
{
@@ -3035,57 +3340,6 @@
return -EINVAL;
}
-static int venus_hfi_set_ocmem(void *dev, struct ocmem_buf *ocmem)
-{
- struct vidc_resource_hdr rhdr;
- struct venus_hfi_device *device = dev;
- int rc = 0;
- if (!device || !ocmem) {
- dprintk(VIDC_ERR, "Invalid params, core:%p, ocmem: %p\n",
- device, ocmem);
- return -EINVAL;
- }
- rhdr.resource_id = VIDC_RESOURCE_OCMEM;
- rhdr.resource_handle = (u32) &device->resources.ocmem;
- rhdr.size = ocmem->len;
- rc = venus_hfi_core_set_resource(device, &rhdr, ocmem);
- if (rc) {
- dprintk(VIDC_ERR, "Failed to set OCMEM on driver\n");
- goto ocmem_set_failed;
- }
- dprintk(VIDC_DBG, "OCMEM set, addr = %lx, size: %ld\n",
- ocmem->addr, ocmem->len);
-ocmem_set_failed:
- return rc;
-}
-
-static int venus_hfi_unset_ocmem(void *dev)
-{
- struct vidc_resource_hdr rhdr;
- struct venus_hfi_device *device = dev;
- int rc = 0;
-
- if (!device) {
- dprintk(VIDC_ERR, "%s Invalid params, device:%p\n",
- __func__, device);
- rc = -EINVAL;
- goto ocmem_unset_failed;
- }
- if (!device->resources.ocmem.buf) {
- dprintk(VIDC_INFO, "%s Trying to free OCMEM which is not set",
- __func__);
- rc = -EINVAL;
- goto ocmem_unset_failed;
- }
- rhdr.resource_id = VIDC_RESOURCE_OCMEM;
- rhdr.resource_handle = (u32) &device->resources.ocmem;
- rc = venus_hfi_core_release_resource(device, &rhdr);
- if (rc)
- dprintk(VIDC_ERR, "Failed to unset OCMEM on driver\n");
-ocmem_unset_failed:
- return rc;
-}
-
static int venus_hfi_ocmem_notify_handler(struct notifier_block *this,
unsigned long event, void *data)
{
@@ -3105,7 +3359,7 @@
struct venus_resources, ocmem);
device = container_of(resources,
struct venus_hfi_device, resources);
- if (venus_hfi_set_ocmem(device, buff)) {
+ if (venus_hfi_set_ocmem(device, buff, 1)) {
dprintk(VIDC_ERR, "Failed to set ocmem: %d\n", rc);
goto err_ocmem_notify;
}
@@ -3131,62 +3385,6 @@
}
}
-static int venus_hfi_alloc_ocmem(void *dev, unsigned long size)
-{
- int rc = 0;
- struct ocmem_buf *ocmem_buffer;
- struct venus_hfi_device *device = dev;
-
- if (!device || !size) {
- dprintk(VIDC_ERR, "%s Invalid param, core: %p, size: %lu\n",
- __func__, device, size);
- return -EINVAL;
- }
- ocmem_buffer = device->resources.ocmem.buf;
- if (!ocmem_buffer || ocmem_buffer->len < size) {
- ocmem_buffer = ocmem_allocate(OCMEM_VIDEO, size);
- if (IS_ERR_OR_NULL(ocmem_buffer)) {
- dprintk(VIDC_ERR,
- "ocmem_allocate_nb failed: %d\n",
- (u32) ocmem_buffer);
- rc = -ENOMEM;
- goto ocmem_set_failed;
- }
- device->resources.ocmem.buf = ocmem_buffer;
- rc = venus_hfi_set_ocmem(device, ocmem_buffer);
- if (rc) {
- dprintk(VIDC_ERR, "Failed to set ocmem: %d\n", rc);
- goto ocmem_set_failed;
- }
- } else
- dprintk(VIDC_DBG,
- "OCMEM is enough. reqd: %lu, available: %lu\n",
- size, ocmem_buffer->len);
-
-ocmem_set_failed:
- return rc;
-}
-
-static int venus_hfi_free_ocmem(void *dev)
-{
- struct venus_hfi_device *device = dev;
- int rc = 0;
-
- if (!device) {
- dprintk(VIDC_ERR, "%s invalid device handle %p",
- __func__, device);
- return -EINVAL;
- }
-
- if (device->resources.ocmem.buf) {
- rc = ocmem_free(OCMEM_VIDEO, device->resources.ocmem.buf);
- if (rc)
- dprintk(VIDC_ERR, "Failed to free ocmem\n");
- device->resources.ocmem.buf = NULL;
- }
- return rc;
-}
-
static void venus_hfi_deinit_ocmem(struct venus_hfi_device *device)
{
if (device->resources.ocmem.handle)
@@ -3354,17 +3552,16 @@
mutex_unlock(&device->clk_pwr_lock);
goto fail_load_fw;
}
- device->power_enabled = 1;
+ device->power_enabled = true;
++device->pwr_cnt;
- mutex_unlock(&device->clk_pwr_lock);
/*Clocks can be enabled only after pil_get since
* gdsc is turned-on in pil_get*/
- rc = venus_hfi_enable_clks(device);
+ rc = venus_hfi_prepare_enable_clks(device);
+ mutex_unlock(&device->clk_pwr_lock);
if (rc) {
dprintk(VIDC_ERR, "Failed to enable clocks: %d\n", rc);
goto fail_enable_clks;
}
-
rc = protect_cp_mem(device);
if (rc) {
dprintk(VIDC_ERR, "Failed to protect memory\n");
@@ -3373,14 +3570,16 @@
return rc;
fail_protect_mem:
- venus_hfi_disable_clks(device);
+ mutex_lock(&device->clk_pwr_lock);
+ venus_hfi_disable_unprepare_clks(device);
+ mutex_unlock(&device->clk_pwr_lock);
fail_enable_clks:
subsystem_put(device->resources.fw.cookie);
fail_load_fw:
mutex_lock(&device->clk_pwr_lock);
device->resources.fw.cookie = NULL;
regulator_disable(device->gdsc);
- device->power_enabled = 0;
+ device->power_enabled = false;
--device->pwr_cnt;
mutex_unlock(&device->clk_pwr_lock);
fail_enable_gdsc:
@@ -3409,16 +3608,60 @@
*/
if(venus_hfi_halt_axi(device))
dprintk(VIDC_WARN, "Failed to halt AXI\n");
- venus_hfi_disable_clks(device);
mutex_lock(&device->clk_pwr_lock);
+ venus_hfi_disable_unprepare_clks(device);
regulator_disable(device->gdsc);
- device->power_enabled = 0;
+ device->power_enabled = false;
--device->pwr_cnt;
mutex_unlock(&device->clk_pwr_lock);
device->resources.fw.cookie = NULL;
}
}
+static int venus_hfi_resurrect_fw(void *dev)
+{
+ struct venus_hfi_device *device = dev;
+ int rc = 0;
+
+ if (!device) {
+ dprintk(VIDC_ERR, "%s Invalid paramter: %p\n",
+ __func__, device);
+ return -EINVAL;
+ }
+
+ rc = venus_hfi_free_ocmem(device);
+ if (rc)
+ dprintk(VIDC_WARN, "%s - failed to free ocmem\n", __func__);
+
+ rc = venus_hfi_core_release(device);
+ if (rc) {
+ dprintk(VIDC_ERR, "%s - failed to release venus core rc = %d\n",
+ __func__, rc);
+ goto exit;
+ }
+
+ dprintk(VIDC_ERR, "praying for firmware resurrection\n");
+
+ venus_hfi_unload_fw(device);
+
+ rc = venus_hfi_scale_buses(device, DDR_MEM);
+ if (rc) {
+ dprintk(VIDC_ERR, "Failed to scale buses");
+ goto exit;
+ }
+
+ rc = venus_hfi_load_fw(device);
+ if (rc) {
+ dprintk(VIDC_ERR, "%s - failed to load venus fw rc = %d\n",
+ __func__, rc);
+ goto exit;
+ }
+
+ dprintk(VIDC_ERR, "Hurray!! firmware has restarted\n");
+exit:
+ return rc;
+}
+
static int venus_hfi_get_fw_info(void *dev, enum fw_info info)
{
int rc = 0;
@@ -3469,7 +3712,10 @@
rc = device->clk_cnt;
break;
case DEV_CLOCK_ENABLED:
- rc = device->clocks_enabled;
+ if (device->clk_state == ENABLED_PREPARED)
+ rc = 1;
+ else
+ rc = 0;
break;
case DEV_PWR_COUNT:
rc = device->pwr_cnt;
@@ -3493,67 +3739,11 @@
int venus_hfi_get_core_capabilities(void)
{
- int i = 0, rc = 0, j = 0, venus_version_length = 0;
- u32 smem_block_size = 0;
- u8 *smem_table_ptr;
- char version[256];
- const u32 version_string_size = 128;
- char venus_version[] = "VIDEO.VE.1.4";
- u8 version_info[256];
- const u32 smem_image_index_venus = 14 * 128;
- /* Venus version is stored at 14th entry in smem table */
-
- smem_table_ptr = smem_get_entry(SMEM_IMAGE_VERSION_TABLE,
- &smem_block_size);
- if (smem_table_ptr &&
- ((smem_image_index_venus + version_string_size) <=
- smem_block_size)) {
- memcpy(version_info, smem_table_ptr + smem_image_index_venus,
- version_string_size);
- } else {
- dprintk(VIDC_ERR,
- "%s: failed to read version info from smem table\n",
- __func__);
- return -EINVAL;
- }
-
- while (version_info[i++] != 'V' && i < version_string_size)
- ;
-
- venus_version_length = strlen(venus_version);
- for (i--, j = 0; i < version_string_size && j < venus_version_length;
- i++)
- version[j++] = version_info[i];
- version[venus_version_length] = '\0';
- dprintk(VIDC_DBG, "F/W version retrieved : %s\n", version);
-
- if (strcmp((const char *)version, (const char *)venus_version) == 0)
- rc = HAL_VIDEO_ENCODER_ROTATION_CAPABILITY |
- HAL_VIDEO_ENCODER_SCALING_CAPABILITY |
- HAL_VIDEO_ENCODER_DEINTERLACE_CAPABILITY |
- HAL_VIDEO_DECODER_MULTI_STREAM_CAPABILITY;
- return rc;
-}
-
-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;
- }
+ rc = HAL_VIDEO_ENCODER_ROTATION_CAPABILITY |
+ HAL_VIDEO_ENCODER_SCALING_CAPABILITY |
+ HAL_VIDEO_ENCODER_DEINTERLACE_CAPABILITY |
+ HAL_VIDEO_DECODER_MULTI_STREAM_CAPABILITY;
return rc;
}
@@ -3584,9 +3774,9 @@
hdevice->device_id = device_id;
hdevice->callback = callback;
- hdevice->clocks_enabled = 0;
+ hdevice->clk_state = DISABLED_UNPREPARED;
hdevice->clk_cnt = 0;
- hdevice->power_enabled = 0;
+ hdevice->power_enabled = false;
hdevice->pwr_cnt = 0;
hdevice->vidc_workq = create_singlethread_workqueue(
@@ -3714,10 +3904,10 @@
hdev->iommu_get_domain_partition = venus_hfi_iommu_get_domain_partition;
hdev->load_fw = venus_hfi_load_fw;
hdev->unload_fw = venus_hfi_unload_fw;
+ hdev->resurrect_fw = venus_hfi_resurrect_fw;
hdev->get_fw_info = venus_hfi_get_fw_info;
hdev->get_info = venus_hfi_get_info;
hdev->get_stride_scanline = venus_hfi_get_stride_scanline;
- hdev->capability_check = venus_hfi_capability_check;
hdev->get_core_capabilities = venus_hfi_get_core_capabilities;
hdev->power_enable = venus_hfi_power_enable;
}
diff --git a/drivers/media/platform/msm/vidc/venus_hfi.h b/drivers/media/platform/msm/vidc/venus_hfi.h
index 4feda45..23a51ba 100644
--- a/drivers/media/platform/msm/vidc/venus_hfi.h
+++ b/drivers/media/platform/msm/vidc/venus_hfi.h
@@ -124,6 +124,12 @@
BUS_IDX_MAX
};
+enum clock_state {
+ DISABLED_UNPREPARED,
+ ENABLED_PREPARED,
+ DISABLED_PREPARED
+};
+
struct vidc_mem_addr {
u8 *align_device_addr;
u8 *align_virtual_addr;
@@ -177,6 +183,11 @@
struct on_chip_mem ocmem;
};
+enum venus_hfi_state {
+ VENUS_STATE_DEINIT = 1,
+ VENUS_STATE_INIT,
+};
+
struct venus_hfi_device {
struct list_head list;
struct list_head sess_head;
@@ -184,8 +195,9 @@
u32 device_id;
u32 clk_load;
u32 bus_load[MSM_VIDC_MAX_DEVICES];
- u32 clocks_enabled;
- u32 power_enabled;
+ unsigned long ocmem_size;
+ enum clock_state clk_state;
+ bool power_enabled;
enum vidc_clocks clk_gating_level;
struct mutex read_lock;
struct mutex write_lock;
@@ -212,6 +224,7 @@
struct venus_resources resources;
struct msm_vidc_platform_resources *res;
struct regulator *gdsc;
+ enum venus_hfi_state state;
};
void venus_hfi_delete_device(void *device);
diff --git a/drivers/media/platform/msm/vidc/vidc_hfi.h b/drivers/media/platform/msm/vidc/vidc_hfi.h
index 75f583f..5566338 100644
--- a/drivers/media/platform/msm/vidc/vidc_hfi.h
+++ b/drivers/media/platform/msm/vidc/vidc_hfi.h
@@ -79,9 +79,8 @@
#define HFI_EXTRADATA_FRAME_RATE 0x00000007
#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_MPEG2_SEQDISP 0x0000000D
+#define HFI_EXTRADATA_STREAM_USERDATA 0x0000000E
#define HFI_EXTRADATA_FRAME_QP 0x0000000F
#define HFI_EXTRADATA_FRAME_BITS_INFO 0x00000010
#define HFI_EXTRADATA_MULTISLICE_INFO 0x7F100000
@@ -190,10 +189,6 @@
#define HFI_PROPERTY_PARAM_VDEC_FRAME_ASSEMBLY \
(HFI_PROPERTY_PARAM_VDEC_OX_START + 0x00E)
-#define HFI_PROPERTY_PARAM_VDEC_CLOSED_CAPTION_EXTRADATA \
- (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x00F)
-#define HFI_PROPERTY_PARAM_VDEC_AFD_EXTRADATA \
- (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x010)
#define HFI_PROPERTY_PARAM_VDEC_VC1_FRAMEDISP_EXTRADATA \
(HFI_PROPERTY_PARAM_VDEC_OX_START + 0x011)
#define HFI_PROPERTY_PARAM_VDEC_VC1_SEQDISP_EXTRADATA \
@@ -206,6 +201,8 @@
(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_PARAM_VDEC_STREAM_USERDATA_EXTRADATA \
+ (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x017)
#define HFI_PROPERTY_PARAM_VDEC_FRAME_QP_EXTRADATA \
(HFI_PROPERTY_PARAM_VDEC_OX_START + 0x018)
#define HFI_PROPERTY_PARAM_VDEC_FRAME_BITS_INFO_EXTRADATA \
diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_api.h b/drivers/media/platform/msm/vidc/vidc_hfi_api.h
index c764758..38c5bdb 100644
--- a/drivers/media/platform/msm/vidc/vidc_hfi_api.h
+++ b/drivers/media/platform/msm/vidc/vidc_hfi_api.h
@@ -44,6 +44,7 @@
#define HAL_BUFFERFLAG_READONLY 0x00000200
#define HAL_BUFFERFLAG_ENDOFSUBFRAME 0x00000400
#define HAL_BUFFERFLAG_EOSEQ 0x00200000
+#define HAL_BUFFERFLAG_MBAFF 0x08000000
#define HAL_BUFFERFLAG_DROP_FRAME 0x20000000
@@ -62,7 +63,7 @@
VIDC_ERR_BAD_HANDLE,
VIDC_ERR_NOT_SUPPORTED,
VIDC_ERR_BAD_STATE,
- VIDC_ERR_MAX_CLIENT,
+ VIDC_ERR_MAX_CLIENTS,
VIDC_ERR_IFRAME_EXPECTED,
VIDC_ERR_HW_FATAL,
VIDC_ERR_BITSTREAM_ERR,
@@ -90,8 +91,6 @@
HAL_EXTRADATA_FRAME_RATE,
HAL_EXTRADATA_PANSCAN_WINDOW,
HAL_EXTRADATA_RECOVERY_POINT_SEI,
- HAL_EXTRADATA_CLOSED_CAPTION_UD,
- HAL_EXTRADATA_AFD_UD,
HAL_EXTRADATA_MULTISLICE_INFO,
HAL_EXTRADATA_INDEX,
HAL_EXTRADATA_NUM_CONCEALED_MB,
@@ -102,6 +101,7 @@
HAL_EXTRADATA_FRAME_BITS_INFO,
HAL_EXTRADATA_LTR_INFO,
HAL_EXTRADATA_METADATA_MBI,
+ HAL_EXTRADATA_STREAM_USERDATA,
};
enum hal_property {
@@ -185,7 +185,9 @@
HAL_CONFIG_VENC_MARKLTRFRAME,
HAL_CONFIG_VENC_USELTRFRAME,
HAL_CONFIG_VENC_LTRPERIOD,
- HAL_PARAM_VENC_HIER_P_NUM_FRAMES,
+ HAL_CONFIG_VENC_HIER_P_NUM_FRAMES,
+ HAL_PARAM_VENC_HIER_P_MAX_ENH_LAYERS,
+ HAL_PARAM_VENC_ENABLE_INITIAL_QP,
};
enum hal_domain {
@@ -631,6 +633,13 @@
u32 layer_id;
};
+struct hal_initial_quantization {
+ u32 qpi;
+ u32 qpp;
+ u32 qpb;
+ u32 initqp_enable;
+};
+
struct hal_quantization_range {
u32 min_qp;
u32 max_qp;
@@ -1154,12 +1163,11 @@
int *domain_num, int *partition_num);
int (*load_fw)(void *dev);
void (*unload_fw)(void *dev);
+ int (*resurrect_fw)(void *dev);
int (*get_fw_info)(void *dev, enum fw_info info);
int (*get_info) (void *dev, enum dev_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);
int (*session_clean)(void *sess);
int (*get_core_capabilities)(void);
int (*power_enable)(void *dev);
diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_helper.h b/drivers/media/platform/msm/vidc/vidc_hfi_helper.h
index 5117266..7c62e77 100644
--- a/drivers/media/platform/msm/vidc/vidc_hfi_helper.h
+++ b/drivers/media/platform/msm/vidc/vidc_hfi_helper.h
@@ -305,8 +305,6 @@
(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x018)
#define HFI_PROPERTY_PARAM_VENC_MULTIREF_P \
(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x019)
-#define HFI_PROPERTY_PARAM_VENC_HIER_P_NUM_ENH_LAYER \
- (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x01A)
#define HFI_PROPERTY_PARAM_VENC_H264_NAL_SVC_EXT \
(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x01B)
#define HFI_PROPERTY_PARAM_VENC_LTRMODE \
@@ -319,6 +317,10 @@
(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x01F)
#define HFI_PROPERTY_PARAM_VENC_MAX_NUM_B_FRAMES \
(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x020)
+#define HFI_PROPERTY_PARAM_VENC_HIER_P_MAX_NUM_ENH_LAYER \
+ (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x026)
+#define HFI_PROPERTY_PARAM_VENC_INITIAL_QP \
+ (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x028)
#define HFI_PROPERTY_CONFIG_VENC_COMMON_START \
(HFI_DOMAIN_BASE_VENC + HFI_ARCH_COMMON_OFFSET + 0x6000)
#define HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE \
@@ -342,6 +344,8 @@
(HFI_PROPERTY_CONFIG_VENC_COMMON_START + 0x009)
#define HFI_PROPERTY_CONFIG_VENC_USELTRFRAME \
(HFI_PROPERTY_CONFIG_VENC_COMMON_START + 0x00A)
+#define HFI_PROPERTY_CONFIG_VENC_HIER_P_ENH_LAYER \
+ (HFI_PROPERTY_CONFIG_VENC_COMMON_START + 0x00B)
#define HFI_PROPERTY_CONFIG_VENC_LTRPERIOD \
(HFI_PROPERTY_CONFIG_VENC_COMMON_START + 0x00C)
#define HFI_PROPERTY_CONFIG_VPE_COMMON_START \
@@ -531,6 +535,13 @@
u32 layer_id;
};
+struct hfi_initial_quantization {
+ u32 qp_i;
+ u32 qp_p;
+ u32 qp_b;
+ u32 init_qp_enable;
+};
+
struct hfi_quantization_range {
u32 min_qp;
u32 max_qp;
diff --git a/drivers/media/platform/msm/wfd/vsg-subdev.c b/drivers/media/platform/msm/wfd/vsg-subdev.c
index 433468e..960e45c 100644
--- a/drivers/media/platform/msm/wfd/vsg-subdev.c
+++ b/drivers/media/platform/msm/wfd/vsg-subdev.c
@@ -337,6 +337,7 @@
static int vsg_start(struct v4l2_subdev *sd)
{
struct vsg_context *context = NULL;
+ int rc = 0;
if (!sd) {
WFD_MSG_ERR("ERROR, invalid arguments into %s\n", __func__);
@@ -345,18 +346,24 @@
context = (struct vsg_context *)sd->dev_priv;
+ mutex_lock(&context->mutex);
if (context->state == VSG_STATE_STARTED) {
WFD_MSG_ERR("VSG not stopped, start not allowed\n");
- return -EINPROGRESS;
+ rc = -EINPROGRESS;
+ goto err_bad_state;
} else if (context->state == VSG_STATE_ERROR) {
WFD_MSG_ERR("VSG in error state, not allowed to restart\n");
- return -ENOTRECOVERABLE;
+ rc = -ENOTRECOVERABLE;
+ goto err_bad_state;
}
context->state = VSG_STATE_STARTED;
hrtimer_start(&context->threshold_timer, ns_to_ktime(context->
max_frame_interval), HRTIMER_MODE_REL);
- return 0;
+
+err_bad_state:
+ mutex_unlock(&context->mutex);
+ return rc;
}
static int vsg_stop(struct v4l2_subdev *sd)
diff --git a/drivers/media/radio/radio-iris.c b/drivers/media/radio/radio-iris.c
index 9f18508..07d3fde 100644
--- a/drivers/media/radio/radio-iris.c
+++ b/drivers/media/radio/radio-iris.c
@@ -1892,13 +1892,6 @@
return;
}
- if (radio->mode == FM_RECV_TURNING_ON) {
- radio->mode = FM_RECV;
- iris_q_event(radio, IRIS_EVT_RADIO_READY);
- } else if (radio->mode == FM_TRANS_TURNING_ON) {
- radio->mode = FM_TRANS;
- iris_q_event(radio, IRIS_EVT_RADIO_READY);
- }
radio_hci_req_complete(hdev, rsp->status);
}
@@ -3728,7 +3721,6 @@
}
saved_val = radio->mute_mode.hard_mute;
radio->mute_mode.hard_mute = ctrl->value;
- radio->mute_mode.soft_mute = IOC_SFT_MUTE;
retval = hci_set_fm_mute_mode(
&radio->mute_mode,
radio->fm_hdev);
@@ -3777,7 +3769,19 @@
radio->mode = FM_OFF;
goto END;
} else {
- initialise_recv(radio);
+ retval = initialise_recv(radio);
+ if (retval < 0) {
+ FMDERR("Error while initialising"\
+ "radio %d\n", retval);
+ hci_cmd(HCI_FM_DISABLE_RECV_CMD,
+ radio->fm_hdev);
+ radio->mode = FM_OFF;
+ goto END;
+ }
+ }
+ if (radio->mode == FM_RECV_TURNING_ON) {
+ radio->mode = FM_RECV;
+ iris_q_event(radio, IRIS_EVT_RADIO_READY);
}
break;
case FM_TRANS:
@@ -3794,7 +3798,19 @@
radio->mode = FM_OFF;
goto END;
} else {
- initialise_trans(radio);
+ retval = initialise_trans(radio);
+ if (retval < 0) {
+ FMDERR("Error while initialising"\
+ "radio %d\n", retval);
+ hci_cmd(HCI_FM_DISABLE_TRANS_CMD,
+ radio->fm_hdev);
+ radio->mode = FM_OFF;
+ goto END;
+ }
+ }
+ if (radio->mode == FM_TRANS_TURNING_ON) {
+ radio->mode = FM_TRANS;
+ iris_q_event(radio, IRIS_EVT_RADIO_READY);
}
break;
case FM_OFF:
diff --git a/drivers/media/video/videobuf2-core.c b/drivers/media/video/videobuf2-core.c
index 668cc73..6cea218 100644
--- a/drivers/media/video/videobuf2-core.c
+++ b/drivers/media/video/videobuf2-core.c
@@ -513,13 +513,6 @@
return -EINVAL;
}
- /*
- * If the same number of buffers and memory access method is requested
- * then return immediately.
- */
- if (q->memory == req->memory && req->count == q->num_buffers)
- return 0;
-
if (req->count == 0 || q->num_buffers != 0 || q->memory != req->memory) {
/*
* We already have buffers allocated, so first check if they
@@ -1104,9 +1097,13 @@
* consistent after getting driver's lock back.
*/
if (q->memory == V4L2_MEMORY_USERPTR) {
- mmap_sem = ¤t->active_mm->mmap_sem;
+ bool mm_exists = !!current->mm;
+
+ mmap_sem = mm_exists ? ¤t->mm->mmap_sem : NULL;
call_qop(q, wait_prepare, q);
- down_read(mmap_sem);
+ /* kthreads have no userspace, hence no pages to lock */
+ if (mmap_sem)
+ down_read(mmap_sem);
call_qop(q, wait_finish, q);
}
diff --git a/drivers/mfd/wcd9xxx-core.c b/drivers/mfd/wcd9xxx-core.c
index 5eb359e..156ce24 100644
--- a/drivers/mfd/wcd9xxx-core.c
+++ b/drivers/mfd/wcd9xxx-core.c
@@ -1719,7 +1719,6 @@
{
struct wcd9xxx *wcd9xxx = slim_get_devicedata(sldev);
- dev_info(wcd9xxx->dev, "%s: device down\n", __func__);
if (!wcd9xxx) {
pr_err("%s: wcd9xxx is NULL\n", __func__);
return -EINVAL;
diff --git a/drivers/mfd/wcd9xxx-irq.c b/drivers/mfd/wcd9xxx-irq.c
index 7644984..d9cf60a 100644
--- a/drivers/mfd/wcd9xxx-irq.c
+++ b/drivers/mfd/wcd9xxx-irq.c
@@ -615,6 +615,10 @@
static int virq_to_phyirq(struct wcd9xxx_core_resource *wcd9xxx_res, int virq)
{
struct irq_data *irq_data = irq_get_irq_data(virq);
+ if (unlikely(!irq_data)) {
+ pr_err("%s: irq_data is NULL", __func__);
+ return -EINVAL;
+ }
return irq_data->hwirq;
}
@@ -664,6 +668,10 @@
} else {
dev_dbg(&pdev->dev, "%s: virq = %d\n", __func__, irq);
domain = irq_find_host(pdev->dev.of_node);
+ if (unlikely(!domain)) {
+ pr_err("%s: domain is NULL", __func__);
+ return -EINVAL;
+ }
data = (struct wcd9xxx_irq_drv_data *)domain->host_data;
data->irq = irq;
wmb();
@@ -679,6 +687,10 @@
struct wcd9xxx_irq_drv_data *data;
domain = irq_find_host(pdev->dev.of_node);
+ if (unlikely(!domain)) {
+ pr_err("%s: domain is NULL", __func__);
+ return -EINVAL;
+ }
data = (struct wcd9xxx_irq_drv_data *)domain->host_data;
data->irq = 0;
wmb();
diff --git a/drivers/mfd/wcd9xxx-slimslave.c b/drivers/mfd/wcd9xxx-slimslave.c
index 81262b58..4f42181 100644
--- a/drivers/mfd/wcd9xxx-slimslave.c
+++ b/drivers/mfd/wcd9xxx-slimslave.c
@@ -499,7 +499,8 @@
/* This function is called with mutex acquired */
int wcd9xxx_tx_vport_validation(u32 vtable, u32 port_id,
- struct wcd9xxx_codec_dai_data *codec_dai)
+ struct wcd9xxx_codec_dai_data *codec_dai,
+ u32 num_codec_dais)
{
struct wcd9xxx_ch *ch;
int ret = 0;
@@ -508,18 +509,25 @@
pr_debug("%s: vtable 0x%x port_id %u size %d\n", __func__,
vtable, port_id, size);
for_each_set_bit(index, (unsigned long *)&vtable, size) {
- list_for_each_entry(ch,
- &codec_dai[index].wcd9xxx_ch_list,
- list) {
- pr_debug("%s: index %u ch->port %u vtable 0x%x\n",
- __func__, index, ch->port, vtable);
- if (ch->port == port_id) {
- pr_err("%s: TX%u is used by AIF%u_CAP Mixer\n",
- __func__, port_id + 1,
- (index + 1)/2);
- ret = -EINVAL;
- break;
+ if (index < num_codec_dais) {
+ list_for_each_entry(ch,
+ &codec_dai[index].wcd9xxx_ch_list,
+ list) {
+ pr_debug("%s: index %u ch->port %u vtable 0x%x\n",
+ __func__, index, ch->port,
+ vtable);
+ if (ch->port == port_id) {
+ pr_err("%s: TX%u is used by AIF%u_CAP Mixer\n",
+ __func__, port_id + 1,
+ (index + 1)/2);
+ ret = -EINVAL;
+ break;
+ }
}
+ } else {
+ pr_err("%s: Invalid index %d of codec dai",
+ __func__, index);
+ ret = -EINVAL;
}
if (ret)
break;
diff --git a/drivers/misc/qfp_fuse.c b/drivers/misc/qfp_fuse.c
index 3a088dc..f271f96 100644
--- a/drivers/misc/qfp_fuse.c
+++ b/drivers/misc/qfp_fuse.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011, 2014 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
@@ -28,7 +28,7 @@
/*
* Time QFPROM requires to reliably burn a fuse.
*/
-#define QFPROM_BLOW_TIMEOUT_US 10
+#define QFPROM_BLOW_TIMEOUT_US 20
#define QFPROM_BLOW_TIMER_OFFSET 0x2038
/*
* Denotes number of cycles required to blow the fuse.
@@ -42,6 +42,10 @@
#define QFP_FUSE_READY 0x01
#define QFP_FUSE_OFF 0x00
+#define QFP_FUSE_BUF_SIZE 64
+#define UINT32_MAX (0xFFFFFFFFU)
+
+
struct qfp_priv_t {
uint32_t base;
uint32_t end;
@@ -53,6 +57,23 @@
/* We need only one instance of this for the driver */
static struct qfp_priv_t *qfp_priv;
+static inline bool is_usr_req_valid(const struct qfp_fuse_req *req)
+{
+ uint32_t size = qfp_priv->end - qfp_priv->base;
+ uint32_t req_size;
+
+ if (req->size >= (UINT32_MAX / sizeof(uint32_t)))
+ return false;
+ req_size = req->size * sizeof(uint32_t);
+ if ((req_size == 0) || (req_size > size))
+ return false;
+ if (req->offset >= size)
+ return false;
+ if ((req->offset + req_size) > size)
+ return false;
+
+ return true;
+}
static int qfp_fuse_open(struct inode *inode, struct file *filp)
{
@@ -177,7 +198,9 @@
{
int err = 0;
struct qfp_fuse_req req;
- u32 *buf = NULL;
+ u32 fuse_buf[QFP_FUSE_BUF_SIZE];
+ u32 *buf = fuse_buf;
+ u32 *ptr = NULL;
int i;
/* Verify user arguments. */
@@ -199,25 +222,21 @@
}
/* Check for limits */
- if (!req.size) {
- pr_err("Request size zero.\n");
- err = -EFAULT;
+ if (is_usr_req_valid(&req) == false) {
+ pr_err("Invalid request\n");
+ err = -EINVAL;
break;
}
- if (qfp_priv->base + req.offset + (req.size - 1) * 4 >
- qfp_priv->end) {
- pr_err("Req size exceeds QFPROM addr space\n");
- err = -EFAULT;
- break;
- }
-
- /* Allocate memory for buffer */
- buf = kzalloc(req.size * 4, GFP_KERNEL);
- if (buf == NULL) {
- pr_alert("No memory for data\n");
- err = -ENOMEM;
- break;
+ if (req.size > QFP_FUSE_BUF_SIZE) {
+ /* Allocate memory for buffer */
+ ptr = kzalloc(req.size * 4, GFP_KERNEL);
+ if (ptr == NULL) {
+ pr_alert("No memory for data\n");
+ err = -ENOMEM;
+ break;
+ }
+ buf = ptr;
}
if (mutex_lock_interruptible(&qfp_priv->lock)) {
@@ -251,24 +270,21 @@
break;
}
/* Check for limits */
- if (!req.size) {
- pr_err("Request size zero.\n");
- err = -EFAULT;
- break;
- }
- if (qfp_priv->base + req.offset + (req.size - 1) * 4 >
- qfp_priv->end) {
- pr_err("Req size exceeds QFPROM space\n");
- err = -EFAULT;
+ if (is_usr_req_valid(&req) == false) {
+ pr_err("Invalid request\n");
+ err = -EINVAL;
break;
}
- /* Allocate memory for buffer */
- buf = kzalloc(4 * (req.size), GFP_KERNEL);
- if (buf == NULL) {
- pr_alert("No memory for data\n");
- err = -ENOMEM;
- break;
+ if (req.size > QFP_FUSE_BUF_SIZE) {
+ /* Allocate memory for buffer */
+ ptr = kzalloc(req.size * 4, GFP_KERNEL);
+ if (ptr == NULL) {
+ pr_alert("No memory for data\n");
+ err = -ENOMEM;
+ break;
+ }
+ buf = ptr;
}
/* Copy user data to local buffer */
@@ -296,7 +312,7 @@
pr_err("Invalid ioctl command.\n");
return -ENOTTY;
}
- kfree(buf);
+ kfree(ptr);
return err;
}
diff --git a/drivers/misc/qseecom.c b/drivers/misc/qseecom.c
index efafa23..23edc8a 100644
--- a/drivers/misc/qseecom.c
+++ b/drivers/misc/qseecom.c
@@ -61,7 +61,7 @@
#define QSEE_CE_CLK_100MHZ 100000000
#define QSEECOM_MAX_SG_ENTRY 512
-#define QSEECOM_DISK_ENCRYTPION_KEY_ID 0
+#define QSEECOM_INVALID_KEY_ID 0xff
/* Save partition image hash for authentication check */
#define SCM_SAVE_PARTITION_HASH_ID 0x01
@@ -127,6 +127,7 @@
uint32_t qsee_ce_hw_instance;
uint32_t hlos_ce_hw_instance;
uint32_t disk_encrypt_pipe;
+ uint32_t file_encrypt_pipe;
};
struct qseecom_clk {
@@ -156,6 +157,7 @@
uint32_t qsee_version;
struct device *pdev;
bool commonlib_loaded;
+ struct ion_handle *cmnlib_ion_handle;
struct ce_hw_usage_info ce_info;
int qsee_bw_count;
@@ -166,6 +168,8 @@
struct qseecom_clk ce_drv;
bool support_bus_scaling;
+ bool support_fde;
+ bool support_pfe;
uint32_t cumulative_mode;
enum qseecom_bandwidth_request_mode current_mode;
struct timer_list bw_scale_down_timer;
@@ -223,8 +227,22 @@
uint32_t len;
};
-uint8_t *key_id_array[QSEECOM_KEY_ID_SIZE] = {
- "Disk Encryption"
+struct qseecom_key_id_usage_desc {
+ uint8_t desc[QSEECOM_KEY_ID_SIZE];
+};
+
+static struct qseecom_key_id_usage_desc key_id_array[] = {
+ {
+ .desc = "Undefined Usage Index",
+ },
+
+ {
+ .desc = "Full Disk Encryption",
+ },
+
+ {
+ .desc = "Per File Encryption",
+ },
};
/* Function proto types */
@@ -287,14 +305,18 @@
/* Get the handle of the shared fd */
svc->ihandle = ion_import_dma_buf(qseecom.ion_clnt,
listener->ifd_data_fd);
- if (svc->ihandle == NULL) {
+ if (IS_ERR_OR_NULL(svc->ihandle)) {
pr_err("Ion client could not retrieve the handle\n");
return -ENOMEM;
}
/* Get the physical address of the ION BUF */
ret = ion_phys(qseecom.ion_clnt, svc->ihandle, &pa, &svc->sb_length);
-
+ if (ret) {
+ pr_err("Cannot get phys_addr for the Ion Client, ret = %d\n",
+ ret);
+ return ret;
+ }
/* Populate the structure for sending scm call to load image */
svc->sb_virt = (char *) ion_map_kernel(qseecom.ion_clnt, svc->ihandle);
svc->sb_phys = pa;
@@ -339,7 +361,6 @@
return -EFAULT;
data->listener.id = 0;
- data->type = QSEECOM_LISTENER_SERVICE;
if (!__qseecom_is_svc_unique(data, &rcvd_lstnr)) {
pr_err("Service is not unique and is already registered\n");
data->released = true;
@@ -503,26 +524,31 @@
return;
}
-static void __qseecom_decrease_clk_ref_count(enum qseecom_ce_hw_instance ce)
+static int __qseecom_decrease_clk_ref_count(enum qseecom_ce_hw_instance ce)
{
struct qseecom_clk *qclk;
+ int ret = 0;
mutex_lock(&clk_access_lock);
if (ce == CLK_QSEE)
qclk = &qseecom.qsee;
else
qclk = &qseecom.ce_drv;
- if (qclk->clk_access_cnt == 0) {
- mutex_unlock(&clk_access_lock);
- return;
+ if (qclk->clk_access_cnt > 2) {
+ pr_err("Invalid clock ref count %d\n", qclk->clk_access_cnt);
+ ret = -EINVAL;
+ goto err_dec_ref_cnt;
}
- qclk->clk_access_cnt--;
+ if (qclk->clk_access_cnt == 2)
+ qclk->clk_access_cnt--;
+
+err_dec_ref_cnt:
mutex_unlock(&clk_access_lock);
- return;
+ return ret;
}
-static int qseecom_scale_bus_bandwidth_timer(uint32_t mode, uint32_t duration)
+static int qseecom_scale_bus_bandwidth_timer(uint32_t mode)
{
int32_t ret = 0;
int32_t request_mode = INACTIVE;
@@ -537,11 +563,23 @@
request_mode = mode;
}
- __qseecom_set_msm_bus_request(request_mode);
- if (qseecom.timer_running) {
- __qseecom_decrease_clk_ref_count(CLK_QSEE);
- del_timer_sync(&(qseecom.bw_scale_down_timer));
+ ret = __qseecom_set_msm_bus_request(request_mode);
+ if (ret) {
+ pr_err("set msm bus request failed (%d),request_mode (%d)\n",
+ ret, request_mode);
+ goto err_scale_timer;
}
+
+ if (qseecom.timer_running) {
+ ret = __qseecom_decrease_clk_ref_count(CLK_QSEE);
+ if (ret) {
+ pr_err("Failed to decrease clk ref count.\n");
+ goto err_scale_timer;
+ }
+ del_timer_sync(&(qseecom.bw_scale_down_timer));
+ qseecom.timer_running = false;
+ }
+err_scale_timer:
mutex_unlock(&qsee_bw_mutex);
return ret;
}
@@ -598,18 +636,24 @@
return ret;
}
+static void __qseecom_add_bw_scale_down_timer(uint32_t duration)
+{
+ mutex_lock(&qsee_bw_mutex);
+ qseecom.bw_scale_down_timer.expires = jiffies +
+ msecs_to_jiffies(duration);
+ mod_timer(&(qseecom.bw_scale_down_timer),
+ qseecom.bw_scale_down_timer.expires);
+ qseecom.timer_running = true;
+ mutex_unlock(&qsee_bw_mutex);
+}
+
static void __qseecom_disable_clk_scale_down(struct qseecom_dev_handle *data)
{
if (!qseecom.support_bus_scaling)
qsee_disable_clock_vote(data, CLK_SFPB);
- else {
- mutex_lock(&qsee_bw_mutex);
- qseecom.bw_scale_down_timer.expires = jiffies +
- msecs_to_jiffies(QSEECOM_LOAD_APP_CRYPTO_TIMEOUT);
- add_timer(&(qseecom.bw_scale_down_timer));
- qseecom.timer_running = true;
- mutex_unlock(&qsee_bw_mutex);
- }
+ else
+ __qseecom_add_bw_scale_down_timer(
+ QSEECOM_LOAD_APP_CRYPTO_TIMEOUT);
return;
}
@@ -617,8 +661,9 @@
{
int ret = 0;
if (qseecom.support_bus_scaling) {
- qseecom_scale_bus_bandwidth_timer(
- MEDIUM, QSEECOM_LOAD_APP_CRYPTO_TIMEOUT);
+ ret = qseecom_scale_bus_bandwidth_timer(MEDIUM);
+ if (ret)
+ pr_err("Failed to set bw MEDIUM.\n");
} else {
ret = qsee_vote_for_clock(data, CLK_SFPB);
if (ret)
@@ -658,6 +703,12 @@
}
/* Get the physical address of the ION BUF */
ret = ion_phys(qseecom.ion_clnt, data->client.ihandle, &pa, &len);
+ if (ret) {
+
+ pr_err("Cannot get phys_addr for the Ion Client, ret = %d\n",
+ ret);
+ return ret;
+ }
/* Populate the structure for sending scm call to load image */
data->client.sb_virt = (char *) ion_map_kernel(qseecom.ion_clnt,
data->client.ihandle);
@@ -830,19 +881,27 @@
pr_err("copy_from_user failed\n");
return -EFAULT;
}
+
+ if (qseecom.support_bus_scaling) {
+ mutex_lock(&qsee_bw_mutex);
+ ret = __qseecom_register_bus_bandwidth_needs(data, MEDIUM);
+ mutex_unlock(&qsee_bw_mutex);
+ if (ret)
+ return ret;
+ }
+
/* Vote for the SFPB clock */
ret = __qseecom_enable_clk_scale_up(data);
if (ret)
- return ret;
+ goto enable_clk_err;
+
req.qsee_cmd_id = QSEOS_APP_LOOKUP_COMMAND;
load_img_req.img_name[MAX_APP_NAME_SIZE-1] = '\0';
memcpy(req.app_name, load_img_req.img_name, MAX_APP_NAME_SIZE);
ret = __qseecom_check_app_exists(req);
- if (ret < 0) {
- __qseecom_disable_clk_scale_down(data);
- return ret;
- }
+ if (ret < 0)
+ goto loadapp_err;
app_id = ret;
if (app_id) {
@@ -866,12 +925,17 @@
load_img_req.ifd_data_fd);
if (IS_ERR_OR_NULL(ihandle)) {
pr_err("Ion client could not retrieve the handle\n");
- __qseecom_disable_clk_scale_down(data);
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto loadapp_err;
}
/* Get the physical address of the ION BUF */
ret = ion_phys(qseecom.ion_clnt, ihandle, &pa, &len);
+ if (ret) {
+ pr_err("Cannot get phys_addr for the Ion Client, ret = %d\n",
+ ret);
+ goto loadapp_err;
+ }
/* Populate the structure for sending scm call to load image */
memcpy(load_req.app_name, load_img_req.img_name,
@@ -891,16 +955,16 @@
pr_err("scm_call to load app failed\n");
if (!IS_ERR_OR_NULL(ihandle))
ion_free(qseecom.ion_clnt, ihandle);
- __qseecom_disable_clk_scale_down(data);
- return -EINVAL;
+ ret = -EINVAL;
+ goto loadapp_err;
}
if (resp.result == QSEOS_RESULT_FAILURE) {
pr_err("scm_call rsp.result is QSEOS_RESULT_FAILURE\n");
if (!IS_ERR_OR_NULL(ihandle))
ion_free(qseecom.ion_clnt, ihandle);
- __qseecom_disable_clk_scale_down(data);
- return -EFAULT;
+ ret = -EFAULT;
+ goto loadapp_err;
}
if (resp.result == QSEOS_RESULT_INCOMPLETE) {
@@ -910,8 +974,8 @@
ret);
if (!IS_ERR_OR_NULL(ihandle))
ion_free(qseecom.ion_clnt, ihandle);
- __qseecom_disable_clk_scale_down(data);
- return ret;
+ ret = -EFAULT;
+ goto loadapp_err;
}
}
@@ -920,8 +984,8 @@
resp.result);
if (!IS_ERR_OR_NULL(ihandle))
ion_free(qseecom.ion_clnt, ihandle);
- __qseecom_disable_clk_scale_down(data);
- return -EFAULT;
+ ret = -EFAULT;
+ goto loadapp_err;
}
app_id = resp.data;
@@ -929,8 +993,8 @@
entry = kmalloc(sizeof(*entry), GFP_KERNEL);
if (!entry) {
pr_err("kmalloc failed\n");
- __qseecom_disable_clk_scale_down(data);
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto loadapp_err;
}
entry->app_id = app_id;
entry->ref_cnt = 1;
@@ -952,11 +1016,18 @@
if (copy_to_user(argp, &load_img_req, sizeof(load_img_req))) {
pr_err("copy_to_user failed\n");
kzfree(entry);
- __qseecom_disable_clk_scale_down(data);
- return -EFAULT;
+ ret = -EFAULT;
}
+
+loadapp_err:
__qseecom_disable_clk_scale_down(data);
- return 0;
+enable_clk_err:
+ if (qseecom.support_bus_scaling) {
+ mutex_lock(&qsee_bw_mutex);
+ qseecom_unregister_bus_bandwidth_needs(data);
+ mutex_unlock(&qsee_bw_mutex);
+ }
+ return ret;
}
static int __qseecom_cleanup_app(struct qseecom_dev_handle *data)
@@ -985,7 +1056,8 @@
return ret;
}
-static int qseecom_unload_app(struct qseecom_dev_handle *data)
+static int qseecom_unload_app(struct qseecom_dev_handle *data,
+ bool app_crash)
{
unsigned long flags;
int ret = 0;
@@ -997,17 +1069,23 @@
if (data->client.app_id > 0) {
spin_lock_irqsave(&qseecom.registered_app_list_lock, flags);
list_for_each_entry(ptr_app, &qseecom.registered_app_list_head,
- list) {
+ list) {
if (ptr_app->app_id == data->client.app_id) {
found_app = true;
- if (ptr_app->ref_cnt == 1) {
+ if (app_crash) {
+ ptr_app->ref_cnt = 0;
unload = true;
break;
} else {
- ptr_app->ref_cnt--;
- pr_debug("Can't unload app(%d) inuse\n",
- ptr_app->app_id);
- break;
+ if (ptr_app->ref_cnt == 1) {
+ unload = true;
+ break;
+ } else {
+ ptr_app->ref_cnt--;
+ pr_debug("Can't unload app(%d) inuse\n",
+ ptr_app->app_id);
+ break;
+ }
}
}
}
@@ -1015,7 +1093,7 @@
flags);
if (found_app == false) {
pr_err("Cannot find app with id = %d\n",
- data->client.app_id);
+ data->client.app_id);
return -EINVAL;
}
}
@@ -1039,7 +1117,7 @@
&resp, sizeof(resp));
if (ret) {
pr_err("scm_call to unload app (id = %d) failed\n",
- req.app_id);
+ req.app_id);
return -EFAULT;
} else {
pr_warn("App id %d now unloaded\n", req.app_id);
@@ -1048,7 +1126,7 @@
ret = __qseecom_process_incomplete_cmd(data, &resp);
if (ret) {
pr_err("process_incomplete_cmd fail err: %d\n",
- ret);
+ ret);
return ret;
}
}
@@ -1168,10 +1246,9 @@
}
if (qseecom.support_bus_scaling) {
- qseecom_scale_bus_bandwidth_timer(HIGH,
- QSEECOM_SEND_CMD_CRYPTO_TIMEOUT);
+ ret = qseecom_scale_bus_bandwidth_timer(HIGH);
if (ret) {
- pr_err("Fail to set bw HIGH%d\n", ret);
+ pr_err("Fail to set bw HIGH\n");
return ret;
}
} else {
@@ -1203,15 +1280,9 @@
qsee_disable_clock_vote(data, CLK_DFAB);
qsee_disable_clock_vote(data, CLK_SFPB);
} else {
- mutex_lock(&qsee_bw_mutex);
- qseecom.bw_scale_down_timer.expires = jiffies +
- msecs_to_jiffies(
+ __qseecom_add_bw_scale_down_timer(
QSEECOM_SEND_CMD_CRYPTO_TIMEOUT);
- add_timer(&(qseecom.bw_scale_down_timer));
- qseecom.timer_running = true;
- mutex_unlock(&qsee_bw_mutex);
}
-
goto exit;
}
@@ -1239,12 +1310,8 @@
qsee_disable_clock_vote(data, CLK_DFAB);
qsee_disable_clock_vote(data, CLK_SFPB);
} else {
- mutex_lock(&qsee_bw_mutex);
- qseecom.bw_scale_down_timer.expires = jiffies +
- msecs_to_jiffies(QSEECOM_SEND_CMD_CRYPTO_TIMEOUT);
- add_timer(&(qseecom.bw_scale_down_timer));
- qseecom.timer_running = true;
- mutex_unlock(&qsee_bw_mutex);
+ __qseecom_add_bw_scale_down_timer(
+ QSEECOM_SEND_CMD_CRYPTO_TIMEOUT);
}
exit:
@@ -1506,6 +1573,13 @@
pr_err("response buffer address not within shared bufffer\n");
return -EINVAL;
}
+
+ if (req.cmd_req_len == 0 || req.cmd_req_len > data->client.sb_length ||
+ req.resp_len > data->client.sb_length) {
+ pr_err("cmd or response buffer length not valid\n");
+ return -EINVAL;
+ }
+
send_cmd_req.cmd_req_buf = req.cmd_req_buf;
send_cmd_req.cmd_req_len = req.cmd_req_len;
send_cmd_req.resp_buf = req.resp_buf;
@@ -1560,7 +1634,7 @@
if (wait_event_freezable(this_lstnr->rcv_req_wq,
__qseecom_listener_has_rcvd_req(data,
this_lstnr))) {
- pr_warning("Interrupted: exiting Listener Service = %d\n",
+ pr_debug("Interrupted: exiting Listener Service = %d\n",
(uint32_t)data->listener.id);
/* woken up for different reason */
return -ERESTARTSYS;
@@ -1718,10 +1792,22 @@
/* Populate the remaining parameters */
load_req.qsee_cmd_id = QSEOS_APP_START_COMMAND;
memcpy(load_req.app_name, appname, MAX_APP_NAME_SIZE);
+
+ if (qseecom.support_bus_scaling) {
+ mutex_lock(&qsee_bw_mutex);
+ ret = __qseecom_register_bus_bandwidth_needs(data, MEDIUM);
+ mutex_unlock(&qsee_bw_mutex);
+ if (ret) {
+ kzfree(img_data);
+ return ret;
+ }
+ }
+
ret = __qseecom_enable_clk_scale_up(data);
if (ret) {
kzfree(img_data);
- return -EIO;
+ ret = -EIO;
+ goto loadfw_err;
}
__cpuc_flush_dcache_area((void *)img_data, fw_size);
@@ -1732,8 +1818,8 @@
kzfree(img_data);
if (ret) {
pr_err("scm_call to load failed : ret %d\n", ret);
- __qseecom_disable_clk_scale_down(data);
- return -EIO;
+ ret = -EIO;
+ goto loadfw_err;
}
switch (resp.result) {
@@ -1755,39 +1841,76 @@
ret = -EINVAL;
break;
}
- __qseecom_disable_clk_scale_down(data);
+loadfw_err:
+ __qseecom_disable_clk_scale_down(data);
+ if (qseecom.support_bus_scaling) {
+ mutex_lock(&qsee_bw_mutex);
+ qseecom_unregister_bus_bandwidth_needs(data);
+ mutex_unlock(&qsee_bw_mutex);
+ }
return ret;
}
static int qseecom_load_commonlib_image(struct qseecom_dev_handle *data)
{
- int32_t ret = 0;
+ int ret = 0;
+ int len = 0;
uint32_t fw_size = 0;
struct qseecom_load_app_ireq load_req = {0, 0, 0, 0};
struct qseecom_command_scm_resp resp;
u8 *img_data = NULL;
+ ion_phys_addr_t pa;
if (__qseecom_get_fw_size("cmnlib", &fw_size))
return -EIO;
- img_data = kzalloc(fw_size, GFP_KERNEL);
- if (!img_data) {
- pr_err("Mem allocation for lib image data failed\n");
+ qseecom.cmnlib_ion_handle = ion_alloc(qseecom.ion_clnt, fw_size,
+ SZ_4K, ION_HEAP(ION_QSECOM_HEAP_ID), 0);
+ if (IS_ERR_OR_NULL(qseecom.cmnlib_ion_handle)) {
+ pr_err("ION alloc failed\n");
return -ENOMEM;
}
+
+ img_data = (u8 *)ion_map_kernel(qseecom.ion_clnt,
+ qseecom.cmnlib_ion_handle);
+ if (IS_ERR_OR_NULL(img_data)) {
+ pr_err("ION memory mapping for cmnlib failed\n");
+ ret = -ENOMEM;
+ goto exit_ion_free;
+ }
ret = __qseecom_get_fw_data("cmnlib", img_data, &load_req);
if (ret) {
- kzfree(img_data);
- return -EIO;
+ ret = -EIO;
+ goto exit_ion_unmap_kernel;
+ }
+ /* Get the physical address of the ION BUF */
+ ret = ion_phys(qseecom.ion_clnt, qseecom.cmnlib_ion_handle,
+ &pa, &len);
+ load_req.phy_addr = (s32)pa;
+ if (ret) {
+ pr_err("physical memory retrieval failure\n");
+ ret = -EIO;
+ goto exit_ion_unmap_kernel;
}
/* Populate the remaining parameters */
load_req.qsee_cmd_id = QSEOS_LOAD_SERV_IMAGE_COMMAND;
+
+ if (qseecom.support_bus_scaling) {
+ mutex_lock(&qsee_bw_mutex);
+ ret = __qseecom_register_bus_bandwidth_needs(data, MEDIUM);
+ mutex_unlock(&qsee_bw_mutex);
+ if (ret) {
+ ret = -EIO;
+ goto exit_ion_unmap_kernel;
+ }
+ }
+
/* Vote for the SFPB clock */
ret = __qseecom_enable_clk_scale_up(data);
if (ret) {
- kzfree(img_data);
- return -EIO;
+ ret = -EIO;
+ goto exit_unregister_bus_bw_need;
}
__cpuc_flush_dcache_area((void *)img_data, fw_size);
@@ -1798,30 +1921,52 @@
if (ret) {
pr_err("scm_call to load failed : ret %d\n", ret);
ret = -EIO;
- } else {
- switch (resp.result) {
- case QSEOS_RESULT_SUCCESS:
- break;
- case QSEOS_RESULT_FAILURE:
- pr_err("scm call failed w/response result%d\n",
- resp.result);
- ret = -EINVAL;
- break;
- case QSEOS_RESULT_INCOMPLETE:
- ret = __qseecom_process_incomplete_cmd(data, &resp);
- if (ret)
- pr_err("process_incomplete_cmd failed err: %d\n",
- ret);
- break;
- default:
- pr_err("scm call return unknown response %d\n",
- resp.result);
- ret = -EINVAL;
- break;
- }
+ goto exit_disable_clk_vote;
}
- kzfree(img_data);
+
+ switch (resp.result) {
+ case QSEOS_RESULT_SUCCESS:
+ break;
+ case QSEOS_RESULT_FAILURE:
+ pr_err("scm call failed w/response result%d\n", resp.result);
+ ret = -EINVAL;
+ goto exit_disable_clk_vote;
+ case QSEOS_RESULT_INCOMPLETE:
+ ret = __qseecom_process_incomplete_cmd(data, &resp);
+ if (ret) {
+ pr_err("process_incomplete_cmd failed err: %d\n", ret);
+ goto exit_disable_clk_vote;
+ }
+ break;
+ default:
+ pr_err("scm call return unknown response %d\n", resp.result);
+ ret = -EINVAL;
+ goto exit_disable_clk_vote;
+ }
__qseecom_disable_clk_scale_down(data);
+ if (qseecom.support_bus_scaling) {
+ mutex_lock(&qsee_bw_mutex);
+ qseecom_unregister_bus_bandwidth_needs(data);
+ mutex_unlock(&qsee_bw_mutex);
+ }
+ return ret;
+
+exit_disable_clk_vote:
+ __qseecom_disable_clk_scale_down(data);
+
+exit_unregister_bus_bw_need:
+ if (qseecom.support_bus_scaling) {
+ mutex_lock(&qsee_bw_mutex);
+ qseecom_unregister_bus_bandwidth_needs(data);
+ mutex_unlock(&qsee_bw_mutex);
+ }
+
+exit_ion_unmap_kernel:
+ ion_unmap_kernel(qseecom.ion_clnt, qseecom.cmnlib_ion_handle);
+
+exit_ion_free:
+ ion_free(qseecom.ion_clnt, qseecom.cmnlib_ion_handle);
+ qseecom.cmnlib_ion_handle = NULL;
return ret;
}
@@ -1854,6 +1999,11 @@
break;
}
}
+
+ ion_unmap_kernel(qseecom.ion_clnt, qseecom.cmnlib_ion_handle);
+ ion_free(qseecom.ion_clnt, qseecom.cmnlib_ion_handle);
+ qseecom.cmnlib_ion_handle = NULL;
+
return ret;
}
@@ -1969,6 +2119,12 @@
/* Get the physical address of the ION BUF */
ret = ion_phys(qseecom.ion_clnt, data->client.ihandle, &pa, &len);
+ if (ret) {
+ pr_err("Cannot get phys_addr for the Ion Client, ret = %d\n",
+ ret);
+ goto err;
+ }
+
/* Populate the structure for sending scm call to load image */
data->client.sb_virt = (char *) ion_map_kernel(qseecom.ion_clnt,
data->client.ihandle);
@@ -2030,7 +2186,7 @@
if (!found_handle)
pr_err("Unable to find the handle, exiting\n");
else
- ret = qseecom_unload_app(data);
+ ret = qseecom_unload_app(data, false);
if (qseecom.support_bus_scaling) {
mutex_lock(&qsee_bw_mutex);
@@ -2079,18 +2235,19 @@
mutex_lock(&app_access_lock);
atomic_inc(&data->ioctl_count);
- if (qseecom.support_bus_scaling)
- qseecom_scale_bus_bandwidth_timer(INACTIVE,
- QSEECOM_SEND_CMD_CRYPTO_TIMEOUT);
- ret = __qseecom_send_cmd(data, &req);
if (qseecom.support_bus_scaling) {
- mutex_lock(&qsee_bw_mutex);
- qseecom.bw_scale_down_timer.expires = jiffies +
- msecs_to_jiffies(QSEECOM_SEND_CMD_CRYPTO_TIMEOUT);
- add_timer(&(qseecom.bw_scale_down_timer));
- qseecom.timer_running = true;
- mutex_unlock(&qsee_bw_mutex);
+ ret = qseecom_scale_bus_bandwidth_timer(INACTIVE);
+ if (ret) {
+ pr_err("Failed to set bw.\n");
+ atomic_dec(&data->ioctl_count);
+ mutex_unlock(&app_access_lock);
+ return ret;
+ }
}
+ ret = __qseecom_send_cmd(data, &req);
+ if (qseecom.support_bus_scaling)
+ __qseecom_add_bw_scale_down_timer(
+ QSEECOM_SEND_CMD_CRYPTO_TIMEOUT);
atomic_dec(&data->ioctl_count);
mutex_unlock(&app_access_lock);
@@ -2135,6 +2292,10 @@
if (!qseecom.support_bus_scaling) {
qsee_disable_clock_vote(handle->dev, CLK_DFAB);
qsee_disable_clock_vote(handle->dev, CLK_SFPB);
+ } else {
+ mutex_lock(&qsee_bw_mutex);
+ qseecom_unregister_bus_bandwidth_needs(handle->dev);
+ mutex_unlock(&qsee_bw_mutex);
}
}
return ret;
@@ -2482,7 +2643,11 @@
/* Get the physical address of the ION BUF */
ret = ion_phys(qseecom.ion_clnt, ihandle, &pa, &len);
-
+ if (ret) {
+ pr_err("Cannot get phys_addr for the Ion Client, ret = %d\n",
+ ret);
+ return ret;
+ }
/* Populate the structure for sending scm call to load image */
load_req.qsee_cmd_id = QSEOS_LOAD_EXTERNAL_ELF_COMMAND;
load_req.mdt_len = load_img_req.mdt_len;
@@ -2499,11 +2664,21 @@
goto exit_ion_free;
}
+ if (qseecom.support_bus_scaling) {
+ mutex_lock(&qsee_bw_mutex);
+ ret = __qseecom_register_bus_bandwidth_needs(data, MEDIUM);
+ mutex_unlock(&qsee_bw_mutex);
+ if (ret) {
+ ret = -EIO;
+ goto exit_cpu_restore;
+ }
+ }
+
/* Vote for the SFPB clock */
ret = __qseecom_enable_clk_scale_up(data);
if (ret) {
ret = -EIO;
- goto exit_cpu_restore;
+ goto exit_register_bus_bandwidth_needs;
}
msm_ion_do_cache_op(qseecom.ion_clnt, ihandle, NULL, len,
ION_IOC_CLEAN_INV_CACHES);
@@ -2540,6 +2715,14 @@
exit_disable_clock:
__qseecom_disable_clk_scale_down(data);
+
+exit_register_bus_bandwidth_needs:
+ if (qseecom.support_bus_scaling) {
+ mutex_lock(&qsee_bw_mutex);
+ ret = qseecom_unregister_bus_bandwidth_needs(data);
+ mutex_unlock(&qsee_bw_mutex);
+ }
+
exit_cpu_restore:
/* Restore the CPU mask */
mask = CPU_MASK_ALL;
@@ -2675,16 +2858,28 @@
int ret;
switch (usage) {
case QSEOS_KM_USAGE_DISK_ENCRYPTION:
- if (qseecom.ce_info.disk_encrypt_pipe == 0xFF ||
- qseecom.ce_info.hlos_ce_hw_instance == 0xFF) {
- pr_err("nfo unavailable: disk encr pipe %d ce_hw %d\n",
- qseecom.ce_info.disk_encrypt_pipe,
- qseecom.ce_info.hlos_ce_hw_instance);
- ret = -EINVAL;
- } else {
+ if (qseecom.support_fde) {
*pipe = qseecom.ce_info.disk_encrypt_pipe;
*ce_hw = qseecom.ce_info.hlos_ce_hw_instance;
ret = 0;
+
+ } else {
+ pr_err("info unavailable: disk encr pipe %d ce_hw %d\n",
+ qseecom.ce_info.disk_encrypt_pipe,
+ qseecom.ce_info.hlos_ce_hw_instance);
+ ret = -EINVAL;
+ }
+ break;
+ case QSEOS_KM_USAGE_FILE_ENCRYPTION:
+ if (qseecom.support_pfe) {
+ *pipe = qseecom.ce_info.file_encrypt_pipe;
+ *ce_hw = qseecom.ce_info.hlos_ce_hw_instance;
+ ret = 0;
+ } else {
+ pr_err("info unavailable: file encr pipe %d ce_hw %d\n",
+ qseecom.ce_info.file_encrypt_pipe,
+ qseecom.ce_info.hlos_ce_hw_instance);
+ ret = -EINVAL;
}
break;
default:
@@ -2721,13 +2916,13 @@
case QSEOS_RESULT_SUCCESS:
break;
case QSEOS_RESULT_FAIL_KEY_ID_EXISTS:
- pr_debug("process_incomplete_cmd return Key ID exists.\n");
+ pr_debug("Key ID exists.\n");
break;
case QSEOS_RESULT_INCOMPLETE:
ret = __qseecom_process_incomplete_cmd(data, &resp);
if (ret) {
if (resp.result == QSEOS_RESULT_FAIL_KEY_ID_EXISTS) {
- pr_debug("process_incomplete_cmd return Key ID exists.\n");
+ pr_debug("Key ID exists.\n");
ret = 0;
} else {
pr_err("process_incomplete_cmd FAILED, resp.result %d\n",
@@ -2879,8 +3074,6 @@
if (ret) {
pr_err("scm call to update key userinfo failed : %d\n", ret);
__qseecom_disable_clk(CLK_QSEE);
- if (qseecom.qsee.instance != qseecom.ce_drv.instance)
- __qseecom_disable_clk(CLK_CE_DRV);
return -EFAULT;
}
@@ -2938,7 +3131,7 @@
memset((void *)generate_key_ireq.key_id, 0, QSEECOM_KEY_ID_SIZE);
memset((void *)generate_key_ireq.hash32, 0, QSEECOM_HASH_SIZE);
memcpy((void *)generate_key_ireq.key_id,
- (void *)key_id_array[create_key_req.usage - 1],
+ (void *)key_id_array[create_key_req.usage].desc,
QSEECOM_KEY_ID_SIZE);
memcpy((void *)generate_key_ireq.hash32,
(void *)create_key_req.hash32, QSEECOM_HASH_SIZE);
@@ -2960,8 +3153,8 @@
memset((void *)set_key_ireq.key_id, 0, QSEECOM_KEY_ID_SIZE);
memset((void *)set_key_ireq.hash32, 0, QSEECOM_HASH_SIZE);
memcpy((void *)set_key_ireq.key_id,
- (void *)key_id_array[create_key_req.usage - 1],
- QSEECOM_KEY_ID_SIZE);
+ (void *)key_id_array[create_key_req.usage].desc,
+ QSEECOM_KEY_ID_SIZE);
memcpy((void *)set_key_ireq.hash32, (void *)create_key_req.hash32,
QSEECOM_HASH_SIZE);
@@ -3006,19 +3199,22 @@
return -EINVAL;
}
- delete_key_ireq.flags = flags;
- delete_key_ireq.qsee_command_id = QSEOS_DELETE_KEY;
- memset((void *)delete_key_ireq.key_id, 0, QSEECOM_KEY_ID_SIZE);
- memcpy((void *)delete_key_ireq.key_id,
- (void *)key_id_array[wipe_key_req.usage - 1],
+ if (wipe_key_req.wipe_key_flag) {
+ delete_key_ireq.flags = flags;
+ delete_key_ireq.qsee_command_id = QSEOS_DELETE_KEY;
+ memset((void *)delete_key_ireq.key_id, 0, QSEECOM_KEY_ID_SIZE);
+ memcpy((void *)delete_key_ireq.key_id,
+ (void *)key_id_array[wipe_key_req.usage].desc,
QSEECOM_KEY_ID_SIZE);
- memset((void *)delete_key_ireq.hash32, 0, QSEECOM_HASH_SIZE);
+ memset((void *)delete_key_ireq.hash32, 0, QSEECOM_HASH_SIZE);
- ret = __qseecom_delete_saved_key(data, wipe_key_req.usage,
+ ret = __qseecom_delete_saved_key(data, wipe_key_req.usage,
&delete_key_ireq);
- if (ret) {
- pr_err("Failed to delete key from ssd storage: %d\n", ret);
- return -EFAULT;
+ if (ret) {
+ pr_err("Failed to delete key from ssd storage: %d\n",
+ ret);
+ return -EFAULT;
+ }
}
clear_key_ireq.qsee_command_id = QSEOS_SET_KEY;
@@ -3027,7 +3223,7 @@
clear_key_ireq.flags = flags;
clear_key_ireq.pipe_type = QSEOS_PIPE_ENC|QSEOS_PIPE_ENC_XTS;
for (i = 0; i < QSEECOM_KEY_ID_SIZE; i++)
- clear_key_ireq.key_id[i] = 0xff;
+ clear_key_ireq.key_id[i] = QSEECOM_INVALID_KEY_ID;
memset((void *)clear_key_ireq.hash32, 0, QSEECOM_HASH_SIZE);
ret = __qseecom_set_clear_ce_key(data, wipe_key_req.usage,
@@ -3066,8 +3262,9 @@
memset(ireq.key_id, 0, QSEECOM_KEY_ID_SIZE);
memset((void *)ireq.current_hash32, 0, QSEECOM_HASH_SIZE);
memset((void *)ireq.new_hash32, 0, QSEECOM_HASH_SIZE);
- memcpy(ireq.key_id, key_id_array[update_key_req.usage - 1],
- QSEECOM_KEY_ID_SIZE);
+ memcpy((void *)ireq.key_id,
+ (void *)key_id_array[update_key_req.usage].desc,
+ QSEECOM_KEY_ID_SIZE);
memcpy((void *)ireq.current_hash32,
(void *)update_key_req.current_hash32, QSEECOM_HASH_SIZE);
memcpy((void *)ireq.new_hash32,
@@ -3210,20 +3407,27 @@
}
/* Only one client allowed here at a time */
mutex_lock(&app_access_lock);
- if (qseecom.support_bus_scaling)
- qseecom_scale_bus_bandwidth_timer(INACTIVE,
- QSEECOM_SEND_CMD_CRYPTO_TIMEOUT);
+ if (qseecom.support_bus_scaling) {
+ /* register bus bw in case the client doesn't do it */
+ if (!data->mode) {
+ mutex_lock(&qsee_bw_mutex);
+ __qseecom_register_bus_bandwidth_needs(
+ data, HIGH);
+ mutex_unlock(&qsee_bw_mutex);
+ }
+ ret = qseecom_scale_bus_bandwidth_timer(INACTIVE);
+ if (ret) {
+ pr_err("Failed to set bw.\n");
+ ret = -EINVAL;
+ mutex_unlock(&app_access_lock);
+ break;
+ }
+ }
atomic_inc(&data->ioctl_count);
ret = qseecom_send_cmd(data, argp);
- if (qseecom.support_bus_scaling) {
- mutex_lock(&qsee_bw_mutex);
- qseecom.bw_scale_down_timer.expires = jiffies +
- msecs_to_jiffies(
+ if (qseecom.support_bus_scaling)
+ __qseecom_add_bw_scale_down_timer(
QSEECOM_SEND_CMD_CRYPTO_TIMEOUT);
- add_timer(&(qseecom.bw_scale_down_timer));
- qseecom.timer_running = true;
- mutex_unlock(&qsee_bw_mutex);
- }
atomic_dec(&data->ioctl_count);
wake_up_all(&data->abort_wq);
mutex_unlock(&app_access_lock);
@@ -3242,20 +3446,27 @@
}
/* Only one client allowed here at a time */
mutex_lock(&app_access_lock);
- if (qseecom.support_bus_scaling)
- qseecom_scale_bus_bandwidth_timer(INACTIVE,
- QSEECOM_SEND_CMD_CRYPTO_TIMEOUT);
+ if (qseecom.support_bus_scaling) {
+ if (!data->mode) {
+ mutex_lock(&qsee_bw_mutex);
+ __qseecom_register_bus_bandwidth_needs(
+ data, HIGH);
+ mutex_unlock(&qsee_bw_mutex);
+ }
+ ret = qseecom_scale_bus_bandwidth_timer(INACTIVE);
+ if (ret) {
+ pr_err("Failed to set bw.\n");
+ mutex_unlock(&app_access_lock);
+ ret = -EINVAL;
+ break;
+ }
+ }
atomic_inc(&data->ioctl_count);
ret = qseecom_send_modfd_cmd(data, argp);
- if (qseecom.support_bus_scaling) {
- mutex_lock(&qsee_bw_mutex);
- qseecom.bw_scale_down_timer.expires = jiffies +
- msecs_to_jiffies(
+ if (qseecom.support_bus_scaling)
+ __qseecom_add_bw_scale_down_timer(
QSEECOM_SEND_CMD_CRYPTO_TIMEOUT);
- add_timer(&(qseecom.bw_scale_down_timer));
- qseecom.timer_running = true;
- mutex_unlock(&qsee_bw_mutex);
- } atomic_dec(&data->ioctl_count);
+ atomic_dec(&data->ioctl_count);
wake_up_all(&data->abort_wq);
mutex_unlock(&app_access_lock);
if (ret)
@@ -3274,7 +3485,7 @@
ret = qseecom_receive_req(data);
atomic_dec(&data->ioctl_count);
wake_up_all(&data->abort_wq);
- if (ret)
+ if (ret && (ret != -ERESTARTSYS))
pr_err("failed qseecom_receive_req: %d\n", ret);
break;
}
@@ -3303,6 +3514,7 @@
ret = -EINVAL;
break;
}
+ data->type = QSEECOM_CLIENT_APP;
pr_debug("SET_MEM_PARAM: qseecom addr = 0x%x\n", (u32)data);
ret = qseecom_set_client_mem_param(data, argp);
if (ret)
@@ -3348,7 +3560,7 @@
pr_debug("UNLOAD_APP: qseecom_addr = 0x%x\n", (u32)data);
mutex_lock(&app_access_lock);
atomic_inc(&data->ioctl_count);
- ret = qseecom_unload_app(data);
+ ret = qseecom_unload_app(data, false);
atomic_dec(&data->ioctl_count);
mutex_unlock(&app_access_lock);
if (ret)
@@ -3413,6 +3625,10 @@
if (!qseecom.support_bus_scaling) {
qsee_disable_clock_vote(data, CLK_DFAB);
qsee_disable_clock_vote(data, CLK_SFPB);
+ } else {
+ mutex_lock(&qsee_bw_mutex);
+ qseecom_unregister_bus_bandwidth_needs(data);
+ mutex_unlock(&qsee_bw_mutex);
}
atomic_dec(&data->ioctl_count);
break;
@@ -3468,6 +3684,7 @@
}
case QSEECOM_IOCTL_APP_LOADED_QUERY_REQ: {
data->type = QSEECOM_CLIENT_APP;
+ pr_debug("APP_LOAD_QUERY: qseecom_addr = 0x%x\n", (u32)data);
mutex_lock(&app_access_lock);
atomic_inc(&data->ioctl_count);
pr_debug("APP_LOAD_QUERY: qseecom_addr = 0x%x\n", (u32)data);
@@ -3497,6 +3714,8 @@
break;
}
case QSEECOM_IOCTL_CREATE_KEY_REQ: {
+ if (!(qseecom.support_pfe || qseecom.support_fde))
+ pr_err("Features requiring key init not supported\n");
if (data->type != QSEECOM_GENERIC) {
pr_err("create key req: invalid handle (%d)\n",
data->type);
@@ -3518,6 +3737,8 @@
break;
}
case QSEECOM_IOCTL_WIPE_KEY_REQ: {
+ if (!(qseecom.support_pfe || qseecom.support_fde))
+ pr_err("Features requiring key init not supported\n");
if (data->type != QSEECOM_GENERIC) {
pr_err("wipe key req: invalid handle (%d)\n",
data->type);
@@ -3538,6 +3759,8 @@
break;
}
case QSEECOM_IOCTL_UPDATE_KEY_USER_INFO_REQ: {
+ if (!(qseecom.support_pfe || qseecom.support_fde))
+ pr_err("Features requiring key init not supported\n");
if (data->type != QSEECOM_GENERIC) {
pr_err("update key req: invalid handle (%d)\n",
data->type);
@@ -3638,20 +3861,20 @@
int ret = 0;
if (data->released == false) {
- pr_warn("data: released=false, type=%d, mode=%d, data=0x%x\n",
- data->type, data->mode, (u32)data);
+ pr_warn("data: released = false, type = %d, data = 0x%x\n",
+ data->type, (u32)data);
switch (data->type) {
case QSEECOM_LISTENER_SERVICE:
ret = qseecom_unregister_listener(data);
break;
case QSEECOM_CLIENT_APP:
- ret = qseecom_unload_app(data);
+ ret = qseecom_unload_app(data, true);
break;
case QSEECOM_SECURE_SERVICE:
case QSEECOM_GENERIC:
ret = qseecom_unmap_ion_allocated_memory(data);
if (ret)
- pr_err("Close failed\n");
+ pr_err("Ion Unmap failed\n");
break;
case QSEECOM_UNAVAILABLE_CLIENT_APP:
break;
@@ -3824,6 +4047,8 @@
qseecom.cumulative_mode = 0;
qseecom.current_mode = INACTIVE;
qseecom.support_bus_scaling = false;
+ qseecom.support_fde = false;
+ qseecom.support_pfe = false;
qseecom.ce_drv.ce_core_clk = NULL;
qseecom.ce_drv.ce_clk = NULL;
@@ -3910,23 +4135,62 @@
"qcom,support-bus-scaling");
pr_warn("support_bus_scaling=0x%x",
qseecom.support_bus_scaling);
- if (of_property_read_u32((&pdev->dev)->of_node,
+ qseecom.support_fde =
+ of_property_read_bool((&pdev->dev)->of_node,
+ "qcom,support-fde");
+ if (qseecom.support_fde) {
+ if (of_property_read_u32((&pdev->dev)->of_node,
"qcom,disk-encrypt-pipe-pair",
&qseecom.ce_info.disk_encrypt_pipe)) {
- pr_err("Fail to get disk-encrypt pipe pair information.\n");
- qseecom.ce_info.disk_encrypt_pipe = 0xff;
- rc = -EINVAL;
- goto exit_destroy_ion_client;
+ pr_err("Fail to get FDE pipe information.\n");
+ rc = -EINVAL;
+ goto exit_destroy_ion_client;
+ } else {
+ pr_warn("disk-encrypt-pipe-pair=0x%x",
+ qseecom.ce_info.disk_encrypt_pipe);
+ }
} else {
- pr_warn("bam_pipe_pair=0x%x",
- qseecom.ce_info.disk_encrypt_pipe);
+ pr_warn("Device does not support FDE");
+ qseecom.ce_info.disk_encrypt_pipe = 0xff;
+ }
+ qseecom.support_pfe =
+ of_property_read_bool((&pdev->dev)->of_node,
+ "qcom,support-pfe");
+ if (qseecom.support_pfe) {
+ if (of_property_read_u32((&pdev->dev)->of_node,
+ "qcom,file-encrypt-pipe-pair",
+ &qseecom.ce_info.disk_encrypt_pipe)) {
+ pr_err("Fail to get PFE pipe information.\n");
+ rc = -EINVAL;
+ goto exit_destroy_ion_client;
+ } else {
+ pr_warn("file-encrypt-pipe-pair=0x%x",
+ qseecom.ce_info.file_encrypt_pipe);
+ }
+ } else {
+ pr_warn("Device does not support PFE");
+ qseecom.ce_info.file_encrypt_pipe = 0xff;
+ }
+ if (qseecom.support_pfe || qseecom.support_fde) {
+ if (of_property_read_u32((&pdev->dev)->of_node,
+ "qcom,hlos-ce-hw-instance",
+ &qseecom.ce_info.hlos_ce_hw_instance)) {
+ pr_err("Fail: get hlos ce hw instanc info\n");
+ rc = -EINVAL;
+ goto exit_destroy_ion_client;
+ } else {
+ pr_warn("hlos-ce-hw-instance=0x%x",
+ qseecom.ce_info.hlos_ce_hw_instance);
+ }
+ } else {
+ pr_warn("Device does not support PFE/FDE");
+ qseecom.ce_info.hlos_ce_hw_instance = 0xff;
}
if (of_property_read_u32((&pdev->dev)->of_node,
"qcom,qsee-ce-hw-instance",
&qseecom.ce_info.qsee_ce_hw_instance)) {
pr_err("Fail to get qsee ce hw instance information.\n");
- qseecom.ce_info.qsee_ce_hw_instance = 0xff;
rc = -EINVAL;
goto exit_destroy_ion_client;
} else {
@@ -3934,18 +4198,6 @@
qseecom.ce_info.qsee_ce_hw_instance);
}
- if (of_property_read_u32((&pdev->dev)->of_node,
- "qcom,hlos-ce-hw-instance",
- &qseecom.ce_info.hlos_ce_hw_instance)) {
- pr_err("Fail to get hlos ce hw instance information.\n");
- qseecom.ce_info.hlos_ce_hw_instance = 0xff;
- rc = -EINVAL;
- goto exit_destroy_ion_client;
- } else {
- pr_warn("hlos-ce-hw-instance=0x%x",
- qseecom.ce_info.hlos_ce_hw_instance);
- }
-
qseecom.qsee.instance = qseecom.ce_info.qsee_ce_hw_instance;
qseecom.ce_drv.instance = qseecom.ce_info.hlos_ce_hw_instance;
@@ -3953,7 +4205,8 @@
if (ret)
goto exit_destroy_ion_client;
- if (qseecom.qsee.instance != qseecom.ce_drv.instance) {
+ if ((qseecom.qsee.instance != qseecom.ce_drv.instance) &&
+ (qseecom.support_pfe || qseecom.support_fde)) {
ret = __qseecom_init_clk(CLK_CE_DRV);
if (ret) {
__qseecom_deinit_clk(CLK_QSEE);
@@ -4051,7 +4304,7 @@
goto exit_free_kc_handle;
list_del(&kclient->list);
- ret = qseecom_unload_app(kclient->handle->dev);
+ ret = qseecom_unload_app(kclient->handle->dev, false);
if (!ret) {
kzfree(kclient->handle->dev);
kzfree(kclient->handle);
@@ -4083,7 +4336,8 @@
/* register client for bus scaling */
if (pdev->dev.of_node) {
__qseecom_deinit_clk(CLK_QSEE);
- if (qseecom.qsee.instance != qseecom.ce_drv.instance)
+ if ((qseecom.qsee.instance != qseecom.ce_drv.instance) &&
+ (qseecom.support_pfe || qseecom.support_fde))
__qseecom_deinit_clk(CLK_CE_DRV);
}
@@ -4106,12 +4360,19 @@
struct qseecom_clk *qclk;
qclk = &qseecom.qsee;
- if (qseecom.cumulative_mode != INACTIVE) {
- ret = __qseecom_set_msm_bus_request(INACTIVE);
+ mutex_lock(&qsee_bw_mutex);
+ mutex_lock(&clk_access_lock);
+
+ if (qseecom.cumulative_mode != INACTIVE &&
+ qseecom.current_mode != INACTIVE) {
+ ret = msm_bus_scale_client_update_request(
+ qseecom.qsee_perf_client, INACTIVE);
if (ret)
pr_err("Fail to scale down bus\n");
+ else
+ qseecom.current_mode = INACTIVE;
}
- mutex_lock(&clk_access_lock);
+
if (qclk->clk_access_cnt) {
if (qclk->ce_clk != NULL)
clk_disable_unprepare(qclk->ce_clk);
@@ -4120,7 +4381,13 @@
if (qclk->ce_bus_clk != NULL)
clk_disable_unprepare(qclk->ce_bus_clk);
}
+
+ del_timer_sync(&(qseecom.bw_scale_down_timer));
+ qseecom.timer_running = false;
+
mutex_unlock(&clk_access_lock);
+ mutex_unlock(&qsee_bw_mutex);
+
return 0;
}
@@ -4131,18 +4398,22 @@
struct qseecom_clk *qclk;
qclk = &qseecom.qsee;
+ mutex_lock(&qsee_bw_mutex);
+ mutex_lock(&clk_access_lock);
if (qseecom.cumulative_mode >= HIGH)
mode = HIGH;
else
mode = qseecom.cumulative_mode;
if (qseecom.cumulative_mode != INACTIVE) {
- ret = __qseecom_set_msm_bus_request(mode);
+ ret = msm_bus_scale_client_update_request(
+ qseecom.qsee_perf_client, mode);
if (ret)
- pr_err("Fail to scale down bus\n");
+ pr_err("Fail to scale up bus to %d\n", mode);
+ else
+ qseecom.current_mode = mode;
}
- mutex_lock(&clk_access_lock);
if (qclk->clk_access_cnt) {
ret = clk_prepare_enable(qclk->ce_core_clk);
@@ -4166,7 +4437,19 @@
goto ce_bus_clk_err;
}
}
+
+ if (qclk->clk_access_cnt || qseecom.cumulative_mode) {
+ qseecom.bw_scale_down_timer.expires = jiffies +
+ msecs_to_jiffies(QSEECOM_SEND_CMD_CRYPTO_TIMEOUT);
+ mod_timer(&(qseecom.bw_scale_down_timer),
+ qseecom.bw_scale_down_timer.expires);
+ qseecom.timer_running = true;
+ }
+
mutex_unlock(&clk_access_lock);
+ mutex_unlock(&qsee_bw_mutex);
+
+
return 0;
ce_bus_clk_err:
@@ -4175,6 +4458,7 @@
clk_disable_unprepare(qclk->ce_core_clk);
err:
mutex_unlock(&clk_access_lock);
+ mutex_unlock(&qsee_bw_mutex);
return -EIO;
}
static struct of_device_id qseecom_match[] = {
diff --git a/drivers/misc/tspp.c b/drivers/misc/tspp.c
index 19f26f2..2dfa404 100644
--- a/drivers/misc/tspp.c
+++ b/drivers/misc/tspp.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2014, 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
@@ -924,9 +924,11 @@
}
ctl = readl_relaxed(tsif_device->base + TSIF_STS_CTL_OFF);
- tsif_device->ref_count++;
+ if (!(ctl & TSIF_STS_CTL_START))
+ return -EBUSY;
- return (ctl & TSIF_STS_CTL_START) ? 0 : -EBUSY;
+ tsif_device->ref_count++;
+ return 0;
}
static void tspp_stop_tsif(struct tspp_tsif_device *tsif_device)
@@ -1339,6 +1341,56 @@
}
}
+static int msm_tspp_req_irqs(struct tspp_device *device)
+{
+ int rc;
+ int i;
+ int j;
+
+ rc = request_irq(device->tspp_irq, tspp_isr, IRQF_SHARED,
+ dev_name(&device->pdev->dev), device);
+ if (rc) {
+ dev_err(&device->pdev->dev,
+ "failed to request TSPP IRQ %d : %d",
+ device->tspp_irq, rc);
+ return rc;
+ }
+
+ for (i = 0; i < TSPP_TSIF_INSTANCES; i++) {
+ rc = request_irq(device->tsif[i].tsif_irq,
+ tsif_isr, IRQF_SHARED, dev_name(&device->pdev->dev),
+ &device->tsif[i]);
+ if (rc) {
+ dev_err(&device->pdev->dev,
+ "failed to request TSIF%d IRQ: %d",
+ i, rc);
+ goto failed;
+ }
+ }
+
+ return 0;
+
+failed:
+ free_irq(device->tspp_irq, device);
+ for (j = 0; j < i; j++)
+ free_irq(device->tsif[j].tsif_irq, device);
+
+ return rc;
+}
+
+static inline void msm_tspp_free_irqs(struct tspp_device *device)
+{
+ int i;
+
+ for (i = 0; i < TSPP_TSIF_INSTANCES; i++) {
+ if (device->tsif[i].tsif_irq)
+ free_irq(device->tsif[i].tsif_irq, &device->tsif[i]);
+ }
+
+ if (device->tspp_irq)
+ free_irq(device->tspp_irq, device);
+}
+
/*** TSPP API functions ***/
/**
@@ -1355,6 +1407,8 @@
struct tspp_select_source *source)
{
u32 val;
+ int rc;
+ bool req_irq = false;
struct tspp_device *pdev;
struct tspp_channel *channel;
@@ -1383,16 +1437,30 @@
source->data_inverse, source->sync_inverse,
source->enable_inverse);
+ /* Request IRQ resources on first open */
+ if ((source->source == TSPP_SOURCE_TSIF0 ||
+ source->source == TSPP_SOURCE_TSIF1) &&
+ (pdev->tsif[0].ref_count + pdev->tsif[1].ref_count) == 0) {
+ rc = msm_tspp_req_irqs(pdev);
+ if (rc) {
+ pr_err("tspp: error requesting irqs\n");
+ return rc;
+ }
+ req_irq = true;
+ }
+
switch (source->source) {
case TSPP_SOURCE_TSIF0:
if (tspp_config_gpios(pdev, channel->src, 1) != 0) {
+ rc = -EBUSY;
pr_err("tspp: error enabling tsif0 GPIOs\n");
- return -EBUSY;
+ goto free_irq;
}
/* make sure TSIF0 is running & enabled */
if (tspp_start_tsif(&pdev->tsif[0]) != 0) {
+ rc = -EBUSY;
pr_err("tspp: error starting tsif0");
- return -EBUSY;
+ goto free_irq;
}
if (pdev->tsif[0].ref_count == 1) {
val = readl_relaxed(pdev->base + TSPP_CONTROL);
@@ -1403,13 +1471,15 @@
break;
case TSPP_SOURCE_TSIF1:
if (tspp_config_gpios(pdev, channel->src, 1) != 0) {
+ rc = -EBUSY;
pr_err("tspp: error enabling tsif1 GPIOs\n");
- return -EBUSY;
+ goto free_irq;
}
/* make sure TSIF1 is running & enabled */
if (tspp_start_tsif(&pdev->tsif[1]) != 0) {
+ rc = -EBUSY;
pr_err("tspp: error starting tsif1");
- return -EBUSY;
+ goto free_irq;
}
if (pdev->tsif[1].ref_count == 1) {
val = readl_relaxed(pdev->base + TSPP_CONTROL);
@@ -1427,6 +1497,11 @@
}
return 0;
+
+free_irq:
+ if (req_irq)
+ msm_tspp_free_irqs(pdev);
+ return rc;
}
EXPORT_SYMBOL(tspp_open_stream);
@@ -1442,7 +1517,7 @@
int tspp_close_stream(u32 dev, u32 channel_id)
{
u32 val;
- u32 prev_ref_count;
+ u32 prev_ref_count = 0;
struct tspp_device *pdev;
struct tspp_channel *channel;
@@ -1491,6 +1566,12 @@
}
channel->src = TSPP_SOURCE_NONE;
+
+ /* Free requested interrupts to save power */
+ if ((pdev->tsif[0].ref_count + pdev->tsif[1].ref_count) == 0 &&
+ prev_ref_count)
+ msm_tspp_free_irqs(pdev);
+
return 0;
}
EXPORT_SYMBOL(tspp_close_stream);
@@ -2812,7 +2893,6 @@
struct tspp_device *device)
{
int rc;
- int i;
/* get IRQ numbers from platform information */
@@ -2820,15 +2900,6 @@
rc = platform_get_irq_byname(pdev, "TSIF_TSPP_IRQ");
if (rc > 0) {
device->tspp_irq = rc;
- rc = request_irq(device->tspp_irq, tspp_isr, IRQF_SHARED,
- dev_name(&pdev->dev), device);
- if (rc) {
- dev_err(&pdev->dev,
- "failed to request TSPP IRQ %d : %d",
- device->tspp_irq, rc);
- device->tspp_irq = 0;
- return -EINVAL;
- }
} else {
dev_err(&pdev->dev, "failed to get TSPP IRQ");
return -EINVAL;
@@ -2851,17 +2922,6 @@
return -EINVAL;
}
- for (i = 0; i < TSPP_TSIF_INSTANCES; i++) {
- rc = request_irq(device->tsif[i].tsif_irq,
- tsif_isr, IRQF_SHARED,
- dev_name(&pdev->dev), &device->tsif[i]);
- if (rc) {
- dev_warn(&pdev->dev, "failed to request TSIF%d IRQ: %d",
- i, rc);
- device->tsif[i].tsif_irq = 0;
- }
- }
-
/* map BAM IRQ */
rc = platform_get_irq_byname(pdev, "TSIF_BAM_IRQ");
if (rc > 0) {
@@ -3129,13 +3189,6 @@
for (i = 0; i < TSPP_TSIF_INSTANCES; i++)
tsif_debugfs_exit(&device->tsif[i]);
err_irq:
- for (i = 0; i < TSPP_TSIF_INSTANCES; i++) {
- if (device->tsif[i].tsif_irq)
- free_irq(device->tsif[i].tsif_irq, &device->tsif[i]);
- }
- if (device->tspp_irq)
- free_irq(device->tspp_irq, device);
-
iounmap(device->bam_props.virt_addr);
err_map_bam:
err_res_bam:
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index 6de1cde..0c70746 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -2524,8 +2524,20 @@
break;
case MMC_BLK_CMD_ERR:
ret = mmc_blk_cmd_err(md, card, brq, req, ret);
- if (!mmc_blk_reset(md, card->host, type))
+ if (!mmc_blk_reset(md, card->host, type)) {
+ if (!ret) {
+ /*
+ * We have successfully completed block
+ * request and notified to upper layers.
+ * As the reset is successful, assume
+ * h/w is in clean state and proceed
+ * with new request.
+ */
+ BUG_ON(card->host->areq);
+ goto start_new_req;
+ }
break;
+ }
goto cmd_abort;
case MMC_BLK_RETRY:
if (retry++ < 5)
@@ -3108,9 +3120,6 @@
MMC_FIXUP(CID_NAME_ANY, CID_MANFID_HYNIX, CID_OEMID_ANY, add_quirk_mmc,
MMC_QUIRK_BROKEN_DATA_TIMEOUT),
- /* Disable cache for this cards */
- MMC_FIXUP("H8G2d", CID_MANFID_HYNIX, CID_OEMID_ANY, add_quirk_mmc,
- MMC_QUIRK_CACHE_DISABLE),
END_FIXUP
};
diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c
index b36faff..a6fcec5 100644
--- a/drivers/mmc/core/bus.c
+++ b/drivers/mmc/core/bus.c
@@ -421,10 +421,17 @@
else if (!mmc_card_sdio(card) && mmc_use_core_runtime_pm(card->host))
pm_runtime_enable(&card->dev);
+ if (mmc_card_sdio(card)) {
+ ret = device_init_wakeup(&card->dev, true);
+ if (ret)
+ pr_err("%s: %s: failed to init wakeup: %d\n",
+ mmc_hostname(card->host), __func__, ret);
+ }
ret = device_add(&card->dev);
if (ret)
return ret;
+ device_enable_async_suspend(&card->dev);
if (mmc_use_core_runtime_pm(card->host) && !mmc_card_sdio(card)) {
card->rpm_attrib.show = show_rpm_delay;
card->rpm_attrib.store = store_rpm_delay;
diff --git a/drivers/mmc/core/cd-gpio.c b/drivers/mmc/core/cd-gpio.c
index 45ec5c2..9d813e9 100644
--- a/drivers/mmc/core/cd-gpio.c
+++ b/drivers/mmc/core/cd-gpio.c
@@ -19,8 +19,8 @@
struct mmc_cd_gpio {
unsigned int gpio;
- char label[0];
bool status;
+ char label[0];
};
static int mmc_cd_get_status(struct mmc_host *host)
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 1feb26b..dbc7d5c 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -169,9 +169,35 @@
#endif /* CONFIG_FAIL_MMC_REQUEST */
+static inline void
+mmc_clk_scaling_update_state(struct mmc_host *host, struct mmc_request *mrq)
+{
+ if (mrq) {
+ switch (mrq->cmd->opcode) {
+ case MMC_READ_SINGLE_BLOCK:
+ case MMC_READ_MULTIPLE_BLOCK:
+ case MMC_WRITE_BLOCK:
+ case MMC_WRITE_MULTIPLE_BLOCK:
+ host->clk_scaling.invalid_state = false;
+ break;
+ default:
+ host->clk_scaling.invalid_state = true;
+ break;
+ }
+ } else {
+ /*
+ * force clock scaling transitions,
+ * if other conditions are met
+ */
+ host->clk_scaling.invalid_state = false;
+ }
+
+ return;
+}
+
static inline void mmc_update_clk_scaling(struct mmc_host *host)
{
- if (host->clk_scaling.enable) {
+ if (host->clk_scaling.enable && !host->clk_scaling.invalid_state) {
host->clk_scaling.busy_time_us +=
ktime_to_us(ktime_sub(ktime_get(),
host->clk_scaling.start_busy));
@@ -333,8 +359,11 @@
* frequency will be done after current thread
* releases host.
*/
- mmc_clk_scaling(host, false);
- host->clk_scaling.start_busy = ktime_get();
+ mmc_clk_scaling_update_state(host, mrq);
+ if (!host->clk_scaling.invalid_state) {
+ mmc_clk_scaling(host, false);
+ host->clk_scaling.start_busy = ktime_get();
+ }
}
host->ops->request(host, mrq);
@@ -2826,7 +2855,8 @@
* this mode.
*/
if (!card || (mmc_card_mmc(card) &&
- card->part_curr == EXT_CSD_PART_CONFIG_ACC_RPMB))
+ card->part_curr == EXT_CSD_PART_CONFIG_ACC_RPMB)
+ || host->clk_scaling.invalid_state)
goto out;
if (mmc_send_status(card, &status)) {
diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c
index c7fa876..c4b9724 100644
--- a/drivers/mmc/core/host.c
+++ b/drivers/mmc/core/host.c
@@ -77,16 +77,40 @@
{
struct mmc_host *host = cls_dev_to_mmc_host(dev);
int ret = 0;
+ unsigned long flags;
if (!mmc_use_core_pm(host))
return 0;
+ spin_lock_irqsave(&host->clk_lock, flags);
+ /*
+ * let the driver know that suspend is in progress and must
+ * be aborted on receiving a sdio card interrupt
+ */
+ host->dev_status = DEV_SUSPENDING;
+ spin_unlock_irqrestore(&host->clk_lock, flags);
if (!pm_runtime_suspended(dev)) {
ret = mmc_suspend_host(host);
if (ret < 0)
pr_err("%s: %s: failed: ret: %d\n", mmc_hostname(host),
__func__, ret);
}
+ /*
+ * If SDIO function driver doesn't want to power off the card,
+ * atleast turn off clocks to allow deep sleep.
+ */
+ if (!ret && host->card && mmc_card_sdio(host->card) &&
+ host->ios.clock) {
+ spin_lock_irqsave(&host->clk_lock, flags);
+ host->clk_old = host->ios.clock;
+ host->ios.clock = 0;
+ host->clk_gated = true;
+ spin_unlock_irqrestore(&host->clk_lock, flags);
+ mmc_set_ios(host);
+ }
+ spin_lock_irqsave(&host->clk_lock, flags);
+ host->dev_status = DEV_SUSPENDED;
+ spin_unlock_irqrestore(&host->clk_lock, flags);
return ret;
}
@@ -104,6 +128,7 @@
pr_err("%s: %s: failed: ret: %d\n", mmc_hostname(host),
__func__, ret);
}
+ host->dev_status = DEV_RESUMED;
return ret;
}
@@ -697,6 +722,7 @@
if (err)
return err;
+ device_enable_async_suspend(&host->class_dev);
led_trigger_register_simple(dev_name(&host->class_dev), &host->led);
#ifdef CONFIG_DEBUG_FS
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 953fe27..c1f366e 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -66,6 +66,21 @@
MMC_FIXUP_EXT_CSD_REV(CID_NAME_ANY, CID_MANFID_HYNIX,
0x014a, add_quirk, MMC_QUIRK_BROKEN_HPI, 5),
+ /* Disable HPI feature for Kingstone card */
+ MMC_FIXUP_EXT_CSD_REV("MMC16G", CID_MANFID_KINGSTON, CID_OEMID_ANY,
+ add_quirk, MMC_QUIRK_BROKEN_HPI, 5),
+
+ /*
+ * Some Hynix cards exhibit data corruption over reboots if cache is
+ * enabled. Disable cache for all versions until a class of cards that
+ * show this behavior is identified.
+ */
+ MMC_FIXUP("H8G2d", CID_MANFID_HYNIX, CID_OEMID_ANY, add_quirk_mmc,
+ MMC_QUIRK_CACHE_DISABLE),
+
+ MMC_FIXUP("MMC16G", CID_MANFID_KINGSTON, CID_OEMID_ANY, add_quirk_mmc,
+ MMC_QUIRK_CACHE_DISABLE),
+
END_FIXUP
};
@@ -1579,11 +1594,6 @@
card->ext_csd.cache_ctrl = 1;
}
}
- if (card->quirks & MMC_QUIRK_CACHE_DISABLE) {
- pr_warn("%s: This is Hynix card, cache disabled!\n",
- mmc_hostname(card->host));
- card->ext_csd.cache_ctrl = 0;
- }
if ((host->caps2 & MMC_CAP2_PACKED_WR &&
card->ext_csd.max_packed_writes > 0) ||
@@ -1707,11 +1717,12 @@
BUG_ON(!host->card);
unregister_reboot_notifier(&host->card->reboot_notify);
+
+ mmc_exit_clk_scaling(host);
mmc_remove_card(host->card);
mmc_claim_host(host);
host->card = NULL;
- mmc_exit_clk_scaling(host);
mmc_release_host(host);
}
diff --git a/drivers/mmc/core/quirks.c b/drivers/mmc/core/quirks.c
index 4407d91..d517205 100644
--- a/drivers/mmc/core/quirks.c
+++ b/drivers/mmc/core/quirks.c
@@ -50,6 +50,14 @@
#define SDIO_DEVICE_ID_MSM_QCA_AR6003_2 0x301
#endif
+#ifndef SDIO_DEVICE_ID_MSM_QCA_AR6004_1
+#define SDIO_DEVICE_ID_MSM_QCA_AR6004_1 0x400
+#endif
+
+#ifndef SDIO_DEVICE_ID_MSM_QCA_AR6004_2
+#define SDIO_DEVICE_ID_MSM_QCA_AR6004_2 0x401
+#endif
+
/*
* This hook just adds a quirk for all sdio devices
*/
@@ -78,6 +86,12 @@
SDIO_FIXUP(SDIO_VENDOR_ID_MSM_QCA, SDIO_DEVICE_ID_MSM_QCA_AR6003_2,
remove_quirk, MMC_QUIRK_BROKEN_CLK_GATING),
+ SDIO_FIXUP(SDIO_VENDOR_ID_MSM_QCA, SDIO_DEVICE_ID_MSM_QCA_AR6004_1,
+ remove_quirk, MMC_QUIRK_BROKEN_CLK_GATING),
+
+ SDIO_FIXUP(SDIO_VENDOR_ID_MSM_QCA, SDIO_DEVICE_ID_MSM_QCA_AR6004_2,
+ remove_quirk, MMC_QUIRK_BROKEN_CLK_GATING),
+
SDIO_FIXUP(SDIO_VENDOR_ID_TI, SDIO_DEVICE_ID_TI_WL1271,
add_quirk, MMC_QUIRK_NONSTD_FUNC_IF),
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index d4d7c18..179632c 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -1132,11 +1132,11 @@
BUG_ON(!host);
BUG_ON(!host->card);
+ mmc_exit_clk_scaling(host);
mmc_remove_card(host->card);
mmc_claim_host(host);
host->card = NULL;
- mmc_exit_clk_scaling(host);
mmc_release_host(host);
}
diff --git a/drivers/mmc/core/sdio_irq.c b/drivers/mmc/core/sdio_irq.c
index 3d8ceb4..2b48f77 100644
--- a/drivers/mmc/core/sdio_irq.c
+++ b/drivers/mmc/core/sdio_irq.c
@@ -85,6 +85,7 @@
struct sched_param param = { .sched_priority = 1 };
unsigned long period, idle_period;
int ret;
+ bool ws;
sched_setscheduler(current, SCHED_FIFO, ¶m);
@@ -118,6 +119,17 @@
ret = __mmc_claim_host(host, &host->sdio_irq_thread_abort);
if (ret)
break;
+ ws = false;
+ /*
+ * prevent suspend if it has started when scheduled;
+ * 100 msec (approx. value) should be enough for the system to
+ * resume and attend to the card's request
+ */
+ if ((host->dev_status == DEV_SUSPENDING) ||
+ (host->dev_status == DEV_SUSPENDED)) {
+ pm_wakeup_event(&host->card->dev, 100);
+ ws = true;
+ }
ret = process_sdio_pending_irqs(host);
host->sdio_irq_pending = false;
mmc_release_host(host);
@@ -154,6 +166,12 @@
host->ops->enable_sdio_irq(host, 1);
mmc_host_clk_release(host);
}
+ /*
+ * function drivers would have processed the event from card
+ * unless suspended, hence release wake source
+ */
+ if (ws && (host->dev_status == DEV_RESUMED))
+ pm_relax(&host->card->dev);
if (!kthread_should_stop())
schedule_timeout(period);
set_current_state(TASK_RUNNING);
diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c
index 739a237..2c96d3c 100644
--- a/drivers/mmc/host/sdhci-msm.c
+++ b/drivers/mmc/host/sdhci-msm.c
@@ -31,6 +31,7 @@
#include <linux/delay.h>
#include <linux/scatterlist.h>
#include <linux/slab.h>
+#include <linux/irq.h>
#include <linux/mmc/mmc.h>
#include <linux/pm.h>
#include <linux/pm_runtime.h>
@@ -38,10 +39,18 @@
#include <linux/dma-mapping.h>
#include <mach/gpio.h>
#include <mach/msm_bus.h>
+#include <mach/mpm.h>
#include <linux/iopoll.h>
#include "sdhci-pltfm.h"
+enum sdc_mpm_pin_state {
+ SDC_DAT1_DISABLE,
+ SDC_DAT1_ENABLE,
+ SDC_DAT1_ENWAKE,
+ SDC_DAT1_DISWAKE,
+};
+
#define SDHCI_VER_100 0x2B
#define CORE_HC_MODE 0x78
#define HC_MODE_EN 0x1
@@ -156,6 +165,9 @@
#define INVALID_TUNING_PHASE -1
+#define sdhci_is_valid_mpm_wakeup_int(_h) ((_h)->pdata->mpm_sdiowakeup_int >= 0)
+#define sdhci_is_valid_gpio_wakeup_int(_h) ((_h)->pdata->sdiowakeup_irq >= 0)
+
static const u32 tuning_block_64[] = {
0x00FF0FFF, 0xCCC3CCFF, 0xFFCC3CC3, 0xEFFEFFFE,
0xDDFFDFFF, 0xFBFFFBFF, 0xFF7FFFBF, 0xEFBDF777,
@@ -283,6 +295,8 @@
struct sdhci_msm_bus_voting_data *voting_data;
u32 *sup_clk_table;
unsigned char sup_clk_cnt;
+ int mpm_sdiowakeup_int;
+ int sdiowakeup_irq;
};
struct sdhci_msm_bus_vote {
@@ -318,6 +332,7 @@
bool calibration_done;
u8 saved_tuning_phase;
atomic_t controller_clock;
+ bool is_sdiowakeup_enabled;
};
enum vdd_io_level {
@@ -861,6 +876,14 @@
memset(data_buf, 0, size);
mmc_wait_for_req(mmc, &mrq);
+ /*
+ * wait for 146 MCLK cycles for the card to send out the data
+ * and thus move to TRANS state. As the MCLK would be minimum
+ * 200MHz when tuning is performed, we need maximum 0.73us
+ * delay. To be on safer side 1ms delay is given.
+ */
+ if (cmd.error)
+ usleep_range(1000, 1200);
if (!cmd.error && !data.error &&
!memcmp(data_buf, tuning_block_pattern, size)) {
/* tuning is successful at this tuning point */
@@ -1338,7 +1361,7 @@
struct device_node *np = dev->of_node;
u32 bus_width = 0;
u32 cpu_dma_latency;
- int len, i;
+ int len, i, mpm_int;
int clk_table_len;
u32 *clk_table = NULL;
enum of_gpio_flags flags = OF_GPIO_ACTIVE_LOW;
@@ -1433,6 +1456,12 @@
if (of_get_property(np, "qcom,nonremovable", NULL))
pdata->nonremovable = true;
+ if (!of_property_read_u32(np, "qcom,dat1-mpm-int",
+ &mpm_int))
+ pdata->mpm_sdiowakeup_int = mpm_int;
+ else
+ pdata->mpm_sdiowakeup_int = -1;
+
return pdata;
out:
return NULL;
@@ -1933,6 +1962,39 @@
return ret;
}
+/*
+ * Acquire spin-lock host->lock before calling this function
+ */
+static void sdhci_msm_cfg_sdiowakeup_gpio_irq(struct sdhci_host *host,
+ bool enable)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+
+ if (enable && !msm_host->is_sdiowakeup_enabled)
+ enable_irq(msm_host->pdata->sdiowakeup_irq);
+ else if (!enable && msm_host->is_sdiowakeup_enabled)
+ disable_irq_nosync(msm_host->pdata->sdiowakeup_irq);
+ else
+ dev_warn(&msm_host->pdev->dev, "%s: wakeup to config: %d curr: %d\n",
+ __func__, enable, msm_host->is_sdiowakeup_enabled);
+ msm_host->is_sdiowakeup_enabled = enable;
+}
+
+static irqreturn_t sdhci_msm_sdiowakeup_irq(int irq, void *data)
+{
+ struct sdhci_host *host = (struct sdhci_host *)data;
+ unsigned long flags;
+
+ pr_debug("%s: irq (%d) received\n", __func__, irq);
+
+ spin_lock_irqsave(&host->lock, flags);
+ sdhci_msm_cfg_sdiowakeup_gpio_irq(host, false);
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ return IRQ_HANDLED;
+}
+
static irqreturn_t sdhci_msm_pwr_irq(int irq, void *data)
{
struct sdhci_host *host = (struct sdhci_host *)data;
@@ -2609,6 +2671,38 @@
.enable_controller_clock = sdhci_msm_enable_controller_clock,
};
+static int sdhci_msm_cfg_mpm_pin_wakeup(struct sdhci_host *host, unsigned mode)
+{
+ int ret = 0;
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ unsigned int pin = msm_host->pdata->mpm_sdiowakeup_int;
+
+ if (!pin)
+ return 0;
+
+ switch (mode) {
+ case SDC_DAT1_DISABLE:
+ ret = msm_mpm_enable_pin(pin, 0);
+ break;
+ case SDC_DAT1_ENABLE:
+ ret = msm_mpm_set_pin_type(pin, IRQ_TYPE_LEVEL_LOW);
+ if (!ret)
+ ret = msm_mpm_enable_pin(pin, 1);
+ break;
+ case SDC_DAT1_ENWAKE:
+ ret = msm_mpm_set_pin_wake(pin, 1);
+ break;
+ case SDC_DAT1_DISWAKE:
+ ret = msm_mpm_set_pin_wake(pin, 0);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
static int __devinit sdhci_msm_probe(struct platform_device *pdev)
{
struct sdhci_host *host;
@@ -2619,6 +2713,7 @@
u32 vdd_max_current;
u16 host_version;
u32 pwr, irq_status, irq_ctl;
+ unsigned long flags;
pr_debug("%s: Enter %s\n", dev_name(&pdev->dev), __func__);
msm_host = devm_kzalloc(&pdev->dev, sizeof(struct sdhci_msm_host),
@@ -2767,7 +2862,7 @@
* max. 1ms for reset completion.
*/
ret = readl_poll_timeout(msm_host->core_mem + CORE_POWER,
- pwr, !(pwr & CORE_SW_RST), 100, 10);
+ pwr, !(pwr & CORE_SW_RST), 10, 1000);
if (ret) {
dev_err(&pdev->dev, "reset failed (%d)\n", ret);
@@ -2810,7 +2905,6 @@
host->quirks |= SDHCI_QUIRK_SINGLE_POWER_WRITE;
host->quirks |= SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN;
host->quirks2 |= SDHCI_QUIRK2_ALWAYS_USE_BASE_CLOCK;
- host->quirks2 |= SDHCI_QUIRK2_IGNORE_CMDCRC_FOR_TUNING;
host->quirks2 |= SDHCI_QUIRK2_USE_MAX_DISCARD_SIZE;
host->quirks2 |= SDHCI_QUIRK2_IGNORE_DATATOUT_FOR_R1BCMD;
host->quirks2 |= SDHCI_QUIRK2_BROKEN_PRESET_VALUE;
@@ -2837,6 +2931,8 @@
host->quirks2 |= SDHCI_QUIRK2_RDWR_TX_ACTIVE_EOT;
}
+ host->quirks2 |= SDHCI_QUIRK2_IGN_DATA_END_BIT_ERROR;
+
/* Setup PWRCTL irq */
msm_host->pwr_irq = platform_get_irq_byname(pdev, "pwr_irq");
if (msm_host->pwr_irq < 0) {
@@ -2891,7 +2987,7 @@
msm_host->mmc->caps2 |= MMC_CAP2_STOP_REQUEST;
msm_host->mmc->caps2 |= MMC_CAP2_ASYNC_SDIO_IRQ_4BIT_MODE;
msm_host->mmc->caps2 |= MMC_CAP2_CORE_PM;
- msm_host->mmc->pm_caps |= MMC_PM_KEEP_POWER;
+ msm_host->mmc->pm_caps |= MMC_PM_KEEP_POWER | MMC_PM_WAKE_SDIO_IRQ;
if (msm_host->pdata->nonremovable)
msm_host->mmc->caps |= MMC_CAP_NONREMOVABLE;
@@ -2917,6 +3013,27 @@
dev_err(&pdev->dev, "%s: Failed to set dma mask\n", __func__);
}
+ msm_host->pdata->sdiowakeup_irq = platform_get_irq_byname(pdev,
+ "sdiowakeup_irq");
+ if (msm_host->pdata->sdiowakeup_irq >= 0) {
+ msm_host->is_sdiowakeup_enabled = true;
+ ret = request_irq(msm_host->pdata->sdiowakeup_irq,
+ sdhci_msm_sdiowakeup_irq,
+ IRQF_SHARED | IRQF_TRIGGER_LOW,
+ "sdhci-msm sdiowakeup", host);
+ if (ret) {
+ dev_err(&pdev->dev, "%s: request sdiowakeup IRQ %d: failed: %d\n",
+ __func__, msm_host->pdata->sdiowakeup_irq, ret);
+ msm_host->pdata->sdiowakeup_irq = -1;
+ msm_host->is_sdiowakeup_enabled = false;
+ goto free_cd_gpio;
+ } else {
+ spin_lock_irqsave(&host->lock, flags);
+ sdhci_msm_cfg_sdiowakeup_gpio_irq(host, false);
+ spin_unlock_irqrestore(&host->lock, flags);
+ }
+ }
+
ret = sdhci_add_host(host);
if (ret) {
dev_err(&pdev->dev, "Add host failed (%d)\n", ret);
@@ -2950,6 +3067,17 @@
else if (mmc_use_core_runtime_pm(host->mmc))
pm_runtime_enable(&pdev->dev);
+ if (msm_host->pdata->mpm_sdiowakeup_int != -1) {
+ ret = sdhci_msm_cfg_mpm_pin_wakeup(host, SDC_DAT1_ENABLE);
+ if (ret) {
+ pr_err("%s: enabling wakeup: failed: ret: %d\n",
+ mmc_hostname(host->mmc), ret);
+ ret = 0;
+ msm_host->pdata->mpm_sdiowakeup_int = -1;
+ }
+ }
+
+ device_enable_async_suspend(&pdev->dev);
/* Successful initialization */
goto out;
@@ -2961,6 +3089,8 @@
free_cd_gpio:
if (gpio_is_valid(msm_host->pdata->status_gpio))
mmc_cd_gpio_free(msm_host->mmc);
+ if (sdhci_is_valid_gpio_wakeup_int(msm_host))
+ free_irq(msm_host->pdata->sdiowakeup_irq, host);
vreg_deinit:
sdhci_msm_vreg_init(&pdev->dev, msm_host->pdata, false);
bus_unregister:
@@ -3006,6 +3136,12 @@
pm_runtime_disable(&pdev->dev);
sdhci_pltfm_free(pdev);
+ if (sdhci_is_valid_mpm_wakeup_int(msm_host))
+ sdhci_msm_cfg_mpm_pin_wakeup(host, SDC_DAT1_DISABLE);
+
+ if (sdhci_is_valid_gpio_wakeup_int(msm_host))
+ free_irq(msm_host->pdata->sdiowakeup_irq, host);
+
if (gpio_is_valid(msm_host->pdata->status_gpio))
mmc_cd_gpio_free(msm_host->mmc);
@@ -3021,13 +3157,75 @@
return 0;
}
+static int sdhci_msm_cfg_sdio_wakeup(struct sdhci_host *host, bool enable)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ unsigned long flags;
+ int ret = 0;
+
+ if (!(host->mmc->card && mmc_card_sdio(host->mmc->card) &&
+ (sdhci_is_valid_mpm_wakeup_int(msm_host) ||
+ sdhci_is_valid_gpio_wakeup_int(msm_host)) &&
+ mmc_card_wake_sdio_irq(host->mmc))) {
+ return 1;
+ }
+
+ spin_lock_irqsave(&host->lock, flags);
+ if (enable) {
+ /* configure DAT1 gpio if applicable */
+ if (sdhci_is_valid_gpio_wakeup_int(msm_host)) {
+ ret = enable_irq_wake(msm_host->pdata->sdiowakeup_irq);
+ if (!ret)
+ sdhci_msm_cfg_sdiowakeup_gpio_irq(host, true);
+ goto out;
+ } else {
+ ret = sdhci_msm_cfg_mpm_pin_wakeup(host,
+ SDC_DAT1_ENWAKE);
+ if (ret)
+ goto out;
+ ret = enable_irq_wake(host->irq);
+ if (ret)
+ sdhci_msm_cfg_mpm_pin_wakeup(host,
+ SDC_DAT1_DISWAKE);
+ }
+ } else {
+ if (sdhci_is_valid_gpio_wakeup_int(msm_host)) {
+ ret = disable_irq_wake(msm_host->pdata->sdiowakeup_irq);
+ sdhci_msm_cfg_sdiowakeup_gpio_irq(host, false);
+ } else {
+ ret = sdhci_msm_cfg_mpm_pin_wakeup(host,
+ SDC_DAT1_DISWAKE);
+ if (ret)
+ goto out;
+ ret = disable_irq_wake(host->irq);
+ }
+ }
+out:
+ if (ret)
+ pr_err("%s: %s: %sable wakeup: failed: %d gpio: %d mpm: %d\n",
+ mmc_hostname(host->mmc), __func__, enable ? "en" : "dis",
+ ret, msm_host->pdata->sdiowakeup_irq,
+ msm_host->pdata->mpm_sdiowakeup_int);
+ spin_unlock_irqrestore(&host->lock, flags);
+ return ret;
+}
+
static int sdhci_msm_runtime_suspend(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ int ret;
+
+ ret = sdhci_msm_cfg_sdio_wakeup(host, true);
+ /* pwr_irq is not monitored by mpm on suspend, hence disable it */
+ if (!ret)
+ goto skip_disable_host_irq;
disable_irq(host->irq);
+
+skip_disable_host_irq:
disable_irq(msm_host->pwr_irq);
/*
@@ -3048,10 +3246,17 @@
struct sdhci_host *host = dev_get_drvdata(dev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ int ret;
- enable_irq(msm_host->pwr_irq);
+ ret = sdhci_msm_cfg_sdio_wakeup(host, false);
+ if (!ret)
+ goto skip_enable_host_irq;
+
enable_irq(host->irq);
+skip_enable_host_irq:
+ enable_irq(msm_host->pwr_irq);
+
return 0;
}
@@ -3103,6 +3308,26 @@
out:
return ret;
}
+
+static int sdhci_msm_suspend_noirq(struct device *dev)
+{
+ struct sdhci_host *host = dev_get_drvdata(dev);
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ int ret = 0;
+
+ /*
+ * ksdioirqd may get scheduled after sdhc suspend, hence retry
+ * suspend in case the clocks are ON
+ */
+ if (atomic_read(&msm_host->clks_on)) {
+ pr_warn("%s: %s: clock ON after suspend, aborting suspend\n",
+ mmc_hostname(host->mmc), __func__);
+ ret = -EAGAIN;
+ }
+
+ return ret;
+}
#endif
#ifdef CONFIG_PM
@@ -3110,6 +3335,7 @@
SET_SYSTEM_SLEEP_PM_OPS(sdhci_msm_suspend, sdhci_msm_resume)
SET_RUNTIME_PM_OPS(sdhci_msm_runtime_suspend, sdhci_msm_runtime_resume,
NULL)
+ .suspend_noirq = sdhci_msm_suspend_noirq,
};
#define SDHCI_MSM_PMOPS (&sdhci_msm_pmops)
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 822548e..277aef5 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -32,6 +32,7 @@
#include "sdhci.h"
#define DRIVER_NAME "sdhci"
+#define SDHCI_SUSPEND_TIMEOUT 300 /* 300 ms */
#define DBG(f, x...) \
pr_debug(DRIVER_NAME " [%s()]: " f, __func__,## x)
@@ -68,6 +69,12 @@
}
#endif
+static inline int sdhci_get_async_int_status(struct sdhci_host *host)
+{
+ return (sdhci_readw(host, SDHCI_HOST_CONTROL2) &
+ SDHCI_CTRL_ASYNC_INT_ENABLE) >> 14;
+}
+
static void sdhci_dump_state(struct sdhci_host *host)
{
struct mmc_host *mmc = host->mmc;
@@ -1579,6 +1586,33 @@
spin_unlock_irqrestore(&host->lock, flags);
}
+static void sdhci_cfg_async_intr(struct sdhci_host *host, bool enable)
+{
+ if (!host->async_int_supp)
+ return;
+
+ if (enable)
+ sdhci_writew(host,
+ sdhci_readw(host, SDHCI_HOST_CONTROL2) |
+ SDHCI_CTRL_ASYNC_INT_ENABLE,
+ SDHCI_HOST_CONTROL2);
+ else
+ sdhci_writew(host, sdhci_readw(host, SDHCI_HOST_CONTROL2) &
+ ~SDHCI_CTRL_ASYNC_INT_ENABLE,
+ SDHCI_HOST_CONTROL2);
+}
+
+static void sdhci_cfg_irq(struct sdhci_host *host, bool enable)
+{
+ if (enable && !host->irq_enabled) {
+ enable_irq(host->irq);
+ host->irq_enabled = true;
+ } else if (!enable && host->irq_enabled) {
+ disable_irq_nosync(host->irq);
+ host->irq_enabled = false;
+ }
+}
+
static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)
{
unsigned long flags;
@@ -1594,9 +1628,26 @@
return;
}
- if (ios->clock)
+ spin_lock_irqsave(&host->lock, flags);
+ /* lock is being released intermittently below, hence disable irq */
+ sdhci_cfg_irq(host, false);
+ spin_unlock_irqrestore(&host->lock, flags);
+ if (ios->clock) {
sdhci_set_clock(host, ios->clock);
-
+ if (host->async_int_supp && sdhci_get_async_int_status(host)) {
+ if (host->disable_sdio_irq_deferred) {
+ pr_debug("%s: %s: disable sdio irq\n",
+ mmc_hostname(host->mmc), __func__);
+ host->mmc->ops->enable_sdio_irq(host->mmc, 0);
+ host->disable_sdio_irq_deferred = false;
+ }
+ spin_lock_irqsave(&host->lock, flags);
+ sdhci_cfg_async_intr(host, false);
+ spin_unlock_irqrestore(&host->lock, flags);
+ pr_debug("%s: %s: unconfig async intr\n",
+ mmc_hostname(host->mmc), __func__);
+ }
+ }
/*
* The controller clocks may be off during power-up and we may end up
* enabling card clock before giving power to the card. Hence, during
@@ -1622,6 +1673,7 @@
}
spin_lock_irqsave(&host->lock, flags);
if (!host->clock) {
+ sdhci_cfg_irq(host, true);
spin_unlock_irqrestore(&host->lock, flags);
mutex_unlock(&host->ios_mutex);
return;
@@ -1781,9 +1833,18 @@
if (host->vmmc && vdd_bit != -1)
mmc_regulator_set_ocr(host->mmc, host->vmmc, vdd_bit);
}
- if (!ios->clock)
+ if (!ios->clock) {
+ if (host->async_int_supp && host->mmc->card &&
+ mmc_card_sdio(host->mmc->card)) {
+ sdhci_cfg_async_intr(host, true);
+ pr_debug("%s: %s: config async intr\n",
+ mmc_hostname(host->mmc), __func__);
+ }
sdhci_set_clock(host, ios->clock);
-
+ }
+ spin_lock_irqsave(&host->lock, flags);
+ sdhci_cfg_irq(host, true);
+ spin_unlock_irqrestore(&host->lock, flags);
mmiowb();
mutex_unlock(&host->ios_mutex);
}
@@ -1863,6 +1924,14 @@
if (host->flags & SDHCI_DEVICE_DEAD)
goto out;
+ if (!enable && !host->clock) {
+ pr_debug("%s: %s: defered disabling card intr\n",
+ host->mmc ? mmc_hostname(host->mmc) : "null",
+ __func__);
+ host->disable_sdio_irq_deferred = true;
+ return;
+ }
+
if (enable)
host->flags |= SDHCI_SDIO_IRQ_ENABLED;
else
@@ -2522,17 +2591,6 @@
host->cmd->error = -EILSEQ;
}
- if (host->quirks2 & SDHCI_QUIRK2_IGNORE_CMDCRC_FOR_TUNING) {
- if ((host->cmd->opcode == MMC_SEND_TUNING_BLOCK_HS400) ||
- (host->cmd->opcode == MMC_SEND_TUNING_BLOCK_HS200) ||
- (host->cmd->opcode == MMC_SEND_TUNING_BLOCK)) {
- if (intmask & SDHCI_INT_CRC) {
- sdhci_reset(host, SDHCI_RESET_CMD);
- host->cmd->error = 0;
- }
- }
- }
-
if (host->cmd->error) {
if (host->cmd->error == -EILSEQ)
host->flags |= SDHCI_NEEDS_RETUNING;
@@ -2562,17 +2620,6 @@
* fall through and take the SDHCI_INT_RESPONSE */
}
- if (host->quirks2 & SDHCI_QUIRK2_IGNORE_CMDCRC_FOR_TUNING) {
- if ((host->cmd->opcode == MMC_SEND_TUNING_BLOCK_HS400) ||
- (host->cmd->opcode == MMC_SEND_TUNING_BLOCK_HS200) ||
- (host->cmd->opcode == MMC_SEND_TUNING_BLOCK)) {
- if (intmask & SDHCI_INT_CRC) {
- sdhci_finish_command(host);
- return;
- }
- }
- }
-
if (intmask & SDHCI_INT_RESPONSE)
sdhci_finish_command(host);
}
@@ -2658,8 +2705,7 @@
host->data->error = -EIO;
}
if (host->data->error) {
- if ((intmask & (SDHCI_INT_DATA_CRC | SDHCI_INT_DATA_TIMEOUT)) &&
- (host->quirks2 & SDHCI_QUIRK2_IGNORE_CMDCRC_FOR_TUNING)) {
+ if (intmask & (SDHCI_INT_DATA_CRC | SDHCI_INT_DATA_TIMEOUT)) {
command = SDHCI_GET_CMD(sdhci_readw(host,
SDHCI_COMMAND));
if ((command != MMC_SEND_TUNING_BLOCK_HS400) &&
@@ -2742,6 +2788,23 @@
return IRQ_HANDLED;
}
+ if (!host->clock && host->mmc->card &&
+ mmc_card_sdio(host->mmc->card)) {
+ /* SDIO async. interrupt is level-sensitive */
+ sdhci_cfg_irq(host, false);
+ pr_debug("%s: got async-irq: clocks: %d gated: %d host-irq[en:1/dis:0]: %d\n",
+ mmc_hostname(host->mmc), host->clock,
+ host->mmc->clk_gated, host->irq_enabled);
+ spin_unlock(&host->lock);
+ /* prevent suspend till the ksdioirqd runs or resume happens */
+ if ((host->mmc->dev_status == DEV_SUSPENDING) ||
+ (host->mmc->dev_status == DEV_SUSPENDED))
+ pm_wakeup_event(&host->mmc->card->dev,
+ SDHCI_SUSPEND_TIMEOUT);
+ else
+ mmc_signal_sdio_irq(host->mmc);
+ return IRQ_HANDLED;
+ }
intmask = sdhci_readl(host, SDHCI_INT_STATUS);
if (!intmask || intmask == 0xffffffff) {
@@ -2837,9 +2900,13 @@
/*
* We have to delay this as it calls back into the driver.
*/
- if (cardint)
+ if (cardint) {
+ /* clks are on, but suspend may be in progress */
+ if (host->mmc->dev_status == DEV_SUSPENDING)
+ pm_wakeup_event(&host->mmc->card->dev,
+ SDHCI_SUSPEND_TIMEOUT);
mmc_signal_sdio_irq(host->mmc);
-
+ }
return result;
}
@@ -3489,6 +3556,7 @@
if (ret)
goto untasklet;
+ host->irq_enabled = true;
host->vmmc = regulator_get(mmc_dev(mmc), "vmmc");
if (IS_ERR(host->vmmc)) {
pr_info("%s: no vmmc regulator found\n", mmc_hostname(mmc));
@@ -3532,8 +3600,12 @@
mmc_hostname(mmc), ret);
}
+ if (caps[0] & SDHCI_ASYNC_INTR)
+ host->async_int_supp = true;
mmc_add_host(mmc);
+ if (host->quirks2 & SDHCI_QUIRK2_IGN_DATA_END_BIT_ERROR)
+ sdhci_clear_set_irqs(host, SDHCI_INT_DATA_END_BIT, 0);
pr_info("%s: SDHCI controller on %s [%s] using %s\n",
mmc_hostname(mmc), host->hw_name, dev_name(mmc_dev(mmc)),
(host->flags & SDHCI_USE_ADMA) ? "ADMA" :
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index db4806d..8c2320b 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -175,6 +175,7 @@
#define SDHCI_CTRL_DRV_TYPE_D 0x0030
#define SDHCI_CTRL_EXEC_TUNING 0x0040
#define SDHCI_CTRL_TUNED_CLK 0x0080
+#define SDHCI_CTRL_ASYNC_INT_ENABLE 0x4000
#define SDHCI_CTRL_PRESET_VAL_ENABLE 0x8000
#define SDHCI_CAPABILITIES 0x40
@@ -195,6 +196,7 @@
#define SDHCI_CAN_VDD_300 0x02000000
#define SDHCI_CAN_VDD_180 0x04000000
#define SDHCI_CAN_64BIT 0x10000000
+#define SDHCI_ASYNC_INTR 0x20000000
#define SDHCI_SUPPORT_SDR50 0x00000001
#define SDHCI_SUPPORT_SDR104 0x00000002
diff --git a/drivers/net/wireless/wcnss/wcnss_wlan.c b/drivers/net/wireless/wcnss/wcnss_wlan.c
index a3e88b4..e03d5be 100644
--- a/drivers/net/wireless/wcnss/wcnss_wlan.c
+++ b/drivers/net/wireless/wcnss/wcnss_wlan.c
@@ -147,6 +147,7 @@
#define PRONTO_PLL_STATUS_OFFSET 0x1c
#define MSM_PRONTO_MCU_BASE 0xfb080c00
+#define MCU_APB2PHY_STATUS_OFFSET 0xec
#define MCU_CBR_CCAHB_ERR_OFFSET 0x380
#define MCU_CBR_CAHB_ERR_OFFSET 0x384
#define MCU_CBR_CCAHB_TIMEOUT_OFFSET 0x388
@@ -185,6 +186,7 @@
#define WCNSS_USR_CTRL_MSG_START 0x00000000
#define WCNSS_USR_SERIAL_NUM (WCNSS_USR_CTRL_MSG_START + 1)
#define WCNSS_USR_HAS_CAL_DATA (WCNSS_USR_CTRL_MSG_START + 2)
+#define WCNSS_USR_WLAN_MAC_ADDR (WCNSS_USR_CTRL_MSG_START + 3)
#define MAC_ADDRESS_STR "%02x:%02x:%02x:%02x:%02x:%02x"
@@ -240,6 +242,13 @@
{"LVS1", 0x060},
};
+static int wcnss_notif_cb(struct notifier_block *this, unsigned long code,
+ void *ss_handle);
+
+static struct notifier_block wnb = {
+ .notifier_call = wcnss_notif_cb,
+};
+
#define NVBIN_FILE "wlan/prima/WCNSS_qcom_wlan_nv.bin"
/*
@@ -408,6 +417,7 @@
u16 unsafe_ch_count;
u16 unsafe_ch_list[WCNSS_MAX_CH_NUM];
void *wcnss_notif_hdle;
+ u8 is_shutdown;
} *penv = NULL;
static ssize_t wcnss_wlan_macaddr_store(struct device *dev,
@@ -741,6 +751,10 @@
reg = readl_relaxed(penv->wlan_tx_phy_aborts);
pr_err("WLAN_TX_PHY_ABORTS %08x\n", reg);
+ reg_addr = penv->pronto_mcu_base + MCU_APB2PHY_STATUS_OFFSET;
+ reg = readl_relaxed(reg_addr);
+ pr_err("MCU_APB2PHY_STATUS %08x\n", reg);
+
reg_addr = penv->pronto_mcu_base + MCU_CBR_CCAHB_ERR_OFFSET;
reg = readl_relaxed(reg_addr);
pr_err("MCU_CBR_CCAHB_ERR %08x\n", reg);
@@ -1124,12 +1138,20 @@
int wcnss_device_ready(void)
{
- if (penv && penv->pdev && penv->nv_downloaded)
+ if (penv && penv->pdev && penv->nv_downloaded &&
+ !wcnss_device_is_shutdown())
return 1;
return 0;
}
EXPORT_SYMBOL(wcnss_device_ready);
+int wcnss_device_is_shutdown(void)
+{
+ if (penv && penv->is_shutdown)
+ return 1;
+ return 0;
+}
+EXPORT_SYMBOL(wcnss_device_is_shutdown);
struct resource *wcnss_wlan_get_memory_map(struct device *dev)
{
@@ -2076,6 +2098,16 @@
has_calibrated_data = buf[2];
break;
+ case WCNSS_USR_WLAN_MAC_ADDR:
+ memcpy(&penv->wlan_nv_macAddr, &buf[2],
+ sizeof(penv->wlan_nv_macAddr));
+
+ pr_debug("%s: MAC Addr:" MAC_ADDRESS_STR "\n", __func__,
+ penv->wlan_nv_macAddr[0], penv->wlan_nv_macAddr[1],
+ penv->wlan_nv_macAddr[2], penv->wlan_nv_macAddr[3],
+ penv->wlan_nv_macAddr[4], penv->wlan_nv_macAddr[5]);
+ break;
+
default:
pr_err("%s: Invalid command %d\n", __func__, cmd);
break;
@@ -2333,6 +2365,10 @@
} while (pil_retry++ < WCNSS_MAX_PIL_RETRY && IS_ERR(penv->pil));
if (pil_retry >= WCNSS_MAX_PIL_RETRY) {
+ wcnss_reset_intr();
+ if (penv->wcnss_notif_hdle)
+ subsys_notif_unregister_notifier(penv->wcnss_notif_hdle,
+ &wnb);
penv->pil = NULL;
goto fail_pil;
}
@@ -2518,15 +2554,14 @@
if (SUBSYS_POWERUP_FAILURE == code)
wcnss_pronto_log_debug_regs();
+ else if (SUBSYS_BEFORE_SHUTDOWN == code)
+ penv->is_shutdown = 1;
+ else if (SUBSYS_AFTER_POWERUP == code)
+ penv->is_shutdown = 0;
return NOTIFY_DONE;
}
-static struct notifier_block wnb = {
- .notifier_call = wcnss_notif_cb,
-};
-
-
static const struct file_operations wcnss_node_fops = {
.owner = THIS_MODULE,
.open = wcnss_node_open,
diff --git a/drivers/nfc/nfc-nci.c b/drivers/nfc/nfc-nci.c
index a44c06c..018c3a6 100644
--- a/drivers/nfc/nfc-nci.c
+++ b/drivers/nfc/nfc-nci.c
@@ -13,6 +13,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
+#include <linux/reboot.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/irq.h>
@@ -47,7 +48,7 @@
MODULE_DEVICE_TABLE(of, msm_match_table);
#define MAX_BUFFER_SIZE (780)
-#define PACKET_MAX_LENGTH (258)
+#define PACKET_MAX_LENGTH (258)
/* Read data */
#define PACKET_HEADER_SIZE_NCI (4)
#define PACKET_TYPE_NCI (16)
@@ -58,7 +59,7 @@
#define NTF_TIMEOUT (10000)
#define CORE_RESET_RSP_GID (0x60)
#define CORE_RESET_OID (0x00)
-#define CORE_RST_NTF_LENGTH (0x02)
+#define CORE_RST_NTF_LENGTH (0x02)
static void clk_req_update(struct work_struct *work);
@@ -115,6 +116,9 @@
static int ftm_werr_code;
+unsigned int disable_ctrl;
+bool region2_sent;
+
static void qca199x_init_stat(struct qca199x_dev *qca199x_dev)
{
qca199x_dev->count_irq = 0;
@@ -491,6 +495,12 @@
}
mutex_unlock(&qca199x_dev->read_mutex);
+ /* If we detect a Region2 command prior to power-down */
+ if ((tmp[0] == 0x2F) && (tmp[1] == 0x01) && (tmp[2] == 0x02) &&
+ (tmp[3] == 0x08) && (tmp[4] == 0x00)) {
+ region2_sent = true;
+ }
+
return ret;
}
@@ -606,7 +616,7 @@
if (r < 0)
goto err_req;
/*
- Also, set flag for initial NCI write following resetas
+ Also, set flag for initial NCI write following reset as
may wish to do some house keeping. Ensure no pending
messages in NFCC buffers which may be wrongly
construed as response to initial message
@@ -718,7 +728,7 @@
int r = 0;
unsigned short slave_addr = 0xE;
unsigned short curr_addr;
-
+ unsigned char raw_nci_wake[] = {0x10, 0x0F};
unsigned char raw_chip_version_addr = 0x00;
unsigned char raw_chip_rev_id_addr = 0x9C;
unsigned char raw_chip_version = 0xFF;
@@ -728,9 +738,22 @@
platform_data = qca199x_dev->client->dev.platform_data;
+ /*
+ * Always wake up chip when reading 0x9C, otherwise this
+ * register is not updated
+ */
+ curr_addr = qca199x_dev->client->addr;
+ qca199x_dev->client->addr = slave_addr;
+ r = nfc_i2c_write(qca199x_dev->client, &raw_nci_wake[0],
+ sizeof(raw_nci_wake));
+ r = sizeof(raw_nci_wake);
+ if (r != sizeof(raw_nci_wake))
+ goto invalid_wake_up;
+ qca199x_dev->state = NFCC_STATE_NORMAL_WAKE;
+
+ /* sleep to ensure the NFCC has time to wake up */
+ usleep(100);
if (arg == 0) {
- curr_addr = qca199x_dev->client->addr;
- qca199x_dev->client->addr = slave_addr;
r = nfc_i2c_write(qca199x_dev->client,
&raw_chip_version_addr, 1);
if (r < 0)
@@ -739,21 +762,19 @@
r = i2c_master_recv(qca199x_dev->client, &raw_chip_version, 1);
/* Restore original NFCC slave I2C address */
qca199x_dev->client->addr = curr_addr;
- }
- if (arg == 1) {
- curr_addr = qca199x_dev->client->addr;
- qca199x_dev->client->addr = slave_addr;
+ } else if (arg == 1) {
r = nfc_i2c_write(qca199x_dev->client,
&raw_chip_rev_id_addr, 1);
if (r < 0)
goto invalid_wr;
- usleep(10);
+ usleep(20);
r = i2c_master_recv(qca199x_dev->client, &raw_chip_version, 1);
/* Restore original NFCC slave I2C address */
qca199x_dev->client->addr = curr_addr;
}
-
return raw_chip_version;
+invalid_wake_up:
+ raw_chip_version = 0xFE;
invalid_wr:
raw_chip_version = 0xFF;
dev_err(&qca199x_dev->client->dev,
@@ -929,121 +950,117 @@
if (r < 0)
goto err_init;
- if (0x10 != (0x10 & buf)) {
- RAW(s73, 0x02);
+ RAW(s73, 0x02);
- r = nfc_i2c_write(client, &raw_s73[0], sizeof(raw_s73));
- if (r < 0)
- goto err_init;
-
- usleep(1000);
- RAW(1p8_CONTROL_011, XTAL_CLOCK | 0x01);
-
- r = nfc_i2c_write(client, &raw_1p8_CONTROL_011[0],
- sizeof(raw_1p8_CONTROL_011));
- if (r < 0)
- goto err_init;
-
- usleep(1000);
- RAW(1P8_CONTROL_010, (0x8));
- r = nfc_i2c_write(client, &raw_1P8_CONTROL_010[0],
- sizeof(raw_1P8_CONTROL_010));
- if (r < 0)
- goto err_init;
-
- usleep(10000); /* 10ms wait */
- RAW(1P8_CONTROL_010, (0xC));
- r = nfc_i2c_write(client, &raw_1P8_CONTROL_010[0],
- sizeof(raw_1P8_CONTROL_010));
- if (r < 0)
- goto err_init;
-
- usleep(100); /* 100uS wait */
- RAW(1P8_X0_0B0, (FREQ_SEL_19));
- r = nfc_i2c_write(client, &raw_1P8_X0_0B0[0],
- sizeof(raw_1P8_X0_0B0));
- if (r < 0)
- goto err_init;
-
- usleep(1000);
-
- /* PWR_EN = 1 */
- RAW(1P8_CONTROL_010, (0xd));
- r = nfc_i2c_write(client, &raw_1P8_CONTROL_010[0],
- sizeof(raw_1P8_CONTROL_010));
- if (r < 0)
- goto err_init;
-
-
- usleep(20000); /* 20ms wait */
- /* LS_EN = 1 */
- RAW(1P8_CONTROL_010, 0xF);
- r = nfc_i2c_write(client, &raw_1P8_CONTROL_010[0],
- sizeof(raw_1P8_CONTROL_010));
- if (r < 0)
- goto err_init;
-
- usleep(20000); /* 20ms wait */
-
- /* Enable the PMIC clock */
- RAW(1P8_PAD_CFG_CLK_REQ, (0x1));
- r = nfc_i2c_write(client, &raw_1P8_PAD_CFG_CLK_REQ[0],
- sizeof(raw_1P8_PAD_CFG_CLK_REQ));
- if (r < 0)
- goto err_init;
-
- usleep(1000);
-
- RAW(1P8_PAD_CFG_PWR_REQ, (0x1));
- r = nfc_i2c_write(client, &raw_1P8_PAD_CFG_PWR_REQ[0],
- sizeof(raw_1P8_PAD_CFG_PWR_REQ));
- if (r < 0)
- goto err_init;
-
- usleep(1000);
-
- RAW(slave2, 0x10);
- r = nfc_i2c_write(client, &raw_slave2[0], sizeof(raw_slave2));
- if (r < 0)
- goto err_init;
-
- usleep(1000);
-
- RAW(slave1, NCI_I2C_SLAVE);
- r = nfc_i2c_write(client, &raw_slave1[0], sizeof(raw_slave1));
- if (r < 0)
- goto err_init;
-
- usleep(1000);
-
- /* QCA199x NFCC CPU should now boot... */
- r = i2c_master_recv(client, &raw_slave1_rd, 1);
- /* Talk on NCI slave address NCI_I2C_SLAVE 0x2C*/
- client->addr = NCI_I2C_SLAVE;
-
- /*
- Start with small delay and then we will poll until we
- get a core reset notification - This is time for chip
- & NFCC controller to come-up.
- */
- usleep(1000); /* 1 ms */
-
- do {
- ret = i2c_master_recv(client, rsp, 5);
- /* Found core reset notification */
- if (((rsp[0] == CORE_RESET_RSP_GID) &&
- (rsp[1] == CORE_RESET_OID) &&
- (rsp[2] == CORE_RST_NTF_LENGTH))
- || time_taken == NTF_TIMEOUT) {
- core_reset_completed = true;
- }
- usleep(10); /* 10us sleep before retry */
- time_taken++;
- } while (!core_reset_completed);
- r = 0;
- } else {
+ r = nfc_i2c_write(client, &raw_s73[0], sizeof(raw_s73));
+ if (r < 0)
goto err_init;
- }
+
+ usleep(1000);
+ RAW(1p8_CONTROL_011, XTAL_CLOCK | 0x01);
+
+ r = nfc_i2c_write(client, &raw_1p8_CONTROL_011[0],
+ sizeof(raw_1p8_CONTROL_011));
+ if (r < 0)
+ goto err_init;
+
+ usleep(1000);
+ RAW(1P8_CONTROL_010, (0x8));
+ r = nfc_i2c_write(client, &raw_1P8_CONTROL_010[0],
+ sizeof(raw_1P8_CONTROL_010));
+ if (r < 0)
+ goto err_init;
+
+ usleep(10000); /* 10ms wait */
+ RAW(1P8_CONTROL_010, (0xC));
+ r = nfc_i2c_write(client, &raw_1P8_CONTROL_010[0],
+ sizeof(raw_1P8_CONTROL_010));
+ if (r < 0)
+ goto err_init;
+
+ usleep(100); /* 100uS wait */
+ RAW(1P8_X0_0B0, (FREQ_SEL_19));
+ r = nfc_i2c_write(client, &raw_1P8_X0_0B0[0],
+ sizeof(raw_1P8_X0_0B0));
+ if (r < 0)
+ goto err_init;
+
+ usleep(1000);
+
+ /* PWR_EN = 1 */
+ RAW(1P8_CONTROL_010, (0xd));
+ r = nfc_i2c_write(client, &raw_1P8_CONTROL_010[0],
+ sizeof(raw_1P8_CONTROL_010));
+ if (r < 0)
+ goto err_init;
+
+
+ usleep(20000); /* 20ms wait */
+ /* LS_EN = 1 */
+ RAW(1P8_CONTROL_010, 0xF);
+ r = nfc_i2c_write(client, &raw_1P8_CONTROL_010[0],
+ sizeof(raw_1P8_CONTROL_010));
+ if (r < 0)
+ goto err_init;
+
+ usleep(20000); /* 20ms wait */
+
+ /* Enable the PMIC clock */
+ RAW(1P8_PAD_CFG_CLK_REQ, (0x1));
+ r = nfc_i2c_write(client, &raw_1P8_PAD_CFG_CLK_REQ[0],
+ sizeof(raw_1P8_PAD_CFG_CLK_REQ));
+ if (r < 0)
+ goto err_init;
+
+ usleep(1000);
+
+ RAW(1P8_PAD_CFG_PWR_REQ, (0x1));
+ r = nfc_i2c_write(client, &raw_1P8_PAD_CFG_PWR_REQ[0],
+ sizeof(raw_1P8_PAD_CFG_PWR_REQ));
+ if (r < 0)
+ goto err_init;
+
+ usleep(1000);
+
+ RAW(slave2, 0x10);
+ r = nfc_i2c_write(client, &raw_slave2[0], sizeof(raw_slave2));
+ if (r < 0)
+ goto err_init;
+
+ usleep(1000);
+
+ RAW(slave1, NCI_I2C_SLAVE);
+ r = nfc_i2c_write(client, &raw_slave1[0], sizeof(raw_slave1));
+ if (r < 0)
+ goto err_init;
+
+ usleep(1000);
+
+ /* QCA199x NFCC CPU should now boot... */
+ r = i2c_master_recv(client, &raw_slave1_rd, 1);
+ /* Talk on NCI slave address NCI_I2C_SLAVE 0x2C*/
+ client->addr = NCI_I2C_SLAVE;
+
+ /*
+ Start with small delay and then we will poll until we
+ get a core reset notification - This is time for chip
+ & NFCC controller to come-up.
+ */
+ usleep(1000); /* 1 ms */
+
+ do {
+ ret = i2c_master_recv(client, rsp, 5);
+ /* Found core reset notification */
+ if (((rsp[0] == CORE_RESET_RSP_GID) &&
+ (rsp[1] == CORE_RESET_OID) &&
+ (rsp[2] == CORE_RST_NTF_LENGTH))
+ || time_taken == NTF_TIMEOUT) {
+ core_reset_completed = true;
+ }
+ usleep(10); /* 10us sleep before retry */
+ time_taken++;
+ } while (!core_reset_completed);
+ r = 0;
return r;
err_init:
r = 1;
@@ -1143,6 +1160,7 @@
pdata->dis_gpio = of_get_named_gpio(np, "qcom,dis-gpio", 0);
if ((!gpio_is_valid(pdata->dis_gpio)))
return -EINVAL;
+ disable_ctrl = pdata->dis_gpio;
pdata->irq_gpio = of_get_named_gpio(np, "qcom,irq-gpio", 0);
if ((!gpio_is_valid(pdata->irq_gpio)))
@@ -1151,10 +1169,7 @@
r = of_property_read_string(np, "qcom,clk-src", &pdata->clk_src_name);
if (strcmp(pdata->clk_src_name, "GPCLK2")) {
- r = of_property_read_u32(np, "qcom,clk-gpio",
- &pdata->clkreq_gpio);
- if (r)
- return -EINVAL;
+ pdata->clkreq_gpio = of_get_named_gpio(np, "qcom,clk-gpio", 0);
}
if ((!strcmp(pdata->clk_src_name, "GPCLK")) ||
@@ -1168,6 +1183,7 @@
if ((!gpio_is_valid(pdata->irq_gpio_clk_req)))
return -EINVAL;
}
+
if (r)
return -EINVAL;
return r;
@@ -1178,7 +1194,6 @@
{
int r = 0;
int irqn = 0;
- struct device_node *node = client->dev.of_node;
struct qca199x_platform_data *platform_data;
struct qca199x_dev *qca199x_dev;
@@ -1246,8 +1261,12 @@
goto err_free_dev;
}
- /* Put device in ULPM */
- gpio_set_value(platform_data->dis_gpio, 0);
+ /* Guarantee that the NFCC starts in a clean state. */
+ gpio_set_value(platform_data->dis_gpio, 1);/* HPD */
+ usleep(200);
+ gpio_set_value(platform_data->dis_gpio, 0);/* ULPM */
+ usleep(200);
+
r = nfcc_hw_check(client, platform_data->reg);
if (r) {
/* We don't think there is hardware but just in case HPD */
@@ -1326,9 +1345,6 @@
}
if (strcmp(platform_data->clk_src_name, "GPCLK2")) {
- platform_data->clkreq_gpio =
- of_get_named_gpio(node, "qcom,clk-gpio", 0);
-
if (gpio_is_valid(platform_data->clkreq_gpio)) {
r = gpio_request(platform_data->clkreq_gpio,
"nfc_clkreq_gpio");
@@ -1439,6 +1455,10 @@
}
i2c_set_clientdata(client, qca199x_dev);
gpio_set_value(platform_data->dis_gpio, 1);
+
+ /* To keep track if region2 command has been sent to controller */
+ region2_sent = false;
+
dev_dbg(&client->dev,
"nfc-nci probe: %s, probing qca1990 exited successfully\n",
__func__);
@@ -1513,17 +1533,52 @@
},
};
+
+static int nfcc_reboot(struct notifier_block *notifier, unsigned long val,
+ void *v)
+{
+ /*
+ * Set DISABLE=1 *ONLY* if the NFC service has been disabled.
+ * This will put NFCC into HPD(Hard Power Down) state for power
+ * saving when powering down(Low Batt. or Power off handset)
+ * If user requires NFC and CE mode when powered down(PD) the
+ * middleware puts NFCC into region2 prior to PD. In this case
+ * we DO NOT HPD chip as this will trash Region2 and CE support
+ * when handset is PD.
+ */
+ if (region2_sent == false) {
+ /* HPD the NFCC */
+ gpio_set_value(disable_ctrl, 1);
+ }
+ return NOTIFY_OK;
+}
+
+
+static struct notifier_block nfcc_notifier = {
+ .notifier_call = nfcc_reboot,
+ .next = NULL,
+ .priority = 0
+};
+
/*
* module load/unload record keeping
*/
static int __init qca199x_dev_init(void)
{
+ int ret;
+
+ ret = register_reboot_notifier(&nfcc_notifier);
+ if (ret) {
+ pr_err("cannot register reboot notifier (err=%d)\n", ret);
+ return ret;
+ }
return i2c_add_driver(&qca199x);
}
module_init(qca199x_dev_init);
static void __exit qca199x_dev_exit(void)
{
+ unregister_reboot_notifier(&nfcc_notifier);
i2c_del_driver(&qca199x);
}
module_exit(qca199x_dev_exit);
diff --git a/drivers/platform/msm/ipa/ipa.c b/drivers/platform/msm/ipa/ipa.c
index 20603f5..c93ec5f 100644
--- a/drivers/platform/msm/ipa/ipa.c
+++ b/drivers/platform/msm/ipa/ipa.c
@@ -544,6 +544,13 @@
retval = -EFAULT;
break;
}
+
+ if (((struct ipa_ioc_query_intf_tx_props *)header)->num_tx_props
+ > IPA_NUM_PROPS_MAX) {
+ retval = -EFAULT;
+ break;
+ }
+
pyld_sz = sz + ((struct ipa_ioc_query_intf_tx_props *)
header)->num_tx_props *
sizeof(struct ipa_ioc_tx_intf_prop);
@@ -572,6 +579,13 @@
retval = -EFAULT;
break;
}
+
+ if (((struct ipa_ioc_query_intf_rx_props *)header)->num_rx_props
+ > IPA_NUM_PROPS_MAX) {
+ retval = -EFAULT;
+ break;
+ }
+
pyld_sz = sz + ((struct ipa_ioc_query_intf_rx_props *)
header)->num_rx_props *
sizeof(struct ipa_ioc_rx_intf_prop);
diff --git a/drivers/platform/msm/ipa/ipa_intf.c b/drivers/platform/msm/ipa/ipa_intf.c
index 5ee1929..ea5c97f 100644
--- a/drivers/platform/msm/ipa/ipa_intf.c
+++ b/drivers/platform/msm/ipa/ipa_intf.c
@@ -60,6 +60,18 @@
return -EINVAL;
}
+ if (tx && tx->num_props > IPA_NUM_PROPS_MAX) {
+ IPAERR("invalid tx num_props=%d max=%d\n", tx->num_props,
+ IPA_NUM_PROPS_MAX);
+ return -EINVAL;
+ }
+
+ if (rx && rx->num_props > IPA_NUM_PROPS_MAX) {
+ IPAERR("invalid rx num_props=%d max=%d\n", rx->num_props,
+ IPA_NUM_PROPS_MAX);
+ return -EINVAL;
+ }
+
len = sizeof(struct ipa_intf);
intf = kzalloc(len, GFP_KERNEL);
if (intf == NULL) {
diff --git a/drivers/platform/msm/qpnp-power-on.c b/drivers/platform/msm/qpnp-power-on.c
index 0ef2639..1a3baf5 100644
--- a/drivers/platform/msm/qpnp-power-on.c
+++ b/drivers/platform/msm/qpnp-power-on.c
@@ -24,6 +24,13 @@
#include <linux/log2.h>
#include <linux/qpnp/power-on.h>
+#define PMIC_VER_8941 0x01
+#define PMIC_VERSION_REG 0x0105
+#define PMIC_VERSION_REV4_REG 0x0103
+
+#define PMIC8941_V1_REV4 0x01
+#define PMIC8941_V2_REV4 0x02
+
/* Common PNP defines */
#define QPNP_PON_REVISION2(base) (base + 0x01)
@@ -36,6 +43,7 @@
#define QPNP_PON_REASON1(base) (base + 0x8)
#define QPNP_PON_WARM_RESET_REASON1(base) (base + 0xA)
#define QPNP_PON_WARM_RESET_REASON2(base) (base + 0xB)
+#define QPNP_POFF_REASON1(base) (base + 0xC)
#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)
@@ -93,7 +101,8 @@
#define QPNP_PON_S3_DBC_DELAY_MASK 0x07
#define QPNP_PON_RESET_TYPE_MAX 0xF
#define PON_S1_COUNT_MAX 0xF
-#define PON_REASON_MAX 8
+#define QPNP_PON_MIN_DBC_US (USEC_PER_SEC / 64)
+#define QPNP_PON_MAX_DBC_US (USEC_PER_SEC * 2)
#define QPNP_KEY_STATUS_DELAY msecs_to_jiffies(250)
#define QPNP_PON_REV_B 0x01
@@ -147,6 +156,26 @@
[7] = "Triggered from KPD (power key press)",
};
+static const char * const qpnp_poff_reason[] = {
+ [0] = "Triggered from SOFT (Software)",
+ [1] = "Triggered from PS_HOLD (PS_HOLD/MSM controlled shutdown)",
+ [2] = "Triggered from PMIC_WD (PMIC watchdog)",
+ [3] = "Triggered from GP1 (Keypad_Reset1)",
+ [4] = "Triggered from GP2 (Keypad_Reset2)",
+ [5] = "Triggered from KPDPWR_AND_RESIN"
+ "(Simultaneous power key and reset line)",
+ [6] = "Triggered from RESIN_N (Reset line/Volume Down Key)",
+ [7] = "Triggered from KPDPWR_N (Long Power Key hold)",
+ [8] = "N/A",
+ [9] = "N/A",
+ [10] = "N/A",
+ [11] = "Triggered from CHARGER (Charger ENUM_TIMER, BOOT_DONE)",
+ [12] = "Triggered from TFT (Thermal Fault Tolerance)",
+ [13] = "Triggered from UVLO (Under Voltage Lock Out)",
+ [14] = "Triggered from OTST3 (Overtemp)",
+ [15] = "Triggered from STAGE3 (Stage 3 reset)",
+};
+
static int
qpnp_pon_masked_write(struct qpnp_pon *pon, u16 addr, u8 mask, u8 val)
{
@@ -754,6 +783,8 @@
struct device_node *pp = NULL;
struct qpnp_pon_config *cfg;
u8 pon_ver;
+ u8 pmic_type;
+ u8 revid_rev4;
/* Check if it is rev B */
rc = spmi_ext_register_readl(pon->spmi->ctrl, pon->spmi->sid,
@@ -836,6 +867,38 @@
cfg->use_bark = of_property_read_bool(pp,
"qcom,use-bark");
+
+ rc = spmi_ext_register_readl(pon->spmi->ctrl,
+ pon->spmi->sid, PMIC_VERSION_REG,
+ &pmic_type, 1);
+
+ if (rc) {
+ dev_err(&pon->spmi->dev,
+ "Unable to read PMIC type\n");
+ return rc;
+ }
+
+ if (pmic_type == PMIC_VER_8941) {
+
+ rc = spmi_ext_register_readl(pon->spmi->ctrl,
+ pon->spmi->sid, PMIC_VERSION_REV4_REG,
+ &revid_rev4, 1);
+
+ if (rc) {
+ dev_err(&pon->spmi->dev,
+ "Unable to read PMIC revision ID\n");
+ return rc;
+ }
+
+ /*PM8941 V3 does not have harware bug. Hence
+ bark is not required from PMIC versions 3.0*/
+ if (!(revid_rev4 == PMIC8941_V1_REV4 ||
+ revid_rev4 == PMIC8941_V2_REV4)) {
+ cfg->support_reset = false;
+ cfg->use_bark = false;
+ }
+ }
+
if (cfg->use_bark) {
cfg->bark_irq = spmi_get_irq_byname(pon->spmi,
NULL, "resin-bark");
@@ -1035,9 +1098,10 @@
struct device_node *itr = NULL;
u32 delay = 0, s3_debounce = 0;
int rc, sys_reset, index;
- u8 pon_sts = 0;
+ u8 pon_sts = 0, buf[2];
const char *s3_src;
u8 s3_src_reg;
+ u16 poff_sts = 0;
pon = devm_kzalloc(&spmi->dev, sizeof(struct qpnp_pon),
GFP_KERNEL);
@@ -1085,14 +1149,39 @@
dev_err(&pon->spmi->dev, "Unable to read PON_RESASON1 reg\n");
return rc;
}
- index = ffs(pon_sts);
- if ((index > PON_REASON_MAX) || (index < 0))
- index = 0;
+ boot_reason = ffs(pon_sts);
+ index = ffs(pon_sts) - 1;
cold_boot = !qpnp_pon_is_warm_reset();
- pr_info("PMIC@SID%d Power-on reason: %s and '%s' boot\n",
- pon->spmi->sid, index ? qpnp_pon_reason[index - 1] :
- "Unknown", cold_boot ? "cold" : "warm");
+ if (index >= ARRAY_SIZE(qpnp_pon_reason) || index < 0)
+ dev_info(&pon->spmi->dev,
+ "PMIC@SID%d Power-on reason: Unknown and '%s' boot\n",
+ pon->spmi->sid, cold_boot ? "cold" : "warm");
+ else
+ dev_info(&pon->spmi->dev,
+ "PMIC@SID%d Power-on reason: %s and '%s' boot\n",
+ pon->spmi->sid, qpnp_pon_reason[index],
+ cold_boot ? "cold" : "warm");
+
+ /* POFF reason */
+ rc = spmi_ext_register_readl(pon->spmi->ctrl, pon->spmi->sid,
+ QPNP_POFF_REASON1(pon->base),
+ buf, 2);
+ if (rc) {
+ dev_err(&pon->spmi->dev, "Unable to read POFF_RESASON regs\n");
+ return rc;
+ }
+ poff_sts = buf[0] | (buf[1] << 8);
+ index = ffs(poff_sts) - 1;
+ if (index >= ARRAY_SIZE(qpnp_poff_reason) || index < 0)
+ dev_info(&pon->spmi->dev,
+ "PMIC@SID%d: Unknown power-off reason\n",
+ pon->spmi->sid);
+ else
+ dev_info(&pon->spmi->dev,
+ "PMIC@SID%d: Power-off reason: %s\n",
+ pon->spmi->sid,
+ qpnp_poff_reason[index]);
rc = of_property_read_u32(pon->spmi->dev.of_node,
"qcom,pon-dbc-delay", &delay);
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index e42337f..e744d04 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -396,6 +396,17 @@
help
Say Y here to enable support for batteries with BQ27520 (I2C) chips.
+config SMB358_CHARGER
+ tristate "SMB358 Battery Charger"
+ depends on I2C
+ help
+ Say Y to include support for SMB358 Battery Charger.
+ SMB358 is a single path switching mode charger capable of charging
+ the battery with 2Amps of current.
+ The driver supports charger enable/disable.
+ The driver reports the charger status via the power supply framework.
+ A charger status change triggers an IRQ via the device STAT pin.
+
config BQ27520_TEST_ENABLE
bool "Enable BQ27520 Fuel Gauge Chip Test"
depends on BATTERY_BQ27520
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index 990bd03..8f152aa 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -56,6 +56,7 @@
obj-$(CONFIG_BATTERY_BQ28400) += bq28400_battery.o
obj-$(CONFIG_SMB137B_CHARGER) += smb137b.o
obj-$(CONFIG_SMB137C_CHARGER) += smb137c-charger.o
+obj-$(CONFIG_SMB358_CHARGER) += smb358-charger.o
obj-$(CONFIG_PM8XXX_CCADC) += pm8xxx-ccadc.o
obj-$(CONFIG_PM8921_BMS) += pm8921-bms.o
obj-$(CONFIG_QPNP_BMS) += qpnp-bms.o
diff --git a/drivers/power/power_supply_core.c b/drivers/power/power_supply_core.c
index 97e952e..f10e1d4 100644
--- a/drivers/power/power_supply_core.c
+++ b/drivers/power/power_supply_core.c
@@ -26,6 +26,28 @@
static struct device_type power_supply_dev_type;
/**
+ * power_supply_set_voltage_limit - set current limit
+ * @psy: the power supply to control
+ * @limit: current limit in uV from the power supply.
+ * 0 will disable the power supply.
+ *
+ * This function will set a maximum supply current from a source
+ * and it will disable the charger when limit is 0.
+ */
+int power_supply_set_voltage_limit(struct power_supply *psy, int limit)
+{
+ const union power_supply_propval ret = {limit,};
+
+ if (psy->set_property)
+ return psy->set_property(psy, POWER_SUPPLY_PROP_VOLTAGE_MAX,
+ &ret);
+
+ return -ENXIO;
+}
+EXPORT_SYMBOL(power_supply_set_voltage_limit);
+
+
+/**
* power_supply_set_current_limit - set current limit
* @psy: the power supply to control
* @limit: current limit in uA from the power supply.
diff --git a/drivers/power/qpnp-bms.c b/drivers/power/qpnp-bms.c
index 82c61c9..e227ecd 100644
--- a/drivers/power/qpnp-bms.c
+++ b/drivers/power/qpnp-bms.c
@@ -281,6 +281,7 @@
u8 charge_increase;
int fcc_resolution;
bool battery_removed;
+ bool in_taper_charge;
struct bms_irq sw_cc_thr_irq;
struct bms_irq ocv_thr_irq;
struct qpnp_vadc_chip *vadc_dev;
@@ -491,7 +492,7 @@
}
static inline int convert_vbatt_raw_to_uv(struct qpnp_bms_chip *chip,
- uint16_t reading)
+ uint16_t reading, bool is_pon_ocv)
{
int64_t uv;
int rc;
@@ -500,7 +501,7 @@
pr_debug("%u raw converted into %lld uv\n", reading, uv);
uv = adjust_vbatt_reading(chip, uv);
pr_debug("adjusted into %lld uv\n", uv);
- rc = qpnp_vbat_sns_comp_result(chip->vadc_dev, &uv);
+ rc = qpnp_vbat_sns_comp_result(chip->vadc_dev, &uv, is_pon_ocv);
if (rc)
pr_debug("could not compensate vbatt\n");
pr_debug("compensated into %lld uv\n", uv);
@@ -699,7 +700,7 @@
static void convert_and_store_ocv(struct qpnp_bms_chip *chip,
struct raw_soc_params *raw,
- int batt_temp)
+ int batt_temp, bool is_pon_ocv)
{
int rc;
@@ -711,7 +712,7 @@
pr_err("Vadc reference voltage read failed, rc = %d\n", rc);
chip->prev_last_good_ocv_raw = raw->last_good_ocv_raw;
raw->last_good_ocv_uv = convert_vbatt_raw_to_uv(chip,
- raw->last_good_ocv_raw);
+ raw->last_good_ocv_raw, is_pon_ocv);
chip->last_ocv_uv = raw->last_good_ocv_uv;
chip->last_ocv_temp = batt_temp;
chip->software_cc_uah = 0;
@@ -751,13 +752,18 @@
static int get_battery_status(struct qpnp_bms_chip *chip)
{
union power_supply_propval ret = {0,};
+ int rc;
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,
+ rc = chip->batt_psy->get_property(chip->batt_psy,
POWER_SUPPLY_PROP_STATUS, &ret);
+ if (rc) {
+ pr_debug("Battery does not export status: %d\n", rc);
+ return POWER_SUPPLY_STATUS_UNKNOWN;
+ }
return ret.intval;
}
@@ -766,22 +772,33 @@
return POWER_SUPPLY_STATUS_UNKNOWN;
}
-static bool is_battery_charging(struct qpnp_bms_chip *chip)
+static int get_battery_charge_type(struct qpnp_bms_chip *chip)
{
union power_supply_propval ret = {0,};
+ int rc;
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_CHARGE_TYPE, &ret);
- return ret.intval != POWER_SUPPLY_CHARGE_TYPE_NONE;
+ /* if battery has been registered, use the type property */
+ rc = chip->batt_psy->get_property(chip->batt_psy,
+ POWER_SUPPLY_PROP_CHARGE_TYPE, &ret);
+ if (rc) {
+ pr_debug("Battery does not export charge type: %d\n"
+ , rc);
+ return POWER_SUPPLY_CHARGE_TYPE_NONE;
+ }
+ return ret.intval;
}
/* Default to false if the battery power supply is not registered. */
pr_debug("battery power supply is not registered\n");
- return false;
+ return POWER_SUPPLY_CHARGE_TYPE_NONE;
+}
+
+static bool is_battery_charging(struct qpnp_bms_chip *chip)
+{
+ return get_battery_status(chip) == POWER_SUPPLY_STATUS_CHARGING;
}
static bool is_battery_full(struct qpnp_bms_chip *chip)
@@ -809,8 +826,12 @@
chip->batt_psy = power_supply_get_by_name("battery");
if (chip->batt_psy) {
/* if battery has been registered, use the present property */
- chip->batt_psy->get_property(chip->batt_psy,
+ rc = chip->batt_psy->get_property(chip->batt_psy,
POWER_SUPPLY_PROP_PRESENT, &ret);
+ if (rc) {
+ pr_debug("battery does not export present: %d\n", rc);
+ return true;
+ }
return ret.intval;
}
@@ -851,13 +872,18 @@
static bool is_batfet_closed(struct qpnp_bms_chip *chip)
{
union power_supply_propval ret = {0,};
+ int rc;
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 online property */
- chip->batt_psy->get_property(chip->batt_psy,
+ rc = chip->batt_psy->get_property(chip->batt_psy,
POWER_SUPPLY_PROP_ONLINE, &ret);
+ if (rc) {
+ pr_debug("Battery does not export online: %d\n", rc);
+ return true;
+ }
return !!ret.intval;
}
@@ -907,13 +933,32 @@
return 0;
}
-static int estimate_ocv(struct qpnp_bms_chip *chip)
+static int get_rbatt(struct qpnp_bms_chip *chip,
+ int soc_rbatt_mohm, int batt_temp)
{
- int ibat_ua, vbat_uv, ocv_est_uv;
- int rc;
- int rbatt_mohm = chip->default_rbatt_mohm + chip->r_conn_mohm
- + chip->rbatt_capacitive_mohm;
+ int rbatt_mohm, scalefactor;
+ rbatt_mohm = chip->default_rbatt_mohm;
+ if (chip->rbatt_sf_lut == NULL) {
+ pr_debug("RBATT = %d\n", rbatt_mohm);
+ return rbatt_mohm;
+ }
+ /* Convert the batt_temp to DegC from deciDegC */
+ scalefactor = interpolate_scalingfactor(chip->rbatt_sf_lut,
+ batt_temp, soc_rbatt_mohm);
+ rbatt_mohm = (rbatt_mohm * scalefactor) / 100;
+
+ rbatt_mohm += chip->r_conn_mohm;
+ rbatt_mohm += chip->rbatt_capacitive_mohm;
+ return rbatt_mohm;
+}
+
+#define DEFAULT_RBATT_SOC 50
+static int estimate_ocv(struct qpnp_bms_chip *chip, int batt_temp)
+{
+ int ibat_ua, vbat_uv, ocv_est_uv, rbatt_mohm, rc;
+
+ rbatt_mohm = get_rbatt(chip, DEFAULT_RBATT_SOC, batt_temp);
rc = get_simultaneous_batt_v_and_i(chip, &ibat_ua, &vbat_uv);
if (rc) {
pr_err("simultaneous failed rc = %d\n", rc);
@@ -921,10 +966,12 @@
}
ocv_est_uv = vbat_uv + (ibat_ua * rbatt_mohm) / 1000;
- pr_debug("estimated pon ocv = %d\n", ocv_est_uv);
+ pr_debug("estimated pon ocv = %d, vbat_uv = %d ibat_ua = %d rbatt_mohm = %d\n",
+ ocv_est_uv, vbat_uv, ibat_ua, rbatt_mohm);
return ocv_est_uv;
}
+#define MIN_IAVG_MA 250
static void reset_for_new_battery(struct qpnp_bms_chip *chip, int batt_temp)
{
chip->last_ocv_uv = chip->insertion_ocv_uv;
@@ -935,7 +982,7 @@
chip->soc_at_cv = -EINVAL;
chip->shutdown_soc_invalid = true;
chip->shutdown_soc = 0;
- chip->shutdown_iavg_ma = 0;
+ chip->shutdown_iavg_ma = MIN_IAVG_MA;
chip->prev_pc_unusable = -EINVAL;
reset_cc(chip, CLEAR_CC | CLEAR_SHDW_CC);
chip->software_cc_uah = 0;
@@ -963,16 +1010,15 @@
static int find_ocv_for_pc(struct qpnp_bms_chip *chip, int batt_temp, int pc)
{
int new_pc;
- int batt_temp_degc = batt_temp / 10;
int ocv_mv;
int delta_mv = 5;
int max_spin_count;
int count = 0;
int sign, new_sign;
- ocv_mv = interpolate_ocv(chip->pc_temp_ocv_lut, batt_temp_degc, pc);
+ ocv_mv = interpolate_ocv(chip->pc_temp_ocv_lut, batt_temp, pc);
- new_pc = interpolate_pc(chip->pc_temp_ocv_lut, batt_temp_degc, ocv_mv);
+ new_pc = interpolate_pc(chip->pc_temp_ocv_lut, batt_temp, ocv_mv);
pr_debug("test revlookup pc = %d for ocv = %d\n", new_pc, ocv_mv);
max_spin_count = 1 + (chip->max_voltage_uv - chip->v_cutoff_uv)
/ UV_PER_SPIN;
@@ -1003,7 +1049,7 @@
ocv_mv = ocv_mv + delta_mv * sign;
new_pc = interpolate_pc(chip->pc_temp_ocv_lut,
- batt_temp_degc, ocv_mv);
+ batt_temp, ocv_mv);
pr_debug("test revlookup pc = %d for ocv = %d\n",
new_pc, ocv_mv);
count++;
@@ -1028,28 +1074,27 @@
chip->base + BMS1_OCV_FOR_SOC_DATA0, 2);
if (rc) {
pr_err("Error reading ocv: rc = %d\n", rc);
- return -ENXIO;
+ goto param_err;
}
rc = read_cc_raw(chip, &raw->cc, CC);
rc = read_cc_raw(chip, &raw->shdw_cc, SHDW_CC);
if (rc) {
pr_err("Failed to read raw cc data, rc = %d\n", rc);
- return rc;
+ goto param_err;
}
unlock_output_data(chip);
mutex_unlock(&chip->bms_output_lock);
if (chip->prev_last_good_ocv_raw == OCV_RAW_UNINITIALIZED) {
- convert_and_store_ocv(chip, raw, batt_temp);
+ convert_and_store_ocv(chip, raw, batt_temp, true);
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) {
+ 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);
+ chip->last_ocv_uv = estimate_ocv(chip, batt_temp);
raw->last_good_ocv_uv = chip->last_ocv_uv;
reset_cc(chip, CLEAR_CC | CLEAR_SHDW_CC);
pr_debug("New PON_OCV_UV = %d, cc = %llx\n",
@@ -1078,7 +1123,7 @@
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);
+ convert_and_store_ocv(chip, raw, batt_temp, false);
/* forget the old cc value upon ocv */
chip->last_cc_uah = INT_MIN;
} else {
@@ -1093,6 +1138,11 @@
raw->last_good_ocv_raw, raw->last_good_ocv_uv);
pr_debug("cc_raw= 0x%llx\n", raw->cc);
return 0;
+
+param_err:
+ unlock_output_data(chip);
+ mutex_unlock(&chip->bms_output_lock);
+ return rc;
}
static int calculate_pc(struct qpnp_bms_chip *chip, int ocv_uv,
@@ -1101,7 +1151,7 @@
int pc;
pc = interpolate_pc(chip->pc_temp_ocv_lut,
- batt_temp / 10, ocv_uv / 1000);
+ batt_temp, ocv_uv / 1000);
pr_debug("pc = %u %% for ocv = %d uv batt_temp = %d\n",
pc, ocv_uv, batt_temp);
/* Multiply the initial FCC value by the scale factor. */
@@ -1213,27 +1263,6 @@
}
}
-static int get_rbatt(struct qpnp_bms_chip *chip,
- int soc_rbatt_mohm, int batt_temp)
-{
- int rbatt_mohm, scalefactor;
-
- rbatt_mohm = chip->default_rbatt_mohm;
- if (chip->rbatt_sf_lut == NULL) {
- pr_debug("RBATT = %d\n", rbatt_mohm);
- return rbatt_mohm;
- }
- /* Convert the batt_temp to DegC from deciDegC */
- batt_temp = batt_temp / 10;
- scalefactor = interpolate_scalingfactor(chip->rbatt_sf_lut,
- batt_temp, soc_rbatt_mohm);
- rbatt_mohm = (rbatt_mohm * scalefactor) / 100;
-
- rbatt_mohm += chip->r_conn_mohm;
- rbatt_mohm += chip->rbatt_capacitive_mohm;
- return rbatt_mohm;
-}
-
#define IAVG_MINIMAL_TIME 2
static void calculate_iavg(struct qpnp_bms_chip *chip, int cc_uah,
int *iavg_ua, int delta_time_s)
@@ -1268,7 +1297,6 @@
int unusable_uv, pc_unusable, uuc_uah;
int i = 0;
int ocv_mv;
- int batt_temp_degc = batt_temp / 10;
int rbatt_mohm;
int delta_uv;
int prev_delta_uv = 0;
@@ -1277,7 +1305,7 @@
for (i = 0; i <= 100; i++) {
ocv_mv = interpolate_ocv(chip->pc_temp_ocv_lut,
- batt_temp_degc, i);
+ batt_temp, i);
rbatt_mohm = get_rbatt(chip, i, batt_temp);
unusable_uv = (rbatt_mohm * uuc_iavg_ma)
+ (chip->v_cutoff_uv);
@@ -1314,13 +1342,12 @@
int batt_temp)
{
int new_unusable_mv, new_iavg_ma;
- int batt_temp_degc = batt_temp / 10;
int max_percent_change;
max_percent_change = max(params->delta_time_s
/ TIME_PER_PERCENT_UUC, 1);
- if (chip->prev_pc_unusable == -EINVAL
+ if (chip->first_time_calc_uuc || chip->prev_pc_unusable == -EINVAL
|| abs(chip->prev_pc_unusable - new_pc_unusable)
<= max_percent_change) {
chip->prev_pc_unusable = new_pc_unusable;
@@ -1337,7 +1364,7 @@
/* also find update the iavg_ma accordingly */
new_unusable_mv = interpolate_ocv(chip->pc_temp_ocv_lut,
- batt_temp_degc, chip->prev_pc_unusable);
+ batt_temp, chip->prev_pc_unusable);
if (new_unusable_mv < chip->v_cutoff_uv/1000)
new_unusable_mv = chip->v_cutoff_uv/1000;
@@ -1353,7 +1380,6 @@
return new_uuc_uah;
}
-#define MIN_IAVG_MA 250
static int calculate_unusable_charge_uah(struct qpnp_bms_chip *chip,
struct soc_params *params,
int batt_temp)
@@ -1503,7 +1529,7 @@
mutex_lock(&chip->bms_output_lock);
lock_output_data(chip);
- read_cc_raw(chip, &cc_raw, false);
+ read_cc_raw(chip, &cc_raw, CC);
unlock_output_data(chip);
mutex_unlock(&chip->bms_output_lock);
@@ -1517,7 +1543,7 @@
mutex_lock(&chip->bms_output_lock);
lock_output_data(chip);
- read_cc_raw(chip, &cc_raw, true);
+ read_cc_raw(chip, &cc_raw, SHDW_CC);
unlock_output_data(chip);
mutex_unlock(&chip->bms_output_lock);
@@ -1809,7 +1835,7 @@
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 = chip->battery_status == POWER_SUPPLY_STATUS_CHARGING;
charging_since_last_report = charging || (chip->last_soc_unbound
&& chip->was_charging_at_sleep);
/*
@@ -1918,7 +1944,8 @@
batt_terminal_uv = vbat_uv + (ibat_ua * chip->r_conn_mohm) / 1000;
if (chip->soc_at_cv == -EINVAL) {
- if (batt_terminal_uv >= chip->max_voltage_uv - VDD_MAX_ERR) {
+ if (batt_terminal_uv >= chip->max_voltage_uv - VDD_MAX_ERR ||
+ chip->in_taper_charge) {
chip->soc_at_cv = soc;
chip->prev_chg_soc = soc;
chip->ibat_at_cv_ua = params->iavg_ua;
@@ -2205,6 +2232,11 @@
pr_err("adc vbat failed err = %d\n", rc);
return soc;
}
+
+ /* only clamp when discharging */
+ if (is_battery_charging(chip))
+ return soc;
+
if (soc <= 0 && vbat_uv > chip->v_cutoff_uv) {
pr_debug("clamping soc to 1, vbat (%d) > cutoff (%d)\n",
vbat_uv, chip->v_cutoff_uv);
@@ -3133,7 +3165,7 @@
if (start) {
chip->start_pc = interpolate_pc(chip->pc_temp_ocv_lut,
- batt_temp / 10, raw.last_good_ocv_uv / 1000);
+ batt_temp, raw.last_good_ocv_uv / 1000);
chip->start_cc_uah = calculate_cc(chip, raw.cc, CC, NORESET);
chip->start_real_soc = calculate_real_soc(chip,
batt_temp, &raw, chip->start_cc_uah);
@@ -3232,6 +3264,7 @@
mutex_lock(&chip->last_ocv_uv_mutex);
chip->soc_at_cv = -EINVAL;
chip->prev_chg_soc = -EINVAL;
+ chip->in_taper_charge = false;
/* update the chargecycles */
if (chip->end_soc > chip->start_soc) {
@@ -3363,6 +3396,11 @@
battery_insertion_check(chip);
batfet_status_check(chip);
battery_status_check(chip);
+
+ if (POWER_SUPPLY_CHARGE_TYPE_TAPER == get_battery_charge_type(chip))
+ chip->in_taper_charge = true;
+ else
+ chip->in_taper_charge = false;
}
static int qpnp_bms_power_get_property(struct power_supply *psy,
@@ -3527,6 +3565,7 @@
* Do a quick run of SoC calculation to find whether the shutdown soc
* is close enough.
*/
+ chip->shutdown_iavg_ma = MIN_IAVG_MA;
calculated_soc = recalculate_raw_soc(chip);
shutdown_soc_out_of_limit = (abs(shutdown_soc - calculated_soc)
> chip->shutdown_soc_valid_limit);
@@ -3900,8 +3939,10 @@
int rc;
SPMI_REQUEST_IRQ(chip, rc, sw_cc_thr);
+ disable_bms_irq(&chip->sw_cc_thr_irq);
enable_irq_wake(chip->sw_cc_thr_irq.irq);
SPMI_REQUEST_IRQ(chip, rc, ocv_thr);
+ disable_bms_irq(&chip->ocv_thr_irq);
enable_irq_wake(chip->ocv_thr_irq.irq);
return 0;
}
diff --git a/drivers/power/qpnp-charger.c b/drivers/power/qpnp-charger.c
index bf294fc..d5c753f 100644
--- a/drivers/power/qpnp-charger.c
+++ b/drivers/power/qpnp-charger.c
@@ -117,7 +117,7 @@
#define USB_SPARE 0xDF
#define DC_COMP_OVR1 0xE9
#define CHGR_COMP_OVR1 0xEE
-
+#define USB_CHGPTH_CTL 0x40
#define REG_OFFSET_PERP_SUBTYPE 0x05
/* SMBB peripheral subtype values */
@@ -164,6 +164,7 @@
#define OCP_THR_900_MA 0x02
#define OCP_THR_500_MA 0x01
#define OCP_THR_200_MA 0x00
+#define DC_HIGHER_PRIORITY BIT(7)
/* Interrupt definitions */
/* smbb_chg_interrupts */
@@ -650,24 +651,42 @@
return (batfet_closed_rt_sts & BAT_FET_ON_IRQ) ? 1 : 0;
}
-#define USB_VALID_BIT BIT(7)
static int
qpnp_chg_is_usb_chg_plugged_in(struct qpnp_chg_chip *chip)
{
- u8 usbin_valid_rt_sts;
+ u8 usb_chgpth_rt_sts;
int rc;
- rc = qpnp_chg_read(chip, &usbin_valid_rt_sts,
- chip->usb_chgpth_base + CHGR_STATUS , 1);
+ rc = qpnp_chg_read(chip, &usb_chgpth_rt_sts,
+ INT_RT_STS(chip->usb_chgpth_base), 1);
if (rc) {
pr_err("spmi read failed: addr=%03X, rc=%d\n",
- chip->usb_chgpth_base + CHGR_STATUS, rc);
+ INT_RT_STS(chip->usb_chgpth_base), rc);
return rc;
}
- pr_debug("chgr usb sts 0x%x\n", usbin_valid_rt_sts);
+ pr_debug("chgr usb sts 0x%x\n", usb_chgpth_rt_sts);
- return (usbin_valid_rt_sts & USB_VALID_BIT) ? 1 : 0;
+ return (usb_chgpth_rt_sts & USBIN_VALID_IRQ) ? 1 : 0;
+}
+
+static bool
+qpnp_is_dc_higher_prio(struct qpnp_chg_chip *chip)
+{
+ int rc;
+ u8 usb_ctl;
+
+ if (!chip->type == SMBB)
+ return false;
+
+ rc = qpnp_chg_read(chip, &usb_ctl,
+ chip->usb_chgpth_base + USB_CHGPTH_CTL, 1);
+ if (rc) {
+ pr_err("failed to read usb ctl rc=%d\n", rc);
+ return 0;
+ }
+
+ return !!(usb_ctl & DC_HIGHER_PRIORITY);
}
static bool
@@ -686,10 +705,10 @@
return !!(buck_sts & IBAT_LOOP_IRQ);
}
-#define USB_VALID_MASK 0xC0
-#define USB_COARSE_DET 0x10
-#define USB_VALID_UVP_VALUE 0x00
-#define USB_VALID_OVP_VALUE 0x40
+#define USB_VALID_MASK 0xC0
+#define USB_VALID_IN_MASK BIT(7)
+#define USB_COARSE_DET 0x10
+#define USB_VALID_OVP_VALUE 0x40
static int
qpnp_chg_check_usb_coarse_det(struct qpnp_chg_chip *chip)
{
@@ -708,7 +727,8 @@
static int
qpnp_chg_check_usbin_health(struct qpnp_chg_chip *chip)
{
- u8 usbin_chg_rt_sts, usbin_health = 0;
+ u8 usbin_chg_rt_sts, usb_chgpth_rt_sts;
+ u8 usbin_health = 0;
int rc;
rc = qpnp_chg_read(chip, &usbin_chg_rt_sts,
@@ -720,13 +740,23 @@
return rc;
}
- pr_debug("chgr usb sts 0x%x\n", usbin_chg_rt_sts);
+ rc = qpnp_chg_read(chip, &usb_chgpth_rt_sts,
+ INT_RT_STS(chip->usb_chgpth_base) , 1);
+
+ if (rc) {
+ pr_err("spmi read failed: addr=%03X, rc=%d\n",
+ INT_RT_STS(chip->usb_chgpth_base), rc);
+ return rc;
+ }
+
+ pr_debug("chgr usb sts 0x%x, chgpth rt sts 0x%x\n",
+ usbin_chg_rt_sts, usb_chgpth_rt_sts);
if ((usbin_chg_rt_sts & USB_COARSE_DET) == USB_COARSE_DET) {
if ((usbin_chg_rt_sts & USB_VALID_MASK)
== USB_VALID_OVP_VALUE) {
usbin_health = USBIN_OVP;
pr_err("Over voltage charger inserted\n");
- } else if ((usbin_chg_rt_sts & USB_VALID_BIT) != 0) {
+ } else if ((usb_chgpth_rt_sts & USBIN_VALID_IRQ) != 0) {
usbin_health = USBIN_OK;
pr_debug("Valid charger inserted\n");
}
@@ -1568,7 +1598,10 @@
return rc;
}
+#define USB_WALL_THRESHOLD_MA 500
#define ENUM_T_STOP_BIT BIT(0)
+#define USB_5V_UV 5000000
+#define USB_9V_UV 9000000
static irqreturn_t
qpnp_chg_usb_usbin_valid_irq_handler(int irq, void *_chip)
{
@@ -1613,6 +1646,10 @@
qpnp_chg_set_appropriate_vddmax(chip);
chip->chg_done = false;
}
+
+ if (!qpnp_is_dc_higher_prio(chip))
+ qpnp_chg_idcmax_set(chip, chip->maxinput_dc_ma);
+
qpnp_chg_usb_suspend_enable(chip, 0);
qpnp_chg_iusbmax_set(chip, QPNP_CHG_I_MAX_MIN_100);
chip->prev_usb_max_ma = -EINVAL;
@@ -1790,6 +1827,23 @@
msecs_to_jiffies(EOC_CHECK_PERIOD_MS));
schedule_work(&chip->soc_check_work);
}
+
+ if (qpnp_is_dc_higher_prio(chip)) {
+ pr_debug("dc has higher priority\n");
+ if (dc_present) {
+ qpnp_chg_iusbmax_set(chip,
+ QPNP_CHG_I_MAX_MIN_100);
+ power_supply_set_voltage_limit(chip->usb_psy,
+ USB_5V_UV);
+ } else {
+ chip->aicl_settled = false;
+ qpnp_chg_iusbmax_set(chip,
+ USB_WALL_THRESHOLD_MA);
+ power_supply_set_voltage_limit(chip->usb_psy,
+ USB_9V_UV);
+ }
+ }
+
pr_debug("psy changed dc_psy\n");
power_supply_changed(&chip->dc_psy);
pr_debug("psy changed batt_psy\n");
@@ -2157,8 +2211,7 @@
static int ext_ovp_present;
module_param(ext_ovp_present, int, 0444);
-#define USB_WALL_THRESHOLD_MA 500
-#define OVP_USB_WALL_THRESHOLD_MA 200
+#define OVP_USB_WALL_TRSH_MA 200
static int
qpnp_power_get_property_mains(struct power_supply *psy,
enum power_supply_property psp,
@@ -2530,15 +2583,23 @@
qpnp_chg_iusbmax_set(chip, QPNP_CHG_I_MAX_MIN_100);
} else {
qpnp_chg_usb_suspend_enable(chip, 0);
- if (((ret.intval / 1000) > USB_WALL_THRESHOLD_MA)
+ if (qpnp_is_dc_higher_prio(chip)
+ && qpnp_chg_is_dc_chg_plugged_in(chip)) {
+ pr_debug("dc has higher priority\n");
+ qpnp_chg_iusbmax_set(chip,
+ QPNP_CHG_I_MAX_MIN_100);
+ } else if (((ret.intval / 1000) > USB_WALL_THRESHOLD_MA)
&& (charger_monitor ||
!chip->charger_monitor_checked)) {
- if (!ext_ovp_present)
- qpnp_chg_iusbmax_set(chip,
- USB_WALL_THRESHOLD_MA);
- else
- qpnp_chg_iusbmax_set(chip,
- OVP_USB_WALL_THRESHOLD_MA);
+ if (!qpnp_is_dc_higher_prio(chip))
+ qpnp_chg_idcmax_set(chip,
+ QPNP_CHG_I_MAX_MIN_100);
+ if (!ext_ovp_present)
+ qpnp_chg_iusbmax_set(chip,
+ USB_WALL_THRESHOLD_MA);
+ else
+ qpnp_chg_iusbmax_set(chip,
+ OVP_USB_WALL_TRSH_MA);
} else {
qpnp_chg_iusbmax_set(chip, ret.intval / 1000);
}
@@ -3983,14 +4044,19 @@
qpnp_batt_system_temp_level_set(chip, val->intval);
break;
case POWER_SUPPLY_PROP_INPUT_CURRENT_MAX:
- if (qpnp_chg_is_usb_chg_plugged_in(chip))
+ if (qpnp_chg_is_usb_chg_plugged_in(chip) &&
+ !(qpnp_is_dc_higher_prio(chip)
+ && qpnp_chg_is_dc_chg_plugged_in(chip)))
qpnp_chg_iusbmax_set(chip, val->intval / 1000);
break;
case POWER_SUPPLY_PROP_INPUT_CURRENT_TRIM:
qpnp_chg_iusb_trim_set(chip, val->intval);
break;
case POWER_SUPPLY_PROP_INPUT_CURRENT_SETTLED:
- qpnp_chg_input_current_settled(chip);
+ if (val->intval)
+ qpnp_chg_input_current_settled(chip);
+ else
+ chip->aicl_settled = false;
break;
case POWER_SUPPLY_PROP_VOLTAGE_MIN:
qpnp_chg_vinmin_set(chip, val->intval / 1000);
diff --git a/drivers/power/smb358-charger.c b/drivers/power/smb358-charger.c
new file mode 100644
index 0000000..6d241a0
--- /dev/null
+++ b/drivers/power/smb358-charger.c
@@ -0,0 +1,2491 @@
+/* Copyright (c) 2014 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "SMB358 %s: " fmt, __func__
+#include <linux/i2c.h>
+#include <linux/debugfs.h>
+#include <linux/gpio.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/power_supply.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/consumer.h>
+#include <linux/regulator/driver.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/mutex.h>
+#include <linux/qpnp/qpnp-adc.h>
+
+/* Config/Control registers */
+#define CHG_CURRENT_CTRL_REG 0x0
+#define CHG_OTH_CURRENT_CTRL_REG 0x1
+#define VARIOUS_FUNC_REG 0x2
+#define VFLOAT_REG 0x3
+#define CHG_CTRL_REG 0x4
+#define STAT_AND_TIMER_CTRL_REG 0x5
+#define CHG_PIN_EN_CTRL_REG 0x6
+#define THERM_A_CTRL_REG 0x7
+#define SYSOK_AND_USB3_REG 0x8
+#define FAULT_INT_REG 0xC
+#define STATUS_INT_REG 0xD
+
+/* Command registers */
+#define CMD_A_REG 0x30
+#define CMD_B_REG 0x31
+
+/* IRQ status registers */
+#define IRQ_A_REG 0x35
+#define IRQ_B_REG 0x36
+#define IRQ_C_REG 0x37
+#define IRQ_D_REG 0x38
+#define IRQ_E_REG 0x39
+#define IRQ_F_REG 0x3A
+
+/* Status registers */
+#define STATUS_C_REG 0x3D
+#define STATUS_D_REG 0x3E
+#define STATUS_E_REG 0x3F
+
+/* Config bits */
+#define CHG_INHI_EN_MASK BIT(1)
+#define CHG_INHI_EN_BIT BIT(1)
+#define CMD_A_CHG_ENABLE_BIT BIT(1)
+#define CMD_A_VOLATILE_W_PERM_BIT BIT(7)
+#define CMD_A_CHG_SUSP_EN_BIT BIT(2)
+#define CMD_A_CHG_SUSP_EN_MASK BIT(2)
+#define CMD_A_OTG_ENABLE_BIT BIT(4)
+#define CMD_A_OTG_ENABLE_MASK BIT(4)
+#define CMD_B_CHG_HC_ENABLE_BIT BIT(0)
+#define USB3_ENABLE_BIT BIT(5)
+#define USB3_ENABLE_MASK BIT(5)
+#define CMD_B_CHG_USB_500_900_ENABLE_BIT BIT(1)
+#define CHG_CTRL_AUTO_RECHARGE_ENABLE_BIT 0x0
+#define CHG_CTRL_CURR_TERM_END_CHG_BIT 0x0
+#define CHG_CTRL_BATT_MISSING_DET_THERM_IO (BIT(5) | BIT(4))
+#define CHG_CTRL_AUTO_RECHARGE_MASK BIT(7)
+#define CHG_CTRL_CURR_TERM_END_MASK BIT(6)
+#define CHG_CTRL_BATT_MISSING_DET_MASK (BIT(5) | BIT(4))
+#define CHG_CTRL_APSD_EN_BIT BIT(2)
+#define CHG_CTRL_APSD_EN_MASK BIT(2)
+#define CHG_ITERM_MASK 0x07
+#define CHG_PIN_CTRL_USBCS_REG_BIT 0x0
+/* This is to select if use external pin EN to control CHG */
+#define CHG_PIN_CTRL_CHG_EN_LOW_PIN_BIT (BIT(5) | BIT(6))
+#define CHG_PIN_CTRL_CHG_EN_LOW_REG_BIT 0x0
+#define CHG_PIN_CTRL_CHG_EN_MASK (BIT(5) | BIT(6))
+
+#define CHG_PIN_CTRL_USBCS_REG_MASK BIT(4)
+#define CHG_PIN_CTRL_APSD_IRQ_BIT BIT(1)
+#define CHG_PIN_CTRL_APSD_IRQ_MASK BIT(1)
+#define CHG_PIN_CTRL_CHG_ERR_IRQ_BIT BIT(2)
+#define CHG_PIN_CTRL_CHG_ERR_IRQ_MASK BIT(2)
+#define VARIOUS_FUNC_USB_SUSP_EN_REG_BIT BIT(6)
+#define VARIOUS_FUNC_USB_SUSP_MASK BIT(6)
+#define FAULT_INT_HOT_COLD_HARD_BIT BIT(7)
+#define FAULT_INT_HOT_COLD_SOFT_BIT BIT(6)
+#define FAULT_INT_INPUT_OV_BIT BIT(3)
+#define FAULT_INT_INPUT_UV_BIT BIT(2)
+#define FAULT_INT_AICL_COMPLETE_BIT BIT(1)
+#define STATUS_INT_CHG_TIMEOUT_BIT BIT(7)
+#define STATUS_INT_OTG_DETECT_BIT BIT(6)
+#define STATUS_INT_BATT_OV_BIT BIT(5)
+#define STATUS_INT_CHGING_BIT BIT(4)
+#define STATUS_INT_CHG_INHI_BIT BIT(3)
+#define STATUS_INT_INOK_BIT BIT(2)
+#define STATUS_INT_MISSING_BATT_BIT BIT(1)
+#define STATUS_INT_LOW_BATT_BIT BIT(0)
+#define THERM_A_THERM_MONITOR_EN_BIT 0x0
+#define THERM_A_THERM_MONITOR_EN_MASK BIT(4)
+#define VFLOAT_MASK 0x3F
+
+/* IRQ status bits */
+#define IRQ_A_HOT_HARD_BIT BIT(6)
+#define IRQ_A_COLD_HARD_BIT BIT(4)
+#define IRQ_A_HOT_SOFT_BIT BIT(2)
+#define IRQ_A_COLD_SOFT_BIT BIT(0)
+#define IRQ_B_BATT_MISSING_BIT BIT(4)
+#define IRQ_B_BATT_LOW_BIT BIT(2)
+#define IRQ_B_BATT_OV_BIT BIT(6)
+#define IRQ_B_PRE_FAST_CHG_BIT BIT(0)
+#define IRQ_C_TAPER_CHG_BIT BIT(2)
+#define IRQ_C_TERM_BIT BIT(0)
+#define IRQ_C_INT_OVER_TEMP_BIT BIT(6)
+#define IRQ_D_CHG_TIMEOUT_BIT (BIT(0) | BIT(2))
+#define IRQ_D_AICL_DONE_BIT BIT(4)
+#define IRQ_D_APSD_COMPLETE BIT(6)
+#define IRQ_E_INPUT_UV_BIT BIT(0)
+#define IRQ_E_INPUT_OV_BIT BIT(2)
+#define IRQ_E_AFVC_ACTIVE BIT(4)
+#define IRQ_F_OTG_VALID_BIT BIT(2)
+#define IRQ_F_OTG_BATT_FAIL_BIT BIT(4)
+#define IRQ_F_OTG_OC_BIT BIT(6)
+#define IRQ_F_POWER_OK BIT(0)
+
+/* Status bits */
+#define STATUS_C_CHARGING_MASK (BIT(1) | BIT(2))
+#define STATUS_C_FAST_CHARGING BIT(2)
+#define STATUS_C_PRE_CHARGING BIT(1)
+#define STATUS_C_TAPER_CHARGING (BIT(2) | BIT(1))
+#define STATUS_C_CHG_ERR_STATUS_BIT BIT(6)
+#define STATUS_C_CHG_ENABLE_STATUS_BIT BIT(0)
+#define STATUS_C_CHG_HOLD_OFF_BIT BIT(3)
+#define STATUS_D_PORT_OTHER BIT(0)
+#define STATUS_D_PORT_SDP BIT(1)
+#define STATUS_D_PORT_DCP BIT(2)
+#define STATUS_D_PORT_CDP BIT(3)
+#define STATUS_D_PORT_ACA_A BIT(4)
+#define STATUS_D_PORT_ACA_B BIT(5)
+#define STATUS_D_PORT_ACA_C BIT(6)
+#define STATUS_D_PORT_ACA_DOCK BIT(7)
+
+/* constants */
+#define USB2_MIN_CURRENT_MA 100
+#define USB2_MAX_CURRENT_MA 500
+#define USB3_MIN_CURRENT_MA 150
+#define USB3_MAX_CURRENT_MA 900
+#define AC_CHG_CURRENT_MASK 0x70
+#define AC_CHG_CURRENT_SHIFT 4
+#define SMB358_IRQ_REG_COUNT 6
+#define SMB358_FAST_CHG_MIN_MA 200
+#define SMB358_FAST_CHG_MAX_MA 2000
+#define SMB358_FAST_CHG_SHIFT 5
+#define SMB_FAST_CHG_CURRENT_MASK 0xE0
+#define SMB358_DEFAULT_BATT_CAPACITY 50
+
+enum {
+ USER = BIT(0),
+ THERMAL = BIT(1),
+ CURRENT = BIT(2),
+};
+
+struct smb358_regulator {
+ struct regulator_desc rdesc;
+ struct regulator_dev *rdev;
+};
+
+struct smb358_charger {
+ struct i2c_client *client;
+ struct device *dev;
+
+ bool recharge_disabled;
+ int recharge_mv;
+ bool iterm_disabled;
+ int iterm_ma;
+ int vfloat_mv;
+ int chg_valid_gpio;
+ int chg_valid_act_low;
+ int chg_present;
+ int fake_battery_soc;
+ bool chg_autonomous_mode;
+ bool disable_apsd;
+ bool using_pmic_therm;
+ bool battery_missing;
+ const char *bms_psy_name;
+ bool resume_completed;
+ bool irq_waiting;
+ struct mutex read_write_lock;
+ struct mutex path_suspend_lock;
+ struct mutex irq_complete;
+ u8 irq_cfg_mask[2];
+ int irq_gpio;
+ int charging_disabled;
+ int fastchg_current_max_ma;
+
+ /* debugfs related */
+#if defined(CONFIG_DEBUG_FS)
+ struct dentry *debug_root;
+ u32 peek_poke_address;
+#endif
+ /* status tracking */
+ bool batt_full;
+ bool batt_hot;
+ bool batt_cold;
+ bool batt_warm;
+ bool batt_cool;
+ int charging_disabled_status;
+ int usb_suspended;
+
+ /* power supply */
+ struct power_supply *usb_psy;
+ struct power_supply *bms_psy;
+ struct power_supply batt_psy;
+
+ /* otg 5V regulator */
+ struct smb358_regulator otg_vreg;
+
+ /* adc_tm paramters */
+ struct qpnp_vadc_chip *vadc_dev;
+ struct qpnp_adc_tm_chip *adc_tm_dev;
+ struct qpnp_adc_tm_btm_param adc_param;
+ int cold_bat_decidegc;
+ int hot_bat_decidegc;
+ int bat_present_decidegc;
+ /* i2c pull up regulator */
+ struct regulator *vcc_i2c;
+};
+
+struct smb_irq_info {
+ const char *name;
+ int (*smb_irq)(struct smb358_charger *chip,
+ u8 rt_stat);
+ int high;
+ int low;
+};
+
+struct irq_handler_info {
+ u8 stat_reg;
+ u8 val;
+ u8 prev_val;
+ struct smb_irq_info irq_info[4];
+};
+
+static int chg_current[] = {
+ 300, 500, 700, 1000, 1200, 1500, 1800, 2000,
+};
+
+static int fast_chg_current[] = {
+ 200, 450, 600, 900, 1300, 1500, 1800, 2000,
+};
+
+/* add supplied to "bms" function */
+static char *pm_batt_supplied_to[] = {
+ "bms",
+};
+
+static int __smb358_read_reg(struct smb358_charger *chip, u8 reg, u8 *val)
+{
+ s32 ret;
+
+ ret = i2c_smbus_read_byte_data(chip->client, reg);
+ if (ret < 0) {
+ dev_err(chip->dev,
+ "i2c read fail: can't read from %02x: %d\n", reg, ret);
+ return ret;
+ } else {
+ *val = ret;
+ }
+
+ return 0;
+}
+
+static int __smb358_write_reg(struct smb358_charger *chip, int reg, u8 val)
+{
+ s32 ret;
+
+ ret = i2c_smbus_write_byte_data(chip->client, reg, val);
+ if (ret < 0) {
+ dev_err(chip->dev,
+ "i2c write fail: can't write %02x to %02x: %d\n",
+ val, reg, ret);
+ return ret;
+ }
+ return 0;
+}
+
+static int smb358_read_reg(struct smb358_charger *chip, int reg,
+ u8 *val)
+{
+ int rc;
+
+ mutex_lock(&chip->read_write_lock);
+ rc = __smb358_read_reg(chip, reg, val);
+ mutex_unlock(&chip->read_write_lock);
+
+ return rc;
+}
+
+static int smb358_write_reg(struct smb358_charger *chip, int reg,
+ u8 val)
+{
+ int rc;
+
+ mutex_lock(&chip->read_write_lock);
+ rc = __smb358_write_reg(chip, reg, val);
+ mutex_unlock(&chip->read_write_lock);
+
+ return rc;
+}
+
+static int smb358_masked_write(struct smb358_charger *chip, int reg,
+ u8 mask, u8 val)
+{
+ s32 rc;
+ u8 temp;
+
+ mutex_lock(&chip->read_write_lock);
+ rc = __smb358_read_reg(chip, reg, &temp);
+ if (rc) {
+ dev_err(chip->dev,
+ "smb358_read_reg Failed: reg=%03X, rc=%d\n", reg, rc);
+ goto out;
+ }
+ temp &= ~mask;
+ temp |= val & mask;
+ rc = __smb358_write_reg(chip, reg, temp);
+ if (rc) {
+ dev_err(chip->dev,
+ "smb358_write Failed: reg=%03X, rc=%d\n", reg, rc);
+ }
+out:
+ mutex_unlock(&chip->read_write_lock);
+ return rc;
+}
+
+static int smb358_enable_volatile_writes(struct smb358_charger *chip)
+{
+ int rc;
+
+ rc = smb358_masked_write(chip, CMD_A_REG, CMD_A_VOLATILE_W_PERM_BIT,
+ CMD_A_VOLATILE_W_PERM_BIT);
+ if (rc)
+ dev_err(chip->dev, "Couldn't write VOLATILE_W_PERM_BIT rc=%d\n",
+ rc);
+
+ return rc;
+}
+
+static int smb358_fastchg_current_set(struct smb358_charger *chip)
+{
+ int i;
+
+ if ((chip->fastchg_current_max_ma < SMB358_FAST_CHG_MIN_MA) ||
+ (chip->fastchg_current_max_ma > SMB358_FAST_CHG_MAX_MA)) {
+ dev_dbg(chip->dev, "bad fastchg current mA=%d asked to set\n",
+ chip->fastchg_current_max_ma);
+ return -EINVAL;
+ }
+
+ for (i = ARRAY_SIZE(fast_chg_current) - 1; i >= 0; i--) {
+ if (fast_chg_current[i] <= chip->fastchg_current_max_ma)
+ break;
+ }
+
+ if (i < 0) {
+ dev_err(chip->dev, "Invalid current setting %dmA\n",
+ chip->fastchg_current_max_ma);
+ i = 0;
+ }
+
+ i = i << SMB358_FAST_CHG_SHIFT;
+ dev_dbg(chip->dev, "fastchg limit=%d setting %02x\n",
+ chip->fastchg_current_max_ma, i);
+
+ return smb358_masked_write(chip, CHG_CURRENT_CTRL_REG,
+ SMB_FAST_CHG_CURRENT_MASK, i);
+}
+
+#define MIN_FLOAT_MV 3500
+#define MAX_FLOAT_MV 4500
+#define VFLOAT_STEP_MV 20
+#define VFLOAT_4350MV 4350
+static int smb358_float_voltage_set(struct smb358_charger *chip, int vfloat_mv)
+{
+ u8 temp;
+
+ if ((vfloat_mv < MIN_FLOAT_MV) || (vfloat_mv > MAX_FLOAT_MV)) {
+ dev_err(chip->dev, "bad float voltage mv =%d asked to set\n",
+ vfloat_mv);
+ return -EINVAL;
+ }
+
+ if (VFLOAT_4350MV == vfloat_mv)
+ temp = 0x2B;
+ else if (vfloat_mv > VFLOAT_4350MV)
+ temp = (vfloat_mv - MIN_FLOAT_MV) / VFLOAT_STEP_MV - 1;
+ else
+ temp = (vfloat_mv - MIN_FLOAT_MV) / VFLOAT_STEP_MV;
+
+ return smb358_masked_write(chip, VFLOAT_REG, VFLOAT_MASK, temp);
+}
+
+#define CHG_ITERM_30MA 0x00
+#define CHG_ITERM_40MA 0x01
+#define CHG_ITERM_60MA 0x02
+#define CHG_ITERM_80MA 0x03
+#define CHG_ITERM_100MA 0x04
+#define CHG_ITERM_125MA 0x05
+#define CHG_ITERM_150MA 0x06
+#define CHG_ITERM_200MA 0x07
+static int smb358_term_current_set(struct smb358_charger *chip)
+{
+ u8 reg = 0;
+ int rc;
+
+ if (chip->iterm_ma != -EINVAL) {
+ if (chip->iterm_disabled)
+ dev_err(chip->dev, "Error: Both iterm_disabled and iterm_ma set\n");
+
+ if (chip->iterm_ma <= 30)
+ reg = CHG_ITERM_30MA;
+ else if (chip->iterm_ma <= 40)
+ reg = CHG_ITERM_40MA;
+ else if (chip->iterm_ma <= 60)
+ reg = CHG_ITERM_60MA;
+ else if (chip->iterm_ma <= 80)
+ reg = CHG_ITERM_80MA;
+ else if (chip->iterm_ma <= 100)
+ reg = CHG_ITERM_100MA;
+ else if (chip->iterm_ma <= 125)
+ reg = CHG_ITERM_125MA;
+ else if (chip->iterm_ma <= 150)
+ reg = CHG_ITERM_150MA;
+ else
+ reg = CHG_ITERM_200MA;
+
+ rc = smb358_masked_write(chip, CHG_CURRENT_CTRL_REG,
+ CHG_ITERM_MASK, reg);
+ if (rc) {
+ dev_err(chip->dev,
+ "Couldn't set iterm rc = %d\n", rc);
+ return rc;
+ }
+ }
+
+ if (chip->iterm_disabled) {
+ rc = smb358_masked_write(chip, CHG_CTRL_REG,
+ CHG_CTRL_CURR_TERM_END_MASK,
+ CHG_CTRL_CURR_TERM_END_MASK);
+ if (rc) {
+ dev_err(chip->dev, "Couldn't set iterm rc = %d\n",
+ rc);
+ return rc;
+ }
+ } else {
+ rc = smb358_masked_write(chip, CHG_CTRL_REG,
+ CHG_CTRL_CURR_TERM_END_MASK, 0);
+ if (rc) {
+ dev_err(chip->dev,
+ "Couldn't enable iterm rc = %d\n", rc);
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
+#define VFLT_300MV 0x0C
+#define VFLT_200MV 0x08
+#define VFLT_100MV 0x04
+#define VFLT_50MV 0x00
+#define VFLT_MASK 0x0C
+static int smb358_recharge_set(struct smb358_charger *chip)
+{
+ u8 reg = 0;
+ int rc;
+
+ if (chip->recharge_disabled)
+ rc = smb358_masked_write(chip, CHG_OTH_CURRENT_CTRL_REG,
+ CHG_INHI_EN_MASK, 0x0);
+ else
+ rc = smb358_masked_write(chip, CHG_OTH_CURRENT_CTRL_REG,
+ CHG_INHI_EN_MASK, CHG_INHI_EN_BIT);
+ if (rc) {
+ dev_err(chip->dev,
+ "Couldn't set inhibit en reg rc = %d\n", rc);
+ return rc;
+ }
+
+ if (chip->recharge_mv != -EINVAL) {
+ if (chip->recharge_mv <= 50)
+ reg = VFLT_50MV;
+ else if (chip->recharge_mv <= 100)
+ reg = VFLT_100MV;
+ else if (chip->recharge_mv <= 200)
+ reg = VFLT_200MV;
+ else
+ reg = VFLT_300MV;
+
+ rc = smb358_masked_write(chip, CHG_OTH_CURRENT_CTRL_REG,
+ VFLT_MASK, reg);
+ if (rc) {
+ dev_err(chip->dev,
+ "Couldn't set inhibit threshold rc = %d\n", rc);
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
+static int smb358_chg_otg_regulator_enable(struct regulator_dev *rdev)
+{
+ int rc = 0;
+ struct smb358_charger *chip = rdev_get_drvdata(rdev);
+
+ rc = smb358_masked_write(chip, CMD_A_REG, CMD_A_OTG_ENABLE_BIT,
+ CMD_A_OTG_ENABLE_BIT);
+ if (rc)
+ dev_err(chip->dev, "Couldn't enable OTG mode rc=%d, reg=%2x\n",
+ rc, CMD_A_REG);
+ return rc;
+}
+
+static int smb358_chg_otg_regulator_disable(struct regulator_dev *rdev)
+{
+ int rc = 0;
+ struct smb358_charger *chip = rdev_get_drvdata(rdev);
+
+ rc = smb358_masked_write(chip, CMD_A_REG, CMD_A_OTG_ENABLE_BIT, 0);
+ if (rc)
+ dev_err(chip->dev, "Couldn't disable OTG mode rc=%d, reg=%2x\n",
+ rc, CMD_A_REG);
+ return rc;
+}
+
+static int smb358_chg_otg_regulator_is_enable(struct regulator_dev *rdev)
+{
+ int rc = 0;
+ u8 reg = 0;
+ struct smb358_charger *chip = rdev_get_drvdata(rdev);
+
+ rc = smb358_read_reg(chip, CMD_A_REG, ®);
+ if (rc) {
+ dev_err(chip->dev,
+ "Couldn't read OTG enable bit rc=%d, reg=%2x\n",
+ rc, CMD_A_REG);
+ return rc;
+ }
+
+ return (reg & CMD_A_OTG_ENABLE_BIT) ? 1 : 0;
+}
+
+struct regulator_ops smb358_chg_otg_reg_ops = {
+ .enable = smb358_chg_otg_regulator_enable,
+ .disable = smb358_chg_otg_regulator_disable,
+ .is_enabled = smb358_chg_otg_regulator_is_enable,
+};
+
+static int smb358_regulator_init(struct smb358_charger *chip)
+{
+ int rc = 0;
+ struct regulator_init_data *init_data;
+ struct regulator_config cfg;
+
+ init_data = of_get_regulator_init_data(chip->dev, chip->dev->of_node);
+ if (!init_data) {
+ dev_err(chip->dev, "Get regulator init data failed\n");
+ return -EINVAL;
+ }
+
+ /* Give the name, then will register */
+ if (init_data->constraints.name) {
+ chip->otg_vreg.rdesc.owner = THIS_MODULE;
+ chip->otg_vreg.rdesc.type = REGULATOR_VOLTAGE;
+ chip->otg_vreg.rdesc.ops = &smb358_chg_otg_reg_ops;
+ chip->otg_vreg.rdesc.name = init_data->constraints.name;
+
+ cfg.dev = chip->dev;
+ cfg.init_data = init_data;
+ cfg.driver_data = chip;
+ cfg.of_node = chip->dev->of_node;
+
+ init_data->constraints.valid_ops_mask
+ |= REGULATOR_CHANGE_STATUS;
+
+ chip->otg_vreg.rdev = regulator_register(
+ &chip->otg_vreg.rdesc, &cfg);
+ if (IS_ERR(chip->otg_vreg.rdev)) {
+ rc = PTR_ERR(chip->otg_vreg.rdev);
+ chip->otg_vreg.rdev = NULL;
+ if (rc != -EPROBE_DEFER)
+ dev_err(chip->dev,
+ "OTG reg failed, rc=%d\n", rc);
+ }
+ }
+ return rc;
+}
+
+static int __smb358_charging_disable(struct smb358_charger *chip, bool disable)
+{
+ int rc;
+
+ rc = smb358_masked_write(chip, CMD_A_REG, CMD_A_CHG_ENABLE_BIT,
+ disable ? 0 : CMD_A_CHG_ENABLE_BIT);
+ if (rc < 0)
+ pr_err("Couldn't set CHG_ENABLE_BIT diable = %d, rc = %d\n",
+ disable, rc);
+ return rc;
+}
+
+static int smb358_charging_disable(struct smb358_charger *chip,
+ int reason, int disable)
+{
+ int rc = 0;
+ int disabled;
+
+ disabled = chip->charging_disabled_status;
+
+ pr_debug("reason = %d requested_disable = %d disabled_status = %d\n",
+ reason, disable, disabled);
+
+ if (disable == true)
+ disabled |= reason;
+ else
+ disabled &= ~reason;
+
+ if (!!disabled == !!chip->charging_disabled_status)
+ goto skip;
+
+ rc = __smb358_charging_disable(chip, !!disabled);
+ if (rc) {
+ pr_err("Failed to disable charging rc = %d\n", rc);
+ return rc;
+ } else {
+ /* will not modify online status in this condition */
+ power_supply_changed(&chip->batt_psy);
+ }
+
+skip:
+ chip->charging_disabled_status = disabled;
+ return rc;
+}
+
+static int smb358_hw_init(struct smb358_charger *chip)
+{
+ int rc;
+ u8 reg = 0, mask = 0;
+
+ /*
+ * If the charger is pre-configured for autonomous operation,
+ * do not apply additonal settings
+ */
+ if (chip->chg_autonomous_mode) {
+ dev_dbg(chip->dev, "Charger configured for autonomous mode\n");
+ return 0;
+ }
+
+ rc = smb358_enable_volatile_writes(chip);
+ if (rc) {
+ dev_err(chip->dev, "Couldn't configure volatile writes rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ /* setup defaults for CHG_CNTRL_REG */
+ reg = CHG_CTRL_BATT_MISSING_DET_THERM_IO;
+ mask = CHG_CTRL_BATT_MISSING_DET_MASK;
+ rc = smb358_masked_write(chip, CHG_CTRL_REG, mask, reg);
+ if (rc) {
+ dev_err(chip->dev, "Couldn't set CHG_CTRL_REG rc=%d\n", rc);
+ return rc;
+ }
+ /* setup defaults for PIN_CTRL_REG */
+ reg = CHG_PIN_CTRL_USBCS_REG_BIT | CHG_PIN_CTRL_CHG_EN_LOW_REG_BIT |
+ CHG_PIN_CTRL_APSD_IRQ_BIT | CHG_PIN_CTRL_CHG_ERR_IRQ_BIT;
+ mask = CHG_PIN_CTRL_CHG_EN_MASK | CHG_PIN_CTRL_USBCS_REG_MASK |
+ CHG_PIN_CTRL_APSD_IRQ_MASK | CHG_PIN_CTRL_CHG_ERR_IRQ_MASK;
+ rc = smb358_masked_write(chip, CHG_PIN_EN_CTRL_REG, mask, reg);
+ if (rc) {
+ dev_err(chip->dev, "Couldn't set CHG_PIN_EN_CTRL_REG rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ /* setup USB suspend and APSD */
+ rc = smb358_masked_write(chip, VARIOUS_FUNC_REG,
+ VARIOUS_FUNC_USB_SUSP_MASK, VARIOUS_FUNC_USB_SUSP_EN_REG_BIT);
+ if (rc) {
+ dev_err(chip->dev, "Couldn't set VARIOUS_FUNC_REG rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ if (!chip->disable_apsd)
+ reg = CHG_CTRL_APSD_EN_BIT;
+ rc = smb358_masked_write(chip, CHG_CTRL_REG,
+ CHG_CTRL_APSD_EN_MASK, reg);
+ if (rc) {
+ dev_err(chip->dev, "Couldn't set CHG_CTRL_REG rc=%d\n",
+ rc);
+ return rc;
+ }
+ /* Fault and Status IRQ configuration */
+ reg = FAULT_INT_HOT_COLD_HARD_BIT | FAULT_INT_HOT_COLD_SOFT_BIT
+ | FAULT_INT_INPUT_UV_BIT | FAULT_INT_AICL_COMPLETE_BIT
+ | FAULT_INT_INPUT_OV_BIT;
+ rc = smb358_write_reg(chip, FAULT_INT_REG, reg);
+ if (rc) {
+ dev_err(chip->dev, "Couldn't set FAULT_INT_REG rc=%d\n", rc);
+ return rc;
+ }
+ reg = STATUS_INT_CHG_TIMEOUT_BIT | STATUS_INT_OTG_DETECT_BIT |
+ STATUS_INT_BATT_OV_BIT | STATUS_INT_CHGING_BIT |
+ STATUS_INT_CHG_INHI_BIT | STATUS_INT_INOK_BIT |
+ STATUS_INT_LOW_BATT_BIT | STATUS_INT_MISSING_BATT_BIT;
+ rc = smb358_write_reg(chip, STATUS_INT_REG, reg);
+ if (rc) {
+ dev_err(chip->dev, "Couldn't set STATUS_INT_REG rc=%d\n", rc);
+ return rc;
+ }
+ /* setup THERM Monitor */
+ rc = smb358_masked_write(chip, THERM_A_CTRL_REG,
+ THERM_A_THERM_MONITOR_EN_MASK, THERM_A_THERM_MONITOR_EN_BIT);
+ if (rc) {
+ dev_err(chip->dev, "Couldn't set THERM_A_CTRL_REG rc=%d\n",
+ rc);
+ return rc;
+ }
+ /* set the fast charge current limit */
+ rc = smb358_fastchg_current_set(chip);
+ if (rc) {
+ dev_err(chip->dev, "Couldn't set fastchg current rc=%d\n", rc);
+ return rc;
+ }
+
+ /* set the float voltage */
+ rc = smb358_float_voltage_set(chip, chip->vfloat_mv);
+ if (rc < 0) {
+ dev_err(chip->dev,
+ "Couldn't set float voltage rc = %d\n", rc);
+ return rc;
+ }
+
+ /* set iterm */
+ rc = smb358_term_current_set(chip);
+ if (rc)
+ dev_err(chip->dev, "Couldn't set term current rc=%d\n", rc);
+
+ /* set recharge */
+ rc = smb358_recharge_set(chip);
+ if (rc)
+ dev_err(chip->dev, "Couldn't set recharge para rc=%d\n", rc);
+
+ /* enable/disable charging */
+ rc = smb358_charging_disable(chip, USER, !!chip->charging_disabled);
+ if (rc)
+ dev_err(chip->dev, "Couldn't '%s' charging rc = %d\n",
+ chip->charging_disabled ? "disable" : "enable", rc);
+
+ return rc;
+}
+
+static enum power_supply_property smb358_battery_properties[] = {
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_CHARGING_ENABLED,
+ POWER_SUPPLY_PROP_CHARGE_TYPE,
+ POWER_SUPPLY_PROP_CAPACITY,
+ POWER_SUPPLY_PROP_HEALTH,
+ POWER_SUPPLY_PROP_TECHNOLOGY,
+ POWER_SUPPLY_PROP_MODEL_NAME,
+ POWER_SUPPLY_PROP_TEMP,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+};
+
+static int smb358_get_prop_batt_status(struct smb358_charger *chip)
+{
+ int rc;
+ u8 reg = 0;
+
+ if (chip->batt_full)
+ return POWER_SUPPLY_STATUS_FULL;
+
+ rc = smb358_read_reg(chip, STATUS_C_REG, ®);
+ if (rc) {
+ dev_err(chip->dev, "Couldn't read STAT_C rc = %d\n", rc);
+ return POWER_SUPPLY_STATUS_UNKNOWN;
+ }
+
+ dev_dbg(chip->dev, "%s: STATUS_C_REG=%x\n", __func__, reg);
+
+ if (reg & STATUS_C_CHG_HOLD_OFF_BIT)
+ return POWER_SUPPLY_STATUS_NOT_CHARGING;
+
+ if ((reg & STATUS_C_CHARGING_MASK) &&
+ !(reg & STATUS_C_CHG_ERR_STATUS_BIT))
+ return POWER_SUPPLY_STATUS_CHARGING;
+
+ return POWER_SUPPLY_STATUS_DISCHARGING;
+}
+
+static int smb358_get_prop_batt_present(struct smb358_charger *chip)
+{
+ return !chip->battery_missing;
+}
+
+static int smb358_get_prop_batt_capacity(struct smb358_charger *chip)
+{
+ union power_supply_propval ret = {0, };
+
+ if (chip->fake_battery_soc >= 0)
+ return chip->fake_battery_soc;
+
+ if (chip->bms_psy) {
+ chip->bms_psy->get_property(chip->bms_psy,
+ POWER_SUPPLY_PROP_CAPACITY, &ret);
+ return ret.intval;
+ }
+
+ dev_dbg(chip->dev,
+ "Couldn't get bms_psy, return default capacity\n");
+ return SMB358_DEFAULT_BATT_CAPACITY;
+}
+
+static int smb358_get_prop_charge_type(struct smb358_charger *chip)
+{
+ int rc;
+ u8 reg = 0;
+
+ rc = smb358_read_reg(chip, STATUS_C_REG, ®);
+ if (rc) {
+ dev_err(chip->dev, "Couldn't read STAT_C rc = %d\n", rc);
+ return POWER_SUPPLY_CHARGE_TYPE_UNKNOWN;
+ }
+
+ dev_dbg(chip->dev, "%s: STATUS_C_REG=%x\n", __func__, reg);
+
+ reg &= STATUS_C_CHARGING_MASK;
+
+ if (reg == STATUS_C_FAST_CHARGING)
+ return POWER_SUPPLY_CHARGE_TYPE_FAST;
+ else if (reg == STATUS_C_TAPER_CHARGING)
+ return POWER_SUPPLY_CHARGE_TYPE_TAPER;
+ else if (reg == STATUS_C_PRE_CHARGING)
+ return POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
+ else
+ return POWER_SUPPLY_CHARGE_TYPE_NONE;
+}
+
+static int smb358_get_prop_batt_health(struct smb358_charger *chip)
+{
+ union power_supply_propval ret = {0, };
+
+ if (chip->batt_hot)
+ ret.intval = POWER_SUPPLY_HEALTH_OVERHEAT;
+ else if (chip->batt_cold)
+ ret.intval = POWER_SUPPLY_HEALTH_COLD;
+ else if (chip->batt_warm)
+ ret.intval = POWER_SUPPLY_HEALTH_WARM;
+ else if (chip->batt_cool)
+ ret.intval = POWER_SUPPLY_HEALTH_COOL;
+ else
+ ret.intval = POWER_SUPPLY_HEALTH_GOOD;
+
+ return ret.intval;
+}
+
+#define DEFAULT_TEMP 250
+static int smb358_get_prop_batt_temp(struct smb358_charger *chip)
+{
+ int rc = 0;
+ struct qpnp_vadc_result results;
+
+ if (!smb358_get_prop_batt_present(chip))
+ return DEFAULT_TEMP;
+
+ rc = qpnp_vadc_read(chip->vadc_dev, LR_MUX1_BATT_THERM, &results);
+ if (rc) {
+ pr_debug("Unable to read batt temperature rc=%d\n", rc);
+ return DEFAULT_TEMP;
+ }
+ pr_debug("get_bat_temp %d, %lld\n",
+ results.adc_code, results.physical);
+
+ return (int)results.physical;
+}
+
+static int
+smb358_get_prop_battery_voltage_now(struct smb358_charger *chip)
+{
+ int rc = 0;
+ struct qpnp_vadc_result results;
+
+ rc = qpnp_vadc_read(chip->vadc_dev, VBAT_SNS, &results);
+ if (rc) {
+ pr_err("Unable to read vbat rc=%d\n", rc);
+ return 0;
+ }
+ return results.physical;
+}
+
+static int __smb358_path_suspend(struct smb358_charger *chip, bool suspend)
+{
+ int rc;
+
+ rc = smb358_masked_write(chip, CMD_A_REG,
+ CMD_A_CHG_SUSP_EN_MASK,
+ suspend ? CMD_A_CHG_SUSP_EN_BIT : 0);
+ if (rc < 0)
+ dev_err(chip->dev, "Couldn't set CMD_A reg, rc = %d\n", rc);
+ return rc;
+}
+
+static int smb358_path_suspend(struct smb358_charger *chip, int reason,
+ bool suspend)
+{
+ int rc = 0;
+ int suspended;
+
+ mutex_lock(&chip->path_suspend_lock);
+ suspended = chip->usb_suspended;
+
+ if (suspend == false)
+ suspended &= ~reason;
+ else
+ suspended |= reason;
+
+ if (!chip->usb_suspended && suspended) {
+ rc = __smb358_path_suspend(chip, true);
+ chip->usb_suspended = suspended;
+ power_supply_set_online(chip->usb_psy, !chip->usb_suspended);
+ power_supply_changed(chip->usb_psy);
+ } else if (chip->usb_suspended && !suspended) {
+ rc = __smb358_path_suspend(chip, false);
+ chip->usb_suspended = suspended;
+ power_supply_set_online(chip->usb_psy, !chip->usb_suspended);
+ power_supply_changed(chip->usb_psy);
+ }
+
+ if (rc)
+ dev_err(chip->dev, "Couldn't set/unset suspend rc = %d\n", rc);
+
+ mutex_unlock(&chip->path_suspend_lock);
+ return rc;
+}
+
+static int smb358_set_usb_chg_current(struct smb358_charger *chip,
+ int current_ma)
+{
+ int i, rc = 0;
+ u8 reg1 = 0, reg2 = 0, mask = 0;
+
+ dev_dbg(chip->dev, "%s: USB current_ma = %d\n", __func__, current_ma);
+
+ if (chip->chg_autonomous_mode) {
+ dev_dbg(chip->dev, "%s: Charger in autonmous mode\n", __func__);
+ return 0;
+ }
+
+ if (current_ma < USB3_MIN_CURRENT_MA && current_ma != 2)
+ current_ma = USB2_MIN_CURRENT_MA;
+
+ if (current_ma == USB2_MIN_CURRENT_MA) {
+ /* USB 2.0 - 100mA */
+ reg1 &= ~USB3_ENABLE_BIT;
+ reg2 &= ~CMD_B_CHG_USB_500_900_ENABLE_BIT;
+ } else if (current_ma == USB2_MAX_CURRENT_MA) {
+ /* USB 2.0 - 500mA */
+ reg1 &= ~USB3_ENABLE_BIT;
+ reg2 |= CMD_B_CHG_USB_500_900_ENABLE_BIT;
+ } else if (current_ma == USB3_MAX_CURRENT_MA) {
+ /* USB 3.0 - 900mA */
+ reg1 |= USB3_ENABLE_BIT;
+ reg2 |= CMD_B_CHG_USB_500_900_ENABLE_BIT;
+ } else if (current_ma > USB2_MAX_CURRENT_MA) {
+ /* HC mode - if none of the above */
+ reg2 |= CMD_B_CHG_HC_ENABLE_BIT;
+
+ for (i = ARRAY_SIZE(chg_current) - 1; i >= 0; i--) {
+ if (chg_current[i] <= current_ma)
+ break;
+ }
+ if (i < 0) {
+ dev_err(chip->dev, "Cannot find %dmA\n", current_ma);
+ i = 0;
+ }
+
+ i = i << AC_CHG_CURRENT_SHIFT;
+ rc = smb358_masked_write(chip, CHG_OTH_CURRENT_CTRL_REG,
+ AC_CHG_CURRENT_MASK, i);
+ if (rc)
+ dev_err(chip->dev, "Couldn't set input mA rc=%d\n", rc);
+ }
+
+ mask = CMD_B_CHG_HC_ENABLE_BIT | CMD_B_CHG_USB_500_900_ENABLE_BIT;
+ rc = smb358_masked_write(chip, CMD_B_REG, mask, reg2);
+ if (rc < 0)
+ dev_err(chip->dev, "Couldn't set charging mode rc = %d\n", rc);
+
+ mask = USB3_ENABLE_MASK;
+ rc = smb358_masked_write(chip, SYSOK_AND_USB3_REG, mask, reg1);
+ if (rc < 0)
+ dev_err(chip->dev, "Couldn't set USB3 mode rc = %d\n", rc);
+
+ /* Only set suspend bit when chg present and current_ma = 2 */
+ if (current_ma == 2 && chip->chg_present) {
+ rc = smb358_path_suspend(chip, CURRENT, true);
+ if (rc < 0)
+ dev_err(chip->dev, "Couldn't suspend rc = %d\n", rc);
+ } else {
+ rc = smb358_path_suspend(chip, CURRENT, false);
+ if (rc < 0)
+ dev_err(chip->dev, "Couldn't set susp rc = %d\n", rc);
+ }
+
+ return rc;
+}
+
+static int
+smb358_batt_property_is_writeable(struct power_supply *psy,
+ enum power_supply_property psp)
+{
+ switch (psp) {
+ case POWER_SUPPLY_PROP_CHARGING_ENABLED:
+ case POWER_SUPPLY_PROP_CAPACITY:
+ return 1;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int bound_soc(int soc)
+{
+ soc = max(0, soc);
+ soc = min(soc, 100);
+ return soc;
+}
+
+static int smb358_battery_set_property(struct power_supply *psy,
+ enum power_supply_property prop,
+ const union power_supply_propval *val)
+{
+ struct smb358_charger *chip = container_of(psy,
+ struct smb358_charger, batt_psy);
+
+ switch (prop) {
+ case POWER_SUPPLY_PROP_CHARGING_ENABLED:
+ smb358_charging_disable(chip, USER, !val->intval);
+ smb358_path_suspend(chip, USER, !val->intval);
+ break;
+ case POWER_SUPPLY_PROP_CAPACITY:
+ chip->fake_battery_soc = bound_soc(val->intval);
+ power_supply_changed(&chip->batt_psy);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int smb358_battery_get_property(struct power_supply *psy,
+ enum power_supply_property prop,
+ union power_supply_propval *val)
+{
+ struct smb358_charger *chip = container_of(psy,
+ struct smb358_charger, batt_psy);
+
+ switch (prop) {
+ case POWER_SUPPLY_PROP_STATUS:
+ val->intval = smb358_get_prop_batt_status(chip);
+ break;
+ case POWER_SUPPLY_PROP_PRESENT:
+ val->intval = smb358_get_prop_batt_present(chip);
+ break;
+ case POWER_SUPPLY_PROP_CAPACITY:
+ val->intval = smb358_get_prop_batt_capacity(chip);
+ break;
+ case POWER_SUPPLY_PROP_CHARGING_ENABLED:
+ val->intval = !(chip->charging_disabled_status & USER);
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_TYPE:
+ val->intval = smb358_get_prop_charge_type(chip);
+ break;
+ case POWER_SUPPLY_PROP_HEALTH:
+ val->intval = smb358_get_prop_batt_health(chip);
+ break;
+ case POWER_SUPPLY_PROP_TECHNOLOGY:
+ val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
+ break;
+ case POWER_SUPPLY_PROP_MODEL_NAME:
+ val->strval = "SMB358";
+ break;
+ case POWER_SUPPLY_PROP_TEMP:
+ val->intval = smb358_get_prop_batt_temp(chip);
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ val->intval = smb358_get_prop_battery_voltage_now(chip);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int apsd_complete(struct smb358_charger *chip, u8 status)
+{
+ int rc;
+ u8 reg = 0;
+ enum power_supply_type type = POWER_SUPPLY_TYPE_UNKNOWN;
+
+ /*
+ * If apsd is disabled, charger detection is done by
+ * DCIN UV irq.
+ * status = ZERO - indicates charger removed, handled
+ * by DCIN UV irq
+ */
+ if (chip->disable_apsd || status == 0) {
+ dev_dbg(chip->dev, "APSD %s, status = %d\n",
+ chip->disable_apsd ? "disabled" : "enabled", !!status);
+ return 0;
+ }
+
+ rc = smb358_read_reg(chip, STATUS_D_REG, ®);
+ if (rc) {
+ dev_err(chip->dev, "Couldn't read STATUS D rc = %d\n", rc);
+ return rc;
+ }
+
+ dev_dbg(chip->dev, "%s: STATUS_D_REG=%x\n", __func__, reg);
+
+ switch (reg) {
+ case STATUS_D_PORT_ACA_DOCK:
+ case STATUS_D_PORT_ACA_C:
+ case STATUS_D_PORT_ACA_B:
+ case STATUS_D_PORT_ACA_A:
+ type = POWER_SUPPLY_TYPE_USB_ACA;
+ break;
+ case STATUS_D_PORT_CDP:
+ type = POWER_SUPPLY_TYPE_USB_CDP;
+ break;
+ case STATUS_D_PORT_DCP:
+ type = POWER_SUPPLY_TYPE_USB_DCP;
+ break;
+ case STATUS_D_PORT_SDP:
+ type = POWER_SUPPLY_TYPE_USB;
+ break;
+ case STATUS_D_PORT_OTHER:
+ type = POWER_SUPPLY_TYPE_USB_DCP;
+ break;
+ default:
+ type = POWER_SUPPLY_TYPE_USB;
+ break;
+ }
+
+ chip->chg_present = !!status;
+
+ dev_dbg(chip->dev, "APSD complete. USB type detected=%d chg_present=%d",
+ type, chip->chg_present);
+
+ power_supply_set_charge_type(chip->usb_psy, type);
+
+ /* SMB is now done sampling the D+/D- lines, indicate USB driver */
+ dev_dbg(chip->dev, "%s updating usb_psy present=%d", __func__,
+ chip->chg_present);
+ power_supply_set_present(chip->usb_psy, chip->chg_present);
+
+ return 0;
+}
+
+static int chg_uv(struct smb358_charger *chip, u8 status)
+{
+ /* use this to detect USB insertion only if !apsd */
+ if (chip->disable_apsd && status == 0) {
+ chip->chg_present = true;
+ dev_dbg(chip->dev, "%s updating usb_psy present=%d",
+ __func__, chip->chg_present);
+ power_supply_set_supply_type(chip->usb_psy,
+ POWER_SUPPLY_TYPE_USB);
+ power_supply_set_present(chip->usb_psy, chip->chg_present);
+ }
+
+ if (status != 0) {
+ chip->chg_present = false;
+ dev_dbg(chip->dev, "%s updating usb_psy present=%d",
+ __func__, chip->chg_present);
+ /* we can't set usb_psy as UNKNOWN here, will lead USERSPACE issue */
+ power_supply_set_present(chip->usb_psy, chip->chg_present);
+ }
+
+ power_supply_changed(chip->usb_psy);
+ dev_dbg(chip->dev, "chip->chg_present = %d\n", chip->chg_present);
+
+ return 0;
+}
+
+static int chg_ov(struct smb358_charger *chip, u8 status)
+{
+ u8 psy_health_sts;
+ if (status)
+ psy_health_sts = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
+ else
+ psy_health_sts = POWER_SUPPLY_HEALTH_GOOD;
+
+ power_supply_set_health_state(
+ chip->usb_psy, psy_health_sts);
+ power_supply_changed(chip->usb_psy);
+
+ return 0;
+}
+
+static int fast_chg(struct smb358_charger *chip, u8 status)
+{
+ dev_dbg(chip->dev, "%s\n", __func__);
+ return 0;
+}
+
+static int chg_term(struct smb358_charger *chip, u8 status)
+{
+ dev_dbg(chip->dev, "%s\n", __func__);
+ chip->batt_full = !!status;
+ return 0;
+}
+
+static int taper_chg(struct smb358_charger *chip, u8 status)
+{
+ dev_dbg(chip->dev, "%s\n", __func__);
+ return 0;
+}
+
+static int chg_recharge(struct smb358_charger *chip, u8 status)
+{
+ dev_dbg(chip->dev, "%s, status = %d\n", __func__, !!status);
+ /* to check the status mean */
+ chip->batt_full = !status;
+ return 0;
+}
+
+#define HYSTERISIS_DECIDEGC 20
+static void smb_chg_adc_notification(enum qpnp_tm_state state, void *ctx)
+{
+ struct smb358_charger *chip = ctx;
+ bool bat_hot = 0, bat_cold = 0, bat_present = 0;
+ int temp;
+
+ if (state >= ADC_TM_STATE_NUM) {
+ pr_err("invallid state parameter %d\n", state);
+ return;
+ }
+
+ temp = smb358_get_prop_batt_temp(chip);
+
+ pr_debug("temp = %d state = %s\n", temp,
+ state == ADC_TM_WARM_STATE ? "hot" : "cold");
+
+ if (state == ADC_TM_WARM_STATE) {
+ if (temp > chip->hot_bat_decidegc) {
+ /* Normal to hot */
+ bat_hot = true;
+ bat_cold = false;
+ bat_present = true;
+
+ chip->adc_param.low_temp =
+ chip->hot_bat_decidegc - HYSTERISIS_DECIDEGC;
+ /* shall we need add high_temp here? */
+ chip->adc_param.state_request =
+ ADC_TM_COOL_THR_ENABLE;
+ } else if (temp >
+ chip->cold_bat_decidegc + HYSTERISIS_DECIDEGC) {
+ /* Cool to normal */
+ bat_hot = false;
+ bat_cold = false;
+ bat_present = true;
+
+ chip->adc_param.low_temp = chip->cold_bat_decidegc;
+ chip->adc_param.high_temp = chip->hot_bat_decidegc;
+ chip->adc_param.state_request =
+ ADC_TM_HIGH_LOW_THR_ENABLE;
+ } else if (temp > chip->bat_present_decidegc) {
+ /* Present to cold */
+ bat_hot = false;
+ bat_cold = true;
+ bat_present = true;
+
+ chip->adc_param.high_temp = chip->cold_bat_decidegc;
+ chip->adc_param.low_temp = chip->bat_present_decidegc;
+ chip->adc_param.state_request =
+ ADC_TM_HIGH_LOW_THR_ENABLE;
+ }
+ } else {
+ if (temp <= chip->bat_present_decidegc) {
+ /* Cold to present */
+ bat_cold = true;
+ bat_hot = false;
+ bat_present = false;
+ chip->adc_param.high_temp =
+ chip->bat_present_decidegc;
+ chip->adc_param.state_request =
+ ADC_TM_WARM_THR_ENABLE;
+ } else if (chip->bat_present_decidegc < temp &&
+ temp < chip->cold_bat_decidegc) {
+ /* Normal to cold */
+ bat_hot = false;
+ bat_cold = true;
+ bat_present = true;
+ chip->adc_param.high_temp =
+ chip->cold_bat_decidegc + HYSTERISIS_DECIDEGC;
+ /* add low_temp to enable batt present check */
+ chip->adc_param.low_temp =
+ chip->bat_present_decidegc;
+ chip->adc_param.state_request =
+ ADC_TM_HIGH_LOW_THR_ENABLE;
+ } else if (temp <
+ chip->hot_bat_decidegc - HYSTERISIS_DECIDEGC) {
+ /* Warm to normal */
+ bat_hot = false;
+ bat_cold = false;
+ bat_present = true;
+
+ chip->adc_param.low_temp = chip->cold_bat_decidegc;
+ chip->adc_param.high_temp = chip->hot_bat_decidegc;
+ chip->adc_param.state_request =
+ ADC_TM_HIGH_LOW_THR_ENABLE;
+ }
+ }
+
+ if (bat_present)
+ chip->battery_missing = false;
+ else
+ chip->battery_missing = true;
+
+ if (bat_hot ^ chip->batt_hot || bat_cold ^ chip->batt_cold) {
+ chip->batt_hot = bat_hot;
+ chip->batt_cold = bat_cold;
+ /* stop charging explicitly since we use PMIC thermal pin*/
+ if (bat_hot || bat_cold || chip->battery_missing)
+ smb358_charging_disable(chip, THERMAL, 1);
+ else
+ smb358_charging_disable(chip, THERMAL, 0);
+ }
+
+ pr_debug("hot %d, cold %d, missing %d, low = %d deciDegC, high = %d deciDegC\n",
+ chip->batt_hot, chip->batt_cold, chip->battery_missing,
+ chip->adc_param.low_temp, chip->adc_param.high_temp);
+ if (qpnp_adc_tm_channel_measure(chip->adc_tm_dev, &chip->adc_param))
+ pr_err("request ADC error\n");
+}
+
+/* only for SMB thermal */
+static int hot_hard_handler(struct smb358_charger *chip, u8 status)
+{
+ pr_debug("status = 0x%02x\n", status);
+ chip->batt_hot = !!status;
+ return 0;
+}
+static int cold_hard_handler(struct smb358_charger *chip, u8 status)
+{
+ pr_debug("status = 0x%02x\n", status);
+ chip->batt_cold = !!status;
+ return 0;
+}
+static int hot_soft_handler(struct smb358_charger *chip, u8 status)
+{
+ pr_debug("status = 0x%02x\n", status);
+ chip->batt_warm = !!status;
+ return 0;
+}
+static int cold_soft_handler(struct smb358_charger *chip, u8 status)
+{
+ pr_debug("status = 0x%02x\n", status);
+ chip->batt_cool = !!status;
+ return 0;
+}
+
+static int battery_missing(struct smb358_charger *chip, u8 status)
+{
+ chip->battery_missing = !!status;
+ return 0;
+}
+
+static struct irq_handler_info handlers[] = {
+ [0] = {
+ .stat_reg = IRQ_A_REG,
+ .val = 0,
+ .prev_val = 0,
+ .irq_info = {
+ {
+ .name = "cold_soft",
+ .smb_irq = cold_soft_handler,
+ },
+ {
+ .name = "hot_soft",
+ .smb_irq = hot_soft_handler,
+ },
+ {
+ .name = "cold_hard",
+ .smb_irq = cold_hard_handler,
+ },
+ {
+ .name = "hot_hard",
+ .smb_irq = hot_hard_handler,
+ },
+ },
+ },
+ [1] = {
+ .stat_reg = IRQ_B_REG,
+ .val = 0,
+ .prev_val = 0,
+ .irq_info = {
+ {
+ .name = "chg_hot",
+ },
+ {
+ .name = "vbat_low",
+ },
+ {
+ .name = "battery_missing",
+ .smb_irq = battery_missing
+ },
+ {
+ .name = "battery_ov",
+ },
+ },
+ },
+ [2] = {
+ .stat_reg = IRQ_C_REG,
+ .val = 0,
+ .prev_val = 0,
+ .irq_info = {
+ {
+ .name = "chg_term",
+ .smb_irq = chg_term,
+ },
+ {
+ .name = "taper",
+ .smb_irq = taper_chg,
+ },
+ {
+ .name = "recharge",
+ .smb_irq = chg_recharge,
+ },
+ {
+ .name = "fast_chg",
+ .smb_irq = fast_chg,
+ },
+ },
+ },
+ [3] = {
+ .stat_reg = IRQ_D_REG,
+ .val = 0,
+ .prev_val = 0,
+ .irq_info = {
+ {
+ .name = "prechg_timeout",
+ },
+ {
+ .name = "safety_timeout",
+ },
+ {
+ .name = "aicl_complete",
+ },
+ {
+ .name = "src_detect",
+ .smb_irq = apsd_complete,
+ },
+ },
+ },
+ [4] = {
+ .stat_reg = IRQ_E_REG,
+ .val = 0,
+ .prev_val = 0,
+ .irq_info = {
+ {
+ .name = "usbin_uv",
+ .smb_irq = chg_uv,
+ },
+ {
+ .name = "usbin_ov",
+ .smb_irq = chg_ov,
+ },
+ {
+ .name = "unknown",
+ },
+ {
+ .name = "unknown",
+ },
+ },
+ },
+ [5] = {
+ .stat_reg = IRQ_F_REG,
+ .val = 0,
+ .prev_val = 0,
+ .irq_info = {
+ {
+ .name = "power_ok",
+ },
+ {
+ .name = "otg_det",
+ },
+ {
+ .name = "otg_batt_uv",
+ },
+ {
+ .name = "otg_oc",
+ },
+ },
+ },
+};
+
+#define IRQ_LATCHED_MASK 0x02
+#define IRQ_STATUS_MASK 0x01
+#define BITS_PER_IRQ 2
+static irqreturn_t smb358_chg_stat_handler(int irq, void *dev_id)
+{
+ struct smb358_charger *chip = dev_id;
+ int i, j;
+ u8 triggered;
+ u8 changed;
+ u8 rt_stat, prev_rt_stat;
+ int rc;
+ int handler_count = 0;
+
+ mutex_lock(&chip->irq_complete);
+
+ chip->irq_waiting = true;
+ if (!chip->resume_completed) {
+ dev_dbg(chip->dev, "IRQ triggered before device-resume\n");
+ disable_irq_nosync(irq);
+ mutex_unlock(&chip->irq_complete);
+ return IRQ_HANDLED;
+ }
+ chip->irq_waiting = false;
+
+ for (i = 0; i < ARRAY_SIZE(handlers); i++) {
+ rc = smb358_read_reg(chip, handlers[i].stat_reg,
+ &handlers[i].val);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't read %d rc = %d\n",
+ handlers[i].stat_reg, rc);
+ continue;
+ }
+
+ for (j = 0; j < ARRAY_SIZE(handlers[i].irq_info); j++) {
+ triggered = handlers[i].val
+ & (IRQ_LATCHED_MASK << (j * BITS_PER_IRQ));
+ rt_stat = handlers[i].val
+ & (IRQ_STATUS_MASK << (j * BITS_PER_IRQ));
+ prev_rt_stat = handlers[i].prev_val
+ & (IRQ_STATUS_MASK << (j * BITS_PER_IRQ));
+ changed = prev_rt_stat ^ rt_stat;
+
+ if (triggered || changed)
+ rt_stat ? handlers[i].irq_info[j].high++ :
+ handlers[i].irq_info[j].low++;
+
+ if ((triggered || changed)
+ && handlers[i].irq_info[j].smb_irq != NULL) {
+ handler_count++;
+ rc = handlers[i].irq_info[j].smb_irq(chip,
+ rt_stat);
+ if (rc < 0)
+ dev_err(chip->dev,
+ "Couldn't handle %d irq for reg 0x%02x rc = %d\n",
+ j, handlers[i].stat_reg, rc);
+ }
+ }
+ handlers[i].prev_val = handlers[i].val;
+ }
+
+ pr_debug("handler count = %d\n", handler_count);
+ if (handler_count) {
+ pr_debug("batt psy changed\n");
+ power_supply_changed(&chip->batt_psy);
+ }
+
+ mutex_unlock(&chip->irq_complete);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t smb358_chg_valid_handler(int irq, void *dev_id)
+{
+ struct smb358_charger *chip = dev_id;
+ int present;
+
+ present = gpio_get_value_cansleep(chip->chg_valid_gpio);
+ if (present < 0) {
+ dev_err(chip->dev, "Couldn't read chg_valid gpio=%d\n",
+ chip->chg_valid_gpio);
+ return IRQ_HANDLED;
+ }
+ present ^= chip->chg_valid_act_low;
+
+ dev_dbg(chip->dev, "%s: chg_present = %d\n", __func__, present);
+
+ if (present != chip->chg_present) {
+ chip->chg_present = present;
+ dev_dbg(chip->dev, "%s updating usb_psy present=%d",
+ __func__, chip->chg_present);
+ power_supply_set_present(chip->usb_psy, chip->chg_present);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static void smb358_external_power_changed(struct power_supply *psy)
+{
+ struct smb358_charger *chip = container_of(psy,
+ struct smb358_charger, batt_psy);
+ union power_supply_propval prop = {0,};
+ int rc, current_limit = 0;
+
+ if (chip->bms_psy_name)
+ chip->bms_psy =
+ power_supply_get_by_name((char *)chip->bms_psy_name);
+
+ rc = chip->usb_psy->get_property(chip->usb_psy,
+ POWER_SUPPLY_PROP_CURRENT_MAX, &prop);
+ if (rc)
+ dev_err(chip->dev,
+ "Couldn't read USB current_max property, rc=%d\n", rc);
+ else
+ current_limit = prop.intval / 1000;
+
+
+ smb358_enable_volatile_writes(chip);
+ smb358_set_usb_chg_current(chip, current_limit);
+
+ dev_dbg(chip->dev, "current_limit = %d\n", current_limit);
+}
+
+#if defined(CONFIG_DEBUG_FS)
+#define LAST_CNFG_REG 0x13
+static int show_cnfg_regs(struct seq_file *m, void *data)
+{
+ struct smb358_charger *chip = m->private;
+ int rc;
+ u8 reg;
+ u8 addr;
+
+ for (addr = 0; addr <= LAST_CNFG_REG; addr++) {
+ rc = smb358_read_reg(chip, addr, ®);
+ if (!rc)
+ seq_printf(m, "0x%02x = 0x%02x\n", addr, reg);
+ }
+
+ return 0;
+}
+
+static int cnfg_debugfs_open(struct inode *inode, struct file *file)
+{
+ struct smb358_charger *chip = inode->i_private;
+
+ return single_open(file, show_cnfg_regs, chip);
+}
+
+static const struct file_operations cnfg_debugfs_ops = {
+ .owner = THIS_MODULE,
+ .open = cnfg_debugfs_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+#define FIRST_CMD_REG 0x30
+#define LAST_CMD_REG 0x33
+static int show_cmd_regs(struct seq_file *m, void *data)
+{
+ struct smb358_charger *chip = m->private;
+ int rc;
+ u8 reg;
+ u8 addr;
+
+ for (addr = FIRST_CMD_REG; addr <= LAST_CMD_REG; addr++) {
+ rc = smb358_read_reg(chip, addr, ®);
+ if (!rc)
+ seq_printf(m, "0x%02x = 0x%02x\n", addr, reg);
+ }
+
+ return 0;
+}
+
+static int cmd_debugfs_open(struct inode *inode, struct file *file)
+{
+ struct smb358_charger *chip = inode->i_private;
+
+ return single_open(file, show_cmd_regs, chip);
+}
+
+static const struct file_operations cmd_debugfs_ops = {
+ .owner = THIS_MODULE,
+ .open = cmd_debugfs_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+#define FIRST_STATUS_REG 0x35
+#define LAST_STATUS_REG 0x3F
+static int show_status_regs(struct seq_file *m, void *data)
+{
+ struct smb358_charger *chip = m->private;
+ int rc;
+ u8 reg;
+ u8 addr;
+
+ for (addr = FIRST_STATUS_REG; addr <= LAST_STATUS_REG; addr++) {
+ rc = smb358_read_reg(chip, addr, ®);
+ if (!rc)
+ seq_printf(m, "0x%02x = 0x%02x\n", addr, reg);
+ }
+
+ return 0;
+}
+
+static int status_debugfs_open(struct inode *inode, struct file *file)
+{
+ struct smb358_charger *chip = inode->i_private;
+
+ return single_open(file, show_status_regs, chip);
+}
+
+static const struct file_operations status_debugfs_ops = {
+ .owner = THIS_MODULE,
+ .open = status_debugfs_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int show_irq_count(struct seq_file *m, void *data)
+{
+ int i, j, total = 0;
+
+ for (i = 0; i < ARRAY_SIZE(handlers); i++)
+ for (j = 0; j < 4; j++) {
+ seq_printf(m, "%s=%d\t(high=%d low=%d)\n",
+ handlers[i].irq_info[j].name,
+ handlers[i].irq_info[j].high
+ + handlers[i].irq_info[j].low,
+ handlers[i].irq_info[j].high,
+ handlers[i].irq_info[j].low);
+ total += (handlers[i].irq_info[j].high
+ + handlers[i].irq_info[j].low);
+ }
+
+ seq_printf(m, "\n\tTotal = %d\n", total);
+
+ return 0;
+}
+
+static int irq_count_debugfs_open(struct inode *inode, struct file *file)
+{
+ struct smb358_charger *chip = inode->i_private;
+
+ return single_open(file, show_irq_count, chip);
+}
+
+static const struct file_operations irq_count_debugfs_ops = {
+ .owner = THIS_MODULE,
+ .open = irq_count_debugfs_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int get_reg(void *data, u64 *val)
+{
+ struct smb358_charger *chip = data;
+ int rc;
+ u8 temp;
+
+ rc = smb358_read_reg(chip, chip->peek_poke_address, &temp);
+ if (rc < 0) {
+ dev_err(chip->dev,
+ "Couldn't read reg %x rc = %d\n",
+ chip->peek_poke_address, rc);
+ return -EAGAIN;
+ }
+ *val = temp;
+ return 0;
+}
+
+static int set_reg(void *data, u64 val)
+{
+ struct smb358_charger *chip = data;
+ int rc;
+ u8 temp;
+
+ temp = (u8) val;
+ rc = smb358_write_reg(chip, chip->peek_poke_address, temp);
+ if (rc < 0) {
+ dev_err(chip->dev,
+ "Couldn't write 0x%02x to 0x%02x rc= %d\n",
+ chip->peek_poke_address, temp, rc);
+ return -EAGAIN;
+ }
+ return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(poke_poke_debug_ops, get_reg, set_reg, "0x%02llx\n");
+
+static int force_irq_set(void *data, u64 val)
+{
+ struct smb358_charger *chip = data;
+
+ smb358_chg_stat_handler(chip->client->irq, data);
+ return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(force_irq_ops, NULL, force_irq_set, "0x%02llx\n");
+#endif
+
+#ifdef DEBUG
+static void dump_regs(struct smb358_charger *chip)
+{
+ int rc;
+ u8 reg;
+ u8 addr;
+
+ for (addr = 0; addr <= LAST_CNFG_REG; addr++) {
+ rc = smb358_read_reg(chip, addr, ®);
+ if (rc)
+ dev_err(chip->dev, "Couldn't read 0x%02x rc = %d\n",
+ addr, rc);
+ else
+ pr_debug("0x%02x = 0x%02x\n", addr, reg);
+ }
+
+ for (addr = FIRST_STATUS_REG; addr <= LAST_STATUS_REG; addr++) {
+ rc = smb358_read_reg(chip, addr, ®);
+ if (rc)
+ dev_err(chip->dev, "Couldn't read 0x%02x rc = %d\n",
+ addr, rc);
+ else
+ pr_debug("0x%02x = 0x%02x\n", addr, reg);
+ }
+
+ for (addr = FIRST_CMD_REG; addr <= LAST_CMD_REG; addr++) {
+ rc = smb358_read_reg(chip, addr, ®);
+ if (rc)
+ dev_err(chip->dev, "Couldn't read 0x%02x rc = %d\n",
+ addr, rc);
+ else
+ pr_debug("0x%02x = 0x%02x\n", addr, reg);
+ }
+}
+#else
+static void dump_regs(struct smb358_charger *chip)
+{
+}
+#endif
+
+static int smb_parse_dt(struct smb358_charger *chip)
+{
+ int rc;
+ enum of_gpio_flags gpio_flags;
+ struct device_node *node = chip->dev->of_node;
+ int batt_present_degree_negative;
+
+ if (!node) {
+ dev_err(chip->dev, "device tree info. missing\n");
+ return -EINVAL;
+ }
+
+ chip->charging_disabled = of_property_read_bool(node,
+ "qcom,charger-disabled");
+
+ chip->chg_autonomous_mode = of_property_read_bool(node,
+ "qcom,chg-autonomous-mode");
+
+ chip->disable_apsd = of_property_read_bool(node, "qcom,disable-apsd");
+
+ chip->using_pmic_therm = of_property_read_bool(node,
+ "qcom,using-pmic-therm");
+
+ rc = of_property_read_string(node, "qcom,bms-psy-name",
+ &chip->bms_psy_name);
+ if (rc)
+ chip->bms_psy_name = NULL;
+
+ chip->chg_valid_gpio = of_get_named_gpio_flags(node,
+ "qcom,chg-valid-gpio", 0, &gpio_flags);
+ if (!gpio_is_valid(chip->chg_valid_gpio))
+ dev_dbg(chip->dev, "Invalid chg-valid-gpio");
+ else
+ chip->chg_valid_act_low = gpio_flags & OF_GPIO_ACTIVE_LOW;
+
+ rc = of_property_read_u32(node, "qcom,fastchg-current-max-ma",
+ &chip->fastchg_current_max_ma);
+ if (rc)
+ chip->fastchg_current_max_ma = SMB358_FAST_CHG_MAX_MA;
+
+ chip->ieerm_disabled = of_property_read_bool(node,
+ "qcom,iterm-disabled");
+
+ rc = of_property_read_u32(node, "qcom,iterm-ma", &chip->iterm_ma);
+ if (rc < 0)
+ chip->iterm_ma = -EINVAL;
+
+ rc = of_property_read_u32(node, "qcom,float-voltage-mv",
+ &chip->vfloat_mv);
+ if (rc < 0) {
+ chip->vfloat_mv = -EINVAL;
+ pr_err("float-voltage-mv property missing, exit\n");
+ return -EINVAL;
+ }
+
+ rc = of_property_read_u32(node, "qcom,recharge-mv",
+ &chip->recharge_mv);
+ if (rc < 0)
+ chip->recharge_mv = -EINVAL;
+
+ chip->recharge_disabled = of_property_read_bool(node,
+ "qcom,recharge-disabled");
+
+ rc = of_property_read_u32(node, "qcom,cold-bat-decidegc",
+ &chip->cold_bat_decidegc);
+ if (rc < 0)
+ chip->cold_bat_decidegc = -EINVAL;
+
+ rc = of_property_read_u32(node, "qcom,hot-bat-decidegc",
+ &chip->hot_bat_decidegc);
+ if (rc < 0)
+ chip->hot_bat_decidegc = -EINVAL;
+
+ rc = of_property_read_u32(node, "qcom,bat-present-decidegc",
+ &batt_present_degree_negative);
+ if (rc < 0)
+ chip->bat_present_decidegc = -EINVAL;
+ else
+ chip->bat_present_decidegc = -batt_present_degree_negative;
+
+ pr_debug("recharge-disabled = %d, recharge-mv = %d,",
+ chip->recharge_disabled, chip->recharge_mv);
+ pr_debug("vfloat-mv = %d, iterm-disabled = %d,",
+ chip->vfloat_mv, chip->iterm_ma);
+ pr_debug("fastchg-current = %d, charging-disabled = %d,",
+ chip->fastchg_current_max_ma,
+ chip->charging_disabled);
+ pr_debug("disable-apsd = %d bms = %s cold-bat-degree = %d,",
+ chip->disable_apsd, chip->bms_psy_name,
+ chip->cold_bat_decidegc);
+ pr_debug("hot-bat-degree = %d, bat-present-decidegc = %d\n",
+ chip->hot_bat_decidegc, chip->bat_present_decidegc);
+ return 0;
+}
+
+static int determine_initial_state(struct smb358_charger *chip)
+{
+ int rc;
+ u8 reg = 0;
+
+ rc = smb358_read_reg(chip, IRQ_B_REG, ®);
+ if (rc) {
+ dev_err(chip->dev, "Couldn't read IRQ_B rc = %d\n", rc);
+ goto fail_init_status;
+ }
+
+ rc = smb358_read_reg(chip, IRQ_C_REG, ®);
+ if (rc) {
+ dev_err(chip->dev, "Couldn't read IRQ_C rc = %d\n", rc);
+ goto fail_init_status;
+ }
+ chip->batt_full = (reg & IRQ_C_TERM_BIT) ? true : false;
+
+ rc = smb358_read_reg(chip, IRQ_A_REG, ®);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't read irq A rc = %d\n", rc);
+ return rc;
+ }
+
+ /* For current design, can ignore this */
+ if (reg & IRQ_A_HOT_HARD_BIT)
+ chip->batt_hot = true;
+ if (reg & IRQ_A_COLD_HARD_BIT)
+ chip->batt_cold = true;
+ if (reg & IRQ_A_HOT_SOFT_BIT)
+ chip->batt_warm = true;
+ if (reg & IRQ_A_COLD_SOFT_BIT)
+ chip->batt_cool = true;
+
+ rc = smb358_read_reg(chip, IRQ_E_REG, ®);
+ if (rc) {
+ dev_err(chip->dev, "Couldn't read IRQ_E rc = %d\n", rc);
+ goto fail_init_status;
+ }
+
+ if (reg & IRQ_E_INPUT_UV_BIT) {
+ chg_uv(chip, 1);
+ } else {
+ chg_uv(chip, 0);
+ apsd_complete(chip, 1);
+ }
+
+ return 0;
+
+fail_init_status:
+ dev_err(chip->dev, "Couldn't determine initial status\n");
+ return rc;
+}
+
+#if defined(CONFIG_DEBUG_FS)
+static void smb358_debugfs_init(struct smb358_charger *chip)
+{
+ int rc;
+ chip->debug_root = debugfs_create_dir("smb358", NULL);
+ if (!chip->debug_root)
+ dev_err(chip->dev, "Couldn't create debug dir\n");
+
+ if (chip->debug_root) {
+ struct dentry *ent;
+
+ ent = debugfs_create_file("config_registers", S_IFREG | S_IRUGO,
+ chip->debug_root, chip,
+ &cnfg_debugfs_ops);
+ if (!ent || IS_ERR(ent)) {
+ rc = PTR_ERR(ent);
+ dev_err(chip->dev,
+ "Couldn't create cnfg debug file rc = %d\n",
+ rc);
+ }
+
+ ent = debugfs_create_file("status_registers", S_IFREG | S_IRUGO,
+ chip->debug_root, chip,
+ &status_debugfs_ops);
+ if (!ent || IS_ERR(ent)) {
+ rc = PTR_ERR(ent);
+ dev_err(chip->dev,
+ "Couldn't create status debug file rc = %d\n",
+ rc);
+ }
+
+ ent = debugfs_create_file("cmd_registers", S_IFREG | S_IRUGO,
+ chip->debug_root, chip,
+ &cmd_debugfs_ops);
+ if (!ent || IS_ERR(ent)) {
+ rc = PTR_ERR(ent);
+ dev_err(chip->dev,
+ "Couldn't create cmd debug file rc = %d\n",
+ rc);
+ }
+
+ ent = debugfs_create_x32("address", S_IFREG | S_IWUSR | S_IRUGO,
+ chip->debug_root,
+ &(chip->peek_poke_address));
+ if (!ent || IS_ERR(ent)) {
+ rc = PTR_ERR(ent);
+ dev_err(chip->dev,
+ "Couldn't create address debug file rc = %d\n",
+ rc);
+ }
+
+ ent = debugfs_create_file("data", S_IFREG | S_IWUSR | S_IRUGO,
+ chip->debug_root, chip,
+ &poke_poke_debug_ops);
+ if (!ent || IS_ERR(ent)) {
+ rc = PTR_ERR(ent);
+ dev_err(chip->dev,
+ "Couldn't create data debug file rc = %d\n",
+ rc);
+ }
+
+ ent = debugfs_create_file("force_irq",
+ S_IFREG | S_IWUSR | S_IRUGO,
+ chip->debug_root, chip,
+ &force_irq_ops);
+ if (!ent || IS_ERR(ent)) {
+ rc = PTR_ERR(ent);
+ dev_err(chip->dev,
+ "Couldn't create force_irq debug file rc =%d\n",
+ rc);
+ }
+
+ ent = debugfs_create_file("irq_count", S_IFREG | S_IRUGO,
+ chip->debug_root, chip,
+ &irq_count_debugfs_ops);
+ if (!ent || IS_ERR(ent)) {
+ rc = PTR_ERR(ent);
+ dev_err(chip->dev,
+ "Couldn't create cnfg irq_count file rc = %d\n",
+ rc);
+ }
+ }
+}
+#else
+static void smb358_debugfs_init(struct smb358_charger *chip)
+{
+}
+#endif
+
+#define SMB_I2C_VTG_MIN_UV 1800000
+#define SMB_I2C_VTG_MAX_UV 1800000
+static int smb358_charger_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int rc, irq;
+ struct smb358_charger *chip;
+ struct power_supply *usb_psy;
+ u8 reg = 0;
+
+ usb_psy = power_supply_get_by_name("usb");
+ if (!usb_psy) {
+ dev_dbg(&client->dev, "USB psy not found; deferring probe\n");
+ return -EPROBE_DEFER;
+ }
+
+ chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
+ if (!chip) {
+ dev_err(&client->dev, "Couldn't allocate memory\n");
+ return -ENOMEM;
+ }
+
+ chip->client = client;
+ chip->dev = &client->dev;
+ chip->usb_psy = usb_psy;
+ chip->fake_battery_soc = -EINVAL;
+
+ /* early for VADC get, defer probe if needed */
+ chip->vadc_dev = qpnp_get_vadc(chip->dev, "chg");
+ if (IS_ERR(chip->vadc_dev)) {
+ rc = PTR_ERR(chip->vadc_dev);
+ if (rc != -EPROBE_DEFER)
+ pr_err("vadc property missing\n");
+ return rc;
+ }
+
+ /* i2c pull up Regulator configuration */
+ chip->vcc_i2c = regulator_get(&client->dev, "vcc-i2c");
+ if (IS_ERR(chip->vcc_i2c)) {
+ dev_err(&client->dev,
+ "%s: Failed to get vcc_i2c regulator\n",
+ __func__);
+ rc = PTR_ERR(chip->vcc_i2c);
+ goto err_get_vtg_i2c;
+ }
+
+ if (regulator_count_voltages(chip->vcc_i2c) > 0) {
+ rc = regulator_set_voltage(chip->vcc_i2c,
+ SMB_I2C_VTG_MIN_UV, SMB_I2C_VTG_MAX_UV);
+ if (rc) {
+ dev_err(&client->dev,
+ "regulator vcc_i2c set failed, rc = %d\n",
+ rc);
+ goto err_set_vtg_i2c;
+ }
+ }
+
+ rc = regulator_enable(chip->vcc_i2c);
+ if (rc) {
+ dev_err(&client->dev,
+ "Regulator vcc_i2c enable failed "
+ "rc=%d\n", rc);
+ goto err_set_vtg_i2c;
+ }
+
+ mutex_init(&chip->irq_complete);
+ mutex_init(&chip->read_write_lock);
+ mutex_init(&chip->path_suspend_lock);
+
+ /* probe the device to check if its actually connected */
+ rc = smb358_read_reg(chip, CHG_OTH_CURRENT_CTRL_REG, ®);
+ if (rc) {
+ pr_err("Failed to detect SMB358, device absent, rc = %d\n", rc);
+ goto err_set_vtg_i2c;
+ }
+
+ rc = smb_parse_dt(chip);
+ if (rc) {
+ dev_err(&client->dev, "Couldn't parse DT nodes rc=%d\n", rc);
+ goto err_set_vtg_i2c;
+ }
+
+ /* using adc_tm for implementing pmic therm */
+ if (chip->using_pmic_therm) {
+ chip->adc_tm_dev = qpnp_get_adc_tm(chip->dev, "chg");
+ if (IS_ERR(chip->adc_tm_dev)) {
+ rc = PTR_ERR(chip->adc_tm_dev);
+ if (rc != -EPROBE_DEFER)
+ pr_err("adc_tm property missing\n");
+ return rc;
+ }
+ }
+
+ i2c_set_clientdata(client, chip);
+
+ chip->batt_psy.name = "battery";
+ chip->batt_psy.type = POWER_SUPPLY_TYPE_BATTERY;
+ chip->batt_psy.get_property = smb358_battery_get_property;
+ chip->batt_psy.set_property = smb358_battery_set_property;
+ chip->batt_psy.property_is_writeable =
+ smb358_batt_property_is_writeable;
+ chip->batt_psy.properties = smb358_battery_properties;
+ chip->batt_psy.num_properties = ARRAY_SIZE(smb358_battery_properties);
+ chip->batt_psy.external_power_changed = smb358_external_power_changed;
+ chip->batt_psy.supplied_to = pm_batt_supplied_to;
+ chip->batt_psy.num_supplicants = ARRAY_SIZE(pm_batt_supplied_to);
+
+ chip->resume_completed = true;
+
+ rc = power_supply_register(chip->dev, &chip->batt_psy);
+ if (rc < 0) {
+ dev_err(&client->dev, "Couldn't register batt psy rc = %d\n",
+ rc);
+ goto err_set_vtg_i2c;
+ }
+
+ dump_regs(chip);
+
+ rc = smb358_regulator_init(chip);
+ if (rc) {
+ dev_err(&client->dev,
+ "Couldn't initialize smb358 ragulator rc=%d\n", rc);
+ goto err_set_vtg_i2c;
+ }
+
+ rc = smb358_hw_init(chip);
+ if (rc) {
+ dev_err(&client->dev,
+ "Couldn't intialize hardware rc=%d\n", rc);
+ goto fail_smb358_hw_init;
+ }
+
+ rc = determine_initial_state(chip);
+ if (rc) {
+ dev_err(&client->dev,
+ "Couldn't determine initial state rc=%d\n", rc);
+ goto fail_smb358_hw_init;
+ }
+
+ /* We will not use it by default */
+ if (gpio_is_valid(chip->chg_valid_gpio)) {
+ rc = gpio_request(chip->chg_valid_gpio, "smb358_chg_valid");
+ if (rc) {
+ dev_err(&client->dev,
+ "gpio_request for %d failed rc=%d\n",
+ chip->chg_valid_gpio, rc);
+ goto fail_chg_valid_irq;
+ }
+ irq = gpio_to_irq(chip->chg_valid_gpio);
+ if (irq < 0) {
+ dev_err(&client->dev,
+ "Invalid chg_valid irq = %d\n", irq);
+ goto fail_chg_valid_irq;
+ }
+ rc = devm_request_threaded_irq(&client->dev, irq,
+ NULL, smb358_chg_valid_handler,
+ IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
+ "smb358_chg_valid_irq", chip);
+ if (rc) {
+ dev_err(&client->dev,
+ "Failed request_irq irq=%d, gpio=%d rc=%d\n",
+ irq, chip->chg_valid_gpio, rc);
+ goto fail_chg_valid_irq;
+ }
+ smb358_chg_valid_handler(irq, chip);
+ enable_irq_wake(irq);
+ }
+
+ chip->irq_gpio = of_get_named_gpio_flags(chip->dev->of_node,
+ "qcom,irq-gpio", 0, NULL);
+
+ /* STAT irq configuration */
+ if (gpio_is_valid(chip->irq_gpio)) {
+ rc = gpio_request(chip->irq_gpio, "smb358_irq");
+ if (rc) {
+ dev_err(&client->dev,
+ "irq gpio request failed, rc=%d", rc);
+ goto fail_smb358_hw_init;
+ }
+ rc = gpio_direction_input(chip->irq_gpio);
+ if (rc) {
+ dev_err(&client->dev,
+ "set_direction for irq gpio failed\n");
+ goto fail_irq_gpio;
+ }
+
+ irq = gpio_to_irq(chip->irq_gpio);
+ if (irq < 0) {
+ dev_err(&client->dev,
+ "Invalid irq_gpio irq = %d\n", irq);
+ goto fail_irq_gpio;
+ }
+ rc = devm_request_threaded_irq(&client->dev, irq, NULL,
+ smb358_chg_stat_handler,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ "smb358_chg_stat_irq", chip);
+ if (rc) {
+ dev_err(&client->dev,
+ "Failed STAT irq=%d request rc = %d\n",
+ irq, rc);
+ goto fail_irq_gpio;
+ }
+ enable_irq_wake(irq);
+ } else {
+ goto fail_irq_gpio;
+ }
+
+ if (chip->using_pmic_therm) {
+ /* add hot/cold temperature monitor */
+ chip->adc_param.low_temp = chip->cold_bat_decidegc;
+ chip->adc_param.high_temp = chip->hot_bat_decidegc;
+ chip->adc_param.timer_interval = ADC_MEAS2_INTERVAL_1S;
+ chip->adc_param.state_request = ADC_TM_HIGH_LOW_THR_ENABLE;
+ chip->adc_param.btm_ctx = chip;
+ chip->adc_param.threshold_notification =
+ smb_chg_adc_notification;
+ chip->adc_param.channel = LR_MUX1_BATT_THERM;
+
+ /* update battery missing info in tm_channel_measure*/
+ rc = qpnp_adc_tm_channel_measure(chip->adc_tm_dev,
+ &chip->adc_param);
+ if (rc)
+ pr_err("requesting ADC error %d\n", rc);
+ }
+
+ smb358_debugfs_init(chip);
+
+ dump_regs(chip);
+
+ dev_info(chip->dev, "SMB358 successfully probed. charger=%d, batt=%d\n",
+ chip->chg_present, smb358_get_prop_batt_present(chip));
+ return 0;
+
+fail_chg_valid_irq:
+ if (gpio_is_valid(chip->chg_valid_gpio))
+ gpio_free(chip->chg_valid_gpio);
+fail_irq_gpio:
+ if (gpio_is_valid(chip->irq_gpio))
+ gpio_free(chip->irq_gpio);
+fail_smb358_hw_init:
+ power_supply_unregister(&chip->batt_psy);
+ regulator_unregister(chip->otg_vreg.rdev);
+err_set_vtg_i2c:
+ if (regulator_count_voltages(chip->vcc_i2c) > 0)
+ regulator_set_voltage(chip->vcc_i2c, 0, SMB_I2C_VTG_MAX_UV);
+err_get_vtg_i2c:
+ regulator_put(chip->vcc_i2c);
+ return rc;
+}
+
+static int smb358_charger_remove(struct i2c_client *client)
+{
+ struct smb358_charger *chip = i2c_get_clientdata(client);
+
+ power_supply_unregister(&chip->batt_psy);
+ if (gpio_is_valid(chip->chg_valid_gpio))
+ gpio_free(chip->chg_valid_gpio);
+
+ regulator_disable(chip->vcc_i2c);
+ regulator_put(chip->vcc_i2c);
+ mutex_destroy(&chip->irq_complete);
+ debugfs_remove_recursive(chip->debug_root);
+ return 0;
+}
+
+static int smb358_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct smb358_charger *chip = i2c_get_clientdata(client);
+ int rc;
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ rc = smb358_read_reg(chip, FAULT_INT_REG + i,
+ &chip->irq_cfg_mask[i]);
+ if (rc)
+ dev_err(chip->dev,
+ "Couldn't save irq cfg regs rc = %d\n", rc);
+ }
+
+ /* enable wake up IRQs */
+ rc = smb358_write_reg(chip, FAULT_INT_REG,
+ FAULT_INT_HOT_COLD_HARD_BIT | FAULT_INT_INPUT_UV_BIT);
+ if (rc < 0)
+ dev_err(chip->dev, "Couldn't set fault_irq_cfg rc = %d\n", rc);
+
+ rc = smb358_write_reg(chip, STATUS_INT_REG,
+ STATUS_INT_LOW_BATT_BIT | STATUS_INT_MISSING_BATT_BIT |
+ STATUS_INT_CHGING_BIT | STATUS_INT_INOK_BIT |
+ STATUS_INT_OTG_DETECT_BIT | STATUS_INT_CHG_INHI_BIT);
+ if (rc < 0)
+ dev_err(chip->dev,
+ "Couldn't set status_irq_cfg rc = %d\n", rc);
+
+ mutex_lock(&chip->irq_complete);
+ rc = regulator_disable(chip->vcc_i2c);
+ if (rc) {
+ dev_err(chip->dev,
+ "Regulator vcc_i2c disable failed rc=%d\n", rc);
+ mutex_unlock(&chip->irq_complete);
+ return rc;
+ }
+
+ chip->resume_completed = false;
+ mutex_unlock(&chip->irq_complete);
+ return 0;
+}
+
+static int smb358_suspend_noirq(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct smb358_charger *chip = i2c_get_clientdata(client);
+
+ if (chip->irq_waiting) {
+ pr_err_ratelimited("Aborting suspend, an interrupt was detected while suspending\n");
+ return -EBUSY;
+ }
+ return 0;
+}
+
+static int smb358_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct smb358_charger *chip = i2c_get_clientdata(client);
+ int rc;
+ int i;
+
+ /* Restore IRQ config */
+ for (i = 0; i < 2; i++) {
+ rc = smb358_write_reg(chip, FAULT_INT_REG + i,
+ chip->irq_cfg_mask[i]);
+ if (rc)
+ dev_err(chip->dev,
+ "Couldn't restore irq cfg regs rc=%d\n", rc);
+ }
+
+ mutex_lock(&chip->irq_complete);
+ rc = regulator_enable(chip->vcc_i2c);
+ if (rc) {
+ dev_err(chip->dev,
+ "Regulator vcc_i2c enable failed rc=%d\n", rc);
+ mutex_unlock(&chip->irq_complete);
+ return rc;
+ }
+ chip->resume_completed = true;
+
+ mutex_unlock(&chip->irq_complete);
+ if (chip->irq_waiting) {
+ smb358_chg_stat_handler(client->irq, chip);
+ enable_irq(client->irq);
+ }
+ return 0;
+}
+
+static const struct dev_pm_ops smb358_pm_ops = {
+ .suspend = smb358_suspend,
+ .suspend_noirq = smb358_suspend_noirq,
+ .resume = smb358_resume,
+};
+
+static struct of_device_id smb358_match_table[] = {
+ { .compatible = "qcom,smb358-charger",},
+ { },
+};
+
+static const struct i2c_device_id smb358_charger_id[] = {
+ {"smb358-charger", 0},
+ {},
+};
+MODULE_DEVICE_TABLE(i2c, smb358_charger_id);
+
+static struct i2c_driver smb358_charger_driver = {
+ .driver = {
+ .name = "smb358-charger",
+ .owner = THIS_MODULE,
+ .of_match_table = smb358_match_table,
+ .pm = &smb358_pm_ops,
+ },
+ .probe = smb358_charger_probe,
+ .remove = smb358_charger_remove,
+ .id_table = smb358_charger_id,
+};
+
+module_i2c_driver(smb358_charger_driver);
+
+MODULE_DESCRIPTION("SMB358 Charger");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("i2c:smb358-charger");
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index e9994c2..f721a25 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -140,6 +140,15 @@
Say y here to support the regulators found on the Freescale MC13892
PMIC.
+config REGULATOR_MEM_ACC
+ tristate "QTI Memory accelerator regulator driver"
+ help
+ Say y here to enable the memory accelerator driver for Qualcomm
+ Technologies (QTI) chips. The accelerator controls delays applied
+ for memory accesses.
+ This driver configures the power-mode (corner) for the memory
+ accelerator.
+
config REGULATOR_ISL6271A
tristate "Intersil ISL6271A Power regulator"
depends on I2C
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index d96e793..1393f23 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -34,6 +34,7 @@
obj-$(CONFIG_REGULATOR_MC13783) += mc13783-regulator.o
obj-$(CONFIG_REGULATOR_MC13892) += mc13892-regulator.o
obj-$(CONFIG_REGULATOR_MC13XXX_CORE) += mc13xxx-regulator-core.o
+obj-$(CONFIG_REGULATOR_MEM_ACC) += mem-acc-regulator.o
obj-$(CONFIG_REGULATOR_ONSEMI_NCP6335D) += onsemi-ncp6335d.o
obj-$(CONFIG_REGULATOR_PCAP) += pcap-regulator.o
obj-$(CONFIG_REGULATOR_PCF50633) += pcf50633-regulator.o
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index 450c4fb..047bbc4 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -1989,8 +1989,8 @@
int regulator_set_voltage(struct regulator *regulator, int min_uV, int max_uV)
{
struct regulator_dev *rdev = regulator->rdev;
- int prev_min_uV, prev_max_uV;
int ret = 0;
+ int old_min_uV, old_max_uV;
mutex_lock(&rdev->mutex);
@@ -2013,24 +2013,28 @@
if (ret < 0)
goto out;
- prev_min_uV = regulator->min_uV;
- prev_max_uV = regulator->max_uV;
-
+ /* restore original values in case of error */
+ old_min_uV = regulator->min_uV;
+ old_max_uV = regulator->max_uV;
regulator->min_uV = min_uV;
regulator->max_uV = max_uV;
ret = regulator_check_consumers(rdev, &min_uV, &max_uV);
- if (ret < 0) {
- regulator->min_uV = prev_min_uV;
- regulator->max_uV = prev_max_uV;
- goto out;
- }
+ if (ret < 0)
+ goto out2;
ret = _regulator_do_set_voltage(rdev, min_uV, max_uV);
+ if (ret < 0)
+ goto out2;
out:
mutex_unlock(&rdev->mutex);
return ret;
+out2:
+ regulator->min_uV = old_min_uV;
+ regulator->max_uV = old_max_uV;
+ mutex_unlock(&rdev->mutex);
+ return ret;
}
EXPORT_SYMBOL_GPL(regulator_set_voltage);
diff --git a/drivers/regulator/mem-acc-regulator.c b/drivers/regulator/mem-acc-regulator.c
new file mode 100644
index 0000000..b66285a
--- /dev/null
+++ b/drivers/regulator/mem-acc-regulator.c
@@ -0,0 +1,461 @@
+/* Copyright (c) 2014, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#define pr_fmt(fmt) "ACC: %s: " fmt, __func__
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+
+#define MEM_ACC_SEL_MASK 0x3
+
+enum {
+ MEMORY_L1,
+ MEMORY_L2,
+ MEMORY_MAX,
+};
+
+struct mem_acc_regulator {
+ struct device *dev;
+ struct regulator_desc rdesc;
+ struct regulator_dev *rdev;
+
+ int corner;
+ bool mem_acc_supported[MEMORY_MAX];
+
+ u32 acc_sel_reg[MEMORY_MAX];
+ u32 *acc_sel_mask[MEMORY_MAX];
+ u32 *acc_sel_bit_pos[MEMORY_MAX];
+ u32 num_acc_sel[MEMORY_MAX];
+ u32 *acc_en_bit_pos;
+ u32 num_acc_en;
+ u32 *corner_acc_map;
+ u32 num_corners;
+
+ void __iomem *acc_sel_base[MEMORY_MAX];
+ void __iomem *acc_en_base;
+ phys_addr_t acc_sel_addr[MEMORY_MAX];
+ phys_addr_t acc_en_addr;
+};
+
+static inline u32 apc_to_acc_corner(struct mem_acc_regulator *mem_acc_vreg,
+ int corner)
+{
+ /*
+ * corner_acc_map maps the corner from index 0 and APC corner value
+ * starts from the value 1
+ */
+ return mem_acc_vreg->corner_acc_map[corner - 1];
+}
+
+static void __update_acc_sel(struct mem_acc_regulator *mem_acc_vreg,
+ int corner, int mem_type)
+{
+ u32 acc_data, i, bit, acc_corner;
+
+ acc_data = mem_acc_vreg->acc_sel_reg[mem_type];
+ for (i = 0; i < mem_acc_vreg->num_acc_sel[mem_type]; i++) {
+ bit = mem_acc_vreg->acc_sel_bit_pos[mem_type][i];
+ acc_data &= ~mem_acc_vreg->acc_sel_mask[mem_type][i];
+ acc_corner = apc_to_acc_corner(mem_acc_vreg, corner);
+ acc_data |= (acc_corner << bit) &
+ mem_acc_vreg->acc_sel_mask[mem_type][i];
+ }
+ pr_debug("corner=%d old_acc_sel=0x%02x new_acc_sel=0x%02x mem_type=%d\n",
+ corner, mem_acc_vreg->acc_sel_reg[mem_type],
+ acc_data, mem_type);
+ writel_relaxed(acc_data, mem_acc_vreg->acc_sel_base[mem_type]);
+ mem_acc_vreg->acc_sel_reg[mem_type] = acc_data;
+}
+
+static void update_acc_sel(struct mem_acc_regulator *mem_acc_vreg, int corner)
+{
+ int i;
+
+ for (i = 0; i < MEMORY_MAX; i++) {
+ if (mem_acc_vreg->mem_acc_supported[i])
+ __update_acc_sel(mem_acc_vreg, corner, i);
+ }
+}
+
+static int mem_acc_regulator_set_voltage(struct regulator_dev *rdev,
+ int corner, int corner_max, unsigned *selector)
+{
+ struct mem_acc_regulator *mem_acc_vreg = rdev_get_drvdata(rdev);
+ int i;
+
+ if (corner > mem_acc_vreg->num_corners) {
+ pr_err("Invalid corner=%d requested\n", corner);
+ return -EINVAL;
+ }
+
+ pr_debug("old corner=%d, new corner=%d\n",
+ mem_acc_vreg->corner, corner);
+
+ if (corner == mem_acc_vreg->corner)
+ return 0;
+
+ /* go up or down one level at a time */
+ if (corner > mem_acc_vreg->corner) {
+ for (i = mem_acc_vreg->corner + 1; i <= corner; i++) {
+ pr_debug("UP: to corner %d\n", i);
+ update_acc_sel(mem_acc_vreg, i);
+ }
+ } else {
+ for (i = mem_acc_vreg->corner - 1; i >= corner; i--) {
+ pr_debug("DOWN: to corner %d\n", i);
+ update_acc_sel(mem_acc_vreg, i);
+ }
+ }
+
+ pr_debug("new voltage corner set %d\n", corner);
+
+ mem_acc_vreg->corner = corner;
+
+ return 0;
+}
+
+static int mem_acc_regulator_get_voltage(struct regulator_dev *rdev)
+{
+ struct mem_acc_regulator *mem_acc_vreg = rdev_get_drvdata(rdev);
+
+ return mem_acc_vreg->corner;
+}
+
+static struct regulator_ops mem_acc_corner_ops = {
+ .set_voltage = mem_acc_regulator_set_voltage,
+ .get_voltage = mem_acc_regulator_get_voltage,
+};
+
+static int __mem_acc_sel_init(struct mem_acc_regulator *mem_acc_vreg,
+ int mem_type)
+{
+ int i;
+ u32 bit;
+
+ mem_acc_vreg->acc_sel_mask[mem_type] = devm_kzalloc(mem_acc_vreg->dev,
+ mem_acc_vreg->num_acc_sel[mem_type] * sizeof(u32), GFP_KERNEL);
+ if (!mem_acc_vreg->acc_sel_mask[mem_type]) {
+ pr_err("Unable to allocate memory for mem_type=%d\n", mem_type);
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < mem_acc_vreg->num_acc_sel[mem_type]; i++) {
+ bit = mem_acc_vreg->acc_sel_bit_pos[mem_type][i];
+ mem_acc_vreg->acc_sel_mask[mem_type][i] =
+ MEM_ACC_SEL_MASK << bit;
+ }
+
+ mem_acc_vreg->acc_sel_reg[mem_type] =
+ readl_relaxed(mem_acc_vreg->acc_sel_base[mem_type]);
+
+ return 0;
+}
+
+static int mem_acc_sel_init(struct mem_acc_regulator *mem_acc_vreg)
+{
+ int i, rc;
+
+ for (i = 0; i < MEMORY_MAX; i++) {
+ if (mem_acc_vreg->mem_acc_supported[i]) {
+ rc = __mem_acc_sel_init(mem_acc_vreg, i);
+ if (rc) {
+ pr_err("Unable to intialize mem_type=%d rc=%d\n",
+ i, rc);
+ return rc;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static void mem_acc_en_init(struct mem_acc_regulator *mem_acc_vreg)
+{
+ int i, bit;
+ u32 acc_data;
+
+ acc_data = readl_relaxed(mem_acc_vreg->acc_en_base);
+ pr_debug("init: acc_en_register=%x\n", acc_data);
+ for (i = 0; i < mem_acc_vreg->num_acc_en; i++) {
+ bit = mem_acc_vreg->acc_en_bit_pos[i];
+ acc_data |= BIT(bit);
+ }
+ pr_debug("final: acc_en_register=%x\n", acc_data);
+ writel_relaxed(acc_data, mem_acc_vreg->acc_en_base);
+}
+
+static int populate_acc_data(struct mem_acc_regulator *mem_acc_vreg,
+ const char *prop_name, u32 **value, u32 *len)
+{
+ int rc;
+
+ if (!of_get_property(mem_acc_vreg->dev->of_node, prop_name, len)) {
+ pr_err("Unable to find %s property\n", prop_name);
+ return -EINVAL;
+ }
+ *len /= sizeof(u32);
+ if (!(*len)) {
+ pr_err("Incorrect entries in %s\n", prop_name);
+ return -EINVAL;
+ }
+
+ *value = devm_kzalloc(mem_acc_vreg->dev, (*len) * sizeof(u32),
+ GFP_KERNEL);
+ if (!(*value)) {
+ pr_err("Unable to allocate memory for %s\n", prop_name);
+ return -ENOMEM;
+ }
+
+ pr_debug("Found %s, data-length = %d\n", prop_name, *len);
+
+ rc = of_property_read_u32_array(mem_acc_vreg->dev->of_node,
+ prop_name, *value, *len);
+ if (rc) {
+ pr_err("Unable to populate %s rc=%d\n", prop_name, rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+static int mem_acc_sel_setup(struct mem_acc_regulator *mem_acc_vreg,
+ struct resource *res, int mem_type)
+{
+ int len, rc;
+ char *mem_select_str;
+
+ mem_acc_vreg->acc_sel_addr[mem_type] = res->start;
+ len = res->end - res->start + 1;
+ pr_debug("'acc_sel_addr' = %pa mem_type=%d (len=%d)\n",
+ &res->start, mem_type, len);
+
+ mem_acc_vreg->acc_sel_base[mem_type] = devm_ioremap(mem_acc_vreg->dev,
+ mem_acc_vreg->acc_sel_addr[mem_type], len);
+ if (!mem_acc_vreg->acc_sel_base[mem_type]) {
+ pr_err("Unable to map 'acc_sel_addr' %pa for mem_type=%d\n",
+ &mem_acc_vreg->acc_sel_addr[mem_type], mem_type);
+ return -EINVAL;
+ }
+
+ switch (mem_type) {
+ case MEMORY_L1:
+ mem_select_str = "qcom,acc-sel-l1-bit-pos";
+ break;
+ case MEMORY_L2:
+ mem_select_str = "qcom,acc-sel-l2-bit-pos";
+ break;
+ }
+
+ rc = populate_acc_data(mem_acc_vreg, mem_select_str,
+ &mem_acc_vreg->acc_sel_bit_pos[mem_type],
+ &mem_acc_vreg->num_acc_sel[mem_type]);
+ if (rc)
+ pr_err("Unable to populate '%s' rc=%d\n", mem_select_str, rc);
+
+ return rc;
+}
+
+static int mem_acc_init(struct platform_device *pdev,
+ struct mem_acc_regulator *mem_acc_vreg)
+{
+ struct resource *res;
+ int len, rc, i;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "acc-en");
+ if (!res || !res->start) {
+ pr_debug("'acc-en' resource missing or not used.\n");
+ } else {
+ mem_acc_vreg->acc_en_addr = res->start;
+ len = res->end - res->start + 1;
+ pr_debug("'acc_en_addr' = %pa (len=0x%x)\n", &res->start, len);
+
+ mem_acc_vreg->acc_en_base = devm_ioremap(mem_acc_vreg->dev,
+ mem_acc_vreg->acc_en_addr, len);
+ if (!mem_acc_vreg->acc_en_base) {
+ pr_err("Unable to map 'acc_en_addr' %pa\n",
+ &mem_acc_vreg->acc_en_addr);
+ return -EINVAL;
+ }
+
+ rc = populate_acc_data(mem_acc_vreg, "qcom,acc-en-bit-pos",
+ &mem_acc_vreg->acc_en_bit_pos,
+ &mem_acc_vreg->num_acc_en);
+ if (rc) {
+ pr_err("Unable to populate 'qcom,acc-en-bit-pos' rc=%d\n",
+ rc);
+ return rc;
+ }
+ }
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "acc-sel-l1");
+ if (!res || !res->start) {
+ pr_debug("'acc-sel-l1' resource missing or not used.\n");
+ } else {
+ rc = mem_acc_sel_setup(mem_acc_vreg, res, MEMORY_L1);
+ if (rc) {
+ pr_err("Unable to setup mem-acc for mem_type=%d rc=%d\n",
+ MEMORY_L1, rc);
+ return rc;
+ }
+ mem_acc_vreg->mem_acc_supported[MEMORY_L1] = true;
+ }
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "acc-sel-l2");
+ if (!res || !res->start) {
+ pr_debug("'acc-sel-l2' resource missing or not used.\n");
+ } else {
+ rc = mem_acc_sel_setup(mem_acc_vreg, res, MEMORY_L2);
+ if (rc) {
+ pr_err("Unable to setup mem-acc for mem_type=%d rc=%d\n",
+ MEMORY_L2, rc);
+ return rc;
+ }
+ mem_acc_vreg->mem_acc_supported[MEMORY_L2] = true;
+ }
+
+ rc = populate_acc_data(mem_acc_vreg, "qcom,corner-acc-map",
+ &mem_acc_vreg->corner_acc_map,
+ &mem_acc_vreg->num_corners);
+ if (rc) {
+ pr_err("Unable to find 'qcom,corner-acc-map' rc=%d\n", rc);
+ return rc;
+ }
+
+ pr_debug("num_corners = %d\n", mem_acc_vreg->num_corners);
+
+ /* Check if at least one valid mem-acc config. is specified */
+ for (i = 0; i < MEMORY_MAX; i++) {
+ if (mem_acc_vreg->mem_acc_supported[i])
+ break;
+ }
+ if (i == MEMORY_MAX) {
+ pr_err("No mem-acc configuration specified\n");
+ return -EINVAL;
+ }
+
+ if (mem_acc_vreg->num_acc_en)
+ mem_acc_en_init(mem_acc_vreg);
+
+ rc = mem_acc_sel_init(mem_acc_vreg);
+ if (rc) {
+ pr_err("Unable to intialize mem_acc_sel reg rc=%d\n", rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+static int mem_acc_regulator_probe(struct platform_device *pdev)
+{
+ struct mem_acc_regulator *mem_acc_vreg;
+ struct regulator_desc *rdesc;
+ struct regulator_init_data *init_data;
+ int rc;
+
+ if (!pdev->dev.of_node) {
+ pr_err("Device tree node is missing\n");
+ return -EINVAL;
+ }
+
+ init_data = of_get_regulator_init_data(&pdev->dev, pdev->dev.of_node);
+ if (!init_data) {
+ pr_err("regulator init data is missing\n");
+ return -EINVAL;
+ } else {
+ init_data->constraints.input_uV
+ = init_data->constraints.max_uV;
+ init_data->constraints.valid_ops_mask
+ |= REGULATOR_CHANGE_VOLTAGE;
+ }
+
+ mem_acc_vreg = devm_kzalloc(&pdev->dev, sizeof(*mem_acc_vreg),
+ GFP_KERNEL);
+ if (!mem_acc_vreg) {
+ pr_err("Can't allocate mem_acc_vreg memory\n");
+ return -ENOMEM;
+ }
+ mem_acc_vreg->dev = &pdev->dev;
+
+ rc = mem_acc_init(pdev, mem_acc_vreg);
+ if (rc) {
+ pr_err("Unable to initialize mem_acc configuration rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ rdesc = &mem_acc_vreg->rdesc;
+ rdesc->owner = THIS_MODULE;
+ rdesc->type = REGULATOR_VOLTAGE;
+ rdesc->ops = &mem_acc_corner_ops;
+ rdesc->name = init_data->constraints.name;
+
+ mem_acc_vreg->rdev = regulator_register(rdesc, &pdev->dev,
+ init_data, mem_acc_vreg, pdev->dev.of_node);
+ if (IS_ERR(mem_acc_vreg->rdev)) {
+ rc = PTR_ERR(mem_acc_vreg->rdev);
+ if (rc != -EPROBE_DEFER)
+ pr_err("regulator_register failed: rc=%d\n", rc);
+ return rc;
+ }
+
+ platform_set_drvdata(pdev, mem_acc_vreg);
+
+ return 0;
+}
+
+static int mem_acc_regulator_remove(struct platform_device *pdev)
+{
+ struct mem_acc_regulator *mem_acc_vreg = platform_get_drvdata(pdev);
+
+ regulator_unregister(mem_acc_vreg->rdev);
+
+ return 0;
+}
+
+static struct of_device_id mem_acc_regulator_match_table[] = {
+ { .compatible = "qcom,mem-acc-regulator", },
+ {}
+};
+
+static struct platform_driver mem_acc_regulator_driver = {
+ .probe = mem_acc_regulator_probe,
+ .remove = mem_acc_regulator_remove,
+ .driver = {
+ .name = "qcom,mem-acc-regulator",
+ .of_match_table = mem_acc_regulator_match_table,
+ .owner = THIS_MODULE,
+ },
+};
+
+int __init mem_acc_regulator_init(void)
+{
+ return platform_driver_register(&mem_acc_regulator_driver);
+}
+postcore_initcall(mem_acc_regulator_init);
+
+static void __exit mem_acc_regulator_exit(void)
+{
+ platform_driver_unregister(&mem_acc_regulator_driver);
+}
+module_exit(mem_acc_regulator_exit);
+
+MODULE_DESCRIPTION("MEM-ACC-SEL regulator driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/rtc/alarm-dev.c b/drivers/rtc/alarm-dev.c
index 4682e9c..b621547 100644
--- a/drivers/rtc/alarm-dev.c
+++ b/drivers/rtc/alarm-dev.c
@@ -49,6 +49,7 @@
#define ANDROID_ALARM_SET_AND_WAIT_OLD _IOW('a', 3, time_t)
static int alarm_opened;
+static DEFINE_MUTEX(alarm_mutex);
static DEFINE_SPINLOCK(alarm_slock);
static struct wake_lock alarm_wake_lock;
static DECLARE_WAIT_QUEUE_HEAD(alarm_wait_queue);
@@ -89,6 +90,7 @@
switch (ANDROID_ALARM_BASE_CMD(cmd)) {
case ANDROID_ALARM_CLEAR(0):
+ mutex_lock(&alarm_mutex);
spin_lock_irqsave(&alarm_slock, flags);
pr_alarm(IO, "alarm %d clear\n", alarm_type);
alarm_try_to_cancel(&alarms[alarm_type]);
@@ -98,9 +100,12 @@
wake_unlock(&alarm_wake_lock);
}
alarm_enabled &= ~alarm_type_mask;
- if (alarm_type == ANDROID_ALARM_RTC_POWEROFF_WAKEUP)
- set_power_on_alarm(0);
spin_unlock_irqrestore(&alarm_slock, flags);
+ if (alarm_type == ANDROID_ALARM_RTC_POWEROFF_WAKEUP)
+ if (!copy_from_user(&new_alarm_time,
+ (void __user *)arg, sizeof(new_alarm_time)))
+ set_power_on_alarm(new_alarm_time.tv_sec, 0);
+ mutex_unlock(&alarm_mutex);
break;
case ANDROID_ALARM_SET_OLD:
@@ -120,6 +125,7 @@
goto err1;
}
from_old_alarm_set:
+ mutex_lock(&alarm_mutex);
spin_lock_irqsave(&alarm_slock, flags);
pr_alarm(IO, "alarm %d set %ld.%09ld\n", alarm_type,
new_alarm_time.tv_sec, new_alarm_time.tv_nsec);
@@ -127,11 +133,12 @@
alarm_start_range(&alarms[alarm_type],
timespec_to_ktime(new_alarm_time),
timespec_to_ktime(new_alarm_time));
+ spin_unlock_irqrestore(&alarm_slock, flags);
if ((alarm_type == ANDROID_ALARM_RTC_POWEROFF_WAKEUP) &&
(ANDROID_ALARM_BASE_CMD(cmd) ==
ANDROID_ALARM_SET(0)))
- set_power_on_alarm(new_alarm_time.tv_sec);
- spin_unlock_irqrestore(&alarm_slock, flags);
+ set_power_on_alarm(new_alarm_time.tv_sec, 1);
+ mutex_unlock(&alarm_mutex);
if (ANDROID_ALARM_BASE_CMD(cmd) != ANDROID_ALARM_SET_AND_WAIT(0)
&& cmd != ANDROID_ALARM_SET_AND_WAIT_OLD)
break;
diff --git a/drivers/rtc/alarm.c b/drivers/rtc/alarm.c
index 06749b9..8531de9 100644
--- a/drivers/rtc/alarm.c
+++ b/drivers/rtc/alarm.c
@@ -65,15 +65,32 @@
static struct rtc_device *alarm_rtc_dev;
static DEFINE_SPINLOCK(alarm_slock);
static DEFINE_MUTEX(alarm_setrtc_mutex);
+static DEFINE_MUTEX(power_on_alarm_mutex);
static struct wake_lock alarm_rtc_wake_lock;
static struct platform_device *alarm_platform_dev;
struct alarm_queue alarms[ANDROID_ALARM_TYPE_COUNT];
static bool suspended;
static long power_on_alarm;
-void set_power_on_alarm(long secs)
+static int set_alarm_time_to_rtc(const long);
+
+void set_power_on_alarm(long secs, bool enable)
{
- power_on_alarm = secs;
+ mutex_lock(&power_on_alarm_mutex);
+ if (enable) {
+ power_on_alarm = secs;
+ } else {
+ if (power_on_alarm && power_on_alarm != secs) {
+ pr_alarm(FLOW, "power-off alarm mismatch: \
+ previous=%ld, now=%ld\n",
+ power_on_alarm, secs);
+ }
+ else
+ power_on_alarm = 0;
+ }
+
+ set_alarm_time_to_rtc(power_on_alarm);
+ mutex_unlock(&power_on_alarm_mutex);
}
@@ -509,28 +526,23 @@
return 0;
}
-static void alarm_shutdown(struct platform_device *dev)
+static int set_alarm_time_to_rtc(const long power_on_time)
{
struct timespec wall_time;
struct rtc_time rtc_time;
struct rtc_wkalrm alarm;
- unsigned long flags;
long rtc_secs, alarm_delta, alarm_time;
- int rc;
+ int rc = -EINVAL;
- spin_lock_irqsave(&alarm_slock, flags);
-
- if (!power_on_alarm) {
- spin_unlock_irqrestore(&alarm_slock, flags);
+ if (power_on_time <= 0) {
goto disable_alarm;
}
- spin_unlock_irqrestore(&alarm_slock, flags);
rtc_read_time(alarm_rtc_dev, &rtc_time);
getnstimeofday(&wall_time);
rtc_tm_to_time(&rtc_time, &rtc_secs);
alarm_delta = wall_time.tv_sec - rtc_secs;
- alarm_time = power_on_alarm - alarm_delta;
+ alarm_time = power_on_time - alarm_delta;
/*
* Substract ALARM_DELTA from actual alarm time
@@ -546,16 +558,19 @@
rtc_time_to_tm(alarm_time, &alarm.time);
alarm.enabled = 1;
rc = rtc_set_alarm(alarm_rtc_dev, &alarm);
- if (rc)
+ if (rc){
pr_alarm(ERROR, "Unable to set power-on alarm\n");
+ goto disable_alarm;
+ }
else
pr_alarm(FLOW, "Power-on alarm set to %lu\n",
alarm_time);
- return;
+ return 0;
disable_alarm:
rtc_alarm_irq_enable(alarm_rtc_dev, 0);
+ return rc;
}
static struct rtc_task alarm_rtc_task = {
@@ -617,7 +632,6 @@
static struct platform_driver alarm_driver = {
.suspend = alarm_suspend,
.resume = alarm_resume,
- .shutdown = alarm_shutdown,
.driver = {
.name = "alarm"
}
diff --git a/drivers/rtc/qpnp-rtc.c b/drivers/rtc/qpnp-rtc.c
index d64b577..e0d5c1e 100644
--- a/drivers/rtc/qpnp-rtc.c
+++ b/drivers/rtc/qpnp-rtc.c
@@ -386,6 +386,7 @@
unsigned long irq_flags;
struct qpnp_rtc *rtc_dd = dev_get_drvdata(dev);
u8 ctrl_reg;
+ u8 value[4] = {0};
spin_lock_irqsave(&rtc_dd->alarm_ctrl_lock, irq_flags);
ctrl_reg = rtc_dd->alarm_ctrl_reg1;
@@ -401,6 +402,15 @@
rtc_dd->alarm_ctrl_reg1 = ctrl_reg;
+ /* Clear Alarm register */
+ if (!enabled) {
+ rc = qpnp_write_wrapper(rtc_dd, value,
+ rtc_dd->alarm_base + REG_OFFSET_ALARM_RW,
+ NUM_8_BIT_RTC_REGS);
+ if (rc)
+ dev_err(dev, "Clear ALARM value reg failed\n");
+ }
+
rtc_rw_fail:
spin_unlock_irqrestore(&rtc_dd->alarm_ctrl_lock, irq_flags);
return rc;
@@ -553,8 +563,15 @@
goto fail_rtc_enable;
}
+ rc = qpnp_read_wrapper(rtc_dd, &rtc_dd->alarm_ctrl_reg1,
+ rtc_dd->alarm_base + REG_OFFSET_ALARM_CTRL1, 1);
+ if (rc) {
+ dev_err(&spmi->dev,
+ "Read from Alarm control reg failed\n");
+ goto fail_rtc_enable;
+ }
/* Enable abort enable feature */
- rtc_dd->alarm_ctrl_reg1 = BIT_RTC_ABORT_ENABLE;
+ rtc_dd->alarm_ctrl_reg1 |= BIT_RTC_ABORT_ENABLE;
rc = qpnp_write_wrapper(rtc_dd, &rtc_dd->alarm_ctrl_reg1,
rtc_dd->alarm_base + REG_OFFSET_ALARM_CTRL1, 1);
if (rc) {
diff --git a/drivers/slimbus/slim-msm-ngd.c b/drivers/slimbus/slim-msm-ngd.c
index d03c3f1..b4c88cd 100644
--- a/drivers/slimbus/slim-msm-ngd.c
+++ b/drivers/slimbus/slim-msm-ngd.c
@@ -82,7 +82,7 @@
};
static int ngd_slim_runtime_resume(struct device *device);
-static int ngd_slim_power_up(struct msm_slim_ctrl *dev);
+static int ngd_slim_power_up(struct msm_slim_ctrl *dev, bool mdm_restart);
static irqreturn_t ngd_slim_interrupt(int irq, void *d)
{
@@ -97,7 +97,7 @@
writel_relaxed(stat, ngd + NGD_INT_CLR);
dev->err = -EIO;
- dev_err(dev->dev, "NGD interrupt error:0x%x, err:%d", stat,
+ SLIM_WARN(dev, "NGD interrupt error:0x%x, err:%d\n", stat,
dev->err);
/* Guarantee that error interrupts are cleared */
mb();
@@ -119,7 +119,7 @@
for (i = 1; i < ((len + 3) >> 2); i++) {
rx_buf[i] = readl_relaxed(ngd + NGD_RX_MSG +
(4 * i));
- dev_dbg(dev->dev, "REG-RX data: %x\n", rx_buf[i]);
+ SLIM_DBG(dev, "REG-RX data: %x\n", rx_buf[i]);
}
msm_slim_rx_enqueue(dev, rx_buf, len);
writel_relaxed(NGD_INT_RX_MSG_RCVD,
@@ -130,8 +130,7 @@
*/
mb();
if (dev->use_rx_msgqs == MSM_MSGQ_ENABLED)
- dev_err(dev->dev,
- "direct message received even with RX MSGQs");
+ SLIM_WARN(dev, "direct msg rcvd with RX MSGQs\n");
else
complete(&dev->rx_msgq_notify);
}
@@ -140,13 +139,13 @@
/* Guarantee RECONFIG DONE interrupt is cleared */
mb();
/* In satellite mode, just log the reconfig done IRQ */
- dev_dbg(dev->dev, "reconfig done IRQ for NGD");
+ SLIM_DBG(dev, "reconfig done IRQ for NGD\n");
}
if (stat & NGD_INT_IE_VE_CHG) {
writel_relaxed(NGD_INT_IE_VE_CHG, ngd + NGD_INT_CLR);
/* Guarantee IE VE change interrupt is cleared */
mb();
- dev_err(dev->dev, "NGD IE VE change");
+ SLIM_DBG(dev, "NGD IE VE change\n");
}
pstat = readl_relaxed(PGD_THIS_EE(PGD_PORT_INT_ST_EEn, dev->ver));
@@ -161,7 +160,7 @@
struct msm_slim_qmi *qmi = container_of(n, struct msm_slim_qmi, nb);
struct msm_slim_ctrl *dev =
container_of(qmi, struct msm_slim_ctrl, qmi);
- pr_info("Slimbus QMI NGD CB received event:%ld", code);
+ SLIM_INFO(dev, "Slimbus QMI NGD CB received event:%ld\n", code);
switch (code) {
case QMI_SERVER_ARRIVE:
schedule_work(&qmi->ssr_up);
@@ -183,32 +182,54 @@
static int mdm_ssr_notify_cb(struct notifier_block *n, unsigned long code,
void *_cmd)
{
+ void __iomem *ngd;
struct msm_slim_mdm *mdm = container_of(n, struct msm_slim_mdm, nb);
struct msm_slim_ctrl *dev = container_of(mdm, struct msm_slim_ctrl,
mdm);
- int ret;
+ struct slim_controller *ctrl = &dev->ctrl;
+ u32 laddr;
+ struct slim_device *sbdev;
switch (code) {
case SUBSYS_BEFORE_SHUTDOWN:
- /* make sure runtime-pm doesn't suspend during modem SSR */
- pm_runtime_get_noresume(dev->dev);
+ SLIM_INFO(dev, "SLIM %lu external_modem SSR notify cb\n", code);
+ /* vote for runtime-pm so that ADSP doesn't go down */
+ msm_slim_get_ctrl(dev);
+ /*
+ * checking framer here will wake-up ADSP and may avoid framer
+ * handover later
+ */
+ msm_slim_qmi_check_framer_request(dev);
+ dev->mdm.state = MSM_CTRL_DOWN;
+ msm_slim_put_ctrl(dev);
break;
case SUBSYS_AFTER_POWERUP:
- ret = msm_slim_qmi_check_framer_request(dev);
- dev_err(dev->dev,
- "%s:SLIM %lu external_modem SSR notify cb, ret %d",
- __func__, code, ret);
- /*
- * Next codec transaction will reinit the HW
- * if it was suspended
- */
- if (pm_runtime_suspended(dev->dev) ||
- dev->state >= MSM_CTRL_ASLEEP) {
- break;
- } else {
- ngd_slim_power_up(dev);
- msm_slim_put_ctrl(dev);
+ if (dev->mdm.state != MSM_CTRL_DOWN)
+ return NOTIFY_DONE;
+ SLIM_INFO(dev,
+ "SLIM %lu external_modem SSR notify cb\n", code);
+ /* vote for runtime-pm so that ADSP doesn't go down */
+ msm_slim_get_ctrl(dev);
+ msm_slim_qmi_check_framer_request(dev);
+ /* If NGD enumeration is lost, we will need to power us up */
+ ngd = dev->base + NGD_BASE(dev->ctrl.nr, dev->ver);
+ laddr = readl_relaxed(ngd + NGD_STATUS);
+ if (!(laddr & NGD_LADDR)) {
+ /* runtime-pm state should be consistent with HW */
+ pm_runtime_disable(dev->dev);
+ pm_runtime_set_suspended(dev->dev);
+ dev->state = MSM_CTRL_DOWN;
+ SLIM_INFO(dev,
+ "SLIM MDM SSR (active framer on MDM) dev-down\n");
+ list_for_each_entry(sbdev, &ctrl->devs, dev_list)
+ slim_report_absent(sbdev);
+ ngd_slim_power_up(dev, true);
+ pm_runtime_set_active(dev->dev);
+ pm_runtime_enable(dev->dev);
}
+ dev->mdm.state = MSM_CTRL_AWAKE;
+ msm_slim_put_ctrl(dev);
+ break;
default:
break;
}
@@ -280,17 +301,9 @@
*/
ngd_slim_runtime_resume(dev->dev);
}
- if ((txn->mc == (SLIM_MSG_CLK_PAUSE_SEQ_FLG |
- SLIM_MSG_MC_RECONFIGURE_NOW)) &&
- dev->state <= MSM_CTRL_IDLE) {
- msm_slim_disconnect_endp(dev, &dev->rx_msgq,
- &dev->use_rx_msgqs);
- msm_slim_disconnect_endp(dev, &dev->tx_msgq,
- &dev->use_tx_msgqs);
- return msm_slim_qmi_power_request(dev, false);
- }
+
else if (txn->mc & SLIM_MSG_CLK_PAUSE_SEQ_FLG)
- return 0;
+ return -EPROTONOSUPPORT;
if (txn->mt == SLIM_MSG_MT_CORE &&
(txn->mc >= SLIM_MSG_MC_BEGIN_RECONFIGURATION &&
@@ -302,7 +315,7 @@
if (dev->state == MSM_CTRL_DOWN) {
u8 mc = (u8)txn->mc;
int timeout;
- dev_err(dev->dev, "ADSP slimbus not up yet");
+ SLIM_INFO(dev, "ADSP slimbus not up yet\n");
/*
* Messages related to data channel management can't
* wait since they are holding reconfiguration lock.
@@ -362,8 +375,9 @@
mutex_lock(&dev->tx_lock);
if (report_sat == false && dev->state != MSM_CTRL_AWAKE) {
- dev_err(dev->dev, "controller not ready");
+ SLIM_ERR(dev, "controller not ready\n");
mutex_unlock(&dev->tx_lock);
+ pm_runtime_set_suspended(dev->dev);
msm_slim_put_ctrl(dev);
return -EREMOTEIO;
}
@@ -372,6 +386,14 @@
txn->mc == SLIM_MSG_MC_CONNECT_SINK ||
txn->mc == SLIM_MSG_MC_DISCONNECT_PORT)) {
int i = 0;
+ if (txn->mc != SLIM_MSG_MC_DISCONNECT_PORT)
+ SLIM_INFO(dev,
+ "Connect port: laddr 0x%x port_num %d chan_num %d\n",
+ txn->la, txn->wbuf[0], txn->wbuf[1]);
+ else
+ SLIM_INFO(dev,
+ "Disconnect port: laddr 0x%x port_num %d\n",
+ txn->la, txn->wbuf[0]);
txn->mt = SLIM_MSG_MT_DEST_REFERRED_USER;
if (txn->mc == SLIM_MSG_MC_CONNECT_SOURCE)
txn->mc = SLIM_USR_MC_CONNECT_SRC;
@@ -388,10 +410,11 @@
mutex_unlock(&dev->tx_lock);
ret = dev->ctrl.get_laddr(&dev->ctrl, ea, 6,
&dev->pgdla);
- pr_debug("SLIM PGD LA:0x%x, ret:%d", dev->pgdla,
- ret);
+ SLIM_DBG(dev, "SLIM PGD LA:0x%x, ret:%d\n",
+ dev->pgdla, ret);
if (ret) {
- pr_err("Incorrect SLIM-PGD EAPC:0x%x",
+ SLIM_ERR(dev,
+ "Incorrect SLIM-PGD EAPC:0x%x\n",
dev->pdata.eapc);
return ret;
}
@@ -406,7 +429,8 @@
wbuf[i++] = txn->wbuf[1];
ret = ngd_get_tid(ctrl, txn, &wbuf[i++], &done);
if (ret) {
- pr_err("TID for connect/disconnect fail:%d", ret);
+ SLIM_ERR(dev, "TID for connect/disconnect fail:%d\n",
+ ret);
goto ngd_xfer_err;
}
txn->len = i;
@@ -416,7 +440,7 @@
txn->rl--;
pbuf = msm_get_msg_buf(dev, txn->rl);
if (!pbuf) {
- dev_err(dev->dev, "Message buffer unavailable");
+ SLIM_ERR(dev, "Message buffer unavailable\n");
ret = -ENOMEM;
goto ngd_xfer_err;
}
@@ -470,7 +494,7 @@
return 0;
}
if (dev->err) {
- dev_err(dev->dev, "pipe-port connect err:%d", dev->err);
+ SLIM_ERR(dev, "pipe-port connect err:%d\n", dev->err);
goto ngd_xfer_err;
}
/* Add port-base to port number if this is manager side port */
@@ -509,7 +533,7 @@
u32 conf, stat, rx_msgq, int_stat, int_en, int_clr;
void __iomem *ngd = dev->base + NGD_BASE(dev->ctrl.nr,
dev->ver);
- dev_err(dev->dev, "TX failed :MC:0x%x,mt:0x%x, ret:%d, ver:%d",
+ SLIM_WARN(dev, "TX failed :MC:0x%x,mt:0x%x, ret:%d, ver:%d\n",
txn_mc, txn_mt, ret, dev->ver);
conf = readl_relaxed(ngd);
stat = readl_relaxed(ngd + NGD_STATUS);
@@ -518,9 +542,10 @@
int_en = readl_relaxed(ngd + NGD_INT_EN);
int_clr = readl_relaxed(ngd + NGD_INT_CLR);
- pr_err("conf:0x%x,stat:0x%x,rxmsgq:0x%x", conf, stat, rx_msgq);
- pr_err("int_stat:0x%x,int_en:0x%x,int_cll:0x%x", int_stat,
- int_en, int_clr);
+ SLIM_WARN(dev, "conf:0x%x,stat:0x%x,rxmsgq:0x%x\n",
+ conf, stat, rx_msgq);
+ SLIM_WARN(dev, "int_stat:0x%x,int_en:0x%x,int_cll:0x%x\n",
+ int_stat, int_en, int_clr);
} else if (txn_mt == SLIM_MSG_MT_DEST_REFERRED_USER &&
(txn_mc == SLIM_USR_MC_CONNECT_SRC ||
txn_mc == SLIM_USR_MC_CONNECT_SINK ||
@@ -534,8 +559,9 @@
else
ret = txn->ec;
if (ret) {
- pr_err("connect/disconnect:0x%x,tid:%d err:%d", txn->mc,
- txn->tid, ret);
+ SLIM_INFO(dev,
+ "connect/disconnect:0x%x,tid:%d err:%d\n",
+ txn->mc, txn->tid, ret);
mutex_lock(&ctrl->m_ctrl);
ctrl->txnt[txn->tid] = NULL;
mutex_unlock(&ctrl->m_ctrl);
@@ -590,6 +616,7 @@
static int ngd_xferandwait_ack(struct slim_controller *ctrl,
struct slim_msg_txn *txn)
{
+ struct msm_slim_ctrl *dev = slim_get_ctrldata(ctrl);
int ret = ngd_xfer_msg(ctrl, txn);
if (!ret) {
int timeout;
@@ -602,8 +629,8 @@
if (ret) {
if (ret != -EREMOTEIO || txn->mc != SLIM_USR_MC_CHAN_CTRL)
- pr_err("master msg:0x%x,tid:%d ret:%d", txn->mc,
- txn->tid, ret);
+ SLIM_ERR(dev, "master msg:0x%x,tid:%d ret:%d\n",
+ txn->mc, txn->tid, ret);
mutex_lock(&ctrl->m_ctrl);
ctrl->txnt[txn->tid] = NULL;
mutex_unlock(&ctrl->m_ctrl);
@@ -614,12 +641,13 @@
static int ngd_allocbw(struct slim_device *sb, int *subfrmc, int *clkgear)
{
- int ret;
+ int ret = 0, num_chan = 0;
struct slim_pending_ch *pch;
struct slim_msg_txn txn;
struct slim_controller *ctrl = sb->ctrl;
DECLARE_COMPLETION_ONSTACK(done);
u8 wbuf[SLIM_MSGQ_BUF_LEN];
+ struct msm_slim_ctrl *dev = slim_get_ctrldata(ctrl);
*clkgear = ctrl->clkgear;
*subfrmc = 0;
@@ -632,7 +660,7 @@
txn.rbuf = NULL;
if (ctrl->sched.msgsl != ctrl->sched.pending_msgsl) {
- pr_debug("slim reserve BW for messaging: req: %d",
+ SLIM_DBG(dev, "slim reserve BW for messaging: req: %d\n",
ctrl->sched.pending_msgsl);
txn.mc = SLIM_USR_MC_REQ_BW;
wbuf[txn.len++] = ((sb->laddr & 0x1f) |
@@ -663,7 +691,7 @@
struct slim_ich *slc;
slc = &ctrl->chans[pch->chan];
if (!slc) {
- pr_err("no channel in define?");
+ SLIM_WARN(dev, "no channel in define?\n");
return -ENXIO;
}
if (txn.len == 0) {
@@ -678,12 +706,14 @@
wbuf[txn.len++] = slc->prrate;
ret = ngd_get_tid(ctrl, &txn, &wbuf[txn.len++], &done);
if (ret) {
- pr_err("no tid for channel define?");
+ SLIM_WARN(dev, "no tid for channel define?\n");
return -ENXIO;
}
}
+ num_chan++;
wbuf[txn.len++] = slc->chan;
- pr_debug("slim define chan:%d, tid:0x%x", slc->chan, txn.tid);
+ SLIM_INFO(dev, "slim activate chan:%d, laddr: 0x%x\n",
+ slc->chan, sb->laddr);
}
if (txn.len) {
txn.mc = SLIM_USR_MC_DEF_ACT_CHAN;
@@ -708,7 +738,7 @@
struct slim_ich *slc;
slc = &ctrl->chans[pch->chan];
if (!slc) {
- pr_err("no channel in removal?");
+ SLIM_WARN(dev, "no channel in removal?\n");
return -ENXIO;
}
if (txn.len == 0) {
@@ -717,12 +747,13 @@
(sb->laddr & 0x1f);
ret = ngd_get_tid(ctrl, &txn, &wbuf[txn.len++], &done);
if (ret) {
- pr_err("no tid for channel define?");
+ SLIM_WARN(dev, "no tid for channel define?\n");
return -ENXIO;
}
}
wbuf[txn.len++] = slc->chan;
- pr_debug("slim remove chan:%d, tid:0x%x", slc->chan, txn.tid);
+ SLIM_INFO(dev, "slim remove chan:%d, laddr: 0x%x\n",
+ slc->chan, sb->laddr);
}
if (txn.len) {
txn.mc = SLIM_USR_MC_CHAN_CTRL;
@@ -830,7 +861,7 @@
wbuf[3] = SAT_MSG_PROT;
txn.wbuf = wbuf;
txn.len = 4;
- pr_info("SLIM SAT: Received master capability");
+ SLIM_INFO(dev, "SLIM SAT: Rcvd master capability\n");
if (dev->state >= MSM_CTRL_ASLEEP) {
ngd_slim_setup_msg_path(dev);
if (dev->use_rx_msgqs == MSM_MSGQ_ENABLED)
@@ -847,18 +878,20 @@
ret = ngd_xfer_msg(&dev->ctrl, &txn);
if (!ret) {
enum msm_ctrl_state prev_state = dev->state;
- pr_info("SLIM SAT: capability exchange successful");
+ SLIM_INFO(dev,
+ "SLIM SAT: capability exchange successful\n");
dev->state = MSM_CTRL_AWAKE;
if (prev_state >= MSM_CTRL_ASLEEP)
complete(&dev->reconf);
else
- pr_err("SLIM: unexpected capability, state:%d",
- prev_state);
+ SLIM_ERR(dev,
+ "SLIM: unexpected capability, state:%d\n",
+ prev_state);
/* ADSP SSR, send device_up notifications */
if (prev_state == MSM_CTRL_DOWN)
complete(&dev->qmi.slave_notify);
} else if (ret == -EIO) {
- pr_info("capability message NACKed, retrying");
+ SLIM_WARN(dev, "capability message NACKed, retrying\n");
if (retries < INIT_MX_RETRIES) {
msleep(DEF_RETRY_MS);
retries++;
@@ -881,8 +914,9 @@
mutex_lock(&dev->ctrl.m_ctrl);
txn = dev->ctrl.txnt[buf[3]];
if (!txn) {
- pr_err("LADDR response after timeout, tid:0x%x",
- buf[3]);
+ SLIM_WARN(dev,
+ "LADDR response after timeout, tid:0x%x\n",
+ buf[3]);
mutex_unlock(&dev->ctrl.m_ctrl);
return;
}
@@ -898,7 +932,7 @@
mutex_lock(&dev->ctrl.m_ctrl);
txn = dev->ctrl.txnt[buf[3]];
if (!txn) {
- pr_err("ACK received after timeout, tid:0x%x",
+ SLIM_WARN(dev, "ACK received after timeout, tid:0x%x\n",
buf[3]);
mutex_unlock(&dev->ctrl.m_ctrl);
return;
@@ -906,7 +940,7 @@
dev_dbg(dev->dev, "got response:tid:%d, response:0x%x",
(int)buf[3], buf[4]);
if (!(buf[4] & MSM_SAT_SUCCSS)) {
- dev_err(dev->dev, "TID:%d, NACK code:0x%x", (int)buf[3],
+ SLIM_WARN(dev, "TID:%d, NACK code:0x%x\n", (int)buf[3],
buf[4]);
txn->ec = -EIO;
}
@@ -916,7 +950,7 @@
}
}
-static int ngd_slim_power_up(struct msm_slim_ctrl *dev)
+static int ngd_slim_power_up(struct msm_slim_ctrl *dev, bool mdm_restart)
{
void __iomem *ngd;
int timeout, ret = 0;
@@ -927,18 +961,20 @@
NGD_INT_IE_VE_CHG | NGD_INT_DEV_ERR |
NGD_INT_TX_MSG_SENT | NGD_INT_RX_MSG_RCVD);
- if (cur_state == MSM_CTRL_DOWN) {
+ if (!mdm_restart && cur_state == MSM_CTRL_DOWN) {
int timeout = wait_for_completion_timeout(&dev->qmi.qmi_comp,
HZ);
if (!timeout)
- pr_err("slimbus QMI init timed out");
+ SLIM_ERR(dev, "slimbus QMI init timed out\n");
}
/* No need to vote if contorller is not in low power mode */
- if (cur_state == MSM_CTRL_DOWN || cur_state == MSM_CTRL_ASLEEP) {
+ if (!mdm_restart &&
+ (cur_state == MSM_CTRL_DOWN || cur_state == MSM_CTRL_ASLEEP)) {
ret = msm_slim_qmi_power_request(dev, true);
if (ret) {
- pr_err("SLIM QMI power request failed:%d", ret);
+ SLIM_ERR(dev, "SLIM QMI power request failed:%d\n",
+ ret);
return ret;
}
}
@@ -955,7 +991,7 @@
* For example, modem restarted when playback was active
*/
if (cur_state == MSM_CTRL_AWAKE) {
- pr_err("SLIM MDM restart: ADSP active framer:NO OP");
+ SLIM_INFO(dev, "Subsys restart: ADSP active framer\n");
return 0;
}
/*
@@ -964,24 +1000,27 @@
*/
ngd_slim_setup_msg_path(dev);
return 0;
- } else if (cur_state == MSM_CTRL_ASLEEP) {
- pr_debug("ADSP P.C. CTRL state:%d NGD not enumerated:0x%x",
- dev->state, laddr);
- } else if (cur_state == MSM_CTRL_IDLE || cur_state == MSM_CTRL_AWAKE) {
+ }
+
+ if (mdm_restart) {
/*
- * external MDM SSR when only voice call is in progress.
+ * external MDM SSR when MDM is active framer
* ADSP will reset slimbus HW. disconnect BAM pipes so that
* they can be connected after capability message is received.
* Set device state to ASLEEP to be synchronous with the HW
*/
- pr_err("SLIM MDM restart: MDM active framer: reinit HW");
- dev->state = MSM_CTRL_ASLEEP;
- msm_slim_disconnect_endp(dev, &dev->rx_msgq,
- &dev->use_rx_msgqs);
- msm_slim_disconnect_endp(dev, &dev->tx_msgq,
- &dev->use_tx_msgqs);
+ /* make current state as DOWN */
+ cur_state = MSM_CTRL_DOWN;
+ SLIM_INFO(dev,
+ "SLIM MDM restart: MDM active framer: reinit HW\n");
+ /* disconnect BAM pipes */
+ if (dev->use_rx_msgqs == MSM_MSGQ_ENABLED)
+ dev->use_rx_msgqs = MSM_MSGQ_DOWN;
+ if (dev->use_tx_msgqs == MSM_MSGQ_ENABLED)
+ dev->use_tx_msgqs = MSM_MSGQ_DOWN;
+ dev->state = MSM_CTRL_DOWN;
}
- /* ADSP SSR scenario, need to disconnect pipe before connecting */
+ /* SSR scenario, need to disconnect pipe before connecting */
if (dev->use_rx_msgqs == MSM_MSGQ_DOWN) {
struct msm_slim_endp *endpoint = &dev->rx_msgq;
sps_disconnect(endpoint->sps);
@@ -1010,11 +1049,14 @@
timeout = wait_for_completion_timeout(&dev->reconf, HZ);
if (!timeout) {
- pr_err("Failed to receive master capability");
+ SLIM_ERR(dev, "Failed to receive master capability\n");
return -ETIMEDOUT;
}
- if (cur_state == MSM_CTRL_DOWN)
+ if (cur_state == MSM_CTRL_DOWN) {
complete(&dev->ctrl_up);
+ /* Resetting the log level */
+ SLIM_RST_LOGLVL(dev);
+ }
return 0;
}
@@ -1025,20 +1067,7 @@
ret = msm_slim_qmi_init(dev, false);
/* controller state should be in sync with framework state */
if (!ret) {
- ret = slim_ctrl_clk_pause(&dev->ctrl, false,
- SLIM_CLK_UNSPECIFIED);
complete(&dev->qmi.qmi_comp);
- /*
- * Power-up won't be called if clock pause failed.
- * This can happen if ADSP SSR happened when audio
- * session is in progress. Framework will think that
- * clock pause failed so no need to wakeup controller.
- * Call power-up explicitly in that case, since slimbus
- * HW needs to be powered-on to be in sync with
- * framework state
- */
- if (ret)
- ngd_slim_power_up(dev);
if (!pm_runtime_enabled(dev->dev) ||
!pm_runtime_suspended(dev->dev))
ngd_slim_runtime_resume(dev->dev);
@@ -1047,7 +1076,7 @@
pm_runtime_mark_last_busy(dev->dev);
pm_runtime_put(dev->dev);
} else
- dev_err(dev->dev, "qmi init fail, ret:%d, state:%d",
+ SLIM_ERR(dev, "qmi init fail, ret:%d, state:%d\n",
ret, dev->state);
} else {
msm_slim_qmi_exit(dev);
@@ -1056,10 +1085,25 @@
return ret;
}
-static int ngd_clk_pause_wakeup(struct slim_controller *ctrl)
+static int ngd_slim_power_down(struct msm_slim_ctrl *dev)
{
- struct msm_slim_ctrl *dev = slim_get_ctrldata(ctrl);
- return ngd_slim_power_up(dev);
+ int i;
+ struct slim_controller *ctrl = &dev->ctrl;
+ mutex_lock(&ctrl->m_ctrl);
+ /* Pending response for a message */
+ for (i = 0; i < ctrl->last_tid; i++) {
+ if (ctrl->txnt[i]) {
+ SLIM_INFO(dev, "NGD down:txn-rsp for %d pending", i);
+ mutex_unlock(&ctrl->m_ctrl);
+ return -EBUSY;
+ }
+ }
+ mutex_unlock(&ctrl->m_ctrl);
+ msm_slim_disconnect_endp(dev, &dev->rx_msgq,
+ &dev->use_rx_msgqs);
+ msm_slim_disconnect_endp(dev, &dev->tx_msgq,
+ &dev->use_tx_msgqs);
+ return msm_slim_qmi_power_request(dev, false);
}
static int ngd_slim_rx_msgq_thread(void *data)
@@ -1074,11 +1118,7 @@
while (!kthread_should_stop()) {
set_current_state(TASK_INTERRUPTIBLE);
- ret = wait_for_completion_interruptible(notify);
- if (ret) {
- dev_err(dev->dev, "rx thread wait err:%d", ret);
- continue;
- }
+ wait_for_completion(notify);
/* 1 irq notification per message */
if (dev->use_rx_msgqs != MSM_MSGQ_ENABLED) {
msm_slim_rx_dequeue(dev, (u8 *)buffer);
@@ -1087,7 +1127,7 @@
}
ret = msm_slim_rx_msgq_get(dev, buffer, index);
if (ret) {
- dev_err(dev->dev, "rx_msgq_get() failed 0x%x\n", ret);
+ SLIM_ERR(dev, "rx_msgq_get() failed 0x%x\n", ret);
continue;
}
@@ -1116,14 +1156,10 @@
int ret, i = 0;
while (!kthread_should_stop()) {
set_current_state(TASK_INTERRUPTIBLE);
- ret = wait_for_completion_timeout(&dev->qmi.slave_notify,
- HZ);
- if (!ret) {
- dev_dbg(dev->dev, "slave thread wait err:%d", ret);
- continue;
- }
+ wait_for_completion(&dev->qmi.slave_notify);
/* Probe devices for first notification */
if (!i) {
+ i++;
dev->err = 0;
if (dev->dev->of_node)
of_register_slim_devices(&dev->ctrl);
@@ -1136,12 +1172,12 @@
} else {
slim_framer_booted(ctrl);
}
- i++;
mutex_lock(&ctrl->m_ctrl);
list_for_each_safe(pos, next, &ctrl->devs) {
+ int j;
sbdev = list_entry(pos, struct slim_device, dev_list);
mutex_unlock(&ctrl->m_ctrl);
- for (i = 0; i < LADDR_RETRY; i++) {
+ for (j = 0; j < LADDR_RETRY; j++) {
ret = slim_get_logical_addr(sbdev,
sbdev->e_addr,
6, &sbdev->laddr);
@@ -1176,7 +1212,7 @@
/* device up should be called again after SSR */
list_for_each_entry(sbdev, &ctrl->devs, dev_list)
slim_report_absent(sbdev);
- pr_info("SLIM ADSP SSR (DOWN) done");
+ SLIM_INFO(dev, "SLIM ADSP SSR (DOWN) done\n");
}
static void ngd_adsp_up(struct work_struct *work)
@@ -1188,6 +1224,28 @@
ngd_slim_enable(dev, true);
}
+static ssize_t show_mask(struct device *device, struct device_attribute *attr,
+ char *buf)
+{
+ struct platform_device *pdev = to_platform_device(device);
+ struct msm_slim_ctrl *dev = platform_get_drvdata(pdev);
+ return snprintf(buf, sizeof(int), "%u\n", dev->ipc_log_mask);
+}
+
+static ssize_t set_mask(struct device *device, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct platform_device *pdev = to_platform_device(device);
+ struct msm_slim_ctrl *dev = platform_get_drvdata(pdev);
+
+ dev->ipc_log_mask = buf[0] - '0';
+ if (dev->ipc_log_mask > DBG_LEV)
+ dev->ipc_log_mask = DBG_LEV;
+ return count;
+}
+
+static DEVICE_ATTR(debug_mask, S_IRUGO | S_IWUSR, show_mask, set_mask);
+
static int __devinit ngd_slim_probe(struct platform_device *pdev)
{
struct msm_slim_ctrl *dev;
@@ -1197,6 +1255,7 @@
struct resource *irq, *bam_irq;
bool rxreg_access = false;
bool slim_mdm = false;
+ const char *ext_modem_id = NULL;
slim_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM,
"slimbus_physical");
@@ -1231,6 +1290,26 @@
dev->dev = &pdev->dev;
platform_set_drvdata(pdev, dev);
slim_set_ctrldata(&dev->ctrl, dev);
+
+ /* Create IPC log context */
+ dev->ipc_slimbus_log = ipc_log_context_create(IPC_SLIMBUS_LOG_PAGES,
+ dev_name(dev->dev));
+ if (!dev->ipc_slimbus_log)
+ dev_err(&pdev->dev, "error creating ipc_logging context\n");
+ else {
+ /* Initialize the log mask */
+ dev->ipc_log_mask = INFO_LEV;
+ dev->default_ipc_log_mask = INFO_LEV;
+ SLIM_INFO(dev, "start logging for slim dev %s\n",
+ dev_name(dev->dev));
+ }
+ ret = sysfs_create_file(&dev->dev->kobj, &dev_attr_debug_mask.attr);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to create dev. attr\n");
+ dev->sysfs_created = false;
+ } else
+ dev->sysfs_created = true;
+
dev->base = ioremap(slim_mem->start, resource_size(slim_mem));
if (!dev->base) {
dev_err(&pdev->dev, "IOremap failed\n");
@@ -1257,8 +1336,10 @@
&dev->pdata.apps_pipes);
of_property_read_u32(pdev->dev.of_node, "qcom,ea-pc",
&dev->pdata.eapc);
- slim_mdm = of_property_read_bool(pdev->dev.of_node,
- "qcom,slim-mdm");
+ ret = of_property_read_string(pdev->dev.of_node,
+ "qcom,slim-mdm", &ext_modem_id);
+ if (!ret)
+ slim_mdm = true;
} else {
dev->ctrl.nr = pdev->id;
}
@@ -1279,7 +1360,7 @@
dev->ctrl.allocbw = ngd_allocbw;
dev->ctrl.xfer_msg = ngd_xfer_msg;
dev->ctrl.xfer_user_msg = ngd_user_msg;
- dev->ctrl.wakeup = ngd_clk_pause_wakeup;
+ dev->ctrl.wakeup = NULL;
dev->ctrl.alloc_port = msm_alloc_port;
dev->ctrl.dealloc_port = msm_dealloc_port;
dev->ctrl.port_xfer = msm_slim_port_xfer;
@@ -1333,7 +1414,7 @@
if (slim_mdm) {
dev->mdm.nb.notifier_call = mdm_ssr_notify_cb;
- dev->mdm.ssr = subsys_notif_register_notifier("external_modem",
+ dev->mdm.ssr = subsys_notif_register_notifier(ext_modem_id,
&dev->mdm.nb);
if (IS_ERR_OR_NULL(dev->mdm.ssr))
dev_err(dev->dev,
@@ -1371,7 +1452,7 @@
dev_err(dev->dev, "Failed to start notifier thread:%d\n", ret);
goto err_notify_thread_create_failed;
}
- dev_dbg(dev->dev, "NGD SB controller is up!\n");
+ SLIM_INFO(dev, "NGD SB controller is up!\n");
return 0;
err_notify_thread_create_failed:
@@ -1389,6 +1470,9 @@
err_ioremap_bam_failed:
iounmap(dev->base);
err_ioremap_failed:
+ if (dev->sysfs_created)
+ sysfs_remove_file(&dev->dev->kobj,
+ &dev_attr_debug_mask.attr);
kfree(dev);
return ret;
}
@@ -1397,6 +1481,9 @@
{
struct msm_slim_ctrl *dev = platform_get_drvdata(pdev);
ngd_slim_enable(dev, false);
+ if (dev->sysfs_created)
+ sysfs_remove_file(&dev->dev->kobj,
+ &dev_attr_debug_mask.attr);
qmi_svc_event_notifier_unregister(SLIMBUS_QMI_SVC_ID,
SLIMBUS_QMI_SVC_V1,
SLIMBUS_QMI_INS_ID, &dev->qmi.nb);
@@ -1436,16 +1523,17 @@
struct msm_slim_ctrl *dev = platform_get_drvdata(pdev);
int ret = 0;
if (dev->state >= MSM_CTRL_ASLEEP)
- ret = slim_ctrl_clk_pause(&dev->ctrl, true, 0);
+ ret = ngd_slim_power_up(dev, false);
if (ret) {
- /* Did SSR cause this clock pause failure */
+ /* Did SSR cause this power up failure */
if (dev->state != MSM_CTRL_DOWN)
dev->state = MSM_CTRL_ASLEEP;
else
- dev_err(device, "HW wakeup attempt during SSR");
+ SLIM_WARN(dev, "HW wakeup attempt during SSR\n");
} else {
dev->state = MSM_CTRL_AWAKE;
}
+ SLIM_INFO(dev, "Slim runtime resume: ret %d\n", ret);
return ret;
}
@@ -1455,14 +1543,15 @@
struct platform_device *pdev = to_platform_device(device);
struct msm_slim_ctrl *dev = platform_get_drvdata(pdev);
int ret = 0;
- ret = slim_ctrl_clk_pause(&dev->ctrl, false, SLIM_CLK_UNSPECIFIED);
+ ret = ngd_slim_power_down(dev);
if (ret) {
if (ret != -EBUSY)
- dev_err(device, "clk pause not entered:%d", ret);
+ SLIM_INFO(dev, "slim resource not idle:%d\n", ret);
dev->state = MSM_CTRL_AWAKE;
} else {
dev->state = MSM_CTRL_ASLEEP;
}
+ SLIM_INFO(dev, "Slim runtime suspend: ret %d\n", ret);
return ret;
}
@@ -1474,7 +1563,6 @@
if (!pm_runtime_enabled(dev) ||
(!pm_runtime_suspended(dev) &&
cdev->state == MSM_CTRL_IDLE)) {
- dev_dbg(dev, "system suspend");
ret = ngd_slim_runtime_suspend(dev);
/*
* If runtime-PM still thinks it's active, then make sure its
@@ -1501,16 +1589,20 @@
*/
ret = 0;
}
+ SLIM_INFO(cdev, "system suspend\n");
return ret;
}
static int ngd_slim_resume(struct device *dev)
{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct msm_slim_ctrl *cdev = platform_get_drvdata(pdev);
/*
* Rely on runtime-PM to call resume in case it is enabled.
* Even if it's not enabled, rely on 1st client transaction to do
* clock/power on
*/
+ SLIM_INFO(cdev, "system resume\n");
return 0;
}
#endif /* CONFIG_PM_SLEEP */
diff --git a/drivers/slimbus/slim-msm.c b/drivers/slimbus/slim-msm.c
index 8589b9f..915bf88 100644
--- a/drivers/slimbus/slim-msm.c
+++ b/drivers/slimbus/slim-msm.c
@@ -51,7 +51,7 @@
if (ret >= 0) {
ref = atomic_read(&dev->dev->power.usage_count);
if (ref <= 0) {
- dev_err(dev->dev, "reference count -ve:%d", ref);
+ SLIM_WARN(dev, "reference count -ve:%d", ref);
ret = -ENODEV;
}
}
@@ -67,7 +67,7 @@
pm_runtime_mark_last_busy(dev->dev);
ref = atomic_read(&dev->dev->power.usage_count);
if (ref <= 0)
- dev_err(dev->dev, "reference count mismatch:%d", ref);
+ SLIM_WARN(dev, "reference count mismatch:%d", ref);
else
pm_runtime_put_sync(dev->dev);
#endif
@@ -109,7 +109,7 @@
/* clear port interrupts */
writel_relaxed(pstat, PGD_THIS_EE(PGD_PORT_INT_CL_EEn,
dev->ver));
- pr_info("disabled overflow/underflow for port 0x%x", pstat);
+ SLIM_INFO(dev, "disabled overflow/underflow for port 0x%x", pstat);
/*
* Guarantee that port interrupt bit(s) clearing writes go
@@ -1133,13 +1133,13 @@
rc = qmi_send_req_wait(dev->qmi.handle, &req_desc, req, sizeof(*req),
&resp_desc, &resp, sizeof(resp), 5000);
if (rc < 0) {
- pr_err("%s: QMI send req failed %d\n", __func__, rc);
+ SLIM_ERR(dev, "%s: QMI send req failed %d\n", __func__, rc);
return rc;
}
/* Check the response */
if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
- pr_err("%s: QMI request failed 0x%x (%s)\n", __func__,
+ SLIM_ERR(dev, "%s: QMI request failed 0x%x (%s)\n", __func__,
resp.resp.result, get_qmi_error(&resp.resp));
return -EREMOTEIO;
}
@@ -1165,13 +1165,13 @@
rc = qmi_send_req_wait(dev->qmi.handle, &req_desc, req, sizeof(*req),
&resp_desc, &resp, sizeof(resp), 5000);
if (rc < 0) {
- pr_err("%s: QMI send req failed %d\n", __func__, rc);
+ SLIM_ERR(dev, "%s: QMI send req failed %d\n", __func__, rc);
return rc;
}
/* Check the response */
if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
- pr_err("%s: QMI request failed 0x%x (%s)\n", __func__,
+ SLIM_ERR(dev, "%s: QMI request failed 0x%x (%s)\n", __func__,
resp.resp.result, get_qmi_error(&resp.resp));
return -EREMOTEIO;
}
@@ -1208,7 +1208,7 @@
SLIMBUS_QMI_SVC_V1,
SLIMBUS_QMI_INS_ID);
if (rc < 0) {
- pr_err("%s: QMI server not found\n", __func__);
+ SLIM_ERR(dev, "%s: QMI server not found\n", __func__);
goto qmi_connect_to_service_failed;
}
@@ -1281,12 +1281,12 @@
rc = qmi_send_req_wait(dev->qmi.handle, &req_desc, NULL, 0,
&resp_desc, &resp, sizeof(resp), 5000);
if (rc < 0) {
- dev_err(dev->dev, "%s: QMI send req failed %d\n", __func__, rc);
+ SLIM_ERR(dev, "%s: QMI send req failed %d\n", __func__, rc);
return rc;
}
/* Check the response */
if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
- dev_err(dev->dev, "%s: QMI request failed 0x%x (%s)\n",
+ SLIM_ERR(dev, "%s: QMI request failed 0x%x (%s)\n",
__func__, resp.resp.result, get_qmi_error(&resp.resp));
return -EREMOTEIO;
}
diff --git a/drivers/slimbus/slim-msm.h b/drivers/slimbus/slim-msm.h
index 63178cc..9673208 100644
--- a/drivers/slimbus/slim-msm.h
+++ b/drivers/slimbus/slim-msm.h
@@ -17,6 +17,7 @@
#include <linux/kthread.h>
#include <mach/msm_qmi_interface.h>
#include <mach/subsystem_notif.h>
+#include <mach/msm_ipc_logging.h>
/* Per spec.max 40 bytes per received message */
#define SLIM_MSGQ_BUF_LEN 40
@@ -216,6 +217,7 @@
struct msm_slim_mdm {
struct notifier_block nb;
void *ssr;
+ enum msm_ctrl_state state;
};
struct msm_slim_pdata {
@@ -266,6 +268,10 @@
struct msm_slim_qmi qmi;
struct msm_slim_pdata pdata;
struct msm_slim_mdm mdm;
+ int default_ipc_log_mask;
+ int ipc_log_mask;
+ bool sysfs_created;
+ void *ipc_slimbus_log;
};
struct msm_sat_chan {
@@ -299,6 +305,57 @@
};
+/* IPC logging stuff */
+#define IPC_SLIMBUS_LOG_PAGES 5
+
+/* Log levels */
+enum {
+ FATAL_LEV = 0U,
+ ERR_LEV = 1U,
+ WARN_LEV = 2U,
+ INFO_LEV = 3U,
+ DBG_LEV = 4U,
+};
+
+/* Default IPC log level INFO */
+#define SLIM_DBG(dev, x...) do { \
+ pr_debug(x); \
+ if (dev->ipc_slimbus_log && dev->ipc_log_mask >= DBG_LEV) { \
+ ipc_log_string(dev->ipc_slimbus_log, x); \
+ } \
+} while (0)
+
+#define SLIM_INFO(dev, x...) do { \
+ pr_debug(x); \
+ if (dev->ipc_slimbus_log && dev->ipc_log_mask >= INFO_LEV) {\
+ ipc_log_string(dev->ipc_slimbus_log, x); \
+ } \
+} while (0)
+
+/* warnings and errors show up on console always */
+#define SLIM_WARN(dev, x...) do { \
+ pr_warn(x); \
+ if (dev->ipc_slimbus_log && dev->ipc_log_mask >= WARN_LEV) \
+ ipc_log_string(dev->ipc_slimbus_log, x); \
+} while (0)
+
+/* ERROR condition in the driver sets the hs_serial_debug_mask
+ * to ERR_FATAL level, so that this message can be seen
+ * in IPC logging. Further errors continue to log on the console
+ */
+#define SLIM_ERR(dev, x...) do { \
+ pr_err(x); \
+ if (dev->ipc_slimbus_log && dev->ipc_log_mask >= ERR_LEV) { \
+ ipc_log_string(dev->ipc_slimbus_log, x); \
+ dev->default_ipc_log_mask = dev->ipc_log_mask; \
+ dev->ipc_log_mask = FATAL_LEV; \
+ } \
+} while (0)
+
+#define SLIM_RST_LOGLVL(dev) { \
+ dev->ipc_log_mask = dev->default_ipc_log_mask; \
+}
+
int msm_slim_rx_enqueue(struct msm_slim_ctrl *dev, u32 *buf, u8 len);
int msm_slim_rx_dequeue(struct msm_slim_ctrl *dev, u8 *buf);
int msm_slim_get_ctrl(struct msm_slim_ctrl *dev);
diff --git a/drivers/spi/spi_qsd.c b/drivers/spi/spi_qsd.c
index 39e81fa..d8dc6f9 100644
--- a/drivers/spi/spi_qsd.c
+++ b/drivers/spi/spi_qsd.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2008-2014, 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
@@ -50,7 +50,7 @@
static int msm_spi_pm_resume_runtime(struct device *device);
static int msm_spi_pm_suspend_runtime(struct device *device);
-
+static inline void msm_spi_dma_unmap_buffers(struct msm_spi *dd);
static inline int msm_spi_configure_gsbi(struct msm_spi *dd,
struct platform_device *pdev)
@@ -135,6 +135,40 @@
}
}
+static inline int msm_spi_request_cs_gpio(struct msm_spi *dd)
+{
+ int cs_num;
+ int rc;
+
+ cs_num = dd->cur_msg->spi->chip_select;
+ if ((!(dd->cur_msg->spi->mode & SPI_LOOP)) &&
+ (!(dd->cs_gpios[cs_num].valid)) &&
+ (dd->cs_gpios[cs_num].gpio_num >= 0)) {
+ rc = gpio_request(dd->cs_gpios[cs_num].gpio_num,
+ spi_cs_rsrcs[cs_num]);
+ if (rc) {
+ dev_err(dd->dev,
+ "gpio_request for pin %d failed,error %d\n",
+ dd->cs_gpios[cs_num].gpio_num, rc);
+ return rc;
+ }
+ dd->cs_gpios[cs_num].valid = 1;
+ }
+ return 0;
+}
+
+static inline void msm_spi_free_cs_gpio(struct msm_spi *dd)
+{
+ int cs_num;
+
+ cs_num = dd->cur_msg->spi->chip_select;
+ if (dd->cs_gpios[cs_num].valid) {
+ gpio_free(dd->cs_gpios[cs_num].gpio_num);
+ dd->cs_gpios[cs_num].valid = 0;
+ }
+}
+
+
/**
* msm_spi_clk_max_rate: finds the nearest lower rate for a clk
* @clk the clock for which to find nearest lower rate
@@ -785,117 +819,190 @@
msm_spi_bam_pipe_flush(dd, SPI_BAM_PRODUCER_PIPE);
}
+static int
+msm_spi_bam_process_rx(struct msm_spi *dd, u32 *bytes_to_send, u32 desc_cnt)
+{
+ int ret = 0;
+ u32 data_xfr_size = 0, rem_bc = 0;
+ u32 prod_flags = 0;
+
+ rem_bc = dd->cur_rx_transfer->len - dd->bam.curr_rx_bytes_recvd;
+ data_xfr_size = (rem_bc < *bytes_to_send) ? rem_bc : *bytes_to_send;
+
+ /*
+ * set flags for last descriptor only
+ */
+ if ((desc_cnt == 1)
+ || (*bytes_to_send == data_xfr_size))
+ prod_flags = (dd->write_buf)
+ ? 0 : (SPS_IOVEC_FLAG_EOT | SPS_IOVEC_FLAG_NWD);
+
+ /*
+ * enqueue read buffer in BAM
+ */
+ ret = sps_transfer_one(dd->bam.prod.handle,
+ dd->cur_rx_transfer->rx_dma
+ + dd->bam.curr_rx_bytes_recvd,
+ data_xfr_size, dd, prod_flags);
+ if (ret < 0) {
+ dev_err(dd->dev,
+ "%s: Failed to queue producer BAM transfer",
+ __func__);
+ return ret;
+ }
+
+ dd->bam.curr_rx_bytes_recvd += data_xfr_size;
+ *bytes_to_send -= data_xfr_size;
+ dd->bam.bam_rx_len -= data_xfr_size;
+
+ if (!(dd->cur_rx_transfer->len - dd->bam.curr_rx_bytes_recvd)) {
+ struct spi_transfer *t = dd->cur_rx_transfer;
+ struct spi_transfer *next;
+ if (t->transfer_list.next != &dd->cur_msg->transfers) {
+ next = list_entry(t->transfer_list.next,
+ struct spi_transfer,
+ transfer_list);
+ dd->read_buf = next->rx_buf;
+ dd->cur_rx_transfer = next;
+ dd->bam.curr_rx_bytes_recvd = 0;
+ }
+ }
+ return data_xfr_size;
+}
+
+static int
+msm_spi_bam_process_tx(struct msm_spi *dd, u32 *bytes_to_send, u32 desc_cnt)
+{
+ int ret = 0;
+ u32 data_xfr_size = 0, rem_bc = 0;
+ u32 cons_flags = 0;
+
+ rem_bc = dd->cur_tx_transfer->len - dd->bam.curr_tx_bytes_sent;
+ data_xfr_size = (rem_bc < *bytes_to_send) ? rem_bc : *bytes_to_send;
+
+ /*
+ * set flags for last descriptor only
+ */
+ if ((desc_cnt == 1)
+ || (*bytes_to_send == data_xfr_size))
+ cons_flags = SPS_IOVEC_FLAG_EOT | SPS_IOVEC_FLAG_NWD;
+
+ /*
+ * enqueue write buffer in BAM
+ */
+ ret = sps_transfer_one(dd->bam.cons.handle,
+ dd->cur_tx_transfer->tx_dma
+ + dd->bam.curr_tx_bytes_sent,
+ data_xfr_size, dd, cons_flags);
+ if (ret < 0) {
+ dev_err(dd->dev,
+ "%s: Failed to queue consumer BAM transfer",
+ __func__);
+ return ret;
+ }
+
+ dd->bam.curr_tx_bytes_sent += data_xfr_size;
+ *bytes_to_send -= data_xfr_size;
+ dd->bam.bam_tx_len -= data_xfr_size;
+
+ if (!(dd->cur_tx_transfer->len - dd->bam.curr_tx_bytes_sent)) {
+ struct spi_transfer *t = dd->cur_tx_transfer;
+ struct spi_transfer *next;
+ if (t->transfer_list.next != &dd->cur_msg->transfers) {
+ next = list_entry(t->transfer_list.next,
+ struct spi_transfer,
+ transfer_list);
+ dd->write_buf = next->tx_buf;
+ dd->cur_tx_transfer = next;
+ dd->bam.curr_tx_bytes_sent = 0;
+ }
+ }
+ return data_xfr_size;
+}
+
+
/**
* msm_spi_bam_begin_transfer: transfer dd->tx_bytes_remaining bytes
* using BAM.
* @brief BAM can transfer SPI_MAX_TRFR_BTWN_RESETS byte at a single
* transfer. Between transfer QUP must change to reset state. A loop is
- * issuing a single BAM transfer at a time. If another tsranfer is
- * required, it waits for the trasfer to finish, then moving to reset
- * state, and back to run state to issue the next transfer.
- * The function dose not wait for the last transfer to end, or if only
- * a single transfer is required, the function dose not wait for it to
- * end.
- * @timeout max time in jiffies to wait for a transfer to finish.
+ * issuing a single BAM transfer at a time.
* @return zero on success
*/
static int
-msm_spi_bam_begin_transfer(struct msm_spi *dd, u32 timeout, u8 bpw)
+msm_spi_bam_begin_transfer(struct msm_spi *dd)
{
- u32 bytes_to_send, bytes_sent, n_words_xfr, cons_flags, prod_flags;
- int ret;
- /*
- * QUP must move to reset mode every 64K-1 bytes of transfer
- * (counter is 16 bit)
- */
- if (dd->tx_bytes_remaining > SPI_MAX_TRFR_BTWN_RESETS) {
- /* assert chip select unconditionally */
- u32 spi_ioc = readl_relaxed(dd->base + SPI_IO_CONTROL);
- if (!(spi_ioc & SPI_IO_C_FORCE_CS))
- writel_relaxed(spi_ioc | SPI_IO_C_FORCE_CS,
- dd->base + SPI_IO_CONTROL);
+ u32 tx_bytes_to_send = 0, rx_bytes_to_recv = 0;
+ u32 n_words_xfr;
+ s32 ret = 0;
+ u32 prod_desc_cnt = SPI_BAM_MAX_DESC_NUM - 1;
+ u32 cons_desc_cnt = SPI_BAM_MAX_DESC_NUM - 1;
+ u32 byte_count = 0;
+
+
+ rx_bytes_to_recv = min_t(u32, dd->bam.bam_rx_len,
+ SPI_MAX_TRFR_BTWN_RESETS);
+ tx_bytes_to_send = min_t(u32, dd->bam.bam_tx_len,
+ SPI_MAX_TRFR_BTWN_RESETS);
+ n_words_xfr = DIV_ROUND_UP(rx_bytes_to_recv,
+ dd->bytes_per_word);
+
+ msm_spi_set_mx_counts(dd, n_words_xfr);
+ ret = msm_spi_set_state(dd, SPI_OP_STATE_RUN);
+ if (ret < 0) {
+ dev_err(dd->dev,
+ "%s: Failed to set QUP state to run",
+ __func__);
+ goto xfr_err;
}
- /* Following flags are required since we are waiting on all transfers */
- cons_flags = SPS_IOVEC_FLAG_EOT | SPS_IOVEC_FLAG_NWD;
- /*
- * on a balanced transaction, BAM will set the flags on the producer
- * pipe based on the flags set on the consumer pipe
- */
- prod_flags = (dd->write_buf) ? 0 : cons_flags;
-
- while (dd->tx_bytes_remaining > 0) {
- bytes_sent = dd->cur_transfer->len - dd->tx_bytes_remaining;
- bytes_to_send = min_t(u32, dd->tx_bytes_remaining
- , SPI_MAX_TRFR_BTWN_RESETS);
- n_words_xfr = DIV_ROUND_UP(bytes_to_send
- , dd->bytes_per_word);
-
- msm_spi_set_mx_counts(dd, n_words_xfr);
-
- ret = msm_spi_set_state(dd, SPI_OP_STATE_RUN);
- if (ret < 0) {
- dev_err(dd->dev,
- "%s: Failed to set QUP state to run",
- __func__);
- goto xfr_err;
+ while ((rx_bytes_to_recv + tx_bytes_to_send) &&
+ ((cons_desc_cnt + prod_desc_cnt) > 0)) {
+ if (dd->read_buf && (prod_desc_cnt > 0)) {
+ ret = msm_spi_bam_process_rx(dd, &rx_bytes_to_recv,
+ prod_desc_cnt);
+ if (ret < 0)
+ goto xfr_err;
+ prod_desc_cnt--;
}
- /* enqueue read buffer in BAM */
- if (dd->read_buf) {
- ret = sps_transfer_one(dd->bam.prod.handle,
- dd->cur_transfer->rx_dma + bytes_sent,
- bytes_to_send, dd, prod_flags);
- if (ret < 0) {
- dev_err(dd->dev,
- "%s: Failed to queue producer BAM transfer",
- __func__);
+ if (dd->write_buf && (cons_desc_cnt > 0)) {
+ ret = msm_spi_bam_process_tx(dd, &tx_bytes_to_send,
+ cons_desc_cnt);
+ if (ret < 0)
goto xfr_err;
- }
+ cons_desc_cnt--;
}
-
- /* enqueue write buffer in BAM */
- if (dd->write_buf) {
- ret = sps_transfer_one(dd->bam.cons.handle,
- dd->cur_transfer->tx_dma + bytes_sent,
- bytes_to_send, dd, cons_flags);
- if (ret < 0) {
- dev_err(dd->dev,
- "%s: Failed to queue consumer BAM transfer",
- __func__);
- goto xfr_err;
- }
- }
-
- dd->tx_bytes_remaining -= bytes_to_send;
-
- /* move to reset state after SPI_MAX_TRFR_BTWN_RESETS */
- if (dd->tx_bytes_remaining > 0) {
- if (!wait_for_completion_timeout(
- &dd->transfer_complete, timeout)) {
- dev_err(dd->dev,
- "%s: SPI transaction timeout",
- __func__);
- dd->cur_msg->status = -EIO;
- ret = -EIO;
- goto xfr_err;
- }
- ret = msm_spi_set_state(dd, SPI_OP_STATE_RESET);
- if (ret < 0) {
- dev_err(dd->dev,
- "%s: Failed to set QUP state to reset",
- __func__);
- goto xfr_err;
- }
- init_completion(&dd->transfer_complete);
- }
+ byte_count += ret;
}
+
+ dd->tx_bytes_remaining -= min_t(u32, byte_count,
+ SPI_MAX_TRFR_BTWN_RESETS);
return 0;
-
xfr_err:
return ret;
}
+static int
+msm_spi_bam_next_transfer(struct msm_spi *dd)
+{
+ if (dd->mode != SPI_BAM_MODE)
+ return 0;
+
+ if (dd->tx_bytes_remaining > 0) {
+ init_completion(&dd->transfer_complete);
+ if (msm_spi_set_state(dd, SPI_OP_STATE_RESET))
+ return 0;
+ if ((msm_spi_bam_begin_transfer(dd)) < 0) {
+ dev_err(dd->dev, "%s: BAM transfer setup failed\n",
+ __func__);
+ return 0;
+ }
+ return 1;
+ }
+ return 0;
+}
+
static void msm_spi_setup_dm_transfer(struct msm_spi *dd)
{
dmov_box *box;
@@ -1098,6 +1205,16 @@
return 0;
}
+static int msm_spi_dma_send_next(struct msm_spi *dd)
+{
+ int ret = 0;
+ if (dd->mode == SPI_DMOV_MODE)
+ ret = msm_spi_dm_send_next(dd);
+ if (dd->mode == SPI_BAM_MODE)
+ ret = msm_spi_bam_next_transfer(dd);
+ return ret;
+}
+
static inline void msm_spi_ack_transfer(struct msm_spi *dd)
{
writel_relaxed(SPI_OP_MAX_INPUT_DONE_FLAG |
@@ -1297,14 +1414,14 @@
}
/**
- * msm_spi_dma_map_buffers: prepares buffer for DMA transfer
+ * msm_spi_dmov_map_buffers: prepares buffer for DMA transfer
* @return zero on success or negative error code
*
* calls dma_map_single() on the read/write buffers, effectively invalidating
* their cash entries. for For WR-WR and WR-RD transfers, allocates temporary
* buffer and copy the data to/from the client buffers
*/
-static int msm_spi_dma_map_buffers(struct msm_spi *dd)
+static int msm_spi_dmov_map_buffers(struct msm_spi *dd)
{
struct device *dev;
struct spi_transfer *first_xfr;
@@ -1323,7 +1440,7 @@
* For WR-WR and WR-RD transfers, we allocate our own temporary
* buffer and copy the data to/from the client buffers.
*/
- if (dd->multi_xfr) {
+ if (!dd->qup_ver && dd->multi_xfr) {
dd->temp_buf = kzalloc(dd->cur_msg_len,
GFP_KERNEL | __GFP_DMA);
if (!dd->temp_buf)
@@ -1384,6 +1501,70 @@
return ret;
}
+static int msm_spi_bam_map_buffers(struct msm_spi *dd)
+{
+ int ret = -EINVAL;
+ struct device *dev;
+ struct spi_transfer *first_xfr;
+ struct spi_transfer *nxt_xfr;
+ void *tx_buf, *rx_buf;
+ u32 tx_len, rx_len;
+ int num_xfrs_grped = dd->num_xfrs_grped;
+
+ dev = &dd->cur_msg->spi->dev;
+ first_xfr = dd->cur_transfer;
+
+ do {
+ tx_buf = (void *)first_xfr->tx_buf;
+ rx_buf = first_xfr->rx_buf;
+ tx_len = rx_len = first_xfr->len;
+ if (tx_buf != NULL) {
+ first_xfr->tx_dma = dma_map_single(dev, tx_buf,
+ tx_len, DMA_TO_DEVICE);
+ if (dma_mapping_error(NULL, first_xfr->tx_dma)) {
+ ret = -ENOMEM;
+ goto error;
+ }
+ }
+
+ if (rx_buf != NULL) {
+ first_xfr->rx_dma = dma_map_single(dev, rx_buf, rx_len,
+ DMA_FROM_DEVICE);
+ if (dma_mapping_error(NULL, first_xfr->rx_dma)) {
+ if (tx_buf != NULL)
+ dma_unmap_single(NULL,
+ first_xfr->tx_dma,
+ tx_len, DMA_TO_DEVICE);
+ ret = -ENOMEM;
+ goto error;
+ }
+ }
+
+ nxt_xfr = list_entry(first_xfr->transfer_list.next,
+ struct spi_transfer, transfer_list);
+
+ if (nxt_xfr == NULL)
+ break;
+ num_xfrs_grped--;
+ first_xfr = nxt_xfr;
+ } while (num_xfrs_grped > 0);
+
+ return 0;
+error:
+ msm_spi_dma_unmap_buffers(dd);
+ return ret;
+}
+
+static int msm_spi_dma_map_buffers(struct msm_spi *dd)
+{
+ int ret = 0;
+ if (dd->mode == SPI_DMOV_MODE)
+ ret = msm_spi_dmov_map_buffers(dd);
+ else if (dd->mode == SPI_BAM_MODE)
+ ret = msm_spi_bam_map_buffers(dd);
+ return ret;
+}
+
static void msm_spi_dmov_unmap_buffers(struct msm_spi *dd)
{
struct device *dev;
@@ -1455,21 +1636,39 @@
static void msm_spi_bam_unmap_buffers(struct msm_spi *dd)
{
struct device *dev;
+ int num_xfrs_grped = dd->num_xfrs_grped;
+ struct spi_transfer *first_xfr;
+ struct spi_transfer *nxt_xfr;
+ void *tx_buf, *rx_buf;
+ u32 tx_len, rx_len;
+
+ dev = &dd->cur_msg->spi->dev;
+ first_xfr = dd->cur_transfer;
/* mapped by client */
if (dd->cur_msg->is_dma_mapped)
return;
- dev = &dd->cur_msg->spi->dev;
- if (dd->cur_transfer->rx_buf)
- dma_unmap_single(dev, dd->cur_transfer->rx_dma,
- dd->cur_transfer->len,
- DMA_FROM_DEVICE);
+ do {
+ tx_buf = (void *)first_xfr->tx_buf;
+ rx_buf = first_xfr->rx_buf;
+ tx_len = rx_len = first_xfr->len;
+ if (tx_buf != NULL)
+ dma_unmap_single(dev, first_xfr->tx_dma,
+ tx_len, DMA_TO_DEVICE);
- if (dd->cur_transfer->tx_buf)
- dma_unmap_single(dev, dd->cur_transfer->tx_dma,
- dd->cur_transfer->len,
- DMA_TO_DEVICE);
+ if (rx_buf != NULL)
+ dma_unmap_single(dev, first_xfr->rx_dma,
+ rx_len, DMA_FROM_DEVICE);
+
+ nxt_xfr = list_entry(first_xfr->transfer_list.next,
+ struct spi_transfer, transfer_list);
+
+ if (nxt_xfr == NULL)
+ break;
+ num_xfrs_grped--;
+ first_xfr = nxt_xfr;
+ } while (num_xfrs_grped > 0);
}
static inline void msm_spi_dma_unmap_buffers(struct msm_spi *dd)
@@ -1506,7 +1705,8 @@
if (dd->cur_msg_len < 3*dd->input_block_size)
return false;
- if (dd->multi_xfr && !dd->read_len && !dd->write_len)
+ if ((dd->qup_ver != SPI_QUP_VERSION_BFAM) &&
+ dd->multi_xfr && !dd->read_len && !dd->write_len)
return false;
if (dd->qup_ver == SPI_QUP_VERSION_NONE) {
@@ -1688,11 +1888,17 @@
msm_spi_set_transfer_mode(dd, bpw, read_count);
msm_spi_set_mx_counts(dd, read_count);
- if ((dd->mode == SPI_BAM_MODE) || (dd->mode == SPI_DMOV_MODE))
+ if (dd->mode == SPI_DMOV_MODE) {
if (msm_spi_dma_map_buffers(dd) < 0) {
pr_err("Mapping DMA buffers\n");
return;
+ }
+ } else if (dd->mode == SPI_BAM_MODE) {
+ if (msm_spi_dma_map_buffers(dd) < 0) {
+ pr_err("Mapping DMA buffers\n");
+ return;
}
+ }
msm_spi_set_qup_io_modes(dd);
msm_spi_set_spi_config(dd, bpw);
msm_spi_set_qup_config(dd, bpw);
@@ -1712,7 +1918,7 @@
goto transfer_end;
msm_spi_start_write(dd, read_count);
} else if (dd->mode == SPI_BAM_MODE) {
- if ((msm_spi_bam_begin_transfer(dd, timeout, bpw)) < 0)
+ if ((msm_spi_bam_begin_transfer(dd)) < 0)
dev_err(dd->dev, "%s: BAM transfer setup failed\n",
__func__);
}
@@ -1749,9 +1955,10 @@
msm_spi_bam_flush(dd);
break;
}
- } while (msm_spi_dm_send_next(dd));
+ } while (msm_spi_dma_send_next(dd));
- msm_spi_udelay(dd->cur_transfer->delay_usecs);
+ msm_spi_udelay(dd->xfrs_delay_usec);
+
transfer_end:
msm_spi_dma_unmap_buffers(dd);
dd->mode = SPI_MODE_NONE;
@@ -1803,26 +2010,6 @@
dd->multi_xfr = 1;
}
-static inline int combine_transfers(struct msm_spi *dd)
-{
- struct spi_transfer *t = dd->cur_transfer;
- struct spi_transfer *nxt;
- int xfrs_grped = 1;
-
- dd->cur_msg_len = dd->cur_transfer->len;
- while (t->transfer_list.next != &dd->cur_msg->transfers) {
- nxt = list_entry(t->transfer_list.next,
- struct spi_transfer,
- transfer_list);
- if (t->cs_change != nxt->cs_change)
- return xfrs_grped;
- dd->cur_msg_len += nxt->len;
- xfrs_grped++;
- t = nxt;
- }
- return xfrs_grped;
-}
-
static inline void write_force_cs(struct msm_spi *dd, bool set_flag)
{
u32 spi_ioc;
@@ -1839,133 +2026,160 @@
writel_relaxed(spi_ioc, dd->base + SPI_IO_CONTROL);
}
+static inline int combine_transfers(struct msm_spi *dd)
+{
+ struct spi_transfer *t = dd->cur_transfer;
+ struct spi_transfer *nxt;
+ int xfrs_grped = 1;
+ dd->xfrs_delay_usec = 0;
+
+ dd->bam.bam_rx_len = dd->bam.bam_tx_len = 0;
+
+ dd->cur_msg_len = dd->cur_transfer->len;
+
+ if (dd->cur_transfer->tx_buf)
+ dd->bam.bam_tx_len += dd->cur_transfer->len;
+ if (dd->cur_transfer->rx_buf)
+ dd->bam.bam_rx_len += dd->cur_transfer->len;
+
+ while (t->transfer_list.next != &dd->cur_msg->transfers) {
+ nxt = list_entry(t->transfer_list.next,
+ struct spi_transfer,
+ transfer_list);
+ if (t->cs_change != nxt->cs_change)
+ return xfrs_grped;
+ if (t->delay_usecs) {
+ dd->xfrs_delay_usec = t->delay_usecs;
+ dev_info(dd->dev, "SPI slave requests delay per txn :%d usecs",
+ t->delay_usecs);
+ return xfrs_grped;
+ }
+ if (nxt->tx_buf)
+ dd->bam.bam_tx_len += nxt->len;
+ if (nxt->rx_buf)
+ dd->bam.bam_rx_len += nxt->len;
+
+ dd->cur_msg_len += nxt->len;
+ xfrs_grped++;
+ t = nxt;
+ }
+
+ if (1 == xfrs_grped)
+ dd->xfrs_delay_usec = dd->cur_transfer->delay_usecs;
+
+ return xfrs_grped;
+}
+
static void msm_spi_process_message(struct msm_spi *dd)
{
int xfrs_grped = 0;
- int cs_num;
int rc;
- bool xfer_delay = false;
- struct spi_transfer *tr;
+ dd->num_xfrs_grped = 0;
+ dd->bam.curr_rx_bytes_recvd = dd->bam.curr_tx_bytes_sent = 0;
dd->write_xfr_cnt = dd->read_xfr_cnt = 0;
- cs_num = dd->cur_msg->spi->chip_select;
- if ((!(dd->cur_msg->spi->mode & SPI_LOOP)) &&
- (!(dd->cs_gpios[cs_num].valid)) &&
- (dd->cs_gpios[cs_num].gpio_num >= 0)) {
- rc = gpio_request(dd->cs_gpios[cs_num].gpio_num,
- spi_cs_rsrcs[cs_num]);
- if (rc) {
- dev_err(dd->dev, "gpio_request for pin %d failed with "
- "error %d\n", dd->cs_gpios[cs_num].gpio_num,
- rc);
- return;
- }
- dd->cs_gpios[cs_num].valid = 1;
- }
+ rc = msm_spi_request_cs_gpio(dd);
+ if (rc)
+ return;
- list_for_each_entry(tr,
- &dd->cur_msg->transfers,
- transfer_list) {
- if (tr->delay_usecs) {
- dev_info(dd->dev, "SPI slave requests delay per txn :%d",
- tr->delay_usecs);
- xfer_delay = true;
- break;
- }
- }
-
- /* Don't combine xfers if delay is needed after every xfer */
- if (dd->qup_ver || xfer_delay) {
- if (dd->qup_ver)
- write_force_cs(dd, 0);
- list_for_each_entry(dd->cur_transfer,
- &dd->cur_msg->transfers,
- transfer_list) {
- struct spi_transfer *t = dd->cur_transfer;
- struct spi_transfer *nxt;
-
- if (t->transfer_list.next != &dd->cur_msg->transfers) {
- nxt = list_entry(t->transfer_list.next,
+ dd->cur_transfer = list_first_entry(&dd->cur_msg->transfers,
struct spi_transfer,
transfer_list);
- if (dd->qup_ver &&
- t->cs_change == nxt->cs_change)
- write_force_cs(dd, 1);
- else if (dd->qup_ver)
- write_force_cs(dd, 0);
- }
+ get_transfer_length(dd);
+ if (dd->qup_ver || (dd->multi_xfr && !dd->read_len && !dd->write_len)) {
- dd->cur_msg_len = dd->cur_transfer->len;
- msm_spi_process_transfer(dd);
- }
- } else {
- dd->cur_transfer = list_first_entry(&dd->cur_msg->transfers,
- struct spi_transfer,
- transfer_list);
- get_transfer_length(dd);
- if (dd->multi_xfr && !dd->read_len && !dd->write_len) {
- /*
- * Handling of multi-transfers.
- * FIFO mode is used by default
- */
- list_for_each_entry(dd->cur_transfer,
- &dd->cur_msg->transfers,
- transfer_list) {
- if (!dd->cur_transfer->len)
- goto error;
- if (xfrs_grped) {
- xfrs_grped--;
- continue;
- } else {
- dd->read_len = dd->write_len = 0;
- xfrs_grped = combine_transfers(dd);
- }
+ if (dd->qup_ver)
+ write_force_cs(dd, 0);
- dd->cur_tx_transfer = dd->cur_transfer;
- dd->cur_rx_transfer = dd->cur_transfer;
- msm_spi_process_transfer(dd);
+ /*
+ * Handling of multi-transfers.
+ * FIFO mode is used by default
+ */
+ list_for_each_entry(dd->cur_transfer,
+ &dd->cur_msg->transfers,
+ transfer_list) {
+ if (!dd->cur_transfer->len)
+ goto error;
+ if (xfrs_grped) {
xfrs_grped--;
- }
- } else {
- /* Handling of a single transfer or
- * WR-WR or WR-RD transfers
- */
- if ((!dd->cur_msg->is_dma_mapped) &&
- (msm_spi_use_dma(dd, dd->cur_transfer,
- dd->cur_transfer->bits_per_word))) {
- /* Mapping of DMA buffers */
- int ret = msm_spi_dma_map_buffers(dd);
- if (ret < 0) {
- dd->cur_msg->status = ret;
- goto error;
- }
+ continue;
+ } else {
+ dd->read_len = dd->write_len = 0;
+ xfrs_grped = combine_transfers(dd);
+ dd->num_xfrs_grped = xfrs_grped;
+ if (dd->qup_ver)
+ write_force_cs(dd, 1);
}
dd->cur_tx_transfer = dd->cur_transfer;
dd->cur_rx_transfer = dd->cur_transfer;
msm_spi_process_transfer(dd);
+ if (dd->qup_ver && !dd->xfrs_delay_usec)
+ write_force_cs(dd, 0);
+ xfrs_grped--;
}
+ } else {
+ /* Handling of a single transfer or
+ * WR-WR or WR-RD transfers
+ */
+ if ((!dd->cur_msg->is_dma_mapped) &&
+ (msm_spi_use_dma(dd, dd->cur_transfer,
+ dd->cur_transfer->bits_per_word))) {
+ /* Mapping of DMA buffers */
+ int ret = msm_spi_dma_map_buffers(dd);
+ if (ret < 0) {
+ dd->cur_msg->status = ret;
+ goto error;
+ }
+ }
+
+ dd->cur_tx_transfer = dd->cur_transfer;
+ dd->cur_rx_transfer = dd->cur_transfer;
+ dd->num_xfrs_grped = 1;
+ msm_spi_process_transfer(dd);
}
return;
error:
- if (dd->cs_gpios[cs_num].valid) {
- gpio_free(dd->cs_gpios[cs_num].gpio_num);
- dd->cs_gpios[cs_num].valid = 0;
- }
+ msm_spi_free_cs_gpio(dd);
}
-/* workqueue - pull messages from queue & process */
-static void msm_spi_workq(struct work_struct *work)
+/**
+ * msm_spi_transfer_one_message: To process one spi message at a time
+ * @master: spi master controller reference
+ * @msg: one multi-segment SPI transaction
+ * @return zero on success or negative error value
+ *
+ */
+static int msm_spi_transfer_one_message(struct spi_master *master,
+ struct spi_message *msg)
{
- struct msm_spi *dd =
- container_of(work, struct msm_spi, work_data);
+ struct msm_spi *dd;
+ struct spi_transfer *tr;
unsigned long flags;
- u32 status_error = 0;
+ u32 status_error = 0;
- pm_runtime_get_sync(dd->dev);
+ dd = spi_master_get_devdata(master);
+
+ if (list_empty(&msg->transfers) || !msg->complete)
+ return -EINVAL;
+
+ list_for_each_entry(tr, &msg->transfers, transfer_list) {
+ /* Check message parameters */
+ if (tr->speed_hz > dd->pdata->max_clock_speed ||
+ (tr->bits_per_word &&
+ (tr->bits_per_word < 4 || tr->bits_per_word > 32)) ||
+ (tr->tx_buf == NULL && tr->rx_buf == NULL)) {
+ dev_err(dd->dev,
+ "Invalid transfer: %d Hz, %d bpw tx=%p, rx=%p\n",
+ tr->speed_hz, tr->bits_per_word,
+ tr->tx_buf, tr->rx_buf);
+ status_error = -EINVAL;
+ goto out;
+ }
+ }
mutex_lock(&dd->core_lock);
@@ -1980,27 +2194,26 @@
if (dd->use_rlock)
remote_mutex_lock(&dd->r_lock);
- if (!msm_spi_is_valid_state(dd)) {
+ spin_lock_irqsave(&dd->queue_lock, flags);
+ dd->transfer_pending = 1;
+ spin_unlock_irqrestore(&dd->queue_lock, flags);
+
+ if (dd->suspended || !msm_spi_is_valid_state(dd)) {
dev_err(dd->dev, "%s: SPI operational state not valid\n",
__func__);
status_error = 1;
}
-
spin_lock_irqsave(&dd->queue_lock, flags);
dd->transfer_pending = 1;
- while (!list_empty(&dd->queue)) {
- dd->cur_msg = list_entry(dd->queue.next,
- struct spi_message, queue);
- list_del_init(&dd->cur_msg->queue);
- spin_unlock_irqrestore(&dd->queue_lock, flags);
- if (status_error)
+ dd->cur_msg = msg;
+ spin_unlock_irqrestore(&dd->queue_lock, flags);
+
+ if (status_error)
dd->cur_msg->status = -EIO;
- else
- msm_spi_process_message(dd);
- if (dd->cur_msg->complete)
- dd->cur_msg->complete(dd->cur_msg->context);
- spin_lock_irqsave(&dd->queue_lock, flags);
- }
+ else
+ msm_spi_process_message(dd);
+
+ spin_lock_irqsave(&dd->queue_lock, flags);
dd->transfer_pending = 0;
spin_unlock_irqrestore(&dd->queue_lock, flags);
@@ -2009,44 +2222,33 @@
mutex_unlock(&dd->core_lock);
- pm_runtime_mark_last_busy(dd->dev);
- pm_runtime_put_autosuspend(dd->dev);
-
- /* If needed, this can be done after the current message is complete,
- and work can be continued upon resume. No motivation for now. */
+ /*
+ * If needed, this can be done after the current message is complete,
+ * and work can be continued upon resume. No motivation for now.
+ */
if (dd->suspended)
wake_up_interruptible(&dd->continue_suspend);
+
+out:
+ dd->cur_msg->status = status_error;
+ spi_finalize_current_message(master);
+ return 0;
}
-static int msm_spi_transfer(struct spi_device *spi, struct spi_message *msg)
+static int msm_spi_prepare_transfer_hardware(struct spi_master *master)
{
- struct msm_spi *dd;
- unsigned long flags;
- struct spi_transfer *tr;
+ struct msm_spi *dd = spi_master_get_devdata(master);
- dd = spi_master_get_devdata(spi->master);
+ pm_runtime_get_sync(dd->dev);
+ return 0;
+}
- if (list_empty(&msg->transfers) || !msg->complete)
- return -EINVAL;
+static int msm_spi_unprepare_transfer_hardware(struct spi_master *master)
+{
+ struct msm_spi *dd = spi_master_get_devdata(master);
- list_for_each_entry(tr, &msg->transfers, transfer_list) {
- /* Check message parameters */
- if (tr->speed_hz > dd->pdata->max_clock_speed ||
- (tr->bits_per_word &&
- (tr->bits_per_word < 4 || tr->bits_per_word > 32)) ||
- (tr->tx_buf == NULL && tr->rx_buf == NULL)) {
- dev_err(&spi->dev, "Invalid transfer: %d Hz, %d bpw"
- "tx=%p, rx=%p\n",
- tr->speed_hz, tr->bits_per_word,
- tr->tx_buf, tr->rx_buf);
- return -EINVAL;
- }
- }
-
- spin_lock_irqsave(&dd->queue_lock, flags);
- list_add_tail(&msg->queue, &dd->queue);
- spin_unlock_irqrestore(&dd->queue_lock, flags);
- queue_work(dd->workqueue, &dd->work_data);
+ pm_runtime_mark_last_busy(dd->dev);
+ pm_runtime_put_autosuspend(dd->dev);
return 0;
}
@@ -2698,6 +2900,8 @@
&dd->cs_gpios[2].gpio_num, DT_OPT, DT_GPIO, -1},
{"qcom,gpio-cs3",
&dd->cs_gpios[3].gpio_num, DT_OPT, DT_GPIO, -1},
+ {"qcom,rt-priority",
+ &pdata->rt_priority, DT_OPT, DT_BOOL, 0},
{NULL, NULL, 0, 0, 0},
};
@@ -2791,7 +2995,11 @@
master->mode_bits = SPI_SUPPORTED_MODES;
master->num_chipselect = SPI_NUM_CHIPSELECTS;
master->setup = msm_spi_setup;
- master->transfer = msm_spi_transfer;
+ master->prepare_transfer_hardware = msm_spi_prepare_transfer_hardware;
+ master->transfer_one_message = msm_spi_transfer_one_message;
+ master->unprepare_transfer_hardware
+ = msm_spi_unprepare_transfer_hardware;
+
platform_set_drvdata(pdev, master);
dd = spi_master_get_devdata(master);
@@ -2831,6 +3039,7 @@
for (i = 0; i < ARRAY_SIZE(spi_cs_rsrcs); ++i)
dd->cs_gpios[i].valid = 0;
+ master->rt = pdata->rt_priority;
dd->pdata = pdata;
resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!resource) {
@@ -2892,13 +3101,6 @@
spin_lock_init(&dd->queue_lock);
mutex_init(&dd->core_lock);
- INIT_LIST_HEAD(&dd->queue);
- INIT_WORK(&dd->work_data, msm_spi_workq);
- init_waitqueue_head(&dd->continue_suspend);
- dd->workqueue = create_singlethread_workqueue(
- dev_name(master->dev.parent));
- if (!dd->workqueue)
- goto err_probe_workq;
if (!devm_request_mem_region(&pdev->dev, dd->mem_phys_addr,
dd->mem_size, SPI_DRV_NAME)) {
@@ -3075,8 +3277,6 @@
}
err_probe_rlock_init:
err_probe_reqmem:
- destroy_workqueue(dd->workqueue);
-err_probe_workq:
err_probe_res:
spi_master_put(master);
err_probe_exit:
@@ -3239,7 +3439,6 @@
clk_put(dd->clk);
clk_put(dd->pclk);
msm_spi_clk_path_teardown(dd);
- destroy_workqueue(dd->workqueue);
platform_set_drvdata(pdev, 0);
spi_unregister_master(master);
spi_master_put(master);
diff --git a/drivers/spi/spi_qsd.h b/drivers/spi/spi_qsd.h
index 2a67a61..a0c399b 100644
--- a/drivers/spi/spi_qsd.h
+++ b/drivers/spi/spi_qsd.h
@@ -291,6 +291,10 @@
struct msm_spi_bam_pipe prod;
struct msm_spi_bam_pipe cons;
bool deregister_required;
+ u32 curr_rx_bytes_recvd;
+ u32 curr_tx_bytes_sent;
+ u32 bam_rx_len;
+ u32 bam_tx_len;
};
struct msm_spi {
@@ -300,9 +304,6 @@
struct device *dev;
spinlock_t queue_lock;
struct mutex core_lock;
- struct list_head queue;
- struct workqueue_struct *workqueue;
- struct work_struct work_data;
struct spi_message *cur_msg;
struct spi_transfer *cur_transfer;
struct completion transfer_complete;
@@ -391,6 +392,8 @@
struct spi_cs_gpio cs_gpios[ARRAY_SIZE(spi_cs_rsrcs)];
enum msm_spi_qup_version qup_ver;
int max_trfr_len;
+ int num_xfrs_grped;
+ u16 xfrs_delay_usec;
};
/* Forward declaration */
diff --git a/drivers/thermal/msm_thermal.c b/drivers/thermal/msm_thermal.c
index 73513ec..7b623e4 100644
--- a/drivers/thermal/msm_thermal.c
+++ b/drivers/thermal/msm_thermal.c
@@ -40,6 +40,7 @@
#define MAX_CURRENT_UA 1000000
#define MAX_RAILS 5
#define MAX_THRESHOLD 2
+#define MONITOR_ALL_TSENS -1
static struct msm_thermal_data msm_thermal_info;
static struct delayed_work check_temp_work;
@@ -53,10 +54,13 @@
static struct work_struct timer_work;
static struct task_struct *hotplug_task;
static struct task_struct *freq_mitigation_task;
+static struct task_struct *thermal_monitor_task;
static struct completion hotplug_notify_complete;
static struct completion freq_mitigation_complete;
+static struct completion thermal_monitor_complete;
static int enabled;
+static int polling_enabled;
static int rails_cnt;
static int psm_rails_cnt;
static int ocr_rail_cnt;
@@ -78,6 +82,8 @@
static bool ocr_enabled;
static bool ocr_nodes_called;
static bool ocr_probed;
+static bool interrupt_mode_enable;
+static bool msm_thermal_probed;
static int *tsens_id_map;
static DEFINE_MUTEX(vdd_rstr_mutex);
static DEFINE_MUTEX(psm_mutex);
@@ -92,9 +98,16 @@
THRESHOLD_MAX_NR,
};
+enum sensor_id_type {
+ THERM_ZONE_ID,
+ THERM_TSENS_ID,
+ THERM_ID_MAX_NR,
+};
+
struct cpu_info {
uint32_t cpu;
const char *sensor_type;
+ enum sensor_id_type id_type;
uint32_t sensor_id;
bool offline;
bool user_offline;
@@ -108,6 +121,21 @@
bool freq_thresh_clear;
};
+struct threshold_info;
+struct therm_threshold {
+ int32_t sensor_id;
+ struct sensor_threshold threshold[MAX_THRESHOLD];
+ int32_t trip_triggered;
+ void (*notify)(struct therm_threshold *);
+ struct threshold_info *parent;
+};
+
+struct threshold_info {
+ uint32_t thresh_ct;
+ bool thresh_triggered;
+ struct therm_threshold *thresh_list;
+};
+
struct rail {
const char *name;
uint32_t freq_req;
@@ -131,10 +159,16 @@
struct attribute_group attr_gp;
};
+enum msm_thresh_list {
+ MSM_VDD_RESTRICTION,
+ MSM_LIST_MAX_NR,
+};
+
static struct psm_rail *psm_rails;
static struct psm_rail *ocr_rails;
static struct rail *rails;
static struct cpu_info cpus[NR_CPUS];
+static struct threshold_info *thresh;
struct vdd_rstr_enable {
struct kobj_attribute ko_attr;
@@ -635,7 +669,7 @@
static int check_sensor_id(int sensor_id)
{
int i = 0;
- bool hw_id_found;
+ bool hw_id_found = false;
int ret = 0;
for (i = 0; i < max_tsens_num; i++) {
@@ -774,31 +808,63 @@
return ret;
}
-static int set_threshold(uint32_t sensor_id,
+static int therm_get_temp(uint32_t id, enum sensor_id_type type, long *temp)
+{
+ int ret = 0;
+ struct tsens_device tsens_dev;
+
+ if (!temp) {
+ pr_err("Invalid value\n");
+ ret = -EINVAL;
+ goto get_temp_exit;
+ }
+
+ switch (type) {
+ case THERM_ZONE_ID:
+ tsens_dev.sensor_num = tsens_id_map[id];
+ break;
+ case THERM_TSENS_ID:
+ tsens_dev.sensor_num = id;
+ break;
+ default:
+ pr_err("Invalid type\n");
+ ret = -EINVAL;
+ goto get_temp_exit;
+ break;
+ }
+
+ ret = tsens_get_temp(&tsens_dev, temp);
+ if (ret) {
+ pr_err("Unable to read TSENS sensor %d\n",
+ tsens_dev.sensor_num);
+ goto get_temp_exit;
+ }
+
+get_temp_exit:
+ return ret;
+}
+
+static int set_threshold(uint32_t zone_id,
struct sensor_threshold *threshold)
{
- struct tsens_device tsens_dev;
int i = 0, ret = 0;
long temp;
- if ((!threshold) || check_sensor_id(sensor_id)) {
+ if ((!threshold) || (zone_id >= max_tsens_num)) {
pr_err("%s: Invalid input\n", KBUILD_MODNAME);
ret = -EINVAL;
goto set_threshold_exit;
}
- tsens_dev.sensor_num = sensor_id;
- ret = tsens_get_temp(&tsens_dev, &temp);
- if (ret) {
- pr_err("%s: Unable to read TSENS sensor %d\n",
- KBUILD_MODNAME, tsens_dev.sensor_num);
+ ret = therm_get_temp(zone_id, THERM_ZONE_ID, &temp);
+ if (ret)
goto set_threshold_exit;
- }
+
while (i < MAX_THRESHOLD) {
switch (threshold[i].trip) {
case THERMAL_TRIP_CONFIGURABLE_HI:
if (threshold[i].temp >= temp) {
- ret = set_and_activate_threshold(sensor_id,
+ ret = set_and_activate_threshold(zone_id,
&threshold[i]);
if (ret)
goto set_threshold_exit;
@@ -806,7 +872,7 @@
break;
case THERMAL_TRIP_CONFIGURABLE_LOW:
if (threshold[i].temp <= temp) {
- ret = set_and_activate_threshold(sensor_id,
+ ret = set_and_activate_threshold(zone_id,
&threshold[i]);
if (ret)
goto set_threshold_exit;
@@ -904,7 +970,9 @@
return -EINVAL;
while (!kthread_should_stop()) {
- wait_for_completion(&hotplug_notify_complete);
+ while (wait_for_completion_interruptible(
+ &hotplug_notify_complete) != 0)
+ ;
INIT_COMPLETION(hotplug_notify_complete);
mask = 0;
@@ -942,7 +1010,6 @@
static int do_ocr(void)
{
- struct tsens_device tsens_dev;
long temp = 0;
int ret = 0;
int i = 0, j = 0;
@@ -953,11 +1020,10 @@
mutex_lock(&ocr_mutex);
for (i = 0; i < max_tsens_num; i++) {
- tsens_dev.sensor_num = tsens_id_map[i];
- ret = tsens_get_temp(&tsens_dev, &temp);
+ ret = therm_get_temp(tsens_id_map[i], THERM_TSENS_ID, &temp);
if (ret) {
pr_debug("%s: Unable to read TSENS sensor %d\n",
- __func__, tsens_dev.sensor_num);
+ __func__, tsens_id_map[i]);
auto_cnt++;
continue;
}
@@ -999,7 +1065,6 @@
static int do_vdd_restriction(void)
{
- struct tsens_device tsens_dev;
long temp = 0;
int ret = 0;
int i = 0;
@@ -1015,11 +1080,10 @@
mutex_lock(&vdd_rstr_mutex);
for (i = 0; i < max_tsens_num; i++) {
- tsens_dev.sensor_num = tsens_id_map[i];
- ret = tsens_get_temp(&tsens_dev, &temp);
+ ret = therm_get_temp(tsens_id_map[i], THERM_TSENS_ID, &temp);
if (ret) {
- pr_debug("%s: Unable to read TSENS sensor %d\n",
- __func__, tsens_dev.sensor_num);
+ pr_debug("Unable to read TSENS sensor %d\n",
+ tsens_id_map[i]);
dis_cnt++;
continue;
}
@@ -1048,7 +1112,6 @@
static int do_psm(void)
{
- struct tsens_device tsens_dev;
long temp = 0;
int ret = 0;
int i = 0;
@@ -1056,11 +1119,10 @@
mutex_lock(&psm_mutex);
for (i = 0; i < max_tsens_num; i++) {
- tsens_dev.sensor_num = tsens_id_map[i];
- ret = tsens_get_temp(&tsens_dev, &temp);
+ ret = therm_get_temp(tsens_id_map[i], THERM_TSENS_ID, &temp);
if (ret) {
pr_debug("%s: Unable to read TSENS sensor %d\n",
- __func__, tsens_dev.sensor_num);
+ __func__, tsens_id_map[i]);
auto_cnt++;
continue;
}
@@ -1137,15 +1199,13 @@
static void __ref check_temp(struct work_struct *work)
{
static int limit_init;
- struct tsens_device tsens_dev;
long temp = 0;
int ret = 0;
- tsens_dev.sensor_num = msm_thermal_info.sensor_id;
- ret = tsens_get_temp(&tsens_dev, &temp);
+ ret = therm_get_temp(msm_thermal_info.sensor_id, THERM_TSENS_ID, &temp);
if (ret) {
- pr_debug("%s: Unable to read TSENS sensor %d\n",
- KBUILD_MODNAME, tsens_dev.sensor_num);
+ pr_debug("Unable to read TSENS sensor %d\n",
+ msm_thermal_info.sensor_id);
goto reschedule;
}
@@ -1164,7 +1224,7 @@
do_freq_control(temp);
reschedule:
- if (enabled)
+ if (polling_enabled)
schedule_delayed_work(&check_temp_work,
msecs_to_jiffies(msm_thermal_info.poll_ms));
}
@@ -1258,7 +1318,6 @@
/* Adjust cpus offlined bit based on temperature reading. */
static int hotplug_init_cpu_offlined(void)
{
- struct tsens_device tsens_dev;
long temp = 0;
uint32_t cpu = 0;
@@ -1269,10 +1328,10 @@
for_each_possible_cpu(cpu) {
if (!(msm_thermal_info.core_control_mask & BIT(cpus[cpu].cpu)))
continue;
- tsens_dev.sensor_num = cpus[cpu].sensor_id;
- if (tsens_get_temp(&tsens_dev, &temp)) {
+ if (therm_get_temp(cpus[cpu].sensor_id, cpus[cpu].id_type,
+ &temp)) {
pr_err("%s: Unable to read TSENS sensor %d\n",
- KBUILD_MODNAME, tsens_dev.sensor_num);
+ KBUILD_MODNAME, cpus[cpu].sensor_id);
mutex_unlock(&core_control_mutex);
return -EINVAL;
}
@@ -1309,6 +1368,7 @@
for_each_possible_cpu(cpu) {
cpus[cpu].sensor_id =
sensor_get_id((char *)cpus[cpu].sensor_type);
+ cpus[cpu].id_type = THERM_ZONE_ID;
if (!(msm_thermal_info.core_control_mask & BIT(cpus[cpu].cpu)))
continue;
@@ -1346,7 +1406,9 @@
uint32_t cpu = 0, max_freq_req = 0, min_freq_req = 0;
while (!kthread_should_stop()) {
- wait_for_completion(&freq_mitigation_complete);
+ while (wait_for_completion_interruptible(
+ &freq_mitigation_complete) != 0)
+ ;
INIT_COMPLETION(freq_mitigation_complete);
get_online_cpus();
@@ -1501,6 +1563,217 @@
return ret;
}
+int therm_set_threshold(struct threshold_info *thresh_inp)
+{
+ int ret = 0, i = 0, err = 0;
+ struct therm_threshold *thresh_ptr;
+
+ if (!thresh_inp) {
+ pr_err("%s: %s: Invalid input\n",
+ KBUILD_MODNAME, __func__);
+ ret = -EINVAL;
+ goto therm_set_exit;
+ }
+
+ thresh_inp->thresh_triggered = false;
+ for (i = 0; i < thresh_inp->thresh_ct; i++) {
+ thresh_ptr = &thresh_inp->thresh_list[i];
+ thresh_ptr->trip_triggered = -1;
+ err = set_threshold(thresh_ptr->sensor_id,
+ thresh_ptr->threshold);
+ if (err) {
+ ret = err;
+ err = 0;
+ }
+ }
+
+therm_set_exit:
+ return ret;
+}
+
+static void vdd_restriction_notify(struct therm_threshold *trig_thresh)
+{
+ int ret = 0;
+ static uint32_t vdd_sens_status;
+
+ if (!vdd_rstr_enabled)
+ return;
+ if (!trig_thresh) {
+ pr_err("%s:%s Invalid input\n", KBUILD_MODNAME, __func__);
+ return;
+ }
+ if (trig_thresh->trip_triggered < 0)
+ goto set_and_exit;
+
+ mutex_lock(&vdd_rstr_mutex);
+ pr_debug("%s: sensor%d reached %d thresh for Vdd restriction\n",
+ KBUILD_MODNAME, trig_thresh->sensor_id,
+ trig_thresh->trip_triggered);
+ switch (trig_thresh->trip_triggered) {
+ case THERMAL_TRIP_CONFIGURABLE_HI:
+ if (vdd_sens_status & BIT(trig_thresh->sensor_id))
+ vdd_sens_status ^= BIT(trig_thresh->sensor_id);
+ break;
+ case THERMAL_TRIP_CONFIGURABLE_LOW:
+ vdd_sens_status |= BIT(trig_thresh->sensor_id);
+ break;
+ default:
+ pr_err("%s:%s: Unsupported trip type\n",
+ KBUILD_MODNAME, __func__);
+ goto unlock_and_exit;
+ break;
+ }
+
+ ret = vdd_restriction_apply_all((vdd_sens_status) ? 1 : 0);
+ if (ret) {
+ pr_err("%s vdd rstr votlage for all failed\n",
+ (vdd_sens_status) ?
+ "Enable" : "Disable");
+ goto unlock_and_exit;
+ }
+
+unlock_and_exit:
+ mutex_unlock(&vdd_rstr_mutex);
+set_and_exit:
+ set_threshold(trig_thresh->sensor_id, trig_thresh->threshold);
+ return;
+}
+
+static __ref int do_thermal_monitor(void *data)
+{
+ int ret = 0, i, j;
+ struct therm_threshold *sensor_list;
+
+ while (!kthread_should_stop()) {
+ while (wait_for_completion_interruptible(
+ &thermal_monitor_complete) != 0)
+ ;
+ INIT_COMPLETION(thermal_monitor_complete);
+
+ for (i = 0; i < MSM_LIST_MAX_NR; i++) {
+ if (!thresh[i].thresh_triggered)
+ continue;
+ thresh[i].thresh_triggered = false;
+ for (j = 0; j < thresh[i].thresh_ct; j++) {
+ sensor_list = &thresh[i].thresh_list[j];
+ if (sensor_list->trip_triggered < 0)
+ continue;
+ sensor_list->notify(sensor_list);
+ sensor_list->trip_triggered = -1;
+ }
+ }
+ }
+ return ret;
+}
+
+static void thermal_monitor_init(void)
+{
+ if (thermal_monitor_task)
+ return;
+
+ init_completion(&thermal_monitor_complete);
+ thermal_monitor_task = kthread_run(do_thermal_monitor, NULL,
+ "msm_thermal:therm_monitor");
+ if (IS_ERR(thermal_monitor_task)) {
+ pr_err("%s: Failed to create thermal monitor thread\n",
+ KBUILD_MODNAME);
+ goto init_exit;
+ }
+
+ if (vdd_rstr_enabled)
+ therm_set_threshold(&thresh[MSM_VDD_RESTRICTION]);
+
+init_exit:
+ return;
+}
+
+static int msm_thermal_notify(enum thermal_trip_type type, int temp, void *data)
+{
+ struct therm_threshold *thresh_data = (struct therm_threshold *)data;
+
+ if (thermal_monitor_task) {
+ thresh_data->trip_triggered = type;
+ thresh_data->parent->thresh_triggered = true;
+ complete(&thermal_monitor_complete);
+ } else {
+ pr_err("%s: Thermal monitor task is not initialized\n",
+ KBUILD_MODNAME);
+ }
+ return 0;
+}
+
+static int init_threshold(enum msm_thresh_list index,
+ int sensor_id, int32_t hi_temp, int32_t low_temp,
+ void (*callback)(struct therm_threshold *))
+{
+ int ret = 0, i;
+ struct therm_threshold *thresh_ptr;
+
+ if (!callback || index >= MSM_LIST_MAX_NR || index < 0
+ || sensor_id == -ENODEV) {
+ pr_err("%s: Invalid input to init_threshold\n",
+ KBUILD_MODNAME);
+ ret = -EINVAL;
+ goto init_thresh_exit;
+ }
+ if (thresh[index].thresh_list) {
+ pr_err("%s: threshold already initialized\n",
+ KBUILD_MODNAME);
+ ret = -EEXIST;
+ goto init_thresh_exit;
+ }
+
+ thresh[index].thresh_ct = (sensor_id == MONITOR_ALL_TSENS) ?
+ max_tsens_num : 1;
+ thresh[index].thresh_triggered = false;
+ thresh[index].thresh_list = kzalloc(sizeof(struct therm_threshold) *
+ thresh[index].thresh_ct, GFP_KERNEL);
+ if (!thresh[index].thresh_list) {
+ pr_err("%s: kzalloc failed\n", KBUILD_MODNAME);
+ ret = -ENOMEM;
+ goto init_thresh_exit;
+ }
+
+ thresh_ptr = thresh[index].thresh_list;
+ if (sensor_id == MONITOR_ALL_TSENS) {
+ for (i = 0; i < max_tsens_num; i++) {
+ thresh_ptr[i].sensor_id = tsens_id_map[i];
+ thresh_ptr[i].notify = callback;
+ thresh_ptr[i].trip_triggered = -1;
+ thresh_ptr[i].parent = &thresh[index];
+ thresh_ptr[i].threshold[0].temp = hi_temp;
+ thresh_ptr[i].threshold[0].trip =
+ THERMAL_TRIP_CONFIGURABLE_HI;
+ thresh_ptr[i].threshold[1].temp = low_temp;
+ thresh_ptr[i].threshold[1].trip =
+ THERMAL_TRIP_CONFIGURABLE_LOW;
+ thresh_ptr[i].threshold[0].notify =
+ thresh_ptr[i].threshold[1].notify = msm_thermal_notify;
+ thresh_ptr[i].threshold[0].data =
+ thresh_ptr[i].threshold[1].data =
+ (void *)&thresh_ptr[i];
+ }
+ } else {
+ thresh_ptr->sensor_id = sensor_id;
+ thresh_ptr->notify = callback;
+ thresh_ptr->trip_triggered = -1;
+ thresh_ptr->parent = &thresh[index];
+ thresh_ptr->threshold[0].temp = hi_temp;
+ thresh_ptr->threshold[0].trip =
+ THERMAL_TRIP_CONFIGURABLE_HI;
+ thresh_ptr->threshold[1].temp = low_temp;
+ thresh_ptr->threshold[1].trip =
+ THERMAL_TRIP_CONFIGURABLE_LOW;
+ thresh_ptr->threshold[0].notify =
+ thresh_ptr->threshold[1].notify = msm_thermal_notify;
+ thresh_ptr->threshold[0].data =
+ thresh_ptr->threshold[1].data = (void *)thresh_ptr;
+ }
+
+init_thresh_exit:
+ return ret;
+}
+
/*
* We will reset the cpu frequencies limits here. The core online/offline
* status will be carried over to the process stopping the msm_thermal, as
@@ -1511,8 +1784,7 @@
uint32_t cpu = 0;
/* make sure check_temp is no longer running */
- cancel_delayed_work(&check_temp_work);
- flush_scheduled_work();
+ cancel_delayed_work_sync(&check_temp_work);
get_online_cpus();
for_each_possible_cpu(cpu) {
@@ -1526,16 +1798,30 @@
put_online_cpus();
}
+static void interrupt_mode_init(void)
+{
+ if (!msm_thermal_probed) {
+ interrupt_mode_enable = true;
+ return;
+ }
+ if (polling_enabled) {
+ pr_info("%s: Interrupt mode init\n", KBUILD_MODNAME);
+ polling_enabled = 0;
+ disable_msm_thermal();
+ hotplug_init();
+ freq_mitigation_init();
+ thermal_monitor_init();
+ }
+}
+
static int __ref set_enabled(const char *val, const struct kernel_param *kp)
{
int ret = 0;
ret = param_set_bool(val, kp);
- if (!enabled) {
- disable_msm_thermal();
- hotplug_init();
- freq_mitigation_init();
- } else
+ if (!enabled)
+ interrupt_mode_init();
+ else
pr_info("%s: no action for enabled = %d\n",
KBUILD_MODNAME, enabled);
@@ -1611,7 +1897,7 @@
goto done_cc;
}
- if (enabled) {
+ if (polling_enabled) {
pr_err("%s: Ignoring request; polling thread is enabled.\n",
KBUILD_MODNAME);
goto done_cc;
@@ -1764,7 +2050,34 @@
return ret;
}
-int __devinit msm_thermal_init(struct msm_thermal_data *pdata)
+int msm_thermal_pre_init(void)
+{
+ int ret = 0;
+
+ tsens_get_max_sensor_num(&max_tsens_num);
+ if (create_sensor_id_map()) {
+ ret = -EINVAL;
+ goto pre_init_exit;
+ }
+
+ if (!thresh) {
+ thresh = kzalloc(
+ sizeof(struct threshold_info) * MSM_LIST_MAX_NR,
+ GFP_KERNEL);
+ if (!thresh) {
+ pr_err("%s:%s: kzalloc failed\n",
+ KBUILD_MODNAME, __func__);
+ ret = -ENOMEM;
+ goto pre_init_exit;
+ }
+ memset(thresh, 0, sizeof(struct threshold_info) *
+ MSM_LIST_MAX_NR);
+ }
+pre_init_exit:
+ return ret;
+}
+
+int msm_thermal_init(struct msm_thermal_data *pdata)
{
int ret = 0;
uint32_t cpu;
@@ -1782,15 +2095,13 @@
cpus[cpu].freq_thresh_clear = false;
}
BUG_ON(!pdata);
- tsens_get_max_sensor_num(&max_tsens_num);
memcpy(&msm_thermal_info, pdata, sizeof(struct msm_thermal_data));
- if (create_sensor_id_map())
- return -EINVAL;
if (check_sensor_id(msm_thermal_info.sensor_id))
return -EINVAL;
enabled = 1;
+ polling_enabled = 1;
ret = cpufreq_register_notifier(&msm_thermal_cpufreq_notifier,
CPUFREQ_POLICY_NOTIFIER);
if (ret)
@@ -2240,6 +2551,11 @@
__func__);
goto read_node_fail;
}
+ ret = init_threshold(MSM_VDD_RESTRICTION, MONITOR_ALL_TSENS,
+ data->vdd_rstr_temp_hyst_degC, data->vdd_rstr_temp_degC,
+ vdd_restriction_notify);
+ if (ret)
+ goto read_node_fail;
vdd_rstr_enabled = true;
}
read_node_fail:
@@ -2516,6 +2832,9 @@
struct msm_thermal_data data;
memset(&data, 0, sizeof(struct msm_thermal_data));
+ ret = msm_thermal_pre_init();
+ if (ret)
+ goto fail;
key = "qcom,sensor-id";
ret = of_property_read_u32(node, key, &data.sensor_id);
@@ -2584,6 +2903,12 @@
}
msm_thermal_ioctl_init();
ret = msm_thermal_init(&data);
+ msm_thermal_probed = true;
+
+ if (interrupt_mode_enable) {
+ interrupt_mode_init();
+ interrupt_mode_enable = false;
+ }
return ret;
fail:
@@ -2597,6 +2922,12 @@
static int msm_thermal_dev_exit(struct platform_device *inp_dev)
{
msm_thermal_ioctl_cleanup();
+ if (thresh) {
+ if (vdd_rstr_enabled)
+ kfree(thresh[MSM_VDD_RESTRICTION].thresh_list);
+ kfree(thresh);
+ thresh = NULL;
+ }
return 0;
}
@@ -2632,6 +2963,7 @@
INIT_WORK(&timer_work, timer_work_fn);
msm_thermal_add_timer_nodes();
+ interrupt_mode_init();
return 0;
}
late_initcall(msm_thermal_late_init);
diff --git a/drivers/thermal/thermal_sys.c b/drivers/thermal/thermal_sys.c
index 21863e8..8cd70ac 100644
--- a/drivers/thermal/thermal_sys.c
+++ b/drivers/thermal/thermal_sys.c
@@ -92,13 +92,43 @@
}
EXPORT_SYMBOL(sensor_get_id);
+static void init_sensor_trip(struct sensor_info *sensor)
+{
+ int ret = 0, i = 0;
+ enum thermal_trip_type type;
+
+ for (i = 0; ((sensor->max_idx == -1) ||
+ (sensor->min_idx == -1)) &&
+ (sensor->tz->ops->get_trip_type) &&
+ (i < sensor->tz->trips); i++) {
+
+ sensor->tz->ops->get_trip_type(sensor->tz, i, &type);
+ if (type == THERMAL_TRIP_CONFIGURABLE_HI)
+ sensor->max_idx = i;
+ if (type == THERMAL_TRIP_CONFIGURABLE_LOW)
+ sensor->min_idx = i;
+ type = 0;
+ }
+
+ ret = sensor->tz->ops->get_trip_temp(sensor->tz,
+ sensor->min_idx, &sensor->threshold_min);
+ if (ret)
+ pr_err("Unable to get MIN trip temp. sensor:%d err:%d\n",
+ sensor->sensor_id, ret);
+
+ ret = sensor->tz->ops->get_trip_temp(sensor->tz,
+ sensor->max_idx, &sensor->threshold_max);
+ if (ret)
+ pr_err("Unable to get MAX trip temp. sensor:%d err:%d\n",
+ sensor->sensor_id, ret);
+}
+
static int __update_sensor_thresholds(struct sensor_info *sensor)
{
long max_of_low_thresh = LONG_MIN;
long min_of_high_thresh = LONG_MAX;
struct sensor_threshold *pos, *var;
- enum thermal_trip_type type;
- int i, ret = 0;
+ int ret = 0;
if (!sensor->tz->ops->set_trip_temp ||
!sensor->tz->ops->activate_trip_type ||
@@ -108,19 +138,8 @@
goto update_done;
}
- for (i = 0; ((sensor->max_idx == -1) || (sensor->min_idx == -1)) &&
- (sensor->tz->ops->get_trip_type) && (i < sensor->tz->trips);
- i++) {
- sensor->tz->ops->get_trip_type(sensor->tz, i, &type);
- if (type == THERMAL_TRIP_CONFIGURABLE_HI)
- sensor->max_idx = i;
- if (type == THERMAL_TRIP_CONFIGURABLE_LOW)
- sensor->min_idx = i;
- sensor->tz->ops->get_trip_temp(sensor->tz,
- THERMAL_TRIP_CONFIGURABLE_LOW, &sensor->threshold_min);
- sensor->tz->ops->get_trip_temp(sensor->tz,
- THERMAL_TRIP_CONFIGURABLE_HI, &sensor->threshold_max);
- }
+ if ((sensor->max_idx == -1) || (sensor->min_idx == -1))
+ init_sensor_trip(sensor);
list_for_each_entry_safe(pos, var, &sensor->threshold_list, list) {
if (!pos->active)
@@ -368,7 +387,7 @@
sensor->sensor_id = tz->id;
sensor->tz = tz;
- sensor->threshold_min = 0;
+ sensor->threshold_min = LONG_MIN;
sensor->threshold_max = LONG_MAX;
sensor->max_idx = -1;
sensor->min_idx = -1;
diff --git a/drivers/tty/serial/msm_serial_hs.c b/drivers/tty/serial/msm_serial_hs.c
index 4058bec..a701ec8 100644
--- a/drivers/tty/serial/msm_serial_hs.c
+++ b/drivers/tty/serial/msm_serial_hs.c
@@ -251,7 +251,6 @@
struct workqueue_struct *hsuart_wq; /* hsuart workqueue */
struct mutex clk_mutex; /* mutex to guard against clock off/clock on */
struct work_struct disconnect_rx_endpoint; /* disconnect rx_endpoint */
- bool tty_flush_receive;
enum uart_core_type uart_type;
u32 bam_handle;
resource_size_t bam_mem;
@@ -319,6 +318,7 @@
[UART_DM_TXFS] = 0x4c,
[UART_DM_RXFS] = 0x50,
[UART_DM_RX_TRANS_CTRL] = 0xcc,
+ [UART_DM_BCR] = 0xc8,
};
static struct of_device_id msm_hs_match_table[] = {
@@ -1838,12 +1838,13 @@
* Do the work buffer related work in BAM
* mode that is equivalent to legacy mode
*/
+ spin_lock_irqsave(&(msm_uport->uport.lock), flags);
- if (!msm_uport->tty_flush_receive)
+ if (!uart_circ_empty(tx_buf))
tx_buf->tail = (tx_buf->tail +
tx->tx_count) & ~UART_XMIT_SIZE;
else
- msm_uport->tty_flush_receive = false;
+ MSM_HS_DBG("%s:circ buffer is empty\n", __func__);
tx->dma_in_flight = 0;
@@ -1860,7 +1861,6 @@
if (uart_circ_chars_pending(tx_buf) < WAKEUP_CHARS)
uart_write_wakeup(uport);
- spin_lock_irqsave(&(msm_uport->uport.lock), flags);
if (msm_uport->tx.flush == FLUSH_STOP) {
msm_uport->tx.flush = FLUSH_SHUTDOWN;
wake_up(&msm_uport->tx.wait);
@@ -2036,14 +2036,6 @@
}
-static void msm_hs_flush_buffer(struct uart_port *uport)
-{
- struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
-
- if (msm_uport->tx.dma_in_flight)
- msm_uport->tty_flush_receive = true;
-}
-
/*
* Standard API, Break Signal
*
@@ -2281,11 +2273,9 @@
/* Do not update tx_buf.tail if uart_flush_buffer already
* called in serial core
*/
- if (!msm_uport->tty_flush_receive)
+ if (!uart_circ_empty(tx_buf))
tx_buf->tail = (tx_buf->tail +
tx->tx_count) & ~UART_XMIT_SIZE;
- else
- msm_uport->tty_flush_receive = false;
tx->dma_in_flight = 0;
@@ -2665,7 +2655,11 @@
}
}
- msm_hs_write(uport, UARTDM_BCR_ADDR, 0x003F);
+ data = (UARTDM_BCR_TX_BREAK_DISABLE | UARTDM_BCR_STALE_IRQ_EMPTY |
+ UARTDM_BCR_RX_DMRX_LOW_EN | UARTDM_BCR_RX_STAL_IRQ_DMRX_EQL |
+ UARTDM_BCR_RX_DMRX_1BYTE_RES_EN);
+ msm_hs_write(uport, UART_DM_BCR, data);
+
/* Set auto RFR Level */
data = msm_hs_read(uport, UART_DM_MR1);
data &= ~UARTDM_MR1_AUTO_RFR_LEVEL1_BMSK;
@@ -2708,8 +2702,6 @@
tx->tx_ready_int_en = 0;
tx->dma_in_flight = 0;
rx->rx_cmd_exec = false;
- msm_uport->tty_flush_receive = false;
- MSM_HS_DBG("%s: Setting tty_flush_receive to false\n", __func__);
if (!is_blsp_uart(msm_uport)) {
tx->xfer.complete_func = msm_hs_dmov_tx_callback;
@@ -3751,7 +3743,7 @@
.config_port = msm_hs_config_port,
.release_port = msm_hs_release_port,
.request_port = msm_hs_request_port,
- .flush_buffer = msm_hs_flush_buffer,
+ .flush_buffer = NULL,
.ioctl = msm_hs_ioctl,
};
diff --git a/drivers/tty/serial/msm_serial_hs_hwreg.h b/drivers/tty/serial/msm_serial_hs_hwreg.h
index d912b9f..064bbda 100644
--- a/drivers/tty/serial/msm_serial_hs_hwreg.h
+++ b/drivers/tty/serial/msm_serial_hs_hwreg.h
@@ -81,6 +81,7 @@
UART_DM_TXFS,
UART_DM_RXFS,
UART_DM_RX_TRANS_CTRL,
+ UART_DM_BCR,
UART_DM_LAST,
};
@@ -94,7 +95,11 @@
* UARTDM Core v1.4 STALE_IRQ_EMPTY bit defination
* Stale interrupt will fire if bit is set when RX-FIFO is empty
*/
+#define UARTDM_BCR_TX_BREAK_DISABLE 0x1
#define UARTDM_BCR_STALE_IRQ_EMPTY 0x2
+#define UARTDM_BCR_RX_DMRX_LOW_EN 0x4
+#define UARTDM_BCR_RX_STAL_IRQ_DMRX_EQL 0x10
+#define UARTDM_BCR_RX_DMRX_1BYTE_RES_EN 0x20
/* TRANSFER_CONTROL Register for UARTDM Core v1.4 */
#define UARTDM_RX_TRANS_CTRL_ADDR 0xcc
diff --git a/drivers/usb/class/ccid_bridge.c b/drivers/usb/class/ccid_bridge.c
index 05483fd..a914902 100644
--- a/drivers/usb/class/ccid_bridge.c
+++ b/drivers/usb/class/ccid_bridge.c
@@ -36,6 +36,10 @@
#define CCID_CONTROL_TIMEOUT 500 /* msec */
#define CCID_BRIDGE_MSG_TIMEOUT 1000 /* msec */
+static unsigned ccid_bulk_msg_timeout = CCID_BRIDGE_MSG_TIMEOUT;
+module_param_named(bulk_msg_timeout, ccid_bulk_msg_timeout, uint, 0644);
+MODULE_PARM_DESC(bulk_msg_timeout, "Bulk message timeout (msecs)");
+
struct ccid_bridge {
struct usb_device *udev;
struct usb_interface *intf;
@@ -304,7 +308,7 @@
ret = wait_event_interruptible_timeout(ccid->write_wq,
ccid->write_result != 0,
- msecs_to_jiffies(CCID_BRIDGE_MSG_TIMEOUT));
+ msecs_to_jiffies(ccid_bulk_msg_timeout));
if (!ret || ret == -ERESTARTSYS) { /* timedout or interrupted */
usb_kill_urb(ccid->writeurb);
if (!ret)
@@ -375,7 +379,7 @@
ret = wait_event_interruptible_timeout(ccid->read_wq,
ccid->read_result != 0,
- msecs_to_jiffies(CCID_BRIDGE_MSG_TIMEOUT));
+ msecs_to_jiffies(ccid_bulk_msg_timeout));
if (!ret || ret == -ERESTARTSYS) { /* timedout or interrupted */
usb_kill_urb(ccid->readurb);
if (!ret)
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 90dd115..e4f56b6 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -116,22 +116,32 @@
* if it failed previously to operate in SS mode.
*/
reg |= DWC3_GCTL_U2RSTECN;
- if (mode == DWC3_GCTL_PRTCAP_HOST) {
+ reg &= ~(DWC3_GCTL_SOFITPSYNC);
+ reg &= ~(DWC3_GCTL_PWRDNSCALEMASK);
+ reg |= DWC3_GCTL_PWRDNSCALE(2);
+ reg |= DWC3_GCTL_U2EXIT_LFPS;
+ dwc3_writel(dwc->regs, DWC3_GCTL, reg);
+
+ if (mode == DWC3_GCTL_PRTCAP_OTG || mode == DWC3_GCTL_PRTCAP_HOST) {
/*
* Allow ITP generated off of ref clk based counter instead
* of UTMI/ULPI clk based counter, when superspeed only is
* active so that UTMI/ULPI PHY can be suspened.
+ *
+ * Starting with revision 2.50A, GFLADJ_REFCLK_LPM_SEL is used
+ * instead.
*/
- reg |= DWC3_GCTL_SOFITPSYNC;
- reg &= ~(DWC3_GCTL_PWRDNSCALEMASK);
- reg |= DWC3_GCTL_PWRDNSCALE(2);
- } else if (mode == DWC3_GCTL_PRTCAP_DEVICE) {
- reg &= ~(DWC3_GCTL_PWRDNSCALEMASK);
- reg |= DWC3_GCTL_PWRDNSCALE(2);
- reg &= ~(DWC3_GCTL_SOFITPSYNC);
+ if (dwc->revision < DWC3_REVISION_250A) {
+ reg = dwc3_readl(dwc->regs, DWC3_GCTL);
+ reg |= DWC3_GCTL_SOFITPSYNC;
+ dwc3_writel(dwc->regs, DWC3_GCTL, reg);
+ } else {
+ reg = dwc3_readl(dwc->regs, DWC3_GFLADJ);
+ reg |= DWC3_GFLADJ_REFCLK_LPM_SEL;
+ dwc3_writel(dwc->regs, DWC3_GFLADJ, reg);
+ }
}
- reg |= DWC3_GCTL_U2EXIT_LFPS;
- dwc3_writel(dwc->regs, DWC3_GCTL, reg);
+
reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));
reg |= DWC3_GUSB3PIPECTL_SUSPHY;
dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg);
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 4e84b94..b338c2d 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -138,6 +138,7 @@
#define DWC3_GEVNTCOUNT(n) (0xc40c + (n * 0x10))
#define DWC3_GHWPARAMS8 0xc600
+#define DWC3_GFLADJ 0xc630
/* Device Registers */
#define DWC3_DCFG 0xc700
@@ -219,6 +220,12 @@
/* Global HWPARAMS6 Register */
#define DWC3_GHWPARAMS6_SRP_SUPPORT (1 << 10)
+/* Global Frame Length Adjustment Register */
+#define DWC3_GFLADJ_REFCLK_240MHZDECR_PLS1 (1 << 31)
+#define DWC3_GFLADJ_REFCLK_240MHZ_DECR (0x7F << 24)
+#define DWC3_GFLADJ_REFCLK_LPM_SEL (1 << 23)
+#define DWC3_GFLADJ_REFCLK_FLADJ (0x3FFF << 8)
+
/* Device Configuration Register */
#define DWC3_DCFG_LPM_CAP (1 << 22)
#define DWC3_DCFG_DEVADDR(addr) ((addr) << 3)
@@ -743,6 +750,7 @@
#define DWC3_REVISION_210A 0x5533210a
#define DWC3_REVISION_220A 0x5533220a
#define DWC3_REVISION_230A 0x5533230a
+#define DWC3_REVISION_250A 0x5533250a
unsigned is_selfpowered:1;
unsigned three_stage_setup:1;
diff --git a/drivers/usb/dwc3/debugfs.c b/drivers/usb/dwc3/debugfs.c
index a192398..eb67c47 100644
--- a/drivers/usb/dwc3/debugfs.c
+++ b/drivers/usb/dwc3/debugfs.c
@@ -231,6 +231,7 @@
dump_register(GEVNTCOUNT(0)),
dump_register(GHWPARAMS8),
+ dump_register(GFLADJ),
dump_register(DCFG),
dump_register(DCTL),
dump_register(DEVTEN),
diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c
index 0e371d5..7d4ecd6 100644
--- a/drivers/usb/dwc3/dwc3-msm.c
+++ b/drivers/usb/dwc3/dwc3-msm.c
@@ -187,6 +187,8 @@
struct clk *sleep_clk;
struct clk *hsphy_sleep_clk;
struct clk *utmi_clk;
+ unsigned int utmi_clk_rate;
+ struct clk *utmi_clk_src;
struct regulator *hsusb_3p3;
struct regulator *hsusb_1p8;
struct regulator *hsusb_vddcx;
@@ -238,6 +240,7 @@
unsigned long lpm_flags;
#define MDWC3_PHY_REF_AND_CORECLK_OFF BIT(0)
#define MDWC3_TCXO_SHUTDOWN BIT(1)
+#define MDWC3_ASYNC_IRQ_WAKE_CAPABILITY BIT(2)
u32 qscratch_ctl_val;
dev_t ext_chg_dev;
@@ -1091,7 +1094,7 @@
struct dwc3 *dwc = container_of(gadget, struct dwc3, gadget);
struct dwc3_msm *mdwc = dev_get_drvdata(dwc->dev->parent);
- if (mdwc)
+ if (!mdwc)
return;
dev_dbg(mdwc->dev, "%s\n", __func__);
@@ -1445,6 +1448,48 @@
0x07f03f07, 0x07f01605);
}
+static void dwc3_msm_update_ref_clk(struct dwc3_msm *mdwc)
+{
+ u32 guctl, gfladj = 0;
+
+ guctl = dwc3_msm_read_reg(mdwc->base, DWC3_GUCTL);
+ guctl &= ~DWC3_GUCTL_REFCLKPER;
+
+ /* GFLADJ register is used starting with revision 2.50a */
+ if (dwc3_msm_read_reg(mdwc->base, DWC3_GSNPSID) >= DWC3_REVISION_250A) {
+ gfladj = dwc3_msm_read_reg(mdwc->base, DWC3_GFLADJ);
+ gfladj &= ~DWC3_GFLADJ_REFCLK_240MHZDECR_PLS1;
+ gfladj &= ~DWC3_GFLADJ_REFCLK_240MHZ_DECR;
+ gfladj &= ~DWC3_GFLADJ_REFCLK_LPM_SEL;
+ gfladj &= ~DWC3_GFLADJ_REFCLK_FLADJ;
+ }
+
+ /* Refer to SNPS Databook Table 6-55 for calculations used */
+ switch (mdwc->utmi_clk_rate) {
+ case 19200000:
+ guctl |= 52 << __ffs(DWC3_GUCTL_REFCLKPER);
+ gfladj |= 12 << __ffs(DWC3_GFLADJ_REFCLK_240MHZ_DECR);
+ gfladj |= DWC3_GFLADJ_REFCLK_240MHZDECR_PLS1;
+ gfladj |= DWC3_GFLADJ_REFCLK_LPM_SEL;
+ gfladj |= 200 << __ffs(DWC3_GFLADJ_REFCLK_FLADJ);
+ break;
+ case 24000000:
+ guctl |= 41 << __ffs(DWC3_GUCTL_REFCLKPER);
+ gfladj |= 10 << __ffs(DWC3_GFLADJ_REFCLK_240MHZ_DECR);
+ gfladj |= DWC3_GFLADJ_REFCLK_LPM_SEL;
+ gfladj |= 2032 << __ffs(DWC3_GFLADJ_REFCLK_FLADJ);
+ break;
+ default:
+ dev_warn(mdwc->dev, "Unsupported utmi_clk_rate: %u\n",
+ mdwc->utmi_clk_rate);
+ break;
+ }
+
+ dwc3_msm_write_reg(mdwc->base, DWC3_GUCTL, guctl);
+ if (gfladj)
+ dwc3_msm_write_reg(mdwc->base, DWC3_GFLADJ, gfladj);
+}
+
/* Initialize QSCRATCH registers for HSPHY and SSPHY operation */
static void dwc3_msm_qscratch_reg_init(struct dwc3_msm *mdwc,
unsigned event_status)
@@ -1529,6 +1574,7 @@
"DWC3_CONTROLLER_POST_RESET_EVENT received\n");
dwc3_msm_qscratch_reg_init(mdwc,
DWC3_CONTROLLER_POST_RESET_EVENT);
+ dwc3_msm_update_ref_clk(mdwc);
dwc->tx_fifo_size = mdwc->tx_fifo_size;
break;
case DWC3_CONTROLLER_POST_INITIALIZATION_EVENT:
@@ -1823,6 +1869,7 @@
bool host_bus_suspend;
bool host_ss_active;
bool host_ss_suspend;
+ bool device_bus_suspend;
dev_dbg(mdwc->dev, "%s: entering lpm\n", __func__);
@@ -1850,6 +1897,8 @@
(mdwc->charger.chg_type == DWC3_FLOATED_CHARGER));
host_bus_suspend = mdwc->host_mode == 1;
host_ss_suspend = host_bus_suspend && host_ss_active;
+ device_bus_suspend = ((mdwc->charger.chg_type == DWC3_SDP_CHARGER) ||
+ (mdwc->charger.chg_type == DWC3_CDP_CHARGER));
if (!dcp && !host_bus_suspend)
dwc3_msm_write_reg(mdwc->base, QSCRATCH_CTRL_REG,
@@ -1952,10 +2001,16 @@
dev_info(mdwc->dev, "DWC3 in low power mode\n");
if (mdwc->hs_phy_irq) {
+ /*
+ * with DCP or during cable disconnect, we dont require wakeup
+ * using HS_PHY_IRQ. Hence enable wakeup only in case of host
+ * bus suspend and device bus suspend.
+ */
+ if (host_bus_suspend || device_bus_suspend) {
+ enable_irq_wake(mdwc->hs_phy_irq);
+ mdwc->lpm_flags |= MDWC3_ASYNC_IRQ_WAKE_CAPABILITY;
+ }
enable_irq(mdwc->hs_phy_irq);
- /* with DCP we dont require wakeup using HS_PHY_IRQ */
- if (dcp)
- disable_irq_wake(mdwc->hs_phy_irq);
}
return 0;
@@ -2089,9 +2144,12 @@
enable_irq(mdwc->hs_phy_irq);
mdwc->lpm_irq_seen = false;
}
- /* it must DCP disconnect, re-enable HS_PHY wakeup IRQ */
- if (mdwc->hs_phy_irq && dcp)
- enable_irq_wake(mdwc->hs_phy_irq);
+ /* Disable wakeup capable for HS_PHY IRQ, if enabled */
+ if (mdwc->hs_phy_irq &&
+ (mdwc->lpm_flags & MDWC3_ASYNC_IRQ_WAKE_CAPABILITY)) {
+ disable_irq_wake(mdwc->hs_phy_irq);
+ mdwc->lpm_flags &= ~MDWC3_ASYNC_IRQ_WAKE_CAPABILITY;
+ }
dev_info(mdwc->dev, "DWC3 exited from low power mode\n");
@@ -2685,6 +2743,18 @@
else
pr_debug("%s:voltage request failed\n", __func__);
break;
+ case MSM_USB_EXT_CHG_TYPE:
+ if (get_user(val, (int __user *)arg)) {
+ pr_err("%s: get_user failed\n\n", __func__);
+ ret = -EFAULT;
+ break;
+ }
+
+ if (val)
+ pr_debug("%s:charger is external charger\n", __func__);
+ else
+ pr_debug("%s:charger is not ext charger\n", __func__);
+ break;
default:
ret = -EINVAL;
}
@@ -2864,12 +2934,36 @@
}
clk_prepare_enable(mdwc->hsphy_sleep_clk);
+ ret = of_property_read_u32(node, "qcom,utmi-clk-rate",
+ (u32 *)&mdwc->utmi_clk_rate);
+ if (ret)
+ mdwc->utmi_clk_rate = 60000000;
+
mdwc->utmi_clk = devm_clk_get(&pdev->dev, "utmi_clk");
if (IS_ERR(mdwc->utmi_clk)) {
dev_err(&pdev->dev, "failed to get utmi_clk\n");
ret = PTR_ERR(mdwc->utmi_clk);
goto disable_sleep_a_clk;
}
+
+ if (mdwc->utmi_clk_rate == 24000000) {
+ /*
+ * For setting utmi clock to 24MHz, first set 48MHz on parent
+ * clock "utmi_clk_src" and then set divider 2 on child branch
+ * "utmi_clk".
+ */
+ mdwc->utmi_clk_src = devm_clk_get(&pdev->dev, "utmi_clk_src");
+ if (IS_ERR(mdwc->utmi_clk_src)) {
+ dev_err(&pdev->dev, "failed to get utmi_clk_src\n");
+ ret = PTR_ERR(mdwc->utmi_clk_src);
+ goto disable_sleep_a_clk;
+ }
+ clk_set_rate(mdwc->utmi_clk_src, 48000000);
+ /* 1 means divide utmi_clk_src by 2 */
+ clk_set_rate(mdwc->utmi_clk, 1);
+ } else {
+ clk_set_rate(mdwc->utmi_clk, mdwc->utmi_clk_rate);
+ }
clk_prepare_enable(mdwc->utmi_clk);
mdwc->ref_clk = devm_clk_get(&pdev->dev, "ref_clk");
@@ -2981,7 +3075,6 @@
dev_err(&pdev->dev, "irqreq HSPHYINT failed\n");
goto disable_hs_ldo;
}
- enable_irq_wake(mdwc->hs_phy_irq);
}
if (mdwc->ext_xceiv.otg_capability) {
diff --git a/drivers/usb/dwc3/dwc3_otg.c b/drivers/usb/dwc3/dwc3_otg.c
index 8a034d6..e373b9b 100644
--- a/drivers/usb/dwc3/dwc3_otg.c
+++ b/drivers/usb/dwc3/dwc3_otg.c
@@ -93,6 +93,17 @@
return 0;
}
+static void dwc3_otg_set_hsphy_auto_suspend(struct dwc3_otg *dotg, bool susp);
+static int dwc3_otg_set_autosuspend(struct usb_phy *phy, int enable_autosuspend)
+{
+ struct usb_otg *otg = phy->otg;
+ struct dwc3_otg *dotg = container_of(otg, struct dwc3_otg, otg);
+
+ dwc3_otg_set_hsphy_auto_suspend(dotg, enable_autosuspend);
+
+ return 0;
+}
+
static void dwc3_otg_set_hsphy_auto_suspend(struct dwc3_otg *dotg, bool susp)
{
struct dwc3 *dwc = dotg->dwc;
@@ -998,6 +1009,7 @@
dotg->otg.phy->dev = dwc->dev;
dotg->otg.phy->set_power = dwc3_otg_set_power;
dotg->otg.phy->set_suspend = dwc3_otg_set_suspend;
+ dotg->otg.phy->set_phy_autosuspend = dwc3_otg_set_autosuspend;
ret = usb_set_transceiver(dotg->otg.phy);
if (ret) {
diff --git a/drivers/usb/gadget/android.c b/drivers/usb/gadget/android.c
index 877b944..6b01472 100644
--- a/drivers/usb/gadget/android.c
+++ b/drivers/usb/gadget/android.c
@@ -1762,6 +1762,7 @@
struct fsg_common *common;
};
+#define MAX_LUN_NAME 8
static int mass_storage_function_init(struct android_usb_function *f,
struct usb_composite_dev *cdev)
{
@@ -1769,8 +1770,9 @@
struct mass_storage_function_config *config;
struct fsg_common *common;
int err;
- int i;
- const char *name[3];
+ int i, n;
+ char name[FSG_MAX_LUNS][MAX_LUN_NAME];
+ u8 uicc_nluns = dev->pdata ? dev->pdata->uicc_nluns : 0;
config = kzalloc(sizeof(struct mass_storage_function_config),
GFP_KERNEL);
@@ -1778,23 +1780,35 @@
return -ENOMEM;
config->fsg.nluns = 1;
- name[0] = "lun";
+ snprintf(name[0], MAX_LUN_NAME, "lun");
+ config->fsg.luns[0].removable = 1;
+
if (dev->pdata && dev->pdata->cdrom) {
config->fsg.luns[config->fsg.nluns].cdrom = 1;
config->fsg.luns[config->fsg.nluns].ro = 1;
config->fsg.luns[config->fsg.nluns].removable = 0;
- name[config->fsg.nluns] = "lun0";
+ snprintf(name[config->fsg.nluns], MAX_LUN_NAME, "lun0");
config->fsg.nluns++;
}
if (dev->pdata && dev->pdata->internal_ums) {
config->fsg.luns[config->fsg.nluns].cdrom = 0;
config->fsg.luns[config->fsg.nluns].ro = 0;
config->fsg.luns[config->fsg.nluns].removable = 1;
- name[config->fsg.nluns] = "lun1";
+ snprintf(name[config->fsg.nluns], MAX_LUN_NAME, "lun1");
config->fsg.nluns++;
}
- config->fsg.luns[0].removable = 1;
+ if (uicc_nluns > FSG_MAX_LUNS - config->fsg.nluns) {
+ uicc_nluns = FSG_MAX_LUNS - config->fsg.nluns;
+ pr_debug("limiting uicc luns to %d\n", uicc_nluns);
+ }
+
+ for (i = 0; i < uicc_nluns; i++) {
+ n = config->fsg.nluns;
+ snprintf(name[n], MAX_LUN_NAME, "uicc%d", i);
+ config->fsg.luns[n].removable = 1;
+ config->fsg.nluns++;
+ }
common = fsg_common_init(NULL, cdev, &config->fsg);
if (IS_ERR(common)) {
@@ -2973,6 +2987,10 @@
}
pdata->streaming_func_count = len;
+
+ ret = of_property_read_u32(pdev->dev.of_node,
+ "qcom,android-usb-uicc-nluns",
+ &pdata->uicc_nluns);
} else {
pdata = pdev->dev.platform_data;
}
diff --git a/drivers/usb/gadget/ci13xxx_msm.c b/drivers/usb/gadget/ci13xxx_msm.c
index f1e4220..be30b5f 100644
--- a/drivers/usb/gadget/ci13xxx_msm.c
+++ b/drivers/usb/gadget/ci13xxx_msm.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2010-2014, 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
@@ -62,11 +62,27 @@
struct ci13xxx *udc = _udc;
struct usb_phy *phy = udc->transceiver;
- if (phy && (phy->flags & ENABLE_DP_MANUAL_PULLUP))
+ if (phy && (phy->flags & ENABLE_DP_MANUAL_PULLUP)) {
+ u32 temp;
+
usb_phy_io_write(phy,
ULPI_MISC_A_VBUSVLDEXT |
ULPI_MISC_A_VBUSVLDEXTSEL,
ULPI_CLR(ULPI_MISC_A));
+
+ /* Notify LINK of VBUS LOW */
+ temp = readl_relaxed(USB_USBCMD);
+ temp &= ~USBCMD_SESS_VLD_CTRL;
+ writel_relaxed(temp, USB_USBCMD);
+
+ /*
+ * Add memory barrier as it is must to complete
+ * above USB PHY and Link register writes before
+ * moving ahead with USB peripheral mode enumeration,
+ * otherwise USB peripheral mode may not work.
+ */
+ mb();
+ }
}
/* Link power management will reduce power consumption by
@@ -345,6 +361,11 @@
return 0;
}
+void ci13xxx_msm_shutdown(struct platform_device *pdev)
+{
+ ci13xxx_pullup(&_udc->gadget, 0);
+}
+
void msm_hw_bam_disable(bool bam_disable)
{
u32 val;
@@ -364,6 +385,7 @@
.name = "msm_hsusb",
},
.remove = ci13xxx_msm_remove,
+ .shutdown = ci13xxx_msm_shutdown,
};
MODULE_ALIAS("platform:msm_hsusb");
diff --git a/drivers/usb/gadget/f_audio_source.c b/drivers/usb/gadget/f_audio_source.c
index 37f229b..33b645e 100644
--- a/drivers/usb/gadget/f_audio_source.c
+++ b/drivers/usb/gadget/f_audio_source.c
@@ -327,15 +327,22 @@
s64 msecs;
s64 frames;
ktime_t now;
+ unsigned long flags;
+ spin_lock_irqsave(&audio->lock, flags);
/* audio->substream will be null if we have been closed */
- if (!audio->substream)
+ if (!audio->substream) {
+ spin_unlock_irqrestore(&audio->lock, flags);
return;
+ }
/* audio->buffer_pos will be null if we have been stopped */
- if (!audio->buffer_pos)
+ if (!audio->buffer_pos) {
+ spin_unlock_irqrestore(&audio->lock, flags);
return;
+ }
runtime = audio->substream->runtime;
+ spin_unlock_irqrestore(&audio->lock, flags);
/* compute number of frames to send */
now = ktime_get();
@@ -359,8 +366,21 @@
while (frames > 0) {
req = audio_req_get(audio);
- if (!req)
+ spin_lock_irqsave(&audio->lock, flags);
+ /* audio->substream will be null if we have been closed */
+ if (!audio->substream) {
+ spin_unlock_irqrestore(&audio->lock, flags);
+ return;
+ }
+ /* audio->buffer_pos will be null if we have been stopped */
+ if (!audio->buffer_pos) {
+ spin_unlock_irqrestore(&audio->lock, flags);
+ return;
+ }
+ if (!req) {
+ spin_unlock_irqrestore(&audio->lock, flags);
break;
+ }
length = frames_to_bytes(runtime, frames);
if (length > IN_EP_MAX_PACKET_SIZE)
@@ -386,6 +406,7 @@
}
req->length = length;
+ spin_unlock_irqrestore(&audio->lock, flags);
ret = usb_ep_queue(audio->in_ep, req, GFP_ATOMIC);
if (ret < 0) {
pr_err("usb_ep_queue failed ret: %d\n", ret);
diff --git a/drivers/usb/gadget/f_diag.c b/drivers/usb/gadget/f_diag.c
index 16f961e..993d9fb 100644
--- a/drivers/usb/gadget/f_diag.c
+++ b/drivers/usb/gadget/f_diag.c
@@ -511,11 +511,11 @@
/* If error add the link to linked list again*/
spin_lock_irqsave(&ctxt->lock, flags);
list_add_tail(&req->list, &ctxt->write_pool);
- spin_unlock_irqrestore(&ctxt->lock, flags);
/* 1 error message for every 10 sec */
if (__ratelimit(&rl))
ERROR(ctxt->cdev, "%s: cannot queue"
" read request\n", __func__);
+ spin_unlock_irqrestore(&ctxt->lock, flags);
return -EIO;
}
diff --git a/drivers/usb/host/ehci-msm2.c b/drivers/usb/host/ehci-msm2.c
index 7ae0a54..ebb226c 100644
--- a/drivers/usb/host/ehci-msm2.c
+++ b/drivers/usb/host/ehci-msm2.c
@@ -46,6 +46,10 @@
#define PDEV_NAME_LEN 20
+static bool uicc_card_present;
+module_param(uicc_card_present, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(uicc_card_present, "UICC card inserted");
+
struct msm_hcd {
struct ehci_hcd ehci;
spinlock_t wakeup_lock;
@@ -295,6 +299,11 @@
static void msm_ehci_vbus_power(struct msm_hcd *mhcd, bool on)
{
int ret;
+ const struct msm_usb_host_platform_data *pdata;
+
+ pdata = mhcd->dev->platform_data;
+ if (pdata && pdata->is_uicc)
+ return;
if (!mhcd->vbus) {
pr_err("vbus is NULL.");
@@ -352,6 +361,10 @@
pdata = mhcd->dev->platform_data;
+ /* For uicc card connection, external vbus is not required */
+ if (pdata && pdata->is_uicc)
+ return 0;
+
if (!init) {
if (pdata && pdata->dock_connect_irq)
free_irq(pdata->dock_connect_irq, mhcd);
@@ -680,6 +693,8 @@
unsigned long timeout;
int ret;
u32 portsc;
+ const struct msm_usb_host_platform_data *pdata;
+ u32 func_ctrl;
if (atomic_read(&mhcd->in_lpm)) {
dev_dbg(mhcd->dev, "%s called in lpm\n", __func__);
@@ -696,6 +711,14 @@
return -EBUSY;
}
+ pdata = mhcd->dev->platform_data;
+ if (pdata && pdata->is_uicc) {
+ /* put the controller in non-driving mode */
+ func_ctrl = msm_ulpi_read(mhcd, ULPI_FUNC_CTRL);
+ func_ctrl &= ~ULPI_FUNC_CTRL_OPMODE_MASK;
+ func_ctrl |= ULPI_FUNC_CTRL_OPMODE_NONDRIVING;
+ msm_ulpi_write(mhcd, func_ctrl, ULPI_FUNC_CTRL);
+ }
/* If port is enabled wait 5ms for PHCD to come up. Reset PHY
* and link if it fails to do so.
* If port is not enabled set the PHCD bit and poll for it to
@@ -800,6 +823,8 @@
unsigned temp;
int ret;
unsigned long flags;
+ u32 func_ctrl;
+ const struct msm_usb_host_platform_data *pdata;
if (!atomic_read(&mhcd->in_lpm)) {
dev_dbg(mhcd->dev, "%s called in !in_lpm\n", __func__);
@@ -869,6 +894,14 @@
}
skip_phy_resume:
+ pdata = mhcd->dev->platform_data;
+ if (pdata && pdata->is_uicc) {
+ /* put the controller in normal mode */
+ func_ctrl = msm_ulpi_read(mhcd, ULPI_FUNC_CTRL);
+ func_ctrl &= ~ULPI_FUNC_CTRL_OPMODE_MASK;
+ func_ctrl |= ULPI_FUNC_CTRL_OPMODE_NORMAL;
+ msm_ulpi_write(mhcd, func_ctrl, ULPI_FUNC_CTRL);
+ }
usb_hcd_resume_root_hub(hcd);
atomic_set(&mhcd->in_lpm, 0);
@@ -1323,6 +1356,8 @@
pdata->resume_gpio = of_get_named_gpio(node, "qcom,resume-gpio", 0);
if (pdata->resume_gpio < 0)
pdata->resume_gpio = 0;
+ pdata->is_uicc = of_property_read_bool(node,
+ "qcom,usb2-enable-uicc");
return pdata;
}
@@ -1339,6 +1374,14 @@
dev_dbg(&pdev->dev, "ehci_msm2 probe\n");
+ /*
+ * Fail probe in case of uicc till userspace activates driver through
+ * sysfs entry.
+ */
+ if (!uicc_card_present && pdev->dev.of_node && of_property_read_bool(
+ pdev->dev.of_node, "qcom,usb2-enable-uicc"))
+ return -ENODEV;
+
if (pdev->dev.of_node) {
dev_dbg(&pdev->dev, "device tree enabled\n");
pdev->dev.platform_data = ehci_msm2_dt_to_pdata(pdev);
@@ -1612,6 +1655,10 @@
if (mhcd->resume_gpio)
gpio_free(mhcd->resume_gpio);
+ /* If the device was removed no need to call pm_runtime_disable */
+ if (pdev->dev.power.power_state.event != PM_EVENT_INVALID)
+ pm_runtime_disable(&pdev->dev);
+
device_init_wakeup(&pdev->dev, 0);
pm_runtime_set_suspended(&pdev->dev);
diff --git a/drivers/usb/host/ice40-hcd.c b/drivers/usb/host/ice40-hcd.c
index 4d62a3e..7b0a789 100644
--- a/drivers/usb/host/ice40-hcd.c
+++ b/drivers/usb/host/ice40-hcd.c
@@ -1225,7 +1225,7 @@
{
struct ice40_hcd *ihcd = hcd_to_ihcd(hcd);
u8 ctrl0;
- int ret;
+ int ret, i;
pm_stay_awake(&ihcd->spi->dev);
trace_ice40_bus_resume(0); /* start */
@@ -1234,7 +1234,18 @@
* Re-program the previous settings. For now we need to
* update the device address only.
*/
- ice40_spi_load_fw(ihcd);
+
+ for (i = 0; i < 3; i++) {
+ ret = ice40_spi_load_fw(ihcd);
+ if (!ret)
+ break;
+ }
+
+ if (ret) {
+ pr_err("Load firmware failed with ret: %d\n", ret);
+ return ret;
+ }
+
ice40_spi_reg_write(ihcd, ihcd->devnum, FADDR_REG);
ihcd->wblen0 = ~0;
@@ -1492,7 +1503,7 @@
static int ice40_spi_load_fw(struct ice40_hcd *ihcd)
{
int ret, i;
- struct gpiomux_setting old_setting;
+ struct gpiomux_setting active_old_setting, suspend_old_setting;
ret = gpio_direction_output(ihcd->reset_gpio, 0);
if (ret < 0) {
@@ -1514,21 +1525,38 @@
* We temporarily override the chip select config to
* drive it low. The SPI bus needs to be locked down during
* this period to avoid other slave data going to our
- * bridge chip.
- *
+ * bridge chip. Disable the SPI runtime suspend for exclusive
+ * chip select access.
*/
+ pm_runtime_get_sync(ihcd->spi->master->dev.parent);
+
spi_bus_lock(ihcd->spi->master);
ret = msm_gpiomux_write(ihcd->slave_select_gpio, GPIOMUX_SUSPENDED,
- &slave_select_setting, &old_setting);
+ &slave_select_setting, &suspend_old_setting);
if (ret < 0) {
- pr_err("fail to select the slave %d\n", ret);
+ pr_err("fail to override suspend setting and select slave %d\n",
+ ret);
+ spi_bus_unlock(ihcd->spi->master);
+ pm_runtime_put_noidle(ihcd->spi->master->dev.parent);
+ goto out;
+ }
+
+ ret = msm_gpiomux_write(ihcd->slave_select_gpio, GPIOMUX_ACTIVE,
+ &slave_select_setting, &active_old_setting);
+ if (ret < 0) {
+ pr_err("fail to override active setting and select slave %d\n",
+ ret);
+ spi_bus_unlock(ihcd->spi->master);
+ pm_runtime_put_noidle(ihcd->spi->master->dev.parent);
goto out;
}
ret = ice40_spi_power_up(ihcd);
if (ret < 0) {
pr_err("fail to power up the chip\n");
+ spi_bus_unlock(ihcd->spi->master);
+ pm_runtime_put_noidle(ihcd->spi->master->dev.parent);
goto out;
}
@@ -1540,12 +1568,25 @@
usleep_range(1200, 1250);
ret = msm_gpiomux_write(ihcd->slave_select_gpio, GPIOMUX_SUSPENDED,
- &old_setting, NULL);
+ &suspend_old_setting, NULL);
if (ret < 0) {
- pr_err("fail to de-select the slave %d\n", ret);
+ pr_err("fail to rewrite suspend setting %d\n", ret);
+ spi_bus_unlock(ihcd->spi->master);
+ pm_runtime_put_noidle(ihcd->spi->master->dev.parent);
goto power_off;
}
+ ret = msm_gpiomux_write(ihcd->slave_select_gpio, GPIOMUX_ACTIVE,
+ &active_old_setting, NULL);
+ if (ret < 0) {
+ pr_err("fail to rewrite active setting %d\n", ret);
+ spi_bus_unlock(ihcd->spi->master);
+ pm_runtime_put_noidle(ihcd->spi->master->dev.parent);
+ goto power_off;
+ }
+
+ pm_runtime_put_noidle(ihcd->spi->master->dev.parent);
+
ret = spi_sync_locked(ihcd->spi, ihcd->fmsg);
spi_bus_unlock(ihcd->spi->master);
@@ -1884,6 +1925,13 @@
ihcd->port_flags |= (USB_PORT_STAT_C_CONNECTION << 16);
ihcd->pcd_pending = true;
usb_hcd_poll_rh_status(ihcd->hcd);
+ } else if (!strcmp(buf, "config_test")) {
+ ice40_spi_power_off(ihcd);
+ ret = ice40_spi_load_fw(ihcd);
+ if (ret) {
+ pr_err("config load failed\n");
+ goto out;
+ }
} else {
ret = -EINVAL;
goto out;
diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c
index cc0c1e0..1634fcf 100644
--- a/drivers/usb/host/xhci-plat.c
+++ b/drivers/usb/host/xhci-plat.c
@@ -51,6 +51,17 @@
return xhci_gen_setup(hcd, xhci_plat_quirks);
}
+static void xhci_plat_phy_autosuspend(struct usb_hcd *hcd,
+ int enable_autosuspend)
+{
+ if (!phy || !phy->set_phy_autosuspend)
+ return;
+
+ usb_phy_set_autosuspend(phy, enable_autosuspend);
+
+ return;
+}
+
static const struct hc_driver xhci_plat_xhci_driver = {
.description = "xhci-hcd",
.product_desc = "xHCI Host Controller",
@@ -98,6 +109,7 @@
.hub_status_data = xhci_hub_status_data,
.bus_suspend = xhci_bus_suspend,
.bus_resume = xhci_bus_resume,
+ .set_autosuspend = xhci_plat_phy_autosuspend,
};
static int xhci_plat_probe(struct platform_device *pdev)
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index b3f3fa8..e1c0096 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -647,6 +647,8 @@
struct xhci_dequeue_state deq_state;
+ if (xhci->main_hcd->driver->set_autosuspend)
+ xhci->main_hcd->driver->set_autosuspend(xhci->main_hcd, 1);
if (unlikely(TRB_TO_SUSPEND_PORT(
le32_to_cpu(xhci->cmd_ring->dequeue->generic.field[3])))) {
slot_id = TRB_TO_SLOT_ID(
@@ -800,6 +802,8 @@
spin_lock_irqsave(&xhci->lock, flags);
+ if (xhci->main_hcd->driver->set_autosuspend)
+ xhci->main_hcd->driver->set_autosuspend(xhci->main_hcd, 1);
ep->stop_cmds_pending--;
if (xhci->xhc_state & XHCI_STATE_DYING) {
xhci_dbg(xhci, "Stop EP timer ran, but another timer marked "
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 0a82e58..f8bc1c5 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -1386,6 +1386,8 @@
ep->stop_cmd_timer.expires = jiffies +
XHCI_STOP_EP_CMD_TIMEOUT * HZ;
add_timer(&ep->stop_cmd_timer);
+ if (hcd->driver->set_autosuspend)
+ hcd->driver->set_autosuspend(hcd, 0);
xhci_queue_stop_endpoint(xhci, urb->dev->slot_id, ep_index, 0);
xhci_ring_cmd_db(xhci);
}
diff --git a/drivers/usb/otg/msm_otg.c b/drivers/usb/otg/msm_otg.c
index 15a9ab0..b5641b6 100644
--- a/drivers/usb/otg/msm_otg.c
+++ b/drivers/usb/otg/msm_otg.c
@@ -1381,6 +1381,16 @@
return -ENXIO;
}
+static void msm_otg_set_online_status(struct msm_otg *motg)
+{
+ if (!psy)
+ dev_dbg(motg->phy.dev, "no usb power supply registered\n");
+
+ /* Set power supply online status to false */
+ if (power_supply_set_online(psy, false))
+ dev_dbg(motg->phy.dev, "error setting power supply property\n");
+}
+
static void msm_otg_notify_charger(struct msm_otg *motg, unsigned mA)
{
struct usb_gadget *g = motg->phy.otg->gadget;
@@ -1400,6 +1410,13 @@
"Failed notifying %d charger type to PMIC\n",
motg->chg_type);
+ /*
+ * This condition will be true when usb cable is disconnected
+ * during bootup before charger detection mechanism starts.
+ */
+ if (motg->online && motg->cur_power == 0 && mA == 0)
+ msm_otg_set_online_status(motg);
+
if (motg->cur_power == mA)
return;
@@ -2526,12 +2543,15 @@
if (motg->ext_chg_active) {
+do_wait:
pr_debug("before msm_otg ext chg wait\n");
t = wait_for_completion_timeout(&motg->ext_chg_wait,
msecs_to_jiffies(3000));
if (!t)
pr_err("msm_otg ext chg wait timeout\n");
+ else if (motg->ext_chg_active)
+ goto do_wait;
else
pr_debug("msm_otg ext chg wait done\n");
}
@@ -2556,8 +2576,10 @@
bool work = 0, srp_reqd, dcp;
pm_runtime_resume(otg->phy->dev);
- if (motg->pm_done)
+ if (motg->pm_done) {
pm_runtime_get_sync(otg->phy->dev);
+ motg->pm_done = 0;
+ }
pr_debug("%s work\n", otg_state_string(otg->phy->state));
switch (otg->phy->state) {
case OTG_STATE_UNDEFINED:
@@ -2683,6 +2705,7 @@
/* Turn off VDP_SRC */
ulpi_write(otg->phy, 0x2, 0x86);
}
+ msm_chg_block_off(motg);
msm_otg_reset(otg->phy);
/*
* There is a small window where ID interrupt
@@ -4031,6 +4054,7 @@
pr_debug("%s: LPM block request %d\n", __func__, val);
if (val) { /* block LPM */
if (motg->chg_type == USB_DCP_CHARGER) {
+ motg->ext_chg_active = true;
/*
* If device is already suspended, resume it.
* The PM usage counter is incremented in
@@ -4077,6 +4101,18 @@
else
pr_debug("%s:voltage request failed\n", __func__);
break;
+ case MSM_USB_EXT_CHG_TYPE:
+ if (get_user(val, (int __user *)arg)) {
+ pr_err("%s: get_user failed\n\n", __func__);
+ ret = -EFAULT;
+ break;
+ }
+
+ if (val)
+ pr_debug("%s:charger is external charger\n", __func__);
+ else
+ pr_debug("%s:charger is not ext charger\n", __func__);
+ break;
default:
ret = -EINVAL;
}
diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c
index 669fca9..05292f9 100644
--- a/drivers/usb/storage/scsiglue.c
+++ b/drivers/usb/storage/scsiglue.c
@@ -235,6 +235,15 @@
sdev->use_rpm_auto = 1;
sdev->autosuspend_delay = us->sdev_autosuspend_delay;
}
+
+ /*
+ * This quirk enables sending consecutive TEST_UNIT_READY
+ * commands in WRITE(10) command processing context. Increase
+ * the timeout to 60 seconds.
+ */
+ if (us->fflags & US_FL_TUR_AFTER_WRITE)
+ blk_queue_rq_timeout(sdev->request_queue, (60 * HZ));
+
} else {
/* Non-disk-type devices don't need to blacklist any pages
diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c
index c70109e..a710d9f 100644
--- a/drivers/usb/storage/transport.c
+++ b/drivers/usb/storage/transport.c
@@ -859,6 +859,42 @@
srb->result = DID_ERROR << 16;
last_sector_hacks(us, srb);
+
+ /*
+ * TMC UICC cards expect 5 TEST_UNIT_READY commands after
+ * writing some data. The card performs the flash related
+ * house keeping operations after receiving these commands.
+ * Send 5 TEST_UNIT_READY commands for every 8 WRITE_10
+ * commands.
+ */
+ if (unlikely((us->fflags & US_FL_TUR_AFTER_WRITE) &&
+ srb->cmnd[0] == WRITE_10)) {
+ int i;
+ int temp_result;
+ struct scsi_eh_save ses;
+ unsigned char cmd[] = {
+ TEST_UNIT_READY, 0, 0, 0, 0, 0,
+ };
+
+ us->tur_count[srb->device->lun]++;
+
+ if (++us->tur_count[srb->device->lun] == 8) {
+
+ us->tur_count[srb->device->lun] = 0;
+
+ scsi_eh_prep_cmnd(srb, &ses, cmd, 6, 0);
+ for (i = 0; i < 5; i++) {
+ temp_result = us->transport(us->srb, us);
+ if (temp_result != USB_STOR_TRANSPORT_GOOD) {
+ US_DEBUGP("TUR failed %d %d\n",
+ i, temp_result);
+ break;
+ }
+ }
+ scsi_eh_restore_cmnd(srb, &ses);
+ }
+ }
+
return;
/* Error and abort processing: try to resynchronize with the device
diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h
index 856ad92..901f6fb 100644
--- a/drivers/usb/storage/unusual_devs.h
+++ b/drivers/usb/storage/unusual_devs.h
@@ -2018,6 +2018,12 @@
"Digital MP3 Audio Player",
USB_SC_DEVICE, USB_PR_DEVICE, NULL, US_FL_NOT_LOCKABLE ),
+/* Reported by Pavankumar Kondeti <pkondeti@codeaurora.org> */
+UNUSUAL_DEV(0x0925, 0x9011, 0x0100, 0x0100,
+ "TMC",
+ "USB DISK",
+ USB_SC_DEVICE, USB_PR_DEVICE, NULL, US_FL_TUR_AFTER_WRITE),
+
/* Control/Bulk transport for all SubClass values */
USUAL_DEV(USB_SC_RBC, USB_PR_CB, USB_US_TYPE_STOR),
USUAL_DEV(USB_SC_8020, USB_PR_CB, USB_US_TYPE_STOR),
diff --git a/drivers/usb/storage/usb.h b/drivers/usb/storage/usb.h
index db75080..b079984 100644
--- a/drivers/usb/storage/usb.h
+++ b/drivers/usb/storage/usb.h
@@ -162,6 +162,8 @@
int use_last_sector_hacks;
int last_sector_retries;
int sdev_autosuspend_delay;
+ /* consecutive TEST_UNIT_READY commands during write */
+ int tur_count[16];
};
/* Convert between us_data and the corresponding Scsi_Host */
diff --git a/drivers/video/msm/mdss/Makefile b/drivers/video/msm/mdss/Makefile
index fd03b63..ae13322 100644
--- a/drivers/video/msm/mdss/Makefile
+++ b/drivers/video/msm/mdss/Makefile
@@ -1,19 +1,20 @@
-mdss-mdp3-objs = mdp3.o mdp3_dma.o mdp3_ctrl.o
+mdss-mdp3-objs = mdp3.o mdp3_dma.o mdp3_ctrl.o dsi_status_v2.o
mdss-mdp3-objs += mdp3_ppp.o mdp3_ppp_hwio.o mdp3_ppp_data.o
obj-$(CONFIG_FB_MSM_MDSS_MDP3) += mdss-mdp3.o
-mdss-mdp-objs := mdss_mdp.o mdss_mdp_ctl.o mdss_mdp_pipe.o mdss_mdp_util.o
+mdss-mdp-objs := mdss_mdp.o mdss_mdp_ctl.o mdss_mdp_pipe.o mdss_mdp_util.o dsi_status_6g.o
mdss-mdp-objs += mdss_mdp_pp.o
mdss-mdp-objs += mdss_mdp_intf_video.o
mdss-mdp-objs += mdss_mdp_intf_cmd.o
mdss-mdp-objs += mdss_mdp_intf_writeback.o
mdss-mdp-objs += mdss_mdp_rotator.o
mdss-mdp-objs += mdss_mdp_overlay.o
+mdss-mdp-objs += mdss_mdp_splash_logo.o
mdss-mdp-objs += mdss_mdp_wb.o
obj-$(CONFIG_FB_MSM_MDSS) += mdss-mdp.o
ifeq ($(CONFIG_FB_MSM_MDSS),y)
-obj-$(CONFIG_DEBUG_FS) += mdss_debug.o
+obj-$(CONFIG_DEBUG_FS) += mdss_debug.o mdss_debug_xlog.o
endif
dsi-v2-objs = dsi_v2.o dsi_host_v2.o dsi_io_v2.o
@@ -42,8 +43,4 @@
obj-$(CONFIG_FB_MSM_MDSS) += mdss_fb.o
-ifeq ($(CONFIG_FB_MSM_MDSS_MDP3),y)
-obj-$(CONFIG_FB_MSM_MDSS_DSI_CTRL_STATUS) += dsi_status_v2.o
-else
obj-$(CONFIG_FB_MSM_MDSS_DSI_CTRL_STATUS) += mdss_dsi_status.o
-endif
diff --git a/drivers/video/msm/mdss/dsi_host_v2.c b/drivers/video/msm/mdss/dsi_host_v2.c
index 884f7f2..99e1c21 100644
--- a/drivers/video/msm/mdss/dsi_host_v2.c
+++ b/drivers/video/msm/mdss/dsi_host_v2.c
@@ -26,6 +26,7 @@
#include "dsi_io_v2.h"
#include "dsi_host_v2.h"
#include "mdss_debug.h"
+#include "mdp3.h"
#define DSI_POLL_SLEEP_US 1000
#define DSI_POLL_TIMEOUT_US 16000
@@ -203,6 +204,13 @@
struct mdss_dsi_ctrl_pdata *ctrl =
(struct mdss_dsi_ctrl_pdata *)ptr;
+ spin_lock(&ctrl->mdp_lock);
+
+ if (ctrl->dsi_irq_mask == 0) {
+ spin_unlock(&ctrl->mdp_lock);
+ return IRQ_HANDLED;
+ }
+
isr = MIPI_INP(dsi_host_private->dsi_base + DSI_INT_CTRL);
MIPI_OUTP(dsi_host_private->dsi_base + DSI_INT_CTRL, isr);
@@ -213,22 +221,20 @@
msm_dsi_error(dsi_host_private->dsi_base);
}
- spin_lock(&ctrl->mdp_lock);
-
if (isr & DSI_INTR_VIDEO_DONE)
complete(&ctrl->video_comp);
if (isr & DSI_INTR_CMD_DMA_DONE)
complete(&ctrl->dma_comp);
- spin_unlock(&ctrl->mdp_lock);
-
if (isr & DSI_INTR_BTA_DONE)
complete(&ctrl->bta_comp);
if (isr & DSI_INTR_CMD_MDP_DONE)
complete(&ctrl->mdp_comp);
+ spin_unlock(&ctrl->mdp_lock);
+
return IRQ_HANDLED;
}
@@ -236,6 +242,13 @@
struct mdss_dsi_ctrl_pdata *ctrl)
{
int ret;
+ u32 isr;
+
+ msm_dsi_ahb_ctrl(1);
+ isr = MIPI_INP(dsi_host_private->dsi_base + DSI_INT_CTRL);
+ isr &= ~DSI_INTR_ALL_MASK;
+ MIPI_OUTP(dsi_host_private->dsi_base + DSI_INT_CTRL, isr);
+ msm_dsi_ahb_ctrl(0);
ret = devm_request_irq(dev, irq_no, msm_dsi_isr_handler,
IRQF_DISABLED, "DSI", ctrl);
@@ -959,7 +972,13 @@
mutex_unlock(&ctrl->cmd_mutex);
return ret;
}
-
+ /*
+ * mdss interrupt is generated in mdp core clock domain
+ * mdp clock need to be enabled to receive dsi interrupt
+ * also, axi bus bandwidth need since dsi controller will
+ * fetch dcs commands from axi bus
+ */
+ mdp3_res_update(1, 1, MDP3_CLIENT_DMA_P);
msm_dsi_clk_ctrl(&ctrl->panel_data, 1);
if (0 == (req->flags & CMD_REQ_LP_MODE))
@@ -974,6 +993,7 @@
dsi_set_tx_power_mode(1);
msm_dsi_clk_ctrl(&ctrl->panel_data, 0);
+ mdp3_res_update(0, 1, MDP3_CLIENT_DMA_P);
mutex_unlock(&ctrl->cmd_mutex);
return 0;
@@ -1419,13 +1439,16 @@
&byteclk_rate, &pclk_rate);
msm_dsi_clk_set_rate(DSI_ESC_CLK_RATE, dsiclk_rate,
byteclk_rate, pclk_rate);
+ msm_dsi_prepare_clocks();
msm_dsi_clk_enable();
}
} else {
dsi_host_private->clk_count--;
if (dsi_host_private->clk_count == 0) {
+ msm_dsi_clear_irq(ctrl_pdata, ctrl_pdata->dsi_irq_mask);
msm_dsi_clk_set_rate(DSI_ESC_CLK_RATE, 0, 0, 0);
msm_dsi_clk_disable();
+ msm_dsi_unprepare_clocks();
msm_dsi_ahb_ctrl(0);
}
}
@@ -1510,14 +1533,6 @@
__func__, __LINE__);
rc = -ENODEV;
goto error_irq_resource;
- } else {
- rc = msm_dsi_irq_init(&pdev->dev, mdss_dsi_mres->start,
- ctrl_pdata);
- if (rc) {
- dev_err(&pdev->dev, "%s: failed to init irq, rc=%d\n",
- __func__, rc);
- goto error_irq_resource;
- }
}
rc = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
@@ -1580,6 +1595,14 @@
msm_dsi_ctrl_init(ctrl_pdata);
+ rc = msm_dsi_irq_init(&pdev->dev, mdss_dsi_mres->start,
+ ctrl_pdata);
+ if (rc) {
+ dev_err(&pdev->dev, "%s: failed to init irq, rc=%d\n",
+ __func__, rc);
+ goto error_device_register;
+ }
+
rc = dsi_panel_device_register_v2(pdev, ctrl_pdata);
if (rc) {
pr_err("%s: dsi panel dev reg failed\n", __func__);
diff --git a/drivers/video/msm/mdss/dsi_host_v2.h b/drivers/video/msm/mdss/dsi_host_v2.h
index b297452..0f3ea8d 100644
--- a/drivers/video/msm/mdss/dsi_host_v2.h
+++ b/drivers/video/msm/mdss/dsi_host_v2.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2014, 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
@@ -25,6 +25,7 @@
#define DSI_INTR_CMD_MDP_DONE BIT(8)
#define DSI_INTR_CMD_DMA_DONE_MASK BIT(1)
#define DSI_INTR_CMD_DMA_DONE BIT(0)
+#define DSI_INTR_ALL_MASK 0x2220202
#define DSI_BTA_TERM BIT(1)
diff --git a/drivers/video/msm/mdss/dsi_status_6g.c b/drivers/video/msm/mdss/dsi_status_6g.c
new file mode 100644
index 0000000..4f7e4da
--- /dev/null
+++ b/drivers/video/msm/mdss/dsi_status_6g.c
@@ -0,0 +1,118 @@
+/* Copyright (c) 2013-2014, 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/workqueue.h>
+#include <linux/delay.h>
+#include <linux/kobject.h>
+#include <linux/sysfs.h>
+
+#include "mdss_dsi.h"
+#include "mdss_mdp.h"
+
+/*
+ * mdss_check_dsi_ctrl_status() - Check MDP5 DSI controller status periodically.
+ * @work : dsi controller status data
+ * @interval : duration in milliseconds to schedule work queue
+ *
+ * This function calls check_status API on DSI controller to send the BTA
+ * command. If DSI controller fails to acknowledge the BTA command, it sends
+ * the PANEL_ALIVE=0 status to HAL layer.
+ */
+void mdss_check_dsi_ctrl_status(struct work_struct *work, uint32_t interval)
+{
+ struct dsi_status_data *pstatus_data = NULL;
+ struct mdss_panel_data *pdata = NULL;
+ struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL;
+ struct mdss_overlay_private *mdp5_data = NULL;
+ struct mdss_mdp_ctl *ctl = NULL;
+ int ret = 0;
+
+ pstatus_data = container_of(to_delayed_work(work),
+ struct dsi_status_data, check_status);
+ if (!pstatus_data || !(pstatus_data->mfd)) {
+ pr_err("%s: mfd not available\n", __func__);
+ return;
+ }
+
+ pdata = dev_get_platdata(&pstatus_data->mfd->pdev->dev);
+ if (!pdata) {
+ pr_err("%s: Panel data not available\n", __func__);
+ return;
+ }
+
+ ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata,
+ panel_data);
+ if (!ctrl_pdata || !ctrl_pdata->check_status) {
+ pr_err("%s: DSI ctrl or status_check callback not available\n",
+ __func__);
+ return;
+ }
+
+ mdp5_data = mfd_to_mdp5_data(pstatus_data->mfd);
+ ctl = mfd_to_ctl(pstatus_data->mfd);
+
+ if (!ctl) {
+ pr_err("%s: Display is off\n", __func__);
+ return;
+ }
+
+ if (ctl->shared_lock)
+ mutex_lock(ctl->shared_lock);
+ mutex_lock(&mdp5_data->ov_lock);
+
+ if (pstatus_data->mfd->shutdown_pending) {
+ mutex_unlock(&mdp5_data->ov_lock);
+ if (ctl->shared_lock)
+ mutex_unlock(ctl->shared_lock);
+ pr_err("%s: DSI turning off, avoiding BTA status check\n",
+ __func__);
+ return;
+ }
+
+ /*
+ * For the command mode panels, we return pan display
+ * IOCTL on vsync interrupt. So, after vsync interrupt comes
+ * and when DMA_P is in progress, if the panel stops responding
+ * and if we trigger BTA before DMA_P finishes, then the DSI
+ * FIFO will not be cleared since the DSI data bus control
+ * doesn't come back to the host after BTA. This may cause the
+ * display reset not to be proper. Hence, wait for DMA_P done
+ * for command mode panels before triggering BTA.
+ */
+ if (ctl->wait_pingpong)
+ ctl->wait_pingpong(ctl, NULL);
+
+ pr_debug("%s: DSI ctrl wait for ping pong done\n", __func__);
+
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
+ ret = ctrl_pdata->check_status(ctrl_pdata);
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
+
+ mutex_unlock(&mdp5_data->ov_lock);
+ if (ctl->shared_lock)
+ mutex_unlock(ctl->shared_lock);
+
+ if ((pstatus_data->mfd->panel_power_on)) {
+ if (ret > 0) {
+ schedule_delayed_work(&pstatus_data->check_status,
+ msecs_to_jiffies(interval));
+ } else {
+ char *envp[2] = {"PANEL_ALIVE=0", NULL};
+ pdata->panel_info.panel_dead = true;
+ ret = kobject_uevent_env(
+ &pstatus_data->mfd->fbi->dev->kobj,
+ KOBJ_CHANGE, envp);
+ pr_err("%s: Panel has gone bad, sending uevent - %s\n",
+ __func__, envp[0]);
+ }
+ }
+}
diff --git a/drivers/video/msm/mdss/dsi_status_v2.c b/drivers/video/msm/mdss/dsi_status_v2.c
index d62ddf3..f0966cc 100644
--- a/drivers/video/msm/mdss/dsi_status_v2.c
+++ b/drivers/video/msm/mdss/dsi_status_v2.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -10,49 +10,26 @@
* GNU General Public License for more details.
*/
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/fb.h>
-#include <linux/notifier.h>
#include <linux/workqueue.h>
#include <linux/delay.h>
-#include <linux/debugfs.h>
-#include <linux/slab.h>
-#include <linux/uaccess.h>
-#include <linux/iopoll.h>
#include <linux/kobject.h>
#include <linux/string.h>
#include <linux/sysfs.h>
-#include "mdss_fb.h"
#include "mdss_dsi.h"
-#include "mdss_panel.h"
#include "mdp3_ctrl.h"
-#define STATUS_CHECK_INTERVAL 5000
-
-/**
- * dsi_status_data - Stores all the data necessary for this module
- * @fb_notif: Used to egister for the fb events
- * @live_status: Delayed worker structure, used to associate the
- * delayed worker function
- * @mfd: Used to store the msm_fb_data_type received when the notifier
- * call back happens
- * @root: Stores the dir created by debuugfs
- * @debugfs_reset_panel: The debugfs variable used to inject errors
+/*
+ * mdp3_check_dsi_ctrl_status() - Check MDP3 DSI controller status periodically.
+ * @work : dsi controller status data
+ * @interval : duration in milliseconds to schedule work queue
+ *
+ * This function calls check_status API on DSI controller to send the BTA
+ * command. If DSI controller fails to acknowledge the BTA command, it sends
+ * the PANEL_ALIVE=0 status to HAL layer.
*/
-
-struct dsi_status_data {
- struct notifier_block fb_notifier;
- struct delayed_work check_status;
- struct msm_fb_data_type *mfd;
- uint32_t check_interval;
-};
-struct dsi_status_data *pstatus_data;
-static uint32_t interval = STATUS_CHECK_INTERVAL;
-
-void check_dsi_ctrl_status(struct work_struct *work)
+void mdp3_check_dsi_ctrl_status(struct work_struct *work,
+ uint32_t interval)
{
struct dsi_status_data *pdsi_status = NULL;
struct mdss_panel_data *pdata = NULL;
@@ -61,9 +38,10 @@
int ret = 0;
pdsi_status = container_of(to_delayed_work(work),
- struct dsi_status_data, check_status);
- if (!pdsi_status) {
- pr_err("%s: DSI status data not available\n", __func__);
+ struct dsi_status_data, check_status);
+
+ if (!pdsi_status || !(pdsi_status->mfd)) {
+ pr_err("%s: mfd not available\n", __func__);
return;
}
@@ -72,117 +50,51 @@
pr_err("%s: Panel data not available\n", __func__);
return;
}
+
ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata,
panel_data);
+
if (!ctrl_pdata || !ctrl_pdata->check_status) {
- pr_err("%s: DSI ctrl or status_check callback not avilable\n",
+ pr_err("%s: DSI ctrl or status_check callback not available\n",
__func__);
return;
}
+
mdp3_session = pdsi_status->mfd->mdp.private1;
+ if (!mdp3_session) {
+ pr_err("%s: Display is off\n", __func__);
+ return;
+ }
+
mutex_lock(&mdp3_session->lock);
+ if (!mdp3_session->status) {
+ pr_info("display off already\n");
+ mutex_unlock(&mdp3_session->lock);
+ return;
+ }
- ret = ctrl_pdata->check_status(ctrl_pdata);
+ if (mdp3_session->wait_for_dma_done)
+ ret = mdp3_session->wait_for_dma_done(mdp3_session);
+ if (!ret)
+ ret = ctrl_pdata->check_status(ctrl_pdata);
+ else
+ pr_err("%s: wait_for_dma_done error\n", __func__);
mutex_unlock(&mdp3_session->lock);
if ((pdsi_status->mfd->panel_power_on)) {
if (ret > 0) {
schedule_delayed_work(&pdsi_status->check_status,
- msecs_to_jiffies(pdsi_status->check_interval));
+ msecs_to_jiffies(interval));
} else {
char *envp[2] = {"PANEL_ALIVE=0", NULL};
pdata->panel_info.panel_dead = true;
ret = kobject_uevent_env(
- &pdsi_status->mfd->fbi->dev->kobj,
- KOBJ_CHANGE, envp);
+ &pdsi_status->mfd->fbi->dev->kobj,
+ KOBJ_CHANGE, envp);
pr_err("%s: Panel has gone bad, sending uevent - %s\n",
__func__, envp[0]);
}
}
}
-/**
- * fb_notifier_callback() - Call back function for the fb_register_client()
- * notifying events
- * @self : notifier block
- * @event : The event that was triggered
- * @data : Of type struct fb_event
- *
- * - This function listens for FB_BLANK_UNBLANK and FB_BLANK_POWERDOWN events
- * - Based on the event the delayed work is either scheduled again after
- * PANEL_STATUS_CHECK_INTERVAL or cancelled
- */
-static int fb_event_callback(struct notifier_block *self,
- unsigned long event, void *data)
-{
- struct fb_event *evdata = (struct fb_event *)data;
- struct dsi_status_data *pdata = container_of(self,
- struct dsi_status_data, fb_notifier);
- pdata->mfd = (struct msm_fb_data_type *)evdata->info->par;
-
- if (event == FB_EVENT_BLANK && evdata) {
- int *blank = evdata->data;
- switch (*blank) {
- case FB_BLANK_UNBLANK:
- schedule_delayed_work(&pdata->check_status,
- msecs_to_jiffies(STATUS_CHECK_INTERVAL));
- break;
- case FB_BLANK_POWERDOWN:
- cancel_delayed_work(&pdata->check_status);
- break;
- }
- }
- return 0;
-}
-
-int __init mdss_dsi_status_init(void)
-{
- int rc;
-
- pstatus_data = kzalloc(sizeof(struct dsi_status_data), GFP_KERNEL);
- if (!pstatus_data) {
- pr_err("%s: can't alloc mem\n", __func__);
- rc = -ENOMEM;
- return rc;
- }
-
- memset(pstatus_data, 0, sizeof(struct dsi_status_data));
-
- pstatus_data->fb_notifier.notifier_call = fb_event_callback;
-
- rc = fb_register_client(&pstatus_data->fb_notifier);
- if (rc < 0) {
- pr_err("%s: fb_register_client failed, returned with rc=%d\n",
- __func__, rc);
- kfree(pstatus_data);
- return -EPERM;
- }
-
- pstatus_data->check_interval = interval;
- pr_info("%s: DSI status check interval:%d\n", __func__, interval);
-
- INIT_DELAYED_WORK(&pstatus_data->check_status, check_dsi_ctrl_status);
-
- pr_debug("%s: DSI ctrl status thread initialized\n", __func__);
-
- return rc;
-}
-
-void __exit mdss_dsi_status_exit(void)
-{
- fb_unregister_client(&pstatus_data->fb_notifier);
- cancel_delayed_work_sync(&pstatus_data->check_status);
- kfree(pstatus_data);
- pr_debug("%s: DSI ctrl status thread removed\n", __func__);
-}
-
-module_param(interval, uint, 0);
-MODULE_PARM_DESC(interval,
- "Duration in milliseconds to send BTA command for checking"
- "DSI status periodically");
-
-module_init(mdss_dsi_status_init);
-module_exit(mdss_dsi_status_exit);
-
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/video/msm/mdss/mdp3.c b/drivers/video/msm/mdss/mdp3.c
index 9f608b8..37134b4 100644
--- a/drivers/video/msm/mdss/mdp3.c
+++ b/drivers/video/msm/mdss/mdp3.c
@@ -47,6 +47,7 @@
#include <mach/iommu.h>
#include <mach/iommu_domains.h>
#include <mach/msm_memtypes.h>
+#include <mach/rpm-regulator-smd.h>
#include "mdp3.h"
#include "mdss_fb.h"
@@ -189,11 +190,11 @@
u32 mdp_status = 0;
spin_lock(&mdata->irq_lock);
- if (!mdata->irq_mask)
+ if (!mdata->irq_mask) {
pr_err("spurious interrupt\n");
-
- clk_enable(mdp3_res->clocks[MDP3_CLK_AHB]);
- clk_enable(mdp3_res->clocks[MDP3_CLK_CORE]);
+ spin_unlock(&mdata->irq_lock);
+ return IRQ_HANDLED;
+ }
mdp_status = MDP3_REG_READ(MDP3_REG_INTR_STATUS);
mdp_interrupt = mdp_status;
@@ -209,9 +210,6 @@
}
MDP3_REG_WRITE(MDP3_REG_INTR_CLEAR, mdp_status);
- clk_disable(mdp3_res->clocks[MDP3_CLK_AHB]);
- clk_disable(mdp3_res->clocks[MDP3_CLK_CORE]);
-
spin_unlock(&mdata->irq_lock);
return IRQ_HANDLED;
@@ -279,19 +277,51 @@
pr_debug("mdp3_irq_register\n");
spin_lock_irqsave(&mdp3_res->irq_lock, flag);
- enable_irq(mdp3_res->irq);
+ mdp3_res->irq_ref_cnt++;
+ if (mdp3_res->irq_ref_cnt == 1) {
+ MDP3_REG_WRITE(MDP3_REG_INTR_ENABLE, mdp3_res->irq_mask);
+ enable_irq(mdp3_res->irq);
+ }
spin_unlock_irqrestore(&mdp3_res->irq_lock, flag);
}
void mdp3_irq_deregister(void)
{
unsigned long flag;
+ bool irq_enabled = true;
pr_debug("mdp3_irq_deregister\n");
spin_lock_irqsave(&mdp3_res->irq_lock, flag);
memset(mdp3_res->irq_ref_count, 0, sizeof(u32) * MDP3_MAX_INTR);
mdp3_res->irq_mask = 0;
- disable_irq_nosync(mdp3_res->irq);
+ MDP3_REG_WRITE(MDP3_REG_INTR_ENABLE, 0);
+ mdp3_res->irq_ref_cnt--;
+ /* This can happen if suspend is called first */
+ if (mdp3_res->irq_ref_cnt < 0) {
+ irq_enabled = false;
+ mdp3_res->irq_ref_cnt = 0;
+ }
+ if (mdp3_res->irq_ref_cnt == 0 && irq_enabled)
+ disable_irq_nosync(mdp3_res->irq);
+ spin_unlock_irqrestore(&mdp3_res->irq_lock, flag);
+}
+
+void mdp3_irq_suspend(void)
+{
+ unsigned long flag;
+ bool irq_enabled = true;
+
+ pr_debug("%s\n", __func__);
+ spin_lock_irqsave(&mdp3_res->irq_lock, flag);
+ mdp3_res->irq_ref_cnt--;
+ if (mdp3_res->irq_ref_cnt < 0) {
+ irq_enabled = false;
+ mdp3_res->irq_ref_cnt = 0;
+ }
+ if (mdp3_res->irq_ref_cnt == 0 && irq_enabled) {
+ MDP3_REG_WRITE(MDP3_REG_INTR_ENABLE, 0);
+ disable_irq_nosync(mdp3_res->irq);
+ }
spin_unlock_irqrestore(&mdp3_res->irq_lock, flag);
}
@@ -401,6 +431,12 @@
}
bus_handle->current_bus_idx = bus_idx;
rc = msm_bus_scale_client_update_request(bus_handle->handle, bus_idx);
+
+ if (!rc && ab_quota != 0 && ib_quota != 0) {
+ bus_handle->restore_ab = ab_quota;
+ bus_handle->restore_ib = ib_quota;
+ }
+
return rc;
}
@@ -423,10 +459,18 @@
count = mdp3_res->clock_ref_count[clk_idx];
if (count == 1 && enable) {
pr_debug("clk=%d en=%d\n", clk_idx, enable);
+ ret = clk_prepare(clk);
+ if (ret) {
+ pr_err("%s: Failed to prepare clock %d",
+ __func__, clk_idx);
+ mdp3_res->clock_ref_count[clk_idx]--;
+ return ret;
+ }
ret = clk_enable(clk);
} else if (count == 0) {
pr_debug("clk=%d disable\n", clk_idx);
clk_disable(clk);
+ clk_unprepare(clk);
ret = 0;
} else if (count < 0) {
pr_err("clk=%d count=%d\n", clk_idx, count);
@@ -578,62 +622,83 @@
return rc;
}
-int mdp3_clk_prepare(void)
+void mdp3_bus_bw_iommu_enable(int enable, int client)
+{
+ struct mdp3_bus_handle_map *bus_handle;
+ int client_idx;
+ u64 ab, ib;
+ int ref_cnt;
+
+ if (client == MDP3_CLIENT_DMA_P) {
+ client_idx = MDP3_BUS_HANDLE_DMA;
+ } else if (client == MDP3_CLIENT_PPP) {
+ client_idx = MDP3_BUS_HANDLE_PPP;
+ } else {
+ pr_err("invalid client %d\n", client);
+ return;
+ }
+
+ bus_handle = &mdp3_res->bus_handle[client_idx];
+ if (bus_handle->handle < 1) {
+ pr_err("invalid bus handle %d\n", bus_handle->handle);
+ return;
+ }
+ mutex_lock(&mdp3_res->res_mutex);
+ if (enable)
+ bus_handle->ref_cnt++;
+ else
+ bus_handle->ref_cnt--;
+ ref_cnt = bus_handle->ref_cnt;
+ mutex_unlock(&mdp3_res->res_mutex);
+
+ if (enable && ref_cnt == 1) {
+ if (mdp3_res->allow_iommu_update)
+ mdp3_iommu_enable(client);
+ ab = bus_handle->restore_ab;
+ ib = bus_handle->restore_ib;
+ mdp3_bus_scale_set_quota(client, ab, ib);
+ } else if (!enable && ref_cnt == 0) {
+ mdp3_bus_scale_set_quota(client, 0, 0);
+ mdp3_iommu_disable(client);
+ } else if (ref_cnt < 0) {
+ pr_err("Ref count < 0, bus client=%d, ref_cnt=%d",
+ client_idx, ref_cnt);
+ }
+}
+
+int mdp3_res_update(int enable, int dsi_clk, int client)
{
int rc = 0;
- mutex_lock(&mdp3_res->res_mutex);
- mdp3_res->clk_prepare_count++;
- if (mdp3_res->clk_prepare_count == 1) {
- rc = clk_prepare(mdp3_res->clocks[MDP3_CLK_AHB]);
- if (rc < 0)
- goto error0;
- rc = clk_prepare(mdp3_res->clocks[MDP3_CLK_CORE]);
- if (rc < 0)
- goto error1;
- rc = clk_prepare(mdp3_res->clocks[MDP3_CLK_VSYNC]);
- if (rc < 0)
- goto error2;
- rc = clk_prepare(mdp3_res->clocks[MDP3_CLK_DSI]);
- if (rc < 0)
- goto error3;
+ if (enable) {
+ rc = mdp3_clk_enable(enable, dsi_clk);
+ if (rc < 0) {
+ pr_err("mdp3_clk_enable failed, enable=%d, dsi_clk=%d\n",
+ enable, dsi_clk);
+ goto done;
+ }
+ mdp3_irq_register();
+ mdp3_bus_bw_iommu_enable(enable, client);
+ } else {
+ mdp3_bus_bw_iommu_enable(enable, client);
+ mdp3_irq_suspend();
+ rc = mdp3_clk_enable(enable, dsi_clk);
+ if (rc < 0) {
+ pr_err("mdp3_clk_enable failed, enable=%d, dsi_clk=%d\n",
+ enable, dsi_clk);
+ goto done;
+ }
}
- mutex_unlock(&mdp3_res->res_mutex);
- return rc;
-error3:
- clk_unprepare(mdp3_res->clocks[MDP3_CLK_VSYNC]);
-error2:
- clk_unprepare(mdp3_res->clocks[MDP3_CLK_CORE]);
-error1:
- clk_unprepare(mdp3_res->clocks[MDP3_CLK_AHB]);
-error0:
- mdp3_res->clk_prepare_count--;
- mutex_unlock(&mdp3_res->res_mutex);
+done:
return rc;
}
-void mdp3_clk_unprepare(void)
-{
- mutex_lock(&mdp3_res->res_mutex);
- mdp3_res->clk_prepare_count--;
- if (mdp3_res->clk_prepare_count == 0) {
- clk_unprepare(mdp3_res->clocks[MDP3_CLK_AHB]);
- clk_unprepare(mdp3_res->clocks[MDP3_CLK_CORE]);
- clk_unprepare(mdp3_res->clocks[MDP3_CLK_VSYNC]);
- clk_unprepare(mdp3_res->clocks[MDP3_CLK_DSI]);
- } else if (mdp3_res->clk_prepare_count < 0) {
- pr_err("mdp3 clk unprepare mismatch\n");
- }
- mutex_unlock(&mdp3_res->res_mutex);
-}
-
int mdp3_get_mdp_dsi_clk(void)
{
int rc;
mutex_lock(&mdp3_res->res_mutex);
- clk_prepare(mdp3_res->clocks[MDP3_CLK_DSI]);
rc = mdp3_clk_update(MDP3_CLK_DSI, 1);
mutex_unlock(&mdp3_res->res_mutex);
return rc;
@@ -644,7 +709,6 @@
int rc;
mutex_lock(&mdp3_res->res_mutex);
rc = mdp3_clk_update(MDP3_CLK_DSI, 0);
- clk_unprepare(mdp3_res->clocks[MDP3_CLK_DSI]);
mutex_unlock(&mdp3_res->res_mutex);
return rc;
}
@@ -674,9 +738,11 @@
if (context >= MDP3_IOMMU_CTX_MAX)
return -EINVAL;
+ mutex_lock(&mdp3_res->iommu_lock);
context_map = mdp3_res->iommu_contexts + context;
if (context_map->attached) {
pr_warn("mdp iommu already attached\n");
+ mutex_unlock(&mdp3_res->iommu_lock);
return 0;
}
@@ -685,6 +751,7 @@
iommu_attach_device(domain_map->domain, context_map->ctx);
context_map->attached = true;
+ mutex_unlock(&mdp3_res->iommu_lock);
return 0;
}
@@ -697,9 +764,11 @@
context >= MDP3_IOMMU_CTX_MAX)
return -EINVAL;
+ mutex_lock(&mdp3_res->iommu_lock);
context_map = mdp3_res->iommu_contexts + context;
if (!context_map->attached) {
pr_warn("mdp iommu not attached\n");
+ mutex_unlock(&mdp3_res->iommu_lock);
return 0;
}
@@ -707,6 +776,7 @@
iommu_detach_device(domain_map->domain, context_map->ctx);
context_map->attached = false;
+ mutex_unlock(&mdp3_res->iommu_lock);
return 0;
}
@@ -1141,6 +1211,54 @@
return 0;
}
+void msm_mdp3_cx_ctrl(int enable)
+{
+ int rc;
+
+ if (!mdp3_res->vdd_cx) {
+ mdp3_res->vdd_cx = devm_regulator_get(&mdp3_res->pdev->dev,
+ "vdd-cx");
+ if (IS_ERR_OR_NULL(mdp3_res->vdd_cx)) {
+ pr_debug("unable to get CX reg. rc=%d\n",
+ PTR_RET(mdp3_res->vdd_cx));
+ mdp3_res->vdd_cx = NULL;
+ return;
+ }
+ }
+
+ if (enable) {
+ rc = regulator_set_voltage(
+ mdp3_res->vdd_cx,
+ RPM_REGULATOR_CORNER_SVS_SOC,
+ RPM_REGULATOR_CORNER_SUPER_TURBO);
+ if (rc < 0)
+ goto vreg_set_voltage_fail;
+
+ rc = regulator_enable(mdp3_res->vdd_cx);
+ if (rc) {
+ pr_err("Failed to enable regulator vdd_cx.\n");
+ return;
+ }
+ } else {
+ rc = regulator_disable(mdp3_res->vdd_cx);
+ if (rc) {
+ pr_err("Failed to disable regulator vdd_cx.\n");
+ return;
+ }
+ rc = regulator_set_voltage(
+ mdp3_res->vdd_cx,
+ RPM_REGULATOR_CORNER_NONE,
+ RPM_REGULATOR_CORNER_SUPER_TURBO);
+ if (rc < 0)
+ goto vreg_set_voltage_fail;
+ }
+
+ return;
+vreg_set_voltage_fail:
+ pr_err("Set vltg failed\n");
+ return;
+}
+
void mdp3_batfet_ctrl(int enable)
{
int rc;
@@ -1173,6 +1291,12 @@
pr_err("%s: reg enable/disable failed", __func__);
}
+void mdp3_enable_regulator(int enable)
+{
+ msm_mdp3_cx_ctrl(enable);
+ mdp3_batfet_ctrl(enable);
+}
+
static void mdp3_iommu_heap_unmap_iommu(struct mdp3_iommu_meta *meta)
{
unsigned int domain_num;
@@ -1849,6 +1973,7 @@
static int mdp3_continuous_splash_on(struct mdss_panel_data *pdata)
{
struct mdss_panel_info *panel_info = &pdata->panel_info;
+ struct mdp3_bus_handle_map *bus_handle;
u64 ab, ib;
int rc;
@@ -1860,26 +1985,23 @@
mdp3_clk_set_rate(MDP3_CLK_CORE, MDP_CORE_CLK_RATE,
MDP3_CLIENT_DMA_P);
- rc = mdp3_clk_prepare();
- if (rc) {
- pr_err("fail to prepare clk\n");
- return rc;
- }
-
- rc = mdp3_clk_enable(1, 1);
- if (rc) {
- pr_err("fail to enable clk\n");
- mdp3_clk_unprepare();
- return rc;
+ bus_handle = &mdp3_res->bus_handle[MDP3_BUS_HANDLE_DMA];
+ if (bus_handle->handle < 1) {
+ pr_err("invalid bus handle %d\n", bus_handle->handle);
+ return -EINVAL;
}
ab = panel_info->xres * panel_info->yres * 4;
ab *= panel_info->mipi.frame_rate;
ib = (ab * 3) / 2;
rc = mdp3_bus_scale_set_quota(MDP3_CLIENT_DMA_P, ab, ib);
+ bus_handle->restore_ab = ab;
+ bus_handle->restore_ib = ib;
+
+ rc = mdp3_res_update(1, 1, MDP3_CLIENT_DMA_P);
if (rc) {
- pr_err("fail to request bus bandwidth\n");
- goto splash_on_err;
+ pr_err("fail to enable clk\n");
+ return rc;
}
rc = mdp3_ppp_init();
@@ -1888,8 +2010,6 @@
goto splash_on_err;
}
- mdp3_irq_register();
-
if (pdata->event_handler) {
rc = pdata->event_handler(pdata, MDSS_EVENT_CONT_SPLASH_BEGIN,
NULL);
@@ -1904,15 +2024,14 @@
else
mdp3_res->intf[MDP3_DMA_OUTPUT_SEL_DSI_CMD].active = 1;
- mdp3_batfet_ctrl(true);
+ mdp3_enable_regulator(true);
mdp3_res->cont_splash_en = 1;
return 0;
splash_on_err:
- if (mdp3_clk_enable(0, 1))
+ if (mdp3_res_update(0, 1, MDP3_CLIENT_DMA_P))
pr_err("%s: Unable to disable mdp3 clocks\n", __func__);
- mdp3_clk_unprepare();
return rc;
}
@@ -1932,6 +2051,12 @@
rc = mdp3_continuous_splash_on(pdata);
}
}
+ /*
+ * We want to prevent iommu from being enabled if there is
+ * continue splash screen. This would have happened in
+ * res_update in continuous_splash_on without this flag.
+ */
+ mdp3_res->allow_iommu_update = true;
return rc;
}
@@ -1946,11 +2071,9 @@
static void mdp3_debug_enable_clock(int on)
{
if (on) {
- mdp3_clk_prepare();
mdp3_clk_enable(1, 0);
} else {
mdp3_clk_enable(0, 0);
- mdp3_clk_unprepare();
}
}
@@ -2149,6 +2272,7 @@
.panel_register_done = mdp3_panel_register_done,
.fb_stride = mdp3_fb_stride,
.fb_mem_alloc_fnc = mdp3_alloc,
+ .check_dsi_status = mdp3_check_dsi_ctrl_status,
};
struct mdp3_intr_cb underrun_cb = {
@@ -2258,13 +2382,13 @@
static int mdp3_suspend_sub(struct mdp3_hw_resource *mdata)
{
- mdp3_batfet_ctrl(false);
+ mdp3_enable_regulator(false);
return 0;
}
static int mdp3_resume_sub(struct mdp3_hw_resource *mdata)
{
- mdp3_batfet_ctrl(true);
+ mdp3_enable_regulator(true);
return 0;
}
diff --git a/drivers/video/msm/mdss/mdp3.h b/drivers/video/msm/mdss/mdp3.h
index a253d8f..137a1b8 100644
--- a/drivers/video/msm/mdss/mdp3.h
+++ b/drivers/video/msm/mdss/mdp3.h
@@ -74,6 +74,9 @@
struct msm_bus_paths *usecases;
struct msm_bus_scale_pdata *scale_pdata;
int current_bus_idx;
+ int ref_cnt;
+ u64 restore_ab;
+ u64 restore_ib;
u32 handle;
};
@@ -134,6 +137,7 @@
struct ion_client *ion_client;
struct mdp3_iommu_domain_map *domains;
struct mdp3_iommu_ctx_map *iommu_contexts;
+ bool allow_iommu_update;
struct ion_handle *ion_handle;
struct mutex iommu_lock;
struct rb_root iommu_root;
@@ -144,6 +148,7 @@
spinlock_t irq_lock;
u32 irq_ref_count[MDP3_MAX_INTR];
u32 irq_mask;
+ int irq_ref_cnt;
struct mdp3_intr_cb callbacks[MDP3_MAX_INTR];
u32 underrun_cnt;
@@ -159,6 +164,7 @@
bool batfet_required;
struct regulator *batfet;
+ struct regulator *vdd_cx;
};
struct mdp3_img_data {
@@ -183,8 +189,7 @@
void mdp3_irq_deregister(void);
int mdp3_clk_set_rate(int clk_type, unsigned long clk_rate, int client);
int mdp3_clk_enable(int enable, int dsi_clk);
-int mdp3_clk_prepare(void);
-void mdp3_clk_unprepare(void);
+int mdp3_res_update(int enable, int dsi_clk, int client);
int mdp3_bus_scale_set_quota(int client, u64 ab_quota, u64 ib_quota);
int mdp3_put_img(struct mdp3_img_data *data, int client);
int mdp3_get_img(struct msmfb_data *img, struct mdp3_img_data *data,
@@ -203,6 +208,9 @@
int mdp3_misr_set(struct mdp_misr *misr_req);
int mdp3_misr_get(struct mdp_misr *misr_resp);
+void mdp3_enable_regulator(int enable);
+void mdp3_check_dsi_ctrl_status(struct work_struct *work,
+ uint32_t interval);
#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 da714ad..8acb9b0 100644
--- a/drivers/video/msm/mdss/mdp3_ctrl.c
+++ b/drivers/video/msm/mdss/mdp3_ctrl.c
@@ -27,10 +27,7 @@
#define VSYNC_EXPIRE_TICK 4
-static void mdp3_ctrl_pan_display(struct msm_fb_data_type *mfd,
- struct mdp_overlay *req,
- int image_size,
- int *pipe_ndx);
+static void mdp3_ctrl_pan_display(struct msm_fb_data_type *mfd);
static int mdp3_overlay_unset(struct msm_fb_data_type *mfd, int ndx);
static int mdp3_histogram_stop(struct mdp3_session_data *session,
u32 block);
@@ -113,6 +110,7 @@
static void mdp3_dispatch_dma_done(struct work_struct *work)
{
struct mdp3_session_data *session;
+ int cnt = 0;
pr_debug("%s\n", __func__);
session = container_of(work, struct mdp3_session_data,
@@ -120,7 +118,13 @@
if (!session)
return;
- mdp3_ctrl_notify(session, MDP_NOTIFY_FRAME_DONE);
+ cnt = atomic_read(&session->dma_done_cnt);
+
+ while (cnt > 0) {
+ mdp3_ctrl_notify(session, MDP_NOTIFY_FRAME_DONE);
+ atomic_dec(&session->dma_done_cnt);
+ cnt--;
+ }
}
static void mdp3_dispatch_clk_off(struct work_struct *work)
@@ -156,7 +160,9 @@
void dma_done_notify_handler(void *arg)
{
struct mdp3_session_data *session = (struct mdp3_session_data *)arg;
+ atomic_inc(&session->dma_done_cnt);
schedule_work(&session->dma_done_work);
+ complete(&session->dma_completion);
}
void vsync_count_down(void *arg)
@@ -296,7 +302,7 @@
vsync_ticks = ktime_to_ns(mdp3_session->vsync_time);
pr_debug("fb%d vsync=%llu", mfd->index, vsync_ticks);
- rc = scnprintf(buf, PAGE_SIZE, "VSYNC=%llu", vsync_ticks);
+ rc = scnprintf(buf, PAGE_SIZE, "VSYNC=%llu\n", vsync_ticks);
return rc;
}
@@ -329,7 +335,7 @@
(!enable && session->clk_on == 1)) {
rc = panel->event_handler(panel,
MDSS_EVENT_PANEL_CLK_CTRL, (void *)enable);
- rc |= mdp3_clk_enable(enable, 1);
+ rc |= mdp3_res_update(enable, 1, MDP3_CLIENT_DMA_P);
} else {
pr_debug("enable = %d, clk_on=%d\n", enable, session->clk_on);
}
@@ -365,24 +371,15 @@
mdp3_clk_set_rate(MDP3_CLK_VSYNC, MDP_VSYNC_CLK_RATE,
MDP3_CLIENT_DMA_P);
- rc = mdp3_clk_prepare();
- if (rc) {
- pr_err("mdp3 clk prepare fail\n");
- return rc;
- }
-
- rc = mdp3_clk_enable(1, 1);
+ rc = mdp3_res_update(1, 1, MDP3_CLIENT_DMA_P);
if (rc) {
pr_err("mdp3 clk enable fail\n");
- mdp3_clk_unprepare();
return rc;
}
} else {
- rc = mdp3_clk_enable(0, 1);
+ rc = mdp3_res_update(0, 1, MDP3_CLIENT_DMA_P);
if (rc)
pr_err("mdp3 clk disable fail\n");
- else
- mdp3_clk_unprepare();
}
return rc;
}
@@ -477,6 +474,7 @@
video->hsync_polarity = 1;
video->vsync_polarity = 1;
video->de_polarity = 1;
+ video->underflow_color = p->lcdc.underflow_clr;
} else if (cfg.type == MDP3_DMA_OUTPUT_SEL_DSI_CMD) {
cfg.dsi_cmd.primary_dsi_cmd_id = 0;
cfg.dsi_cmd.secondary_dsi_cmd_id = 1;
@@ -505,6 +503,7 @@
int vbp, vfp, vspw;
int vtotal, vporch;
struct mdp3_notification dma_done_callback;
+ struct mdp3_tear_check te;
vbp = panel_info->lcdc.v_back_porch;
vfp = panel_info->lcdc.v_front_porch;
@@ -536,12 +535,28 @@
(MDP3_DMA_OUTPUT_COMP_BITS_8 << 2)|
MDP3_DMA_OUTPUT_COMP_BITS_8;
+ te.frame_rate = panel_info->mipi.frame_rate;
+ te.hw_vsync_mode = panel_info->mipi.hw_vsync_mode;
+ te.tear_check_en = panel_info->te.tear_check_en;
+ te.sync_cfg_height = panel_info->te.sync_cfg_height;
+ te.vsync_init_val = panel_info->te.vsync_init_val;
+ te.sync_threshold_start = panel_info->te.sync_threshold_start;
+ te.sync_threshold_continue = panel_info->te.sync_threshold_continue;
+ te.start_pos = panel_info->te.start_pos;
+ te.rd_ptr_irq = panel_info->te.rd_ptr_irq;
+ te.refx100 = panel_info->te.refx100;
+
if (dma->dma_config)
rc = dma->dma_config(dma, &sourceConfig, &outputConfig);
else
rc = -EINVAL;
if (outputConfig.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_CMD) {
+ if (dma->dma_sync_config)
+ rc = dma->dma_sync_config(dma,
+ &sourceConfig, &te);
+ else
+ rc = -EINVAL;
dma_done_callback.handler = dma_done_notify_handler;
dma_done_callback.arg = mfd->mdp.private1;
dma->dma_done_notifier(dma, &dma_done_callback);
@@ -574,16 +589,10 @@
goto on_error;
}
- mdp3_batfet_ctrl(true);
+ mdp3_enable_regulator(true);
mdp3_ctrl_notifier_register(mdp3_session,
&mdp3_session->mfd->mdp_sync_pt_data.notifier);
- rc = mdp3_iommu_enable(MDP3_CLIENT_DMA_P);
- if (rc) {
- pr_err("fail to attach MDP DMA SMMU\n");
- goto on_error;
- }
-
/* request bus bandwidth before DSI DMA traffic */
rc = mdp3_ctrl_res_req_bus(mfd, 1);
if (rc) {
@@ -606,8 +615,6 @@
goto on_error;
}
- mdp3_irq_register();
-
rc = mdp3_ctrl_dma_init(mfd, mdp3_session->dma);
if (rc) {
pr_err("dma init failed\n");
@@ -625,7 +632,6 @@
pr_err("display interface init failed\n");
goto on_error;
}
-
mdp3_session->clk_on = 1;
mdp3_session->first_commit = true;
@@ -654,6 +660,9 @@
panel = mdp3_session->panel;
mutex_lock(&mdp3_session->lock);
+ if (panel && panel->set_backlight)
+ panel->set_backlight(panel, 0);
+
if (!mdp3_session->status) {
pr_debug("fb%d is off already", mfd->index);
goto off_error;
@@ -677,7 +686,7 @@
pr_debug("mdp3_ctrl_off stop clock\n");
if (mdp3_session->clk_on) {
- rc = mdp3_clk_enable(0, 1);
+ rc = mdp3_res_update(0, 1, MDP3_CLIENT_DMA_P);
if (rc)
pr_err("mdp clock resource release failed\n");
@@ -688,23 +697,15 @@
if (rc)
pr_err("fail to turn off the panel\n");
}
- mdp3_clk_unprepare();
-
- pr_debug("mdp3_ctrl_off release bus\n");
- rc = mdp3_ctrl_res_req_bus(mfd, 0);
- if (rc)
- pr_err("mdp bus resource release failed\n");
-
- rc = mdp3_iommu_disable(MDP3_CLIENT_DMA_P);
- if (rc)
- pr_err("fail to dettach MDP DMA SMMU\n");
mdp3_ctrl_notifier_unregister(mdp3_session,
&mdp3_session->mfd->mdp_sync_pt_data.notifier);
- mdp3_batfet_ctrl(false);
+ mdp3_enable_regulator(false);
mdp3_session->vsync_enabled = 0;
atomic_set(&mdp3_session->vsync_countdown, 0);
+ atomic_set(&mdp3_session->dma_done_cnt, 0);
mdp3_session->clk_on = 0;
+ mdp3_session->in_splash_screen = 0;
off_error:
mdp3_session->status = 0;
mdp3_bufq_deinit(&mdp3_session->bufq_out);
@@ -757,6 +758,7 @@
mdp3_dma->vsync_enable(mdp3_dma, &vsync_client);
mdp3_session->first_commit = true;
+ mdp3_session->in_splash_screen = 0;
reset_error:
mutex_unlock(&mdp3_session->lock);
@@ -845,6 +847,7 @@
mdp3_dma->vsync_enable(mdp3_dma, &vsync_client);
mdp3_session->first_commit = true;
+ mdp3_session->in_splash_screen = 0;
reset_error:
mutex_unlock(&mdp3_session->lock);
@@ -999,7 +1002,7 @@
}
panel = mdp3_session->panel;
- if (!mdp3_iommu_is_attached(MDP3_CLIENT_DMA_P)) {
+ if (mdp3_session->in_splash_screen) {
pr_debug("continuous splash screen, IOMMU not attached\n");
rc = mdp3_ctrl_reset(mfd);
if (rc) {
@@ -1036,7 +1039,8 @@
MDP_NOTIFY_FRAME_DONE);
}
}
-
+ mdp3_session->dma_active = 1;
+ init_completion(&mdp3_session->dma_completion);
mdp3_ctrl_notify(mdp3_session, MDP_NOTIFY_FRAME_FLUSHED);
mdp3_bufq_push(&mdp3_session->bufq_out, data);
}
@@ -1064,10 +1068,7 @@
return 0;
}
-static void mdp3_ctrl_pan_display(struct msm_fb_data_type *mfd,
- struct mdp_overlay *req,
- int image_size,
- int *pipe_ndx)
+static void mdp3_ctrl_pan_display(struct msm_fb_data_type *mfd)
{
struct fb_info *fbi;
struct mdp3_session_data *mdp3_session;
@@ -1084,7 +1085,7 @@
if (!mdp3_session || !mdp3_session->dma)
return;
- if (!mdp3_iommu_is_attached(MDP3_CLIENT_DMA_P)) {
+ if (mdp3_session->in_splash_screen) {
pr_debug("continuous splash screen, IOMMU not attached\n");
rc = mdp3_ctrl_reset(mfd);
if (rc) {
@@ -1130,6 +1131,8 @@
MDP_NOTIFY_FRAME_DONE);
}
}
+ mdp3_session->dma_active = 1;
+ init_completion(&mdp3_session->dma_completion);
mdp3_ctrl_notify(mdp3_session, MDP_NOTIFY_FRAME_FLUSHED);
} else {
pr_debug("mdp3_ctrl_pan_display no memory, stop interface");
@@ -1276,7 +1279,7 @@
return -EBUSY;
}
- mdp3_clk_enable(1, 0);
+ mdp3_res_update(1, 0, MDP3_CLIENT_DMA_P);
ret = session->dma->histo_op(session->dma, MDP3_DMA_HISTO_OP_RESET);
if (ret) {
pr_err("mdp3_histogram_start reset error\n");
@@ -1302,7 +1305,7 @@
session->histo_status = 1;
histogram_start_err:
- mdp3_clk_enable(0, 0);
+ mdp3_res_update(0, 0, MDP3_CLIENT_DMA_P);
mutex_unlock(&session->histo_lock);
return ret;
}
@@ -1611,6 +1614,7 @@
{
struct mdp_overlay_list ovlist;
struct mdp3_session_data *mdp3_session = mfd->mdp.private1;
+ struct mdp_overlay *req_list;
struct mdp_overlay *req;
int rc;
@@ -1627,12 +1631,15 @@
return -EINVAL;
}
- if (copy_from_user(req, ovlist.overlay_list[0], sizeof(*req)))
+ if (copy_from_user(&req_list, ovlist.overlay_list, sizeof(struct mdp_overlay*)))
+ return -EFAULT;
+
+ if (copy_from_user(req, req_list, sizeof(*req)))
return -EFAULT;
rc = mdp3_overlay_set(mfd, req);
if (!IS_ERR_VALUE(rc)) {
- if (copy_to_user(ovlist.overlay_list[0], req, sizeof(*req)))
+ if (copy_to_user(req_list, req, sizeof(*req)))
return -EFAULT;
}
@@ -1751,6 +1758,23 @@
return rc;
}
+int mdp3_wait_for_dma_done(struct mdp3_session_data *session)
+{
+ int rc = 0;
+
+ if (session->dma_active) {
+ rc = wait_for_completion_timeout(&session->dma_completion,
+ KOFF_TIMEOUT);
+ if (rc > 0) {
+ session->dma_active = 0;
+ rc = 0;
+ } else if (rc == 0) {
+ rc = -ETIME;
+ }
+ }
+ return rc;
+}
+
int mdp3_ctrl_init(struct msm_fb_data_type *mfd)
{
struct device *dev = mfd->fbi->dev;
@@ -1825,6 +1849,9 @@
mdp3_session->vsync_timer.data = (u32)mdp3_session;
mdp3_session->vsync_period = 1000 / mfd->panel_info->mipi.frame_rate;
mfd->mdp.private1 = mdp3_session;
+ init_completion(&mdp3_session->dma_completion);
+ if (intf_type != MDP3_DMA_OUTPUT_SEL_DSI_VIDEO)
+ mdp3_session->wait_for_dma_done = mdp3_wait_for_dma_done;
rc = sysfs_create_group(&dev->kobj, &vsync_fs_attr_group);
if (rc) {
@@ -1849,6 +1876,7 @@
if (mdp3_get_cont_spash_en()) {
mdp3_session->clk_on = 1;
+ mdp3_session->in_splash_screen = 1;
mdp3_ctrl_notifier_register(mdp3_session,
&mdp3_session->mfd->mdp_sync_pt_data.notifier);
}
diff --git a/drivers/video/msm/mdss/mdp3_ctrl.h b/drivers/video/msm/mdss/mdp3_ctrl.h
index cfad1d3..93356e2 100644
--- a/drivers/video/msm/mdss/mdp3_ctrl.h
+++ b/drivers/video/msm/mdss/mdp3_ctrl.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2014, 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
@@ -50,6 +50,7 @@
struct mdp3_buffer_queue bufq_out;
struct work_struct clk_off_work;
struct work_struct dma_done_work;
+ atomic_t dma_done_cnt;
int histo_status;
struct mutex histo_lock;
int lut_sel;
@@ -61,6 +62,11 @@
int vsync_enabled;
atomic_t vsync_countdown; /* Used to count down */
+ bool in_splash_screen;
+
+ bool dma_active;
+ struct completion dma_completion;
+ int (*wait_for_dma_done)(struct mdp3_session_data *session);
};
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 8a13de2..2dd66f8 100644
--- a/drivers/video/msm/mdss/mdp3_dma.c
+++ b/drivers/video/msm/mdss/mdp3_dma.c
@@ -24,6 +24,11 @@
#define DMA_CCS_CONFIG_MASK 0xfffffc17
#define HIST_WAIT_TIMEOUT(frame) ((75 * HZ * (frame)) / 1000)
+#define VSYNC_SELECT 0x024
+#define VSYNC_TOTAL_LINES_SHIFT 21
+#define VSYNC_COUNT_MASK 0x7ffff
+#define VSYNC_THRESH_CONT_SHIFT 16
+
static void mdp3_vsync_intr_handler(int type, void *arg)
{
struct mdp3_dma *dma = (struct mdp3_dma *)arg;
@@ -266,32 +271,48 @@
}
}
-static int mdp3_dma_sync_config(struct mdp3_dma *dma,
- struct mdp3_dma_source *source_config)
+
+int mdp3_dma_sync_config(struct mdp3_dma *dma,
+ struct mdp3_dma_source *source_config, struct mdp3_tear_check *te)
{
- u32 sync_config;
+ u32 vsync_clk_speed_hz, vclks_line, cfg;
+ int porch = source_config->vporch;
+ int height = source_config->height;
+ int total_lines = height + porch;
int dma_sel = dma->dma_sel;
- pr_debug("mdp3_dma_sync_config\n");
+ vsync_clk_speed_hz = MDP_VSYNC_CLK_RATE;
- if (dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_CMD) {
- int porch = source_config->vporch;
- int height = source_config->height;
- int vtotal = height + porch;
- sync_config = vtotal << 21;
- sync_config |= source_config->vsync_count;
- sync_config |= BIT(19);
- sync_config |= BIT(20);
+ cfg = total_lines << VSYNC_TOTAL_LINES_SHIFT;
+ total_lines *= te->frame_rate;
- MDP3_REG_WRITE(MDP3_REG_SYNC_CONFIG_0 + dma_sel, sync_config);
- MDP3_REG_WRITE(MDP3_REG_VSYNC_SEL, 0x024);
- MDP3_REG_WRITE(MDP3_REG_PRIMARY_VSYNC_INIT_VAL + dma_sel,
- height);
- MDP3_REG_WRITE(MDP3_REG_PRIMARY_RD_PTR_IRQ, 0x5);
- MDP3_REG_WRITE(MDP3_REG_SYNC_THRESH_0 + dma_sel, (4 << 16 | 2));
- MDP3_REG_WRITE(MDP3_REG_PRIMARY_START_P0S + dma_sel, porch);
- MDP3_REG_WRITE(MDP3_REG_TEAR_CHECK_EN, 0x1);
+ vclks_line = (total_lines) ? vsync_clk_speed_hz / total_lines : 0;
+
+ cfg |= BIT(19);
+ if (te->hw_vsync_mode)
+ cfg |= BIT(20);
+
+ if (te->refx100) {
+ vclks_line = vclks_line * te->frame_rate *
+ 100 / te->refx100;
+ } else {
+ pr_warn("refx100 cannot be zero! Use 6000 as default\n");
+ vclks_line = vclks_line * te->frame_rate *
+ 100 / 6000;
}
+
+ cfg |= (vclks_line & VSYNC_COUNT_MASK);
+
+ MDP3_REG_WRITE(MDP3_REG_SYNC_CONFIG_0 + dma_sel, cfg);
+ MDP3_REG_WRITE(MDP3_REG_VSYNC_SEL, VSYNC_SELECT);
+ MDP3_REG_WRITE(MDP3_REG_PRIMARY_VSYNC_INIT_VAL + dma_sel,
+ te->vsync_init_val);
+ MDP3_REG_WRITE(MDP3_REG_PRIMARY_RD_PTR_IRQ, te->rd_ptr_irq);
+ MDP3_REG_WRITE(MDP3_REG_SYNC_THRESH_0 + dma_sel,
+ ((te->sync_threshold_continue << VSYNC_THRESH_CONT_SHIFT) |
+ te->sync_threshold_start));
+ MDP3_REG_WRITE(MDP3_REG_PRIMARY_START_P0S + dma_sel, te->start_pos);
+ MDP3_REG_WRITE(MDP3_REG_TEAR_CHECK_EN, te->tear_check_en);
return 0;
}
@@ -324,8 +345,6 @@
dma->source_config = *source_config;
dma->output_config = *output_config;
- mdp3_dma_sync_config(dma, source_config);
-
mdp3_irq_enable(MDP3_INTR_LCDC_UNDERFLOW);
mdp3_dma_callback_setup(dma);
return 0;
@@ -378,7 +397,6 @@
dma->source_config = *source_config;
dma->output_config = *output_config;
- mdp3_dma_sync_config(dma, source_config);
mdp3_dma_callback_setup(dma);
return 0;
@@ -925,6 +943,7 @@
switch (dma->dma_sel) {
case MDP3_DMA_P:
dma->dma_config = mdp3_dmap_config;
+ dma->dma_sync_config = mdp3_dma_sync_config;
dma->dma_config_source = mdp3_dmap_config_source;
dma->config_cursor = mdp3_dmap_cursor_config;
dma->config_ccs = mdp3_dmap_ccs_config;
@@ -941,6 +960,7 @@
break;
case MDP3_DMA_S:
dma->dma_config = mdp3_dmas_config;
+ dma->dma_sync_config = mdp3_dma_sync_config;
dma->dma_config_source = mdp3_dmas_config_source;
dma->config_cursor = NULL;
dma->config_ccs = NULL;
@@ -1060,7 +1080,9 @@
temp |= BIT(2);
MDP3_REG_WRITE(MDP3_REG_DSI_VIDEO_CTL_POLARITY, temp);
- MDP3_REG_WRITE(MDP3_REG_DSI_VIDEO_UNDERFLOW_CTL, 0x800000ff);
+ v->underflow_color |= 0x80000000;
+ MDP3_REG_WRITE(MDP3_REG_DSI_VIDEO_UNDERFLOW_CTL, v->underflow_color);
+
return 0;
}
diff --git a/drivers/video/msm/mdss/mdp3_dma.h b/drivers/video/msm/mdss/mdp3_dma.h
index 80ebb9b..d07e06d 100644
--- a/drivers/video/msm/mdss/mdp3_dma.h
+++ b/drivers/video/msm/mdss/mdp3_dma.h
@@ -233,6 +233,19 @@
void *arg;
};
+struct mdp3_tear_check {
+ int frame_rate;
+ bool hw_vsync_mode;
+ u32 tear_check_en;
+ u32 sync_cfg_height;
+ u32 vsync_init_val;
+ u32 sync_threshold_start;
+ u32 sync_threshold_continue;
+ u32 start_pos;
+ u32 rd_ptr_irq;
+ u32 refx100;
+};
+
struct mdp3_intf;
struct mdp3_dma {
@@ -265,6 +278,9 @@
struct mdp3_dma_source *source_config,
struct mdp3_dma_output_config *output_config);
+ int (*dma_sync_config)(struct mdp3_dma *dma, struct mdp3_dma_source
+ *source_config, struct mdp3_tear_check *te);
+
void (*dma_config_source)(struct mdp3_dma *dma);
int (*start)(struct mdp3_dma *dma, struct mdp3_intf *intf);
@@ -319,6 +335,7 @@
int hsync_polarity;
int vsync_polarity;
int de_polarity;
+ int underflow_color;
};
struct mdp3_dsi_cmd_intf_cfg {
diff --git a/drivers/video/msm/mdss/mdp3_ppp.c b/drivers/video/msm/mdss/mdp3_ppp.c
index 7e590b3a..afa0b7c 100644
--- a/drivers/video/msm/mdss/mdp3_ppp.c
+++ b/drivers/video/msm/mdss/mdp3_ppp.c
@@ -386,14 +386,14 @@
ab = req_bw;
}
mdp3_clk_set_rate(MDP3_CLK_CORE, rate, MDP3_CLIENT_PPP);
- rc = mdp3_clk_enable(on_off, 0);
+ rc = mdp3_res_update(on_off, 0, MDP3_CLIENT_PPP);
if (rc < 0) {
pr_err("%s: mdp3_clk_enable failed\n", __func__);
return rc;
}
rc = mdp3_bus_scale_set_quota(MDP3_CLIENT_PPP, ab, ib);
if (rc < 0) {
- mdp3_clk_enable(!on_off, 0);
+ mdp3_res_update(!on_off, 0, MDP3_CLIENT_PPP);
pr_err("%s: scale_set_quota failed\n", __func__);
return rc;
}
@@ -865,17 +865,17 @@
if (req->flags & MDP_ROT_90) {
if (((req->dst_rect.h == 1) && ((req->src_rect.w != 1) ||
- (req->dst_rect.w != req->src_rect.h))) ||
+ (req->dst_rect.w == req->src_rect.h))) ||
((req->dst_rect.w == 1) && ((req->src_rect.h != 1) ||
- (req->dst_rect.h != req->src_rect.w)))) {
+ (req->dst_rect.h == req->src_rect.w)))) {
pr_err("mdp_ppp: error scaling when size is 1!\n");
return -EINVAL;
}
} else {
if (((req->dst_rect.w == 1) && ((req->src_rect.w != 1) ||
- (req->dst_rect.h != req->src_rect.h))) ||
+ (req->dst_rect.h == req->src_rect.h))) ||
((req->dst_rect.h == 1) && ((req->src_rect.h != 1) ||
- (req->dst_rect.w != req->src_rect.w)))) {
+ (req->dst_rect.w == req->src_rect.w)))) {
pr_err("mdp_ppp: error scaling when size is 1!\n");
return -EINVAL;
}
@@ -1039,14 +1039,10 @@
static void mdp3_free_bw_wq_handler(struct work_struct *work)
{
struct msm_fb_data_type *mfd = ppp_stat->mfd;
- int rc;
mutex_lock(&ppp_stat->config_ppp_mutex);
if (ppp_stat->bw_on) {
mdp3_ppp_turnon(mfd, 0);
- rc = mdp3_iommu_disable(MDP3_CLIENT_PPP);
- if (rc < 0)
- WARN(1, "Unable to disable ppp iommu\n");
}
mutex_unlock(&ppp_stat->config_ppp_mutex);
}
@@ -1065,16 +1061,9 @@
}
if (!ppp_stat->bw_on) {
- rc = mdp3_iommu_enable(MDP3_CLIENT_PPP);
- if (rc < 0) {
- mutex_unlock(&ppp_stat->config_ppp_mutex);
- pr_err("%s: mdp3_iommu_enable failed\n", __func__);
- return;
- }
ppp_stat->bw_optimal = mdp3_optimal_bw(req->count);
mdp3_ppp_turnon(mfd, 1);
if (rc < 0) {
- mdp3_iommu_disable(MDP3_CLIENT_PPP);
mutex_unlock(&ppp_stat->config_ppp_mutex);
pr_err("%s: Enable ppp resources failed\n", __func__);
return;
diff --git a/drivers/video/msm/mdss/mdp3_ppp.h b/drivers/video/msm/mdss/mdp3_ppp.h
index 9753e94..a0ad3c3 100644
--- a/drivers/video/msm/mdss/mdp3_ppp.h
+++ b/drivers/video/msm/mdss/mdp3_ppp.h
@@ -391,7 +391,7 @@
uint32_t ppp_bpp(uint32_t type);
uint32_t ppp_src_config(uint32_t type);
uint32_t ppp_out_config(uint32_t type);
-uint32_t ppp_pack_pattern(uint32_t type);
+uint32_t ppp_pack_pattern(uint32_t type, uint32_t yuv2rgb);
uint32_t ppp_dst_op_reg(uint32_t type);
uint32_t ppp_src_op_reg(uint32_t type);
bool ppp_per_p_alpha(uint32_t type);
diff --git a/drivers/video/msm/mdss/mdp3_ppp_data.c b/drivers/video/msm/mdss/mdp3_ppp_data.c
index e562ad3..5748842 100644
--- a/drivers/video/msm/mdss/mdp3_ppp_data.c
+++ b/drivers/video/msm/mdss/mdp3_ppp_data.c
@@ -88,6 +88,35 @@
CLR_G, CLR_R, 8),
};
+const uint32_t swapped_pack_patt_lut[MDP_IMGTYPE_LIMIT] = {
+ [MDP_RGB_565] = PPP_GET_PACK_PATTERN(0, CLR_B, CLR_G, CLR_R, 8),
+ [MDP_BGR_565] = PPP_GET_PACK_PATTERN(0, CLR_R, CLR_G, CLR_B, 8),
+ [MDP_RGB_888] = PPP_GET_PACK_PATTERN(0, CLR_B, CLR_G, CLR_R, 8),
+ [MDP_BGR_888] = PPP_GET_PACK_PATTERN(0, CLR_R, CLR_G, CLR_B, 8),
+ [MDP_BGRA_8888] = PPP_GET_PACK_PATTERN(CLR_ALPHA, CLR_R,
+ CLR_G, CLR_B, 8),
+ [MDP_RGBA_8888] = PPP_GET_PACK_PATTERN(CLR_ALPHA, CLR_B,
+ CLR_G, CLR_R, 8),
+ [MDP_ARGB_8888] = PPP_GET_PACK_PATTERN(CLR_ALPHA, CLR_B,
+ CLR_G, CLR_R, 8),
+ [MDP_XRGB_8888] = PPP_GET_PACK_PATTERN(CLR_ALPHA, CLR_B,
+ CLR_G, CLR_R, 8),
+ [MDP_RGBX_8888] = PPP_GET_PACK_PATTERN(CLR_ALPHA, CLR_B,
+ CLR_G, CLR_R, 8),
+ [MDP_Y_CRCB_H2V2] = PPP_GET_PACK_PATTERN(0, 0, CLR_CB, CLR_CR, 8),
+ [MDP_Y_CBCR_H2V2] = PPP_GET_PACK_PATTERN(0, 0, CLR_CR, CLR_CB, 8),
+ [MDP_Y_CBCR_H2V2_ADRENO] = PPP_GET_PACK_PATTERN(0, 0, CLR_CR,
+ CLR_CB, 8),
+ [MDP_Y_CBCR_H2V2_VENUS] = PPP_GET_PACK_PATTERN(0, 0, CLR_CR,
+ CLR_CB, 8),
+ [MDP_YCRYCB_H2V1] = PPP_GET_PACK_PATTERN(CLR_Y,
+ CLR_CB, CLR_Y, CLR_CR, 8),
+ [MDP_Y_CBCR_H2V1] = PPP_GET_PACK_PATTERN(0, 0, CLR_CR, CLR_CB, 8),
+ [MDP_Y_CRCB_H2V1] = PPP_GET_PACK_PATTERN(0, 0, CLR_CB, CLR_CR, 8),
+ [MDP_BGRX_8888] = PPP_GET_PACK_PATTERN(CLR_ALPHA, CLR_R,
+ CLR_G, CLR_B, 8),
+};
+
const uint32_t dst_op_reg[MDP_IMGTYPE_LIMIT] = {
[MDP_Y_CRCB_H2V2] = PPP_OP_DST_CHROMA_420,
[MDP_Y_CBCR_H2V2] = PPP_OP_DST_CHROMA_420,
@@ -1530,10 +1559,13 @@
return out_cfg_lut[type];
}
-uint32_t ppp_pack_pattern(uint32_t type)
+uint32_t ppp_pack_pattern(uint32_t type, uint32_t yuv2rgb)
{
if (MDP_IS_IMGTYPE_BAD(type))
return 0;
+ if (yuv2rgb)
+ return swapped_pack_patt_lut[type];
+
return pack_patt_lut[type];
}
diff --git a/drivers/video/msm/mdss/mdp3_ppp_hwio.c b/drivers/video/msm/mdss/mdp3_ppp_hwio.c
index eb01d00..a25c2c7 100644
--- a/drivers/video/msm/mdss/mdp3_ppp_hwio.c
+++ b/drivers/video/msm/mdss/mdp3_ppp_hwio.c
@@ -486,7 +486,7 @@
return load_secondary_matrix(csc);
}
-int config_ppp_src(struct ppp_img_desc *src)
+int config_ppp_src(struct ppp_img_desc *src, uint32_t yuv2rgb)
{
uint32_t val;
@@ -510,12 +510,12 @@
val |= (src->roi.x % 2) ? PPP_SRC_BPP_ROI_ODD_X : 0;
val |= (src->roi.y % 2) ? PPP_SRC_BPP_ROI_ODD_Y : 0;
PPP_WRITEL(val, MDP3_PPP_SRC_FORMAT);
- PPP_WRITEL(ppp_pack_pattern(src->color_fmt),
+ PPP_WRITEL(ppp_pack_pattern(src->color_fmt, yuv2rgb),
MDP3_PPP_SRC_UNPACK_PATTERN1);
return 0;
}
-int config_ppp_out(struct ppp_img_desc *dst)
+int config_ppp_out(struct ppp_img_desc *dst, uint32_t yuv2rgb)
{
uint32_t val;
bool pseudoplanr_output = false;
@@ -534,7 +534,7 @@
if (pseudoplanr_output)
val |= PPP_DST_PLANE_PSEUDOPLN;
PPP_WRITEL(val, MDP3_PPP_OUT_FORMAT);
- PPP_WRITEL(ppp_pack_pattern(dst->color_fmt),
+ PPP_WRITEL(ppp_pack_pattern(dst->color_fmt, yuv2rgb),
MDP3_PPP_OUT_PACK_PATTERN1);
val = ((dst->roi.height & MDP3_PPP_XY_MASK) << MDP3_PPP_XY_OFFSET) |
@@ -573,7 +573,7 @@
PPP_WRITEL(ppp_src_config(bg->color_fmt),
MDP3_PPP_BG_FORMAT);
- PPP_WRITEL(ppp_pack_pattern(bg->color_fmt),
+ PPP_WRITEL(ppp_pack_pattern(bg->color_fmt, 0),
MDP3_PPP_BG_UNPACK_PATTERN1);
return 0;
}
@@ -1108,6 +1108,7 @@
int config_ppp_op_mode(struct ppp_blit_op *blit_op)
{
+ uint32_t yuv2rgb;
uint32_t ppp_operation_reg = 0;
int sv_slice, sh_slice;
int dv_slice, dh_slice;
@@ -1153,6 +1154,7 @@
config_ppp_csc(blit_op->src.color_fmt,
blit_op->dst.color_fmt, &ppp_operation_reg);
+ yuv2rgb = ppp_operation_reg & PPP_OP_CONVERT_YCBCR2RGB;
if (blit_op->mdp_op & MDPOP_DITHER)
ppp_operation_reg |= PPP_OP_DITHER_EN;
@@ -1197,8 +1199,8 @@
config_ppp_blend(blit_op, &ppp_operation_reg);
- config_ppp_src(&blit_op->src);
- config_ppp_out(&blit_op->dst);
+ config_ppp_src(&blit_op->src, yuv2rgb);
+ config_ppp_out(&blit_op->dst, yuv2rgb);
PPP_WRITEL(ppp_operation_reg, MDP3_PPP_OP_MODE);
mb();
return 0;
diff --git a/drivers/video/msm/mdss/mdss.h b/drivers/video/msm/mdss/mdss.h
index 7e6faa8..72cceaa 100644
--- a/drivers/video/msm/mdss/mdss.h
+++ b/drivers/video/msm/mdss/mdss.h
@@ -117,6 +117,7 @@
u32 has_bwc;
u32 has_decimation;
u8 has_wfd_blk;
+ u32 has_no_lut_read;
u8 has_wb_ad;
u32 rotator_ot_limit;
@@ -148,8 +149,12 @@
struct mdss_fudge_factor ab_factor;
struct mdss_fudge_factor ib_factor;
+ struct mdss_fudge_factor ib_factor_overlap;
struct mdss_fudge_factor clk_factor;
+ u32 *clock_levels;
+ u32 nclk_lvl;
+
struct mdss_hw_settings *hw_settings;
struct mdss_mdp_pipe *vig_pipes;
diff --git a/drivers/video/msm/mdss/mdss_debug.c b/drivers/video/msm/mdss/mdss_debug.c
index 0d0240f..aeb3f86 100644
--- a/drivers/video/msm/mdss/mdss_debug.c
+++ b/drivers/video/msm/mdss/mdss_debug.c
@@ -30,21 +30,6 @@
#define GROUP_BYTES 4
#define ROW_BYTES 16
#define MAX_VSYNC_COUNT 0xFFFFFFF
-struct mdss_debug_data {
- struct dentry *root;
- struct list_head base_list;
-};
-
-struct mdss_debug_base {
- struct mdss_debug_data *mdd;
- void __iomem *base;
- size_t off;
- size_t cnt;
- size_t max_offset;
- char *buf;
- size_t buf_len;
- struct list_head head;
-};
static int mdss_debug_base_open(struct inode *inode, struct file *file)
{
@@ -265,12 +250,14 @@
if (!dbg)
return -ENOMEM;
+ if (name)
+ strlcpy(dbg->name, name, sizeof(dbg->name));
dbg->base = base;
dbg->max_offset = max_offset;
dbg->off = 0;
dbg->cnt = DEFAULT_BASE_REG_CNT;
- if (name)
+ if (name && strcmp(name, "mdp"))
prefix_len = snprintf(dn, sizeof(dn), "%s_", name);
strlcpy(dn + prefix_len, "off", sizeof(dn) - prefix_len);
@@ -395,6 +382,11 @@
debugfs_create_u32("min_mdp_clk", 0644, mdd->root,
(u32 *)&mdata->min_mdp_clk);
+ if (mdss_create_xlog_debug(mdd)) {
+ mdss_debugfs_cleanup(mdd);
+ return -ENODEV;
+ }
+
mdata->debug_inf.debug_data = mdd;
return 0;
@@ -410,6 +402,29 @@
return 0;
}
+void mdss_dump_reg(char __iomem *base, int len)
+{
+ char *addr;
+ u32 x0, x4, x8, xc;
+ int i;
+
+ addr = base;
+ if (len % 16)
+ len += 16;
+ len /= 16;
+
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
+ for (i = 0; i < len; i++) {
+ x0 = readl_relaxed(addr+0x0);
+ x4 = readl_relaxed(addr+0x4);
+ x8 = readl_relaxed(addr+0x8);
+ xc = readl_relaxed(addr+0xc);
+ pr_info("%p : %08x %08x %08x %08x\n", addr, x0, x4, x8, xc);
+ addr += 16;
+ }
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
+}
+
int vsync_count;
static struct mdss_mdp_misr_map {
u32 ctrl_reg;
diff --git a/drivers/video/msm/mdss/mdss_debug.h b/drivers/video/msm/mdss/mdss_debug.h
index 984caab..e2c9edd 100644
--- a/drivers/video/msm/mdss/mdss_debug.h
+++ b/drivers/video/msm/mdss/mdss_debug.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2014, 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
@@ -14,13 +14,48 @@
#ifndef MDSS_DEBUG_H
#define MDSS_DEBUG_H
+#include <stdarg.h>
#include "mdss.h"
#define MISR_POLL_SLEEP 2000
#define MISR_POLL_TIMEOUT 32000
#define MISR_CRC_BATCH_CFG 0x101
+#define DATA_LIMITER (-1)
+#define XLOG_TOUT_DATA_LIMITER (NULL)
+#define XLOG_FUNC_ENTRY 0x1111
+#define XLOG_FUNC_EXIT 0x2222
+#define MDSS_REG_BLOCK_NAME_LEN (5)
+
+#define MDSS_XLOG(...) mdss_xlog(__func__, ##__VA_ARGS__, DATA_LIMITER)
+#define MDSS_XLOG_TOUT_HANDLER(...) \
+ mdss_xlog_tout_handler(__func__, ##__VA_ARGS__, XLOG_TOUT_DATA_LIMITER)
#ifdef CONFIG_DEBUG_FS
+struct mdss_debug_base {
+ struct mdss_debug_data *mdd;
+ char name[80];
+ void __iomem *base;
+ size_t off;
+ size_t cnt;
+ size_t max_offset;
+ char *buf;
+ size_t buf_len;
+ struct list_head head;
+};
+
+struct debug_log {
+ struct dentry *xlog;
+ u32 xlog_enable;
+ u32 panic_on_err;
+ u32 enable_reg_dump;
+};
+
+struct mdss_debug_data {
+ struct dentry *root;
+ struct list_head base_list;
+ struct debug_log logd;
+};
+
int mdss_debugfs_init(struct mdss_data_type *mdata);
int mdss_debugfs_remove(struct mdss_data_type *mdata);
int mdss_debug_register_base(const char *name, void __iomem *base,
@@ -30,6 +65,13 @@
int mdss_misr_get(struct mdss_data_type *mdata, struct mdp_misr *resp,
struct mdss_mdp_ctl *ctl);
void mdss_misr_crc_collect(struct mdss_data_type *mdata, int block_id);
+
+int mdss_create_xlog_debug(struct mdss_debug_data *mdd);
+void mdss_xlog(const char *name, ...);
+void mdss_xlog_dump(void);
+void mdss_dump_reg(char __iomem *base, int len);
+void mdss_dsi_debug_check_te(struct mdss_panel_data *pdata);
+void mdss_xlog_tout_handler(const char *name, ...);
#else
static inline int mdss_debugfs_init(struct mdss_data_type *mdata) { return 0; }
static inline int mdss_debugfs_remove(struct mdss_data_type *mdata)
@@ -46,5 +88,12 @@
{ return 0; }
static inline void mdss_misr_crc_collect(struct mdss_data_type *mdata,
int block_id) { }
+
+static inline int create_xlog_debug(struct mdss_data_type *mdata) { }
+static inline void mdss_xlog(const char *name, ...) { }
+static inline void mdss_xlog_dump(void) { }
+static inline void mdss_dump_reg(char __iomem *base, int len) { }
+static inline void mdss_dsi_debug_check_te(struct mdss_panel_data *pdata) { }
+static inline void mdss_xlog_tout_handler(const char *name, ...) { }
#endif
#endif /* MDSS_DEBUG_H */
diff --git a/drivers/video/msm/mdss/mdss_debug_xlog.c b/drivers/video/msm/mdss/mdss_debug_xlog.c
new file mode 100644
index 0000000..0c24e1e
--- /dev/null
+++ b/drivers/video/msm/mdss/mdss_debug_xlog.c
@@ -0,0 +1,196 @@
+/* Copyright (c) 2014, 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/delay.h>
+#include <linux/spinlock.h>
+#include <linux/ktime.h>
+#include <linux/debugfs.h>
+
+#include "mdss.h"
+#include "mdss_mdp.h"
+#include "mdss_debug.h"
+
+#define MDSS_XLOG_ENTRY 256
+#define MDSS_XLOG_MAX_DATA 6
+#define MDSS_XLOG_BUF_MAX 512
+
+struct tlog {
+ u32 tick;
+ const char *name;
+ u32 data[MDSS_XLOG_MAX_DATA];
+ u32 data_cnt;
+};
+
+struct mdss_dbg_xlog {
+ struct tlog logs[MDSS_XLOG_ENTRY];
+ int first;
+ int last;
+ spinlock_t xlock;
+} mdss_dbg_xlog;
+
+static int mdss_xlog_dump_open(struct inode *inode, struct file *file)
+{
+ /* non-seekable */
+ file->f_mode &= ~(FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE);
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+static ssize_t mdss_xlog_dump_read(struct file *file, char __user *buff,
+ size_t count, loff_t *ppos)
+{
+ MDSS_XLOG_TOUT_HANDLER("mdp", "dsi0", "dsi1", "edp", "hdmi", "panic");
+ return 0;
+}
+
+static const struct file_operations mdss_xlog_fops = {
+ .open = mdss_xlog_dump_open,
+ .read = mdss_xlog_dump_read,
+};
+
+int mdss_create_xlog_debug(struct mdss_debug_data *mdd)
+{
+ spin_lock_init(&mdss_dbg_xlog.xlock);
+
+ mdd->logd.xlog = debugfs_create_dir("xlog", mdd->root);
+ if (IS_ERR_OR_NULL(mdd->logd.xlog)) {
+ pr_err("debugfs_create_dir fail, error %ld\n",
+ PTR_ERR(mdd->logd.xlog));
+ mdd->logd.xlog = NULL;
+ return -ENODEV;
+ }
+ debugfs_create_file("dump", 0644, mdd->logd.xlog, NULL,
+ &mdss_xlog_fops);
+ debugfs_create_bool("enable", 0644, mdd->logd.xlog,
+ &mdd->logd.xlog_enable);
+ debugfs_create_bool("panic", 0644, mdd->logd.xlog,
+ &mdd->logd.panic_on_err);
+ debugfs_create_bool("reg_dump", 0644, mdd->logd.xlog,
+ &mdd->logd.enable_reg_dump);
+ return 0;
+}
+
+void mdss_xlog(const char *name, ...)
+{
+ struct mdss_data_type *mdata = mdss_mdp_get_mdata();
+ struct mdss_debug_data *mdd = mdata->debug_inf.debug_data;
+ unsigned long flags;
+ int i, val = 0;
+ va_list args;
+ struct tlog *log;
+ ktime_t time;
+
+ if (!mdd->logd.xlog_enable)
+ return;
+
+ spin_lock_irqsave(&mdss_dbg_xlog.xlock, flags);
+
+ time = ktime_get();
+
+ log = &mdss_dbg_xlog.logs[mdss_dbg_xlog.first];
+ log->tick = ktime_to_us(time);
+ log->name = name;
+ log->data_cnt = 0;
+
+ va_start(args, name);
+ for (i = 0; i < MDSS_XLOG_MAX_DATA; i++) {
+
+ val = va_arg(args, int);
+ if (val == DATA_LIMITER)
+ break;
+
+ log->data[i] = val;
+ }
+ va_end(args);
+
+ log->data_cnt = i;
+
+ mdss_dbg_xlog.last = mdss_dbg_xlog.first;
+ mdss_dbg_xlog.first++;
+ mdss_dbg_xlog.first %= MDSS_XLOG_ENTRY;
+
+ spin_unlock_irqrestore(&mdss_dbg_xlog.xlock, flags);
+}
+
+void mdss_xlog_dump(void)
+{
+ struct mdss_data_type *mdata = mdss_mdp_get_mdata();
+ struct mdss_debug_data *mdd = mdata->debug_inf.debug_data;
+ int i, n, d_cnt, off;
+ unsigned long flags;
+ struct tlog *log;
+ char xlog_buf[MDSS_XLOG_BUF_MAX];
+
+ if (!mdd->logd.xlog_enable)
+ return;
+
+ spin_lock_irqsave(&mdss_dbg_xlog.xlock, flags);
+ i = mdss_dbg_xlog.first;
+ for (n = 0; n < MDSS_XLOG_ENTRY; n++) {
+ log = &mdss_dbg_xlog.logs[i];
+ off = snprintf(xlog_buf, MDSS_XLOG_BUF_MAX, "%-32s => %08d: ",
+ log->name, log->tick);
+ for (d_cnt = 0; d_cnt < log->data_cnt;) {
+ off += snprintf((xlog_buf + off),
+ (MDSS_XLOG_BUF_MAX - off),
+ "%x ", log->data[d_cnt]);
+ d_cnt++;
+ }
+ pr_err("%s\n", xlog_buf);
+
+ i = (i + 1) % MDSS_XLOG_ENTRY;
+ }
+ spin_unlock_irqrestore(&mdss_dbg_xlog.xlock, flags);
+}
+
+void mdss_xlog_tout_handler(const char *name, ...)
+{
+ struct mdss_data_type *mdata = mdss_mdp_get_mdata();
+ struct mdss_debug_data *mdd = mdata->debug_inf.debug_data;
+ struct mdss_debug_base *blk_base, *tmp;
+ int i, dead = 0;
+ va_list args;
+ char *blk_name = NULL;
+
+ if (!mdd->logd.xlog_enable)
+ return;
+
+ va_start(args, name);
+ for (i = 0; i < MDSS_XLOG_MAX_DATA; i++) {
+
+ blk_name = va_arg(args, char*);
+ if (IS_ERR_OR_NULL(blk_name))
+ break;
+
+ list_for_each_entry_safe(blk_base, tmp, &mdd->base_list, head) {
+
+ if (blk_base->name &&
+ !strcmp(blk_base->name, blk_name) &&
+ mdd->logd.enable_reg_dump) {
+ pr_info("\n%s : =========%s DUMP=========\n",
+ __func__, blk_base->name);
+ mdss_dump_reg(blk_base->base,
+ blk_base->max_offset);
+ }
+ }
+ if (!strcmp(blk_name, "panic"))
+ dead = 1;
+ }
+ va_end(args);
+
+ MDSS_XLOG(0xffff, 0xffff, 0xffff, 0xffff, 0xffff);
+ mdss_xlog_dump();
+
+ if (dead && mdd->logd.panic_on_err)
+ panic(name);
+}
diff --git a/drivers/video/msm/mdss/mdss_dsi.c b/drivers/video/msm/mdss/mdss_dsi.c
index 7b4b065..dd8eec5 100644
--- a/drivers/video/msm/mdss/mdss_dsi.c
+++ b/drivers/video/msm/mdss/mdss_dsi.c
@@ -321,7 +321,7 @@
ctrl_pdata, ctrl_pdata->ndx);
if (pdata->panel_info.type == MIPI_CMD_PANEL)
- mdss_dsi_clk_ctrl(ctrl_pdata, 1);
+ mdss_dsi_clk_ctrl(ctrl_pdata, DSI_ALL_CLKS, 1);
/* disable DSI controller */
mdss_dsi_controller_cfg(0, pdata);
@@ -329,7 +329,7 @@
/* disable DSI phy */
mdss_dsi_phy_disable(ctrl_pdata);
- mdss_dsi_clk_ctrl(ctrl_pdata, 0);
+ mdss_dsi_clk_ctrl(ctrl_pdata, DSI_ALL_CLKS, 0);
ret = mdss_dsi_panel_power_on(pdata, 0);
if (ret) {
@@ -443,7 +443,9 @@
{
int ret = 0;
struct mdss_panel_data *pdata = NULL;
+ struct mipi_panel_info *pinfo = NULL;
u32 lane_status = 0;
+ u32 active_lanes = 0;
if (!ctrl_pdata) {
pr_err("%s: invalid input\n", __func__);
@@ -451,6 +453,11 @@
}
pdata = &ctrl_pdata->panel_data;
+ if (!pdata) {
+ pr_err("%s: Invalid panel data\n", __func__);
+ return -EINVAL;
+ }
+ pinfo = &pdata->panel_info.mipi;
if (!__mdss_dsi_ulps_feature_enabled(pdata)) {
pr_debug("%s: ULPS feature not supported. enable=%d\n",
@@ -465,14 +472,20 @@
goto error;
}
- if (__mdss_dsi_clk_enabled(ctrl_pdata)) {
+ if (__mdss_dsi_clk_enabled(ctrl_pdata, DSI_LINK_CLKS)) {
pr_err("%s: cannot enter ulps mode if dsi clocks are on\n",
__func__);
ret = -EPERM;
goto error;
}
- mdss_dsi_clk_ctrl(ctrl_pdata, 1);
+ ret = mdss_dsi_clk_ctrl(ctrl_pdata, DSI_ALL_CLKS, 1);
+ if (ret) {
+ pr_err("%s: Failed to enable clocks. rc=%d\n",
+ __func__, ret);
+ goto error;
+ }
+
/*
* ULPS Entry Request.
* Wait for a short duration to ensure that the lanes
@@ -481,6 +494,25 @@
MIPI_OUTP(ctrl_pdata->ctrl_base + 0x0AC, 0x01F);
usleep(100);
+ /* Check to make sure that all active data lanes are in ULPS */
+ if (pinfo->data_lane3)
+ active_lanes |= BIT(11);
+ if (pinfo->data_lane2)
+ active_lanes |= BIT(10);
+ if (pinfo->data_lane1)
+ active_lanes |= BIT(9);
+ if (pinfo->data_lane0)
+ active_lanes |= BIT(8);
+ active_lanes |= BIT(12); /* clock lane */
+ lane_status = MIPI_INP(ctrl_pdata->ctrl_base + 0xA8);
+ if (lane_status & active_lanes) {
+ pr_err("%s: ULPS entry req failed. Lane status=0x%08x\n",
+ __func__, lane_status);
+ ret = -EINVAL;
+ mdss_dsi_clk_ctrl(ctrl_pdata, DSI_ALL_CLKS, 0);
+ goto error;
+ }
+
/* Enable MMSS DSI Clamps */
MIPI_OUTP(ctrl_pdata->mmss_misc_io.base + 0x14, 0x3FF);
MIPI_OUTP(ctrl_pdata->mmss_misc_io.base + 0x14, 0x83FF);
@@ -491,10 +523,17 @@
/* disable DSI controller */
mdss_dsi_controller_cfg(0, pdata);
- lane_status = MIPI_INP(ctrl_pdata->ctrl_base + 0xA8),
- mdss_dsi_clk_ctrl(ctrl_pdata, 0);
+ mdss_dsi_clk_ctrl(ctrl_pdata, DSI_ALL_CLKS, 0);
ctrl_pdata->ulps = true;
} else if (ctrl_pdata->ulps) {
+ ret = mdss_dsi_clk_ctrl(ctrl_pdata, DSI_BUS_CLKS, 1);
+ if (ret) {
+ pr_err("%s: Failed to enable bus clocks. rc=%d\n",
+ __func__, ret);
+ goto error;
+ }
+
+ MIPI_OUTP(ctrl_pdata->mmss_misc_io.base + 0x108, 0x0);
mdss_dsi_phy_init(pdata);
__mdss_dsi_ctrl_setup(pdata);
@@ -503,9 +542,28 @@
mdss_dsi_op_mode_config(pdata->panel_info.mipi.mode,
pdata);
+ /*
+ * ULPS Entry Request. This is needed because, after power
+ * collapse and reset, the DSI controller resets back to
+ * idle state and not ULPS.
+ * Wait for a short duration to ensure that the lanes
+ * enter ULP state.
+ */
+ MIPI_OUTP(ctrl_pdata->ctrl_base + 0x0AC, 0x01F);
+ usleep(100);
+
/* Disable MMSS DSI Clamps */
+ MIPI_OUTP(ctrl_pdata->mmss_misc_io.base + 0x14, 0x3FF);
MIPI_OUTP(ctrl_pdata->mmss_misc_io.base + 0x14, 0x0);
+ ret = mdss_dsi_clk_ctrl(ctrl_pdata, DSI_LINK_CLKS, 1);
+ if (ret) {
+ pr_err("%s: Failed to enable link clocks. rc=%d\n",
+ __func__, ret);
+ mdss_dsi_clk_ctrl(ctrl_pdata, DSI_BUS_CLKS, 0);
+ goto error;
+ }
+
/*
* ULPS Exit Request
* Hardware requirement is to wait for at least 1ms
@@ -520,7 +578,9 @@
*/
usleep(100);
- lane_status = MIPI_INP(ctrl_pdata->ctrl_base + 0xA8),
+ lane_status = MIPI_INP(ctrl_pdata->ctrl_base + 0xA8);
+ mdss_dsi_clk_ctrl(ctrl_pdata, DSI_LINK_CLKS, 0);
+ mdss_dsi_clk_ctrl(ctrl_pdata, DSI_BUS_CLKS, 0);
ctrl_pdata->ulps = false;
}
@@ -535,21 +595,31 @@
int enable)
{
int rc;
- struct mdss_dsi_ctrl_pdata *sctrl = NULL;
+ struct mdss_dsi_ctrl_pdata *mctrl = NULL;
- if (ctrl->flags & DSI_FLAG_CLOCK_MASTER)
- sctrl = mdss_dsi_ctrl_slave(ctrl);
+ if (&ctrl->mmss_misc_io == NULL) {
+ pr_err("%s: mmss_misc_io is NULL. ULPS not valid\n", __func__);
+ return -EINVAL;
+ }
- if (sctrl) {
- pr_debug("%s: configuring ulps (%s) for slave ctrl\n",
- __func__, (enable ? "on" : "off"));
- rc = mdss_dsi_ulps_config_sub(sctrl, enable);
+ if (mdss_dsi_is_slave_ctrl(ctrl)) {
+ mctrl = mdss_dsi_get_master_ctrl();
+ if (!mctrl) {
+ pr_err("%s: Unable to get master control\n", __func__);
+ return -EINVAL;
+ }
+ }
+
+ if (mctrl) {
+ pr_debug("%s: configuring ulps (%s) for master ctrl%d\n",
+ __func__, (enable ? "on" : "off"), ctrl->ndx);
+ rc = mdss_dsi_ulps_config_sub(mctrl, enable);
if (rc)
return rc;
}
- pr_debug("%s: configuring ulps (%s) for master ctrl\n",
- __func__, (enable ? "on" : "off"));
+ pr_debug("%s: configuring ulps (%s) for ctrl%d\n",
+ __func__, (enable ? "on" : "off"), ctrl->ndx);
return mdss_dsi_ulps_config_sub(ctrl, enable);
}
@@ -585,7 +655,7 @@
return ret;
}
- ret = mdss_dsi_bus_clk_start(ctrl_pdata);
+ mdss_dsi_clk_ctrl(ctrl_pdata, DSI_BUS_CLKS, 1);
if (ret) {
pr_err("%s: failed to enable bus clocks. rc=%d\n", __func__,
ret);
@@ -602,9 +672,9 @@
mdss_dsi_phy_sw_reset((ctrl_pdata->ctrl_base));
mdss_dsi_phy_init(pdata);
- mdss_dsi_bus_clk_stop(ctrl_pdata);
+ mdss_dsi_clk_ctrl(ctrl_pdata, DSI_BUS_CLKS, 0);
- mdss_dsi_clk_ctrl(ctrl_pdata, 1);
+ mdss_dsi_clk_ctrl(ctrl_pdata, DSI_ALL_CLKS, 1);
__mdss_dsi_ctrl_setup(pdata);
mdss_dsi_sw_reset(pdata);
@@ -630,7 +700,7 @@
}
if (pdata->panel_info.type == MIPI_CMD_PANEL)
- mdss_dsi_clk_ctrl(ctrl_pdata, 0);
+ mdss_dsi_clk_ctrl(ctrl_pdata, DSI_ALL_CLKS, 0);
pr_debug("%s-:\n", __func__);
return 0;
@@ -703,6 +773,12 @@
}
}
+ if (pdata->panel_info.type == MIPI_VIDEO_PANEL &&
+ ctrl_pdata->off_cmds.link_state == DSI_LP_MODE) {
+ mdss_dsi_sw_reset(pdata);
+ mdss_dsi_host_init(pdata);
+ }
+
mdss_dsi_op_mode_config(DSI_CMD_MODE, pdata);
if (pdata->panel_info.type == MIPI_CMD_PANEL) {
@@ -751,15 +827,6 @@
mdss_dsi_sw_reset(pdata);
mdss_dsi_host_init(pdata);
mdss_dsi_op_mode_config(mipi->mode, pdata);
-
- if (ctrl_pdata->on_cmds.link_state == DSI_LP_MODE) {
- ret = mdss_dsi_unblank(pdata);
- if (ret) {
- pr_err("%s: unblank failed\n", __func__);
- return ret;
- }
- }
-
pr_debug("%s-:End\n", __func__);
return ret;
}
@@ -834,8 +901,8 @@
MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x0004,
dsi_ctrl);
mdss_dsi_controller_cfg(true, pdata);
- mdss_dsi_clk_ctrl(ctrl_pdata, 0);
- mdss_dsi_clk_ctrl(ctrl_pdata, 1);
+ mdss_dsi_clk_ctrl(ctrl_pdata, DSI_ALL_CLKS, 0);
+ mdss_dsi_clk_ctrl(ctrl_pdata, DSI_ALL_CLKS, 1);
dsi_ctrl |= 0x2;
MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x0004,
dsi_ctrl);
@@ -899,6 +966,8 @@
panel_data);
pr_debug("%s+:event=%d\n", __func__, event);
+ MDSS_XLOG(event, arg, ctrl_pdata->ndx, 0x3333);
+
switch (event) {
case MDSS_EVENT_UNBLANK:
rc = mdss_dsi_on(pdata);
@@ -923,15 +992,10 @@
rc = mdss_dsi_off(pdata);
break;
case MDSS_EVENT_CONT_SPLASH_FINISH:
+ if (ctrl_pdata->off_cmds.link_state == DSI_LP_MODE)
+ rc = mdss_dsi_blank(pdata);
ctrl_pdata->ctrl_state &= ~CTRL_STATE_MDP_ACTIVE;
- if (ctrl_pdata->on_cmds.link_state == DSI_LP_MODE) {
- rc = mdss_dsi_cont_splash_on(pdata);
- } else {
- pr_debug("%s:event=%d, Dsi On not called: ctrl_state: %d\n",
- __func__, event,
- ctrl_pdata->on_cmds.link_state);
- rc = -EINVAL;
- }
+ rc = mdss_dsi_cont_splash_on(pdata);
break;
case MDSS_EVENT_PANEL_CLK_CTRL:
mdss_dsi_clk_req(ctrl_pdata, (int)arg);
@@ -1190,6 +1254,8 @@
mdss_dsi_put_dt_vreg_data(&pdev->dev, &ctrl_pdata->power_data);
mfd = platform_get_drvdata(pdev);
msm_dss_iounmap(&ctrl_pdata->mmss_misc_io);
+ msm_dss_iounmap(&ctrl_pdata->phy_io);
+ msm_dss_iounmap(&ctrl_pdata->ctrl_io);
return 0;
}
@@ -1200,7 +1266,6 @@
{
int rc = 0;
u32 index;
- struct resource *mdss_dsi_mres;
rc = of_property_read_u32(pdev->dev.of_node, "cell-index", &index);
if (rc) {
@@ -1228,31 +1293,32 @@
return -EPERM;
}
- mdss_dsi_mres = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!mdss_dsi_mres) {
- pr_err("%s:%d unable to get the DSI ctrl resources",
+ rc = msm_dss_ioremap_byname(pdev, &ctrl->ctrl_io, "dsi_ctrl");
+ if (rc) {
+ pr_err("%s:%d unable to remap dsi ctrl resources",
__func__, __LINE__);
- return -ENOMEM;
+ return rc;
}
- ctrl->ctrl_base = ioremap(mdss_dsi_mres->start,
- resource_size(mdss_dsi_mres));
- if (!(ctrl->ctrl_base)) {
- pr_err("%s:%d unable to remap dsi resources",
+ ctrl->ctrl_base = ctrl->ctrl_io.base;
+ ctrl->reg_size = ctrl->ctrl_io.len;
+
+ rc = msm_dss_ioremap_byname(pdev, &ctrl->phy_io, "dsi_phy");
+ if (rc) {
+ pr_err("%s:%d unable to remap dsi phy resources",
__func__, __LINE__);
- return -ENOMEM;
+ return rc;
}
- ctrl->reg_size = resource_size(mdss_dsi_mres);
-
- pr_info("%s: dsi base=%x size=%x\n",
- __func__, (int)ctrl->ctrl_base, ctrl->reg_size);
+ pr_info("%s: ctrl_base=%p ctrl_size=%x phy_base=%p phy_size=%x\n",
+ __func__, ctrl->ctrl_base, ctrl->reg_size, ctrl->phy_io.base,
+ ctrl->phy_io.len);
rc = msm_dss_ioremap_byname(pdev, &ctrl->mmss_misc_io,
"mmss_misc_phys");
if (rc) {
- pr_err("%s:%d mmss_misc IO remap failed\n", __func__, __LINE__);
- return rc;
+ pr_debug("%s:%d mmss_misc IO remap failed\n",
+ __func__, __LINE__);
}
return 0;
@@ -1492,7 +1558,7 @@
return rc;
}
- mdss_dsi_clk_ctrl(ctrl_pdata, 1);
+ mdss_dsi_clk_ctrl(ctrl_pdata, DSI_ALL_CLKS, 1);
ctrl_pdata->ctrl_state |=
(CTRL_STATE_PANEL_INIT | CTRL_STATE_MDP_ACTIVE);
} else {
diff --git a/drivers/video/msm/mdss/mdss_dsi.h b/drivers/video/msm/mdss/mdss_dsi.h
index 57b0e75..962599d 100644
--- a/drivers/video/msm/mdss/mdss_dsi.h
+++ b/drivers/video/msm/mdss/mdss_dsi.h
@@ -151,8 +151,8 @@
#define DSI_CMD_TERM BIT(0)
extern struct device dsi_dev;
-extern int mdss_dsi_clk_on;
extern u32 dsi_irq;
+extern struct mdss_dsi_ctrl_pdata *ctrl_list[];
struct dsiphy_pll_divider_config {
u32 clk_rate;
@@ -223,12 +223,18 @@
DSI_CTRL_MAX,
};
+/* DSI controller #0 is always treated as a master in broadcast mode */
+#define DSI_CTRL_MASTER DSI_CTRL_0
+#define DSI_CTRL_SLAVE DSI_CTRL_1
+
+#define DSI_BUS_CLKS BIT(0)
+#define DSI_LINK_CLKS BIT(1)
+#define DSI_ALL_CLKS ((DSI_BUS_CLKS) | (DSI_LINK_CLKS))
+
#define DSI_EV_PLL_UNLOCKED 0x0001
#define DSI_EV_MDP_FIFO_UNDERFLOW 0x0002
#define DSI_EV_MDP_BUSY_RELEASE 0x80000000
-#define DSI_FLAG_CLOCK_MASTER 0x80000000
-
struct mdss_dsi_ctrl_pdata {
int ndx; /* panel_num */
int (*on) (struct mdss_panel_data *pdata);
@@ -238,10 +244,12 @@
int (*cmdlist_commit)(struct mdss_dsi_ctrl_pdata *ctrl, int from_mdp);
struct mdss_panel_data panel_data;
unsigned char *ctrl_base;
+ struct dss_io_data ctrl_io;
struct dss_io_data mmss_misc_io;
+ struct dss_io_data phy_io;
int reg_size;
- u32 clk_cnt;
- int clk_cnt_sub;
+ u32 bus_clk_cnt;
+ u32 link_clk_cnt;
u32 flags;
struct clk *mdp_core_clk;
struct clk *ahb_clk;
@@ -253,7 +261,6 @@
u8 ctrl_state;
int panel_mode;
int irq_cnt;
- int mdss_dsi_clk_on;
int rst_gpio;
int disp_en_gpio;
int disp_te_gpio;
@@ -295,6 +302,12 @@
struct dsi_buf rx_buf;
};
+struct dsi_status_data {
+ struct notifier_block fb_notifier;
+ struct delayed_work check_status;
+ struct msm_fb_data_type *mfd;
+};
+
int dsi_panel_device_register(struct device_node *pan_node,
struct mdss_dsi_ctrl_pdata *ctrl_pdata);
@@ -312,24 +325,18 @@
void mdss_dsi_cmd_mdp_start(struct mdss_dsi_ctrl_pdata *ctrl);
void mdss_dsi_cmd_bta_sw_trigger(struct mdss_panel_data *pdata);
void mdss_dsi_ack_err_status(struct mdss_dsi_ctrl_pdata *ctrl);
-void mdss_dsi_clk_ctrl(struct mdss_dsi_ctrl_pdata *ctrl, int enable);
-int mdss_dsi_link_clk_start(struct mdss_dsi_ctrl_pdata *ctrl);
-void mdss_dsi_link_clk_stop(struct mdss_dsi_ctrl_pdata *ctrl);
-int mdss_dsi_bus_clk_start(struct mdss_dsi_ctrl_pdata *ctrl);
-void mdss_dsi_bus_clk_stop(struct mdss_dsi_ctrl_pdata *ctrl);
+int mdss_dsi_clk_ctrl(struct mdss_dsi_ctrl_pdata *ctrl,
+ u8 clk_type, int enable);
void mdss_dsi_clk_req(struct mdss_dsi_ctrl_pdata *ctrl,
int enable);
void mdss_dsi_controller_cfg(int enable,
struct mdss_panel_data *pdata);
void mdss_dsi_sw_reset(struct mdss_panel_data *pdata);
-struct mdss_dsi_ctrl_pdata *mdss_dsi_ctrl_slave(
- struct mdss_dsi_ctrl_pdata *ctrl);
-
irqreturn_t mdss_dsi_isr(int irq, void *ptr);
void mdss_dsi_irq_handler_config(struct mdss_dsi_ctrl_pdata *ctrl_pdata);
-void mipi_set_tx_power_mode(int mode, struct mdss_panel_data *pdata);
+void mdss_dsi_set_tx_power_mode(int mode, struct mdss_panel_data *pdata);
int mdss_dsi_clk_div_config(struct mdss_panel_info *panel_info,
int frame_rate);
int mdss_dsi_clk_init(struct platform_device *pdev,
@@ -351,9 +358,52 @@
int mdss_dsi_cmdlist_commit(struct mdss_dsi_ctrl_pdata *ctrl, int from_mdp);
void mdss_dsi_cmdlist_kickoff(int intf);
int mdss_dsi_bta_status_check(struct mdss_dsi_ctrl_pdata *ctrl);
-bool __mdss_dsi_clk_enabled(struct mdss_dsi_ctrl_pdata *ctrl);
+bool __mdss_dsi_clk_enabled(struct mdss_dsi_ctrl_pdata *ctrl, u8 clk_type);
int mdss_dsi_panel_init(struct device_node *node,
struct mdss_dsi_ctrl_pdata *ctrl_pdata,
bool cmd_cfg_cont_splash);
+
+static inline bool mdss_dsi_broadcast_mode_enabled(void)
+{
+ return ctrl_list[DSI_CTRL_MASTER]->shared_pdata.broadcast_enable &&
+ ctrl_list[DSI_CTRL_SLAVE] &&
+ ctrl_list[DSI_CTRL_SLAVE]->shared_pdata.broadcast_enable;
+}
+
+static inline struct mdss_dsi_ctrl_pdata *mdss_dsi_get_master_ctrl(void)
+{
+ if (mdss_dsi_broadcast_mode_enabled())
+ return ctrl_list[DSI_CTRL_MASTER];
+ else
+ return NULL;
+}
+
+static inline struct mdss_dsi_ctrl_pdata *mdss_dsi_get_slave_ctrl(void)
+{
+ if (mdss_dsi_broadcast_mode_enabled())
+ return ctrl_list[DSI_CTRL_SLAVE];
+ else
+ return NULL;
+}
+
+static inline bool mdss_dsi_is_master_ctrl(struct mdss_dsi_ctrl_pdata *ctrl)
+{
+ return mdss_dsi_broadcast_mode_enabled() &&
+ (ctrl->ndx == DSI_CTRL_MASTER);
+}
+
+static inline bool mdss_dsi_is_slave_ctrl(struct mdss_dsi_ctrl_pdata *ctrl)
+{
+ return mdss_dsi_broadcast_mode_enabled() &&
+ (ctrl->ndx == DSI_CTRL_SLAVE);
+}
+
+static inline struct mdss_dsi_ctrl_pdata *mdss_dsi_get_ctrl_by_index(int ndx)
+{
+ if (ndx >= DSI_CTRL_MAX)
+ return NULL;
+
+ return ctrl_list[ndx];
+}
#endif /* MDSS_DSI_H */
diff --git a/drivers/video/msm/mdss/mdss_dsi_host.c b/drivers/video/msm/mdss/mdss_dsi_host.c
index f342c56..95e7c6e 100644
--- a/drivers/video/msm/mdss/mdss_dsi_host.c
+++ b/drivers/video/msm/mdss/mdss_dsi_host.c
@@ -26,13 +26,11 @@
#include "mdss.h"
#include "mdss_dsi.h"
#include "mdss_panel.h"
+#include "mdss_debug.h"
#define VSYNC_PERIOD 17
-static struct mdss_dsi_ctrl_pdata *left_ctrl_pdata;
-
-static struct mdss_dsi_ctrl_pdata *ctrl_list[DSI_CTRL_MAX];
-
+struct mdss_dsi_ctrl_pdata *ctrl_list[DSI_CTRL_MAX];
struct mdss_hw mdss_dsi0_hw = {
.hw_ndx = MDSS_HW_DSI0,
@@ -72,14 +70,6 @@
void mdss_dsi_ctrl_init(struct mdss_dsi_ctrl_pdata *ctrl)
{
- if (ctrl->shared_pdata.broadcast_enable)
- if (ctrl->panel_data.panel_info.pdest
- == DISPLAY_1) {
- pr_debug("%s: Broadcast mode enabled.\n",
- __func__);
- left_ctrl_pdata = ctrl;
- }
-
if (ctrl->panel_data.panel_info.pdest == DISPLAY_1) {
mdss_dsi0_hw.ptr = (void *)(ctrl);
ctrl->dsi_hw = &mdss_dsi0_hw;
@@ -94,10 +84,6 @@
ctrl_list[ctrl->ndx] = ctrl; /* keep it */
- if (ctrl->shared_pdata.broadcast_enable)
- if (ctrl->ndx == DSI_CTRL_1)
- ctrl->flags |= DSI_FLAG_CLOCK_MASTER;
-
if (mdss_register_irq(ctrl->dsi_hw))
pr_err("%s: mdss_register_irq failed.\n", __func__);
@@ -123,24 +109,9 @@
}
}
-struct mdss_dsi_ctrl_pdata *mdss_dsi_ctrl_slave(
- struct mdss_dsi_ctrl_pdata *ctrl)
-{
- int ndx;
- struct mdss_dsi_ctrl_pdata *sctrl = NULL;
-
- /* only two controllers */
- ndx = ctrl->ndx;
- ndx += 1;
- ndx %= DSI_CTRL_MAX;
- sctrl = ctrl_list[ndx];
-
- return sctrl;
-
-}
-
void mdss_dsi_clk_req(struct mdss_dsi_ctrl_pdata *ctrl, int enable)
{
+ MDSS_XLOG(ctrl->ndx, enable, ctrl->mdp_busy, current->pid);
if (enable == 0) {
/* need wait before disable */
mutex_lock(&ctrl->cmd_mutex);
@@ -148,22 +119,27 @@
mutex_unlock(&ctrl->cmd_mutex);
}
- mdss_dsi_clk_ctrl(ctrl, enable);
+ MDSS_XLOG(ctrl->ndx, enable, ctrl->mdp_busy, current->pid);
+ mdss_dsi_clk_ctrl(ctrl, DSI_ALL_CLKS, enable);
}
void mdss_dsi_pll_relock(struct mdss_dsi_ctrl_pdata *ctrl)
{
int i, cnt;
- cnt = ctrl->clk_cnt;
+ /*
+ * todo: this code does not work very well with dual
+ * dsi use cases. Need to fix this eventually.
+ */
+ cnt = ctrl->link_clk_cnt;
/* disable dsi clk */
for (i = 0; i < cnt; i++)
- mdss_dsi_clk_ctrl(ctrl, 0);
+ mdss_dsi_clk_ctrl(ctrl, DSI_LINK_CLKS, 0);
/* enable dsi clk */
for (i = 0; i < cnt; i++)
- mdss_dsi_clk_ctrl(ctrl, 1);
+ mdss_dsi_clk_ctrl(ctrl, DSI_LINK_CLKS, 1);
}
void mdss_dsi_enable_irq(struct mdss_dsi_ctrl_pdata *ctrl, u32 term)
@@ -176,6 +152,7 @@
return;
}
if (ctrl->dsi_irq_mask == 0) {
+ MDSS_XLOG(ctrl->ndx, term);
mdss_enable_irq(ctrl->dsi_hw);
pr_debug("%s: IRQ Enable, ndx=%d mask=%x term=%x\n", __func__,
ctrl->ndx, (int)ctrl->dsi_irq_mask, (int)term);
@@ -195,6 +172,7 @@
}
ctrl->dsi_irq_mask &= ~term;
if (ctrl->dsi_irq_mask == 0) {
+ MDSS_XLOG(ctrl->ndx, term);
mdss_disable_irq(ctrl->dsi_hw);
pr_debug("%s: IRQ Disable, ndx=%d mask=%x term=%x\n", __func__,
ctrl->ndx, (int)ctrl->dsi_irq_mask, (int)term);
@@ -215,6 +193,7 @@
}
ctrl->dsi_irq_mask &= ~term;
if (ctrl->dsi_irq_mask == 0) {
+ MDSS_XLOG(ctrl->ndx, term);
mdss_disable_irq_nosync(ctrl->dsi_hw);
pr_debug("%s: IRQ Disable, ndx=%d mask=%x term=%x\n", __func__,
ctrl->ndx, (int)ctrl->dsi_irq_mask, (int)term);
@@ -341,7 +320,7 @@
/* from frame buffer, low power mode */
/* DSI_COMMAND_MODE_DMA_CTRL */
- if (ctrl_pdata->shared_pdata.broadcast_enable)
+ if (mdss_dsi_broadcast_mode_enabled())
MIPI_OUTP(ctrl_pdata->ctrl_base + 0x3C, 0x94000000);
else
MIPI_OUTP(ctrl_pdata->ctrl_base + 0x3C, 0x14000000);
@@ -392,7 +371,7 @@
wmb();
}
-void mdss_set_tx_power_mode(int mode, struct mdss_panel_data *pdata)
+void mdss_dsi_set_tx_power_mode(int mode, struct mdss_panel_data *pdata)
{
struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL;
u32 data;
@@ -544,6 +523,7 @@
{
u32 dsi_ctrl, intr_ctrl;
struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL;
+ struct mdss_dsi_ctrl_pdata *mctrl = NULL;
if (pdata == NULL) {
pr_err("%s: Invalid input data\n", __func__);
@@ -553,12 +533,15 @@
ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata,
panel_data);
- if (ctrl_pdata->shared_pdata.broadcast_enable)
- if (pdata->panel_info.pdest == DISPLAY_1) {
- pr_debug("%s: Broadcast mode. 1st ctrl\n",
- __func__);
- return;
- }
+ /*
+ * In broadcast mode, the configuration for master controller
+ * would be done when the slave controller is configured
+ */
+ if (mdss_dsi_is_master_ctrl(ctrl_pdata)) {
+ pr_debug("%s: Broadcast mode enabled. skipping config for ctrl%d\n",
+ __func__, ctrl_pdata->ndx);
+ return;
+ }
dsi_ctrl = MIPI_INP((ctrl_pdata->ctrl_base) + 0x0004);
/*If Video enabled, Keep Video and Cmd mode ON */
@@ -579,17 +562,22 @@
DSI_INTR_CMD_MDP_DONE_MASK | DSI_INTR_BTA_DONE_MASK;
}
- if (ctrl_pdata->shared_pdata.broadcast_enable)
- if ((pdata->panel_info.pdest == DISPLAY_2)
- && (left_ctrl_pdata != NULL)) {
- MIPI_OUTP(left_ctrl_pdata->ctrl_base + 0x0110,
- intr_ctrl); /* DSI_INTL_CTRL */
- MIPI_OUTP(left_ctrl_pdata->ctrl_base + 0x0004,
- dsi_ctrl);
+ /* Ensure that for slave controller, master is also configured */
+ if (mdss_dsi_is_slave_ctrl(ctrl_pdata)) {
+ mctrl = mdss_dsi_get_master_ctrl();
+ if (mctrl) {
+ pr_debug("%s: configuring ctrl%d\n", __func__,
+ mctrl->ndx);
+ MIPI_OUTP(mctrl->ctrl_base + 0x0110, intr_ctrl);
+ MIPI_OUTP(mctrl->ctrl_base + 0x0004, dsi_ctrl);
+ } else {
+ pr_warn("%s: Unable to get master control\n",
+ __func__);
}
+ }
- MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x0110,
- intr_ctrl); /* DSI_INTL_CTRL */
+ pr_debug("%s: configuring ctrl%d\n", __func__, ctrl_pdata->ndx);
+ MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x0110, intr_ctrl);
MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x0004, dsi_ctrl);
wmb();
}
@@ -640,7 +628,7 @@
pr_debug("%s: Checking BTA status\n", __func__);
- mdss_dsi_clk_ctrl(ctrl_pdata, 1);
+ mdss_dsi_clk_ctrl(ctrl_pdata, DSI_ALL_CLKS, 1);
spin_lock_irqsave(&ctrl_pdata->mdp_lock, flag);
INIT_COMPLETION(ctrl_pdata->bta_comp);
mdss_dsi_enable_irq(ctrl_pdata, DSI_BTA_TERM);
@@ -655,7 +643,7 @@
pr_err("%s: DSI BTA error: %i\n", __func__, ret);
}
- mdss_dsi_clk_ctrl(ctrl_pdata, 0);
+ mdss_dsi_clk_ctrl(ctrl_pdata, DSI_ALL_CLKS, 0);
pr_debug("%s: BTA done with ret: %d\n", __func__, ret);
return ret;
@@ -744,6 +732,40 @@
return tot;
}
+/**
+ * __mdss_dsi_cmd_mode_config() - Enable/disable command mode engine
+ * @ctrl: pointer to the dsi controller structure
+ * @enable: true to enable command mode, false to disable command mode
+ *
+ * This function can be used to temporarily enable the command mode
+ * engine (even for video mode panels) so as to transfer any dma commands to
+ * the panel. It can also be used to disable the command mode engine
+ * when no longer needed.
+ *
+ * Return: true, if there was a mode switch to command mode for video mode
+ * panels.
+ */
+static inline bool __mdss_dsi_cmd_mode_config(
+ struct mdss_dsi_ctrl_pdata *ctrl, bool enable)
+{
+ bool mode_changed = false;
+ u32 dsi_ctrl;
+
+ dsi_ctrl = MIPI_INP((ctrl->ctrl_base) + 0x0004);
+ /* if currently in video mode, enable command mode */
+ if (enable) {
+ if ((dsi_ctrl) & BIT(1)) {
+ MIPI_OUTP((ctrl->ctrl_base) + 0x0004,
+ dsi_ctrl | BIT(2));
+ mode_changed = true;
+ }
+ } else {
+ MIPI_OUTP((ctrl->ctrl_base) + 0x0004, dsi_ctrl & ~BIT(2));
+ }
+
+ return mode_changed;
+}
+
/*
* mdss_dsi_cmds_tx:
* thread context only
@@ -751,61 +773,49 @@
int mdss_dsi_cmds_tx(struct mdss_dsi_ctrl_pdata *ctrl,
struct dsi_cmd_desc *cmds, int cnt)
{
- u32 dsi_ctrl, data;
- int video_mode, ret = 0;
- u32 left_dsi_ctrl = 0;
- bool left_ctrl_restore = false;
+ int ret = 0;
+ bool ctrl_restore = false, mctrl_restore = false;
+ struct mdss_dsi_ctrl_pdata *mctrl = NULL;
- if (ctrl->shared_pdata.broadcast_enable) {
- if (ctrl->ndx == DSI_CTRL_0) {
- pr_debug("%s: Broadcast mode. 1st ctrl\n",
- __func__);
- return 0;
- }
+ /*
+ * In broadcast mode, the configuration for master controller
+ * would be done when the slave controller is configured
+ */
+ if (mdss_dsi_is_master_ctrl(ctrl)) {
+ pr_debug("%s: Broadcast mode enabled. skipping config for ctrl%d\n",
+ __func__, ctrl->ndx);
+ return 0;
}
- if (ctrl->shared_pdata.broadcast_enable) {
- if ((ctrl->ndx == DSI_CTRL_1)
- && (left_ctrl_pdata != NULL)) {
- left_dsi_ctrl = MIPI_INP(left_ctrl_pdata->ctrl_base
- + 0x0004);
- video_mode =
- left_dsi_ctrl & 0x02; /* VIDEO_MODE_EN */
- if (video_mode) {
- data = left_dsi_ctrl | 0x04; /* CMD_MODE_EN */
- MIPI_OUTP(left_ctrl_pdata->ctrl_base + 0x0004,
- data);
- left_ctrl_restore = true;
- }
- }
+ /*
+ * Turn on cmd mode in order to transmit the commands.
+ * For video mode, do not send cmds more than one pixel line,
+ * since it only transmit it during BLLP.
+ * Ensure that for slave controller, master is also configured
+ */
+ if (mdss_dsi_is_slave_ctrl(ctrl)) {
+ mctrl = mdss_dsi_get_master_ctrl();
+ if (!mctrl)
+ pr_warn("%s: Unable to get master control\n",
+ __func__);
+ else
+ mctrl_restore = __mdss_dsi_cmd_mode_config(mctrl, 1);
}
- /* turn on cmd mode
- * for video mode, do not send cmds more than
- * one pixel line, since it only transmit it
- * during BLLP.
- */
- dsi_ctrl = MIPI_INP((ctrl->ctrl_base) + 0x0004);
- video_mode = dsi_ctrl & 0x02; /* VIDEO_MODE_EN */
- if (video_mode) {
- data = dsi_ctrl | 0x04; /* CMD_MODE_EN */
- MIPI_OUTP((ctrl->ctrl_base) + 0x0004, data);
- }
+ ctrl_restore = __mdss_dsi_cmd_mode_config(ctrl, 1);
ret = mdss_dsi_cmds2buf_tx(ctrl, cmds, cnt);
if (IS_ERR_VALUE(ret)) {
- pr_err("%s: failed to call\n",
- __func__);
+ pr_err("%s: failed to call\n", __func__);
cnt = -EINVAL;
}
- if (left_ctrl_restore)
- MIPI_OUTP(left_ctrl_pdata->ctrl_base + 0x0004,
- left_dsi_ctrl); /*restore */
+ if (mctrl_restore)
+ __mdss_dsi_cmd_mode_config(mctrl, 0);
- if (video_mode)
- MIPI_OUTP((ctrl->ctrl_base) + 0x0004,
- dsi_ctrl); /* restore */
+ if (ctrl_restore)
+ __mdss_dsi_cmd_mode_config(ctrl, 0);
+
return cnt;
}
@@ -838,47 +848,37 @@
int short_response, diff, pkt_size, ret = 0;
struct dsi_buf *tp, *rp;
char cmd;
- u32 dsi_ctrl, data;
- int video_mode;
- u32 left_dsi_ctrl = 0;
- bool left_ctrl_restore = false;
+ bool ctrl_restore = false, mctrl_restore = false;
+ struct mdss_dsi_ctrl_pdata *mctrl = NULL;
- if (ctrl->shared_pdata.broadcast_enable) {
- if (ctrl->ndx == DSI_CTRL_0) {
- pr_debug("%s: Broadcast mode. 1st ctrl\n",
- __func__);
- return 0;
- }
+ /*
+ * In broadcast mode, the configuration for master controller
+ * would be done when the slave controller is configured
+ */
+ if (mdss_dsi_is_master_ctrl(ctrl)) {
+ pr_debug("%s: Broadcast mode enabled. skipping config for ctrl%d\n",
+ __func__, ctrl->ndx);
+ return 0;
}
- if (ctrl->shared_pdata.broadcast_enable) {
- if ((ctrl->ndx == DSI_CTRL_1)
- && (left_ctrl_pdata != NULL)) {
- left_dsi_ctrl = MIPI_INP(left_ctrl_pdata->ctrl_base
- + 0x0004);
- video_mode = left_dsi_ctrl & 0x02; /* VIDEO_MODE_EN */
- if (video_mode) {
- data = left_dsi_ctrl | 0x04; /* CMD_MODE_EN */
- MIPI_OUTP(left_ctrl_pdata->ctrl_base + 0x0004,
- data);
- left_ctrl_restore = true;
- }
- }
+ /*
+ * Turn on cmd mode in order to transmit the commands.
+ * For video mode, do not send cmds more than one pixel line,
+ * since it only transmit it during BLLP.
+ * Ensure that for slave controller, master is also configured
+ */
+ if (mdss_dsi_is_slave_ctrl(ctrl)) {
+ mctrl = mdss_dsi_get_master_ctrl();
+ if (!mctrl)
+ pr_warn("%s: Unable to get master control\n",
+ __func__);
+ else
+ mctrl_restore = __mdss_dsi_cmd_mode_config(mctrl, 1);
}
- /* turn on cmd mode
- * for video mode, do not send cmds more than
- * one pixel line, since it only transmit it
- * during BLLP.
- */
- dsi_ctrl = MIPI_INP((ctrl->ctrl_base) + 0x0004);
- video_mode = dsi_ctrl & 0x02; /* VIDEO_MODE_EN */
- if (video_mode) {
- data = dsi_ctrl | 0x04; /* CMD_MODE_EN */
- MIPI_OUTP((ctrl->ctrl_base) + 0x0004, data);
- }
+ ctrl_restore = __mdss_dsi_cmd_mode_config(ctrl, 1);
- if (rlen == 0) {
+ if (rlen <= 2) {
short_response = 1;
rx_byte = 4;
} else {
@@ -1003,12 +1003,11 @@
rp->len = 0;
}
end:
- if (left_ctrl_restore)
- MIPI_OUTP(left_ctrl_pdata->ctrl_base + 0x0004,
- left_dsi_ctrl); /*restore */
- if (video_mode)
- MIPI_OUTP((ctrl->ctrl_base) + 0x0004,
- dsi_ctrl); /* restore */
+ if (mctrl_restore)
+ __mdss_dsi_cmd_mode_config(mctrl, 0);
+
+ if (ctrl_restore)
+ __mdss_dsi_cmd_mode_config(ctrl, 0);
return rp->len;
}
@@ -1022,6 +1021,7 @@
int domain = MDSS_IOMMU_DOMAIN_UNSECURE;
char *bp;
unsigned long size, addr;
+ struct mdss_dsi_ctrl_pdata *mctrl = NULL;
bp = tp->data;
@@ -1043,24 +1043,27 @@
INIT_COMPLETION(ctrl->dma_comp);
- if (ctrl->shared_pdata.broadcast_enable)
- if ((ctrl->ndx == DSI_CTRL_1)
- && (left_ctrl_pdata != NULL)) {
- MIPI_OUTP(left_ctrl_pdata->ctrl_base + 0x048, addr);
- MIPI_OUTP(left_ctrl_pdata->ctrl_base + 0x04c, len);
+ /* Ensure that for slave controller, master is also configured */
+ if (mdss_dsi_is_slave_ctrl(ctrl)) {
+ mctrl = mdss_dsi_get_master_ctrl();
+ if (mctrl) {
+ MIPI_OUTP(mctrl->ctrl_base + 0x048, addr);
+ MIPI_OUTP(mctrl->ctrl_base + 0x04c, len);
+ } else {
+ pr_warn("%s: Unable to get master control\n",
+ __func__);
}
+ }
MIPI_OUTP((ctrl->ctrl_base) + 0x048, addr);
MIPI_OUTP((ctrl->ctrl_base) + 0x04c, len);
wmb();
- if (ctrl->shared_pdata.broadcast_enable)
- if ((ctrl->ndx == DSI_CTRL_1)
- && (left_ctrl_pdata != NULL)) {
- MIPI_OUTP(left_ctrl_pdata->ctrl_base + 0x090, 0x01);
- }
+ /* Trigger on master controller as well */
+ if (mctrl)
+ MIPI_OUTP(mctrl->ctrl_base + 0x090, 0x01);
- MIPI_OUTP((ctrl->ctrl_base) + 0x090, 0x01); /* trigger */
+ MIPI_OUTP((ctrl->ctrl_base) + 0x090, 0x01);
wmb();
ret = wait_for_completion_timeout(&ctrl->dma_comp,
@@ -1156,6 +1159,7 @@
mdss_dsi_enable_irq(ctrl, DSI_MDP_TERM);
ctrl->mdp_busy = true;
INIT_COMPLETION(ctrl->mdp_comp);
+ MDSS_XLOG(ctrl->ndx, ctrl->mdp_busy, current->pid);
spin_unlock_irqrestore(&ctrl->mdp_lock, flag);
}
@@ -1166,6 +1170,8 @@
pr_debug("%s: start pid=%d\n",
__func__, current->pid);
+
+ MDSS_XLOG(ctrl->ndx, ctrl->mdp_busy, current->pid, XLOG_FUNC_ENTRY);
spin_lock_irqsave(&ctrl->mdp_lock, flags);
if (ctrl->mdp_busy == true)
need_wait++;
@@ -1176,11 +1182,14 @@
pr_debug("%s: pending pid=%d\n",
__func__, current->pid);
if (!wait_for_completion_timeout(&ctrl->mdp_comp,
- msecs_to_jiffies(DMA_TX_TIMEOUT)))
+ msecs_to_jiffies(DMA_TX_TIMEOUT))) {
pr_err("%s: timeout error\n", __func__);
+ MDSS_XLOG_TOUT_HANDLER("mdp", "dsi0", "dsi1",
+ "edp", "hdmi", "panic");
+ }
}
- pr_debug("%s: done pid=%d\n",
- __func__, current->pid);
+ pr_debug("%s: done pid=%d\n", __func__, current->pid);
+ MDSS_XLOG(ctrl->ndx, ctrl->mdp_busy, current->pid, XLOG_FUNC_EXIT);
}
int mdss_dsi_cmdlist_tx(struct mdss_dsi_ctrl_pdata *ctrl,
@@ -1210,13 +1219,11 @@
len = mdss_dsi_cmds_rx(ctrl, req->cmds, req->rlen);
memcpy(req->rbuf, rp->data, rp->len);
/*
- * For dual DSI cases, early return of controller - 0
+ * For dual DSI cases, early return of master ctrl
* is valid. Hence, for those cases the return value
* is zero even though we don't send any commands.
- *
*/
- if ((ctrl->shared_pdata.broadcast_enable &&
- ctrl->ndx == DSI_CTRL_0) || (len != 0))
+ if (mdss_dsi_is_master_ctrl(ctrl) || (len != 0))
ret = 0;
} else {
pr_err("%s: No rx buffer provided\n", __func__);
@@ -1236,6 +1243,9 @@
mutex_lock(&ctrl->cmd_mutex);
req = mdss_dsi_cmdlist_get(ctrl);
+ MDSS_XLOG(ctrl->ndx, from_mdp, ctrl->mdp_busy, current->pid,
+ XLOG_FUNC_ENTRY);
+
/* make sure dsi_cmd_mdp is idle */
mdss_dsi_cmd_mdp_busy(ctrl);
@@ -1244,6 +1254,8 @@
if (req == NULL)
goto need_lock;
+ MDSS_XLOG(ctrl->ndx, req->flags, req->cmds_cnt, from_mdp, current->pid);
+
/*
* mdss interrupt is generated in mdp core clock domain
* mdp clock need to be enabled to receive dsi interrupt
@@ -1253,14 +1265,14 @@
mdss_bus_bandwidth_ctrl(1);
pr_debug("%s: from_mdp=%d pid=%d\n", __func__, from_mdp, current->pid);
- mdss_dsi_clk_ctrl(ctrl, 1);
+ mdss_dsi_clk_ctrl(ctrl, DSI_ALL_CLKS, 1);
if (req->flags & CMD_REQ_RX)
ret = mdss_dsi_cmdlist_rx(ctrl, req);
else
ret = mdss_dsi_cmdlist_tx(ctrl, req);
- mdss_dsi_clk_ctrl(ctrl, 0);
+ mdss_dsi_clk_ctrl(ctrl, DSI_ALL_CLKS, 0);
mdss_bus_bandwidth_ctrl(0);
need_lock:
@@ -1268,10 +1280,46 @@
if (from_mdp) /* from pipe_commit */
mdss_dsi_cmd_mdp_start(ctrl);
+ MDSS_XLOG(ctrl->ndx, from_mdp, ctrl->mdp_busy, current->pid,
+ XLOG_FUNC_EXIT);
mutex_unlock(&ctrl->cmd_mutex);
return ret;
}
+void mdss_dsi_debug_check_te(struct mdss_panel_data *pdata)
+{
+ struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL;
+ u8 rc, te_count = 0;
+ u8 te_max = 250;
+
+ if (pdata == NULL) {
+ pr_err("%s: Invalid input data\n", __func__);
+ return;
+ }
+ ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata,
+ panel_data);
+
+ pr_info(" ============ start waiting for TE ============\n");
+ for (te_count = 0; te_count < te_max; te_count++) {
+ rc = gpio_get_value(ctrl_pdata->disp_te_gpio);
+ if (rc != 0) {
+ pr_info("%s: gpio_get_value(disp_te_gpio) = %d ",
+ __func__, rc);
+ pr_info("te_count = %d\n", te_count);
+ break;
+ }
+ /* usleep suspends the calling thread whereas udelay is a
+ * busy wait. Here the value of te_gpio is checked in a loop of
+ * max count = 250. If this loop has to iterate multiple
+ * times before the te_gpio is 1, the calling thread will end
+ * up in suspend/wakeup sequence multiple times if usleep is
+ * used, which is an overhead. So use udelay instead of usleep.
+ */
+ udelay(80);
+ }
+ pr_info(" ============ finish waiting for TE ============\n");
+}
+
static void dsi_send_events(struct mdss_dsi_ctrl_pdata *ctrl, u32 events)
{
struct dsi_event_q *evq;
@@ -1333,11 +1381,11 @@
}
if (todo & DSI_EV_MDP_BUSY_RELEASE) {
- spin_lock(&ctrl->mdp_lock);
+ spin_lock_irqsave(&ctrl->mdp_lock, flag);
ctrl->mdp_busy = false;
mdss_dsi_disable_irq_nosync(ctrl, DSI_MDP_TERM);
complete(&ctrl->mdp_comp);
- spin_unlock(&ctrl->mdp_lock);
+ spin_unlock_irqrestore(&ctrl->mdp_lock, flag);
/* enable dsi error interrupt */
mdss_dsi_err_intr_ctrl(ctrl, DSI_INTR_ERROR_MASK, 1);
@@ -1410,6 +1458,8 @@
pr_err("%s: status=%x\n", __func__, status);
if (status & 0x0080) /* CMD_DMA_FIFO_UNDERFLOW */
dsi_send_events(ctrl, DSI_EV_MDP_FIFO_UNDERFLOW);
+ MDSS_XLOG_TOUT_HANDLER("mdp", "dsi0", "dsi1",
+ "edp", "hdmi", "panic");
}
}
@@ -1465,6 +1515,7 @@
u32 isr;
struct mdss_dsi_ctrl_pdata *ctrl =
(struct mdss_dsi_ctrl_pdata *)ptr;
+ struct mdss_dsi_ctrl_pdata *mctrl = NULL;
if (!ctrl->ctrl_base)
pr_err("%s:%d DSI base adr no Initialized",
@@ -1473,20 +1524,24 @@
isr = MIPI_INP(ctrl->ctrl_base + 0x0110);/* DSI_INTR_CTRL */
MIPI_OUTP(ctrl->ctrl_base + 0x0110, isr);
- if (ctrl->shared_pdata.broadcast_enable)
- if ((ctrl->panel_data.panel_info.pdest == DISPLAY_2)
- && (left_ctrl_pdata != NULL)) {
+ if (mdss_dsi_is_slave_ctrl(ctrl)) {
+ mctrl = mdss_dsi_get_master_ctrl();
+ if (mctrl) {
u32 isr0;
- isr0 = MIPI_INP(left_ctrl_pdata->ctrl_base
- + 0x0110);/* DSI_INTR_CTRL */
+ isr0 = MIPI_INP(mctrl->ctrl_base + 0x0110);
if (isr0 & DSI_INTR_CMD_DMA_DONE)
- MIPI_OUTP(left_ctrl_pdata->ctrl_base + 0x0110,
+ MIPI_OUTP(mctrl->ctrl_base + 0x0110,
DSI_INTR_CMD_DMA_DONE);
+ } else {
+ pr_warn("%s: Unable to get master control\n",
+ __func__);
}
+ }
pr_debug("%s: ndx=%d isr=%x\n", __func__, ctrl->ndx, isr);
if (isr & DSI_INTR_ERROR) {
+ MDSS_XLOG(ctrl->ndx, ctrl->mdp_busy, isr, 0x97);
pr_err("%s: ndx=%d isr=%x\n", __func__, ctrl->ndx, isr);
mdss_dsi_error(ctrl);
}
@@ -1499,6 +1554,7 @@
}
if (isr & DSI_INTR_CMD_DMA_DONE) {
+ MDSS_XLOG(ctrl->ndx, ctrl->mdp_busy, isr, 0x98);
spin_lock(&ctrl->mdp_lock);
mdss_dsi_disable_irq_nosync(ctrl, DSI_CMD_TERM);
complete(&ctrl->dma_comp);
@@ -1506,6 +1562,7 @@
}
if (isr & DSI_INTR_CMD_MDP_DONE) {
+ MDSS_XLOG(ctrl->ndx, ctrl->mdp_busy, isr, 0x99);
spin_lock(&ctrl->mdp_lock);
ctrl->mdp_busy = false;
mdss_dsi_disable_irq_nosync(ctrl, DSI_MDP_TERM);
diff --git a/drivers/video/msm/mdss/mdss_dsi_panel.c b/drivers/video/msm/mdss/mdss_dsi_panel.c
index 5415a7e..f58e2a1 100644
--- a/drivers/video/msm/mdss/mdss_dsi_panel.c
+++ b/drivers/video/msm/mdss/mdss_dsi_panel.c
@@ -317,17 +317,6 @@
return rc;
}
-static struct mdss_dsi_ctrl_pdata *get_rctrl_data(struct mdss_panel_data *pdata)
-{
- if (!pdata || !pdata->next) {
- pr_err("%s: Invalid panel data\n", __func__);
- return NULL;
- }
-
- return container_of(pdata->next, struct mdss_dsi_ctrl_pdata,
- panel_data);
-}
-
static void mdss_dsi_panel_bl_ctrl(struct mdss_panel_data *pdata,
u32 bl_level)
{
@@ -359,15 +348,15 @@
break;
case BL_DCS_CMD:
mdss_dsi_panel_bklt_dcs(ctrl_pdata, bl_level);
- if (ctrl_pdata->shared_pdata.broadcast_enable &&
- ctrl_pdata->ndx == DSI_CTRL_0) {
- struct mdss_dsi_ctrl_pdata *rctrl_pdata = NULL;
- rctrl_pdata = get_rctrl_data(pdata);
- if (!rctrl_pdata) {
- pr_err("%s: Right ctrl data NULL\n", __func__);
+ if (mdss_dsi_is_master_ctrl(ctrl_pdata)) {
+ struct mdss_dsi_ctrl_pdata *sctrl =
+ mdss_dsi_get_slave_ctrl();
+ if (!sctrl) {
+ pr_err("%s: Invalid slave ctrl data\n",
+ __func__);
return;
}
- mdss_dsi_panel_bklt_dcs(rctrl_pdata, bl_level);
+ mdss_dsi_panel_bklt_dcs(sctrl, bl_level);
}
break;
default:
@@ -675,6 +664,41 @@
return 0;
}
+static void mdss_panel_parse_te_params(struct device_node *np,
+ struct mdss_panel_info *panel_info)
+{
+
+ u32 tmp;
+ int rc = 0;
+ /*
+ * TE default: dsi byte clock calculated base on 70 fps;
+ * around 14 ms to complete a kickoff cycle if te disabled;
+ * vclk_line base on 60 fps; write is faster than read;
+ * init == start == rdptr;
+ */
+ panel_info->te.tear_check_en =
+ !of_property_read_bool(np, "qcom,mdss-tear-check-disable");
+ rc = of_property_read_u32
+ (np, "qcom,mdss-tear-check-sync-cfg-height", &tmp);
+ panel_info->te.sync_cfg_height = (!rc ? tmp : 0xfff0);
+ rc = of_property_read_u32
+ (np, "qcom,mdss-tear-check-sync-init-val", &tmp);
+ panel_info->te.vsync_init_val = (!rc ? tmp : panel_info->yres);
+ rc = of_property_read_u32
+ (np, "qcom,mdss-tear-check-sync-threshold-start", &tmp);
+ panel_info->te.sync_threshold_start = (!rc ? tmp : 4);
+ rc = of_property_read_u32
+ (np, "qcom,mdss-tear-check-sync-threshold-continue", &tmp);
+ panel_info->te.sync_threshold_continue = (!rc ? tmp : 4);
+ rc = of_property_read_u32(np, "qcom,mdss-tear-check-start-pos", &tmp);
+ panel_info->te.start_pos = (!rc ? tmp : panel_info->yres);
+ rc = of_property_read_u32
+ (np, "qcom,mdss-tear-check-rd-ptr-trigger-intr", &tmp);
+ panel_info->te.rd_ptr_irq = (!rc ? tmp : panel_info->yres + 1);
+ rc = of_property_read_u32(np, "qcom,mdss-tear-check-frame-rate", &tmp);
+ panel_info->te.refx100 = (!rc ? tmp : 6000);
+}
+
static int mdss_dsi_parse_reset_seq(struct device_node *np,
u32 rst_seq[MDSS_DSI_RST_SEQ_LEN], u32 *rst_len,
@@ -1005,6 +1029,7 @@
mdss_dsi_parse_reset_seq(np, pinfo->rst_seq, &(pinfo->rst_seq_len),
"qcom,mdss-dsi-reset-sequence");
+ mdss_panel_parse_te_params(np, pinfo);
mdss_dsi_parse_dcs_cmds(np, &ctrl_pdata->on_cmds,
"qcom,mdss-dsi-on-command", "qcom,mdss-dsi-on-command-state");
diff --git a/drivers/video/msm/mdss/mdss_dsi_status.c b/drivers/video/msm/mdss/mdss_dsi_status.c
index f0c4f4c..c68155a 100644
--- a/drivers/video/msm/mdss/mdss_dsi_status.c
+++ b/drivers/video/msm/mdss/mdss_dsi_status.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2014, 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
@@ -30,99 +30,37 @@
#include "mdss_panel.h"
#include "mdss_mdp.h"
-#define STATUS_CHECK_INTERVAL 5000
+#define STATUS_CHECK_INTERVAL_MS 5000
+#define STATUS_CHECK_INTERVAL_MIN_MS 200
+#define DSI_STATUS_CHECK_DISABLE 1
-struct dsi_status_data {
- struct notifier_block fb_notifier;
- struct delayed_work check_status;
- struct msm_fb_data_type *mfd;
- uint32_t check_interval;
-};
+static uint32_t interval = STATUS_CHECK_INTERVAL_MS;
+static uint32_t dsi_status_disable = DSI_STATUS_CHECK_DISABLE;
struct dsi_status_data *pstatus_data;
-static uint32_t interval = STATUS_CHECK_INTERVAL;
/*
- * check_dsi_ctrl_status() - Check DSI controller status periodically.
+ * check_dsi_ctrl_status() - Reads MFD structure and
+ * calls platform specific DSI ctrl Status function.
* @work : dsi controller status data
- *
- * This function calls check_status API on DSI controller to send the BTA
- * command. If DSI controller fails to acknowledge the BTA command, it sends
- * the PANEL_ALIVE=0 status to HAL layer.
*/
static void check_dsi_ctrl_status(struct work_struct *work)
{
struct dsi_status_data *pdsi_status = NULL;
- struct mdss_panel_data *pdata = NULL;
- struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL;
- struct mdss_overlay_private *mdp5_data = NULL;
- struct mdss_mdp_ctl *ctl = NULL;
- int ret = 0;
pdsi_status = container_of(to_delayed_work(work),
struct dsi_status_data, check_status);
+
if (!pdsi_status) {
pr_err("%s: DSI status data not available\n", __func__);
return;
}
- pdata = dev_get_platdata(&pdsi_status->mfd->pdev->dev);
- if (!pdata) {
- pr_err("%s: Panel data not available\n", __func__);
+ if (!pdsi_status->mfd) {
+ pr_err("%s: FB data not available\n", __func__);
return;
}
- ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata,
- panel_data);
- if (!ctrl_pdata || !ctrl_pdata->check_status) {
- pr_err("%s: DSI ctrl or status_check callback not available\n",
- __func__);
- return;
- }
-
- mdp5_data = mfd_to_mdp5_data(pdsi_status->mfd);
- ctl = mfd_to_ctl(pdsi_status->mfd);
-
- if (ctl->shared_lock)
- mutex_lock(ctl->shared_lock);
- mutex_lock(&mdp5_data->ov_lock);
-
- /*
- * For the command mode panels, we return pan display
- * IOCTL on vsync interrupt. So, after vsync interrupt comes
- * and when DMA_P is in progress, if the panel stops responding
- * and if we trigger BTA before DMA_P finishes, then the DSI
- * FIFO will not be cleared since the DSI data bus control
- * doesn't come back to the host after BTA. This may cause the
- * display reset not to be proper. Hence, wait for DMA_P done
- * for command mode panels before triggering BTA.
- */
- if (ctl->wait_pingpong)
- ctl->wait_pingpong(ctl, NULL);
-
- pr_debug("%s: DSI ctrl wait for ping pong done\n", __func__);
-
- mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
- ret = ctrl_pdata->check_status(ctrl_pdata);
- mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
-
- mutex_unlock(&mdp5_data->ov_lock);
- if (ctl->shared_lock)
- mutex_unlock(ctl->shared_lock);
-
- if ((pdsi_status->mfd->panel_power_on)) {
- if (ret > 0) {
- schedule_delayed_work(&pdsi_status->check_status,
- msecs_to_jiffies(pdsi_status->check_interval));
- } else {
- char *envp[2] = {"PANEL_ALIVE=0", NULL};
- pdata->panel_info.panel_dead = true;
- ret = kobject_uevent_env(
- &pdsi_status->mfd->fbi->dev->kobj,
- KOBJ_CHANGE, envp);
- pr_err("%s: Panel has gone bad, sending uevent - %s\n",
- __func__, envp[0]);
- }
- }
+ pdsi_status->mfd->mdp.check_dsi_status(work, interval);
}
/*
@@ -142,23 +80,80 @@
struct fb_event *evdata = data;
struct dsi_status_data *pdata = container_of(self,
struct dsi_status_data, fb_notifier);
+ struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL;
+
pdata->mfd = evdata->info->par;
+ ctrl_pdata = container_of(dev_get_platdata(&pdata->mfd->pdev->dev),
+ struct mdss_dsi_ctrl_pdata, panel_data);
+ if (!ctrl_pdata) {
+ pr_err("%s: DSI ctrl not available\n", __func__);
+ return NOTIFY_BAD;
+ }
+ if (dsi_status_disable) {
+ pr_debug("%s: DSI status disabled\n", __func__);
+ return NOTIFY_DONE;
+ }
if (event == FB_EVENT_BLANK && evdata) {
int *blank = evdata->data;
+ struct dsi_status_data *pdata = container_of(self,
+ struct dsi_status_data, fb_notifier);
+ pdata->mfd = evdata->info->par;
+
switch (*blank) {
case FB_BLANK_UNBLANK:
schedule_delayed_work(&pdata->check_status,
- msecs_to_jiffies(pdata->check_interval));
+ msecs_to_jiffies(interval));
break;
case FB_BLANK_POWERDOWN:
+ case FB_BLANK_HSYNC_SUSPEND:
+ case FB_BLANK_VSYNC_SUSPEND:
+ case FB_BLANK_NORMAL:
cancel_delayed_work(&pdata->check_status);
break;
+ default:
+ pr_err("Unknown case in FB_EVENT_BLANK event\n");
+ break;
}
}
return 0;
}
+static int param_dsi_status_disable(const char *val, struct kernel_param *kp)
+{
+ int ret = 0;
+ int int_val;
+
+ ret = kstrtos32(val, 0, &int_val);
+ if (ret)
+ return ret;
+
+ pr_info("%s: Set DSI status disable to %d\n",
+ __func__, int_val);
+ *((int *)kp->arg) = int_val;
+ return ret;
+}
+
+static int param_set_interval(const char *val, struct kernel_param *kp)
+{
+ int ret = 0;
+ int int_val;
+
+ ret = kstrtos32(val, 0, &int_val);
+ if (ret)
+ return ret;
+ if (int_val < STATUS_CHECK_INTERVAL_MIN_MS) {
+ pr_err("%s: Invalid value %d used, ignoring\n",
+ __func__, int_val);
+ ret = -EINVAL;
+ } else {
+ pr_info("%s: Set check interval to %d msecs\n",
+ __func__, int_val);
+ *((int *)kp->arg) = int_val;
+ }
+ return ret;
+}
+
int __init mdss_dsi_status_init(void)
{
int rc = 0;
@@ -179,7 +174,6 @@
return -EPERM;
}
- pstatus_data->check_interval = interval;
pr_info("%s: DSI status check interval:%d\n", __func__, interval);
INIT_DELAYED_WORK(&pstatus_data->check_status, check_dsi_ctrl_status);
@@ -197,11 +191,17 @@
pr_debug("%s: DSI ctrl status work queue removed\n", __func__);
}
-module_param(interval, uint, 0);
+module_param_call(interval, param_set_interval, param_get_uint,
+ &interval, 0644);
MODULE_PARM_DESC(interval,
"Duration in milliseconds to send BTA command for checking"
"DSI status periodically");
+module_param_call(dsi_status_disable, param_dsi_status_disable, param_get_uint,
+ &dsi_status_disable, 0644);
+MODULE_PARM_DESC(dsi_status_disable,
+ "Disable DSI status check");
+
module_init(mdss_dsi_status_init);
module_exit(mdss_dsi_status_exit);
diff --git a/drivers/video/msm/mdss/mdss_fb.c b/drivers/video/msm/mdss/mdss_fb.c
index 252a86e..4395d1b 100644
--- a/drivers/video/msm/mdss/mdss_fb.c
+++ b/drivers/video/msm/mdss/mdss_fb.c
@@ -52,6 +52,7 @@
#include <mach/msm_memtypes.h>
#include "mdss_fb.h"
+#include "mdss_mdp_splash_logo.h"
#ifdef CONFIG_FB_MSM_TRIPLE_BUFFER
#define MDSS_FB_NUM 3
@@ -106,6 +107,31 @@
complete(&mfd->no_update.comp);
}
+void mdss_fb_bl_update_notify(struct msm_fb_data_type *mfd)
+{
+ if (!mfd) {
+ pr_err("%s mfd NULL\n", __func__);
+ return;
+ }
+ mutex_lock(&mfd->update.lock);
+ if (mfd->update.ref_count > 0) {
+ mutex_unlock(&mfd->update.lock);
+ mfd->update.value = NOTIFY_TYPE_BL_UPDATE;
+ complete(&mfd->update.comp);
+ mutex_lock(&mfd->update.lock);
+ }
+ mutex_unlock(&mfd->update.lock);
+
+ mutex_lock(&mfd->no_update.lock);
+ if (mfd->no_update.ref_count > 0) {
+ mutex_unlock(&mfd->no_update.lock);
+ mfd->no_update.value = NOTIFY_TYPE_BL_UPDATE;
+ complete(&mfd->no_update.comp);
+ mutex_lock(&mfd->no_update.lock);
+ }
+ mutex_unlock(&mfd->no_update.lock);
+}
+
static int mdss_fb_notify_update(struct msm_fb_data_type *mfd,
unsigned long *argp)
{
@@ -127,8 +153,14 @@
ret = 1;
} else if (notify == NOTIFY_UPDATE_START) {
INIT_COMPLETION(mfd->update.comp);
- ret = wait_for_completion_timeout(
+ mutex_lock(&mfd->update.lock);
+ mfd->update.ref_count++;
+ mutex_unlock(&mfd->update.lock);
+ ret = wait_for_completion_interruptible_timeout(
&mfd->update.comp, 4 * HZ);
+ mutex_lock(&mfd->update.lock);
+ mfd->update.ref_count--;
+ mutex_unlock(&mfd->update.lock);
to_user = (unsigned int)mfd->update.value;
if (mfd->update.type == NOTIFY_TYPE_SUSPEND) {
to_user = (unsigned int)mfd->update.type;
@@ -136,13 +168,19 @@
}
} else if (notify == NOTIFY_UPDATE_STOP) {
INIT_COMPLETION(mfd->no_update.comp);
- ret = wait_for_completion_timeout(
+ mutex_lock(&mfd->no_update.lock);
+ mfd->no_update.ref_count++;
+ mutex_unlock(&mfd->no_update.lock);
+ ret = wait_for_completion_interruptible_timeout(
&mfd->no_update.comp, 4 * HZ);
+ mutex_lock(&mfd->no_update.lock);
+ mfd->no_update.ref_count--;
+ mutex_unlock(&mfd->no_update.lock);
to_user = (unsigned int)mfd->no_update.value;
} else {
if (mfd->panel_power_on) {
INIT_COMPLETION(mfd->power_off_comp);
- ret = wait_for_completion_timeout(
+ ret = wait_for_completion_interruptible_timeout(
&mfd->power_off_comp, 1 * HZ);
}
}
@@ -154,47 +192,6 @@
return ret;
}
-static int mdss_fb_splash_thread(void *data)
-{
- struct msm_fb_data_type *mfd = data;
- int ret = -EINVAL;
- struct fb_info *fbi = NULL;
- int ov_index[2];
-
- if (!mfd || !mfd->fbi || !mfd->mdp.splash_fnc) {
- pr_err("Invalid input parameter\n");
- goto end;
- }
-
- fbi = mfd->fbi;
-
- ret = mdss_fb_open(fbi, current->tgid);
- if (ret) {
- pr_err("fb_open failed\n");
- goto end;
- }
-
- mfd->bl_updated = true;
- mdss_fb_set_backlight(mfd, mfd->panel_info->bl_max >> 1);
-
- ret = mfd->mdp.splash_fnc(mfd, ov_index, MDP_CREATE_SPLASH_OV);
- if (ret) {
- pr_err("Splash image failed\n");
- goto splash_err;
- }
-
- do {
- schedule_timeout_interruptible(SPLASH_THREAD_WAIT_TIMEOUT * HZ);
- } while (!kthread_should_stop());
-
- mfd->mdp.splash_fnc(mfd, ov_index, MDP_REMOVE_SPLASH_OV);
-
-splash_err:
- mdss_fb_release(fbi, current->tgid);
-end:
- return ret;
-}
-
static int lcd_backlight_registered;
static void mdss_fb_set_bl_brightness(struct led_classdev *led_cdev,
@@ -270,25 +267,30 @@
static void mdss_fb_parse_dt(struct msm_fb_data_type *mfd)
{
- u32 data[2];
+ u32 data[2] = {0};
+ u32 panel_xres;
struct platform_device *pdev = mfd->pdev;
- mfd->splash_logo_enabled = of_property_read_bool(pdev->dev.of_node,
- "qcom,mdss-fb-splash-logo-enabled");
+ of_property_read_u32_array(pdev->dev.of_node,
+ "qcom,mdss-fb-split", data, 2);
- if (of_property_read_u32_array(pdev->dev.of_node, "qcom,mdss-fb-split",
- data, 2))
- return;
- if (data[0] && data[1] &&
- (mfd->panel_info->xres == (data[0] + data[1]))) {
- mfd->split_fb_left = data[0];
- mfd->split_fb_right = data[1];
- pr_info("split framebuffer left=%d right=%d\n",
- mfd->split_fb_left, mfd->split_fb_right);
+ panel_xres = mfd->panel_info->xres;
+ if (data[0] && data[1]) {
+ if (mfd->split_display)
+ panel_xres *= 2;
+
+ if (panel_xres == data[0] + data[1]) {
+ mfd->split_fb_left = data[0];
+ mfd->split_fb_right = data[1];
+ }
} else {
- mfd->split_fb_left = 0;
- mfd->split_fb_right = 0;
+ if (mfd->split_display)
+ mfd->split_fb_left = mfd->split_fb_right = panel_xres;
+ else
+ mfd->split_fb_left = mfd->split_fb_right = 0;
}
+ pr_info("split framebuffer left=%d right=%d\n",
+ mfd->split_fb_left, mfd->split_fb_right);
}
static ssize_t mdss_fb_get_split(struct device *dev,
@@ -316,14 +318,75 @@
return ret;
}
+static void __mdss_fb_idle_notify_work(struct work_struct *work)
+{
+ struct delayed_work *dw = to_delayed_work(work);
+ struct msm_fb_data_type *mfd = container_of(dw, struct msm_fb_data_type,
+ idle_notify_work);
+
+ /* Notify idle-ness here */
+ pr_debug("Idle timeout %dms expired!\n", mfd->idle_time);
+ sysfs_notify(&mfd->fbi->dev->kobj, NULL, "idle_notify");
+}
+
+static ssize_t mdss_fb_get_idle_time(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct fb_info *fbi = dev_get_drvdata(dev);
+ struct msm_fb_data_type *mfd = fbi->par;
+ int ret;
+
+ ret = scnprintf(buf, PAGE_SIZE, "%d", mfd->idle_time);
+
+ return ret;
+}
+
+static ssize_t mdss_fb_set_idle_time(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct fb_info *fbi = dev_get_drvdata(dev);
+ struct msm_fb_data_type *mfd = fbi->par;
+ int rc = 0;
+ int idle_time = 0;
+
+ rc = kstrtoint(buf, 10, &idle_time);
+ if (rc) {
+ pr_err("kstrtoint failed. rc=%d\n", rc);
+ return rc;
+ }
+
+ pr_debug("Idle time = %d\n", idle_time);
+ mfd->idle_time = idle_time;
+
+ return count;
+}
+
+static ssize_t mdss_fb_get_idle_notify(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct fb_info *fbi = dev_get_drvdata(dev);
+ struct msm_fb_data_type *mfd = fbi->par;
+ int ret;
+
+ ret = scnprintf(buf, PAGE_SIZE, "%s",
+ work_busy(&mfd->idle_notify_work.work) ? "no" : "yes");
+
+ return ret;
+}
+
static DEVICE_ATTR(msm_fb_type, S_IRUGO, mdss_fb_get_type, NULL);
static DEVICE_ATTR(msm_fb_split, S_IRUGO, mdss_fb_get_split, NULL);
static DEVICE_ATTR(show_blank_event, S_IRUGO, mdss_mdp_show_blank_event, NULL);
+static DEVICE_ATTR(idle_time, S_IRUGO | S_IWUSR | S_IWGRP,
+ mdss_fb_get_idle_time, mdss_fb_set_idle_time);
+static DEVICE_ATTR(idle_notify, S_IRUGO, mdss_fb_get_idle_notify, NULL);
static struct attribute *mdss_fb_attrs[] = {
&dev_attr_msm_fb_type.attr,
&dev_attr_msm_fb_split.attr,
&dev_attr_show_blank_event.attr,
+ &dev_attr_idle_time.attr,
+ &dev_attr_idle_notify.attr,
NULL,
};
@@ -469,15 +532,10 @@
break;
}
- if (mfd->splash_logo_enabled) {
- mfd->splash_thread = kthread_run(mdss_fb_splash_thread, mfd,
- "mdss_fb_splash");
- if (IS_ERR(mfd->splash_thread)) {
- pr_err("unable to start splash thread %d\n",
- mfd->index);
- mfd->splash_thread = NULL;
- }
- }
+ if (mfd->mdp.splash_init_fnc)
+ mfd->mdp.splash_init_fnc(mfd);
+
+ INIT_DELAYED_WORK(&mfd->idle_notify_work, __mdss_fb_idle_notify_work);
return rc;
}
@@ -747,6 +805,7 @@
mutex_unlock(&mfd->bl_lock);
/* Will trigger ad_setup which will grab bl_lock */
update_ad_input(mfd);
+ mdss_fb_bl_update_notify(mfd);
mutex_lock(&mfd->bl_lock);
}
}
@@ -793,6 +852,11 @@
mfd->update.type = NOTIFY_TYPE_UPDATE;
mfd->update.is_suspend = 0;
mutex_unlock(&mfd->update.lock);
+
+ /* Start the work thread to signal idle time */
+ if (mfd->idle_time)
+ schedule_delayed_work(&mfd->idle_notify_work,
+ msecs_to_jiffies(mfd->idle_time));
}
break;
@@ -828,6 +892,8 @@
}
break;
}
+ /* Notify listeners */
+ sysfs_notify(&mfd->fbi->dev->kobj, NULL, "show_blank_event");
return ret;
}
@@ -1179,16 +1245,22 @@
mutex_init(&mfd->mdp_sync_pt_data.sync_mutex);
atomic_set(&mfd->mdp_sync_pt_data.commit_cnt, 0);
atomic_set(&mfd->commits_pending, 0);
+ atomic_set(&mfd->ioctl_ref_cnt, 0);
+ atomic_set(&mfd->kickoff_pending, 0);
init_timer(&mfd->no_update.timer);
mfd->no_update.timer.function = mdss_fb_no_update_notify_timer_cb;
mfd->no_update.timer.data = (unsigned long)mfd;
+ mfd->update.ref_count = 0;
+ mfd->no_update.ref_count = 0;
init_completion(&mfd->update.comp);
init_completion(&mfd->no_update.comp);
init_completion(&mfd->power_off_comp);
init_completion(&mfd->power_set_comp);
init_waitqueue_head(&mfd->commit_wait_q);
init_waitqueue_head(&mfd->idle_wait_q);
+ init_waitqueue_head(&mfd->ioctl_q);
+ init_waitqueue_head(&mfd->kickoff_wait_q);
ret = fb_alloc_cmap(&fbi->cmap, 256, 0);
if (ret)
@@ -1269,12 +1341,6 @@
pinfo->ref_cnt++;
mfd->ref_cnt++;
- /* Stop the splash thread once userspace open the fb node */
- if (mfd->splash_thread && mfd->ref_cnt > 1) {
- kthread_stop(mfd->splash_thread);
- mfd->splash_thread = NULL;
- }
-
return 0;
blank_error:
@@ -1307,6 +1373,12 @@
return -EINVAL;
}
+ if (!wait_event_timeout(mfd->ioctl_q,
+ !atomic_read(&mfd->ioctl_ref_cnt) || !release_all,
+ msecs_to_jiffies(1000)))
+ pr_warn("fb%d ioctl could not finish. waited 1 sec.\n",
+ mfd->index);
+
mdss_fb_pan_idle(mfd);
pr_debug("release_all = %s\n", release_all ? "true" : "false");
@@ -1384,6 +1456,7 @@
mfd->index, ret, task->comm, pid);
return ret;
}
+ atomic_set(&mfd->ioctl_ref_cnt, 0);
}
return ret;
@@ -1520,10 +1593,20 @@
unsigned long event, void *data)
{
struct msm_sync_pt_data *sync_pt_data;
+ struct msm_fb_data_type *mfd;
sync_pt_data = container_of(p, struct msm_sync_pt_data, notifier);
+ mfd = container_of(sync_pt_data, struct msm_fb_data_type,
+ mdp_sync_pt_data);
switch (event) {
+ case MDP_NOTIFY_FRAME_BEGIN:
+ if (mfd->idle_time) {
+ cancel_delayed_work_sync(&mfd->idle_notify_work);
+ schedule_delayed_work(&mfd->idle_notify_work,
+ msecs_to_jiffies(mfd->idle_time));
+ }
+ break;
case MDP_NOTIFY_FRAME_READY:
if (sync_pt_data->async_wait_fences)
mdss_fb_wait_for_fence(sync_pt_data);
@@ -1574,6 +1657,25 @@
return 0;
}
+static int mdss_fb_wait_for_kickoff(struct msm_fb_data_type *mfd)
+{
+ int ret = 0;
+
+ ret = wait_event_timeout(mfd->kickoff_wait_q,
+ (!atomic_read(&mfd->kickoff_pending) ||
+ mfd->shutdown_pending),
+ msecs_to_jiffies(WAIT_DISP_OP_TIMEOUT / 2));
+ if (!ret) {
+ pr_err("wait for kickoff timeout %d pending=%d\n",
+ ret, atomic_read(&mfd->kickoff_pending));
+
+ } else if (mfd->shutdown_pending) {
+ pr_debug("Shutdown signalled\n");
+ return -EPERM;
+ }
+
+ return 0;
+}
static int mdss_fb_pan_display_ex(struct fb_info *info,
struct mdp_display_commit *disp_commit)
@@ -1612,6 +1714,7 @@
atomic_inc(&mfd->mdp_sync_pt_data.commit_cnt);
atomic_inc(&mfd->commits_pending);
+ atomic_inc(&mfd->kickoff_pending);
wake_up_all(&mfd->commit_wait_q);
mutex_unlock(&mfd->mdp_sync_pt_data.sync_mutex);
if (wait_for_finish)
@@ -1652,7 +1755,7 @@
(var->yoffset / info->fix.ypanstep) * info->fix.ypanstep;
if (mfd->mdp.dma_fnc)
- mfd->mdp.dma_fnc(mfd, NULL, 0, NULL);
+ mfd->mdp.dma_fnc(mfd);
else
pr_warn("dma function not set for panel type=%d\n",
mfd->panel.type);
@@ -1704,6 +1807,8 @@
if (ret)
pr_err("pan display failed %x on fb%d\n", ret,
mfd->index);
+ atomic_set(&mfd->kickoff_pending, 0);
+ wake_up_all(&mfd->kickoff_wait_q);
}
if (!ret)
mdss_fb_update_backlight(mfd);
@@ -1740,6 +1845,7 @@
}
atomic_set(&mfd->commits_pending, 0);
+ atomic_set(&mfd->kickoff_pending, 0);
wake_up_all(&mfd->idle_wait_q);
return ret;
@@ -2199,6 +2305,27 @@
return ret;
}
+static int __ioctl_wait_idle(struct msm_fb_data_type *mfd, u32 cmd)
+{
+ int ret = 0;
+
+ if (mfd->wait_for_kickoff &&
+ ((cmd == MSMFB_OVERLAY_PREPARE) ||
+ (cmd == MSMFB_BUFFER_SYNC) ||
+ (cmd == MSMFB_OVERLAY_SET))) {
+ ret = mdss_fb_wait_for_kickoff(mfd);
+ } else if ((cmd != MSMFB_VSYNC_CTRL) &&
+ (cmd != MSMFB_OVERLAY_VSYNC_CTRL) &&
+ (cmd != MSMFB_ASYNC_BLIT) &&
+ (cmd != MSMFB_BLIT) &&
+ (cmd != MSMFB_NOTIFY_UPDATE)) {
+ ret = mdss_fb_pan_idle(mfd);
+ }
+
+ if (ret)
+ pr_debug("Shutdown pending. Aborting operation %x\n", cmd);
+ return ret;
+}
static int mdss_fb_ioctl(struct fb_info *info, unsigned int cmd,
unsigned long arg)
@@ -2209,21 +2336,24 @@
int ret = -ENOSYS;
struct mdp_buf_sync buf_sync;
struct msm_sync_pt_data *sync_pt_data = NULL;
+
if (!info || !info->par)
return -EINVAL;
+
mfd = (struct msm_fb_data_type *)info->par;
+ if (!mfd)
+ return -EINVAL;
+
+ if (mfd->shutdown_pending)
+ return -EPERM;
+
+ atomic_inc(&mfd->ioctl_ref_cnt);
+
mdss_fb_power_setting_idle(mfd);
- if ((cmd != MSMFB_VSYNC_CTRL) && (cmd != MSMFB_OVERLAY_VSYNC_CTRL) &&
- (cmd != MSMFB_ASYNC_BLIT) && (cmd != MSMFB_BLIT) &&
- (cmd != MSMFB_NOTIFY_UPDATE) &&
- (cmd != MSMFB_OVERLAY_PREPARE)) {
- ret = mdss_fb_pan_idle(mfd);
- if (ret) {
- pr_debug("Shutdown pending. Aborting operation %x\n",
- cmd);
- return ret;
- }
- }
+
+ ret = __ioctl_wait_idle(mfd, cmd);
+ if (ret)
+ goto exit;
switch (cmd) {
case MSMFB_CURSOR:
@@ -2240,15 +2370,19 @@
ret = copy_to_user(argp, &fb_page_protection,
sizeof(fb_page_protection));
if (ret)
- return ret;
+ goto exit;
break;
case MSMFB_BUFFER_SYNC:
ret = copy_from_user(&buf_sync, argp, sizeof(buf_sync));
if (ret)
- return ret;
- if ((!mfd->op_enable) || (!mfd->panel_power_on))
- return -EPERM;
+ goto exit;
+
+ if ((!mfd->op_enable) || (!mfd->panel_power_on)) {
+ ret = -EPERM;
+ goto exit;
+ }
+
if (mfd->mdp.get_sync_fnc)
sync_pt_data = mfd->mdp.get_sync_fnc(mfd, &buf_sync);
if (!sync_pt_data)
@@ -2277,6 +2411,10 @@
if (ret == -ENOSYS)
pr_err("unsupported ioctl (%x)\n", cmd);
+exit:
+ if (!atomic_dec_return(&mfd->ioctl_ref_cnt))
+ wake_up_all(&mfd->ioctl_q);
+
return ret;
}
diff --git a/drivers/video/msm/mdss/mdss_fb.h b/drivers/video/msm/mdss/mdss_fb.h
index 2e024c9..3416b9e 100644
--- a/drivers/video/msm/mdss/mdss_fb.h
+++ b/drivers/video/msm/mdss/mdss_fb.h
@@ -21,6 +21,7 @@
#include <linux/notifier.h>
#include "mdss_panel.h"
+#include "mdss_mdp_splash_logo.h"
#define MSM_FB_DEFAULT_PAGE_SIZE 2
#define MFD_KEY 0x11161126
@@ -33,8 +34,6 @@
#define WAIT_DISP_OP_TIMEOUT ((WAIT_FENCE_FIRST_TIMEOUT + \
WAIT_FENCE_FINAL_TIMEOUT) * MDP_MAX_FENCE_FD)
-#define SPLASH_THREAD_WAIT_TIMEOUT 3
-
#ifndef MAX
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
#endif
@@ -69,11 +68,6 @@
MDP_NOTIFY_FRAME_TIMEOUT,
};
-enum mdp_splash_event {
- MDP_CREATE_SPLASH_OV = 0,
- MDP_REMOVE_SPLASH_OV,
-};
-
struct disp_info_type_suspend {
int op_enable;
int panel_power_on;
@@ -86,6 +80,7 @@
struct mutex lock;
int value;
int is_suspend;
+ int ref_count;
};
struct msm_sync_pt_data {
@@ -121,8 +116,7 @@
int (*kickoff_fnc)(struct msm_fb_data_type *mfd,
struct mdp_display_commit *data);
int (*ioctl_handler)(struct msm_fb_data_type *mfd, u32 cmd, void *arg);
- void (*dma_fnc)(struct msm_fb_data_type *mfd, struct mdp_overlay *req,
- int image_len, int *pipe_ndx);
+ void (*dma_fnc)(struct msm_fb_data_type *mfd);
int (*cursor_update)(struct msm_fb_data_type *mfd,
struct fb_cursor *cursor);
int (*lut_update)(struct msm_fb_data_type *mfd, struct fb_cmap *cmap);
@@ -131,9 +125,10 @@
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);
- int (*splash_fnc) (struct msm_fb_data_type *mfd, int *index, int req);
+ int (*splash_init_fnc)(struct msm_fb_data_type *mfd);
struct msm_sync_pt_data *(*get_sync_fnc)(struct msm_fb_data_type *mfd,
const struct mdp_buf_sync *buf_sync);
+ void (*check_dsi_status)(struct work_struct *work, uint32_t interval);
void *private1;
};
@@ -169,6 +164,9 @@
u32 dest;
struct fb_info *fbi;
+ int idle_time;
+ struct delayed_work idle_notify_work;
+
int op_enable;
u32 fb_imgType;
int panel_reconfig;
@@ -210,12 +208,16 @@
/* for non-blocking */
struct task_struct *disp_thread;
atomic_t commits_pending;
+ atomic_t kickoff_pending;
wait_queue_head_t commit_wait_q;
wait_queue_head_t idle_wait_q;
+ wait_queue_head_t kickoff_wait_q;
bool shutdown_pending;
- struct task_struct *splash_thread;
- bool splash_logo_enabled;
+ struct msm_fb_splash_info splash_info;
+
+ wait_queue_head_t ioctl_q;
+ atomic_t ioctl_ref_cnt;
struct msm_fb_backup_type msm_fb_backup;
struct completion power_set_comp;
@@ -223,6 +225,7 @@
u32 dcm_state;
struct list_head proc_list;
+ u32 wait_for_kickoff;
};
static inline void mdss_fb_update_notify_update(struct msm_fb_data_type *mfd)
diff --git a/drivers/video/msm/mdss/mdss_mdp.c b/drivers/video/msm/mdss/mdss_mdp.c
index cfa594c..97d4dc9 100644
--- a/drivers/video/msm/mdss/mdss_mdp.c
+++ b/drivers/video/msm/mdss/mdss_mdp.c
@@ -68,6 +68,7 @@
.fb_mem_get_iommu_domain = mdss_fb_mem_get_iommu_domain,
.panel_register_done = mdss_panel_register_done,
.fb_stride = mdss_mdp_fb_stride,
+ .check_dsi_status = mdss_check_dsi_ctrl_status,
};
#define DEFAULT_TOTAL_RGB_PIPES 3
@@ -296,6 +297,7 @@
pr_debug("Disable HW=%d irq ena=%d mask=%x\n", hw->hw_ndx,
mdss_res->irq_ena, mdss_res->irq_mask);
+ spin_lock(&mdss_lock);
if (!(mdss_res->irq_mask & ndx_bit)) {
pr_warn("MDSS HW ndx=%d is NOT set, mask=%x, hist mask=%x\n",
hw->hw_ndx, mdss_res->mdp_irq_mask,
@@ -307,6 +309,7 @@
disable_irq_nosync(mdss_res->irq);
}
}
+ spin_unlock(&mdss_lock);
}
EXPORT_SYMBOL(mdss_disable_irq_nosync);
@@ -505,7 +508,16 @@
spin_unlock_irqrestore(&mdp_lock, irq_flags);
}
-/* called from interrupt context */
+/**
+ * mdss_mdp_irq_disable_nosync() - disable mdp irq
+ * @intr_type: mdp interface type
+ * @intf_num: mdp interface num
+ *
+ * This fucntion is called from interrupt context
+ * mdp_lock is already held at up stream (mdss_irq_handler)
+ * therefore spin_lock(&mdp_lock) is not allowed here
+ *
+*/
void mdss_mdp_irq_disable_nosync(u32 intr_type, u32 intf_num)
{
u32 irq;
@@ -678,6 +690,7 @@
}
}
+ MDSS_XLOG(mdp_clk_cnt, changed, enable, current->pid);
pr_debug("%s: clk_cnt=%d changed=%d enable=%d\n",
__func__, mdp_clk_cnt, changed, enable);
@@ -781,6 +794,8 @@
int i;
mutex_lock(&mdp_iommu_lock);
+ MDSS_XLOG(mdata->iommu_attached);
+
if (mdata->iommu_attached) {
pr_debug("mdp iommu already attached\n");
mutex_unlock(&mdp_iommu_lock);
@@ -812,6 +827,8 @@
int i;
mutex_lock(&mdp_iommu_lock);
+ MDSS_XLOG(mdata->iommu_attached);
+
if (!mdata->iommu_attached) {
pr_debug("mdp iommu already dettached\n");
mutex_unlock(&mdp_iommu_lock);
@@ -954,7 +971,7 @@
if (rc)
return rc;
- mdss_debug_register_base(NULL, mdata->mdp_base, mdata->mdp_reg_size);
+ mdss_debug_register_base("mdp", mdata->mdp_base, mdata->mdp_reg_size);
return 0;
}
@@ -1001,8 +1018,9 @@
writel_relaxed(1, offset + 16);
}
- mdata->nmax_concurrent_ad_hw = (mdata->mdp_rev <= MDSS_MDP_HW_REV_102) ?
- 1 : 2;
+ mdata->nmax_concurrent_ad_hw =
+ (mdata->mdp_rev < MDSS_MDP_HW_REV_103) ? 1 : 2;
+
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
pr_debug("MDP hw init done\n");
@@ -1578,6 +1596,61 @@
return 0;
}
+static int mdss_mdp_parse_dt_pipe_clk_ctrl(struct platform_device *pdev,
+ char *prop_name, struct mdss_mdp_pipe *pipe_list, u32 npipes)
+{
+ int rc = 0;
+ size_t len;
+ const u32 *arr;
+
+ arr = of_get_property(pdev->dev.of_node, prop_name, &len);
+ if (arr) {
+ int i, j;
+
+ len /= sizeof(u32);
+ for (i = 0, j = 0; i < len; j++) {
+ struct mdss_mdp_pipe *pipe = NULL;
+
+ if (j >= npipes) {
+ pr_err("invalid clk ctrl enries for prop: %s\n",
+ prop_name);
+ return -EINVAL;
+ }
+
+ pipe = &pipe_list[j];
+
+ pipe->clk_ctrl.reg_off = be32_to_cpu(arr[i++]);
+ pipe->clk_ctrl.bit_off = be32_to_cpu(arr[i++]);
+
+ /* status register is next in line to ctrl register */
+ pipe->clk_status.reg_off = pipe->clk_ctrl.reg_off + 4;
+ pipe->clk_status.bit_off = be32_to_cpu(arr[i++]);
+
+ pr_debug("%s[%d]: ctrl: reg_off: 0x%x bit_off: %d\n",
+ prop_name, j, pipe->clk_ctrl.reg_off,
+ pipe->clk_ctrl.bit_off);
+ pr_debug("%s[%d]: status: reg_off: 0x%x bit_off: %d\n",
+ prop_name, j, pipe->clk_status.reg_off,
+ pipe->clk_status.bit_off);
+ }
+ if (j != npipes) {
+ pr_err("%s: %d entries found. required %d\n",
+ prop_name, j, npipes);
+ for (i = 0; i < npipes; i++) {
+ memset(&pipe_list[i].clk_ctrl, 0,
+ sizeof(pipe_list[i].clk_ctrl));
+ memset(&pipe_list[i].clk_status, 0,
+ sizeof(pipe_list[i].clk_status));
+ }
+ rc = -EINVAL;
+ }
+ } else {
+ pr_err("error mandatory property '%s' not found\n", prop_name);
+ rc = -EINVAL;
+ }
+
+ return rc;
+}
static int mdss_mdp_parse_dt_pipe(struct platform_device *pdev)
{
@@ -1761,6 +1834,25 @@
setup_cnt += mdata->nrgb_pipes - DEFAULT_TOTAL_RGB_PIPES;
}
+ rc = mdss_mdp_parse_dt_pipe_clk_ctrl(pdev,
+ "qcom,mdss-pipe-vig-clk-ctrl-offsets", mdata->vig_pipes,
+ mdata->nvig_pipes);
+ if (rc)
+ goto parse_fail;
+
+ rc = mdss_mdp_parse_dt_pipe_clk_ctrl(pdev,
+ "qcom,mdss-pipe-rgb-clk-ctrl-offsets", mdata->rgb_pipes,
+ mdata->nrgb_pipes);
+ if (rc)
+ goto parse_fail;
+
+ rc = mdss_mdp_parse_dt_pipe_clk_ctrl(pdev,
+ "qcom,mdss-pipe-dma-clk-ctrl-offsets", mdata->dma_pipes,
+ mdata->ndma_pipes);
+ if (rc)
+ goto parse_fail;
+
+
goto parse_done;
parse_fail:
@@ -2130,6 +2222,8 @@
"qcom,mdss-has-decimation");
mdata->has_wfd_blk = of_property_read_bool(pdev->dev.of_node,
"qcom,mdss-has-wfd-blk");
+ mdata->has_no_lut_read = of_property_read_bool(pdev->dev.of_node,
+ "qcom,mdss-no-lut-read");
prop = of_find_property(pdev->dev.of_node, "batfet-supply", NULL);
mdata->batfet_required = prop ? true : false;
rc = of_property_read_u32(pdev->dev.of_node,
@@ -2156,6 +2250,17 @@
mdss_mdp_parse_dt_fudge_factors(pdev, "qcom,mdss-ib-factor",
&mdata->ib_factor);
+ /*
+ * Set overlap ib value equal to ib by default. This value can
+ * be tuned in device tree to be different from ib.
+ * This factor apply when the max bandwidth per pipe
+ * is the overlap BW.
+ */
+ mdata->ib_factor_overlap.numer = mdata->ib_factor.numer;
+ mdata->ib_factor_overlap.denom = mdata->ib_factor.denom;
+ mdss_mdp_parse_dt_fudge_factors(pdev, "qcom,mdss-ib-factor-overlap",
+ &mdata->ib_factor_overlap);
+
mdata->clk_factor.numer = 1;
mdata->clk_factor.denom = 1;
mdss_mdp_parse_dt_fudge_factors(pdev, "qcom,mdss-clk-factor",
@@ -2171,6 +2276,23 @@
if (rc)
pr_debug("max bandwidth (high) property not specified\n");
+ mdata->nclk_lvl = mdss_mdp_parse_dt_prop_len(pdev,
+ "qcom,mdss-clk-levels");
+
+ if (mdata->nclk_lvl) {
+ mdata->clock_levels = kzalloc(sizeof(u32) * mdata->nclk_lvl,
+ GFP_KERNEL);
+ if (!mdata->clock_levels) {
+ pr_err("no mem assigned for mdata clock_levels\n");
+ return -ENOMEM;
+ }
+
+ rc = mdss_mdp_parse_dt_handler(pdev, "qcom,mdss-clk-levels",
+ mdata->clock_levels, mdata->nclk_lvl);
+ if (rc)
+ pr_debug("clock levels not found\n");
+ }
+
return 0;
}
diff --git a/drivers/video/msm/mdss/mdss_mdp.h b/drivers/video/msm/mdss/mdss_mdp.h
index f3b7ce1..1e17205 100644
--- a/drivers/video/msm/mdss/mdss_mdp.h
+++ b/drivers/video/msm/mdss/mdss_mdp.h
@@ -19,6 +19,7 @@
#include <linux/msm_mdp.h>
#include <linux/platform_device.h>
#include <linux/notifier.h>
+#include <linux/kref.h>
#include "mdss.h"
#include "mdss_mdp_hwio.h"
@@ -32,6 +33,7 @@
#define MDP_CLK_DEFAULT_RATE 200000000
#define PHASE_STEP_SHIFT 21
#define MAX_MIXER_WIDTH 2048
+#define MAX_LINE_BUFFER_WIDTH 2048
#define MAX_MIXER_HEIGHT 0xFFFF
#define MAX_IMG_WIDTH 0x3FFF
#define MAX_IMG_HEIGHT 0x3FFF
@@ -75,6 +77,13 @@
#define MDSS_MDP_REG_WRITE(addr, val) MDSS_REG_WRITE((u32)(addr), (u32)(val))
#define MDSS_MDP_REG_READ(addr) MDSS_REG_READ((u32)(addr))
#endif
+#define PERF_STATUS_DONE 0
+#define PERF_STATUS_BUSY 1
+
+enum mdss_mdp_perf_state_type {
+ PERF_SW_COMMIT_STATE = 0,
+ PERF_HW_MDP_STATE,
+};
enum mdss_mdp_block_power_state {
MDP_BLOCK_POWER_OFF = 0,
@@ -180,7 +189,7 @@
int force_screen_state;
struct mdss_mdp_perf_params cur_perf;
struct mdss_mdp_perf_params new_perf;
- int perf_status;
+ u32 perf_transaction_status;
struct mdss_data_type *mdata;
struct msm_fb_data_type *mfd;
@@ -192,8 +201,12 @@
struct mdss_panel_data *panel_data;
struct mdss_mdp_vsync_handler vsync_handler;
+ struct mdss_mdp_vsync_handler recover_underrun_handler;
+ struct work_struct recover_work;
+ struct work_struct remove_underrun_handler;
struct mdss_mdp_img_rect roi;
+ struct mdss_mdp_img_rect roi_bkup;
u8 roi_changed;
int (*start_fnc) (struct mdss_mdp_ctl *ctl);
@@ -349,6 +362,11 @@
DECLARE_BITMAP(fixed, MAX_DRV_SUP_MMB_BLKS);
};
+struct mdss_mdp_shared_reg_ctrl {
+ u32 reg_off;
+ u32 bit_off;
+};
+
struct mdss_mdp_pipe {
u32 num;
u32 type;
@@ -356,7 +374,11 @@
char __iomem *base;
u32 ftch_id;
u32 xin_id;
- atomic_t ref_cnt;
+ struct mdss_mdp_shared_reg_ctrl clk_ctrl;
+ struct mdss_mdp_shared_reg_ctrl clk_status;
+
+ struct kref kref;
+
u32 play_cnt;
int pid;
bool is_handed_off;
@@ -484,6 +506,17 @@
return readl_relaxed(mixer->pingpong_base + reg);
}
+static inline int mdss_mdp_iommu_dyn_attach_supported(
+ struct mdss_data_type *mdata)
+{
+ return (mdata->mdp_rev >= MDSS_MDP_HW_REV_103);
+}
+
+static inline int mdss_mdp_line_buffer_width(void)
+{
+ return MAX_LINE_BUFFER_WIDTH;
+}
+
irqreturn_t mdss_mdp_isr(int irq, void *ptr);
int mdss_iommu_attach(struct mdss_data_type *mdata);
int mdss_iommu_dettach(struct mdss_data_type *mdata);
@@ -517,6 +550,12 @@
struct msmfb_data *planes,
int num_planes,
u32 flags);
+int mdss_mdp_overlay_pipe_setup(struct msm_fb_data_type *mfd,
+ struct mdp_overlay *req, struct mdss_mdp_pipe **ppipe);
+void mdss_mdp_handoff_cleanup_pipes(struct msm_fb_data_type *mfd,
+ u32 type);
+int mdss_mdp_overlay_release(struct msm_fb_data_type *mfd, int ndx);
+int mdss_mdp_overlay_start(struct msm_fb_data_type *mfd);
int mdss_mdp_video_addr_setup(struct mdss_data_type *mdata,
u32 *offsets, u32 count);
int mdss_mdp_video_start(struct mdss_mdp_ctl *ctl);
@@ -543,7 +582,8 @@
struct mdss_mdp_pipe **left_plist, int left_cnt,
struct mdss_mdp_pipe **right_plist, int right_cnt);
int mdss_mdp_perf_calc_pipe(struct mdss_mdp_pipe *pipe,
- struct mdss_mdp_perf_params *perf, struct mdss_mdp_img_rect *roi);
+ struct mdss_mdp_perf_params *perf, struct mdss_mdp_img_rect *roi,
+ bool apply_fudge);
int mdss_mdp_ctl_notify(struct mdss_mdp_ctl *ctl, int event);
void mdss_mdp_ctl_notifier_register(struct mdss_mdp_ctl *ctl,
struct notifier_block *notifier);
@@ -555,8 +595,8 @@
int mdss_mdp_scan_pipes(void);
-void mdss_mdp_ctl_perf_taken(struct mdss_mdp_ctl *ctl);
-void mdss_mdp_ctl_perf_done(struct mdss_mdp_ctl *ctl);
+void mdss_mdp_ctl_perf_set_transaction_status(struct mdss_mdp_ctl *ctl,
+ enum mdss_mdp_perf_state_type component, bool new_status);
void mdss_mdp_ctl_perf_release_bw(struct mdss_mdp_ctl *ctl);
struct mdss_mdp_mixer *mdss_mdp_wb_mixer_alloc(int rotator);
@@ -566,6 +606,7 @@
int mux, int stage);
int mdss_mdp_mixer_pipe_update(struct mdss_mdp_pipe *pipe, int params_changed);
int mdss_mdp_mixer_pipe_unstage(struct mdss_mdp_pipe *pipe);
+void mdss_mdp_mixer_unstage_all(struct mdss_mdp_mixer *mixer);
int mdss_mdp_display_commit(struct mdss_mdp_ctl *ctl, void *arg);
int mdss_mdp_display_wait4comp(struct mdss_mdp_ctl *ctl);
int mdss_mdp_display_wait4pingpong(struct mdss_mdp_ctl *ctl);
@@ -639,10 +680,10 @@
int mdss_mdp_ctl_addr_setup(struct mdss_data_type *mdata, u32 *ctl_offsets,
u32 *wb_offsets, u32 len);
+int mdss_mdp_pipe_fetch_halt(struct mdss_mdp_pipe *pipe);
int mdss_mdp_pipe_destroy(struct mdss_mdp_pipe *pipe);
int mdss_mdp_pipe_queue_data(struct mdss_mdp_pipe *pipe,
struct mdss_mdp_data *src_data);
-int mdss_mdp_pipe_fetch_halt(struct mdss_mdp_pipe *pipe);
int mdss_mdp_data_check(struct mdss_mdp_data *data,
struct mdss_mdp_plane_sizes *ps);
@@ -671,6 +712,7 @@
int mdss_mdp_get_ctl_mixers(u32 fb_num, u32 *mixer_id);
u32 mdss_mdp_fb_stride(u32 fb_index, u32 xres, int bpp);
+void mdss_check_dsi_ctrl_status(struct work_struct *work, uint32_t interval);
int mdss_panel_register_done(struct mdss_panel_data *pdata);
int mdss_mdp_limited_lut_igc_config(struct mdss_mdp_ctl *ctl);
@@ -685,7 +727,7 @@
void mdss_mdp_set_roi(struct mdss_mdp_ctl *ctl,
struct mdp_display_commit *data);
-int mdss_mdp_wb_set_format(struct msm_fb_data_type *mfd, int dst_format);
+int mdss_mdp_wb_set_format(struct msm_fb_data_type *mfd, u32 dst_format);
int mdss_mdp_wb_get_format(struct msm_fb_data_type *mfd,
struct mdp_mixer_cfg *mixer_cfg);
diff --git a/drivers/video/msm/mdss/mdss_mdp_ctl.c b/drivers/video/msm/mdss/mdss_mdp_ctl.c
index fe42669..65d28a0 100644
--- a/drivers/video/msm/mdss/mdss_mdp_ctl.c
+++ b/drivers/video/msm/mdss/mdss_mdp_ctl.c
@@ -22,7 +22,9 @@
#include "mdss_fb.h"
#include "mdss_mdp.h"
+#include "mdss_debug.h"
+static void mdss_mdp_xlog_mixer_reg(struct mdss_mdp_ctl *ctl);
static inline u64 fudge_factor(u64 val, u32 numer, u32 denom)
{
u64 result = (val * (u64)numer);
@@ -30,14 +32,11 @@
return result;
}
-#define AB_FUDGE_FACTOR(val) fudge_factor((val), \
- (mdss_res->ab_factor.numer), (mdss_res->ab_factor.denom))
-
-#define IB_FUDGE_FACTOR(val) fudge_factor((val), \
- (mdss_res->ib_factor.numer), (mdss_res->ib_factor.denom))
-
-#define CLK_FUDGE_FACTOR(val) fudge_factor((val), \
- (mdss_res->clk_factor.numer), (mdss_res->clk_factor.denom))
+static inline u64 apply_fudge_factor(u64 val,
+ struct mdss_fudge_factor *factor)
+{
+ return fudge_factor(val, factor->numer, factor->denom);
+}
static DEFINE_MUTEX(mdss_mdp_ctl_lock);
@@ -50,6 +49,11 @@
writel_relaxed(val, mixer->base + reg);
}
+static inline u32 mdp_mixer_read(struct mdss_mdp_mixer *mixer, u32 reg)
+{
+ return readl_relaxed(mixer->base + reg);
+}
+
static inline u32 mdss_mdp_get_pclk_rate(struct mdss_mdp_ctl *ctl)
{
struct mdss_panel_info *pinfo = &ctl->panel_data->panel_info;
@@ -64,7 +68,7 @@
{
struct mdss_panel_info *pinfo = &mixer->ctl->panel_data->panel_info;
- rate = CLK_FUDGE_FACTOR(rate);
+ rate = apply_fudge_factor(rate, &mdss_res->clk_factor);
/*
* If the panel is video mode and its back porch period is
@@ -73,7 +77,7 @@
*/
if (mixer->ctl->is_video_mode && pinfo &&
(pinfo->lcdc.v_back_porch < MDP_MIN_VBP))
- rate = CLK_FUDGE_FACTOR(rate);
+ rate = apply_fudge_factor(rate, &mdss_res->clk_factor);
return rate;
}
@@ -303,6 +307,7 @@
* @pipe: Source pipe struct containing updated pipe params
* @perf: Structure containing values that should be updated for
* performance tuning
+ * @apply_fudge: Boolean to determine if mdp clock fudge is applicable
*
* Function calculates the minimum required performance calculations in order
* to avoid MDP underflow. The calculations are based on the way MDP
@@ -310,7 +315,8 @@
* (MDP clock requirement) based on frame size and scaling requirements.
*/
int mdss_mdp_perf_calc_pipe(struct mdss_mdp_pipe *pipe,
- struct mdss_mdp_perf_params *perf, struct mdss_mdp_img_rect *roi)
+ struct mdss_mdp_perf_params *perf, struct mdss_mdp_img_rect *roi,
+ bool apply_fudge)
{
struct mdss_mdp_mixer *mixer;
int fps = DEFAULT_FRAME_RATE;
@@ -384,7 +390,10 @@
perf->bw_overlap = (quota / dst.h) * v_total;
}
- perf->mdp_clk_rate = mdss_mdp_clk_fudge_factor(mixer, rate);
+ if (apply_fudge)
+ perf->mdp_clk_rate = mdss_mdp_clk_fudge_factor(mixer, rate);
+ else
+ perf->mdp_clk_rate = rate;
prefill_params.smp_bytes = mdss_mdp_smp_get_size(pipe);
prefill_params.xres = xres;
@@ -418,7 +427,7 @@
static inline int mdss_mdp_perf_is_overlap(u32 y00, u32 y01, u32 y10, u32 y11)
{
- return (y10 < y00 && y11 >= y01) || (y10 >= y00 && y10 <= y01);
+ return (y10 < y00 && y11 >= y01) || (y10 >= y00 && y10 < y01);
}
static inline int cmpu32(const void *a, const void *b)
@@ -440,6 +449,8 @@
u64 bw_overlap[MDSS_MDP_MAX_STAGE] = { 0 };
u32 v_region[MDSS_MDP_MAX_STAGE * 2] = { 0 };
u32 prefill_bytes = 0;
+ struct mdss_data_type *mdata = mdss_mdp_get_mdata();
+ bool apply_fudge = true;
BUG_ON(num_pipes > MDSS_MDP_MAX_STAGE);
@@ -469,13 +480,36 @@
memset(bw_overlap, 0, sizeof(u64) * MDSS_MDP_MAX_STAGE);
memset(v_region, 0, sizeof(u32) * MDSS_MDP_MAX_STAGE * 2);
+ /*
+ * Apply this logic only for 8x26 to reduce clock rate
+ * for single video playback use case
+ */
+ if (IS_MDSS_MAJOR_MINOR_SAME(mdata->mdp_rev, MDSS_MDP_HW_REV_101)
+ && mixer->type == MDSS_MDP_MIXER_TYPE_INTF) {
+ u32 npipes = 0;
+ for (i = 0; i < MDSS_MDP_MAX_STAGE; i++) {
+ pipe = mixer->stage_pipe[i];
+ if (pipe) {
+ if (npipes) {
+ apply_fudge = true;
+ break;
+ }
+ npipes++;
+ apply_fudge = !(pipe->src_fmt->is_yuv)
+ || !(pipe->flags
+ & MDP_SOURCE_ROTATED_90);
+ }
+ }
+ }
+
for (i = 0; i < num_pipes; i++) {
struct mdss_mdp_perf_params tmp;
pipe = pipe_list[i];
if (pipe == NULL)
continue;
- if (mdss_mdp_perf_calc_pipe(pipe, &tmp, &mixer->roi))
+ if (mdss_mdp_perf_calc_pipe(pipe, &tmp, &mixer->roi,
+ apply_fudge))
continue;
prefill_bytes += tmp.prefill_bytes;
bw_overlap[i] = tmp.bw_overlap;
@@ -499,7 +533,7 @@
pr_debug("v_region[%d]%d\n", i, v_region[i]);
if (v_region[i] == v_region[i-1])
continue;
- y0 = (v_region[i-1]) ? v_region[i-1] + 1 : 0;
+ y0 = v_region[i-1];
y1 = v_region[i];
for (j = 0; j < num_pipes; j++) {
if (!bw_overlap[j])
@@ -622,9 +656,6 @@
}
perf->bw_ctl = max(perf->bw_prefill, perf->bw_overlap);
-
- if (ctl->is_video_mode)
- perf->bw_ctl = IB_FUDGE_FACTOR(perf->bw_ctl);
}
int mdss_mdp_perf_bw_check(struct mdss_mdp_ctl *ctl,
@@ -667,84 +698,116 @@
left_plist, (left_plist ? MDSS_MDP_MAX_STAGE : 0),
right_plist, (right_plist ? MDSS_MDP_MAX_STAGE : 0));
+ if (ctl->is_video_mode) {
+ if (perf->bw_overlap > perf->bw_prefill)
+ perf->bw_ctl = apply_fudge_factor(perf->bw_ctl,
+ &mdss_res->ib_factor_overlap);
+ else
+ perf->bw_ctl = apply_fudge_factor(perf->bw_ctl,
+ &mdss_res->ib_factor);
+ }
pr_debug("ctl=%d clk_rate=%u\n", ctl->num, perf->mdp_clk_rate);
- pr_debug("bw_overlap=%llu bw_prefill=%llu prefill_byptes=%d\n",
+ pr_debug("bw_overlap=%llu bw_prefill=%llu prefill_bytes=%d\n",
perf->bw_overlap, perf->bw_prefill, perf->prefill_bytes);
}
-static bool mdss_mdp_ctl_perf_bw_released(struct mdss_mdp_ctl *ctl)
+static void set_status(u32 *value, bool status, u32 bit_num)
{
- unsigned long flags;
- bool released = false;
-
- if (!ctl || !ctl->panel_data ||
- (ctl->panel_data->panel_info.type != MIPI_CMD_PANEL))
- return released;
-
- spin_lock_irqsave(&ctl->spin_lock, flags);
- if (ctl->perf_status == 0) {
- released = true;
- ctl->perf_status++;
- } else if (ctl->perf_status <= 2) {
- ctl->perf_status++;
- } else {
- pr_err("pervious commit was not done\n");
- }
-
- pr_debug("perf_status=%d\n", ctl->perf_status);
- spin_unlock_irqrestore(&ctl->spin_lock, flags);
-
- return released;
+ if (status)
+ *value |= BIT(bit_num);
+ else
+ *value &= ~BIT(bit_num);
}
/**
- * @mdss_mdp_ctl_perf_taken() - indicates a committed buffer is taken
- * by h/w
- * @ctl - pointer to ctl data structure
- *
- * A committed buffer to be displayed is taken at a vsync or reader
- * pointer interrupt by h/w. This function must be called in vsync
- * interrupt context to indicate the buf status is changed.
- */
-void mdss_mdp_ctl_perf_taken(struct mdss_mdp_ctl *ctl)
-{
- if (!ctl || !ctl->panel_data ||
- (ctl->panel_data->panel_info.type != MIPI_CMD_PANEL))
- return;
-
- spin_lock(&ctl->spin_lock);
- if (ctl->perf_status)
- ctl->perf_status++;
- pr_debug("perf_status=%d\n", ctl->perf_status);
- spin_unlock(&ctl->spin_lock);
-}
-
-/**
- * @mdss_mdp_ctl_perf_done() - indicates a committed buffer is
- * displayed, so resources such as
- * bandwidth that are associated to this
- * buffer can be released.
+ * @ mdss_mdp_ctl_perf_set_transaction_status() -
+ * Set the status of the on-going operations
+ * for the command mode panels.
* @ctl - pointer to a ctl
*
- * When pingping done interrupt is trigged, mdp finishes displaying a
- * buffer which was committed by user and taken by h/w and calling
- * this function to clear those two states. This function must be
- * called in pinppong done interrupt context.
+ * This function is called to set the status bit in the perf_transaction_status
+ * according to the operation that it is on-going for the command mode
+ * panels, where:
+ *
+ * PERF_SW_COMMIT_STATE:
+ * 1 - If SW operation has been commited and bw
+ * has been requested (HW transaction have not started yet).
+ * 0 - If there is no SW operation pending
+ * PERF_HW_MDP_STATE:
+ * 1 - If HW transaction is on-going
+ * 0 - If there is no HW transaction on going (ping-pong interrupt
+ * has finished)
+ * Only if both states are zero there are no pending operations and
+ * BW could be released.
+ * State can be queried calling "mdss_mdp_ctl_perf_get_transaction_status"
*/
-void mdss_mdp_ctl_perf_done(struct mdss_mdp_ctl *ctl)
+void mdss_mdp_ctl_perf_set_transaction_status(struct mdss_mdp_ctl *ctl,
+ enum mdss_mdp_perf_state_type component, bool new_status)
{
+ u32 previous_transaction;
+ bool previous_status;
+ unsigned long flags;
+
if (!ctl || !ctl->panel_data ||
(ctl->panel_data->panel_info.type != MIPI_CMD_PANEL))
return;
- spin_lock(&ctl->spin_lock);
- if (ctl->perf_status) {
- ctl->perf_status--;
- if (ctl->perf_status)
- ctl->perf_status--;
+ spin_lock_irqsave(&ctl->spin_lock, flags);
+
+ previous_transaction = ctl->perf_transaction_status;
+ previous_status = previous_transaction & BIT(component) ?
+ PERF_STATUS_BUSY : PERF_STATUS_DONE;
+
+ /*
+ * If we set "done" state when previous state was not "busy",
+ * we want to print a warning since maybe there is a state
+ * that we are not considering
+ */
+ WARN((PERF_STATUS_DONE == new_status) &&
+ (PERF_STATUS_BUSY != previous_status),
+ "unexpected previous state for component: %d\n", component);
+
+ set_status(&ctl->perf_transaction_status, new_status,
+ (u32)component);
+
+ pr_debug("component:%d previous_transaction:%d transaction_status:%d\n",
+ component, previous_transaction, ctl->perf_transaction_status);
+ pr_debug("new_status:%d prev_status:%d\n",
+ new_status, previous_status);
+
+ spin_unlock_irqrestore(&ctl->spin_lock, flags);
+}
+
+/**
+ * @ mdss_mdp_ctl_perf_get_transaction_status() -
+ * Get the status of the on-going operations
+ * for the command mode panels.
+ * @ctl - pointer to a ctl
+ *
+ * Return:
+ * The status of the transactions for the command mode panels,
+ * note that the bandwidth can be released only if all transaction
+ * status bits are zero.
+ */
+u32 mdss_mdp_ctl_perf_get_transaction_status(struct mdss_mdp_ctl *ctl)
+{
+ unsigned long flags;
+ u32 transaction_status;
+
+ /*
+ * If Video Mode or not valid data to determine the status, return busy
+ * status, so the bandwidth cannot be freed by the caller
+ */
+ if (!ctl || !ctl->panel_data ||
+ (ctl->panel_data->panel_info.type != MIPI_CMD_PANEL)) {
+ return PERF_STATUS_BUSY;
}
- pr_debug("perf_status=%d\n", ctl->perf_status);
- spin_unlock(&ctl->spin_lock);
+
+ spin_lock_irqsave(&ctl->spin_lock, flags);
+ transaction_status = ctl->perf_transaction_status;
+ spin_unlock_irqrestore(&ctl->spin_lock, flags);
+
+ return transaction_status;
}
static inline void mdss_mdp_ctl_perf_update_bus(struct mdss_mdp_ctl *ctl)
@@ -768,7 +831,8 @@
}
}
bus_ib_quota = bw_sum_of_intfs;
- bus_ab_quota = AB_FUDGE_FACTOR(bw_sum_of_intfs);
+ bus_ab_quota = apply_fudge_factor(bw_sum_of_intfs,
+ &mdss_res->ab_factor);
mdss_mdp_bus_scale_set_quota(bus_ab_quota, bus_ib_quota);
pr_debug("ab=%llu ib=%llu\n", bus_ab_quota, bus_ib_quota);
}
@@ -778,13 +842,12 @@
* @ctl - pointer to a ctl
*
* Function checks a state variable for the ctl, if all pending commit
- * requests are done, meanning no more bandwidth is needed, release
+ * requests are done, meaning no more bandwidth is needed, release
* bandwidth request.
*/
void mdss_mdp_ctl_perf_release_bw(struct mdss_mdp_ctl *ctl)
{
- unsigned long flags;
- int need_release = 0;
+ int transaction_status;
struct mdss_data_type *mdata;
int i;
@@ -802,24 +865,45 @@
for (i = 0; i < mdata->nctl; i++) {
struct mdss_mdp_ctl *ctl = mdata->ctl_off + i;
- if (ctl->power_on && ctl->is_video_mode) {
- mutex_unlock(&mdss_mdp_ctl_lock);
- return;
+ if (ctl->power_on && ctl->is_video_mode)
+ goto exit;
+ }
+
+ transaction_status = mdss_mdp_ctl_perf_get_transaction_status(ctl);
+ pr_debug("transaction_status=0x%x\n", transaction_status);
+
+ /*Release the bandwidth only if there are no transactions pending*/
+ if (!transaction_status) {
+ ctl->cur_perf.bw_ctl = 0;
+ ctl->new_perf.bw_ctl = 0;
+ pr_debug("Release BW ctl=%d\n", ctl->num);
+ mdss_mdp_ctl_perf_update_bus(ctl);
+ }
+exit:
+ mutex_unlock(&mdss_mdp_ctl_lock);
+}
+
+static int mdss_mdp_select_clk_lvl(struct mdss_mdp_ctl *ctl,
+ u32 clk_rate)
+{
+ int i;
+ struct mdss_data_type *mdata;
+
+ if (!ctl)
+ return -ENODEV;
+
+ mdata = ctl->mdata;
+
+ for (i = 0; i < mdata->nclk_lvl; i++) {
+ if (clk_rate > mdata->clock_levels[i]) {
+ continue;
+ } else {
+ clk_rate = mdata->clock_levels[i];
+ break;
}
}
- spin_lock_irqsave(&ctl->spin_lock, flags);
- if (!ctl->perf_status)
- need_release = 1;
- pr_debug("need release=%d\n", need_release);
- spin_unlock_irqrestore(&ctl->spin_lock, flags);
-
- if (need_release) {
- ctl->cur_perf.bw_ctl = 0;
- ctl->new_perf.bw_ctl = 0;
- mdss_mdp_ctl_perf_update_bus(ctl);
- }
- mutex_unlock(&mdss_mdp_ctl_lock);
+ return clk_rate;
}
static void mdss_mdp_ctl_perf_update(struct mdss_mdp_ctl *ctl,
@@ -828,6 +912,7 @@
struct mdss_mdp_perf_params *new, *old;
int update_bus = 0, update_clk = 0;
struct mdss_data_type *mdata;
+ bool is_bw_released;
if (!ctl || !ctl->mdata)
return;
@@ -838,8 +923,14 @@
old = &ctl->cur_perf;
new = &ctl->new_perf;
+ /*
+ * We could have released the bandwidth if there were no transactions
+ * pending, so we want to re-calculate the bandwidth in this situation
+ */
+ is_bw_released = !mdss_mdp_ctl_perf_get_transaction_status(ctl);
+
if (ctl->power_on) {
- if (params_changed || mdss_mdp_ctl_perf_bw_released(ctl))
+ if (is_bw_released || params_changed)
mdss_mdp_perf_calc_ctl(ctl, new);
/*
* if params have just changed delay the update until
@@ -882,6 +973,8 @@
clk_rate = max(ctl->cur_perf.mdp_clk_rate,
clk_rate);
}
+
+ clk_rate = mdss_mdp_select_clk_lvl(ctl, clk_rate);
mdss_mdp_set_clk_rate(clk_rate);
pr_debug("update clk rate = %d HZ\n", clk_rate);
}
@@ -1591,11 +1684,13 @@
{
u32 temp;
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
temp = readl_relaxed(ctl->mdata->mdp_base +
MDSS_MDP_REG_DISP_INTF_SEL);
temp |= (ctl->intf_type << ((ctl->intf_num - MDSS_MDP_INTF0) * 8));
writel_relaxed(temp, ctl->mdata->mdp_base +
MDSS_MDP_REG_DISP_INTF_SEL);
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
}
static int mdss_mdp_ctl_start_sub(struct mdss_mdp_ctl *ctl, bool handoff)
@@ -1864,6 +1959,7 @@
int stage, secure = 0;
int screen_state;
int outsize = 0;
+ u32 op_mode;
screen_state = ctl->force_screen_state;
@@ -1997,6 +2093,11 @@
else
ctl->flush_bits |= BIT(6) << mixer->num;
+ op_mode = mdp_mixer_read(mixer, MDSS_MDP_REG_LM_OP_MODE);
+ /* Read GC enable/disable status on LM */
+ op_mode = (op_mode & BIT(0));
+ blend_color_out |= op_mode;
+
mdp_mixer_write(mixer, MDSS_MDP_REG_LM_OP_MODE, blend_color_out);
off = __mdss_mdp_ctl_get_mixer_off(mixer);
mdss_mdp_ctl_write(ctl, off, mixercfg);
@@ -2211,6 +2312,33 @@
return 0;
}
+/**
+ * mdss_mdp_mixer_unstage_all() - Unstage all pipes from mixer
+ * @mixer: Mixer from which to unstage all pipes
+ *
+ * Unstage any pipes that are currently attached to mixer.
+ *
+ * NOTE: this will not update the pipe structure, and thus a full
+ * deinitialization or reconfiguration of all pipes is expected after this call.
+ */
+void mdss_mdp_mixer_unstage_all(struct mdss_mdp_mixer *mixer)
+{
+ struct mdss_mdp_pipe *tmp;
+ int i;
+
+ if (!mixer)
+ return;
+
+ for (i = 0; i < MDSS_MDP_MAX_STAGE; i++) {
+ tmp = mixer->stage_pipe[i];
+ if (tmp) {
+ mixer->stage_pipe[i] = NULL;
+ mixer->params_changed++;
+ tmp->params_changed++;
+ }
+ }
+}
+
int mdss_mdp_mixer_pipe_unstage(struct mdss_mdp_pipe *pipe)
{
struct mdss_mdp_ctl *ctl;
@@ -2391,6 +2519,7 @@
struct mdss_mdp_ctl *sctl = NULL;
int mixer1_changed, mixer2_changed;
int ret = 0;
+ bool is_bw_released;
if (!ctl) {
pr_err("display function not set\n");
@@ -2412,7 +2541,15 @@
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
- if (mixer1_changed || mixer2_changed
+ /*
+ * We could have released the bandwidth if there were no transactions
+ * pending, so we want to re-calculate the bandwidth in this situation
+ */
+ is_bw_released = !mdss_mdp_ctl_perf_get_transaction_status(ctl);
+ mdss_mdp_ctl_perf_set_transaction_status(ctl, PERF_SW_COMMIT_STATE,
+ PERF_STATUS_BUSY);
+
+ if (is_bw_released || mixer1_changed || mixer2_changed
|| ctl->force_screen_state) {
if (ctl->prepare_fnc)
ret = ctl->prepare_fnc(ctl, arg);
@@ -2438,11 +2575,15 @@
}
}
- mdss_mdp_ctl_notify(ctl, MDP_NOTIFY_FRAME_READY);
+ if (!ctl->shared_lock)
+ mdss_mdp_ctl_notify(ctl, MDP_NOTIFY_FRAME_READY);
if (ctl->wait_pingpong)
ctl->wait_pingpong(ctl, NULL);
+ ctl->roi_bkup.w = ctl->roi.w;
+ ctl->roi_bkup.h = ctl->roi.h;
+
if (ctl->mfd && ctl->mfd->dcm_state != DTM_ENTER)
/* postprocessing setup, including dspp */
mdss_mdp_pp_setup_locked(ctl);
@@ -2455,6 +2596,8 @@
wmb();
ctl->flush_bits = 0;
+ mdss_mdp_xlog_mixer_reg(ctl);
+
if (ctl->display_fnc)
ret = ctl->display_fnc(ctl, arg); /* kickoff */
if (ret)
@@ -2620,3 +2763,18 @@
return rc;
}
+
+static void mdss_mdp_xlog_mixer_reg(struct mdss_mdp_ctl *ctl)
+{
+ int i, off;
+ u32 data[MDSS_MDP_INTF_MAX_LAYERMIXER];
+
+ for (i = 0; i < MDSS_MDP_INTF_MAX_LAYERMIXER; i++) {
+ off = MDSS_MDP_REG_CTL_LAYER(i);
+ data[i] = mdss_mdp_ctl_read(ctl, off);
+ }
+ MDSS_XLOG(data[MDSS_MDP_INTF_LAYERMIXER0],
+ data[MDSS_MDP_INTF_LAYERMIXER1],
+ data[MDSS_MDP_INTF_LAYERMIXER2],
+ data[MDSS_MDP_INTF_LAYERMIXER3], off);
+}
diff --git a/drivers/video/msm/mdss/mdss_mdp_hwio.h b/drivers/video/msm/mdss/mdss_mdp_hwio.h
index bff56d2..c11b438 100644
--- a/drivers/video/msm/mdss/mdss_mdp_hwio.h
+++ b/drivers/video/msm/mdss/mdss_mdp_hwio.h
@@ -21,10 +21,6 @@
#define ENHIST_LUT_ENTRIES 256
#define HIST_V_SIZE 256
-#define MDSS_MDP_HW_REV_100 0x10000000
-#define MDSS_MDP_HW_REV_102 0x10020000
-#define MDSS_MDP_HW_REV_103 0x10030000
-
#define MDSS_MDP_FETCH_CONFIG_RESET_VALUE 0x00000087
#define MDSS_REG_HW_VERSION 0x0
@@ -113,7 +109,8 @@
#define MDSS_MDP_REG_CTL_OFFSET(ctl) (0x00600 + ((ctl) * \
MDSS_MDP_CTL_ADDRESS_OFFSET))
-#define MDSS_MDP_REG_CTL_LAYER(lm) ((lm) * 0x004)
+#define MDSS_MDP_REG_CTL_LAYER(lm) \
+ ((lm == 5) ? (0x024) : ((lm) * 0x004))
#define MDSS_MDP_REG_CTL_TOP 0x014
#define MDSS_MDP_REG_CTL_FLUSH 0x018
#define MDSS_MDP_REG_CTL_START 0x01C
diff --git a/drivers/video/msm/mdss/mdss_mdp_intf_cmd.c b/drivers/video/msm/mdss/mdss_mdp_intf_cmd.c
index 78ecf16..293b192 100644
--- a/drivers/video/msm/mdss/mdss_mdp_intf_cmd.c
+++ b/drivers/video/msm/mdss/mdss_mdp_intf_cmd.c
@@ -15,18 +15,16 @@
#include "mdss_mdp.h"
#include "mdss_panel.h"
+#include "mdss_debug.h"
#define VSYNC_EXPIRE_TICK 4
-#define START_THRESHOLD 4
-#define CONTINUE_THRESHOLD 4
-
#define MAX_SESSIONS 2
/* wait for at most 2 vsync for lowest refresh rate (24hz) */
#define KOFF_TIMEOUT msecs_to_jiffies(84)
-#define STOP_TIMEOUT msecs_to_jiffies(16 * (VSYNC_EXPIRE_TICK + 2))
+#define STOP_TIMEOUT(hz) msecs_to_jiffies((1000 / hz) * (VSYNC_EXPIRE_TICK + 2))
#define ULPS_ENTER_TIME msecs_to_jiffies(100)
struct mdss_mdp_cmd_ctx {
@@ -104,119 +102,117 @@
return cnt;
}
-/*
- * TE configuration:
- * dsi byte clock calculated base on 70 fps
- * around 14 ms to complete a kickoff cycle if te disabled
- * vclk_line base on 60 fps
- * write is faster than read
- * init == start == rdptr
- */
-static int mdss_mdp_cmd_tearcheck_cfg(struct mdss_mdp_mixer *mixer,
- struct mdss_mdp_cmd_ctx *ctx, int enable)
-{
- u32 cfg;
- cfg = BIT(19); /* VSYNC_COUNTER_EN */
- if (ctx->tear_check)
- cfg |= BIT(20); /* VSYNC_IN_EN */
- cfg |= ctx->vclk_line;
+static int mdss_mdp_cmd_tearcheck_cfg(struct mdss_mdp_ctl *ctl,
+ struct mdss_mdp_mixer *mixer)
+{
+ struct mdss_mdp_pp_tear_check *te;
+ struct mdss_panel_info *pinfo;
+ u32 vsync_clk_speed_hz, total_lines, vclks_line, cfg;
+
+ if (IS_ERR_OR_NULL(ctl->panel_data)) {
+ pr_err("no panel data\n");
+ return -ENODEV;
+ }
+
+ pinfo = &ctl->panel_data->panel_info;
+ te = &ctl->panel_data->panel_info.te;
+
+ mdss_mdp_vsync_clk_enable(1);
+
+ vsync_clk_speed_hz =
+ mdss_mdp_get_clk_rate(MDSS_CLK_MDP_VSYNC);
+
+ total_lines = mdss_panel_get_vtotal(pinfo);
+
+ total_lines *= pinfo->mipi.frame_rate;
+
+ vclks_line = (total_lines) ? vsync_clk_speed_hz / total_lines : 0;
+
+ cfg = BIT(19);
+ if (pinfo->mipi.hw_vsync_mode)
+ cfg |= BIT(20);
+
+ if (te->refx100)
+ vclks_line = vclks_line * pinfo->mipi.frame_rate *
+ 100 / te->refx100;
+ else {
+ pr_warn("refx100 cannot be zero! Use 6000 as default\n");
+ vclks_line = vclks_line * pinfo->mipi.frame_rate *
+ 100 / 6000;
+ }
+
+ cfg |= vclks_line;
+
+ pr_debug("%s: yres=%d vclks=%x height=%d init=%d rd=%d start=%d ",
+ __func__, pinfo->yres, vclks_line, te->sync_cfg_height,
+ te->vsync_init_val, te->rd_ptr_irq, te->start_pos);
+ pr_debug("thrd_start =%d thrd_cont=%d\n",
+ te->sync_threshold_start, te->sync_threshold_continue);
mdss_mdp_pingpong_write(mixer, MDSS_MDP_REG_PP_SYNC_CONFIG_VSYNC, cfg);
mdss_mdp_pingpong_write(mixer, MDSS_MDP_REG_PP_SYNC_CONFIG_HEIGHT,
- 0xfff0); /* set to verh height */
-
+ te->sync_cfg_height);
mdss_mdp_pingpong_write(mixer, MDSS_MDP_REG_PP_VSYNC_INIT_VAL,
- ctx->height);
-
+ te->vsync_init_val);
mdss_mdp_pingpong_write(mixer, MDSS_MDP_REG_PP_RD_PTR_IRQ,
- ctx->height + 1);
-
+ te->rd_ptr_irq);
mdss_mdp_pingpong_write(mixer, MDSS_MDP_REG_PP_START_POS,
- ctx->height);
-
+ te->start_pos);
mdss_mdp_pingpong_write(mixer, MDSS_MDP_REG_PP_SYNC_THRESH,
- (CONTINUE_THRESHOLD << 16) | (ctx->start_threshold));
-
- mdss_mdp_pingpong_write(mixer, MDSS_MDP_REG_PP_TEAR_CHECK_EN, enable);
+ ((te->sync_threshold_continue << 16) |
+ te->sync_threshold_start));
+ mdss_mdp_pingpong_write(mixer, MDSS_MDP_REG_PP_TEAR_CHECK_EN,
+ te->tear_check_en);
return 0;
}
-static int mdss_mdp_cmd_tearcheck_setup(struct mdss_mdp_ctl *ctl, int enable)
+static int mdss_mdp_cmd_tearcheck_setup(struct mdss_mdp_ctl *ctl)
{
- struct mdss_mdp_cmd_ctx *ctx = ctl->priv_data;
- struct mdss_panel_info *pinfo;
struct mdss_mdp_mixer *mixer;
-
- pinfo = &ctl->panel_data->panel_info;
-
- if (pinfo->mipi.vsync_enable && enable) {
- u32 mdp_vsync_clk_speed_hz, total_lines;
-
- mdss_mdp_vsync_clk_enable(1);
-
- mdp_vsync_clk_speed_hz =
- mdss_mdp_get_clk_rate(MDSS_CLK_MDP_VSYNC);
- pr_debug("%s: vsync_clk_rate=%d\n", __func__,
- mdp_vsync_clk_speed_hz);
-
- if (mdp_vsync_clk_speed_hz == 0) {
- pr_err("can't get clk speed\n");
- return -EINVAL;
- }
-
- ctx->tear_check = pinfo->mipi.hw_vsync_mode;
- ctx->height = pinfo->yres;
- ctx->vporch = pinfo->lcdc.v_back_porch +
- pinfo->lcdc.v_front_porch +
- pinfo->lcdc.v_pulse_width;
-
- ctx->start_threshold = START_THRESHOLD;
-
- total_lines = ctx->height + ctx->vporch;
- total_lines *= pinfo->mipi.frame_rate;
- ctx->vclk_line = mdp_vsync_clk_speed_hz / total_lines;
-
- pr_debug("%s: fr=%d tline=%d vcnt=%d thold=%d vrate=%d\n",
- __func__, pinfo->mipi.frame_rate, total_lines,
- ctx->vclk_line, ctx->start_threshold,
- mdp_vsync_clk_speed_hz);
- } else {
- enable = 0;
- }
-
+ int rc = 0;
mixer = mdss_mdp_mixer_get(ctl, MDSS_MDP_MIXER_MUX_LEFT);
- if (mixer)
- mdss_mdp_cmd_tearcheck_cfg(mixer, ctx, enable);
-
+ if (mixer) {
+ rc = mdss_mdp_cmd_tearcheck_cfg(ctl, mixer);
+ if (rc)
+ goto err;
+ }
mixer = mdss_mdp_mixer_get(ctl, MDSS_MDP_MIXER_MUX_RIGHT);
if (mixer)
- mdss_mdp_cmd_tearcheck_cfg(mixer, ctx, enable);
-
- return 0;
+ rc = mdss_mdp_cmd_tearcheck_cfg(ctl, mixer);
+ err:
+ return rc;
}
static inline void mdss_mdp_cmd_clk_on(struct mdss_mdp_cmd_ctx *ctx)
{
unsigned long flags;
struct mdss_data_type *mdata = mdss_mdp_get_mdata();
+
+ if (!ctx->panel_on)
+ return;
+
mutex_lock(&ctx->clk_mtx);
+ MDSS_XLOG(ctx->pp_num, ctx->koff_cnt, ctx->clk_enabled,
+ ctx->rdptr_enabled);
if (!ctx->clk_enabled) {
ctx->clk_enabled = 1;
if (cancel_delayed_work_sync(&ctx->ulps_work))
pr_debug("deleted pending ulps work\n");
- mdss_mdp_ctl_intf_event
- (ctx->ctl, MDSS_EVENT_PANEL_CLK_CTRL, (void *)1);
+
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
if (ctx->ulps) {
- if (mdss_mdp_cmd_tearcheck_setup(ctx->ctl, 1))
+ if (mdss_mdp_cmd_tearcheck_setup(ctx->ctl))
pr_warn("tearcheck setup failed\n");
mdss_mdp_ctl_intf_event(ctx->ctl,
MDSS_EVENT_DSI_ULPS_CTRL, (void *)0);
ctx->ulps = false;
}
- mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
+ mdss_mdp_ctl_intf_event
+ (ctx->ctl, MDSS_EVENT_PANEL_CLK_CTRL, (void *)1);
+
mdss_mdp_hist_intr_setup(&mdata->hist_intr, MDSS_IRQ_RESUME);
}
spin_lock_irqsave(&ctx->clk_lock, flags);
@@ -234,6 +230,8 @@
int set_clk_off = 0;
mutex_lock(&ctx->clk_mtx);
+ MDSS_XLOG(ctx->pp_num, ctx->koff_cnt, ctx->clk_enabled,
+ ctx->rdptr_enabled);
spin_lock_irqsave(&ctx->clk_lock, flags);
if (!ctx->rdptr_enabled)
set_clk_off = 1;
@@ -263,10 +261,10 @@
return;
}
- mdss_mdp_ctl_perf_taken(ctl);
-
vsync_time = ktime_get();
ctl->vsync_cnt++;
+ MDSS_XLOG(ctl->num, ctx->koff_cnt, ctx->clk_enabled,
+ ctx->rdptr_enabled);
spin_lock(&ctx->clk_lock);
list_for_each_entry(tmp, &ctx->vsync_handlers, list) {
@@ -277,6 +275,10 @@
if (!ctx->vsync_enabled) {
if (ctx->rdptr_enabled)
ctx->rdptr_enabled--;
+
+ /* keep clk on during kickoff */
+ if (ctx->rdptr_enabled == 0 && ctx->koff_cnt)
+ ctx->rdptr_enabled++;
}
if (ctx->rdptr_enabled == 0) {
@@ -326,7 +328,8 @@
return;
}
- mdss_mdp_ctl_perf_done(ctl);
+ mdss_mdp_ctl_perf_set_transaction_status(ctl,
+ PERF_HW_MDP_STATE, PERF_STATUS_DONE);
spin_lock(&ctx->clk_lock);
list_for_each_entry(tmp, &ctx->vsync_handlers, list) {
@@ -336,6 +339,8 @@
mdss_mdp_irq_disable_nosync(MDSS_MDP_IRQ_PING_PONG_COMP, ctx->pp_num);
complete_all(&ctx->pp_comp);
+ MDSS_XLOG(ctl->num, ctx->koff_cnt, ctx->clk_enabled,
+ ctx->rdptr_enabled);
if (ctx->koff_cnt) {
atomic_inc(&ctx->pp_done_cnt);
@@ -426,6 +431,9 @@
return -ENODEV;
}
+ MDSS_XLOG(ctl->num, ctx->koff_cnt, ctx->clk_enabled,
+ ctx->rdptr_enabled);
+
spin_lock_irqsave(&ctx->clk_lock, flags);
if (!handle->enabled) {
handle->enabled = true;
@@ -455,6 +463,9 @@
return -ENODEV;
}
+ MDSS_XLOG(ctl->num, ctx->koff_cnt, ctx->clk_enabled,
+ ctx->rdptr_enabled, 0x88888);
+
spin_lock_irqsave(&ctx->clk_lock, flags);
if (handle->enabled) {
handle->enabled = false;
@@ -488,6 +499,7 @@
static int mdss_mdp_cmd_wait4pingpong(struct mdss_mdp_ctl *ctl, void *arg)
{
struct mdss_mdp_cmd_ctx *ctx;
+ struct mdss_panel_data *pdata;
unsigned long flags;
int need_wait = 0;
int rc = 0;
@@ -498,11 +510,20 @@
return -ENODEV;
}
+ pdata = ctl->panel_data;
+
spin_lock_irqsave(&ctx->clk_lock, flags);
if (ctx->koff_cnt > 0)
need_wait = 1;
spin_unlock_irqrestore(&ctx->clk_lock, flags);
+ ctl->roi_bkup.w = ctl->width;
+ ctl->roi_bkup.h = ctl->height;
+
+ MDSS_XLOG(ctl->num, ctx->koff_cnt, ctx->clk_enabled,
+ ctx->rdptr_enabled, ctl->roi_bkup.w,
+ ctl->roi_bkup.h);
+
pr_debug("%s: need_wait=%d intf_num=%d ctx=%p\n",
__func__, need_wait, ctl->intf_num, ctx);
@@ -513,6 +534,9 @@
if (rc <= 0) {
WARN(1, "cmd kickoff timed out (%d) ctl=%d\n",
rc, ctl->num);
+ mdss_dsi_debug_check_te(pdata);
+ MDSS_XLOG_TOUT_HANDLER("mdp", "dsi0", "dsi1",
+ "edp", "hdmi", "panic");
rc = -EPERM;
mdss_mdp_ctl_notify(ctl, MDP_NOTIFY_FRAME_TIMEOUT);
} else {
@@ -520,6 +544,8 @@
}
}
+ MDSS_XLOG(ctl->num, ctx->koff_cnt, ctx->clk_enabled,
+ ctx->rdptr_enabled, rc);
return rc;
}
@@ -551,6 +577,9 @@
return -ENODEV;
}
+ mdss_mdp_ctl_perf_set_transaction_status(ctl,
+ PERF_HW_MDP_STATE, PERF_STATUS_BUSY);
+
if (ctx->panel_on == 0) {
rc = mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_UNBLANK, NULL);
WARN(rc, "intf %d unblank error (%d)\n", ctl->intf_num, rc);
@@ -561,23 +590,30 @@
WARN(rc, "intf %d panel on error (%d)\n", ctl->intf_num, rc);
}
- mdss_mdp_cmd_set_partial_roi(ctl);
+ MDSS_XLOG(ctl->num, ctl->roi.x, ctl->roi.y, ctl->roi.w,
+ ctl->roi.h);
+
+ spin_lock_irqsave(&ctx->clk_lock, flags);
+ ctx->koff_cnt++;
+ spin_unlock_irqrestore(&ctx->clk_lock, flags);
mdss_mdp_cmd_clk_on(ctx);
+ mdss_mdp_cmd_set_partial_roi(ctl);
+
/*
* tx dcs command if had any
*/
mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_DSI_CMDLIST_KOFF,
(void *)&ctx->recovery);
-
INIT_COMPLETION(ctx->pp_comp);
mdss_mdp_irq_enable(MDSS_MDP_IRQ_PING_PONG_COMP, ctx->pp_num);
mdss_mdp_ctl_write(ctl, MDSS_MDP_REG_CTL_START, 1);
- spin_lock_irqsave(&ctx->clk_lock, flags);
- ctx->koff_cnt++;
- spin_unlock_irqrestore(&ctx->clk_lock, flags);
+ mdss_mdp_ctl_perf_set_transaction_status(ctl,
+ PERF_SW_COMMIT_STATE, PERF_STATUS_DONE);
mb();
+ MDSS_XLOG(ctl->num, ctx->koff_cnt, ctx->clk_enabled,
+ ctx->rdptr_enabled);
return 0;
}
@@ -590,6 +626,7 @@
struct mdss_mdp_vsync_handler *tmp, *handle;
int need_wait = 0;
int ret = 0;
+ int hz;
ctx = (struct mdss_mdp_cmd_ctx *) ctl->priv_data;
if (!ctx) {
@@ -599,6 +636,8 @@
list_for_each_entry_safe(handle, tmp, &ctx->vsync_handlers, list)
mdss_mdp_cmd_remove_vsync_handler(ctl, handle);
+ MDSS_XLOG(ctl->num, ctx->koff_cnt, ctx->clk_enabled,
+ ctx->rdptr_enabled, XLOG_FUNC_ENTRY);
spin_lock_irqsave(&ctx->clk_lock, flags);
if (ctx->rdptr_enabled) {
@@ -607,8 +646,11 @@
}
spin_unlock_irqrestore(&ctx->clk_lock, flags);
+ hz = mdss_panel_get_framerate(&ctl->panel_data->panel_info);
+
if (need_wait)
- if (wait_for_completion_timeout(&ctx->stop_comp, STOP_TIMEOUT)
+ if (wait_for_completion_timeout(&ctx->stop_comp,
+ STOP_TIMEOUT(hz))
<= 0) {
WARN(1, "stop cmd time out\n");
@@ -658,6 +700,8 @@
ctl->add_vsync_handler = NULL;
ctl->remove_vsync_handler = NULL;
+ MDSS_XLOG(ctl->num, ctx->koff_cnt, ctx->clk_enabled,
+ ctx->rdptr_enabled, XLOG_FUNC_EXIT);
pr_debug("%s:-\n", __func__);
return 0;
@@ -712,6 +756,8 @@
pr_debug("%s: ctx=%p num=%d mixer=%d\n", __func__,
ctx, ctx->pp_num, mixer->num);
+ MDSS_XLOG(ctl->num, ctx->koff_cnt, ctx->clk_enabled,
+ ctx->rdptr_enabled);
mdss_mdp_set_intr_callback(MDSS_MDP_IRQ_PING_PONG_RD_PTR, ctx->pp_num,
mdss_mdp_cmd_readptr_done, ctl);
@@ -719,7 +765,8 @@
mdss_mdp_set_intr_callback(MDSS_MDP_IRQ_PING_PONG_COMP, ctx->pp_num,
mdss_mdp_cmd_pingpong_done, ctl);
- ret = mdss_mdp_cmd_tearcheck_setup(ctl, 1);
+ ret = mdss_mdp_cmd_tearcheck_setup(ctl);
+
if (ret) {
pr_err("tearcheck setup failed\n");
return ret;
diff --git a/drivers/video/msm/mdss/mdss_mdp_intf_video.c b/drivers/video/msm/mdss/mdss_mdp_intf_video.c
index f8bdc04..b07aab4 100644
--- a/drivers/video/msm/mdss/mdss_mdp_intf_video.c
+++ b/drivers/video/msm/mdss/mdss_mdp_intf_video.c
@@ -16,12 +16,12 @@
#include <linux/iopoll.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
-#include <linux/bootmem.h>
#include <linux/memblock.h>
#include "mdss_fb.h"
#include "mdss_mdp.h"
#include "mdss_panel.h"
+#include "mdss_debug.h"
/* wait for at least 2 vsyncs for lowest refresh rate (24hz) */
#define VSYNC_TIMEOUT_US 100000
@@ -256,6 +256,8 @@
goto exit;
}
+ MDSS_XLOG(ctl->num, ctl->vsync_cnt, handle->enabled);
+
spin_lock_irqsave(&ctx->vsync_lock, flags);
if (!handle->enabled) {
handle->enabled = true;
@@ -282,6 +284,8 @@
return -ENODEV;
}
+ MDSS_XLOG(ctl->num, ctl->vsync_cnt, handle->enabled);
+
spin_lock_irqsave(&ctx->vsync_lock, flags);
if (handle->enabled) {
handle->enabled = false;
@@ -309,7 +313,7 @@
pr_err("invalid ctx for ctl=%d\n", ctl->num);
return -ENODEV;
}
-
+ MDSS_XLOG(ctl->num, ctl->vsync_cnt);
if (ctx->timegen_en) {
rc = mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_BLANK, NULL);
if (rc == -EBUSY) {
@@ -372,6 +376,8 @@
vsync_time = ktime_get();
ctl->vsync_cnt++;
+ MDSS_XLOG(ctl->num, ctl->vsync_cnt, ctl->vsync_cnt);
+
pr_debug("intr ctl=%d vsync cnt=%u vsync_time=%d\n",
ctl->num, ctl->vsync_cnt, (int)ktime_to_ms(vsync_time));
@@ -401,6 +407,7 @@
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
if (rc == 0) {
+ MDSS_XLOG(ctl->num, ctl->vsync_cnt);
pr_debug("vsync poll successful! rc=%d status=0x%x\n",
rc, status);
ctx->poll_cnt++;
@@ -438,8 +445,10 @@
if (ctx->polling_en) {
rc = mdss_mdp_video_pollwait(ctl);
} else {
+ mutex_unlock(&ctl->lock);
rc = wait_for_completion_timeout(&ctx->vsync_comp,
usecs_to_jiffies(VSYNC_TIMEOUT_US));
+ mutex_lock(&ctl->lock);
if (rc == 0) {
pr_warn("vsync wait timeout %d, fallback to poll mode\n",
ctl->num);
@@ -460,6 +469,21 @@
return rc;
}
+static void recover_underrun_work(struct work_struct *work)
+{
+ struct mdss_mdp_ctl *ctl =
+ container_of(work, typeof(*ctl), recover_work);
+
+ if (!ctl || !ctl->add_vsync_handler) {
+ pr_err("ctl or vsync handler is NULL\n");
+ return;
+ }
+
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
+ ctl->add_vsync_handler(ctl, &ctl->recover_underrun_handler);
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
+}
+
static void mdss_mdp_video_underrun_intr_done(void *arg)
{
struct mdss_mdp_ctl *ctl = arg;
@@ -467,8 +491,13 @@
return;
ctl->underrun_cnt++;
+ MDSS_XLOG(ctl->num, ctl->underrun_cnt);
+ MDSS_XLOG_TOUT_HANDLER("mdp", "dsi0", "dsi1", "edp", "hdmi", "panic");
pr_debug("display underrun detected for ctl=%d count=%d\n", ctl->num,
ctl->underrun_cnt);
+
+ if (ctl->opmode & MDSS_MDP_CTL_OP_PACK_3D_ENABLE)
+ schedule_work(&ctl->recover_work);
}
static int mdss_mdp_video_vfp_fps_update(struct mdss_mdp_ctl *ctl, int new_fps)
@@ -640,9 +669,6 @@
return rc;
}
-
-
-
static int mdss_mdp_video_display(struct mdss_mdp_ctl *ctl, void *arg)
{
struct mdss_mdp_video_ctx *ctx;
@@ -665,6 +691,8 @@
WARN(1, "commit without wait! ctl=%d", ctl->num);
}
+ MDSS_XLOG(ctl->num, ctl->underrun_cnt);
+
if (!ctx->timegen_en) {
rc = mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_UNBLANK, NULL);
if (rc) {
@@ -706,7 +734,6 @@
{
struct mdss_panel_data *pdata = ctl->panel_data;
int i, ret = 0;
- struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(ctl->mfd);
struct mdss_mdp_video_ctx *ctx;
struct mdss_data_type *mdata = ctl->mdata;
@@ -741,12 +768,6 @@
error:
pdata->panel_info.cont_splash_enabled = 0;
-
- /* Give back the reserved memory to the system */
- memblock_free(mdp5_data->splash_mem_addr, mdp5_data->splash_mem_size);
- free_bootmem_late(mdp5_data->splash_mem_addr,
- mdp5_data->splash_mem_size);
-
return ret;
}
@@ -783,6 +804,7 @@
return -EINVAL;
}
+ MDSS_XLOG(ctl->num, ctl->vsync_cnt);
pr_debug("start ctl=%u\n", ctl->num);
ctl->priv_data = ctx;
@@ -791,6 +813,7 @@
spin_lock_init(&ctx->vsync_lock);
mutex_init(&ctx->vsync_mtx);
atomic_set(&ctx->vsync_ref, 0);
+ INIT_WORK(&ctl->recover_work, recover_underrun_work);
mdss_mdp_set_intr_callback(MDSS_MDP_IRQ_INTF_VSYNC, ctl->intf_num,
mdss_mdp_video_vsync_intr_done, ctl);
diff --git a/drivers/video/msm/mdss/mdss_mdp_overlay.c b/drivers/video/msm/mdss/mdss_mdp_overlay.c
index 34cfe23..057483c 100644
--- a/drivers/video/msm/mdss/mdss_mdp_overlay.c
+++ b/drivers/video/msm/mdss/mdss_mdp_overlay.c
@@ -35,8 +35,6 @@
#include "mdss_mdp.h"
#include "mdss_mdp_rotator.h"
-#include "splash.h"
-
#define VSYNC_PERIOD 16
#define BORDERFILL_NDX 0x0BF000BF
#define CHECK_BOUNDS(offset, size, max_size) \
@@ -47,7 +45,7 @@
#define MEM_PROTECT_SD_CTRL 0xF
-#define INVALID_PIPE_INDEX 0xFFFF
+#define OVERLAY_MAX 10
struct sd_ctrl_req {
unsigned int enable;
@@ -57,7 +55,7 @@
static int mdss_mdp_overlay_free_fb_pipe(struct msm_fb_data_type *mfd);
static int mdss_mdp_overlay_fb_parse_dt(struct msm_fb_data_type *mfd);
static int mdss_mdp_overlay_off(struct msm_fb_data_type *mfd);
-static int mdss_mdp_overlay_splash_parse_dt(struct msm_fb_data_type *mfd);
+static void __overlay_kickoff_requeue(struct msm_fb_data_type *mfd);
static int mdss_mdp_overlay_sd_ctrl(struct msm_fb_data_type *mfd,
unsigned int enable)
@@ -268,7 +266,7 @@
int rc;
for (;;) {
- rc = mdss_mdp_perf_calc_pipe(pipe, &perf, NULL);
+ rc = mdss_mdp_perf_calc_pipe(pipe, &perf, NULL, true);
if (!rc && (perf.mdp_clk_rate <= mdata->max_mdp_clk_rate))
break;
@@ -321,10 +319,6 @@
rc, src, pipe->dst.h);
return rc;
}
- pipe->scale.init_phase_x[0] = (pipe->scale.phase_step_x[0] -
- (1 << PHASE_STEP_SHIFT)) / 2;
- pipe->scale.init_phase_y[0] = (pipe->scale.phase_step_y[0] -
- (1 << PHASE_STEP_SHIFT)) / 2;
return rc;
}
@@ -351,7 +345,7 @@
pipe->chroma_sample_v = 0;
}
-static int mdss_mdp_overlay_pipe_setup(struct msm_fb_data_type *mfd,
+int mdss_mdp_overlay_pipe_setup(struct msm_fb_data_type *mfd,
struct mdp_overlay *req,
struct mdss_mdp_pipe **ppipe)
{
@@ -767,20 +761,41 @@
{
struct mdss_mdp_pipe *pipe, *tmp;
struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
+ struct mdss_mdp_ctl *ctl = mfd_to_ctl(mfd);
+ bool recovery_mode = false;
LIST_HEAD(destroy_pipes);
mutex_lock(&mfd->lock);
- __mdss_mdp_overlay_free_list_purge(mfd);
-
list_for_each_entry_safe(pipe, tmp, &mdp5_data->pipes_cleanup,
cleanup_list) {
list_move(&pipe->cleanup_list, &destroy_pipes);
- mdss_mdp_pipe_fetch_halt(pipe);
- mdss_mdp_overlay_free_buf(&pipe->back_buf);
- __mdss_mdp_overlay_free_list_add(mfd, &pipe->front_buf);
- pipe->mfd = NULL;
+
+ /* make sure pipe fetch has been halted before freeing buffer */
+ if (mdss_mdp_pipe_fetch_halt(pipe)) {
+ /*
+ * if pipe is not able to halt. Enter recovery mode,
+ * by un-staging any pipes that are attached to mixer
+ * so that any freed pipes that are not able to halt
+ * can be staged in solid fill mode and be reset
+ * with next vsync
+ */
+ if (!recovery_mode) {
+ recovery_mode = true;
+ mdss_mdp_mixer_unstage_all(ctl->mixer_left);
+ mdss_mdp_mixer_unstage_all(ctl->mixer_right);
+ }
+ pipe->params_changed++;
+ mdss_mdp_pipe_queue_data(pipe, NULL);
+ }
}
+ if (recovery_mode) {
+ pr_warn("performing recovery sequence for fb%d\n", mfd->index);
+ __overlay_kickoff_requeue(mfd);
+ }
+
+ __mdss_mdp_overlay_free_list_purge(mfd);
+
list_for_each_entry(pipe, &mdp5_data->pipes_used, used_list) {
if (pipe->back_buf.num_planes) {
/* make back buffer active */
@@ -788,12 +803,23 @@
swap(pipe->back_buf, pipe->front_buf);
}
}
- mutex_unlock(&mfd->lock);
- list_for_each_entry_safe(pipe, tmp, &destroy_pipes, cleanup_list)
+
+ list_for_each_entry_safe(pipe, tmp, &destroy_pipes, cleanup_list) {
+ /*
+ * in case of secure UI, the buffer needs to be released as
+ * soon as session is closed.
+ */
+ if (pipe->flags & MDP_SECURE_DISPLAY_OVERLAY_SESSION)
+ mdss_mdp_overlay_free_buf(&pipe->front_buf);
+ else
+ __mdss_mdp_overlay_free_list_add(mfd, &pipe->front_buf);
+ mdss_mdp_overlay_free_buf(&pipe->back_buf);
mdss_mdp_pipe_destroy(pipe);
+ }
+ mutex_unlock(&mfd->lock);
}
-static void __mdss_mdp_handoff_cleanup_pipes(struct msm_fb_data_type *mfd,
+void mdss_mdp_handoff_cleanup_pipes(struct msm_fb_data_type *mfd,
u32 type)
{
u32 i, npipes;
@@ -840,7 +866,7 @@
* from the the splash screen to the android boot animation when the
* continuous splash screen feature is enabled.
*/
-static int mdss_mdp_overlay_start(struct msm_fb_data_type *mfd)
+int mdss_mdp_overlay_start(struct msm_fb_data_type *mfd)
{
int rc;
struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
@@ -856,6 +882,7 @@
mdss_mdp_batfet_ctrl(mdp5_data->mdata, true);
if (!mfd->panel_info->cont_splash_enabled)
mdss_iommu_attach(mdp5_data->mdata);
+ mdss_mdp_release_splash_pipe(mfd);
return 0;
}
@@ -892,55 +919,7 @@
goto error;
}
- if (mfd->panel_info->cont_splash_enabled) {
- if (mdp5_data->handoff) {
- /*
- * Set up border-fill on the handed off pipes.
- * This is needed to ensure that there are no memory
- * accesses prior to attaching iommu during continuous
- * splash screen case. However, for command mode
- * displays, this is not necessary since the panels can
- * refresh from their internal memory if no data is sent
- * out on the dsi lanes.
- */
- if (ctl && ctl->is_video_mode) {
- rc = mdss_mdp_display_commit(ctl, NULL);
- if (!IS_ERR_VALUE(rc)) {
- mdss_mdp_display_wait4comp(ctl);
- } else {
- /*
- * Since border-fill setup failed, we
- * need to ensure that we turn off the
- * MDP timing generator before attaching
- * iommu
- */
- pr_err("failed to set BF at handoff\n");
- mdp5_data->handoff = false;
- rc = 0;
- }
- }
-
- /* Add all the handed off pipes to the cleanup list */
- __mdss_mdp_handoff_cleanup_pipes(mfd,
- MDSS_MDP_PIPE_TYPE_RGB);
- __mdss_mdp_handoff_cleanup_pipes(mfd,
- MDSS_MDP_PIPE_TYPE_VIG);
- __mdss_mdp_handoff_cleanup_pipes(mfd,
- MDSS_MDP_PIPE_TYPE_DMA);
- }
- rc = mdss_mdp_ctl_splash_finish(ctl, mdp5_data->handoff);
- /*
- * Remove the vote for footswitch even if above function
- * returned error
- */
- mdss_mdp_footswitch_ctrl_splash(0);
- if (rc)
- goto error;
-
- if (!is_mdss_iommu_attached())
- mdss_iommu_attach(mdss_res);
- }
-
+ rc = mdss_mdp_splash_cleanup(mfd, true);
error:
if (rc) {
mdss_mdp_ctl_destroy(ctl);
@@ -964,51 +943,22 @@
activate_event_timer(mdp5_data->cpu_pm_hdl, wakeup_time);
}
-int mdss_mdp_overlay_kickoff(struct msm_fb_data_type *mfd,
- struct mdp_display_commit *data)
+static int __overlay_queue_pipes(struct msm_fb_data_type *mfd)
{
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);
struct mdss_mdp_ctl *tmp;
int ret = 0;
- int sd_in_pipe = 0;
-
- if (!ctl) {
- pr_warn("kickoff on fb=%d without a ctl attched\n", mfd->index);
- return ret;
- }
-
- if (ctl->shared_lock)
- mutex_lock(ctl->shared_lock);
-
- mutex_lock(&mdp5_data->ov_lock);
- mutex_lock(&mfd->lock);
/*
- * check if there is a secure display session
+ * Setup pipe in solid fill before unstaging,
+ * to ensure no fetches are happening after dettach or reattach.
*/
- list_for_each_entry(pipe, &mdp5_data->pipes_used, used_list) {
- if (pipe->flags & MDP_SECURE_DISPLAY_OVERLAY_SESSION) {
- sd_in_pipe |= 1;
- pr_debug("Secure pipe: %u : %08X\n",
- pipe->num, pipe->flags);
- }
+ list_for_each_entry(pipe, &mdp5_data->pipes_cleanup, cleanup_list) {
+ mdss_mdp_pipe_queue_data(pipe, NULL);
+ mdss_mdp_mixer_pipe_unstage(pipe);
}
- /*
- * If there is no secure display session and sd_enabled, disable the
- * secure display session
- */
- if (!sd_in_pipe && mdp5_data->sd_enabled) {
- if (0 == mdss_mdp_overlay_sd_ctrl(mfd, 0))
- mdp5_data->sd_enabled = 0;
- }
-
- mdss_mdp_ctl_notify(ctl, MDP_NOTIFY_FRAME_BEGIN);
- mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
-
- if (data)
- mdss_mdp_set_roi(ctl, data);
list_for_each_entry(pipe, &mdp5_data->pipes_used, used_list) {
struct mdss_mdp_data *buf;
@@ -1038,17 +988,13 @@
if (ret) {
pr_err("can't reset DMA pipe ret=%d ctl=%d\n",
ret, ctl->num);
- mutex_unlock(&mfd->lock);
- goto commit_fail;
+ return ret;
}
tmp = mdss_mdp_ctl_mixer_switch(ctl,
MDSS_MDP_WB_CTL_TYPE_LINE);
- if (!tmp) {
- mutex_unlock(&mfd->lock);
- ret = -EINVAL;
- goto commit_fail;
- }
+ if (!tmp)
+ return -EINVAL;
pipe->mixer = mdss_mdp_mixer_get(tmp,
MDSS_MDP_MIXER_MUX_DEFAULT);
}
@@ -1077,19 +1023,95 @@
}
}
+ return 0;
+}
+
+static void __overlay_kickoff_requeue(struct msm_fb_data_type *mfd)
+{
+ struct mdss_mdp_ctl *ctl = mfd_to_ctl(mfd);
+
+ mdss_mdp_display_commit(ctl, NULL);
+ mdss_mdp_display_wait4comp(ctl);
+
+ __overlay_queue_pipes(mfd);
+
+ mdss_mdp_display_commit(ctl, NULL);
+ mdss_mdp_display_wait4comp(ctl);
+}
+
+int mdss_mdp_overlay_kickoff(struct msm_fb_data_type *mfd,
+ struct mdp_display_commit *data)
+{
+ 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 = 0;
+ int sd_in_pipe = 0;
+
+ if (ctl->shared_lock) {
+ mdss_mdp_ctl_notify(ctl, MDP_NOTIFY_FRAME_BEGIN);
+ mdss_mdp_ctl_notify(ctl, MDP_NOTIFY_FRAME_READY);
+ mutex_lock(ctl->shared_lock);
+ }
+
+ mutex_lock(&mdp5_data->ov_lock);
+ mutex_lock(&mfd->lock);
+
+ /*
+ * check if there is a secure display session
+ */
+ list_for_each_entry(pipe, &mdp5_data->pipes_used, used_list) {
+ if (pipe->flags & MDP_SECURE_DISPLAY_OVERLAY_SESSION) {
+ sd_in_pipe = 1;
+ pr_debug("Secure pipe: %u : %08X\n",
+ pipe->num, pipe->flags);
+ }
+ }
+ /*
+ * If there is no secure display session and sd_enabled, disable the
+ * secure display session
+ */
+ if (!sd_in_pipe && mdp5_data->sd_enabled) {
+ if (0 == mdss_mdp_overlay_sd_ctrl(mfd, 0))
+ mdp5_data->sd_enabled = 0;
+ }
+
+ if (!ctl->shared_lock)
+ mdss_mdp_ctl_notify(ctl, MDP_NOTIFY_FRAME_BEGIN);
+
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
+
+ if (data)
+ mdss_mdp_set_roi(ctl, data);
+
+ /*
+ * Setup pipe in solid fill before unstaging,
+ * to ensure no fetches are happening after dettach or reattach.
+ */
+ list_for_each_entry(pipe, &mdp5_data->pipes_cleanup, cleanup_list) {
+ mdss_mdp_pipe_queue_data(pipe, NULL);
+ mdss_mdp_mixer_pipe_unstage(pipe);
+ }
+
+ ret = __overlay_queue_pipes(mfd);
+
if (mfd->panel.type == WRITEBACK_PANEL)
ret = mdss_mdp_wb_kickoff(mfd);
else
ret = mdss_mdp_display_commit(mdp5_data->ctl, NULL);
+ atomic_set(&mfd->kickoff_pending, 0);
+ wake_up_all(&mfd->kickoff_wait_q);
mutex_unlock(&mfd->lock);
if (IS_ERR_VALUE(ret))
goto commit_fail;
+ mutex_unlock(&mdp5_data->ov_lock);
mdss_mdp_overlay_update_pm(mdp5_data);
ret = mdss_mdp_display_wait4comp(mdp5_data->ctl);
+ mutex_lock(&mdp5_data->ov_lock);
if (ret == 0) {
mutex_lock(&mfd->lock);
@@ -1114,7 +1136,7 @@
return ret;
}
-static int mdss_mdp_overlay_release(struct msm_fb_data_type *mfd, int ndx)
+int mdss_mdp_overlay_release(struct msm_fb_data_type *mfd, int ndx)
{
struct mdss_mdp_pipe *pipe;
struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
@@ -1142,7 +1164,6 @@
&mdp5_data->pipes_cleanup);
}
mutex_unlock(&mfd->lock);
- mdss_mdp_mixer_pipe_unstage(pipe);
mdss_mdp_pipe_unmap(pipe);
if (destroy_pipe)
mdss_mdp_pipe_destroy(pipe);
@@ -1339,8 +1360,13 @@
for (i = 0; i < mdata->ndma_pipes; i++) {
pipe = mdata->dma_pipes + i;
- if (atomic_read(&pipe->ref_cnt) && pipe->mfd)
- mdss_mdp_overlay_force_cleanup(pipe->mfd);
+
+ if (!mdss_mdp_pipe_map(pipe)) {
+ struct msm_fb_data_type *mfd = pipe->mfd;
+ mdss_mdp_pipe_unmap(pipe);
+ if (mfd)
+ mdss_mdp_overlay_force_cleanup(mfd);
+ }
}
}
@@ -1412,8 +1438,7 @@
static int mdss_mdp_overlay_get_fb_pipe(struct msm_fb_data_type *mfd,
struct mdss_mdp_pipe **ppipe,
- int mixer_mux,
- struct mdp_overlay *req_ov)
+ int mixer_mux)
{
struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
struct mdss_mdp_pipe *pipe;
@@ -1423,7 +1448,10 @@
MDSS_MDP_STAGE_BASE);
if (pipe == NULL) {
+ struct mdp_overlay req;
+ struct fb_info *fbi = mfd->fbi;
struct mdss_mdp_mixer *mixer;
+ int bpp;
mixer = mdss_mdp_mixer_get(mdp5_data->ctl,
MDSS_MDP_MIXER_MUX_LEFT);
@@ -1432,73 +1460,46 @@
return -ENODEV;
}
- if (req_ov == NULL) {
- struct mdp_overlay req;
- struct fb_info *fbi = mfd->fbi;
- int bpp;
+ memset(&req, 0, sizeof(req));
- memset(&req, 0, sizeof(req));
-
- bpp = fbi->var.bits_per_pixel / 8;
- req.id = MSMFB_NEW_REQUEST;
- req.src.format = mfd->fb_imgType;
- req.src.height = fbi->var.yres;
- req.src.width = fbi->fix.line_length / bpp;
- if (mixer_mux == MDSS_MDP_MIXER_MUX_RIGHT) {
- if (req.src.width <= mixer->width) {
- pr_warn("right fb pipe not needed\n");
- return -EINVAL;
- }
-
- req.flags |= MDSS_MDP_RIGHT_MIXER;
- req.src_rect.x = mixer->width;
- req.src_rect.w = fbi->var.xres - mixer->width;
- } else {
- req.src_rect.x = 0;
- req.src_rect.w = MIN(fbi->var.xres,
- mixer->width);
+ bpp = fbi->var.bits_per_pixel / 8;
+ req.id = MSMFB_NEW_REQUEST;
+ req.src.format = mfd->fb_imgType;
+ req.src.height = fbi->var.yres;
+ req.src.width = fbi->fix.line_length / bpp;
+ if (mixer_mux == MDSS_MDP_MIXER_MUX_RIGHT) {
+ if (req.src.width <= mixer->width) {
+ pr_warn("right fb pipe not needed\n");
+ return -EINVAL;
}
- req.src_rect.y = 0;
- req.src_rect.h = req.src.height;
- req.dst_rect.x = 0;
- req.dst_rect.y = 0;
- req.dst_rect.w = req.src_rect.w;
- req.dst_rect.h = req.src_rect.h;
- req.z_order = MDSS_MDP_STAGE_BASE;
-
- pr_debug("allocating base pipe mux=%d\n", mixer_mux);
-
- ret = mdss_mdp_overlay_pipe_setup(mfd, &req, &pipe);
- if (ret)
- return ret;
+ req.src_rect.x = mixer->width;
+ req.src_rect.w = fbi->var.xres - mixer->width;
} else {
- if (mixer_mux == MDSS_MDP_MIXER_MUX_RIGHT) {
- req_ov->id = MSMFB_NEW_REQUEST;
- req_ov->flags |= MDSS_MDP_RIGHT_MIXER;
- req_ov->src_rect.w = MIN(mixer->width,
- req_ov->src_rect.w >> 1);
- req_ov->dst_rect.w = req_ov->src_rect.w;
- req_ov->src_rect.x = req_ov->src_rect.w;
- req_ov->dst_rect.x = 0;
- }
-
- ret = mdss_mdp_overlay_pipe_setup(mfd, req_ov, &pipe);
- if (ret)
- return ret;
+ req.src_rect.x = 0;
+ req.src_rect.w = MIN(fbi->var.xres,
+ mixer->width);
}
- }
+ req.src_rect.y = 0;
+ req.src_rect.h = req.src.height;
+ req.dst_rect.w = req.src_rect.w;
+ req.dst_rect.h = req.src_rect.h;
+ req.z_order = MDSS_MDP_STAGE_BASE;
+
+ pr_debug("allocating base pipe mux=%d\n", mixer_mux);
+
+ ret = mdss_mdp_overlay_pipe_setup(mfd, &req, &pipe);
+ if (ret)
+ return ret;
+ }
pr_debug("ctl=%d pnum=%d\n", mdp5_data->ctl->num, pipe->num);
*ppipe = pipe;
return 0;
}
-static void mdss_mdp_overlay_pan_display(struct msm_fb_data_type *mfd,
- struct mdp_overlay *req,
- int image_size,
- int *pipe_ndx)
+static void mdss_mdp_overlay_pan_display(struct msm_fb_data_type *mfd)
{
struct mdss_mdp_data *buf;
struct mdss_mdp_pipe *pipe;
@@ -1530,6 +1531,7 @@
return;
}
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
bpp = fbi->var.bits_per_pixel / 8;
offset = fbi->var.xoffset * bpp +
fbi->var.yoffset * fbi->fix.line_length;
@@ -1547,7 +1549,7 @@
}
ret = mdss_mdp_overlay_get_fb_pipe(mfd, &pipe,
- MDSS_MDP_MIXER_MUX_LEFT, req);
+ MDSS_MDP_MIXER_MUX_LEFT);
if (ret) {
pr_err("unable to allocate base pipe\n");
goto pan_display_error;
@@ -1557,14 +1559,13 @@
pr_err("unable to map base pipe\n");
goto pan_display_error;
}
- if (pipe_ndx)
- pipe_ndx[0] = pipe->ndx;
buf = &pipe->back_buf;
if (is_mdss_iommu_attached()) {
if (!mfd->iova) {
pr_err("mfd iova is zero\n");
- goto attach_err;
+ mdss_mdp_pipe_unmap(pipe);
+ goto pan_display_error;
}
buf->p[0].addr = mfd->iova;
} else {
@@ -1572,26 +1573,21 @@
}
buf->p[0].addr += offset;
- if (image_size)
- buf->p[0].len = image_size;
- else
- buf->p[0].len = fbi->fix.smem_len - offset;
+ buf->p[0].len = fbi->fix.smem_len - offset;
buf->num_planes = 1;
mdss_mdp_pipe_unmap(pipe);
if (fbi->var.xres > MAX_MIXER_WIDTH || mfd->split_display) {
ret = mdss_mdp_overlay_get_fb_pipe(mfd, &pipe,
- MDSS_MDP_MIXER_MUX_RIGHT, req);
+ MDSS_MDP_MIXER_MUX_RIGHT);
if (ret) {
pr_err("unable to allocate right base pipe\n");
- goto attach_err;
+ goto pan_display_error;
}
if (mdss_mdp_pipe_map(pipe)) {
pr_err("unable to map right base pipe\n");
- goto attach_err;
+ goto pan_display_error;
}
- if (pipe_ndx)
- pipe_ndx[1] = pipe->ndx;
pipe->back_buf = *buf;
mdss_mdp_pipe_unmap(pipe);
@@ -1602,18 +1598,43 @@
(fbi->var.activate & FB_ACTIVATE_FORCE))
mfd->mdp.kickoff_fnc(mfd, NULL);
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
return;
-attach_err:
- mutex_unlock(&mdp5_data->ov_lock);
- mdss_mdp_overlay_unset(mfd, pipe->ndx);
- if (pipe_ndx)
- pipe_ndx[0] = INVALID_PIPE_INDEX;
- return;
pan_display_error:
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
mutex_unlock(&mdp5_data->ov_lock);
}
+static void remove_underrun_vsync_handler(struct work_struct *work)
+{
+ int rc;
+ struct mdss_mdp_ctl *ctl =
+ container_of(work, typeof(*ctl), remove_underrun_handler);
+
+ if (!ctl || !ctl->remove_vsync_handler) {
+ pr_err("ctl or vsync handler is NULL\n");
+ return;
+ }
+
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
+ rc = ctl->remove_vsync_handler(ctl,
+ &ctl->recover_underrun_handler);
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
+}
+
+static void mdss_mdp_recover_underrun_handler(struct mdss_mdp_ctl *ctl,
+ ktime_t t)
+{
+ if (!ctl) {
+ pr_err("ctl is NULL\n");
+ return;
+ }
+
+ mdss_mdp_ctl_reset(ctl);
+ schedule_work(&ctl->remove_underrun_handler);
+}
+
/* function is called in irq context should have minimum processing */
static void mdss_mdp_overlay_handle_vsync(struct mdss_mdp_ctl *ctl,
ktime_t t)
@@ -1779,7 +1800,7 @@
vsync_ticks = ktime_to_ns(mdp5_data->vsync_time);
pr_debug("fb%d vsync=%llu", mfd->index, vsync_ticks);
- ret = scnprintf(buf, PAGE_SIZE, "VSYNC=%llu", vsync_ticks);
+ ret = scnprintf(buf, PAGE_SIZE, "VSYNC=%llu\n", vsync_ticks);
return ret;
}
@@ -2353,20 +2374,32 @@
void __user *argp)
{
struct mdp_overlay_list ovlist;
+ struct mdp_overlay *req_list[OVERLAY_MAX];
struct mdp_overlay *overlays;
int i, ret;
if (copy_from_user(&ovlist, argp, sizeof(ovlist)))
return -EFAULT;
+ if (ovlist.num_overlays >= OVERLAY_MAX) {
+ pr_err("Number of overlays exceeds max\n");
+ return -EINVAL;
+ }
+
overlays = kmalloc(ovlist.num_overlays * sizeof(*overlays), GFP_KERNEL);
if (!overlays) {
pr_err("Unable to allocate memory for overlays\n");
return -ENOMEM;
}
+ if (copy_from_user(req_list, ovlist.overlay_list,
+ sizeof(struct mdp_overlay*) * ovlist.num_overlays)) {
+ ret = -EFAULT;
+ goto validate_exit;
+ }
+
for (i = 0; i < ovlist.num_overlays; i++) {
- if (copy_from_user(overlays + i, ovlist.overlay_list[i],
+ if (copy_from_user(overlays + i, req_list[i],
sizeof(struct mdp_overlay))) {
ret = -EFAULT;
goto validate_exit;
@@ -2376,7 +2409,7 @@
ret = __handle_overlay_prepare(mfd, &ovlist, overlays);
if (!IS_ERR_VALUE(ret)) {
for (i = 0; i < ovlist.num_overlays; i++) {
- if (copy_to_user(ovlist.overlay_list[i], overlays + i,
+ if (copy_to_user(req_list[i], overlays + i,
sizeof(struct mdp_overlay))) {
ret = -EFAULT;
goto validate_exit;
@@ -2567,6 +2600,13 @@
mdss_mdp_overlay_handle_vsync;
ctl->vsync_handler.cmd_post_flush = false;
+ ctl->recover_underrun_handler.vsync_handler =
+ mdss_mdp_recover_underrun_handler;
+ ctl->recover_underrun_handler.cmd_post_flush = false;
+
+ INIT_WORK(&ctl->remove_underrun_handler,
+ remove_underrun_vsync_handler);
+
if (mfd->split_display && pdata->next) {
/* enable split display */
rc = mdss_mdp_ctl_split_display_setup(ctl, pdata->next);
@@ -2816,9 +2856,9 @@
error:
if (rc && ctl) {
- __mdss_mdp_handoff_cleanup_pipes(mfd, MDSS_MDP_PIPE_TYPE_RGB);
- __mdss_mdp_handoff_cleanup_pipes(mfd, MDSS_MDP_PIPE_TYPE_VIG);
- __mdss_mdp_handoff_cleanup_pipes(mfd, MDSS_MDP_PIPE_TYPE_DMA);
+ mdss_mdp_handoff_cleanup_pipes(mfd, MDSS_MDP_PIPE_TYPE_RGB);
+ mdss_mdp_handoff_cleanup_pipes(mfd, MDSS_MDP_PIPE_TYPE_VIG);
+ mdss_mdp_handoff_cleanup_pipes(mfd, MDSS_MDP_PIPE_TYPE_DMA);
mdss_mdp_ctl_destroy(ctl);
mdp5_data->ctl = NULL;
mdp5_data->handoff = false;
@@ -2827,63 +2867,6 @@
return rc;
}
-static int mdss_mdp_overlay_splash_image(struct msm_fb_data_type *mfd,
- int *pipe_ndx, int splash_event)
-{
- struct mdp_overlay req;
- int rc = 0;
- struct fb_info *fbi = NULL;
- int image_len = 0;
-
- if (!mfd || !mfd->fbi || !mfd->fbi->screen_base || !pipe_ndx) {
- pr_err("Invalid input parameter\n");
- return -EINVAL;
- }
-
- fbi = mfd->fbi;
- image_len = SPLASH_IMAGE_WIDTH * SPLASH_IMAGE_HEIGHT * SPLASH_IMAGE_BPP;
-
- if (SPLASH_IMAGE_WIDTH > fbi->var.xres ||
- SPLASH_IMAGE_HEIGHT > fbi->var.yres ||
- SPLASH_IMAGE_BPP > fbi->var.bits_per_pixel / 8 ||
- image_len > fbi->fix.smem_len) {
- pr_err("Invalid splash parameter configuration\n");
- return -EINVAL;
- }
-
- if (splash_event == MDP_CREATE_SPLASH_OV) {
- pipe_ndx[0] = INVALID_PIPE_INDEX;
- pipe_ndx[1] = INVALID_PIPE_INDEX;
-
- memset(&req, 0, sizeof(struct mdp_overlay));
- req.src.width = req.dst_rect.w = req.src_rect.w =
- SPLASH_IMAGE_WIDTH;
- req.src.height = req.dst_rect.h = req.src_rect.h =
- SPLASH_IMAGE_HEIGHT;
- req.src.format = SPLASH_IMAGE_FORMAT;
- req.id = MSMFB_NEW_REQUEST;
- req.z_order = MDSS_MDP_STAGE_0;
- req.is_fg = 1;
- req.alpha = 0xff;
- req.transp_mask = MDP_TRANSP_NOP;
- req.dst_rect.x =
- (fbi->var.xres >> 1) - (SPLASH_IMAGE_WIDTH >> 1);
- req.dst_rect.y =
- (fbi->var.yres >> 1) - (SPLASH_IMAGE_HEIGHT >> 1);
-
- memcpy(fbi->screen_base, splash_bgr888_image, image_len);
- mdss_mdp_overlay_pan_display(mfd, &req, image_len, pipe_ndx);
-
- } else if (splash_event == MDP_REMOVE_SPLASH_OV) {
- if (pipe_ndx[0] != INVALID_PIPE_INDEX)
- mdss_mdp_overlay_unset(mfd, pipe_ndx[0]);
- if (pipe_ndx[1] != INVALID_PIPE_INDEX)
- mdss_mdp_overlay_unset(mfd, pipe_ndx[1]);
- }
-
- return rc;
-}
-
static void __vsync_retire_handle_vsync(struct mdss_mdp_ctl *ctl, ktime_t t)
{
struct msm_fb_data_type *mfd = ctl->mfd;
@@ -3002,7 +2985,7 @@
mdp5_interface->panel_register_done = mdss_panel_register_done;
mdp5_interface->kickoff_fnc = mdss_mdp_overlay_kickoff;
mdp5_interface->get_sync_fnc = mdss_mdp_rotator_sync_pt_get;
- mdp5_interface->splash_fnc = mdss_mdp_overlay_splash_image;
+ mdp5_interface->splash_init_fnc = mdss_mdp_splash_init;
mdp5_data = kmalloc(sizeof(struct mdss_overlay_private), GFP_KERNEL);
if (!mdp5_data) {
@@ -3025,6 +3008,7 @@
goto init_fail;
}
mfd->mdp.private1 = mdp5_data;
+ mfd->wait_for_kickoff = true;
rc = mdss_mdp_overlay_fb_parse_dt(mfd);
if (rc)
@@ -3095,45 +3079,6 @@
return rc;
}
-static __ref int mdss_mdp_overlay_splash_parse_dt(struct msm_fb_data_type *mfd)
-{
- struct platform_device *pdev = mfd->pdev;
- struct mdss_overlay_private *mdp5_mdata = mfd_to_mdp5_data(mfd);
- int len = 0, rc = 0;
- u32 offsets[2];
-
- of_find_property(pdev->dev.of_node, "qcom,memblock-reserve", &len);
-
- if (len < 1) {
- pr_debug("mem reservation for splash screen fb not present\n");
- rc = -EINVAL;
- goto error;
- }
-
- len = len/sizeof(u32);
-
- rc = of_property_read_u32_array(pdev->dev.of_node,
- "qcom,memblock-reserve", offsets, len);
- if (rc) {
- pr_debug("Error reading mem reserve settings for fb\n");
- goto error;
- }
-
- if (!memblock_is_reserved(offsets[0])) {
- pr_debug("failed to reserve memory for fb splash\n");
- rc = -EINVAL;
- goto error;
- }
-
- mdp5_mdata->splash_mem_addr = offsets[0];
- mdp5_mdata->splash_mem_size = offsets[1];
- pr_debug("memaddr=%x size=%x\n", mdp5_mdata->splash_mem_addr,
- mdp5_mdata->splash_mem_size);
-
-error:
- return rc;
-}
-
static int mdss_mdp_overlay_fb_parse_dt(struct msm_fb_data_type *mfd)
{
int rc = 0;
@@ -3147,13 +3092,5 @@
pdev->name);
}
- rc = mdss_mdp_overlay_splash_parse_dt(mfd);
- if (rc && mfd->panel_info->cont_splash_enabled) {
- pr_err("No rsvd mem found in DT for splash screen\n");
- } else {
- pr_debug("Mem reservation not reqd if cont splash diasbled\n");
- rc = 0;
- }
-
return rc;
}
diff --git a/drivers/video/msm/mdss/mdss_mdp_pipe.c b/drivers/video/msm/mdss/mdss_mdp_pipe.c
index b6f9b17..c522857 100644
--- a/drivers/video/msm/mdss/mdss_mdp_pipe.c
+++ b/drivers/video/msm/mdss/mdss_mdp_pipe.c
@@ -28,10 +28,16 @@
#define PIPE_HALT_TIMEOUT_US 0x4000
+/* following offsets are relative to ctrl register bit offset */
+#define CLK_FORCE_ON_OFFSET 0x0
+#define CLK_FORCE_OFF_OFFSET 0x1
+/* following offsets are relative to status register bit offset */
+#define CLK_STATUS_OFFSET 0x0
+
static DEFINE_MUTEX(mdss_mdp_sspp_lock);
static DEFINE_MUTEX(mdss_mdp_smp_lock);
-static int mdss_mdp_pipe_free(struct mdss_mdp_pipe *pipe);
+static void mdss_mdp_pipe_free(struct kref *kref);
static int mdss_mdp_smp_mmb_set(int client_id, unsigned long *smp);
static void mdss_mdp_smp_mmb_free(unsigned long *smp, bool write);
static struct mdss_mdp_pipe *mdss_mdp_pipe_search_by_client_id(
@@ -210,7 +216,7 @@
struct mdss_mdp_plane_sizes ps;
int i;
int rc = 0, rot_mode = 0, wb_mixer = 0;
- u32 nlines, format;
+ u32 nlines, format, seg_w;
u16 width;
width = pipe->src.w >> pipe->horz_deci;
@@ -222,19 +228,17 @@
return rc;
/*
* Override fetch strides with SMP buffer size for both the
- * planes
+ * planes. BWC line buffer needs to be divided into 16
+ * segments and every segment is aligned to format
+ * specific RAU size
*/
+ seg_w = DIV_ROUND_UP(pipe->src.w, 16);
if (pipe->src_fmt->fetch_planes == MDSS_MDP_PLANE_INTERLEAVED) {
- /*
- * BWC line buffer needs to be divided into 16
- * segments and every segment is aligned to format
- * specific RAU size
- */
- ps.ystride[0] = ALIGN(pipe->src.w / 16 , 32) * 16 *
- ps.rau_h[0] * pipe->src_fmt->bpp;
+ ps.ystride[0] = ALIGN(seg_w, 32) * 16 * ps.rau_h[0] *
+ pipe->src_fmt->bpp;
ps.ystride[1] = 0;
} else {
- u32 bwc_width = ALIGN(pipe->src.w / 16, 64) * 16;
+ u32 bwc_width = ALIGN(seg_w, 64) * 16;
ps.ystride[0] = bwc_width * ps.rau_h[0];
ps.ystride[1] = bwc_width * ps.rau_h[1];
/*
@@ -477,21 +481,17 @@
void mdss_mdp_pipe_unmap(struct mdss_mdp_pipe *pipe)
{
- int tmp;
-
- tmp = atomic_dec_return(&pipe->ref_cnt);
-
- WARN(tmp < 0, "Invalid unmap with ref_cnt=%d", tmp);
- if (tmp == 0)
- mdss_mdp_pipe_free(pipe);
+ if (kref_put_mutex(&pipe->kref, mdss_mdp_pipe_free,
+ &mdss_mdp_sspp_lock)) {
+ WARN(1, "Unexpected free pipe during unmap");
+ mutex_unlock(&mdss_mdp_sspp_lock);
+ }
}
int mdss_mdp_pipe_map(struct mdss_mdp_pipe *pipe)
{
- if (!atomic_inc_not_zero(&pipe->ref_cnt)) {
- pr_err("attempting to map unallocated pipe (%d)", pipe->num);
+ if (!kref_get_unless_zero(&pipe->kref))
return -EINVAL;
- }
return 0;
}
@@ -537,7 +537,7 @@
for (i = off; i < npipes; i++) {
pipe = pipe_pool + i;
- if (atomic_cmpxchg(&pipe->ref_cnt, 0, 1) == 0) {
+ if (atomic_read(&pipe->kref.refcount) == 0) {
pipe->mixer = mixer;
break;
}
@@ -545,9 +545,8 @@
}
if (pipe && mdss_mdp_pipe_fetch_halt(pipe)) {
- pr_err("%d failed because vbif client is in bad state\n",
+ pr_err("%d failed because pipe is in bad state\n",
pipe->num);
- atomic_dec(&pipe->ref_cnt);
return NULL;
}
@@ -555,13 +554,14 @@
pr_debug("type=%x pnum=%d\n", pipe->type, pipe->num);
mutex_init(&pipe->pp_res.hist.hist_mutex);
spin_lock_init(&pipe->pp_res.hist.hist_lock);
+ kref_init(&pipe->kref);
} else if (pipe_share) {
/*
* when there is no dedicated wfd blk, DMA pipe can be
* shared as long as its attached to a writeback mixer
*/
pipe = mdata->dma_pipes + mixer->num;
- atomic_inc(&pipe->ref_cnt);
+ kref_get(&pipe->kref);
pr_debug("pipe sharing for pipe=%d\n", pipe->num);
} else {
pr_err("no %d type pipes available\n", type);
@@ -583,7 +583,7 @@
} else if (pipe != &mdata->dma_pipes[mixer->num]) {
pr_err("Requested DMA pnum=%d not available\n",
mdata->dma_pipes[mixer->num].num);
- mdss_mdp_pipe_unmap(pipe);
+ kref_put(&pipe->kref, mdss_mdp_pipe_free);
pipe = NULL;
} else {
pipe->mixer = mixer;
@@ -670,13 +670,17 @@
return NULL;
}
-static int mdss_mdp_pipe_free(struct mdss_mdp_pipe *pipe)
+static void mdss_mdp_pipe_free(struct kref *kref)
{
- pr_debug("ndx=%x pnum=%d ref_cnt=%d\n", pipe->ndx, pipe->num,
- atomic_read(&pipe->ref_cnt));
+ struct mdss_mdp_pipe *pipe;
+
+ pipe = container_of(kref, struct mdss_mdp_pipe, kref);
+
+ pr_debug("ndx=%x pnum=%d\n", pipe->ndx, pipe->num);
if (pipe->play_cnt) {
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
+ mdss_mdp_pipe_fetch_halt(pipe);
mdss_mdp_pipe_sspp_term(pipe);
mdss_mdp_smp_free(pipe);
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
@@ -688,8 +692,53 @@
pipe->bwc_mode = 0;
pipe->mfd = NULL;
memset(&pipe->scale, 0, sizeof(struct mdp_scale_data));
+}
- return 0;
+static int mdss_mdp_is_pipe_idle(struct mdss_mdp_pipe *pipe,
+ bool ignore_force_on)
+{
+ u32 reg_val;
+ u32 vbif_idle_mask, forced_on_mask, clk_status_idle_mask;
+ bool is_idle = false, is_forced_on;
+ struct mdss_data_type *mdata = mdss_mdp_get_mdata();
+
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
+
+ forced_on_mask = BIT(pipe->clk_ctrl.bit_off + CLK_FORCE_ON_OFFSET);
+ reg_val = readl_relaxed(mdata->mdp_base + pipe->clk_ctrl.reg_off);
+ is_forced_on = (reg_val & forced_on_mask) ? true : false;
+
+ pr_debug("pipe#:%d clk_ctrl: 0x%x forced_on_mask: 0x%x\n", pipe->num,
+ reg_val, forced_on_mask);
+ /* if forced on then no need to check status */
+ if (!is_forced_on) {
+ clk_status_idle_mask =
+ BIT(pipe->clk_status.bit_off + CLK_STATUS_OFFSET);
+ reg_val = readl_relaxed(mdata->mdp_base +
+ pipe->clk_status.reg_off);
+
+ if (reg_val & clk_status_idle_mask)
+ is_idle = false;
+
+ pr_debug("pipe#:%d clk_status:0x%x clk_status_idle_mask:0x%x\n",
+ pipe->num, reg_val, clk_status_idle_mask);
+ }
+
+ if (!ignore_force_on && (is_forced_on || !is_idle))
+ goto exit;
+
+ vbif_idle_mask = BIT(pipe->xin_id + 16);
+ reg_val = readl_relaxed(mdata->vbif_base + MMSS_VBIF_XIN_HALT_CTRL1);
+
+ if (reg_val & vbif_idle_mask)
+ is_idle = true;
+
+ pr_debug("pipe#:%d XIN_HALT_CTRL1: 0x%x\n", pipe->num, reg_val);
+
+exit:
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
+
+ return is_idle;
}
/**
@@ -711,18 +760,15 @@
u32 reg_val, idle_mask, status;
struct mdss_data_type *mdata = mdss_mdp_get_mdata();
- mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
-
- idle_mask = BIT(pipe->xin_id + 16);
- reg_val = readl_relaxed(mdata->vbif_base + MMSS_VBIF_XIN_HALT_CTRL1);
-
- is_idle = (reg_val & idle_mask) ? true : false;
+ is_idle = mdss_mdp_is_pipe_idle(pipe, true);
if (!is_idle) {
- pr_debug("%pS: pipe%d is not idle. xin_id=%d halt_ctrl1=0x%x\n",
- __builtin_return_address(0), pipe->num, pipe->xin_id,
- reg_val);
+ pr_err("%pS: pipe%d is not idle. xin_id=%d\n",
+ __builtin_return_address(0), pipe->num, pipe->xin_id);
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
mutex_lock(&mdata->reg_lock);
+ idle_mask = BIT(pipe->xin_id + 16);
+
reg_val = readl_relaxed(mdata->vbif_base +
MMSS_VBIF_XIN_HALT_CTRL0);
writel_relaxed(reg_val | BIT(pipe->xin_id),
@@ -743,28 +789,26 @@
MMSS_VBIF_XIN_HALT_CTRL0);
writel_relaxed(reg_val & ~BIT(pipe->xin_id),
mdata->vbif_base + MMSS_VBIF_XIN_HALT_CTRL0);
+
mutex_unlock(&mdata->reg_lock);
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
}
- mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
return rc;
}
int mdss_mdp_pipe_destroy(struct mdss_mdp_pipe *pipe)
{
- int tmp;
-
- tmp = atomic_dec_return(&pipe->ref_cnt);
-
- if (tmp != 0) {
- pr_err("unable to free pipe %d while still in use (%d)\n",
- pipe->num, tmp);
+ if (!kref_put_mutex(&pipe->kref, mdss_mdp_pipe_free,
+ &mdss_mdp_sspp_lock)) {
+ pr_err("unable to free pipe %d while still in use\n",
+ pipe->num);
return -EBUSY;
}
- mdss_mdp_pipe_free(pipe);
+
+ mutex_unlock(&mdss_mdp_sspp_lock);
return 0;
-
}
/**
@@ -823,7 +867,7 @@
pipe->is_handed_off = true;
pipe->play_cnt = 1;
- atomic_inc(&pipe->ref_cnt);
+ kref_init(&pipe->kref);
error:
return rc;
@@ -1065,7 +1109,7 @@
static int mdss_mdp_pipe_solidfill_setup(struct mdss_mdp_pipe *pipe)
{
int ret;
- u32 secure, format;
+ u32 secure, format, unpack;
pr_debug("solid fill setup on pnum=%d\n", pipe->num);
@@ -1078,10 +1122,21 @@
format = MDSS_MDP_FMT_SOLID_FILL;
secure = (pipe->flags & MDP_SECURE_OVERLAY_SESSION ? 0xF : 0x0);
+ /* support ARGB color format only */
+ unpack = (C3_ALPHA << 24) | (C2_R_Cr << 16) |
+ (C1_B_Cb << 8) | (C0_G_Y << 0);
mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC_FORMAT, format);
mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC_CONSTANT_COLOR,
pipe->bg_color);
+ mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC_UNPACK_PATTERN, unpack);
mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC_ADDR_SW_STATUS, secure);
+ mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC_OP_MODE, 0);
+
+ if (pipe->type != MDSS_MDP_PIPE_TYPE_DMA) {
+ mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SCALE_CONFIG, 0);
+ if (pipe->type == MDSS_MDP_PIPE_TYPE_VIG)
+ mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_VIG_OP_MODE, 0);
+ }
return 0;
}
diff --git a/drivers/video/msm/mdss/mdss_mdp_pp.c b/drivers/video/msm/mdss/mdss_mdp_pp.c
index 54ec6f8..6b497bb 100644
--- a/drivers/video/msm/mdss/mdss_mdp_pp.c
+++ b/drivers/video/msm/mdss/mdss_mdp_pp.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2014, 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
@@ -181,6 +181,7 @@
#define MDSS_MDP_GAMUT_SIZE 0x5C
#define MDSS_MDP_IGC_DSPP_SIZE 0x28
#define MDSS_MDP_IGC_SSPP_SIZE 0x88
+#define MDSS_MDP_VIG_QSEED2_SHARP_SIZE 0x0C
#define TOTAL_BLEND_STAGES 0x4
#define PP_FLAGS_DIRTY_PA 0x1
@@ -789,9 +790,15 @@
unsigned long flags = 0;
char __iomem *offset;
struct mdss_data_type *mdata;
+ u32 current_opmode;
+ u32 csc_reset;
+ u32 dcm_state = DCM_UNINIT;
pr_debug("pnum=%x\n", pipe->num);
+ if (pipe->mixer && pipe->mixer->ctl && pipe->mixer->ctl->mfd)
+ dcm_state = pipe->mixer->ctl->mfd->dcm_state;
+
mdata = mdss_mdp_get_mdata();
if ((pipe->flags & MDP_OVERLAY_PP_CFG_EN) &&
(pipe->pp_cfg.config_ops & MDP_OVERLAY_PP_CSC_CFG)) {
@@ -825,6 +832,16 @@
pp_histogram_setup(&opmode, MDSS_PP_SSPP_CFG | pipe->num, pipe->mixer);
+ /* Update CSC state only if tuning mode is enable */
+ if (dcm_state == DTM_ENTER) {
+ /* Reset bit 16 to 19 for CSC_STATE in VIG_OP_MODE */
+ csc_reset = 0xFFF0FFFF;
+ current_opmode = readl_relaxed(pipe->base +
+ MDSS_MDP_REG_VIG_OP_MODE);
+ *op |= ((current_opmode & csc_reset) | opmode);
+ return 0;
+ }
+
if (pipe->flags & MDP_OVERLAY_PP_CFG_EN) {
if ((pipe->pp_cfg.config_ops & MDP_OVERLAY_PP_PA_CFG) &&
(mdata->mdp_rev < MDSS_MDP_HW_REV_103)) {
@@ -910,10 +927,15 @@
u32 filter_mode;
struct mdss_data_type *mdata;
u32 src_w, src_h;
+ u32 dcm_state = DCM_UNINIT;
pr_debug("pipe=%d, change pxl ext=%d\n", pipe->num,
pipe->scale.enable_pxl_ext);
mdata = mdss_mdp_get_mdata();
+
+ if (pipe->mixer && pipe->mixer->ctl && pipe->mixer->ctl->mfd)
+ dcm_state = pipe->mixer->ctl->mfd->dcm_state;
+
if (mdata->mdp_rev >= MDSS_MDP_HW_REV_102 && pipe->src_fmt->is_yuv)
filter_mode = MDSS_MDP_SCALE_FILTER_CA;
else
@@ -948,12 +970,13 @@
pipe->pp_cfg.sharp_cfg.noise_thr = SHARP_NOISE_THR_DEFAULT;
}
- if ((pipe->src_fmt->is_yuv) &&
- !((pipe->dst.w < src_w) || (pipe->dst.h < src_h))) {
- pp_sharp_config(pipe->base +
- MDSS_MDP_REG_VIG_QSEED2_SHARP,
- &pipe->pp_res.pp_sts,
- &pipe->pp_cfg.sharp_cfg);
+ if (dcm_state != DTM_ENTER &&
+ ((pipe->src_fmt->is_yuv) &&
+ !((pipe->dst.w < src_w) || (pipe->dst.h < src_h)))) {
+ pp_sharp_config(pipe->base +
+ MDSS_MDP_REG_VIG_QSEED2_SHARP,
+ &pipe->pp_res.pp_sts,
+ &pipe->pp_cfg.sharp_cfg);
}
if ((src_h != pipe->dst.h) ||
@@ -1155,10 +1178,23 @@
char __iomem *pipe_base;
u32 pipe_num;
struct mdss_data_type *mdata = mdss_mdp_get_mdata();
+ u32 current_opmode;
+ u32 dcm_state = DCM_UNINIT;
if (pipe == NULL)
return -EINVAL;
+ if (pipe->mixer && pipe->mixer->ctl && pipe->mixer->ctl->mfd)
+ dcm_state = pipe->mixer->ctl->mfd->dcm_state;
+
+ /* Read IGC state and update the same if tuning mode is enable */
+ if (dcm_state == DTM_ENTER) {
+ current_opmode = readl_relaxed(pipe->base +
+ MDSS_MDP_REG_SSPP_SRC_OP_MODE);
+ *op |= (current_opmode & BIT(16));
+ return ret;
+ }
+
/*
* TODO: should this function be responsible for masking multiple
* pipes to be written in dual pipe case?
@@ -1306,19 +1342,19 @@
goto error;
}
+ mutex_lock(&hist_info->hist_mutex);
+ spin_lock_irqsave(&hist_info->hist_lock, flag);
if (hist_info->col_en) {
*op |= op_flags;
- mutex_lock(&hist_info->hist_mutex);
- spin_lock_irqsave(&hist_info->hist_lock, flag);
col_state = hist_info->col_state;
if (col_state == HIST_IDLE) {
/* Kick off collection */
writel_relaxed(1, base + kick_base);
hist_info->col_state = HIST_START;
}
- spin_unlock_irqrestore(&hist_info->hist_lock, flag);
- mutex_unlock(&hist_info->hist_mutex);
}
+ spin_unlock_irqrestore(&hist_info->hist_lock, flag);
+ mutex_unlock(&hist_info->hist_mutex);
ret = 0;
error:
return ret;
@@ -1750,6 +1786,8 @@
&mdss_pp_res->dspp_hist[i].hist_mutex);
spin_lock_init(
&mdss_pp_res->dspp_hist[i].hist_lock);
+ init_completion(
+ &mdss_pp_res->dspp_hist[i].comp);
}
}
}
@@ -1758,6 +1796,7 @@
for (i = 0; i < mdata->nvig_pipes; i++) {
mutex_init(&vig[i].pp_res.hist.hist_mutex);
spin_lock_init(&vig[i].pp_res.hist.hist_lock);
+ init_completion(&vig[i].pp_res.hist.comp);
}
if (!mdata->pp_bus_hdl) {
pp_bus_pdata = &mdp_pp_bus_scale_table;
@@ -2205,6 +2244,20 @@
return ret;
}
+static void pp_read_igc_lut_cached(struct mdp_igc_lut_data *cfg)
+{
+ int i;
+ u32 disp_num;
+
+ disp_num = cfg->block - MDP_LOGICAL_BLOCK_DISP_0;
+ for (i = 0; i < IGC_LUT_ENTRIES; i++) {
+ cfg->c0_c1_data[i] =
+ mdss_pp_res->igc_disp_cfg[disp_num].c0_c1_data[i];
+ cfg->c2_data[i] =
+ mdss_pp_res->igc_disp_cfg[disp_num].c2_data[i];
+ }
+}
+
static void pp_read_igc_lut(struct mdp_igc_lut_data *cfg,
char __iomem *addr, u32 blk_idx)
{
@@ -2325,14 +2378,17 @@
&mdss_pp_res->igc_lut_c0c1[disp_num][0];
local_cfg.c2_data =
&mdss_pp_res->igc_lut_c2[disp_num][0];
- pp_read_igc_lut(&local_cfg, igc_addr, dspp_num);
- if (copy_to_user(config->c0_c1_data, local_cfg.c2_data,
+ if (mdata->has_no_lut_read)
+ pp_read_igc_lut_cached(&local_cfg);
+ else
+ pp_read_igc_lut(&local_cfg, igc_addr, dspp_num);
+ if (copy_to_user(config->c0_c1_data, local_cfg.c0_c1_data,
config->len * sizeof(u32))) {
ret = -EFAULT;
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
goto igc_config_exit;
}
- if (copy_to_user(config->c2_data, local_cfg.c0_c1_data,
+ if (copy_to_user(config->c2_data, local_cfg.c2_data,
config->len * sizeof(u32))) {
ret = -EFAULT;
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
@@ -2472,6 +2528,41 @@
return ret;
}
+static int pp_read_argc_lut_cached(struct mdp_pgc_lut_data *config)
+{
+ int i;
+ u32 disp_num;
+ struct mdp_pgc_lut_data *pgc_ptr;
+
+ disp_num = PP_BLOCK(config->block) - MDP_LOGICAL_BLOCK_DISP_0;
+ switch (PP_LOCAT(config->block)) {
+ case MDSS_PP_LM_CFG:
+ pgc_ptr = &mdss_pp_res->argc_disp_cfg[disp_num];
+ break;
+ case MDSS_PP_DSPP_CFG:
+ pgc_ptr = &mdss_pp_res->pgc_disp_cfg[disp_num];
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ for (i = 0; i < GC_LUT_SEGMENTS; i++) {
+ config->r_data[i].x_start = pgc_ptr->r_data[i].x_start;
+ config->r_data[i].slope = pgc_ptr->r_data[i].slope;
+ config->r_data[i].offset = pgc_ptr->r_data[i].offset;
+
+ config->g_data[i].x_start = pgc_ptr->g_data[i].x_start;
+ config->g_data[i].slope = pgc_ptr->g_data[i].slope;
+ config->g_data[i].offset = pgc_ptr->g_data[i].offset;
+
+ config->b_data[i].x_start = pgc_ptr->b_data[i].x_start;
+ config->b_data[i].slope = pgc_ptr->b_data[i].slope;
+ config->b_data[i].offset = pgc_ptr->b_data[i].offset;
+ }
+
+ return 0;
+}
+
/* Note: Assumes that its inputs have been checked by calling function */
static void pp_update_hist_lut(char __iomem *addr,
struct mdp_hist_lut_data *cfg)
@@ -2495,6 +2586,10 @@
struct mdp_pgc_lut_data *pgc_ptr;
u32 tbl_size, r_size, g_size, b_size;
char __iomem *argc_addr = 0;
+ struct mdss_data_type *mdata = mdss_mdp_get_mdata();
+
+ if (mdata == NULL)
+ return -EPERM;
if ((PP_BLOCK(config->block) < MDP_LOGICAL_BLOCK_DISP_0) ||
(PP_BLOCK(config->block) >= MDP_BLOCK_MAX))
@@ -2508,6 +2603,12 @@
mutex_lock(&mdss_pp_mutex);
disp_num = PP_BLOCK(config->block) - MDP_LOGICAL_BLOCK_DISP_0;
+ ret = pp_get_dspp_num(disp_num, &dspp_num);
+ if (ret) {
+ pr_err("%s, no dspp connects to disp %d", __func__, disp_num);
+ goto argc_config_exit;
+ }
+
switch (PP_LOCAT(config->block)) {
case MDSS_PP_LM_CFG:
argc_addr = mdss_mdp_get_mixer_addr_off(dspp_num) +
@@ -2533,12 +2634,6 @@
tbl_size = GC_LUT_SEGMENTS * sizeof(struct mdp_ar_gc_lut_data);
if (config->flags & MDP_PP_OPS_READ) {
- ret = pp_get_dspp_num(disp_num, &dspp_num);
- if (ret) {
- pr_err("%s, no dspp connects to disp %d",
- __func__, disp_num);
- goto argc_config_exit;
- }
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
local_cfg = *config;
local_cfg.r_data =
@@ -2547,21 +2642,31 @@
&mdss_pp_res->gc_lut_g[disp_num][0];
local_cfg.b_data =
&mdss_pp_res->gc_lut_b[disp_num][0];
- pp_read_argc_lut(&local_cfg, argc_addr);
- if (copy_to_user(config->r_data,
- &mdss_pp_res->gc_lut_r[disp_num][0], tbl_size)) {
+ if (mdata->has_no_lut_read)
+ pp_read_argc_lut_cached(&local_cfg);
+ else
+ pp_read_argc_lut(&local_cfg, argc_addr);
+
+ if ((tbl_size != local_cfg.num_r_stages *
+ sizeof(struct mdp_ar_gc_lut_data)) ||
+ (copy_to_user(config->r_data, local_cfg.r_data,
+ tbl_size))) {
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
ret = -EFAULT;
goto argc_config_exit;
}
- if (copy_to_user(config->g_data,
- &mdss_pp_res->gc_lut_g[disp_num][0], tbl_size)) {
+ if ((tbl_size != local_cfg.num_g_stages *
+ sizeof(struct mdp_ar_gc_lut_data)) ||
+ (copy_to_user(config->g_data, local_cfg.g_data,
+ tbl_size))) {
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
ret = -EFAULT;
goto argc_config_exit;
}
- if (copy_to_user(config->b_data,
- &mdss_pp_res->gc_lut_b[disp_num][0], tbl_size)) {
+ if ((tbl_size != local_cfg.num_b_stages *
+ sizeof(struct mdp_ar_gc_lut_data)) ||
+ (copy_to_user(config->b_data, local_cfg.b_data,
+ tbl_size))) {
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
ret = -EFAULT;
goto argc_config_exit;
@@ -2887,22 +2992,23 @@
mutex_lock(&hist_info->hist_mutex);
/* check if it is idle */
+ spin_lock_irqsave(&hist_info->hist_lock, flag);
if (hist_info->col_en) {
+ spin_unlock_irqrestore(&hist_info->hist_lock, flag);
pr_info("%s Hist collection has already been enabled %d",
__func__, (u32) ctl_base);
ret = -EINVAL;
goto exit;
}
- hist_info->frame_cnt = req->frame_cnt;
- init_completion(&hist_info->comp);
- hist_info->hist_cnt_read = 0;
- hist_info->hist_cnt_sent = 0;
- hist_info->hist_cnt_time = 0;
- spin_lock_irqsave(&hist_info->hist_lock, flag);
hist_info->read_request = 0;
hist_info->col_state = HIST_RESET;
hist_info->col_en = true;
spin_unlock_irqrestore(&hist_info->hist_lock, flag);
+ hist_info->frame_cnt = req->frame_cnt;
+ INIT_COMPLETION(hist_info->comp);
+ hist_info->hist_cnt_read = 0;
+ hist_info->hist_cnt_sent = 0;
+ hist_info->hist_cnt_time = 0;
mdss_mdp_hist_intr_req(&mdata->hist_intr, 3 << shift_bit, true);
writel_relaxed(req->frame_cnt, ctl_base + 8);
/* Kick out reset start */
@@ -3013,17 +3119,18 @@
struct mdss_data_type *mdata = mdss_mdp_get_mdata();
mutex_lock(&hist_info->hist_mutex);
+ spin_lock_irqsave(&hist_info->hist_lock, flag);
if (hist_info->col_en == false) {
+ spin_unlock_irqrestore(&hist_info->hist_lock, flag);
pr_debug("Histogram already disabled (%d)", (u32) ctl_base);
ret = -EINVAL;
goto exit;
}
- complete_all(&hist_info->comp);
- spin_lock_irqsave(&hist_info->hist_lock, flag);
hist_info->col_en = false;
hist_info->col_state = HIST_UNKNOWN;
spin_unlock_irqrestore(&hist_info->hist_lock, flag);
mdss_mdp_hist_intr_req(&mdata->hist_intr, done_bit, false);
+ complete_all(&hist_info->comp);
writel_relaxed(BIT(1), ctl_base);/* cancel */
ret = 0;
exit:
@@ -3264,12 +3371,13 @@
struct mdss_mdp_pipe *pipe;
mutex_lock(&hist_info->hist_mutex);
+ spin_lock_irqsave(&hist_info->hist_lock, flag);
if ((hist_info->col_en == 0) ||
(hist_info->col_state == HIST_UNKNOWN)) {
+ spin_unlock_irqrestore(&hist_info->hist_lock, flag);
ret = -EINVAL;
goto hist_collect_exit;
}
- spin_lock_irqsave(&hist_info->hist_lock, flag);
/* wait for hist done if cache has no data */
if (hist_info->col_state != HIST_READY) {
spin_unlock_irqrestore(&hist_info->hist_lock, flag);
@@ -3285,9 +3393,9 @@
&(hist_info->comp), timeout);
mutex_lock(&hist_info->hist_mutex);
+ spin_lock_irqsave(&hist_info->hist_lock, flag);
if (wait_ret == 0) {
ret = -ETIMEDOUT;
- spin_lock_irqsave(&hist_info->hist_lock, flag);
pr_debug("bin collection timedout, state %d",
hist_info->col_state);
/*
@@ -3302,37 +3410,36 @@
*/
hist_info->hist_cnt_time++;
hist_info->col_state = HIST_READY;
- spin_unlock_irqrestore(&hist_info->hist_lock, flag);
} else if (wait_ret < 0) {
+ spin_unlock_irqrestore(&hist_info->hist_lock, flag);
ret = -EINTR;
pr_debug("%s: bin collection interrupted",
__func__);
goto hist_collect_exit;
}
- if (hist_info->col_state != HIST_READY) {
+ if (hist_info->col_state != HIST_READY &&
+ hist_info->col_state != HIST_UNKNOWN) {
ret = -ENODATA;
- spin_lock_irqsave(&hist_info->hist_lock, flag);
hist_info->col_state = HIST_READY;
- spin_unlock_irqrestore(&hist_info->hist_lock, flag);
pr_debug("%s: state is not ready: %d",
__func__, hist_info->col_state);
}
- } else {
- spin_unlock_irqrestore(&hist_info->hist_lock, flag);
}
- spin_lock_irqsave(&hist_info->hist_lock, flag);
if (hist_info->col_state == HIST_READY) {
+ hist_info->col_state = HIST_IDLE;
spin_unlock_irqrestore(&hist_info->hist_lock, flag);
v_base = ctl_base + 0x1C;
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
sum = pp_hist_read(v_base, hist_info);
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
- spin_lock_irqsave(&hist_info->hist_lock, flag);
- if (expect_sum && sum != expect_sum)
+ if (expect_sum && sum != expect_sum) {
+ pr_debug("hist error: bin sum incorrect! (%d/%d)\n",
+ sum, expect_sum);
ret = -ENODATA;
- hist_info->col_state = HIST_IDLE;
+ }
+ } else {
+ spin_unlock_irqrestore(&hist_info->hist_lock, flag);
}
- spin_unlock_irqrestore(&hist_info->hist_lock, flag);
hist_collect_exit:
mutex_unlock(&hist_info->hist_mutex);
return ret;
@@ -3396,6 +3503,9 @@
temp_ret = ret;
ret = pp_hist_collect(hist, hists[i], ctl_base,
exp_sum);
+ if (ret)
+ pr_debug("hist error: dspp[%d] collect %d\n",
+ dspp_num, ret);
}
for (i = 0; i < hist_cnt; i++) {
/* reset read requests and re-intialize completions */
@@ -3493,6 +3603,10 @@
temp_ret = ret;
ret = pp_hist_collect(hist, hist_info, ctl_base,
exp_sum);
+ if (ret)
+ pr_debug("hist error: pipe[%d] collect: %d\n",
+ pipe->num, ret);
+
mdss_mdp_pipe_unmap(pipe);
}
for (i = pipe_num; i < MDSS_PP_ARG_NUM; i++) {
@@ -4584,7 +4698,8 @@
break;
}
- for (stage = 0; stage < mdss_res->nmixers_intf; stage++)
+ for (stage = 0; stage < (mdss_res->nmixers_intf +
+ mdss_res->nmixers_wb); stage++)
if (ptr == base + MDSS_MDP_REG_CTL_LAYER(stage)) {
ret = MDP_PP_OPS_READ | MDP_PP_OPS_WRITE;
goto End;
@@ -4675,7 +4790,10 @@
} else if (ptr == base + MDSS_MDP_REG_SSPP_SRC_OP_MODE) {
ret = MDP_PP_OPS_READ | MDP_PP_OPS_WRITE;
break;
- } else if ((ptr == base + MDSS_MDP_REG_VIG_QSEED2_SHARP)) {
+ /* QSEED2 range */
+ } else if ((ptr >= base + MDSS_MDP_REG_VIG_QSEED2_SHARP) &&
+ (ptr <= base + MDSS_MDP_REG_VIG_QSEED2_SHARP +
+ MDSS_MDP_VIG_QSEED2_SHARP_SIZE)) {
ret = MDP_PP_OPS_READ | MDP_PP_OPS_WRITE;
break;
/* PA range */
@@ -4760,7 +4878,8 @@
int stage = 0;
struct mdss_mdp_mixer *mixer;
- for (counter = 0; counter < mdss_res->nmixers_intf; counter++) {
+ for (counter = 0; counter < (mdss_res->nmixers_intf +
+ mdss_res->nmixers_wb); counter++) {
mixer = mdss_res->mixer_intf + counter;
base = mixer->base;
@@ -4811,6 +4930,7 @@
if ((unsigned int)addr % 4) {
ret = 0;
} else if (ptr == (mdss_res->mdp_base + MDSS_MDP_REG_HW_VERSION) ||
+ ptr == (mdss_res->mdp_base + MDSS_REG_HW_VERSION) ||
ptr == (mdss_res->mdp_base + MDSS_MDP_REG_DISP_INTF_SEL)) {
ret = MDP_PP_OPS_READ;
/* IGC DSPP range */
diff --git a/drivers/video/msm/mdss/mdss_mdp_rotator.c b/drivers/video/msm/mdss/mdss_mdp_rotator.c
old mode 100644
new mode 100755
index 91e6373..ee892e2
--- a/drivers/video/msm/mdss/mdss_mdp_rotator.c
+++ b/drivers/video/msm/mdss/mdss_mdp_rotator.c
@@ -226,7 +226,7 @@
ret = mdss_mdp_smp_reserve(pipe);
if (ret) {
- pr_err("unable to mdss_mdp_smp_reserve rot data\n");
+ pr_debug("unable to mdss_mdp_smp_reserve rot data\n");
return ret;
}
@@ -642,7 +642,7 @@
if (rot_pipe) {
struct mdss_mdp_mixer *mixer = rot_pipe->mixer;
- mdss_mdp_pipe_unmap(rot_pipe);
+ mdss_mdp_pipe_destroy(rot_pipe);
tmp = mdss_mdp_ctl_mixer_switch(mixer->ctl,
MDSS_MDP_WB_CTL_TYPE_BLOCK);
if (!tmp)
diff --git a/drivers/video/msm/mdss/mdss_mdp_splash_logo.c b/drivers/video/msm/mdss/mdss_mdp_splash_logo.c
new file mode 100644
index 0000000..77f6554
--- /dev/null
+++ b/drivers/video/msm/mdss/mdss_mdp_splash_logo.c
@@ -0,0 +1,623 @@
+/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/kthread.h>
+#include <linux/memblock.h>
+#include <linux/bootmem.h>
+#include <linux/iommu.h>
+#include <linux/fb.h>
+
+#include "mdss_fb.h"
+#include "mdss_mdp.h"
+#include "splash.h"
+#include "mdss_mdp_splash_logo.h"
+
+#define INVALID_PIPE_INDEX 0xFFFF
+#define MAX_FRAME_DONE_COUNT_WAIT 2
+
+static int mdss_mdp_splash_alloc_memory(struct msm_fb_data_type *mfd,
+ uint32_t size)
+{
+ int rc;
+ struct msm_fb_splash_info *sinfo;
+ unsigned long buf_size = size;
+ struct mdss_data_type *mdata;
+
+ if (!mfd || !size)
+ return -EINVAL;
+
+ mdata = mfd_to_mdata(mfd);
+ sinfo = &mfd->splash_info;
+
+ if (!mdata || !mdata->iclient || sinfo->splash_buffer)
+ return -EINVAL;
+
+ sinfo->ion_handle = ion_alloc(mdata->iclient, size, SZ_4K,
+ ION_HEAP(ION_SYSTEM_HEAP_ID), 0);
+ if (IS_ERR_OR_NULL(sinfo->ion_handle)) {
+ pr_err("ion memory allocation failed\n");
+ rc = PTR_RET(sinfo->ion_handle);
+ goto end;
+ }
+
+ rc = ion_map_iommu(mdata->iclient, sinfo->ion_handle,
+ mdss_get_iommu_domain(MDSS_IOMMU_DOMAIN_UNSECURE),
+ 0, SZ_4K, 0, (unsigned long *)&sinfo->iova,
+ (unsigned long *)&buf_size, 0, 0);
+ if (rc) {
+ pr_err("ion memory map failed\n");
+ goto imap_err;
+ }
+
+ sinfo->splash_buffer = ion_map_kernel(mdata->iclient,
+ sinfo->ion_handle);
+ if (IS_ERR_OR_NULL(sinfo->splash_buffer)) {
+ pr_err("ion kernel memory mapping failed\n");
+ rc = IS_ERR(sinfo->splash_buffer);
+ goto kmap_err;
+ }
+
+ return rc;
+
+kmap_err:
+ ion_unmap_iommu(mdata->iclient, sinfo->ion_handle,
+ mdss_get_iommu_domain(MDSS_IOMMU_DOMAIN_UNSECURE), 0);
+imap_err:
+ ion_free(mdata->iclient, sinfo->ion_handle);
+end:
+ return rc;
+}
+
+static void mdss_mdp_splash_free_memory(struct msm_fb_data_type *mfd)
+{
+ struct msm_fb_splash_info *sinfo;
+ struct mdss_data_type *mdata;
+
+ if (!mfd)
+ return;
+
+ sinfo = &mfd->splash_info;
+ mdata = mfd_to_mdata(mfd);
+
+ if (!mdata || !mdata->iclient || !sinfo->ion_handle)
+ return;
+
+ ion_unmap_kernel(mdata->iclient, sinfo->ion_handle);
+
+ ion_unmap_iommu(mdata->iclient, sinfo->ion_handle,
+ mdss_get_iommu_domain(MDSS_IOMMU_DOMAIN_UNSECURE), 0);
+
+ ion_free(mdata->iclient, sinfo->ion_handle);
+ sinfo->splash_buffer = NULL;
+}
+
+static int mdss_mdp_splash_iommu_attach(struct msm_fb_data_type *mfd)
+{
+ struct iommu_domain *domain;
+ struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
+ int rc;
+
+ /*
+ * iommu dynamic attach for following conditions.
+ * 1. it is still not attached
+ * 2. MDP hardware version supports the feature
+ * 3. configuration is with valid splash buffer
+ */
+ if (is_mdss_iommu_attached() ||
+ !mfd->panel_info->cont_splash_enabled ||
+ !mdss_mdp_iommu_dyn_attach_supported(mdp5_data->mdata) ||
+ !mdp5_data->splash_mem_addr ||
+ !mdp5_data->splash_mem_size) {
+ pr_debug("dynamic attach is not supported\n");
+ return -EPERM;
+ }
+
+ domain = msm_get_iommu_domain(mdss_get_iommu_domain(
+ MDSS_IOMMU_DOMAIN_UNSECURE));
+ if (!domain) {
+ pr_debug("mdss iommu domain get failed\n");
+ return -EINVAL;
+ }
+
+ rc = iommu_map(domain, mdp5_data->splash_mem_addr,
+ mdp5_data->splash_mem_addr,
+ mdp5_data->splash_mem_size, IOMMU_READ);
+ if (rc) {
+ pr_debug("iommu memory mapping failed rc=%d\n", rc);
+ } else {
+ rc = mdss_iommu_attach(mdss_res);
+ if (rc) {
+ pr_debug("mdss iommu attach failed\n");
+ iommu_unmap(domain, mdp5_data->splash_mem_addr,
+ mdp5_data->splash_mem_size);
+ } else {
+ mfd->splash_info.iommu_dynamic_attached = true;
+ }
+ }
+
+ return rc;
+}
+
+static void mdss_mdp_splash_unmap_splash_mem(struct msm_fb_data_type *mfd)
+{
+ struct iommu_domain *domain;
+ struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
+
+ if (mfd->splash_info.iommu_dynamic_attached) {
+ domain = msm_get_iommu_domain(mdss_get_iommu_domain(
+ MDSS_IOMMU_DOMAIN_UNSECURE));
+ if (!domain) {
+ pr_err("mdss iommu domain get failed\n");
+ return;
+ }
+
+ iommu_unmap(domain, mdp5_data->splash_mem_addr,
+ mdp5_data->splash_mem_size);
+ mfd->splash_info.iommu_dynamic_attached = false;
+ }
+}
+
+void mdss_mdp_release_splash_pipe(struct msm_fb_data_type *mfd)
+{
+ struct msm_fb_splash_info *sinfo;
+
+ if (!mfd || !mfd->splash_info.splash_pipe_allocated)
+ return;
+
+ sinfo = &mfd->splash_info;
+
+ if (sinfo->pipe_ndx[0] != INVALID_PIPE_INDEX)
+ mdss_mdp_overlay_release(mfd, sinfo->pipe_ndx[0]);
+ if (sinfo->pipe_ndx[1] != INVALID_PIPE_INDEX)
+ mdss_mdp_overlay_release(mfd, sinfo->pipe_ndx[1]);
+ sinfo->splash_pipe_allocated = false;
+}
+
+int mdss_mdp_splash_cleanup(struct msm_fb_data_type *mfd,
+ bool use_borderfill)
+{
+ struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
+ struct mdss_mdp_ctl *ctl = mdp5_data->ctl;
+ int rc = 0;
+
+ if (!mfd || !mdp5_data)
+ return -EINVAL;
+
+ if (mfd->splash_info.iommu_dynamic_attached ||
+ !mfd->panel_info->cont_splash_enabled)
+ goto end;
+
+ if (use_borderfill && mdp5_data->handoff) {
+ /*
+ * Set up border-fill on the handed off pipes.
+ * This is needed to ensure that there are no memory
+ * accesses prior to attaching iommu during continuous
+ * splash screen case. However, for command mode
+ * displays, this is not necessary since the panels can
+ * refresh from their internal memory if no data is sent
+ * out on the dsi lanes.
+ */
+ if (mdp5_data->handoff && ctl && ctl->is_video_mode) {
+ rc = mdss_mdp_display_commit(ctl, NULL);
+ if (!IS_ERR_VALUE(rc)) {
+ mdss_mdp_display_wait4comp(ctl);
+ } else {
+ /*
+ * Since border-fill setup failed, we
+ * need to ensure that we turn off the
+ * MDP timing generator before attaching
+ * iommu
+ */
+ pr_err("failed to set BF at handoff\n");
+ mdp5_data->handoff = false;
+ }
+ }
+ }
+
+ if (rc || mdp5_data->handoff) {
+ /* Add all the handed off pipes to the cleanup list */
+ mdss_mdp_handoff_cleanup_pipes(mfd, MDSS_MDP_PIPE_TYPE_RGB);
+ mdss_mdp_handoff_cleanup_pipes(mfd, MDSS_MDP_PIPE_TYPE_VIG);
+ mdss_mdp_handoff_cleanup_pipes(mfd, MDSS_MDP_PIPE_TYPE_DMA);
+ }
+
+ mdss_mdp_ctl_splash_finish(ctl, mdp5_data->handoff);
+
+ if (mdp5_data->splash_mem_addr) {
+ /* Give back the reserved memory to the system */
+ memblock_free(mdp5_data->splash_mem_addr,
+ mdp5_data->splash_mem_size);
+ free_bootmem_late(mdp5_data->splash_mem_addr,
+ mdp5_data->splash_mem_size);
+ }
+
+ mdss_mdp_footswitch_ctrl_splash(0);
+ if (!is_mdss_iommu_attached()) {
+ rc = mdss_iommu_attach(mdss_res);
+ if (rc)
+ pr_err("iommu attach failed rc=%d\n", rc);
+ }
+
+end:
+ return rc;
+}
+
+static struct mdss_mdp_pipe *mdss_mdp_splash_get_pipe(
+ struct msm_fb_data_type *mfd,
+ struct mdp_overlay *req)
+{
+ struct mdss_mdp_pipe *pipe;
+ int ret;
+ struct mdss_mdp_data *buf;
+ uint32_t image_size = SPLASH_IMAGE_WIDTH * SPLASH_IMAGE_HEIGHT
+ * SPLASH_IMAGE_BPP;
+
+ ret = mdss_mdp_overlay_pipe_setup(mfd, req, &pipe);
+ if (ret)
+ return NULL;
+
+ if (mdss_mdp_pipe_map(pipe)) {
+ pr_err("unable to map base pipe\n");
+ return NULL;
+ }
+
+ buf = &pipe->back_buf;
+ buf->p[0].addr = mfd->splash_info.iova;
+ buf->p[0].len = image_size;
+ buf->num_planes = 1;
+ mdss_mdp_pipe_unmap(pipe);
+
+ return pipe;
+}
+
+static int mdss_mdp_splash_kickoff(struct msm_fb_data_type *mfd,
+ struct mdss_mdp_img_rect *src_rect,
+ struct mdss_mdp_img_rect *dest_rect)
+{
+ struct mdss_mdp_pipe *pipe;
+ struct fb_info *fbi;
+ struct mdp_overlay req;
+ struct mdss_overlay_private *mdp5_data;
+ struct mdss_data_type *mdata;
+ struct mdss_mdp_mixer *mixer;
+ int ret;
+ bool use_single_pipe = false;
+ struct msm_fb_splash_info *sinfo;
+
+ if (!mfd)
+ return -EINVAL;
+
+ fbi = mfd->fbi;
+ mdp5_data = mfd_to_mdp5_data(mfd);
+ mdata = mfd_to_mdata(mfd);
+ sinfo = &mfd->splash_info;
+
+ if (!mdp5_data || !mdp5_data->ctl)
+ return -EINVAL;
+
+ if (mutex_lock_interruptible(&mdp5_data->ov_lock))
+ return -EINVAL;
+
+ ret = mdss_mdp_overlay_start(mfd);
+ if (ret) {
+ pr_err("unable to start overlay %d (%d)\n", mfd->index, ret);
+ goto end;
+ }
+
+ mixer = mdss_mdp_mixer_get(mdp5_data->ctl, MDSS_MDP_MIXER_MUX_LEFT);
+ if (!mixer) {
+ pr_err("unable to retrieve mixer\n");
+ ret = -EINVAL;
+ goto end;
+ }
+
+ memset(&req, 0, sizeof(struct mdp_overlay));
+ /*
+ * use single pipe for
+ * 1. split display disabled
+ * 2. splash image is only on one side of panel
+ */
+ use_single_pipe =
+ !mfd->split_display ||
+ (mfd->split_display &&
+ ((dest_rect->x + dest_rect->w) < mfd->split_fb_left ||
+ dest_rect->x > mfd->split_fb_left));
+
+ req.src.width = src_rect->w;
+ if (use_single_pipe)
+ req.src_rect.w = src_rect->w;
+ else
+ req.src_rect.w = min_t(u16, mixer->width, src_rect->w >> 1);
+ req.dst_rect.w = req.src_rect.w;
+ req.src.height = req.dst_rect.h = req.src_rect.h =
+ src_rect->h;
+ req.src.format = SPLASH_IMAGE_FORMAT;
+ req.id = MSMFB_NEW_REQUEST;
+ req.z_order = MDSS_MDP_STAGE_0;
+ req.alpha = 0xff;
+ req.transp_mask = MDP_TRANSP_NOP;
+ req.dst_rect.x = dest_rect->x;
+ req.dst_rect.y = dest_rect->y;
+
+ pipe = mdss_mdp_splash_get_pipe(mfd, &req);
+ if (!pipe) {
+ pr_err("unable to allocate base pipe\n");
+ ret = -EINVAL;
+ goto end;
+ }
+
+ sinfo->pipe_ndx[0] = pipe->ndx;
+
+ if (!use_single_pipe) {
+ req.id = MSMFB_NEW_REQUEST;
+ req.src_rect.x = src_rect->x + min_t(u16, mixer->width,
+ src_rect->w - req.src_rect.w);
+ req.dst_rect.x = mixer->width;
+ pipe = mdss_mdp_splash_get_pipe(mfd, &req);
+ if (!pipe) {
+ pr_err("unable to allocate right base pipe\n");
+ mdss_mdp_overlay_release(mfd, sinfo->pipe_ndx[0]);
+ ret = -EINVAL;
+ goto end;
+ }
+ sinfo->pipe_ndx[1] = pipe->ndx;
+ }
+ mutex_unlock(&mdp5_data->ov_lock);
+
+ ret = mfd->mdp.kickoff_fnc(mfd, NULL);
+ if (ret) {
+ pr_err("error in displaying image\n");
+ mdss_mdp_overlay_release(mfd, sinfo->pipe_ndx[0] |
+ sinfo->pipe_ndx[1]);
+ }
+
+ return ret;
+end:
+ sinfo->pipe_ndx[0] = INVALID_PIPE_INDEX;
+ sinfo->pipe_ndx[1] = INVALID_PIPE_INDEX;
+ mutex_unlock(&mdp5_data->ov_lock);
+ return ret;
+}
+
+static int mdss_mdp_display_splash_image(struct msm_fb_data_type *mfd)
+{
+ int rc = 0;
+ struct fb_info *fbi;
+ uint32_t image_len = SPLASH_IMAGE_WIDTH * SPLASH_IMAGE_HEIGHT
+ * SPLASH_IMAGE_BPP;
+ struct mdss_mdp_img_rect src_rect, dest_rect;
+ struct msm_fb_splash_info *sinfo;
+
+ if (!mfd || !mfd->fbi) {
+ pr_err("invalid input parameter\n");
+ rc = -EINVAL;
+ goto end;
+ }
+
+ fbi = mfd->fbi;
+ sinfo = &mfd->splash_info;
+
+ if (SPLASH_IMAGE_WIDTH > fbi->var.xres ||
+ SPLASH_IMAGE_HEIGHT > fbi->var.yres ||
+ SPLASH_IMAGE_BPP > (fbi->var.bits_per_pixel >> 3)) {
+ pr_err("invalid splash parameter configuration\n");
+ rc = -EINVAL;
+ goto end;
+ }
+
+ sinfo->pipe_ndx[0] = INVALID_PIPE_INDEX;
+ sinfo->pipe_ndx[1] = INVALID_PIPE_INDEX;
+
+ src_rect.x = 0;
+ src_rect.y = 0;
+ dest_rect.w = src_rect.w = SPLASH_IMAGE_WIDTH;
+ dest_rect.h = src_rect.h = SPLASH_IMAGE_HEIGHT;
+ dest_rect.x = (fbi->var.xres >> 1) - (SPLASH_IMAGE_WIDTH >> 1);
+ dest_rect.y = (fbi->var.yres >> 1) - (SPLASH_IMAGE_HEIGHT >> 1);
+
+ rc = mdss_mdp_splash_alloc_memory(mfd, image_len);
+ if (rc) {
+ pr_err("splash buffer allocation failed\n");
+ goto end;
+ }
+
+ memcpy(sinfo->splash_buffer, splash_bgr888_image, image_len);
+
+ rc = mdss_mdp_splash_iommu_attach(mfd);
+ if (rc)
+ pr_debug("iommu dynamic attach failed\n");
+
+ rc = mdss_mdp_splash_kickoff(mfd, &src_rect, &dest_rect);
+ if (rc)
+ pr_err("splash image display failed\n");
+ else
+ sinfo->splash_pipe_allocated = true;
+end:
+ return rc;
+}
+
+static int mdss_mdp_splash_ctl_cb(struct notifier_block *self,
+ unsigned long event, void *data)
+{
+ struct msm_fb_splash_info *sinfo = container_of(self,
+ struct msm_fb_splash_info, notifier);
+ struct msm_fb_data_type *mfd;
+
+ if (!sinfo)
+ goto done;
+
+ mfd = container_of(sinfo, struct msm_fb_data_type, splash_info);
+
+ if (!mfd)
+ goto done;
+
+ if (event != MDP_NOTIFY_FRAME_DONE)
+ goto done;
+
+ if (!sinfo->frame_done_count) {
+ mdss_mdp_splash_unmap_splash_mem(mfd);
+ mdss_mdp_splash_cleanup(mfd, false);
+ /* wait for 2 frame done events before releasing memory */
+ } else if (sinfo->frame_done_count > MAX_FRAME_DONE_COUNT_WAIT &&
+ sinfo->splash_thread) {
+ complete(&sinfo->frame_done);
+ sinfo->splash_thread = NULL;
+ }
+
+ /* increase frame done count after pipes are staged from other client */
+ if (!sinfo->splash_pipe_allocated)
+ sinfo->frame_done_count++;
+done:
+ return NOTIFY_OK;
+}
+
+static int mdss_mdp_splash_thread(void *data)
+{
+ struct msm_fb_data_type *mfd = data;
+ struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
+ int ret = -EINVAL;
+
+ if (!mfd) {
+ pr_err("invalid input parameter\n");
+ goto end;
+ }
+
+ lock_fb_info(mfd->fbi);
+ ret = fb_blank(mfd->fbi, FB_BLANK_UNBLANK);
+ if (ret) {
+ pr_err("can't turn on fb!\n");
+ goto end;
+ }
+ unlock_fb_info(mfd->fbi);
+
+ mfd->bl_updated = true;
+ mutex_lock(&mfd->bl_lock);
+ mdss_fb_set_backlight(mfd, mfd->panel_info->bl_max >> 1);
+ mutex_unlock(&mfd->bl_lock);
+
+ init_completion(&mfd->splash_info.frame_done);
+
+ mfd->splash_info.notifier.notifier_call = mdss_mdp_splash_ctl_cb;
+ mdss_mdp_ctl_notifier_register(mdp5_data->ctl,
+ &mfd->splash_info.notifier);
+
+ ret = mdss_mdp_display_splash_image(mfd);
+ if (ret) {
+ /*
+ * keep thread alive to release dynamically allocated
+ * resources
+ */
+ pr_err("splash image display failed\n");
+ }
+
+ /* wait for second display complete to release splash resources */
+ ret = wait_for_completion_killable(&mfd->splash_info.frame_done);
+
+ mdss_mdp_splash_free_memory(mfd);
+
+ mdss_mdp_ctl_notifier_unregister(mdp5_data->ctl,
+ &mfd->splash_info.notifier);
+end:
+ return ret;
+}
+
+static __ref int mdss_mdp_splash_parse_dt(struct msm_fb_data_type *mfd)
+{
+ struct platform_device *pdev = mfd->pdev;
+ struct mdss_overlay_private *mdp5_mdata = mfd_to_mdp5_data(mfd);
+ int len = 0, rc = 0;
+ u32 offsets[2];
+
+ mfd->splash_info.splash_logo_enabled =
+ of_property_read_bool(pdev->dev.of_node,
+ "qcom,mdss-fb-splash-logo-enabled");
+
+ of_find_property(pdev->dev.of_node, "qcom,memblock-reserve", &len);
+ if (len < 1) {
+ pr_debug("mem reservation for splash screen fb not present\n");
+ rc = -EINVAL;
+ goto error;
+ }
+
+ len = len / sizeof(u32);
+
+ rc = of_property_read_u32_array(pdev->dev.of_node,
+ "qcom,memblock-reserve", offsets, len);
+ if (rc) {
+ pr_debug("error reading mem reserve settings for fb\n");
+ goto error;
+ }
+
+ if (!memblock_is_reserved(offsets[0])) {
+ pr_debug("failed to reserve memory for fb splash\n");
+ rc = -EINVAL;
+ goto error;
+ }
+
+ mdp5_mdata->splash_mem_addr = offsets[0];
+ mdp5_mdata->splash_mem_size = offsets[1];
+ pr_debug("memaddr=%x size=%x\n", mdp5_mdata->splash_mem_addr,
+ mdp5_mdata->splash_mem_size);
+
+error:
+ if (!rc && !mfd->panel_info->cont_splash_enabled &&
+ mdp5_mdata->splash_mem_addr) {
+ pr_debug("mem reservation not reqd if cont splash disabled\n");
+ memblock_free(mdp5_mdata->splash_mem_addr,
+ mdp5_mdata->splash_mem_size);
+ free_bootmem_late(mdp5_mdata->splash_mem_addr,
+ mdp5_mdata->splash_mem_size);
+ } else if (rc && mfd->panel_info->cont_splash_enabled) {
+ pr_err("no rsvd mem found in DT for splash screen\n");
+ } else {
+ rc = 0;
+ }
+
+ return rc;
+}
+
+int mdss_mdp_splash_init(struct msm_fb_data_type *mfd)
+{
+ int rc;
+
+ if (!mfd) {
+ rc = -EINVAL;
+ goto end;
+ }
+
+ rc = mdss_mdp_splash_parse_dt(mfd);
+ if (rc) {
+ pr_err("splash memory reserve failed\n");
+ goto end;
+ }
+
+ if (!mfd->splash_info.splash_logo_enabled) {
+ rc = -EINVAL;
+ goto end;
+ }
+
+ mfd->splash_info.splash_thread = kthread_run(mdss_mdp_splash_thread,
+ mfd, "mdss_fb_splash");
+
+ if (IS_ERR(mfd->splash_info.splash_thread)) {
+ pr_err("unable to start splash thread %d\n", mfd->index);
+ mfd->splash_info.splash_thread = NULL;
+ }
+
+end:
+ return rc;
+}
diff --git a/drivers/video/msm/mdss/mdss_mdp_splash_logo.h b/drivers/video/msm/mdss/mdss_mdp_splash_logo.h
new file mode 100644
index 0000000..0a4e83b
--- /dev/null
+++ b/drivers/video/msm/mdss/mdss_mdp_splash_logo.h
@@ -0,0 +1,43 @@
+/* Copyright (c) 2014, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef MDSS_MDP_SPLASH_LOGO
+#define MDSS_MDP_SPLASH_LOGO
+
+#include <linux/types.h>
+#include <linux/notifier.h>
+#include <linux/kthread.h>
+#include <linux/completion.h>
+
+struct msm_fb_splash_info {
+ struct task_struct *splash_thread;
+ bool splash_logo_enabled;
+ bool iommu_dynamic_attached;
+ struct notifier_block notifier;
+ uint32_t frame_done_count;
+ struct completion frame_done;
+
+ struct ion_handle *ion_handle;
+ dma_addr_t iova;
+ void *splash_buffer;
+ int pipe_ndx[2];
+ bool splash_pipe_allocated;
+};
+
+struct msm_fb_data_type;
+
+void mdss_mdp_release_splash_pipe(struct msm_fb_data_type *mfd);
+int mdss_mdp_splash_cleanup(struct msm_fb_data_type *mfd,
+ bool use_borderfill);
+int mdss_mdp_splash_init(struct msm_fb_data_type *mfd);
+
+#endif
diff --git a/drivers/video/msm/mdss/mdss_mdp_wb.c b/drivers/video/msm/mdss/mdss_mdp_wb.c
index 454183d..aa17472 100644
--- a/drivers/video/msm/mdss/mdss_mdp_wb.c
+++ b/drivers/video/msm/mdss/mdss_mdp_wb.c
@@ -671,77 +671,30 @@
int mdss_mdp_wb_get_format(struct msm_fb_data_type *mfd,
struct mdp_mixer_cfg *mixer_cfg)
{
- int dst_format;
struct mdss_mdp_ctl *ctl = mfd_to_ctl(mfd);
if (!ctl) {
pr_err("No panel data!\n");
return -EINVAL;
+ } else {
+ mixer_cfg->writeback_format = ctl->dst_format;
}
- switch (ctl->dst_format) {
- case MDP_RGB_888:
- dst_format = WB_FORMAT_RGB_888;
- break;
- case MDP_RGB_565:
- dst_format = WB_FORMAT_RGB_565;
- break;
- case MDP_XRGB_8888:
- dst_format = WB_FORMAT_xRGB_8888;
- break;
- case MDP_ARGB_8888:
- dst_format = WB_FORMAT_ARGB_8888;
- break;
- case MDP_BGRA_8888:
- dst_format = WB_FORMAT_BGRA_8888;
- break;
- case MDP_BGRX_8888:
- dst_format = WB_FORMAT_BGRX_8888;
- break;
- case MDP_Y_CBCR_H2V2_VENUS:
- dst_format = WB_FORMAT_NV12;
- break;
- default:
- return -EINVAL;
- }
- mixer_cfg->writeback_format = dst_format;
return 0;
}
-int mdss_mdp_wb_set_format(struct msm_fb_data_type *mfd, int dst_format)
+int mdss_mdp_wb_set_format(struct msm_fb_data_type *mfd, u32 dst_format)
{
struct mdss_mdp_ctl *ctl = mfd_to_ctl(mfd);
if (!ctl) {
pr_err("No panel data!\n");
return -EINVAL;
- }
-
- switch (dst_format) {
- case WB_FORMAT_RGB_888:
- ctl->dst_format = MDP_RGB_888;
- break;
- case WB_FORMAT_RGB_565:
- ctl->dst_format = MDP_RGB_565;
- break;
- case WB_FORMAT_xRGB_8888:
- ctl->dst_format = MDP_XRGB_8888;
- break;
- case WB_FORMAT_ARGB_8888:
- ctl->dst_format = MDP_ARGB_8888;
- break;
- case WB_FORMAT_BGRA_8888:
- ctl->dst_format = MDP_BGRA_8888;
- break;
- case WB_FORMAT_BGRX_8888:
- ctl->dst_format = MDP_BGRX_8888;
- break;
- case WB_FORMAT_NV12:
- ctl->dst_format = MDP_Y_CBCR_H2V2_VENUS;
- break;
- default:
- pr_err("wfd format not supported\n");
+ } else if (dst_format >= MDP_IMGTYPE_LIMIT2) {
+ pr_err("Invalid dst format=%u\n", dst_format);
return -EINVAL;
+ } else {
+ ctl->dst_format = dst_format;
}
pr_debug("wfd format %d\n", ctl->dst_format);
diff --git a/drivers/video/msm/mdss/mdss_panel.h b/drivers/video/msm/mdss/mdss_panel.h
index 135a00a..c5ac72e 100644
--- a/drivers/video/msm/mdss/mdss_panel.h
+++ b/drivers/video/msm/mdss/mdss_panel.h
@@ -274,6 +274,17 @@
u32 lossy_mode_idx;
};
+struct mdss_mdp_pp_tear_check {
+ u32 tear_check_en;
+ u32 sync_cfg_height;
+ u32 vsync_init_val;
+ u32 sync_threshold_start;
+ u32 sync_threshold_continue;
+ u32 start_pos;
+ u32 rd_ptr_irq;
+ u32 refx100;
+};
+
struct mdss_panel_info {
u32 xres;
u32 yres;
@@ -316,6 +327,7 @@
u32 panel_power_on;
uint32_t panel_dead;
+ struct mdss_mdp_pp_tear_check te;
struct lcd_panel_info lcdc;
struct fbc_panel_info fbc;
diff --git a/drivers/video/msm/mdss/msm_mdss_io_8974.c b/drivers/video/msm/mdss/msm_mdss_io_8974.c
index a0663e3..34e383e 100644
--- a/drivers/video/msm/mdss/msm_mdss_io_8974.c
+++ b/drivers/video/msm/mdss/msm_mdss_io_8974.c
@@ -259,7 +259,7 @@
return 0;
}
-int mdss_dsi_bus_clk_start(struct mdss_dsi_ctrl_pdata *ctrl_pdata)
+static int mdss_dsi_bus_clk_start(struct mdss_dsi_ctrl_pdata *ctrl_pdata)
{
int rc = 0;
@@ -303,7 +303,7 @@
return rc;
}
-void mdss_dsi_bus_clk_stop(struct mdss_dsi_ctrl_pdata *ctrl_pdata)
+static void mdss_dsi_bus_clk_stop(struct mdss_dsi_ctrl_pdata *ctrl_pdata)
{
if (ctrl_pdata->mmss_misc_ahb_clk)
clk_disable_unprepare(ctrl_pdata->mmss_misc_ahb_clk);
@@ -408,11 +408,6 @@
pr_debug("%s: ndx=%d\n", __func__, ctrl_pdata->ndx);
- if (ctrl_pdata->mdss_dsi_clk_on) {
- pr_info("%s: mdss_dsi_clks already ON\n", __func__);
- return 0;
- }
-
rc = clk_enable(ctrl_pdata->esc_clk);
if (rc) {
pr_err("%s: Failed to enable dsi esc clk\n", __func__);
@@ -431,8 +426,6 @@
goto pixel_clk_err;
}
- ctrl_pdata->mdss_dsi_clk_on = 1;
-
return rc;
pixel_clk_err:
@@ -452,19 +445,12 @@
pr_debug("%s: ndx=%d\n", __func__, ctrl_pdata->ndx);
- if (ctrl_pdata->mdss_dsi_clk_on == 0) {
- pr_info("%s: mdss_dsi_clks already OFF\n", __func__);
- return;
- }
-
clk_disable(ctrl_pdata->esc_clk);
clk_disable(ctrl_pdata->pixel_clk);
clk_disable(ctrl_pdata->byte_clk);
-
- ctrl_pdata->mdss_dsi_clk_on = 0;
}
-int mdss_dsi_link_clk_start(struct mdss_dsi_ctrl_pdata *ctrl)
+static int mdss_dsi_link_clk_start(struct mdss_dsi_ctrl_pdata *ctrl)
{
int rc = 0;
@@ -494,90 +480,197 @@
return rc;
}
-void mdss_dsi_link_clk_stop(struct mdss_dsi_ctrl_pdata *ctrl)
+static void mdss_dsi_link_clk_stop(struct mdss_dsi_ctrl_pdata *ctrl)
{
mdss_dsi_link_clk_disable(ctrl);
mdss_dsi_link_clk_unprepare(ctrl);
}
-static void mdss_dsi_clk_ctrl_sub(struct mdss_dsi_ctrl_pdata *ctrl, int enable)
+static int __mdss_dsi_update_clk_cnt(u32 *clk_cnt, int enable)
{
int changed = 0;
if (enable) {
- if (ctrl->clk_cnt_sub == 0)
+ if (*clk_cnt == 0)
changed++;
- ctrl->clk_cnt_sub++;
+ (*clk_cnt)++;
} else {
- if (ctrl->clk_cnt_sub) {
- ctrl->clk_cnt_sub--;
- if (ctrl->clk_cnt_sub == 0)
+ if (*clk_cnt != 0) {
+ (*clk_cnt)--;
+ if (*clk_cnt == 0)
changed++;
} else {
- pr_debug("%s: Can not be turned off\n", __func__);
+ pr_debug("%s: clk cnt already zero\n", __func__);
}
}
- pr_debug("%s: ndx=%d clk_cnt_sub=%d changed=%d enable=%d\n",
- __func__, ctrl->ndx, ctrl->clk_cnt_sub, changed, enable);
- if (changed) {
- if (enable) {
- if (mdss_dsi_bus_clk_start(ctrl) == 0)
- mdss_dsi_link_clk_start(ctrl);
- } else {
- mdss_dsi_link_clk_stop(ctrl);
- mdss_dsi_bus_clk_stop(ctrl);
- }
+ return changed;
+}
+
+static int mdss_dsi_clk_ctrl_sub(struct mdss_dsi_ctrl_pdata *ctrl,
+ u8 clk_type, int enable)
+{
+ int rc = 0;
+
+ if (!ctrl) {
+ pr_err("%s: Invalid arg\n", __func__);
+ return -EINVAL;
}
+
+ pr_debug("%s: ndx=%d clk_type=%08x enable=%d\n", __func__,
+ ctrl->ndx, clk_type, enable);
+
+ if (enable) {
+ if (clk_type & DSI_BUS_CLKS) {
+ rc = mdss_dsi_bus_clk_start(ctrl);
+ if (rc) {
+ pr_err("Failed to start bus clocks. rc=%d\n",
+ rc);
+ goto error;
+ }
+ }
+ if (clk_type & DSI_LINK_CLKS) {
+ rc = mdss_dsi_link_clk_start(ctrl);
+ if (rc) {
+ pr_err("Failed to start link clocks. rc=%d\n",
+ rc);
+ if (clk_type & DSI_BUS_CLKS)
+ mdss_dsi_bus_clk_stop(ctrl);
+ goto error;
+ }
+ }
+ } else {
+ if (clk_type & DSI_LINK_CLKS)
+ mdss_dsi_link_clk_stop(ctrl);
+ if (clk_type & DSI_BUS_CLKS)
+ mdss_dsi_bus_clk_stop(ctrl);
+ }
+
+error:
+ return rc;
}
static DEFINE_MUTEX(dsi_clk_lock); /* per system */
-bool __mdss_dsi_clk_enabled(struct mdss_dsi_ctrl_pdata *ctrl)
+bool __mdss_dsi_clk_enabled(struct mdss_dsi_ctrl_pdata *ctrl, u8 clk_type)
{
- bool enabled;
+ bool bus_enabled = true;
+ bool link_enabled = true;
+
mutex_lock(&dsi_clk_lock);
- enabled = ctrl->clk_cnt ? true : false;
+ if (clk_type & DSI_BUS_CLKS)
+ bus_enabled = ctrl->bus_clk_cnt ? true : false;
+ if (clk_type & DSI_LINK_CLKS)
+ link_enabled = ctrl->link_clk_cnt ? true : false;
mutex_unlock(&dsi_clk_lock);
- return enabled;
+ return bus_enabled && link_enabled;
}
-void mdss_dsi_clk_ctrl(struct mdss_dsi_ctrl_pdata *ctrl, int enable)
+int mdss_dsi_clk_ctrl(struct mdss_dsi_ctrl_pdata *ctrl,
+ u8 clk_type, int enable)
{
- int changed = 0;
- struct mdss_dsi_ctrl_pdata *sctrl = NULL;
+ int rc = 0;
+ int changed = 0, m_changed = 0;
+ struct mdss_dsi_ctrl_pdata *mctrl = NULL;
+
+ if (!ctrl) {
+ pr_err("%s: Invalid arg\n", __func__);
+ return -EINVAL;
+ }
+
+ /*
+ * In broadcast mode, we need to enable clocks for the
+ * master controller as well when enabling clocks for the
+ * slave controller
+ */
+ if (mdss_dsi_is_slave_ctrl(ctrl)) {
+ mctrl = mdss_dsi_get_master_ctrl();
+ if (!mctrl)
+ pr_warn("%s: Unable to get master control\n", __func__);
+ }
+
+ pr_debug("%s++: ndx=%d clk_type=%d bus_clk_cnt=%d link_clk_cnt=%d",
+ __func__, ctrl->ndx, clk_type, ctrl->bus_clk_cnt,
+ ctrl->link_clk_cnt);
+ pr_debug("%s++: mctrl=%s m_bus_clk_cnt=%d m_link_clk_cnt=%d\n, enable=%d\n",
+ __func__, mctrl ? "yes" : "no", mctrl ? mctrl->bus_clk_cnt : -1,
+ mctrl ? mctrl->link_clk_cnt : -1, enable);
mutex_lock(&dsi_clk_lock);
- if (enable) {
- if (ctrl->clk_cnt == 0)
- changed++;
- ctrl->clk_cnt++;
- } else {
- if (ctrl->clk_cnt) {
- ctrl->clk_cnt--;
- if (ctrl->clk_cnt == 0)
- changed++;
- } else {
- pr_debug("%s: Can not be turned off\n", __func__);
- }
+ if (clk_type & DSI_BUS_CLKS) {
+ changed = __mdss_dsi_update_clk_cnt(&ctrl->bus_clk_cnt,
+ enable);
+ if (changed && mctrl)
+ m_changed = __mdss_dsi_update_clk_cnt(
+ &mctrl->bus_clk_cnt, enable);
}
- pr_debug("%s: ndx=%d clk_cnt=%d changed=%d enable=%d\n",
- __func__, ctrl->ndx, ctrl->clk_cnt, changed, enable);
- if (ctrl->flags & DSI_FLAG_CLOCK_MASTER)
- sctrl = mdss_dsi_ctrl_slave(ctrl);
+ if (clk_type & DSI_LINK_CLKS) {
+ changed += __mdss_dsi_update_clk_cnt(&ctrl->link_clk_cnt,
+ enable);
+ if (changed && mctrl)
+ m_changed += __mdss_dsi_update_clk_cnt(
+ &mctrl->link_clk_cnt, enable);
+ }
if (changed) {
- if (enable && sctrl)
- mdss_dsi_clk_ctrl_sub(sctrl, enable);
+ if (enable && m_changed) {
+ rc = mdss_dsi_clk_ctrl_sub(mctrl, clk_type, enable);
+ if (rc) {
+ pr_err("Failed to start mctrl clocks. rc=%d\n",
+ rc);
+ goto error_mctrl_start;
+ }
+ }
- mdss_dsi_clk_ctrl_sub(ctrl, enable);
+ rc = mdss_dsi_clk_ctrl_sub(ctrl, clk_type, enable);
+ if (rc) {
+ pr_err("Failed to %s ctrl clocks. rc=%d\n",
+ (enable ? "start" : "stop"), rc);
+ goto error_ctrl;
+ }
- if (!enable && sctrl)
- mdss_dsi_clk_ctrl_sub(sctrl, enable);
+ if (!enable && m_changed) {
+ rc = mdss_dsi_clk_ctrl_sub(mctrl, clk_type, enable);
+ if (rc) {
+ pr_err("Failed to stop mctrl clocks. rc=%d\n",
+ rc);
+ goto error_mctrl_stop;
+ }
+ }
}
+ goto no_error;
+
+error_mctrl_stop:
+ mdss_dsi_clk_ctrl_sub(ctrl, clk_type, enable ? 0 : 1);
+error_ctrl:
+ if (enable && m_changed)
+ mdss_dsi_clk_ctrl_sub(mctrl, clk_type, 0);
+error_mctrl_start:
+ if (clk_type & DSI_BUS_CLKS) {
+ if (mctrl)
+ __mdss_dsi_update_clk_cnt(&mctrl->bus_clk_cnt,
+ enable ? 0 : 1);
+ __mdss_dsi_update_clk_cnt(&ctrl->bus_clk_cnt, enable ? 0 : 1);
+ }
+ if (clk_type & DSI_LINK_CLKS) {
+ if (mctrl)
+ __mdss_dsi_update_clk_cnt(&mctrl->link_clk_cnt,
+ enable ? 0 : 1);
+ __mdss_dsi_update_clk_cnt(&ctrl->link_clk_cnt, enable ? 0 : 1);
+ }
+
+no_error:
mutex_unlock(&dsi_clk_lock);
+ pr_debug("%s++: ndx=%d clk_type=%d bus_clk_cnt=%d link_clk_cnt=%d changed=%d",
+ __func__, ctrl->ndx, clk_type, ctrl->bus_clk_cnt,
+ ctrl->link_clk_cnt, changed);
+ pr_debug("%s++: mctrl=%s m_bus_clk_cnt=%d m_link_clk_cnt=%d\n, m_changed=%d, enable=%d\n",
+ __func__, mctrl ? "yes" : "no", mctrl ? mctrl->bus_clk_cnt : -1,
+ mctrl ? mctrl->link_clk_cnt : -1, m_changed, enable);
+
+ return rc;
}
void mdss_dsi_phy_sw_reset(unsigned char *ctrl_base)
@@ -594,29 +687,39 @@
void mdss_dsi_phy_disable(struct mdss_dsi_ctrl_pdata *ctrl)
{
- static struct mdss_dsi_ctrl_pdata *left_ctrl;
+ struct mdss_dsi_ctrl_pdata *ctrl0 = NULL;
if (ctrl == NULL) {
pr_err("%s: Invalid input data\n", __func__);
return;
}
- if (left_ctrl &&
- (ctrl->panel_data.panel_info.pdest == DISPLAY_1))
+ /*
+ * In dual-dsi configuration, the phy should be disabled for the
+ * first controller only when the second controller is disabled.
+ * This is true regardless of whether broadcast mode is enabled
+ * or not.
+ */
+ if ((ctrl->ndx == DSI_CTRL_0) &&
+ mdss_dsi_get_ctrl_by_index(DSI_CTRL_1)) {
+ pr_debug("%s: Dual dsi detected. skipping config for ctrl%d\n",
+ __func__, ctrl->ndx);
return;
-
- if (left_ctrl &&
- (ctrl->panel_data.panel_info.pdest
- ==
- DISPLAY_2)) {
- MIPI_OUTP(left_ctrl->ctrl_base + 0x0470,
- 0x000);
- MIPI_OUTP(left_ctrl->ctrl_base + 0x0598,
- 0x000);
}
- MIPI_OUTP(ctrl->ctrl_base + 0x0470, 0x000);
- MIPI_OUTP(ctrl->ctrl_base + 0x0598, 0x000);
+ if (ctrl->ndx == DSI_CTRL_1) {
+ ctrl0 = mdss_dsi_get_ctrl_by_index(DSI_CTRL_0);
+ if (ctrl0) {
+ MIPI_OUTP(ctrl0->phy_io.base + 0x0170, 0x000);
+ MIPI_OUTP(ctrl0->phy_io.base + 0x0298, 0x000);
+ } else {
+ pr_warn("%s: Unable to get control%d\n",
+ __func__, DSI_CTRL_0);
+ }
+ }
+
+ MIPI_OUTP(ctrl->phy_io.base + 0x0170, 0x000);
+ MIPI_OUTP(ctrl->phy_io.base + 0x0298, 0x000);
/*
* Wait for the registers writes to complete in order to
@@ -629,7 +732,7 @@
{
struct mdss_dsi_phy_ctrl *pd;
int i, off, ln, offset;
- struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL;
+ struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL, *temp_ctrl = NULL;
ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata,
panel_data);
@@ -637,67 +740,75 @@
pr_err("%s: Invalid input data\n", __func__);
return;
}
+ temp_ctrl = ctrl_pdata;
pd = &(((ctrl_pdata->panel_data).panel_info.mipi).dsi_phy_db);
/* Strength ctrl 0 */
- MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x0484, pd->strength[0]);
+ MIPI_OUTP((ctrl_pdata->phy_io.base) + 0x0184, pd->strength[0]);
- /* phy regulator ctrl settings. Both the DSI controller
- have one regulator */
- if ((ctrl_pdata->panel_data).panel_info.pdest == DISPLAY_1)
- off = 0x0580;
- else
- off = 0x0580 - 0x600;
+ /*
+ * Phy regulator ctrl settings.
+ * In dual dsi configuration, the second controller also uses
+ * the regulators of the first controller, irrespective of whether
+ * broadcast mode is enabled or not.
+ */
+ if (ctrl_pdata->ndx == DSI_CTRL_1) {
+ temp_ctrl = mdss_dsi_get_ctrl_by_index(DSI_CTRL_0);
+ if (!temp_ctrl) {
+ pr_err("%s: Unable to get master ctrl\n", __func__);
+ return;
+ }
+ }
/* Regulator ctrl 0 */
- MIPI_OUTP((ctrl_pdata->ctrl_base) + off + (4 * 0), 0x0);
+ MIPI_OUTP((temp_ctrl->phy_io.base) + 0x280, 0x0);
/* Regulator ctrl - CAL_PWR_CFG */
- MIPI_OUTP((ctrl_pdata->ctrl_base) + off + (4 * 6), pd->regulator[6]);
+ MIPI_OUTP((temp_ctrl->phy_io.base) + 0x298, pd->regulator[6]);
/* Regulator ctrl - TEST */
- MIPI_OUTP((ctrl_pdata->ctrl_base) + off + (4 * 5), pd->regulator[5]);
+ MIPI_OUTP((temp_ctrl->phy_io.base) + 0x294, pd->regulator[5]);
/* Regulator ctrl 3 */
- MIPI_OUTP((ctrl_pdata->ctrl_base) + off + (4 * 3), pd->regulator[3]);
+ MIPI_OUTP((temp_ctrl->phy_io.base) + 0x28c, pd->regulator[3]);
/* Regulator ctrl 2 */
- MIPI_OUTP((ctrl_pdata->ctrl_base) + off + (4 * 2), pd->regulator[2]);
+ MIPI_OUTP((temp_ctrl->phy_io.base) + 0x288, pd->regulator[2]);
/* Regulator ctrl 1 */
- MIPI_OUTP((ctrl_pdata->ctrl_base) + off + (4 * 1), pd->regulator[1]);
+ MIPI_OUTP((temp_ctrl->phy_io.base) + 0x284, pd->regulator[1]);
/* Regulator ctrl 0 */
- MIPI_OUTP((ctrl_pdata->ctrl_base) + off + (4 * 0), pd->regulator[0]);
+ MIPI_OUTP((temp_ctrl->phy_io.base) + 0x280, pd->regulator[0]);
/* Regulator ctrl 4 */
- MIPI_OUTP((ctrl_pdata->ctrl_base) + off + (4 * 4), pd->regulator[4]);
+ MIPI_OUTP((temp_ctrl->phy_io.base) + 0x290, pd->regulator[4]);
/* LDO ctrl 0 */
if ((ctrl_pdata->panel_data).panel_info.pdest == DISPLAY_1)
- MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x4dc, 0x00);
+ MIPI_OUTP((ctrl_pdata->phy_io.base) + 0x1dc, 0x00);
else
- MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x4dc, 0x00);
+ MIPI_OUTP((ctrl_pdata->phy_io.base) + 0x1dc, 0x00);
- off = 0x0440; /* phy timing ctrl 0 - 11 */
+ off = 0x0140; /* phy timing ctrl 0 - 11 */
for (i = 0; i < 12; i++) {
- MIPI_OUTP((ctrl_pdata->ctrl_base) + off, pd->timing[i]);
+ MIPI_OUTP((ctrl_pdata->phy_io.base) + off, pd->timing[i]);
wmb();
off += 4;
}
/* MMSS_DSI_0_PHY_DSIPHY_CTRL_1 */
- MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x0474, 0x00);
+ MIPI_OUTP((ctrl_pdata->phy_io.base) + 0x0174, 0x00);
/* MMSS_DSI_0_PHY_DSIPHY_CTRL_0 */
- MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x0470, 0x5f);
+ MIPI_OUTP((ctrl_pdata->phy_io.base) + 0x0170, 0x5f);
wmb();
/* Strength ctrl 1 */
- MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x0488, pd->strength[1]);
+ MIPI_OUTP((ctrl_pdata->phy_io.base) + 0x0188, pd->strength[1]);
wmb();
/* 4 lanes + clk lane configuration */
/* lane config n * (0 - 4) & DataPath setup */
for (ln = 0; ln < 5; ln++) {
- off = 0x0300 + (ln * 0x40);
+ off = (ln * 0x40);
for (i = 0; i < 9; i++) {
offset = i + (ln * 9);
- MIPI_OUTP((ctrl_pdata->ctrl_base) + off,
+ MIPI_OUTP((ctrl_pdata->phy_io.base) + off,
pd->lanecfg[offset]);
wmb();
off += 4;
@@ -705,19 +816,19 @@
}
/* MMSS_DSI_0_PHY_DSIPHY_CTRL_0 */
- MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x0470, 0x5f);
+ MIPI_OUTP((ctrl_pdata->phy_io.base) + 0x0170, 0x5f);
wmb();
/* DSI_0_PHY_DSIPHY_GLBL_TEST_CTRL */
if ((ctrl_pdata->panel_data).panel_info.pdest == DISPLAY_1)
- MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x04d4, 0x01);
+ MIPI_OUTP((ctrl_pdata->phy_io.base) + 0x01d4, 0x01);
else
- MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x04d4, 0x00);
+ MIPI_OUTP((ctrl_pdata->phy_io.base) + 0x01d4, 0x00);
wmb();
- off = 0x04b4; /* phy BIST ctrl 0 - 5 */
+ off = 0x01b4; /* phy BIST ctrl 0 - 5 */
for (i = 0; i < 6; i++) {
- MIPI_OUTP((ctrl_pdata->ctrl_base) + off, pd->bistctrl[i]);
+ MIPI_OUTP((ctrl_pdata->phy_io.base) + off, pd->bistctrl[i]);
wmb();
off += 4;
}
diff --git a/drivers/video/msm/vidc/common/dec/vdec.c b/drivers/video/msm/vidc/common/dec/vdec.c
index 276a48f..0958e07 100755
--- a/drivers/video/msm/vidc/common/dec/vdec.c
+++ b/drivers/video/msm/vidc/common/dec/vdec.c
@@ -899,6 +899,12 @@
vcd_meta_buffer->offset = meta_buffers->offset;
vcd_meta_buffer->pmem_fd_iommu = meta_buffers->pmem_fd_iommu;
+ if (meta_buffers->count > MAX_META_BUFFERS) {
+ ERR("meta buffers maximum count reached, count = %d",
+ meta_buffers->count);
+ return false;
+ }
+
if (!vcd_get_ion_status()) {
pr_err("PMEM Not available\n");
return false;
@@ -1104,6 +1110,12 @@
vcd_h264_mv_buffer->pmem_fd = mv_data->pmem_fd;
vcd_h264_mv_buffer->offset = mv_data->offset;
+ if (mv_data->count > MAX_MV_BUFFERS) {
+ ERR("MV buffers maximum count reached, count = %d",
+ mv_data->count);
+ return false;
+ }
+
if (!vcd_get_ion_status()) {
pr_err("PMEM not available\n");
return false;
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 48609bc..6577ae3 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -43,6 +43,7 @@
#include <linux/kthread.h>
#include <linux/freezer.h>
+#include <linux/ratelimit.h>
#include "ext4.h"
#include "ext4_extents.h"
@@ -667,7 +668,7 @@
va_start(args, fmt);
vaf.fmt = fmt;
vaf.va = &args;
- printk("%sEXT4-fs (%s): %pV\n", prefix, sb->s_id, &vaf);
+ printk_ratelimited("%sEXT4-fs (%s): %pV\n", prefix, sb->s_id, &vaf);
va_end(args);
}
diff --git a/include/linux/android_alarm.h b/include/linux/android_alarm.h
index b017caa..65227ad 100644
--- a/include/linux/android_alarm.h
+++ b/include/linux/android_alarm.h
@@ -71,7 +71,7 @@
void alarm_start_range(struct alarm *alarm, ktime_t start, ktime_t end);
int alarm_try_to_cancel(struct alarm *alarm);
int alarm_cancel(struct alarm *alarm);
-void set_power_on_alarm(long secs);
+void set_power_on_alarm(long secs, bool enable);
ktime_t alarm_get_elapsed_realtime(void);
/* set rtc while preserving elapsed realtime */
diff --git a/include/linux/batterydata-lib.h b/include/linux/batterydata-lib.h
index ff38eb6..f556236 100644
--- a/include/linux/batterydata-lib.h
+++ b/include/linux/batterydata-lib.h
@@ -27,6 +27,7 @@
#define MAX_SINGLE_LUT_COLS 20
#define MAX_BATT_ID_NUM 4
+#define DEGC_SCALE 10
struct single_row_lut {
int x[MAX_SINGLE_LUT_COLS];
diff --git a/include/linux/iopoll.h b/include/linux/iopoll.h
index b882fe2..d085e03 100644
--- a/include/linux/iopoll.h
+++ b/include/linux/iopoll.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012-2013 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2014 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,7 +17,7 @@
#include <linux/kernel.h>
#include <linux/types.h>
-#include <linux/jiffies.h>
+#include <linux/hrtimer.h>
#include <linux/delay.h>
#include <asm-generic/errno.h>
#include <asm/io.h>
@@ -36,13 +36,13 @@
*/
#define readl_poll_timeout(addr, val, cond, sleep_us, timeout_us) \
({ \
- unsigned long timeout = jiffies + usecs_to_jiffies(timeout_us); \
+ ktime_t timeout = ktime_add_us(ktime_get(), timeout_us); \
might_sleep_if(timeout_us); \
for (;;) { \
(val) = readl(addr); \
if (cond) \
break; \
- if (timeout_us && time_after(jiffies, timeout)) { \
+ if (timeout_us && ktime_compare(ktime_get(), timeout) > 0) { \
(val) = readl(addr); \
break; \
} \
diff --git a/include/linux/ktime.h b/include/linux/ktime.h
index 603bec2..04ad123 100644
--- a/include/linux/ktime.h
+++ b/include/linux/ktime.h
@@ -289,6 +289,25 @@
return cmp1.tv64 == cmp2.tv64;
}
+/**
+ * ktime_compare - Compares two ktime_t variables for less, greater or equal
+ * @cmp1: comparable1
+ * @cmp2: comparable2
+ *
+ * Returns ...
+ * cmp1 < cmp2: return <0
+ * cmp1 == cmp2: return 0
+ * cmp1 > cmp2: return >0
+ */
+static inline int ktime_compare(const ktime_t cmp1, const ktime_t cmp2)
+{
+ if (cmp1.tv64 < cmp2.tv64)
+ return -1;
+ if (cmp1.tv64 > cmp2.tv64)
+ return 1;
+ return 0;
+}
+
static inline s64 ktime_to_us(const ktime_t kt)
{
struct timeval tv = ktime_to_timeval(kt);
diff --git a/include/linux/mfd/wcd9xxx/core.h b/include/linux/mfd/wcd9xxx/core.h
index 85be7c3..a8e792b 100644
--- a/include/linux/mfd/wcd9xxx/core.h
+++ b/include/linux/mfd/wcd9xxx/core.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2014, 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
@@ -133,6 +133,7 @@
u16 grph; /* slimbus group handle */
unsigned long ch_mask;
wait_queue_head_t dai_wait;
+ bool bus_down_in_recovery;
};
#define WCD9XXX_CH(xport, xshift) \
diff --git a/include/linux/mfd/wcd9xxx/wcd9xxx-slimslave.h b/include/linux/mfd/wcd9xxx/wcd9xxx-slimslave.h
index 066a423..263552b 100644
--- a/include/linux/mfd/wcd9xxx/wcd9xxx-slimslave.h
+++ b/include/linux/mfd/wcd9xxx/wcd9xxx-slimslave.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2014, 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
@@ -112,5 +112,6 @@
int wcd9xxx_rx_vport_validation(u32 port_id,
struct list_head *codec_dai_list);
int wcd9xxx_tx_vport_validation(u32 vtable, u32 port_id,
- struct wcd9xxx_codec_dai_data *codec_dai);
+ struct wcd9xxx_codec_dai_data *codec_dai,
+ u32 num_codec_dais);
#endif /* __WCD9310_SLIMSLAVE_H_ */
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index 8b604e3..7d51070 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -454,6 +454,7 @@
#define CID_MANFID_TOSHIBA 0x11
#define CID_MANFID_MICRON 0x13
#define CID_MANFID_SAMSUNG 0x15
+#define CID_MANFID_KINGSTON 0x70
#define CID_MANFID_HYNIX 0x90
#define END_FIXUP { 0 }
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index bff056d..2ed8a05 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -197,6 +197,12 @@
void *handler_priv;
};
+enum dev_state {
+ DEV_SUSPENDING = 1,
+ DEV_SUSPENDED,
+ DEV_RESUMED,
+};
+
struct mmc_host {
struct device *parent;
struct device class_dev;
@@ -418,9 +424,12 @@
bool enable;
bool initialized;
bool in_progress;
+ /* freq. transitions are not allowed in invalid state */
+ bool invalid_state;
struct delayed_work work;
enum mmc_load state;
} clk_scaling;
+ enum dev_state dev_status;
unsigned long private[0] ____cacheline_aligned;
};
diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h
index 08ca341..9bdf508 100644
--- a/include/linux/mmc/sdhci.h
+++ b/include/linux/mmc/sdhci.h
@@ -113,39 +113,37 @@
* be called twice.
*/
#define SDHCI_QUIRK2_SLOW_INT_CLR (1<<2)
-/* Ignore CMD CRC errors for tuning commands */
-#define SDHCI_QUIRK2_IGNORE_CMDCRC_FOR_TUNING (1<<3)
/*
* If the base clock can be scalable, then there should be no further
* clock dividing as the input clock itself will be scaled down to
* required frequency.
*/
-#define SDHCI_QUIRK2_ALWAYS_USE_BASE_CLOCK (1<<4)
+#define SDHCI_QUIRK2_ALWAYS_USE_BASE_CLOCK (1<<3)
/*
* Dont use the max_discard_to in sdhci driver so that the maximum discard
* unit gets picked by the mmc queue. Otherwise, it takes a long time for
* secure discard kind of operations to complete.
*/
-#define SDHCI_QUIRK2_USE_MAX_DISCARD_SIZE (1<<5)
+#define SDHCI_QUIRK2_USE_MAX_DISCARD_SIZE (1<<4)
/*
* Ignore data timeout error for R1B commands as there will be no
* data associated and the busy timeout value for these commands
* could be lager than the maximum timeout value that controller
* can handle.
*/
-#define SDHCI_QUIRK2_IGNORE_DATATOUT_FOR_R1BCMD (1<<6)
+#define SDHCI_QUIRK2_IGNORE_DATATOUT_FOR_R1BCMD (1<<5)
/*
* The preset value registers are not properly initialized by
* some hardware and hence preset value must not be enabled for
* such controllers.
*/
-#define SDHCI_QUIRK2_BROKEN_PRESET_VALUE (1<<7)
+#define SDHCI_QUIRK2_BROKEN_PRESET_VALUE (1<<6)
/*
* Some controllers define the usage of 0xF in data timeout counter
* register (0x2E) which is actually a reserved bit as per
* specification.
*/
-#define SDHCI_QUIRK2_USE_RESERVED_MAX_TIMEOUT (1<<8)
+#define SDHCI_QUIRK2_USE_RESERVED_MAX_TIMEOUT (1<<7)
/*
* This is applicable for controllers that advertize timeout clock
* value in capabilities register (bit 5-0) as just 50MHz whereas the
@@ -158,8 +156,13 @@
* will be used in such cases to avoid controller mulplication when timeout is
* calculated based on the base clock.
*/
-#define SDHCI_QUIRK2_DIVIDE_TOUT_BY_4 (1 << 9)
+#define SDHCI_QUIRK2_DIVIDE_TOUT_BY_4 (1 << 8)
+/*
+ * Some SDHC controllers are unable to handle data-end bit error in
+ * 1-bit mode of SDIO.
+ */
+#define SDHCI_QUIRK2_IGN_DATA_END_BIT_ERROR (1<<9)
int irq; /* Device IRQ */
void __iomem *ioaddr; /* Mapped address */
@@ -252,6 +255,9 @@
struct mutex ios_mutex;
enum sdhci_power_policy power_policy;
+ bool irq_enabled; /* host irq status flag */
+ bool async_int_supp; /* async support to rxv int, when clks are off */
+ bool disable_sdio_irq_deferred; /* status of disabling sdio irq */
u32 auto_cmd_err_sts;
unsigned long private[0] ____cacheline_aligned;
};
diff --git a/include/linux/msm_ipa.h b/include/linux/msm_ipa.h
index b2229d3..9368d8f 100644
--- a/include/linux/msm_ipa.h
+++ b/include/linux/msm_ipa.h
@@ -64,6 +64,11 @@
#define IPA_RESOURCE_NAME_MAX 20
/**
+ * max number of interface properties
+ */
+#define IPA_NUM_PROPS_MAX 20
+
+/**
* size of the mac address
*/
#define IPA_MAC_ADDR_SIZE 6
diff --git a/include/linux/msm_mdp.h b/include/linux/msm_mdp.h
index e332368..00eba66 100644
--- a/include/linux/msm_mdp.h
+++ b/include/linux/msm_mdp.h
@@ -85,6 +85,30 @@
#define MDP_IMGTYPE2_START 0x10000
#define MSMFB_DRIVER_VERSION 0xF9E8D701
+/* HW Revisions for different MDSS targets */
+#define MDSS_GET_MAJOR(rev) ((rev) >> 28)
+#define MDSS_GET_MINOR(rev) (((rev) >> 16) & 0xFFF)
+#define MDSS_GET_STEP(rev) ((rev) & 0xFFFF)
+#define MDSS_GET_MAJOR_MINOR(rev) ((rev) >> 16)
+
+#define IS_MDSS_MAJOR_MINOR_SAME(rev1, rev2) \
+ (MDSS_GET_MAJOR_MINOR((rev1)) == MDSS_GET_MAJOR_MINOR((rev2)))
+
+#define MDSS_MDP_REV(major, minor, step) \
+ ((((major) & 0x000F) << 28) | \
+ (((minor) & 0x0FFF) << 16) | \
+ ((step) & 0xFFFF))
+
+#define MDSS_MDP_HW_REV_100 MDSS_MDP_REV(1, 0, 0) /* 8974 v1.0 */
+#define MDSS_MDP_HW_REV_101 MDSS_MDP_REV(1, 1, 0) /* 8x26 v1.0 */
+#define MDSS_MDP_HW_REV_101_1 MDSS_MDP_REV(1, 1, 1) /* 8x26 v2.0, 8926 v1.0 */
+#define MDSS_MDP_HW_REV_101_2 MDSS_MDP_REV(1, 1, 2) /* 8926 v2.0 */
+#define MDSS_MDP_HW_REV_102 MDSS_MDP_REV(1, 2, 0) /* 8974 v2.0 */
+#define MDSS_MDP_HW_REV_102_1 MDSS_MDP_REV(1, 2, 1) /* 8974 v3.0 (Pro) */
+#define MDSS_MDP_HW_REV_103 MDSS_MDP_REV(1, 3, 0) /* 8084 v1.0 */
+#define MDSS_MDP_HW_REV_103_1 MDSS_MDP_REV(1, 3, 1) /* 8084 v1.1 */
+#define MDSS_MDP_HW_REV_200 MDSS_MDP_REV(2, 0, 0) /* 8092 v1.0 */
+
enum {
NOTIFY_UPDATE_START,
NOTIFY_UPDATE_STOP,
@@ -95,6 +119,7 @@
NOTIFY_TYPE_NO_UPDATE,
NOTIFY_TYPE_SUSPEND,
NOTIFY_TYPE_UPDATE,
+ NOTIFY_TYPE_BL_UPDATE,
};
enum {
diff --git a/include/linux/msm_vidc_dec.h b/include/linux/msm_vidc_dec.h
index 35279bf..b09fc2d 100644
--- a/include/linux/msm_vidc_dec.h
+++ b/include/linux/msm_vidc_dec.h
@@ -58,6 +58,8 @@
#define VDEC_MSG_EVT_HW_ERROR (VDEC_MSG_BASE + 14)
#define VDEC_MSG_EVT_INFO_CONFIG_CHANGED (VDEC_MSG_BASE + 15)
#define VDEC_MSG_EVT_INFO_FIELD_DROPPED (VDEC_MSG_BASE + 16)
+#define VDEC_MSG_EVT_HW_OVERLOAD (VDEC_MSG_BASE + 17)
+#define VDEC_MSG_EVT_MAX_CLIENTS (VDEC_MSG_BASE + 18)
/*Buffer flags bits masks.*/
#define VDEC_BUFFERFLAG_EOS 0x00000001
diff --git a/include/linux/msm_vidc_enc.h b/include/linux/msm_vidc_enc.h
index 4ce3db1..36625a7 100644
--- a/include/linux/msm_vidc_enc.h
+++ b/include/linux/msm_vidc_enc.h
@@ -45,7 +45,8 @@
#define VEN_MSG_RESUME 9
#define VEN_MSG_STOP_READING_MSG 10
#define VEN_MSG_LTRUSE_FAILED 11
-
+#define VEN_MSG_HW_OVERLOAD 12
+#define VEN_MSG_MAX_CLIENTS 13
/*Buffer flags bits masks*/
#define VEN_BUFFLAG_EOS 0x00000001
diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h
index d797797..0b95337 100644
--- a/include/linux/nl80211.h
+++ b/include/linux/nl80211.h
@@ -370,8 +370,18 @@
* %NL80211_ATTR_SSID attribute, and can optionally specify the association
* IEs in %NL80211_ATTR_IE, %NL80211_ATTR_AUTH_TYPE, %NL80211_ATTR_USE_MFP,
* %NL80211_ATTR_MAC, %NL80211_ATTR_WIPHY_FREQ, %NL80211_ATTR_CONTROL_PORT,
- * %NL80211_ATTR_CONTROL_PORT_ETHERTYPE and
- * %NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT.
+ * %NL80211_ATTR_CONTROL_PORT_ETHERTYPE,
+ * %NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT, %NL80211_ATTR_MAC_HINT, and
+ * %NL80211_ATTR_WIPHY_FREQ_HINT.
+ * If included, %NL80211_ATTR_MAC and %NL80211_ATTR_WIPHY_FREQ are
+ * restrictions on BSS selection, i.e., they effectively prevent roaming
+ * within the ESS. %NL80211_ATTR_MAC_HINT and %NL80211_ATTR_WIPHY_FREQ_HINT
+ * can be included to provide a recommendation of the initial BSS while
+ * allowing the driver to roam to other BSSes within the ESS and also to
+ * ignore this recommendation if the indicated BSS is not ideal. Only one
+ * set of BSSID,frequency parameters is used (i.e., either the enforcing
+ * %NL80211_ATTR_MAC,%NL80211_ATTR_WIPHY_FREQ or the less strict
+ * %NL80211_ATTR_MAC_HINT and %NL80211_ATTR_WIPHY_FREQ_HINT).
* Background scan period can optionally be
* specified in %NL80211_ATTR_BG_SCAN_PERIOD,
* if not specified default background scan configuration
@@ -637,6 +647,12 @@
* (&struct nl80211_vendor_cmd_info) of the supported vendor commands.
* This may also be sent as an event with the same attributes.
*
+ * @NL80211_CMD_SET_QOS_MAP: Set Interworking QoS mapping for IP DSCP values.
+ * The QoS mapping information is included in %NL80211_ATTR_QOS_MAP. If
+ * that attribute is not included, QoS mapping is disabled. Since this
+ * QoS mapping is relevant for IP packets, it is only valid during an
+ * association. This is cleared on disassociation and AP restart.
+ *
* @NL80211_CMD_MAX: highest used command number
* @__NL80211_CMD_AFTER_LAST: internal use
*/
@@ -807,6 +823,8 @@
NL80211_CMD_VENDOR,
+ NL80211_CMD_SET_QOS_MAP,
+
/* add new commands above here */
/* used to define NL80211_CMD_MAX below */
@@ -1446,6 +1464,23 @@
* @NL80211_ATTR_VENDOR_EVENTS: used for event list advertising in the wiphy
* info, containing a nested array of possible events
*
+ * @NL80211_ATTR_QOS_MAP: IP DSCP mapping for Interworking QoS mapping. This
+ * data is in the format defined for the payload of the QoS Map Set element
+ * in IEEE Std 802.11-2012, 8.4.2.97.
+ *
+ * @NL80211_ATTR_MAC_HINT: MAC address recommendation as initial BSS
+ * @NL80211_ATTR_WIPHY_FREQ_HINT: frequency of the recommended initial BSS
+ *
+ * @NL80211_ATTR_MAX_AP_ASSOC_STA: Device attribute that indicates how many
+ * associated stations are supported in AP mode (including P2P GO); u32.
+ * Since drivers may not have a fixed limit on the maximum number (e.g.,
+ * other concurrent operations may affect this), drivers are allowed to
+ * advertise values that cannot always be met. In such cases, an attempt
+ * to add a new station entry with @NL80211_CMD_NEW_STATION may fail.
+ *
+ * @NL80211_ATTR_TDLS_PEER_CAPABILITY: flags for TDLS peer capabilities, u32.
+ * As specified in the &enum nl80211_tdls_peer_capability.
+ *
* @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use
*/
@@ -1773,6 +1808,15 @@
NL80211_ATTR_VENDOR_EVENTS,
+ NL80211_ATTR_QOS_MAP,
+
+ NL80211_ATTR_MAC_HINT,
+ NL80211_ATTR_WIPHY_FREQ_HINT,
+
+ NL80211_ATTR_MAX_AP_ASSOC_STA,
+
+ NL80211_ATTR_TDLS_PEER_CAPABILITY,
+
/* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST,
@@ -3256,4 +3300,20 @@
__u32 subcmd;
};
+/**
+ * enum nl80211_tdls_peer_capability - TDLS peer flags.
+ *
+ * Used by tdls_mgmt() to determine which conditional elements need
+ * to be added to TDLS Setup frames.
+ *
+ * @NL80211_TDLS_PEER_HT: TDLS peer is HT capable.
+ * @NL80211_TDLS_PEER_VHT: TDLS peer is VHT capable.
+ * @NL80211_TDLS_PEER_WMM: TDLS peer is WMM capable.
+ */
+enum nl80211_tdls_peer_capability {
+ NL80211_TDLS_PEER_HT = 1<<0,
+ NL80211_TDLS_PEER_VHT = 1<<1,
+ NL80211_TDLS_PEER_WMM = 1<<2,
+};
+
#endif /* __LINUX_NL80211_H */
diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h
index 0065203..0c89a42 100644
--- a/include/linux/power_supply.h
+++ b/include/linux/power_supply.h
@@ -45,6 +45,7 @@
POWER_SUPPLY_CHARGE_TYPE_NONE,
POWER_SUPPLY_CHARGE_TYPE_TRICKLE,
POWER_SUPPLY_CHARGE_TYPE_FAST,
+ POWER_SUPPLY_CHARGE_TYPE_TAPER,
};
enum {
@@ -230,6 +231,7 @@
extern int power_supply_am_i_supplied(struct power_supply *psy);
extern int power_supply_set_battery_charged(struct power_supply *psy);
extern int power_supply_set_current_limit(struct power_supply *psy, int limit);
+extern int power_supply_set_voltage_limit(struct power_supply *psy, int limit);
extern int power_supply_set_online(struct power_supply *psy, bool enable);
extern int power_supply_set_health_state(struct power_supply *psy, int health);
extern int power_supply_set_present(struct power_supply *psy, bool enable);
@@ -250,6 +252,9 @@
{ return -ENOSYS; }
static inline int power_supply_set_battery_charged(struct power_supply *psy)
{ return -ENOSYS; }
+static inline int power_supply_set_voltage_limit(struct power_supply *psy,
+ int limit)
+ { return -ENOSYS; }
static inline int power_supply_set_current_limit(struct power_supply *psy,
int limit)
{ return -ENOSYS; }
diff --git a/include/linux/qpnp-revid.h b/include/linux/qpnp-revid.h
index 3cf9f1c..3d271f0 100644
--- a/include/linux/qpnp-revid.h
+++ b/include/linux/qpnp-revid.h
@@ -13,6 +13,13 @@
#ifndef __QPNP_REVID
#define __QPNP_REVID
+#define PM8226_V2P2_REV1 0x00
+#define PM8226_V2P2_REV2 0x00
+#define PM8226_V2P2_REV3 0x02
+#define PM8226_V2P2_REV4 0x02
+#define PM8226_V2P2_TYPE 0x51
+#define PM8226_V2P2_SUBTYPE 0x04
+
#define PM8226_V2P1_REV1 0x00
#define PM8226_V2P1_REV2 0x00
#define PM8226_V2P1_REV3 0x01
diff --git a/include/linux/qpnp/qpnp-adc.h b/include/linux/qpnp/qpnp-adc.h
index 13eb461..52195c1 100644
--- a/include/linux/qpnp/qpnp-adc.h
+++ b/include/linux/qpnp/qpnp-adc.h
@@ -20,6 +20,7 @@
#include <linux/kernel.h>
#include <linux/list.h>
+#include <linux/qpnp-revid.h>
/**
* enum qpnp_vadc_channels - QPNP AMUX arbiter channels
*/
@@ -839,6 +840,7 @@
enum qpnp_adc_meas_timer_2 meas_interval2;
enum qpnp_adc_tm_channel_select tm_channel_select;
enum qpnp_state_request state_request;
+ enum qpnp_adc_calib_type calib_type;
struct qpnp_vadc_linear_graph adc_graph[2];
};
@@ -882,6 +884,7 @@
enum qpnp_adc_scale_fn_type adc_scale_fn;
enum qpnp_adc_fast_avg_ctl fast_avg_setup;
enum qpnp_adc_hw_settle_time hw_settle_time;
+ enum qpnp_adc_calib_type calib_type;
};
/**
@@ -1002,15 +1005,27 @@
* @chan_prop - Represent the channel properties of the ADC.
*/
struct qpnp_adc_amux_properties {
- uint32_t amux_channel;
- uint32_t decimation;
- uint32_t mode_sel;
- uint32_t hw_settle_time;
- uint32_t fast_avg_setup;
- enum qpnp_vadc_trigger trigger_channel;
+ uint32_t amux_channel;
+ uint32_t decimation;
+ uint32_t mode_sel;
+ uint32_t hw_settle_time;
+ uint32_t fast_avg_setup;
+ enum qpnp_vadc_trigger trigger_channel;
struct qpnp_vadc_chan_properties chan_prop[0];
};
+/* SW index's for PMIC type and version used by QPNP VADC and IADC */
+#define QPNP_REV_ID_8941_3_1 1
+#define QPNP_REV_ID_8026_1_0 2
+#define QPNP_REV_ID_8026_2_0 3
+#define QPNP_REV_ID_8110_1_0 4
+#define QPNP_REV_ID_8026_2_1 5
+#define QPNP_REV_ID_8110_2_0 6
+#define QPNP_REV_ID_8026_2_2 7
+#define QPNP_REV_ID_8941_3_0 8
+#define QPNP_REV_ID_8941_2_0 9
+
+
/* Public API */
#if defined(CONFIG_SENSORS_QPNP_ADC_VOLTAGE) \
|| defined(CONFIG_SENSORS_QPNP_ADC_VOLTAGE_MODULE)
@@ -1366,9 +1381,16 @@
* qpnp_vadc_sns_comp_result() - Compensate vbatt readings based on temperature
* @dev: Structure device for qpnp vadc
* @result: Voltage in uV that needs compensation.
+ * @is_pon_ocv: Whether the reading is from a power on OCV or not
*/
int32_t qpnp_vbat_sns_comp_result(struct qpnp_vadc_chip *dev,
- int64_t *result);
+ int64_t *result, bool is_pon_ocv);
+/**
+ * qpnp_adc_get_revid_version() - Obtain the PMIC number and revision.
+ * @dev: Structure device node.
+ * returns internal mapped PMIC number and revision id.
+ */
+int qpnp_adc_get_revid_version(struct device *dev);
#else
static inline int32_t qpnp_vadc_read(struct qpnp_vadc_chip *dev,
uint32_t channel,
@@ -1482,6 +1504,8 @@
static inline int32_t qpnp_vbat_sns_comp_result(struct qpnp_vadc_chip *dev,
int64_t *result)
{ return -ENXIO; }
+static inline int qpnp_adc_get_revid_version(struct device *dev)
+{ return -ENXIO; }
#endif
/* Public API */
diff --git a/include/linux/qseecom.h b/include/linux/qseecom.h
index b7ba63a..b63b36e 100644
--- a/include/linux/qseecom.h
+++ b/include/linux/qseecom.h
@@ -127,6 +127,7 @@
enum qseecom_key_management_usage_type {
QSEOS_KM_USAGE_DISK_ENCRYPTION = 0x01,
+ QSEOS_KM_USAGE_FILE_ENCRYPTION = 0x02,
QSEOS_KM_USAGE_MAX
};
@@ -137,6 +138,8 @@
struct qseecom_wipe_key_req {
enum qseecom_key_management_usage_type usage;
+ int wipe_key_flag;/* 1->remove key from storage(alone with clear key) */
+ /* 0->do not remove from storage (clear key) */
};
struct qseecom_update_key_userinfo_req {
diff --git a/include/linux/usb/android.h b/include/linux/usb/android.h
index 8d5e51b..4accfa0 100644
--- a/include/linux/usb/android.h
+++ b/include/linux/usb/android.h
@@ -29,6 +29,7 @@
bool internal_ums;
char streaming_func[MAX_STREAMING_FUNCS][FUNC_NAME_LEN];
int streaming_func_count;
+ u32 uicc_nluns;
};
#ifndef CONFIG_TARGET_CORE
diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h
index f9dec0b..ac0428d 100644
--- a/include/linux/usb/hcd.h
+++ b/include/linux/usb/hcd.h
@@ -352,6 +352,7 @@
void (*dump_regs)(struct usb_hcd *);
void (*set_autosuspend_delay)(struct usb_device *);
void (*reset_sof_bug_handler)(struct usb_hcd *hcd, u32 val);
+ void (*set_autosuspend)(struct usb_hcd *hcd, int enable_autosuspend);
};
extern int usb_hcd_link_urb_to_ep(struct usb_hcd *hcd, struct urb *urb);
diff --git a/include/linux/usb/msm_ext_chg.h b/include/linux/usb/msm_ext_chg.h
index 596ab49..9dbac1c 100644
--- a/include/linux/usb/msm_ext_chg.h
+++ b/include/linux/usb/msm_ext_chg.h
@@ -36,4 +36,7 @@
/* To tell kernel about voltage request result */
#define MSM_USB_EXT_CHG_RESULT _IOW('M', 3, int)
+/* To tell kernel whether charger connected is external charger or not */
+#define MSM_USB_EXT_CHG_TYPE _IOW('M', 4, int)
+
#endif /* __LINUX_USB_MSM_EXT_CHG_H */
diff --git a/include/linux/usb/msm_hsusb.h b/include/linux/usb/msm_hsusb.h
index d16448a..4ecacc7 100644
--- a/include/linux/usb/msm_hsusb.h
+++ b/include/linux/usb/msm_hsusb.h
@@ -346,7 +346,10 @@
* @chg_check_timer: The timer used to implement the workaround to detect
* very slow plug in of wall charger.
* @ui_enabled: USB Intterupt is enabled or disabled.
- * @pm_done: Indicates whether USB is PM resumed
+ * @pm_done: It is used to increment the pm counter using pm_runtime_get_sync.
+ This handles the race case when PM resume thread returns before
+ the charger detection starts. When USB is disconnected pm_done
+ is set to true.
*/
struct msm_otg {
struct usb_phy phy;
@@ -523,6 +526,7 @@
bool use_sec_phy;
bool no_selective_suspend;
int resume_gpio;
+ bool is_uicc;
};
/**
diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h
index ae3ffe4..7c82579 100644
--- a/include/linux/usb/otg.h
+++ b/include/linux/usb/otg.h
@@ -149,6 +149,9 @@
int (*set_suspend)(struct usb_phy *x,
int suspend);
+ /* To enable/disable phy autosuspend feature */
+ int (*set_phy_autosuspend)(struct usb_phy *x,
+ int enable_autosuspend);
};
@@ -288,6 +291,15 @@
}
static inline int
+usb_phy_set_autosuspend(struct usb_phy *x, int enable_autosuspend)
+{
+ if (x && x->set_phy_autosuspend != NULL)
+ return x->set_phy_autosuspend(x, enable_autosuspend);
+ else
+ return 0;
+}
+
+static inline int
otg_start_srp(struct usb_otg *otg)
{
if (otg && otg->start_srp)
diff --git a/include/linux/usb_usual.h b/include/linux/usb_usual.h
index 17df360..ff30988 100644
--- a/include/linux/usb_usual.h
+++ b/include/linux/usb_usual.h
@@ -64,7 +64,9 @@
US_FLAG(NO_READ_CAPACITY_16, 0x00080000) \
/* cannot handle READ_CAPACITY_16 */ \
US_FLAG(INITIAL_READ10, 0x00100000) \
- /* Initial READ(10) (and others) must be retried */
+ /* Initial READ(10) (and others) must be retried */ \
+ US_FLAG(TUR_AFTER_WRITE, 0x00200000) \
+ /* 5 TEST_UNIT_READY after 8 WRITE(10) */ \
#define US_FLAG(name, value) US_FL_##name = value ,
enum { US_DO_ALL_FLAGS };
diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h
index 81d5b9c..f760672 100644
--- a/include/linux/videodev2.h
+++ b/include/linux/videodev2.h
@@ -708,6 +708,7 @@
#define V4L2_QCOM_BUF_INPUT_UNSUPPORTED 0x200000
#define V4L2_QCOM_BUF_FLAG_EOS 0x2000
#define V4L2_QCOM_BUF_FLAG_READONLY 0x400000
+#define V4L2_MSM_BUF_FLAG_MBAFF 0x800000
/*
* O V E R L A Y P R E V I E W
@@ -1823,29 +1824,30 @@
#define V4L2_CID_MPEG_VIDC_VIDEO_EXTRADATA \
(V4L2_CID_MPEG_MSM_VIDC_BASE + 25)
enum v4l2_mpeg_vidc_extradata {
- V4L2_MPEG_VIDC_EXTRADATA_NONE,
- V4L2_MPEG_VIDC_EXTRADATA_MB_QUANTIZATION,
- V4L2_MPEG_VIDC_EXTRADATA_INTERLACE_VIDEO,
- V4L2_MPEG_VIDC_EXTRADATA_VC1_FRAMEDISP,
- V4L2_MPEG_VIDC_EXTRADATA_VC1_SEQDISP,
- V4L2_MPEG_VIDC_EXTRADATA_TIMESTAMP,
- V4L2_MPEG_VIDC_EXTRADATA_S3D_FRAME_PACKING,
- V4L2_MPEG_VIDC_EXTRADATA_FRAME_RATE,
- V4L2_MPEG_VIDC_EXTRADATA_PANSCAN_WINDOW,
- V4L2_MPEG_VIDC_EXTRADATA_RECOVERY_POINT_SEI,
- V4L2_MPEG_VIDC_EXTRADATA_CLOSED_CAPTION_UD,
- V4L2_MPEG_VIDC_EXTRADATA_AFD_UD,
- V4L2_MPEG_VIDC_EXTRADATA_MULTISLICE_INFO,
- V4L2_MPEG_VIDC_EXTRADATA_NUM_CONCEALED_MB,
- V4L2_MPEG_VIDC_EXTRADATA_METADATA_FILLER,
- 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,
- V4L2_MPEG_VIDC_EXTRADATA_FRAME_QP,
- V4L2_MPEG_VIDC_EXTRADATA_FRAME_BITS_INFO,
- V4L2_MPEG_VIDC_EXTRADATA_LTR,
- V4L2_MPEG_VIDC_EXTRADATA_METADATA_MBI,
+ V4L2_MPEG_VIDC_EXTRADATA_NONE = 0,
+ V4L2_MPEG_VIDC_EXTRADATA_MB_QUANTIZATION = 1,
+ V4L2_MPEG_VIDC_EXTRADATA_INTERLACE_VIDEO = 2,
+ V4L2_MPEG_VIDC_EXTRADATA_VC1_FRAMEDISP = 3,
+ V4L2_MPEG_VIDC_EXTRADATA_VC1_SEQDISP = 4,
+ V4L2_MPEG_VIDC_EXTRADATA_TIMESTAMP = 5,
+ V4L2_MPEG_VIDC_EXTRADATA_S3D_FRAME_PACKING = 6,
+ V4L2_MPEG_VIDC_EXTRADATA_FRAME_RATE = 7,
+ V4L2_MPEG_VIDC_EXTRADATA_PANSCAN_WINDOW = 8,
+ V4L2_MPEG_VIDC_EXTRADATA_RECOVERY_POINT_SEI = 9,
+ V4L2_MPEG_VIDC_EXTRADATA_CLOSED_CAPTION_UD = 10,
+ V4L2_MPEG_VIDC_EXTRADATA_AFD_UD = 11,
+ V4L2_MPEG_VIDC_EXTRADATA_MULTISLICE_INFO = 12,
+ V4L2_MPEG_VIDC_EXTRADATA_NUM_CONCEALED_MB = 13,
+ V4L2_MPEG_VIDC_EXTRADATA_METADATA_FILLER = 14,
+ V4L2_MPEG_VIDC_INDEX_EXTRADATA_INPUT_CROP = 15,
+ V4L2_MPEG_VIDC_INDEX_EXTRADATA_DIGITAL_ZOOM = 16,
+ V4L2_MPEG_VIDC_INDEX_EXTRADATA_ASPECT_RATIO = 17,
+ V4L2_MPEG_VIDC_EXTRADATA_MPEG2_SEQDISP = 18,
+ V4L2_MPEG_VIDC_EXTRADATA_FRAME_QP = 19,
+ V4L2_MPEG_VIDC_EXTRADATA_FRAME_BITS_INFO = 20,
+ V4L2_MPEG_VIDC_EXTRADATA_LTR = 21,
+ V4L2_MPEG_VIDC_EXTRADATA_METADATA_MBI = 22,
+ V4L2_MPEG_VIDC_EXTRADATA_STREAM_USERDATA = 23,
};
#define V4L2_CID_MPEG_VIDC_SET_PERF_LEVEL (V4L2_CID_MPEG_MSM_VIDC_BASE + 26)
@@ -1934,6 +1936,18 @@
#define V4L2_CID_MPEG_VIDC_VIDEO_HIER_P_NUM_LAYERS \
(V4L2_CID_MPEG_MSM_VIDC_BASE + 43)
+#define V4L2_CID_MPEG_VIDC_VIDEO_ENABLE_INITIAL_QP \
+ (V4L2_CID_MPEG_MSM_VIDC_BASE + 44)
+
+#define V4L2_CID_MPEG_VIDC_VIDEO_I_FRAME_QP \
+ (V4L2_CID_MPEG_MSM_VIDC_BASE + 45)
+
+#define V4L2_CID_MPEG_VIDC_VIDEO_P_FRAME_QP \
+ (V4L2_CID_MPEG_MSM_VIDC_BASE + 46)
+
+#define V4L2_CID_MPEG_VIDC_VIDEO_B_FRAME_QP \
+ (V4L2_CID_MPEG_MSM_VIDC_BASE + 47)
+
/* Camera class control IDs */
#define V4L2_CID_CAMERA_CLASS_BASE (V4L2_CTRL_CLASS_CAMERA | 0x900)
#define V4L2_CID_CAMERA_CLASS (V4L2_CTRL_CLASS_CAMERA | 1)
@@ -2499,6 +2513,8 @@
(V4L2_EVENT_MSM_VIDC_START + 6)
#define V4L2_EVENT_MSM_VIDC_RELEASE_UNQUEUED_BUFFER \
(V4L2_EVENT_MSM_VIDC_START + 7)
+#define V4L2_EVENT_MSM_VIDC_HW_OVERLOAD (V4L2_EVENT_MSM_VIDC_START + 8)
+#define V4L2_EVENT_MSM_VIDC_MAX_CLIENTS (V4L2_EVENT_MSM_VIDC_START + 9)
/* Payload for V4L2_EVENT_VSYNC */
struct v4l2_event_vsync {
diff --git a/include/linux/wcnss_wlan.h b/include/linux/wcnss_wlan.h
index 9badfa3..8836e4a 100644
--- a/include/linux/wcnss_wlan.h
+++ b/include/linux/wcnss_wlan.h
@@ -77,6 +77,7 @@
void wcnss_riva_log_debug_regs(void);
void wcnss_pronto_log_debug_regs(void);
int wcnss_device_ready(void);
+int wcnss_device_is_shutdown(void);
void wcnss_riva_dump_pmic_regs(void);
int wcnss_xo_auto_detect_enabled(void);
u32 wcnss_get_wlan_rx_buff_count(void);
diff --git a/include/media/msm/vidc_init.h b/include/media/msm/vidc_init.h
index bcc0370..9c9a270 100644
--- a/include/media/msm/vidc_init.h
+++ b/include/media/msm/vidc_init.h
@@ -20,6 +20,7 @@
#define VIDC_MAX_NUM_CLIENTS 4
#define MAX_VIDEO_NUM_OF_BUFF 100
#define MAX_META_BUFFERS 32
+#define MAX_MV_BUFFERS 32
enum buffer_dir {
BUFFER_TYPE_INPUT,
diff --git a/include/media/msm_cam_sensor.h b/include/media/msm_cam_sensor.h
index a88a71d..46cec76 100644
--- a/include/media/msm_cam_sensor.h
+++ b/include/media/msm_cam_sensor.h
@@ -246,16 +246,6 @@
MASTER_MAX,
};
-struct msm_camera_sensor_slave_info {
- char sensor_name[32];
- char eeprom_name[32];
- char actuator_name[32];
- enum msm_sensor_camera_id_t camera_id;
- uint16_t slave_addr;
- enum msm_camera_i2c_reg_addr_type addr_type;
- struct msm_sensor_id_info_t sensor_id_info;
- struct msm_sensor_power_setting_array power_setting_array;
-};
struct msm_camera_i2c_reg_array {
uint16_t reg_addr;
@@ -343,6 +333,7 @@
enum camb_position_t {
BACK_CAMERA_B,
FRONT_CAMERA_B,
+ INVALID_CAMERA_B,
};
struct msm_sensor_info_t {
@@ -366,7 +357,8 @@
enum camerab_mode_t {
CAMERA_MODE_2D_B = (1<<0),
- CAMERA_MODE_3D_B = (1<<1)
+ CAMERA_MODE_3D_B = (1<<1),
+ CAMERA_MODE_INVALID = (1<<2),
};
struct msm_sensor_init_params {
@@ -378,6 +370,19 @@
uint32_t sensor_mount_angle;
};
+struct msm_camera_sensor_slave_info {
+ char sensor_name[32];
+ char eeprom_name[32];
+ char actuator_name[32];
+ enum msm_sensor_camera_id_t camera_id;
+ uint16_t slave_addr;
+ enum msm_camera_i2c_reg_addr_type addr_type;
+ struct msm_sensor_id_info_t sensor_id_info;
+ struct msm_sensor_power_setting_array power_setting_array;
+ uint8_t is_init_params_valid;
+ struct msm_sensor_init_params sensor_init_params;
+};
+
struct sensorb_cfg_data {
int cfgtype;
union {
@@ -475,8 +480,8 @@
CFG_GET_ACTUATOR_INFO,
CFG_SET_ACTUATOR_INFO,
CFG_SET_DEFAULT_FOCUS,
- CFG_SET_POSITION,
CFG_MOVE_FOCUS,
+ CFG_SET_POSITION,
};
enum actuator_type {
@@ -653,10 +658,10 @@
_IOWR('V', BASE_VIDIOC_PRIVATE + 3, uint32_t)
#define VIDIOC_MSM_CSIPHY_IO_CFG \
- _IOWR('V', BASE_VIDIOC_PRIVATE + 4, struct csid_cfg_data)
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 4, struct csiphy_cfg_data)
#define VIDIOC_MSM_CSID_IO_CFG \
- _IOWR('V', BASE_VIDIOC_PRIVATE + 5, struct csiphy_cfg_data)
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 5, struct csid_cfg_data)
#define VIDIOC_MSM_ACTUATOR_CFG \
_IOWR('V', BASE_VIDIOC_PRIVATE + 6, struct msm_actuator_cfg_data)
diff --git a/include/media/msm_media_info.h b/include/media/msm_media_info.h
index ddf9c8e..d46b505 100644
--- a/include/media/msm_media_info.h
+++ b/include/media/msm_media_info.h
@@ -79,6 +79,18 @@
COLOR_FMT_NV21,
};
+static inline unsigned int VENUS_EXTRADATA_SIZE(int width, int height)
+{
+ (void)height;
+ (void)width;
+
+ /*
+ * In the future, calculate the size based on the w/h but just
+ * hardcode it for now since 8K satisfies all current usecases.
+ */
+ return 8 * 1024;
+}
+
static inline unsigned int VENUS_Y_STRIDE(int color_fmt, int width)
{
unsigned int alignment, stride = 0;
@@ -158,8 +170,8 @@
static inline unsigned int VENUS_BUFFER_SIZE(
int color_fmt, int width, int height)
{
- unsigned int uv_alignment;
- unsigned int size = 0;
+ const unsigned int extra_size = VENUS_EXTRADATA_SIZE(width, height);
+ unsigned int uv_alignment = 0, size = 0;
unsigned int y_plane, uv_plane, y_stride,
uv_stride, y_sclines, uv_sclines;
if (!width || !height)
@@ -175,7 +187,7 @@
uv_alignment = 4096;
y_plane = y_stride * y_sclines;
uv_plane = uv_stride * uv_sclines + uv_alignment;
- size = y_plane + uv_plane;
+ size = y_plane + uv_plane + extra_size;
size = MSM_MEDIA_ALIGN(size, 4096);
break;
default:
diff --git a/include/media/msm_vidc.h b/include/media/msm_vidc.h
index 9028b1a..bbde6ef 100644
--- a/include/media/msm_vidc.h
+++ b/include/media/msm_vidc.h
@@ -179,6 +179,11 @@
unsigned int header_bits;
};
+struct msm_vidc_stream_userdata_payload {
+ unsigned int type;
+ unsigned int data[1];
+};
+
enum msm_vidc_extradata_type {
EXTRADATA_NONE = 0x00000000,
EXTRADATA_MB_QUANTIZATION = 0x00000001,
@@ -191,6 +196,7 @@
EXTRADATA_PANSCAN_WINDOW = 0x00000008,
EXTRADATA_RECOVERY_POINT_SEI = 0x00000009,
EXTRADATA_MPEG2_SEQDISP = 0x0000000D,
+ EXTRADATA_STREAM_USERDATA = 0x0000000E,
EXTRADATA_FRAME_QP = 0x0000000F,
EXTRADATA_FRAME_BITS_INFO = 0x00000010,
EXTRADATA_MULTISLICE_INFO = 0x7F100000,
@@ -214,4 +220,9 @@
FRAME_RECONSTRUCTION_APPROXIMATELY_CORRECT = 0x02,
};
+enum msm_vidc_userdata_type {
+ MSM_VIDC_USERDATA_TYPE_FRAME = 0x1,
+ MSM_VIDC_USERDATA_TYPE_TOP_FIELD = 0x2,
+ MSM_VIDC_USERDATA_TYPE_BOTTOM_FIELD = 0x3,
+};
#endif
diff --git a/include/media/msmb_pproc.h b/include/media/msmb_pproc.h
index 26c1048..df9f9e7 100644
--- a/include/media/msmb_pproc.h
+++ b/include/media/msmb_pproc.h
@@ -232,6 +232,9 @@
#define VIDIOC_MSM_CPP_APPEND_STREAM_BUFF_INFO \
_IOWR('V', BASE_VIDIOC_PRIVATE + 15, struct msm_camera_v4l2_ioctl_t)
+#define VIDIOC_MSM_CPP_SET_CLOCK \
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 16, struct msm_camera_v4l2_ioctl_t)
+
#define V4L2_EVENT_CPP_FRAME_DONE (V4L2_EVENT_PRIVATE_START + 0)
#define V4L2_EVENT_VPE_FRAME_DONE (V4L2_EVENT_PRIVATE_START + 1)
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index ecaef21..8b833dd 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -58,6 +58,8 @@
* structures here describe these capabilities in detail.
*/
+#define TDLS_MGMT_VERSION2 1
+
/*
* wireless hardware capability structures
*/
@@ -1263,8 +1265,14 @@
*
* @channel: The channel to use or %NULL if not specified (auto-select based
* on scan results)
+ * @channel_hint: The channel of the recommended BSS for initial connection or
+ * %NULL if not specified
* @bssid: The AP BSSID or %NULL if not specified (auto-select based on scan
* results)
+ * @bssid_hint: The recommended AP BSSID for initial connection to the BSS or
+ * %NULL if not specified. Unlike the @bssid parameter, the driver is
+ * allowed to ignore this @bssid_hint if it has knowledge of a better BSS
+ * to use.
* @ssid: SSID
* @ssid_len: Length of ssid in octets
* @auth_type: Authentication type (algorithm)
@@ -1285,7 +1293,9 @@
*/
struct cfg80211_connect_params {
struct ieee80211_channel *channel;
+ struct ieee80211_channel *channel_hint;
u8 *bssid;
+ const u8 *bssid_hint;
u8 *ssid;
size_t ssid_len;
enum nl80211_auth_type auth_type;
@@ -1407,6 +1417,50 @@
};
/**
+ * struct cfg80211_dscp_exception - DSCP exception
+ *
+ * @dscp: DSCP value that does not adhere to the user priority range definition
+ * @up: user priority value to which the corresponding DSCP value belongs
+ */
+struct cfg80211_dscp_exception {
+ u8 dscp;
+ u8 up;
+};
+
+/**
+ * struct cfg80211_dscp_range - DSCP range definition for user priority
+ *
+ * @low: lowest DSCP value of this user priority range, inclusive
+ * @high: highest DSCP value of this user priority range, inclusive
+ */
+struct cfg80211_dscp_range {
+ u8 low;
+ u8 high;
+};
+
+/* QoS Map Set element length defined in IEEE Std 802.11-2012, 8.4.2.97 */
+#define IEEE80211_QOS_MAP_MAX_EX 21
+#define IEEE80211_QOS_MAP_LEN_MIN 16
+#define IEEE80211_QOS_MAP_LEN_MAX \
+ (IEEE80211_QOS_MAP_LEN_MIN + 2 * IEEE80211_QOS_MAP_MAX_EX)
+
+/**
+ * struct cfg80211_qos_map - QoS Map Information
+ *
+ * This struct defines the Interworking QoS map setting for DSCP values
+ *
+ * @num_des: number of DSCP exceptions (0..21)
+ * @dscp_exception: optionally up to maximum of 21 DSCP exceptions from
+ * the user priority DSCP range definition
+ * @up: DSCP range definition for a particular user priority
+ */
+struct cfg80211_qos_map {
+ u8 num_des;
+ struct cfg80211_dscp_exception dscp_exception[IEEE80211_QOS_MAP_MAX_EX];
+ struct cfg80211_dscp_range up[8];
+};
+
+/**
* struct cfg80211_ops - backend description for wireless configuration
*
* This struct is registered by fullmac card drivers and/or wireless stacks
@@ -1597,6 +1651,7 @@
* when number of MAC addresses entries is passed as 0. Drivers which
* advertise the support for MAC based ACL have to implement this callback.
*
+ * @set_qos_map: Set QoS mapping information to the driver
*/
struct cfg80211_ops {
int (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow);
@@ -1781,7 +1836,8 @@
int (*tdls_mgmt)(struct wiphy *wiphy, struct net_device *dev,
u8 *peer, u8 action_code, u8 dialog_token,
- u16 status_code, const u8 *buf, size_t len);
+ u16 status_code, u32 peer_capability,
+ const u8 *buf, size_t len);
int (*tdls_oper)(struct wiphy *wiphy, struct net_device *dev,
u8 *peer, enum nl80211_tdls_operation oper);
@@ -1798,6 +1854,10 @@
int (*set_mac_acl)(struct wiphy *wiphy, struct net_device *dev,
const struct cfg80211_acl_data *params);
+ int (*set_qos_map)(struct wiphy *wiphy,
+ struct net_device *dev,
+ struct cfg80211_qos_map *qos_map);
+
};
/*
@@ -2177,6 +2237,11 @@
* @n_vendor_commands: number of vendor commands
* @vendor_events: array of vendor events supported by the hardware
* @n_vendor_events: number of vendor events
+ *
+ * @max_ap_assoc_sta: maximum number of associated stations supported in AP mode
+ * (including P2P GO) or 0 to indicate no such limit is advertised. The
+ * driver is allowed to advertise a theoretical limit that it can reach in
+ * some cases, but may not always reach.
*/
struct wiphy {
/* assign these fields before you register the wiphy */
@@ -2285,6 +2350,8 @@
const struct nl80211_vendor_cmd_info *vendor_events;
int n_vendor_commands, n_vendor_events;
+ u16 max_ap_assoc_sta;
+
char priv[0] __attribute__((__aligned__(NETDEV_ALIGN)));
};
@@ -2705,8 +2772,10 @@
/**
* cfg80211_classify8021d - determine the 802.1p/1d tag for a data frame
* @skb: the data frame
+ * @qos_map: Interworking QoS mapping or %NULL if not in use
*/
-unsigned int cfg80211_classify8021d(struct sk_buff *skb);
+unsigned int cfg80211_classify8021d(struct sk_buff *skb,
+ struct cfg80211_qos_map *qos_map);
/**
* cfg80211_find_ie - find information element in data
@@ -3730,6 +3799,15 @@
* @gfp: context flags
*/
void cfg80211_ap_stopped(struct net_device *netdev, gfp_t gfp);
+/**
+ * cfg80211_is_gratuitous_arp_unsolicited_na - packet is grat. ARP/unsol. NA
+ * @skb: the input packet, must be an ethernet frame already
+ *
+ * Return: %true if the packet is a gratuitous ARP or unsolicited NA packet.
+ * This is used to drop packets that shouldn't occur because the AP implements
+ * a proxy service.
+ */
+bool cfg80211_is_gratuitous_arp_unsolicited_na(struct sk_buff *skb);
/* Logging, debugging and troubleshooting/diagnostic helpers. */
diff --git a/include/sound/apr_audio-v2.h b/include/sound/apr_audio-v2.h
index 1c6ea04..3b8b1cc 100644
--- a/include/sound/apr_audio-v2.h
+++ b/include/sound/apr_audio-v2.h
@@ -2264,6 +2264,7 @@
#define VPM_TX_SM_ECNS_COPP_TOPOLOGY 0x00010F71
#define VPM_TX_DM_FLUENCE_COPP_TOPOLOGY 0x00010F72
#define VPM_TX_QMIC_FLUENCE_COPP_TOPOLOGY 0x00010F75
+#define VPM_TX_DM_RFECNS_COPP_TOPOLOGY 0x00010F86
/* Memory map regions command payload used by the
* #ASM_CMD_SHARED_MEM_MAP_REGIONS ,#ADM_CMD_SHARED_MEM_MAP_REGIONS
diff --git a/include/sound/q6asm-v2.h b/include/sound/q6asm-v2.h
index 1635fc3..07199e0 100644
--- a/include/sound/q6asm-v2.h
+++ b/include/sound/q6asm-v2.h
@@ -180,6 +180,7 @@
int stream_id;
/* audio cache operations fptr*/
int (*fptr_cache_ops)(struct audio_buffer *abuff, int cache_op);
+ atomic_t unmap_cb_success;
};
void q6asm_audio_client_free(struct audio_client *ac);
diff --git a/include/trace/events/exception.h b/include/trace/events/exception.h
index 110e920..69dbf7c 100644
--- a/include/trace/events/exception.h
+++ b/include/trace/events/exception.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
@@ -63,6 +63,27 @@
TP_printk("pc:%p", __entry->prog_cnt)
);
+TRACE_EVENT(unhandled_abort,
+
+ TP_PROTO(struct pt_regs *regs, unsigned long addr, unsigned int fsr),
+
+ TP_ARGS(regs, addr, fsr),
+
+ TP_STRUCT__entry(
+ __field(struct pt_regs *, regs)
+ __field(unsigned long, addr)
+ __field(unsigned int, fsr)
+ ),
+
+ TP_fast_assign(
+ __entry->regs = regs;
+ __entry->addr = addr;
+ __entry->fsr = fsr;
+ ),
+
+ TP_printk("addr:%lu, fsr:%u", __entry->addr, __entry->fsr)
+);
+
#endif
#include <trace/define_trace.h>
diff --git a/kernel/ksysfs.c b/kernel/ksysfs.c
index 4e316e1..8715a79 100644
--- a/kernel/ksysfs.c
+++ b/kernel/ksysfs.c
@@ -141,6 +141,23 @@
}
KERNEL_ATTR_RO(fscaps);
+int rcu_expedited;
+static ssize_t rcu_expedited_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%d\n", rcu_expedited);
+}
+static ssize_t rcu_expedited_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf, size_t count)
+{
+ if (kstrtoint(buf, 0, &rcu_expedited))
+ return -EINVAL;
+
+ return count;
+}
+KERNEL_ATTR_RW(rcu_expedited);
+
/*
* Make /sys/kernel/notes give the raw contents of our kernel .notes section.
*/
@@ -182,6 +199,7 @@
&kexec_crash_size_attr.attr,
&vmcoreinfo_attr.attr,
#endif
+ &rcu_expedited_attr.attr,
NULL
};
diff --git a/kernel/rcu.h b/kernel/rcu.h
index 8ba99cd..20dfba5 100644
--- a/kernel/rcu.h
+++ b/kernel/rcu.h
@@ -109,4 +109,6 @@
}
}
+extern int rcu_expedited;
+
#endif /* __LINUX_RCU_H */
diff --git a/kernel/rcupdate.c b/kernel/rcupdate.c
index a86f174..5746f18 100644
--- a/kernel/rcupdate.c
+++ b/kernel/rcupdate.c
@@ -45,12 +45,15 @@
#include <linux/mutex.h>
#include <linux/export.h>
#include <linux/hardirq.h>
+#include <linux/module.h>
#define CREATE_TRACE_POINTS
#include <trace/events/rcu.h>
#include "rcu.h"
+module_param(rcu_expedited, int, 0);
+
#ifdef CONFIG_DEBUG_LOCK_ALLOC
static struct lock_class_key rcu_lock_key;
struct lockdep_map rcu_lock_map =
diff --git a/kernel/rcutiny_plugin.h b/kernel/rcutiny_plugin.h
index 22ecea0..5a0f324 100644
--- a/kernel/rcutiny_plugin.h
+++ b/kernel/rcutiny_plugin.h
@@ -750,7 +750,10 @@
return;
/* Once we get past the fastpath checks, same code as rcu_barrier(). */
- rcu_barrier();
+ if (rcu_expedited)
+ synchronize_rcu_expedited();
+ else
+ rcu_barrier();
}
EXPORT_SYMBOL_GPL(synchronize_rcu);
diff --git a/kernel/rcutree.c b/kernel/rcutree.c
index 4eec66e..e269782 100644
--- a/kernel/rcutree.c
+++ b/kernel/rcutree.c
@@ -1926,7 +1926,10 @@
"Illegal synchronize_sched() in RCU-sched read-side critical section");
if (rcu_blocking_is_gp())
return;
- wait_rcu_gp(call_rcu_sched);
+ if (rcu_expedited)
+ synchronize_sched_expedited();
+ else
+ wait_rcu_gp(call_rcu_sched);
}
EXPORT_SYMBOL_GPL(synchronize_sched);
@@ -1947,7 +1950,10 @@
"Illegal synchronize_rcu_bh() in RCU-bh read-side critical section");
if (rcu_blocking_is_gp())
return;
- wait_rcu_gp(call_rcu_bh);
+ if (rcu_expedited)
+ synchronize_rcu_bh_expedited();
+ else
+ wait_rcu_gp(call_rcu_bh);
}
EXPORT_SYMBOL_GPL(synchronize_rcu_bh);
diff --git a/kernel/rcutree_plugin.h b/kernel/rcutree_plugin.h
index c023464..beafb9c 100644
--- a/kernel/rcutree_plugin.h
+++ b/kernel/rcutree_plugin.h
@@ -737,7 +737,10 @@
"Illegal synchronize_rcu() in RCU read-side critical section");
if (!rcu_scheduler_active)
return;
- wait_rcu_gp(call_rcu);
+ if (rcu_expedited)
+ synchronize_rcu_expedited();
+ else
+ wait_rcu_gp(call_rcu);
}
EXPORT_SYMBOL_GPL(synchronize_rcu);
diff --git a/kernel/sched/rt.c b/kernel/sched/rt.c
index be427c5..3d4b1e2 100644
--- a/kernel/sched/rt.c
+++ b/kernel/sched/rt.c
@@ -560,7 +560,7 @@
static int do_balance_runtime(struct rt_rq *rt_rq)
{
struct rt_bandwidth *rt_b = sched_rt_bandwidth(rt_rq);
- struct root_domain *rd = cpu_rq(smp_processor_id())->rd;
+ struct root_domain *rd = rq_of_rt_rq(rt_rq)->rd;
int i, weight, more = 0;
u64 rt_period;
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index 700d2ae..f320017 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -384,7 +384,7 @@
void tracing_off(void)
{
if (global_trace.buffer)
- ring_buffer_record_on(global_trace.buffer);
+ ring_buffer_record_off(global_trace.buffer);
/*
* This flag is only looked at when buffers haven't been
* allocated yet. We don't really care about the race
diff --git a/mm/debug-pagealloc.c b/mm/debug-pagealloc.c
index bc91cba..87dde36 100644
--- a/mm/debug-pagealloc.c
+++ b/mm/debug-pagealloc.c
@@ -6,6 +6,14 @@
#include <linux/poison.h>
#include <linux/ratelimit.h>
+#ifndef mark_addr_rdonly
+#define mark_addr_rdonly(a)
+#endif
+
+#ifndef mark_addr_rdwrite
+#define mark_addr_rdwrite(a)
+#endif
+
static inline void set_page_poison(struct page *page)
{
__set_bit(PAGE_DEBUG_FLAG_POISON, &page->debug_flags);
@@ -27,6 +35,7 @@
set_page_poison(page);
memset(addr, PAGE_POISON, PAGE_SIZE);
+ mark_addr_rdonly(addr);
kunmap_atomic(addr);
}
@@ -82,6 +91,7 @@
addr = kmap_atomic(page);
check_poison_mem(addr, PAGE_SIZE);
+ mark_addr_rdwrite(addr);
clear_page_poison(page);
kunmap_atomic(addr);
}
diff --git a/mm/ksm.c b/mm/ksm.c
index 47c8853..fa73fc6 100644
--- a/mm/ksm.c
+++ b/mm/ksm.c
@@ -189,6 +189,9 @@
/* Milliseconds ksmd should sleep between batches */
static unsigned int ksm_thread_sleep_millisecs = 20;
+/* Boolean to indicate whether to use deferred timer or not */
+static bool use_deferred_timer;
+
#define KSM_RUN_STOP 0
#define KSM_RUN_MERGE 1
#define KSM_RUN_UNMERGE 2
@@ -1427,6 +1430,41 @@
}
}
+static void process_timeout(unsigned long __data)
+{
+ wake_up_process((struct task_struct *)__data);
+}
+
+static signed long __sched deferred_schedule_timeout(signed long timeout)
+{
+ struct timer_list timer;
+ unsigned long expire;
+
+ __set_current_state(TASK_INTERRUPTIBLE);
+ if (timeout < 0) {
+ pr_err("schedule_timeout: wrong timeout value %lx\n",
+ timeout);
+ __set_current_state(TASK_RUNNING);
+ goto out;
+ }
+
+ expire = timeout + jiffies;
+
+ setup_deferrable_timer_on_stack(&timer, process_timeout,
+ (unsigned long)current);
+ mod_timer(&timer, expire);
+ schedule();
+ del_singleshot_timer_sync(&timer);
+
+ /* Remove the timer from the object tracker */
+ destroy_timer_on_stack(&timer);
+
+ timeout = expire - jiffies;
+
+out:
+ return timeout < 0 ? 0 : timeout;
+}
+
static int ksmd_should_run(void)
{
return (ksm_run & KSM_RUN_MERGE) && !list_empty(&ksm_mm_head.mm_list);
@@ -1446,7 +1484,11 @@
try_to_freeze();
if (ksmd_should_run()) {
- schedule_timeout_interruptible(
+ if (use_deferred_timer)
+ deferred_schedule_timeout(
+ msecs_to_jiffies(ksm_thread_sleep_millisecs));
+ else
+ schedule_timeout_interruptible(
msecs_to_jiffies(ksm_thread_sleep_millisecs));
} else {
wait_event_freezable(ksm_thread_wait,
@@ -1926,6 +1968,26 @@
}
KSM_ATTR(run);
+static ssize_t deferred_timer_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ return snprintf(buf, 8, "%d\n", use_deferred_timer);
+}
+
+static ssize_t deferred_timer_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned long enable;
+ int err;
+
+ err = kstrtoul(buf, 10, &enable);
+ use_deferred_timer = enable;
+
+ return count;
+}
+KSM_ATTR(deferred_timer);
+
static ssize_t pages_shared_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
@@ -1980,6 +2042,7 @@
&pages_unshared_attr.attr,
&pages_volatile_attr.attr,
&full_scans_attr.attr,
+ &deferred_timer_attr.attr,
NULL,
};
diff --git a/net/core/pktgen.c b/net/core/pktgen.c
index b81369b..f6c785c 100644
--- a/net/core/pktgen.c
+++ b/net/core/pktgen.c
@@ -419,20 +419,6 @@
#define REMOVE 1
#define FIND 0
-static inline ktime_t ktime_now(void)
-{
- struct timespec ts;
- ktime_get_ts(&ts);
-
- return timespec_to_ktime(ts);
-}
-
-/* This works even if 32 bit because of careful byte order choice */
-static inline int ktime_lt(const ktime_t cmp1, const ktime_t cmp2)
-{
- return cmp1.tv64 < cmp2.tv64;
-}
-
static const char version[] =
"Packet Generator for packet performance testing. "
"Version: " VERSION "\n";
@@ -677,7 +663,7 @@
seq_puts(seq, "\n");
/* not really stopped, more like last-running-at */
- stopped = pkt_dev->running ? ktime_now() : pkt_dev->stopped_at;
+ stopped = pkt_dev->running ? ktime_get() : pkt_dev->stopped_at;
idle = pkt_dev->idle_acc;
do_div(idle, NSEC_PER_USEC);
@@ -2141,12 +2127,12 @@
return;
}
- start_time = ktime_now();
+ start_time = ktime_get();
if (remaining < 100000) {
/* for small delays (<100us), just loop until limit is reached */
do {
- end_time = ktime_now();
- } while (ktime_lt(end_time, spin_until));
+ end_time = ktime_get();
+ } while (ktime_compare(end_time, spin_until) < 0);
} else {
/* see do_nanosleep */
hrtimer_init_sleeper(&t, current);
@@ -2162,7 +2148,7 @@
hrtimer_cancel(&t.timer);
} while (t.task && pkt_dev->running && !signal_pending(current));
__set_current_state(TASK_RUNNING);
- end_time = ktime_now();
+ end_time = ktime_get();
}
pkt_dev->idle_acc += ktime_to_ns(ktime_sub(end_time, start_time));
@@ -3008,8 +2994,7 @@
pktgen_clear_counters(pkt_dev);
pkt_dev->running = 1; /* Cranke yeself! */
pkt_dev->skb = NULL;
- pkt_dev->started_at =
- pkt_dev->next_tx = ktime_now();
+ pkt_dev->started_at = pkt_dev->next_tx = ktime_get();
set_pkt_overhead(pkt_dev);
@@ -3168,7 +3153,7 @@
kfree_skb(pkt_dev->skb);
pkt_dev->skb = NULL;
- pkt_dev->stopped_at = ktime_now();
+ pkt_dev->stopped_at = ktime_get();
pkt_dev->running = 0;
show_results(pkt_dev, nr_frags);
@@ -3187,7 +3172,7 @@
continue;
if (best == NULL)
best = pkt_dev;
- else if (ktime_lt(pkt_dev->next_tx, best->next_tx))
+ else if (ktime_compare(pkt_dev->next_tx, best->next_tx) < 0)
best = pkt_dev;
}
if_unlock(t);
@@ -3272,14 +3257,14 @@
static void pktgen_resched(struct pktgen_dev *pkt_dev)
{
- ktime_t idle_start = ktime_now();
+ ktime_t idle_start = ktime_get();
schedule();
- pkt_dev->idle_acc += ktime_to_ns(ktime_sub(ktime_now(), idle_start));
+ pkt_dev->idle_acc += ktime_to_ns(ktime_sub(ktime_get(), idle_start));
}
static void pktgen_wait_for_skb(struct pktgen_dev *pkt_dev)
{
- ktime_t idle_start = ktime_now();
+ ktime_t idle_start = ktime_get();
while (atomic_read(&(pkt_dev->skb->users)) != 1) {
if (signal_pending(current))
@@ -3290,7 +3275,7 @@
else
cpu_relax();
}
- pkt_dev->idle_acc += ktime_to_ns(ktime_sub(ktime_now(), idle_start));
+ pkt_dev->idle_acc += ktime_to_ns(ktime_sub(ktime_get(), idle_start));
}
static void pktgen_xmit(struct pktgen_dev *pkt_dev)
@@ -3312,7 +3297,7 @@
* "never transmit"
*/
if (unlikely(pkt_dev->delay == ULLONG_MAX)) {
- pkt_dev->next_tx = ktime_add_ns(ktime_now(), ULONG_MAX);
+ pkt_dev->next_tx = ktime_add_ns(ktime_get(), ULONG_MAX);
return;
}
diff --git a/net/ipv4/sysfs_net_ipv4.c b/net/ipv4/sysfs_net_ipv4.c
index 0cbbf10..699075a 100644
--- a/net/ipv4/sysfs_net_ipv4.c
+++ b/net/ipv4/sysfs_net_ipv4.c
@@ -53,6 +53,9 @@
CREATE_IPV4_FILE(tcp_rmem_def, sysctl_tcp_rmem[1]);
CREATE_IPV4_FILE(tcp_rmem_max, sysctl_tcp_rmem[2]);
+CREATE_IPV4_FILE(tcp_delack_seg, sysctl_tcp_delack_seg);
+CREATE_IPV4_FILE(tcp_use_userconfig, sysctl_tcp_use_userconfig);
+
static struct attribute *ipv4_attrs[] = {
&tcp_wmem_min_attr.attr,
&tcp_wmem_def_attr.attr,
@@ -60,6 +63,8 @@
&tcp_rmem_min_attr.attr,
&tcp_rmem_def_attr.attr,
&tcp_rmem_max_attr.attr,
+ &tcp_delack_seg_attr.attr,
+ &tcp_use_userconfig_attr.attr,
NULL
};
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 685553b..ff11148 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -2440,8 +2440,8 @@
static int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
u8 *peer, u8 action_code, u8 dialog_token,
- u16 status_code, const u8 *extra_ies,
- size_t extra_ies_len)
+ u16 status_code, u32 peer_capability,
+ const u8 *extra_ies, size_t extra_ies_len)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct ieee80211_local *local = sdata->local;
diff --git a/net/mac80211/wme.c b/net/mac80211/wme.c
index 89511be..262c305 100644
--- a/net/mac80211/wme.c
+++ b/net/mac80211/wme.c
@@ -134,7 +134,7 @@
/* use the data classifier to determine what 802.1d tag the
* data frame has */
- skb->priority = cfg80211_classify8021d(skb);
+ skb->priority = cfg80211_classify8021d(skb, NULL);
return ieee80211_downgrade_queue(local, skb);
}
diff --git a/net/wireless/db.txt b/net/wireless/db.txt
index f6c74c9..6a77ffc 100644
--- a/net/wireless/db.txt
+++ b/net/wireless/db.txt
@@ -266,6 +266,9 @@
country DZ:
(2402 - 2482 @ 40), (N/A, 20)
+ (5150 - 5250 @ 80), (N/A, 23)
+ (5250 - 5350 @ 80), (N/A,23), DFS
+ (5470 - 5670 @ 80), (N/A, 20), DFS
country EC:
(2402 - 2482 @ 40), (N/A, 20)
@@ -416,8 +419,8 @@
country ID:
# ref: http://www.postel.go.id/content/ID/regulasi/standardisasi/kepdir/bwa%205,8%20ghz.pdf
- (2402 - 2482 @ 20), (N/A, 20)
- (5735 - 5815 @ 20), (N/A, 23)
+ (2402 - 2482 @ 20), (N/A, 30)
+ (5735 - 5815 @ 20), (N/A, 30)
country IE: DFS-ETSI
(2402 - 2482 @ 40), (N/A, 20)
diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c
index 30f20fe..9890342 100644
--- a/net/wireless/ibss.c
+++ b/net/wireless/ibss.c
@@ -155,6 +155,10 @@
kfree(wdev->connect_keys);
wdev->connect_keys = NULL;
+ if (rdev->ops->set_qos_map) {
+ rdev->ops->set_qos_map(&rdev->wiphy, dev, NULL);
+ }
+
/*
* Delete all the keys ... pairwise keys can't really
* exist any more anyway, but default keys might.
diff --git a/net/wireless/mesh.c b/net/wireless/mesh.c
index ba21ab2..c892cce 100644
--- a/net/wireless/mesh.c
+++ b/net/wireless/mesh.c
@@ -152,8 +152,12 @@
return -ENOTCONN;
err = rdev->ops->leave_mesh(&rdev->wiphy, dev);
- if (!err)
+ if (!err) {
wdev->mesh_id_len = 0;
+ if (rdev->ops->set_qos_map) {
+ rdev->ops->set_qos_map(&rdev->wiphy, dev, NULL);
+ }
+ }
return err;
}
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index b73cfe5..c3adef8 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -237,6 +237,11 @@
[NL80211_ATTR_VENDOR_ID] = { .type = NLA_U32 },
[NL80211_ATTR_VENDOR_SUBCMD] = { .type = NLA_U32 },
[NL80211_ATTR_VENDOR_DATA] = { .type = NLA_BINARY },
+ [NL80211_ATTR_QOS_MAP] = { .type = NLA_BINARY,
+ .len = IEEE80211_QOS_MAP_LEN_MAX },
+ [NL80211_ATTR_MAC_HINT] = { .len = ETH_ALEN },
+ [NL80211_ATTR_WIPHY_FREQ_HINT] = { .type = NLA_U32 },
+ [NL80211_ATTR_TDLS_PEER_CAPABILITY] = { .type = NLA_U32 },
};
/* policy for the key attributes */
@@ -950,6 +955,7 @@
i++;
NLA_PUT_U32(msg, i, NL80211_CMD_REGISTER_BEACONS);
}
+ CMD(set_qos_map, SET_QOS_MAP);
#ifdef CONFIG_NL80211_TESTMODE
CMD(testmode_cmd, TESTMODE);
@@ -1088,6 +1094,11 @@
NLA_PUT_U32(msg, NL80211_ATTR_MAC_ACL_MAX,
dev->wiphy.max_acl_mac_addrs);
+ if (dev->wiphy.max_ap_assoc_sta &&
+ nla_put_u32(msg, NL80211_ATTR_MAX_AP_ASSOC_STA,
+ dev->wiphy.max_ap_assoc_sta))
+ goto nla_put_failure;
+
if (dev->wiphy.n_vendor_commands) {
const struct nl80211_vendor_cmd_info *info;
struct nlattr *nested;
@@ -5530,6 +5541,9 @@
if (info->attrs[NL80211_ATTR_MAC])
connect.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
+ else if (info->attrs[NL80211_ATTR_MAC_HINT])
+ connect.bssid_hint =
+ nla_data(info->attrs[NL80211_ATTR_MAC_HINT]);
connect.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
connect.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
@@ -5554,6 +5568,14 @@
if (!connect.channel ||
connect.channel->flags & IEEE80211_CHAN_DISABLED)
return -EINVAL;
+ } else if (info->attrs[NL80211_ATTR_WIPHY_FREQ_HINT]) {
+ connect.channel_hint =
+ ieee80211_get_channel(wiphy,
+ nla_get_u32(
+ info->attrs[NL80211_ATTR_WIPHY_FREQ_HINT]));
+ if (!connect.channel_hint ||
+ connect.channel_hint->flags & IEEE80211_CHAN_DISABLED)
+ return -EINVAL;
}
if (connect.privacy && info->attrs[NL80211_ATTR_KEYS]) {
@@ -5693,6 +5715,7 @@
struct cfg80211_registered_device *rdev = info->user_ptr[0];
struct net_device *dev = info->user_ptr[1];
u8 action_code, dialog_token;
+ u32 peer_capability = 0;
u16 status_code;
u8 *peer;
@@ -5711,9 +5734,12 @@
action_code = nla_get_u8(info->attrs[NL80211_ATTR_TDLS_ACTION]);
status_code = nla_get_u16(info->attrs[NL80211_ATTR_STATUS_CODE]);
dialog_token = nla_get_u8(info->attrs[NL80211_ATTR_TDLS_DIALOG_TOKEN]);
+ if (info->attrs[NL80211_ATTR_TDLS_PEER_CAPABILITY])
+ peer_capability =
+ nla_get_u32(info->attrs[NL80211_ATTR_TDLS_PEER_CAPABILITY]);
return rdev->ops->tdls_mgmt(&rdev->wiphy, dev, peer, action_code,
- dialog_token, status_code,
+ dialog_token, status_code, peer_capability,
nla_data(info->attrs[NL80211_ATTR_IE]),
nla_len(info->attrs[NL80211_ATTR_IE]));
}
@@ -6796,6 +6822,57 @@
}
EXPORT_SYMBOL(cfg80211_vendor_cmd_reply);
+static int nl80211_set_qos_map(struct sk_buff *skb,
+ struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct cfg80211_qos_map *qos_map = NULL;
+ struct net_device *dev = info->user_ptr[1];
+ u8 *pos, len, num_des, des_len, des;
+ int ret;
+
+ if (!rdev->ops->set_qos_map)
+ return -EOPNOTSUPP;
+
+ if (info->attrs[NL80211_ATTR_QOS_MAP]) {
+ pos = nla_data(info->attrs[NL80211_ATTR_QOS_MAP]);
+ len = nla_len(info->attrs[NL80211_ATTR_QOS_MAP]);
+
+ if (len % 2 || len < IEEE80211_QOS_MAP_LEN_MIN ||
+ len > IEEE80211_QOS_MAP_LEN_MAX)
+ return -EINVAL;
+
+ qos_map = kzalloc(sizeof(struct cfg80211_qos_map), GFP_KERNEL);
+ if (!qos_map)
+ return -ENOMEM;
+
+ num_des = (len - IEEE80211_QOS_MAP_LEN_MIN) >> 1;
+ if (num_des) {
+ des_len = num_des *
+ sizeof(struct cfg80211_dscp_exception);
+ memcpy(qos_map->dscp_exception, pos, des_len);
+ qos_map->num_des = num_des;
+ for (des = 0; des < num_des; des++) {
+ if (qos_map->dscp_exception[des].up > 7) {
+ kfree(qos_map);
+ return -EINVAL;
+ }
+ }
+ pos += des_len;
+ }
+ memcpy(qos_map->up, pos, IEEE80211_QOS_MAP_LEN_MIN);
+ }
+
+ wdev_lock(dev->ieee80211_ptr);
+ ret = nl80211_key_allowed(dev->ieee80211_ptr);
+ if (!ret)
+ ret = rdev->ops->set_qos_map(&rdev->wiphy, dev, qos_map);
+ wdev_unlock(dev->ieee80211_ptr);
+
+ kfree(qos_map);
+ return ret;
+}
+
#define NL80211_FLAG_NEED_WIPHY 0x01
#define NL80211_FLAG_NEED_NETDEV 0x02
#define NL80211_FLAG_NEED_RTNL 0x04
@@ -7405,7 +7482,15 @@
.doit = nl80211_vendor_cmd,
.policy = nl80211_policy,
.flags = GENL_ADMIN_PERM,
- .internal_flags = NL80211_FLAG_NEED_WIPHY |
+ .internal_flags = NL80211_FLAG_NEED_NETDEV |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_SET_QOS_MAP,
+ .doit = nl80211_set_qos_map,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
NL80211_FLAG_NEED_RTNL,
},
};
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index 78aa256..1f547f9 100755
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -302,6 +302,11 @@
return true;
}
+static bool is_cfg80211_regdom_intersected(void)
+{
+ return is_intersected_alpha2(cfg80211_regdomain->alpha2);
+}
+
static int reg_copy_regd(const struct ieee80211_regdomain **dst_regd,
const struct ieee80211_regdomain *src_regd)
{
@@ -340,6 +345,9 @@
struct reg_regdb_search_request *request;
const struct ieee80211_regdomain *curdom, *regdom;
int i, r;
+ bool set_reg = false;
+
+ mutex_lock(&cfg80211_mutex);
mutex_lock(®_regdb_search_mutex);
while (!list_empty(®_regdb_search_list)) {
@@ -355,9 +363,7 @@
r = reg_copy_regd(®dom, curdom);
if (r)
break;
- mutex_lock(&cfg80211_mutex);
- set_regdom(regdom);
- mutex_unlock(&cfg80211_mutex);
+ set_reg = true;
break;
}
}
@@ -365,6 +371,11 @@
kfree(request);
}
mutex_unlock(®_regdb_search_mutex);
+
+ if (set_reg)
+ set_regdom(regdom);
+
+ mutex_unlock(&cfg80211_mutex);
}
static DECLARE_WORK(reg_regdb_work, reg_regdb_search);
@@ -1381,11 +1392,15 @@
* Process user requests only after previous user/driver/core
* requests have been processed
*/
- if (last_request->initiator == NL80211_REGDOM_SET_BY_CORE ||
- last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER ||
- last_request->initiator == NL80211_REGDOM_SET_BY_USER) {
- if (regdom_changes(last_request->alpha2))
+ if ((last_request->initiator == NL80211_REGDOM_SET_BY_CORE ||
+ last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER ||
+ last_request->initiator == NL80211_REGDOM_SET_BY_USER)) {
+ if (last_request->intersect) {
+ if (!is_cfg80211_regdom_intersected())
+ return -EAGAIN;
+ } else if (regdom_changes(last_request->alpha2)) {
return -EAGAIN;
+ }
}
if (!regdom_changes(pending_request->alpha2))
diff --git a/net/wireless/sme.c b/net/wireless/sme.c
index ab91446..87547ca 100644
--- a/net/wireless/sme.c
+++ b/net/wireless/sme.c
@@ -721,6 +721,10 @@
for (i = 0; i < 6; i++)
rdev->ops->del_key(wdev->wiphy, dev, i, false, NULL);
+ if (rdev->ops->set_qos_map) {
+ rdev->ops->set_qos_map(&rdev->wiphy, dev, NULL);
+ }
+
#ifdef CONFIG_CFG80211_WEXT
memset(&wrqu, 0, sizeof(wrqu));
wrqu.ap_addr.sa_family = ARPHRD_ETHER;
diff --git a/net/wireless/util.c b/net/wireless/util.c
index b89fb94..b83c5b2 100644
--- a/net/wireless/util.c
+++ b/net/wireless/util.c
@@ -10,6 +10,8 @@
#include <net/cfg80211.h>
#include <net/ip.h>
#include <net/dsfield.h>
+#include <net/ndisc.h>
+#include <linux/if_arp.h>
#include "core.h"
struct ieee80211_rate *
@@ -641,7 +643,8 @@
EXPORT_SYMBOL(ieee80211_amsdu_to_8023s);
/* Given a data frame determine the 802.1p/1d tag to use. */
-unsigned int cfg80211_classify8021d(struct sk_buff *skb)
+unsigned int cfg80211_classify8021d(struct sk_buff *skb,
+ struct cfg80211_qos_map *qos_map)
{
unsigned int dscp;
@@ -664,6 +667,21 @@
return 0;
}
+ if (qos_map) {
+ unsigned int i, tmp_dscp = dscp >> 2;
+
+ for (i = 0; i < qos_map->num_des; i++) {
+ if (tmp_dscp == qos_map->dscp_exception[i].dscp)
+ return qos_map->dscp_exception[i].up;
+ }
+
+ for (i = 0; i < 8; i++) {
+ if (tmp_dscp >= qos_map->up[i].low &&
+ tmp_dscp <= qos_map->up[i].high)
+ return i;
+ }
+ }
+
return dscp >> 5;
}
EXPORT_SYMBOL(cfg80211_classify8021d);
@@ -817,6 +835,9 @@
dev->ieee80211_ptr->use_4addr = false;
dev->ieee80211_ptr->mesh_id_up_len = 0;
+ if (rdev->ops->set_qos_map) {
+ rdev->ops->set_qos_map(&rdev->wiphy, dev, NULL);
+ }
switch (otype) {
case NL80211_IFTYPE_ADHOC:
@@ -1131,3 +1152,54 @@
const unsigned char bridge_tunnel_header[] __aligned(2) =
{ 0xaa, 0xaa, 0x03, 0x00, 0x00, 0xf8 };
EXPORT_SYMBOL(bridge_tunnel_header);
+
+bool cfg80211_is_gratuitous_arp_unsolicited_na(struct sk_buff *skb)
+{
+ const struct ethhdr *eth = (void *)skb->data;
+ const struct {
+ struct arphdr hdr;
+ u8 ar_sha[ETH_ALEN];
+ u8 ar_sip[4];
+ u8 ar_tha[ETH_ALEN];
+ u8 ar_tip[4];
+ } __packed *arp;
+ const struct ipv6hdr *ipv6;
+ const struct icmp6hdr *icmpv6;
+
+ switch (eth->h_proto) {
+ case cpu_to_be16(ETH_P_ARP):
+ /* can't say - but will probably be dropped later anyway */
+ if (!pskb_may_pull(skb, sizeof(*eth) + sizeof(*arp)))
+ return false;
+
+ arp = (void *)(eth + 1);
+
+ if ((arp->hdr.ar_op == cpu_to_be16(ARPOP_REPLY) ||
+ arp->hdr.ar_op == cpu_to_be16(ARPOP_REQUEST)) &&
+ !memcmp(arp->ar_sip, arp->ar_tip, sizeof(arp->ar_sip)))
+ return true;
+ break;
+ case cpu_to_be16(ETH_P_IPV6):
+ /* can't say - but will probably be dropped later anyway */
+ if (!pskb_may_pull(skb, sizeof(*eth) + sizeof(*ipv6) +
+ sizeof(*icmpv6)))
+ return false;
+
+ ipv6 = (void *)(eth + 1);
+ icmpv6 = (void *)(ipv6 + 1);
+
+ if (icmpv6->icmp6_type == NDISC_NEIGHBOUR_ADVERTISEMENT &&
+ !memcmp(&ipv6->saddr, &ipv6->daddr, sizeof(ipv6->saddr)))
+ return true;
+ break;
+ default:
+ /*
+ * no need to support other protocols, proxy service isn't
+ * specified for any others
+ */
+ break;
+ }
+
+ return false;
+}
+EXPORT_SYMBOL(cfg80211_is_gratuitous_arp_unsolicited_na);
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index b28b7eb..c868a74 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -3816,6 +3816,11 @@
struct lsm_network_audit net = {0,};
u32 tsid = task_sid(task);
+ if (unlikely(!sksec)) {
+ pr_warn("SELinux: sksec is NULL, socket is already freed\n");
+ return -EINVAL;
+ }
+
if (sksec->sid == SECINITSID_KERNEL)
return 0;
diff --git a/sound/soc/codecs/msm8x10-wcd.c b/sound/soc/codecs/msm8x10-wcd.c
index c73b2c8..a5e0bb3 100644
--- a/sound/soc/codecs/msm8x10-wcd.c
+++ b/sound/soc/codecs/msm8x10-wcd.c
@@ -2361,6 +2361,10 @@
struct snd_kcontrol *kcontrol, int event)
{
switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_update_bits(w->codec, MSM8X10_WCD_A_CDC_ANA_CLK_CTL,
+ 0x4, 0x4);
+ break;
case SND_SOC_DAPM_POST_PMU:
dev_dbg(w->codec->dev,
"%s: Sleeping 20ms after enabling EAR PA\n",
@@ -2371,6 +2375,8 @@
dev_dbg(w->codec->dev,
"%s: Sleeping 20ms after disabling EAR PA\n",
__func__);
+ snd_soc_update_bits(w->codec, MSM8X10_WCD_A_CDC_ANA_CLK_CTL,
+ 0x4, 0x0);
msleep(20);
break;
}
@@ -2382,7 +2388,7 @@
SND_SOC_DAPM_OUTPUT("EAR"),
SND_SOC_DAPM_PGA_E("EAR PA", MSM8X10_WCD_A_RX_EAR_EN, 4, 0, NULL, 0,
- msm8x10_wcd_codec_enable_ear_pa,
+ msm8x10_wcd_codec_enable_ear_pa, SND_SOC_DAPM_PRE_PMU |
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_MIXER("DAC1", MSM8X10_WCD_A_RX_EAR_EN, 6, 0, dac1_switch,
@@ -2471,7 +2477,7 @@
SND_SOC_DAPM_SUPPLY("HPHL CLK", MSM8X10_WCD_A_CDC_ANA_CLK_CTL,
1, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("EAR CLK", MSM8X10_WCD_A_CDC_ANA_CLK_CTL,
- 2, 0, NULL, 0),
+ 6, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("LINEOUT CLK", MSM8X10_WCD_A_CDC_ANA_CLK_CTL,
3, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("SPK CLK", MSM8X10_WCD_A_CDC_ANA_CLK_CTL,
@@ -3145,9 +3151,25 @@
return 0;
}
+static const struct wcd9xxx_mbhc_intr cdc_intr_ids = {
+ .poll_plug_rem = MSM8X10_WCD_IRQ_MBHC_REMOVAL,
+ .shortavg_complete = MSM8X10_WCD_IRQ_MBHC_SHORT_TERM,
+ .potential_button_press = MSM8X10_WCD_IRQ_MBHC_PRESS,
+ .button_release = MSM8X10_WCD_IRQ_MBHC_RELEASE,
+ .dce_est_complete = MSM8X10_WCD_IRQ_MBHC_POTENTIAL,
+ .insertion = MSM8X10_WCD_IRQ_MBHC_INSERTION,
+ .hph_left_ocp = MSM8X10_WCD_IRQ_HPH_PA_OCPL_FAULT,
+ .hph_right_ocp = MSM8X10_WCD_IRQ_HPH_PA_OCPR_FAULT,
+ .hs_jack_switch = MSM8X10_WCD_IRQ_MBHC_HS_DET,
+};
+
static int msm8x10_wcd_device_up(struct snd_soc_codec *codec)
{
- dev_dbg(codec->dev, "%s: device up!\n", __func__);
+ int ret = 0;
+ struct msm8x10_wcd_priv *msm8x10_wcd_priv =
+ snd_soc_codec_get_drvdata(codec);
+
+ dev_err(codec->dev, "%s: device up!\n", __func__);
snd_soc_card_change_online_state(codec->card, 1);
/* delay is required to make sure sound card state updated */
@@ -3156,9 +3178,27 @@
mutex_lock(&codec->mutex);
msm8x10_wcd_bringup(codec);
- msm8x10_wcd_codec_init_reg(codec);
+
msm8x10_wcd_update_reg_defaults(codec);
+ msm8x10_wcd_codec_init_reg(codec);
+
+ wcd9xxx_resmgr_post_ssr(&msm8x10_wcd_priv->resmgr);
+
+ wcd9xxx_mbhc_deinit(&msm8x10_wcd_priv->mbhc);
+
+ ret = wcd9xxx_mbhc_init(&msm8x10_wcd_priv->mbhc,
+ &msm8x10_wcd_priv->resmgr,
+ codec, msm8x10_wcd_enable_mbhc_micbias,
+ &mbhc_cb, &cdc_intr_ids,
+ HELICON_MCLK_CLK_9P6MHZ, true);
+ if (ret)
+ dev_err(codec->dev, "%s: Failed to initialize mbhc\n",
+ __func__);
+ else
+ wcd9xxx_mbhc_start(&msm8x10_wcd_priv->mbhc,
+ msm8x10_wcd_priv->mbhc.mbhc_cfg);
+
mutex_unlock(&codec->mutex);
return 0;
@@ -3197,18 +3237,6 @@
.priority = -INT_MAX,
};
-static const struct wcd9xxx_mbhc_intr cdc_intr_ids = {
- .poll_plug_rem = MSM8X10_WCD_IRQ_MBHC_REMOVAL,
- .shortavg_complete = MSM8X10_WCD_IRQ_MBHC_SHORT_TERM,
- .potential_button_press = MSM8X10_WCD_IRQ_MBHC_PRESS,
- .button_release = MSM8X10_WCD_IRQ_MBHC_RELEASE,
- .dce_est_complete = MSM8X10_WCD_IRQ_MBHC_POTENTIAL,
- .insertion = MSM8X10_WCD_IRQ_MBHC_INSERTION,
- .hph_left_ocp = MSM8X10_WCD_IRQ_HPH_PA_OCPL_FAULT,
- .hph_right_ocp = MSM8X10_WCD_IRQ_HPH_PA_OCPR_FAULT,
- .hs_jack_switch = MSM8X10_WCD_IRQ_MBHC_HS_DET,
-};
-
static int msm8x10_wcd_handle_pdata(struct snd_soc_codec *codec,
struct msm8x10_wcd_pdata *pdata)
{
diff --git a/sound/soc/codecs/wcd9304.c b/sound/soc/codecs/wcd9304.c
index a68722c..aec228a 100644
--- a/sound/soc/codecs/wcd9304.c
+++ b/sound/soc/codecs/wcd9304.c
@@ -1368,7 +1368,7 @@
if (wcd9xxx_tx_vport_validation(
vport_check_table[dai_id],
port_id,
- sitar_p->dai)) {
+ sitar_p->dai, NUM_CODEC_DAIS)) {
dev_dbg(codec->dev, "%s: TX%u is used by other virtual port\n",
__func__, port_id + 1);
mutex_unlock(&codec->mutex);
diff --git a/sound/soc/codecs/wcd9306.c b/sound/soc/codecs/wcd9306.c
index 95f2041..38d7901 100644
--- a/sound/soc/codecs/wcd9306.c
+++ b/sound/soc/codecs/wcd9306.c
@@ -1344,6 +1344,9 @@
static const struct soc_enum rx3_mix1_inp2_chain_enum =
SOC_ENUM_SINGLE(TAPAN_A_CDC_CONN_RX3_B1_CTL, 4, 13, rx_3_4_mix1_text);
+static const struct soc_enum rx3_mix1_inp3_chain_enum =
+ SOC_ENUM_SINGLE(TAPAN_A_CDC_CONN_RX3_B2_CTL, 0, 13, rx_3_4_mix1_text);
+
static const struct soc_enum rx4_mix1_inp1_chain_enum =
SOC_ENUM_SINGLE(TAPAN_A_CDC_CONN_RX4_B1_CTL, 0, 13, rx_3_4_mix1_text);
@@ -1442,6 +1445,9 @@
static const struct snd_kcontrol_new rx3_mix1_inp2_mux =
SOC_DAPM_ENUM("RX3 MIX1 INP2 Mux", rx3_mix1_inp2_chain_enum);
+static const struct snd_kcontrol_new rx3_mix1_inp3_mux =
+ SOC_DAPM_ENUM("RX3 MIX1 INP3 Mux", rx3_mix1_inp3_chain_enum);
+
static const struct snd_kcontrol_new rx4_mix1_inp1_mux =
SOC_DAPM_ENUM("RX4 MIX1 INP1 Mux", rx4_mix1_inp1_chain_enum);
@@ -1507,6 +1513,7 @@
u16 tx_mux_ctl_reg;
u8 adc_dmic_sel = 0x0;
int ret = 0;
+ char *srch = NULL;
if (ucontrol->value.enumerated.item[0] > e->max - 1)
return -EINVAL;
@@ -1525,8 +1532,12 @@
ret = -EINVAL;
goto out;
}
-
- ret = kstrtouint(strpbrk(dec_name, "1234"), 10, &decimator);
+ srch = strpbrk(dec_name, "1234");
+ if (srch == NULL) {
+ pr_err("%s: Invalid decimator name %s\n", __func__, dec_name);
+ return -EINVAL;
+ }
+ ret = kstrtouint(srch, 10, &decimator);
if (ret < 0) {
pr_err("%s: Invalid decimator = %s\n", __func__, dec_name);
ret = -EINVAL;
@@ -1694,7 +1705,7 @@
if (wcd9xxx_tx_vport_validation(
vtable,
port_id,
- tapan_p->dai)) {
+ tapan_p->dai, NUM_CODEC_DAIS)) {
dev_dbg(codec->dev, "%s: TX%u is used by other virtual port\n",
__func__, port_id + 1);
mutex_unlock(&codec->mutex);
@@ -2017,8 +2028,15 @@
s32 *dmic_clk_cnt;
unsigned int dmic;
int ret;
+ char *srch = NULL;
- ret = kstrtouint(strpbrk(w->name, "1234"), 10, &dmic);
+ srch = strpbrk(w->name, "1234");
+ if (srch == NULL) {
+ pr_err("%s: Invalid widget name %s\n", __func__, w->name);
+ return -EINVAL;
+ }
+
+ ret = kstrtouint(srch, 10, &dmic);
if (ret < 0) {
pr_err("%s: Invalid DMIC line on the codec\n", __func__);
return -EINVAL;
@@ -2350,6 +2368,7 @@
u16 dec_reset_reg, tx_vol_ctl_reg, tx_mux_ctl_reg;
u8 dec_hpf_cut_of_freq;
int offset;
+ char *srch = NULL;
dev_dbg(codec->dev, "%s %d\n", __func__, event);
@@ -2365,8 +2384,12 @@
ret = -EINVAL;
goto out;
}
-
- ret = kstrtouint(strpbrk(dec_name, "123456789"), 10, &decimator);
+ srch = strpbrk(dec_name, "123456789");
+ if (srch == NULL) {
+ pr_err("%s: Invalid decimator name %s\n", __func__, dec_name);
+ return -EINVAL;
+ }
+ ret = kstrtouint(srch, 10, &decimator);
if (ret < 0) {
pr_err("%s: Invalid decimator = %s\n", __func__, dec_name);
ret = -EINVAL;
@@ -2496,6 +2519,29 @@
return 0;
}
+static int tapan_codec_rx_dem_select(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+
+ struct snd_soc_codec *codec = w->codec;
+
+ pr_debug("%s %d %s\n", __func__, event, w->name);
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (codec_ver == WCD9306)
+ snd_soc_update_bits(codec, TAPAN_A_CDC_RX2_B6_CTL,
+ 1 << 5, 1 << 5);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ if (codec_ver == WCD9306)
+ snd_soc_update_bits(codec, TAPAN_A_CDC_RX2_B6_CTL,
+ 1 << 5, 0);
+ break;
+ }
+
+ return 0;
+}
+
static int tapan_codec_enable_interpolator(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
@@ -3310,7 +3356,7 @@
dev_dbg(dai->codec->dev, "%s(): substream = %s stream = %d\n",
__func__, substream->name, substream->stream);
- if (dai->id <= NUM_CODEC_DAIS) {
+ if (dai->id < NUM_CODEC_DAIS) {
if (tapan->dai[dai->id].ch_mask) {
active = 1;
dev_dbg(dai->codec->dev, "%s(): Codec DAI: chmask[%d] = 0x%lx\n",
@@ -3429,7 +3475,7 @@
{
struct tapan_priv *tapan = snd_soc_codec_get_drvdata(dai->codec);
struct wcd9xxx *core = dev_get_drvdata(dai->codec->dev->parent);
- if (!tx_slot && !rx_slot) {
+ if (!tx_slot || !rx_slot) {
pr_err("%s: Invalid\n", __func__);
return -EINVAL;
}
@@ -4108,6 +4154,7 @@
switch (event) {
case SND_SOC_DAPM_POST_PMU:
+ dai->bus_down_in_recovery = false;
(void) tapan_codec_enable_slim_chmask(dai, true);
ret = wcd9xxx_cfg_slim_sch_rx(core, &dai->wcd9xxx_ch_list,
dai->rate, dai->bit_width,
@@ -4116,7 +4163,8 @@
case SND_SOC_DAPM_POST_PMD:
ret = wcd9xxx_close_slim_sch_rx(core, &dai->wcd9xxx_ch_list,
dai->grph);
- ret = tapan_codec_enable_slim_chmask(dai, false);
+ if (!dai->bus_down_in_recovery)
+ ret = tapan_codec_enable_slim_chmask(dai, false);
if (ret < 0) {
ret = wcd9xxx_disconnect_port(core,
&dai->wcd9xxx_ch_list,
@@ -4131,6 +4179,7 @@
pm_runtime_put(core->dev->parent);
dev_dbg(codec->dev, "%s: unvote requested", __func__);
}
+ dai->bus_down_in_recovery = false;
break;
}
return ret;
@@ -4162,6 +4211,7 @@
dai = &tapan_p->dai[w->shift];
switch (event) {
case SND_SOC_DAPM_POST_PMU:
+ dai->bus_down_in_recovery = false;
(void) tapan_codec_enable_slim_chmask(dai, true);
ret = wcd9xxx_cfg_slim_sch_tx(core, &dai->wcd9xxx_ch_list,
dai->rate, dai->bit_width,
@@ -4170,7 +4220,8 @@
case SND_SOC_DAPM_POST_PMD:
ret = wcd9xxx_close_slim_sch_tx(core, &dai->wcd9xxx_ch_list,
dai->grph);
- ret = tapan_codec_enable_slim_chmask(dai, false);
+ if (!dai->bus_down_in_recovery)
+ ret = tapan_codec_enable_slim_chmask(dai, false);
if (ret < 0) {
ret = wcd9xxx_disconnect_port(core,
&dai->wcd9xxx_ch_list,
@@ -4185,6 +4236,7 @@
pm_runtime_put(core->dev->parent);
dev_dbg(codec->dev, "%s: unvote requested", __func__);
}
+ dai->bus_down_in_recovery = false;
break;
}
return ret;
@@ -4238,6 +4290,23 @@
return 0;
}
+static int tapan_codec_iir_mux_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ pr_debug("%s: event = %d\n", __func__, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_write(codec, w->reg, snd_soc_read(codec, w->reg));
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_write(codec, w->reg, snd_soc_read(codec, w->reg));
+ break;
+ }
+ return 0;
+}
+
static int tapan_codec_dsm_mux_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
@@ -4488,7 +4557,7 @@
SND_SOC_DAPM_MUX("RX3 MIX1 INP2", SND_SOC_NOPM, 0, 0,
&rx3_mix1_inp2_mux),
SND_SOC_DAPM_MUX("RX3 MIX1 INP3", SND_SOC_NOPM, 0, 0,
- &rx3_mix1_inp2_mux),
+ &rx3_mix1_inp3_mux),
/* RX1 MIX2 mux inputs */
SND_SOC_DAPM_MUX("RX1 MIX2 INP1", SND_SOC_NOPM, 0, 0,
@@ -4517,8 +4586,10 @@
SND_SOC_DAPM_MIXER("RX1 CHAIN", TAPAN_A_CDC_RX1_B6_CTL, 5, 0,
NULL, 0),
- SND_SOC_DAPM_MIXER("RX2 CHAIN", TAPAN_A_CDC_RX2_B6_CTL, 5, 0,
- NULL, 0),
+
+ SND_SOC_DAPM_MIXER_E("RX2 CHAIN", SND_SOC_NOPM, 0, 0, NULL,
+ 0, tapan_codec_rx_dem_select, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_MUX_E("CLASS_H_DSM MUX", SND_SOC_NOPM, 0, 0,
&class_h_dsm_mux, tapan_codec_dsm_mux_event,
@@ -4725,7 +4796,10 @@
SND_SOC_DAPM_POST_PMD),
/* Sidetone */
- SND_SOC_DAPM_MUX("IIR1 INP1 MUX", SND_SOC_NOPM, 0, 0, &iir1_inp1_mux),
+ SND_SOC_DAPM_MUX_E("IIR1 INP1 MUX", TAPAN_A_CDC_IIR1_GAIN_B1_CTL, 0, 0,
+ &iir1_inp1_mux, tapan_codec_iir_mux_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
SND_SOC_DAPM_PGA("IIR1", TAPAN_A_CDC_CLK_SD_CTL, 0, 0, NULL, 0),
/* AUX PGA */
@@ -4826,9 +4900,9 @@
struct snd_soc_codec *codec = tapan->codec;
struct wcd9xxx_pdata *pdata = tapan->resmgr.pdata;
int k1, k2, k3, rc = 0;
- u8 txfe_bypass = pdata->amic_settings.txfe_enable;
- u8 txfe_buff = pdata->amic_settings.txfe_buff;
- u8 flag = pdata->amic_settings.use_pdata;
+ u8 txfe_bypass;
+ u8 txfe_buff;
+ u8 flag;
u8 i = 0, j = 0;
u8 val_txfe = 0, value = 0;
u8 dmic_sample_rate_value = 0;
@@ -4840,6 +4914,9 @@
rc = -ENODEV;
goto done;
}
+ txfe_bypass = pdata->amic_settings.txfe_enable;
+ txfe_buff = pdata->amic_settings.txfe_buff;
+ flag = pdata->amic_settings.use_pdata;
/* Make sure settings are correct */
if ((pdata->micbias.ldoh_v > WCD9XXX_LDOH_3P0_V) ||
@@ -5018,7 +5095,7 @@
/* RX1 and RX2 defaults */
TAPAN_REG_VAL(TAPAN_A_CDC_RX1_B6_CTL, 0xA0),
- TAPAN_REG_VAL(TAPAN_A_CDC_RX2_B6_CTL, 0xA0),
+ TAPAN_REG_VAL(TAPAN_A_CDC_RX2_B6_CTL, 0x80),
/* Heaset set Right from RX2 */
TAPAN_REG_VAL(TAPAN_A_CDC_CONN_RX2_B2_CTL, 0x10),
@@ -5661,6 +5738,7 @@
int rco_clk_rate;
struct snd_soc_codec *codec;
struct tapan_priv *tapan;
+ int count;
codec = (struct snd_soc_codec *)(wcd9xxx->ssr_priv);
tapan = snd_soc_codec_get_drvdata(codec);
@@ -5717,6 +5795,9 @@
tapan->machine_codec_event_cb(codec, WCD9XXX_CODEC_EVENT_CODEC_UP);
+ for (count = 0; count < NUM_CODEC_DAIS; count++)
+ tapan->dai[count].bus_down_in_recovery = true;
+
mutex_unlock(&codec->mutex);
return ret;
}
@@ -5740,7 +5821,17 @@
const char *name)
{
int i;
- struct wcd9xxx *core = dev_get_drvdata(codec->dev->parent);
+ struct wcd9xxx *core = NULL;
+
+ if (codec == NULL) {
+ dev_err(codec->dev, "%s: codec not initialized\n", __func__);
+ return NULL;
+ }
+ core = dev_get_drvdata(codec->dev->parent);
+ if (core == NULL) {
+ dev_err(codec->dev, "%s: core not initialized\n", __func__);
+ return NULL;
+ }
for (i = 0; i < core->num_of_supplies; i++) {
if (core->supplies[i].supply &&
diff --git a/sound/soc/codecs/wcd9310.c b/sound/soc/codecs/wcd9310.c
index 725c51f..727a6d6 100644
--- a/sound/soc/codecs/wcd9310.c
+++ b/sound/soc/codecs/wcd9310.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2014, 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
@@ -2032,7 +2032,7 @@
if (wcd9xxx_tx_vport_validation(
vtable,
port_id,
- tabla_p->dai)) {
+ tabla_p->dai, NUM_CODEC_DAIS)) {
dev_dbg(codec->dev, "%s: TX%u is used by other virtual port\n",
__func__, port_id + 1);
mutex_unlock(&codec->mutex);
diff --git a/sound/soc/codecs/wcd9320.c b/sound/soc/codecs/wcd9320.c
index b72590f..4c5d327 100644
--- a/sound/soc/codecs/wcd9320.c
+++ b/sound/soc/codecs/wcd9320.c
@@ -1130,6 +1130,24 @@
static const struct snd_kcontrol_new class_h_dsm_mux =
SOC_DAPM_ENUM("CLASS_H_DSM MUX Mux", class_h_dsm_enum);
+static const char * const rx1_interpolator_text[] = {
+ "ZERO", "RX1 MIX2"
+};
+static const struct soc_enum rx1_interpolator_enum =
+ SOC_ENUM_SINGLE(TAIKO_A_CDC_CLK_RX_B1_CTL, 0, 2, rx1_interpolator_text);
+
+static const struct snd_kcontrol_new rx1_interpolator =
+ SOC_DAPM_ENUM("RX1 INTERP Mux", rx1_interpolator_enum);
+
+static const char * const rx2_interpolator_text[] = {
+ "ZERO", "RX2 MIX2"
+};
+static const struct soc_enum rx2_interpolator_enum =
+ SOC_ENUM_SINGLE(TAIKO_A_CDC_CLK_RX_B1_CTL, 1, 2, rx2_interpolator_text);
+
+static const struct snd_kcontrol_new rx2_interpolator =
+ SOC_DAPM_ENUM("RX2 INTERP Mux", rx2_interpolator_enum);
+
static const char *const taiko_conn_mad_text[] = {
"ADC_MB", "ADC1", "ADC2", "ADC3", "ADC4", "ADC5", "ADC6", "NOTUSED1",
"DMIC1", "DMIC2", "DMIC3", "DMIC4", "DMIC5", "DMIC6", "NOTUSED2",
@@ -2194,7 +2212,7 @@
if (wcd9xxx_tx_vport_validation(
vtable,
port_id,
- taiko_p->dai)) {
+ taiko_p->dai, NUM_CODEC_DAIS)) {
dev_dbg(codec->dev, "%s: TX%u is used by other virtual port\n",
__func__, port_id + 1);
mutex_unlock(&codec->mutex);
@@ -3241,6 +3259,7 @@
case SND_SOC_DAPM_POST_PMD:
snd_soc_update_bits(codec, TAIKO_A_CDC_CLK_RDAC_CLK_EN_CTL,
0x02, 0x00);
+ break;
}
return 0;
}
@@ -3743,8 +3762,11 @@
{"CLASS_H_DSM MUX", "DSM_HPHL_RX1", "RX1 CHAIN"},
- {"RX1 CHAIN", NULL, "RX1 MIX2"},
- {"RX2 CHAIN", NULL, "RX2 MIX2"},
+ {"RX1 INTERP", NULL, "RX1 MIX2"},
+ {"RX1 CHAIN", NULL, "RX1 INTERP"},
+ {"RX2 INTERP", NULL, "RX2 MIX2"},
+ {"RX2 CHAIN", NULL, "RX2 INTERP"},
+
{"RX1 MIX2", NULL, "ANC1 MUX"},
{"RX2 MIX2", NULL, "ANC2 MUX"},
@@ -4260,6 +4282,10 @@
if (reg == TAIKO_A_RX_HPH_L_STATUS || reg == TAIKO_A_RX_HPH_R_STATUS)
return 1;
+ /* HPH PA Enable */
+ if (reg == TAIKO_A_RX_HPH_CNP_EN)
+ return 1;
+
if (reg == TAIKO_A_MBHC_INSERT_DET_STATUS)
return 1;
@@ -4334,13 +4360,8 @@
static int taiko_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct wcd9xxx *taiko_core = dev_get_drvdata(dai->codec->dev->parent);
pr_debug("%s(): substream = %s stream = %d\n" , __func__,
substream->name, substream->stream);
- if ((taiko_core != NULL) &&
- (taiko_core->dev != NULL) &&
- (taiko_core->dev->parent != NULL))
- pm_runtime_get_sync(taiko_core->dev->parent);
return 0;
}
@@ -4348,15 +4369,8 @@
static void taiko_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct wcd9xxx *taiko_core = dev_get_drvdata(dai->codec->dev->parent);
pr_debug("%s(): substream = %s stream = %d\n" , __func__,
substream->name, substream->stream);
- if ((taiko_core != NULL) &&
- (taiko_core->dev != NULL) &&
- (taiko_core->dev->parent != NULL)) {
- pm_runtime_mark_last_busy(taiko_core->dev->parent);
- pm_runtime_put(taiko_core->dev->parent);
- }
}
int taiko_mclk_enable(struct snd_soc_codec *codec, int mclk_enable, bool dapm)
@@ -5130,6 +5144,7 @@
switch (event) {
case SND_SOC_DAPM_POST_PMU:
+ dai->bus_down_in_recovery = false;
taiko_codec_enable_int_port(dai, codec);
(void) taiko_codec_enable_slim_chmask(dai, true);
ret = wcd9xxx_cfg_slim_sch_rx(core, &dai->wcd9xxx_ch_list,
@@ -5139,7 +5154,9 @@
case SND_SOC_DAPM_POST_PMD:
ret = wcd9xxx_close_slim_sch_rx(core, &dai->wcd9xxx_ch_list,
dai->grph);
- ret = taiko_codec_enable_slim_chmask(dai, false);
+ if (!dai->bus_down_in_recovery)
+ ret = taiko_codec_enable_slim_chmask(dai, false);
+
if (ret < 0) {
ret = wcd9xxx_disconnect_port(core,
&dai->wcd9xxx_ch_list,
@@ -5147,6 +5164,7 @@
pr_debug("%s: Disconnect RX port, ret = %d\n",
__func__, ret);
}
+ dai->bus_down_in_recovery = false;
break;
}
return ret;
@@ -5196,6 +5214,7 @@
snd_soc_update_bits(codec,
TAIKO_A_CDC_CLK_TX_CLK_EN_B2_CTL, 0xC, 0xC);
taiko_codec_enable_int_port(dai, codec);
+ dai->bus_down_in_recovery = false;
(void) taiko_codec_enable_slim_chmask(dai, true);
ret = wcd9xxx_cfg_slim_sch_tx(core, &dai->wcd9xxx_ch_list,
dai->rate, dai->bit_width,
@@ -5212,6 +5231,8 @@
/*Disable V&I sensing*/
snd_soc_update_bits(codec, TAIKO_A_SPKR_PROT_EN,
0x88, 0x00);
+
+ dai->bus_down_in_recovery = false;
break;
}
out_vi:
@@ -5241,9 +5262,11 @@
__func__, w->name, event, w->shift);
dai = &taiko_p->dai[w->shift];
+
switch (event) {
case SND_SOC_DAPM_POST_PMU:
taiko_codec_enable_int_port(dai, codec);
+ dai->bus_down_in_recovery = false;
(void) taiko_codec_enable_slim_chmask(dai, true);
ret = wcd9xxx_cfg_slim_sch_tx(core, &dai->wcd9xxx_ch_list,
dai->rate, dai->bit_width,
@@ -5252,7 +5275,9 @@
case SND_SOC_DAPM_POST_PMD:
ret = wcd9xxx_close_slim_sch_tx(core, &dai->wcd9xxx_ch_list,
dai->grph);
- ret = taiko_codec_enable_slim_chmask(dai, false);
+ if (!dai->bus_down_in_recovery)
+ ret = taiko_codec_enable_slim_chmask(dai, false);
+
if (ret < 0) {
ret = wcd9xxx_disconnect_port(core,
&dai->wcd9xxx_ch_list,
@@ -5260,6 +5285,8 @@
pr_debug("%s: Disconnect RX port, ret = %d\n",
__func__, ret);
}
+
+ dai->bus_down_in_recovery = false;
break;
}
return ret;
@@ -5505,12 +5532,9 @@
SND_SOC_DAPM_MIXER("RX2 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_MIXER("RX7 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
- SND_SOC_DAPM_MIXER_E("RX1 MIX2", TAIKO_A_CDC_CLK_RX_B1_CTL, 0, 0, NULL,
- 0, taiko_codec_enable_interpolator, SND_SOC_DAPM_PRE_PMU |
- SND_SOC_DAPM_POST_PMU),
- SND_SOC_DAPM_MIXER_E("RX2 MIX2", TAIKO_A_CDC_CLK_RX_B1_CTL, 1, 0, NULL,
- 0, taiko_codec_enable_interpolator, SND_SOC_DAPM_PRE_PMU |
- SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_MIXER("RX1 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("RX2 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+
SND_SOC_DAPM_MIXER_E("RX3 MIX1", TAIKO_A_CDC_CLK_RX_B1_CTL, 2, 0, NULL,
0, taiko_codec_enable_interpolator, SND_SOC_DAPM_PRE_PMU |
SND_SOC_DAPM_POST_PMU),
@@ -5527,6 +5551,12 @@
0, taiko_codec_enable_interpolator, SND_SOC_DAPM_PRE_PMU |
SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_MUX_E("RX1 INTERP", TAIKO_A_CDC_CLK_RX_B1_CTL, 0, 0,
+ &rx1_interpolator, taiko_codec_enable_interpolator,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_MUX_E("RX2 INTERP", TAIKO_A_CDC_CLK_RX_B1_CTL, 1, 0,
+ &rx2_interpolator, taiko_codec_enable_interpolator,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_MIXER("RX1 CHAIN", TAIKO_A_CDC_RX1_B6_CTL, 5, 0, NULL, 0),
SND_SOC_DAPM_MIXER("RX2 CHAIN", TAIKO_A_CDC_RX2_B6_CTL, 5, 0, NULL, 0),
@@ -6468,6 +6498,9 @@
/* set MAD input MIC to DMIC1 */
{TAIKO_A_CDC_CONN_MAD, 0x0F, 0x08},
+
+ /* set DMIC CLK drive strength to 4mA */
+ {TAIKO_A_HDRIVE_OVERRIDE, 0x07, 0x01},
};
static void taiko_codec_init_reg(struct snd_soc_codec *codec)
@@ -6790,6 +6823,7 @@
struct snd_soc_codec *codec;
struct taiko_priv *taiko;
int rco_clk_rate;
+ int count;
codec = (struct snd_soc_codec *)(wcd9xxx->ssr_priv);
taiko = snd_soc_codec_get_drvdata(codec);
@@ -6797,18 +6831,21 @@
snd_soc_card_change_online_state(codec->card, 1);
mutex_lock(&codec->mutex);
- if (codec->reg_def_copy) {
- pr_debug("%s: Update ASOC cache", __func__);
- kfree(codec->reg_cache);
- codec->reg_cache = kmemdup(codec->reg_def_copy,
- codec->reg_size, GFP_KERNEL);
- }
+
+ taiko_update_reg_defaults(codec);
+ if (wcd9xxx->mclk_rate == TAIKO_MCLK_CLK_12P288MHZ)
+ snd_soc_update_bits(codec, TAIKO_A_CHIP_CTL, 0x06, 0x0);
+ else if (wcd9xxx->mclk_rate == TAIKO_MCLK_CLK_9P6MHZ)
+ snd_soc_update_bits(codec, TAIKO_A_CHIP_CTL, 0x06, 0x2);
+ taiko_codec_init_reg(codec);
if (spkr_drv_wrnd == 1)
snd_soc_update_bits(codec, TAIKO_A_SPKR_DRV_EN, 0x80, 0x80);
- taiko_update_reg_defaults(codec);
- taiko_codec_init_reg(codec);
+ codec->cache_sync = true;
+ snd_soc_cache_sync(codec);
+ codec->cache_sync = false;
+
ret = taiko_handle_pdata(taiko);
if (IS_ERR_VALUE(ret))
pr_err("%s: bad pdata\n", __func__);
@@ -6843,6 +6880,9 @@
if (ret)
pr_err("%s: Failed to setup irq: %d\n", __func__, ret);
+ for (count = 0; count < NUM_CODEC_DAIS; count++)
+ taiko->dai[count].bus_down_in_recovery = true;
+
mutex_unlock(&codec->mutex);
return ret;
}
@@ -7208,6 +7248,11 @@
{
struct platform_device *pdev = to_platform_device(dev);
struct taiko_priv *taiko = platform_get_drvdata(pdev);
+
+ if (!taiko) {
+ dev_err(dev, "%s: taiko private data is NULL\n", __func__);
+ return -EINVAL;
+ }
dev_dbg(dev, "%s: system resume\n", __func__);
/* Notify */
wcd9xxx_resmgr_notifier_call(&taiko->resmgr, WCD9XXX_EVENT_POST_RESUME);
diff --git a/sound/soc/codecs/wcd9xxx-common.c b/sound/soc/codecs/wcd9xxx-common.c
index b104a6b..675e378 100644
--- a/sound/soc/codecs/wcd9xxx-common.c
+++ b/sound/soc/codecs/wcd9xxx-common.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2014, 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
@@ -552,7 +552,13 @@
__func__);
goto ret;
}
- for (i = 0; i < ARRAY_SIZE(imped_index); i++) {
+ if (imped >= imped_index[ARRAY_SIZE(imped_index) - 1].imped_val) {
+ pr_debug("%s, detected impedance is greater than 32164 Ohm\n",
+ __func__);
+ i = ARRAY_SIZE(imped_index) - 1;
+ goto ret;
+ }
+ for (i = 0; i < ARRAY_SIZE(imped_index) - 1; i++) {
if (imped >= imped_index[i].imped_val &&
imped < imped_index[i + 1].imped_val)
break;
@@ -569,7 +575,7 @@
int i = 0;
int index = 0;
index = get_impedance_index(imped);
- if (index > ARRAY_SIZE(imped_index)) {
+ if (index >= ARRAY_SIZE(imped_index)) {
pr_err("%s, invalid imped = %d\n", __func__, imped);
return;
}
@@ -651,6 +657,29 @@
}
EXPORT_SYMBOL(wcd9xxx_restore_registers);
+static void wcd9xxx_dynamic_bypass_buck_ctrl(struct snd_soc_codec *cdc,
+ bool enable)
+{
+ int i;
+ const struct wcd9xxx_reg_mask_val reg_set[] = {
+ {WCD9XXX_A_BUCK_MODE_3, (0x1 << 3), (enable << 3)},
+ {WCD9XXX_A_BUCK_MODE_5, (0x1 << 1), (enable << 1)},
+ {WCD9XXX_A_BUCK_MODE_5, 0x1, enable}
+ };
+ if (!enable) {
+ snd_soc_update_bits(cdc, WCD9XXX_A_BUCK_MODE_1,
+ (0x1 << 3), 0x00);
+ snd_soc_update_bits(cdc, WCD9XXX_A_BUCK_MODE_4,
+ 0xFF, BUCK_VREF_2V);
+ }
+ for (i = 0; i < ARRAY_SIZE(reg_set); i++)
+ snd_soc_update_bits(cdc, reg_set[i].reg, reg_set[i].mask,
+ reg_set[i].val);
+
+ /* 50us sleep is reqd. as per the class H HW design sequence */
+ usleep_range(BUCK_SETTLE_TIME_US, BUCK_SETTLE_TIME_US+10);
+}
+
static void wcd9xxx_set_buck_mode(struct snd_soc_codec *codec, u8 buck_vref)
{
int i;
@@ -807,6 +836,239 @@
__func__);
}
+static void wcd9xxx_ncp_bypass_enable(struct snd_soc_codec *cdc, bool enable)
+{
+ snd_soc_update_bits(cdc, WCD9XXX_A_NCP_STATIC, 0x10, (enable << 4));
+ /* 50us sleep is reqd. as per the class H HW design sequence */
+ usleep_range(BUCK_SETTLE_TIME_US, BUCK_SETTLE_TIME_US+10);
+}
+
+static void wcd9xxx_clsh_set_Iest(struct snd_soc_codec *codec,
+ u8 value)
+{
+ snd_soc_update_bits(codec, WCD9XXX_A_BUCK_MODE_5,
+ 0x01, (0x01 & 0x03));
+ snd_soc_update_bits(codec, WCD9XXX_A_BUCK_MODE_5,
+ 0xFC, (value << 2));
+}
+
+static void wcd9xxx_clsh_state_hph_ear(struct snd_soc_codec *codec,
+ struct wcd9xxx_clsh_cdc_data *clsh_d,
+ u8 req_state, bool is_enable)
+{
+ int compute_pa = 0;
+
+ dev_dbg(codec->dev, "%s: enter %s\n", __func__,
+ is_enable ? "enable" : "disable");
+
+ if (is_enable) {
+ /*
+ * The below check condition is required to make sure
+ * functions inside if condition will execute only once.
+ */
+ if ((clsh_d->state == WCD9XXX_CLSH_STATE_EAR) ||
+ (req_state == WCD9XXX_CLSH_STATE_EAR)) {
+ wcd9xxx_dynamic_bypass_buck_ctrl(codec, false);
+ wcd9xxx_ncp_bypass_enable(codec, true);
+ }
+ switch (req_state) {
+ case WCD9XXX_CLSH_STATE_HPHL:
+ compute_pa = CLSH_COMPUTE_HPH_L;
+ break;
+ case WCD9XXX_CLSH_STATE_HPHR:
+ compute_pa = CLSH_COMPUTE_HPH_R;
+ break;
+ case WCD9XXX_CLSH_STATE_EAR:
+ compute_pa = CLSH_COMPUTE_EAR;
+ break;
+ default:
+ dev_dbg(codec->dev,
+ "%s:Invalid state:0x%x,enable:0x%x\n",
+ __func__, req_state, is_enable);
+ break;
+ }
+ wcd9xxx_clsh_comp_req(codec, clsh_d, compute_pa, true);
+
+ dev_dbg(codec->dev, "%s: Enabled hph+ear mode clsh\n",
+ __func__);
+ } else {
+ switch (req_state) {
+ case WCD9XXX_CLSH_STATE_HPHL:
+ compute_pa = CLSH_COMPUTE_HPH_L;
+ break;
+ case WCD9XXX_CLSH_STATE_HPHR:
+ compute_pa = CLSH_COMPUTE_HPH_R;
+ break;
+ case WCD9XXX_CLSH_STATE_EAR:
+ compute_pa = CLSH_COMPUTE_EAR;
+ break;
+ default:
+ dev_dbg(codec->dev,
+ "%s:Invalid state:0x%x,enable:0x%x\n",
+ __func__, req_state, is_enable);
+ break;
+ }
+ wcd9xxx_clsh_comp_req(codec, clsh_d, compute_pa, false);
+
+ if (((clsh_d->state & (~req_state)) ==
+ WCD9XXX_CLSH_STATE_EAR) ||
+ (req_state == WCD9XXX_CLSH_STATE_EAR)) {
+ wcd9xxx_ncp_bypass_enable(codec, false);
+ wcd9xxx_dynamic_bypass_buck_ctrl(codec, true);
+ }
+ }
+}
+
+static void wcd9xxx_clsh_state_hph_lo(struct snd_soc_codec *codec,
+ struct wcd9xxx_clsh_cdc_data *clsh_d,
+ u8 req_state, bool is_enable)
+{
+
+ dev_dbg(codec->dev, "%s: enter %s\n", __func__,
+ is_enable ? "enable" : "disable");
+ if (is_enable) {
+ if ((clsh_d->state == WCD9XXX_CLSH_STATE_LO) ||
+ (req_state == WCD9XXX_CLSH_STATE_LO)) {
+ wcd9xxx_dynamic_bypass_buck_ctrl(codec, false);
+ wcd9xxx_enable_buck(codec, clsh_d, true);
+ wcd9xxx_set_fclk_get_ncp(codec, clsh_d,
+ NCP_FCLK_LEVEL_8);
+ if (req_state & WCD9XXX_CLSH_STATE_HPH_ST) {
+ wcd9xxx_ncp_bypass_enable(codec, true);
+ wcd9xxx_enable_clsh_block(codec, clsh_d, true);
+ wcd9xxx_chargepump_request(codec, true);
+ wcd9xxx_enable_anc_delay(codec, true);
+ }
+ }
+ if (req_state == WCD9XXX_CLSH_STATE_HPHL)
+ wcd9xxx_clsh_comp_req(codec, clsh_d,
+ CLSH_COMPUTE_HPH_L, true);
+ if (req_state == WCD9XXX_CLSH_STATE_HPHR)
+ wcd9xxx_clsh_comp_req(codec, clsh_d,
+ CLSH_COMPUTE_HPH_R, true);
+ } else {
+ switch (req_state) {
+ case WCD9XXX_CLSH_STATE_LO:
+ snd_soc_update_bits(codec, WCD9XXX_A_NCP_STATIC,
+ 0x20, 0x00);
+ wcd9xxx_dynamic_bypass_buck_ctrl(codec, true);
+ break;
+ case WCD9XXX_CLSH_STATE_HPHL:
+ wcd9xxx_clsh_comp_req(codec, clsh_d,
+ CLSH_COMPUTE_HPH_L, false);
+ break;
+ case WCD9XXX_CLSH_STATE_HPHR:
+ wcd9xxx_clsh_comp_req(codec, clsh_d,
+ CLSH_COMPUTE_HPH_R, false);
+ break;
+ default:
+ dev_dbg(codec->dev,
+ "%s:Invalid state:0x%x,enable:0x%x\n",
+ __func__, req_state, is_enable);
+ break;
+ }
+ if ((req_state == WCD9XXX_CLSH_STATE_LO) ||
+ ((clsh_d->state & (~req_state)) == WCD9XXX_CLSH_STATE_LO)) {
+ wcd9xxx_set_fclk_put_ncp(codec, clsh_d,
+ NCP_FCLK_LEVEL_8);
+ wcd9xxx_ncp_bypass_enable(codec, false);
+
+ if (req_state & WCD9XXX_CLSH_STATE_HPH_ST) {
+ usleep_range(BUCK_SETTLE_TIME_US,
+ BUCK_SETTLE_TIME_US + 10);
+ if (clsh_d->buck_mv ==
+ WCD9XXX_CDC_BUCK_MV_1P8) {
+ wcd9xxx_enable_buck(codec, clsh_d,
+ false);
+ wcd9xxx_ncp_bypass_enable(codec, true);
+ } else {
+ /*
+ *NCP settle time recommended by codec
+ *specification
+ */
+ usleep_range(NCP_SETTLE_TIME_US,
+ NCP_SETTLE_TIME_US + 10);
+ wcd9xxx_clsh_set_Iest(codec, 0x02);
+ }
+ snd_soc_update_bits(codec,
+ WCD9XXX_A_BUCK_MODE_1,
+ 0x04, 0x00);
+ snd_soc_update_bits(codec,
+ WCD9XXX_A_BUCK_MODE_4,
+ 0xFF, BUCK_VREF_1P8V);
+ }
+ }
+ }
+}
+
+static void wcd9xxx_clsh_state_ear_lo(struct snd_soc_codec *codec,
+ struct wcd9xxx_clsh_cdc_data *clsh_d,
+ u8 req_state, bool is_enable)
+{
+
+ dev_dbg(codec->dev, "%s: enter %s\n", __func__,
+ is_enable ? "enable" : "disable");
+ if (is_enable) {
+ wcd9xxx_dynamic_bypass_buck_ctrl(codec, false);
+ wcd9xxx_enable_buck(codec, clsh_d, true);
+ if (req_state & WCD9XXX_CLSH_STATE_EAR) {
+ wcd9xxx_set_fclk_get_ncp(codec, clsh_d,
+ NCP_FCLK_LEVEL_8);
+ wcd9xxx_ncp_bypass_enable(codec, true);
+ wcd9xxx_enable_clsh_block(codec, clsh_d, true);
+ wcd9xxx_chargepump_request(codec, true);
+ wcd9xxx_enable_anc_delay(codec, true);
+ wcd9xxx_clsh_comp_req(codec, clsh_d,
+ CLSH_COMPUTE_EAR, true);
+ }
+ } else {
+ wcd9xxx_set_fclk_put_ncp(codec, clsh_d, NCP_FCLK_LEVEL_8);
+ wcd9xxx_ncp_bypass_enable(codec, false);
+ if (req_state & WCD9XXX_CLSH_STATE_LO) {
+ snd_soc_update_bits(codec, WCD9XXX_A_NCP_STATIC,
+ 0x20, 0x00);
+ wcd9xxx_dynamic_bypass_buck_ctrl(codec, true);
+ } else if (req_state & WCD9XXX_CLSH_STATE_EAR) {
+ wcd9xxx_clsh_comp_req(codec, clsh_d, CLSH_COMPUTE_EAR,
+ false);
+ /*sleep 5ms*/
+ if (clsh_d->buck_mv == WCD9XXX_CDC_BUCK_MV_1P8) {
+ wcd9xxx_enable_buck(codec, clsh_d, false);
+ wcd9xxx_ncp_bypass_enable(codec, true);
+ } else {
+ /* NCP settle time recommended by codec spec */
+ usleep_range(NCP_SETTLE_TIME_US,
+ NCP_SETTLE_TIME_US + 10);
+ wcd9xxx_clsh_set_Iest(codec, 0x02);
+ }
+ snd_soc_update_bits(codec, WCD9XXX_A_BUCK_MODE_1,
+ 0x04, 0x00);
+ snd_soc_update_bits(codec, WCD9XXX_A_BUCK_MODE_4,
+ 0xFF, BUCK_VREF_1P8V);
+ }
+ }
+}
+
+static void wcd9xxx_clsh_state_hph_ear_lo(struct snd_soc_codec *codec,
+ struct wcd9xxx_clsh_cdc_data *clsh_d,
+ u8 req_state, bool is_enable)
+{
+ dev_dbg(codec->dev, "%s: enter %s\n", __func__,
+ is_enable ? "enable" : "disable");
+
+ if (req_state & WCD9XXX_CLSH_STATE_HPHL)
+ wcd9xxx_clsh_comp_req(codec, clsh_d, CLSH_COMPUTE_HPH_L,
+ is_enable);
+
+ if (req_state & WCD9XXX_CLSH_STATE_HPHR)
+ wcd9xxx_clsh_comp_req(codec, clsh_d, CLSH_COMPUTE_HPH_R,
+ is_enable);
+
+ if (req_state & WCD9XXX_CLSH_STATE_EAR)
+ wcd9xxx_clsh_comp_req(codec, clsh_d, CLSH_COMPUTE_EAR,
+ is_enable);
+}
+
static void wcd9xxx_clsh_state_ear(struct snd_soc_codec *codec,
struct wcd9xxx_clsh_cdc_data *clsh_d,
u8 req_state, bool is_enable)
@@ -895,10 +1157,20 @@
pr_debug("%s: enter %s\n", __func__, is_enable ? "enable" : "disable");
if (is_enable) {
- wcd9xxx_clsh_comp_req(codec, clsh_d, CLSH_COMPUTE_HPH_L, true);
- wcd9xxx_clsh_comp_req(codec, clsh_d, CLSH_COMPUTE_HPH_R, true);
+ if (req_state == WCD9XXX_CLSH_STATE_HPHL)
+ wcd9xxx_clsh_comp_req(codec, clsh_d,
+ CLSH_COMPUTE_HPH_L, true);
+ if (req_state == WCD9XXX_CLSH_STATE_HPHR)
+ wcd9xxx_clsh_comp_req(codec, clsh_d,
+ CLSH_COMPUTE_HPH_R, true);
} else {
dev_dbg(codec->dev, "%s: stub fallback to hph_st\n", __func__);
+ if (req_state == WCD9XXX_CLSH_STATE_HPHL)
+ wcd9xxx_clsh_comp_req(codec, clsh_d,
+ CLSH_COMPUTE_HPH_L, false);
+ if (req_state == WCD9XXX_CLSH_STATE_HPHR)
+ wcd9xxx_clsh_comp_req(codec, clsh_d,
+ CLSH_COMPUTE_HPH_R, false);
}
}
@@ -952,6 +1224,47 @@
WARN_ON(1);
}
+/*
+ * Function: wcd9xxx_clsh_is_state_valid
+ * Params: state
+ * Description:
+ * Provides information on valid states of Class H configuration
+ */
+static int wcd9xxx_clsh_is_state_valid(u8 state)
+{
+ switch (state) {
+ case WCD9XXX_CLSH_STATE_IDLE:
+ case WCD9XXX_CLSH_STATE_EAR:
+ case WCD9XXX_CLSH_STATE_HPHL:
+ case WCD9XXX_CLSH_STATE_HPHR:
+ case WCD9XXX_CLSH_STATE_HPH_ST:
+ case WCD9XXX_CLSH_STATE_LO:
+ case WCD9XXX_CLSH_STATE_HPHL_EAR:
+ case WCD9XXX_CLSH_STATE_HPHR_EAR:
+ case WCD9XXX_CLSH_STATE_HPH_ST_EAR:
+ case WCD9XXX_CLSH_STATE_HPHL_LO:
+ case WCD9XXX_CLSH_STATE_HPHR_LO:
+ case WCD9XXX_CLSH_STATE_HPH_ST_LO:
+ case WCD9XXX_CLSH_STATE_EAR_LO:
+ case WCD9XXX_CLSH_STATE_HPHL_EAR_LO:
+ case WCD9XXX_CLSH_STATE_HPHR_EAR_LO:
+ case WCD9XXX_CLSH_STATE_HPH_ST_EAR_LO:
+ return 1;
+ default:
+ break;
+ }
+ return 0;
+}
+
+/*
+ * Function: wcd9xxx_clsh_fsm
+ * Params: codec, cdc_clsh_d, req_state, req_type, clsh_event
+ * Description:
+ * This function handles PRE DAC and POST DAC conditions of different devices
+ * and updates class H configuration of different combination of devices
+ * based on validity of their states. cdc_clsh_d will contain current
+ * class h state information
+ */
void wcd9xxx_clsh_fsm(struct snd_soc_codec *codec,
struct wcd9xxx_clsh_cdc_data *cdc_clsh_d,
u8 req_state, bool req_type, u8 clsh_event)
@@ -967,10 +1280,25 @@
old_state = cdc_clsh_d->state;
new_state = old_state | req_state;
- (*clsh_state_fp[req_state]) (codec, cdc_clsh_d, req_state,
+ if (!wcd9xxx_clsh_is_state_valid(new_state)) {
+ dev_dbg(codec->dev,
+ "%s: classH not a valid new state: %s\n",
+ __func__,
+ state_to_str(new_state, msg0, sizeof(msg0)));
+ return;
+ }
+ if (new_state == old_state) {
+ dev_dbg(codec->dev,
+ "%s: classH already in requested state: %s\n",
+ __func__,
+ state_to_str(new_state, msg0, sizeof(msg0)));
+ return;
+ }
+ (*clsh_state_fp[new_state]) (codec, cdc_clsh_d, req_state,
req_type);
cdc_clsh_d->state = new_state;
- dev_dbg(codec->dev, "%s: ClassH state transition from %s to %s\n",
+ dev_dbg(codec->dev,
+ "%s: ClassH state transition from %s to %s\n",
__func__, state_to_str(old_state, msg0, sizeof(msg0)),
state_to_str(cdc_clsh_d->state, msg1, sizeof(msg1)));
@@ -981,7 +1309,23 @@
new_state = old_state & (~req_state);
if (new_state < NUM_CLSH_STATES) {
- (*clsh_state_fp[req_state]) (codec, cdc_clsh_d,
+ if (!wcd9xxx_clsh_is_state_valid(old_state)) {
+ dev_dbg(codec->dev,
+ "%s:Invalid old state:%s\n",
+ __func__,
+ state_to_str(old_state, msg0,
+ sizeof(msg0)));
+ return;
+ }
+ if (new_state == old_state) {
+ dev_dbg(codec->dev,
+ "%s: clsH already in old state: %s\n",
+ __func__,
+ state_to_str(new_state, msg0,
+ sizeof(msg0)));
+ return;
+ }
+ (*clsh_state_fp[old_state]) (codec, cdc_clsh_d,
req_state,
req_type);
cdc_clsh_d->state = new_state;
@@ -992,7 +1336,7 @@
sizeof(msg1)));
} else {
- dev_dbg(codec->dev, "%s: wrong new state = %x\n",
+ dev_dbg(codec->dev, "%s:wrong new state=0x%x\n",
__func__, new_state);
}
} else if (!(cdc_clsh_d->state & WCD9XXX_CLSH_STATE_LO)) {
@@ -1023,6 +1367,23 @@
clsh_state_fp[WCD9XXX_CLSH_STATE_HPH_ST] =
wcd9xxx_clsh_state_hph_st;
clsh_state_fp[WCD9XXX_CLSH_STATE_LO] = wcd9xxx_clsh_state_lo;
+ clsh_state_fp[WCD9XXX_CLSH_STATE_HPHL_EAR] =
+ wcd9xxx_clsh_state_hph_ear;
+ clsh_state_fp[WCD9XXX_CLSH_STATE_HPHR_EAR] =
+ wcd9xxx_clsh_state_hph_ear;
+ clsh_state_fp[WCD9XXX_CLSH_STATE_HPH_ST_EAR] =
+ wcd9xxx_clsh_state_hph_ear;
+ clsh_state_fp[WCD9XXX_CLSH_STATE_HPHL_LO] = wcd9xxx_clsh_state_hph_lo;
+ clsh_state_fp[WCD9XXX_CLSH_STATE_HPHR_LO] = wcd9xxx_clsh_state_hph_lo;
+ clsh_state_fp[WCD9XXX_CLSH_STATE_HPH_ST_LO] =
+ wcd9xxx_clsh_state_hph_lo;
+ clsh_state_fp[WCD9XXX_CLSH_STATE_EAR_LO] = wcd9xxx_clsh_state_ear_lo;
+ clsh_state_fp[WCD9XXX_CLSH_STATE_HPHL_EAR_LO] =
+ wcd9xxx_clsh_state_hph_ear_lo;
+ clsh_state_fp[WCD9XXX_CLSH_STATE_HPHR_EAR_LO] =
+ wcd9xxx_clsh_state_hph_ear_lo;
+ clsh_state_fp[WCD9XXX_CLSH_STATE_HPH_ST_EAR_LO] =
+ wcd9xxx_clsh_state_hph_ear_lo;
}
EXPORT_SYMBOL_GPL(wcd9xxx_clsh_init);
diff --git a/sound/soc/codecs/wcd9xxx-common.h b/sound/soc/codecs/wcd9xxx-common.h
index 324f6e9..13f91ed 100644
--- a/sound/soc/codecs/wcd9xxx-common.h
+++ b/sound/soc/codecs/wcd9xxx-common.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2014, 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
@@ -35,7 +35,7 @@
#define WCD9XXX_CLSH_STATE_HPHL (0x01 << 1)
#define WCD9XXX_CLSH_STATE_HPHR (0x01 << 2)
#define WCD9XXX_CLSH_STATE_LO (0x01 << 3)
-#define NUM_CLSH_STATES ((0x01 << 4) - 1)
+#define NUM_CLSH_STATES (0x01 << 4)
#define WCD9XXX_DMIC_SAMPLE_RATE_DIV_2 0x0
#define WCD9XXX_DMIC_SAMPLE_RATE_DIV_3 0x1
@@ -56,6 +56,34 @@
#define WCD9XXX_CLSH_STATE_HPH_ST (WCD9XXX_CLSH_STATE_HPHL | \
WCD9XXX_CLSH_STATE_HPHR)
+#define WCD9XXX_CLSH_STATE_HPHL_EAR (WCD9XXX_CLSH_STATE_HPHL | \
+ WCD9XXX_CLSH_STATE_EAR)
+#define WCD9XXX_CLSH_STATE_HPHR_EAR (WCD9XXX_CLSH_STATE_HPHR | \
+ WCD9XXX_CLSH_STATE_EAR)
+
+#define WCD9XXX_CLSH_STATE_HPH_ST_EAR (WCD9XXX_CLSH_STATE_HPH_ST | \
+ WCD9XXX_CLSH_STATE_EAR)
+
+#define WCD9XXX_CLSH_STATE_HPHL_LO (WCD9XXX_CLSH_STATE_HPHL | \
+ WCD9XXX_CLSH_STATE_LO)
+#define WCD9XXX_CLSH_STATE_HPHR_LO (WCD9XXX_CLSH_STATE_HPHR | \
+ WCD9XXX_CLSH_STATE_LO)
+
+#define WCD9XXX_CLSH_STATE_HPH_ST_LO (WCD9XXX_CLSH_STATE_HPH_ST | \
+ WCD9XXX_CLSH_STATE_LO)
+
+#define WCD9XXX_CLSH_STATE_EAR_LO (WCD9XXX_CLSH_STATE_EAR | \
+ WCD9XXX_CLSH_STATE_LO)
+
+#define WCD9XXX_CLSH_STATE_HPHL_EAR_LO (WCD9XXX_CLSH_STATE_HPHL | \
+ WCD9XXX_CLSH_STATE_EAR | \
+ WCD9XXX_CLSH_STATE_LO)
+#define WCD9XXX_CLSH_STATE_HPHR_EAR_LO (WCD9XXX_CLSH_STATE_HPHR | \
+ WCD9XXX_CLSH_STATE_EAR | \
+ WCD9XXX_CLSH_STATE_LO)
+#define WCD9XXX_CLSH_STATE_HPH_ST_EAR_LO (WCD9XXX_CLSH_STATE_HPH_ST | \
+ WCD9XXX_CLSH_STATE_EAR | \
+ WCD9XXX_CLSH_STATE_LO)
struct wcd9xxx_reg_mask_val {
u16 reg;
diff --git a/sound/soc/codecs/wcd9xxx-mbhc.c b/sound/soc/codecs/wcd9xxx-mbhc.c
index 4426e4a..8a71891 100644
--- a/sound/soc/codecs/wcd9xxx-mbhc.c
+++ b/sound/soc/codecs/wcd9xxx-mbhc.c
@@ -188,7 +188,7 @@
static bool wcd9xxx_mbhc_polling(struct wcd9xxx_mbhc *mbhc)
{
- return mbhc->polling_active;
+ return snd_soc_read(mbhc->codec, WCD9XXX_A_CDC_MBHC_EN_CTL) & 0x1;
}
static void wcd9xxx_turn_onoff_override(struct wcd9xxx_mbhc *mbhc, bool on)
@@ -542,13 +542,13 @@
if (cfilt_mode.cur_mode_val
!= cfilt_mode.reg_mode_val) {
- if (mbhc->polling_active)
+ if (mbhc->polling_active && wcd9xxx_mbhc_polling(mbhc))
wcd9xxx_pause_hs_polling(mbhc);
snd_soc_update_bits(codec,
mbhc->mbhc_bias_regs.cfilt_ctl,
cfilt_mode.reg_mask,
cfilt_mode.reg_mode_val);
- if (mbhc->polling_active)
+ if (mbhc->polling_active && wcd9xxx_mbhc_polling(mbhc))
wcd9xxx_start_hs_polling(mbhc);
pr_debug("%s: CFILT mode change (%x to %x)\n", __func__,
cfilt_mode.cur_mode_val,
@@ -884,7 +884,8 @@
0, WCD9XXX_JACK_MASK);
mbhc->hph_status &= ~(SND_JACK_HEADSET |
SND_JACK_LINEOUT |
- SND_JACK_ANC_HEADPHONE);
+ SND_JACK_ANC_HEADPHONE |
+ SND_JACK_UNSUPPORTED);
}
/* Report insertion */
@@ -1608,7 +1609,7 @@
continue;
}
- if ((i > 0) && (d->_type != dprev->_type)) {
+ if ((i > 0) && (dprev != NULL) && (d->_type != dprev->_type)) {
pr_debug("%s: Invalid, inconsistent types\n", __func__);
type = PLUG_TYPE_INVALID;
goto exit;
@@ -3050,11 +3051,14 @@
if (!mbhc->mbhc_cfg->detect_extn_cable &&
retry == NUM_ATTEMPTS_TO_REPORT &&
mbhc->current_plug == PLUG_TYPE_NONE) {
+ WCD9XXX_BCL_LOCK(mbhc->resmgr);
wcd9xxx_report_plug(mbhc, 1,
SND_JACK_HEADPHONE);
+ WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
}
} else if (plug_type == PLUG_TYPE_HEADPHONE) {
pr_debug("Good headphone detected, continue polling\n");
+ WCD9XXX_BCL_LOCK(mbhc->resmgr);
if (mbhc->mbhc_cfg->detect_extn_cable) {
if (mbhc->current_plug != plug_type)
wcd9xxx_report_plug(mbhc, 1,
@@ -3063,9 +3067,11 @@
wcd9xxx_report_plug(mbhc, 1,
SND_JACK_HEADPHONE);
}
+ WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
} else if (plug_type == PLUG_TYPE_HIGH_HPH) {
pr_debug("%s: High HPH detected, continue polling\n",
__func__);
+ WCD9XXX_BCL_LOCK(mbhc->resmgr);
if (mbhc->mbhc_cfg->detect_extn_cable) {
if (mbhc->current_plug != plug_type)
wcd9xxx_report_plug(mbhc, 1,
@@ -3074,6 +3080,7 @@
wcd9xxx_report_plug(mbhc, 1,
SND_JACK_HEADPHONE);
}
+ WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
} else {
if (plug_type == PLUG_TYPE_GND_MIC_SWAP) {
pt_gnd_mic_swap_cnt++;
@@ -3441,7 +3448,8 @@
* wcd9xxx_update_rel_threshold : update mbhc release upper bound threshold
* to ceilmv + buffer
*/
-static int wcd9xxx_update_rel_threshold(struct wcd9xxx_mbhc *mbhc, int ceilmv)
+static int wcd9xxx_update_rel_threshold(struct wcd9xxx_mbhc *mbhc, int ceilmv,
+ bool vddio)
{
u16 v_brh, v_b1_hu;
int mv;
@@ -3451,6 +3459,8 @@
btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(calibration);
mv = ceilmv + btn_det->v_btn_press_delta_cic;
+ if (vddio)
+ mv = scale_v_micb_vddio(mbhc, mv, true);
pr_debug("%s: reprogram vb1hu/vbrh to %dmv\n", __func__, mv);
if (mbhc->mbhc_state != MBHC_STATE_POTENTIAL_RECOVERY) {
@@ -3496,6 +3506,7 @@
pr_debug("%s: enter\n", __func__);
WCD9XXX_BCL_LOCK(mbhc->resmgr);
+ mutex_lock(&mbhc->mbhc_lock);
mbhc_status = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_STATUS) & 0x3E;
if (mbhc->mbhc_state == MBHC_STATE_POTENTIAL_RECOVERY) {
@@ -3651,7 +3662,7 @@
MBHC_BTN_DET_V_BTN_HIGH);
WARN_ON(btn >= btn_det->num_btn);
/* reprogram release threshold to catch voltage ramp up early */
- wcd9xxx_update_rel_threshold(mbhc, v_btn_high[btn]);
+ wcd9xxx_update_rel_threshold(mbhc, v_btn_high[btn], vddio);
mask = wcd9xxx_get_button_mask(btn);
mbhc->buttons_pressed |= mask;
@@ -3668,6 +3679,7 @@
done:
pr_debug("%s: leave\n", __func__);
+ mutex_unlock(&mbhc->mbhc_lock);
WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
return IRQ_HANDLED;
}
@@ -4515,6 +4527,7 @@
pr_debug("%s: enter event %s(%d)\n", __func__,
wcd9xxx_get_event_string(event), event);
+ mutex_lock(&mbhc->mbhc_lock);
switch (event) {
/* MICBIAS usage change */
case WCD9XXX_EVENT_PRE_MICBIAS_1_ON:
@@ -4708,6 +4721,7 @@
WARN(1, "Unknown event %d\n", event);
ret = -EINVAL;
}
+ mutex_unlock(&mbhc->mbhc_lock);
pr_debug("%s: leave\n", __func__);
@@ -4917,17 +4931,19 @@
wcd9xxx_mbhc_insert_work);
}
+ mutex_init(&mbhc->mbhc_lock);
+
/* Register event notifier */
mbhc->nblock.notifier_call = wcd9xxx_event_notify;
ret = wcd9xxx_resmgr_register_notifier(mbhc->resmgr, &mbhc->nblock);
if (ret) {
pr_err("%s: Failed to register notifier %d\n", __func__, ret);
+ mutex_destroy(&mbhc->mbhc_lock);
return ret;
}
wcd9xxx_init_debugfs(mbhc);
-
/* Disable Impedance detection by default for certain codec types */
if (mbhc->mbhc_cb &&
mbhc->mbhc_cb->get_cdc_type() == WCD9XXX_CDC_TYPE_HELICON)
@@ -5012,6 +5028,8 @@
err_insert_irq:
wcd9xxx_resmgr_unregister_notifier(mbhc->resmgr, &mbhc->nblock);
+ mutex_destroy(&mbhc->mbhc_lock);
+
pr_debug("%s: leave ret %d\n", __func__, ret);
return ret;
}
@@ -5033,6 +5051,7 @@
wcd9xxx_free_irq(core_res, mbhc->intr_ids->hph_left_ocp, mbhc);
wcd9xxx_free_irq(core_res, mbhc->intr_ids->hph_right_ocp, mbhc);
+ mutex_destroy(&mbhc->mbhc_lock);
wcd9xxx_resmgr_unregister_notifier(mbhc->resmgr, &mbhc->nblock);
wcd9xxx_cleanup_debugfs(mbhc);
}
diff --git a/sound/soc/codecs/wcd9xxx-mbhc.h b/sound/soc/codecs/wcd9xxx-mbhc.h
index cf25798..91edaca 100644
--- a/sound/soc/codecs/wcd9xxx-mbhc.h
+++ b/sound/soc/codecs/wcd9xxx-mbhc.h
@@ -368,6 +368,8 @@
struct dentry *debugfs_poke;
struct dentry *debugfs_mbhc;
#endif
+
+ struct mutex mbhc_lock;
};
#define WCD9XXX_MBHC_CAL_SIZE(buttons, rload) ( \
diff --git a/sound/soc/msm/apq8074.c b/sound/soc/msm/apq8074.c
index 4e79109..3a143aa 100644
--- a/sound/soc/msm/apq8074.c
+++ b/sound/soc/msm/apq8074.c
@@ -1913,9 +1913,24 @@
},
/* LSM FE */
{
- .name = "Listen Audio Service",
- .stream_name = "Listen Audio Service",
- .cpu_dai_name = "LSM",
+ .name = "Listen 1 Audio Service",
+ .stream_name = "Listen 1 Audio Service",
+ .cpu_dai_name = "LSM1",
+ .platform_name = "msm-lsm-client",
+ .dynamic = 1,
+ .trigger = { SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST },
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_LSM1,
+ },
+ {
+ .name = "Listen 2 Audio Service",
+ .stream_name = "Listen 2 Audio Service",
+ .cpu_dai_name = "LSM2",
.platform_name = "msm-lsm-client",
.dynamic = 1,
.trigger = { SND_SOC_DPCM_TRIGGER_POST,
@@ -1925,7 +1940,97 @@
.ignore_pmdown_time = 1,
.codec_dai_name = "snd-soc-dummy-dai",
.codec_name = "snd-soc-dummy",
- .be_id = MSM_FRONTEND_DAI_LSM1,
+ .be_id = MSM_FRONTEND_DAI_LSM2,
+ },
+ {
+ .name = "Listen 3 Audio Service",
+ .stream_name = "Listen 3 Audio Service",
+ .cpu_dai_name = "LSM3",
+ .platform_name = "msm-lsm-client",
+ .dynamic = 1,
+ .trigger = { SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST },
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_LSM3,
+ },
+ {
+ .name = "Listen 4 Audio Service",
+ .stream_name = "Listen 4 Audio Service",
+ .cpu_dai_name = "LSM4",
+ .platform_name = "msm-lsm-client",
+ .dynamic = 1,
+ .trigger = { SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST },
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_LSM4,
+ },
+ {
+ .name = "Listen 5 Audio Service",
+ .stream_name = "Listen 5 Audio Service",
+ .cpu_dai_name = "LSM5",
+ .platform_name = "msm-lsm-client",
+ .dynamic = 1,
+ .trigger = { SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST },
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_LSM5,
+ },
+ {
+ .name = "Listen 6 Audio Service",
+ .stream_name = "Listen 6 Audio Service",
+ .cpu_dai_name = "LSM6",
+ .platform_name = "msm-lsm-client",
+ .dynamic = 1,
+ .trigger = { SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST },
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_LSM6,
+ },
+ {
+ .name = "Listen 7 Audio Service",
+ .stream_name = "Listen 7 Audio Service",
+ .cpu_dai_name = "LSM7",
+ .platform_name = "msm-lsm-client",
+ .dynamic = 1,
+ .trigger = { SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST },
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_LSM7,
+ },
+ {
+ .name = "Listen 8 Audio Service",
+ .stream_name = "Listen 8 Audio Service",
+ .cpu_dai_name = "LSM8",
+ .platform_name = "msm-lsm-client",
+ .dynamic = 1,
+ .trigger = { SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST },
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_LSM8,
},
/* Backend BT/FM DAI Links */
{
diff --git a/sound/soc/msm/msm8974.c b/sound/soc/msm/msm8974.c
index ca5e217..9c01a08 100644
--- a/sound/soc/msm/msm8974.c
+++ b/sound/soc/msm/msm8974.c
@@ -2397,6 +2397,21 @@
.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
.ops = &msm8974_slimbus_2_be_ops,
},
+ {
+ .name = "MSM8974 Media9",
+ .stream_name = "MultiMedia9",
+ .cpu_dai_name = "MultiMedia9",
+ .platform_name = "msm-pcm-dsp.0",
+ .dynamic = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA9,
+ },
/* Backend BT/FM DAI Links */
{
.name = LPASS_BE_INT_BT_SCO_RX,
@@ -2688,6 +2703,19 @@
.be_hw_params_fixup = msm_be_hw_params_fixup,
.ignore_suspend = 1,
},
+ /* Incall Music 2 BACK END DAI Link */
+ {
+ .name = LPASS_BE_VOICE2_PLAYBACK_TX,
+ .stream_name = "Voice2 Farend Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.32770",
+ .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_VOICE2_PLAYBACK_TX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ignore_suspend = 1,
+ }
};
static struct snd_soc_dai_link msm8974_hdmi_dai_link[] = {
diff --git a/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c
index bb325d8..5176bbe 100755
--- a/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c
@@ -1030,12 +1030,19 @@
int i;
struct snd_dec_ddp *ddp =
&compr->info.codec_param.codec.options.ddp;
- uint32_t params_length = ddp->params_length*sizeof(int);
+ uint32_t params_length = 0;
+ /* check integer overflow */
+ if (ddp->params_length > UINT_MAX/sizeof(int)) {
+ pr_err("%s: Integer overflow ddp->params_length %d\n",
+ __func__, ddp->params_length);
+ return -EINVAL;
+ }
+ params_length = ddp->params_length*sizeof(int);
if (params_length > MAX_AC3_PARAM_SIZE) {
/*MAX is 36*sizeof(int) this should not happen*/
- pr_err("params_length(%d) is greater than %d",
- params_length, MAX_AC3_PARAM_SIZE);
- params_length = MAX_AC3_PARAM_SIZE;
+ pr_err("%s: params_length(%d) is greater than %zd\n",
+ __func__, params_length, MAX_AC3_PARAM_SIZE);
+ return -EINVAL;
}
pr_debug("SND_AUDIOCODEC_AC3\n");
compr->codec = FORMAT_AC3;
@@ -1067,12 +1074,18 @@
int i;
struct snd_dec_ddp *ddp =
&compr->info.codec_param.codec.options.ddp;
- uint32_t params_length = ddp->params_length*sizeof(int);
+ uint32_t params_length = 0;
+ /* check integer overflow */
+ if (ddp->params_length > UINT_MAX/sizeof(int)) {
+ pr_err("%s: Integer overflow ddp->params_length %d\n",
+ __func__, ddp->params_length);
+ return -EINVAL;
+ }
if (params_length > MAX_AC3_PARAM_SIZE) {
/*MAX is 36*sizeof(int) this should not happen*/
- pr_err("params_length(%d) is greater than %d",
- params_length, MAX_AC3_PARAM_SIZE);
- params_length = MAX_AC3_PARAM_SIZE;
+ pr_err("%s: params_length(%d) is greater than %d\n",
+ __func__, params_length, MAX_AC3_PARAM_SIZE);
+ return -EINVAL;
}
pr_debug("SND_AUDIOCODEC_EAC3\n");
compr->codec = FORMAT_EAC3;
diff --git a/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c
index 143508f..cdfd042 100644
--- a/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c
@@ -167,6 +167,10 @@
pr_debug("%s: volume_l %d volume_r %d\n",
__func__, volume_l, volume_r);
+ if (!cstream || !cstream->runtime) {
+ pr_err("%s: session not active\n", __func__);
+ return -EPERM;
+ }
prtd = cstream->runtime->private_data;
if (prtd && prtd->audio_client) {
if (volume_l != volume_r) {
@@ -646,6 +650,7 @@
kzalloc(sizeof(struct msm_compr_audio_effects), GFP_KERNEL);
if (!pdata->audio_effects[rtd->dai_link->be_id]) {
pr_err("%s: Could not allocate memory for effects\n", __func__);
+ pdata->cstream[rtd->dai_link->be_id] = NULL;
kfree(prtd);
return -ENOMEM;
}
@@ -654,6 +659,8 @@
if (!pdata->dec_params[rtd->dai_link->be_id]) {
pr_err("%s: Could not allocate memory for dec params\n",
__func__);
+ kfree(pdata->audio_effects[rtd->dai_link->be_id]);
+ pdata->cstream[rtd->dai_link->be_id] = NULL;
kfree(prtd);
return -ENOMEM;
}
@@ -663,6 +670,7 @@
pr_err("%s: Could not allocate memory for client\n", __func__);
kfree(pdata->audio_effects[rtd->dai_link->be_id]);
kfree(pdata->dec_params[rtd->dai_link->be_id]);
+ pdata->cstream[rtd->dai_link->be_id] = NULL;
kfree(prtd);
return -ENOMEM;
}
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-loopback-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-loopback-v2.c
index 57fc268..2755c29 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-loopback-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-loopback-v2.c
@@ -202,19 +202,23 @@
static void stop_pcm(struct msm_pcm_loopback *pcm)
{
- struct snd_soc_pcm_runtime *soc_pcm_rx =
- pcm->playback_substream->private_data;
- struct snd_soc_pcm_runtime *soc_pcm_tx =
- pcm->capture_substream->private_data;
+ struct snd_soc_pcm_runtime *soc_pcm_rx;
+ struct snd_soc_pcm_runtime *soc_pcm_tx;
if (pcm->audio_client == NULL)
return;
q6asm_cmd(pcm->audio_client, CMD_CLOSE);
- msm_pcm_routing_dereg_phy_stream(soc_pcm_rx->dai_link->be_id,
- SNDRV_PCM_STREAM_PLAYBACK);
- msm_pcm_routing_dereg_phy_stream(soc_pcm_tx->dai_link->be_id,
- SNDRV_PCM_STREAM_CAPTURE);
+ if (pcm->playback_substream != NULL) {
+ soc_pcm_rx = pcm->playback_substream->private_data;
+ msm_pcm_routing_dereg_phy_stream(soc_pcm_rx->dai_link->be_id,
+ SNDRV_PCM_STREAM_PLAYBACK);
+ }
+ if (pcm->capture_substream != NULL) {
+ soc_pcm_tx = pcm->capture_substream->private_data;
+ msm_pcm_routing_dereg_phy_stream(soc_pcm_tx->dai_link->be_id,
+ SNDRV_PCM_STREAM_CAPTURE);
+ }
q6asm_audio_client_free(pcm->audio_client);
pcm->audio_client = NULL;
}
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c
index 8e69a2b..4893990 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c
@@ -73,7 +73,7 @@
static int srs_alsa_ctrl_ever_called;
static int lsm_mux_slim_port;
static int slim0_rx_aanc_fb_port;
-static int msm_route_ec_ref_rx = 3; /* NONE */
+static int msm_route_ec_ref_rx = 7; /* NONE */
static uint32_t voc_session_id = ALL_SESSION_VSID;
static int msm_route_ext_ec_ref = AFE_PORT_INVALID;
@@ -104,6 +104,10 @@
static const DECLARE_TLV_DB_LINEAR(fm_rx_vol_gain, 0,
INT_RX_VOL_MAX_STEPS);
+static int msm_route_hfp_vol_control;
+static const DECLARE_TLV_DB_LINEAR(hfp_rx_vol_gain, 0,
+ INT_RX_VOL_MAX_STEPS);
+
static int msm_route_multimedia2_vol_control;
static const DECLARE_TLV_DB_LINEAR(multimedia2_rx_vol_gain, 0,
INT_RX_VOL_MAX_STEPS);
@@ -725,9 +729,9 @@
if (voc_get_route_flag(session_id, RX_PATH) &&
voc_get_route_flag(session_id, TX_PATH))
- voc_enable_cvp(session_id);
+ voc_enable_device(session_id);
} else {
- voc_disable_cvp(session_id);
+ voc_disable_device(session_id);
}
} else {
voc_set_route_flag(session_id, TX_PATH, set);
@@ -736,9 +740,9 @@
msm_bedais[reg].port_id, DEV_TX);
if (voc_get_route_flag(session_id, RX_PATH) &&
voc_get_route_flag(session_id, TX_PATH))
- voc_enable_cvp(session_id);
+ voc_enable_device(session_id);
} else {
- voc_disable_cvp(session_id);
+ voc_disable_device(session_id);
}
}
}
@@ -1095,6 +1099,23 @@
return 0;
}
+static int msm_routing_get_hfp_vol_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = msm_route_hfp_vol_control;
+ return 0;
+}
+
+static int msm_routing_set_hfp_vol_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ afe_loopback_gain(INT_BT_SCO_TX , ucontrol->value.integer.value[0]);
+
+ msm_route_hfp_vol_control = ucontrol->value.integer.value[0];
+
+ return 0;
+}
+
static int msm_routing_get_multimedia2_vol_mixer(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -1398,38 +1419,97 @@
struct snd_ctl_elem_value *ucontrol)
{
int ec_ref_port_id;
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+ int mux = ucontrol->value.enumerated.item[0];
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+
mutex_lock(&routing_lock);
switch (ucontrol->value.integer.value[0]) {
case 0:
msm_route_ec_ref_rx = 0;
- ec_ref_port_id = SLIMBUS_0_RX;
+ ec_ref_port_id = AFE_PORT_INVALID;
break;
case 1:
msm_route_ec_ref_rx = 1;
+ ec_ref_port_id = SLIMBUS_0_RX;
+ break;
+ case 2:
+ msm_route_ec_ref_rx = 2;
ec_ref_port_id = AFE_PORT_ID_PRIMARY_MI2S_RX;
break;
+ case 3:
+ msm_route_ec_ref_rx = 3;
+ ec_ref_port_id = AFE_PORT_ID_PRIMARY_MI2S_TX;
+ break;
+ case 4:
+ msm_route_ec_ref_rx = 4;
+ ec_ref_port_id = AFE_PORT_ID_SECONDARY_MI2S_TX;
+ break;
+ case 5:
+ msm_route_ec_ref_rx = 5;
+ ec_ref_port_id = AFE_PORT_ID_TERTIARY_MI2S_TX;
+ break;
+ case 6:
+ msm_route_ec_ref_rx = 6;
+ ec_ref_port_id = AFE_PORT_ID_QUATERNARY_MI2S_TX;
+ break;
default:
- msm_route_ec_ref_rx = 3; /* NONE */
- ec_ref_port_id = -1;
+ msm_route_ec_ref_rx = 0; /* NONE */
+ pr_err("%s EC ref rx %ld not valid\n",
+ __func__, ucontrol->value.integer.value[0]);
+ ec_ref_port_id = AFE_PORT_INVALID;
break;
}
adm_ec_ref_rx_id(ec_ref_port_id);
pr_debug("%s: msm_route_ec_ref_rx = %d\n",
__func__, msm_route_ec_ref_rx);
mutex_unlock(&routing_lock);
+ snd_soc_dapm_mux_update_power(widget, kcontrol, 1, mux, e);
return 0;
}
-static const char *const ec_ref_rx[] = { "SLIM_RX", "I2S_RX", "PROXY_RX",
- "NONE" };
+static const char *const ec_ref_rx[] = { "None", "SLIM_RX", "I2S_RX",
+ "PRI_MI2S_TX",
+ "SEC_MI2S_TX", "TERT_MI2S_TX", "QUAT_MI2S_TX", "PROXY_RX"};
static const struct soc_enum msm_route_ec_ref_rx_enum[] = {
- SOC_ENUM_SINGLE_EXT(4, ec_ref_rx),
+ SOC_ENUM_SINGLE_EXT(8, ec_ref_rx),
};
-static const struct snd_kcontrol_new ec_ref_rx_mixer_controls[] = {
- SOC_ENUM_EXT("EC_REF_RX", msm_route_ec_ref_rx_enum[0],
- msm_routing_ec_ref_rx_get, msm_routing_ec_ref_rx_put),
-};
+static const struct snd_kcontrol_new ext_ec_ref_mux_ul1 =
+ SOC_DAPM_ENUM_EXT("AUDIO_REF_EC_UL1 MUX Mux",
+ msm_route_ec_ref_rx_enum[0],
+ msm_routing_ec_ref_rx_get, msm_routing_ec_ref_rx_put);
+
+static const struct snd_kcontrol_new ext_ec_ref_mux_ul2 =
+ SOC_DAPM_ENUM_EXT("AUDIO_REF_EC_UL2 MUX Mux",
+ msm_route_ec_ref_rx_enum[0],
+ msm_routing_ec_ref_rx_get, msm_routing_ec_ref_rx_put);
+
+static const struct snd_kcontrol_new ext_ec_ref_mux_ul4 =
+ SOC_DAPM_ENUM_EXT("AUDIO_REF_EC_UL4 MUX Mux",
+ msm_route_ec_ref_rx_enum[0],
+ msm_routing_ec_ref_rx_get, msm_routing_ec_ref_rx_put);
+
+static const struct snd_kcontrol_new ext_ec_ref_mux_ul5 =
+ SOC_DAPM_ENUM_EXT("AUDIO_REF_EC_UL5 MUX Mux",
+ msm_route_ec_ref_rx_enum[0],
+ msm_routing_ec_ref_rx_get, msm_routing_ec_ref_rx_put);
+
+static const struct snd_kcontrol_new ext_ec_ref_mux_ul6 =
+ SOC_DAPM_ENUM_EXT("AUDIO_REF_EC_UL6 MUX Mux",
+ msm_route_ec_ref_rx_enum[0],
+ msm_routing_ec_ref_rx_get, msm_routing_ec_ref_rx_put);
+
+static const struct snd_kcontrol_new ext_ec_ref_mux_ul8 =
+ SOC_DAPM_ENUM_EXT("AUDIO_REF_EC_UL8 MUX Mux",
+ msm_route_ec_ref_rx_enum[0],
+ msm_routing_ec_ref_rx_get, msm_routing_ec_ref_rx_put);
+
+static const struct snd_kcontrol_new ext_ec_ref_mux_ul9 =
+ SOC_DAPM_ENUM_EXT("AUDIO_REF_EC_UL9 MUX Mux",
+ msm_route_ec_ref_rx_enum[0],
+ msm_routing_ec_ref_rx_get, msm_routing_ec_ref_rx_put);
static int msm_routing_ext_ec_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
@@ -2757,6 +2837,12 @@
msm_routing_set_fm_vol_mixer, fm_rx_vol_gain),
};
+static const struct snd_kcontrol_new int_hfp_vol_mixer_controls[] = {
+ SOC_SINGLE_EXT_TLV("Internal HFP RX Volume", SND_SOC_NOPM, 0,
+ INT_RX_VOL_GAIN, 0, msm_routing_get_hfp_vol_mixer,
+ msm_routing_set_hfp_vol_mixer, hfp_rx_vol_gain),
+};
+
static const struct snd_kcontrol_new multimedia2_vol_mixer_controls[] = {
SOC_SINGLE_EXT_TLV("HIFI2 RX Volume", SND_SOC_NOPM, 0,
INT_RX_VOL_GAIN, 0, msm_routing_get_multimedia2_vol_mixer,
@@ -3546,7 +3632,20 @@
&slim0_rx_vi_fb_lch_mux),
SND_SOC_DAPM_MUX("VOC_EXT_EC MUX", SND_SOC_NOPM, 0, 0,
&voc_ext_ec_mux),
-
+ SND_SOC_DAPM_MUX("AUDIO_REF_EC_UL1 MUX", SND_SOC_NOPM, 0, 0,
+ &ext_ec_ref_mux_ul1),
+ SND_SOC_DAPM_MUX("AUDIO_REF_EC_UL2 MUX", SND_SOC_NOPM, 0, 0,
+ &ext_ec_ref_mux_ul2),
+ SND_SOC_DAPM_MUX("AUDIO_REF_EC_UL4 MUX", SND_SOC_NOPM, 0, 0,
+ &ext_ec_ref_mux_ul4),
+ SND_SOC_DAPM_MUX("AUDIO_REF_EC_UL5 MUX", SND_SOC_NOPM, 0, 0,
+ &ext_ec_ref_mux_ul5),
+ SND_SOC_DAPM_MUX("AUDIO_REF_EC_UL6 MUX", SND_SOC_NOPM, 0, 0,
+ &ext_ec_ref_mux_ul6),
+ SND_SOC_DAPM_MUX("AUDIO_REF_EC_UL8 MUX", SND_SOC_NOPM, 0, 0,
+ &ext_ec_ref_mux_ul8),
+ SND_SOC_DAPM_MUX("AUDIO_REF_EC_UL9 MUX", SND_SOC_NOPM, 0, 0,
+ &ext_ec_ref_mux_ul9),
};
static const struct snd_soc_dapm_route intercon[] = {
@@ -3865,6 +3964,65 @@
{"VOC_EXT_EC MUX", "TERT_MI2S_TX" , "TERT_MI2S_TX"},
{"VOC_EXT_EC MUX", "QUAT_MI2S_TX" , "QUAT_MI2S_TX"},
{"CS-VOICE_UL1", NULL, "VOC_EXT_EC MUX"},
+ {"VOIP_UL", NULL, "VOC_EXT_EC MUX"},
+ {"VoLTE_UL", NULL, "VOC_EXT_EC MUX"},
+
+ {"AUDIO_REF_EC_UL1 MUX", "PRI_MI2S_TX" , "PRI_MI2S_TX"},
+ {"AUDIO_REF_EC_UL1 MUX", "SEC_MI2S_TX" , "SEC_MI2S_TX"},
+ {"AUDIO_REF_EC_UL1 MUX", "TERT_MI2S_TX" , "TERT_MI2S_TX"},
+ {"AUDIO_REF_EC_UL1 MUX", "QUAT_MI2S_TX" , "QUAT_MI2S_TX"},
+ {"AUDIO_REF_EC_UL1 MUX", "I2S_RX" , "PRI_I2S_TX"},
+ {"AUDIO_REF_EC_UL1 MUX", "SLIM_RX" , "SLIMBUS_0_TX"},
+
+ {"AUDIO_REF_EC_UL2 MUX", "PRI_MI2S_TX" , "PRI_MI2S_TX"},
+ {"AUDIO_REF_EC_UL2 MUX", "SEC_MI2S_TX" , "SEC_MI2S_TX"},
+ {"AUDIO_REF_EC_UL2 MUX", "TERT_MI2S_TX" , "TERT_MI2S_TX"},
+ {"AUDIO_REF_EC_UL2 MUX", "QUAT_MI2S_TX" , "QUAT_MI2S_TX"},
+ {"AUDIO_REF_EC_UL2 MUX", "I2S_RX" , "PRI_I2S_TX"},
+ {"AUDIO_REF_EC_UL2 MUX", "SLIM_RX" , "SLIMBUS_0_TX"},
+
+ {"AUDIO_REF_EC_UL4 MUX", "PRI_MI2S_TX" , "PRI_MI2S_TX"},
+ {"AUDIO_REF_EC_UL4 MUX", "SEC_MI2S_TX" , "SEC_MI2S_TX"},
+ {"AUDIO_REF_EC_UL4 MUX", "TERT_MI2S_TX" , "TERT_MI2S_TX"},
+ {"AUDIO_REF_EC_UL4 MUX", "QUAT_MI2S_TX" , "QUAT_MI2S_TX"},
+ {"AUDIO_REF_EC_UL4 MUX", "I2S_RX" , "PRI_I2S_TX"},
+ {"AUDIO_REF_EC_UL4 MUX", "SLIM_RX" , "SLIMBUS_0_TX"},
+
+ {"AUDIO_REF_EC_UL5 MUX", "PRI_MI2S_TX" , "PRI_MI2S_TX"},
+ {"AUDIO_REF_EC_UL5 MUX", "SEC_MI2S_TX" , "SEC_MI2S_TX"},
+ {"AUDIO_REF_EC_UL5 MUX", "TERT_MI2S_TX" , "TERT_MI2S_TX"},
+ {"AUDIO_REF_EC_UL5 MUX", "QUAT_MI2S_TX" , "QUAT_MI2S_TX"},
+ {"AUDIO_REF_EC_UL5 MUX", "I2S_RX" , "PRI_I2S_TX"},
+ {"AUDIO_REF_EC_UL5 MUX", "SLIM_RX" , "SLIMBUS_0_TX"},
+
+ {"AUDIO_REF_EC_UL6 MUX", "PRI_MI2S_TX" , "PRI_MI2S_TX"},
+ {"AUDIO_REF_EC_UL6 MUX", "SEC_MI2S_TX" , "SEC_MI2S_TX"},
+ {"AUDIO_REF_EC_UL6 MUX", "TERT_MI2S_TX" , "TERT_MI2S_TX"},
+ {"AUDIO_REF_EC_UL6 MUX", "QUAT_MI2S_TX" , "QUAT_MI2S_TX"},
+ {"AUDIO_REF_EC_UL6 MUX", "I2S_RX" , "PRI_I2S_TX"},
+ {"AUDIO_REF_EC_UL6 MUX", "SLIM_RX" , "SLIMBUS_0_TX"},
+
+ {"AUDIO_REF_EC_UL8 MUX", "PRI_MI2S_TX" , "PRI_MI2S_TX"},
+ {"AUDIO_REF_EC_UL8 MUX", "SEC_MI2S_TX" , "SEC_MI2S_TX"},
+ {"AUDIO_REF_EC_UL8 MUX", "TERT_MI2S_TX" , "TERT_MI2S_TX"},
+ {"AUDIO_REF_EC_UL8 MUX", "QUAT_MI2S_TX" , "QUAT_MI2S_TX"},
+ {"AUDIO_REF_EC_UL8 MUX", "I2S_RX" , "PRI_I2S_TX"},
+ {"AUDIO_REF_EC_UL8 MUX", "SLIM_RX" , "SLIMBUS_0_TX"},
+
+ {"AUDIO_REF_EC_UL9 MUX", "PRI_MI2S_TX" , "PRI_MI2S_TX"},
+ {"AUDIO_REF_EC_UL9 MUX", "SEC_MI2S_TX" , "SEC_MI2S_TX"},
+ {"AUDIO_REF_EC_UL9 MUX", "TERT_MI2S_TX" , "TERT_MI2S_TX"},
+ {"AUDIO_REF_EC_UL9 MUX", "QUAT_MI2S_TX" , "QUAT_MI2S_TX"},
+ {"AUDIO_REF_EC_UL9 MUX", "I2S_RX" , "PRI_I2S_TX"},
+ {"AUDIO_REF_EC_UL9 MUX", "SLIM_RX" , "SLIMBUS_0_TX"},
+
+ {"MM_UL1", NULL, "AUDIO_REF_EC_UL1 MUX"},
+ {"MM_UL2", NULL, "AUDIO_REF_EC_UL2 MUX"},
+ {"MM_UL4", NULL, "AUDIO_REF_EC_UL4 MUX"},
+ {"MM_UL5", NULL, "AUDIO_REF_EC_UL5 MUX"},
+ {"MM_UL6", NULL, "AUDIO_REF_EC_UL6 MUX"},
+ {"MM_UL8", NULL, "AUDIO_REF_EC_UL8 MUX"},
+ {"MM_UL9", NULL, "AUDIO_REF_EC_UL9 MUX"},
{"Voice_Tx Mixer", "PRI_TX_Voice", "PRI_I2S_TX"},
{"Voice_Tx Mixer", "PRI_MI2S_TX_Voice", "PRI_MI2S_TX"},
@@ -4327,6 +4485,10 @@
ARRAY_SIZE(int_fm_vol_mixer_controls));
snd_soc_add_platform_controls(platform,
+ int_hfp_vol_mixer_controls,
+ ARRAY_SIZE(int_hfp_vol_mixer_controls));
+
+ snd_soc_add_platform_controls(platform,
eq_enable_mixer_controls,
ARRAY_SIZE(eq_enable_mixer_controls));
@@ -4390,10 +4552,6 @@
ARRAY_SIZE(dolby_dap_param_end_point_controls));
snd_soc_add_platform_controls(platform,
- ec_ref_rx_mixer_controls,
- ARRAY_SIZE(ec_ref_rx_mixer_controls));
-
- snd_soc_add_platform_controls(platform,
get_rms_controls,
ARRAY_SIZE(get_rms_controls));
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-voice-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-voice-v2.c
index fac5845..c54d03c 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-voice-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-voice-v2.c
@@ -405,12 +405,37 @@
pr_debug("%s: mute=%d session_id=%#x ramp_duration=%d\n", __func__,
mute, session_id, ramp_duration);
- voc_set_tx_mute(session_id, TX_PATH, mute, ramp_duration);
+ ret = voc_set_tx_mute(session_id, TX_PATH, mute, ramp_duration);
done:
return ret;
}
+static int msm_voice_tx_device_mute_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ret = 0;
+ int mute = ucontrol->value.integer.value[0];
+ uint32_t session_id = ucontrol->value.integer.value[1];
+ int ramp_duration = ucontrol->value.integer.value[2];
+
+ if ((mute < 0) || (mute > 1) || (ramp_duration < 0) ||
+ (ramp_duration > MAX_RAMP_DURATION)) {
+ pr_err(" %s Invalid arguments", __func__);
+
+ ret = -EINVAL;
+ goto done;
+ }
+
+ pr_debug("%s: mute=%d session_id=%#x ramp_duration=%d\n", __func__,
+ mute, session_id, ramp_duration);
+
+ ret = voc_set_device_mute(session_id, VSS_IVOLUME_DIRECTION_TX,
+ mute, ramp_duration);
+
+done:
+ return ret;
+}
static int msm_voice_rx_device_mute_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
@@ -420,8 +445,8 @@
uint32_t session_id = ucontrol->value.integer.value[1];
int ramp_duration = ucontrol->value.integer.value[2];
- if ((mute < 0) || (mute > 1) || (ramp_duration < 0)
- || (ramp_duration > MAX_RAMP_DURATION)) {
+ if ((mute < 0) || (mute > 1) || (ramp_duration < 0) ||
+ (ramp_duration > MAX_RAMP_DURATION)) {
pr_err(" %s Invalid arguments", __func__);
ret = -EINVAL;
@@ -429,9 +454,10 @@
}
pr_debug("%s: mute=%d session_id=%#x ramp_duration=%d\n", __func__,
- mute, session_id, ramp_duration);
+ mute, session_id, ramp_duration);
- voc_set_rx_device_mute(session_id, mute, ramp_duration);
+ voc_set_device_mute(session_id, VSS_IVOLUME_DIRECTION_RX,
+ mute, ramp_duration);
done:
return ret;
@@ -485,6 +511,8 @@
static struct snd_kcontrol_new msm_voice_controls[] = {
SOC_SINGLE_MULTI_EXT("Voice Rx Device Mute", SND_SOC_NOPM, 0, VSID_MAX,
0, 3, NULL, msm_voice_rx_device_mute_put),
+ SOC_SINGLE_MULTI_EXT("Voice Tx Device Mute", SND_SOC_NOPM, 0, VSID_MAX,
+ 0, 3, NULL, msm_voice_tx_device_mute_put),
SOC_SINGLE_MULTI_EXT("Voice Tx Mute", SND_SOC_NOPM, 0, VSID_MAX,
0, 3, NULL, msm_voice_mute_put),
SOC_SINGLE_MULTI_EXT("Voice Rx Gain", SND_SOC_NOPM, 0, VSID_MAX, 0, 3,
diff --git a/sound/soc/msm/qdsp6v2/q6adm.c b/sound/soc/msm/qdsp6v2/q6adm.c
index 2a6ce43..7b10815 100644
--- a/sound/soc/msm/qdsp6v2/q6adm.c
+++ b/sound/soc/msm/qdsp6v2/q6adm.c
@@ -1167,7 +1167,8 @@
open.topology_id = topology;
if ((open.topology_id == VPM_TX_SM_ECNS_COPP_TOPOLOGY) ||
- (open.topology_id == VPM_TX_DM_FLUENCE_COPP_TOPOLOGY))
+ (open.topology_id == VPM_TX_DM_FLUENCE_COPP_TOPOLOGY) ||
+ (open.topology_id == VPM_TX_DM_RFECNS_COPP_TOPOLOGY))
rate = 16000;
if (perf_mode == ULTRA_LOW_LATENCY_PCM_MODE) {
@@ -1175,6 +1176,10 @@
rate = ULL_SUPPORTED_SAMPLE_RATE;
if(channel_mode > ULL_MAX_SUPPORTED_CHANNEL)
channel_mode = ULL_MAX_SUPPORTED_CHANNEL;
+ } else if (perf_mode == LOW_LATENCY_PCM_MODE) {
+ if ((open.topology_id == DOLBY_ADM_COPP_TOPOLOGY_ID) ||
+ (open.topology_id == SRS_TRUMEDIA_TOPOLOGY_ID))
+ open.topology_id = DEFAULT_COPP_TOPOLOGY;
}
open.dev_num_channel = channel_mode & 0x00FF;
open.bit_width = bits_per_sample;
diff --git a/sound/soc/msm/qdsp6v2/q6asm.c b/sound/soc/msm/qdsp6v2/q6asm.c
index 48bf2f4..4b9d079 100644
--- a/sound/soc/msm/qdsp6v2/q6asm.c
+++ b/sound/soc/msm/qdsp6v2/q6asm.c
@@ -393,60 +393,52 @@
struct list_head *ptr, *next;
int result;
int size = 4096;
+
+ if (!set_custom_topology)
+ return;
+
get_asm_custom_topology(&cal_block);
if (cal_block.cal_size == 0) {
- pr_debug("%s: no cal to send addr= 0x%x\n",
- __func__, cal_block.cal_paddr);
- goto done;
+ pr_debug("%s: no cal to send addr= 0x%pa\n",
+ __func__, &cal_block.cal_paddr);
+ return;
}
- if (set_custom_topology) {
- if (common_client.mmap_apr == NULL) {
- common_client.mmap_apr = q6asm_mmap_apr_reg();
- common_client.apr = common_client.mmap_apr;
- if (common_client.mmap_apr == NULL) {
- pr_err("%s: q6asm_mmap_apr_reg failed\n",
- __func__);
- result = -EPERM;
- goto done;
- }
- }
- /* Only call this once */
- set_custom_topology = 0;
+ common_client.mmap_apr = q6asm_mmap_apr_reg();
+ common_client.apr = common_client.mmap_apr;
+ if (common_client.mmap_apr == NULL) {
+ pr_err("%s: q6asm_mmap_apr_reg failed\n",
+ __func__);
+ result = -EPERM;
+ goto mmap_fail;
+ }
+ /* Only call this once */
+ set_custom_topology = 0;
- /* Use first asm buf to map memory */
- if (common_client.port[IN].buf == NULL) {
- pr_err("%s: common buf is NULL\n",
- __func__);
- goto done;
- }
- common_client.port[IN].buf->phys = cal_block.cal_paddr;
+ /* Use first asm buf to map memory */
+ if (common_client.port[IN].buf == NULL) {
+ pr_err("%s: common buf is NULL\n",
+ __func__);
+ goto err_map;
+ }
+ common_client.port[IN].buf->phys = cal_block.cal_paddr;
- result = q6asm_memory_map_regions(&common_client,
- IN, size, 1, 1);
- if (result < 0) {
- pr_err("%s: mmap did not work! addr = 0x%x, size = %d\n",
- __func__, cal_block.cal_paddr,
- cal_block.cal_size);
- goto done;
- }
+ result = q6asm_memory_map_regions(&common_client,
+ IN, size, 1, 1);
+ if (result < 0) {
+ pr_err("%s: mmap did not work! addr = 0x%pa, size = %zd\n",
+ __func__, &cal_block.cal_paddr,
+ cal_block.cal_size);
+ goto err_map;
+ }
- list_for_each_safe(ptr, next,
- &common_client.port[IN].mem_map_handle) {
- buf_node = list_entry(ptr, struct asm_buffer_node,
- list);
- if (buf_node->buf_addr_lsw == cal_block.cal_paddr) {
- topology_map_handle = buf_node->mmap_hdl;
- break;
- }
- }
-
- result = q6asm_mmap_apr_dereg();
- if (result < 0) {
- pr_err("%s: q6asm_mmap_apr_dereg failed, err %d\n",
- __func__, result);
- } else {
- common_client.mmap_apr = NULL;
+ list_for_each_safe(ptr, next,
+ &common_client.port[IN].mem_map_handle) {
+ buf_node = list_entry(ptr, struct asm_buffer_node,
+ list);
+ if (buf_node->buf_addr_lsw == cal_block.cal_paddr) {
+ topology_map_handle = buf_node->mmap_hdl;
+ break;
}
}
@@ -468,7 +460,7 @@
if (result < 0) {
pr_err("%s: Set topologies failed payload = 0x%x\n",
__func__, cal_block.cal_paddr);
- goto done;
+ goto err_unmap;
}
result = wait_event_timeout(ac->cmd_wait,
@@ -476,10 +468,15 @@
if (!result) {
pr_err("%s: Set topologies failed after timedout payload = 0x%x\n",
__func__, cal_block.cal_paddr);
- goto done;
+ goto err_unmap;
}
-
-done:
+ return;
+err_unmap:
+ q6asm_memory_unmap_regions(ac, IN);
+err_map:
+ q6asm_mmap_apr_dereg();
+ set_custom_topology = 1;
+mmap_fail:
return;
}
@@ -679,8 +676,11 @@
while (cnt >= 0) {
if (port->buf[cnt].data) {
- msm_audio_ion_free(port->buf[cnt].client,
- port->buf[cnt].handle);
+ if (!rc)
+ msm_audio_ion_free(
+ port->buf[cnt].client,
+ port->buf[cnt].handle);
+
port->buf[cnt].client = NULL;
port->buf[cnt].handle = NULL;
port->buf[cnt].data = NULL;
@@ -726,7 +726,9 @@
(void *)&port->buf[0].phys,
(void *)port->buf[0].client,
(void *)port->buf[0].handle);
- msm_audio_ion_free(port->buf[0].client, port->buf[0].handle);
+ if (!rc)
+ msm_audio_ion_free(port->buf[0].client,
+ port->buf[0].handle);
port->buf[0].client = NULL;
port->buf[0].handle = NULL;
}
@@ -768,6 +770,7 @@
apr_deregister(ac->apr);
ac->apr = NULL;
ac->mmap_apr = NULL;
+ rtac_set_asm_handle(ac->session, ac->apr);
q6asm_session_free(ac);
q6asm_mmap_apr_dereg();
@@ -857,7 +860,7 @@
if (ac->apr == NULL) {
pr_err("%s Registration with APR failed\n", __func__);
mutex_unlock(&session_lock);
- goto fail;
+ goto fail_apr1;
}
ac->apr2 = apr_register("ADSP", "ASM", \
(apr_fn)q6asm_callback,\
@@ -867,16 +870,17 @@
if (ac->apr2 == NULL) {
pr_err("%s Registration with APR-2 failed\n", __func__);
mutex_unlock(&session_lock);
- goto fail;
+ goto fail_apr2;
}
+
rtac_set_asm_handle(n, ac->apr);
pr_debug("%s Registering the common port with APR\n", __func__);
ac->mmap_apr = q6asm_mmap_apr_reg();
if (ac->mmap_apr == NULL) {
mutex_unlock(&session_lock);
- goto fail;
- }
+ goto fail_mmap;
+ }
init_waitqueue_head(&ac->cmd_wait);
init_waitqueue_head(&ac->time_wait);
@@ -899,9 +903,12 @@
mutex_unlock(&session_lock);
return ac;
-fail:
- q6asm_audio_client_free(ac);
- return NULL;
+fail_mmap:
+ apr_deregister(ac->apr2);
+fail_apr2:
+ apr_deregister(ac->apr);
+fail_apr1:
+ q6asm_session_free(ac);
fail_session:
kfree(ac);
return NULL;
@@ -1115,12 +1122,20 @@
payload = data->payload;
if (data->opcode == RESET_EVENTS) {
+ struct audio_client *ac_mmap = (struct audio_client *)priv;
+ if (ac_mmap == NULL) {
+ pr_err("%s ac or priv NULL\n", __func__);
+ return -EINVAL;
+ }
pr_debug("%s: Reset event is received: %d %d apr[%p]\n",
__func__,
data->reset_event,
data->reset_proc,
this_mmap.apr);
+ atomic_set(&this_mmap.ref_cnt, 0);
apr_reset(this_mmap.apr);
+ this_mmap.apr = NULL;
+ ac_mmap->mmap_apr = NULL;
for (; i <= OUT; i++) {
list_for_each_safe(ptr, next,
&common_client.port[i].mem_map_handle) {
@@ -1135,7 +1150,6 @@
}
pr_debug("%s:Clearing custom topology\n", __func__);
}
- this_mmap.apr = NULL;
reset_custom_topology_flags();
set_custom_topology = 1;
topology_map_handle = 0;
@@ -1162,6 +1176,13 @@
if (payload[1] != 0) {
pr_err("%s: cmd = 0x%x returned error = 0x%x sid:%d\n",
__func__, payload[0], payload[1], sid);
+ if (payload[0] ==
+ ASM_CMD_SHARED_MEM_UNMAP_REGIONS)
+ atomic_set(&ac->unmap_cb_success, 0);
+ } else {
+ if (payload[0] ==
+ ASM_CMD_SHARED_MEM_UNMAP_REGIONS)
+ atomic_set(&ac->unmap_cb_success, 1);
}
if (atomic_read(&ac->cmd_state)) {
@@ -3175,6 +3196,7 @@
TRUE, ((ac->session << 8) | dir));
atomic_set(&ac->cmd_state, 1);
mem_unmap.hdr.opcode = ASM_CMD_SHARED_MEM_UNMAP_REGIONS;
+ mem_unmap.mem_map_handle = 0;
list_for_each_safe(ptr, next, &ac->port[dir].mem_map_handle) {
buf_node = list_entry(ptr, struct asm_buffer_node,
list);
@@ -3186,6 +3208,12 @@
}
pr_debug("%s: mem_unmap-mem_map_handle: 0x%x",
__func__, mem_unmap.mem_map_handle);
+
+ if (mem_unmap.mem_map_handle == 0) {
+ pr_err("%s Do not send null mem handle to DSP\n", __func__);
+ rc = 0;
+ goto fail_cmd;
+ }
rc = apr_send_pkt(ac->mmap_apr, (uint32_t *) &mem_unmap);
if (rc < 0) {
pr_err("mem_unmap op[0x%x]rc[%d]\n",
@@ -3197,7 +3225,13 @@
rc = wait_event_timeout(ac->cmd_wait,
(atomic_read(&ac->cmd_state) == 0), 5 * HZ);
if (!rc) {
- pr_err("timeout. waited for memory_unmap\n");
+ pr_err("%s timeout. waited for memory_unmap of handle 0x%x\n",
+ __func__, mem_unmap.mem_map_handle);
+ rc = -ETIMEDOUT;
+ goto fail_cmd;
+ } else if (atomic_read(&ac->unmap_cb_success) == 0) {
+ pr_err("%s Error in mem unmap callback of handle 0x%x\n",
+ __func__, mem_unmap.mem_map_handle);
rc = -EINVAL;
goto fail_cmd;
}
@@ -3357,6 +3391,7 @@
port = &ac->port[dir];
buf_add = (uint32_t)port->buf->phys;
mem_unmap.hdr.opcode = ASM_CMD_SHARED_MEM_UNMAP_REGIONS;
+ mem_unmap.mem_map_handle = 0;
list_for_each_safe(ptr, next, &ac->port[dir].mem_map_handle) {
buf_node = list_entry(ptr, struct asm_buffer_node,
list);
@@ -3369,6 +3404,12 @@
pr_debug("%s: mem_unmap-mem_map_handle: 0x%x",
__func__, mem_unmap.mem_map_handle);
+
+ if (mem_unmap.mem_map_handle == 0) {
+ pr_err("%s Do not send null mem handle to DSP\n", __func__);
+ rc = 0;
+ goto fail_cmd;
+ }
rc = apr_send_pkt(ac->mmap_apr, (uint32_t *) &mem_unmap);
if (rc < 0) {
pr_err("mmap_regions op[0x%x]rc[%d]\n",
@@ -3379,7 +3420,14 @@
rc = wait_event_timeout(ac->cmd_wait,
(atomic_read(&ac->cmd_state) == 0), 5*HZ);
if (!rc) {
- pr_err("timeout. waited for memory_unmap\n");
+ pr_err("%s timeout. waited for memory_unmap of handle 0x%x\n",
+ __func__, mem_unmap.mem_map_handle);
+ rc = -ETIMEDOUT;
+ goto fail_cmd;
+ } else if (atomic_read(&ac->unmap_cb_success) == 0) {
+ pr_err("%s Error in mem unmap callback of handle 0x%x\n",
+ __func__, mem_unmap.mem_map_handle);
+ rc = -EINVAL;
goto fail_cmd;
}
rc = 0;
diff --git a/sound/soc/msm/qdsp6v2/q6lsm.c b/sound/soc/msm/qdsp6v2/q6lsm.c
index a88b733..61222fe 100644
--- a/sound/soc/msm/qdsp6v2/q6lsm.c
+++ b/sound/soc/msm/qdsp6v2/q6lsm.c
@@ -783,7 +783,7 @@
client->lsm_cal_size = lsm_cal.cal_size;
memcpy((client->sound_model.data + pad_zero +
client->sound_model.size),
- (uint32_t *)lsm_cal.cal_kvaddr, len);
+ (uint32_t *)lsm_cal.cal_kvaddr, lsm_cal.cal_size);
} else {
rc = -EBUSY;
goto fail;
diff --git a/sound/soc/msm/qdsp6v2/q6voice.c b/sound/soc/msm/qdsp6v2/q6voice.c
index ac8b018..1ca9b1a 100644
--- a/sound/soc/msm/qdsp6v2/q6voice.c
+++ b/sound/soc/msm/qdsp6v2/q6voice.c
@@ -94,6 +94,8 @@
static int voice_alloc_oob_mem_table(void);
static int voice_alloc_and_map_cal_mem(struct voice_data *v);
static int voice_alloc_and_map_oob_mem(struct voice_data *v);
+static int voc_disable_cvp(uint32_t session_id);
+static int voc_enable_cvp(uint32_t session_id);
static struct voice_data *voice_get_session_by_idx(int idx);
@@ -1081,39 +1083,37 @@
}
mvm_handle = voice_get_mvm_handle(v);
- if (v->tty_mode) {
- /* send tty mode cmd to mvm */
- mvm_tty_mode_cmd.hdr.hdr_field = APR_HDR_FIELD(
- APR_MSG_TYPE_SEQ_CMD,
- APR_HDR_LEN(APR_HDR_SIZE),
- APR_PKT_VER);
- mvm_tty_mode_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
- sizeof(mvm_tty_mode_cmd) -
- APR_HDR_SIZE);
- pr_debug("%s: pkt size = %d\n",
- __func__, mvm_tty_mode_cmd.hdr.pkt_size);
- mvm_tty_mode_cmd.hdr.src_port =
- voice_get_idx_for_session(v->session_id);
- mvm_tty_mode_cmd.hdr.dest_port = mvm_handle;
- mvm_tty_mode_cmd.hdr.token = 0;
- mvm_tty_mode_cmd.hdr.opcode = VSS_ISTREAM_CMD_SET_TTY_MODE;
- mvm_tty_mode_cmd.tty_mode.mode = v->tty_mode;
- pr_debug("tty mode =%d\n", mvm_tty_mode_cmd.tty_mode.mode);
+ /* send tty mode cmd to mvm */
+ mvm_tty_mode_cmd.hdr.hdr_field = APR_HDR_FIELD(
+ APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ mvm_tty_mode_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(mvm_tty_mode_cmd) -
+ APR_HDR_SIZE);
+ pr_debug("%s: pkt size = %d\n",
+ __func__, mvm_tty_mode_cmd.hdr.pkt_size);
+ mvm_tty_mode_cmd.hdr.src_port =
+ voice_get_idx_for_session(v->session_id);
+ mvm_tty_mode_cmd.hdr.dest_port = mvm_handle;
+ mvm_tty_mode_cmd.hdr.token = 0;
+ mvm_tty_mode_cmd.hdr.opcode = VSS_ISTREAM_CMD_SET_TTY_MODE;
+ mvm_tty_mode_cmd.tty_mode.mode = v->tty_mode;
+ pr_debug("tty mode =%d\n", mvm_tty_mode_cmd.tty_mode.mode);
- v->mvm_state = CMD_STATUS_FAIL;
- ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_tty_mode_cmd);
- if (ret < 0) {
- pr_err("%s: Error %d sending SET_TTY_MODE\n",
- __func__, ret);
- goto fail;
- }
- ret = wait_event_timeout(v->mvm_wait,
- (v->mvm_state == CMD_STATUS_SUCCESS),
- msecs_to_jiffies(TIMEOUT_MS));
- if (!ret) {
- pr_err("%s: wait_event timeout\n", __func__);
- goto fail;
- }
+ v->mvm_state = CMD_STATUS_FAIL;
+ ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_tty_mode_cmd);
+ if (ret < 0) {
+ pr_err("%s: Error %d sending SET_TTY_MODE\n",
+ __func__, ret);
+ goto fail;
+ }
+ ret = wait_event_timeout(v->mvm_wait,
+ (v->mvm_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+ goto fail;
}
return 0;
fail:
@@ -3166,8 +3166,7 @@
voice_send_netid_timing_cmd(v);
}
- /* enable slowtalk if st_enable is set */
- if (v->st_enable)
+ if (v->st_enable && !v->tty_mode)
voice_send_set_pp_enable_cmd(v,
MODULE_ID_VOICE_MODULE_ST,
v->st_enable);
@@ -4379,7 +4378,7 @@
return ret;
}
-int voc_disable_cvp(uint32_t session_id)
+static int voc_disable_cvp(uint32_t session_id)
{
struct voice_data *v = voice_get_session(session_id);
int ret = 0;
@@ -4414,7 +4413,7 @@
return ret;
}
-int voc_enable_cvp(uint32_t session_id)
+static int voc_enable_cvp(uint32_t session_id)
{
struct voice_data *v = voice_get_session(session_id);
int ret = 0;
@@ -4486,10 +4485,8 @@
goto fail;
}
- /* Send tty mode if tty device is used */
voice_send_tty_mode_cmd(v);
- /* enable slowtalk */
- if (v->st_enable)
+ if (v->st_enable && !v->tty_mode)
voice_send_set_pp_enable_cmd(v,
MODULE_ID_VOICE_MODULE_ST,
v->st_enable);
@@ -4570,8 +4567,8 @@
return ret;
}
-int voc_set_rx_device_mute(uint32_t session_id, uint32_t mute,
- uint32_t ramp_duration)
+int voc_set_device_mute(uint32_t session_id, uint32_t dir, uint32_t mute,
+ uint32_t ramp_duration)
{
struct voice_data *v = NULL;
int ret = 0;
@@ -4581,16 +4578,23 @@
while (voice_itr_get_next_session(&itr, &v)) {
if (v != NULL) {
mutex_lock(&v->lock);
- v->dev_rx.dev_mute = mute;
- v->dev_rx.dev_mute_ramp_duration_ms =
+ if (dir == VSS_IVOLUME_DIRECTION_TX) {
+ v->dev_tx.dev_mute = mute;
+ v->dev_tx.dev_mute_ramp_duration_ms =
ramp_duration;
+ } else {
+ v->dev_rx.dev_mute = mute;
+ v->dev_rx.dev_mute_ramp_duration_ms =
+ ramp_duration;
+ }
+
if (((v->voc_state == VOC_RUN) ||
(v->voc_state == VOC_STANDBY)) &&
(v->lch_mode == 0))
ret = voice_send_device_mute_cmd(v,
- VSS_IVOLUME_DIRECTION_RX,
- v->dev_rx.dev_mute,
- ramp_duration);
+ dir,
+ mute,
+ ramp_duration);
mutex_unlock(&v->lock);
} else {
pr_err("%s: invalid session_id 0x%x\n", __func__,
@@ -4682,8 +4686,8 @@
v->st_enable = enable;
if (v->voc_state == VOC_RUN) {
- if (module_id ==
- MODULE_ID_VOICE_MODULE_ST)
+ if ((module_id == MODULE_ID_VOICE_MODULE_ST) &&
+ (!v->tty_mode))
ret = voice_send_set_pp_enable_cmd(v,
MODULE_ID_VOICE_MODULE_ST,
enable);
@@ -4906,6 +4910,110 @@
return ret;
}
+int voc_disable_device(uint32_t session_id)
+{
+ struct voice_data *v = voice_get_session(session_id);
+ int ret = 0;
+
+ pr_debug("%s: voc state=%d\n", __func__, v->voc_state);
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ mutex_lock(&v->lock);
+ if (v->voc_state == VOC_RUN) {
+ ret = voice_pause_voice_call(v);
+ if (ret < 0) {
+ pr_err("%s: Pause Voice Call failed for session 0x%x, err %d!\n",
+ __func__, v->session_id, ret);
+ goto done;
+ }
+ rtac_remove_voice(voice_get_cvs_handle(v));
+ voice_send_cvp_deregister_vol_cal_cmd(v);
+ voice_send_cvp_deregister_cal_cmd(v);
+ voice_send_cvp_deregister_dev_cfg_cmd(v);
+
+ v->voc_state = VOC_CHANGE;
+ } else {
+ pr_debug("%s: called in voc state=%d, No_OP\n",
+ __func__, v->voc_state);
+ }
+
+ if (common.ec_ref_ext)
+ voc_set_ext_ec_ref(AFE_PORT_INVALID, false);
+done:
+ mutex_unlock(&v->lock);
+
+ return ret;
+}
+
+int voc_enable_device(uint32_t session_id)
+{
+ struct voice_data *v = voice_get_session(session_id);
+ int ret = 0;
+
+ pr_debug("%s: voc state=%d\n", __func__, v->voc_state);
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ mutex_lock(&v->lock);
+ if (v->voc_state == VOC_CHANGE) {
+ ret = voice_send_tty_mode_cmd(v);
+ if (ret < 0) {
+ pr_err("%s: Sending TTY mode failed, ret=%d\n",
+ __func__, ret);
+ /* Not a critical error, allow voice call to continue */
+ }
+
+ if (v->tty_mode) {
+ /* disable slowtalk */
+ voice_send_set_pp_enable_cmd(v,
+ MODULE_ID_VOICE_MODULE_ST,
+ 0);
+ } else {
+ /* restore slowtalk */
+ voice_send_set_pp_enable_cmd(v,
+ MODULE_ID_VOICE_MODULE_ST,
+ v->st_enable);
+ }
+
+ ret = voice_send_set_device_cmd(v);
+ if (ret < 0) {
+ pr_err("%s: Set device failed, ret=%d\n",
+ __func__, ret);
+ goto done;
+ }
+
+ voice_send_cvp_register_dev_cfg_cmd(v);
+ voice_send_cvp_register_cal_cmd(v);
+ voice_send_cvp_register_vol_cal_cmd(v);
+
+ rtac_add_voice(voice_get_cvs_handle(v),
+ voice_get_cvp_handle(v),
+ v->dev_rx.port_id, v->dev_tx.port_id,
+ v->session_id);
+
+ ret = voice_send_start_voice_cmd(v);
+ if (ret < 0) {
+ pr_err("%s: Fail in sending START_VOICE, ret=%d\n",
+ __func__, ret);
+ goto done;
+ }
+ v->voc_state = VOC_RUN;
+ } else {
+ pr_debug("%s: called in voc state=%d, No_OP\n",
+ __func__, v->voc_state);
+ }
+
+done:
+ mutex_unlock(&v->lock);
+
+ return ret;
+}
+
int voc_set_lch(uint32_t session_id, enum voice_lch_mode lch_mode)
{
struct voice_data *v = voice_get_session(session_id);
diff --git a/sound/soc/msm/qdsp6v2/q6voice.h b/sound/soc/msm/qdsp6v2/q6voice.h
index 59c86cd..c6f3482 100644
--- a/sound/soc/msm/qdsp6v2/q6voice.h
+++ b/sound/soc/msm/qdsp6v2/q6voice.h
@@ -1444,11 +1444,9 @@
uint32_t ramp_duration);
int voc_set_tx_mute(uint32_t session_id, uint32_t dir, uint32_t mute,
uint32_t ramp_duration);
-int voc_set_rx_device_mute(uint32_t session_id, uint32_t mute,
- uint32_t ramp_duration);
+int voc_set_device_mute(uint32_t session_id, uint32_t dir, uint32_t mute,
+ uint32_t ramp_duration);
int voc_get_rx_device_mute(uint32_t session_id);
-int voc_disable_cvp(uint32_t session_id);
-int voc_enable_cvp(uint32_t session_id);
int voc_set_route_flag(uint32_t session_id, uint8_t path_dir, uint8_t set);
uint8_t voc_get_route_flag(uint32_t session_id, uint8_t path_dir);
int voc_enable_dtmf_rx_detection(uint32_t session_id, uint32_t enable);
@@ -1470,5 +1468,7 @@
int voice_get_idx_for_session(u32 session_id);
int voc_set_ext_ec_ref(uint16_t port_id, bool state);
int voc_update_amr_vocoder_rate(uint32_t session_id);
+int voc_disable_device(uint32_t session_id);
+int voc_enable_device(uint32_t session_id);
#endif
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index 56efb97..796d6b4 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -2102,8 +2102,10 @@
{
struct snd_soc_dapm_widget *w = dapm_find_widget(dapm, pin, true);
+ mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_PCM);
if (!w) {
dev_err(dapm->dev, "dapm: unknown pin %s\n", pin);
+ mutex_unlock(&dapm->card->dapm_mutex);
return -EINVAL;
}
@@ -2112,6 +2114,7 @@
w->force = 0;
dapm_mark_dirty(w, "pin configuration");
+ mutex_unlock(&dapm->card->dapm_mutex);
return 0;
}
@@ -3101,7 +3104,7 @@
dev_dbg(rtd->dev, "rtd stream %d event %d\n", stream, event);
- mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+ mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_PCM);
if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
widget_stream_event(pdapm, rtd->cpu_dai->playback_aif, event);
widget_stream_event(cdapm, rtd->codec_dai->playback_aif, event);
@@ -3186,8 +3189,10 @@
{
struct snd_soc_dapm_widget *w = dapm_find_widget(dapm, pin, true);
+ mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_PCM);
if (!w) {
dev_err(dapm->dev, "dapm: unknown pin %s\n", pin);
+ mutex_unlock(&dapm->card->dapm_mutex);
return -EINVAL;
}
@@ -3195,6 +3200,7 @@
w->connected = 1;
w->force = 1;
dapm_mark_dirty(w, "force enable");
+ mutex_unlock(&dapm->card->dapm_mutex);
return 0;
}